Skip to content

Commit

Permalink
fix: Adds missing rate limiting headers (calcom#13430)
Browse files Browse the repository at this point in the history
  • Loading branch information
zomars authored Jan 29, 2024
1 parent d91e74e commit fdaa696
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 2 deletions.
6 changes: 6 additions & 0 deletions apps/api/lib/helpers/rateLimitApiKey.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { NextMiddleware } from "next-api-middleware";

import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
import { API_KEY_RATE_LIMIT } from "@calcom/lib/rateLimit";

export const rateLimitApiKey: NextMiddleware = async (req, res, next) => {
if (!req.query.apiKey) return res.status(401).json({ message: "No apiKey provided" });
Expand All @@ -9,6 +10,11 @@ export const rateLimitApiKey: NextMiddleware = async (req, res, next) => {
await checkRateLimitAndThrowError({
identifier: req.query.apiKey as string,
rateLimitingType: "api",
onRateLimiterResponse: (response) => {
res.setHeader("X-RateLimit-Limit", API_KEY_RATE_LIMIT);
res.setHeader("X-RateLimit-Remaining", response.remaining);
res.setHeader("X-RateLimit-Reset", response.reset);
},
});

await next();
Expand Down
6 changes: 5 additions & 1 deletion packages/lib/checkRateLimitAndThrowError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import { rateLimiter } from "./rateLimit";
export async function checkRateLimitAndThrowError({
rateLimitingType = "core",
identifier,
onRateLimiterResponse,
}: RateLimitHelper) {
const { remaining, reset } = await rateLimiter()({ rateLimitingType, identifier });
const response = await rateLimiter()({ rateLimitingType, identifier });
const { remaining, reset } = response;

if (onRateLimiterResponse) onRateLimiterResponse(response);

if (remaining < 1) {
const convertToSeconds = (ms: number) => Math.floor(ms / 1000);
Expand Down
9 changes: 8 additions & 1 deletion packages/lib/rateLimit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ const log = logger.getSubLogger({ prefix: ["RateLimit"] });
export type RateLimitHelper = {
rateLimitingType?: "core" | "forcedSlowMode" | "common" | "api" | "ai";
identifier: string;
/**
* 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;
};

export type RatelimitResponse = {
Expand Down Expand Up @@ -41,6 +46,8 @@ function logOnce(message: string) {
warningDisplayed = true;
}

export const API_KEY_RATE_LIMIT = 10;

export function rateLimiter() {
const UPSATCH_ENV_FOUND = process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN;

Expand Down Expand Up @@ -73,7 +80,7 @@ export function rateLimiter() {
redis,
analytics: true,
prefix: "ratelimit:api",
limiter: Ratelimit.fixedWindow(10, "60s"),
limiter: Ratelimit.fixedWindow(API_KEY_RATE_LIMIT, "60s"),
}),
ai: new Ratelimit({
redis,
Expand Down

0 comments on commit fdaa696

Please sign in to comment.