borsa / nextjs-app /src /lib /rate-limit.ts
veteroner's picture
feat: sync US market support β€” bootstrap fix, symbol validation, ghost cleanup, correct dir paths
cb11e81
/**
* Simple in-memory rate limiter using sliding window counters.
* Suitable for single-instance deployments.
* For multi-instance, replace with Redis-backed solution.
*/
interface WindowEntry {
count: number
resetAt: number
}
const store = new Map<string, WindowEntry>()
// Clean up stale entries every 60 seconds
let lastCleanup = Date.now()
const CLEANUP_INTERVAL = 60_000
function cleanup() {
const now = Date.now()
if (now - lastCleanup < CLEANUP_INTERVAL) return
lastCleanup = now
for (const [key, entry] of store) {
if (entry.resetAt <= now) {
store.delete(key)
}
}
}
export interface RateLimitResult {
allowed: boolean
remaining: number
resetAt: number
}
/**
* Check rate limit for a given key.
* @param key - Unique identifier (e.g., IP + route)
* @param maxRequests - Maximum requests allowed in the window
* @param windowMs - Time window in milliseconds
*/
export function checkRateLimit(
key: string,
maxRequests: number,
windowMs: number
): RateLimitResult {
cleanup()
const now = Date.now()
const entry = store.get(key)
if (!entry || entry.resetAt <= now) {
// New window
store.set(key, { count: 1, resetAt: now + windowMs })
return { allowed: true, remaining: maxRequests - 1, resetAt: now + windowMs }
}
if (entry.count < maxRequests) {
entry.count++
return { allowed: true, remaining: maxRequests - entry.count, resetAt: entry.resetAt }
}
return { allowed: false, remaining: 0, resetAt: entry.resetAt }
}
/**
* Extract client IP from request headers.
* Handles x-forwarded-for, x-real-ip, and falls back to 'unknown'.
*/
export function getClientIp(headers: Headers): string {
const xff = headers.get('x-forwarded-for')
if (xff) {
// Take the first IP (client IP)
const first = xff.split(',')[0]?.trim()
if (first) return first
}
const realIp = headers.get('x-real-ip')
if (realIp) return realIp.trim()
return 'unknown'
}
// ─── Rate limit presets ───
/** General API: 60 requests per minute */
export const API_RATE_LIMIT = { maxRequests: 60, windowMs: 60_000 }
/** Write operations (POST actions): 20 per minute */
export const WRITE_RATE_LIMIT = { maxRequests: 20, windowMs: 60_000 }
/** Auth routes (login, register): 10 per minute */
export const AUTH_RATE_LIMIT = { maxRequests: 10, windowMs: 60_000 }
/** Heavy compute (ML, backtest, scan): 10 per minute */
export const HEAVY_RATE_LIMIT = { maxRequests: 10, windowMs: 60_000 }