Files
konstitisyon.nu/lib/rate-limit.js
T

62 lines
1.7 KiB
JavaScript
Raw Normal View History

/**
* Fabrique de rate-limiter en mémoire.
*
* Adapté à un déploiement single-process (PM2 sans cluster).
* Pour un déploiement multi-instances, remplacer le Map par un store
* Redis partagé (ex. @upstash/ratelimit).
*
* @param {object} options
* @param {number} options.windowMs - Durée de la fenêtre en millisecondes
* @param {number} options.max - Nombre maximum de requêtes par fenêtre
*/
export function createRateLimiter({windowMs, max}) {
const store = new Map()
let lastCleanup = Date.now()
/**
* Supprime les entrées expirées du store.
* Appelé automatiquement une fois par fenêtre temporelle.
*/
function cleanup(now) {
for (const [storeKey, entry] of store) {
if (now - entry.start >= windowMs) {
store.delete(storeKey)
}
}
lastCleanup = now
}
/**
* Vérifie si la clé (IP:route) dépasse la limite autorisée.
*
* @param {string} key - Identifiant unique (ex. "1.2.3.4:/api/auth/register")
* @returns {{ success: boolean, retryAfter?: number }}
*/
return key => {
const now = Date.now()
if (now - lastCleanup >= windowMs) {
cleanup(now)
}
const entry = store.get(key)
// Première requête ou fenêtre expirée : on repart à zéro
if (!entry || now - entry.start >= windowMs) {
store.set(key, {count: 1, start: now})
return {success: true}
}
// Limite atteinte
if (entry.count >= max) {
const retryAfter = Math.ceil((entry.start + windowMs - now) / 1000)
return {success: false, retryAfter}
}
// Incrément normal
store.set(key, {...entry, count: entry.count + 1})
return {success: true}
}
}