precise-memory-rate-limit
Version:
A store for express-rate-limit that provides precise in memory rate limit storage.
94 lines (78 loc) • 2.17 kB
text/typescript
import {Store, IncrementResponse, Options, ValueDeterminingMiddleware} from "express-rate-limit"
function time(): number {
return Date.now();
}
function calculateNextResetTime(windowMs: number, user: User): Date {
return new Date((windowMs - (time() - user.ts)) + time())
}
interface hits {
[key: string]: User
}
function getDict(): any {
return new Proxy({}, {
get: (target: hits, name: string) => name in target ? target[name] : new User()
});
}
class User {
hits: number
ts: number
constructor() {
this.hits = 0
this.ts = Date.now()
}
}
class MemoryStore implements Store {
windowMs!: number
hits!: hits
max!: number | ValueDeterminingMiddleware<number>
constructor(max: number | ValueDeterminingMiddleware<number>) {
this.max = max
}
init(options: Options) {
this.windowMs = options.windowMs
this.hits = getDict()
setInterval(() => {
for (let k in this.hits) {
let v = this.hits[k]
if (v.ts + this.windowMs <= time()) {
delete this.hits[k]
}
}
}, 60000);
}
increment(key: string): IncrementResponse {
let user = this.hits[key]
let reset = calculateNextResetTime(this.windowMs, user)
if (user.ts + this.windowMs <= time()) {
user.hits = 0
}
if (user.hits > this.max) {
return {
totalHits: user.hits,
resetTime: reset
}
}
user.hits++
user.ts = time()
this.hits[key] = user
return {
totalHits: user.hits,
resetTime: reset
}
}
decrement(key: string) {
let user = this.hits[key]
if (user.hits <= 0) {
return
}
user.hits--
this.hits[key] = user
}
resetAll() {
this.hits = getDict()
}
resetKey(key: string) {
delete this.hits[key]
}
}
export default MemoryStore