121 lines
3.1 KiB
TypeScript
121 lines
3.1 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);
|
|
};
|