Cache-busting : assets versionnés (?v=model_version) + pages HTML no-cache
Le navigateur gardait l'ancien CSS/JS (aucun Cache-Control). Les pages sont maintenant servies no-cache avec les URLs d'assets versionnées à la volée — chaque release devient visible immédiatement. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 changed file +26 −1
modified
backend/app/main.py
+26 −1
@@ -87,12 +87,37 @@ def health(): | |||||
| 87 | PAGES = ["index", "aujourdhui", "carte", "circonscriptions", "sondages", | 87 | PAGES = ["index", "aujourdhui", "carte", "circonscriptions", "sondages", | ||
| 88 | "signaux", "simulateur", "intelligence", "methodologie", "backtest", | 88 | "signaux", "simulateur", "intelligence", "methodologie", "backtest", | ||
| 89 | "admin"] | 89 | "admin"] | ||
| 90 | + | ||||
| 91 | +# Cache-busting : la version du modèle est injectée dans les URLs d'assets | ||||
| 92 | +# (style.css?v=…, app.js?v=…) et les pages HTML sont servies en no-cache — | ||||
| 93 | +# chaque release est visible immédiatement, sans vider le cache du navigateur. | ||||
| 94 | +_page_cache: dict[str, tuple[float, str]] = {} | ||||
| 95 | + | ||||
| 96 | + | ||||
| 97 | +def _render_page(name: str) -> str: | ||||
| 98 | + f = FRONTEND_DIR / f"{name}.html" | ||||
| 99 | + mtime = f.stat().st_mtime | ||||
| 100 | + cached = _page_cache.get(name) | ||||
| 101 | + if cached and cached[0] == mtime: | ||||
| 102 | + return cached[1] | ||||
| 103 | + html = f.read_text() | ||||
| 104 | + v = settings.model_version | ||||
| 105 | + html = html.replace('href="/assets/style.css"', | ||||
| 106 | + f'href="/assets/style.css?v={v}"') | ||||
| 107 | + html = html.replace('src="/assets/app.js"', | ||||
| 108 | + f'src="/assets/app.js?v={v}"') | ||||
| 109 | + _page_cache[name] = (mtime, html) | ||||
| 110 | + return html | ||||
| 111 | + | ||||
| 112 | + | ||||
| 90 | for page in PAGES: | 113 | for page in PAGES: | ||
| 91 | path = "/" if page == "index" else f"/{page}" | 114 | path = "/" if page == "index" else f"/{page}" | ||
| 92 | 115 | ||||
| 93 | def make_handler(p=page): | 116 | def make_handler(p=page): | ||
| 94 | def handler(): | 117 | def handler(): | ||
| 95 | − return FileResponse(FRONTEND_DIR / f"{p}.html") | 118 | + from fastapi.responses import HTMLResponse | ||
| 119 | + return HTMLResponse(_render_page(p), | ||||
| 120 | + headers={"Cache-Control": "no-cache"}) | ||||
| 96 | return handler | 121 | return handler | ||
| 97 | app.get(path, include_in_schema=False)(make_handler()) | 122 | app.get(path, include_in_schema=False)(make_handler()) | ||
| 98 | 123 | ||||
| 99 | 124 | ||||