mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
fix(api): dress only the 429s a limiter actually refused
The request-scoped capture kept the last verdict of any kind and fell back to it when nothing had refused. That fallback fires in exactly one state — no limiter refused this request — which is the definition of "this 429 came from somewhere else". So a daily AI budget that is spent until midnight, an hourly generation quota, and a provider-side 429 relayed onward all went out carrying our own bucket's numbers: a remainder that says there is room left and a Retry-After measured against an unrelated window. On the AI paths the captured window can also expire while the provider call runs, so the dressed delay could land at zero and tell the client to retry immediately into the ceiling that had just refused it. The cell now keeps refusals only. A request every bucket let through reports nothing, and a 429 from another cause leaves undressed, which is correct: there is no bucket to describe. The two-limiter rule is unchanged — the last refusal wins, and a refusal still outranks a pass. The unit case that pinned the fallback is turned round, and both directions now run through the real wrapper: a limiter refusal gets the headers, a 429 raised without one gets none. The integration file gains the two shapes it was missing — a route that consults two buckets must report the one that refused, and a route whose 429 comes from the daily budget must carry no rate-limit headers at all.
This commit is contained in:
1 parent
21dd7c7e48
commit
dd3844a7a2
5 files changed
+305
-23
No files matched your search
@@ -380,6 +380,11 @@ export function apiHandler<T extends (...args: any[]) => Promise<Response>>(
|
||||
* `Retry-After` is the presence test rather than a flag: a handler that built
|
||||
* its own headers already carries it (they come from the same builder), so
|
||||
* this leaves that response exactly as it was written.
|
||||
*
|
||||
* The cell holds refusals only, so a 429 no limiter produced — a spent daily
|
||||
* AI budget, an hourly generation quota, a provider-side 429 relayed onward —
|
||||
* reads null here and goes out undressed. There is no bucket to describe, and
|
||||
* one that still has room would name a delay against the wrong window.
|
||||
*/
|
||||
function attachRateLimitHeaders(response: NextResponse): void {
|
||||
if (response.status !== 429) return;
|
||||
|
||||
Reference in new issue
Block a user