2017-03-02 14:57:33 +03:00
|
|
|
'use strict';
|
|
|
|
|
|
2023-12-18 17:35:37 -05:00
|
|
|
const fs = require('fs');
|
2021-02-04 00:06:15 -07:00
|
|
|
const nconf = require('nconf');
|
|
|
|
|
const winston = require('winston');
|
|
|
|
|
const validator = require('validator');
|
2023-12-18 17:35:37 -05:00
|
|
|
const path = require('path');
|
2021-07-09 11:40:05 -04:00
|
|
|
const translator = require('../translator');
|
2021-02-04 00:06:15 -07:00
|
|
|
const plugins = require('../plugins');
|
|
|
|
|
const middleware = require('../middleware');
|
2021-07-09 10:30:46 -04:00
|
|
|
const middlewareHelpers = require('../middleware/helpers');
|
|
|
|
|
const helpers = require('./helpers');
|
2017-03-02 14:57:33 +03:00
|
|
|
|
2020-06-04 01:14:46 -04:00
|
|
|
exports.handleURIErrors = async function handleURIErrors(err, req, res, next) {
|
2017-03-02 14:57:33 +03:00
|
|
|
// Handle cases where malformed URIs are passed in
|
|
|
|
|
if (err instanceof URIError) {
|
2021-02-03 23:59:08 -07:00
|
|
|
const cleanPath = req.path.replace(new RegExp(`^${nconf.get('relative_path')}`), '');
|
2021-02-04 00:06:15 -07:00
|
|
|
const tidMatch = cleanPath.match(/^\/topic\/(\d+)\//);
|
|
|
|
|
const cidMatch = cleanPath.match(/^\/category\/(\d+)\//);
|
2017-03-02 14:57:33 +03:00
|
|
|
|
|
|
|
|
if (tidMatch) {
|
|
|
|
|
res.redirect(nconf.get('relative_path') + tidMatch[0]);
|
|
|
|
|
} else if (cidMatch) {
|
|
|
|
|
res.redirect(nconf.get('relative_path') + cidMatch[0]);
|
|
|
|
|
} else {
|
2021-02-03 23:59:08 -07:00
|
|
|
winston.warn(`[controller] Bad request: ${req.path}`);
|
|
|
|
|
if (req.path.startsWith(`${nconf.get('relative_path')}/api`)) {
|
2017-03-02 14:57:33 +03:00
|
|
|
res.status(400).json({
|
|
|
|
|
error: '[[global:400.title]]',
|
|
|
|
|
});
|
|
|
|
|
} else {
|
2020-06-04 01:14:46 -04:00
|
|
|
await middleware.buildHeaderAsync(req, res);
|
|
|
|
|
res.status(400).render('400', { error: validator.escape(String(err.message)) });
|
2017-03-02 14:57:33 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
next(err);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// this needs to have four arguments or express treats it as `(req, res, next)`
|
|
|
|
|
// don't remove `next`!
|
2021-10-21 10:28:27 -04:00
|
|
|
exports.handleErrors = async function handleErrors(err, req, res, next) { // eslint-disable-line no-unused-vars
|
2021-02-04 00:06:15 -07:00
|
|
|
const cases = {
|
2017-09-12 13:46:51 -04:00
|
|
|
EBADCSRFTOKEN: function () {
|
2022-01-14 14:34:08 -05:00
|
|
|
winston.error(`${req.method} ${req.originalUrl}\n${err.message}`);
|
2017-09-12 13:46:51 -04:00
|
|
|
res.sendStatus(403);
|
|
|
|
|
},
|
|
|
|
|
'blacklisted-ip': function () {
|
|
|
|
|
res.status(403).type('text/plain').send(err.message);
|
|
|
|
|
},
|
|
|
|
|
};
|
2023-10-11 13:37:35 -04:00
|
|
|
|
|
|
|
|
const notFoundHandler = () => {
|
|
|
|
|
const controllers = require('.');
|
|
|
|
|
controllers['404'].handle404(req, res);
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-18 17:35:37 -05:00
|
|
|
const notBuiltHandler = async () => {
|
|
|
|
|
let file = await fs.promises.readFile(path.join(__dirname, '../../public/500.html'), { encoding: 'utf-8' });
|
|
|
|
|
file = file.replace('{message}', 'Failed to lookup view! Did you run `./nodebb build`?');
|
|
|
|
|
return res.type('text/html').send(file);
|
|
|
|
|
};
|
|
|
|
|
|
2021-02-04 00:06:15 -07:00
|
|
|
const defaultHandler = async function () {
|
2021-10-25 20:47:10 -04:00
|
|
|
if (res.headersSent) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-09-12 13:46:51 -04:00
|
|
|
// Display NodeBB error page
|
2021-02-04 00:06:15 -07:00
|
|
|
const status = parseInt(err.status, 10);
|
2017-09-12 13:46:51 -04:00
|
|
|
if ((status === 302 || status === 308) && err.path) {
|
2018-11-30 21:35:57 -05:00
|
|
|
return res.locals.isAPI ? res.set('X-Redirect', err.path).status(200).json(err.path) : res.redirect(nconf.get('relative_path') + err.path);
|
2017-09-12 13:46:51 -04:00
|
|
|
}
|
2017-03-02 14:57:33 +03:00
|
|
|
|
2021-07-09 10:30:46 -04:00
|
|
|
const path = String(req.path || '');
|
2017-03-02 14:57:33 +03:00
|
|
|
|
2021-07-09 10:30:46 -04:00
|
|
|
if (path.startsWith(`${nconf.get('relative_path')}/api/v3`)) {
|
2021-07-09 11:40:05 -04:00
|
|
|
let status = 500;
|
|
|
|
|
if (err.message.startsWith('[[')) {
|
|
|
|
|
status = 400;
|
|
|
|
|
err.message = await translator.translate(err.message);
|
|
|
|
|
}
|
|
|
|
|
return helpers.formatApiResponse(status, res, err);
|
2021-07-09 10:30:46 -04:00
|
|
|
}
|
2017-03-02 14:57:33 +03:00
|
|
|
|
2022-01-14 14:34:08 -05:00
|
|
|
winston.error(`${req.method} ${req.originalUrl}\n${err.stack}`);
|
2021-07-09 10:30:46 -04:00
|
|
|
res.status(status || 500);
|
2021-06-18 09:55:08 -04:00
|
|
|
const data = {
|
|
|
|
|
path: validator.escape(path),
|
|
|
|
|
error: validator.escape(String(err.message)),
|
2021-07-09 10:30:46 -04:00
|
|
|
bodyClass: middlewareHelpers.buildBodyClass(req, res),
|
2021-06-18 09:55:08 -04:00
|
|
|
};
|
2017-09-12 13:46:51 -04:00
|
|
|
if (res.locals.isAPI) {
|
2021-06-18 09:55:08 -04:00
|
|
|
res.json(data);
|
2017-09-12 13:46:51 -04:00
|
|
|
} else {
|
2020-06-04 01:14:46 -04:00
|
|
|
await middleware.buildHeaderAsync(req, res);
|
2021-06-18 09:55:08 -04:00
|
|
|
res.render('500', data);
|
2017-09-12 13:46:51 -04:00
|
|
|
}
|
|
|
|
|
};
|
2021-10-21 10:28:27 -04:00
|
|
|
const data = await getErrorHandlers(cases);
|
|
|
|
|
try {
|
2017-09-12 13:46:51 -04:00
|
|
|
if (data.cases.hasOwnProperty(err.code)) {
|
2017-09-12 18:55:47 -04:00
|
|
|
data.cases[err.code](err, req, res, defaultHandler);
|
2024-08-07 14:18:44 -04:00
|
|
|
} else if (err.message && err.message.startsWith('[[error:no-') && err.message !== '[[error:no-privileges]]') {
|
2024-05-23 16:53:42 -04:00
|
|
|
notFoundHandler();
|
2024-08-07 14:18:44 -04:00
|
|
|
} else if (err.message && err.message.startsWith('Failed to lookup view')) {
|
2024-05-23 16:53:42 -04:00
|
|
|
notBuiltHandler();
|
2017-09-12 13:46:51 -04:00
|
|
|
} else {
|
2021-10-21 10:28:27 -04:00
|
|
|
await defaultHandler();
|
2017-09-12 13:46:51 -04:00
|
|
|
}
|
2021-10-21 10:28:27 -04:00
|
|
|
} catch (_err) {
|
2022-01-14 14:34:08 -05:00
|
|
|
winston.error(`${req.method} ${req.originalUrl}\n${_err.stack}`);
|
2021-10-25 16:26:50 -04:00
|
|
|
if (!res.headersSent) {
|
|
|
|
|
res.status(500).send(_err.message);
|
|
|
|
|
}
|
2021-10-21 10:28:27 -04:00
|
|
|
}
|
2017-03-02 14:57:33 +03:00
|
|
|
};
|
2021-10-21 10:28:27 -04:00
|
|
|
|
|
|
|
|
async function getErrorHandlers(cases) {
|
|
|
|
|
try {
|
|
|
|
|
return await plugins.hooks.fire('filter:error.handle', {
|
|
|
|
|
cases: cases,
|
|
|
|
|
});
|
|
|
|
|
} catch (err) {
|
|
|
|
|
// Assume defaults
|
|
|
|
|
winston.warn(`[errors/handle] Unable to retrieve plugin handlers for errors: ${err.message}`);
|
|
|
|
|
return { cases };
|
|
|
|
|
}
|
|
|
|
|
}
|