MAJOR: Updates for Authenticated Web UI and CLI #30
| @ -338,9 +338,12 @@ controllers.relay = function (req, res, opts) { | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   console.log('POST /api/relay:'); | ||||
|   console.log(opts.body); | ||||
|   console.log(); | ||||
|   return urequestAsync(opts.body).then(function (resp) { | ||||
|     res.setHeader('Content-Type', 'application/json'); | ||||
|     var resp = resp.toJSON(); | ||||
|     resp = resp.toJSON(); | ||||
|     res.end(JSON.stringify(resp)); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| @ -104,6 +104,7 @@ | ||||
| 
 | ||||
|   <script src="/js/vue.js"></script> | ||||
|   <script src="/js/telebit.js"></script> | ||||
|   <script src="/js/telebit-token.js"></script> | ||||
|   <script src="/js/app.js"></script> | ||||
| </body> | ||||
| </html> | ||||
|  | ||||
| @ -5,7 +5,7 @@ var Vue = window.Vue; | ||||
| var Telebit = window.TELEBIT; | ||||
| var api = {}; | ||||
| 
 | ||||
| /*globals AbortController*/ | ||||
| /* | ||||
| function safeFetch(url, opts) { | ||||
|   var controller = new AbortController(); | ||||
|   var tok = setTimeout(function () { | ||||
| @ -19,28 +19,29 @@ function safeFetch(url, opts) { | ||||
|     clearTimeout(tok); | ||||
|   }); | ||||
| } | ||||
| */ | ||||
| 
 | ||||
| api.config = function apiConfig() { | ||||
|   return safeFetch("/api/config", { | ||||
|     method: "GET" | ||||
|   return Telebit.reqLocalAsync({ | ||||
|     url: "/api/config" | ||||
|   , method: "GET" | ||||
|   }).then(function (resp) { | ||||
|     return resp.json().then(function (json) { | ||||
|     var json = resp.body; | ||||
|     appData.config = json; | ||||
|     return json; | ||||
|   }); | ||||
|   }); | ||||
| }; | ||||
| api.status = function apiStatus() { | ||||
|   return safeFetch("/api/status", { method: "GET" }).then(function (resp) { | ||||
|     return resp.json().then(function (json) { | ||||
|   return Telebit.reqLocalAsync({ url: "/api/status", method: "GET" }).then(function (resp) { | ||||
|     var json = resp.body; | ||||
|     appData.status = json; | ||||
|     return json; | ||||
|   }); | ||||
|   }); | ||||
| }; | ||||
| api.initialize = function apiInitialize() { | ||||
|   var opts = { | ||||
|     method: "POST" | ||||
|     url: "/api/init" | ||||
|   , method: "POST" | ||||
|   , headers: { | ||||
|       'Content-Type': 'application/json' | ||||
|     } | ||||
| @ -48,15 +49,14 @@ api.initialize = function apiInitialize() { | ||||
|       foo: 'bar' | ||||
|     }) | ||||
|   }; | ||||
|   return safeFetch("/api/init", opts).then(function (resp) { | ||||
|     return resp.json().then(function (json) { | ||||
|   return Telebit.reqLocalAsync(opts).then(function (resp) { | ||||
|     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))); | ||||
|   }); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| // TODO test for internet connectivity (and telebit connectivity)
 | ||||
| @ -104,15 +104,27 @@ var appMethods = { | ||||
|         return; | ||||
|       } | ||||
|       if (-1 !== TELEBIT_RELAYS.indexOf(appData.init.relay)) { | ||||
|         if (!telebitState.config) { telebitState.config = {}; } | ||||
|         if (!telebitState.config.relay) { telebitState.config.relay = telebitState.relay; } | ||||
|         telebitState.config.email = appData.init.email; | ||||
|         telebitState.config._otp = Telebit.otp(); | ||||
|         return Telebit.authorize(telebitState).then(function () { | ||||
|           console.log('1 api.init...'); | ||||
|           return api.initialize(); | ||||
|         }).catch(function (err) { | ||||
|           console.error(err); | ||||
|           window.alert("Error: [authorize] " + (err.message || JSON.stringify(err, null, 2))); | ||||
|         }); | ||||
|       } else { | ||||
|         changeState('advanced'); | ||||
|       } | ||||
|     }).catch(function (err) { | ||||
|       console.error(err); | ||||
|       window.alert("Error: [directory] " + (err.message || JSON.stringify(err, null, 2))); | ||||
|     }); | ||||
|   } | ||||
| , advance: function () { | ||||
|     console.log('2 api.init...'); | ||||
|     return api.initialize(); | ||||
|   } | ||||
| , productionAcme: function () { | ||||
|  | ||||
							
								
								
									
										123
									
								
								lib/admin/js/telebit-token.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								lib/admin/js/telebit-token.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | ||||
| ;(function (exports) { | ||||
| 'use strict'; | ||||
| 
 | ||||
| /* global Promise */ | ||||
| var PromiseA; | ||||
| if ('undefined' !== typeof Promise) { | ||||
|   PromiseA = Promise; | ||||
| } else { | ||||
|   throw new Error("no Promise implementation defined"); | ||||
| } | ||||
| 
 | ||||
| var common = exports.TELEBIT || require('./lib/common.js'); | ||||
| 
 | ||||
| common.authorize = common.getToken = function getToken(state) { | ||||
|   state.relay = state.config.relay; | ||||
| 
 | ||||
|   // { _otp, config: {} }
 | ||||
|   return common.api.token(state, { | ||||
|     error: function (err) { | ||||
|       console.error("[Error] common.api.token handlers.error:"); | ||||
|       console.error(err); | ||||
|       return PromiseA.reject(err); | ||||
|     } | ||||
|   , directory: function (dir) { | ||||
|       //console.log('[directory] Telebit Relay Discovered:');
 | ||||
|       //console.log(dir);
 | ||||
|       state._apiDirectory = dir; | ||||
|       return PromiseA.resolve(); | ||||
|     } | ||||
|   , tunnelUrl: function (tunnelUrl) { | ||||
|       //console.log('[tunnelUrl] Telebit Relay Tunnel Socket:', tunnelUrl);
 | ||||
|       state.wss = tunnelUrl; | ||||
|       return PromiseA.resolve(); | ||||
|     } | ||||
|   , requested: function (authReq) { | ||||
|       console.log("[requested] Pairing Requested"); | ||||
|       state.config._otp = state.config._otp = authReq.otp; | ||||
| 
 | ||||
|       if (!state.config.token && state._can_pair) { | ||||
|         console.info("0000".replace(/0000/g, state.config._otp)); | ||||
|       } | ||||
| 
 | ||||
|       return PromiseA.resolve(); | ||||
|     } | ||||
|   , connect: function (pretoken) { | ||||
|       console.log("[connect] Enabling Pairing Locally..."); | ||||
|       state.config.pretoken = pretoken; | ||||
|       state._connecting = true; | ||||
| 
 | ||||
|       return common.reqLocalAsync({ url: '/api/config', method: 'POST', data: state.config || {} }).then(function () { | ||||
|         console.info("waiting..."); | ||||
|         return PromiseA.resolve(); | ||||
|       }).catch(function (err) { | ||||
|         state._error = err; | ||||
|         console.error("Error while initializing config [connect]:"); | ||||
|         console.error(err); | ||||
|         return PromiseA.reject(err); | ||||
|       }); | ||||
|     } | ||||
|   , offer: function (token) { | ||||
|       //console.log("[offer] Pairing Enabled by Relay");
 | ||||
|       state.config.token = token; | ||||
|       if (state._error) { | ||||
|         return; | ||||
|       } | ||||
|       state._connecting = true; | ||||
|       try { | ||||
|         //require('jsonwebtoken').decode(token);
 | ||||
|         token = token.split('.'); | ||||
|         token[0] = token[0].replace(/_/g, '/').replace(/-/g, '+'); | ||||
|         while (token[0].length % 4) { token[0] += '='; } | ||||
|         btoa(token[0]); | ||||
|         token[1] = token[1].replace(/_/g, '/').replace(/-/g, '+'); | ||||
|         while (token[1].length % 4) { token[1] += '='; } | ||||
|         btoa(token[1]); | ||||
|         //console.log(require('jsonwebtoken').decode(token));
 | ||||
|       } catch(e) { | ||||
|         console.warn("[warning] could not decode token"); | ||||
|       } | ||||
|       return common.reqLocalAsync({ url: '/api/config', method: 'POST', data: state.config }).then(function () { | ||||
|         //console.log("Pairing Enabled Locally");
 | ||||
|         return PromiseA.resolve(); | ||||
|       }).catch(function (err) { | ||||
|         state._error = err; | ||||
|         console.error("Error while initializing config [offer]:"); | ||||
|         console.error(err); | ||||
|         return PromiseA.reject(err); | ||||
|       }); | ||||
|     } | ||||
|   , granted: function (/*_*/) { | ||||
|       //console.log("[grant] Pairing complete!");
 | ||||
|       return PromiseA.resolve(); | ||||
|     } | ||||
|   , end: function () { | ||||
|       return common.reqLocalAsync({ url: '/api/enable', method: 'POST', data: [] }).then(function () { | ||||
|         console.info("Success"); | ||||
| 
 | ||||
|         // workaround for https://github.com/nodejs/node/issues/21319
 | ||||
|         if (state._useTty) { | ||||
|           setTimeout(function () { | ||||
|             console.info("Some fun things to try first:\n"); | ||||
|             console.info("    ~/telebit http ~/public"); | ||||
|             console.info("    ~/telebit tcp 5050"); | ||||
|             console.info("    ~/telebit ssh auto"); | ||||
|             console.info(); | ||||
|             console.info("Press any key to continue..."); | ||||
|             console.info(); | ||||
|             process.exit(0); | ||||
|           }, 0.5 * 1000); | ||||
|           return; | ||||
|         } | ||||
|         // end workaround
 | ||||
| 
 | ||||
|         //parseCli(state);
 | ||||
|       }).catch(function (err) { | ||||
|         console.error('[end] [error]', err); | ||||
|         return PromiseA.reject(err); | ||||
|       }); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| }('undefined' === typeof module ? window : module.exports)); | ||||
| @ -2,6 +2,7 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| var common = exports.TELEBIT = {}; | ||||
| common.debug = true; | ||||
| 
 | ||||
| /* global Promise */ | ||||
| var PromiseA; | ||||
| @ -14,19 +15,6 @@ if ('undefined' !== typeof Promise) { | ||||
| /*globals AbortController*/ | ||||
| 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'; | ||||
|       } | ||||
|     } | ||||
|     */ | ||||
|     // funnel requests through the local server
 | ||||
|     // (avoid CORS, for now)
 | ||||
|     var relayOpts = { | ||||
| @ -49,13 +37,6 @@ if ('undefined' !== typeof fetch) { | ||||
|     return window.fetch(relayOpts.url, relayOpts).then(function (resp) { | ||||
|       clearTimeout(tok); | ||||
|       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 }; | ||||
|         */ | ||||
|         if (json.error) { | ||||
|           return PromiseA.reject(new Error(json.error && json.error.message || JSON.stringify(json.error))); | ||||
|         } | ||||
| @ -63,8 +44,38 @@ if ('undefined' !== typeof fetch) { | ||||
|       }); | ||||
|     }); | ||||
|   }; | ||||
|   common.reqLocalAsync = function (opts) { | ||||
|     if (!opts) { 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'; | ||||
|       } | ||||
|     } | ||||
|     var controller = new AbortController(); | ||||
|     var tok = setTimeout(function () { | ||||
|       controller.abort(); | ||||
|     }, 4000); | ||||
|     opts.signal = controller.signal; | ||||
|     return window.fetch(opts.url, opts).then(function (resp) { | ||||
|       clearTimeout(tok); | ||||
|       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.reqLocalAsync = require('util').promisify(require('@coolaj86/urequest')); | ||||
| } | ||||
| 
 | ||||
| common.parseUrl = function (hostname) { | ||||
| @ -78,7 +89,12 @@ common.parseUrl = function (hostname) { | ||||
|   return hostname; | ||||
| }; | ||||
| common.parseHostname = function (hostname) { | ||||
|   var location = new URL(hostname); | ||||
|   var location = {}; | ||||
|   try { | ||||
|     location = new URL(hostname); | ||||
|   } catch(e) { | ||||
|     // ignore
 | ||||
|   } | ||||
|   if (!location.protocol || /\./.test(location.protocol)) { | ||||
|     hostname = 'https://' + hostname; | ||||
|     location = new URL(hostname); | ||||
| @ -109,6 +125,17 @@ common.signToken = function (state) { | ||||
| 
 | ||||
|   return jwt.sign(tokenData, state.config.secret); | ||||
| }; | ||||
| common.promiseTimeout = function (ms) { | ||||
|   var x = new PromiseA(function (resolve) { | ||||
|     x._tok = setTimeout(function () { | ||||
|       resolve(); | ||||
|     }, ms); | ||||
|   }); | ||||
|   x.cancel = function () { | ||||
|     clearTimeout(x._tok); | ||||
|   }; | ||||
|   return x; | ||||
| }; | ||||
| common.api = {}; | ||||
| common.api.directory = function (state) { | ||||
|   console.log('[DEBUG] state:'); | ||||
| @ -118,11 +145,14 @@ common.api.directory = function (state) { | ||||
|   if (state._relays[state._relayUrl]) { | ||||
|     return PromiseA.resolve(state._relays[state._relayUrl]); | ||||
|   } | ||||
|   console.error('aaaaaaaaabsnthsnth'); | ||||
|   return common.requestAsync({ url: state._relayUrl + common.apiDirectory, json: true }).then(function (resp) { | ||||
|     console.error('123aaaaaaaaabsnthsnth'); | ||||
|     var dir = resp.body; | ||||
|     state._relays[state._relayUrl] = dir; | ||||
|     return dir; | ||||
|   }).catch(function (err) { | ||||
|     console.error('bsnthsnth'); | ||||
|     return PromiseA.reject(err); | ||||
|   }); | ||||
| }; | ||||
| @ -133,18 +163,18 @@ common.api._parseWss = function (state, dir) { | ||||
|   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).then(function (dir) { | ||||
|     cb(null, common.api._parseWss(state, dir)); | ||||
|   }).catch(cb); | ||||
| common.api.wss = function (state) { | ||||
|   return common.api.directory(state).then(function (dir) { | ||||
|     return common.api._parseWss(state, dir); | ||||
|   }); | ||||
| }; | ||||
| common.api.token = function (state, handlers) { | ||||
|   // directory, requested, connect, tunnelUrl, offer, granted, end
 | ||||
|   function afterDir(err, dir) { | ||||
|   function afterDir(dir) { | ||||
|     if (common.debug) { console.log('[debug] after dir'); } | ||||
|     state.wss = common.api._parseWss(state, dir); | ||||
| 
 | ||||
|     handlers.tunnelUrl(state.wss, function () { | ||||
|     return PromiseA.resolve(handlers.tunnelUrl(state.wss)).then(function () { | ||||
|       if (common.debug) { console.log('[debug] after tunnelUrl'); } | ||||
|       if (state.config.secret /* && !state.config.token */) { | ||||
|         state.config._token = common.signToken(state); | ||||
| @ -153,21 +183,19 @@ common.api.token = function (state, handlers) { | ||||
|       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 PromiseA.resolve(handlers.connect(state.token)).then(function () { | ||||
|           return PromiseA.resolve(handlers.end(null)); | ||||
|         }); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       // backwards compat (TODO remove)
 | ||||
|       if (err || !dir || !dir.pair_request) { | ||||
|       if (!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; | ||||
|         return PromiseA.resolve(handlers.error(err || new Error("No token found or generated, and no pair_request api found."))); | ||||
|       } | ||||
| 
 | ||||
|       // TODO sign token with own private key, including public key and thumbprint
 | ||||
|       //      (much like ACME JOSE account)
 | ||||
|       // TODO handle agree
 | ||||
|       var otp = state.config._otp; // common.otp();
 | ||||
|       var authReq = { | ||||
|         subject: state.config.email | ||||
| @ -187,8 +215,21 @@ common.api.token = function (state, handlers) { | ||||
|       */ | ||||
|       }; | ||||
|       var pairRequestUrl = new URL(dir.pair_request.pathname, 'https://' + dir.api_host.replace(/:hostname/g, state._relayHostname)); | ||||
|       console.log('pairRequestUrl:', pairRequestUrl); | ||||
|       //console.log('pairRequestUrl:', JSON.stringify(pairRequestUrl.toJSON()));
 | ||||
|       var req = { | ||||
|         url: pairRequestUrl | ||||
|         // WHATWG URL defines .toJSON() but, of course, it's not implemented
 | ||||
|         // because... why would we implement JavaScript objects in the DOM
 | ||||
|         // when we can have perfectly incompatible non-JS objects?
 | ||||
|         url: { | ||||
|           host: pairRequestUrl.host | ||||
|         , hostname: pairRequestUrl.hostname | ||||
|         , href: pairRequestUrl.href | ||||
|         , pathname: pairRequestUrl.pathname | ||||
|         , port: pairRequestUrl.port | ||||
|         , protocol: pairRequestUrl.protocol | ||||
|         , search: pairRequestUrl.search | ||||
|         } | ||||
|       , method: dir.pair_request.method | ||||
|       , json: authReq | ||||
|       }; | ||||
| @ -198,7 +239,7 @@ common.api.token = function (state, handlers) { | ||||
|       function gotoNext(req) { | ||||
|         if (common.debug) { console.log('[debug] gotoNext called'); } | ||||
|         if (common.debug) { console.log(req); } | ||||
|         common.requestAsync(req).then(function (resp) { | ||||
|         return common.requestAsync(req).then(function (resp) { | ||||
|           var body = resp.body; | ||||
| 
 | ||||
|           function checkLocation() { | ||||
| @ -207,86 +248,88 @@ common.api.token = function (state, handlers) { | ||||
|             // 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) { | ||||
|               return common.promiseTimeout(2 * 1000).then(function () { | ||||
|                 return gotoNext({ url: resp.headers.location, json: true }); | ||||
|               }); | ||||
|             } else 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 () { | ||||
|                 // falls through on purpose
 | ||||
|                 PromiseA.resolve(handlers.offer(body.access_token)).then(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 common.promiseTimeout(2 * 1000).then(function () { | ||||
|                 return gotoNext(req); | ||||
|               }); | ||||
|               return; | ||||
|             } | ||||
| 
 | ||||
|             } else if ('complete' === body.status) { | ||||
|               if (common.debug) { console.log('[debug] complete'); } | ||||
|               return PromiseA.resolve(handlers.granted(null)).then(function () { | ||||
|                 return PromiseA.resolve(handlers.end(null)).then(function () {}); | ||||
|               }); | ||||
|             } else { | ||||
|               if (common.debug) { console.log('[debug] bad status'); } | ||||
|               var err = new Error("Bad State:" + body.status); | ||||
|               err._request = req; | ||||
|             handlers.error(err, function () {}); | ||||
|               return PromiseA.resolve(handlers.error(err)); | ||||
|             } | ||||
|           } | ||||
| 
 | ||||
|           if (firstReq) { | ||||
|             if (common.debug) { console.log('[debug] first req'); } | ||||
|             handlers.requested(authReq, function () { | ||||
|               handlers.connect(body.access_token || body.jwt, function () { | ||||
|             if (!body.access_token && !body.jwt) { | ||||
|               return PromiseA.reject(new Error("something wrong with pre-authorization request")); | ||||
|             } | ||||
|             firstReq = false; | ||||
|             return PromiseA.resolve(handlers.requested(authReq)).then(function () { | ||||
|               return PromiseA.resolve(handlers.connect(body.access_token || body.jwt)).then(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; | ||||
|                   return PromiseA.resolve(handlers.error(err)).then(function () {}); | ||||
|                 } | ||||
|                 setTimeout(gotoNext, 2 * 1000, { url: resp.headers.location, json: true }); | ||||
|                 return common.promiseTimeout(2 * 1000).then(function () { | ||||
|                   return gotoNext({ url: resp.headers.location, json: true }); | ||||
|                 }); | ||||
|               }); | ||||
|             }); | ||||
|             firstReq = false; | ||||
|             return; | ||||
|           } else { | ||||
|             if (common.debug) { console.log('[debug] other req'); } | ||||
|             checkLocation(); | ||||
|             return 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 () {}); | ||||
|           return PromiseA.resolve(handlers.error(err)).then(function () {}); | ||||
|         }); | ||||
|       } | ||||
| 
 | ||||
|       gotoNext(req); | ||||
|       return gotoNext(req); | ||||
| 
 | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   // 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); | ||||
|   return common.api.directory(state).then(function (dir) { | ||||
|     console.log('[debug] [directory]', dir); | ||||
|     if (!dir.api_host) { dir = JSON.parse(failoverDir); } | ||||
|     return dir; | ||||
|   }).catch(function (err) { | ||||
|       return PromiseA.reject(err); | ||||
|     console.warn('[warn] [directory] fetch fail, using failover'); | ||||
|     console.warn(err); | ||||
|     return JSON.parse(failoverDir); | ||||
|   }).then(function (dir) { | ||||
|     return PromiseA.resolve(handlers.directory(dir)).then(function () { | ||||
|       console.log('[debug] [directory]', dir); | ||||
|       return afterDir(dir); | ||||
|     }); | ||||
|   }).catch(function (err) { | ||||
|     return afterDir(err, JSON.parse(failoverDir)); | ||||
|   }); | ||||
| 
 | ||||
| }; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user