- Add comprehensive database migrations (001-024) for schema evolution - Enhance API schema with expanded type definitions and resolvers - Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth - Implement new services: AI optimization, billing, blockchain, compliance, marketplace - Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage) - Update Crossplane provider with enhanced VM management capabilities - Add comprehensive test suite for API endpoints and services - Update frontend components with improved GraphQL subscriptions and real-time updates - Enhance security configurations and headers (CSP, CORS, etc.) - Update documentation and configuration files - Add new CI/CD workflows and validation scripts - Implement design system improvements and UI enhancements
205 lines
5.2 KiB
TypeScript
205 lines
5.2 KiB
TypeScript
/**
|
|
* Standardized Error Handling
|
|
* Provides consistent error types and handling patterns across the codebase
|
|
*/
|
|
|
|
export enum ErrorCode {
|
|
// Authentication & Authorization
|
|
UNAUTHENTICATED = 'UNAUTHENTICATED',
|
|
FORBIDDEN = 'FORBIDDEN',
|
|
UNAUTHORIZED = 'UNAUTHORIZED',
|
|
|
|
// Validation
|
|
BAD_USER_INPUT = 'BAD_USER_INPUT',
|
|
VALIDATION_ERROR = 'VALIDATION_ERROR',
|
|
|
|
// Not Found
|
|
NOT_FOUND = 'NOT_FOUND',
|
|
RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',
|
|
TENANT_NOT_FOUND = 'TENANT_NOT_FOUND',
|
|
|
|
// Business Logic
|
|
QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',
|
|
RESOURCE_CONFLICT = 'RESOURCE_CONFLICT',
|
|
OPERATION_FAILED = 'OPERATION_FAILED',
|
|
|
|
// External Services
|
|
EXTERNAL_SERVICE_ERROR = 'EXTERNAL_SERVICE_ERROR',
|
|
BLOCKCHAIN_ERROR = 'BLOCKCHAIN_ERROR',
|
|
PROXMOX_ERROR = 'PROXMOX_ERROR',
|
|
|
|
// System
|
|
INTERNAL_ERROR = 'INTERNAL_ERROR',
|
|
DATABASE_ERROR = 'DATABASE_ERROR',
|
|
NETWORK_ERROR = 'NETWORK_ERROR',
|
|
}
|
|
|
|
export interface AppErrorOptions {
|
|
code: ErrorCode
|
|
message: string
|
|
details?: unknown
|
|
cause?: Error
|
|
statusCode?: number
|
|
}
|
|
|
|
/**
|
|
* Standardized application error
|
|
*/
|
|
export class AppError extends Error {
|
|
public readonly code: ErrorCode
|
|
public readonly details?: unknown
|
|
public readonly cause?: Error
|
|
public readonly statusCode: number
|
|
|
|
constructor(options: AppErrorOptions) {
|
|
super(options.message)
|
|
this.name = 'AppError'
|
|
this.code = options.code
|
|
this.details = options.details
|
|
this.cause = options.cause
|
|
this.statusCode = options.statusCode || this.getDefaultStatusCode(options.code)
|
|
|
|
// Maintain proper stack trace
|
|
if (Error.captureStackTrace) {
|
|
Error.captureStackTrace(this, AppError)
|
|
}
|
|
}
|
|
|
|
private getDefaultStatusCode(code: ErrorCode): number {
|
|
switch (code) {
|
|
case ErrorCode.UNAUTHENTICATED:
|
|
return 401
|
|
case ErrorCode.FORBIDDEN:
|
|
case ErrorCode.UNAUTHORIZED:
|
|
return 403
|
|
case ErrorCode.BAD_USER_INPUT:
|
|
case ErrorCode.VALIDATION_ERROR:
|
|
return 400
|
|
case ErrorCode.NOT_FOUND:
|
|
case ErrorCode.RESOURCE_NOT_FOUND:
|
|
case ErrorCode.TENANT_NOT_FOUND:
|
|
return 404
|
|
case ErrorCode.RESOURCE_CONFLICT:
|
|
return 409
|
|
case ErrorCode.QUOTA_EXCEEDED:
|
|
return 429
|
|
case ErrorCode.EXTERNAL_SERVICE_ERROR:
|
|
case ErrorCode.BLOCKCHAIN_ERROR:
|
|
case ErrorCode.PROXMOX_ERROR:
|
|
return 502
|
|
case ErrorCode.DATABASE_ERROR:
|
|
case ErrorCode.NETWORK_ERROR:
|
|
return 503
|
|
default:
|
|
return 500
|
|
}
|
|
}
|
|
|
|
toJSON() {
|
|
return {
|
|
name: this.name,
|
|
code: this.code,
|
|
message: this.message,
|
|
details: this.details,
|
|
statusCode: this.statusCode,
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper functions for common error scenarios
|
|
*/
|
|
export const AppErrors = {
|
|
unauthenticated: (message = 'Authentication required', details?: unknown) =>
|
|
new AppError({ code: ErrorCode.UNAUTHENTICATED, message, details }),
|
|
|
|
forbidden: (message = 'Access denied', details?: unknown) =>
|
|
new AppError({ code: ErrorCode.FORBIDDEN, message, details }),
|
|
|
|
badInput: (message: string, details?: unknown) =>
|
|
new AppError({ code: ErrorCode.BAD_USER_INPUT, message, details }),
|
|
|
|
validation: (message: string, details?: unknown) =>
|
|
new AppError({ code: ErrorCode.VALIDATION_ERROR, message, details }),
|
|
|
|
notFound: (resource: string, id?: string) =>
|
|
new AppError({
|
|
code: ErrorCode.RESOURCE_NOT_FOUND,
|
|
message: id ? `${resource} with id ${id} not found` : `${resource} not found`,
|
|
details: { resource, id },
|
|
}),
|
|
|
|
tenantNotFound: (tenantId: string) =>
|
|
new AppError({
|
|
code: ErrorCode.TENANT_NOT_FOUND,
|
|
message: `Tenant ${tenantId} not found`,
|
|
details: { tenantId },
|
|
}),
|
|
|
|
quotaExceeded: (message: string, details?: unknown) =>
|
|
new AppError({ code: ErrorCode.QUOTA_EXCEEDED, message, details }),
|
|
|
|
conflict: (message: string, details?: unknown) =>
|
|
new AppError({ code: ErrorCode.RESOURCE_CONFLICT, message, details }),
|
|
|
|
externalService: (service: string, message: string, cause?: Error) =>
|
|
new AppError({
|
|
code: ErrorCode.EXTERNAL_SERVICE_ERROR,
|
|
message: `${service}: ${message}`,
|
|
cause,
|
|
details: { service },
|
|
}),
|
|
|
|
blockchain: (message: string, cause?: Error) =>
|
|
new AppError({
|
|
code: ErrorCode.BLOCKCHAIN_ERROR,
|
|
message,
|
|
cause,
|
|
}),
|
|
|
|
proxmox: (message: string, cause?: Error) =>
|
|
new AppError({
|
|
code: ErrorCode.PROXMOX_ERROR,
|
|
message,
|
|
cause,
|
|
}),
|
|
|
|
database: (message: string, cause?: Error) =>
|
|
new AppError({
|
|
code: ErrorCode.DATABASE_ERROR,
|
|
message,
|
|
cause,
|
|
}),
|
|
|
|
internal: (message: string, cause?: Error, details?: unknown) =>
|
|
new AppError({
|
|
code: ErrorCode.INTERNAL_ERROR,
|
|
message,
|
|
cause,
|
|
details,
|
|
}),
|
|
}
|
|
|
|
/**
|
|
* Check if error is an AppError
|
|
*/
|
|
export function isAppError(error: unknown): error is AppError {
|
|
return error instanceof AppError
|
|
}
|
|
|
|
/**
|
|
* Convert any error to AppError
|
|
*/
|
|
export function toAppError(error: unknown, defaultMessage = 'An error occurred'): AppError {
|
|
if (isAppError(error)) {
|
|
return error
|
|
}
|
|
|
|
if (error instanceof Error) {
|
|
return AppErrors.internal(defaultMessage, error)
|
|
}
|
|
|
|
return AppErrors.internal(defaultMessage, undefined, { originalError: error })
|
|
}
|
|
|