From 693a524549d996175a97afc3ac8979a6a66465fa Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 28 Feb 2019 00:00:23 -0700 Subject: [PATCH] use eggspress for routing --- bin/telebitd.js | 645 ++++++++++++++++++++++------------------------ lib/eggspress.js | 78 ++++++ package-lock.json | 171 ------------ package.json | 1 - 4 files changed, 391 insertions(+), 504 deletions(-) create mode 100644 lib/eggspress.js diff --git a/bin/telebitd.js b/bin/telebitd.js index ccd32d8..7d7c4c5 100755 --- a/bin/telebitd.js +++ b/bin/telebitd.js @@ -28,6 +28,7 @@ var TPLS = TOML.parse(fs.readFileSync(path.join(__dirname, "../lib/en-us.toml"), var startTime = Date.now(); var connectTimes = []; var isConnected = false; +var eggspress = require('../lib/eggspress.js'); var TelebitRemote = require('../lib/daemon/index.js').TelebitRemote; @@ -112,7 +113,7 @@ function saveConfig(cb) { fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), cb); } var controllers = {}; -controllers.http = function (req, res, opts) { +controllers.http = function (req, res) { function getAppname(pathname) { // port number if (String(pathname) === String(parseInt(pathname, 10))) { @@ -138,7 +139,7 @@ controllers.http = function (req, res, opts) { } } - if (!opts.body) { + if (!req.body) { res.statusCode = 422; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({"error":{"message":"module \'http\' needs some arguments"}})); @@ -146,9 +147,9 @@ controllers.http = function (req, res, opts) { } var active = true; - var portOrPath = opts.body.handler || opts.body[0]; - var subdomain = opts.body.name || opts.body[1]; - var indexes = opts.body.indexes; + var portOrPath = req.body.handler || req.body[0]; + var subdomain = req.body.name || req.body[1]; + var indexes = req.body.indexes; var remoteHost; if (!portOrPath) { @@ -260,8 +261,8 @@ controllers.http = function (req, res, opts) { })); }); }; -controllers.tcp = function (req, res, opts) { - if (!opts.body) { +controllers.tcp = function (req, res) { + if (!req.body) { res.statusCode = 422; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ error: { message: "module 'tcp' needs more arguments" } })); @@ -269,8 +270,8 @@ controllers.tcp = function (req, res, opts) { } var active; - var remotePort = opts.body[1]; - var portOrPath = opts.body[0]; + var remotePort = req.body[1]; + var portOrPath = req.body[0]; // portnum if (remotePort) { @@ -309,8 +310,8 @@ controllers.tcp = function (req, res, opts) { })); }); }; -controllers.ssh = function (req, res, opts) { - if (!opts.body) { +controllers.ssh = function (req, res) { + if (!req.body) { res.statusCode = 422; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({"error":{"message":"module 'ssh' needs more arguments"}})); @@ -336,7 +337,7 @@ controllers.ssh = function (req, res, opts) { }); } - var rawSshAuto = opts.body.port || opts.body[0]; + var rawSshAuto = req.body.port || req.body[0]; var sshAuto = rawSshAuto; if (-1 !== [ -1, 'false', 'none', 'off', 'disable' ].indexOf(sshAuto)) { state.config.sshAuto = false; @@ -358,41 +359,64 @@ controllers.ssh = function (req, res, opts) { state.config.sshAuto = sshAuto; sshSuccess(); }; -controllers.relay = function (req, res, opts) { - if (!opts.body) { +controllers.relay = function (req, res) { + if (!req.body) { res.statusCode = 422; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({"error":{"message":"module \'relay\' needs more arguments"}})); return; } - return urequestAsync(opts.body).then(function (resp) { + return urequestAsync(req.body).then(function (resp) { res.setHeader('Content-Type', 'application/json'); resp = resp.toJSON(); res.end(JSON.stringify(resp)); }); }; -var serveStatic = require('serve-static')(path.join(__dirname, '../lib/admin/')); -function handleRemoteClient(req, res) { - if (/^\/(rpc|api)\//.test(req.url)) { - return handleApi(req, res); - } - serveStatic(req, res, require('finalhandler')(req, res)); -} -function handleApi(req, res) { - var opts = url.parse(req.url, true); - if (false && opts.query._body) { - try { - opts.body = JSON.parse(decodeURIComponent(opts.query._body, true)); - } catch(e) { - res.statusCode = 500; - res.end('{"error":{"message":"?_body={{bad_format}}"}}'); +function handleApi() { + var app = eggspress(); + + app.use('/', function (req, res, next) { + var opts = url.parse(req.url, true); + if (false && opts.query._body) { + try { + req.body = JSON.parse(decodeURIComponent(opts.query._body, true)); + } catch(e) { + res.statusCode = 500; + res.end('{"error":{"message":"?_body={{bad_format}}"}}'); + return; + } + } + + var hasLength = req.headers['content-length'] > 0; + if (!hasLength && !req.headers['content-type']) { + next(); return; } - } - function listSuccess() { + var body = ''; + req.on('readable', function () { + var data; + while (true) { + data = req.read(); + if (!data) { break; } + body += data.toString(); + } + }); + req.on('end', function () { + try { + req.body = JSON.parse(body); + } catch(e) { + res.statusCode = 400; + res.end('{"error":{"message":"POST body is not valid json"}}'); + return; + } + next(); + }); + }); + + function listSuccess(req, res) { var dumpy = { servernames: state.servernames , ports: state.ports @@ -411,7 +435,7 @@ function handleApi(req, res) { res.end(JSON.stringify(dumpy)); } - function getConfigOnly() { + function getConfigOnly(req, res) { var resp = JSON.parse(JSON.stringify(state.config)); resp.version = pkg.version; resp._otp = state.otp; @@ -422,7 +446,7 @@ function handleApi(req, res) { // // without proper config // - function saveAndReport() { + function saveAndReport(req, res) { console.log('[DEBUG] saveAndReport config write', confpath); console.log(YAML.safeDump(snakeCopy(state.config))); fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { @@ -438,337 +462,294 @@ function handleApi(req, res) { }); } - function initOrConfig() { - var conf = {}; - if (!opts.body) { - res.statusCode = 422; - res.end('{"error":{"message":"module \'init\' needs more arguments"}}'); - return; - } - - if (Array.isArray(opts.body)) { - // relay, email, agree_tos, servernames, ports - // - opts.body.forEach(function (opt) { - var parts = opt.split(/:/); - if ('true' === parts[1]) { - parts[1] = true; - } else if ('false' === parts[1]) { - parts[1] = false; - } else if ('null' === parts[1]) { - parts[1] = null; - } else if ('undefined' === parts[1]) { - parts[1] = undefined; - } - conf[parts[0]] = parts[1]; - }); - } else { - conf = opts.body; - } - - conf = camelCopy(conf); - - // TODO deep merge - // greenlock config - if (!state.config.greenlock) { state.config.greenlock = {}; } - if (conf.greenlock) { - if ('undefined' !== typeof conf.greenlock.agree) { - state.config.greenlock.agree = conf.greenlock.agree; + function initOrConfig(req, res) { + var conf = {}; + if (!req.body) { + res.statusCode = 422; + res.end('{"error":{"message":"module \'init\' needs more arguments"}}'); + return; } - if (conf.greenlock.server) { state.config.greenlock.server = conf.greenlock.server; } - if (conf.greenlock.version) { state.config.greenlock.version = conf.greenlock.version; } - } - // main config - if (conf.email) { state.config.email = conf.email; } - if (conf.relay) { state.config.relay = conf.relay; } - if (conf.token) { state.config.token = conf.token; } - if (conf.secret) { state.config.secret = conf.secret; } - if ('undefined' !== typeof conf.agreeTos) { - state.config.agreeTos = conf.agreeTos; - } + if (Array.isArray(req.body)) { + // relay, email, agree_tos, servernames, ports + // + req.body.forEach(function (opt) { + var parts = opt.split(/:/); + if ('true' === parts[1]) { + parts[1] = true; + } else if ('false' === parts[1]) { + parts[1] = false; + } else if ('null' === parts[1]) { + parts[1] = null; + } else if ('undefined' === parts[1]) { + parts[1] = undefined; + } + conf[parts[0]] = parts[1]; + }); + } else { + conf = req.body; + } - // to state - if (conf.pretoken) { state.pretoken = conf.pretoken; } - if (conf._otp) { - state.otp = conf._otp; // TODO should this only be done on the client side? - delete conf._otp; - } + conf = camelCopy(conf); - console.log(); - console.log('conf.token', typeof conf.token, conf.token); - console.log('state.config.token', typeof state.config.token, state.config.token); - - if (state.secret) { console.log('state.secret'); state.token = common.signToken(state); } - if (!state.token) { console.log('!state.token'); state.token = conf._token; } - - console.log(); - console.log('JSON.stringify(conf)'); - console.log(JSON.stringify(conf)); - console.log(); - console.log('JSON.stringify(state)'); - console.log(JSON.stringify(state)); - console.log(); - if ('undefined' !== typeof conf.newsletter) { - state.config.newsletter = conf.newsletter; - } - if ('undefined' !== typeof conf.communityMember - || 'undefined' !== typeof conf.community_member) { - state.config.communityMember = conf.communityMember || conf.community_member; - } - if ('undefined' !== typeof conf.telemetry) { - state.config.telemetry = conf.telemetry; - } - if (conf._servernames) { - (conf._servernames||'').split(/,/g).forEach(function (key) { - if (!state.config.servernames[key]) { - state.config.servernames[key] = { sub: undefined }; + // TODO deep merge + // greenlock config + if (!state.config.greenlock) { state.config.greenlock = {}; } + if (conf.greenlock) { + if ('undefined' !== typeof conf.greenlock.agree) { + state.config.greenlock.agree = conf.greenlock.agree; } - }); - } - if (conf._ports) { - (conf._ports||'').split(/,/g).forEach(function (key) { - if (!state.config.ports[key]) { - state.config.ports[key] = {}; - } - }); - } + if (conf.greenlock.server) { state.config.greenlock.server = conf.greenlock.server; } + if (conf.greenlock.version) { state.config.greenlock.version = conf.greenlock.version; } + } - if (!state.config.relay || !state.config.email || !state.config.agreeTos) { - console.warn('missing config'); - res.statusCode = 400; + // main config + if (conf.email) { state.config.email = conf.email; } + if (conf.relay) { state.config.relay = conf.relay; } + if (conf.token) { state.config.token = conf.token; } + if (conf.secret) { state.config.secret = conf.secret; } + if ('undefined' !== typeof conf.agreeTos) { + state.config.agreeTos = conf.agreeTos; + } - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify({ - error: { - code: "E_INIT" - , message: "Missing important config file params" - , _params: JSON.stringify(conf) - , _config: JSON.stringify(state.config) - , _body: JSON.stringify(opts.body) - } - })); - return; - } + // to state + if (conf.pretoken) { state.pretoken = conf.pretoken; } + if (conf._otp) { + state.otp = conf._otp; // TODO should this only be done on the client side? + delete conf._otp; + } - // init also means enable - delete state.config.disable; - safeStartTelebitRemote(true).then(saveAndReport).catch(handleError); - } + console.log(); + console.log('conf.token', typeof conf.token, conf.token); + console.log('state.config.token', typeof state.config.token, state.config.token); - function restart() { - console.info("[telebitd.js] server closing..."); - state.keepAlive.state = false; - if (myRemote) { - myRemote.end(); - myRemote.on('end', respondAndClose); - // failsafe - setTimeout(function () { - console.info("[telebitd.js] closing too slowly, force quit"); - respondAndClose(); - }, 5 * 1000); - } else { - respondAndClose(); - } + if (state.secret) { console.log('state.secret'); state.token = common.signToken(state); } + if (!state.token) { console.log('!state.token'); state.token = conf._token; } - function respondAndClose() { - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify({ success: true })); - controlServer.close(function () { - console.info("[telebitd.js] server closed"); - setTimeout(function () { - // system daemon will restart the process - process.exit(22); // use non-success exit code - }, 100); - }); - } - } + console.log(); + console.log('JSON.stringify(conf)'); + console.log(JSON.stringify(conf)); + console.log(); + console.log('JSON.stringify(state)'); + console.log(JSON.stringify(state)); + console.log(); + if ('undefined' !== typeof conf.newsletter) { + state.config.newsletter = conf.newsletter; + } + if ('undefined' !== typeof conf.communityMember + || 'undefined' !== typeof conf.community_member) { + state.config.communityMember = conf.communityMember || conf.community_member; + } + if ('undefined' !== typeof conf.telemetry) { + state.config.telemetry = conf.telemetry; + } + if (conf._servernames) { + (conf._servernames||'').split(/,/g).forEach(function (key) { + if (!state.config.servernames[key]) { + state.config.servernames[key] = { sub: undefined }; + } + }); + } + if (conf._ports) { + (conf._ports||'').split(/,/g).forEach(function (key) { + if (!state.config.ports[key]) { + state.config.ports[key] = {}; + } + }); + } - function invalidConfig() { - res.statusCode = 400; - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify({ - error: { code: "E_CONFIG", message: "Invalid config file. Please run 'telebit init'" } - })); - } + if (!state.config.relay || !state.config.email || !state.config.agreeTos) { + console.warn('missing config'); + res.statusCode = 400; - function saveAndCommit() { - state.config.servernames = state.servernames; - state.config.ports = state.ports; - fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { - if (err) { - res.statusCode = 500; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ - "error":{"message":"Could not save config file. Perhaps you're not running as root?"} + error: { + code: "E_INIT" + , message: "Missing important config file params" + , _params: JSON.stringify(conf) + , _config: JSON.stringify(state.config) + , _body: JSON.stringify(req.body) + } })); return; } - listSuccess(); - }); - } - function handleError(err) { - res.statusCode = 500; - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify({ - error: { message: err.message, code: err.code } - })); - } + // init also means enable + delete state.config.disable; + safeStartTelebitRemote(true).then(saveAndReport).catch(handleError); + } - function enable() { - delete state.config.disable;// = undefined; - state.keepAlive.state = true; - - fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { - if (err) { - err.message = "Could not save config file. Perhaps you're user doesn't have permission?"; - handleError(err); - return; - } - // TODO XXX myRemote.active + function restart(req, res) { + console.info("[telebitd.js] server closing..."); + state.keepAlive.state = false; if (myRemote) { - listSuccess(); + myRemote.end(); + myRemote.on('end', respondAndClose); + // failsafe + setTimeout(function () { + console.info("[telebitd.js] closing too slowly, force quit"); + respondAndClose(); + }, 5 * 1000); + } else { + respondAndClose(); + } + + function respondAndClose() { + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({ success: true })); + controlServer.close(function () { + console.info("[telebitd.js] server closed"); + setTimeout(function () { + // system daemon will restart the process + process.exit(22); // use non-success exit code + }, 100); + }); + } + } + + function mustHaveValidConfig(req, res, next) { + // + // Check for proper config + // + if (state.config.relay && state.config.email && state.config.agreeTos) { + next(); return; } - safeStartTelebitRemote(true).then(listSuccess).catch(handleError); - }); - } - function disable() { - state.config.disable = true; - state.keepAlive.state = false; - - if (myRemote) { myRemote.end(); myRemote = null; } - fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { + res.statusCode = 400; res.setHeader('Content-Type', 'application/json'); - if (err) { - err.message = "Could not save config file. Perhaps you're user doesn't have permission?"; - handleError(err); - return; - } - res.end('{"success":true}'); - }); - } + res.end(JSON.stringify({ + error: { code: "E_CONFIG", message: "Invalid config file. Please run 'telebit init'" } + })); + } - function getStatus() { - var now = Date.now(); - res.setHeader('Content-Type', 'application/json'); - require('../lib/ssh.js').checkSecurity().then(function (ssh) { - res.end(JSON.stringify( - { module: 'status' - , version: pkg.version - , 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) - , uptime: now - startTime - , runtime: isConnected && connectTimes.length && (now - connectTimes[0]) || 0 - , reconnects: connectTimes.length - , servernames: state.servernames - , ssh: state.config.sshAuto - , ssh_permit_root_login: ssh.permit_root_login - , ssh_password_authentication: ssh.password_authentication - , ssh_requests_password: ssh.requests_password + function saveAndCommit(req, res) { + state.config.servernames = state.servernames; + state.config.ports = state.ports; + fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { + if (err) { + res.statusCode = 500; + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({ + "error":{"message":"Could not save config file. Perhaps you're not running as root?"} + })); + return; } - )); - }); - } - - function route() { - if (/\b(relay)\b/.test(opts.pathname)) { - controllers.relay(req, res, opts); - return; - } - if (/\b(config)\b/.test(opts.pathname) && /get/i.test(req.method)) { - getConfigOnly(); - return; - } - if (/\b(init|config)\b/.test(opts.pathname)) { - initOrConfig(); - return; - } - if (/restart/.test(opts.pathname)) { - restart(); - return; - } - // - // Check for proper config - // - if (!state.config.relay || !state.config.email || !state.config.agreeTos) { - invalidConfig(); - return; - } - // - // With proper config - // - if (/http/.test(opts.pathname)) { - controllers.http(req, res, opts); - return; - } - if (/tcp/.test(opts.pathname)) { - controllers.tcp(req, res, opts); - return; - } - if (/save|commit/.test(opts.pathname)) { - saveAndCommit(); - return; - } - if (/ssh/.test(opts.pathname)) { - controllers.ssh(req, res, opts); - return; - } - if (/enable/.test(opts.pathname)) { - enable(); - return; - } - if (/disable/.test(opts.pathname)) { - disable(); - return; - } - if (/status/.test(opts.pathname)) { - getStatus(); - return; - } - if (/list/.test(opts.pathname)) { - listSuccess(); - return; + listSuccess(); + }); } + function handleError(err, req, res) { + res.statusCode = 500; + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify({ + error: { message: err.message, code: err.code } + })); + } + + function enable(req, res) { + delete state.config.disable;// = undefined; + state.keepAlive.state = true; + + fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { + if (err) { + err.message = "Could not save config file. Perhaps you're user doesn't have permission?"; + handleError(err, req, res); + return; + } + // TODO XXX myRemote.active + if (myRemote) { + listSuccess(req, res); + return; + } + safeStartTelebitRemote(true).then(listSuccess).catch(function () { + handleError(err, req, res); + }); + }); + } + + function disable(req, res) { + state.config.disable = true; + state.keepAlive.state = false; + + if (myRemote) { myRemote.end(); myRemote = null; } + fs.writeFile(confpath, YAML.safeDump(snakeCopy(state.config)), function (err) { + res.setHeader('Content-Type', 'application/json'); + if (err) { + err.message = "Could not save config file. Perhaps you're user doesn't have permission?"; + handleError(err); + return; + } + res.end('{"success":true}'); + }); + } + + function getStatus(req, res) { + var now = Date.now(); + res.setHeader('Content-Type', 'application/json'); + require('../lib/ssh.js').checkSecurity().then(function (ssh) { + res.end(JSON.stringify( + { module: 'status' + , version: pkg.version + , 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) + , uptime: now - startTime + , runtime: isConnected && connectTimes.length && (now - connectTimes[0]) || 0 + , reconnects: connectTimes.length + , servernames: state.servernames + , ssh: state.config.sshAuto + , ssh_permit_root_login: ssh.permit_root_login + , ssh_password_authentication: ssh.password_authentication + , ssh_requests_password: ssh.requests_password + } + )); + }); + } + + // TODO turn strings into regexes to match beginnings + app.use(/\b(relay)\b/, controllers.relay); + app.get(/\b(config)\b/, getConfigOnly); + app.use(/\b(init|config)\b/, initOrConfig); + app.use(/\b(restart)\b/, restart); + + // Position is important with eggspress + // This should stay here, right before the other methods + app.use('/', mustHaveValidConfig); + + // + // With proper config + // + app.use(/\b(http)\b/, controllers.http); + app.use(/\b(tcp)\b/, controllers.tcp); + app.use(/\b(save|commit)\b/, saveAndCommit); + app.use(/\b(ssh)\b/, controllers.ssh); + app.use(/\b(enable)\b/, enable); + app.use(/\b(disable)\b/, disable); + app.use(/\b(status)\b/, getStatus); + app.use(/\b(list)\b/, listSuccess); + app.use('/', function (req, res) { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({"error":{"message":"unrecognized rpc"}})); - } - - var hasLength = req.headers['content-length'] > 0; - if (!hasLength && !req.headers['content-type']) { - route(); - return; - } - - var body = ''; - req.on('readable', function () { - var data; - while (true) { - data = req.read(); - if (!data) { break; } - body += data.toString(); - } - }); - req.on('end', function () { - try { - opts.body = JSON.parse(body); - } catch(e) { - res.statusCode = 400; - res.end('{"error":{"message":"POST body is not valid json"}}'); - return; - } - route(); }); + + return app; } + function serveControlsHelper() { - controlServer = http.createServer(handleRemoteClient); + var app = eggspress(); + var serveStatic = require('serve-static')(path.join(__dirname, '../lib/admin/')); + var apiHandler = handleApi(); + + app.use('/rpc/', apiHandler); + app.use('/api/', apiHandler); + app.use('/', serveStatic); + + controlServer = http.createServer(app); if (fs.existsSync(state._ipc.path)) { fs.unlinkSync(state._ipc.path); diff --git a/lib/eggspress.js b/lib/eggspress.js new file mode 100644 index 0000000..0979a29 --- /dev/null +++ b/lib/eggspress.js @@ -0,0 +1,78 @@ +'use strict'; + +module.exports = function eggspress() { + //var patternsMap = {}; + var allPatterns = []; + var app = function (req, res) { + var patterns = allPatterns.slice(0).reverse(); + function next() { + var todo = patterns.pop(); + if (!todo) { + console.log('[eggspress] Did not match any patterns', req.url); + require('finalhandler')(req, res)(); + return; + } + + // '', GET, POST, DELETE + if (todo[2] && req.method.toLowerCase() !== todo[2]) { + //console.log("[eggspress] HTTP method doesn't match", req.url); + next(); + return; + } + + if (!req.url.match(todo[0])) { + //console.log("[eggspress] pattern doesn't match", todo[0], req.url); + next(); + return; + } else if ('string' === typeof todo[0] && 0 !== req.url.match(todo[0]).index) { + //console.log("[eggspress] string pattern is not the start", todo[0], req.url); + next(); + return; + } + + try { + //console.log("[eggspress] matched pattern", todo[0], req.url); + todo[1](req, res, next); + } catch(e) { + console.error("[eggspress] error", todo[2], todo[0], req.url); + console.error(e); + // TODO make a nice error message + res.end(e.message); + return; + } + } + next(); + }; + + app.use = function (pattern, fn) { + return app._use('', pattern, fn); + }; + [ 'GET', 'POST', 'DELETE' ].forEach(function (method) { + app[method.toLowerCase()] = function (pattern, fn) { + return app._use(method, pattern, fn); + }; + }); + + app.post = function (pattern, fn) { + return app._use('POST', pattern, fn); + }; + app._use = function (method, pattern, fn) { + // always end in a slash, for now + if ('string' === typeof pattern) { + pattern = pattern.replace(/\/$/, '') + '/'; + } + /* + if (!patternsMap[pattern]) { + patternsMap[pattern] = []; + } + patternsMap[pattern].push(fn); + patterns = Object.keys(patternsMap).sort(function (a, b) { + return b.length - a.length; + }); + */ + allPatterns.push([pattern, fn, method.toLowerCase()]); + return app; + }; + + return app; +}; diff --git a/package-lock.json b/package-lock.json index 851faff..58e2c53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,11 +49,6 @@ "sprintf-js": "~1.0.2" } }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, "asn1js": { "version": "1.2.12", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-1.2.12.tgz", @@ -74,23 +69,6 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, - "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" - } - }, "buffer-alloc": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", @@ -125,11 +103,6 @@ "resolved": "https://registry.npmjs.org/buffer-v6-polyfill/-/buffer-v6-polyfill-1.0.5.tgz", "integrity": "sha1-0c2v61YAvvbCjWFElkJWvRLgFnc=" }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, "certpem": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/certpem/-/certpem-1.0.1.tgz", @@ -141,26 +114,6 @@ "pkijs": "^1.3.27" } }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -238,43 +191,6 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, - "express": { - "version": "4.16.4", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", - "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", - "requires": { - "accepts": "~1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.3", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.4", - "qs": "6.5.2", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.2", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, "finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -289,11 +205,6 @@ "unpipe": "~1.0.0" } }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -374,14 +285,6 @@ "statuses": ">= 1.4.0 < 2" } }, - "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -395,11 +298,6 @@ "from2": "^2.1.1" } }, - "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" - }, "is": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", @@ -525,21 +423,6 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, "mime": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", @@ -659,11 +542,6 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -692,15 +570,6 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, - "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" - } - }, "proxy-packer": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proxy-packer/-/proxy-packer-2.0.2.tgz", @@ -723,11 +592,6 @@ "safe-replace": "^1.0.2" } }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, "range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", @@ -738,17 +602,6 @@ "resolved": "https://registry.npmjs.org/rasha/-/rasha-1.2.1.tgz", "integrity": "sha512-cs4Hu/rVF3/Qucq+V7lxSz449VfHNMVXJaeajAHno9H5FC1PWlmS4NM6IAX5jPKFF0IC2rOdHdf7iNxQuIWZag==" }, - "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", - "unpipe": "1.0.0" - } - }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -801,11 +654,6 @@ "resolved": "https://registry.npmjs.org/safe-replace/-/safe-replace-1.0.2.tgz", "integrity": "sha1-sYGrQJWs32qwqPhAUXSRyoqZIro=" }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, "sclient": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sclient/-/sclient-1.4.1.tgz", @@ -985,15 +833,6 @@ "hoek": "2.x.x" } }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1004,16 +843,6 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, "ws": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/ws/-/ws-6.0.0.tgz", diff --git a/package.json b/package.json index cb08ef7..e8dcfaf 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "homepage": "https://git.coolaj86.com/coolaj86/telebit.js#readme", "dependencies": { "@coolaj86/urequest": "^1.3.5", - "express": "^4.16.4", "finalhandler": "^1.1.1", "greenlock": "^2.3.1", "js-yaml": "^3.11.0",