cal.pub0.org/packages/lib/rateLimit.ts

90 lines
2.2 KiB
TypeScript

import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { isIpInBanListString } from "./getIP";
import logger from "./logger";
const log = logger.getChildLogger({ prefix: ["RateLimit"] });
export type RateLimitHelper = {
rateLimitingType?: "core" | "forcedSlowMode" | "common" | "api";
identifier: string;
};
export type RatelimitResponse = {
/**
* Whether the request may pass(true) or exceeded the limit(false)
*/
success: boolean;
/**
* Maximum number of requests allowed within a window.
*/
limit: number;
/**
* How many requests the user has left within the current window.
*/
remaining: number;
/**
* Unix timestamp in milliseconds when the limits are reset.
*/
reset: number;
pending: Promise<unknown>;
};
let warningDisplayed = false;
/** Prevent flooding the logs while testing/building */
function logOnce(message: string) {
if (warningDisplayed) return;
log.warn(message);
warningDisplayed = true;
}
export function rateLimiter() {
const UPSATCH_ENV_FOUND = process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN;
if (!UPSATCH_ENV_FOUND) {
logOnce("Disabled due to not finding UPSTASH env variables");
return () => ({ success: true, limit: 10, remaining: 999, reset: 0 } as RatelimitResponse);
}
const redis = Redis.fromEnv();
const limiter = {
core: new Ratelimit({
redis,
analytics: true,
prefix: "ratelimit",
limiter: Ratelimit.fixedWindow(10, "60s"),
}),
common: new Ratelimit({
redis,
analytics: true,
prefix: "ratelimit",
limiter: Ratelimit.fixedWindow(200, "60s"),
}),
forcedSlowMode: new Ratelimit({
redis,
analytics: true,
prefix: "ratelimit:slowmode",
limiter: Ratelimit.fixedWindow(1, "30s"),
}),
api: new Ratelimit({
redis,
analytics: true,
prefix: "ratelimit:api",
limiter: Ratelimit.fixedWindow(10, "60s"),
}),
};
async function rateLimit({ rateLimitingType = "core", identifier }: RateLimitHelper) {
if (isIpInBanListString(identifier)) {
return await limiter.forcedSlowMode.limit(identifier);
}
return await limiter[rateLimitingType].limit(identifier);
}
return rateLimit;
}