Import source: app/backend/src/lib/rate-limit
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
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<RateLimitResult> {
|
||||
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<RateLimitResult> {
|
||||
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 });
|
||||
}
|
||||
Reference in New Issue
Block a user