166 lines
4.5 KiB
JavaScript
166 lines
4.5 KiB
JavaScript
// Service worker cache strategy for player pages, assets, media, and playlists.
|
|
|
|
const CACHE_VERSION = 'v38';
|
|
const PAGE_CACHE = `pulse-signage-player-pages-${CACHE_VERSION}`;
|
|
const ASSET_CACHE = `pulse-signage-player-assets-${CACHE_VERSION}`;
|
|
const MEDIA_CACHE = `pulse-signage-player-media-${CACHE_VERSION}`;
|
|
const PLAYLIST_CACHE = `pulse-signage-player-playlists-${CACHE_VERSION}`;
|
|
|
|
function normalizeRequest(request) {
|
|
const url = new URL(request.url);
|
|
return new Request(`${url.origin}${url.pathname}`, {
|
|
method: 'GET',
|
|
headers: request.headers,
|
|
mode: 'same-origin',
|
|
credentials: 'same-origin'
|
|
});
|
|
}
|
|
|
|
function shouldBypassCache(request) {
|
|
if (!request) {
|
|
return false;
|
|
}
|
|
|
|
if (request.cache === 'reload' || request.cache === 'no-store') {
|
|
return true;
|
|
}
|
|
|
|
const cacheControl = String(request.headers.get('cache-control') || '').toLowerCase();
|
|
const pragma = String(request.headers.get('pragma') || '').toLowerCase();
|
|
return cacheControl.includes('no-cache') || cacheControl.includes('max-age=0') || pragma.includes('no-cache');
|
|
}
|
|
|
|
async function cacheResponse(cacheName, request, response, cacheKeyRequest) {
|
|
if (!response || !response.ok) {
|
|
return;
|
|
}
|
|
const cache = await caches.open(cacheName);
|
|
await cache.put(cacheKeyRequest || request, response.clone());
|
|
}
|
|
|
|
async function networkFirst(request, cacheName, cacheKeyRequest) {
|
|
try {
|
|
const response = await fetch(request);
|
|
if (response && response.ok) {
|
|
await cacheResponse(cacheName, request, response, cacheKeyRequest);
|
|
return response;
|
|
}
|
|
if (response && response.status === 304) {
|
|
const cached = await caches.match(cacheKeyRequest || request);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
}
|
|
const cached = await caches.match(cacheKeyRequest || request);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
return response;
|
|
} catch (_error) {
|
|
const cached = await caches.match(cacheKeyRequest || request);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
throw _error;
|
|
}
|
|
}
|
|
|
|
async function cacheFirst(request, cacheName) {
|
|
const cached = await caches.match(request);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
const response = await fetch(request);
|
|
if (response && response.ok) {
|
|
await cacheResponse(cacheName, request, response);
|
|
}
|
|
return response;
|
|
}
|
|
|
|
async function staleWhileRevalidate(request, cacheName) {
|
|
const cached = await caches.match(request);
|
|
const networkPromise = fetch(request).then(async function (response) {
|
|
if (response && response.ok) {
|
|
await cacheResponse(cacheName, request, response);
|
|
}
|
|
return response;
|
|
}).catch(function () {
|
|
return null;
|
|
});
|
|
|
|
if (cached) {
|
|
networkPromise.catch(function () {
|
|
return null;
|
|
});
|
|
return cached;
|
|
}
|
|
|
|
const networkResponse = await networkPromise;
|
|
if (networkResponse) {
|
|
return networkResponse;
|
|
}
|
|
|
|
return new Response('', { status: 504, statusText: 'Offline' });
|
|
}
|
|
|
|
async function networkOnly(request) {
|
|
return fetch(request);
|
|
}
|
|
|
|
self.addEventListener('install', function (event) {
|
|
self.skipWaiting();
|
|
event.waitUntil(Promise.resolve());
|
|
});
|
|
|
|
self.addEventListener('activate', function (event) {
|
|
event.waitUntil((async function () {
|
|
const expected = [PAGE_CACHE, ASSET_CACHE, MEDIA_CACHE, PLAYLIST_CACHE];
|
|
const keys = await caches.keys();
|
|
await Promise.all(keys.filter(function (key) {
|
|
return expected.indexOf(key) === -1;
|
|
}).map(function (key) {
|
|
return caches.delete(key);
|
|
}));
|
|
await self.clients.claim();
|
|
})());
|
|
});
|
|
|
|
self.addEventListener('fetch', function (event) {
|
|
const request = event.request;
|
|
if (request.method !== 'GET') {
|
|
return;
|
|
}
|
|
|
|
const url = new URL(request.url);
|
|
if (url.origin !== self.location.origin) {
|
|
return;
|
|
}
|
|
|
|
if (url.pathname === '/sw.js') {
|
|
return;
|
|
}
|
|
|
|
if (shouldBypassCache(request)) {
|
|
event.respondWith(networkOnly(request));
|
|
return;
|
|
}
|
|
|
|
if (url.pathname.startsWith('/assets/')) {
|
|
event.respondWith(cacheFirst(request, ASSET_CACHE));
|
|
return;
|
|
}
|
|
|
|
if (url.pathname.startsWith('/media/')) {
|
|
event.respondWith(networkOnly(request));
|
|
return;
|
|
}
|
|
|
|
if (request.mode === 'navigate' || url.pathname === '/' || url.pathname === '/onboard' || /^\/screen\/[^/]+$/.test(url.pathname)) {
|
|
event.respondWith(networkFirst(request, PAGE_CACHE, normalizeRequest(request)));
|
|
return;
|
|
}
|
|
|
|
if (url.pathname.startsWith('/api/screens/') && url.pathname.endsWith('/playlist')) {
|
|
event.respondWith(networkFirst(request, PLAYLIST_CACHE, normalizeRequest(request)));
|
|
}
|
|
}); |