/** * Global Error Handler Middleware * Catches all errors and returns consistent JSON responses */ export class AppError extends Error { constructor(message, statusCode = 500, code = 'INTERNAL_ERROR') { super(message) this.statusCode = statusCode this.code = code this.isOperational = true Error.captureStackTrace(this, this.constructor) } } export class ValidationError extends AppError { constructor(message, fields = {}) { super(message, 400, 'VALIDATION_ERROR') this.fields = fields } } export class AuthenticationError extends AppError { constructor(message = 'Nicht authentifiziert') { super(message, 401, 'AUTHENTICATION_ERROR') } } export class AuthorizationError extends AppError { constructor(message = 'Keine Berechtigung') { super(message, 403, 'AUTHORIZATION_ERROR') } } export class NotFoundError extends AppError { constructor(resource = 'Ressource') { super(`${resource} nicht gefunden`, 404, 'NOT_FOUND') } } export class RateLimitError extends AppError { constructor(message = 'Zu viele Anfragen') { super(message, 429, 'RATE_LIMIT_EXCEEDED') } } /** * Error handler middleware */ export function errorHandler(err, req, res, next) { // Log error console.error(`[ERROR] ${new Date().toISOString()}`, { method: req.method, path: req.path, error: err.message, stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, }) // Default error values let statusCode = err.statusCode || 500 let code = err.code || 'INTERNAL_ERROR' let message = err.message || 'Ein Fehler ist aufgetreten' // Handle specific error types if (err.name === 'ValidationError') { statusCode = 400 code = 'VALIDATION_ERROR' } if (err.name === 'JsonWebTokenError') { statusCode = 401 code = 'INVALID_TOKEN' message = 'Ungültiger Token' } if (err.name === 'TokenExpiredError') { statusCode = 401 code = 'TOKEN_EXPIRED' message = 'Token abgelaufen' } // Don't expose internal errors in production if (!err.isOperational && process.env.NODE_ENV === 'production') { message = 'Ein interner Fehler ist aufgetreten' } // Send response res.status(statusCode).json({ success: false, error: { code, message, ...(err.fields && { fields: err.fields }), ...(process.env.NODE_ENV === 'development' && { stack: err.stack }), }, }) } /** * Async handler wrapper to catch errors in async routes */ export function asyncHandler(fn) { return (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next) } }