| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var escapeStringRegexp = require('escape-string-regexp'); | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | var runApi = require('./package-server-apis').runApi; | 
					
						
							|  |  |  | var layerItUp = require('./package-server-static').layerItUp; | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | function compileVhosts(vhostsMap) { | 
					
						
							|  |  |  |   var results = { | 
					
						
							|  |  |  |     patterns: [] | 
					
						
							|  |  |  |   , conflictsMap: {} | 
					
						
							|  |  |  |   , matchesMap: {} | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // compli
 | 
					
						
							|  |  |  |   Object.keys(vhostsMap).forEach(function (key) { | 
					
						
							|  |  |  |     var vhost = vhostsMap[key]; | 
					
						
							|  |  |  |     var bare; | 
					
						
							|  |  |  |     var www; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ('.' === vhost.hostname[0]) { | 
					
						
							|  |  |  |       // for consistency
 | 
					
						
							|  |  |  |       // TODO this should happen at the database level
 | 
					
						
							|  |  |  |       vhost.hostname = '*' + vhost.hostname; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ('*' === vhost.hostname[0]) { | 
					
						
							|  |  |  |       // TODO check that we are not trying to redirect a tld (.com, .co.uk, .org, etc)
 | 
					
						
							|  |  |  |       // tlds should follow the global policy
 | 
					
						
							|  |  |  |       if (vhost.hostname[1] && '.' !== vhost.hostname[1]) { | 
					
						
							|  |  |  |         // this is not a good place to throw as the consequences of a bug would be
 | 
					
						
							|  |  |  |         // very bad, but errors should never be silent, so we'll compromise
 | 
					
						
							|  |  |  |         console.warn("[NON-FATAL ERROR]: ignoring pattern '" + vhost.hostname + "'"); | 
					
						
							|  |  |  |         results.conflictsMap[vhost.hostname] = vhost; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // nix the '*' for easier matching
 | 
					
						
							|  |  |  |       vhost.hostname = vhost.hostname.slice(1); | 
					
						
							|  |  |  |       // except the default
 | 
					
						
							|  |  |  |       if (!vhost.hostname) { | 
					
						
							|  |  |  |         vhost.hostname = '*'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (results.conflictsMap[vhost.hostname]) { | 
					
						
							|  |  |  |         console.warn("[NON-FATAL ERROR]: duplicate entry for pattern '" + vhost.hostname + "'"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       results.conflictsMap[vhost.hostname] = vhost; | 
					
						
							|  |  |  |       results.patterns.push(vhost); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bare = vhost.hostname.replace(/^www\./i, ''); | 
					
						
							|  |  |  |     www = vhost.hostname.replace(/^(www\.)?/i, 'www.'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     results.matchesMap[bare] = vhost; | 
					
						
							|  |  |  |     results.matchesMap[www] = vhost; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   results.patterns.sort(function (a, b) { | 
					
						
							|  |  |  |     return b.id.length - a.id.length; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return results; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function mapToApp(opts, req, res, next) { | 
					
						
							|  |  |  |   // opts = { config, deps, services }
 | 
					
						
							|  |  |  |   var vhost; | 
					
						
							|  |  |  |   var router; | 
					
						
							|  |  |  |   var pkgConf = opts.config; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!pkgConf.vhostConf) { | 
					
						
							|  |  |  |     pkgConf.vhostConf = compileVhosts(pkgConf.vhostsMap); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //console.log('req.hostname');
 | 
					
						
							|  |  |  |   //console.log(req.hostname);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //console.log(Object.keys(pkgConf.vhostConf.matchesMap));
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // TODO www vs no-www?
 | 
					
						
							|  |  |  |   vhost = pkgConf.vhostConf.matchesMap[req.hostname]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!vhost) { | 
					
						
							|  |  |  |     pkgConf.vhostConf.patterns.some(function (pkg) { | 
					
						
							| 
									
										
										
										
											2015-11-23 10:22:04 +00:00
										 |  |  |       // TODO this should be done in the compile phase
 | 
					
						
							|  |  |  |       if ('*' === pkg.id[0] && '.' === pkg.id[1]) { | 
					
						
							|  |  |  |         pkg.id = pkg.id.slice(1); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (pkg.id === req.hostname.slice(req.hostname.length - pkg.id.length)) { | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |         vhost = pkg; | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!vhost) { | 
					
						
							|  |  |  |     next(); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // TODO don't modify route here (or in subloaders), modify some other variable instead
 | 
					
						
							|  |  |  |   // TODO precompile RegExps and pre-sort app vs api
 | 
					
						
							|  |  |  |   vhost.pathnames.some(function (routes) { | 
					
						
							|  |  |  |     var pathname = routes.pathname; | 
					
						
							|  |  |  |     if ('/' === pathname) { | 
					
						
							|  |  |  |       pathname = ''; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!routes._re_app) { | 
					
						
							|  |  |  |       routes._re_app = new RegExp(escapeStringRegexp(pathname) + '(#|\\/|\\?|$)'); | 
					
						
							|  |  |  |       //console.log('[static re]', routes._re_app);
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!routes._re_api) { | 
					
						
							|  |  |  |       // TODO allow for special apis that do not follow convention (.well_known, webfinger, oauth3.html, etc)
 | 
					
						
							|  |  |  |       routes._re_api = new RegExp(escapeStringRegexp(pathname + '/api/') + '([\\w\\.\\-]+)(\\/|\\?|$)'); | 
					
						
							|  |  |  |       //console.log('[api re]', routes._re_api);
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (routes._re_app.test(req.url)) { | 
					
						
							|  |  |  |       router = routes; | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // no need to test for api yet as it is a postfix
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (!router) { | 
					
						
							|  |  |  |     //console.log('[no router for]', req.url);
 | 
					
						
							|  |  |  |     next(); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-11 05:54:30 +00:00
										 |  |  |   // TODO .well-known can be an API (webfinger, letsencrypt, oauth3)
 | 
					
						
							|  |  |  |   // or static (...???)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |   if (!router._re_api.test(req.url)) { | 
					
						
							|  |  |  |     //console.log('[static router]');
 | 
					
						
							|  |  |  |     //console.log(router._re_api, req.url);
 | 
					
						
							| 
									
										
										
										
											2015-12-11 05:54:30 +00:00
										 |  |  |     layerItUp(pkgConf, router, req, res, function (err) { | 
					
						
							|  |  |  |       if (err) { | 
					
						
							|  |  |  |         next(err); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (/\/\.well-known([\/?]|$)/.test(req.url)) { | 
					
						
							|  |  |  |         console.log('[TODO] handle .well-known as API'); | 
					
						
							|  |  |  |         // rewrite api as /api/org.ietf/.well-known ?
 | 
					
						
							|  |  |  |         // pass through simply as /.well-known ?
 | 
					
						
							|  |  |  |         // runApi(opts, router, req, res, next)
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       next(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2015-11-21 12:36:22 +00:00
										 |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   //console.log('[api router]', req.url);
 | 
					
						
							|  |  |  |   return runApi(opts, router, req, res, next); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports.compileVhosts = compileVhosts; | 
					
						
							|  |  |  | module.exports.mapToApp = mapToApp; |