This commit is contained in:
Ken
2026-01-19 09:33:35 +07:00
parent 374dc12b2d
commit 70838a4bc1
103 changed files with 16929 additions and 2 deletions

144
middleware/errorHandler.js Normal file
View 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,
};