mirror of
https://github.com/formbricks/formbricks.git
synced 2026-03-07 23:12:09 -06:00
Co-authored-by: Johannes <johannes@formbricks.com> Co-authored-by: Matti Nannt <mail@matthiasnannt.com>
50 lines
1.4 KiB
TypeScript
50 lines
1.4 KiB
TypeScript
import { LRUCache } from "lru-cache";
|
|
import { REDIS_HTTP_URL } from "@formbricks/lib/constants";
|
|
|
|
type Options = {
|
|
interval: number;
|
|
allowedPerInterval: number;
|
|
};
|
|
|
|
const inMemoryRateLimiter = (options: Options) => {
|
|
const tokenCache = new LRUCache<string, number>({
|
|
max: 1000,
|
|
ttl: options.interval * 1000, // converts to expected input of milliseconds
|
|
});
|
|
|
|
return async (token: string) => {
|
|
const currentUsage = tokenCache.get(token) || 0;
|
|
if (currentUsage >= options.allowedPerInterval) {
|
|
throw new Error("Rate limit exceeded");
|
|
}
|
|
tokenCache.set(token, currentUsage + 1);
|
|
};
|
|
};
|
|
|
|
const redisRateLimiter = (options: Options) => async (token: string) => {
|
|
try {
|
|
const tokenCountResponse = await fetch(`${REDIS_HTTP_URL}/INCR/${token}`);
|
|
if (!tokenCountResponse.ok) {
|
|
console.error("Failed to increment token count in Redis", tokenCountResponse);
|
|
return;
|
|
}
|
|
|
|
const { INCR } = await tokenCountResponse.json();
|
|
if (INCR === 1) {
|
|
await fetch(`${REDIS_HTTP_URL}/EXPIRE/${token}/${options.interval}`);
|
|
} else if (INCR > options.allowedPerInterval) {
|
|
throw new Error();
|
|
}
|
|
} catch (e) {
|
|
throw new Error("Rate limit exceeded for IP: " + token);
|
|
}
|
|
};
|
|
|
|
export const rateLimit = (options: Options) => {
|
|
if (REDIS_HTTP_URL) {
|
|
return redisRateLimiter(options);
|
|
} else {
|
|
return inMemoryRateLimiter(options);
|
|
}
|
|
};
|