/home/techb158/cosmic-risk.abdallabala.com/src/lib
NameSizeModeActions
api-client.js10120644editdlrm
crypto.js16770644editdlrm
email.js21750644editdlrm
http.js5630644editdlrm
idempotency.js5700644editdlrm
metrics.js10610644editdlrm
oidc.js17300644editdlrm
prisma.js2800644editdlrm
rate-limit.js26620644editdlrm
redis.js35170644editdlrm
request-context.js37250644editdlrm
tenant-guard.js14270644editdlrm
Edit: /home/techb158/cosmic-risk.abdallabala.com/src/lib/rate-limit.js (2662B)
const { redisIncr, redisTtl } = require('./redis'); const WINDOW_MS = 60_000; const MAX_PER_WINDOW = 100; const REDIS_ENABLED = process.env.REDIS_URL && process.env.COSMIC_REDIS_ENABLED !== 'false'; const localHits = new Map(); function getClientIp(request) { const forwarded = request.headers?.get?.('x-forwarded-for'); if (forwarded) return forwarded.split(',')[0].trim(); const realIp = request.headers?.get?.('x-real-ip'); if (realIp) return realIp; return '127.0.0.1'; } async function checkRateLimitRedis(key, max = MAX_PER_WINDOW, windowMs = WINDOW_MS) { const now = Date.now(); const windowKey = `rl:${key}:${Math.floor(now / windowMs)}`; const count = await redisIncr(windowKey, windowMs); const ttl = await redisTtl(windowKey); const windowSec = Math.ceil(windowMs / 1000); const reset = ttl > 0 ? Math.ceil(now / 1000) + ttl : Math.ceil(now / 1000) + windowSec; return { allowed: count <= max, remaining: Math.max(0, max - count), reset }; } function checkRateLimitLocal(key, max = MAX_PER_WINDOW, windowMs = WINDOW_MS) { const now = Date.now(); if (!localHits.has(key)) localHits.set(key, []); const timestamps = localHits.get(key).filter(t => now - t < windowMs); timestamps.push(now); localHits.set(key, timestamps); return { allowed: timestamps.length <= max, remaining: Math.max(0, max - timestamps.length), reset: Math.ceil(now / 1000) + Math.ceil(windowMs / 1000) }; } function checkRateLimit(key, max = MAX_PER_WINDOW, windowMs = WINDOW_MS) { if (REDIS_ENABLED) return checkRateLimitRedis(key, max, windowMs); return checkRateLimitLocal(key, max, windowMs); } async function withRateLimit(request, handler, max = MAX_PER_WINDOW, windowMs = WINDOW_MS) { const ip = getClientIp(request); const result = await checkRateLimit(`handler:${ip}`, max, windowMs); if (!result.allowed) { return new Response(JSON.stringify({ error: 'Too many requests. Try again shortly.' }), { status: 429, headers: { 'Content-Type': 'application/json', 'X-RateLimit-Limit': String(max), 'X-RateLimit-Remaining': '0', 'X-RateLimit-Reset': String(result.reset), 'Cache-Control': 'no-store' } }); } const response = await handler(request); if (response?.headers) { response.headers.set('X-RateLimit-Limit', String(max)); response.headers.set('X-RateLimit-Remaining', String(result.remaining)); response.headers.set('X-RateLimit-Reset', String(result.reset)); } return response; } function resetRateLimits() { localHits.clear(); } module.exports = { checkRateLimit, getClientIp, resetRateLimits, withRateLimit };