125 lines
4.9 KiB
JavaScript
125 lines
4.9 KiB
JavaScript
// Service Worker — Contrôles StreetUp
|
|
// Stratégie :
|
|
// • Pages de navigation /controls/ et /mobile/controls/ → Network-first, fallback cache, puis page offline
|
|
// • Assets statiques /static/ → Cache-first, mise en cache au premier accès
|
|
|
|
const CACHE_VERSION = 'v6';
|
|
const PAGES_CACHE = `streetup-controls-pages-${CACHE_VERSION}`;
|
|
const STATIC_CACHE = `streetup-controls-static-${CACHE_VERSION}`;
|
|
|
|
// URL de la page de fallback offline (pré-cachée à l'installation)
|
|
const OFFLINE_URL = '/controls/offline/';
|
|
|
|
// ─── Installation ────────────────────────────────────────────────────────────
|
|
self.addEventListener('install', event => {
|
|
console.log(`[Controls SW] install ${CACHE_VERSION}`);
|
|
self.skipWaiting();
|
|
event.waitUntil(
|
|
caches.open(PAGES_CACHE).then(cache => cache.addAll([OFFLINE_URL]))
|
|
);
|
|
});
|
|
|
|
// ─── Activation — nettoyage des anciens caches ───────────────────────────────
|
|
self.addEventListener('activate', event => {
|
|
console.log(`[Controls SW] activate ${CACHE_VERSION}`);
|
|
const knownCaches = [PAGES_CACHE, STATIC_CACHE];
|
|
event.waitUntil(
|
|
caches.keys()
|
|
.then(names => Promise.all(
|
|
names
|
|
.filter(name => !knownCaches.includes(name))
|
|
.map(name => caches.delete(name))
|
|
))
|
|
.then(() => self.clients.claim())
|
|
);
|
|
});
|
|
|
|
// ─── Interception des requêtes ───────────────────────────────────────────────
|
|
self.addEventListener('fetch', event => {
|
|
const { request } = event;
|
|
const url = new URL(request.url);
|
|
|
|
// Uniquement les GET sur la même origine
|
|
if (request.method !== 'GET' || url.origin !== self.location.origin) return;
|
|
|
|
// Assets statiques : cache-first
|
|
if (url.pathname.startsWith('/static/')) {
|
|
event.respondWith(cacheFirst(request, STATIC_CACHE));
|
|
return;
|
|
}
|
|
|
|
// Catalogue de traductions i18n : network-first avec fallback cache
|
|
if (url.pathname === '/jsi18n/') {
|
|
event.respondWith(networkFirstCache(request, PAGES_CACHE));
|
|
return;
|
|
}
|
|
|
|
// Pages de navigation des contrôles (desktop + mobile) : network-first avec fallback
|
|
const isControlsNav = request.mode === 'navigate' && (
|
|
url.pathname.startsWith('/controls/') ||
|
|
url.pathname.startsWith('/mobile/controls/')
|
|
);
|
|
if (isControlsNav) {
|
|
event.respondWith(networkFirstControls(request));
|
|
return;
|
|
}
|
|
});
|
|
|
|
// ─── Stratégie Cache-first (assets statiques) ────────────────────────────────
|
|
async function cacheFirst(request, cacheName) {
|
|
const cached = await caches.match(request);
|
|
if (cached) return cached;
|
|
try {
|
|
const response = await fetch(request);
|
|
if (response.ok) {
|
|
const cache = await caches.open(cacheName);
|
|
cache.put(request, response.clone());
|
|
}
|
|
return response;
|
|
} catch {
|
|
return new Response('', { status: 503 });
|
|
}
|
|
}
|
|
|
|
// ─── Stratégie Network-first générique (sans fallback page) ──────────────────
|
|
// Utilisée pour les ressources dynamiques (ex. /jsi18n/) : réseau d'abord,
|
|
// sinon cache, sinon réponse vide.
|
|
async function networkFirstCache(request, cacheName) {
|
|
const cache = await caches.open(cacheName);
|
|
try {
|
|
const response = await fetch(request);
|
|
if (response.ok) {
|
|
cache.put(request, response.clone());
|
|
}
|
|
return response;
|
|
} catch {
|
|
const cached = await cache.match(request);
|
|
if (cached) return cached;
|
|
return new Response('', { status: 503 });
|
|
}
|
|
}
|
|
|
|
// ─── Stratégie Network-first (pages controls) ────────────────────────────────
|
|
// 1. Réseau → mise en cache si succès
|
|
// 2. Réseau KO → cache existant
|
|
// 3. Pas en cache → page offline pré-cachée
|
|
async function networkFirstControls(request) {
|
|
const cache = await caches.open(PAGES_CACHE);
|
|
try {
|
|
const response = await fetch(request);
|
|
if (response.ok) {
|
|
cache.put(request, response.clone());
|
|
}
|
|
return response;
|
|
} catch {
|
|
const cached = await cache.match(request);
|
|
if (cached) return cached;
|
|
|
|
// Fallback page offline
|
|
const offlinePage = await caches.match(OFFLINE_URL);
|
|
return offlinePage || new Response(
|
|
'<!DOCTYPE html><html><head><meta charset="utf-8"></head><body><h2>Hors ligne</h2><p>Connexion indisponible.</p></body></html>',
|
|
{ status: 503, headers: { 'Content-Type': 'text/html; charset=utf-8' } }
|
|
);
|
|
}
|
|
}
|