UNPKG

periodicjs

Version:

Periodic is a rapid enterprise application framework for data driven web and mobile applications.

678 lines (625 loc) 23.3 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: lib/init/express.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: lib/init/express.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>'use strict'; require('isomorphic-fetch'); const os = require('os'); const path = require('path'); const fs = require('fs-extra'); const semver = require('semver'); const express = require('express'); const Sequelize = require('sequelize'); const EJS = require('ejs'); const LRU = require('lru-cache'); const responseTime = require('response-time'); const flash = require('connect-flash'); const bodyParser = require('body-parser'); const methodOverride = require('method-override'); const cookieParser = require('cookie-parser'); const favicon = require('serve-favicon'); const compress = require('compression'); const morgan = require('morgan'); const csrf = require('csurf'); const connectLoki = require('connect-loki'); const connectMongo = require('connect-mongo'); const connectSql = require('connect-sequelize'); const connectRedis = require('connect-redis'); const session = require('express-session'); const crypto = require('crypto'); /** * configure express view rendering options * * @returns {Promise} */ function configureViews() { return new Promise((resolve, reject) => { try { if (this.settings.express.config.trust_proxy) { this.app.enable('trust proxy'); } this.app.set('view engine', this.settings.express.views.engine || 'ejs'); this.app.set('views', path.resolve(this.config.app_root, 'app/views')); if (this.settings.express.views.lru_cache) { EJS.cache = LRU(this.settings.express.views.lru); } this.app.engine('html', EJS.renderFile); this.app.engine('ejs', EJS.renderFile); if (this.settings.express.views.engine !== 'ejs') { this.app.engine(this.settings.express.views.engine, require(this.settings.express.views.package).renderFile); } resolve(true); } catch (e) { reject(e); } }); } function configureExpress() { return new Promise((resolve, reject) => { try { this.app.use(responseTime(this.settings.express.response_time)); if (this.settings.express.use_flash) { this.app.use(flash()); } this.app.use(bodyParser.urlencoded(this.settings.express.body_parser.urlencoded)); this.app.use(bodyParser.json(this.settings.express.body_parser.json)); this.app.use(methodOverride()); this.app.use(methodOverride('_method')); this.app.use(methodOverride('X-HTTP-Method')); // Microsoft this.app.use(methodOverride('X-HTTP-Method-Override')); // Google/GData this.app.use(methodOverride('X-Method-Override')); // IBM this.app.use(methodOverride(function(req, res) { if (req.body &amp;&amp; typeof req.body === 'object' &amp;&amp; '_method' in req.body) { // look in urlencoded POST bodies and delete it const method = req.body._method; delete req.body._method; return method; } })); this.app.use(cookieParser(this.settings.express.cookies.cookie_parser)); this.app.use(favicon(path.resolve(this.config.app_root, 'public/favicon.png'))); resolve(true); } catch (e) { reject(e); } }); } function customizeExpress() { return new Promise((resolve, reject) => { try { const startup = require(path.join(this.config.app_root, 'content/config/startup.js')); resolve(startup.customExpressConfiguration.call(this)); } catch (e) { reject(e); } }); } function staticCacheExpress() { return new Promise((resolve, reject) => { try { const expressStaticOptions = (this.settings.express.config.use_static_caching) ? { maxAge: 86400000, } : {}; this.app.use(express.static(path.resolve(this.config.app_root, 'public'), expressStaticOptions)); resolve(true); } catch (e) { reject(e); } }); } function compressExpress() { return new Promise((resolve, reject) => { try { if (this.settings.express.config.use_compression) { this.app.use(compress()); } resolve(true); } catch (e) { reject(e); } }); } function morganColors(req, res) { const status = res.statusCode; let color = 32; // green if (status >= 500) { color = 31; } // red else if (status >= 400) { color = 33; } // yellow else if (status >= 300) { color = 36; } // cyan return '\x1b[' + color + 'm' + status + '\x1b[90m'; } function morganLogger(req, res, next) { return (req, res, next) => { if (this.settings.express.config.skip_logging_paths.indexOf(req.path)>-1) { return next(); } else { return morgan('app', { format: '\x1b[90m:remote-addr :method \x1b[37m:url\x1b[90m :colorstatus \x1b[97m:response-time ms\x1b[90m :date :referrer :user-agent\x1b[0m', })(req, res, next); } }; } function logExpress() { return new Promise((resolve, reject) => { try { if (this.settings.express.config.debug) { morgan.token('colorstatus', morganColors); morgan.format('app', '\x1b[90m:remote-addr :method \x1b[37m:url\x1b[90m :colorstatus \x1b[97m:response-time ms\x1b[90m :date :referrer :user-agent\x1b[0m'); // if (this.settings.application.status !== 'install') { // this.app.use(morgan('app', { // format: '\x1b[90m:remote-addr :method \x1b[37m:url\x1b[90m :colorstatus \x1b[97m:response-time ms\x1b[90m :date :referrer :user-agent\x1b[0m', // })); this.app.use(morganLogger.call(this)); // } else { // app.use(morgan('app', { // format: '\x1b[90m:remote-addr :method \x1b[37m:url\x1b[90m :colorstatus \x1b[97m:response-time ms\x1b[90m :date :referrer :user-agent\x1b[0m' // })); // } } resolve(true); } catch (e) { reject(e); } }); } function skipSessions(options) { const { req, } = options; if (req.headers.authorization) { return true; } else if (req.query &amp;&amp; req.query.skip_session &amp;&amp; (req.query.skip_session === 'true' || req.query.skip_session === true)) { return true; } else if (req.controllerData &amp;&amp; req.controllerData.skip_session &amp;&amp; (req.controllerData.skip_session === 'true' || req.controllerData.skip_session === true)) { return true; } else { return false; } } function enabledSessionMiddleware(sessionMiddleware) { return (req, res, next) => { // console.log('skipSessions({ req })',skipSessions({ req })) if (skipSessions({ req, })) { return next(); } else { return sessionMiddleware(req, res, next); } }; } function enabledCSRFMiddleware(csrfMiddleware) { return (req, res, next) => { let skipSessionCheck = skipSessions({ req, }); if (skipSessionCheck) { return next(); } else if (Array.isArray(this.settings.express.config.skip_csrf_header)) { let skipCSRF = false; this.settings.express.config.skip_csrf_header.forEach(skipHeader => { if (req.headers[skipHeader]) { skipCSRF = true; } }); if (skipCSRF || skipSessionCheck) { next(); } else { return csrfMiddleware(req, res, next); } } else if (req.headers[this.settings.express.config.skip_csrf_header]) { return next(); } else { return csrfMiddleware(req, res, next); } }; } function expressSessions() { return new Promise((resolve, reject) => { try { if (this.settings.express.sessions.enabled) { const express_session_config = Object.assign({ proxy: true, // name:'connect.id', resave: false, //true, //default // rolling:false, //default saveUninitialized: false, //true, //default // secret:'somescreted', //default // store:MemoryStore //default // unset:'keep' //default cookie: { expires: 604800000, //one week maxAge: 604800000, //one week secure: 'auto', }, }, this.settings.express.sessions.config); // let store; switch (this.settings.express.sessions.type) { case 'loki': var LokiStore = connectLoki(session); express_session_config.store = new LokiStore(this.settings.express.sessions.store_settings); break; case 'mongo': var MongoStore = connectMongo(session); express_session_config.store = new MongoStore(this.settings.express.sessions.store_settings); break; case 'sql': var SequeliezeStore = connectSql(session); var { db, options, modelName, } = this.settings.express.sessions.store_settings; var sqldb = new Sequelize(...db); express_session_config.store = new SequeliezeStore(sqldb, options, modelName); break; case 'redis': var RedisStore = connectRedis(session); express_session_config.store = new RedisStore(this.settings.express.sessions.store_settings); break; default: reject(new Error('Invalid express session type')); break; } const sessionMiddleware = session(express_session_config); this.app.use(enabledSessionMiddleware(sessionMiddleware)); const csrfMiddleWare = enabledCSRFMiddleware.bind(this); if (this.settings.express.config.csrf) { this.app.use(csrfMiddleWare(csrf())); } resolve(true); } else { resolve(true); } } catch (e) { reject(e); } }); } function useCSRFMiddleware(req, res, next) { res.locals.token = (this.settings.express.config.csrf &amp;&amp; req.csrfToken) ? req.csrfToken() : ''; next(); } function useLocalsMiddleware(req, res, next) { res.locals.periodic_addons = { additionalHeadHTML: {}, additionalFooterHTML: {}, additionalPreContentHTML: {}, additionalPostContentHTML: {}, ['x-forwarded-for']: req.headers['x-forwarded-for'], remoteAddress: req.connection.remoteAddress, }; res.locals.isLoggedIn = (req.user &amp;&amp; Object.keys(req.user).length) ? true : false; next(); } function worker_index(ip, len) { const hash = crypto.createHash('sha256'); hash.update(ip); let hex = hash.digest('hex').substring(0, 8); return parseInt(hex, 16).toString(10) % len; } function assignWorkersMiddleware(req, res, next) { if (this.settings.application.cluster_process &amp;&amp; (this.settings.application.server.socketio ||this.settings.application.use_sticky_sessions)) { let worker = this.workers[ worker_index(req._remoteAddress, this.numWorkers)]; worker.send('sticky-session:connection', req.connection.server); } next(); } function expressLocals() { return new Promise((resolve, reject) => { try { this.app.locals.viewHelper = this.utilities.viewHelper; const useCSRFMiddlewareFunc = useCSRFMiddleware.bind(this); const useLocalsMiddlewareFunc = useLocalsMiddleware.bind(this); const assignWorkersMiddlewareFunc = assignWorkersMiddleware.bind(this); let core_data_list = []; for (let key of this.datas.keys()) { if (key !== 'configuration' &amp;&amp; key !== 'extension') { core_data_list.push(key); } } this.app.locals.core_data_list = core_data_list.reduce(this.utilities.routing.splitModelNameReducer, {}); this.app.locals.appenvironment = this.environment; this.app.locals.appname = this.settings.name; this.app.locals.additionalHTMLFunctions = []; this.app.use(useCSRFMiddlewareFunc); this.app.use(useLocalsMiddlewareFunc); this.app.use(assignWorkersMiddlewareFunc); // for clustering and sockets to work together resolve(true); } catch (e) { reject(e); } }); } function expressRouting() { return new Promise((resolve, reject) => { try { this.app.locals.page_data = Object.assign({}, this.settings.express.views.page_data, this.app.locals.page_data); //assign container routers //assign extension routers //assign data routers this.routers.forEach((value /*, key*/ ) => { if (value.type === 'container') { this.app.use(value.router); } else if (value.type === 'extension') { this.app.use(value.router); } else if (value.type === 'data') { this.app.use(getDataRoute.call(this, value.router_base), value.router); } // else }); this.app.get('*', (req, res, next) => { const skippable_routes = this.settings.express.views.skippable_routes; const skip404 = skippable_routes.reduce((result, route) => { if (req.path.indexOf(route) >= 0) { result.push(route); } return result; }, []); if (skip404.length) { next(); } else { const viewtemplate = 'home/error404'; const viewdata = Object.assign({}, { periodic: { appname: this.settings.name, page_data: { version: this.settings.application.version, }, }, error: new Error('page not found'), }, this.app.locals); res.status(404); this.core.controller.renderView(req, res, viewtemplate, viewdata); } }); resolve(true); } catch (e) { reject(e); } }); } function getDataRoute(data_route) { return this.utilities.routing.route_prefix(path.join(this.settings.express.routing.data, data_route.replace(/_/gi, '/'))); } function displayOutOfDatePeriodic(options) { const { latestPeriodicVersion, } = options; console.log('\u0007'); this.logger.debug(` ====================================================== | | | Your instance of Periodic is out of date. | | Your Version: ${this.settings.application.version}, Current Version: ${latestPeriodicVersion} | | | ====================================================== `); } function handleLatestPeriodicVersionCheckError(error) { this.logger.warn('Could not check latest version of Periodic', error); } function extVersionReducer(result, key) { result[key.name] = key.version; return result; } function checkOutdatedExtensionVersions() { const extensionNamesToFetch = Array.from(this.extensions.keys()).filter(ext => ext.indexOf('@') &lt; 0); const extensions = Array.from(this.extensions.values()).filter(ext => ext.name.indexOf('@') &lt; 0); const semverRegEx = RegExp(/[^0-9.]/g); const registryExtensionFetchs = extensionNamesToFetch.map(extname => { return new Promise((resolve, reject) => { fetch(`https://registry.npmjs.org/${extname}`, { headers: { Accept: 'application/json', }, }) .then(this.utilities.fetchUtils.checkStatus) .then(res => res.json()) .then(resolve) .catch(reject); }); }); const outOfDateExtensions = []; fs.readJson(path.join(this.config.app_root, 'package.json')).then(packageJSON => { const extensionVersionObj = Array.from(this.extensions.values()).reduce(extVersionReducer, {}); Object.keys(extensionVersionObj).forEach(extVer => { if (packageJSON.dependencies[extVer]) { const packageJsonExtVer = packageJSON.dependencies[extVer].replace(semverRegEx, '').split('.').slice(0, 3).join('.'); const extJsonVer = extensionVersionObj[extVer]; if (this.utilities.fetchUtils.isVersionUpToDate(packageJsonExtVer, extJsonVer) === false) { this.logger.error(`${extVer}@${packageJsonExtVer} in package.json is out of date with version ${extJsonVer} in the configuration database`); } } else { this.logger.error(`${extVer}@${extensionVersionObj[extVer]} from the configuration database in missing in your package.json file`); } }); return Promise.all(registryExtensionFetchs); }) .then(extensionsFromNPM => { extensionsFromNPM.forEach((extFromNPM, i) => { const latestExtVersion = extFromNPM['dist-tags'].latest; if (this.utilities.fetchUtils.isVersionUpToDate(extensions[i].version, latestExtVersion) === false) { outOfDateExtensions.push({ name: extensions[i].name, installed: extensions[i].version, latest: latestExtVersion, }); } }); if (outOfDateExtensions.length) { this.logger.verbose('Your current extensions are out of date: ', outOfDateExtensions); } }) .catch(this.logger.error); } function checkLatestPeriodicVersion() { fetch('https://registry.npmjs.org/periodicjs', { headers: { Accept: 'application/json', }, }) .then(this.utilities.fetchUtils.checkStatus) .then(res => res.json()) .then(res => { const latestPeriodicVersion = res['dist-tags'].latest; if (this.utilities.fetchUtils.isVersionUpToDate(this.settings.application.version, latestPeriodicVersion)) { if (this.config.debug) { this.logger.debug(`Your version of Periodic[${this.settings.application.version}] is up to date with the current version [${latestPeriodicVersion}]`); } } else { displayOutOfDatePeriodic.call(this, { latestPeriodicVersion, }); } }) .catch(handleLatestPeriodicVersionCheckError.bind(this)); } function expressStatus() { return new Promise((resolve, reject) => { try { if (this.config.debug) { this.logger.debug(`Running in environment: ${this.environment}`); } if (this.settings.application.check_for_updates) { checkLatestPeriodicVersion.call(this); } if (this.settings.application.check_for_outdated_extensions) { checkOutdatedExtensionVersions.call(this); } resolve(true); } catch (e) { reject(e); } }); } const errorLogMiddleware = function errorLogMiddleware(err, req, res, next) { // console.log('err', err, 'next', next); var userdata = {}; if (req &amp;&amp; req.user &amp;&amp; req.user.email) { userdata = { email: req.user.email, username: req.user.username, firstname: req.user.firstname, lastname: req.user.lastname, }; } this.logger.error(err.message, err.stack, { err: err, ipinfo: { date: new Date(), 'x-forwarded-for': req.headers['x-forwarded-for'], remoteAddress: req.connection.remoteAddress, originalUrl: req.originalUrl, headerHost: req.headers.host, userAgent: req.headers['user-agent'], referer: req.headers.referer, user: userdata, osHostname: os.hostname(), }, }); // logger.error(err.stack); next(err); }; const catchAllErrorMiddleware = function catchAllErrorMiddleware(err, req, res, next) { if (!err) { next(); } else if (req.query.format === 'json' || req.is('json') || req.is('application/json')) { res.status(500); res.send({ result: 'error', data: { error:err.toString(), }, error: err, }); } else { res.status(500); res.render(this.settings.express.views.custom_error_view || 'home/error500', { user: req.user, message: err.message, error: err, }); } }; function expressErrors() { return new Promise((resolve, reject) => { try { const catchAllErrorMiddlewareFunc = catchAllErrorMiddleware.bind(this); const errorLogMiddlewareFunc = errorLogMiddleware.bind(this); this.app.use(errorLogMiddlewareFunc); this.app.use(catchAllErrorMiddlewareFunc); resolve(true); } catch (e) { reject(e); } }); } /** * sets the runtime environment correctly * * @returns {Promise} configRuntimeEnvironment sets up application config db */ function initializeExpress() { return Promise.all([ configureViews.call(this), configureExpress.call(this), customizeExpress.call(this), staticCacheExpress.call(this), compressExpress.call(this), logExpress.call(this), expressSessions.call(this), expressLocals.call(this), expressRouting.call(this), expressStatus.call(this), expressErrors.call(this), ]); } module.exports = { initializeExpress, configureViews, configureExpress, customizeExpress, staticCacheExpress, compressExpress, logExpress, expressSessions, expressLocals, expressRouting, expressStatus, expressErrors, morganColors, morganLogger, errorLogMiddleware, catchAllErrorMiddleware, checkLatestPeriodicVersion, useLocalsMiddleware, useCSRFMiddleware, skipSessions, enabledCSRFMiddleware, enabledSessionMiddleware, handleLatestPeriodicVersionCheckError, displayOutOfDatePeriodic, };</code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-periodic.html">periodic</a></li></ul><h3>Classes</h3><ul><li><a href="Periodic.html">Periodic</a></li><li><a href="Periodic.Periodic.html">Periodic</a></li></ul><h3>Global</h3><ul><li><a href="global.html#_admin_prefix">_admin_prefix</a></li><li><a href="global.html#_manifest_prefix">_manifest_prefix</a></li><li><a href="global.html#_route_prefix">_route_prefix</a></li><li><a href="global.html#all_prefixes">all_prefixes</a></li><li><a href="global.html#configRuntimeEnvironment">configRuntimeEnvironment</a></li><li><a href="global.html#configureLogger">configureLogger</a></li><li><a href="global.html#configureLowkie">configureLowkie</a></li><li><a href="global.html#configureMongoose">configureMongoose</a></li><li><a href="global.html#configureSequelize">configureSequelize</a></li><li><a href="global.html#configureViews">configureViews</a></li><li><a href="global.html#endTimer">endTimer</a></li><li><a href="global.html#formatResponse">formatResponse</a></li><li><a href="global.html#getEnv">getEnv</a></li><li><a href="global.html#handler">handler</a></li><li><a href="global.html#initializeExpress">initializeExpress</a></li><li><a href="global.html#loadConfiguration">loadConfiguration</a></li><li><a href="global.html#setAppRunningEnv">setAppRunningEnv</a></li><li><a href="global.html#setUpFolderStructure">setUpFolderStructure</a></li><li><a href="global.html#startTimer">startTimer</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Wed Jul 18 2018 23:08:12 GMT-0400 (Eastern Daylight Time) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>