radiantrh/service-worker.js
2025-12-20 22:43:09 +00:00

220 lines
6.8 KiB
JavaScript

// Service Worker pour Portail RH Inter Santé
const CACHE_NAME = 'inter-sante-portal-v1.1';
const OFFLINE_URL = '/offline.html';
// Ressources ESSENTIELLES à mettre en cache (vérifier l'existence)
const PRECACHE_URLS = [
'/',
'/Bootstrap_new/css/style_office.css',
'/Bootstrap_new/css/ux_enhancements.css',
'/Bootstrap_new/js/ux-manager.js',
'/Js/fonctions.js',
'/manifest.json',
'/Bootstrap_new/images/new/favicon.png'
];
// Installation - Pré-cache des ressources essentielles
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('[Service Worker] Pré-cache des ressources');
// Cache only resources that exist
const cachePromises = PRECACHE_URLS.map(url => {
return fetch(url, { mode: 'no-cors' })
.then(response => {
if (response.ok || response.type === 'opaque') {
return cache.put(url, response);
}
console.warn(`[SW] Resource not found: ${url}`);
return Promise.resolve();
})
.catch(error => {
console.warn(`[SW] Failed to cache ${url}:`, error);
return Promise.resolve();
});
});
return Promise.all(cachePromises);
})
.then(() => {
console.log('[Service Worker] Installation terminée');
return self.skipWaiting();
})
.catch(error => {
console.error('[Service Worker] Erreur installation:', error);
})
);
});
// Activation - Nettoyage des anciens caches
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== CACHE_NAME) {
console.log('[Service Worker] Suppression ancien cache:', cacheName);
return caches.delete(cacheName);
}
})
);
}).then(() => {
console.log('[Service Worker] Activation terminée');
return self.clients.claim();
})
);
});
// Stratégie de cache: Stale-While-Revalidate
self.addEventListener('fetch', event => {
// Ignorer les requêtes non-GET
if (event.request.method !== 'GET') {
return;
}
// Ignorer les requêtes chrome-extension
if (event.request.url.includes('chrome-extension')) {
return;
}
// Pour les pages HTML: Network First avec fallback cache
if (event.request.headers.get('accept').includes('text/html')) {
event.respondWith(
fetch(event.request)
.then(response => {
// Vérifier si la réponse est valide
if (!response || response.status !== 200 || response.type === 'error') {
throw new Error('Network response was not ok');
}
// Cloner la réponse pour le cache
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
return response;
})
.catch(() => {
// Fallback au cache
return caches.match(event.request)
.then(cachedResponse => {
return cachedResponse || caches.match('/');
});
})
);
return;
}
// Pour les autres ressources: Cache First, fallback Network
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
if (cachedResponse) {
// Toujours rafraîchir le cache en arrière-plan
fetch(event.request)
.then(response => {
if (response && response.ok) {
caches.open(CACHE_NAME)
.then(cache => cache.put(event.request, response));
}
})
.catch(() => {}); // Ignorer les erreurs de mise à jour
return cachedResponse;
}
// Pas dans le cache, aller au réseau
return fetch(event.request)
.then(response => {
// Vérifier si nous avons reçu une réponse valide
if (!response || !response.ok) {
return response; // Retourner la réponse même si elle a échoué
}
// Mettre en cache la réponse pour la prochaine fois
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
return response;
})
.catch(error => {
console.error('[SW] Fetch failed:', error);
// Pour les CSS/JS, retourner des réponses de secours
const url = event.request.url;
if (url.includes('.css')) {
return new Response('/* Ressource temporairement indisponible */', {
headers: { 'Content-Type': 'text/css' }
});
}
if (url.includes('.js')) {
return new Response('// Ressource temporairement indisponible', {
headers: { 'Content-Type': 'application/javascript' }
});
}
// Pour les images, retourner une image de secours
if (url.includes('.png') || url.includes('.jpg') || url.includes('.svg')) {
return fetch('/Bootstrap_new/images/new/favicon.png')
.catch(() => {
// Si même l'icône de secours échoue, retourner une réponse vide
return new Response('', { status: 404 });
});
}
return new Response('Ressource non disponible hors ligne', {
headers: { 'Content-Type': 'text/plain' }
});
});
})
);
});
// Gestion des messages entre l'app et le Service Worker
self.addEventListener('message', event => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
if (event.data && event.data.type === 'GET_CACHE_STATUS') {
event.ports[0].postMessage({
cacheName: CACHE_NAME,
status: 'active'
});
}
});
// Gestion des notifications push (optionnel)
self.addEventListener('push', event => {
if (!event.data) return;
const data = event.data.json();
const options = {
body: data.body || 'Nouvelle notification',
icon: '/Bootstrap_new/images/new/favicon.png',
badge: '/Bootstrap_new/images/new/favicon.png',
vibrate: [200, 100, 200],
data: {
url: data.url || '/'
}
};
event.waitUntil(
self.registration.showNotification(data.title || 'Inter Santé', options)
);
});
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data.url || '/')
);
});