web ui updates
This commit is contained in:
		
							parent
							
								
									b81f0ecede
								
							
						
					
					
						commit
						9e1c9c00ca
					
				| @ -7,7 +7,7 @@ | ||||
|   <div class="v-app"> | ||||
|     <h1>Telebit (Remote) Setup</h1> | ||||
| 
 | ||||
|     <section v-if="views.section.create"> | ||||
|     <section v-if="views.section.setup"> | ||||
|       <h2>Create Account</h2> | ||||
|       <form v-on:submit.stop.prevent="initialize"> | ||||
| 
 | ||||
| @ -43,12 +43,23 @@ | ||||
|         </small> | ||||
| 
 | ||||
|         <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> | ||||
|         <button type="button" v-on:click="defaultRelay">Use Default</button> | ||||
|         <button type="button" v-on:click="betaRelay">Use Beta</button> | ||||
|         <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> | ||||
| 
 | ||||
|         <button type="submit">Accept & Continue</button> | ||||
| @ -58,22 +69,32 @@ | ||||
|     </section> | ||||
| 
 | ||||
|     <section v-if="views.section.advanced"> | ||||
|       <h2>Advanced Setup</h2> | ||||
|       <form v-on:submit.stop.prevent="initialize"> | ||||
|       <h2>Advanced Setup for {{ init.relay }}</h2> | ||||
|       <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"> | ||||
|         <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"> | ||||
|           Contribute to Telebit by sharing telemetry</label> | ||||
|         <br> | ||||
| 
 | ||||
|         <label for="-relay">[Advanced] Relay:</label> | ||||
|         <input id="-relay" v-model="init.relay" type="text" placeholder="telebit.cloud"> | ||||
|         <br> | ||||
| 
 | ||||
|         <button type="submit">Accept & Continue</button> | ||||
|         <button type="submit">Finish</button> | ||||
| 
 | ||||
|       </form> | ||||
|       <pre><code>{{ init }}</code></pre> | ||||
|  | ||||
| @ -1,14 +1,29 @@ | ||||
| ;(function () { | ||||
| 'use strict'; | ||||
| 
 | ||||
| console.log("hello"); | ||||
| 
 | ||||
| var Vue = window.Vue; | ||||
| var Telebit = window.TELEBIT; | ||||
| 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() { | ||||
|   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) { | ||||
|       appData.config = json; | ||||
|       return json; | ||||
| @ -16,17 +31,43 @@ api.config = function apiConfig() { | ||||
|   }); | ||||
| }; | ||||
| 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) { | ||||
|       appData.status = 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)
 | ||||
| var DEFAULT_RELAY = 'telebit.cloud'; | ||||
| 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 = { | ||||
|   config: null | ||||
| , status: null | ||||
| @ -35,40 +76,93 @@ var appData = { | ||||
|   , letos: true | ||||
|   , notifications: "important" | ||||
|   , relay: DEFAULT_RELAY | ||||
|   , telemetry: true | ||||
|   , acmeServer: PRODUCTION_ACME | ||||
|   } | ||||
| , http: null | ||||
| , tcp: null | ||||
| , ssh: null | ||||
| , views: { | ||||
|     section: { | ||||
|       create: true | ||||
|       setup: false | ||||
|     , advanced: false | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| var telebitState = {}; | ||||
| var appMethods = { | ||||
|   initialize: function () { | ||||
|     console.log("call initialize"); | ||||
|     if (!appData.init.relay) { | ||||
|       appData.init.relay = DEFAULT_RELAY; | ||||
|     } | ||||
|     if (DEFAULT_RELAY !== appData.init.relay) { | ||||
|       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))); | ||||
|     appData.init.relay = appData.init.relay.toLowerCase(); | ||||
|     telebitState = { relay: appData.init.relay }; | ||||
|     return Telebit.api.directory(telebitState).then(function (dir) { | ||||
|       if (!dir.api_host) { | ||||
|         window.alert("Error: '" + telebitState.relay + "' does not appear to be a valid telebit service"); | ||||
|         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 () { | ||||
|     appData.init.relay = DEFAULT_RELAY; | ||||
|   } | ||||
| , betaRelay: function () { | ||||
|     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({ | ||||
|   el: ".v-app" | ||||
| @ -76,8 +170,12 @@ new Vue({ | ||||
| , methods: appMethods | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| api.config(); | ||||
| api.status(); | ||||
| api.status().then(function () { | ||||
|   changeState('setup'); | ||||
|   setState(); | ||||
| }); | ||||
| 
 | ||||
| window.api = api; | ||||
| }()); | ||||
|  | ||||
| @ -11,6 +11,7 @@ if ('undefined' !== typeof Promise) { | ||||
|   throw new Error("no Promise implementation defined"); | ||||
| } | ||||
| 
 | ||||
| /*globals AbortController*/ | ||||
| if ('undefined' !== typeof fetch) { | ||||
|   common.requestAsync = function (opts) { | ||||
|     /* | ||||
| @ -37,7 +38,16 @@ if ('undefined' !== typeof fetch) { | ||||
|       } | ||||
|     , 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) { | ||||
|       clearTimeout(tok); | ||||
|       return resp.json().then(function (json) { | ||||
|         /* | ||||
|         var headers = {}; | ||||
| @ -100,17 +110,20 @@ common.signToken = function (state) { | ||||
|   return jwt.sign(tokenData, state.config.secret); | ||||
| }; | ||||
| common.api = {}; | ||||
| common.api.directory = function (state, next) { | ||||
|   console.log('state:'); | ||||
| common.api.directory = function (state) { | ||||
|   console.log('[DEBUG] state:'); | ||||
|   console.log(state); | ||||
|   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; | ||||
|     if (!dir) { dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } }; } | ||||
|     state._apiDirectory = dir; | ||||
|     next(null, dir); | ||||
|     state._relays[state._relayUrl] = dir; | ||||
|     return dir; | ||||
|   }).catch(function (err) { | ||||
|     next(err); | ||||
|     return PromiseA.reject(err); | ||||
|   }); | ||||
| }; | ||||
| common.api._parseWss = function (state, dir) { | ||||
| @ -121,14 +134,13 @@ common.api._parseWss = function (state, dir) { | ||||
|   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.directory(state).then(function (dir) { | ||||
|     cb(null, common.api._parseWss(state, dir)); | ||||
|   }).catch(cb); | ||||
| }; | ||||
| common.api.token = function (state, handlers) { | ||||
|   common.api.directory(state, function (err, dir) { | ||||
|   // directory, requested, connect, tunnelUrl, offer, granted, end
 | ||||
|     function afterDir() { | ||||
|   function afterDir(err, dir) { | ||||
|     if (common.debug) { console.log('[debug] after dir'); } | ||||
|     state.wss = common.api._parseWss(state, dir); | ||||
| 
 | ||||
| @ -261,13 +273,20 @@ common.api.token = function (state, handlers) { | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|     if (dir && dir.api_host) { | ||||
|       handlers.directory(dir, afterDir); | ||||
|     } else { | ||||
|       // backwards compat
 | ||||
|       dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } }; | ||||
|       afterDir(); | ||||
|   // backwards compat (TODO verify we can remove this)
 | ||||
|   var failoverDir = '{ "api_host": ":hostname", "tunnel": { "method": "wss", "pathname": "" } }'; | ||||
|   common.api.directory(state).then(function (dir) { | ||||
|     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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user