You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

112 lines
3.4 KiB
JavaScript

'use strict';
const nconf = require('nconf');
const winston = require('winston');
const validator = require('validator');
const translator = require('../translator');
const plugins = require('../plugins');
const middleware = require('../middleware');
const middlewareHelpers = require('../middleware/helpers');
const helpers = require('./helpers');
exports.handleURIErrors = async function handleURIErrors(err, req, res, next) {
// Handle cases where malformed URIs are passed in
if (err instanceof URIError) {
const cleanPath = req.path.replace(new RegExp(`^${nconf.get('relative_path')}`), '');
const tidMatch = cleanPath.match(/^\/topic\/(\d+)\//);
const cidMatch = cleanPath.match(/^\/category\/(\d+)\//);
if (tidMatch) {
res.redirect(nconf.get('relative_path') + tidMatch[0]);
} else if (cidMatch) {
res.redirect(nconf.get('relative_path') + cidMatch[0]);
} else {
winston.warn(`[controller] Bad request: ${req.path}`);
if (req.path.startsWith(`${nconf.get('relative_path')}/api`)) {
res.status(400).json({
error: '[[global:400.title]]',
});
} else {
await middleware.buildHeaderAsync(req, res);
res.status(400).render('400', { error: validator.escape(String(err.message)) });
}
}
} else {
next(err);
}
};
// this needs to have four arguments or express treats it as `(req, res, next)`
// don't remove `next`!
exports.handleErrors = async function handleErrors(err, req, res, next) { // eslint-disable-line no-unused-vars
const cases = {
EBADCSRFTOKEN: function () {
winston.error(`${req.method} ${req.originalUrl}\n${err.message}`);
res.sendStatus(403);
},
'blacklisted-ip': function () {
res.status(403).type('text/plain').send(err.message);
},
};
const defaultHandler = async function () {
if (res.headersSent) {
return;
}
// Display NodeBB error page
const status = parseInt(err.status, 10);
if ((status === 302 || status === 308) && err.path) {
return res.locals.isAPI ? res.set('X-Redirect', err.path).status(200).json(err.path) : res.redirect(nconf.get('relative_path') + err.path);
}
const path = String(req.path || '');
if (path.startsWith(`${nconf.get('relative_path')}/api/v3`)) {
let status = 500;
if (err.message.startsWith('[[')) {
status = 400;
err.message = await translator.translate(err.message);
}
return helpers.formatApiResponse(status, res, err);
}
winston.error(`${req.method} ${req.originalUrl}\n${err.stack}`);
res.status(status || 500);
const data = {
path: validator.escape(path),
error: validator.escape(String(err.message)),
bodyClass: middlewareHelpers.buildBodyClass(req, res),
};
if (res.locals.isAPI) {
res.json(data);
} else {
await middleware.buildHeaderAsync(req, res);
res.render('500', data);
}
};
const data = await getErrorHandlers(cases);
try {
if (data.cases.hasOwnProperty(err.code)) {
data.cases[err.code](err, req, res, defaultHandler);
} else {
await defaultHandler();
}
} catch (_err) {
winston.error(`${req.method} ${req.originalUrl}\n${_err.stack}`);
if (!res.headersSent) {
res.status(500).send(_err.message);
}
}
};
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 };
}
}