From f2c4034e48013da6f5f634963a83e0d791380cf6 Mon Sep 17 00:00:00 2001 From: dinesh Date: Sat, 30 May 2026 00:27:00 +0530 Subject: [PATCH 1/2] fix: prune stale timestamps from active memory buckets --- src/middleware.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/middleware.ts b/src/middleware.ts index 86870596c..da431c99d 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -80,9 +80,7 @@ function checkMemoryLimit( const active = (memoryBuckets.get(key) ?? []).filter( (timestamp) => timestamp > cutoff ); - const reset = Math.ceil( - ((active[0] ?? now) + WINDOW_SECONDS * 1000) / 1000 - ); + const reset = Math.ceil(((active[0] ?? now) + WINDOW_SECONDS * 1000) / 1000); if (active.length >= limit) { memoryBuckets.set(key, active); @@ -172,7 +170,7 @@ async function checkUpstashLimit( } const data = await response.json(); - + // Upstash REST eval response format: { result: [allowed_flag, current_count] } const [allowedFlag, currentCount] = data.result as [number, number]; const isAllowed = allowedFlag === 1; @@ -184,7 +182,10 @@ async function checkUpstashLimit( reset, }; } catch (error) { - console.error("Rate-limiter cloud pipeline failure, falling back to local memory storage:", error); + console.error( + "Rate-limiter cloud pipeline failure, falling back to local memory storage:", + error + ); return null; } } From 0be20e316d64988e51e102719230658ec992da15 Mon Sep 17 00:00:00 2001 From: dinesh Date: Sat, 30 May 2026 00:43:10 +0530 Subject: [PATCH 2/2] fix: prevent unlimited pending streak freeze stockpiling --- src/app/api/streak/freeze/route.ts | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/app/api/streak/freeze/route.ts b/src/app/api/streak/freeze/route.ts index 96a96881b..fabab960b 100644 --- a/src/app/api/streak/freeze/route.ts +++ b/src/app/api/streak/freeze/route.ts @@ -70,6 +70,22 @@ export async function POST() { const today = todayStr(); + // Prevent users from stockpiling unused freezes + const { count } = await supabaseAdmin + .from("streak_freezes") + .select("*", { count: "exact", head: true }) + .eq("user_id", user.id) + .gte("freeze_date", today); + + const MAX_PENDING_FREEZES = 1; + + if (count !== null && count >= MAX_PENDING_FREEZES) { + return Response.json( + { error: "You already have a pending freeze." }, + { status: 409 } + ); + } + const { data: existing } = await supabaseAdmin .from("streak_freezes") .select("id") @@ -79,7 +95,10 @@ export async function POST() { const { data: freeze, error } = await supabaseAdmin .from("streak_freezes") - .upsert({ user_id: user.id, freeze_date: today }, { onConflict: "user_id,freeze_date" }) + .upsert( + { user_id: user.id, freeze_date: today }, + { onConflict: "user_id,freeze_date" } + ) .select() .single(); @@ -89,7 +108,10 @@ export async function POST() { const alreadyExisted = existing !== null; - return Response.json({ freeze, already_existed: alreadyExisted }, { status: 201 }); + return Response.json( + { freeze, already_existed: alreadyExisted }, + { status: 201 } + ); } // DELETE /api/streak/freeze @@ -109,7 +131,8 @@ export async function DELETE() { .eq("user_id", user.id) .eq("freeze_date", todayStr()); - if (error) return Response.json({ error: "Failed to cancel freeze" }, { status: 500 }); + if (error) + return Response.json({ error: "Failed to cancel freeze" }, { status: 500 }); return Response.json({ success: true }); }