the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 1241cd4e999bb3f48dca4cab7bcff14fe6f5d6ad 59 lines 1.6 kB view raw
1import type { RequestHandler } from "express"; 2 3interface RateLimiterOptions { 4 windowMs?: number; 5 max?: number; 6 keyPrefix?: string; 7} 8 9export function createRateLimiter( 10 options: RateLimiterOptions = {}, 11): RequestHandler { 12 const windowMs = options.windowMs ?? 60_000; // 1 minute 13 const max = options.max ?? 100; 14 const keyPrefix = options.keyPrefix ?? "rl"; 15 16 return async (req, res, next) => { 17 const ip = 18 (req.headers["x-forwarded-for"] as string | undefined) 19 ?.split(",")[0] 20 ?.trim() ?? 21 req.socket.remoteAddress ?? 22 "unknown"; 23 24 const key = `${keyPrefix}:${ip}`; 25 const now = Date.now(); 26 const windowStart = now - windowMs; 27 28 try { 29 const redis = req.ctx.redis; 30 31 const [, , count] = (await redis 32 .multi() 33 .zRemRangeByScore(key, 0, windowStart) 34 .zAdd(key, { score: now, value: `${now}:${Math.random()}` }) 35 .zCard(key) 36 .pExpire(key, windowMs) 37 .exec()) as [unknown, unknown, number, unknown]; 38 39 const remaining = Math.max(0, max - count); 40 41 res.setHeader("X-RateLimit-Limit", max); 42 res.setHeader("X-RateLimit-Remaining", remaining); 43 res.setHeader("X-RateLimit-Reset", Math.ceil((now + windowMs) / 1000)); 44 45 if (count > max) { 46 res.setHeader("Retry-After", Math.ceil(windowMs / 1000)); 47 res 48 .status(429) 49 .json({ error: "Too many requests, please try again later." }); 50 return; 51 } 52 53 next(); 54 } catch { 55 // Fail open — don't block traffic if Redis is unavailable 56 next(); 57 } 58 }; 59}