fix: navigation dying after an app-pool restart
Two independent ways a restart leaves the SPA unable to navigate, both of which look identical to a user - a click that does nothing. 1. The router awaits loadEnabledPlugins() to gate plugin routes. An app-pool restart leaves that request hanging (IIS queues it while the worker starts) and axios sets no timeout, so the navigation never resolves. Worse, the promise is cached, so every later navigation awaited the same dead request and stayed frozen long after the backend recovered. Bound the wait and fail open on expiry, and drop the cached promise when an attempt times out or fails so the next navigation retries. The setup-state probe in the guard gets the same bound (it already fails open, defaulting to "complete"). 2. A deploy replaces the content-hashed chunk files, so a tab open across it asks for chunks that no longer exist and the dynamic import rejects with nothing handling it. Reload once on a chunk-load error, via router.onError and Vite's preloadError, guarded by a sessionStorage flag against a reload loop and cleared on the next successful navigation. Also stop index.html being cached: it names the hashed chunks, so a stale copy points at files the deploy already deleted. It now revalidates while the hashed assets under assets/ cache for a year.
This commit is contained in:
@@ -244,11 +244,23 @@ def register_frontend_routes(app: Flask):
|
||||
# (the probe was a path-traversal risk surface).
|
||||
if path:
|
||||
try:
|
||||
return send_from_directory(frontend_dist, path)
|
||||
response = send_from_directory(frontend_dist, path)
|
||||
# Asset filenames carry a content hash, so a given URL never
|
||||
# changes - cache them hard. Everything else stays revalidated.
|
||||
if path.startswith('assets/'):
|
||||
response.headers['Cache-Control'] = 'public, max-age=31536000, immutable'
|
||||
else:
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
return response
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return send_from_directory(frontend_dist, 'index.html')
|
||||
# index.html names the hashed chunks, so a stale copy points at files a
|
||||
# deploy has already deleted and the SPA stops navigating. Always
|
||||
# revalidate it.
|
||||
response = send_from_directory(frontend_dist, 'index.html')
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
return response
|
||||
|
||||
|
||||
def configure_logging(app: Flask):
|
||||
|
||||
Reference in New Issue
Block a user