SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
1.8 KB · 59 lines typescript
Raw Blame History
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