update
This commit is contained in:
144
middleware/errorHandler.js
Normal file
144
middleware/errorHandler.js
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Error Handler Middleware
|
||||
* Xử lý tất cả errors trong ứng dụng
|
||||
*/
|
||||
|
||||
class AppError extends Error {
|
||||
constructor(message, statusCode = 500, isOperational = true) {
|
||||
super(message);
|
||||
this.statusCode = statusCode;
|
||||
this.isOperational = isOperational;
|
||||
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
|
||||
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Sequelize Validation Errors
|
||||
*/
|
||||
const handleSequelizeValidationError = (err) => {
|
||||
const errors = err.errors.map(e => ({
|
||||
field: e.path,
|
||||
message: e.message,
|
||||
}));
|
||||
|
||||
return new AppError(
|
||||
'Validation Error',
|
||||
400,
|
||||
true,
|
||||
errors
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle Sequelize Unique Constraint Error
|
||||
*/
|
||||
const handleSequelizeUniqueError = (err) => {
|
||||
const field = err.errors[0]?.path || 'field';
|
||||
const message = `${field} already exists`;
|
||||
|
||||
return new AppError(message, 409);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle Sequelize Foreign Key Error
|
||||
*/
|
||||
const handleSequelizeForeignKeyError = (err) => {
|
||||
return new AppError('Referenced record not found', 404);
|
||||
};
|
||||
|
||||
/**
|
||||
* Development Error Response
|
||||
*/
|
||||
const sendErrorDev = (err, res) => {
|
||||
res.status(err.statusCode).json({
|
||||
success: false,
|
||||
status: err.status,
|
||||
error: err,
|
||||
message: err.message,
|
||||
stack: err.stack,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Production Error Response
|
||||
*/
|
||||
const sendErrorProd = (err, res) => {
|
||||
// Operational, trusted error: send message to client
|
||||
if (err.isOperational) {
|
||||
res.status(err.statusCode).json({
|
||||
success: false,
|
||||
status: err.status,
|
||||
message: err.message,
|
||||
errors: err.errors,
|
||||
});
|
||||
}
|
||||
// Programming or unknown error: don't leak error details
|
||||
else {
|
||||
console.error('❌ ERROR:', err);
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
status: 'error',
|
||||
message: 'Something went wrong!',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Global Error Handler Middleware
|
||||
*/
|
||||
const errorHandler = (err, req, res, next) => {
|
||||
err.statusCode = err.statusCode || 500;
|
||||
err.status = err.status || 'error';
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
sendErrorDev(err, res);
|
||||
} else {
|
||||
let error = { ...err };
|
||||
error.message = err.message;
|
||||
|
||||
// Handle specific Sequelize errors
|
||||
if (err.name === 'SequelizeValidationError') {
|
||||
error = handleSequelizeValidationError(err);
|
||||
}
|
||||
|
||||
if (err.name === 'SequelizeUniqueConstraintError') {
|
||||
error = handleSequelizeUniqueError(err);
|
||||
}
|
||||
|
||||
if (err.name === 'SequelizeForeignKeyConstraintError') {
|
||||
error = handleSequelizeForeignKeyError(err);
|
||||
}
|
||||
|
||||
sendErrorProd(error, res);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 404 Handler
|
||||
*/
|
||||
const notFoundHandler = (req, res, next) => {
|
||||
const err = new AppError(
|
||||
`Cannot find ${req.originalUrl} on this server`,
|
||||
404
|
||||
);
|
||||
next(err);
|
||||
};
|
||||
|
||||
/**
|
||||
* Async Error Wrapper
|
||||
*/
|
||||
const catchAsync = (fn) => {
|
||||
return (req, res, next) => {
|
||||
fn(req, res, next).catch(next);
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
AppError,
|
||||
errorHandler,
|
||||
notFoundHandler,
|
||||
catchAsync,
|
||||
};
|
||||
Reference in New Issue
Block a user