MAJOR: Updates for Authenticated Web UI and CLI #30

Open
coolaj86 wants to merge 77 commits from next into master
3 changed files with 110 additions and 25 deletions
Showing only changes of commit ce20b058e6 - Show all commits

View File

@ -27,6 +27,7 @@ var snakeCopy = recase.snakeCopy.bind(recase);
var TPLS = TOML.parse(fs.readFileSync(path.join(__dirname, "../lib/en-us.toml"), 'utf8')); var TPLS = TOML.parse(fs.readFileSync(path.join(__dirname, "../lib/en-us.toml"), 'utf8'));
var startTime = Date.now(); var startTime = Date.now();
var connectTimes = []; var connectTimes = [];
var isConnected = false;
var TelebitRemote = require('../lib/daemon/index.js').TelebitRemote; var TelebitRemote = require('../lib/daemon/index.js').TelebitRemote;
@ -628,17 +629,17 @@ function handleApi(req, res) {
res.setHeader('Content-Type', 'application/json'); res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify( res.end(JSON.stringify(
{ module: 'status' { module: 'status'
, port: (state.config.ipc && state.config.ipc.port || state._ipc.port || undefined)
, status: (state.config.disable ? 'disabled' : 'enabled')
, ready: ((state.config.relay && (state.config.token || state.config.agreeTos)) ? true : false)
, active: !!myRemote
, connected: 'maybe (todo)'
, version: pkg.version , version: pkg.version
, servernames: state.servernames , port: (state.config.ipc && state.config.ipc.port || state._ipc.port || undefined)
, enabled: !state.config.disable
, active: !!myRemote
, initialized: (state.config.relay && state.config.token && state.config.agreeTos) ? true : false
, connected: isConnected
, proctime: Math.round(process.uptime() * 1000) , proctime: Math.round(process.uptime() * 1000)
, uptime: now - startTime , uptime: now - startTime
, runtime: connectTimes.length && (now - connectTimes[0]) || 0 , runtime: isConnected && connectTimes.length && (now - connectTimes[0]) || 0
, reconnects: connectTimes.length , reconnects: connectTimes.length
, servernames: state.servernames
} }
)); ));
} }
@ -707,7 +708,8 @@ function handleApi(req, res) {
res.end(JSON.stringify({"error":{"message":"unrecognized rpc"}})); res.end(JSON.stringify({"error":{"message":"unrecognized rpc"}}));
} }
if (!req.headers['content-length'] && !req.headers['content-type']) { var hasLength = req.headers['content-length'] > 0;
if (!hasLength && !req.headers['content-type']) {
route(); route();
return; return;
} }
@ -1042,6 +1044,7 @@ function rawStartTelebitRemote(keepAlive) {
} }
function onConnect() { function onConnect() {
isConnected = true;
connectTimes.unshift(Date.now()); connectTimes.unshift(Date.now());
console.info('[connect] relay established'); console.info('[connect] relay established');
myRemote.removeListener('error', onConnectError); myRemote.removeListener('error', onConnectError);
@ -1060,6 +1063,7 @@ function rawStartTelebitRemote(keepAlive) {
function onConnectError(err) { function onConnectError(err) {
myRemote = null; myRemote = null;
isConnected = false;
if (handleError(err, 'onConnectError')) { if (handleError(err, 'onConnectError')) {
if (!keepAlive.state) { if (!keepAlive.state) {
reject(err); reject(err);
@ -1075,6 +1079,7 @@ function rawStartTelebitRemote(keepAlive) {
} }
function retryLoop() { function retryLoop() {
isConnected = false;
console.warn('[Warn] disconnected. Will retry?', keepAlive.state); console.warn('[Warn] disconnected. Will retry?', keepAlive.state);
if (keepAlive.state) { if (keepAlive.state) {
safeReload(10 * 1000).then(resolve).catch(reject); safeReload(10 * 1000).then(resolve).catch(reject);

View File

@ -7,7 +7,7 @@
<script>document.body.hidden = true;</script> <script>document.body.hidden = true;</script>
<div class="v-app"> <div class="v-app">
<h1>Telebit (Remote) Setup</h1> <h1>Telebit (Remote) Setup v{{ config.version }}</h1>
<section v-if="views.flash.error"> <section v-if="views.flash.error">
{{ views.flash.error }} {{ views.flash.error }}
@ -115,6 +115,19 @@
</section> </section>
<section v-if="views.section.status"> <section v-if="views.section.status">
<button v-if="!status.enabled" v-on:click="enable">Enable</button>
<button v-if="status.enabled" v-on:click="disable">Disable</button>
<br>
http://localhost:{{ status.port }}
<br>
Proctime: {{ statusProctime }}
<br>
Uptime: {{ statusUptime }}
<br>
Runtime: {{ statusRuntime }}
<br>
Reconnects: {{ status.reconnects }}
<br>
<pre><code>{{ status }}</code></pre> <pre><code>{{ status }}</code></pre>
</section> </section>

View File

@ -37,14 +37,26 @@ api.status = function apiStatus() {
return json; return json;
}); });
}; };
api.initialize = function apiInitialize() { api.enable = function apiEnable() {
var opts = { var opts = {
url: "/api/xxinitxx" url: "/api/enable"
, method: "POST" , method: "POST"
, headers: { //, headers: { 'Content-Type': 'application/json' }
'Content-Type': 'application/json' };
} return Telebit.reqLocalAsync(opts).then(function (resp) {
, body: JSON.stringify(telebitState.config) var json = resp.body;
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)));
});
};
api.disable = function apiDisable() {
var opts = {
url: "/api/disable"
, method: "POST"
//, headers: { 'Content-Type': 'application/json' }
}; };
return Telebit.reqLocalAsync(opts).then(function (resp) { return Telebit.reqLocalAsync(opts).then(function (resp) {
var json = resp.body; var json = resp.body;
@ -107,8 +119,8 @@ var TELEBIT_RELAYS = [
var PRODUCTION_ACME = 'https://acme-v02.api.letsencrypt.org/directory'; var PRODUCTION_ACME = 'https://acme-v02.api.letsencrypt.org/directory';
var STAGING_ACME = 'https://acme-staging-v02.api.letsencrypt.org/directory'; var STAGING_ACME = 'https://acme-staging-v02.api.letsencrypt.org/directory';
var appData = { var appData = {
config: null config: {}
, status: null , status: {}
, init: { , init: {
teletos: true teletos: true
, letos: true , letos: true
@ -181,11 +193,11 @@ var appMethods = {
, betaRelay: function () { , betaRelay: function () {
appData.init.relay = BETA_RELAY; appData.init.relay = BETA_RELAY;
} }
, defaultRhubarb: function () { , enable: function () {
appData.init.rhubarb = DEFAULT_RELAY; api.enable();
} }
, betaRhubarb: function () { , disable: function () {
appData.init.rhubarb = BETA_RELAY; api.disable();
} }
}; };
var appStates = { var appStates = {
@ -200,9 +212,15 @@ var appStates = {
} }
, status: function () { , status: function () {
appData.views.section = { status: true }; appData.views.section = { status: true };
return api.status().then(function (status) { var tok = setInterval(function () {
api.status().then(function (status) {
appData.status = status; appData.status = status;
}); });
}, 2000);
return function cancelState() {
clearInterval(tok);
};
} }
}; };
@ -216,23 +234,72 @@ function changeState(newstate) {
} }
location.hash = newhash; location.hash = newhash;
} }
/*globals Promise*/
window.addEventListener('hashchange', setState, false); window.addEventListener('hashchange', setState, false);
function setState(/*ev*/) { function setState(/*ev*/) {
//ev.oldURL //ev.oldURL
//ev.newURL //ev.newURL
if (appData.exit) {
appData.exit.then(function (exit) {
if ('function' === typeof appData.exit) {
exit();
}
});
}
var parts = location.hash.substr(1).replace(/^\//, '').replace(/\/$/, '').split('/'); var parts = location.hash.substr(1).replace(/^\//, '').replace(/\/$/, '').split('/');
var fn = appStates; var fn = appStates;
parts.forEach(function (s) { parts.forEach(function (s) {
console.log("state:", s); console.log("state:", s);
fn = fn[s]; fn = fn[s];
}); });
fn(); appData.exit = Promise.resolve(fn());
//appMethods.states[newstate](); //appMethods.states[newstate]();
} }
function msToHumanReadable(ms) {
var uptime = ms;
var uptimed = uptime / 1000;
var minute = 60;
var hour = 60 * minute;
var day = 24 * hour;
var days = 0;
var times = [];
while (uptimed > day) {
uptimed -= day;
days += 1;
}
times.push(days + " days ");
var hours = 0;
while (uptimed > hour) {
uptimed -= hour;
hours += 1;
}
times.push(hours.toString().padStart(2, "0") + " h ");
var minutes = 0;
while (uptimed > minute) {
uptimed -= minute;
minutes += 1;
}
times.push(minutes.toString().padStart(2, "0") + " m ");
var seconds = Math.round(uptimed);
times.push(seconds.toString().padStart(2, "0") + " s ");
return times.join('');
}
new Vue({ new Vue({
el: ".v-app" el: ".v-app"
, data: appData , data: appData
, computed: {
statusProctime: function () {
return msToHumanReadable(this.status.proctime);
}
, statusRuntime: function () {
return msToHumanReadable(this.status.runtime);
}
, statusUptime: function () {
return msToHumanReadable(this.status.uptime);
}
}
, methods: appMethods , methods: appMethods
}); });