TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1/**2 * In-memory sliding-window rate limiter. PolyLLM runs as a single PM2 process on the3 * cluster so a process-local limiter is sufficient; the interface is async so a Redis4 * store can be dropped in without touching call sites.5 */6interface Bucket {7 hits: number[];8}910const buckets = new Map<string, Bucket>();11let lastSweep = Date.now();1213function sweep(now: number) {14 if (now - lastSweep < 60_000) return;15 lastSweep = now;16 for (const [k, b] of buckets) {17 if (b.hits.length === 0 || b.hits[b.hits.length - 1] < now - 10 * 60_000) buckets.delete(k);18 }19}2021export interface RateLimitResult {22 ok: boolean;23 remaining: number;24 retryAfterMs: number;25}2627export async function rateLimit(key: string, max: number, windowMs: number): Promise<RateLimitResult> {28 const now = Date.now();29 sweep(now);30 const b = buckets.get(key) ?? { hits: [] };31 b.hits = b.hits.filter((t) => t > now - windowMs);32 if (b.hits.length >= max) {33 buckets.set(key, b);34 return { ok: false, remaining: 0, retryAfterMs: b.hits[0] + windowMs - now };35 }36 b.hits.push(now);37 buckets.set(key, b);38 return { ok: true, remaining: max - b.hits.length, retryAfterMs: 0 };39}4041export function clientIp(req: Request): string {42 const h = req.headers;43 // ngrok forwards the visitor IP in X-Forwarded-For (first hop).44 const xff = h.get("x-forwarded-for");45 if (xff) return xff.split(",")[0].trim();46 return h.get("x-real-ip") ?? "unknown";47}4849export const LIMITS = {50 chat: { max: 60, windowMs: 60_000 },51 arena: { max: 20, windowMs: 60_000 },52 keyValidate: { max: 10, windowMs: 60_000 },53 keySave: { max: 12, windowMs: 60_000 },54 modelSync: { max: 6, windowMs: 60_000 },55 upload: { max: 40, windowMs: 60_000 },56 search: { max: 120, windowMs: 60_000 },57 share: { max: 20, windowMs: 60_000 },58} as const;59