mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(api): put Retry-After and the rate-limit triple on every 429
A refused client had nothing to back off against. `rateLimitHeaders()` emitted `X-RateLimit-Remaining` and an ISO `X-RateLimit-Reset`, no `Retry-After` and no cap, and only about 70 of the 199 route files that consult a limiter attached even those two. The rest answered with a bare prose envelope, including every endpoint an offline replay queue hits: the four batch ingests, `/api/sync/changes` and `/api/auth/refresh`. A first-run Apple Health import trips the 60-per-minute batch ceiling reliably, and the only strategy left to the client was a blind backoff. Threading headers through 129 handlers is 129 edits the next new route forgets, so the verdict is carried instead: `checkRateLimit` records it in a request-scoped cell and `apiHandler` dresses any 429 that leaves without headers. A route that already built its own is left alone — `Retry-After` is the presence test, and it comes from the same builder. `Retry-After` is whole seconds rounding UP (RFC 9110 §10.2.3), floored at zero. `X-RateLimit-Limit` is new and needs the cap, so the limiter result carries it. `X-RateLimit-Reset` keeps its ISO form: it is the wrong convention for the name, but it is on the wire today and changing it is a decision of its own. The cell lives in its own leaf module because 177 test files replace `@/lib/rate-limit` wholesale with a mock factory, and `apiHandler` must not depend on anything those factories stand in for.
This commit is contained in:
1 parent
1d372df3a0
commit
08100b6207
56 files changed
+681
-161
No files matched your search
@@ -101,7 +101,10 @@ paths:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorEnvelope"
|
||||
"429": &a5
|
||||
description: Rate limit exceeded.
|
||||
description: Rate limit exceeded. The response carries `Retry-After` (whole seconds, rounded up — wait at least that
|
||||
long before retrying), `X-RateLimit-Limit` (the bucket's cap), `X-RateLimit-Remaining` and
|
||||
`X-RateLimit-Reset` (the reset instant, ISO-8601, not epoch seconds). Back off on `Retry-After` rather than
|
||||
guessing.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
|
||||
Reference in new issue
Block a user