From 12ffc5cb2acf02a58f860cd8005dc6a5c6f7eba8 Mon Sep 17 00:00:00 2001 From: Alzer Del Zeyger <75466035+Dx-Alz-xD@users.noreply.github.com> Date: Wed, 11 Nov 2026 19:32:01 +0530 Subject: [PATCH] fix(ratelimit): run without upstash in development without the upstash variables redis was an empty object, so upload, request, subscribe and report-tag all failed with a 500. skip rate limiting in development when upstash is missing, and answer 503 with a clear message in production --- src/lib/utils/rate-limiter.ts | 7 +++++++ src/lib/utils/ratelimit.ts | 26 +++++++++++--------------- src/lib/utils/redis.ts | 11 ++++------- src/middleware.ts | 7 +++++++ 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/lib/utils/rate-limiter.ts b/src/lib/utils/rate-limiter.ts index bef77232..0d3f260a 100644 --- a/src/lib/utils/rate-limiter.ts +++ b/src/lib/utils/rate-limiter.ts @@ -15,6 +15,13 @@ function getClientIp(req: Request & { ip?: string}): string { } export async function rateLimitCheck(req: Request & { ip?: string }, paperId: string) { + if (!redis) { + if (process.env.NODE_ENV === "production") { + throw new CustomError("rate limiting is not configured", 503); + } + return; + } + const ratelimit = new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(3, "1 h"),//per id - 3 request - per hour diff --git a/src/lib/utils/ratelimit.ts b/src/lib/utils/ratelimit.ts index 55542a9e..183daf8a 100644 --- a/src/lib/utils/ratelimit.ts +++ b/src/lib/utils/ratelimit.ts @@ -1,20 +1,16 @@ import { Ratelimit } from "@upstash/ratelimit"; import { redis } from "./redis"; -// Rate limiter for AI Upload (5 requests per 15 minutes) -export const aiUploadRatelimit = new Ratelimit({ - redis: redis, - limiter: Ratelimit.slidingWindow(5, "900 s"), -}); +type Window = Parameters[1]; -// Rate limiter for Paper Requests (5 requests per 15 minutes) -export const paperRequestRatelimit = new Ratelimit({ - redis: redis, - limiter: Ratelimit.slidingWindow(5, "900 s"), -}); +function allow(requests: number, window: Window) { + return redis + ? new Ratelimit({ redis, limiter: Ratelimit.slidingWindow(requests, window) }) + : null; +} -// Rate limiter for Subscriptions (3 requests per hour) -export const subscribeRatelimit = new Ratelimit({ - redis: redis, - limiter: Ratelimit.slidingWindow(3, "1 h"), -}); +export const aiUploadRatelimit = allow(5, "900 s"); + +export const paperRequestRatelimit = allow(5, "900 s"); + +export const subscribeRatelimit = allow(3, "1 h"); diff --git a/src/lib/utils/redis.ts b/src/lib/utils/redis.ts index 962a3181..2e7d5d26 100644 --- a/src/lib/utils/redis.ts +++ b/src/lib/utils/redis.ts @@ -1,9 +1,6 @@ import { Redis } from "@upstash/redis"; -export const redis = - process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN - ? new Redis({ - url: process.env.UPSTASH_REDIS_REST_URL, - token: process.env.UPSTASH_REDIS_REST_TOKEN, - }) - : ({} as Redis); +const url = process.env.UPSTASH_REDIS_REST_URL; +const token = process.env.UPSTASH_REDIS_REST_TOKEN; + +export const redis = url && token ? new Redis({ url, token }) : null; diff --git a/src/middleware.ts b/src/middleware.ts index 42cd0362..5bf8cdb4 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -11,6 +11,13 @@ export const config = { }; export default async function middleware(request: NextRequest) { + if (!aiUploadRatelimit || !paperRequestRatelimit || !subscribeRatelimit) { + if (process.env.NODE_ENV === "production") { + return failure("rate limiting is not configured", 503); + } + return NextResponse.next(); + } + const ip = request.ip ?? "127.0.0.1"; const { pathname } = request.nextUrl;