Files
ipal-kit/dist/modules/forms/rateLimit.js
T
2026-07-31 23:21:51 +02:00

51 lines
1.7 KiB
JavaScript

/**
* Per-IP sliding window, in memory.
*
* Deliberately simple: no Redis, no dependency. The trade-off is that the
* counter lives in one process — with several instances behind a load balancer
* each keeps its own, so the effective limit is per-instance, not global. For a
* contact form that's fine (it raises the cost of flooding without pretending
* to be airtight); a high-security form should put a real limiter in front.
*
* State is module-level, so it survives between requests but resets on redeploy
* — acceptable for abuse throttling.
*/ const buckets = new Map();
/** Sweep expired buckets occasionally so the map doesn't grow unbounded. */ let lastSweep = Date.now();
const SWEEP_INTERVAL = 60_000;
function sweep(now) {
if (now - lastSweep < SWEEP_INTERVAL) {
return;
}
lastSweep = now;
for (const [key, bucket] of buckets){
if (bucket.resetAt <= now) {
buckets.delete(key);
}
}
}
/**
* Returns true when the request is within the limit, false when it should be
* rejected. A missing key (no IP) is allowed through — better to accept a
* submission than to block everyone behind a proxy that strips the header.
*/ export function checkRateLimit({ key, max = 5, windowMs = 60_000 }) {
if (!key) {
return true;
}
const now = Date.now();
sweep(now);
const bucket = buckets.get(key);
if (!bucket || bucket.resetAt <= now) {
buckets.set(key, {
count: 1,
resetAt: now + windowMs
});
return true;
}
if (bucket.count >= max) {
return false;
}
bucket.count += 1;
return true;
}
//# sourceMappingURL=rateLimit.js.map