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