forked from coolaj86/goldilocks.js
		
	
		
			
				
	
	
		
			352 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			352 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| '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({})
 | |
|     , 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 (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 = '<script src="//'
 | |
|         + (host || opts.sites[0].name).split(':')[0]
 | |
|         + ':35729/livereload.js?snipver=1"></script>';
 | |
|       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);
 | |
|     */
 | |
|   });
 | |
| };
 |