wip trying to get auth request and token...
This commit is contained in:
		
							parent
							
								
									9e1c9c00ca
								
							
						
					
					
						commit
						e6b7ba575f
					
				@ -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) {
 | 
			
		||||
      appData.config = json;
 | 
			
		||||
      return 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) {
 | 
			
		||||
      appData.status = json;
 | 
			
		||||
      return 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,14 +49,13 @@ api.initialize = function apiInitialize() {
 | 
			
		||||
      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)));
 | 
			
		||||
    });
 | 
			
		||||
  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)));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -104,15 +104,27 @@ var appMethods = {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (-1 !== TELEBIT_RELAYS.indexOf(appData.init.relay)) {
 | 
			
		||||
        return api.initialize();
 | 
			
		||||
        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;
 | 
			
		||||
              return PromiseA.resolve(handlers.error(err));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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 () {
 | 
			
		||||
            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);
 | 
			
		||||
    }).catch(function (err) {
 | 
			
		||||
      return PromiseA.reject(err);
 | 
			
		||||
    });
 | 
			
		||||
  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 afterDir(err, JSON.parse(failoverDir));
 | 
			
		||||
    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);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user