92 lines
7.9 KiB
JavaScript
92 lines
7.9 KiB
JavaScript
import { rateLimiter } from 'hono-rate-limiter';
|
|
import { recordViolation } from './autoBan.js';
|
|
// Rate limit configuration from environment variables
|
|
const RATE_LIMIT_MAX = parseInt(process.env.RATE_LIMIT_MAX || '1', 10);
|
|
const RATE_LIMIT_WINDOW_MS = parseInt(process.env.RATE_LIMIT_WINDOW_MS || '50', 10);
|
|
console.log('🔒 Rate Limiter Configuration:');
|
|
console.log(` Max: ${RATE_LIMIT_MAX} requests per ${RATE_LIMIT_WINDOW_MS}ms`);
|
|
console.log(` Auto-ban threshold: ${process.env.AUTO_BAN_THRESHOLD || 10} violations`);
|
|
/**
|
|
* Rate limiter middleware configuration
|
|
* Limits requests per user/IP address
|
|
*/
|
|
export const apiRateLimiter = rateLimiter({
|
|
windowMs: RATE_LIMIT_WINDOW_MS, // Time window in milliseconds
|
|
limit: RATE_LIMIT_MAX, // Max requests per window
|
|
standardHeaders: 'draft-6', // Return rate limit info in headers
|
|
keyGenerator: (c) => {
|
|
// Try to get user ID from auth header, fallback to IP
|
|
const authHeader = c.req.header('authorization');
|
|
if (authHeader) {
|
|
// Extract user ID from JWT or auth token if available
|
|
// For now, use the auth header as key
|
|
return `user:${authHeader}`;
|
|
}
|
|
// Fallback to IP address
|
|
const forwarded = c.req.header('x-forwarded-for');
|
|
const ip = forwarded ? forwarded.split(',')[0] : c.req.header('x-real-ip') || 'unknown';
|
|
return `ip:${ip}`;
|
|
},
|
|
handler: (c) => {
|
|
// Record violation for auto-ban tracking
|
|
const authHeader = c.req.header('authorization');
|
|
let key;
|
|
if (authHeader) {
|
|
key = `user:${authHeader}`;
|
|
}
|
|
else {
|
|
const forwarded = c.req.header('x-forwarded-for');
|
|
const ip = forwarded ? forwarded.split(',')[0] : c.req.header('x-real-ip') || 'unknown';
|
|
key = `ip:${ip}`;
|
|
}
|
|
console.log(`⚠️ Rate limit exceeded for ${key}`);
|
|
recordViolation(key);
|
|
return c.json({
|
|
error: 'Too many requests',
|
|
message: `Rate limit exceeded. Maximum ${RATE_LIMIT_MAX} requests per ${RATE_LIMIT_WINDOW_MS}ms`,
|
|
}, 429);
|
|
},
|
|
});
|
|
/**
|
|
* Custom rate limiter for specific endpoints with different limits
|
|
*/
|
|
export function createCustomRateLimiter(limit, windowMs) {
|
|
return rateLimiter({
|
|
windowMs,
|
|
limit,
|
|
standardHeaders: 'draft-6',
|
|
keyGenerator: (c) => {
|
|
const authHeader = c.req.header('authorization');
|
|
if (authHeader) {
|
|
return `user:${authHeader}`;
|
|
}
|
|
const forwarded = c.req.header('x-forwarded-for');
|
|
const ip = forwarded ? forwarded.split(',')[0] : c.req.header('x-real-ip') || 'unknown';
|
|
return `ip:${ip}`;
|
|
},
|
|
handler: (c) => {
|
|
// Record violation for auto-ban tracking
|
|
const authHeader = c.req.header('authorization');
|
|
let key;
|
|
if (authHeader) {
|
|
key = `user:${authHeader}`;
|
|
}
|
|
else {
|
|
const forwarded = c.req.header('x-forwarded-for');
|
|
const ip = forwarded ? forwarded.split(',')[0] : c.req.header('x-real-ip') || 'unknown';
|
|
key = `ip:${ip}`;
|
|
}
|
|
recordViolation(key);
|
|
return c.json({
|
|
error: 'Too many requests',
|
|
message: `Rate limit exceeded. Maximum ${limit} requests per ${windowMs}ms`,
|
|
}, 429);
|
|
},
|
|
});
|
|
}
|
|
// Export configuration for testing
|
|
export const rateLimitConfig = {
|
|
max: RATE_LIMIT_MAX,
|
|
windowMs: RATE_LIMIT_WINDOW_MS,
|
|
};
|
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmF0ZUxpbWl0ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbWlkZGxld2FyZS9yYXRlTGltaXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDL0MsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGNBQWMsQ0FBQTtBQUU5QyxzREFBc0Q7QUFDdEQsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxJQUFJLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQTtBQUN0RSxNQUFNLG9CQUFvQixHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLG9CQUFvQixJQUFJLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQTtBQUVuRixPQUFPLENBQUMsR0FBRyxDQUFDLGdDQUFnQyxDQUFDLENBQUE7QUFDN0MsT0FBTyxDQUFDLEdBQUcsQ0FBQyxXQUFXLGNBQWMsaUJBQWlCLG9CQUFvQixJQUFJLENBQUMsQ0FBQTtBQUMvRSxPQUFPLENBQUMsR0FBRyxDQUFDLDBCQUEwQixPQUFPLENBQUMsR0FBRyxDQUFDLGtCQUFrQixJQUFJLEVBQUUsYUFBYSxDQUFDLENBQUE7QUFHeEY7OztHQUdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLFdBQVcsQ0FBQztJQUN0QyxRQUFRLEVBQUUsb0JBQW9CLEVBQUUsOEJBQThCO0lBQzlELEtBQUssRUFBRSxjQUFjLEVBQUUsMEJBQTBCO0lBQ2pELGVBQWUsRUFBRSxTQUFTLEVBQUUsb0NBQW9DO0lBQ2hFLFlBQVksRUFBRSxDQUFDLENBQVUsRUFBRSxFQUFFO1FBQ3pCLHNEQUFzRDtRQUN0RCxNQUFNLFVBQVUsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQTtRQUNoRCxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQ2Isc0RBQXNEO1lB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
|