fix: serve admin bundle as hashed immutable assets
The lazy-loaded admin CSS/JS resolved to bare runtime names served no-store, the same scheme that left the main bundle vulnerable to stale CSS in iOS WebViews after a deploy. The original reason for keeping them bare (hashed admin files could 404 when nginx fronts aiohttp) no longer holds: the backend image now carries the same deterministically hashed assets nginx serves, and the App.svelte loader already falls back to the bare name if a hashed asset ever 404s. Resolve the admin assets through the same hashed/version-stable path as the main bundle so they are emitted as immutable, cache-busting URLs. Also drop the inert <meta http-equiv="Cache-Control/Pragma/Expires"> tags from the shell: browsers ignore http-equiv caching directives for the document and use the real HTTP headers, which are already set.
This commit is contained in:
@@ -6,9 +6,6 @@
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"
|
||||
/>
|
||||
<meta http-equiv="Cache-Control" content="no-store" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<meta name="theme-color" content="#03070b" />
|
||||
<link id="app-favicon" rel="icon" href="/favicon.ico" sizes="any" />
|
||||
|
||||
@@ -1454,10 +1454,15 @@ def _resolve_webapp_js_asset_name() -> str:
|
||||
|
||||
|
||||
def _resolve_webapp_admin_js_asset_name() -> str:
|
||||
# The admin bundle is lazy-loaded from the already running Mini App. In
|
||||
# deployments where nginx serves static files in front of aiohttp, stale
|
||||
# hashed admin filenames can 404 even though the runtime build asset exists.
|
||||
return _set_cached_asset_name("admin-js", "subscription_webapp_admin.js")
|
||||
# The admin bundle is lazy-loaded from the already running Mini App. It now
|
||||
# ships content-hashed alongside the main bundle (same build, deterministic
|
||||
# hashes, served immutable), so iOS WebViews fetch fresh admin assets on every
|
||||
# deploy. The App.svelte loader falls back to the bare runtime build name if a
|
||||
# hashed asset ever 404s.
|
||||
return _resolve_hashed_js_asset_name(
|
||||
kind="admin-js",
|
||||
base_name="subscription_webapp_admin",
|
||||
)
|
||||
|
||||
|
||||
def _resolve_hashed_js_asset_name(*, kind: str, base_name: str) -> str:
|
||||
@@ -1487,9 +1492,11 @@ def _resolve_webapp_css_asset_name() -> str:
|
||||
|
||||
|
||||
def _resolve_webapp_admin_css_asset_name() -> str:
|
||||
# Keep the lazy-loaded admin stylesheet on the stable build filename for
|
||||
# the same reason as the JS bundle above.
|
||||
return _set_cached_asset_name("admin-css", "subscription_webapp_admin.css")
|
||||
# Content-hashed and immutable, same rationale as the admin JS bundle above.
|
||||
return _resolve_hashed_css_asset_name(
|
||||
kind="admin-css",
|
||||
base_name="subscription_webapp_admin",
|
||||
)
|
||||
|
||||
|
||||
def _resolve_hashed_css_asset_name(*, kind: str, base_name: str) -> str:
|
||||
|
||||
@@ -1009,7 +1009,7 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase):
|
||||
r"^subscription_webapp\.css\?v=[0-9a-f]{8}$",
|
||||
)
|
||||
|
||||
def test_resolve_webapp_admin_asset_names_use_stable_runtime_builds(self):
|
||||
def test_resolve_webapp_admin_asset_names_prefer_latest_hashed_builds(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
asset_dir = Path(tmpdir)
|
||||
(asset_dir / "subscription_webapp_admin.js").write_text(
|
||||
@@ -1032,13 +1032,35 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase):
|
||||
os.utime(new_css, (2, 2))
|
||||
|
||||
with patch.object(webapp_assets, "ASSET_DIR", asset_dir):
|
||||
webapp_assets._ASSET_NAME_CACHE.clear()
|
||||
self.assertEqual(
|
||||
subscription_webapp._resolve_webapp_admin_js_asset_name(),
|
||||
"subscription_webapp_admin.js",
|
||||
"subscription_webapp_admin.min.22222222.js",
|
||||
)
|
||||
self.assertEqual(
|
||||
subscription_webapp._resolve_webapp_admin_css_asset_name(),
|
||||
"subscription_webapp_admin.css",
|
||||
"subscription_webapp_admin.22222222.css",
|
||||
)
|
||||
|
||||
def test_resolve_webapp_admin_asset_names_fall_back_to_runtime_builds(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
asset_dir = Path(tmpdir)
|
||||
(asset_dir / "subscription_webapp_admin.js").write_text(
|
||||
"console.log('admin fallback');", encoding="utf-8"
|
||||
)
|
||||
(asset_dir / "subscription_webapp_admin.css").write_text(
|
||||
".admin{color:red}", encoding="utf-8"
|
||||
)
|
||||
|
||||
with patch.object(webapp_assets, "ASSET_DIR", asset_dir):
|
||||
webapp_assets._ASSET_NAME_CACHE.clear()
|
||||
self.assertRegex(
|
||||
subscription_webapp._resolve_webapp_admin_js_asset_name(),
|
||||
r"^subscription_webapp_admin\.js\?v=[0-9a-f]{8}$",
|
||||
)
|
||||
self.assertRegex(
|
||||
subscription_webapp._resolve_webapp_admin_css_asset_name(),
|
||||
r"^subscription_webapp_admin\.css\?v=[0-9a-f]{8}$",
|
||||
)
|
||||
|
||||
async def test_js_asset_route_sets_immutable_cache_control_for_minified_asset(self):
|
||||
|
||||
Reference in New Issue
Block a user