Repository navigation
Tell the user when their CORS proxy stops working (#86) - #119
Merged
Merged
Conversation
✅ Deploy Preview for savrlist ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
A capped Cloudflare worker (error 1027) is answered by the edge, not our code, so the response has no CORS headers: the browser reports a bare TypeError and the governor's 429 breaker never tripped. On a network-level failure of a proxied fetch, the governor now asks a proxy-health monitor to probe the configured prefix with an empty target, once with CORS and once with no-cors. Our worker answers that with a CORS 400, so "CORS fails, no-cors succeeds" means reachable-but-not-our-code and "both fail while online" means unreachable. A bad verdict trips the existing breaker (one trip per burst); probes are coalesced and throttled to one per minute; offline gives no verdict. The app shows what it observed — a non-blocking banner on the list and add screens, a status line under Preferences → CORS Proxy, and the raw probe evidence plus a per-device daily request count in Diagnostics. It clears when a request or probe succeeds, and resets when the proxy setting changes. No automatic switching, no reset-time guesses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Diagnose timeouts too: a hung proxy now reads as unreachable (a slow target behind a working proxy is cleared by the probe's quick 400). Deliberate aborts are still skipped. - Any readable response through the proxy (not just a 2xx) proves it works, so it clears the banner. - Count only probe requests actually sent (none while offline). - Reset proxy health when the setting changed, on blur/unmount rather than on every keystroke. - Plain-language status line in Diagnostics, not just the raw evidence. - SELF_HOST_PROXY_URL and the localStorage helper live in corsProxy.ts. - DEVELOPMENT.md: governor diagnosis + a Proxy health section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The public proxy now refuses unlisted calling sites (#118). The probe must still read as "running" there: an allowed caller gets the CORS 400, a refused one a 404 that still carries CORS headers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A target site's own 429 comes back through the worker unchanged, so a
429 alone doesn't say the proxy is rate-limiting. The governor now asks
`diagnoseProxy` on a 429 too (renamed from diagnoseNetworkFailure): the
probe getting 429 itself means the proxy is rate-limiting (breaker +
banner); its usual 400 means the site was, so only that URL backs off.
Without diagnoseProxy every 429 still trips the breaker. The probe
shares the existing once-a-minute throttle.
While the proxy is known to be failing, a failed save or refetch says
so ("Couldn't save: your CORS proxy isn't working…") instead of offering
the "report this article" dialog, and on desktop the banner notes that
extension saves need no proxy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main gained English/Spanish i18n while this branch was open. The banner, Preferences/Diagnostics status lines, the "Couldn't save/refetch" snackbars and the Diagnostics panel heading now go through i18next: shared strings under common.proxyHealth, the panel text in diagnostics. proxyProblemMessage/proxyProblemSummary take the app's `t`, so proxyHealth.ts stays free of i18n setup; their tests pin the issue's exact English copy and check the Spanish. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jonocodes
force-pushed
the
feature/implement-86
branch
from
October 16, 2026 18:08
fd89bb4 to
6e8b1a9
Compare
jonocodes
added a commit
that referenced
this pull request
Oct 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #86.
Problem
When the CORS proxy is maxed out (Cloudflare free plan, error 1027), saves and images fail with no explanation. The 1027 page comes from Cloudflare's edge, not our worker, so it has no CORS headers. The browser then reports a bare
TypeErrorwith no status, and the governor's 429 breaker never tripped.What this does
Detection (
src/utils/net/proxyHealth.ts, pure and unit-tested):no-cors. Our worker answers that probe with a CORS400, for public, path-token and query-token prefixes, and also behind the Restrict the public CORS proxy to allowlisted calling sites #118 origin allowlist.no-corsrequest gets an answerUX: says only what was observed, with the issue's exact copy and no reset times.
A non-blocking banner on the article list and the add screen. It links to Preferences → CORS Proxy and, on the shared default proxy, to "Set up your own proxy".
On desktop, the banner also notes that saving with the browser extension needs no proxy.
While the proxy is failing, a failed save or refetch says so ("Couldn't save: your CORS proxy isn't working. See the notice above.") instead of offering "Report this failed article?". The article isn't at fault, so a report would only be noise.
A status line in Preferences and in Diagnostics.
Diagnostics also shows the raw probe evidence and a per-device "proxy requests today" count. The proxy token stays masked.
The banner clears as soon as any response comes back through the proxy or a probe succeeds. Changing the proxy setting resets it.
All new copy is translated (English and Spanish) via i18next, under
common.proxyHealthanddiagnostics.*.There is no automatic switching or fallback, as the issue's non-goal requires.
Tests
Unit:
resetBreaker.Worker: the empty-target probe gets a CORS 400 for every prefix shape, and behind the origin allowlist.
Playwright (
tests/e2e/proxy-health.spec.ts):route.fulfill()addsAccess-Control-Allow-Originby itself, so the capped-edge case uses a real local server that answers429 error code: 1027with no CORS headers.Results (after rebasing onto
mainwith i18n): Jest 472 passed,node --testfor the worker/stats 63 passed,tscclean. Full e2e: 100 passed and 2 failed. The two failures are multi-browser/natural sync specs that configure no proxy, so they never reach this code, and they passed when re-run alone (4/4). They've been flaky under full-suite load on this branch before.Still open
🤖 Generated with Claude Code
Screenshots
Capped proxy (stand-in 1027 answer, no CORS headers): banner on the article list, and the failed save names the proxy instead of offering a failure report
The same observation as a status line under Preferences → CORS Proxy