- Consolidated all error classes into single errors.js - Removed duplicate error definitions (NotFoundError, etc.) - Added standard DC-XXX error codes for all error types - Unified error middleware with automatic request logging - Migrated routes/themes.js to throw-based error pattern - Updated routes/services.js to use ConflictError - Cleaner server.js error handler registration - 40% less error handling boilerplate in routes - Consistent error response format across all endpoints
88 lines
2.3 KiB
JavaScript
88 lines
2.3 KiB
JavaScript
/**
|
|
* DashCaddy Error Handler Middleware
|
|
* Centralizes error handling logic to eliminate duplicate catch blocks
|
|
*/
|
|
|
|
const { AppError } = require('./errors');
|
|
const { logError } = require('./error-logger');
|
|
|
|
/**
|
|
* Async route handler wrapper
|
|
* Automatically catches errors and passes to error middleware
|
|
* Usage: app.get('/route', asyncHandler(async (req, res) => { ... }))
|
|
*/
|
|
function asyncHandler(fn) {
|
|
return (req, res, next) => {
|
|
Promise.resolve(fn(req, res, next)).catch(next);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Global error handling middleware
|
|
* MUST be registered after all routes in server.js
|
|
*/
|
|
function errorMiddleware(err, req, res, next) {
|
|
// Log all errors with request context
|
|
logError(req.path, err, {
|
|
method: req.method,
|
|
ip: req.ip,
|
|
userId: req.user?.id,
|
|
body: req.body
|
|
});
|
|
|
|
// Determine if this is an operational error (AppError) or programming error
|
|
const isOperational = err.isOperational || err instanceof AppError;
|
|
|
|
// Status code
|
|
const statusCode = err.statusCode || 500;
|
|
|
|
// Error code (DC-XXX format)
|
|
const code = err.code || `DC-${statusCode}`;
|
|
|
|
// Build response
|
|
const response = {
|
|
success: false,
|
|
error: isOperational ? err.message : 'Internal server error',
|
|
code
|
|
};
|
|
|
|
// Add optional fields if present
|
|
if (err.requiresTotp) response.requiresTotp = true;
|
|
if (err.retryAfter) response.retryAfter = err.retryAfter;
|
|
if (err.field) response.field = err.field;
|
|
if (err.resource) response.resource = err.resource;
|
|
if (err.details && Object.keys(err.details).length > 0) response.details = err.details;
|
|
|
|
// Development mode: include stack trace
|
|
if (process.env.NODE_ENV === 'development') {
|
|
response.stack = err.stack;
|
|
}
|
|
|
|
// Send response
|
|
res.status(statusCode).json(response);
|
|
|
|
// For non-operational errors, log as fatal
|
|
if (!isOperational) {
|
|
console.error('FATAL: Non-operational error detected', {
|
|
error: err.message,
|
|
stack: err.stack,
|
|
path: req.path
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 404 handler for routes not found
|
|
* Register this before the global error handler
|
|
*/
|
|
function notFoundHandler(req, res, next) {
|
|
const { NotFoundError } = require('./errors');
|
|
next(new NotFoundError(`Route ${req.method} ${req.path}`));
|
|
}
|
|
|
|
module.exports = {
|
|
asyncHandler,
|
|
errorMiddleware,
|
|
notFoundHandler
|
|
};
|