Aktueller Stand
This commit is contained in:
57
lib/rate-limit.ts
Normal file
57
lib/rate-limit.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { prisma } from "./prisma";
|
||||
|
||||
type RateLimitResult = {
|
||||
ok: boolean;
|
||||
remaining: number;
|
||||
resetAt: Date;
|
||||
};
|
||||
|
||||
const parseNumber = (value: string | undefined, fallback: number) => {
|
||||
if (!value) return fallback;
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
||||
};
|
||||
|
||||
export const getRateLimitConfig = (envKey: string, defaultLimit: number) => {
|
||||
const limit = parseNumber(process.env[envKey], defaultLimit);
|
||||
const windowMinutes = parseNumber(process.env.RATE_LIMIT_WINDOW_MINUTES, 15);
|
||||
return { limit, windowMs: windowMinutes * 60 * 1000 };
|
||||
};
|
||||
|
||||
export async function checkRateLimit({
|
||||
key,
|
||||
limit,
|
||||
windowMs
|
||||
}: {
|
||||
key: string;
|
||||
limit: number;
|
||||
windowMs: number;
|
||||
}): Promise<RateLimitResult> {
|
||||
const now = new Date();
|
||||
const resetAt = new Date(now.getTime() + windowMs);
|
||||
|
||||
const existing = await prisma.rateLimit.findUnique({ where: { key } });
|
||||
if (!existing || existing.resetAt <= now) {
|
||||
await prisma.rateLimit.upsert({
|
||||
where: { key },
|
||||
update: { count: 1, resetAt },
|
||||
create: { key, count: 1, resetAt }
|
||||
});
|
||||
return { ok: true, remaining: Math.max(0, limit - 1), resetAt };
|
||||
}
|
||||
|
||||
if (existing.count >= limit) {
|
||||
return { ok: false, remaining: 0, resetAt: existing.resetAt };
|
||||
}
|
||||
|
||||
const updated = await prisma.rateLimit.update({
|
||||
where: { key },
|
||||
data: { count: { increment: 1 } }
|
||||
});
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
remaining: Math.max(0, limit - updated.count),
|
||||
resetAt: updated.resetAt
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user