2019-03-27 02:03:26 +11:00
|
|
|
'use strict';
|
|
|
|
|
|
2019-06-11 15:09:11 +00:00
|
|
|
process
|
|
|
|
|
.on('uncaughtException', console.error)
|
|
|
|
|
.on('unhandledRejection', console.error);
|
2019-03-27 02:03:26 +11:00
|
|
|
|
2022-04-12 04:32:59 +10:00
|
|
|
const config = require(__dirname+'/lib/misc/config.js')
|
2021-02-03 07:14:15 +00:00
|
|
|
, express = require('express')
|
2019-06-01 11:20:59 +00:00
|
|
|
, path = require('path')
|
|
|
|
|
, app = express()
|
2019-09-21 11:29:35 +00:00
|
|
|
, server = require('http').createServer(app)
|
2019-03-27 02:03:26 +11:00
|
|
|
, cookieParser = require('cookie-parser')
|
2023-05-19 23:45:11 +10:00
|
|
|
, { port, cookieSecret, debugLogs, google, hcaptcha, yandex } = require(__dirname+'/configs/secrets.js')
|
2019-09-12 12:22:26 +00:00
|
|
|
, Mongo = require(__dirname+'/db/db.js')
|
2022-04-12 04:32:59 +10:00
|
|
|
, dynamicResponse = require(__dirname+'/lib/misc/dynamic.js')
|
|
|
|
|
, commit = require(__dirname+'/lib/misc/commit.js')
|
2021-02-12 08:53:34 +00:00
|
|
|
, { version } = require(__dirname+'/package.json')
|
2022-04-12 04:32:59 +10:00
|
|
|
, formatSize = require(__dirname+'/lib/converter/formatsize.js')
|
2022-02-28 02:25:16 +11:00
|
|
|
, CachePugTemplates = require('cache-pug-templates')
|
2023-01-14 22:47:30 +11:00
|
|
|
, { Permissions } = require(__dirname+'/lib/permission/permissions.js')
|
|
|
|
|
, i18n = require(__dirname+'/lib/locale/locale.js');
|
2019-03-27 02:03:26 +11:00
|
|
|
|
|
|
|
|
(async () => {
|
|
|
|
|
|
2019-10-11 02:58:05 +00:00
|
|
|
const env = process.env.NODE_ENV;
|
|
|
|
|
const production = env === 'production';
|
2021-02-03 07:14:15 +00:00
|
|
|
debugLogs && console.log('process.env.NODE_ENV =', env);
|
2023-02-10 21:07:01 +11:00
|
|
|
process.env.NO_CAPTCHA && console.warn('WARNING, RUNNING WITH process.env.NO_CAPTCHA, CAPTCHA CHECKS ARE SKIPPED!');
|
2019-06-15 10:38:54 +00:00
|
|
|
|
2019-08-31 06:20:20 +00:00
|
|
|
// connect to mongodb
|
2020-01-15 04:04:56 +01:00
|
|
|
debugLogs && console.log('CONNECTING TO MONGODB');
|
2019-03-27 02:03:26 +11:00
|
|
|
await Mongo.connect();
|
2020-06-07 11:50:51 +10:00
|
|
|
await Mongo.checkVersion();
|
2021-02-06 08:49:21 +00:00
|
|
|
await config.load();
|
2019-08-30 09:47:11 +00:00
|
|
|
|
2019-08-31 06:20:20 +00:00
|
|
|
// connect to redis
|
2020-01-15 04:04:56 +01:00
|
|
|
debugLogs && console.log('CONNECTING TO REDIS');
|
2022-04-12 04:32:59 +10:00
|
|
|
const redis = require(__dirname+'/lib/redis/redis.js');
|
2019-03-27 02:03:26 +11:00
|
|
|
|
2022-03-04 00:17:33 +11:00
|
|
|
// load roles early
|
2022-04-12 04:32:59 +10:00
|
|
|
const roleManager = require(__dirname+'/lib/permission/rolemanager.js');
|
2022-03-05 01:39:16 +11:00
|
|
|
await roleManager.load();
|
2022-03-04 00:17:33 +11:00
|
|
|
|
2019-06-15 10:38:54 +00:00
|
|
|
// disable useless express header
|
|
|
|
|
app.disable('x-powered-by');
|
2021-03-20 07:51:52 +00:00
|
|
|
//query strings
|
|
|
|
|
app.set('query parser', 'simple');
|
2019-09-16 23:09:34 +00:00
|
|
|
// parse forms
|
2020-01-09 12:01:51 +01:00
|
|
|
app.use(express.urlencoded({extended: false}));
|
2019-08-31 06:20:20 +00:00
|
|
|
// parse cookies
|
2020-08-15 21:45:21 +10:00
|
|
|
app.use(cookieParser(cookieSecret));
|
2019-06-01 11:20:59 +00:00
|
|
|
|
2019-03-27 02:03:26 +11:00
|
|
|
// session store
|
2022-04-12 04:32:59 +10:00
|
|
|
const sessionMiddleware = require(__dirname+'/lib/middleware/permission/usesession.js');
|
2020-08-01 16:19:31 +10:00
|
|
|
|
|
|
|
|
// connect socketio
|
2022-04-12 04:32:59 +10:00
|
|
|
const Socketio = require(__dirname+'/lib/misc/socketio.js');
|
2020-08-01 16:19:31 +10:00
|
|
|
debugLogs && console.log('STARTING WEBSOCKET');
|
|
|
|
|
Socketio.connect(server, sessionMiddleware);
|
2019-03-27 02:03:26 +11:00
|
|
|
|
2019-06-01 11:20:59 +00:00
|
|
|
//trust proxy for nginx
|
|
|
|
|
app.set('trust proxy', 1);
|
2019-03-27 02:03:26 +11:00
|
|
|
|
|
|
|
|
// use pug view engine
|
2019-09-12 12:22:26 +00:00
|
|
|
const views = path.join(__dirname, 'views/pages');
|
2019-03-27 02:03:26 +11:00
|
|
|
app.set('view engine', 'pug');
|
2019-09-12 12:22:26 +00:00
|
|
|
app.set('views', views);
|
2019-03-27 02:03:26 +11:00
|
|
|
|
2021-02-03 07:14:15 +00:00
|
|
|
const loadAppLocals = () => {
|
2023-01-15 20:04:55 +11:00
|
|
|
const { language, cacheTemplates, boardDefaults, globalLimits, captchaOptions, archiveLinksURL,
|
2023-06-24 23:12:20 +10:00
|
|
|
reverseImageLinksURL, meta, enableWebring, globalAnnouncement, enableWeb3, ethereumLinksURL } = config.get;
|
2021-02-03 07:14:15 +00:00
|
|
|
//cache loaded templates
|
2021-02-03 14:51:32 +00:00
|
|
|
app.cache = {};
|
2021-02-03 07:14:15 +00:00
|
|
|
app[cacheTemplates === true ? 'enable' : 'disable']('view cache');
|
|
|
|
|
//default settings
|
2022-02-27 20:05:43 +11:00
|
|
|
app.locals.Permissions = Permissions;
|
2021-02-03 07:14:15 +00:00
|
|
|
app.locals.defaultTheme = boardDefaults.theme;
|
|
|
|
|
app.locals.defaultCodeTheme = boardDefaults.codeTheme;
|
|
|
|
|
app.locals.globalLimits = globalLimits;
|
2023-06-24 23:12:20 +10:00
|
|
|
app.locals.ethereumLinksURL = ethereumLinksURL;
|
2021-09-21 12:46:08 +00:00
|
|
|
app.locals.archiveLinksURL = archiveLinksURL;
|
|
|
|
|
app.locals.reverseImageLinksURL = reverseImageLinksURL;
|
2021-02-03 07:14:15 +00:00
|
|
|
app.locals.enableWebring = enableWebring;
|
2023-06-18 13:35:25 +10:00
|
|
|
app.locals.enableWeb3 = enableWeb3;
|
2021-02-03 07:14:15 +00:00
|
|
|
app.locals.commit = commit;
|
2021-02-12 08:56:39 +00:00
|
|
|
app.locals.version = version;
|
2021-02-03 07:14:15 +00:00
|
|
|
app.locals.meta = meta;
|
|
|
|
|
app.locals.postFilesSize = formatSize(globalLimits.postFilesSize.max);
|
2023-05-21 16:31:58 +10:00
|
|
|
app.locals.googleRecaptchaSiteKey = google ? google.siteKey : '';
|
|
|
|
|
app.locals.hcaptchaSiteKey = hcaptcha ? hcaptcha.siteKey : '';
|
|
|
|
|
app.locals.yandexSiteKey = yandex ? yandex.siteKey : '';
|
2021-02-12 08:53:34 +00:00
|
|
|
app.locals.globalAnnouncement = globalAnnouncement;
|
2022-06-08 02:08:22 +10:00
|
|
|
app.locals.captchaOptions = captchaOptions;
|
2023-01-16 22:10:29 +11:00
|
|
|
app.locals.globalLanguage = language;
|
|
|
|
|
i18n.init(app.locals);
|
2023-01-16 23:09:58 +11:00
|
|
|
app.locals.setLocale(app.locals, language);
|
2022-05-02 17:32:55 +10:00
|
|
|
};
|
2021-02-03 14:51:32 +00:00
|
|
|
loadAppLocals();
|
2021-02-04 08:33:05 +00:00
|
|
|
redis.addCallback('config', loadAppLocals);
|
2019-09-04 09:31:46 +00:00
|
|
|
|
2019-03-27 02:03:26 +11:00
|
|
|
// routes
|
2019-10-12 12:53:53 +11:00
|
|
|
if (!production) {
|
2020-08-12 23:14:22 -07:00
|
|
|
app.use(express.static(__dirname+'/static', { redirect: false }));
|
|
|
|
|
app.use(express.static(__dirname+'/static/html', { redirect: false }));
|
|
|
|
|
app.use(express.static(__dirname+'/static/json', { redirect: false }));
|
2019-10-12 12:53:53 +11:00
|
|
|
}
|
2020-08-15 21:45:21 +10:00
|
|
|
|
2023-01-14 22:47:30 +11:00
|
|
|
//localisation
|
2023-01-15 17:15:25 +11:00
|
|
|
const { setGlobalLanguage } = require(__dirname+'/lib/middleware/locale/locale.js');
|
2023-01-14 22:47:30 +11:00
|
|
|
app.use(i18n.init);
|
2023-01-15 17:15:25 +11:00
|
|
|
app.use(setGlobalLanguage);
|
2023-01-14 22:47:30 +11:00
|
|
|
|
2023-04-25 22:33:55 +10:00
|
|
|
//referer check middleware
|
|
|
|
|
const referrerCheck = require(__dirname+'/lib/middleware/misc/referrercheck.js');
|
|
|
|
|
app.use(referrerCheck);
|
|
|
|
|
|
2019-10-12 17:27:28 +11:00
|
|
|
app.use('/forms', require(__dirname+'/controllers/forms.js'));
|
|
|
|
|
app.use('/', require(__dirname+'/controllers/pages.js'));
|
2019-03-27 02:03:26 +11:00
|
|
|
|
2019-04-18 11:47:24 +00:00
|
|
|
//404 catchall
|
2019-03-27 02:03:26 +11:00
|
|
|
app.get('*', (req, res) => {
|
2019-06-11 15:09:11 +00:00
|
|
|
res.status(404).render('404');
|
2022-05-02 17:32:55 +10:00
|
|
|
});
|
2019-03-27 02:03:26 +11:00
|
|
|
|
2019-03-27 14:18:55 +11:00
|
|
|
// catch any unhandled errors
|
2022-05-28 02:43:37 +10:00
|
|
|
app.use((err, req, res, next) => { // eslint-disable-line no-unused-vars
|
2023-02-04 17:15:51 +11:00
|
|
|
const { __ } = res.locals;
|
2020-11-08 06:21:15 +00:00
|
|
|
let errStatus = 500;
|
|
|
|
|
let errMessage = 'Internal Server Error';
|
2019-03-27 02:03:26 +11:00
|
|
|
if (err.code === 'EBADCSRFTOKEN') {
|
2020-11-08 06:21:15 +00:00
|
|
|
errMessage = 'Invalid CSRF token';
|
|
|
|
|
errStatus= 403;
|
2019-03-27 02:03:26 +11:00
|
|
|
}
|
2020-11-08 06:21:15 +00:00
|
|
|
if (err.type != null) {
|
|
|
|
|
//body-parser errors
|
|
|
|
|
errStatus = err.status;
|
|
|
|
|
switch (err.type) {
|
|
|
|
|
case 'charset.unsupported':
|
|
|
|
|
case 'entity.parse.failed':
|
|
|
|
|
case 'entity.verify.failed':
|
|
|
|
|
case 'encoding.unsupported':
|
|
|
|
|
case 'request.size.invalid':
|
|
|
|
|
case 'parameters.too.many':
|
|
|
|
|
//no need to give an error for every one, since these will never happen to a legit user anyway
|
|
|
|
|
errMessage = 'Invalid request body';
|
|
|
|
|
break;
|
|
|
|
|
case 'request.aborted':
|
|
|
|
|
errMessage = 'Client aborted request';
|
|
|
|
|
break;
|
|
|
|
|
case 'entity.too.large':
|
2023-02-04 17:15:51 +11:00
|
|
|
errMessage = 'Your upload was too large';
|
2020-11-08 06:21:15 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (errStatus === 500 && errMessage === 'Internal Server Error') {
|
|
|
|
|
//no specific/friendly error, probably something worth logging
|
|
|
|
|
console.error(err);
|
|
|
|
|
}
|
|
|
|
|
return dynamicResponse(req, res, errStatus, 'message', {
|
2023-02-04 17:15:51 +11:00
|
|
|
'title': __(errStatus === 500 ? 'Internal Server Error' : 'Bad Request'),
|
|
|
|
|
'error': __(errMessage),
|
2019-04-29 15:35:47 +00:00
|
|
|
'redirect': req.headers.referer || '/'
|
2019-06-11 15:09:11 +00:00
|
|
|
});
|
2022-05-02 17:32:55 +10:00
|
|
|
});
|
2019-03-27 02:03:26 +11:00
|
|
|
|
2019-08-29 14:36:46 +00:00
|
|
|
//listen
|
2022-04-06 01:52:26 +10:00
|
|
|
server.listen(port, (process.env.JSCHAN_IP || '127.0.0.1'), () => {
|
2019-09-12 12:22:26 +00:00
|
|
|
new CachePugTemplates({ app, views }).start();
|
2020-01-15 04:04:56 +01:00
|
|
|
debugLogs && console.log(`LISTENING ON :${port}`);
|
2019-08-29 14:36:46 +00:00
|
|
|
//let PM2 know that this is ready for graceful reloads and to serialise startup
|
|
|
|
|
if (typeof process.send === 'function') {
|
|
|
|
|
//make sure we are a child process of PM2 i.e. not in dev
|
2020-01-15 04:04:56 +01:00
|
|
|
debugLogs && console.log('SENT READY SIGNAL TO PM2');
|
2019-05-21 10:53:32 +00:00
|
|
|
process.send('ready');
|
|
|
|
|
}
|
2019-08-28 16:11:09 +00:00
|
|
|
});
|
2019-05-16 11:01:17 +00:00
|
|
|
|
2020-04-04 13:46:48 +02:00
|
|
|
const gracefulStop = () => {
|
2020-01-15 04:04:56 +01:00
|
|
|
debugLogs && console.log('SIGINT SIGNAL RECEIVED');
|
2019-05-16 11:01:17 +00:00
|
|
|
// Stops the server from accepting new connections and finishes existing connections.
|
2019-09-27 13:02:07 +00:00
|
|
|
Socketio.io.close((err) => {
|
2019-05-16 11:01:17 +00:00
|
|
|
// if error, log and exit with error (1 code)
|
2020-01-15 04:04:56 +01:00
|
|
|
debugLogs && console.log('CLOSING SERVER');
|
2019-05-16 11:01:17 +00:00
|
|
|
if (err) {
|
|
|
|
|
console.error(err);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
// close database connection
|
2020-01-15 04:04:56 +01:00
|
|
|
debugLogs && console.log('DISCONNECTING MONGODB');
|
2019-05-16 11:01:17 +00:00
|
|
|
Mongo.client.close();
|
2019-08-30 09:47:11 +00:00
|
|
|
//close redis connection
|
2022-05-02 17:32:55 +10:00
|
|
|
debugLogs && console.log('DISCONNECTING REDIS');
|
2021-02-04 08:33:05 +00:00
|
|
|
redis.close();
|
2019-05-16 11:01:17 +00:00
|
|
|
// now close without error
|
|
|
|
|
process.exit(0);
|
2019-08-29 14:36:46 +00:00
|
|
|
});
|
2020-04-04 13:46:48 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//graceful stop
|
|
|
|
|
process.on('SIGINT', gracefulStop);
|
|
|
|
|
process.on('message', (message) => {
|
|
|
|
|
if (message === 'shutdown') {
|
|
|
|
|
gracefulStop();
|
|
|
|
|
}
|
2019-08-29 14:36:46 +00:00
|
|
|
});
|
2019-05-16 11:01:17 +00:00
|
|
|
|
2019-03-27 02:03:26 +11:00
|
|
|
})();
|