107 lines
4.0 KiB
JavaScript
107 lines
4.0 KiB
JavaScript
'use strict';
|
|
|
|
const { MulterError } = require("multer"),
|
|
{ ValidationError } = require("joi"),
|
|
{ Errors, LIMIT_FILE_SIZE_ERR } = require('../helpers/constants'),
|
|
{ AppError, AppAuthError } = require("../helpers/app_error"),
|
|
env = require('../helpers/env'),
|
|
utils = require('../helpers/utils'),
|
|
debug = require('debug')('agm:errHandler');
|
|
|
|
function createAppErrObj(tag = Errors.UNKNOWN_APP_ERROR, err = null) {
|
|
const errObj = { error: { '.tag': tag } };
|
|
|
|
// Check if the tag (passed message) contains underscore (indicating it's a constant from Errors)
|
|
if (tag && tag.indexOf('_') === -1) {
|
|
// Tag doesn't contain underscore - treat as custom message
|
|
// Use defaultMessage for .tag value and original tag as message
|
|
errObj.error['.tag'] = err?.defaultMessage || Errors.UNKNOWN_APP_ERROR;
|
|
|
|
|
|
if (!env.PRODUCTION) {
|
|
errObj.error.message = tag;
|
|
}
|
|
} else {
|
|
// Tag contains underscore - treat as constant from Errors or fallback to defaultMessage or UNKNOWN_APP_ERROR
|
|
errObj.error['.tag'] = tag || err?.defaultMessage || Errors.UNKNOWN_APP_ERROR;
|
|
}
|
|
if (!env.PRODUCTION && err) {
|
|
// Include messageLong or the error info dev mode for debugging
|
|
const errMessage = typeof err === 'string' ? err : err?.messageLong || err?.toString();
|
|
errObj.error.message = errMessage;
|
|
}
|
|
|
|
return errObj;
|
|
}
|
|
|
|
function createAppValidationErrObj(valError) {
|
|
let errObj = createAppErrObj(Errors.INVALID_PARAM);
|
|
if (!utils.isEmptyObj(valError) && valError.message) {
|
|
errObj.error.message = valError.message;
|
|
}
|
|
return errObj;
|
|
}
|
|
|
|
/**
|
|
* Handle Application error for a given request with the provided error object
|
|
* @param {*} req The Request object of the request
|
|
* @param {*} res The Response object of the request
|
|
* @param {*} err The error object (runtime error object or App* error classes)
|
|
* Notes: return HTTP statusCode for the custom App errors with 409 as default.
|
|
* For Stripe errors the code will always be 402. The client should base on the .code value for detail error.
|
|
* @returns
|
|
*/
|
|
function ErrorHandler(err, req, res, next) {
|
|
const statusCode = err && err.statusCode || 409; // Application error default HTTP status code
|
|
|
|
try {
|
|
// Check if response has already been sent or is in invalid state
|
|
if (res.headersSent || res.writableEnded || res.destroyed) {
|
|
env.LOG_ALL_ERRORS && debug('Response already sent, cannot send error:', err);
|
|
return;
|
|
}
|
|
|
|
if (err.type && err.type.startsWith('Stripe')) {
|
|
/*
|
|
For Stripe errors the code will always be 402. The client should base on the .code value for detailed error.
|
|
TODO: In Production mode => consider filtering Stripe errors to send back less detail.
|
|
*/
|
|
return res.status(402).send(err);
|
|
}
|
|
|
|
// Handle Joi ValidationError
|
|
if (err instanceof ValidationError) {
|
|
return res.status(statusCode).send(createAppValidationErrObj(err));
|
|
}
|
|
|
|
// Handle Mongoose ValidationError as AppParamError
|
|
if (err.name === 'ValidationError') {
|
|
// const messages = Object.values(err.errors).map((e) => e.message).join(', ');
|
|
return res.status(409).send(createAppValidationErrObj(err));
|
|
}
|
|
|
|
if (err instanceof AppError) {
|
|
return res.status(err.statusCode).send(createAppErrObj(err.message, err));
|
|
}
|
|
|
|
// Handle Mongoose duplicate key error (e.g., unique index violation)
|
|
if (typeof err === "object" && err.code === 11000) {
|
|
return res.status(statusCode).send(createAppErrObj(Errors.DUPLICATED_VALUE));
|
|
}
|
|
|
|
// Handle Multer file upload errors
|
|
if (err instanceof MulterError) {
|
|
return res.status(statusCode).send(createAppErrObj(err.code == LIMIT_FILE_SIZE_ERR ? Errors.FILE_TOO_LARGE : Errors.INVALID_PARAM));
|
|
}
|
|
|
|
res.status(500).send(createAppErrObj(Errors.UNKNOWN_APP_ERROR, err));
|
|
// Handle unknown errors
|
|
} catch (error) {
|
|
res.status(500).send(createAppErrObj(Errors.UNKNOWN_ERROR, error));
|
|
} finally {
|
|
env.LOG_ALL_ERRORS && (debug(err instanceof AppAuthError ? err.message : err));
|
|
}
|
|
}
|
|
|
|
module.exports = { ErrorHandler }
|