Add cache-busted static asset URLs

This commit is contained in:
Abel Luck 2026-03-31 10:37:33 +02:00
parent 99fd33f770
commit df68aa95e9
2 changed files with 78 additions and 1 deletions

View file

@ -90,6 +90,9 @@ DEFAULT_PANGEA_CONTENT_FORMAT = "MOBILE_3"
DEFAULT_PANGEA_CONTENT_TYPE = "articles"
DEFAULT_PANGEA_MAX_ARTICLES = "10"
DEFAULT_PANGEA_OLDEST_ARTICLE = "3"
STATIC_DIR = Path(__file__).resolve().parent / "static"
CACHE_BUSTED_STATIC_ASSETS = frozenset({"app.css"})
CACHE_BUSTED_HASH_LENGTH = 12
def _render_shim_page(
@ -106,6 +109,24 @@ def _render_shim_page(
return body, etag
def versioned_static_asset_filename(filename: str) -> str:
_require_cache_busted_static_asset(filename)
asset_path = STATIC_DIR / filename
truncated_hash = hashlib.sha256(asset_path.read_bytes()).hexdigest()[
:CACHE_BUSTED_HASH_LENGTH
]
return f"{asset_path.stem}-{truncated_hash}{asset_path.suffix}"
def versioned_static_asset_href(filename: str) -> str:
return f"/static/{versioned_static_asset_filename(filename)}"
def _require_cache_busted_static_asset(filename: str) -> None:
if filename not in CACHE_BUSTED_STATIC_ASSETS:
raise ValueError(f"Unsupported cache-busted static asset: {filename}")
def create_app(*, dev_mode: bool = False) -> Quart:
app = Quart(__name__)
app.config["REPUB_DB_PATH"] = str(initialize_database())
@ -127,6 +148,22 @@ def create_app(*, dev_mode: bool = False) -> Quart:
response.mimetype = "application/rss+xml"
return response
@app.get("/static/<string:asset_name>-<string:asset_hash>.<string:extension>")
async def versioned_static_asset(
asset_name: str, asset_hash: str, extension: str
) -> Response:
logical_filename = f"{asset_name}.{extension}"
requested_filename = f"{asset_name}-{asset_hash}.{extension}"
if logical_filename in CACHE_BUSTED_STATIC_ASSETS:
response = await send_from_directory(str(STATIC_DIR), logical_filename)
response.cache_control.public = True
response.cache_control.max_age = 31536000
response.cache_control.immutable = True
return response
response = await send_from_directory(str(STATIC_DIR), requested_filename)
return response
@app.get("/")
@app.get("/sources")
@app.get("/sources/create")
@ -141,7 +178,7 @@ def create_app(*, dev_mode: bool = False) -> Quart:
) -> Response:
del slug, job_id, execution_id
body, etag = _render_shim_page(
stylesheet_href=url_for("static", filename="app.css"),
stylesheet_href=versioned_static_asset_href("app.css"),
datastar_src=url_for("static", filename="datastar@1.0.0-RC.8.js"),
current_path=request.path,
)