Spaces:
Sleeping
Sleeping
| import { NextRequest, NextResponse } from 'next/server'; | |
| // Error types | |
| export enum ErrorType { | |
| VALIDATION_ERROR = 'VALIDATION_ERROR', | |
| AUTHENTICATION_ERROR = 'AUTHENTICATION_ERROR', | |
| AUTHORIZATION_ERROR = 'AUTHORIZATION_ERROR', | |
| NOT_FOUND_ERROR = 'NOT_FOUND_ERROR', | |
| DUPLICATE_ERROR = 'DUPLICATE_ERROR', | |
| DATABASE_ERROR = 'DATABASE_ERROR', | |
| RATE_LIMIT_ERROR = 'RATE_LIMIT_ERROR', | |
| INTERNAL_ERROR = 'INTERNAL_ERROR' | |
| } | |
| // Custom error class | |
| export class AppError extends Error { | |
| public readonly type: ErrorType; | |
| public readonly statusCode: number; | |
| public readonly isOperational: boolean; | |
| public readonly context?: any; | |
| constructor( | |
| message: string, | |
| type: ErrorType, | |
| statusCode: number = 500, | |
| isOperational: boolean = true, | |
| context?: any | |
| ) { | |
| super(message); | |
| this.type = type; | |
| this.statusCode = statusCode; | |
| this.isOperational = isOperational; | |
| this.context = context; | |
| Error.captureStackTrace(this, this.constructor); | |
| } | |
| } | |
| // Logger utility | |
| export class Logger { | |
| private static log(level: string, message: string, meta?: any) { | |
| const timestamp = new Date().toISOString(); | |
| const logEntry = { | |
| timestamp, | |
| level, | |
| message, | |
| meta, | |
| environment: process.env.NODE_ENV || 'development' | |
| }; | |
| // In production, send to logging service | |
| if (process.env.NODE_ENV === 'production') { | |
| console.log(JSON.stringify(logEntry)); | |
| } else { | |
| console.log(`[${timestamp}] ${level.toUpperCase()}: ${message}`); | |
| if (meta) console.log('Meta:', meta); | |
| } | |
| } | |
| static info(message: string, meta?: any) { | |
| this.log('info', message, meta); | |
| } | |
| static warn(message: string, meta?: any) { | |
| this.log('warn', message, meta); | |
| } | |
| static error(message: string, error?: Error | any, meta?: any) { | |
| this.log('error', message, { | |
| error: error?.message || error, | |
| stack: error?.stack, | |
| ...meta | |
| }); | |
| } | |
| static debug(message: string, meta?: any) { | |
| if (process.env.NODE_ENV === 'development') { | |
| this.log('debug', message, meta); | |
| } | |
| } | |
| static security(message: string, meta?: any) { | |
| this.log('security', message, { | |
| ...meta, | |
| timestamp: new Date().toISOString(), | |
| ip: meta?.ip || 'unknown', | |
| userAgent: meta?.userAgent || 'unknown' | |
| }); | |
| } | |
| } | |
| // Audit logging for data changes | |
| export class AuditLogger { | |
| static log( | |
| action: string, | |
| entity: string, | |
| entityId: string, | |
| userId: string, | |
| changes?: any, | |
| meta?: any | |
| ) { | |
| const auditEntry = { | |
| timestamp: new Date().toISOString(), | |
| action, | |
| entity, | |
| entityId, | |
| userId, | |
| changes, | |
| meta, | |
| environment: process.env.NODE_ENV || 'development' | |
| }; | |
| Logger.info(`AUDIT: ${action} on ${entity} ${entityId}`, auditEntry); | |
| // In production, store in secure audit database | |
| if (process.env.NODE_ENV === 'production') { | |
| // TODO: Implement secure audit log storage | |
| } | |
| } | |
| static logSecurity( | |
| eventType: string, | |
| identifier: string, | |
| details?: any | |
| ) { | |
| const securityEntry = { | |
| timestamp: new Date().toISOString(), | |
| eventType, | |
| identifier, | |
| details, | |
| environment: process.env.NODE_ENV || 'development' | |
| }; | |
| Logger.security(`SECURITY: ${eventType} from ${identifier}`, securityEntry); | |
| // In production, store in secure security log database | |
| if (process.env.NODE_ENV === 'production') { | |
| // TODO: Implement secure security log storage | |
| } | |
| } | |
| } | |
| // Error handler middleware | |
| export function handleApiError(error: any, request?: NextRequest): NextResponse { | |
| Logger.error('API Error occurred', error, { | |
| url: request?.url, | |
| method: request?.method, | |
| userAgent: request?.headers.get('user-agent'), | |
| ip: request?.headers.get('x-forwarded-for') || request?.headers.get('x-real-ip') | |
| }); | |
| // Handle validation errors | |
| if (error.type === ErrorType.VALIDATION_ERROR) { | |
| return NextResponse.json( | |
| { | |
| success: false, | |
| error: 'Validation Error', | |
| message: error.message, | |
| details: error.context?.errors || [] | |
| }, | |
| { status: 400 } | |
| ); | |
| } | |
| // Handle authentication errors | |
| if (error.type === ErrorType.AUTHENTICATION_ERROR) { | |
| Logger.security('Authentication failed', { | |
| url: request?.url, | |
| userAgent: request?.headers.get('user-agent') | |
| }); | |
| return NextResponse.json( | |
| { | |
| success: false, | |
| error: 'Authentication Error', | |
| message: 'Please provide valid authentication credentials' | |
| }, | |
| { status: 401 } | |
| ); | |
| } | |
| // Handle authorization errors | |
| if (error.type === ErrorType.AUTHORIZATION_ERROR) { | |
| Logger.security('Authorization failed', { | |
| url: request?.url, | |
| userAgent: request?.headers.get('user-agent') | |
| }); | |
| return NextResponse.json( | |
| { | |
| success: false, | |
| error: 'Authorization Error', | |
| message: 'You do not have permission to perform this action' | |
| }, | |
| { status: 403 } | |
| ); | |
| } | |
| // Handle not found errors | |
| if (error.type === ErrorType.NOT_FOUND_ERROR) { | |
| return NextResponse.json( | |
| { | |
| success: false, | |
| error: 'Not Found', | |
| message: error.message | |
| }, | |
| { status: 404 } | |
| ); | |
| } | |
| // Handle duplicate errors | |
| if (error.type === ErrorType.DUPLICATE_ERROR) { | |
| return NextResponse.json( | |
| { | |
| success: false, | |
| error: 'Duplicate Entry', | |
| message: error.message | |
| }, | |
| { status: 409 } | |
| ); | |
| } | |
| // Handle rate limit errors | |
| if (error.type === ErrorType.RATE_LIMIT_ERROR) { | |
| return NextResponse.json( | |
| { | |
| success: false, | |
| error: 'Rate Limit Exceeded', | |
| message: 'Too many requests. Please try again later.' | |
| }, | |
| { status: 429 } | |
| ); | |
| } | |
| // Handle database errors | |
| if (error.type === ErrorType.DATABASE_ERROR) { | |
| Logger.error('Database error occurred', error); | |
| return NextResponse.json( | |
| { | |
| success: false, | |
| error: 'Database Error', | |
| message: 'An error occurred while processing your request' | |
| }, | |
| { status: 500 } | |
| ); | |
| } | |
| // Handle all other errors | |
| const isDevelopment = process.env.NODE_ENV === 'development'; | |
| return NextResponse.json( | |
| { | |
| success: false, | |
| error: 'Internal Server Error', | |
| message: isDevelopment ? error.message : 'An unexpected error occurred', | |
| ...(isDevelopment && { stack: error.stack, context: error.context }) | |
| }, | |
| { status: error.statusCode || 500 } | |
| ); | |
| } | |
| // Validation error helper | |
| export function createValidationError(errors: string[], context?: any): AppError { | |
| return new AppError( | |
| 'Validation failed', | |
| ErrorType.VALIDATION_ERROR, | |
| 400, | |
| true, | |
| { errors, ...context } | |
| ); | |
| } | |
| // Authentication error helper | |
| export function createAuthenticationError(message: string = 'Authentication required'): AppError { | |
| return new AppError(message, ErrorType.AUTHENTICATION_ERROR, 401); | |
| } | |
| // Authorization error helper | |
| export function createAuthorizationError(message: string = 'Insufficient permissions'): AppError { | |
| return new AppError(message, ErrorType.AUTHORIZATION_ERROR, 403); | |
| } | |
| // Not found error helper | |
| export function createNotFoundError(entity: string, id?: string): AppError { | |
| const message = id ? `${entity} with id ${id} not found` : `${entity} not found`; | |
| return new AppError(message, ErrorType.NOT_FOUND_ERROR, 404); | |
| } | |
| // Duplicate error helper | |
| export function createDuplicateError(entity: string, field: string, value: string): AppError { | |
| return new AppError( | |
| `${entity} with ${field} '${value}' already exists`, | |
| ErrorType.DUPLICATE_ERROR, | |
| 409 | |
| ); | |
| } | |
| // Database error helper | |
| export function createDatabaseError(message: string, originalError?: any): AppError { | |
| return new AppError( | |
| message, | |
| ErrorType.DATABASE_ERROR, | |
| 500, | |
| true, | |
| { originalError: originalError?.message } | |
| ); | |
| } | |
| // Rate limit error helper | |
| export function createRateLimitError(limit: number, windowMs: number): AppError { | |
| return new AppError( | |
| `Rate limit exceeded. Maximum ${limit} requests per ${windowMs}ms allowed.`, | |
| ErrorType.RATE_LIMIT_ERROR, | |
| 429 | |
| ); | |
| } | |