Files
voice-call-service/internal/middleware/ratelimit.js
2026-05-29 01:06:47 +03:00

60 lines
1.6 KiB
JavaScript

/**
* RateLimiter limits connection attempts using a sliding window.
*/
export class RateLimiter {
/**
* @param {number} limit Maximum connections allowed
* @param {number} windowMs Sliding window length in milliseconds
*/
constructor(limit, windowMs) {
this.limit = limit;
this.windowMs = windowMs;
this.attempts = new Map();
// Start background cleanup worker every 5 minutes
this.cleanupInterval = setInterval(() => this.cleanup(), 5 * 60 * 1000);
if (this.cleanupInterval.unref) {
this.cleanupInterval.unref();
}
}
/**
* Checks if an attempt is allowed under rate limits for the given IP.
* @param {string} ip
* @returns {boolean} True if connection attempt is within rate limits
*/
allow(ip) {
const now = Date.now();
const cutoff = now - this.windowMs;
let timestamps = this.attempts.get(ip) || [];
// Filter out historical connection attempts outside the current window
timestamps = timestamps.filter(t => t > cutoff);
if (timestamps.length >= this.limit) {
this.attempts.set(ip, timestamps);
return false;
}
timestamps.push(now);
this.attempts.set(ip, timestamps);
return true;
}
/**
* Sweeps inactive keys to reclaim memory.
*/
cleanup() {
const cutoff = Date.now() - this.windowMs;
for (const [ip, timestamps] of this.attempts.entries()) {
const active = timestamps.filter(t => t > cutoff);
if (active.length === 0) {
this.attempts.delete(ip);
} else {
this.attempts.set(ip, active);
}
}
}
}