diff --git a/src/database/index.js b/src/database/index.js index 818efb4fe0..0590b042d6 100644 --- a/src/database/index.js +++ b/src/database/index.js @@ -9,7 +9,7 @@ if (!databaseName) { process.exit(); } -var primaryDB = require('./' + databaseName); +const primaryDB = require('./' + databaseName); primaryDB.parseIntFields = function (data, intFields, requestedFields) { intFields.forEach((field) => { @@ -19,7 +19,7 @@ primaryDB.parseIntFields = function (data, intFields, requestedFields) { }); }; -primaryDB.initSessionStore = function (callback) { +primaryDB.initSessionStore = async function () { const sessionStoreConfig = nconf.get('session_store') || nconf.get('redis') || nconf.get(databaseName); let sessionStoreDB = primaryDB; @@ -30,13 +30,7 @@ primaryDB.initSessionStore = function (callback) { sessionStoreDB = require('./redis'); } - sessionStoreDB.createSessionStore(sessionStoreConfig, function (err, sessionStore) { - if (err) { - return callback(err); - } - primaryDB.sessionStore = sessionStore; - callback(); - }); + primaryDB.sessionStore = await sessionStoreDB.createSessionStore(sessionStoreConfig); }; module.exports = primaryDB; diff --git a/src/start.js b/src/start.js index ef1d624d65..04152a2822 100644 --- a/src/start.js +++ b/src/start.js @@ -1,90 +1,75 @@ 'use strict'; -var async = require('async'); -var nconf = require('nconf'); -var url = require('url'); -var winston = require('winston'); +const nconf = require('nconf'); +const url = require('url'); +const winston = require('winston'); -var start = module.exports; +const start = module.exports; -start.start = function () { - var db = require('./database'); +start.start = async function () { + const db = require('./database'); setupConfigs(); printStartupInfo(); addProcessHandlers(); + try { + await db.init(); + + const meta = require('./meta'); + await Promise.all([ + db.checkCompatibility(), + meta.configs.init(), + require('./upgrade').check(), + ]); + + if (nconf.get('dep-check') === undefined || nconf.get('dep-check') !== false) { + await meta.dependencies.check(); + } else { + winston.warn('[init] Dependency checking skipped!'); + } - async.waterfall([ - function (next) { - db.init(next); - }, - function (next) { - var meta = require('./meta'); - async.parallel([ - async.apply(db.checkCompatibility), - async.apply(meta.configs.init), - function (next) { - if (nconf.get('dep-check') === undefined || nconf.get('dep-check') !== false) { - meta.dependencies.check(next); - } else { - winston.warn('[init] Dependency checking skipped!'); - setImmediate(next); - } - }, - function (next) { - require('./upgrade').check(next); - }, - ], function (err) { - next(err); - }); - }, - function (next) { - db.initSessionStore(next); - }, - function (next) { - var webserver = require('./webserver'); - require('./socket.io').init(webserver.server); - - if (nconf.get('runJobs')) { - require('./notifications').startJobs(); - require('./user').startJobs(); - require('./plugins').startJobs(); - } - - webserver.listen(next); - }, - ], function (err) { - if (err) { - switch (err.message) { - case 'schema-out-of-date': - winston.error('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); - winston.error(' ./nodebb upgrade'); - break; - case 'dependencies-out-of-date': - winston.error('One or more of NodeBB\'s dependent packages are out-of-date. Please run the following command to update them:'); - winston.error(' ./nodebb upgrade'); - break; - case 'dependencies-missing': - winston.error('One or more of NodeBB\'s dependent packages are missing. Please run the following command to update them:'); - winston.error(' ./nodebb upgrade'); - break; - default: - winston.error(err); - break; - } - - // Either way, bad stuff happened. Abort start. - process.exit(); + await db.initSessionStore(); + + const webserver = require('./webserver'); + require('./socket.io').init(webserver.server); + + if (nconf.get('runJobs')) { + require('./notifications').startJobs(); + require('./user').startJobs(); + require('./plugins').startJobs(); } + await webserver.listen(); + if (process.send) { process.send({ action: 'listening', }); } - }); + } catch (err) { + switch (err.message) { + case 'schema-out-of-date': + winston.error('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:'); + winston.error(' ./nodebb upgrade'); + break; + case 'dependencies-out-of-date': + winston.error('One or more of NodeBB\'s dependent packages are out-of-date. Please run the following command to update them:'); + winston.error(' ./nodebb upgrade'); + break; + case 'dependencies-missing': + winston.error('One or more of NodeBB\'s dependent packages are missing. Please run the following command to update them:'); + winston.error(' ./nodebb upgrade'); + break; + default: + winston.error(err); + break; + } + + // Either way, bad stuff happened. Abort start. + process.exit(); + } }; function setupConfigs() { @@ -93,8 +78,8 @@ function setupConfigs() { nconf.set('sessionKey', 'express.sid'); } // Parse out the relative_url and other goodies from the configured URL - var urlObject = url.parse(nconf.get('url')); - var relativePath = urlObject.pathname !== '/' ? urlObject.pathname.replace(/\/+$/, '') : ''; + const urlObject = url.parse(nconf.get('url')); + const relativePath = urlObject.pathname !== '/' ? urlObject.pathname.replace(/\/+$/, '') : ''; nconf.set('base_url', urlObject.protocol + '//' + urlObject.host); nconf.set('secure', urlObject.protocol === 'https:'); nconf.set('use_port', !!urlObject.port); @@ -106,8 +91,8 @@ function printStartupInfo() { if (nconf.get('isPrimary') === 'true') { winston.info('Initializing NodeBB v%s %s', nconf.get('version'), nconf.get('url')); - var host = nconf.get(nconf.get('database') + ':host'); - var storeLocation = host ? 'at ' + host + (!host.includes('/') ? ':' + nconf.get(nconf.get('database') + ':port') : '') : ''; + const host = nconf.get(nconf.get('database') + ':host'); + const storeLocation = host ? 'at ' + host + (!host.includes('/') ? ':' + nconf.get(nconf.get('database') + ':port') : '') : ''; winston.verbose('* using %s store %s', nconf.get('database'), storeLocation); winston.verbose('* using themes stored in: %s', nconf.get('themes_path')); @@ -138,27 +123,19 @@ function restart() { } } -function shutdown(code) { +async function shutdown(code) { winston.info('[app] Shutdown (SIGTERM/SIGINT) Initialised.'); - async.waterfall([ - function (next) { - require('./webserver').destroy(next); - }, - function (next) { - winston.info('[app] Web server closed to connections.'); - require('./analytics').writeData(next); - }, - function (next) { - winston.info('[app] Live analytics saved.'); - require('./database').close(next); - }, - ], function (err) { - if (err) { - winston.error(err); - return process.exit(code || 0); - } + try { + await require('./webserver').destroy(); + winston.info('[app] Web server closed to connections.'); + await require('./analytics').writeData(); + winston.info('[app] Live analytics saved.'); + await require('./database').close(); winston.info('[app] Database connection closed.'); winston.info('[app] Shutdown complete.'); process.exit(code || 0); - }); + } catch (err) { + winston.error(err); + return process.exit(code || 0); + } } diff --git a/test/build.js b/test/build.js index ca4c4ab866..f3dea9a468 100644 --- a/test/build.js +++ b/test/build.js @@ -125,7 +125,7 @@ describe('Build', function (done) { before(function (done) { async.parallel([ async.apply(rimraf, path.join(__dirname, '../build/public')), - async.apply(db.activatePlugin, 'nodebb-plugin-markdown'), + async.apply(db.sortedSetAdd, 'plugins:active', Date.now(), 'nodebb-plugin-markdown'), ], done); }); diff --git a/test/mocks/databasemock.js b/test/mocks/databasemock.js index ed2234467c..ab353cd923 100644 --- a/test/mocks/databasemock.js +++ b/test/mocks/databasemock.js @@ -7,15 +7,15 @@ require('../../require-main'); -var async = require('async'); -var path = require('path'); -var nconf = require('nconf'); -var url = require('url'); +const path = require('path'); +const nconf = require('nconf'); +const url = require('url'); +const util = require('util'); global.env = process.env.TEST_ENV || 'production'; -var winston = require('winston'); -var packageInfo = require('../../package'); +const winston = require('winston'); +const packageInfo = require('../../package'); winston.add(new winston.transports.Console({ format: winston.format.combine( @@ -33,8 +33,8 @@ nconf.defaults({ relative_path: '', }); -var urlObject = url.parse(nconf.get('url')); -var relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; +const urlObject = url.parse(nconf.get('url')); +const relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; nconf.set('relative_path', relativePath); if (!nconf.get('isCluster')) { @@ -42,9 +42,9 @@ if (!nconf.get('isCluster')) { nconf.set('isCluster', 'true'); } -var dbType = nconf.get('database'); -var testDbConfig = nconf.get('test_database'); -var productionDbConfig = nconf.get(dbType); +const dbType = nconf.get('database'); +const testDbConfig = nconf.get('test_database'); +const productionDbConfig = nconf.get(dbType); if (!testDbConfig) { const errorText = 'test_database is not defined'; @@ -100,161 +100,127 @@ nconf.set(dbType, testDbConfig); winston.info('database config %s', dbType, testDbConfig); winston.info('environment ' + global.env); -var db = require('../../src/database'); +const db = require('../../src/database'); module.exports = db; -before(function (done) { +before(async function () { this.timeout(30000); - async.series([ - function (next) { - db.init(next); - }, - function (next) { - db.createIndices(next); - }, - function (next) { - setupMockDefaults(next); - }, - function (next) { - db.initSessionStore(next); - }, - function (next) { - var meta = require('../../src/meta'); - - // nconf defaults, if not set in config - if (!nconf.get('sessionKey')) { - nconf.set('sessionKey', 'express.sid'); - } - // Parse out the relative_url and other goodies from the configured URL - var urlObject = url.parse(nconf.get('url')); - var relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; - nconf.set('base_url', urlObject.protocol + '//' + urlObject.host); - nconf.set('secure', urlObject.protocol === 'https:'); - nconf.set('use_port', !!urlObject.port); - nconf.set('relative_path', relativePath); - nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); - nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path'))); - nconf.set('upload_url', '/assets/uploads'); - - nconf.set('core_templates_path', path.join(__dirname, '../../src/views')); - nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates')); - nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path')); - nconf.set('theme_config', path.join(nconf.get('themes_path'), 'nodebb-theme-persona', 'theme.json')); - nconf.set('bcrypt_rounds', 1); - - nconf.set('version', packageInfo.version); - - meta.dependencies.check(next); - }, - function (next) { - var webserver = require('../../src/webserver'); - var sockets = require('../../src/socket.io'); - sockets.init(webserver.server); - - require('../../src/notifications').startJobs(); - require('../../src/user').startJobs(); - - webserver.listen(next); - }, - ], done); + await db.init(); + await db.createIndices(); + await setupMockDefaults(); + await db.initSessionStore(); + + const meta = require('../../src/meta'); + + // nconf defaults, if not set in config + if (!nconf.get('sessionKey')) { + nconf.set('sessionKey', 'express.sid'); + } + // Parse out the relative_url and other goodies from the configured URL + const urlObject = url.parse(nconf.get('url')); + const relativePath = urlObject.pathname !== '/' ? urlObject.pathname : ''; + nconf.set('base_url', urlObject.protocol + '//' + urlObject.host); + nconf.set('secure', urlObject.protocol === 'https:'); + nconf.set('use_port', !!urlObject.port); + nconf.set('relative_path', relativePath); + nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); + nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path'))); + nconf.set('upload_url', '/assets/uploads'); + + nconf.set('core_templates_path', path.join(__dirname, '../../src/views')); + nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates')); + nconf.set('theme_templates_path', meta.config['theme:templates'] ? path.join(nconf.get('themes_path'), meta.config['theme:id'], meta.config['theme:templates']) : nconf.get('base_templates_path')); + nconf.set('theme_config', path.join(nconf.get('themes_path'), 'nodebb-theme-persona', 'theme.json')); + nconf.set('bcrypt_rounds', 1); + + nconf.set('version', packageInfo.version); + + await meta.dependencies.check(); + + const webserver = require('../../src/webserver'); + const sockets = require('../../src/socket.io'); + sockets.init(webserver.server); + + require('../../src/notifications').startJobs(); + require('../../src/user').startJobs(); + + await webserver.listen(); // Iterate over all of the test suites/contexts this.test.parent.suites.forEach(function (suite) { // Attach an afterAll listener that resets the defaults - suite.afterAll(function (done) { - setupMockDefaults(done); + suite.afterAll(async function () { + await setupMockDefaults(); }); }); }); -function setupMockDefaults(callback) { - var meta = require('../../src/meta'); - - async.series([ - function (next) { - db.emptydb(next); - }, - function (next) { - var groups = require('../../src/groups'); - groups.resetCache(); - var postCache = require('../../src/posts/cache'); - postCache.reset(); - var localCache = require('../../src/cache'); - localCache.reset(); - next(); - }, - function (next) { - winston.info('test_database flushed'); - setupDefaultConfigs(meta, next); - }, - function (next) { - giveDefaultGlobalPrivileges(next); - }, - function (next) { - meta.configs.init(next); - }, - function (next) { - meta.config.postDelay = 0; - meta.config.initialPostDelay = 0; - meta.config.newbiePostDelay = 0; - - enableDefaultPlugins(next); - }, - function (next) { - meta.themes.set({ - type: 'local', - id: 'nodebb-theme-persona', - }, next); - }, - function (next) { - var rimraf = require('rimraf'); - rimraf('test/uploads', next); - }, - function (next) { - var mkdirp = require('mkdirp'); - async.eachSeries([ - 'test/uploads', - 'test/uploads/category', - 'test/uploads/files', - 'test/uploads/system', - 'test/uploads/sounds', - 'test/uploads/profile', - ], mkdirp, next); - }, - ], callback); +async function setupMockDefaults() { + const meta = require('../../src/meta'); + await db.emptydb(); + + require('../../src/groups').resetCache(); + require('../../src/posts/cache').reset(); + require('../../src/cache').reset(); + winston.info('test_database flushed'); + await setupDefaultConfigs(meta); + await giveDefaultGlobalPrivileges(); + await meta.configs.init(); + meta.config.postDelay = 0; + meta.config.initialPostDelay = 0; + meta.config.newbiePostDelay = 0; + + await enableDefaultPlugins(); + + await meta.themes.set({ + type: 'local', + id: 'nodebb-theme-persona', + }); + + const rimraf = util.promisify(require('rimraf')); + await rimraf('test/uploads'); + + const mkdirp = util.promisify(require('mkdirp')); + + const folders = [ + 'test/uploads', + 'test/uploads/category', + 'test/uploads/files', + 'test/uploads/system', + 'test/uploads/sounds', + 'test/uploads/profile', + ]; + for (const folder of folders) { + /* eslint-disable no-await-in-loop */ + await mkdirp(folder); + } } db.setupMockDefaults = setupMockDefaults; -function setupDefaultConfigs(meta, next) { +async function setupDefaultConfigs(meta) { winston.info('Populating database with default configs, if not already set...\n'); - var defaults = require(path.join(nconf.get('base_dir'), 'install/data/defaults.json')); + const defaults = require(path.join(nconf.get('base_dir'), 'install/data/defaults.json')); defaults.eventLoopCheckEnabled = 0; defaults.minimumPasswordStrength = 0; - meta.configs.setOnEmpty(defaults, next); + await meta.configs.setOnEmpty(defaults); } -function giveDefaultGlobalPrivileges(next) { - var privileges = require('../../src/privileges'); - async.waterfall([ - function (next) { - privileges.global.give([ - 'chat', 'upload:post:image', 'signature', 'search:content', - 'search:users', 'search:tags', 'local:login', 'view:users', 'view:tags', 'view:groups', - ], 'registered-users', next); - }, - function (next) { - privileges.global.give([ - 'view:users', 'view:tags', 'view:groups', - ], 'guests', next); - }, - ], next); +async function giveDefaultGlobalPrivileges() { + const privileges = require('../../src/privileges'); + await privileges.global.give([ + 'chat', 'upload:post:image', 'signature', 'search:content', + 'search:users', 'search:tags', 'local:login', 'view:users', 'view:tags', 'view:groups', + ], 'registered-users'); + await privileges.global.give([ + 'view:users', 'view:tags', 'view:groups', + ], 'guests'); } -function enableDefaultPlugins(callback) { +async function enableDefaultPlugins() { winston.info('Enabling default plugins\n'); - var defaultEnabled = [ + const defaultEnabled = [ 'nodebb-plugin-dbsearch', 'nodebb-plugin-soundpack-default', 'nodebb-widget-essentials', @@ -262,9 +228,5 @@ function enableDefaultPlugins(callback) { winston.info('[install/enableDefaultPlugins] activating default plugins', defaultEnabled); - db.sortedSetAdd('plugins:active', Object.keys(defaultEnabled), defaultEnabled, callback); + await db.sortedSetAdd('plugins:active', Object.keys(defaultEnabled), defaultEnabled); } - -db.activatePlugin = function (id, callback) { - db.sortedSetAdd('plugins:active', Date.now(), id, callback); -};