ironbar/bundle.js
2018-08-31 04:36:26 -05:00

162 lines
3.7 KiB
JavaScript

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;
}
}
}