forked from coolaj86/goldilocks.js
225 lines
5.8 KiB
JavaScript
Executable File
225 lines
5.8 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
var cluster = require('cluster');
|
|
|
|
if (!cluster.isMaster) {
|
|
require('../lib/worker.js');
|
|
return;
|
|
}
|
|
|
|
function run(config) {
|
|
// TODO spin up multiple workers
|
|
// TODO use greenlock-cluster
|
|
function work() {
|
|
var worker = cluster.fork();
|
|
worker.on('exit', work).on('online', function () {
|
|
console.log('[worker]', worker.id, 'online');
|
|
// Worker is listening
|
|
worker.send(config);
|
|
});
|
|
}
|
|
console.log('config.tcp.ports', config.tcp.ports);
|
|
work();
|
|
}
|
|
|
|
function readConfigAndRun(args) {
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
var cwd = args.cwd;
|
|
var text;
|
|
var filename;
|
|
var config;
|
|
|
|
if (args.config) {
|
|
text = fs.readFileSync(path.join(cwd, args.config), 'utf8');
|
|
}
|
|
else {
|
|
filename = path.join(cwd, 'goldilocks.yml');
|
|
|
|
if (fs.existsSync(filename)) {
|
|
text = fs.readFileSync(filename, 'utf8');
|
|
}
|
|
else {
|
|
filename = path.join(cwd, 'goldilocks.json');
|
|
if (fs.existsSync(filename)) {
|
|
text = fs.readFileSync(filename, 'utf8');
|
|
} else {
|
|
text = '{}';
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
config = JSON.parse(text);
|
|
} catch(e) {
|
|
try {
|
|
config = require('js-yaml').safeLoad(text);
|
|
} catch(e) {
|
|
throw new Error(
|
|
"Could not load '" + filename + "' as JSON nor YAML"
|
|
);
|
|
}
|
|
}
|
|
if (!config.dns) {
|
|
config.dns = { proxy: { port: 3053 } };
|
|
}
|
|
if (!config.tcp) {
|
|
config.tcp = {};
|
|
}
|
|
if (!config.http) {
|
|
config.http = { proxy: { port: 3000 } };
|
|
}
|
|
if (!config.tls) {
|
|
config.tls = {
|
|
agreeTos: args.agreeTos || args.agree || args['agree-tos']
|
|
, servernames: (args.servernames||'').split(',').filter(Boolean).map(function (str) { return str.toLowerCase(); })
|
|
};
|
|
}
|
|
if (args.email) {
|
|
config.email = args.email;
|
|
config.tls.email = args.email;
|
|
}
|
|
|
|
// maybe this should not go in config... but be ephemeral in some way?
|
|
if (args.cwd) {
|
|
config.cwd = args.cwd;
|
|
}
|
|
if (!config.cwd) {
|
|
config.cwd = process.cwd();
|
|
}
|
|
|
|
var ipaddr = require('ipaddr.js');
|
|
var addresses = [];
|
|
var ifaces = require('../lib/local-ip.js').find();
|
|
|
|
Object.keys(ifaces).forEach(function (ifacename) {
|
|
var iface = ifaces[ifacename];
|
|
iface.ipv4.forEach(function (ip) {
|
|
addresses.push(ip);
|
|
});
|
|
iface.ipv6.forEach(function (ip) {
|
|
addresses.push(ip);
|
|
});
|
|
});
|
|
|
|
addresses.sort(function (a, b) {
|
|
if (a.family !== b.family) {
|
|
return 'IPv4' === a.family ? 1 : -1;
|
|
}
|
|
|
|
return a.address > b.address ? 1 : -1;
|
|
});
|
|
|
|
addresses.forEach(function (addr) {
|
|
addr.range = ipaddr.parse(addr.address).range();
|
|
});
|
|
|
|
// TODO maybe move to config.state.addresses (?)
|
|
config.addresses = addresses;
|
|
config.device = { hostname: 'daplien-pod' };
|
|
|
|
var PromiseA = require('bluebird');
|
|
var tcpProm, dnsProm;
|
|
|
|
if (config.tcp.ports) {
|
|
tcpProm = PromiseA.resolve();
|
|
} else {
|
|
tcpProm = new PromiseA(function (resolve, reject) {
|
|
require('../lib/check-ports').checkTcpPorts(function (failed, bound) {
|
|
config.tcp.ports = Object.keys(bound);
|
|
if (config.tcp.ports.length) {
|
|
resolve();
|
|
} else {
|
|
reject(failed);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
if (config.dns.bind) {
|
|
dnsProm = PromiseA.resolve();
|
|
} else {
|
|
dnsProm = new PromiseA(function (resolve) {
|
|
require('../lib/check-ports').checkUdpPorts(function (failed, bound) {
|
|
var ports = Object.keys(bound);
|
|
|
|
if (ports.length === 0) {
|
|
// I don't think we want to prevent the rest of the app from running in
|
|
// this case like we do for TCP, do don't call reject.
|
|
console.warn('could not bind to the desired ports for DNS');
|
|
Object.keys(failed).forEach(function (key) {
|
|
console.log('[error bind]', key, failed[key].code);
|
|
});
|
|
}
|
|
else if (ports.length === 1) {
|
|
config.dns.bind = parseInt(ports[0], 10);
|
|
}
|
|
else {
|
|
config.dns.bind = ports.map(function (numStr) {
|
|
return parseInt(numStr, 10);
|
|
});
|
|
}
|
|
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
PromiseA.all([tcpProm, dnsProm])
|
|
.then(function () {
|
|
run(config);
|
|
})
|
|
.catch(function (failed) {
|
|
console.warn("could not bind to the desired ports");
|
|
Object.keys(failed).forEach(function (key) {
|
|
console.log('[error bind]', key, failed[key].code);
|
|
});
|
|
});
|
|
}
|
|
|
|
function readEnv(args) {
|
|
// TODO
|
|
var env = {
|
|
tunnel: process.env.GOLDILOCKS_TUNNEL_TOKEN || process.env.GOLDILOCKS_TUNNEL && true
|
|
, email: process.env.GOLDILOCKS_EMAIL
|
|
, cwd: process.env.GOLDILOCKS_HOME
|
|
, debug: process.env.GOLDILOCKS_DEBUG && true
|
|
};
|
|
args.cwd = args.cwd || env.cwd;
|
|
Object.keys(env).forEach(function (key) {
|
|
if ('undefined' === typeof args[key]) {
|
|
args[key] = env[key];
|
|
}
|
|
});
|
|
|
|
readConfigAndRun(args);
|
|
}
|
|
|
|
if (process.argv.length === 2) {
|
|
readEnv({ cwd: process.cwd() });
|
|
}
|
|
else if (process.argv.length === 4) {
|
|
if ('-c' === process.argv[3] || '--config' === process.argv[3]) {
|
|
readEnv({ config: process.argv[4] });
|
|
}
|
|
}
|
|
else if (process.argv.length > 2) {
|
|
var program = require('commander');
|
|
|
|
program
|
|
.version(require('../package.json').version)
|
|
.option('--agree-tos [url1,url2]', "agree to all Terms of Service for Daplie, Let's Encrypt, etc (or specific URLs only)")
|
|
.option('--config', 'Path to config file (Goldilocks.json or Goldilocks.yml) example: --config /etc/goldilocks/Goldilocks.json')
|
|
.option('--tunnel [token]', 'Turn tunnel on. This will enter interactive mode for login if no token is specified.')
|
|
.option('--email <email>', "(Re)set default email to use for Daplie, Let's Encrypt, ACME, etc.")
|
|
.option('--debug', "Enable debug output")
|
|
.parse(process.argv);
|
|
|
|
program.cwd = process.cwd();
|
|
readEnv(program);
|
|
}
|
|
else {
|
|
throw new Error("impossible number of arguments: " + process.argv.length);
|
|
}
|