/** * Industrial Standard: Middleware Suite * * Includes: * - Request logging * - Response caching * - Error handling * - Performance monitoring * - Security headers */ /** * Simple in-memory cache for frequently accessed data * In production, use Redis for distributed caching */ class CacheManager { private cache = new Map(); set(key: string, data: unknown, ttlSeconds: number = 300): void { const expiresAt = Date.now() + ttlSeconds * 1000; this.cache.set(key, { data, expiresAt }); } get(key: string): unknown | null { const entry = this.cache.get(key); if (!entry) return null; if (Date.now() > entry.expiresAt) { this.cache.delete(key); return null; } return entry.data; } clear(): void { this.cache.clear(); } delete(key: string): void { this.cache.delete(key); } } export const cacheManager = new CacheManager(); /** * Request logger with performance tracking */ export function createRequestLogger() { return (req: any, res: any, next: any) => { const startTime = Date.now(); const { method, url, ip } = req; // Log request console.log(`[${new Date().toISOString()}] ${method} ${url} from ${ip}`); // Capture response const originalSend = res.send; res.send = function (data: any) { const duration = Date.now() - startTime; const statusCode = res.statusCode; console.log( `[${new Date().toISOString()}] ${method} ${url} ${statusCode} ${duration}ms` ); return originalSend.call(this, data); }; next(); }; } /** * Error handler middleware */ export function createErrorHandler() { return (err: any, req: any, res: any, next: any) => { console.error("[ERROR]", err); const statusCode = err.statusCode || 500; const message = err.message || "Internal Server Error"; res.status(statusCode).json({ success: false, error: message, ...(process.env.NODE_ENV === "development" && { stack: err.stack }), }); }; } /** * Security headers middleware */ export function createSecurityHeaders() { return (req: any, res: any, next: any) => { res.setHeader("X-Content-Type-Options", "nosniff"); res.setHeader("X-Frame-Options", "DENY"); res.setHeader("X-XSS-Protection", "1; mode=block"); res.setHeader("Strict-Transport-Security", "max-age=31536000"); res.setHeader( "Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" ); next(); }; } /** * Performance monitoring */ export class PerformanceMonitor { private metrics = new Map(); record(operation: string, duration: number): void { if (!this.metrics.has(operation)) { this.metrics.set(operation, []); } this.metrics.get(operation)!.push(duration); } getStats(operation: string): { count: number; avg: number; min: number; max: number; } | null { const durations = this.metrics.get(operation); if (!durations || durations.length === 0) return null; const count = durations.length; const avg = durations.reduce((a, b) => a + b, 0) / count; const min = Math.min(...durations); const max = Math.max(...durations); return { count, avg, min, max }; } getAllStats(): Record< string, { count: number; avg: number; min: number; max: number } > { const stats: Record< string, { count: number; avg: number; min: number; max: number } > = {}; this.metrics.forEach((durations, operation) => { if (durations.length > 0) { const count = durations.length; const avg = durations.reduce((a: number, b: number) => a + b, 0) / count; const min = Math.min(...durations); const max = Math.max(...durations); stats[operation] = { count, avg, min, max }; } }); return stats; } clear(): void { this.metrics.clear(); } } export const performanceMonitor = new PerformanceMonitor(); /** * Health check endpoint data */ export interface HealthStatus { status: "healthy" | "degraded" | "unhealthy"; timestamp: string; uptime: number; database: "connected" | "disconnected"; cache: "active" | "inactive"; memoryUsage: number; requestsPerMinute: number; } export function getHealthStatus(): HealthStatus { const uptime = process.uptime(); const memoryUsage = process.memoryUsage().heapUsed / 1024 / 1024; // MB return { status: memoryUsage > 500 ? "degraded" : "healthy", timestamp: new Date().toISOString(), uptime, database: "connected", // Would check actual DB connection cache: "active", memoryUsage: Math.round(memoryUsage), requestsPerMinute: 0, // Would track actual requests }; }