Addresses #365 (reported by JohnBaumb), parts 1 & 2 of the proposal.
Service worker, client-side IDB/sessionStorage, and PWA manifest
deferred to follow-up PRs.
1. Static asset cache (CSS/JS/icons/fonts).
`SEND_FILE_MAX_AGE_DEFAULT` flipped from 0 to 31536000 (1 year) in
production. Safe because every static URL is bust-tagged with
`?v=static_v` (computed once per process start), so each server
restart effectively invalidates every cached asset for every user.
Within a single deploy, repeat page loads hit zero round-trips on
static files — was a 304 round-trip per asset before.
Dev override (`SOULSYNC_WEB_DEV_NO_CACHE=1`) keeps it at 0 so
iterating on JS/CSS doesn't need a server restart between edits.
Collateral fixes from the bump:
- Music streaming endpoint (L16140): `response.headers.add('Cache-Control',
'no-cache')` → bracket-assign. Under the old max-age=0, send_file
set `no-cache` and `.add()` duplicated harmlessly. Under the new
max-age=31536000, `.add()` would APPEND a second Cache-Control
value → two conflicting headers, browser-undefined behavior.
Bracket-assign replaces.
- Backup download endpoint (L25181): explicit `Cache-Control:
no-store` on the response so DB backups don't inherit the new
long max-age — sensitive content, must never cache.
2. Discover GET browser cache (5 min).
New `@app.after_request` hook scoped to `/api/discover/` and
`/api/discovery/` paths, GET method, 2xx responses only. Sets
`Cache-Control: public, max-age=300`. Skipped when the endpoint
already set its own Cache-Control. Toggling between Discover
sections within 5 min serves from browser cache, no backend hit.
Try/except wraps the hook body and logs a warning if anything
throws — never let a header-tagging bug turn a successful response
into a 500. (Logging instead of `pass` since silent except-pass is
exactly the anti-pattern issue #369 is about.)
Audited every other Cache-Control set site in web_server.py — only
the two `send_file` callers needed adjustment. Range-branch streaming
uses `Response()` directly, unaffected by the config change.
603 tests pass.