Spaces:
Running
Running
File size: 1,638 Bytes
5dfbe50 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import { Context, Next } from 'hono';
import { config } from '../config';
interface RateLimitStore {
[key: string]: {
count: number;
resetTime: number;
};
}
const store: RateLimitStore = {};
// Simple in-memory rate limiter
export const rateLimitMiddleware = async (c: Context, next: Next) => {
const ip = c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown';
const now = Date.now();
const windowStart = now - config.rateLimit.windowMs;
// Clean up old entries
Object.keys(store).forEach(key => {
if (store[key].resetTime < windowStart) {
delete store[key];
}
});
// Check rate limit
if (!store[ip]) {
store[ip] = { count: 1, resetTime: now + config.rateLimit.windowMs };
} else if (store[ip].resetTime < now) {
store[ip] = { count: 1, resetTime: now + config.rateLimit.windowMs };
} else {
store[ip].count++;
}
if (store[ip].count > config.rateLimit.max) {
return c.json(
{ error: 'Too many requests', retryAfter: Math.ceil((store[ip].resetTime - now) / 1000) },
429,
{
'Retry-After': Math.ceil((store[ip].resetTime - now) / 1000).toString(),
'X-RateLimit-Limit': config.rateLimit.max.toString(),
'X-RateLimit-Remaining': '0',
'X-RateLimit-Reset': new Date(store[ip].resetTime).toISOString(),
}
);
}
// Add rate limit headers
c.header('X-RateLimit-Limit', config.rateLimit.max.toString());
c.header('X-RateLimit-Remaining', (config.rateLimit.max - store[ip].count).toString());
c.header('X-RateLimit-Reset', new Date(store[ip].resetTime).toISOString());
await next();
}; |