Files
SkyArtShop/backend/middleware/errorHandler.js

125 lines
2.9 KiB
JavaScript
Raw Normal View History

2025-12-19 20:44:46 -06:00
const logger = require("../config/logger");
const {
isDevelopment,
PG_ERROR_CODES,
MULTER_ERROR_CODES,
STATIC_ASSET_EXTENSIONS,
} = require("../config/constants");
class AppError extends Error {
constructor(message, statusCode, isOperational = true) {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
this.timestamp = new Date().toISOString();
Error.captureStackTrace(this, this.constructor);
}
}
const ERROR_MAPPINGS = {
[PG_ERROR_CODES.UNIQUE_VIOLATION]: {
message: "Duplicate entry: Resource already exists",
statusCode: 409,
},
[PG_ERROR_CODES.FOREIGN_KEY_VIOLATION]: {
message: "Referenced resource does not exist",
statusCode: 400,
},
[PG_ERROR_CODES.INVALID_TEXT]: {
message: "Invalid data format",
statusCode: 400,
},
[MULTER_ERROR_CODES.FILE_SIZE]: {
message: "File too large. Maximum size is 5MB",
statusCode: 400,
},
[MULTER_ERROR_CODES.FILE_COUNT]: {
message: "Too many files. Maximum is 10 files per upload",
statusCode: 400,
},
};
// Global error handler middleware
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
error.statusCode = err.statusCode || 500;
// Log error
logger.error("Error occurred", {
message: error.message,
statusCode: error.statusCode,
path: req.path,
method: req.method,
ip: req.ip,
stack: err.stack,
});
// Map known error codes
const errorMapping = ERROR_MAPPINGS[err.code];
if (errorMapping) {
error.message = errorMapping.message;
error.statusCode = errorMapping.statusCode;
}
2026-01-04 17:52:37 -06:00
// SAFEGUARD: Don't send response if headers already sent
if (res.headersSent) {
logger.warn("Headers already sent in error handler", {
path: req.path,
error: error.message,
});
return next(err);
}
2025-12-19 20:44:46 -06:00
res.status(error.statusCode).json({
success: false,
message: error.message || "Server error",
...(isDevelopment() && {
error: err.message,
stack: err.stack,
}),
});
};
// 404 handler
const notFoundHandler = (req, res) => {
const isStaticAsset = STATIC_ASSET_EXTENSIONS.test(req.path);
if (!isStaticAsset) {
logger.warn("Route not found", {
path: req.path,
method: req.method,
ip: req.ip,
});
} else {
logger.debug("Static asset not found", {
path: req.path,
method: req.method,
});
}
2026-01-04 17:52:37 -06:00
// SAFEGUARD: Check if response already sent
if (res.headersSent) {
logger.warn("Headers already sent in 404 handler", { path: req.path });
return;
}
2025-12-19 20:44:46 -06:00
res.status(404).json({
success: false,
message: "Route not found",
path: req.path,
});
};
// Async handler wrapper to catch errors in async routes
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
module.exports = {
AppError,
errorHandler,
notFoundHandler,
asyncHandler,
};