MAJOR: Updates for Authenticated Web UI and CLI #30
|
@ -7,7 +7,7 @@
|
||||||
<div class="v-app">
|
<div class="v-app">
|
||||||
<h1>Telebit (Remote) Setup</h1>
|
<h1>Telebit (Remote) Setup</h1>
|
||||||
|
|
||||||
<section v-if="views.section.create">
|
<section v-if="views.section.setup">
|
||||||
<h2>Create Account</h2>
|
<h2>Create Account</h2>
|
||||||
<form v-on:submit.stop.prevent="initialize">
|
<form v-on:submit.stop.prevent="initialize">
|
||||||
|
|
||||||
|
@ -43,12 +43,23 @@
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
<details><summary><small>Advanced</small></summary>
|
<details><summary><small>Advanced</small></summary>
|
||||||
<label for="-relay">Relay:</label><input id="-relay" v-model="init.relay" type="text" placeholder="telebit.cloud">
|
|
||||||
|
<label for="-relay">Relay:</label>
|
||||||
|
<input id="-relay" v-model="init.relay" type="text" placeholder="telebit.cloud">
|
||||||
<br>
|
<br>
|
||||||
<button type="button" v-on:click="defaultRelay">Use Default</button>
|
<button type="button" v-on:click="defaultRelay">Use Default</button>
|
||||||
<button type="button" v-on:click="betaRelay">Use Beta</button>
|
<button type="button" v-on:click="betaRelay">Use Beta</button>
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
<label for="-acme-server">ACME (Let's Encrypt) Server:</label>
|
||||||
|
<input id="-acme-server" v-model="init.acmeServer" type="text" placeholder="https://acme-v02.api.letsencrypt.org/directory">
|
||||||
|
<br>
|
||||||
|
<button type="button" v-on:click="productionAcme">Use Production</button>
|
||||||
|
<button type="button" v-on:click="stagingAcme">Use Staging</button>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<button type="submit">Accept & Continue</button>
|
<button type="submit">Accept & Continue</button>
|
||||||
|
@ -58,22 +69,32 @@
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section v-if="views.section.advanced">
|
<section v-if="views.section.advanced">
|
||||||
<h2>Advanced Setup</h2>
|
<h2>Advanced Setup for {{ init.relay }}</h2>
|
||||||
<form v-on:submit.stop.prevent="initialize">
|
<form v-on:submit.stop.prevent="advance">
|
||||||
|
|
||||||
<label for="-secret">Relay Secret:</label>
|
<strong><label for="-secret">Relay Shared Secret:</label></strong>
|
||||||
<input id="-secret" v-model="init.secret" type="text" placeholder="ex: xxxxxxxxxxxx">
|
<input id="-secret" v-model="init.secret" type="text" placeholder="ex: xxxxxxxxxxxx">
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
<strong><label for="-domains">Domains:</label></strong>
|
||||||
|
<br>
|
||||||
|
<small>(comma separated list of domains to use for http, tls, https, etc)</small>
|
||||||
|
<br>
|
||||||
|
<input id="-domains" v-model="init.domains" type="text" placeholder="ex: whatever.com, example.com">
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<strong><label for="-ports">TCP Ports:</label></strong>
|
||||||
|
<br>
|
||||||
|
<small>(comman separated list of ports, excluding 80 and 443, typically port over 1024)</small>
|
||||||
|
<br>
|
||||||
|
<input id="-ports" v-model="init.ports" type="text" placeholder="ex: 5050, 3000, 8080">
|
||||||
|
<br>
|
||||||
|
|
||||||
<label for="-telemetry"><input id="-telemetry" v-model="init.telemetry" type="checkbox">
|
<label for="-telemetry"><input id="-telemetry" v-model="init.telemetry" type="checkbox">
|
||||||
Contribute to Telebit by sharing telemetry</label>
|
Contribute to Telebit by sharing telemetry</label>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<label for="-relay">[Advanced] Relay:</label>
|
<button type="submit">Finish</button>
|
||||||
<input id="-relay" v-model="init.relay" type="text" placeholder="telebit.cloud">
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<button type="submit">Accept & Continue</button>
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
<pre><code>{{ init }}</code></pre>
|
<pre><code>{{ init }}</code></pre>
|
||||||
|
|
|
@ -1,14 +1,29 @@
|
||||||
;(function () {
|
;(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
console.log("hello");
|
|
||||||
|
|
||||||
var Vue = window.Vue;
|
var Vue = window.Vue;
|
||||||
var Telebit = window.TELEBIT;
|
var Telebit = window.TELEBIT;
|
||||||
var api = {};
|
var api = {};
|
||||||
|
|
||||||
|
/*globals AbortController*/
|
||||||
|
function safeFetch(url, opts) {
|
||||||
|
var controller = new AbortController();
|
||||||
|
var tok = setTimeout(function () {
|
||||||
|
controller.abort();
|
||||||
|
}, 4000);
|
||||||
|
if (!opts) {
|
||||||
|
opts = {};
|
||||||
|
}
|
||||||
|
opts.signal = controller.signal;
|
||||||
|
return window.fetch(url, opts).finally(function () {
|
||||||
|
clearTimeout(tok);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
api.config = function apiConfig() {
|
api.config = function apiConfig() {
|
||||||
return window.fetch("/api/config", { method: "GET" }).then(function (resp) {
|
return safeFetch("/api/config", {
|
||||||
|
method: "GET"
|
||||||
|
}).then(function (resp) {
|
||||||
return resp.json().then(function (json) {
|
return resp.json().then(function (json) {
|
||||||
appData.config = json;
|
appData.config = json;
|
||||||
return json;
|
return json;
|
||||||
|
@ -16,17 +31,43 @@ api.config = function apiConfig() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
api.status = function apiStatus() {
|
api.status = function apiStatus() {
|
||||||
return window.fetch("/api/status", { method: "GET" }).then(function (resp) {
|
return safeFetch("/api/status", { method: "GET" }).then(function (resp) {
|
||||||
return resp.json().then(function (json) {
|
return resp.json().then(function (json) {
|
||||||
appData.status = json;
|
appData.status = json;
|
||||||
return json;
|
return json;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
api.initialize = function apiInitialize() {
|
||||||
|
var opts = {
|
||||||
|
method: "POST"
|
||||||
|
, headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
, body: JSON.stringify({
|
||||||
|
foo: 'bar'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
return safeFetch("/api/init", opts).then(function (resp) {
|
||||||
|
return resp.json().then(function (json) {
|
||||||
|
appData.initResult = json;
|
||||||
|
window.alert("Error: [success] " + JSON.stringify(json, null, 2));
|
||||||
|
return json;
|
||||||
|
}).catch(function (err) {
|
||||||
|
window.alert("Error: [init] " + (err.message || JSON.stringify(err, null, 2)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// TODO test for internet connectivity (and telebit connectivity)
|
// TODO test for internet connectivity (and telebit connectivity)
|
||||||
var DEFAULT_RELAY = 'telebit.cloud';
|
var DEFAULT_RELAY = 'telebit.cloud';
|
||||||
var BETA_RELAY = 'telebit.ppl.family';
|
var BETA_RELAY = 'telebit.ppl.family';
|
||||||
|
var TELEBIT_RELAYS = [
|
||||||
|
DEFAULT_RELAY
|
||||||
|
, BETA_RELAY
|
||||||
|
];
|
||||||
|
var PRODUCTION_ACME = 'https://acme-v02.api.letsencrypt.org/directory';
|
||||||
|
var STAGING_ACME = 'https://acme-staging-v02.api.letsencrypt.org/directory';
|
||||||
var appData = {
|
var appData = {
|
||||||
config: null
|
config: null
|
||||||
, status: null
|
, status: null
|
||||||
|
@ -35,40 +76,93 @@ var appData = {
|
||||||
, letos: true
|
, letos: true
|
||||||
, notifications: "important"
|
, notifications: "important"
|
||||||
, relay: DEFAULT_RELAY
|
, relay: DEFAULT_RELAY
|
||||||
|
, telemetry: true
|
||||||
|
, acmeServer: PRODUCTION_ACME
|
||||||
}
|
}
|
||||||
, http: null
|
, http: null
|
||||||
, tcp: null
|
, tcp: null
|
||||||
, ssh: null
|
, ssh: null
|
||||||
, views: {
|
, views: {
|
||||||
section: {
|
section: {
|
||||||
create: true
|
setup: false
|
||||||
|
, advanced: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
var telebitState = {};
|
||||||
var appMethods = {
|
var appMethods = {
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
console.log("call initialize");
|
console.log("call initialize");
|
||||||
if (!appData.init.relay) {
|
if (!appData.init.relay) {
|
||||||
appData.init.relay = DEFAULT_RELAY;
|
appData.init.relay = DEFAULT_RELAY;
|
||||||
}
|
}
|
||||||
if (DEFAULT_RELAY !== appData.init.relay) {
|
appData.init.relay = appData.init.relay.toLowerCase();
|
||||||
window.alert("TODO: Custom Relay Not Implemented Yet");
|
telebitState = { relay: appData.init.relay };
|
||||||
}
|
return Telebit.api.directory(telebitState).then(function (dir) {
|
||||||
Telebit.api.directory({ relay: appData.init.relay }, function (err, dir) {
|
if (!dir.api_host) {
|
||||||
if (err) {
|
window.alert("Error: '" + telebitState.relay + "' does not appear to be a valid telebit service");
|
||||||
window.alert("Error:" + (err.message || JSON.stringify(err, null, 2)));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
window.alert("Success:" + JSON.stringify(dir, null, 2));
|
if (-1 !== TELEBIT_RELAYS.indexOf(appData.init.relay)) {
|
||||||
|
return api.initialize();
|
||||||
|
} else {
|
||||||
|
changeState('advanced');
|
||||||
|
}
|
||||||
|
}).catch(function (err) {
|
||||||
|
window.alert("Error: [directory] " + (err.message || JSON.stringify(err, null, 2)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
, advance: function () {
|
||||||
|
return api.initialize();
|
||||||
|
}
|
||||||
|
, productionAcme: function () {
|
||||||
|
console.log("prod acme:");
|
||||||
|
appData.init.acmeServer = PRODUCTION_ACME;
|
||||||
|
console.log(appData.init.acmeServer);
|
||||||
|
}
|
||||||
|
, stagingAcme: function () {
|
||||||
|
console.log("staging acme:");
|
||||||
|
appData.init.acmeServer = STAGING_ACME;
|
||||||
|
console.log(appData.init.acmeServer);
|
||||||
|
}
|
||||||
, defaultRelay: function () {
|
, defaultRelay: function () {
|
||||||
appData.init.relay = DEFAULT_RELAY;
|
appData.init.relay = DEFAULT_RELAY;
|
||||||
}
|
}
|
||||||
, betaRelay: function () {
|
, betaRelay: function () {
|
||||||
appData.init.relay = BETA_RELAY;
|
appData.init.relay = BETA_RELAY;
|
||||||
}
|
}
|
||||||
|
, defaultRhubarb: function () {
|
||||||
|
appData.init.rhubarb = DEFAULT_RELAY;
|
||||||
|
}
|
||||||
|
, betaRhubarb: function () {
|
||||||
|
appData.init.rhubarb = BETA_RELAY;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
var appStates = {
|
||||||
|
setup: function () {
|
||||||
|
appData.views.section = { setup: true };
|
||||||
|
}
|
||||||
|
, advanced: function () {
|
||||||
|
appData.views.section = { advanced: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function changeState(newstate) {
|
||||||
|
location.hash = '#/' + newstate + '/';
|
||||||
|
}
|
||||||
|
window.addEventListener('hashchange', setState, false);
|
||||||
|
function setState(/*ev*/) {
|
||||||
|
//ev.oldURL
|
||||||
|
//ev.newURL
|
||||||
|
var parts = location.hash.substr(1).replace(/^\//, '').replace(/\/$/, '').split('/');
|
||||||
|
var fn = appStates;
|
||||||
|
parts.forEach(function (s) {
|
||||||
|
console.log("state:", s);
|
||||||
|
fn = fn[s];
|
||||||
|
});
|
||||||
|
fn();
|
||||||
|
//appMethods.states[newstate]();
|
||||||
|
}
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
el: ".v-app"
|
el: ".v-app"
|
||||||
|
@ -76,8 +170,12 @@ new Vue({
|
||||||
, methods: appMethods
|
, methods: appMethods
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
api.config();
|
api.config();
|
||||||
api.status();
|
api.status().then(function () {
|
||||||
|
changeState('setup');
|
||||||
|
setState();
|
||||||
|
});
|
||||||
|
|
||||||
window.api = api;
|
window.api = api;
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -11,6 +11,7 @@ if ('undefined' !== typeof Promise) {
|
||||||
throw new Error("no Promise implementation defined");
|
throw new Error("no Promise implementation defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*globals AbortController*/
|
||||||
if ('undefined' !== typeof fetch) {
|
if ('undefined' !== typeof fetch) {
|
||||||
common.requestAsync = function (opts) {
|
common.requestAsync = function (opts) {
|
||||||
/*
|
/*
|
||||||
|
@ -37,7 +38,16 @@ if ('undefined' !== typeof fetch) {
|
||||||
}
|
}
|
||||||
, body: JSON.stringify(opts)
|
, body: JSON.stringify(opts)
|
||||||
};
|
};
|
||||||
|
var controller = new AbortController();
|
||||||
|
var tok = setTimeout(function () {
|
||||||
|
controller.abort();
|
||||||
|
}, 4000);
|
||||||
|
if (!relayOpts) {
|
||||||
|
relayOpts = {};
|
||||||
|
}
|
||||||
|
relayOpts.signal = controller.signal;
|
||||||
return window.fetch(relayOpts.url, relayOpts).then(function (resp) {
|
return window.fetch(relayOpts.url, relayOpts).then(function (resp) {
|
||||||
|
clearTimeout(tok);
|
||||||
return resp.json().then(function (json) {
|
return resp.json().then(function (json) {
|
||||||
/*
|
/*
|
||||||
var headers = {};
|
var headers = {};
|
||||||
|
@ -100,17 +110,20 @@ common.signToken = function (state) {
|
||||||
return jwt.sign(tokenData, state.config.secret);
|
return jwt.sign(tokenData, state.config.secret);
|
||||||
};
|
};
|
||||||
common.api = {};
|
common.api = {};
|
||||||
common.api.directory = function (state, next) {
|
common.api.directory = function (state) {
|
||||||
console.log('state:');
|
console.log('[DEBUG] state:');
|
||||||
console.log(state);
|
console.log(state);
|
||||||
state._relayUrl = common.parseUrl(state.relay);
|
state._relayUrl = common.parseUrl(state.relay);
|
||||||
common.requestAsync({ url: state._relayUrl + common.apiDirectory, json: true }).then(function (resp) {
|
if (!state._relays) { state._relays = {}; }
|
||||||
|
if (state._relays[state._relayUrl]) {
|
||||||
|
return PromiseA.resolve(state._relays[state._relayUrl]);
|
||||||
|
}
|
||||||
|
return common.requestAsync({ url: state._relayUrl + common.apiDirectory, json: true }).then(function (resp) {
|
||||||
var dir = resp.body;
|
var dir = resp.body;
|
||||||
if (!dir) { dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } }; }
|
state._relays[state._relayUrl] = dir;
|
||||||
state._apiDirectory = dir;
|
return dir;
|
||||||
next(null, dir);
|
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
next(err);
|
return PromiseA.reject(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
common.api._parseWss = function (state, dir) {
|
common.api._parseWss = function (state, dir) {
|
||||||
|
@ -121,153 +134,159 @@ common.api._parseWss = function (state, dir) {
|
||||||
return dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state._relayHostname) + dir.tunnel.pathname;
|
return dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state._relayHostname) + dir.tunnel.pathname;
|
||||||
};
|
};
|
||||||
common.api.wss = function (state, cb) {
|
common.api.wss = function (state, cb) {
|
||||||
common.api.directory(state, function (err, dir) {
|
common.api.directory(state).then(function (dir) {
|
||||||
cb(err, common.api._parseWss(state, dir));
|
cb(null, common.api._parseWss(state, dir));
|
||||||
});
|
}).catch(cb);
|
||||||
};
|
};
|
||||||
common.api.token = function (state, handlers) {
|
common.api.token = function (state, handlers) {
|
||||||
common.api.directory(state, function (err, dir) {
|
// directory, requested, connect, tunnelUrl, offer, granted, end
|
||||||
// directory, requested, connect, tunnelUrl, offer, granted, end
|
function afterDir(err, dir) {
|
||||||
function afterDir() {
|
if (common.debug) { console.log('[debug] after dir'); }
|
||||||
if (common.debug) { console.log('[debug] after dir'); }
|
state.wss = common.api._parseWss(state, dir);
|
||||||
state.wss = common.api._parseWss(state, dir);
|
|
||||||
|
|
||||||
handlers.tunnelUrl(state.wss, function () {
|
handlers.tunnelUrl(state.wss, function () {
|
||||||
if (common.debug) { console.log('[debug] after tunnelUrl'); }
|
if (common.debug) { console.log('[debug] after tunnelUrl'); }
|
||||||
if (state.config.secret /* && !state.config.token */) {
|
if (state.config.secret /* && !state.config.token */) {
|
||||||
state.config._token = common.signToken(state);
|
state.config._token = common.signToken(state);
|
||||||
}
|
}
|
||||||
state.token = state.token || state.config.token || state.config._token;
|
state.token = state.token || state.config.token || state.config._token;
|
||||||
if (state.token) {
|
if (state.token) {
|
||||||
if (common.debug) { console.log('[debug] token via token or secret'); }
|
if (common.debug) { console.log('[debug] token via token or secret'); }
|
||||||
// { token, pretoken }
|
// { token, pretoken }
|
||||||
handlers.connect(state.token, function () {
|
handlers.connect(state.token, function () {
|
||||||
handlers.end(null, function () {});
|
handlers.end(null, function () {});
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// backwards compat (TODO remove)
|
// backwards compat (TODO remove)
|
||||||
if (err || !dir || !dir.pair_request) {
|
if (err || !dir || !dir.pair_request) {
|
||||||
if (common.debug) { console.log('[debug] no dir, connect'); }
|
if (common.debug) { console.log('[debug] no dir, connect'); }
|
||||||
handlers.error(new Error("No token found or generated, and no pair_request api found."));
|
handlers.error(new Error("No token found or generated, and no pair_request api found."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO sign token with own private key, including public key and thumbprint
|
// TODO sign token with own private key, including public key and thumbprint
|
||||||
// (much like ACME JOSE account)
|
// (much like ACME JOSE account)
|
||||||
var otp = state.config._otp; // common.otp();
|
var otp = state.config._otp; // common.otp();
|
||||||
var authReq = {
|
var authReq = {
|
||||||
subject: state.config.email
|
subject: state.config.email
|
||||||
, subject_scheme: 'mailto'
|
, subject_scheme: 'mailto'
|
||||||
// TODO create domains list earlier
|
// TODO create domains list earlier
|
||||||
, scope: (state.config._servernames || Object.keys(state.config.servernames || {}))
|
, scope: (state.config._servernames || Object.keys(state.config.servernames || {}))
|
||||||
.concat(state.config._ports || Object.keys(state.config.ports || {})).join(',')
|
.concat(state.config._ports || Object.keys(state.config.ports || {})).join(',')
|
||||||
, otp: otp
|
, otp: otp
|
||||||
// TODO make call to daemon for this info beforehand
|
// TODO make call to daemon for this info beforehand
|
||||||
/*
|
/*
|
||||||
, hostname: os.hostname()
|
, hostname: os.hostname()
|
||||||
// Used for User-Agent
|
// Used for User-Agent
|
||||||
, os_type: os.type()
|
, os_type: os.type()
|
||||||
, os_platform: os.platform()
|
, os_platform: os.platform()
|
||||||
, os_release: os.release()
|
, os_release: os.release()
|
||||||
, os_arch: os.arch()
|
, os_arch: os.arch()
|
||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
var pairRequestUrl = new URL(dir.pair_request.pathname, 'https://' + dir.api_host.replace(/:hostname/g, state._relayHostname));
|
var pairRequestUrl = new URL(dir.pair_request.pathname, 'https://' + dir.api_host.replace(/:hostname/g, state._relayHostname));
|
||||||
var req = {
|
var req = {
|
||||||
url: pairRequestUrl
|
url: pairRequestUrl
|
||||||
, method: dir.pair_request.method
|
, method: dir.pair_request.method
|
||||||
, json: authReq
|
, json: authReq
|
||||||
};
|
};
|
||||||
var firstReq = true;
|
var firstReq = true;
|
||||||
var firstReady = true;
|
var firstReady = true;
|
||||||
|
|
||||||
function gotoNext(req) {
|
function gotoNext(req) {
|
||||||
if (common.debug) { console.log('[debug] gotoNext called'); }
|
if (common.debug) { console.log('[debug] gotoNext called'); }
|
||||||
if (common.debug) { console.log(req); }
|
if (common.debug) { console.log(req); }
|
||||||
common.requestAsync(req).then(function (resp) {
|
common.requestAsync(req).then(function (resp) {
|
||||||
var body = resp.body;
|
var body = resp.body;
|
||||||
|
|
||||||
function checkLocation() {
|
function checkLocation() {
|
||||||
if (common.debug) { console.log('[debug] checkLocation'); }
|
if (common.debug) { console.log('[debug] checkLocation'); }
|
||||||
if (common.debug) { console.log(body); }
|
if (common.debug) { console.log(body); }
|
||||||
// pending, try again
|
// pending, try again
|
||||||
if ('pending' === body.status && resp.headers.location) {
|
if ('pending' === body.status && resp.headers.location) {
|
||||||
if (common.debug) { console.log('[debug] pending'); }
|
if (common.debug) { console.log('[debug] pending'); }
|
||||||
setTimeout(gotoNext, 2 * 1000, { url: resp.headers.location, json: true });
|
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;
|
return;
|
||||||
} else {
|
|
||||||
if (common.debug) { console.log('[debug] other req'); }
|
|
||||||
checkLocation();
|
|
||||||
}
|
}
|
||||||
}).catch(function (err) {
|
|
||||||
if (common.debug) { console.log('[debug] gotoNext error'); }
|
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;
|
err._request = req;
|
||||||
err._hint = '[telebitd.js] pair request';
|
|
||||||
handlers.error(err, function () {});
|
handlers.error(err, function () {});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
gotoNext(req);
|
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 (TODO verify we can remove this)
|
||||||
// backwards compat
|
var failoverDir = '{ "api_host": ":hostname", "tunnel": { "method": "wss", "pathname": "" } }';
|
||||||
dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } };
|
common.api.directory(state).then(function (dir) {
|
||||||
afterDir();
|
if (!dir.api_host) {
|
||||||
|
dir = JSON.parse(failoverDir);
|
||||||
|
return afterDir(null, dir);
|
||||||
}
|
}
|
||||||
|
handlers.directory(dir).then(function (dir) {
|
||||||
|
return afterDir(null, dir);
|
||||||
|
}).catch(function (err) {
|
||||||
|
return PromiseA.reject(err);
|
||||||
|
});
|
||||||
|
}).catch(function (err) {
|
||||||
|
return afterDir(err, JSON.parse(failoverDir));
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue