File size: 1,723 Bytes
e218aaf | 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 55 56 57 58 59 60 61 62 63 64 65 66 | import { db } from '@/lib/db';
export async function rateLimit(
key: string,
maxRequests: number,
windowMs: number
): Promise<{ allowed: boolean; retryAfterMs: number }> {
const now = new Date();
try {
const record = await db.rateLimit.findUnique({
where: { key },
});
if (!record) {
await db.rateLimit.create({
data: {
key,
count: 1,
resetAt: new Date(now.getTime() + windowMs),
},
});
return { allowed: true, retryAfterMs: 0 };
}
if (now >= record.resetAt) {
// Window expired — reset
const updated = await db.rateLimit.update({
where: { key },
data: {
count: 1,
resetAt: new Date(now.getTime() + windowMs),
},
});
return { allowed: true, retryAfterMs: 0 };
}
if (record.count >= maxRequests) {
const retryAfterMs = record.resetAt.getTime() - now.getTime();
return { allowed: false, retryAfterMs };
}
await db.rateLimit.update({
where: { key },
data: { count: { increment: 1 } },
});
return { allowed: true, retryAfterMs: 0 };
} catch {
// On DB error, allow the request through (fail-open)
return { allowed: true, retryAfterMs: 0 };
}
}
// Pre-configured rate limiters
export const authRateLimit = (key: string) =>
rateLimit(key, 5, 60_000); // 5 requests per minute
export const uploadRateLimit = (key: string) =>
rateLimit(key, 20, 60_000); // 20 requests per minute
export const chatRateLimit = (key: string) =>
rateLimit(key, 20, 60_000); // 20 requests per minute
export const generalRateLimit = (key: string) =>
rateLimit(key, 60, 60_000); // 60 requests per minute |