Add CORS middleware — frontend at pal-e-production hostname cannot fetch API #256
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/pal-e-api#256
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Bug
Lineage
Standalone — discovered during 2026-04-11 session while creating target-architecture and user-story notes for the new
westside-emailsproject. Tried to view them atpal-e-production.tail5b443a.ts.net/notes/<slug>and observed "failed to fetch" in the browser. Root-caused to missing CORS middleware.Repo
forgejo_admin/pal-e-apiWhat Broke
Every note detail, board, and list page on
pal-e-production.tail5b443a.ts.netfails client-side with "failed to fetch." The nav renders, then everygetNote,getNoteBlocks,getNoteToc,getNoteLinks,listNotescall from the browser is blocked by same-origin policy and returns no data.The API itself is healthy —
curltopal-e-docs.tail5b443a.ts.net/notes/<slug>returns correct JSON with200 OK. The problem is that the response has zero CORS headers, so browsers discard it on cross-origin requests frompal-e-production.tail5b443a.ts.net.Root cause:
src/pal_e_docs/main.pyinstantiatesFastAPI()withoutCORSMiddleware.git log -S "CORSMiddleware"returns zero commits — the middleware has never existed in this repo. The bug was hidden while the frontend was Jinja2 server-rendered in-process or reverse-proxied under the same hostname. It surfaced ~7 days ago when thepal-e-productioningress was created as a separate hostname frompal-e-docs.Repro Steps
/notes/<slug>XHR has noAccess-Control-Allow-Originheader and is blocked.HTTP/2 200, JSON body, noaccess-control-allow-originheader anywhere in the response headers.git log -S "CORSMiddleware"inpal-e-apireturns zero results.Expected Behavior
API responses to cross-origin browser requests from
pal-e-production.tail5b443a.ts.net(andhttp://localhost:5173for local Vite dev) must include a matchingAccess-Control-Allow-Originheader so the browser permits the fetch. PreflightOPTIONSrequests must return200/204withAccess-Control-Allow-MethodsincludingGET, POST, PATCH, PUT, DELETE.Environment
pal-e-docs(API) andpal-e-production(frontend)pal-e-docs.tail5b443a.ts.net(46d old, uvicorn, FastAPI)pal-e-production.tail5b443a.ts.net(7d old, nginx serving SvelteKit SPA, last built 2026-03-28)forgejo_admin/pal-e-api(renamed frompal-e-docson 2026-03-27 per issue #217)pal-e-docs-app(renamed frompal-e-app, checked out locally at~/pal-e-app)VITE_PAL_E_DOCS_API_URLdefaults tohttps://pal-e-docs.tail5b443a.ts.netAcceptance Criteria
CORSMiddlewareadded tosrc/pal_e_docs/main.pybefore the router includes.allow_originslist is configurable via an env var (e.g.PAL_E_DOCS_CORS_ORIGINS, comma-separated) so non-prod deployments can add origins without a code change.https://pal-e-production.tail5b443a.ts.netandhttp://localhost:5173.allow_methodsincludesGET, POST, PATCH, PUT, DELETE, OPTIONS.allow_headers="*".allow_credentials=Trueonly if the frontend sends credentialed requests (confirm againstsrc/lib/api-client.tswhich attaches the Keycloak Bearer token).Origin: https://pal-e-production.tail5b443a.ts.netreturnsaccess-control-allow-origin: https://pal-e-production.tail5b443a.ts.netin the response headers.OPTIONSpreflight withOrigin: https://pal-e-production.tail5b443a.ts.netandAccess-Control-Request-Method: GETreturns200/204withaccess-control-allow-methodsincludingGET.tests/asserting CORS headers on a sample endpoint whenOriginis set.forgejo_admin/pal-e-api) — no Tofu/kustomize change needed.Related
arch-westside-emails— the note that surfaced the bug when it couldn't be renderedpal-e-docs— pal-e-docs project, ownsboard-pal-e-docswhere this ticket livesforgejo_admin/pal-e-api #217— the rename frompal-e-docstopal-e-apiforgejo_admin/pal-e-platform#278— downstream dependency: hostname swap structural ticket. When the swap lands, the new frontend hostnamepal-e-docs.tail5b443a.ts.netmust be added to thePAL_E_DOCS_CORS_ORIGINSenv var. The env-var-driven design in this ticket makes that a config update, not a code change.src/pal_e_docs/main.py:49— the line whereCORSMiddlewaremust be added~/pal-e-app/src/lib/api-client.ts:16— frontend origin of failing fetchesarch:notes-apitraceability gap — no backing arch note exists for the API-routes layer; follow-up ticket TBD (accepted as known debt for this ticket)Scope Review: NEEDS_REFINEMENT
Review note:
review-971-2026-04-11Scope is sound. File target, line reference, git-log claim, and tests/ infra all verified. Fits in a single agent pass (<5 min). Two minor traceability gaps noted:
[BODY]Add #972 /forgejo_admin/pal-e-platform#278(hostname swap) to Related — this ticket is a blocker for that design decision.[SCOPE]Noarch-notes-apibacking note exists.arch-domain-pal-e-docsonly documents the entity/table layer, not the FastAPI router layer. Non-blocking for fix — file as a separate backlog item.Neither recommendation blocks dev dispatch. Scope and fix plan are ready.