122 lines
3.9 KiB
JavaScript
122 lines
3.9 KiB
JavaScript
const CACHE_NAME = 'fleet-c2-v3'; // incrementare a ogni modifica degli asset precaricati
|
|
const PRECACHE_URLS = [
|
|
'/',
|
|
'/manifest.json',
|
|
'/icon-512.png'
|
|
];
|
|
|
|
// --- 1. INSTALL: precache "best effort" -------------------------------------
|
|
// Se un file manca (es. icona non ancora caricata) l'installazione NON fallisce:
|
|
// un SW che non si installa significa niente notifiche push.
|
|
self.addEventListener('install', event => {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME)
|
|
.then(cache => Promise.allSettled(PRECACHE_URLS.map(url => cache.add(url))))
|
|
.then(() => self.skipWaiting())
|
|
);
|
|
});
|
|
|
|
// --- 2. ACTIVATE: elimina le cache delle versioni precedenti -----------------
|
|
self.addEventListener('activate', event => {
|
|
event.waitUntil(
|
|
caches.keys()
|
|
.then(keys => Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k))))
|
|
.then(() => self.clients.claim())
|
|
);
|
|
});
|
|
|
|
// --- 3. FETCH: network-first, cache come ripiego offline ---------------------
|
|
// Gestiamo solo le GET della nostra origine. API e WebSocket (socket.io) passano
|
|
// sempre direttamente dalla rete: i dati della flotta non vanno mai serviti "vecchi".
|
|
self.addEventListener('fetch', event => {
|
|
const req = event.request;
|
|
if (req.method !== 'GET') return;
|
|
|
|
const url = new URL(req.url);
|
|
if (url.origin !== self.location.origin) return;
|
|
if (url.pathname.startsWith('/api/') ||
|
|
url.pathname.startsWith('/socket.io/') ||
|
|
url.pathname === '/sw.js') return;
|
|
|
|
event.respondWith(
|
|
fetch(req)
|
|
.then(res => {
|
|
if (res && res.ok && res.type === 'basic') {
|
|
const copy = res.clone();
|
|
caches.open(CACHE_NAME).then(cache => cache.put(req, copy)).catch(() => {});
|
|
}
|
|
return res;
|
|
})
|
|
.catch(async () => {
|
|
const cached = await caches.match(req);
|
|
if (cached) return cached;
|
|
if (req.mode === 'navigate') {
|
|
const shell = await caches.match('/');
|
|
if (shell) return shell;
|
|
}
|
|
return new Response('Offline', {
|
|
status: 503,
|
|
statusText: 'Offline',
|
|
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
|
|
});
|
|
})
|
|
);
|
|
});
|
|
|
|
// --- 4. NOTIFICHE PUSH -------------------------------------------------------
|
|
self.addEventListener('push', event => {
|
|
let data = { title: 'Fleet Alert', body: 'Nuovo messaggio dal sistema.' };
|
|
|
|
if (event.data) {
|
|
try {
|
|
data = Object.assign(data, event.data.json());
|
|
} catch (e) {
|
|
data.body = event.data.text();
|
|
}
|
|
}
|
|
|
|
const options = {
|
|
body: data.body,
|
|
icon: '/icon-512.png',
|
|
badge: '/icon-512.png',
|
|
vibrate: [200, 100, 200],
|
|
// Stesso tag = la nuova notifica dello stesso nodo sostituisce la precedente
|
|
// (invece di accumulare 10 notifiche) e rifà vibrare/suonare.
|
|
tag: data.tag || 'fleet-alert',
|
|
renotify: true,
|
|
data: {
|
|
dateOfArrival: Date.now(),
|
|
url: data.url || '/'
|
|
},
|
|
actions: [
|
|
{ action: 'explore', title: 'Apri Dashboard' },
|
|
{ action: 'close', title: 'Chiudi' }
|
|
]
|
|
};
|
|
|
|
event.waitUntil(
|
|
self.registration.showNotification(data.title, options)
|
|
);
|
|
});
|
|
|
|
// --- 5. CLICK SULLA NOTIFICA -------------------------------------------------
|
|
// Click sul corpo O su "Apri Dashboard": porta in primo piano la finestra già aperta,
|
|
// altrimenti ne apre una nuova. "Chiudi" chiude soltanto.
|
|
self.addEventListener('notificationclick', event => {
|
|
event.notification.close();
|
|
if (event.action === 'close') return;
|
|
|
|
const target = new URL((event.notification.data && event.notification.data.url) || '/', self.location.origin).href;
|
|
|
|
event.waitUntil(
|
|
clients.matchAll({ type: 'window', includeUncontrolled: true }).then(list => {
|
|
for (const client of list) {
|
|
if (new URL(client.url).origin === self.location.origin && 'focus' in client) {
|
|
return client.focus();
|
|
}
|
|
}
|
|
return clients.openWindow(target);
|
|
})
|
|
);
|
|
});
|