Cache-bust style.css so a deploy doesn't leave visitors on a stale copy
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 19s

Browsers apply much more aggressive heuristic caching to a linked
stylesheet than to the dynamically-rendered HTML that references it, so a
CSS-only deploy could silently leave an already-open browser serving the
old styles until a hard refresh -- exactly what made today's dashboard
polish changes look broken while testing, until forcing a fresh fetch.
Appends a content-hash query param computed once at startup.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
dlawler489 2026-09-13 15:54:31 +10:00
parent 8423f01014
commit c8a9dea993
2 changed files with 21 additions and 1 deletions

View file

@ -1,5 +1,8 @@
from __future__ import annotations from __future__ import annotations
import hashlib
from pathlib import Path
from fastapi import APIRouter, Request from fastapi import APIRouter, Request
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
@ -9,6 +12,23 @@ router = APIRouter()
templates = Jinja2Templates(directory="app/web/templates") templates = Jinja2Templates(directory="app/web/templates")
def _asset_version(path: str) -> str:
"""A short hash of a static asset's content, used to cache-bust it in
templates -- browsers apply much more aggressive heuristic caching to
linked CSS/JS than to the dynamically-rendered HTML that references it,
so without this, a deploy that only changes style.css can leave a
visitor's browser silently serving the old stylesheet until they
hard-refresh. Computed once at startup since the file is baked into the
image and won't change while this process is running."""
try:
return hashlib.md5(Path(path).read_bytes()).hexdigest()[:8]
except FileNotFoundError:
return "0"
templates.env.globals["static_version"] = _asset_version("app/static/css/style.css")
@router.get("/") @router.get("/")
def dashboard(request: Request): def dashboard(request: Request):
return templates.TemplateResponse(request, "dashboard.html", {}) return templates.TemplateResponse(request, "dashboard.html", {})

View file

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Power Compare</title> <title>Power Compare</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css?v={{ static_version }}">
</head> </head>
<body> <body>
<nav class="topnav"> <nav class="topnav">