Load any page directly, not just the calendar (#164) #170

Merged
claude-bot merged 2 commits from fix/164-spa-fallback into main 2026-09-26 09:31:44 +00:00
Collaborator

Closes #164.

StaticFiles(html=True) returned index.html only for /. So a bookmark to /remote, a reload of /settings, or a deep link into the review queue all returned a 404. The installable app (#154) needs its start URL and shortcuts to load directly.

What

backend/app/spa.py adds SPAStaticFiles: on a 404 from the normal lookup, it retries with index.html and sets Cache-Control: no-cache so a new deploy's shell is picked up straight away. Two kinds of request still 404 for real:

  • Backend paths (/api/, /auth/, /metrics, /openapi.json), so an unknown API route stays a JSON 404 and doesn't turn into a 200 of HTML.
  • Missing assets (any path with a file extension, or under /assets/). A 200 of HTML for a missing .js file fails in the browser as a MIME error, which is much harder to diagnose than a 404.

This is a subclass rather than a catch-all route, because the mount is already registered last. Anything that reaches it has already failed every backend route, so no routing knowledge is duplicated.

Verification

  • Backend: pytest 923 passed (11 new, covering the cases above); ruff and mypy are clean.
  • E2E, run the CI way: 10/10 passed. The 2 new tests load /settings directly, then reload it. They use /settings rather than /remote because the E2E container has no controller configured, so /remote would be testing that gate, not the routing.

🤖 Generated with Claude Code

Closes #164. `StaticFiles(html=True)` returned `index.html` only for `/`. So a bookmark to `/remote`, a reload of `/settings`, or a deep link into the review queue all returned a 404. The installable app (#154) needs its start URL and shortcuts to load directly. ## What `backend/app/spa.py` adds `SPAStaticFiles`: on a 404 from the normal lookup, it retries with `index.html` and sets `Cache-Control: no-cache` so a new deploy's shell is picked up straight away. Two kinds of request **still 404 for real:** - **Backend paths** (`/api/`, `/auth/`, `/metrics`, `/openapi.json`), so an unknown API route stays a JSON 404 and doesn't turn into a 200 of HTML. - **Missing assets** (any path with a file extension, or under `/assets/`). A 200 of HTML for a missing `.js` file fails in the browser as a MIME error, which is much harder to diagnose than a 404. This is a subclass rather than a catch-all route, because the mount is already registered last. Anything that reaches it has already failed every backend route, so no routing knowledge is duplicated. ## Verification - **Backend:** pytest 923 passed (11 new, covering the cases above); ruff and mypy are clean. - **E2E, run the CI way:** 10/10 passed. The 2 new tests load `/settings` directly, then reload it. They use `/settings` rather than `/remote` because the E2E container has no controller configured, so `/remote` would be testing that gate, not the routing. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Name the installable-app issue the SPA fallback unblocks
All checks were successful
CI / Alembic migration check (pull_request) Successful in 41s
CI / Dockerfile lint (pull_request) Successful in 6s
CI / Pre-commit hooks (pull_request) Successful in 2m17s
CI / Python lint & type-check (pull_request) Successful in 2m34s
CI / Frontend lint, test & build (pull_request) Successful in 1m31s
CI / Python tests (pull_request) Successful in 4m53s
CI / Docker build, health smoke & E2E (pull_request) Successful in 2m9s
5f6f729c7f
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-bot deleted branch fix/164-spa-fallback 2026-09-26 09:31:46 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Iris-WLED!170
No description provided.