add shortcut to loading any static app
This commit is contained in:
parent
5d27a81bac
commit
9d157c12a2
87
lib/main.js
87
lib/main.js
|
@ -69,6 +69,8 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
|
||||||
|
|
||||||
function loadHandler(name) {
|
function loadHandler(name) {
|
||||||
return function handler(req, res, next) {
|
return function handler(req, res, next) {
|
||||||
|
// path.join('packages/pages', 'com.daplie.hello') // package name (used as file-link)
|
||||||
|
// path.join('packages/pages', 'domain.tld#hello') // dynamic exact url match
|
||||||
var packagepath = path.join(xconfx.staticpath, name);
|
var packagepath = path.join(xconfx.staticpath, name);
|
||||||
|
|
||||||
return fs.lstatAsync(packagepath).then(function (stat) {
|
return fs.lstatAsync(packagepath).then(function (stat) {
|
||||||
|
@ -84,8 +86,18 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
|
||||||
return disallowNonFiles;
|
return disallowNonFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// path.join('packages/pages', 'domain.tld#hello') // a file (not folder) which contains a list of roots
|
||||||
|
// may look like this:
|
||||||
|
//
|
||||||
|
// com.daplie.hello
|
||||||
|
// tld.domain.app
|
||||||
|
//
|
||||||
|
// this is basically a 'recursive mount' to signify that 'com.daplie.hello' should be tried first
|
||||||
|
// and if no file matches that 'tld.domain.app' may be tried next, and so on
|
||||||
|
//
|
||||||
|
// this may well become a .htaccess type of situation allowing for redirects and such
|
||||||
return fs.readFileAsync(packagepath, 'utf8').then(function (text) {
|
return fs.readFileAsync(packagepath, 'utf8').then(function (text) {
|
||||||
// TODO allow cascading
|
// TODO allow cascading multiple lines
|
||||||
text = text.trim().split(/\n/)[0];
|
text = text.trim().split(/\n/)[0];
|
||||||
|
|
||||||
// TODO rerun the above, disallowing link-style (or count or memoize to prevent infinite loop)
|
// TODO rerun the above, disallowing link-style (or count or memoize to prevent infinite loop)
|
||||||
|
@ -95,6 +107,8 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
|
||||||
return securityError;
|
return securityError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// instead of actually creating new instances of express.static
|
||||||
|
// this same effect could be managed by internally re-writing the url (and restoring it)
|
||||||
return express.static(packagepath);
|
return express.static(packagepath);
|
||||||
});
|
});
|
||||||
}, function (/*err*/) {
|
}, function (/*err*/) {
|
||||||
|
@ -109,22 +123,27 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function staticHelper(appId, opts) {
|
function staticHelper(appId, opts) {
|
||||||
|
console.log('[staticHelper]', appId);
|
||||||
// TODO inter-process cache expirey
|
// TODO inter-process cache expirey
|
||||||
// TODO add to xconfx.staticpath
|
// TODO add to xconfx.staticpath
|
||||||
xconfx.staticpath = path.join(__dirname, '..', '..', 'packages', 'pages');
|
xconfx.staticpath = path.join(__dirname, '..', '..', 'packages', 'pages');
|
||||||
|
|
||||||
|
// Reads in each of the static apps as 'nodes'
|
||||||
return fs.readdirAsync(xconfx.staticpath).then(function (nodes) {
|
return fs.readdirAsync(xconfx.staticpath).then(function (nodes) {
|
||||||
if (opts && opts.clear) {
|
if (opts && opts.clear) {
|
||||||
localCache.statics = {};
|
localCache.statics = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// longest to shortest
|
// Order from longest (index length - 1) to shortest (index 0)
|
||||||
function shortToLong(a, b) {
|
function shortToLong(a, b) {
|
||||||
return b.length - a.length;
|
return b.length - a.length;
|
||||||
}
|
}
|
||||||
nodes.sort(shortToLong);
|
nodes.sort(shortToLong);
|
||||||
|
|
||||||
nodes.forEach(function (name) {
|
nodes.forEach(function (name) {
|
||||||
|
console.log('[all apps]', name);
|
||||||
if (!localCache.statics[name]) {
|
if (!localCache.statics[name]) {
|
||||||
|
console.log('[load this app]', name);
|
||||||
localCache.statics[name] = { handler: loadHandler(name), createdAt: Date.now() };
|
localCache.statics[name] = { handler: loadHandler(name), createdAt: Date.now() };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -151,10 +170,7 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function serveStatic(req, res, next) {
|
function serveStaticHelper(appId, opts, req, res, next) {
|
||||||
// If we get this far we can be pretty confident that
|
|
||||||
// the domain was already set up because it's encrypted
|
|
||||||
var appId = req.hostname + req.url.replace(/\/+/g, '#').replace(/#$/, '');
|
|
||||||
var appIdParts = appId.split('#');
|
var appIdParts = appId.split('#');
|
||||||
var appIdPart;
|
var appIdPart;
|
||||||
|
|
||||||
|
@ -168,13 +184,14 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
|
||||||
require('./no-www').scrubTheDub(req, res);
|
require('./no-www').scrubTheDub(req, res);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
if (!redirectives && config.redirects) {
|
if (!redirectives && config.redirects) {
|
||||||
redirectives = require('./hostname-redirects').compile(config.redirects);
|
redirectives = require('./hostname-redirects').compile(config.redirects);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO assets.example.com/sub/assets/com.example.xyz/
|
// If this looks like an API, we shouldn't be here
|
||||||
if (/^api\./.test(req.hostname) && /\/api(\/|$)/.test(req.url)) {
|
if (/^api\./.test(req.hostname) && /\/api(\/|$)/.test(req.url)) {
|
||||||
// supports api.example.com/sub/app/api/com.example.xyz/
|
// supports api.example.com/sub/app/api/com.example.xyz/
|
||||||
if (!apiApp) {
|
if (!apiApp) {
|
||||||
|
@ -199,8 +216,18 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// TODO assets.example.com/sub/assets/com.example.xyz/
|
||||||
|
if (/^assets\./.test(req.hostname) && /\/assets(\/|$)/.test(req.url)) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// There may be some app folders named 'apple.com', 'apple.com#foo', and 'apple.com#foo#bar'
|
||||||
|
// Here we're sorting an appId broken into parts like [ 'apple.com', 'foo', 'bar' ]
|
||||||
|
// and wer're checking to see if this is perhaps '/' of 'apple.com/foo/bar' or '/foo/bar' of 'apple.com', etc
|
||||||
while (appIdParts.length) {
|
while (appIdParts.length) {
|
||||||
// TODO needs IPC to expire cache
|
// TODO needs IPC to expire cache when an API says the app mounts have been updated
|
||||||
appIdPart = appIdParts.join('#');
|
appIdPart = appIdParts.join('#');
|
||||||
if (localCache.statics[appIdPart]) {
|
if (localCache.statics[appIdPart]) {
|
||||||
break;
|
break;
|
||||||
|
@ -211,18 +238,58 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!appIdPart || !localCache.statics[appIdPart]) {
|
if (!appIdPart || !localCache.statics[appIdPart]) {
|
||||||
return staticHelper(appId).then(function () {
|
console.log('[serveStaticHelper] appId', appId);
|
||||||
localCache.statics[appId].handler(req, res, next);
|
return staticHelper(appId).then(function (webapp) {
|
||||||
|
//localCache.statics[appId].handler(req, res, next);
|
||||||
|
webapp.handler(req, res, next);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('[serveStaticHelper] appIdPart', appIdPart);
|
||||||
|
if (opts && opts.rewrite && -1 !== req.url.indexOf(appIdPart.replace(/#/, '/').replace(/\/$/, ''))) {
|
||||||
|
console.log('[staticHelper ReWrite]', req.url.slice(req.url.indexOf(appIdPart.replace(/#/, '/').replace(/\/$/, '')) + appIdPart.replace(/\/$/, '').length));
|
||||||
|
req.url = req.url.slice(req.url.indexOf(appIdPart.replace(/#/, '/').replace(/\/$/, '')) + appIdPart.replace(/\/$/, '').length);
|
||||||
|
if (0 !== req.url.indexOf('/')) {
|
||||||
|
req.url = '/' + req.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
localCache.statics[appIdPart].handler(req, res, next);
|
localCache.statics[appIdPart].handler(req, res, next);
|
||||||
if (Date.now() - localCache.statics[appIdPart].createdAt > (5 * 60 * 1000)) {
|
if (Date.now() - localCache.statics[appIdPart].createdAt > (5 * 60 * 1000)) {
|
||||||
staticHelper(appId, { clear: true });
|
staticHelper(appId, { clear: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function serveStatic(req, res, next) {
|
||||||
|
// We convert the URL that was sent in the browser bar from
|
||||||
|
// 'https://domain.tld/foo/bar' to 'domain.tld#foo#bar'
|
||||||
|
var appId = req.hostname + req.url.replace(/\/+/g, '#').replace(/#$/, '');
|
||||||
|
serveStaticHelper(appId, null, req, res, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
function serveApps(req, res, next) {
|
||||||
|
var appId = req.url.slice(1).replace(/\/+/g, '#').replace(/#$/, '');
|
||||||
|
|
||||||
|
if (/^apps\./.test(req.hostname)) {
|
||||||
|
appId = appId.replace(/^apps#/, '');
|
||||||
|
} else if (/\bapps#/.test(appId)) {
|
||||||
|
appId = appId.replace(/.*\bapps#/, '');
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[serveApps] appId', appId);
|
||||||
|
serveStaticHelper(appId, { rewrite: true }, req, res, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO set header 'X-ExperienceId: domain.tld/sub/path'
|
||||||
|
// This would let an app know whether its app is 'domain.tld' with a path of '/sub/path'
|
||||||
|
// or if its app is 'domain.tld/sub' with a path of '/path'
|
||||||
|
|
||||||
|
// TODO handle assets.example.com/sub/assets/com.example.xyz/
|
||||||
|
|
||||||
app.use('/', serveStatic);
|
app.use('/', serveStatic);
|
||||||
|
app.use('/', serveApps);
|
||||||
|
|
||||||
return PromiseA.resolve();
|
return PromiseA.resolve();
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue