+
diff --git a/bin/goldilocks.js b/bin/goldilocks.js
index e7f8808..9fd2119 100755
--- a/bin/goldilocks.js
+++ b/bin/goldilocks.js
@@ -94,20 +94,10 @@ function createServer(port, _delete_me_, content, opts) {
var app = require('../lib/app.js');
var directive = {
- content: content
- , livereload: opts.livereload
- , global: {
- greenlock: { email: opts.email, tos: opts.tos }
- , rvpn: { email: opts.email, tos: opts.tos }
- , paths: {
- '/assets/': { serve: [ opts.assetsPath ] }
- // TODO figure this b out
- , '/.well-known/': { serve: [ path.resolve(opts.assetsPath, 'well-known') ] }
- , '/': { serve: [ opts.webRoot ], indexes: [ opts.webRoot ] }
- }
- }
+ global: opts.global
, sites: opts.sites
- , expressApp: opts.expressApp
+ , defaults: opts.defaults
+ , cwd: process.cwd()
};
var server;
var insecureServer;
@@ -260,13 +250,15 @@ function run() {
var argv = minimist(process.argv.slice(2));
var port = parseInt(argv.p || argv.port || argv._[0], 10) || httpsPort;
var livereload = argv.livereload;
- var defaultWebRoot = path.resolve(argv['default-web-root'] || argv.d || argv._[1] || process.cwd());
+ var defaultWebRoot = path.normalize(argv['default-web-root'] || argv.d || argv._[1] || '.');
+ var assetsPath = path.join(__dirname, '..', 'packages', 'assets');
var content = argv.c;
var letsencryptHost = argv['letsencrypt-certs'];
var yaml = require('js-yaml');
var fs = PromiseA.promisifyAll(require('fs'));
var configFile = argv.c || argv.conf || argv.config;
var config;
+ console.log('defaultWebRoot', defaultWebRoot);
try {
config = fs.readFileSync(configFile || 'Goldilocks.yml');
@@ -383,7 +375,8 @@ function run() {
opts.cwd = process.cwd();
- opts.sites = {};
+ opts.sites = [];
+ opts.sites._map = {};
if (argv.sites) {
opts._externalHost = false;
@@ -394,48 +387,86 @@ function run() {
opts._externalHost = opts._externalHost || !/(^|\.)localhost\./.test(servername);
// TODO allow reverse proxy
- if (!opts.sites[servername]) {
- opts.sites[servername] = { paths: {} };
+ if (!opts.sites._map[servername]) {
+ opts.sites._map[servername] = { $id: servername, paths: [] };
+ opts.sites._map[servername].paths._map = {};
+ opts.sites.push(opts.sites._map[servername]);
}
if (!nameparts.length) {
return;
}
- if (!opts.sites[servername].paths['/']) {
- opts.sites[servername].paths['/'] = {};
+ if (!opts.sites._map[servername].paths._map['/']) {
+ opts.sites._map[servername].paths._map['/'] = { $id: '/', modules: [] };
+ opts.sites._map[servername].paths.push(opts.sites._map[servername].paths._map['/']);
}
- modules = opts.sites[servername].paths['/'];
- modules.serve = nameparts;
- modules.indexes = nameparts;
+ modules = opts.sites._map[servername].paths._map['/'].modules;
+ modules.push({
+ $id: 'serve'
+ , paths: nameparts
+ });
+ modules.push({
+ $id: 'indexes'
+ , paths: nameparts
+ });
});
}
- opts.groups = {};
+ opts.groups = [];
// 'packages', 'assets', 'com.daplie.caddy'
- opts.sites['localhost.alpha.daplie.me'] = {
+ opts.global = {
+ modules: [ // TODO uh-oh we've got a mixed bag of modules (various types), a true map
+ { $id: 'greenlock', email: opts.email, tos: opts.tos }
+ , { $id: 'rvpn', email: opts.email, tos: opts.tos }
+ , { $id: 'content', content: content }
+ , { $id: 'livereload', on: opts.livereload }
+ , { $id: 'app', path: opts.expressApp }
+ ]
+ , paths: [
+ { $id: '/assets/', modules: [ { $id: 'serve', paths: [ assetsPath ] } ] }
+ // TODO figure this b out
+ , { $id: '/.well-known/', modules: [
+ { $id: 'serve', paths: [ path.join(assetsPath, 'well-known') ] }
+ ] }
+ ]
+ };
+ opts.defaults = {
+ modules: []
+ , paths: [
+ { $id: '/', modules: [
+ { $id: 'serve', paths: [ defaultWebRoot ] }
+ , { $id: 'indexes', paths: [ defaultWebRoot ] }
+ ] }
+ ]
+ };
+ opts.sites.push({
// greenlock: {}
- paths: {
- '/': { serve: [ path.resolve(__dirname, '..', 'admin', 'public') ] }
- , '/api/': { app: path.join(__dirname, 'admin') }
- }
- };
- opts.sites['localhost.daplie.invalid'] = {
- paths: {
- '/': { serve: [ path.resolve(__dirname, '..', 'admin', 'public') ] }
- , '/api/': { app: path.join(__dirname, 'admin') }
- }
- };
- opts.assetsPath = path.join(__dirname, '..', 'packages', 'assets');
- opts.webRoot = defaultWebRoot;
+ $id: 'localhost.alpha.daplie.me'
+ , paths: [
+ { $id: '/', modules: [
+ { $id: 'serve', paths: [ path.resolve(__dirname, '..', 'admin', 'public') ] }
+ ] }
+ , { $id: '/api/', modules: [
+ { $id: 'app', path: path.join(__dirname, 'admin') }
+ ] }
+ ]
+ });
+ opts.sites.push({
+ $id: 'localhost.daplie.invalid'
+ , paths: [
+ { $id: '/', modules: [ { $id: 'serve', paths: [ path.resolve(__dirname, '..', 'admin', 'public') ] } ] }
+ , { $id: '/api/', modules: [ { $id: 'app', path: path.join(__dirname, 'admin') } ] }
+ ]
+ });
// ifaces
opts.ifaces = require('../lib/local-ip.js').find();
// TODO use arrays in all things
- opts._old_server_name = Object.keys(opts.sites)[0];
+ opts._old_server_name = opts.sites[0].$id;
opts.pubdir = defaultWebRoot.replace(/(:hostname|:servername).*/, '');
if (argv.p || argv.port || argv._[0]) {
@@ -454,7 +485,7 @@ function run() {
opts.livereload = livereload;
if (argv['express-app']) {
- opts.expressApp = require(path.resolve(process.cwd(), argv['express-app']));
+ opts.expressApp = require(argv['express-app']);
}
if (opts.email || opts._externalHost) {
diff --git a/lib/app.js b/lib/app.js
index 7a5f38b..741f948 100644
--- a/lib/app.js
+++ b/lib/app.js
@@ -81,6 +81,69 @@ module.exports = function (opts) {
app = express();
+ if (!opts.sites) {
+ opts.sites = [];
+ }
+ opts.sites._map = {};
+ opts.sites.forEach(function (site) {
+
+ if (!opts.sites._map[site.$id]) {
+ opts.sites._map[site.$id] = site;
+ }
+
+ if (!site.paths) {
+ site.paths = [];
+ }
+ if (!site.paths._map) {
+ site.paths._map = {};
+ }
+ site.paths.forEach(function (path) {
+
+ site.paths._map[path.$id] = path;
+
+ if (!path.modules) {
+ path.modules = [];
+ }
+ if (!path.modules._map) {
+ path.modules._map = {};
+ }
+ path.modules.forEach(function (module) {
+
+ path.modules._map[module.$id] = module;
+ });
+ });
+ });
+
+ function mapMap(el, i, arr) {
+ arr._map[el.$id] = el;
+ }
+ opts.global.modules._map = {};
+ opts.global.modules.forEach(mapMap);
+ opts.global.paths._map = {};
+ opts.global.paths.forEach(function (path, i, arr) {
+ mapMap(path, i, arr);
+ //opts.global.paths._map[path.$id] = path;
+ path.modules._map = {};
+ path.modules.forEach(mapMap);
+ });
+ opts.sites.forEach(function (site) {
+ site.paths._map = {};
+ site.paths.forEach(function (path, i, arr) {
+ mapMap(path, i, arr);
+ //site.paths._map[path.$id] = path;
+ path.modules._map = {};
+ path.modules.forEach(mapMap);
+ });
+ });
+ opts.defaults.modules._map = {};
+ opts.defaults.modules.forEach(mapMap);
+ opts.defaults.paths._map = {};
+ opts.defaults.paths.forEach(function (path, i, arr) {
+ mapMap(path, i, arr);
+ //opts.global.paths._map[path.$id] = path;
+ path.modules._map = {};
+ path.modules.forEach(mapMap);
+ });
return app.use('/', function (req, res, next) {
if (!req.headers.host) {
next(new Error('missing HTTP Host header'));
@@ -112,21 +175,22 @@ module.exports = function (opts) {
var hostname = (host||'').split(':')[0].toLowerCase();
console.log('opts.global', opts.global);
- var sites = [ opts.global || {}, opts.sites[hostname] || {}, opts.defer || {} ];
+ var sites = [ opts.global || null, opts.sites._map[hostname] || null, opts.defaults || null ];
var loadables = {
serve: function (config, hostname, pathname, req, res, next) {
- config = config.slice(0);
var originalUrl = req.url;
+ var dirpaths = config.paths.slice(0);
function nextServe() {
- var dirname = config.pop();
- console.log('[serve]', req.url, hostname, pathname, dirname);
+ var dirname = dirpaths.pop();
if (!dirname) {
req.url = originalUrl;
next();
return;
}
+ console.log('[serve]', req.url, hostname, pathname, dirname);
+ dirname = path.resolve(opts.cwd, dirname.replace(/:hostname/, hostname));
if (!serveStaticMap[dirname]) {
serveStaticMap[dirname] = serveStatic(dirname);
}
@@ -138,18 +202,19 @@ module.exports = function (opts) {
nextServe();
}
, indexes: function (config, hostname, pathname, req, res, next) {
- config = config.slice(0);
var originalUrl = req.url;
+ var dirpaths = config.paths.slice(0);
function nextIndex() {
- var dirname = config.pop();
- console.log('[indexes]', req.url, hostname, pathname, dirname);
+ var dirname = dirpaths.pop();
if (!dirname) {
req.url = originalUrl;
next();
return;
}
+ console.log('[indexes]', req.url, hostname, pathname, dirname);
+ dirname = path.resolve(opts.cwd, dirname.replace(/:hostname/, hostname));
if (!serveStaticMap[dirname]) {
serveIndexMap[dirname] = serveIndex(dirname);
}
@@ -159,18 +224,26 @@ module.exports = function (opts) {
req.url = req.url.substr(pathname.length - 1);
nextIndex();
}
+ , app: function (config, hostname, pathname, req, res, next) {
+ //var appfile = path.resolve(/*process.cwd(), */config.path.replace(/:hostname/, hostname));
+ var appfile = config.path.replace(/:hostname/, hostname);
+ var app = require(appfile);
+ app(req, res, next);
+ }
};
- function runModule(config, hostname, pathname, modulename, req, res, next) {
+ function runModule(module, hostname, pathname, modulename, req, res, next) {
if (!loadables[modulename]) {
next(new Error("no module '" + modulename + "' found"));
return;
}
- loadables[modulename](config, hostname, pathname, req, res, next);
+ loadables[modulename](module, hostname, pathname, req, res, next);
}
function iterModules(modules, hostname, pathname, req, res, next) {
- var modulenames = Object.keys(modules);
+ console.log('modules');
+ console.log(modules);
+ var modulenames = Object.keys(modules._map);
function nextModule() {
var modulename = modulenames.pop();
@@ -180,14 +253,17 @@ module.exports = function (opts) {
}
console.log('modules', modules);
- runModule(modules[modulename], hostname, pathname, modulename, req, res, nextModule);
+ runModule(modules._map[modulename], hostname, pathname, modulename, req, res, nextModule);
}
nextModule();
}
function iterPaths(site, hostname, req, res, next) {
- var pathnames = Object.keys(site.paths || {});
+ console.log('site', hostname);
+ console.log(site);
+ var pathnames = Object.keys(site.paths._map);
+ console.log('pathnames', pathnames);
pathnames = pathnames.filter(function (pathname) {
// TODO ensure that pathname has trailing /
return (0 === req.url.indexOf(pathname));
@@ -196,27 +272,34 @@ module.exports = function (opts) {
pathnames.sort(function (a, b) {
return b.length - a.length;
});
+ console.log('pathnames', pathnames);
function nextPath() {
- var pathname = pathnames.pop();
+ var pathname = pathnames.shift();
if (!pathname) {
next();
return;
}
console.log('iterPaths', hostname, pathname, req.url);
- iterModules(site.paths[pathname], hostname, pathname, req, res, nextPath);
+ iterModules(site.paths._map[pathname].modules, hostname, pathname, req, res, nextPath);
}
nextPath();
}
function nextSite() {
- var site = sites.pop();
- if (!site) {
+ console.log('hostname', hostname, sites);
+ var site;
+ if (!sites.length) {
next(); // 404
return;
}
+ site = sites.shift();
+ if (!site) {
+ nextSite();
+ return;
+ }
iterPaths(site, hostname, req, res, nextSite);
}