magic-building/src/server/middleware/errorHandler.ts

103 lines
3.0 KiB
TypeScript

import { Request, Response, NextFunction } from 'express';
import { GenerateImageResponse } from '../types/api';
/**
* Global error handler for the Express application
*/
export const errorHandler = (
error: Error,
req: Request,
res: Response,
next: NextFunction
) => {
const timestamp = new Date().toISOString();
const requestId = req.requestId || 'unknown';
// Log the error
console.error(`[${timestamp}] [${requestId}] ERROR:`, {
message: error.message,
stack: error.stack,
path: req.path,
method: req.method,
body: req.body,
query: req.query
});
// Don't send error response if headers already sent
if (res.headersSent) {
return next(error);
}
// Determine error type and status code
let statusCode = 500;
let errorMessage = 'Internal server error';
let errorType = 'INTERNAL_ERROR';
if (error.name === 'ValidationError') {
statusCode = 400;
errorMessage = error.message;
errorType = 'VALIDATION_ERROR';
} else if (error.message.includes('API key') || error.message.includes('authentication')) {
statusCode = 401;
errorMessage = 'Authentication failed';
errorType = 'AUTH_ERROR';
} else if (error.message.includes('not found') || error.message.includes('404')) {
statusCode = 404;
errorMessage = 'Resource not found';
errorType = 'NOT_FOUND';
} else if (error.message.includes('timeout') || error.message.includes('503')) {
statusCode = 503;
errorMessage = 'Service temporarily unavailable';
errorType = 'SERVICE_UNAVAILABLE';
} else if (error.message.includes('overloaded') || error.message.includes('rate limit')) {
statusCode = 429;
errorMessage = 'Service overloaded, please try again later';
errorType = 'RATE_LIMITED';
}
// Create error response
const errorResponse: GenerateImageResponse = {
success: false,
message: 'Request failed',
error: errorMessage
};
// Add additional debug info in development
if (process.env.NODE_ENV === 'development') {
(errorResponse as any).debug = {
originalError: error.message,
errorType,
requestId,
timestamp
};
}
console.log(`[${timestamp}] [${requestId}] Sending error response: ${statusCode} - ${errorMessage}`);
res.status(statusCode).json(errorResponse);
};
/**
* 404 handler for unmatched routes
*/
export const notFoundHandler = (req: Request, res: Response) => {
const timestamp = new Date().toISOString();
const requestId = req.requestId || 'unknown';
console.log(`[${timestamp}] [${requestId}] 404 - Route not found: ${req.method} ${req.path}`);
const notFoundResponse: GenerateImageResponse = {
success: false,
message: 'Route not found',
error: `The requested endpoint ${req.method} ${req.path} does not exist`
};
res.status(404).json(notFoundResponse);
};
/**
* Async error wrapper to catch errors in async route handlers
*/
export const asyncHandler = (fn: Function) => (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next)).catch(next);
};