61 lines
1.3 KiB
TypeScript
61 lines
1.3 KiB
TypeScript
interface RateLimitData {
|
|
count: number
|
|
resetTime: number
|
|
}
|
|
|
|
const cache = new Map<string, RateLimitData>()
|
|
|
|
export interface RateLimitConfig {
|
|
windowMs: number
|
|
maxRequests: number
|
|
}
|
|
|
|
const defaultConfig: RateLimitConfig = {
|
|
windowMs: 60 * 1000, // 1 minute
|
|
maxRequests: 100
|
|
}
|
|
|
|
export function rateLimit(
|
|
identifier: string,
|
|
config: RateLimitConfig = defaultConfig
|
|
): { allowed: boolean; remaining: number; resetTime: number } {
|
|
|
|
const now = Date.now()
|
|
const windowStart = now - config.windowMs
|
|
|
|
// Clean expired entries
|
|
for (const [key, data] of cache.entries()) {
|
|
if (data.resetTime < now) {
|
|
cache.delete(key)
|
|
}
|
|
}
|
|
|
|
let data = cache.get(identifier)
|
|
|
|
if (!data || data.resetTime < now) {
|
|
data = {
|
|
count: 0,
|
|
resetTime: now + config.windowMs
|
|
}
|
|
}
|
|
|
|
data.count++
|
|
cache.set(identifier, data)
|
|
|
|
const allowed = data.count <= config.maxRequests
|
|
const remaining = Math.max(0, config.maxRequests - data.count)
|
|
|
|
return {
|
|
allowed,
|
|
remaining,
|
|
resetTime: data.resetTime
|
|
}
|
|
}
|
|
|
|
export function getRateLimitHeaders(result: ReturnType<typeof rateLimit>) {
|
|
return {
|
|
'X-RateLimit-Limit': '100',
|
|
'X-RateLimit-Remaining': result.remaining.toString(),
|
|
'X-RateLimit-Reset': new Date(result.resetTime).toISOString()
|
|
}
|
|
} |