sanket3280/code-execution
0
1/**2 * Centralized Error Handling3 * Provides consistent error responses and logging4 */5 6const logger = require('./logger');7const { config } = require('../config/env.config');8 9/**10 * Custom Application Error11 */12class AppError extends Error {13 constructor(message, statusCode = 500, isOperational = true) {14 super(message);15 this.statusCode = statusCode;16 this.isOperational = isOperational;17 this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';18 19 Error.captureStackTrace(this, this.constructor);20 }21}22 23/**24 * Async handler wrapper to catch errors in async route handlers25 */26const asyncHandler = (fn) => {27 return (req, res, next) => {28 Promise.resolve(fn(req, res, next)).catch(next);29 };30};31 32/**33 * Handle validation errors34 */35const handleValidationError = (err) => {36 const errors = Object.values(err.errors).map(el => el.message);37 const message = `Invalid input data. ${errors.join('. ')}`;38 return new AppError(message, 400);39};40 41/**42 * Handle duplicate key errors43 */44const handleDuplicateKeyError = (err) => {45 const field = Object.keys(err.keyValue)[0];46 const value = err.keyValue[field];47 const message = `${field} '${value}' already exists. Please use another value.`;48 return new AppError(message, 400);49};50 51/**52 * Handle JWT errors53 */54const handleJWTError = () => {55 return new AppError('Invalid token. Please log in again.', 401);56};57 58/**59 * Handle JWT expired error60 */61const handleJWTExpiredError = () => {62 return new AppError('Your token has expired. Please log in again.', 401);63};64 65/**66 * Handle cast errors (invalid MongoDB ObjectId)67 */68const handleCastError = (err) => {69 const message = `Invalid ${err.path}: ${err.value}`;70 return new AppError(message, 400);71};72 73/**74 * Send error response in development75 */76const sendErrorDev = (err, res) => {77 res.status(err.statusCode).json({78 status: err.status,79 error: err,80 message: err.message,81 stack: err.stack,82 });83};84 85/**86 * Send error response in production87 */88const sendErrorProd = (err, res) => {89 // Operational, trusted error: send message to client90 if (err.isOperational) {91 res.status(err.statusCode).json({92 status: err.status,93 message: err.message,94 });95 } 96 // Programming or unknown error: don't leak error details97 else {98 logger.error('Unexpected error', err);99 res.status(500).json({100 status: 'error',101 message: 'Something went wrong',102 });103 }104};105 106/**107 * Global error handling middleware108 */109const errorHandler = (err, req, res, next) => {110 err.statusCode = err.statusCode || 500;111 err.status = err.status || 'error';112 113 // Log error114 logger.error(`Error in ${req.method} ${req.path}`, err, {115 statusCode: err.statusCode,116 userId: req.user?.id,117 });118 119 if (config.isDevelopment) {120 sendErrorDev(err, res);121 } else {122 let error = { ...err };123 error.message = err.message;124 125 // Handle specific error types126 if (err.name === 'ValidationError') error = handleValidationError(err);127 if (err.code === 11000) error = handleDuplicateKeyError(err);128 if (err.name === 'JsonWebTokenError') error = handleJWTError();129 if (err.name === 'TokenExpiredError') error = handleJWTExpiredError();130 if (err.name === 'CastError') error = handleCastError(err);131 132 sendErrorProd(error, res);133 }134};135 136/**137 * Handle 404 errors138 */139const notFound = (req, res, next) => {140 const error = new AppError(`Route ${req.originalUrl} not found`, 404);141 next(error);142};143 144module.exports = {145 AppError,146 asyncHandler,147 errorHandler,148 notFound,149};150 