feat(static-assets): cache-bust JS/CSS via ?v=<commit-sha>, immutable in prod
All checks were successful
All checks were successful
Adds a `static_v(request, name, path=...)` Jinja helper that appends
?v=<commit-sha> from app.core.build_info, plus a CachedStaticFiles
subclass that serves Cache-Control: public, max-age=31536000, immutable
in production and no-cache in development. Browsers refetch JS/CSS
automatically on every deploy without the user having to hard-reload.
- New: app/core/static_files.py (CachedStaticFiles)
- Updated: app/templates_config.py (static_v helper)
- Updated: main.py (use CachedStaticFiles for *_static mounts)
- Codemod: 143 url_for('*_static', path='*.js'|'*.css') → static_v(...)
across 123 templates. Images/fonts/JSON locales intentionally
unchanged (out of scope).
- Arch rule: FE-024 (warning) flags raw url_for on JS/CSS to prevent
drift. Note: FE-008 was already taken by the number_stepper rule.
- docs/proposals/static-asset-cache-busting.md marked Done.
Closes plan from docs/proposals/static-asset-cache-busting.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
5
main.py
5
main.py
@@ -40,6 +40,7 @@ from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
|
||||
# ConnectionRefusedError at runtime. The isort: split below stops ruff from
|
||||
# alphabetically reordering this back behind api_router.
|
||||
from app.core import celery_config # noqa: F401 # side-effect: set_default()
|
||||
from app.core.static_files import CachedStaticFiles
|
||||
|
||||
# isort: split
|
||||
from app.api.main import api_router
|
||||
@@ -235,12 +236,12 @@ if MODULES_DIR.exists():
|
||||
module_static = module_dir / "static"
|
||||
if module_static.exists():
|
||||
mount_path = f"/static/modules/{module_name}"
|
||||
app.mount(mount_path, StaticFiles(directory=str(module_static)), name=f"{module_name}_static")
|
||||
app.mount(mount_path, CachedStaticFiles(directory=str(module_static)), name=f"{module_name}_static")
|
||||
logger.info(f"Mounted module static files: {mount_path} -> {module_static}")
|
||||
|
||||
# Mount main static directory AFTER module statics
|
||||
if STATIC_DIR.exists():
|
||||
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
||||
app.mount("/static", CachedStaticFiles(directory=str(STATIC_DIR)), name="static")
|
||||
logger.info(f"Mounted static files from: {STATIC_DIR}")
|
||||
else:
|
||||
logger.warning(f"Static directory not found at {STATIC_DIR}")
|
||||
|
||||
Reference in New Issue
Block a user