Add cache-busting version query to static assets

Cloudflare was caching /static/style.css and /static/app.js at the
edge for up to 4 hours (its default Browser Cache TTL, since the app
sets no explicit Cache-Control), so deploys could look like they
hadn't landed even though the origin was already up to date. A
content hash of style.css/app.js/htmx.min.js, computed once at
startup, is now appended as ?v=<hash> to their URLs in base.html, so
every deploy that changes those files produces new, never-cached
URLs and needs no manual cache purge.

Also commits the live-sync-updates design spec, which was written
but never staged earlier in the session.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bastian Wagner
2026-08-16 13:51:28 +02:00
parent 450e4e935f
commit 8a23147dc8
4 changed files with 219 additions and 3 deletions

View File

@@ -1,3 +1,4 @@
import hashlib
from datetime import timedelta
from pathlib import Path
@@ -13,11 +14,27 @@ from app.security.credentials import CredentialCipher
from app.web.forms import UserFormData
DASHBOARD_SUMMARY_WINDOW = timedelta(days=7)
CACHE_BUSTED_STATIC_FILES = ("style.css", "app.js", "htmx.min.js")
router = APIRouter()
templates = Jinja2Templates(directory=str(Path(__file__).resolve().parent / "templates"))
def _compute_static_version(static_dir: Path) -> str:
hasher = hashlib.sha256()
for name in CACHE_BUSTED_STATIC_FILES:
try:
hasher.update((static_dir / name).read_bytes())
except FileNotFoundError:
continue
return hasher.hexdigest()[:10]
templates.env.globals["static_version"] = _compute_static_version(
Path(__file__).resolve().parent / "static"
)
def _next_sync_tick(request: Request):
scheduler = getattr(request.app.state, "scheduler", None)
return getattr(scheduler, "next_tick", None)