| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports.create = function (xconfx, apiFactories, apiDeps) { | 
					
						
							|  |  |  |   var PromiseA = apiDeps.Promise; | 
					
						
							| 
									
										
										
										
											2017-05-26 20:23:17 +00:00
										 |  |  |   var mkdirpAsync = PromiseA.promisify(require('mkdirp')); | 
					
						
							| 
									
										
										
										
											2017-05-24 04:27:52 +00:00
										 |  |  |   //var express = require('express');
 | 
					
						
							|  |  |  |   var express = require('express-lazy'); | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  |   var fs = PromiseA.promisifyAll(require('fs')); | 
					
						
							|  |  |  |   var path = require('path'); | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |   var localCache = { rests: {}, pkgs: {} }; | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // TODO xconfx.apispath
 | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |   xconfx.restPath = path.join(__dirname, '..', '..', 'packages', 'rest'); | 
					
						
							| 
									
										
										
										
											2017-05-27 01:17:50 +00:00
										 |  |  |   xconfx.apiPath = path.join(__dirname, '..', '..', 'packages', 'api'); | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |   xconfx.appApiGrantsPath = path.join(__dirname, '..', '..', 'packages', 'client-api-grants'); | 
					
						
							| 
									
										
										
										
											2017-05-24 07:34:34 +00:00
										 |  |  |   xconfx.appConfigPath = path.join(__dirname, '..', '..', 'var'); | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |   function notConfigured(req, res) { | 
					
						
							| 
									
										
										
										
											2017-05-20 04:51:44 +00:00
										 |  |  |     var msg = "api package '" + req.pkgId + "' not configured for client uri '" + req.experienceId + "'" | 
					
						
							|  |  |  |       + ". To configure it place a new line '" + req.pkgId + "' in the file '/srv/walnut/packages/client-api-grants/" + req.experienceId + "'" | 
					
						
							|  |  |  |       ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     res.send({ error: { message: msg } }); | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |   /* | 
					
						
							|  |  |  |   function isThisPkgInstalled(myConf, pkgId) { | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |   */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function isThisClientAllowedToUseThisPkg(myConf, clientUrih, pkgId) { | 
					
						
							|  |  |  |     var appApiGrantsPath = path.join(myConf.appApiGrantsPath, clientUrih); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return fs.readFileAsync(appApiGrantsPath, 'utf8').then(function (text) { | 
					
						
							|  |  |  |       return text.trim().split(/\n/); | 
					
						
							| 
									
										
										
										
											2017-05-20 04:51:44 +00:00
										 |  |  |     }, function (err) { | 
					
						
							|  |  |  |       if ('ENOENT' !== err.code) { | 
					
						
							|  |  |  |         console.error(err); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       return []; | 
					
						
							|  |  |  |     }).then(function (apis) { | 
					
						
							| 
									
										
										
										
											2017-05-20 00:09:48 +00:00
										 |  |  |       if (apis.some(function (api) { | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |         if (api === pkgId) { | 
					
						
							|  |  |  |           return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       })) { | 
					
						
							| 
									
										
										
										
											2017-05-20 00:09:48 +00:00
										 |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |         if (clientUrih === ('api.' + xconfx.setupDomain) && 'org.oauth3.consumer' === pkgId) { | 
					
						
							|  |  |  |           // fallthrough
 | 
					
						
							|  |  |  |           return true; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           return null; | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |   function getSitePackageConfig(clientUrih, pkgId) { | 
					
						
							|  |  |  |     var siteConfigPath = path.join(xconfx.appConfigPath, clientUrih); | 
					
						
							|  |  |  |     return mkdirpAsync(siteConfigPath).then(function () { | 
					
						
							|  |  |  |       return fs.readFileAsync(path.join(siteConfigPath, pkgId + '.json'), 'utf8').then(function (text) { | 
					
						
							|  |  |  |         return JSON.parse(text); | 
					
						
							|  |  |  |       }).then(function (data) { return data; }, function (/*err*/) { return {}; }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-24 07:34:34 +00:00
										 |  |  |   function getSiteConfig(clientUrih) { | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |     // TODO test if the requesting package has permission to the root-level site config
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:23:17 +00:00
										 |  |  |     var siteConfigPath = path.join(xconfx.appConfigPath, clientUrih); | 
					
						
							|  |  |  |     return mkdirpAsync(siteConfigPath).then(function () { | 
					
						
							|  |  |  |       return fs.readFileAsync(path.join(siteConfigPath, 'config.json'), 'utf8').then(function (text) { | 
					
						
							|  |  |  |         return JSON.parse(text); | 
					
						
							|  |  |  |       }).then(function (data) { return data; }, function (/*err*/) { return {}; }); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-05-24 07:34:34 +00:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 22:06:21 +00:00
										 |  |  |   var modelsCache = {}; | 
					
						
							|  |  |  |   function getSiteStore(clientUrih, pkgId, dir) { | 
					
						
							| 
									
										
										
										
											2017-05-30 01:39:21 +00:00
										 |  |  |     if (!modelsCache[clientUrih]) { | 
					
						
							|  |  |  |       modelsCache[clientUrih] = apiFactories.systemSqlFactory.create({ | 
					
						
							|  |  |  |         init: true | 
					
						
							|  |  |  |       , dbname: clientUrih // '#' is a valid file name character
 | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-05-26 22:06:21 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // DB scopes:
 | 
					
						
							|  |  |  |     // system (global)
 | 
					
						
							|  |  |  |     // experience (per domain)
 | 
					
						
							|  |  |  |     // api (per api)
 | 
					
						
							|  |  |  |     // account (per user account)
 | 
					
						
							|  |  |  |     // client (per 3rd party client)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // scope Experience to db
 | 
					
						
							|  |  |  |     // scope Api by table
 | 
					
						
							|  |  |  |     // scope Account and Client by column
 | 
					
						
							| 
									
										
										
										
											2017-05-30 01:39:21 +00:00
										 |  |  |     return modelsCache[clientUrih].then(function (db) { | 
					
						
							| 
									
										
										
										
											2017-05-26 22:06:21 +00:00
										 |  |  |       var wrap = require('masterquest-sqlite3'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return wrap.wrap(db, dir).then(function (models) { | 
					
						
							| 
									
										
										
										
											2017-05-30 01:39:21 +00:00
										 |  |  |         //modelsCache[clientUrih] = PromiseA.resolve(models);
 | 
					
						
							| 
									
										
										
										
											2017-05-26 22:06:21 +00:00
										 |  |  |         return models; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |   function loadRestHelper(myConf, clientUrih, pkgId) { | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |     var pkgPath = path.join(myConf.restPath, pkgId); | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |     var pkgLinks = []; | 
					
						
							|  |  |  |     pkgLinks.push(pkgId); | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |     // TODO allow recursion, but catch cycles
 | 
					
						
							|  |  |  |     return fs.lstatAsync(pkgPath).then(function (stat) { | 
					
						
							|  |  |  |       if (!stat.isFile()) { | 
					
						
							|  |  |  |         return; | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |       return fs.readFileAsync(pkgPath, 'utf8').then(function (text) { | 
					
						
							|  |  |  |         pkgId = text.trim(); | 
					
						
							|  |  |  |         pkgPath = path.join(myConf.restPath, pkgId); | 
					
						
							| 
									
										
										
										
											2017-05-20 05:16:01 +00:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |     }, function () { | 
					
						
							|  |  |  |       // ignore error
 | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     }).then(function () { | 
					
						
							|  |  |  |       // TODO should not require package.json. Should work with files alone.
 | 
					
						
							|  |  |  |       return fs.readFileAsync(path.join(pkgPath, 'package.json'), 'utf8').then(function (text) { | 
					
						
							|  |  |  |         var pkg = JSON.parse(text); | 
					
						
							|  |  |  |         var pkgDeps = {}; | 
					
						
							|  |  |  |         var myApp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (pkg.walnut) { | 
					
						
							|  |  |  |           pkgPath = path.join(pkgPath, pkg.walnut); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |         Object.keys(apiDeps).forEach(function (key) { | 
					
						
							|  |  |  |           pkgDeps[key] = apiDeps[key]; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         Object.keys(apiFactories).forEach(function (key) { | 
					
						
							|  |  |  |           pkgDeps[key] = apiFactories[key]; | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |         // TODO pull db stuff from package.json somehow and pass allowed data models as deps
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         // how can we tell which of these would be correct?
 | 
					
						
							|  |  |  |         // deps.memstore = apiFactories.memstoreFactory.create(pkgId);
 | 
					
						
							|  |  |  |         // deps.memstore = apiFactories.memstoreFactory.create(req.experienceId);
 | 
					
						
							|  |  |  |         // deps.memstore = apiFactories.memstoreFactory.create(req.experienceId + pkgId);
 | 
					
						
							| 
									
										
										
										
											2017-05-26 21:10:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |         // let's go with this one for now and the api can choose to scope or not to scope
 | 
					
						
							|  |  |  |         pkgDeps.memstore = apiFactories.memstoreFactory.create(pkgId); | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |         console.log('DEBUG pkgPath', pkgPath); | 
					
						
							|  |  |  |         myApp = express(); | 
					
						
							| 
									
										
										
										
											2017-05-29 18:35:59 +00:00
										 |  |  |         myApp.handlePromise = require('./common').promisableRequest; | 
					
						
							|  |  |  |         myApp.handleRejection = require('./common').rejectableRequest; | 
					
						
							| 
									
										
										
										
											2017-05-29 21:39:52 +00:00
										 |  |  |         myApp.grantsRequired = function (grants) { | 
					
						
							|  |  |  |           if (!Array.isArray(grants)) { | 
					
						
							|  |  |  |             throw new Error("Usage: app.grantsRequired([ 'name|altname|altname2', 'othergrant' ])"); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (!grants.length) { | 
					
						
							|  |  |  |             return function (req, res, next) { | 
					
						
							|  |  |  |               next(); | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return function (req, res, next) { | 
					
						
							|  |  |  |             if (!(req.oauth3 || req.oauth3.token)) { | 
					
						
							|  |  |  |               // TODO some error generator for standard messages
 | 
					
						
							|  |  |  |               res.send({ error: { message: "You must be logged in", code: "E_NO_AUTHN" } }); | 
					
						
							|  |  |  |               return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-05-30 01:21:43 +00:00
										 |  |  |             if ('string' !== typeof req.oauth3.token.scp) { | 
					
						
							| 
									
										
										
										
											2017-05-29 21:39:52 +00:00
										 |  |  |               res.send({ error: { message: "Token must contain a grants string in 'scp'", code: "E_NO_GRANTS" } }); | 
					
						
							|  |  |  |               return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // every grant in the array must be present
 | 
					
						
							|  |  |  |             if (!grants.every(function (grant) { | 
					
						
							|  |  |  |               var scopes = grant.split(/\|/g); | 
					
						
							|  |  |  |               return scopes.some(function (scp) { | 
					
						
							|  |  |  |                 return req.oauth3.token.scp.split(/[,\s]+/mg).some(function (s) { | 
					
						
							|  |  |  |                   return scp === s; | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |             })) { | 
					
						
							|  |  |  |               res.send({ error: { message: "Token does not contain valid grants: '" + grants + "'", code: "E_NO_GRANTS" } }); | 
					
						
							|  |  |  |               return; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             next(); | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2017-05-26 22:06:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |         var _getOauth3Controllers = pkgDeps.getOauth3Controllers = require('oauthcommon/example-oauthmodels').create( | 
					
						
							|  |  |  |           { sqlite3Sock: xconfx.sqlite3Sock, ipcKey: xconfx.ipcKey } | 
					
						
							|  |  |  |         ).getControllers; | 
					
						
							|  |  |  |         //require('oauthcommon').inject(packagedApi._getOauth3Controllers, packagedApi._api, pkgConf, pkgDeps);
 | 
					
						
							|  |  |  |         require('oauthcommon').inject(_getOauth3Controllers, myApp/*, pkgConf, pkgDeps*/); | 
					
						
							| 
									
										
										
										
											2017-05-26 22:06:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |         myApp.use('/public', function preHandler(req, res, next) { | 
					
						
							|  |  |  |           // TODO authenticate or use guest user
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |           next(); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-29 18:29:05 +00:00
										 |  |  |         // TODO delete these caches when config changes
 | 
					
						
							|  |  |  |         var _stripe; | 
					
						
							|  |  |  |         var _stripe_test; | 
					
						
							|  |  |  |         var _mandrill; | 
					
						
							|  |  |  |         var _mailchimp; | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |         myApp.use('/', function preHandler(req, res, next) { | 
					
						
							|  |  |  |           return getSiteConfig(clientUrih).then(function (siteConfig) { | 
					
						
							|  |  |  |             Object.defineProperty(req, 'getSiteMailer', { | 
					
						
							|  |  |  |               enumerable: true | 
					
						
							|  |  |  |             , configurable: false | 
					
						
							|  |  |  |             , writable: false | 
					
						
							|  |  |  |             , value: function getSiteMailerProp() { | 
					
						
							|  |  |  |                 var nodemailer = require('nodemailer'); | 
					
						
							|  |  |  |                 var transport = require('nodemailer-mailgun-transport'); | 
					
						
							|  |  |  |                 //var mailconf = require('../../../com.daplie.mailer/config.mailgun');
 | 
					
						
							|  |  |  |                 var mailconf = siteConfig['mailgun.org']; | 
					
						
							|  |  |  |                 var mailer = PromiseA.promisifyAll(nodemailer.createTransport(transport(mailconf))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return mailer; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Object.defineProperty(req, 'getSiteConfig', { | 
					
						
							|  |  |  |               enumerable: true | 
					
						
							|  |  |  |             , configurable: false | 
					
						
							|  |  |  |             , writable: false | 
					
						
							|  |  |  |             , value: function getSiteMailerProp(section) { | 
					
						
							|  |  |  |                 return PromiseA.resolve((siteConfig || {})[section]); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Object.defineProperty(req, 'getSitePackageConfig', { | 
					
						
							|  |  |  |               enumerable: true | 
					
						
							|  |  |  |             , configurable: false | 
					
						
							|  |  |  |             , writable: false | 
					
						
							|  |  |  |             , value: function getSitePackageConfigProp() { | 
					
						
							|  |  |  |                 return getSitePackageConfig(clientUrih, pkgId); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Object.defineProperty(req, 'getSiteStore', { | 
					
						
							|  |  |  |               enumerable: true | 
					
						
							|  |  |  |             , configurable: false | 
					
						
							|  |  |  |             , writable: false | 
					
						
							|  |  |  |             , value: function getSiteStoreProp() { | 
					
						
							|  |  |  |                 var restPath = path.join(myConf.restPath, pkgId); | 
					
						
							|  |  |  |                 var apiPath = path.join(myConf.apiPath, pkgId); | 
					
						
							|  |  |  |                 var dir; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // TODO usage package.json as a falback if the standard location is not used
 | 
					
						
							|  |  |  |                 try { | 
					
						
							|  |  |  |                   dir = require(path.join(apiPath, 'models.js')); | 
					
						
							|  |  |  |                 } catch(e) { | 
					
						
							|  |  |  |                   dir = require(path.join(restPath, 'models.js')); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 return getSiteStore(clientUrih, pkgId, dir); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-29 17:35:52 +00:00
										 |  |  |             /* | 
					
						
							|  |  |  |             Object.defineProperty(req, 'getSitePayments', { | 
					
						
							|  |  |  |               enumerable: true | 
					
						
							|  |  |  |             , configurable: false | 
					
						
							|  |  |  |             , writable: false | 
					
						
							|  |  |  |             , value: function getSitePaymentsProp() { | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |             */ | 
					
						
							| 
									
										
										
										
											2017-05-29 17:43:58 +00:00
										 |  |  |             // TODO allow third-party clients stripe ids destination
 | 
					
						
							|  |  |  |             // https://stripe.com/docs/connect/payments-fees
 | 
					
						
							| 
									
										
										
										
											2017-05-29 18:29:05 +00:00
										 |  |  |             Object.defineProperty(req, 'Stripe', { | 
					
						
							|  |  |  |               enumerable: true | 
					
						
							|  |  |  |             , configurable: false | 
					
						
							|  |  |  |             , get: function () { | 
					
						
							|  |  |  |                 _stripe = _stripe || require('stripe')(siteConfig['stripe.com'].live.secret); | 
					
						
							|  |  |  |                 return _stripe; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Object.defineProperty(req, 'StripeTest', { | 
					
						
							|  |  |  |               enumerable: true | 
					
						
							|  |  |  |             , configurable: false | 
					
						
							|  |  |  |             , get: function () { | 
					
						
							|  |  |  |                 _stripe_test = _stripe_test || require('stripe')(siteConfig['stripe.com'].test.secret); | 
					
						
							|  |  |  |                 return _stripe_test; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Object.defineProperty(req, 'Mandrill', { | 
					
						
							|  |  |  |               enumerable: true | 
					
						
							|  |  |  |             , configurable: false | 
					
						
							|  |  |  |             , get: function () { | 
					
						
							|  |  |  |                 if (!_mandrill) { | 
					
						
							|  |  |  |                   var Mandrill = require('mandrill-api/mandrill'); | 
					
						
							|  |  |  |                   _mandrill = new Mandrill.Mandrill(siteConfig['mandrill.com'].apiKey); | 
					
						
							|  |  |  |                   _mandrill.messages.sendTemplateAsync = function (opts) { | 
					
						
							|  |  |  |                     return new PromiseA(function (resolve, reject) { | 
					
						
							|  |  |  |                       _mandrill.messages.sendTemplate(opts, resolve, reject); | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                   }; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 return _mandrill; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Object.defineProperty(req, 'Mailchimp', { | 
					
						
							|  |  |  |               enumerable: true | 
					
						
							|  |  |  |             , configurable: false | 
					
						
							|  |  |  |             , get: function () { | 
					
						
							|  |  |  |                 var Mailchimp = require('mailchimp-api-v3'); | 
					
						
							|  |  |  |                 _mailchimp = _mailchimp || new Mailchimp(siteConfig['mailchimp.com'].apiKey); | 
					
						
							|  |  |  |                 return _stripe_test; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2017-05-29 17:32:44 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |             req._walnutOriginalUrl = req.url; | 
					
						
							|  |  |  |             // "/path/api/com.example/hello".replace(/.*\/api\//, '').replace(/([^\/]*\/+)/, '/') => '/hello'
 | 
					
						
							|  |  |  |             req.url = req.url.replace(/\/api\//, '').replace(/.*\/api\//, '').replace(/([^\/]*\/+)/, '/'); | 
					
						
							|  |  |  |             next(); | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2017-05-24 05:25:11 +00:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-05-29 18:29:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |         //
 | 
					
						
							|  |  |  |         // TODO handle /accounts/:accountId
 | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |         return PromiseA.resolve(require(pkgPath).create({ | 
					
						
							|  |  |  |           etcpath: xconfx.etcpath | 
					
						
							|  |  |  |         }/*pkgConf*/, pkgDeps/*pkgDeps*/, myApp/*myApp*/)).then(function (handler) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           myApp.use('/', function postHandler(req, res, next) { | 
					
						
							|  |  |  |             req.url = req._walnutOriginalUrl; | 
					
						
							|  |  |  |             next(); | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2017-05-24 05:25:11 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 23:09:49 +00:00
										 |  |  |           localCache.pkgs[pkgId] = { pkgId: pkgId, pkg: pkg, handler: handler || myApp, createdAt: Date.now() }; | 
					
						
							|  |  |  |           pkgLinks.forEach(function (pkgLink) { | 
					
						
							|  |  |  |             localCache.pkgs[pkgLink] = localCache.pkgs[pkgId]; | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           return localCache.pkgs[pkgId]; | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Read packages/apis/sub.sld.tld (forward dns) to find list of apis as tld.sld.sub (reverse dns)
 | 
					
						
							|  |  |  |   // TODO packages/allowed_apis/sub.sld.tld (?)
 | 
					
						
							|  |  |  |   // TODO auto-register org.oauth3.consumer for primaryDomain (and all sites?)
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |   function loadRestHandler(myConf, clientUrih, pkgId) { | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |     return PromiseA.resolve().then(function () { | 
					
						
							|  |  |  |       if (!localCache.pkgs[pkgId]) { | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |         return loadRestHelper(myConf, clientUrih, pkgId); | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       return localCache.pkgs[pkgId]; | 
					
						
							|  |  |  |       // TODO expire require cache
 | 
					
						
							|  |  |  |       /* | 
					
						
							|  |  |  |       if (Date.now() - localCache.pkgs[pkgId].createdAt < (5 * 60 * 1000)) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       */ | 
					
						
							|  |  |  |     }, function (/*err*/) { | 
					
						
							|  |  |  |       // TODO what kind of errors might we want to handle?
 | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     }).then(function (restPkg) { | 
					
						
							|  |  |  |       return restPkg; | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  |   var CORS = require('connect-cors'); | 
					
						
							|  |  |  |   var cors = CORS({ credentials: true, headers: [ | 
					
						
							|  |  |  |     'X-Requested-With' | 
					
						
							|  |  |  |   , 'X-HTTP-Method-Override' | 
					
						
							|  |  |  |   , 'Content-Type' | 
					
						
							|  |  |  |   , 'Accept' | 
					
						
							|  |  |  |   , 'Authorization' | 
					
						
							|  |  |  |   ], methods: [ "GET", "POST", "PATCH", "PUT", "DELETE" ] }); | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |   var staleAfter = (5 * 60 * 1000); | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  |   return function (req, res, next) { | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  |     cors(req, res, function () { | 
					
						
							| 
									
										
										
										
											2017-05-20 04:51:44 +00:00
										 |  |  |       // Canonical client names
 | 
					
						
							|  |  |  |       // example.com should use api.example.com/api for all requests
 | 
					
						
							|  |  |  |       // sub.example.com/api should resolve to sub.example.com
 | 
					
						
							|  |  |  |       // example.com/subpath/api should resolve to example.com#subapp
 | 
					
						
							|  |  |  |       // sub.example.com/subpath/api should resolve to sub.example.com#subapp
 | 
					
						
							| 
									
										
										
										
											2017-05-20 00:09:48 +00:00
										 |  |  |       var clientUrih = req.hostname.replace(/^api\./, '') + req.url.replace(/\/api\/.*/, '/').replace(/\/+/g, '#').replace(/#$/, ''); | 
					
						
							| 
									
										
										
										
											2017-05-30 17:29:14 +00:00
										 |  |  |       var clientApiUri = req.hostname + req.url.replace(/\/api\/.*/, '/').replace(/\/$/, ''); | 
					
						
							| 
									
										
										
										
											2017-05-20 04:51:44 +00:00
										 |  |  |       // Canonical package names
 | 
					
						
							|  |  |  |       // '/api/com.daplie.hello/hello' should resolve to 'com.daplie.hello'
 | 
					
						
							|  |  |  |       // '/subapp/api/com.daplie.hello/hello' should also 'com.daplie.hello'
 | 
					
						
							|  |  |  |       // '/subapp/api/com.daplie.hello/' may exist... must be a small api
 | 
					
						
							|  |  |  |       var pkgId = req.url.replace(/.*\/api\//, '').replace(/^\//, '').replace(/\/.*/, ''); | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       var now = Date.now(); | 
					
						
							|  |  |  |       var hasBeenHandled = false; | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       // Existing (Deprecated)
 | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  |       Object.defineProperty(req, 'experienceId', { | 
					
						
							|  |  |  |         enumerable: true | 
					
						
							|  |  |  |       , configurable: false | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       , writable: false | 
					
						
							|  |  |  |       , value: clientUrih | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-05-30 17:29:14 +00:00
										 |  |  |       Object.defineProperty(req, 'clientApiUri', { | 
					
						
							|  |  |  |         enumerable: true | 
					
						
							|  |  |  |       , configurable: false | 
					
						
							|  |  |  |       , writable: false | 
					
						
							|  |  |  |       , value: clientApiUri | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-05-20 00:09:48 +00:00
										 |  |  |       Object.defineProperty(req, 'apiId', { | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |         enumerable: true | 
					
						
							|  |  |  |       , configurable: false | 
					
						
							|  |  |  |       , writable: false | 
					
						
							|  |  |  |       , value: pkgId | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // New
 | 
					
						
							|  |  |  |       Object.defineProperty(req, 'clientUrih', { | 
					
						
							|  |  |  |         enumerable: true | 
					
						
							|  |  |  |       , configurable: false | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  |       , writable: false | 
					
						
							|  |  |  |         // TODO this identifier may need to be non-deterministic as to transfer if a domain name changes but is still the "same" app
 | 
					
						
							|  |  |  |         // (i.e. a company name change. maybe auto vs manual register - just like oauth3?)
 | 
					
						
							|  |  |  |         // NOTE: probably best to alias the name logically
 | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       , value: clientUrih | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       Object.defineProperty(req, 'pkgId', { | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  |         enumerable: true | 
					
						
							|  |  |  |       , configurable: false | 
					
						
							|  |  |  |       , writable: false | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       , value: pkgId | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       // TODO cache permission (although the FS is already cached, NBD)
 | 
					
						
							|  |  |  |       return isThisClientAllowedToUseThisPkg(xconfx, clientUrih, pkgId).then(function (yes) { | 
					
						
							|  |  |  |         if (!yes) { | 
					
						
							|  |  |  |           notConfigured(req, res); | 
					
						
							|  |  |  |           return null; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |         if (localCache.rests[pkgId]) { | 
					
						
							|  |  |  |           localCache.rests[pkgId].handler(req, res, next); | 
					
						
							|  |  |  |           hasBeenHandled = true; | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |           if (now - localCache.rests[pkgId].createdAt > staleAfter) { | 
					
						
							|  |  |  |             localCache.rests[pkgId] = null; | 
					
						
							| 
									
										
										
										
											2017-05-24 07:34:34 +00:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |         if (!localCache.rests[pkgId]) { | 
					
						
							|  |  |  |           //return doesThisPkgExist
 | 
					
						
							| 
									
										
										
										
											2017-05-24 07:34:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |           return loadRestHandler(xconfx, clientUrih, pkgId).then(function (myHandler) { | 
					
						
							|  |  |  |             if (!myHandler) { | 
					
						
							|  |  |  |               notConfigured(req, res); | 
					
						
							|  |  |  |               return; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2017-05-24 07:34:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-26 20:55:18 +00:00
										 |  |  |             localCache.rests[pkgId] = { handler: myHandler.handler, createdAt: now }; | 
					
						
							|  |  |  |             if (!hasBeenHandled) { | 
					
						
							|  |  |  |               myHandler.handler(req, res, next); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-05-19 23:37:28 +00:00
										 |  |  |       }); | 
					
						
							| 
									
										
										
										
											2016-06-08 14:05:10 -04:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2016-04-09 19:14:00 -04:00
										 |  |  |   }; | 
					
						
							|  |  |  | }; |