wip adapt common init for web setup

This commit is contained in:
AJ ONeal 2018-10-18 01:52:30 -06:00
parent f2e60dae5e
commit f0222baff6
3 changed files with 256 additions and 0 deletions

View File

@ -80,6 +80,7 @@
</div> </div>
<script src="/js/vue.js"></script> <script src="/js/vue.js"></script>
<script src="/js/telebit.js"></script>
<script src="/js/app.js"></script> <script src="/js/app.js"></script>
</body> </body>
</html> </html>

View File

@ -4,6 +4,7 @@
console.log("hello"); console.log("hello");
var Vue = window.Vue; var Vue = window.Vue;
var Telebit = window.TELEBIT;
var api = {}; var api = {};
api.config = function apiConfig() { api.config = function apiConfig() {
@ -53,6 +54,13 @@ var appMethods = {
if (DEFAULT_RELAY !== appData.init.relay) { if (DEFAULT_RELAY !== appData.init.relay) {
window.alert("TODO: Custom Relay Not Implemented Yet"); window.alert("TODO: Custom Relay Not Implemented Yet");
} }
Telebit.api.directory({ relay: appData.init.relay }, function (err, dir) {
if (err) {
window.alert("Error:" + (err.message || JSON.stringify(err, null, 2)));
return;
}
window.alert("Success:" + JSON.stringify(dir, null, 2));
});
} }
, defaultRelay: function () { , defaultRelay: function () {
appData.init.relay = DEFAULT_RELAY; appData.init.relay = DEFAULT_RELAY;

247
lib/admin/js/telebit.js Normal file
View File

@ -0,0 +1,247 @@
;(function (exports) {
'use strict';
var common = exports.TELEBIT = {};
if ('undefined' !== typeof fetch) {
common.requestAsync = function (opts) {
if (opts.json && true !== opts.json) {
opts.body = opts.json;
}
if (opts.json) {
if (!opts.headers) { opts.headers = {}; }
if (opts.body) {
opts.headers['Content-Type'] = 'application/json';
} else {
opts.headers.Accepts = 'application/json';
}
}
return window.fetch(opts.url, opts).then(function (resp) {
return resp.json().then(function (json) {
var headers = {};
resp.headers.forEach(function (k, v) {
headers[k] = v;
});
return { statusCode: resp.status, headers: headers, body: json };
});
});
};
} else {
common.requestAsync = require('util').promisify(require('@coolaj86/urequest'));
}
common.parseUrl = function (hostname) {
// add scheme, if missing
if (!/:\/\//.test(hostname)) {
hostname = 'https://' + hostname;
}
var location = new URL(hostname);
hostname = location.hostname + (location.port ? ':' + location.port : '');
hostname = location.protocol.replace(/https?/, 'https') + '//' + hostname + location.pathname;
return hostname;
};
common.parseHostname = function (hostname) {
var location = new URL(hostname);
if (!location.protocol || /\./.test(location.protocol)) {
hostname = 'https://' + hostname;
location = new URL(hostname);
}
//hostname = location.hostname + (location.port ? ':' + location.port : '');
//hostname = location.protocol.replace(/https?/, 'https') + '//' + hostname + location.pathname;
return location.hostname;
};
common.apiDirectory = '_apis/telebit.cloud/index.json';
common.otp = function getOtp() {
return Math.round(Math.random() * 9999).toString().padStart(4, '0');
};
common.signToken = function (state) {
var jwt = require('jsonwebtoken');
var tokenData = {
domains: Object.keys(state.config.servernames || {}).filter(function (name) {
return /\./.test(name);
})
, ports: Object.keys(state.config.ports || {}).filter(function (port) {
port = parseInt(port, 10);
return port > 0 && port <= 65535;
})
, aud: state._relayUrl
, iss: Math.round(Date.now() / 1000)
};
return jwt.sign(tokenData, state.config.secret);
};
common.api = {};
common.api.directory = function (state, next) {
console.log('state:');
console.log(state);
state._relayUrl = common.parseUrl(state.relay);
common.requestAsync({ url: state._relayUrl + common.apiDirectory, json: true }).then(function (resp) {
var dir = resp.body;
if (!dir) { dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } }; }
state._apiDirectory = dir;
next(null, dir);
}).catch(function (err) {
next(err);
});
};
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);
return dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state._relayHostname) + dir.tunnel.pathname;
};
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() {
if (common.debug) { console.log('[debug] after dir'); }
state.wss = common.api._parseWss(state, dir);
handlers.tunnelUrl(state.wss, function () {
if (common.debug) { console.log('[debug] after tunnelUrl'); }
if (state.config.secret /* && !state.config.token */) {
state.config._token = common.signToken(state);
}
state.token = state.token || state.config.token || state.config._token;
if (state.token) {
if (common.debug) { console.log('[debug] token via token or secret'); }
// { token, pretoken }
handlers.connect(state.token, function () {
handlers.end(null, function () {});
});
return;
}
// backwards compat (TODO remove)
if (err || !dir || !dir.pair_request) {
if (common.debug) { console.log('[debug] no dir, connect'); }
handlers.error(new Error("No token found or generated, and no pair_request api found."));
return;
}
// TODO sign token with own private key, including public key and thumbprint
// (much like ACME JOSE account)
var otp = state.config._otp; // common.otp();
var authReq = {
subject: state.config.email
, subject_scheme: 'mailto'
// TODO create domains list earlier
, scope: (state.config._servernames || Object.keys(state.config.servernames || {}))
.concat(state.config._ports || Object.keys(state.config.ports || {})).join(',')
, otp: otp
// TODO make call to daemon for this info beforehand
/*
, hostname: os.hostname()
// Used for User-Agent
, os_type: os.type()
, os_platform: os.platform()
, os_release: os.release()
, os_arch: os.arch()
*/
};
var pairRequestUrl = new URL(dir.pair_request.pathname, 'https://' + dir.api_host.replace(/:hostname/g, state._relayHostname));
var req = {
url: pairRequestUrl
, method: dir.pair_request.method
, json: authReq
};
var firstReq = true;
var firstReady = true;
function gotoNext(req) {
if (common.debug) { console.log('[debug] gotoNext called'); }
if (common.debug) { console.log(req); }
common.requestAsync(req).then(function (resp) {
var body = resp.body;
function checkLocation() {
if (common.debug) { console.log('[debug] checkLocation'); }
if (common.debug) { console.log(body); }
// pending, try again
if ('pending' === body.status && resp.headers.location) {
if (common.debug) { console.log('[debug] pending'); }
setTimeout(gotoNext, 2 * 1000, { url: resp.headers.location, json: true });
return;
}
if ('ready' === body.status) {
if (common.debug) { console.log('[debug] ready'); }
if (firstReady) {
if (common.debug) { console.log('[debug] first ready'); }
firstReady = false;
state.token = body.access_token;
state.config.token = state.token;
handlers.offer(body.access_token, function () {
/*ignore*/
});
}
setTimeout(gotoNext, 2 * 1000, req);
return;
}
if ('complete' === body.status) {
if (common.debug) { console.log('[debug] complete'); }
handlers.granted(null, function () {
handlers.end(null, function () {});
});
return;
}
if (common.debug) { console.log('[debug] bad status'); }
var err = new Error("Bad State:" + body.status);
err._request = req;
handlers.error(err, function () {});
}
if (firstReq) {
if (common.debug) { console.log('[debug] first req'); }
handlers.requested(authReq, function () {
handlers.connect(body.access_token || body.jwt, function () {
var err;
if (!resp.headers.location) {
err = new Error("bad authentication request response");
err._resp = resp.toJSON && resp.toJSON();
handlers.error(err, function () {});
return;
}
setTimeout(gotoNext, 2 * 1000, { url: resp.headers.location, json: true });
});
});
firstReq = false;
return;
} else {
if (common.debug) { console.log('[debug] other req'); }
checkLocation();
}
}).catch(function (err) {
if (common.debug) { console.log('[debug] gotoNext error'); }
err._request = req;
err._hint = '[telebitd.js] pair request';
handlers.error(err, function () {});
});
}
gotoNext(req);
});
}
if (dir && dir.api_host) {
handlers.directory(dir, afterDir);
} else {
// backwards compat
dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } };
afterDir();
}
});
};
}('undefined' !== typeof module ? module.exports : window));