fix(cache): bust browser cache for JS and i18n files after updates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
30
app/main.py
30
app/main.py
@@ -90,16 +90,36 @@ STATIC_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "static")
|
|||||||
if os.path.isdir(STATIC_DIR):
|
if os.path.isdir(STATIC_DIR):
|
||||||
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
||||||
|
|
||||||
# Serve index.html at root
|
# Serve index.html at root — inject cache-busting version into static asset URLs
|
||||||
from fastapi.responses import FileResponse
|
# so the browser always loads fresh JS/CSS after a container update.
|
||||||
|
from fastapi.responses import FileResponse, HTMLResponse
|
||||||
|
from app.services import update_service
|
||||||
|
|
||||||
|
_STATIC_ASSETS = (
|
||||||
|
'"/static/js/app.js"',
|
||||||
|
'"/static/js/i18n.js"',
|
||||||
|
'"/static/css/styles.css"',
|
||||||
|
)
|
||||||
|
|
||||||
|
def _cache_bust_index(html: str, version: str) -> str:
|
||||||
|
# Inject version as a global JS variable so i18n.js can bust lang file caches too
|
||||||
|
html = html.replace("</head>", f'<script>window.STATIC_VERSION="{version}";</script>\n</head>', 1)
|
||||||
|
for asset in _STATIC_ASSETS:
|
||||||
|
busted = asset.rstrip('"') + f'?v={version}"'
|
||||||
|
html = html.replace(asset, busted)
|
||||||
|
return html
|
||||||
|
|
||||||
|
|
||||||
@app.get("/", include_in_schema=False)
|
@app.get("/", include_in_schema=False)
|
||||||
async def serve_index():
|
async def serve_index():
|
||||||
"""Serve the main dashboard."""
|
"""Serve the main dashboard with cache-busted static asset URLs."""
|
||||||
index_path = os.path.join(STATIC_DIR, "index.html")
|
index_path = os.path.join(STATIC_DIR, "index.html")
|
||||||
if os.path.isfile(index_path):
|
if not os.path.isfile(index_path):
|
||||||
return FileResponse(index_path)
|
|
||||||
return JSONResponse({"message": "NetBird MSP Appliance API is running."})
|
return JSONResponse({"message": "NetBird MSP Appliance API is running."})
|
||||||
|
version = update_service.get_current_version().get("commit", "unknown")
|
||||||
|
html = open(index_path, encoding="utf-8").read()
|
||||||
|
html = _cache_bust_index(html, version)
|
||||||
|
return HTMLResponse(content=html, headers={"Cache-Control": "no-cache"})
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ function detectLanguage() {
|
|||||||
async function loadLanguage(lang) {
|
async function loadLanguage(lang) {
|
||||||
if (translations[lang]) return;
|
if (translations[lang]) return;
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/static/lang/${lang}.json`);
|
const v = window.STATIC_VERSION ? `?v=${window.STATIC_VERSION}` : '';
|
||||||
|
const resp = await fetch(`/static/lang/${lang}.json${v}`);
|
||||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
||||||
translations[lang] = await resp.json();
|
translations[lang] = await resp.json();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user