Adds security middleware, rate limiting, Redis caching, and monitoring services for improved security, performance, and analytics. Replit-Commit-Author: Agent Replit-Commit-Session-Id: c5f0c281-8dd8-4846-b452-4a07bcd21062 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9777c70b-fc38-4831-8d6b-78dfffe041b0/c9b78723-c2e4-4754-83c6-8d40e8eb250e.jpg
118 lines
3.2 KiB
TypeScript
118 lines
3.2 KiB
TypeScript
import rateLimit from 'express-rate-limit';
|
|
import helmet from 'helmet';
|
|
import compression from 'compression';
|
|
import type { Request, Response, NextFunction } from 'express';
|
|
|
|
// Rate limiting configurations
|
|
export const generalRateLimit = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 100, // 100 requests per window
|
|
message: {
|
|
error: 'Too many requests from this IP, please try again later.',
|
|
retryAfter: '15 minutes'
|
|
},
|
|
standardHeaders: true,
|
|
legacyHeaders: false
|
|
});
|
|
|
|
export const authRateLimit = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 5, // 5 login attempts per window
|
|
skipSuccessfulRequests: true,
|
|
message: {
|
|
error: 'Too many authentication attempts, please try again later.',
|
|
retryAfter: '15 minutes'
|
|
}
|
|
});
|
|
|
|
export const voiceRateLimit = rateLimit({
|
|
windowMs: 60 * 1000, // 1 minute
|
|
max: 30, // 30 voice commands per minute
|
|
message: {
|
|
error: 'Voice command rate limit exceeded, please wait before trying again.',
|
|
retryAfter: '1 minute'
|
|
}
|
|
});
|
|
|
|
export const aiRateLimit = rateLimit({
|
|
windowMs: 60 * 1000, // 1 minute
|
|
max: 10, // 10 AI requests per minute
|
|
message: {
|
|
error: 'AI service rate limit exceeded, please wait before trying again.',
|
|
retryAfter: '1 minute'
|
|
}
|
|
});
|
|
|
|
// Security middleware
|
|
export const securityMiddleware = helmet({
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
|
|
fontSrc: ["'self'", "https://fonts.gstatic.com"],
|
|
scriptSrc: ["'self'"],
|
|
imgSrc: ["'self'", "data:", "https:"],
|
|
connectSrc: ["'self'", "ws:", "wss:"]
|
|
}
|
|
},
|
|
crossOriginEmbedderPolicy: false
|
|
});
|
|
|
|
// Compression middleware
|
|
export const compressionMiddleware = compression({
|
|
filter: (req: Request, res: Response) => {
|
|
if (req.headers['x-no-compression']) {
|
|
return false;
|
|
}
|
|
return compression.filter(req, res);
|
|
},
|
|
threshold: 1024 // Only compress responses larger than 1KB
|
|
});
|
|
|
|
// Error tracking middleware
|
|
export const errorTrackingMiddleware = (
|
|
error: any,
|
|
req: Request,
|
|
res: Response,
|
|
next: NextFunction
|
|
) => {
|
|
// Log error details
|
|
console.error('Error:', {
|
|
message: error.message,
|
|
stack: error.stack,
|
|
url: req.url,
|
|
method: req.method,
|
|
ip: req.ip,
|
|
userAgent: req.get('User-Agent'),
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
// Don't expose internal errors in production
|
|
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
|
|
if (error.status && error.status < 500) {
|
|
// Client errors (4xx)
|
|
res.status(error.status).json({
|
|
error: error.message,
|
|
...(isDevelopment && { stack: error.stack })
|
|
});
|
|
} else {
|
|
// Server errors (5xx)
|
|
res.status(500).json({
|
|
error: isDevelopment ? error.message : 'Internal server error',
|
|
...(isDevelopment && { stack: error.stack })
|
|
});
|
|
}
|
|
};
|
|
|
|
// Request logging middleware
|
|
export const requestLoggingMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
|
const start = Date.now();
|
|
|
|
res.on('finish', () => {
|
|
const duration = Date.now() - start;
|
|
console.log(`${req.method} ${req.url} ${res.statusCode} ${duration}ms`);
|
|
});
|
|
|
|
next();
|
|
}; |