48 lines
1.1 KiB
TypeScript
48 lines
1.1 KiB
TypeScript
import { createClient } from "redis";
|
|
|
|
import { isProductionEnvironment } from "@/lib/constants";
|
|
import { ChatbotError } from "@/lib/errors";
|
|
|
|
const MAX_MESSAGES = 10;
|
|
const TTL_SECONDS = 60 * 60;
|
|
|
|
let client: ReturnType<typeof createClient> | null = null;
|
|
|
|
function getClient() {
|
|
if (!client && process.env.REDIS_URL) {
|
|
client = createClient({ url: process.env.REDIS_URL });
|
|
client.on("error", () => undefined);
|
|
client.connect().catch(() => {
|
|
client = null;
|
|
});
|
|
}
|
|
return client;
|
|
}
|
|
|
|
export async function checkIpRateLimit(ip: string | undefined) {
|
|
if (!isProductionEnvironment || !ip) {
|
|
return;
|
|
}
|
|
|
|
const redis = getClient();
|
|
if (!redis?.isReady) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const key = `ip-rate-limit:${ip}`;
|
|
const [count] = await redis
|
|
.multi()
|
|
.incr(key)
|
|
.expire(key, TTL_SECONDS, "NX")
|
|
.exec();
|
|
|
|
if (typeof count === "number" && count > MAX_MESSAGES) {
|
|
throw new ChatbotError("rate_limit:chat");
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof ChatbotError) {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|