cal/packages/lib/rateLimit.ts

96 lines
2.4 KiB
TypeScript
Raw Normal View History

import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
import { isIpInBanListString } from "./getIP";
import logger from "./logger";
const log = logger.getSubLogger({ prefix: ["RateLimit"] });
export type RateLimitHelper = {
rateLimitingType?: "core" | "forcedSlowMode" | "common" | "api" | "ai";
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>;
};
2023-07-14 19:59:04 -03:00
let warningDisplayed = false;
/** Prevent flooding the logs while testing/building */
function logOnce(message: string) {
2023-07-14 20:02:10 -03:00
if (warningDisplayed) return;
2023-07-14 19:59:04 -03:00
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) {
2023-07-14 19:59:04 -03:00
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"),
}),
2023-07-27 05:41:43 -03:00
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"),
}),
2023-10-11 19:44:00 -03:00
api: new Ratelimit({
redis,
analytics: true,
prefix: "ratelimit:api",
limiter: Ratelimit.fixedWindow(10, "60s"),
}),
ai: new Ratelimit({
redis,
analytics: true,
prefix: "ratelimit",
limiter: Ratelimit.fixedWindow(20, "1d"),
}),
};
async function rateLimit({ rateLimitingType = "core", identifier }: RateLimitHelper) {
if (isIpInBanListString(identifier)) {
return await limiter.forcedSlowMode.limit(identifier);
}
return await limiter[rateLimitingType].limit(identifier);
}
return rateLimit;
}