server will wait for token

This commit is contained in:
AJ ONeal 2018-06-28 20:35:58 -06:00
parent 2456228ae5
commit 02a89b52a4
4 changed files with 284 additions and 234 deletions

View File

@ -325,7 +325,7 @@ function askForConfig(answers, mainCb) {
var q = nextSet.shift(); var q = nextSet.shift();
if (!q) { if (!q) {
// https://github.com/nodejs/node/issues/21319 // https://github.com/nodejs/node/issues/21319
if (useTty) { stdin.push(null); } if (useTty) { try { stdin.push(null); } catch(e) { /*ignore*/ } }
rl.close(); rl.close();
if (useTty) { try { stdin.close(); } catch(e) { /*ignore*/ } } if (useTty) { try { stdin.close(); } catch(e) { /*ignore*/ } }
mainCb(null, answers); mainCb(null, answers);
@ -531,11 +531,34 @@ function parseConfig(err, text) {
if (-1 !== argv.indexOf('init')) { if (-1 !== argv.indexOf('init')) {
parsers.init(argv, function (err, answers) { parsers.init(argv, function (err, answers) {
if (err) { if (err) {
console.error("Error while initializing config:"); console.error("Error while initializing config [init]:");
throw err; throw err;
} }
// TODO make one request to set and then poll for readiness common.api.token(state, {
error: function (err/*, next*/) {
console.error("[Error] common.api.token:");
console.error(err);
return;
}
, directory: function (dir, next) {
//console.log('Telebit Relay Discovered:');
state._apiDirectory = dir;
//console.log(dir);
//console.log();
next();
}
, tunnelUrl: function (tunnelUrl, next) {
//console.log('Telebit Relay Tunnel Socket:', tunnelUrl);
state.wss = tunnelUrl;
next();
}
, requested: function (authReq, next) {
//console.log("Pairing Requested");
var pin = authReq.pin || authReq.otp || authReq.pairCode;
state.otp = state._otp = pin;
state.auth = state.authRequest = state._auth = authReq;
if (!answers.token && answers._can_pair) { if (!answers.token && answers._can_pair) {
console.info(""); console.info("");
console.info("=============================================="); console.info("==============================================");
@ -550,17 +573,54 @@ function parseConfig(err, text) {
console.info(""); console.info("");
} }
next();
}
, connect: function (pretoken, next) {
//console.log("Enabling Pairing Locally...");
answers.token = pretoken;
answers._connecting = true;
// TODO use php-style object querification // TODO use php-style object querification
utils.putConfig('config', Object.keys(answers).map(function (key) { utils.putConfig('config', Object.keys(answers).map(function (key) {
return key + ':' + answers[key]; return key + ':' + answers[key];
}), function (err/*, body*/) { }), function (err/*, body*/) {
if (err) { if (err) {
console.error("Error while initializing config:"); answers._error = err;
throw err; console.error("Error while initializing config [connect]:");
console.error(err);
return;
} }
//console.log("Pairing Enabled Locally");
// need just a little time to let the grants occur next();
setTimeout(function () { });
}
, offer: function (token, next) {
//console.log("Pairing Enabled by Relay");
answers.token = token;
if (answers._error) {
return;
}
if (answers._connecting) {
return;
}
answers._connecting = true;
utils.putConfig('config', Object.keys(answers).map(function (key) {
return key + ':' + answers[key];
}), function (err/*, body*/) {
if (err) {
answers._error = err;
console.error("Error while initializing config [offer]:");
console.error(err);
return;
}
//console.log("Pairing Enabled Locally");
next();
});
}
, granted: function (_, next) {
//console.log("Token has been granted!");
next();
}
, end: function () {
utils.putConfig('enable', [], function () { utils.putConfig('enable', [], function () {
utils.putConfig('list', [], function (err) { utils.putConfig('list', [], function (err) {
if (err) { console.error(err); return; } if (err) { console.error(err); return; }
@ -577,8 +637,7 @@ function parseConfig(err, text) {
// end workaround // end workaround
}); });
}); });
}
}, 1 * 1000);
}); });
}); });
return; return;

View File

@ -29,7 +29,7 @@ if (-1 !== confIndex) {
confpath = confargs[1]; confpath = confargs[1];
} }
require('../lib/updater')(pkg); var cancelUpdater = require('../lib/updater')(pkg);
function help() { function help() {
console.info(''); console.info('');
@ -54,7 +54,7 @@ if (-1 === confIndex) {
// * {install}/etc/telebitd.yml // * {install}/etc/telebitd.yml
// * ~/.config/telebit/telebitd.yml // * ~/.config/telebit/telebitd.yml
// We'll asume the later since the installers include --config in the system launcher script // We'll asume the later since the installers include --config in the system launcher script
confpath = path.join(state.homedir, '.config/telebit/telebitd.yml'); confpath = common.DEFAULT_CONFIG_PATH;
verstr.push('(--config "' + confpath + '")'); verstr.push('(--config "' + confpath + '")');
} }
@ -69,12 +69,11 @@ if (!confpath || /^--/.test(confpath)) {
var tokenpath = path.join(path.dirname(confpath), 'access_token.txt'); var tokenpath = path.join(path.dirname(confpath), 'access_token.txt');
var token; var token;
try { try {
token = require('fs').readFileSync(tokenpath, 'ascii').trim(); token = fs.readFileSync(tokenpath, 'ascii').trim();
} catch(e) { } catch(e) {
// ignore // ignore
} }
var controlServer; var controlServer;
var tun; var tun;
function serveControlsHelper() { function serveControlsHelper() {
@ -103,12 +102,7 @@ function serveControlsHelper() {
if (state._can_pair && state.config.email && !state.token) { if (state._can_pair && state.config.email && !state.token) {
dumpy.code = "AWAIT_AUTH"; dumpy.code = "AWAIT_AUTH";
dumpy.message = [ dumpy.message = "Please run 'telebit init' to authenticate.";
"Check your email."
, "You must verify your email address to activate this device."
, ""
, " Device Pairing Code: " + state.otp
].join('\n');
} }
res.end(JSON.stringify(dumpy)); res.end(JSON.stringify(dumpy));
@ -134,9 +128,23 @@ function serveControlsHelper() {
// //
// without proper config // without proper config
// //
function saveAndReport(err, _tun) {
if (err) { throw err; }
tun = _tun;
fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) {
if (err) {
res.statusCode = 500;
res.setHeader('Content-Type', 'application/json');
res.end('{"error":{"message":"Could not save config file after init: ' + err.message.replace(/"/g, "'")
+ '.\nPerhaps check that the file exists and your user has permissions to write it?"}}');
return;
}
listSuccess();
});
}
if (/\b(init|config)\b/.test(opts.pathname)) { if (/\b(init|config)\b/.test(opts.pathname)) {
var conf = {}; var conf = {};
var fresh;
if (!opts.body) { if (!opts.body) {
res.statusCode = 422; res.statusCode = 422;
res.end('{"error":{"message":"module \'init\' needs more arguments"}}'); res.end('{"error":{"message":"module \'init\' needs more arguments"}}');
@ -157,9 +165,6 @@ function serveControlsHelper() {
} }
conf[parts[0]] = parts[1]; conf[parts[0]] = parts[1];
}); });
if (!state.config.relay || !state.config.email || !state.config.agreeTos) {
fresh = true;
}
// TODO camelCase query // TODO camelCase query
state.config.email = conf.email || state.config.email || ''; state.config.email = conf.email || state.config.email || '';
@ -170,6 +175,10 @@ function serveControlsHelper() {
state.config.relay = conf.relay || state.config.relay || ''; state.config.relay = conf.relay || state.config.relay || '';
state.config.token = conf.token || state.config.token || null; state.config.token = conf.token || state.config.token || null;
state.config.secret = conf.secret || state.config.secret || null; state.config.secret = conf.secret || state.config.secret || null;
state.pretoken = conf.pretoken || state.config.pretoken || null;
if (state.secret) {
state.token = common.signToken(state);
}
if ('undefined' !== typeof conf.newsletter) { if ('undefined' !== typeof conf.newsletter) {
state.config.newsletter = conf.newsletter; state.config.newsletter = conf.newsletter;
} }
@ -197,6 +206,7 @@ function serveControlsHelper() {
if (!state.config.relay || !state.config.email || !state.config.agreeTos) { if (!state.config.relay || !state.config.email || !state.config.agreeTos) {
res.statusCode = 400; res.statusCode = 400;
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ res.end(JSON.stringify({
error: { error: {
code: "E_INIT" code: "E_INIT"
@ -222,28 +232,13 @@ function serveControlsHelper() {
} else { } else {
rawTunnel(saveAndReport); rawTunnel(saveAndReport);
} }
function saveAndReport(err, _tun) {
if (err) { throw err; }
tun = _tun;
fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) {
if (err) {
res.statusCode = 500;
res.end('{"error":{"message":"Could not save config file after init: ' + err.message.replace(/"/g, "'")
+ '.\nPerhaps check that the file exists and your user has permissions to write it?"}}');
return;
}
listSuccess();
});
}
return; return;
} }
if (/restart/.test(opts.pathname)) { if (/restart/.test(opts.pathname)) {
tun.end(); tun.end();
res.end('{"success":true}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ success: true }));
controlServer.close(function () { controlServer.close(function () {
// TODO closeAll other things // TODO closeAll other things
process.nextTick(function () { process.nextTick(function () {
@ -259,7 +254,10 @@ function serveControlsHelper() {
// //
if (!state.config.relay || !state.config.email || !state.config.agreeTos) { if (!state.config.relay || !state.config.email || !state.config.agreeTos) {
res.statusCode = 400; res.statusCode = 400;
res.end('{"error":{"code":"E_CONFIG","message":"Invalid config file. Please run \'telebit init\'"}}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({
error: { code: "E_CONFIG", message: "Invalid config file. Please run 'telebit init'" }
}));
return; return;
} }
@ -269,13 +267,15 @@ function serveControlsHelper() {
if (/http/.test(opts.pathname)) { if (/http/.test(opts.pathname)) {
if (!opts.body) { if (!opts.body) {
res.statusCode = 422; res.statusCode = 422;
res.end('{"error":{"message":"module \'http\' needs more arguments"}}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({"error":{"message":"module \'http\' needs more arguments"}}));
return; return;
} }
if (opts.body[1]) { if (opts.body[1]) {
if (!state.servernames[opts.body[1]]) { if (!state.servernames[opts.body[1]]) {
res.statusCode = 400; res.statusCode = 400;
res.end('{"error":{"message":"bad servername \'' + opts.body[1] + '\'"'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: { "message":"bad servername '" + opts.body[1] + "'" } }));
return; return;
} }
state.servernames[opts.body[1]].handler = opts.body[0]; state.servernames[opts.body[1]].handler = opts.body[0];
@ -284,14 +284,16 @@ function serveControlsHelper() {
state.servernames[key].handler = opts.body[0]; state.servernames[key].handler = opts.body[0];
}); });
} }
res.end('{"success":true}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ success: true }));
return; return;
} }
if (/tcp/.test(opts.pathname)) { if (/tcp/.test(opts.pathname)) {
if (!opts.body) { if (!opts.body) {
res.statusCode = 422; res.statusCode = 422;
res.end('{"error":{"message":"module \'tcp\' needs more arguments"}}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: { message: "module 'tcp' needs more arguments" } }));
return; return;
} }
@ -299,7 +301,8 @@ function serveControlsHelper() {
if (opts.body[1]) { if (opts.body[1]) {
if (!state.ports[opts.body[1]]) { if (!state.ports[opts.body[1]]) {
res.statusCode = 400; res.statusCode = 400;
res.end('{"error":{"message":"bad port \'' + opts.body[1] + '\'"'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: { "message":"bad port '" + opts.body[1] + "'" } }));
return; return;
} }
// forward-to port-or-module // forward-to port-or-module
@ -309,7 +312,8 @@ function serveControlsHelper() {
state.ports[key].handler = opts.body[0]; state.ports[key].handler = opts.body[0];
}); });
} }
res.end('{"success":true}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ success: true }));
return; return;
} }
@ -319,7 +323,10 @@ function serveControlsHelper() {
fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) {
if (err) { if (err) {
res.statusCode = 500; res.statusCode = 500;
res.end('{"error":{"message":"Could not save config file. Perhaps you\'re not running as root?"}}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({
"error":{"message":"Could not save config file. Perhaps you're not running as root?"}
}));
return; return;
} }
listSuccess(); listSuccess();
@ -331,7 +338,8 @@ function serveControlsHelper() {
var sshAuto; var sshAuto;
if (!opts.body) { if (!opts.body) {
res.statusCode = 422; res.statusCode = 422;
res.end('{"error":{"message":"module \'ssh\' needs more arguments"}}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({"error":{"message":"module 'ssh' needs more arguments"}}));
return; return;
} }
@ -349,7 +357,8 @@ function serveControlsHelper() {
sshAuto = parseInt(sshAuto, 10); sshAuto = parseInt(sshAuto, 10);
if (!sshAuto || sshAuto <= 0 || sshAuto > 65535) { if (!sshAuto || sshAuto <= 0 || sshAuto > 65535) {
res.statusCode = 400; res.statusCode = 400;
res.end('{"error":{"message":"bad ssh_auto option \'' + opts.body[0] + '\'"'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({ error: { message: "bad ssh_auto option '" + opts.body[0] + "'" } }));
return; return;
} }
state.config.sshAuto = sshAuto; state.config.sshAuto = sshAuto;
@ -369,7 +378,10 @@ function serveControlsHelper() {
fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) {
if (err) { if (err) {
res.statusCode = 500; res.statusCode = 500;
res.end('{"error":{"message":"Could not save config file. Perhaps you\'re user doesn\'t have permission?"}}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({
error: { message: "Could not save config file. Perhaps you're user doesn't have permission?" }
}));
return; return;
} }
listSuccess(); listSuccess();
@ -382,9 +394,12 @@ function serveControlsHelper() {
state.config.disable = true; state.config.disable = true;
if (tun) { tun.end(); tun = null; } if (tun) { tun.end(); tun = null; }
fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) {
res.setHeader('Content-Type', 'application/json');
if (err) { if (err) {
res.statusCode = 500; res.statusCode = 500;
res.end('{"error":{"message":"Could not save config file. Perhaps you\'re not running as root?"}}'); res.end(JSON.stringify({
"error":{"message":"Could not save config file. Perhaps you're not running as root?"}
}));
return; return;
} }
res.end('{"success":true}'); res.end('{"success":true}');
@ -393,6 +408,7 @@ function serveControlsHelper() {
} }
if (/status/.test(opts.pathname)) { if (/status/.test(opts.pathname)) {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify( res.end(JSON.stringify(
{ status: (state.config.disable ? 'disabled' : 'enabled') { status: (state.config.disable ? 'disabled' : 'enabled')
, ready: ((state.config.relay && (state.config.token || state.config.agreeTos)) ? true : false) , ready: ((state.config.relay && (state.config.token || state.config.agreeTos)) ? true : false)
@ -406,7 +422,8 @@ function serveControlsHelper() {
return; return;
} }
res.end('{"error":{"message":"unrecognized rpc"}}'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify({"error":{"message":"unrecognized rpc"}}));
}); });
if (fs.existsSync(state._ipc.path)) { if (fs.existsSync(state._ipc.path)) {
fs.unlinkSync(state._ipc.path); fs.unlinkSync(state._ipc.path);
@ -422,24 +439,32 @@ function serveControlsHelper() {
, exclusive: false , exclusive: false
}, function () { }, function () {
process.umask(oldUmask); process.umask(oldUmask);
//console.log(this.address());
console.info("[info] Listening for commands on " + state._ipc.path);
}); });
} }
function serveControls() { function serveControls() {
if (!state.config.disable) { if (state.config.disable) {
if (state.config.relay && (state.config.token || state.config.agreeTos)) { console.info("[info] starting disabled");
setTimeout(function () {
// TODO move back to callback once mutual-dependency on clientside init is resolved
// (requires moving token fetching to clientside)
serveControlsHelper();
}, 350);
rawTunnel(function (err, _tun) {
if (err) { throw err; }
tun = _tun;
});
return; return;
} }
if (state.config.relay && (state.config.token || state.config.pretoken)) {
console.info("[info] connecting with stored token");
rawTunnel(function (err, _tun) {
if (err) { throw err; }
if (_tun) { tun = _tun; }
setTimeout(function () {
// TODO attach handler to tunnel
serveControlsHelper();
}, 150);
});
return;
} else {
console.info("[info] waiting for init/authentication (missing relay and/or token)");
} }
serveControlsHelper(); serveControlsHelper();
} }
@ -451,7 +476,7 @@ function parseConfig(err, text) {
} }
common._init( common._init(
state.config.root || path.join(__dirname, '..') state.config.root || path.join(__dirname, '..')
, (state.config.root && path.join(state.config.root, 'etc')) || path.join(os.homedir(), '.config/telebit') , (state.config.root && path.join(state.config.root, 'etc')) || path.resolve(common.DEFAULT_CONFIG_PATH, '..')
); );
state._ipc = common.pipename(state.config, true); state._ipc = common.pipename(state.config, true);
console.info(''); console.info('');
@ -461,6 +486,7 @@ function parseConfig(err, text) {
} }
console.info(''); console.info('');
state.token = state.token || state.config.token || token; state.token = state.token || state.config.token || token;
state.pretoken = state.pretoken || state.config.pretoken;
state._confpath = confpath; state._confpath = confpath;
if (!state.config.servernames) { if (!state.config.servernames) {
@ -513,82 +539,25 @@ function rawTunnel(rawCb) {
return; return;
} }
// TODO move to client bin/telebit.js if (!(state.token || state.pretoken)) {
common.api.token(state, { rawCb(null, null);
error: function (err/*, next*/) {
console.error("[Error] common.api.token:");
console.error(err);
rawCb(err);
}
, directory: function (dir, next) {
console.log('Telebit Relay Discovered:');
state._apiDirectory = dir;
console.log(dir);
console.log();
next();
}
, tunnelUrl: function (tunnelUrl, next) {
console.log('Telebit Relay Tunnel Socket:', tunnelUrl);
state.wss = tunnelUrl;
next();
}
, requested: function (authReq, next) {
console.log("Pairing Requested");
var pin = authReq.pin || authReq.otp || authReq.pairCode;
state.otp = state._otp = pin;
state.auth = state.authRequest = state._auth = authReq;
console.info();
console.info('====================================');
console.info('= HEY! LISTEN! =');
console.info('====================================');
console.info('= =');
console.info('= 1. CHECK YOUR EMAIL =');
console.info('= =');
console.info('= 2. DEVICE PAIRING CODE: 0000 ='.replace('0000', pin));
console.info('= =');
console.info('====================================');
console.info();
next();
}
, connect: function (pretoken, next) {
console.log("Enabling Pairing Locally...");
connectTunnel(pretoken, function (err, _tun) {
console.log("Pairing Enabled Locally");
tun = _tun;
next();
});
}
, offer: function (token, next) {
console.log("Pairing Enabled by Relay");
state.token = token;
state.config.token = token;
state.handlers.access_token({ jwt: token });
if (tun) {
tun.append(token);
} else {
connectTunnel(token, function (err, _tun) {
tun = _tun;
});
}
next();
}
, granted: function (token, next) {
console.log("Relay-Remote Pairing Complete");
next();
}
, end: function () {
rawCb(null, tun);
}
});
}
function connectTunnel(token, cb) {
if (tun) {
cb(null, tun);
return; return;
} }
if (tun) {
rawCb(null, tun);
return;
}
common.api.wss(state, function (err, wss) {
if (err) { rawCb(err); return; }
state.wss = wss;
// Saves the token
// state.handlers.access_token({ jwt: token });
// Adds the token to the connection
// tun.append(token);
state.greenlockConf = state.config.greenlock || {}; state.greenlockConf = state.config.greenlock || {};
state.sortingHat = state.config.sortingHat; state.sortingHat = state.config.sortingHat;
@ -636,14 +605,15 @@ function connectTunnel(token, cb) {
, sortingHat: state.sortingHat , sortingHat: state.sortingHat
, net: state.net , net: state.net
, insecure: state.insecure , insecure: state.insecure
, token: token // instance , token: state.token || state.pretoken // instance
, servernames: state.servernames , servernames: state.servernames
, ports: state.ports , ports: state.ports
, handlers: state.handlers , handlers: state.handlers
, greenlockConfig: state.greenlockConfig , greenlockConfig: state.greenlockConfig
}); });
cb(null, tun); rawCb(null, tun);
});
} }
state.handlers = { state.handlers = {
@ -709,6 +679,7 @@ function sigHandler() {
if (controlServer) { if (controlServer) {
controlServer.close(); controlServer.close();
} }
cancelUpdater();
} }
// reverse 2FA otp // reverse 2FA otp

View File

@ -9,12 +9,9 @@ var os = require('os');
var homedir = os.homedir(); var homedir = os.homedir();
var urequest = require('@coolaj86/urequest'); var urequest = require('@coolaj86/urequest');
var localshare = '.local/share/telebit';
var localconf = '.config/telebit';
common.pipename = function (config, newApi) { common.pipename = function (config, newApi) {
var _ipc = { var _ipc = {
path: (config.sock || common.DEFAULT_SOCK_NAME) path: (config.sock || common.DEFAULT_SOCK_PATH)
, comment: (/^win/i.test(os.platform()) ? 'windows pipe' : 'unix socket') , comment: (/^win/i.test(os.platform()) ? 'windows pipe' : 'unix socket')
, type: (/^win/i.test(os.platform()) ? 'pipe' : 'socket') , type: (/^win/i.test(os.platform()) ? 'pipe' : 'socket')
}; };
@ -26,7 +23,8 @@ common.pipename = function (config, newApi) {
} }
return _ipc.path; return _ipc.path;
}; };
common.DEFAULT_SOCK_NAME = path.join(homedir, localshare, 'var', 'run', 'telebit.sock'); common.DEFAULT_SOCK_PATH = path.join(homedir, '.local/share/telebit/var/run', 'telebit.sock');
common.DEFAULT_CONFIG_PATH = path.join(homedir, '.config/telebit', 'telebitd.yml');
common.parseUrl = function (hostname) { common.parseUrl = function (hostname) {
var location = url.parse(hostname); var location = url.parse(hostname);
@ -54,24 +52,7 @@ common.apiDirectory = '_apis/telebit.cloud/index.json';
common.otp = function getOtp() { common.otp = function getOtp() {
return Math.round(Math.random() * 9999).toString().padStart(4, '0'); return Math.round(Math.random() * 9999).toString().padStart(4, '0');
}; };
common.api = {}; common.signToken = function (state) {
common.api.directory = function (state, next) {
state.relayUrl = common.parseUrl(state.relay);
urequest({ url: state.relayUrl + common.apiDirectory, json: true }, function (err, resp, body) {
next(err, body);
});
};
common.api.token = function (state, handlers) {
common.api.directory(state, function (err, dir) {
// directory, requested, connect, tunnelUrl, offer, granted, end
function afterDir() {
//console.log('[debug] after dir');
state.relayHostname = common.parseHostname(state.relay);
state.wss = dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state.relayHostname) + dir.tunnel.pathname;
handlers.tunnelUrl(state.wss, function () {
//console.log('[debug] after tunnelUrl');
if (!state.config.token && state.config.secret) {
var jwt = require('jsonwebtoken'); var jwt = require('jsonwebtoken');
var tokenData = { var tokenData = {
domains: Object.keys(state.config.servernames || {}).filter(function (name) { domains: Object.keys(state.config.servernames || {}).filter(function (name) {
@ -85,7 +66,41 @@ common.api.token = function (state, handlers) {
, iss: Math.round(Date.now() / 1000) , iss: Math.round(Date.now() / 1000)
}; };
state.token = jwt.sign(tokenData, state.config.secret); return jwt.sign(tokenData, state.config.secret);
};
common.api = {};
common.api.directory = function (state, next) {
state.relayUrl = common.parseUrl(state.relay);
urequest({ url: state.relayUrl + common.apiDirectory, json: true }, function (err, resp, dir) {
if (!dir) { dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } }; }
state._apiDirectory = dir;
next(err, dir);
});
};
common.api._parseWss = function (state, dir) {
if (!dir || !dir.api_host) {
dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } };
}
state.relayHostname = common.parseHostname(state.relay);
state.wss = dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state.relayHostname) + dir.tunnel.pathname;
return state.wss;
};
common.api.wss = function (state, cb) {
common.api.directory(state, function (err, dir) {
cb(err, common.api._parseWss(state, dir));
});
};
common.api.token = function (state, handlers) {
common.api.directory(state, function (err, dir) {
// directory, requested, connect, tunnelUrl, offer, granted, end
function afterDir() {
//console.log('[debug] after dir');
state.wss = common.api._parseWss(state, dir);
handlers.tunnelUrl(state.wss, function () {
//console.log('[debug] after tunnelUrl');
if (/*!state.config.token &&*/ state.config.secret) {
state.token = common.signToken(state);
} }
state.token = state.token || state.config.token; state.token = state.token || state.config.token;
if (state.token) { if (state.token) {

View File

@ -128,8 +128,13 @@ module.exports = function (pkg) {
}); });
}); });
} }
setInterval(checkUpgrade, 2 * 60 * 60 * 1000);
var _interval = setInterval(checkUpgrade, 2 * 60 * 60 * 1000);
process.nextTick(function () { process.nextTick(function () {
checkUpgrade(); checkUpgrade();
}); });
return function cancel() {
clearInterval(_interval);
};
}; };