Files
NodeBB/src/webserver.js

335 lines
9.3 KiB
JavaScript
Raw Normal View History

2015-07-21 11:23:16 -04:00
'use strict';
2021-02-04 00:06:15 -07:00
const fs = require('fs');
2019-07-22 18:16:18 -04:00
const util = require('util');
2021-02-04 00:06:15 -07:00
const path = require('path');
const os = require('os');
const nconf = require('nconf');
const express = require('express');
const chalk = require('chalk');
2021-02-04 00:06:15 -07:00
const app = express();
2020-10-14 20:02:32 -04:00
app.renderAsync = util.promisify((tpl, data, callback) => app.render(tpl, data, callback));
2021-02-04 00:06:15 -07:00
let server;
const winston = require('winston');
const flash = require('connect-flash');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const useragent = require('express-useragent');
const favicon = require('serve-favicon');
const detector = require('spider-detector');
const helmet = require('helmet');
const Benchpress = require('benchpressjs');
const db = require('./database');
const analytics = require('./analytics');
const file = require('./file');
const emailer = require('./emailer');
const meta = require('./meta');
const logger = require('./logger');
const plugins = require('./plugins');
const flags = require('./flags');
const topicEvents = require('./topics/events');
Webpack5 (#10311) * feat: webpack 5 part 1 * fix: gruntfile fixes * fix: fix taskbar warning add app.importScript copy public/src/modules to build folder * refactor: remove commented old code * feat: reenable admin * fix: acp settings pages, fix sortable on manage categories embedded require in html not allowed * fix: bundle serialize/deserizeli so plugins dont break * test: fixe util tests * test: fix require path * test: more test fixes * test: require correct utils module * test: require correct utils * test: log stack * test: fix db require blowing up tests * test: move and disable bundle test * refactor: add aliases * test: disable testing route * fix: move webpack modules necessary for build, into `dependencies` * test: fix one more test remove 500-embed.tpl * fix: restore use of assets/nodebb.min.js, at least for now * fix: remove unnecessary line break * fix: point to proper ACP bundle * test: maybe fix build test * test: composer * refactor: dont need dist * refactor: more cleanup use everything from build/public folder * get rid of conditional import in app.js * fix: ace * refactor: cropper alias * test: lint and test fixes * lint: fix * refactor: rename function to app.require * refactor: go back to using app.require * chore: use github branch * chore: use webpack branch * feat: webpack webinstaller * feat: add chunkFile name with contenthash * refactor: move hooks to top * refactor: get rid of template500Function * fix(deps): use webpack5 branch of 2factor plugin * chore: tagging v2.0.0-beta.0 pre-release version :boom: :shipit: :tada: :rocket: * refactor: disable cache on templates loadTemplate is called once by benchpress and the result is cache internally * refactor: add server side helpers.js * feat: deprecate /plugins shorthand route, closes #10343 * refactor: use build/public for webpack * test: fix filename * fix: more specific selector * lint: ignore * refactor: fix comments * test: add debug for random failing test * refactor: cleanup remove test page, remove dupe functions in utils.common * lint: use relative path for now * chore: bump prerelease version * feat: add translateKeys * fix: optional params * fix: get rid of extra timeago files * refactor: cleanup, require timeago locale earlier remove translator.prepareDOM, it is in header.tpl html tag * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels (#10378) * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels - Existing hooks are preserved (to be deprecated at a later date, possibly) - New init hooks are called on NodeBB start, and provide a one-stop shop to add new privileges, instead of having to add to four different hooks * docs: fix typo in comment * test: spec changes * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels (#10378) * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels - Existing hooks are preserved (to be deprecated at a later date, possibly) - New init hooks are called on NodeBB start, and provide a one-stop shop to add new privileges, instead of having to add to four different hooks * docs: fix typo in comment * test: spec changes * feat: allow app.require('bootbox'/'benchpressjs') * refactor: require server side utils * test: jquery ready * change istaller to use build/public * test: use document.addEventListener * refactor: closes #10301 * refactor: generateTopicClass * fix: column counts for other privileges * fix: #10443, regression where sorted-list items did not render into the DOM in the predicted order [breaking] * fix: typo in hook name * refactor: introduce a generic autocomplete.init() method that can be called to add nodebb-style autocompletion but using different data sources (e.g. not user/groups/tags) * fix: crash if `delay` not passed in (as it cannot be destructured) * refactor: replace substr * feat: set --panel-offset style in html element based on stored value in localStorage * refactor: addDropupHandler() logic to be less naive - Take into account height of the menu - Don't apply dropUp logic if there's nothing in the dropdown - Remove 'hidden' class (added by default in Persona for post tools) when menu items are added closes #10423 * refactor: simplify utils.params [breaking] Retrospective analysis of the usage of this method suggests that the options passed in are superfluous, and that only `url` is required. Using a browser built-in makes more sense to accomplish what this method sets out to do. * feat: add support for returning full URLSearchParams for utils.params * fix: utils.params() fallback handling * fix: default empty obj for params() * fix: remove \'loggedin\' and \'register\' qs parameters once they have been used, delay invocation of messages until ajaxify.end * fix: utils.params() not allowing relative paths to be passed in * refactor(DRY): new assertPasswordValidity utils method * fix: incorrect error message returned on insufficient privilege on flag edit * fix: read/update/delete access to flags API should be limited for moderators to only post flags in categories they moderate - added failing tests and patched up middleware.assert.flags to fix * refactor: flag api v3 tests to create new post and flags on every round * fix: missing error:no-flag language key * refactor: flags.canView to check flag existence, simplify middleware.assert.flag * feat: flag deletion API endpoint, #10426 * feat: UI for flag deletion, closes #10426 * chore: update plugin versions * chore: up emoji * chore: update markdown * chore: up emoji-android * fix: regression caused by utils.params() refactor, supports arrays and pipes all values through utils.toType, adjusts tests to type check Co-authored-by: Julian Lam <julian@nodebb.org>
2022-04-29 21:39:33 -04:00
const privileges = require('./privileges');
2021-02-04 00:06:15 -07:00
const routes = require('./routes');
const auth = require('./routes/authentication');
Webpack5 (#10311) * feat: webpack 5 part 1 * fix: gruntfile fixes * fix: fix taskbar warning add app.importScript copy public/src/modules to build folder * refactor: remove commented old code * feat: reenable admin * fix: acp settings pages, fix sortable on manage categories embedded require in html not allowed * fix: bundle serialize/deserizeli so plugins dont break * test: fixe util tests * test: fix require path * test: more test fixes * test: require correct utils module * test: require correct utils * test: log stack * test: fix db require blowing up tests * test: move and disable bundle test * refactor: add aliases * test: disable testing route * fix: move webpack modules necessary for build, into `dependencies` * test: fix one more test remove 500-embed.tpl * fix: restore use of assets/nodebb.min.js, at least for now * fix: remove unnecessary line break * fix: point to proper ACP bundle * test: maybe fix build test * test: composer * refactor: dont need dist * refactor: more cleanup use everything from build/public folder * get rid of conditional import in app.js * fix: ace * refactor: cropper alias * test: lint and test fixes * lint: fix * refactor: rename function to app.require * refactor: go back to using app.require * chore: use github branch * chore: use webpack branch * feat: webpack webinstaller * feat: add chunkFile name with contenthash * refactor: move hooks to top * refactor: get rid of template500Function * fix(deps): use webpack5 branch of 2factor plugin * chore: tagging v2.0.0-beta.0 pre-release version :boom: :shipit: :tada: :rocket: * refactor: disable cache on templates loadTemplate is called once by benchpress and the result is cache internally * refactor: add server side helpers.js * feat: deprecate /plugins shorthand route, closes #10343 * refactor: use build/public for webpack * test: fix filename * fix: more specific selector * lint: ignore * refactor: fix comments * test: add debug for random failing test * refactor: cleanup remove test page, remove dupe functions in utils.common * lint: use relative path for now * chore: bump prerelease version * feat: add translateKeys * fix: optional params * fix: get rid of extra timeago files * refactor: cleanup, require timeago locale earlier remove translator.prepareDOM, it is in header.tpl html tag * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels (#10378) * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels - Existing hooks are preserved (to be deprecated at a later date, possibly) - New init hooks are called on NodeBB start, and provide a one-stop shop to add new privileges, instead of having to add to four different hooks * docs: fix typo in comment * test: spec changes * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels (#10378) * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels - Existing hooks are preserved (to be deprecated at a later date, possibly) - New init hooks are called on NodeBB start, and provide a one-stop shop to add new privileges, instead of having to add to four different hooks * docs: fix typo in comment * test: spec changes * feat: allow app.require('bootbox'/'benchpressjs') * refactor: require server side utils * test: jquery ready * change istaller to use build/public * test: use document.addEventListener * refactor: closes #10301 * refactor: generateTopicClass * fix: column counts for other privileges * fix: #10443, regression where sorted-list items did not render into the DOM in the predicted order [breaking] * fix: typo in hook name * refactor: introduce a generic autocomplete.init() method that can be called to add nodebb-style autocompletion but using different data sources (e.g. not user/groups/tags) * fix: crash if `delay` not passed in (as it cannot be destructured) * refactor: replace substr * feat: set --panel-offset style in html element based on stored value in localStorage * refactor: addDropupHandler() logic to be less naive - Take into account height of the menu - Don't apply dropUp logic if there's nothing in the dropdown - Remove 'hidden' class (added by default in Persona for post tools) when menu items are added closes #10423 * refactor: simplify utils.params [breaking] Retrospective analysis of the usage of this method suggests that the options passed in are superfluous, and that only `url` is required. Using a browser built-in makes more sense to accomplish what this method sets out to do. * feat: add support for returning full URLSearchParams for utils.params * fix: utils.params() fallback handling * fix: default empty obj for params() * fix: remove \'loggedin\' and \'register\' qs parameters once they have been used, delay invocation of messages until ajaxify.end * fix: utils.params() not allowing relative paths to be passed in * refactor(DRY): new assertPasswordValidity utils method * fix: incorrect error message returned on insufficient privilege on flag edit * fix: read/update/delete access to flags API should be limited for moderators to only post flags in categories they moderate - added failing tests and patched up middleware.assert.flags to fix * refactor: flag api v3 tests to create new post and flags on every round * fix: missing error:no-flag language key * refactor: flags.canView to check flag existence, simplify middleware.assert.flag * feat: flag deletion API endpoint, #10426 * feat: UI for flag deletion, closes #10426 * chore: update plugin versions * chore: up emoji * chore: update markdown * chore: up emoji-android * fix: regression caused by utils.params() refactor, supports arrays and pipes all values through utils.toType, adjusts tests to type check Co-authored-by: Julian Lam <julian@nodebb.org>
2022-04-29 21:39:33 -04:00
const helpers = require('./helpers');
2015-07-21 11:23:16 -04:00
if (nconf.get('ssl')) {
server = require('https').createServer({
key: fs.readFileSync(nconf.get('ssl').key),
2017-02-17 19:31:21 -07:00
cert: fs.readFileSync(nconf.get('ssl').cert),
2015-07-21 11:23:16 -04:00
}, app);
} else {
server = require('http').createServer(app);
}
module.exports.server = server;
2018-11-13 14:11:41 -05:00
module.exports.app = app;
2015-07-21 11:23:16 -04:00
2021-02-04 00:01:39 -07:00
server.on('error', (err) => {
2015-07-21 11:23:16 -04:00
if (err.code === 'EADDRINUSE') {
2021-02-03 23:59:08 -07:00
winston.error(`NodeBB address in use, exiting...\n${err.stack}`);
2015-07-21 11:23:16 -04:00
} else {
winston.error(err.stack);
2015-07-21 11:23:16 -04:00
}
throw err;
2015-07-21 11:23:16 -04:00
});
// see https://github.com/isaacs/server-destroy/blob/master/index.js
2021-02-04 00:06:15 -07:00
const connections = {};
2021-02-04 00:01:39 -07:00
server.on('connection', (conn) => {
2021-02-04 00:06:15 -07:00
const key = `${conn.remoteAddress}:${conn.remotePort}`;
connections[key] = conn;
2021-02-04 00:01:39 -07:00
conn.on('close', () => {
delete connections[key];
});
});
2019-07-22 18:16:18 -04:00
exports.destroy = function (callback) {
server.close(callback);
2021-02-04 01:34:30 -07:00
for (const connection of Object.values(connections)) {
connection.destroy();
}
};
2019-07-22 18:16:18 -04:00
exports.listen = async function () {
2015-07-21 11:23:16 -04:00
emailer.registerApp(app);
2019-07-22 18:16:18 -04:00
setupExpressApp(app);
helpers.register();
logger.init(app);
await initializeNodeBB();
winston.info('🎉 NodeBB Ready');
2019-07-22 18:16:18 -04:00
require('./socket.io').server.emit('event:nodebb.ready', {
'cache-buster': meta.config['cache-buster'],
hostname: os.hostname(),
});
2015-07-21 11:23:16 -04:00
plugins.hooks.fire('action:nodebb.ready');
2016-10-16 21:51:42 +03:00
2021-03-05 18:18:16 -05:00
await listen();
2015-07-21 11:23:16 -04:00
};
2019-07-22 18:16:18 -04:00
async function initializeNodeBB() {
const middleware = require('./middleware');
await meta.themes.setupPaths();
await plugins.init(app, middleware);
await plugins.hooks.fire('static:assets.prepare', {});
await plugins.hooks.fire('static:app.preload', {
2019-07-22 18:16:18 -04:00
app: app,
middleware: middleware,
2017-03-02 17:25:32 +03:00
});
2019-07-22 18:16:18 -04:00
await routes(app, middleware);
Webpack5 (#10311) * feat: webpack 5 part 1 * fix: gruntfile fixes * fix: fix taskbar warning add app.importScript copy public/src/modules to build folder * refactor: remove commented old code * feat: reenable admin * fix: acp settings pages, fix sortable on manage categories embedded require in html not allowed * fix: bundle serialize/deserizeli so plugins dont break * test: fixe util tests * test: fix require path * test: more test fixes * test: require correct utils module * test: require correct utils * test: log stack * test: fix db require blowing up tests * test: move and disable bundle test * refactor: add aliases * test: disable testing route * fix: move webpack modules necessary for build, into `dependencies` * test: fix one more test remove 500-embed.tpl * fix: restore use of assets/nodebb.min.js, at least for now * fix: remove unnecessary line break * fix: point to proper ACP bundle * test: maybe fix build test * test: composer * refactor: dont need dist * refactor: more cleanup use everything from build/public folder * get rid of conditional import in app.js * fix: ace * refactor: cropper alias * test: lint and test fixes * lint: fix * refactor: rename function to app.require * refactor: go back to using app.require * chore: use github branch * chore: use webpack branch * feat: webpack webinstaller * feat: add chunkFile name with contenthash * refactor: move hooks to top * refactor: get rid of template500Function * fix(deps): use webpack5 branch of 2factor plugin * chore: tagging v2.0.0-beta.0 pre-release version :boom: :shipit: :tada: :rocket: * refactor: disable cache on templates loadTemplate is called once by benchpress and the result is cache internally * refactor: add server side helpers.js * feat: deprecate /plugins shorthand route, closes #10343 * refactor: use build/public for webpack * test: fix filename * fix: more specific selector * lint: ignore * refactor: fix comments * test: add debug for random failing test * refactor: cleanup remove test page, remove dupe functions in utils.common * lint: use relative path for now * chore: bump prerelease version * feat: add translateKeys * fix: optional params * fix: get rid of extra timeago files * refactor: cleanup, require timeago locale earlier remove translator.prepareDOM, it is in header.tpl html tag * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels (#10378) * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels - Existing hooks are preserved (to be deprecated at a later date, possibly) - New init hooks are called on NodeBB start, and provide a one-stop shop to add new privileges, instead of having to add to four different hooks * docs: fix typo in comment * test: spec changes * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels (#10378) * refactor: privileges system to use a Map in the backend instead of separate objects for keys and labels - Existing hooks are preserved (to be deprecated at a later date, possibly) - New init hooks are called on NodeBB start, and provide a one-stop shop to add new privileges, instead of having to add to four different hooks * docs: fix typo in comment * test: spec changes * feat: allow app.require('bootbox'/'benchpressjs') * refactor: require server side utils * test: jquery ready * change istaller to use build/public * test: use document.addEventListener * refactor: closes #10301 * refactor: generateTopicClass * fix: column counts for other privileges * fix: #10443, regression where sorted-list items did not render into the DOM in the predicted order [breaking] * fix: typo in hook name * refactor: introduce a generic autocomplete.init() method that can be called to add nodebb-style autocompletion but using different data sources (e.g. not user/groups/tags) * fix: crash if `delay` not passed in (as it cannot be destructured) * refactor: replace substr * feat: set --panel-offset style in html element based on stored value in localStorage * refactor: addDropupHandler() logic to be less naive - Take into account height of the menu - Don't apply dropUp logic if there's nothing in the dropdown - Remove 'hidden' class (added by default in Persona for post tools) when menu items are added closes #10423 * refactor: simplify utils.params [breaking] Retrospective analysis of the usage of this method suggests that the options passed in are superfluous, and that only `url` is required. Using a browser built-in makes more sense to accomplish what this method sets out to do. * feat: add support for returning full URLSearchParams for utils.params * fix: utils.params() fallback handling * fix: default empty obj for params() * fix: remove \'loggedin\' and \'register\' qs parameters once they have been used, delay invocation of messages until ajaxify.end * fix: utils.params() not allowing relative paths to be passed in * refactor(DRY): new assertPasswordValidity utils method * fix: incorrect error message returned on insufficient privilege on flag edit * fix: read/update/delete access to flags API should be limited for moderators to only post flags in categories they moderate - added failing tests and patched up middleware.assert.flags to fix * refactor: flag api v3 tests to create new post and flags on every round * fix: missing error:no-flag language key * refactor: flags.canView to check flag existence, simplify middleware.assert.flag * feat: flag deletion API endpoint, #10426 * feat: UI for flag deletion, closes #10426 * chore: update plugin versions * chore: up emoji * chore: update markdown * chore: up emoji-android * fix: regression caused by utils.params() refactor, supports arrays and pipes all values through utils.toType, adjusts tests to type check Co-authored-by: Julian Lam <julian@nodebb.org>
2022-04-29 21:39:33 -04:00
await privileges.init();
2019-07-22 18:16:18 -04:00
await meta.blacklist.load();
await flags.init();
await analytics.init();
await topicEvents.init();
}
2019-07-22 18:16:18 -04:00
function setupExpressApp(app) {
const middleware = require('./middleware');
const pingController = require('./controllers/ping');
2016-08-26 19:40:14 +03:00
2019-07-22 18:16:18 -04:00
const relativePath = nconf.get('relative_path');
const viewsDir = nconf.get('views_dir');
2021-02-04 00:01:39 -07:00
app.engine('tpl', (filepath, data, next) => {
2017-09-23 12:51:20 -06:00
filepath = filepath.replace(/\.tpl$/, '.js');
Benchpress.__express(filepath, data, next);
});
app.set('view engine', 'tpl');
app.set('views', viewsDir);
app.set('json spaces', global.env === 'development' ? 4 : 0);
app.use(flash());
app.enable('view cache');
2016-09-21 12:00:37 +03:00
if (global.env !== 'development') {
app.enable('cache');
app.enable('minification');
}
if (meta.config.useCompression) {
const compression = require('compression');
app.use(compression());
}
if (relativePath) {
app.use((req, res, next) => {
if (!req.path.startsWith(relativePath)) {
return require('./controllers/helpers').redirect(res, req.path);
}
next();
});
}
2021-02-03 23:59:08 -07:00
app.get(`${relativePath}/ping`, pingController.ping);
app.get(`${relativePath}/sping`, pingController.ping);
2017-04-28 19:35:48 -04:00
setupFavicon(app);
2021-02-03 23:59:08 -07:00
app.use(`${relativePath}/apple-touch-icon`, middleware.routeTouchIcon);
2019-09-16 18:39:25 -04:00
configureBodyParser(app);
app.use(cookieParser(nconf.get('secret')));
2021-10-20 12:45:09 -04:00
app.use(useragent.express());
app.use(detector.middleware());
app.use(session({
store: db.sessionStore,
secret: nconf.get('secret'),
2016-08-30 13:25:20 +03:00
key: nconf.get('sessionKey'),
2016-08-27 16:16:49 +03:00
cookie: setupCookie(),
2018-12-14 21:18:42 -05:00
resave: nconf.get('sessionResave') || false,
saveUninitialized: nconf.get('sessionSaveUninitialized') || false,
}));
2020-08-04 09:51:30 -04:00
setupHelmet(app);
2019-07-03 14:10:29 -04:00
app.use(middleware.addHeaders);
app.use(middleware.processRender);
auth.initialize(app, middleware);
const als = require('./als');
const apiHelpers = require('./api/helpers');
app.use((req, res, next) => {
als.run({
uid: req.uid,
req: apiHelpers.buildReqObject(req),
}, next);
});
2021-11-18 16:42:18 -05:00
app.use(middleware.autoLocale); // must be added after auth middlewares are added
2021-02-04 00:06:15 -07:00
const toobusy = require('toobusy-js');
toobusy.maxLag(meta.config.eventLoopLagThreshold);
toobusy.interval(meta.config.eventLoopInterval);
}
2020-08-04 09:51:30 -04:00
function setupHelmet(app) {
2022-07-19 09:54:44 -04:00
const options = {
contentSecurityPolicy: false, // defaults are too restrive and break plugins that load external assets... 🔜
crossOriginOpenerPolicy: { policy: meta.config['cross-origin-opener-policy'] },
crossOriginResourcePolicy: { policy: meta.config['cross-origin-resource-policy'] },
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
crossOriginEmbedderPolicy: !!meta.config['cross-origin-embedder-policy'],
2022-07-19 09:54:44 -04:00
};
2020-08-04 09:51:30 -04:00
if (meta.config['hsts-enabled']) {
2022-07-19 09:54:44 -04:00
options.hsts = {
maxAge: Math.max(0, meta.config['hsts-maxage']),
2020-08-04 09:51:30 -04:00
includeSubDomains: !!meta.config['hsts-subdomains'],
preload: !!meta.config['hsts-preload'],
2022-07-19 09:54:44 -04:00
};
2020-08-04 09:51:30 -04:00
}
2022-07-19 09:54:44 -04:00
try {
app.use(helmet(options));
} catch (err) {
winston.error(`[startup] unable to initialize helmet \n${err.stack}`);
}
2020-08-04 09:51:30 -04:00
}
function setupFavicon(app) {
2021-02-04 00:06:15 -07:00
let faviconPath = meta.config['brand:favicon'] || 'favicon.ico';
2017-02-23 14:18:07 -07:00
faviconPath = path.join(nconf.get('base_dir'), 'public', faviconPath.replace(/assets\/uploads/, 'uploads'));
if (file.existsSync(faviconPath)) {
app.use(nconf.get('relative_path'), favicon(faviconPath));
}
}
2019-09-16 18:39:25 -04:00
function configureBodyParser(app) {
const urlencodedOpts = nconf.get('bodyParser:urlencoded') || {};
if (!urlencodedOpts.hasOwnProperty('extended')) {
urlencodedOpts.extended = true;
}
app.use(bodyParser.urlencoded(urlencodedOpts));
const jsonOpts = nconf.get('bodyParser:json') || {};
app.use(bodyParser.json(jsonOpts));
}
2016-08-27 16:16:49 +03:00
function setupCookie() {
const cookie = meta.configs.cookie.get();
const ttl = meta.getSessionTTLSeconds() * 1000;
cookie.maxAge = ttl;
2016-08-27 16:16:49 +03:00
return cookie;
}
2021-03-05 18:18:16 -05:00
async function listen() {
2021-02-04 00:06:15 -07:00
let port = nconf.get('port');
const isSocket = isNaN(port) && !Array.isArray(port);
const socketPath = isSocket ? nconf.get('port') : '';
2015-07-21 11:23:16 -04:00
if (Array.isArray(port)) {
if (!port.length) {
winston.error('[startup] empty ports array in config.json');
process.exit();
}
winston.warn('[startup] If you want to start nodebb on multiple ports please use loader.js');
2021-02-03 23:59:08 -07:00
winston.warn(`[startup] Defaulting to first port in array, ${port[0]}`);
2015-07-21 11:23:16 -04:00
port = port[0];
if (!port) {
winston.error('[startup] Invalid port, exiting');
process.exit();
}
}
2017-10-25 10:48:12 -04:00
port = parseInt(port, 10);
if ((port !== 80 && port !== 443) || nconf.get('trust_proxy') === true) {
winston.info('🤝 Enabling \'trust proxy\'');
2015-07-21 11:23:16 -04:00
app.enable('trust proxy');
}
if ((port === 80 || port === 443) && process.env.NODE_ENV !== 'development') {
winston.info('Using ports 80 and 443 is not recommend; use a proxy instead. See README.md');
}
2021-02-04 00:06:15 -07:00
const bind_address = ((nconf.get('bind_address') === '0.0.0.0' || !nconf.get('bind_address')) ? '0.0.0.0' : nconf.get('bind_address'));
const args = isSocket ? [socketPath] : [port, bind_address];
let oldUmask;
2015-07-21 11:23:16 -04:00
if (isSocket) {
oldUmask = process.umask('0000');
2021-03-05 18:18:16 -05:00
try {
await exports.testSocket(socketPath);
} catch (err) {
winston.error(`[startup] NodeBB was unable to secure domain socket access (${socketPath})\n${err.stack}`);
throw err;
}
}
return new Promise((resolve, reject) => {
server.listen(...args.concat([function (err) {
const onText = `${isSocket ? socketPath : `${bind_address}:${port}`}`;
if (err) {
winston.error(`[startup] NodeBB was unable to listen on: ${chalk.yellow(onText)}`);
2021-03-05 18:18:16 -05:00
reject(err);
2015-07-21 11:23:16 -04:00
}
winston.info(`📡 NodeBB is now listening on: ${chalk.yellow(onText)}`);
winston.info(`🔗 Canonical URL: ${chalk.yellow(nconf.get('url'))}`);
2021-03-05 18:18:16 -05:00
if (oldUmask) {
process.umask(oldUmask);
}
resolve();
}]));
});
2015-07-21 11:23:16 -04:00
}
2021-03-05 18:18:16 -05:00
exports.testSocket = async function (socketPath) {
2015-07-21 11:23:16 -04:00
if (typeof socketPath !== 'string') {
2021-03-05 18:18:16 -05:00
throw new Error(`invalid socket path : ${socketPath}`);
2015-07-21 11:23:16 -04:00
}
2021-02-04 00:06:15 -07:00
const net = require('net');
const file = require('./file');
2021-03-05 18:18:16 -05:00
const exists = await file.exists(socketPath);
if (!exists) {
return;
}
return new Promise((resolve, reject) => {
const testSocket = new net.Socket();
testSocket.on('error', (err) => {
if (err.code !== 'ECONNREFUSED') {
return reject(err);
}
// The socket was stale, kick it out of the way
fs.unlink(socketPath, (err) => {
if (err) reject(err); else resolve();
2015-07-21 11:23:16 -04:00
});
2021-03-05 18:18:16 -05:00
});
testSocket.connect({ path: socketPath }, () => {
// Something's listening here, abort
reject(new Error('port-in-use'));
});
});
2015-07-21 11:23:16 -04:00
};
2019-07-22 18:16:18 -04:00
require('./promisify')(exports);