SPB Git
1.0 KB · 15 lines typescript
1import { sql } from "@/db";2/** Login limiter backed by the DB: 5 failures / 15 min per IP or per e-mail, exponential lockout up to 60 min. */3export async function loginAllowed(ip: string, email: string): Promise<{ ok: true } | { ok: false; retryAfterSec: number }> {4  const [r] = await sql`SELECT5      (SELECT count(*) FROM login_attempts WHERE ip = ${ip} AND success = false AND created_at > now() - interval '15 minutes') AS by_ip,6      (SELECT count(*) FROM login_attempts WHERE email = ${email} AND success = false AND created_at > now() - interval '15 minutes') AS by_user`;7  const failures = Math.max(Number(r.by_ip), Number(r.by_user));8  if (failures < 5) return { ok: true };9  return { ok: false, retryAfterSec: Math.min(60, 2 ** (failures - 4)) * 60 };10}11export async function recordAttempt(ip: string, email: string | null, success: boolean) {12  await sql`INSERT INTO login_attempts (ip, email, success) VALUES (${ip}, ${email}, ${success})`;13  if (Math.random() < 0.05) await sql`DELETE FROM login_attempts WHERE created_at < now() - interval '2 days'`;14}15
Files · main
Menu

Theme