load apps from DB
This commit is contained in:
		
							parent
							
								
									1bb21f5a07
								
							
						
					
					
						commit
						65645a7602
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -6,6 +6,8 @@ dyndns-token.js | |||||||
| vhosts | vhosts | ||||||
| certs | certs | ||||||
| .*.sw* | .*.sw* | ||||||
|  | packages | ||||||
|  | var | ||||||
| 
 | 
 | ||||||
| # Logs | # Logs | ||||||
| logs | logs | ||||||
|  | |||||||
							
								
								
									
										133
									
								
								lib/api-server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								lib/api-server.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,133 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | // TODO handle static app urls?
 | ||||||
|  | // NOTE rejecting non-api urls should happen before this
 | ||||||
|  | module.exports.create = function (conf, deps, app) { | ||||||
|  |   var escapeStringRegexp = require('escape-string-regexp'); | ||||||
|  |   var vhostsMap = conf.vhostsMap; | ||||||
|  |   if (!app) { | ||||||
|  |     app = deps.app; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function getApi(route) { | ||||||
|  |     // TODO don't modify route, modify some other variable instead
 | ||||||
|  | 
 | ||||||
|  |     var PromiseA = require('bluebird'); | ||||||
|  |     var path = require('path'); | ||||||
|  |     console.log(route); | ||||||
|  |     // TODO needs some version stuff (which would also allow hot-loading of updates)
 | ||||||
|  |     // TODO version could be tied to sha256sum
 | ||||||
|  |     var pkgpath = path.join(conf.apipath, (route.api.package || route.api.id), (route.api.version || '')); | ||||||
|  | 
 | ||||||
|  |     console.log('pkgpath'); | ||||||
|  |     console.log(pkgpath); | ||||||
|  | 
 | ||||||
|  |     return new PromiseA(function (resolve, reject) { | ||||||
|  |       try { | ||||||
|  |         route.route = require(pkgpath).create(conf, deps.app, app); | ||||||
|  |       } catch(e) { | ||||||
|  |         reject(e); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       resolve(route.route); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function api(req, res, next) { | ||||||
|  |     var apps;  | ||||||
|  | 
 | ||||||
|  |     console.log('hostname', req.hostname); | ||||||
|  |     console.log('headers', req.headers); | ||||||
|  | 
 | ||||||
|  |     if (!vhostsMap[req.hostname]) { | ||||||
|  |       // TODO keep track of match-only vhosts, such as '*.example.com',
 | ||||||
|  |       // separate from exact matches
 | ||||||
|  |       next(new Error("this domain is not registered")); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     vhostsMap[req.hostname].pathnames.some(function (route) { | ||||||
|  |       var pathname = route.pathname; | ||||||
|  |       if ('/' === pathname) { | ||||||
|  |         pathname = '/api'; | ||||||
|  |       } | ||||||
|  |       if (-1 === pathname.indexOf('/api')) { | ||||||
|  |         pathname = '/api' + pathname; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (!route.re) { | ||||||
|  |         route.re = new RegExp(escapeStringRegexp(pathname) + '(#|\\/|\\?|$)'); | ||||||
|  |       } | ||||||
|  |       // re.test("/api")
 | ||||||
|  |       // re.test("/api?")
 | ||||||
|  |       // re.test("/api/")
 | ||||||
|  |       // re.test("/api/foo")
 | ||||||
|  |       // re.test("/apifoo") // false
 | ||||||
|  |       if (route.re.test(req.url)) { | ||||||
|  |         apps = route.apps; | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     if (!apps) { | ||||||
|  |       console.log('No apps to try for this hostname'); | ||||||
|  |       console.log(vhostsMap[req.hostname]); | ||||||
|  |       next(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     //console.log(apps);
 | ||||||
|  | 
 | ||||||
|  |     function nextify(err) { | ||||||
|  |       var route; | ||||||
|  | 
 | ||||||
|  |       if (err) { | ||||||
|  |         next(err); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |        | ||||||
|  |       // shortest to longest
 | ||||||
|  |       //route = apps.pop();
 | ||||||
|  |       // longest to shortest
 | ||||||
|  |       route = apps.shift(); | ||||||
|  |       if (!route) { | ||||||
|  |         next(); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (route.route) { | ||||||
|  |         route.route(req, res, nextify); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |      if (route._errored) { | ||||||
|  |         nextify(new Error("couldn't load api")); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (!route.api) { | ||||||
|  |         nextify(new Error("no api available for this route")); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       getApi(route).then(function (route) { | ||||||
|  |         try { | ||||||
|  |           route(req, res, nextify); | ||||||
|  |           route.route = route;  | ||||||
|  |         } catch(e) { | ||||||
|  |           route._errored = true; | ||||||
|  |           console.error('[App Load Error]'); | ||||||
|  |           console.error(e.stack); | ||||||
|  |           nextify(new Error("couldn't load api")); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     nextify(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     api: api | ||||||
|  |   }; | ||||||
|  | }; | ||||||
| @ -12,8 +12,12 @@ module.exports.create = function (securePort, insecurePort, redirects) { | |||||||
|     var host = req.headers.host || ''; |     var host = req.headers.host || ''; | ||||||
|     var url = req.url; |     var url = req.url; | ||||||
| 
 | 
 | ||||||
|  |     if (require('./unbrick-appcache').unbrick(req, res)) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // because I have domains for which I don't want to pay for SSL certs
 |     // because I have domains for which I don't want to pay for SSL certs
 | ||||||
|     insecureRedirects = redirects.sort(function (a, b) { |     insecureRedirects = (redirects||[]).sort(function (a, b) { | ||||||
|       var hlen = b.from.hostname.length - a.from.hostname.length; |       var hlen = b.from.hostname.length - a.from.hostname.length; | ||||||
|       var plen; |       var plen; | ||||||
|       if (!hlen) { |       if (!hlen) { | ||||||
|  | |||||||
| @ -13,12 +13,20 @@ module.exports.create = function (certPaths, port, serverCallback) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     server.on('error', serverCallback); |     server.on('error', serverCallback); | ||||||
|     server.listen(port, function () { |     server.listen(port, '0.0.0.0', function () { | ||||||
|       // is it even theoritically possible for
 |       // is it even theoritically possible for
 | ||||||
|       // a request to come in before this callback has fired?
 |       // a request to come in before this callback has fired?
 | ||||||
|       // I'm assuming this event must fire before any request event
 |       // I'm assuming this event must fire before any request event
 | ||||||
|       promiseApp = serverCallback(null, server); |       promiseApp = serverCallback(null, server); | ||||||
|     }); |     }); | ||||||
|  |     /* | ||||||
|  |     server.listen(port, '::::', function () { | ||||||
|  |       // is it even theoritically possible for
 | ||||||
|  |       // a request to come in before this callback has fired?
 | ||||||
|  |       // I'm assuming this event must fire before any request event
 | ||||||
|  |       promiseApp = serverCallback(null, server); | ||||||
|  |     }); | ||||||
|  |     */ | ||||||
| 
 | 
 | ||||||
|     // Get up and listening as absolutely quickly as possible
 |     // Get up and listening as absolutely quickly as possible
 | ||||||
|     server.on('request', function (req, res) { |     server.on('request', function (req, res) { | ||||||
| @ -29,6 +37,7 @@ module.exports.create = function (certPaths, port, serverCallback) { | |||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       promiseApp.then(function (_app) { |       promiseApp.then(function (_app) { | ||||||
|  |         console.log('[Server]', req.method, req.host || req.headers['x-forwarded-host'] || req.headers.host, req.url); | ||||||
|         app = _app; |         app = _app; | ||||||
|         app(req, res); |         app(req, res); | ||||||
|       }); |       }); | ||||||
|  | |||||||
							
								
								
									
										27
									
								
								lib/no-www.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								lib/no-www.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,27 @@ | |||||||
|  | module.exports.scrubTheDub = function (req, res) { | ||||||
|  |   // hack for bricked app-cache
 | ||||||
|  |   // Also 301 redirects will not work for appcache (must issue html)
 | ||||||
|  |   if (require('./unbrick-appcache').unbrick(req, res)) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // TODO port number for non-443
 | ||||||
|  |   var escapeHtml = require('escape-html'); | ||||||
|  |   var newLocation = 'https://' + req.hostname.replace(/^www\./, '') + req.url; | ||||||
|  |   var safeLocation = escapeHtml(newLocation); | ||||||
|  | 
 | ||||||
|  |   var metaRedirect = '' | ||||||
|  |     + '<html>\n' | ||||||
|  |     + '<head>\n' | ||||||
|  |     + '  <style>* { background-color: white; color: white; text-decoration: none; }</style>\n' | ||||||
|  |     + '  <META http-equiv="refresh" content="0;URL=' + safeLocation + '">\n' | ||||||
|  |     + '</head>\n' | ||||||
|  |     + '<body style="display: none;">\n' | ||||||
|  |     + '  <p>You requested an old resource. Please use this instead: \n' | ||||||
|  |     + '    <a href="' + safeLocation + '">' + safeLocation + '</a></p>\n' | ||||||
|  |     + '</body>\n' | ||||||
|  |     + '</html>\n' | ||||||
|  |     ; | ||||||
|  | 
 | ||||||
|  |   res.end(metaRedirect); | ||||||
|  | }; | ||||||
| @ -1,5 +1,7 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
|  | var getDomainInfo = require('../lib/utils').getDomainInfo; | ||||||
|  | 
 | ||||||
| function deserialize(results) { | function deserialize(results) { | ||||||
|   var config = { apis: {}, apps: {}, domains: {} }; |   var config = { apis: {}, apps: {}, domains: {} }; | ||||||
|   results.apis.forEach(function (api) { |   results.apis.forEach(function (api) { | ||||||
| @ -70,7 +72,62 @@ function deserialize(results) { | |||||||
|   return config; |   return config; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | function getVhostsMap(config) { | ||||||
|  |   var vhosts = []; | ||||||
|  |   var vhostsMap = {}; | ||||||
|  | 
 | ||||||
|  |   function sortApps(a, b) { | ||||||
|  |     // hlen isn't important in this current use of the sorter,
 | ||||||
|  |     // but is important for an alternate version
 | ||||||
|  |     var hlen = b.hostname.length - a.hostname.length; | ||||||
|  |     var plen = b.pathname.length - a.pathname.length; | ||||||
|  | 
 | ||||||
|  |     // A directory could be named example.com, example.com# example.com##
 | ||||||
|  |     // to indicate order of preference (for API addons, for example)
 | ||||||
|  |     var dlen = (b.priority || b.dirname.length) - (a.priority || a.dirname.length); | ||||||
|  | 
 | ||||||
|  |     if (!hlen) { | ||||||
|  |       if (!plen) { | ||||||
|  |         return dlen; | ||||||
|  |       } | ||||||
|  |       return plen; | ||||||
|  |     } | ||||||
|  |     return hlen; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Object.keys(config.domains).forEach(function (domainname) { | ||||||
|  |     var domain = config.domains[domainname]; | ||||||
|  |     var info = getDomainInfo(domainname); | ||||||
|  | 
 | ||||||
|  |     domain.hostname = info.hostname; | ||||||
|  |     domain.pathname = '/' + (info.pathname || ''); | ||||||
|  |     domain.dirname = info.dirname; | ||||||
|  | 
 | ||||||
|  |     vhosts.push(domain); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   vhosts.sort(sortApps); | ||||||
|  | 
 | ||||||
|  |   vhosts.forEach(function (domain) { | ||||||
|  |     console.log(domain.hostname, domain.pathname, domain.dirname); | ||||||
|  | 
 | ||||||
|  |     if (!vhostsMap[domain.hostname]) { | ||||||
|  |       vhostsMap[domain.hostname] = { pathnamesMap: {}, pathnames: [] }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!vhostsMap[domain.hostname].pathnamesMap[domain.pathname]) { | ||||||
|  |       vhostsMap[domain.hostname].pathnamesMap[domain.pathname] = { pathname: domain.pathname, apps: [] }; | ||||||
|  |       vhostsMap[domain.hostname].pathnames.push(vhostsMap[domain.hostname].pathnamesMap[domain.pathname]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     vhostsMap[domain.hostname].pathnamesMap[domain.pathname].apps.push(domain); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   return vhostsMap; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| module.exports.deserialize = deserialize; | module.exports.deserialize = deserialize; | ||||||
|  | module.exports.getVhostsMap = getVhostsMap; | ||||||
| module.exports.create = function (db) { | module.exports.create = function (db) { | ||||||
|   console.log('[DB -1]'); |   console.log('[DB -1]'); | ||||||
|   var wrap = require('dbwrap'); |   var wrap = require('dbwrap'); | ||||||
| @ -162,9 +219,7 @@ module.exports.create = function (db) { | |||||||
|           // create fixture with which to test
 |           // create fixture with which to test
 | ||||||
|           // console.log(JSON.stringify(results));
 |           // console.log(JSON.stringify(results));
 | ||||||
| 
 | 
 | ||||||
|           var config = deserialize(results); |           return getVhostsMap(deserialize(results)); | ||||||
|            |  | ||||||
|           return config; |  | ||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
|     }; |     }; | ||||||
|  | |||||||
| @ -20,8 +20,12 @@ function tplCaddyfile(conf) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     content +=  |     content +=  | ||||||
|       "  proxy /api http://localhost:" + conf.localPort.toString() + "\n" |       "  proxy /api http://localhost:" + conf.localPort.toString() + " {\n" | ||||||
|  |     + "    proxy_header Host {host}\n" | ||||||
|  |     + "    proxy_header X-Forwarded-Host {host}\n" | ||||||
|  |     + "    proxy_header X-Forwarded-Proto {scheme}\n" | ||||||
|       // # TODO internal
 |       // # TODO internal
 | ||||||
|  |     + "  }\n" | ||||||
|     + "}"; |     + "}"; | ||||||
| 
 | 
 | ||||||
|     contents.push(content); |     contents.push(content); | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								lib/unbrick-appcache.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								lib/unbrick-appcache.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | module.exports.unbrick = function (req, res) { | ||||||
|  |   // hack for bricked app-cache
 | ||||||
|  |   if (/\.(appcache|manifest)\b/.test(req.url)) { | ||||||
|  |     res.setHeader('Content-Type', 'text/cache-manifest'); | ||||||
|  |     res.end('CACHE MANIFEST\n\n# v0__DELETE__CACHE__MANIFEST__\n\nNETWORK:\n*'); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return false; | ||||||
|  | }; | ||||||
							
								
								
									
										165
									
								
								lib/worker.js
									
									
									
									
									
								
							
							
						
						
									
										165
									
								
								lib/worker.js
									
									
									
									
									
								
							| @ -7,8 +7,9 @@ module.exports.create = function (webserver, info, state) { | |||||||
| 
 | 
 | ||||||
|   var PromiseA = state.Promise || require('bluebird'); |   var PromiseA = state.Promise || require('bluebird'); | ||||||
|   var path = require('path'); |   var path = require('path'); | ||||||
|   var vhostsdir = path.join(__dirname, 'vhosts'); |   //var vhostsdir = path.join(__dirname, 'vhosts');
 | ||||||
|   var app = require('express')(); |   var express = require('express-lazy'); | ||||||
|  |   var app = express(); | ||||||
|   var apiHandler; |   var apiHandler; | ||||||
|   var memstore; |   var memstore; | ||||||
|   var sqlstores = {}; |   var sqlstores = {}; | ||||||
| @ -58,89 +59,29 @@ module.exports.create = function (webserver, info, state) { | |||||||
|   } |   } | ||||||
|   */ |   */ | ||||||
| 
 | 
 | ||||||
|   function scrubTheDubHelper(req, res/*, next*/) { |  | ||||||
|     // hack for bricked app-cache
 |  | ||||||
|     if (/\.appcache\b/.test(req.url)) { |  | ||||||
|       res.setHeader('Content-Type', 'text/cache-manifest'); |  | ||||||
|       res.end('CACHE MANIFEST\n\n# v0__DELETE__CACHE__MANIFEST__\n\nNETWORK:\n*'); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // TODO port number for non-443
 |  | ||||||
|     var escapeHtml = require('escape-html'); |  | ||||||
|     var newLocation = 'https://' + req.hostname.replace(/^www\./, '') + req.url; |  | ||||||
|     var safeLocation = escapeHtml(newLocation); |  | ||||||
| 
 |  | ||||||
|     var metaRedirect = '' |  | ||||||
|       + '<html>\n' |  | ||||||
|       + '<head>\n' |  | ||||||
|       + '  <style>* { background-color: white; color: white; text-decoration: none; }</style>\n' |  | ||||||
|       + '  <META http-equiv="refresh" content="0;URL=' + safeLocation + '">\n' |  | ||||||
|       + '</head>\n' |  | ||||||
|       + '<body style="display: none;">\n' |  | ||||||
|       + '  <p>You requested an old resource. Please use this instead: \n' |  | ||||||
|       + '    <a href="' + safeLocation + '">' + safeLocation + '</a></p>\n' |  | ||||||
|       + '</body>\n' |  | ||||||
|       + '</html>\n' |  | ||||||
|       ; |  | ||||||
| 
 |  | ||||||
|     // 301 redirects will not work for appcache
 |  | ||||||
|     res.end(metaRedirect); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   // TODO handle insecure to actual redirect
 |   // TODO handle insecure to actual redirect
 | ||||||
|   // blog.coolaj86.com -> coolaj86.com/blog
 |   // blog.coolaj86.com -> coolaj86.com/blog
 | ||||||
|   // hmm... that won't really matter with hsts
 |   // hmm... that won't really matter with hsts
 | ||||||
|   // I guess I just needs letsencrypt
 |   // I guess I just needs letsencrypt
 | ||||||
| 
 | 
 | ||||||
|   function scrubTheDub(req, res, next) { |   function scrubTheDub(req, res, next) { | ||||||
|  |     console.log('[no-www]', req.method, req.url); | ||||||
|     var host = req.hostname; |     var host = req.hostname; | ||||||
| 
 | 
 | ||||||
|     if (!host || 'string' !== typeof host) { |     if (!host || 'string' !== typeof host) { | ||||||
|       next(); |       next(); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     // TODO test if this is even necessary
 | ||||||
|     host = host.toLowerCase(); |     host = host.toLowerCase(); | ||||||
| 
 | 
 | ||||||
|     if (/^www\./.test(host)) { |     if (!/^www\./.test(host)) { | ||||||
|       scrubTheDubHelper(req, res, next); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   function handleApi(req, res, next) { |  | ||||||
|     if (!/^\/api/.test(req.url)) { |  | ||||||
|       next(); |       next(); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // TODO move to caddy parser?
 |     require('./no-www').scrubTheDub(req, res); | ||||||
|     if (/(^|\.)proxyable\./.test(req.hostname)) { |  | ||||||
|       // device-id-12345678.proxyable.myapp.mydomain.com => myapp.mydomain.com
 |  | ||||||
|       // proxyable.myapp.mydomain.com => myapp.mydomain.com
 |  | ||||||
|       // TODO myapp.mydomain.com.daplieproxyable.com => myapp.mydomain.com
 |  | ||||||
|       req.hostname = req.hostname.replace(/.*\.?proxyable\./, ''); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (apiHandler) { |  | ||||||
|       if (apiHandler.then) { |  | ||||||
|         apiHandler.then(function (app) { |  | ||||||
|           app(req, res, next); |  | ||||||
|         }); |  | ||||||
|         return; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       apiHandler(req, res, next); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     apiHandler = require('./vhost-server').create(info.localPort, vhostsdir).create(webserver, app).then(function (app) { |  | ||||||
|       // X-Forwarded-For
 |  | ||||||
|       // X-Forwarded-Proto
 |  | ||||||
|       console.log('api server', req.hostname, req.secure, req.ip); |  | ||||||
|       apiHandler = app; |  | ||||||
|       app(req, res, next); |  | ||||||
|     }); |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (info.trustProxy) { |   if (info.trustProxy) { | ||||||
| @ -148,7 +89,6 @@ module.exports.create = function (webserver, info, state) { | |||||||
|     //app.set('trust proxy', function (ip) { ... });
 |     //app.set('trust proxy', function (ip) { ... });
 | ||||||
|   } |   } | ||||||
|   app.use('/', scrubTheDub); |   app.use('/', scrubTheDub); | ||||||
|   app.use('/', handleApi); |  | ||||||
| 
 | 
 | ||||||
|   return PromiseA.all([ |   return PromiseA.all([ | ||||||
|     cstore.create({ |     cstore.create({ | ||||||
| @ -186,7 +126,7 @@ module.exports.create = function (webserver, info, state) { | |||||||
| 
 | 
 | ||||||
|     return require('../lib/schemes-config').create(sqlstores.config).then(function (tables) { |     return require('../lib/schemes-config').create(sqlstores.config).then(function (tables) { | ||||||
|       models.Config = tables; |       models.Config = tables; | ||||||
|       models.Config.Config.get().then(function (circ) { |       return models.Config.Config.get().then(function (vhostsMap) { | ||||||
|        |        | ||||||
|         /* |         /* | ||||||
|           // todo getDomainInfo
 |           // todo getDomainInfo
 | ||||||
| @ -195,7 +135,92 @@ module.exports.create = function (webserver, info, state) { | |||||||
|             utils.getDomainInfo(domain.id); |             utils.getDomainInfo(domain.id); | ||||||
|           }); |           }); | ||||||
|         */ |         */ | ||||||
|         console.log(circ); | 
 | ||||||
|  |         function handleApi(req, res, next) { | ||||||
|  |           console.log('[API]', req.method, req.url); | ||||||
|  |           var myApp; | ||||||
|  | 
 | ||||||
|  |           if (!/^\/api/.test(req.url)) { | ||||||
|  |             next(); | ||||||
|  |             return; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           // TODO move to caddy parser?
 | ||||||
|  |           if (/(^|\.)proxyable\./.test(req.hostname)) { | ||||||
|  |             // device-id-12345678.proxyable.myapp.mydomain.com => myapp.mydomain.com
 | ||||||
|  |             // proxyable.myapp.mydomain.com => myapp.mydomain.com
 | ||||||
|  |             // TODO myapp.mydomain.com.daplieproxyable.com => myapp.mydomain.com
 | ||||||
|  |             req.hostname = req.hostname.replace(/.*\.?proxyable\./, ''); | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           if (apiHandler) { | ||||||
|  |             if (apiHandler.then) { | ||||||
|  |               apiHandler.then(function (app) { | ||||||
|  |                 app(req, res, next); | ||||||
|  |               }); | ||||||
|  |               return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             apiHandler(req, res, next); | ||||||
|  |             return; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           // apiHandler = require('./vhost-server').create(info.localPort, vhostsdir).create(webserver, app)
 | ||||||
|  |           myApp = express(); | ||||||
|  |           apiHandler = require('./api-server').create( | ||||||
|  |             { apppath: '../packages/apps/' | ||||||
|  |             , apipath: '../packages/apis/' | ||||||
|  |             , vhostsMap: vhostsMap | ||||||
|  |             , server: webserver | ||||||
|  |             , externalPort: info.externalPort | ||||||
|  |             } | ||||||
|  |           , { app: myApp | ||||||
|  |             , memstore: memstore | ||||||
|  |             , sqlstores: sqlstores | ||||||
|  |             , clientSqlFactory: clientFactory | ||||||
|  |             , systemSqlFactory: systemFactory | ||||||
|  |             //, handlePromise: require('./lib/common').promisableRequest;
 | ||||||
|  |             //, handleRejection: require('./lib/common').rejectableRequest;
 | ||||||
|  |             //, localPort: info.localPort
 | ||||||
|  |             } | ||||||
|  |           ).api; | ||||||
|  | 
 | ||||||
|  |           // TODO
 | ||||||
|  |           // X-Forwarded-For
 | ||||||
|  |           // X-Forwarded-Proto
 | ||||||
|  |           console.log('api server', req.hostname, req.secure, req.ip); | ||||||
|  | 
 | ||||||
|  |           apiHandler(req, res, next); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // TODO recase
 | ||||||
|  | 
 | ||||||
|  |         //
 | ||||||
|  |         // Generic Template API
 | ||||||
|  |         //
 | ||||||
|  |         app | ||||||
|  |           .use(require('body-parser').json({ | ||||||
|  |             strict: true // only objects and arrays
 | ||||||
|  |           , inflate: true | ||||||
|  |             // limited to due performance issues with JSON.parse and JSON.stringify
 | ||||||
|  |             // http://josh.zeigler.us/technology/web-development/how-big-is-too-big-for-json/
 | ||||||
|  |           //, limit: 128 * 1024
 | ||||||
|  |           , limit: 1.5 * 1024 * 1024 | ||||||
|  |           , reviver: undefined | ||||||
|  |           , type: 'json' | ||||||
|  |           , verify: undefined | ||||||
|  |           })) | ||||||
|  |           // DO NOT allow urlencoded at any point, it is expressly forbidden
 | ||||||
|  |           //.use(require('body-parser').urlencoded({
 | ||||||
|  |           //  extended: true
 | ||||||
|  |           //, inflate: true
 | ||||||
|  |           //, limit: 100 * 1024
 | ||||||
|  |           //, type: 'urlencoded'
 | ||||||
|  |           //, verify: undefined
 | ||||||
|  |           //}))
 | ||||||
|  |           .use(require('connect-send-error').error()) | ||||||
|  |           ; | ||||||
|  |         app.use('/', handleApi); | ||||||
| 
 | 
 | ||||||
|         return app; |         return app; | ||||||
|       }); |       }); | ||||||
|  | |||||||
							
								
								
									
										0
									
								
								packages/apps/.gitkeep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								packages/apps/.gitkeep
									
									
									
									
									
										Normal file
									
								
							| @ -1,9 +1,13 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
|  | var deserialize = require('../lib/schemes-config').deserialize; | ||||||
|  | var getVhostsMap = require('../lib/schemes-config').getVhostsMap; | ||||||
|  | var getDomainInfo = require('../lib/utils').getDomainInfo; | ||||||
|  | 
 | ||||||
| // var results = {"apis":[{"id":"oauth3-api","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null}],"apps":[{"id":"oauth3-app","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null},{"id":"hellabit-app","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null},{"id":"ldsio-app","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null},{"id":"ldsconnect-app","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null}],"domains":[{"id":"oauth3.org","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null},{"id":"lds.io","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null},{"id":"ldsconnect.org","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null},{"id":"hellabit.com","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null},{"id":"hellabit.com#connect","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null}],"apisDomains":[{"id":"oauth3-api_oauth3.org","createdAt":null,"updatedAt":null,"deletedAt":null,"apiId":"oauth3-api","domainId":"oauth3.org","json":null}],"appsDomains":[{"id":"oauth3-app_oauth3.org","createdAt":null,"updatedAt":null,"deletedAt":null,"appId":"oauth3-app","domainId":"oauth3.org","json":null},{"id":"hellabit-app_hellabit.com","createdAt":null,"updatedAt":null,"deletedAt":null,"appId":"hellabit-app","domainId":"hellabit.com","json":null},{"id":"ldsio-app_lds.io","createdAt":null,"updatedAt":null,"deletedAt":null,"appId":"ldsio-app","domainId":"lds.io","json":null},{"id":"ldsconnect-app_ldsconnect.org","createdAt":null,"updatedAt":null,"deletedAt":null,"appId":"ldsconnect-app","domainId":"ldsconnect.org","json":null}]};
 | // var results = {"apis":[{"id":"oauth3-api","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null}],"apps":[{"id":"oauth3-app","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null},{"id":"hellabit-app","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null},{"id":"ldsio-app","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null},{"id":"ldsconnect-app","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"json":null}],"domains":[{"id":"oauth3.org","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null},{"id":"lds.io","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null},{"id":"ldsconnect.org","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null},{"id":"hellabit.com","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null},{"id":"hellabit.com#connect","createdAt":null,"updatedAt":null,"deletedAt":null,"revokedAt":null,"name":null,"token":null,"accountId":null,"json":null}],"apisDomains":[{"id":"oauth3-api_oauth3.org","createdAt":null,"updatedAt":null,"deletedAt":null,"apiId":"oauth3-api","domainId":"oauth3.org","json":null}],"appsDomains":[{"id":"oauth3-app_oauth3.org","createdAt":null,"updatedAt":null,"deletedAt":null,"appId":"oauth3-app","domainId":"oauth3.org","json":null},{"id":"hellabit-app_hellabit.com","createdAt":null,"updatedAt":null,"deletedAt":null,"appId":"hellabit-app","domainId":"hellabit.com","json":null},{"id":"ldsio-app_lds.io","createdAt":null,"updatedAt":null,"deletedAt":null,"appId":"ldsio-app","domainId":"lds.io","json":null},{"id":"ldsconnect-app_ldsconnect.org","createdAt":null,"updatedAt":null,"deletedAt":null,"appId":"ldsconnect-app","domainId":"ldsconnect.org","json":null}]};
 | ||||||
| var results = { | var results = { | ||||||
|   "apis":[ |   "apis":[ | ||||||
|     {"id":"oauth3-api"} |     {"id":"org.oauth3"} | ||||||
|   ] |   ] | ||||||
| , "apps":[ | , "apps":[ | ||||||
|     {"id":"oauth3-app"} |     {"id":"oauth3-app"} | ||||||
| @ -23,7 +27,8 @@ var results = { | |||||||
|   , {"id":"hellabit.com#connect#too"} |   , {"id":"hellabit.com#connect#too"} | ||||||
|   ] |   ] | ||||||
| , "apisDomains":[ | , "apisDomains":[ | ||||||
|     {"id":"oauth3-api_oauth3.org","apiId":"oauth3-api","domainId":"oauth3.org"} |     {"id":"org.oauth3_oauth3.org","apiId":"org.oauth3","domainId":"oauth3.org"} | ||||||
|  |   , {"id":"org.oauth3_hellabit.com#connect###","apiId":"org.oauth3","domainId":"hellabit.com#connect###"} | ||||||
|   ] |   ] | ||||||
| ,"appsDomains":[ | ,"appsDomains":[ | ||||||
|     {"id":"oauth3-app_oauth3.org","appId":"oauth3-app","domainId":"oauth3.org"} |     {"id":"oauth3-app_oauth3.org","appId":"oauth3-app","domainId":"oauth3.org"} | ||||||
| @ -34,127 +39,9 @@ var results = { | |||||||
|   ] |   ] | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| var deserialize = require('../lib/schemes-config').deserialize; |  | ||||||
| var getDomainInfo = require('../lib/utils').getDomainInfo; |  | ||||||
| var config = deserialize(results); |  | ||||||
| var req = { host: 'hellabit.com', url: '/connect' }; | var req = { host: 'hellabit.com', url: '/connect' }; | ||||||
| var vhosts = []; | module.exports.create({ | ||||||
| var vhostsMap = {}; |   apppath: '../packages/apps/' | ||||||
| 
 | , apipath: '../packages/apis/' | ||||||
| function sortApps(a, b) { | , vhostsMap: vhostsMap | ||||||
|   // hlen isn't important in this current use of the sorter,
 | }).api(req); | ||||||
|   // but is important for an alternate version
 |  | ||||||
|   var hlen = b.hostname.length - a.hostname.length; |  | ||||||
|   var plen = b.pathname.length - a.pathname.length; |  | ||||||
| 
 |  | ||||||
|   // A directory could be named example.com, example.com# example.com##
 |  | ||||||
|   // to indicate order of preference (for API addons, for example)
 |  | ||||||
|   var dlen = (b.priority || b.dirname.length) - (a.priority || a.dirname.length); |  | ||||||
| 
 |  | ||||||
|   if (!hlen) { |  | ||||||
|     if (!plen) { |  | ||||||
|       return dlen; |  | ||||||
|     } |  | ||||||
|     return plen; |  | ||||||
|   } |  | ||||||
|   return hlen; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Object.keys(config.domains).forEach(function (domainname) { |  | ||||||
|   var domain = config.domains[domainname]; |  | ||||||
|   var info = getDomainInfo(domainname); |  | ||||||
| 
 |  | ||||||
|   domain.hostname = info.hostname; |  | ||||||
|   domain.pathname = '/' + (info.pathname || ''); |  | ||||||
|   domain.dirname = info.dirname; |  | ||||||
| 
 |  | ||||||
|   vhosts.push(domain); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| vhosts.sort(sortApps); |  | ||||||
| 
 |  | ||||||
| vhosts.forEach(function (domain) { |  | ||||||
|   console.log(domain.hostname, domain.pathname, domain.dirname); |  | ||||||
| 
 |  | ||||||
|   if (!vhostsMap[domain.hostname]) { |  | ||||||
|     vhostsMap[domain.hostname] = { pathnamesMap: {}, pathnames: [] }; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   if (!vhostsMap[domain.hostname].pathnamesMap[domain.pathname]) { |  | ||||||
|     vhostsMap[domain.hostname].pathnamesMap[domain.pathname] = { pathname: domain.pathname, apps: [] }; |  | ||||||
|     vhostsMap[domain.hostname].pathnames.push(vhostsMap[domain.hostname].pathnamesMap[domain.pathname]); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   vhostsMap[domain.hostname].pathnamesMap[domain.pathname].apps.push(domain); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| if (!vhostsMap[req.host]) { |  | ||||||
|   console.log("there's no app for this hostname"); |  | ||||||
|   return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| //console.log("load an app", vhosts[req.host]);
 |  | ||||||
| 
 |  | ||||||
| //console.log(vhosts[req.host]);
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| function getApp(route) { |  | ||||||
|   var PromiseA = require('bluebird'); |  | ||||||
| 
 |  | ||||||
|   return new PromiseA(function (resolve, reject) { |  | ||||||
|     console.log(route); |  | ||||||
|     // route.hostname
 |  | ||||||
|   }); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function api(req, res, next) { |  | ||||||
|   var apps;  |  | ||||||
| 
 |  | ||||||
|   vhostsMap[req.host].pathnames.some(function (route) { |  | ||||||
|     // /connect /
 |  | ||||||
|     if (req.url.match(route.pathname) && route.pathname.match(req.url)) { |  | ||||||
|       apps = route.apps; |  | ||||||
|       return true; |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   //console.log(apps);
 |  | ||||||
| 
 |  | ||||||
|   function nextify(err) { |  | ||||||
|     var route; |  | ||||||
| 
 |  | ||||||
|     if (err) { |  | ||||||
|       next(err); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     // shortest to longest
 |  | ||||||
|     //route = apps.pop();
 |  | ||||||
|     // longest to shortest
 |  | ||||||
|     route = apps.shift(); |  | ||||||
|     if (!route) { |  | ||||||
|       next(); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (route.route) { |  | ||||||
|       route.route(req, res, nextify); |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     getApp(route).then(function (route) { |  | ||||||
|       route.route = route; |  | ||||||
|       try { |  | ||||||
|         route.route(req, res, nextify); |  | ||||||
|       } catch(e) { |  | ||||||
|         console.error('[App Load Error]'); |  | ||||||
|         console.error(e.stack); |  | ||||||
|         nextify(new Error("couldn't load app")); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   nextify(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| api(req); |  | ||||||
|  | |||||||
| @ -12,7 +12,6 @@ function waitForInit(message) { | |||||||
| 
 | 
 | ||||||
|   var msg = message.conf; |   var msg = message.conf; | ||||||
|   process.removeListener('message', waitForInit); |   process.removeListener('message', waitForInit); | ||||||
| 
 |  | ||||||
|   require('./lib/local-server').create(msg.certPaths, msg.localPort, function (err, webserver) { |   require('./lib/local-server').create(msg.certPaths, msg.localPort, function (err, webserver) { | ||||||
|     if (err) { |     if (err) { | ||||||
|       console.error('[ERROR] worker.js'); |       console.error('[ERROR] worker.js'); | ||||||
| @ -38,6 +37,9 @@ function waitForInit(message) { | |||||||
|       process.on('message', initWebServer); |       process.on('message', initWebServer); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   // TODO conditional if 80 is being served by caddy
 | ||||||
|  |   require('./lib/insecure-server').create(msg.externalPort, msg.insecurePort); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // We have to wait to get the configuration from the master process
 | // We have to wait to get the configuration from the master process
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user