Files
annu-kute-ced/sw.js
T

226 lines
6.7 KiB
JavaScript
Raw Normal View History

// Version du cache : à bumper à chaque déploiement (format JJMMAAAA-HHMM).
// Tout changement de ce fichier déclenche l'installation d'un nouveau
// Service Worker chez les visiteurs ; les anciens caches sont purgés à
// l'activation (voir l'event 'activate' plus bas).
2026-07-26 19:47:15 +04:00
const STATIC_CACHE_NAME = 'annu-kute-ced-static-26072026-1946';
const DYNAMIC_CACHE_NAME = 'annu-kute-ced-dynamic-26072026-1946';
2025-07-17 20:09:55 +04:00
// Nombre maximal d'entrées conservées dans le cache dynamique (LRU :
// les entrées les moins récemment utilisées sont évincées en premier).
const DYNAMIC_CACHE_LIMIT = 50;
2025-07-17 20:09:55 +04:00
// Ressources à mettre en cache immédiatement
const STATIC_ASSETS = [
'/',
'/index.php',
'/css/styles.css',
'/css/categories.css',
'/css/search.css',
'/css/video-page.css',
'/css/mastodon-timeline.min.css',
'/js/main.js',
'/js/search.js',
'/js/mastodon-timeline.umd.js',
'/img/logo.png',
'/img/android-chrome-192x192.png',
'/img/android-chrome-512x512.png',
'/img/apple-touch-icon.png',
'/img/favicon-32x32.png',
'/img/favicon-16x16.png',
'/img/favicon.ico',
'/img/play-icon.svg',
'/site.webmanifest',
2025-09-14 13:49:57 +04:00
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css'
2025-07-17 20:09:55 +04:00
];
// Installation du Service Worker
self.addEventListener('install', event => {
console.log('Service Worker: Installation');
2025-07-18 09:48:45 +04:00
2025-07-17 20:09:55 +04:00
event.waitUntil(
caches.open(STATIC_CACHE_NAME)
.then(cache => {
console.log('Service Worker: Mise en cache des assets statiques');
return cache.addAll(STATIC_ASSETS);
})
.catch(err => {
console.error('Service Worker: Erreur lors de la mise en cache:', err);
})
);
// Note : pas de skipWaiting() ici. Lors d'une mise à jour, le nouveau SW
// reste en attente jusqu'à ce que l'utilisateur accepte la mise à jour
// via le modal (message SKIP_WAITING envoyé par js/pwa-update.js).
// Lors de la toute première visite (aucun SW actif), l'activation est
// immédiate.
2025-07-17 20:09:55 +04:00
});
// Activation du Service Worker
self.addEventListener('activate', event => {
console.log('Service Worker: Activation');
2025-07-18 09:48:45 +04:00
2025-07-17 20:09:55 +04:00
event.waitUntil(
caches.keys()
.then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== STATIC_CACHE_NAME && cacheName !== DYNAMIC_CACHE_NAME) {
console.log('Service Worker: Suppression du cache obsolète:', cacheName);
return caches.delete(cacheName);
}
})
);
})
.then(() => {
return self.clients.claim();
})
);
});
// Interception des requêtes
self.addEventListener('fetch', event => {
const { request } = event;
const url = new URL(request.url);
// Ignorer les requêtes non-GET
if (request.method !== 'GET') {
return;
}
// Ignorer les requêtes vers des domaines externes (sauf CDN)
2025-07-18 09:48:45 +04:00
if (url.origin !== location.origin &&
2025-07-17 20:09:55 +04:00
!url.hostname.includes('cdnjs.cloudflare.com')) {
return;
}
// Stratégie Cache First pour les assets statiques
if (isStaticAsset(request.url)) {
event.respondWith(
caches.match(request)
.then(response => {
if (response) {
return response;
}
return fetch(request)
.then(response => {
if (response.status === 200) {
const responseClone = response.clone();
caches.open(STATIC_CACHE_NAME)
.then(cache => cache.put(request, responseClone));
}
return response;
});
})
.catch(() => {
// Fallback pour les images
if (request.destination === 'image') {
return caches.match('/img/logo.png');
}
})
);
return;
}
// Stratégie Network First pour les pages dynamiques
if (isPageRequest(request.url)) {
event.respondWith(
fetch(request)
.then(response => {
if (response.status === 200) {
const responseClone = response.clone();
caches.open(DYNAMIC_CACHE_NAME)
.then(cache => {
// cache.put() replace une entrée existante en fin de liste
// (clés ordonnées par insertion) : la taille limitée évince
// donc bien les entrées les moins récemment utilisées.
return cache.put(request, responseClone)
.then(() => limitCacheSize(cache, DYNAMIC_CACHE_LIMIT));
});
2025-07-17 20:09:55 +04:00
}
return response;
})
.catch(() => {
return caches.match(request)
.then(response => {
if (response) {
return response;
}
// Fallback vers la page d'accueil
return caches.match('/') || caches.match('/index.php');
});
})
);
return;
}
// Stratégie Network First pour les API et AJAX
if (isApiRequest(request.url)) {
event.respondWith(
fetch(request)
.catch(() => {
return new Response(
JSON.stringify({
error: 'Pas de connexion internet',
offline: true
}),
{
status: 503,
headers: {
'Content-Type': 'application/json'
}
}
);
})
);
return;
}
});
// Fonctions utilitaires
// Évince les plus anciennes clés tant que le cache dépasse maxItems entrées.
function limitCacheSize(cache, maxItems) {
return cache.keys().then(keys => {
if (keys.length > maxItems) {
return cache.delete(keys[0]).then(() => limitCacheSize(cache, maxItems));
}
});
}
2025-07-17 20:09:55 +04:00
function isStaticAsset(url) {
2025-07-18 09:48:45 +04:00
return url.includes('/css/') ||
url.includes('/js/') ||
2025-07-17 20:09:55 +04:00
url.includes('/img/') ||
url.includes('cdnjs.cloudflare.com') ||
url.endsWith('.css') ||
url.endsWith('.js') ||
url.endsWith('.png') ||
url.endsWith('.jpg') ||
url.endsWith('.jpeg') ||
url.endsWith('.svg') ||
url.endsWith('.ico') ||
url.endsWith('.webmanifest');
}
function isPageRequest(url) {
2025-07-18 09:48:45 +04:00
return url.endsWith('/') ||
2025-07-17 20:09:55 +04:00
url.endsWith('.php') ||
url.includes('index.php') ||
url.includes('categories.php') ||
url.includes('recherche.php') ||
url.includes('video.php') ||
url.includes('mentions-legales.php');
}
function isApiRequest(url) {
2025-07-18 09:48:45 +04:00
return url.includes('/ajax/') ||
2025-07-17 20:09:55 +04:00
url.includes('api') ||
url.includes('mastodon-config.php');
}
// Gestion des messages du client : l'utilisateur a accepté la mise à jour,
// le SW en attente prend le contrôle (purge des anciens caches à l'activation).
2025-07-17 20:09:55 +04:00
self.addEventListener('message', event => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});