refactor: new truss API
This commit is contained in:
parent
e68d3e8e5a
commit
89f98034d3
75
build.js
75
build.js
|
@ -1,75 +0,0 @@
|
||||||
const path = require('path');
|
|
||||||
const {spawnSync} = require('child_process');
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
const dotenv = require('dotenv');
|
|
||||||
const mkdirp = require('mkdirp');
|
|
||||||
|
|
||||||
const {emitObject, emitString} = require('./compose');
|
|
||||||
|
|
||||||
['.common.env', '.production.env', '.env'].forEach((filename) => {
|
|
||||||
dotenv.config({path: path.join(__dirname, filename)});
|
|
||||||
});
|
|
||||||
|
|
||||||
const cwd = process.cwd();
|
|
||||||
const distPath = path.join(cwd, 'dist', 'production');
|
|
||||||
|
|
||||||
const services = process.env.SERVICES.split(',');
|
|
||||||
services.push('gateway');
|
|
||||||
|
|
||||||
const composeFile = emitString(emitObject(services));
|
|
||||||
|
|
||||||
mkdirp.sync(distPath);
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(distPath, 'docker-compose.yml'), composeFile
|
|
||||||
);
|
|
||||||
|
|
||||||
fs.copyFileSync(
|
|
||||||
path.join(cwd, '.common.env'),
|
|
||||||
path.join(distPath, '.env'),
|
|
||||||
);
|
|
||||||
|
|
||||||
fs.appendFileSync(
|
|
||||||
path.join(distPath, '.env'),
|
|
||||||
fs.readFileSync(path.join(cwd, '.production.env')),
|
|
||||||
);
|
|
||||||
|
|
||||||
fs.appendFileSync(
|
|
||||||
path.join(distPath, '.env'),
|
|
||||||
fs.readFileSync(path.join(cwd, '.env')),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const service of services) {
|
|
||||||
|
|
||||||
const servicePath = path.join(cwd, 'services', service);
|
|
||||||
const serviceDistPath = path.join(distPath, service);
|
|
||||||
|
|
||||||
mkdirp.sync(serviceDistPath);
|
|
||||||
|
|
||||||
spawnSync('docker', [
|
|
||||||
'run',
|
|
||||||
'--env-file', './.common.env',
|
|
||||||
'--env-file', './.production.env',
|
|
||||||
'--env-file', './.env',
|
|
||||||
|
|
||||||
'-e', 'DEBUG=truss:*',
|
|
||||||
'-e', 'DEBUG_COLORS=1',
|
|
||||||
'-e', 'DEBUG_HIDE_DATE=1',
|
|
||||||
'-e', 'NODE_PRESERVE_SYMLINKS=1',
|
|
||||||
|
|
||||||
'-v', `${path.join(cwd, 'lib')}:/var/node/lib:ro`,
|
|
||||||
'-v', `${servicePath}:/var/node/src:ro`,
|
|
||||||
'-v', `${serviceDistPath}:/var/node/dist`,
|
|
||||||
|
|
||||||
'docker.i12e.cha0s.io/cha0s6983/truss-dev',
|
|
||||||
|
|
||||||
'yarn', 'run', 'build',
|
|
||||||
], {
|
|
||||||
stdio: 'inherit',
|
|
||||||
});
|
|
||||||
|
|
||||||
require('rimraf').sync(
|
|
||||||
path.join(serviceDistPath, '{node_modules,package.json,yarn.lock}')
|
|
||||||
)
|
|
||||||
}
|
|
161
bundle.js
161
bundle.js
|
@ -1,161 +0,0 @@
|
||||||
const path = require('path');
|
|
||||||
const {fork} = require('child_process');
|
|
||||||
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const dotenv = require('dotenv');
|
|
||||||
|
|
||||||
['.common.env', '.dev.env', '.env'].forEach((filename) => {
|
|
||||||
dotenv.config({path: path.join(__dirname, filename)});
|
|
||||||
});
|
|
||||||
|
|
||||||
const services = process.env.SERVICES.split(',');
|
|
||||||
services.push('gateway');
|
|
||||||
|
|
||||||
const longestServiceNameLength = services.reduce((l, r) => {
|
|
||||||
return (r.length > l.length) ? r : l;
|
|
||||||
}, '').length;
|
|
||||||
|
|
||||||
let color = 1;
|
|
||||||
const colors = [
|
|
||||||
'redBright', 'greenBright', 'yellowBright', 'blueBright', 'magentaBright',
|
|
||||||
'cyanBright', 'whiteBright', 'red', 'green', 'yellow', 'blue', 'magenta',
|
|
||||||
'cyan', 'white', 'gray',
|
|
||||||
];
|
|
||||||
|
|
||||||
const children = [];
|
|
||||||
const childrenOfService = {};
|
|
||||||
|
|
||||||
const bundleServices = (process.env.BUNDLE_SERVICES || '').split(',');
|
|
||||||
const serviceCounts = bundleServices.reduce((l, r) => {
|
|
||||||
const [service, count = 1] = r.split(':');
|
|
||||||
if (service in l) { l[service] = count; }
|
|
||||||
return l;
|
|
||||||
}, services.reduce((l, r) => {
|
|
||||||
l[r] = 1;
|
|
||||||
return l;
|
|
||||||
}, {}));
|
|
||||||
|
|
||||||
const cwd = process.cwd();
|
|
||||||
services.forEach((service) => {
|
|
||||||
for (let i = 0; i < serviceCounts[service]; ++i) {
|
|
||||||
forkService(service);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function forkService(service) {
|
|
||||||
|
|
||||||
const servicePath = path.join(cwd, 'services', service);
|
|
||||||
|
|
||||||
let index;
|
|
||||||
const colorizer = chalk[colors[color++]];
|
|
||||||
const dresser = (line) => {
|
|
||||||
const prefix = colorizer(`${(index + '').padStart(2, '0')} » ${
|
|
||||||
service.padEnd(longestServiceNameLength)
|
|
||||||
} |`);
|
|
||||||
console.error(`${prefix} ${line}`);
|
|
||||||
};
|
|
||||||
const logger = (message) => {
|
|
||||||
message.split('\n').forEach(dresser);
|
|
||||||
};
|
|
||||||
|
|
||||||
const child = fork(servicePath, [], {
|
|
||||||
env: Object.assign({
|
|
||||||
SOURCE_PATH: servicePath,
|
|
||||||
}, process.env),
|
|
||||||
stdio: 'pipe',
|
|
||||||
});
|
|
||||||
|
|
||||||
const forwardStream = (data) => {
|
|
||||||
logger(data.toString('utf8').replace(/\n$/, ''));
|
|
||||||
};
|
|
||||||
child.stdout.on('data', forwardStream);
|
|
||||||
child.stderr.on('data', forwardStream);
|
|
||||||
|
|
||||||
childrenOfService[service] = childrenOfService[service] || [];
|
|
||||||
childrenOfService[service].push(child);
|
|
||||||
|
|
||||||
index = children.length;
|
|
||||||
logger(`forking...`);
|
|
||||||
children.push(child);
|
|
||||||
|
|
||||||
const createChildActions = (child) => {
|
|
||||||
const actions = createActions(child);
|
|
||||||
return (action) => {
|
|
||||||
actions[action.type](action);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
child.on('message', createChildActions(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
function createActions (child) {
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
listening: () => (child.listening = true),
|
|
||||||
|
|
||||||
connect: ({payload: {clientId, service}}) => {
|
|
||||||
|
|
||||||
const candidate = randomService(service);
|
|
||||||
if (candidate) {
|
|
||||||
|
|
||||||
candidate.send({
|
|
||||||
type: 'connection_from',
|
|
||||||
payload: {
|
|
||||||
clientId,
|
|
||||||
from: children.indexOf(child),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
child.send({
|
|
||||||
type: 'connection_to',
|
|
||||||
payload: {
|
|
||||||
clientId,
|
|
||||||
to: children.indexOf(candidate),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
child.send({
|
|
||||||
type: 'connection_error',
|
|
||||||
payload: {
|
|
||||||
clientId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
client_send: ({payload}) => {
|
|
||||||
children[payload.to].send({
|
|
||||||
type: 'server_recv',
|
|
||||||
payload: {
|
|
||||||
clientId: payload.clientId,
|
|
||||||
data: payload.data,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
server_send: ({payload}) => {
|
|
||||||
children[payload.to].send({
|
|
||||||
type: 'client_recv',
|
|
||||||
payload: {
|
|
||||||
clientId: payload.clientId,
|
|
||||||
data: payload.data,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function randomService(service) {
|
|
||||||
|
|
||||||
const candidates = [...childrenOfService[service]];
|
|
||||||
while (candidates.length > 0) {
|
|
||||||
const index = Math.floor(Math.random() * candidates.length);
|
|
||||||
const [candidate] = candidates.splice(index, 1);
|
|
||||||
if (candidate.listening) {
|
|
||||||
return candidate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
66
compose.js
66
compose.js
|
@ -1,66 +0,0 @@
|
||||||
const yaml = require('js-yaml');
|
|
||||||
|
|
||||||
exports.emitString = function(object) {
|
|
||||||
return yaml.safeDump(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.emitObject = function(services) {
|
|
||||||
|
|
||||||
services = services.slice(0);
|
|
||||||
if (-1 === services.indexOf('gateway')) {
|
|
||||||
services.push('gateway');
|
|
||||||
}
|
|
||||||
|
|
||||||
const composeFile = {
|
|
||||||
version: '3',
|
|
||||||
services: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const service of services) {
|
|
||||||
composeFile.services[service] = emitService(service);
|
|
||||||
const modulePath = `./services/${service}/compose`;
|
|
||||||
try {
|
|
||||||
require(modulePath)(composeFile.services[service], composeFile);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
const message = `Cannot find module '${modulePath}'`;
|
|
||||||
if (message !== error.message) {
|
|
||||||
console.error(error);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return composeFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
function emitService(service) {
|
|
||||||
const definition = {};
|
|
||||||
|
|
||||||
definition.image = `docker.i12e.cha0s.io/cha0s6983/truss-${
|
|
||||||
'production' === process.env.NODE_ENV ? 'production': 'dev'
|
|
||||||
}`;
|
|
||||||
if ('production' === process.env.NODE_ENV) {
|
|
||||||
definition.env_file = [
|
|
||||||
'.env',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
definition.env_file = [
|
|
||||||
'.common.env', `.dev.env`, '.env',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if ('production' === process.env.NODE_ENV) {
|
|
||||||
definition.volumes = [
|
|
||||||
`./${service}:/var/node/dist`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
definition.volumes = [
|
|
||||||
'./lib:/var/node/lib',
|
|
||||||
`./services/${service}:/var/node/src`,
|
|
||||||
`./dist/dev/${service}:/var/node/dist`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return definition;
|
|
||||||
}
|
|
44
dev.js
44
dev.js
|
@ -1,44 +0,0 @@
|
||||||
const {spawn} = require('child_process');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const dotenv = require('dotenv');
|
|
||||||
|
|
||||||
['.common.env', '.dev.env', '.env'].forEach((filename) => {
|
|
||||||
dotenv.config({path: path.join(__dirname, filename)});
|
|
||||||
});
|
|
||||||
|
|
||||||
const debug = require('debug')('truss:dev');
|
|
||||||
|
|
||||||
const {emitObject, emitString} = require('./compose');
|
|
||||||
|
|
||||||
const services = process.env.SERVICES.split(',');
|
|
||||||
services.push('gateway');
|
|
||||||
|
|
||||||
const composeFile = emitString(emitObject(services));
|
|
||||||
debug('Compose file:');
|
|
||||||
debug(composeFile);
|
|
||||||
|
|
||||||
debug('Ensuring dist directory exists...');
|
|
||||||
require('mkdirp').sync(path.join(process.cwd(), 'dist', 'dev'));
|
|
||||||
|
|
||||||
['down', 'up'].reduce((promise, op) => {
|
|
||||||
|
|
||||||
const child = spawn('docker-compose', [
|
|
||||||
'-f', '-',
|
|
||||||
op,
|
|
||||||
], {
|
|
||||||
env: process.env,
|
|
||||||
stdio: ['pipe', 'inherit', 'inherit'],
|
|
||||||
});
|
|
||||||
child.stdin.write(composeFile);
|
|
||||||
|
|
||||||
return promise.then(() => {
|
|
||||||
child.stdin.end();
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
child.on('exit', () => {
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
}, Promise.resolve());
|
|
16
package.json
16
package.json
|
@ -4,20 +4,14 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "cha0s",
|
"author": "cha0s",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bundle": "node bundle.js",
|
"bundle": "node -e '' -r @truss/core/task/bundle",
|
||||||
"build": "node build.js",
|
"build": "node -e '' -r @truss/docker/task/build",
|
||||||
"start": "npm run dev",
|
"start": "npm run dev",
|
||||||
"dev": "node dev.js"
|
"dev": "node -e '' -r @truss/docker/task/development"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@truss/truss": "^1.2.5",
|
"@truss/core": "1.x",
|
||||||
"chalk": "^2.4.1",
|
"@truss/docker": "1.x"
|
||||||
"debug": "^3.1.0",
|
|
||||||
"dotenv": "^6.0.0",
|
|
||||||
"js-yaml": "^3.12.0",
|
|
||||||
"mkdirp": "^0.5.1",
|
|
||||||
"rimraf": "^2.6.2",
|
|
||||||
"supports-color": "^5.5.0"
|
|
||||||
},
|
},
|
||||||
"license": "UNLICENSED"
|
"license": "UNLICENSED"
|
||||||
}
|
}
|
||||||
|
|
35
services/gateway/actions.js
Normal file
35
services/gateway/actions.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
const {sendActionToService} = require('@truss/comm');
|
||||||
|
|
||||||
|
module.exports = (serviceMap) => ({
|
||||||
|
'truss/hook-services': ({payload: {hook, args}}) => {
|
||||||
|
if (!(hook in serviceMap.hooks)) { return []; }
|
||||||
|
return serviceMap.hooks[hook];
|
||||||
|
},
|
||||||
|
'truss/invoke': ({payload: {hook, args}}) => {
|
||||||
|
if (!(hook in serviceMap.hooks)) { return {}; }
|
||||||
|
return invokeHook(serviceMap.hooks[hook], hook, args);
|
||||||
|
},
|
||||||
|
'truss/invoke-flat': ({payload: {hook, args}}) => {
|
||||||
|
if (!(hook in serviceMap.hooks)) { return []; }
|
||||||
|
return invokeHookFlat(serviceMap.hooks[hook], hook, args);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function invokeHookFlat(services, hook, args) {
|
||||||
|
debug(`invoking hook flat(${hook}(${JSON.stringify(args, null, ' ')}))...`);
|
||||||
|
invokeHookFlatInternal(services, hook, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function invokeHookFlatInternal(services, hook, args) {
|
||||||
|
const action = {type: 'truss/hook', payload: {hook, args}};
|
||||||
|
return Promise.all(services.map((service) => {
|
||||||
|
return sendActionToService(action, service);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function invokeHook(services, hook, args) {
|
||||||
|
debug(`invoking hook ${hook}(${JSON.stringify(args, null, ' ')})...`);
|
||||||
|
return invokeHookFlatInternal(services, hook, args).then((result) => {
|
||||||
|
return result.reduce((l, r, i) => (l[services[i]] = r, l), {});
|
||||||
|
});
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ const http = require('http');
|
||||||
|
|
||||||
const debug = require('debug')('truss:gateway');
|
const debug = require('debug')('truss:gateway');
|
||||||
|
|
||||||
import {createDispatcher, sendActionToService} from '@truss/truss';
|
const {createDispatcher, sendActionToService} = require('@truss/comm');
|
||||||
|
|
||||||
let listener = require('./listener').default;
|
let listener = require('./listener').default;
|
||||||
|
|
||||||
|
@ -21,24 +21,27 @@ const schemas$ = services.map((service) => {
|
||||||
|
|
||||||
// build service map
|
// build service map
|
||||||
const reduceServiceMap$ = Promise.all(schemas$).then((schemas) => {
|
const reduceServiceMap$ = Promise.all(schemas$).then((schemas) => {
|
||||||
return schemas.reduce((l, r, i) => {
|
return schemas.reduce((serviceMap, schema, i) => {
|
||||||
for (const type of (r.executors || [])) {
|
const service = services[i];
|
||||||
if (l.executors[type]) {
|
for (const type of (schema.executors || [])) {
|
||||||
throw new Error(`
|
if (serviceMap.executors[type]) {
|
||||||
Only one executor may be specified per action type! "${services[i]}" tried to
|
throw new RangeError(`
|
||||||
register an executor but "${l.executors[type]}" already registered one for
|
Only one executor may be specified per action type! "${service}"
|
||||||
${type}.
|
tried to register an executor but "${serviceMap.executors[type]}"
|
||||||
`);
|
already registered one for "${type}".
|
||||||
|
`.replace(/\s+/g, ' ').trim());
|
||||||
}
|
}
|
||||||
l.executors[type] = services[i];
|
serviceMap.executors[type] = service;
|
||||||
}
|
}
|
||||||
for (const type of (r.listeners || [])) {
|
for (const type of (schema.listeners || [])) {
|
||||||
(l.listeners[type] = l.listeners[type] || []).push(services[i]);
|
serviceMap.listeners[type] = serviceMap.listeners[type] || [];
|
||||||
|
serviceMap.listeners[type].push(service);
|
||||||
}
|
}
|
||||||
for (const hook of (r.hooks || [])) {
|
for (const hook of (schema.hooks || [])) {
|
||||||
(l.hooks[hook] = l.hooks[hook] || []).push(services[i]);
|
serviceMap.hooks[hook] = serviceMap.hooks[hook] || [];
|
||||||
|
serviceMap.hooks[hook].push(service);
|
||||||
}
|
}
|
||||||
return l;
|
return serviceMap;
|
||||||
}, {
|
}, {
|
||||||
executors: {},
|
executors: {},
|
||||||
hooks: {},
|
hooks: {},
|
||||||
|
@ -51,53 +54,27 @@ const httpServer = http.createServer();
|
||||||
reduceServiceMap$.then((serviceMap) => {
|
reduceServiceMap$.then((serviceMap) => {
|
||||||
debug(`service map: ${JSON.stringify(serviceMap, null, ' ')}`);
|
debug(`service map: ${JSON.stringify(serviceMap, null, ' ')}`);
|
||||||
|
|
||||||
const port = process.env.GATEWAY_PORT || 8000;
|
const dispatcher = createDispatcher();
|
||||||
httpServer.listen(port);
|
dispatcher.setArgs(serviceMap);
|
||||||
httpServer.on('listening', () => {
|
dispatcher.lookupActions(require('./actions'));
|
||||||
debug(`HTTP listening on port ${port}...`);
|
if (module.hot) {
|
||||||
})
|
module.hot.accept('./actions', () => {
|
||||||
httpServer.on('request', (req, res) => {
|
dispatcher.lookupActions(require('./actions'));
|
||||||
listener(serviceMap, req, res);
|
});
|
||||||
|
}
|
||||||
|
const server = dispatcher.connect();
|
||||||
|
server.on('listening', () => {
|
||||||
|
const port = process.env.GATEWAY_PORT || 8000;
|
||||||
|
httpServer.listen(port);
|
||||||
|
httpServer.on('listening', () => {
|
||||||
|
debug(`HTTP listening on port ${port}...`);
|
||||||
|
})
|
||||||
|
httpServer.on('request', (req, res) => {
|
||||||
|
listener(serviceMap, req, res);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// hooks
|
|
||||||
createDispatcher({
|
|
||||||
'truss/hook-services': ({payload: {hook, args}}) => {
|
|
||||||
if (!(hook in serviceMap.hooks)) { return []; }
|
|
||||||
return serviceMap.hooks[hook];
|
|
||||||
},
|
|
||||||
'truss/invoke': ({payload: {hook, args}}) => {
|
|
||||||
if (!(hook in serviceMap.hooks)) { return {}; }
|
|
||||||
return invokeHook(serviceMap.hooks[hook], hook, args);
|
|
||||||
},
|
|
||||||
'truss/invoke-flat': ({payload: {hook, args}}) => {
|
|
||||||
if (!(hook in serviceMap.hooks)) { return []; }
|
|
||||||
return invokeHookFlat(serviceMap.hooks[hook], hook, args);
|
|
||||||
},
|
|
||||||
}).connect();
|
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
|
|
||||||
function invokeHookFlat(services, hook, args) {
|
|
||||||
debug(`invoking hook flat(${hook}(${JSON.stringify(args, null, ' ')}))...`);
|
|
||||||
|
|
||||||
invokeHookFlatInternal(services, hook, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
function invokeHookFlatInternal(services, hook, args) {
|
|
||||||
const action = {type: 'truss/hook', payload: {hook, args}};
|
|
||||||
return Promise.all(services.map((service) => {
|
|
||||||
return sendActionToService(action, service);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function invokeHook(services, hook, args) {
|
|
||||||
debug(`invoking hook ${hook}(${JSON.stringify(args, null, ' ')})...`);
|
|
||||||
|
|
||||||
return invokeHookFlatInternal(services, hook, args).then((result) => {
|
|
||||||
return result.reduce((l, r, i) => (l[services[i]] = r, l), {});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (module.hot) {
|
if (module.hot) {
|
||||||
module.hot.accept('./listener', () => {
|
module.hot.accept('./listener', () => {
|
||||||
listener = require('./listener').default;
|
listener = require('./listener').default;
|
||||||
|
|
|
@ -1,40 +1,37 @@
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
|
|
||||||
const debug = require('debug')('truss:gateway:listener');
|
import {sendActionToService} from '@truss/comm';
|
||||||
|
|
||||||
import {sendActionToService} from '@truss/truss';
|
const debug = require('debug')('truss:gateway:listener');
|
||||||
|
|
||||||
const parser = bodyParser.json();
|
const parser = bodyParser.json();
|
||||||
|
|
||||||
export default function(serviceMap, req, res) { parser(req, res, () => {
|
export default function(serviceMap, req, res) { parser(req, res, () => {
|
||||||
debug(`HTTP ${req.method} ${req.url}`);
|
debug(`HTTP ${req.method} ${req.url}`);
|
||||||
|
|
||||||
// map to action
|
// map to action
|
||||||
const action = {type: undefined, payload: {url: req.url}};
|
const action = {type: undefined, payload: {url: req.url}};
|
||||||
switch (req.method) {
|
switch (req.method) {
|
||||||
|
|
||||||
case 'POST':
|
case 'POST':
|
||||||
|
// truss action
|
||||||
action.type = req.url.startsWith('/truss/action/') ?
|
if (req.url.startsWith('/truss/action/')) {
|
||||||
req.url.substr('/truss/action/'.length): 'truss/http-post'
|
action.type = req.url.substr('/truss/action/'.length);
|
||||||
;
|
}
|
||||||
|
// truss/http-post
|
||||||
|
else {
|
||||||
|
action.type = 'truss/http-post';
|
||||||
|
}
|
||||||
action.payload.body = req.body;
|
action.payload.body = req.body;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
// truss/http-(get|delete|...)
|
||||||
// truss/http-(GET|DELETE|...)
|
|
||||||
action.type = `truss/http-${req.method.toLowerCase()}`;
|
action.type = `truss/http-${req.method.toLowerCase()}`;
|
||||||
action.payload.headers = req.headers;
|
action.payload.headers = req.headers;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// listeners...
|
// listeners...
|
||||||
for (service of serviceMap.listeners[action.type] || []) {
|
for (service of serviceMap.listeners[action.type] || []) {
|
||||||
sendActionToService(action, service).done();
|
sendActionToService(action, service).done();
|
||||||
}
|
}
|
||||||
|
|
||||||
// executor
|
// executor
|
||||||
if (!(action.type in serviceMap.executors)) {
|
if (!(action.type in serviceMap.executors)) {
|
||||||
debug(`No executor for ${action.type}!`);
|
debug(`No executor for ${action.type}!`);
|
||||||
|
@ -46,12 +43,9 @@ export default function(serviceMap, req, res) { parser(req, res, () => {
|
||||||
}
|
}
|
||||||
sendActionToService(
|
sendActionToService(
|
||||||
action, serviceMap.executors[action.type]
|
action, serviceMap.executors[action.type]
|
||||||
|
|
||||||
).then(({status, headers, html}) => {
|
).then(({status, headers, html}) => {
|
||||||
|
|
||||||
// deliver
|
// deliver
|
||||||
res.writeHead(status || 200, headers);
|
res.writeHead(status || 200, headers);
|
||||||
res.end(html);
|
res.end(html);
|
||||||
|
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
})};
|
})};
|
||||||
|
|
|
@ -4,14 +4,15 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node -e '' -r '@truss/truss/task/build'",
|
"build": "node -e '' -r '@truss/webpack/task/build'",
|
||||||
"default": "yarn run dev",
|
"default": "yarn run dev",
|
||||||
"dev": "node -e '' -r '@truss/truss/task/scaffold'"
|
"dev": "node -e '' -r '@truss/webpack/task/scaffold'"
|
||||||
},
|
},
|
||||||
"author": "cha0s",
|
"author": "cha0s",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@truss/truss": "1.x",
|
"@truss/comm": "1.x",
|
||||||
|
"@truss/webpack": "1.x",
|
||||||
"body-parser": "1.18.3",
|
"body-parser": "1.18.3",
|
||||||
"debug": "3.1.0"
|
"debug": "3.1.0"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user