Unified error handling system

- 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
This commit is contained in:
Krystie
2026-03-29 18:46:02 -07:00
parent 51d6c37e4a
commit 64a0018d00
6 changed files with 366 additions and 117 deletions

View File

@@ -1,48 +1,105 @@
/**
* Typed Error Classes for DashCaddy API
* Provides structured errors that the global error handler catches automatically.
* DashCaddy API Error Classes
* All errors inherit from AppError and provide consistent structure.
*/
class AppError extends Error {
constructor(message, statusCode = 500, code = 'INTERNAL_ERROR') {
constructor(message, statusCode = 500, code = null) {
super(message);
this.name = this.constructor.name;
this.statusCode = statusCode;
this.code = code;
this.code = code || this.constructor.name.toUpperCase().replace(/ERROR$/, '_ERROR');
this.isOperational = true; // Distinguishes from programming errors
}
}
class DockerError extends AppError {
constructor(message, details = {}) {
super(message, 500, 'DOCKER_ERROR');
this.details = details;
}
}
// 4xx Client Errors
class CaddyError extends AppError {
constructor(message, details = {}) {
super(message, 502, 'CADDY_ERROR');
this.details = details;
}
}
class DNSError extends AppError {
constructor(message, details = {}) {
super(message, 502, 'DNS_ERROR');
this.details = details;
class ValidationError extends AppError {
constructor(message, field = null) {
super(message, 400, 'DC-400');
this.field = field;
}
}
class AuthenticationError extends AppError {
constructor(message = 'Authentication required') {
super(message, 401, 'AUTH_REQUIRED');
constructor(message = 'Authentication required', requiresTotp = false) {
super(message, 401, 'DC-401');
this.requiresTotp = requiresTotp;
}
}
class ForbiddenError extends AppError {
constructor(message = 'Forbidden') {
super(message, 403, 'DC-403');
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404, 'NOT_FOUND');
super(`${resource} not found`, 404, 'DC-404');
this.resource = resource;
}
}
module.exports = { AppError, DockerError, CaddyError, DNSError, AuthenticationError, NotFoundError };
class ConflictError extends AppError {
constructor(message, conflictingResource = null) {
super(message, 409, 'DC-409');
this.conflictingResource = conflictingResource;
}
}
class RateLimitError extends AppError {
constructor(retryAfter = 60) {
super('Rate limit exceeded', 429, 'DC-429');
this.retryAfter = retryAfter;
}
}
// 5xx Server Errors
class DockerError extends AppError {
constructor(message, operation = null, details = {}) {
super(message, 500, 'DC-500-DOCKER');
this.operation = operation;
this.details = details;
}
}
class CaddyError extends AppError {
constructor(message, operation = null, details = {}) {
super(message, 502, 'DC-502-CADDY');
this.operation = operation;
this.details = details;
}
}
class DNSError extends AppError {
constructor(message, operation = null, details = {}) {
super(message, 502, 'DC-502-DNS');
this.operation = operation;
this.details = details;
}
}
class ServiceUnavailableError extends AppError {
constructor(service, retryAfter = null) {
super(`Service unavailable: ${service}`, 503, 'DC-503');
this.service = service;
this.retryAfter = retryAfter;
}
}
module.exports = {
AppError,
ValidationError,
AuthenticationError,
ForbiddenError,
NotFoundError,
ConflictError,
RateLimitError,
DockerError,
CaddyError,
DNSError,
ServiceUnavailableError
};