Apply Composer changes: comprehensive API updates, migrations, middleware, and infrastructure improvements
- 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
This commit is contained in:
204
api/src/lib/errors.ts
Normal file
204
api/src/lib/errors.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* 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 })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user