first commit
This commit is contained in:
110
calcom/packages/lib/rateLimit.ts
Normal file
110
calcom/packages/lib/rateLimit.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { Ratelimit, type LimitOptions, type RatelimitResponse } from "@unkey/ratelimit";
|
||||
|
||||
import { isIpInBanListString } from "./getIP";
|
||||
import logger from "./logger";
|
||||
|
||||
const log = logger.getSubLogger({ prefix: ["RateLimit"] });
|
||||
|
||||
export type RateLimitHelper = {
|
||||
rateLimitingType?: "core" | "forcedSlowMode" | "common" | "api" | "ai" | "sms" | "smsMonth";
|
||||
identifier: string;
|
||||
opts?: LimitOptions;
|
||||
/**
|
||||
* Using a callback instead of a regular return to provide headers even
|
||||
* when the rate limit is reached and an error is thrown.
|
||||
**/
|
||||
onRateLimiterResponse?: (response: RatelimitResponse) => void;
|
||||
};
|
||||
|
||||
let warningDisplayed = false;
|
||||
|
||||
/** Prevent flooding the logs while testing/building */
|
||||
function logOnce(message: string) {
|
||||
if (warningDisplayed) return;
|
||||
log.warn(message);
|
||||
warningDisplayed = true;
|
||||
}
|
||||
|
||||
export const API_KEY_RATE_LIMIT = 30;
|
||||
|
||||
export function rateLimiter() {
|
||||
const { UNKEY_ROOT_KEY } = process.env;
|
||||
|
||||
if (!UNKEY_ROOT_KEY) {
|
||||
logOnce("Disabled due to not finding UNKEY_ROOT_KEY env variable");
|
||||
return () => ({ success: true, limit: 10, remaining: 999, reset: 0 } as RatelimitResponse);
|
||||
}
|
||||
const timeout = {
|
||||
fallback: { success: true, limit: 10, remaining: 999, reset: 0 },
|
||||
ms: 5000,
|
||||
};
|
||||
|
||||
const limiter = {
|
||||
core: new Ratelimit({
|
||||
rootKey: UNKEY_ROOT_KEY,
|
||||
namespace: "core",
|
||||
limit: 10,
|
||||
duration: "60s",
|
||||
async: true,
|
||||
timeout,
|
||||
}),
|
||||
common: new Ratelimit({
|
||||
rootKey: UNKEY_ROOT_KEY,
|
||||
namespace: "common",
|
||||
limit: 200,
|
||||
duration: "60s",
|
||||
async: true,
|
||||
timeout,
|
||||
}),
|
||||
forcedSlowMode: new Ratelimit({
|
||||
rootKey: UNKEY_ROOT_KEY,
|
||||
namespace: "forcedSlowMode",
|
||||
limit: 1,
|
||||
duration: "30s",
|
||||
async: true,
|
||||
timeout,
|
||||
}),
|
||||
api: new Ratelimit({
|
||||
rootKey: UNKEY_ROOT_KEY,
|
||||
namespace: "api",
|
||||
limit: API_KEY_RATE_LIMIT,
|
||||
duration: "60s",
|
||||
async: true,
|
||||
timeout,
|
||||
}),
|
||||
ai: new Ratelimit({
|
||||
rootKey: UNKEY_ROOT_KEY,
|
||||
namespace: "ai",
|
||||
limit: 20,
|
||||
duration: "1d",
|
||||
async: true,
|
||||
timeout,
|
||||
}),
|
||||
sms: new Ratelimit({
|
||||
rootKey: UNKEY_ROOT_KEY,
|
||||
namespace: "sms",
|
||||
limit: 50,
|
||||
duration: "5m",
|
||||
async: true,
|
||||
timeout,
|
||||
}),
|
||||
smsMonth: new Ratelimit({
|
||||
rootKey: UNKEY_ROOT_KEY,
|
||||
namespace: "smsMonth",
|
||||
limit: 250,
|
||||
duration: "30d",
|
||||
async: true,
|
||||
timeout,
|
||||
}),
|
||||
};
|
||||
|
||||
async function rateLimit({ rateLimitingType = "core", identifier, opts }: RateLimitHelper) {
|
||||
if (isIpInBanListString(identifier)) {
|
||||
return await limiter.forcedSlowMode.limit(identifier, opts);
|
||||
}
|
||||
|
||||
return await limiter[rateLimitingType].limit(identifier, opts);
|
||||
}
|
||||
|
||||
return rateLimit;
|
||||
}
|
||||
Reference in New Issue
Block a user