| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-12 11:14:59 +00:00
										 |  |  | module.exports.create = function (webserver, info, state) { | 
					
						
							|  |  |  |   if (!state) { | 
					
						
							|  |  |  |     state = {}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   var PromiseA = state.Promise || require('bluebird'); | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  |   var path = require('path'); | 
					
						
							| 
									
										
										
										
											2015-11-14 04:25:12 +00:00
										 |  |  |   //var vhostsdir = path.join(__dirname, 'vhosts');
 | 
					
						
							|  |  |  |   var express = require('express-lazy'); | 
					
						
							|  |  |  |   var app = express(); | 
					
						
							| 
									
										
										
										
											2015-11-12 11:14:59 +00:00
										 |  |  |   var memstore; | 
					
						
							|  |  |  |   var sqlstores = {}; | 
					
						
							|  |  |  |   var models = {}; | 
					
						
							|  |  |  |   var systemFactory = require('sqlite3-cluster/client').createClientFactory({ | 
					
						
							| 
									
										
										
										
											2015-11-19 22:13:20 +00:00
										 |  |  |       dirname: path.join(__dirname, '..', '..', 'var') // TODO info.conf
 | 
					
						
							| 
									
										
										
										
											2015-11-28 05:57:07 +00:00
										 |  |  |     , prefix: 'com.example.' | 
					
						
							| 
									
										
										
										
											2015-11-12 11:14:59 +00:00
										 |  |  |     //, dbname: 'config'
 | 
					
						
							|  |  |  |     , suffix: '' | 
					
						
							|  |  |  |     , ext: '.sqlite3' | 
					
						
							|  |  |  |     , sock: info.conf.sqlite3Sock | 
					
						
							|  |  |  |     , ipcKey: info.conf.ipcKey | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   var clientFactory = require('sqlite3-cluster/client').createClientFactory({ | 
					
						
							|  |  |  |       algorithm: 'aes' | 
					
						
							|  |  |  |     , bits: 128 | 
					
						
							|  |  |  |     , mode: 'cbc' | 
					
						
							| 
									
										
										
										
											2015-11-19 22:13:20 +00:00
										 |  |  |     , dirname: path.join(__dirname, '..', '..', 'var') // TODO info.conf
 | 
					
						
							| 
									
										
										
										
											2015-11-28 05:57:07 +00:00
										 |  |  |     , prefix: 'com.example.' | 
					
						
							| 
									
										
										
										
											2015-11-12 11:14:59 +00:00
										 |  |  |     //, dbname: 'cluster'
 | 
					
						
							|  |  |  |     , suffix: '' | 
					
						
							|  |  |  |     , ext: '.sqlcipher' | 
					
						
							|  |  |  |     , sock: info.conf.sqlite3Sock | 
					
						
							|  |  |  |     , ipcKey: info.conf.ipcKey | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   var cstore = require('cluster-store'); | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |   var redirectives; | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-19 12:34:59 +00:00
										 |  |  |   app.disable('x-powered-by'); | 
					
						
							| 
									
										
										
										
											2015-11-17 08:18:56 +00:00
										 |  |  |   if (info.conf.trustProxy) { | 
					
						
							|  |  |  |     console.info('[Trust Proxy]'); | 
					
						
							|  |  |  |     app.set('trust proxy', ['loopback']); | 
					
						
							|  |  |  |     //app.set('trust proxy', function (ip) { console.log('[ip]', ip); return true; });
 | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     console.info('[DO NOT trust proxy]'); | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |     // TODO make sure the gzip module loads if there isn't a proxy gzip-ing for us
 | 
					
						
							|  |  |  |     // app.use(compression())
 | 
					
						
							| 
									
										
										
										
											2015-11-17 08:18:56 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  |   /* | 
					
						
							|  |  |  |   function unlockDevice(conf, state) { | 
					
						
							|  |  |  |     return require('./lib/unlock-device').create().then(function (result) { | 
					
						
							|  |  |  |       result.promise.then(function (_rootMasterKey) { | 
					
						
							|  |  |  |         process.send({ | 
					
						
							| 
									
										
										
										
											2015-11-28 05:57:07 +00:00
										 |  |  |           type: 'walnut.keys.root' | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  |           conf: { | 
					
						
							|  |  |  |             rootMasterKey: _rootMasterkey | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         conf.locked = false; | 
					
						
							|  |  |  |         if (state.caddy) { | 
					
						
							|  |  |  |           state.caddy.update(conf); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         conf.rootMasterKey = _rootMasterKey; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return result.app; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-12 11:14:59 +00:00
										 |  |  |   // TODO handle insecure to actual redirect
 | 
					
						
							|  |  |  |   // blog.coolaj86.com -> coolaj86.com/blog
 | 
					
						
							|  |  |  |   // hmm... that won't really matter with hsts
 | 
					
						
							|  |  |  |   // I guess I just needs letsencrypt
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  |   function scrubTheDub(req, res, next) { | 
					
						
							|  |  |  |     var host = req.hostname; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!host || 'string' !== typeof host) { | 
					
						
							|  |  |  |       next(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-14 04:25:12 +00:00
										 |  |  |     // TODO test if this is even necessary
 | 
					
						
							|  |  |  |     host = host.toLowerCase(); | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |     // TODO this should be hot loadable / changeable
 | 
					
						
							|  |  |  |     if (!redirectives && info.conf.redirects) { | 
					
						
							|  |  |  |       redirectives = require('./hostname-redirects').compile(info.conf.redirects); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!/^www\./.test(host) && !redirectives) { | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  |       next(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |     // TODO misnomer, handles all exact redirects
 | 
					
						
							|  |  |  |     if (!require('./no-www').scrubTheDub(req, res, redirectives)) { | 
					
						
							|  |  |  |       next(); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-17 08:18:56 +00:00
										 |  |  |   function caddyBugfix(req, res, next) { | 
					
						
							|  |  |  |     // workaround for Caddy
 | 
					
						
							|  |  |  |     // https://github.com/mholt/caddy/issues/341
 | 
					
						
							|  |  |  |     if (app.get('trust proxy')) { | 
					
						
							|  |  |  |       if (req.headers['x-forwarded-proto']) { | 
					
						
							|  |  |  |         req.headers['x-forwarded-proto'] = (req.headers['x-forwarded-proto'] || '').split(/,\s+/g)[0] || undefined; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (req.headers['x-forwarded-host']) { | 
					
						
							|  |  |  |         req.headers['x-forwarded-host'] = (req.headers['x-forwarded-host'] || '').split(/,\s+/g)[0] || undefined; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     next(); | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-11-17 08:18:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |   // TODO misnomer, this can handle nowww, yeswww, and exact hostname redirects
 | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  |   app.use('/', scrubTheDub); | 
					
						
							| 
									
										
										
										
											2015-11-17 08:18:56 +00:00
										 |  |  |   app.use('/', caddyBugfix); | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-12 11:14:59 +00:00
										 |  |  |   return PromiseA.all([ | 
					
						
							| 
									
										
										
										
											2015-11-18 11:44:22 +00:00
										 |  |  |     // TODO security on memstore
 | 
					
						
							|  |  |  |     // TODO memstoreFactory.create
 | 
					
						
							| 
									
										
										
										
											2015-11-12 11:14:59 +00:00
										 |  |  |     cstore.create({ | 
					
						
							|  |  |  |       sock: info.conf.memstoreSock | 
					
						
							|  |  |  |     , connect: info.conf.memstoreSock | 
					
						
							|  |  |  |       // TODO implement
 | 
					
						
							|  |  |  |     , key: info.conf.ipcKey | 
					
						
							|  |  |  |     }).then(function (_memstore) { | 
					
						
							|  |  |  |       memstore = _memstore; | 
					
						
							|  |  |  |       return memstore; | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     // TODO mark a device as lost, stolen, missing in DNS records
 | 
					
						
							|  |  |  |     // (and in turn allow other devices to lock it, turn on location reporting, etc)
 | 
					
						
							|  |  |  |   , systemFactory.create({ | 
					
						
							|  |  |  |         init: true | 
					
						
							|  |  |  |       , dbname: 'config' | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   , clientFactory.create({ | 
					
						
							|  |  |  |         init: true | 
					
						
							|  |  |  |       , key: '00000000000000000000000000000000' | 
					
						
							|  |  |  |       // TODO only complain if the values are different
 | 
					
						
							|  |  |  |       //, algo: 'aes'
 | 
					
						
							|  |  |  |       , dbname: 'auth' | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   , clientFactory.create({ | 
					
						
							|  |  |  |         init: false | 
					
						
							|  |  |  |       , dbname: 'system' | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   ]).then(function (args) { | 
					
						
							|  |  |  |     memstore = args[0]; | 
					
						
							|  |  |  |     sqlstores.config = args[1]; | 
					
						
							|  |  |  |     sqlstores.auth = args[2]; | 
					
						
							|  |  |  |     sqlstores.system = args[3]; | 
					
						
							|  |  |  |     sqlstores.create = clientFactory.create; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return require('../lib/schemes-config').create(sqlstores.config).then(function (tables) { | 
					
						
							|  |  |  |       models.Config = tables; | 
					
						
							| 
									
										
										
										
											2015-11-14 04:25:12 +00:00
										 |  |  |       return models.Config.Config.get().then(function (vhostsMap) { | 
					
						
							| 
									
										
										
										
											2015-11-18 11:44:22 +00:00
										 |  |  |         // TODO the core needs to be replacable in one shot
 | 
					
						
							|  |  |  |         // rm -rf /tmp/walnut/; tar xvf -C /tmp/walnut/; mv /srv/walnut /srv/walnut.{{version}}; mv /tmp/walnut /srv/
 | 
					
						
							|  |  |  |         // this means that any packages must be outside, perhaps /srv/walnut/{boot,core,packages}
 | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |         var pkgConf = { | 
					
						
							| 
									
										
										
										
											2015-11-21 20:47:28 +00:00
										 |  |  |           pagespath: path.join(__dirname, '..', '..', 'packages', 'pages') + path.sep | 
					
						
							| 
									
										
										
										
											2015-11-19 22:13:20 +00:00
										 |  |  |         , apipath: path.join(__dirname, '..', '..', 'packages', 'apis') + path.sep | 
					
						
							|  |  |  |         , servicespath: path.join(__dirname, '..', '..', 'packages', 'services') | 
					
						
							| 
									
										
										
										
											2015-11-18 11:44:22 +00:00
										 |  |  |         , vhostsMap: vhostsMap | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |         , vhostPatterns: null | 
					
						
							| 
									
										
										
										
											2015-11-18 11:44:22 +00:00
										 |  |  |         , server: webserver | 
					
						
							|  |  |  |         , externalPort: info.conf.externalPort | 
					
						
							|  |  |  |         , primaryNameserver: info.conf.primaryNameserver | 
					
						
							|  |  |  |         , nameservers: info.conf.nameservers | 
					
						
							| 
									
										
										
										
											2015-11-19 12:34:59 +00:00
										 |  |  |         , privkey: info.conf.privkey | 
					
						
							|  |  |  |         , pubkey: info.conf.pubkey | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |         , redirects: info.conf.redirects | 
					
						
							| 
									
										
										
										
											2015-11-18 11:44:22 +00:00
										 |  |  |         , apiPrefix: '/api' | 
					
						
							| 
									
										
										
										
											2015-12-04 07:00:30 +00:00
										 |  |  |         , 'org.oauth3.consumer': info.conf['org.oauth3.consumer'] | 
					
						
							|  |  |  |         , 'org.oauth3.provider': info.conf['org.oauth3.provider'] | 
					
						
							|  |  |  |         , keys: info.conf.keys | 
					
						
							| 
									
										
										
										
											2015-11-18 11:44:22 +00:00
										 |  |  |         }; | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |         var pkgDeps = { | 
					
						
							|  |  |  |           memstore: memstore | 
					
						
							|  |  |  |         , sqlstores: sqlstores | 
					
						
							|  |  |  |         , clientSqlFactory: clientFactory | 
					
						
							|  |  |  |         , systemSqlFactory: systemFactory | 
					
						
							|  |  |  |         //, handlePromise: require('./lib/common').promisableRequest;
 | 
					
						
							|  |  |  |         //, handleRejection: require('./lib/common').rejectableRequest;
 | 
					
						
							|  |  |  |         //, localPort: info.conf.localPort
 | 
					
						
							|  |  |  |         , Promise: PromiseA | 
					
						
							|  |  |  |         , express: express | 
					
						
							|  |  |  |         , app: app | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         var Services = require('./services-loader').create(pkgConf, { | 
					
						
							| 
									
										
										
										
											2015-11-18 11:44:22 +00:00
										 |  |  |           memstore: memstore | 
					
						
							|  |  |  |         , sqlstores: sqlstores | 
					
						
							|  |  |  |         , clientSqlFactory: clientFactory | 
					
						
							|  |  |  |         , systemSqlFactory: systemFactory | 
					
						
							|  |  |  |         , Promise: PromiseA | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2015-12-04 07:00:30 +00:00
										 |  |  |         var recase = require('connect-recase')({ | 
					
						
							|  |  |  |           // TODO allow explicit and or default flag
 | 
					
						
							|  |  |  |           explicit: false | 
					
						
							|  |  |  |         , default: 'snake' | 
					
						
							|  |  |  |         , prefixes: ['/api'] | 
					
						
							|  |  |  |           // TODO allow exclude
 | 
					
						
							|  |  |  |         //, exclusions: [config.oauthPrefix]
 | 
					
						
							|  |  |  |         , exceptions: {} | 
					
						
							|  |  |  |         //, cancelParam: 'camel'
 | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2015-11-14 04:25:12 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |         function handlePackages(req, res, next) { | 
					
						
							| 
									
										
										
										
											2015-11-14 04:25:12 +00:00
										 |  |  |           // 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
 | 
					
						
							| 
									
										
										
										
											2015-11-28 05:57:07 +00:00
										 |  |  |             // TODO myapp.mydomain.com.example.proxyable.com => myapp.mydomain.com
 | 
					
						
							| 
									
										
										
										
											2015-11-14 04:25:12 +00:00
										 |  |  |             req.hostname = req.hostname.replace(/.*\.?proxyable\./, ''); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |           require('./package-server').mapToApp({ | 
					
						
							|  |  |  |             config: pkgConf | 
					
						
							|  |  |  |           , deps: pkgDeps | 
					
						
							|  |  |  |           , services: Services | 
					
						
							| 
									
										
										
										
											2015-12-08 00:51:48 +00:00
										 |  |  |           , conf: info.conf | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |           }, req, res, next); | 
					
						
							| 
									
										
										
										
											2015-11-14 04:25:12 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // TODO recase
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         // Generic Template API
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         app | 
					
						
							| 
									
										
										
										
											2015-12-04 07:00:30 +00:00
										 |  |  |           .use('/api', require('body-parser').json({ | 
					
						
							| 
									
										
										
										
											2015-11-14 04:25:12 +00:00
										 |  |  |             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()) | 
					
						
							|  |  |  |           ; | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-04 07:00:30 +00:00
										 |  |  |         app.use('/api', recase); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |         app.use('/', handlePackages); | 
					
						
							| 
									
										
										
										
											2015-11-19 07:42:20 +00:00
										 |  |  |         app.use('/', function (err, req, res, next) { | 
					
						
							|  |  |  |           console.error('[Error Handler]'); | 
					
						
							|  |  |  |           console.error(err.stack); | 
					
						
							|  |  |  |           if (req.xhr) { | 
					
						
							|  |  |  |             res.send({ error: { message: "kinda unknownish error" } }); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             res.send('<html><head><title>ERROR</title></head><body>Error</body></html>'); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2015-11-19 12:34:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |           // sadly express uses arity checking
 | 
					
						
							|  |  |  |           // so the fourth parameter must exist
 | 
					
						
							|  |  |  |           if (false) { | 
					
						
							|  |  |  |             next(); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2015-11-19 07:42:20 +00:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2015-11-12 11:14:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return app; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2015-11-06 11:05:32 +00:00
										 |  |  | }; |