'use strict'; module.exports.create = function (webserver, info, state) { if (!state) { state = {}; } var PromiseA = state.Promise || require('bluebird'); var path = require('path'); //var vhostsdir = path.join(__dirname, 'vhosts'); var express = require('express-lazy'); var app = express(); var apiHandler; var memstore; var sqlstores = {}; var models = {}; var systemFactory = require('sqlite3-cluster/client').createClientFactory({ dirname: path.join(__dirname, '..', 'var') // TODO info.conf , prefix: 'com.daplie.' //, 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' , dirname: path.join(__dirname, '..', 'var') // TODO info.conf , prefix: 'com.daplie.' //, dbname: 'cluster' , suffix: '' , ext: '.sqlcipher' , sock: info.conf.sqlite3Sock , ipcKey: info.conf.ipcKey }); var cstore = require('cluster-store'); /* function unlockDevice(conf, state) { return require('./lib/unlock-device').create().then(function (result) { result.promise.then(function (_rootMasterKey) { process.send({ type: 'com.daplie.walnut.keys.root' conf: { rootMasterKey: _rootMasterkey } }); conf.locked = false; if (state.caddy) { state.caddy.update(conf); } conf.rootMasterKey = _rootMasterKey; }); return result.app; }); } */ // 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 function scrubTheDub(req, res, next) { console.log('[no-www]', req.method, req.url); var host = req.hostname; if (!host || 'string' !== typeof host) { next(); return; } // TODO test if this is even necessary host = host.toLowerCase(); if (!/^www\./.test(host)) { next(); return; } require('./no-www').scrubTheDub(req, res); } if (info.trustProxy) { app.set('trust proxy', ['loopback']); //app.set('trust proxy', function (ip) { ... }); } app.use('/', scrubTheDub); return PromiseA.all([ 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; return models.Config.Config.get().then(function (vhostsMap) { /* // todo getDomainInfo var utils = require('./utils'); results.domains.forEach(function (domain) { utils.getDomainInfo(domain.id); }); */ 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; }); }); }); };