fix(pwa): BUG-005 chargement mobile via Cloudflare (SW network-first + no-cache)
CI / lint (push) Successful in 58s
CI / security (push) Successful in 40s
CI / test (push) Successful in 1m19s
CI / build (push) Successful in 37s
CI / e2e (push) Successful in 10m53s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s

This commit is contained in:
2026-09-11 17:13:16 -04:00
parent 4c4e415975
commit 7cf7d33b6d
10 changed files with 382 additions and 153 deletions
+11 -7
View File
@@ -628,9 +628,12 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
"worker-src 'self' blob:; "
"frame-src 'self' blob:;"
)
# Cache static assets aggressively (fingerprinted by PWA manifest hash)
# Static assets are NOT content-hashed, so they must revalidate:
# ``immutable``/long max-age made Cloudflare and mobile browsers serve
# a stale build for a year (the service worker cache compounded it).
# ``no-cache`` keeps caching but forces revalidation (ETag/Last-Modified).
if request.url.path.startswith("/static/"):
response.headers["Cache-Control"] = "public, max-age=31536000, immutable"
response.headers["Cache-Control"] = "no-cache"
return response
@@ -4904,6 +4907,7 @@ async def api_conflict_resolve(body: dict = Body(...), current_user=Depends(requ
# ---------------------------------------------------------------------------
if FRONTEND_DIR.exists():
# ``Cache-Control`` for /static is set by SecurityHeadersMiddleware (no-cache).
app.mount("/static", StaticFiles(directory=str(FRONTEND_DIR)), name="static")
@app.get("/sw.js")
@@ -4929,7 +4933,7 @@ if FRONTEND_DIR.exists():
return FileResponse(
manifest_file,
media_type="application/manifest+json",
headers={"Cache-Control": "public, max-age=3600"}
headers={"Cache-Control": "no-cache"}
)
raise HTTPException(status_code=404, detail="Manifest not found")
@@ -4938,7 +4942,7 @@ if FRONTEND_DIR.exists():
"""Serve the minimalist popout page for a specific file."""
popout_file = FRONTEND_DIR / "popout.html"
if popout_file.exists():
return HTMLResponse(content=popout_file.read_text(encoding="utf-8"))
return HTMLResponse(content=popout_file.read_text(encoding="utf-8"), headers={"Cache-Control": "no-cache"})
raise HTTPException(status_code=404, detail="Popout template not found")
@app.get("/editor-poc")
@@ -4946,7 +4950,7 @@ if FRONTEND_DIR.exists():
"""Serve the standalone Editor POC page (multi-zone toolbar demo)."""
poc_file = FRONTEND_DIR / "editor-poc.html"
if poc_file.exists():
return HTMLResponse(content=poc_file.read_text(encoding="utf-8"))
return HTMLResponse(content=poc_file.read_text(encoding="utf-8"), headers={"Cache-Control": "no-cache"})
raise HTTPException(status_code=404, detail="Editor POC not found")
@app.get("/admin.html", response_class=HTMLResponse)
@@ -4959,7 +4963,7 @@ if FRONTEND_DIR.exists():
"""
admin_file = FRONTEND_DIR / "admin.html"
if admin_file.exists():
return HTMLResponse(content=admin_file.read_text(encoding="utf-8"))
return HTMLResponse(content=admin_file.read_text(encoding="utf-8"), headers={"Cache-Control": "no-cache"})
raise HTTPException(status_code=404, detail="Admin page not found")
@app.get("/{full_path:path}")
@@ -4967,5 +4971,5 @@ if FRONTEND_DIR.exists():
"""Serve the SPA index.html for all non-API routes."""
index_file = FRONTEND_DIR / "index.html"
if index_file.exists():
return HTMLResponse(content=index_file.read_text(encoding="utf-8"))
return HTMLResponse(content=index_file.read_text(encoding="utf-8"), headers={"Cache-Control": "no-cache"})
raise HTTPException(status_code=404, detail="Frontend not found")