From d91cc8da26deb8525bb33714091754ea3fa73c2f Mon Sep 17 00:00:00 2001 From: rene Date: Sat, 9 May 2026 19:55:13 +0200 Subject: [PATCH] =?UTF-8?q?Feature:=20X-App-Version=20Header=20+=20api.js?= =?UTF-8?q?=20auto-reload=20bei=20navigate()=20=E2=80=94=20kein=20Banner-C?= =?UTF-8?q?lick=20mehr=20n=C3=B6tig=20(SW=20by-v798)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/main.py | 16 +++++++++++++++- backend/static/js/api.js | 6 ++++++ backend/static/js/app.js | 19 +++++++------------ backend/static/sw.js | 2 +- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/backend/main.py b/backend/main.py index 58b0679..6f53676 100644 --- a/backend/main.py +++ b/backend/main.py @@ -140,6 +140,20 @@ class _UploadSizeMiddleware(BaseHTTPMiddleware): app.add_middleware(_UploadSizeMiddleware) +class _AppVersionMiddleware(BaseHTTPMiddleware): + """Fügt X-App-Version zu allen /api/-Antworten hinzu. + api.js erkennt damit sofort wenn eine neue Version deployed wurde + und lädt beim nächsten Seitenwechsel automatisch neu — kein Banner nötig. + """ + async def dispatch(self, request: Request, call_next): + response = await call_next(request) + if request.url.path.startswith('/api/'): + response.headers['X-App-Version'] = APP_VER + return response + +app.add_middleware(_AppVersionMiddleware) + + class _CacheControlMiddleware(BaseHTTPMiddleware): """Setzt Cache-Control-Header für statische Assets. CSS/JS: no-cache (ETag-Validierung) — iOS cached sonst ewig ohne Ablaufdatum. @@ -327,7 +341,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media") os.makedirs(MEDIA_DIR, exist_ok=True) app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") -APP_VER = "797" # muss mit APP_VER in app.js übereinstimmen +APP_VER = "798" # muss mit APP_VER in app.js übereinstimmen @app.get("/.well-known/assetlinks.json") async def assetlinks(): diff --git a/backend/static/js/api.js b/backend/static/js/api.js index 1071fdd..1b5fe4c 100644 --- a/backend/static/js/api.js +++ b/backend/static/js/api.js @@ -45,6 +45,12 @@ const API = (() => { throw new APIError(msg, 0, 'network'); } + // Versions-Check: Server meldet neue Version → beim nächsten Seitenwechsel neu laden + const serverVer = response.headers.get('x-app-version'); + if (serverVer && serverVer !== APP_VER && !window._byUpdatePending) { + window._byUpdatePending = true; + } + if (response.status === 204) return null; let data; diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 8ebb039..e6e5d90 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '797'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '798'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const IS_STAGING = location.hostname === 'staging.banyaro.app'; // Cache-Bust-Parameter nach Update-Reload sofort entfernen @@ -119,6 +119,12 @@ const App = (() => { // ---------------------------------------------------------- function navigate(pageId, pushHistory = true, params = {}) { if (!pages[pageId]) return; + // Neue Version vom Server erkannt → jetzt sicher neu laden (kein aktiver Speichervorgang) + if (window._byUpdatePending) { + window._byUpdatePending = false; + location.replace('/?_t=' + Date.now()); + return; + } if (window.Worlds?._visible) window.Worlds.hide(); // Aktive Seite ausblenden @@ -1045,17 +1051,6 @@ const App = (() => { banner.querySelector('#upd-btn-close').addEventListener('click', () => banner.remove()); banner.querySelector('#upd-btn-reload').addEventListener('click', () => { - const btn = banner.querySelector('#upd-btn-reload'); - btn.textContent = 'Lädt…'; - btn.disabled = true; - // Cleanup fire-and-forget — kein await, nie blockieren - try { - navigator.serviceWorker?.getRegistrations() - .then(regs => regs.forEach(r => r.unregister())).catch(() => {}); - caches.keys() - .then(keys => keys.forEach(k => caches.delete(k))).catch(() => {}); - } catch { } - // Sofort neu laden — nicht auf Cleanup warten location.href = '/?_nocache=' + Date.now(); }); } diff --git a/backend/static/sw.js b/backend/static/sw.js index 2a89ce1..b9a02e4 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -3,7 +3,7 @@ Offline-Cache + Push Notifications + Tile-Cache ============================================================ */ -const CACHE_VERSION = 'by-v791'; +const CACHE_VERSION = 'by-v798'; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache