2015-11-14 04:25:12 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
// TODO handle static app urls?
|
|
|
|
// NOTE rejecting non-api urls should happen before this
|
2015-11-19 07:42:20 +00:00
|
|
|
module.exports.create = function (conf, deps/*, Services*/) {
|
|
|
|
var PromiseA = deps.Promise;
|
|
|
|
var app = deps.app;
|
|
|
|
var express = deps.express;
|
2015-11-14 04:25:12 +00:00
|
|
|
var escapeStringRegexp = require('escape-string-regexp');
|
|
|
|
var vhostsMap = conf.vhostsMap;
|
|
|
|
|
|
|
|
function getApi(route) {
|
|
|
|
// TODO don't modify route, modify some other variable instead
|
|
|
|
|
|
|
|
var path = require('path');
|
|
|
|
// 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 || ''));
|
|
|
|
|
|
|
|
return new PromiseA(function (resolve, reject) {
|
2015-11-19 07:42:20 +00:00
|
|
|
var myApp;
|
2015-11-19 12:34:59 +00:00
|
|
|
var ursa;
|
2015-11-19 07:42:20 +00:00
|
|
|
|
2015-11-14 04:25:12 +00:00
|
|
|
try {
|
2015-11-18 11:44:22 +00:00
|
|
|
// TODO dynamic requires are a no-no
|
|
|
|
// can we statically generate a require-er? on each install?
|
|
|
|
// module.exports = { {{pkgpath}}: function () { return require({{pkgpath}}) } }
|
|
|
|
// requirer[pkgpath]()
|
2015-11-19 07:42:20 +00:00
|
|
|
myApp = express();
|
2015-11-19 12:34:59 +00:00
|
|
|
myApp.disable('x-powered-by');
|
2015-11-19 07:42:20 +00:00
|
|
|
if (app.get('trust proxy')) {
|
|
|
|
myApp.set('trust proxy', app.get('trust proxy'));
|
|
|
|
}
|
2015-11-19 12:34:59 +00:00
|
|
|
if (!conf.pubkey) {
|
|
|
|
/*
|
|
|
|
return ursa.createPrivateKey(pem, password, encoding);
|
|
|
|
var pem = myKey.toPrivatePem();
|
|
|
|
return jwt.verifyAsync(token, myKey.toPublicPem(), { ignoreExpiration: false && true }).then(function (decoded) {
|
|
|
|
});
|
|
|
|
*/
|
|
|
|
ursa = require('ursa');
|
|
|
|
conf.keypair = ursa.createPrivateKey(conf.privkey, 'ascii');
|
|
|
|
conf.pubkey = ursa.createPublicKey(conf.pubkey, 'ascii'); //conf.keypair.toPublicKey();
|
|
|
|
}
|
|
|
|
// TODO give pub/priv pair for app and all public keys
|
2015-11-19 07:42:20 +00:00
|
|
|
route.route = require(pkgpath).create(conf, deps, myApp);
|
2015-11-14 04:25:12 +00:00
|
|
|
} catch(e) {
|
|
|
|
reject(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(route.route);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function api(req, res, next) {
|
|
|
|
var apps;
|
|
|
|
|
|
|
|
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')) {
|
2015-11-19 07:42:20 +00:00
|
|
|
// TODO needs namespace for current api
|
2015-11-14 04:25:12 +00:00
|
|
|
pathname = '/api' + pathname;
|
|
|
|
}
|
2015-11-19 07:42:20 +00:00
|
|
|
// pathname += '.local';
|
2015-11-14 04:25:12 +00:00
|
|
|
|
|
|
|
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)) {
|
2015-11-19 07:42:20 +00:00
|
|
|
// make a copy
|
|
|
|
apps = route.apps.slice(0);
|
2015-11-14 04:25:12 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!apps) {
|
|
|
|
next();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2015-11-19 07:42:20 +00:00
|
|
|
if (route.route.then) {
|
|
|
|
route.route.then(function (expressApp) {
|
|
|
|
expressApp(req, res, nextify);
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2015-11-14 04:25:12 +00:00
|
|
|
route.route(req, res, nextify);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-19 07:42:20 +00:00
|
|
|
if (route._errored) {
|
2015-11-14 04:25:12 +00:00
|
|
|
nextify(new Error("couldn't load api"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!route.api) {
|
2015-11-19 12:34:59 +00:00
|
|
|
console.error('missing route:', req.url);
|
2015-11-14 04:25:12 +00:00
|
|
|
nextify(new Error("no api available for this route"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-11-19 07:42:20 +00:00
|
|
|
return getApi(route).then(function (expressApp) {
|
2015-11-14 04:25:12 +00:00
|
|
|
try {
|
2015-11-19 07:42:20 +00:00
|
|
|
expressApp(req, res, nextify);
|
|
|
|
route.route = expressApp;
|
2015-11-14 04:25:12 +00:00
|
|
|
} catch(e) {
|
|
|
|
route._errored = true;
|
|
|
|
console.error('[App Load Error]');
|
|
|
|
nextify(new Error("couldn't load api"));
|
|
|
|
}
|
2015-11-19 07:42:20 +00:00
|
|
|
|
|
|
|
return expressApp;
|
|
|
|
}, function (err) {
|
|
|
|
console.error('[App Promise Error]');
|
|
|
|
nextify(err);
|
2015-11-14 04:25:12 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
nextify();
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
api: api
|
|
|
|
};
|
|
|
|
};
|