import { redis } from '../redis.js'; export interface RateLimitResult { limited: boolean; retryAfter: number; } export async function checkRateLimit( ip: string, action: string, max: number, windowSeconds: number, ): Promise { const key = `rl:${action}:${ip}`; const hits = await redis.incr(key); if (hits === 1) await redis.expire(key, windowSeconds); if (hits > max) { const ttl = await redis.ttl(key); return { limited: true, retryAfter: Math.max(ttl, 1) }; } return { limited: false, retryAfter: 0 }; } export async function checkRateLimitBySubject( subject: string, action: string, max: number, windowSeconds: number, ): Promise { const key = `rl:${action}:${subject}`; const hits = await redis.incr(key); if (hits === 1) await redis.expire(key, windowSeconds); if (hits > max) { const ttl = await redis.ttl(key); return { limited: true, retryAfter: Math.max(ttl, 1) }; } return { limited: false, retryAfter: 0 }; } export function rateLimitResponse(reply: import('fastify').FastifyReply, retryAfter: number) { reply.header('Retry-After', String(retryAfter)); return reply.code(429).send({ error: 'rate_limited', retryAfter }); }