![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/lavocat.ca/public_html/src/lib/ |
import { NextApiRequest, NextApiResponse } from 'next';
// Simple in-memory rate limiter (for production, use Redis)
const rateLimitMap = new Map<string, { count: number; resetTime: number }>();
interface RateLimitOptions {
interval: number; // Time window in milliseconds
uniqueTokenPerInterval: number; // Max requests per interval
}
export function rateLimit(options: RateLimitOptions) {
return async (req: NextApiRequest, res: NextApiResponse, next: () => void) => {
// Get client identifier (IP + User-Agent for better uniqueness)
const identifier = `${req.headers['x-forwarded-for'] || req.connection.remoteAddress || 'unknown'}-${req.headers['user-agent'] || 'unknown'}`;
const now = Date.now();
const windowStart = now - options.interval;
// Get or create rate limit data for this identifier
const rateLimitData = rateLimitMap.get(identifier) || { count: 0, resetTime: now + options.interval };
// Reset if window has passed
if (now > rateLimitData.resetTime) {
rateLimitData.count = 0;
rateLimitData.resetTime = now + options.interval;
}
// Check if limit exceeded
if (rateLimitData.count >= options.uniqueTokenPerInterval) {
const resetIn = Math.ceil((rateLimitData.resetTime - now) / 1000);
console.log('🚫 Rate limit exceeded for:', identifier, 'count:', rateLimitData.count);
res.status(429).json({
error: 'Too many requests',
message: `Rate limit exceeded. Try again in ${resetIn} seconds.`,
resetIn
});
return;
}
// Increment counter
rateLimitData.count++;
rateLimitMap.set(identifier, rateLimitData);
// Clean up old entries periodically
if (Math.random() < 0.01) { // 1% chance to clean up
cleanupOldEntries();
}
// Set rate limit headers
res.setHeader('X-RateLimit-Limit', options.uniqueTokenPerInterval);
res.setHeader('X-RateLimit-Remaining', Math.max(0, options.uniqueTokenPerInterval - rateLimitData.count));
res.setHeader('X-RateLimit-Reset', Math.ceil(rateLimitData.resetTime / 1000));
next();
};
}
function cleanupOldEntries() {
const now = Date.now();
rateLimitMap.forEach((data, key) => {
if (now > data.resetTime) {
rateLimitMap.delete(key);
}
});
}
// Common rate limit configurations
export const rateLimits = {
// Strict rate limit for sensitive operations
strict: { interval: 15 * 60 * 1000, uniqueTokenPerInterval: 5 }, // 5 requests per 15 minutes
// Moderate rate limit for admin operations
moderate: { interval: 5 * 60 * 1000, uniqueTokenPerInterval: 20 }, // 20 requests per 5 minutes
// Standard rate limit for API endpoints
standard: { interval: 60 * 1000, uniqueTokenPerInterval: 100 }, // 100 requests per minute
// Lenient rate limit for general use
lenient: { interval: 60 * 1000, uniqueTokenPerInterval: 300 }, // 300 requests per minute
};