Fix: iOS SW-Update — SKIP_WAITING Handler, location.replace() statt reload(), no-store Header (SW by-v762)

This commit is contained in:
rene 2026-05-07 19:22:22 +02:00
parent a8b4fd781f
commit a3c8d77a14
4 changed files with 20 additions and 14 deletions

View file

@ -327,7 +327,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
os.makedirs(MEDIA_DIR, exist_ok=True) os.makedirs(MEDIA_DIR, exist_ok=True)
app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media") app.mount("/media", StaticFiles(directory=MEDIA_DIR), name="media")
APP_VER = "761" # muss mit APP_VER in app.js übereinstimmen APP_VER = "762" # muss mit APP_VER in app.js übereinstimmen
@app.get("/api/version") @app.get("/api/version")
async def app_version(): async def app_version():
@ -848,7 +848,7 @@ async def share_target(request: Request):
# Weiterleitung zur App mit den Daten # Weiterleitung zur App mit den Daten
return FileResponse( return FileResponse(
f"{STATIC_DIR}/index.html", f"{STATIC_DIR}/index.html",
headers={"Cache-Control": "no-cache"} headers={"Cache-Control": "no-store, no-cache"}
) )
# Öffentliche Hunde-Profilseite (für NFC-Tags, kein Login nötig) # Öffentliche Hunde-Profilseite (für NFC-Tags, kein Login nötig)
@ -1182,17 +1182,17 @@ async def public_dog_page(dog_id: int):
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@app.get("/teilen/{token}") @app.get("/teilen/{token}")
async def invite_page(token: str): async def invite_page(token: str):
return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"})
@app.get("/breeder/{zwingername}") @app.get("/breeder/{zwingername}")
async def breeder_profile_page(zwingername: str): async def breeder_profile_page(zwingername: str):
return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"})
@app.get("/litters") @app.get("/litters")
async def litters_page(): async def litters_page():
return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"})
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@ -1200,7 +1200,7 @@ async def litters_page():
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@app.get("/widget") @app.get("/widget")
async def widget_page(): async def widget_page():
return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-cache"}) return FileResponse(f"{STATIC_DIR}/index.html", headers={"Cache-Control": "no-store, no-cache"})
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@ -1726,7 +1726,7 @@ async def partner_landing():
</div> </div>
</body> </body>
</html>""" </html>"""
return HTMLResponse(content=html, headers={"Cache-Control": "no-cache"}) return HTMLResponse(content=html, headers={"Cache-Control": "no-store, no-cache"})
# ------------------------------------------------------------------ # ------------------------------------------------------------------
@ -1924,8 +1924,8 @@ async def spa_fallback(full_path: str):
'<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-180.png">', '<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-180.png">',
'<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-180-staging.png">', '<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-180-staging.png">',
) )
return HTMLResponse(content=html, headers={"Cache-Control": "no-cache"}) return HTMLResponse(content=html, headers={"Cache-Control": "no-store, no-cache"})
return FileResponse( return FileResponse(
f"{STATIC_DIR}/index.html", f"{STATIC_DIR}/index.html",
headers={"Cache-Control": "no-cache"} headers={"Cache-Control": "no-store, no-cache"}
) )

View file

@ -578,7 +578,7 @@
<script src="/js/api.js?v=94"></script> <script src="/js/api.js?v=94"></script>
<script src="/js/ui.js?v=94"></script> <script src="/js/ui.js?v=94"></script>
<script src="/js/app.js?v=94"></script> <script src="/js/app.js?v=94"></script>
<script src="/js/worlds.js?v=761"></script> <script src="/js/worlds.js?v=762"></script>
<!-- Feature-Seiten werden lazy geladen --> <!-- Feature-Seiten werden lazy geladen -->
@ -640,7 +640,8 @@
// Wenn neuer SW die Kontrolle übernimmt → Seite neu laden // Wenn neuer SW die Kontrolle übernimmt → Seite neu laden
navigator.serviceWorker.addEventListener('controllerchange', () => { navigator.serviceWorker.addEventListener('controllerchange', () => {
window.location.reload(); // location.replace statt reload() — bypassed iOS bfcache
window.location.replace(window.location.href);
}); });
navigator.serviceWorker.addEventListener('message', e => { navigator.serviceWorker.addEventListener('message', e => {

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung. Router, State-Management, Navigation, Initialisierung.
============================================================ */ ============================================================ */
const APP_VER = '761'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VER = '762'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt const APP_VERSION = '1.5.0'; // ← semantische Version, wird bei make release gesetzt
const IS_STAGING = location.hostname === 'staging.banyaro.app'; const IS_STAGING = location.hostname === 'staging.banyaro.app';
@ -1045,7 +1045,8 @@ const App = (() => {
const keys = await caches.keys(); const keys = await caches.keys();
await Promise.all(keys.map(k => caches.delete(k))); await Promise.all(keys.map(k => caches.delete(k)));
} catch { /* ignorieren */ } } catch { /* ignorieren */ }
setTimeout(() => location.reload(), 600); // location.replace bypassed iOS bfcache (reload() stellt alte Seite wieder her)
setTimeout(() => location.replace(location.href), 600);
}); });
} }

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache Offline-Cache + Push Notifications + Tile-Cache
============================================================ */ ============================================================ */
const CACHE_VERSION = 'by-v761'; const CACHE_VERSION = 'by-v762';
const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache
@ -344,6 +344,10 @@ self.addEventListener('sync', event => {
// MESSAGE — Tile-Vorausladung (Offline-Speicherung) + Queue-Steuerung // MESSAGE — Tile-Vorausladung (Offline-Speicherung) + Queue-Steuerung
// ---------------------------------------------------------- // ----------------------------------------------------------
self.addEventListener('message', event => { self.addEventListener('message', event => {
if (event.data?.type === 'SKIP_WAITING') {
self.skipWaiting();
return;
}
if (event.data?.type === 'PROCESS_QUEUE') { if (event.data?.type === 'PROCESS_QUEUE') {
event.waitUntil(_processQueue()); event.waitUntil(_processQueue());
return; return;