UNPKG

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
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