lila/apps/api/src/middleware/rateLimiters.ts
lila 0118798e36 feat: guest play — allow singleplayer quiz without auth
- Add optionalAuth middleware: attaches session when present,
  never blocks (guests pass through)
- Make game endpoints (start/answer) accept optional auth
- GameSessionStore.userId: string → string | null
- Rate limiter falls back to IP for unauthenticated users
- Frontend: remove /play route guard, show 'Create account' CTA
  on score screen for guests
- Add tests for guest session creation, answer submission,
  and cross-user session isolation
2026-05-31 21:28:08 +02:00

55 lines
1.5 KiB
TypeScript

import rateLimit, { ipKeyGenerator } from "express-rate-limit";
import type { Request } from "express";
// TODO: When Valkey is wired up, swap the default in-memory store for
// rate-limit-redis to persist limits across restarts:
//
// import { RedisStore } from "rate-limit-redis";
// import { valkey } from "../lib/valkey.js";
// Then add to each limiter: store: new RedisStore({ sendCommand: (...args) => valkey.call(...args) })
export const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
limit: 20,
standardHeaders: "draft-8",
legacyHeaders: false,
skip: (req) => {
const path = req.path;
return (
path.includes("/get-session") ||
path.includes("/sign-out") ||
path.startsWith("/callback/") ||
path.includes("/callback/")
);
},
message: {
success: false,
error: "Too many requests, please try again later.",
},
});
export const gameLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
limit: 150,
standardHeaders: "draft-8",
legacyHeaders: false,
keyGenerator: (req: Request) =>
req.session?.user.id ?? ipKeyGenerator(req.ip ?? "unknown"),
message: {
success: false,
error: "Too many requests, please try again later.",
},
});
export const lobbyLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
limit: 20,
standardHeaders: "draft-8",
legacyHeaders: false,
keyGenerator: (req: Request) =>
req.session?.user.id ?? ipKeyGenerator(req.ip ?? "unknown"),
message: {
success: false,
error: "Too many requests, please try again later.",
},
});