walnut.js/lib/worker.js

230 lines
6.5 KiB
JavaScript

'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;
});
});
});
};