template-web-astro/src/libs/RateLimiter.ts
Florian Bouillon ff07f8f4a5 fet: Add changes lol
Signed-off-by: Florian BOUILLON <f.bouillon@aptatio.com>
2023-06-28 17:30:18 +02:00

75 lines
1.9 KiB
TypeScript

import StatusCode from './HTTP/StatusCode'
import { buildRFC7807 } from './RFCs/RFC7807'
import ResponseBuilder from './ResponseBuilder'
interface StorageItem {
pointsRemaining: number
timeReset: number
}
export interface RateLimitHeaders {
"Retry-After"?: string,
"X-RateLimit-Limit": string,
"X-RateLimit-Remaining": string,
"X-RateLimit-Reset": string
}
export default class RateLimiter {
/**
* number of points that can be used per {timeSpan}
*/
public static points = 10
/**
* timeSpan in seconds
*/
public static timeSpan = 60
private static instance: RateLimiter = new RateLimiter()
public static getInstance(): RateLimiter {
return this.instance
}
private storage: Record<string, StorageItem> = {}
public constructor(
private points = RateLimiter.points,
private timeSpan = RateLimiter.timeSpan
) {}
public consume(key: string, value: number = 1): Response | RateLimitHeaders {
let item = this.storage[key]
const now = (new Date().getTime() / 1000)
if (!item) {
item = {
pointsRemaining: this.points,
timeReset: now + this.timeSpan
}
}
if (item.timeReset <= now) {
item.timeReset = now + this.timeSpan
item.pointsRemaining = this.points
}
item.pointsRemaining -= value
this.storage[key] = item
const headers: RateLimitHeaders = {
"X-RateLimit-Limit": this.points.toFixed(0),
"X-RateLimit-Remaining": Math.max(item.pointsRemaining, 0).toFixed(0),
"X-RateLimit-Reset": item.timeReset.toFixed(0)
}
if (item.pointsRemaining < 0) {
const res = new ResponseBuilder()
const resp = buildRFC7807({
type: '/docs/error/rate-limited',
status: StatusCode.TOO_MANY_REQUESTS,
title: 'You are being rate limited as you have done too many requests to the server'
}, res)
res.addHeader('Retry-After', (item.timeReset - now).toFixed(0))
res.addHeaders(headers as any)
return resp
}
return headers
}
}