'use strict'; module.exports = function (opts) { var express = require('express'); //var finalhandler = require('finalhandler'); var serveStatic = require('serve-static'); var serveIndex = require('serve-index'); //var assetServer = serveStatic(opts.assetsPath); var path = require('path'); //var wellKnownServer = serveStatic(path.join(opts.assetsPath, 'well-known')); var serveStaticMap = {}; var serveIndexMap = {}; var content = opts.content; //var server; var serveInit; var app; function _reloadWrite(data, enc, cb) { /*jshint validthis: true */ if (this.headersSent) { this.__write(data, enc, cb); return; } if (!/html/i.test(this.getHeader('Content-Type'))) { this.__write(data, enc, cb); return; } if (this.getHeader('Content-Length')) { this.setHeader('Content-Length', this.getHeader('Content-Length') + this.__my_addLen); } this.__write(this.__my_livereload); this.__write(data, enc, cb); } function createServeInit() { var PromiseA = require('bluebird'); var fs = PromiseA.promisifyAll(require('fs')); var ownersPath = path.join(__dirname, '..', 'var', 'owners.json'); return require('../packages/apis/com.daplie.caddy').create({ PromiseA: PromiseA , storage: { owners: { all: function () { var owners; try { owners = require(ownersPath); } catch(e) { owners = {}; } return PromiseA.resolve(Object.keys(owners).map(function (key) { var owner = owners[key]; owner.id = key; return owner; })); } , set: function (id, obj) { var owners; try { owners = require(ownersPath); } catch(e) { owners = {}; } obj.id = id; owners[id] = obj; return fs.writeFileAsync(ownersPath, JSON.stringify(owners), 'utf8'); } } } , recase: require('recase').create({}) , request: PromiseA.promisify(require('request')) , options: 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')); return; } if (0 === req.url.indexOf('/api/com.daplie.caddy/')) { if (!serveInit) { serveInit = createServeInit(); } } if ('/api/com.daplie.caddy/init' === req.url) { serveInit.init(req, res); return; } if ('/api/com.daplie.caddy/config' === req.url) { serveInit.config(req, res); return; } if ('/api/com.daplie.caddy/request' === req.url) { serveInit.request(req, res); return; } if (content && '/' === req.url) { // res.setHeader('Content-Type', 'application/octet-stream'); res.end(content); return; } //var done = finalhandler(req, res); var host = req.headers.host; var hostname = (host||'').split(':')[0].toLowerCase(); console.log('opts.global', opts.global); var sites = [ opts.global || null, opts.sites._map[hostname] || null, opts.defaults || null ]; var loadables = { serve: function (config, hostname, pathname, req, res, next) { var originalUrl = req.url; var dirpaths = config.paths.slice(0); function nextServe() { 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); } serveStaticMap[dirname](req, res, nextServe); } req.url = req.url.substr(pathname.length - 1); nextServe(); } , indexes: function (config, hostname, pathname, req, res, next) { var originalUrl = req.url; var dirpaths = config.paths.slice(0); function nextIndex() { 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); } serveIndexMap[dirname](req, res, nextIndex); } 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(module, hostname, pathname, modulename, req, res, next) { if (!loadables[modulename]) { next(new Error("no module '" + modulename + "' found")); return; } loadables[modulename](module, hostname, pathname, req, res, next); } function iterModules(modules, hostname, pathname, req, res, next) { console.log('modules'); console.log(modules); var modulenames = Object.keys(modules._map); function nextModule() { var modulename = modulenames.pop(); if (!modulename) { next(); return; } console.log('modules', modules); runModule(modules._map[modulename], hostname, pathname, modulename, req, res, nextModule); } nextModule(); } function iterPaths(site, hostname, req, res, next) { 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)); //return req.url.match(pathname); }); pathnames.sort(function (a, b) { return b.length - a.length; }); console.log('pathnames', pathnames); function nextPath() { var pathname = pathnames.shift(); if (!pathname) { next(); return; } console.log('iterPaths', hostname, pathname, req.url); iterModules(site.paths._map[pathname].modules, hostname, pathname, req, res, nextPath); } nextPath(); } function nextSite() { 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); } nextSite(); /* function serveStaticly(server) { function serveTheStatic() { server.serve(req, res, function (err) { if (err) { return done(err); } server.index(req, res, function (err) { if (err) { return done(err); } req.url = req.url.replace(/\/assets/, ''); assetServer(req, res, function () { if (err) { return done(err); } req.url = req.url.replace(/\/\.well-known/, ''); wellKnownServer(req, res, done); }); }); }); } if (server.expressApp) { server.expressApp(req, res, serveTheStatic); return; } serveTheStatic(); } if (opts.livereload) { res.__my_livereload = ''; res.__my_addLen = res.__my_livereload.length; // TODO modify prototype instead of each instance? res.__write = res.write; res.write = _reloadWrite; } console.log('hostname:', hostname, opts.sites[0].paths); addServer(hostname); server = hostsMap[hostname] || hostsMap[opts.sites[0].name]; serveStaticly(server); */ }); };