Domify-Academy-Bot / middleware.ts
Domify's picture
Upload 35 files
93c19dc verified
/**
* 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<string, { data: unknown; expiresAt: number }>();
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<string, number[]>();
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
};
}