From fc19f3af6155f73691b1edb78c53d0fe7b3738fa Mon Sep 17 00:00:00 2001 From: Peter Jaszkowiak Date: Mon, 4 Dec 2017 13:49:44 -0700 Subject: [PATCH] Misc fixes and improvements (#6143) * `setup` command fixes and improvements - Enable using the `./nodebb setup` command for auto-setup with a JSON argument - Change CLI so package-install and dependency install are separate steps - Fix #6142 * Prevent compiling templates multiple times - Multiple requests for same template get pooled - Hopefully fixes the "templateFunction is not a function" error which happens if site is restarted during high-traffic times * More helpful upgrade template --- src/cli/index.js | 40 +++++++++++++++++++++++++++++++++------- src/cli/paths.js | 2 ++ src/cli/setup.js | 17 +++++++++++++++-- src/install.js | 19 +++++++------------ src/middleware/index.js | 24 +++++++++++++++++++++++- src/upgrades/TEMPLATE | 3 +++ 6 files changed, 83 insertions(+), 22 deletions(-) diff --git a/src/cli/index.js b/src/cli/index.js index da2d4dfc71..0bc95a7c6d 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -9,14 +9,30 @@ var dirname = require('./paths').baseDir; // check to make sure dependencies are installed try { fs.readFileSync(path.join(dirname, 'package.json')); - fs.readFileSync(path.join(dirname, 'node_modules/async/package.json')); } catch (e) { if (e.code === 'ENOENT') { - console.warn('Dependencies not yet installed.'); - console.log('Installing them now...\n'); + console.warn('package.json not found.'); + console.log('Populating package.json...\n'); packageInstall.updatePackageFile(); packageInstall.preserveExtraneousPlugins(); + + console.log('OK'.green + '\n'.reset); + } else { + throw e; + } +} + +try { + fs.readFileSync(path.join(dirname, 'node_modules/async/package.json'), 'utf8'); + fs.readFileSync(path.join(dirname, 'node_modules/commander/package.json'), 'utf8'); + fs.readFileSync(path.join(dirname, 'node_modules/colors/package.json'), 'utf8'); + fs.readFileSync(path.join(dirname, 'node_modules/nconf/package.json'), 'utf8'); +} catch (e) { + if (e.code === 'ENOENT') { + console.warn('Dependencies not yet installed.'); + console.log('Installing them now...\n'); + packageInstall.npmInstallProduction(); require('colors'); @@ -121,10 +137,20 @@ program // management commands program - .command('setup') - .description('Run the NodeBB setup script') - .action(function () { - require('./setup').setup(); + .command('setup [config]') + .description('Run the NodeBB setup script, or setup with an initial config') + .action(function (initConfig) { + if (initConfig) { + try { + initConfig = JSON.parse(initConfig); + } catch (e) { + console.warn('Invalid JSON passed as initial config value.'.red); + console.log('If you meant to pass in an initial config value, please try again.\n'); + + throw e; + } + } + require('./setup').setup(initConfig); }); program diff --git a/src/cli/paths.js b/src/cli/paths.js index df5532cacd..2a9bec3547 100644 --- a/src/cli/paths.js +++ b/src/cli/paths.js @@ -6,10 +6,12 @@ var baseDir = path.join(__dirname, '../../'); var loader = path.join(baseDir, 'loader.js'); var app = path.join(baseDir, 'app.js'); var pidfile = path.join(baseDir, 'pidfile'); +var config = path.join(baseDir, 'config.json'); module.exports = { baseDir: baseDir, loader: loader, app: app, pidfile: pidfile, + config: config, }; diff --git a/src/cli/setup.js b/src/cli/setup.js index 6fcb49bd93..509de52ddb 100644 --- a/src/cli/setup.js +++ b/src/cli/setup.js @@ -2,10 +2,13 @@ var winston = require('winston'); var async = require('async'); +var path = require('path'); +var nconf = require('nconf'); var install = require('../../install/web').install; -function setup() { +function setup(initConfig) { + var paths = require('./paths'); var install = require('../install'); var build = require('../meta/build'); var prestart = require('../prestart'); @@ -17,9 +20,19 @@ function setup() { console.log('\nThis looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.'); console.log('Press enter to accept the default setting (shown in brackets).'); + install.values = initConfig; + async.series([ install.setup, - prestart.loadConfig, + function (next) { + var configFile = paths.config; + if (nconf.get('config')) { + configFile = path.resolve(paths.baseDir, nconf.get('config')); + } + + prestart.loadConfig(configFile); + next(); + }, build.buildAll, ], function (err, data) { // Disregard build step data diff --git a/src/install.js b/src/install.js index 89b3d3fa49..b55b1ed08e 100644 --- a/src/install.js +++ b/src/install.js @@ -8,7 +8,7 @@ var winston = require('winston'); var nconf = require('nconf'); var utils = require('./utils.js'); -var install = {}; +var install = module.exports; var questions = {}; questions.main = [ @@ -42,17 +42,15 @@ questions.optional = [ ]; function checkSetupFlag(next) { - var setupVal; + var setupVal = install.values; try { if (nconf.get('setup')) { setupVal = JSON.parse(nconf.get('setup')); } - } catch (err) { - setupVal = undefined; - } + } catch (err) {} - if (setupVal && setupVal instanceof Object) { + if (setupVal && typeof setupVal === 'object') { if (setupVal['admin:username'] && setupVal['admin:password'] && setupVal['admin:password:confirm'] && setupVal['admin:email']) { install.values = setupVal; next(); @@ -74,9 +72,8 @@ function checkSetupFlag(next) { process.exit(); } } else if (nconf.get('database')) { - install.values = { - database: nconf.get('database'), - }; + install.values = install.values || {}; + install.values.database = nconf.get('database'); next(); } else { next(); @@ -549,11 +546,9 @@ install.save = function (server_conf, callback) { console.log('Configuration Saved OK'); nconf.file({ - file: path.join(__dirname, '..', 'config.json'), + file: serverConfigPath, }); callback(); }); }; - -module.exports = install; diff --git a/src/middleware/index.js b/src/middleware/index.js index 28ce7c3e10..cb91f82339 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -203,12 +203,19 @@ middleware.delayLoading = function (req, res, next) { }; var viewsDir = nconf.get('views_dir'); +var workingCache = {}; + middleware.templatesOnDemand = function (req, res, next) { var filePath = req.filePath || path.join(viewsDir, req.path); if (!filePath.endsWith('.js')) { return next(); } + if (workingCache[filePath]) { + workingCache[filePath].push(next); + return; + } + async.waterfall([ function (cb) { file.exists(filePath, cb); @@ -218,6 +225,14 @@ middleware.templatesOnDemand = function (req, res, next) { return next(); } + // need to check here again + // because compilation could have started since last check + if (workingCache[filePath]) { + workingCache[filePath].push(next); + return; + } + + workingCache[filePath] = [next]; fs.readFile(filePath.replace(/\.js$/, '.tpl'), 'utf8', cb); }, function (source, cb) { @@ -229,5 +244,12 @@ middleware.templatesOnDemand = function (req, res, next) { function (compiled, cb) { fs.writeFile(filePath, compiled, cb); }, - ], next); + ], function (err) { + var arr = workingCache[filePath]; + workingCache[filePath] = null; + + arr.forEach(function (callback) { + callback(err); + }); + }); }; diff --git a/src/upgrades/TEMPLATE b/src/upgrades/TEMPLATE index 9618bc4f9e..54e4e2a2c0 100644 --- a/src/upgrades/TEMPLATE +++ b/src/upgrades/TEMPLATE @@ -6,7 +6,10 @@ var async = require('async'); var winston = require('winston'); module.exports = { + // you should use spaces + // the underscores are there so you can double click to select the whole thing name: 'User_friendly_upgrade_script_name', + // remember, month is zero-indexed (so January is 0, December is 11) timestamp: Date.UTC(2017, 0, 1), method: function (callback) { // Do stuff here...