Files
nexus-timer/src/sw.js
cpu 19fbae06fc PWA fixed
added systemd service howto

traefik

nginix set_real_ip_from

improved readme

visuals fixed on mobile

labels removed

updated readme

fixed visuals

overlay for the hotkey

disable screen lock

clean up

git precommit hooks

clean up

clean up

update

check for update feature

added build-time information

fixed date

clean up

added hook script

fix

fix

fix

hooks fixed

webhook setup

players stay in run all timers mode

mqtt

mqtt allways connected

mqtt messages work

capturing mqtt in edit player

mqtt in Setup

updated readme

state of the mqtt

Global Pass turn
2025-05-14 23:57:38 +02:00

150 lines
6.1 KiB
JavaScript

// This global constant __APP_CACHE_VERSION__ will be replaced by Vite
// during the build process due to the `define` config in vite.config.js.
const CACHE_VERSION = typeof __APP_CACHE_VERSION__ !== 'undefined'
? __APP_CACHE_VERSION__
: 'nexus-timer-cache-fallback-dev-vManual'; // Fallback for dev or if define fails
const APP_SHELL_URLS = [
// Note: '/' (index.html) is handled by NetworkFirst strategy, no need to precache explicitly here.
'/manifest.json', // Will be served from public, copied to dist root
'/favicon.ico', // Will be served from public, copied to dist root
// Icons from public/icons, will be copied to dist/icons
'/icons/icon-192x192.png',
'/icons/icon-512x512.png',
'/icons/maskable-icon-192x192.png',
'/icons/maskable-icon-512x512.png',
'/icons/shortcut-setup-96x96.png',
'/icons/shortcut-info-96x96.png',
// Any other critical static assets from the public folder that should be part of the app shell
];
self.addEventListener('install', event => {
console.log(`[SW ${CACHE_VERSION}] Install`);
event.waitUntil(
caches.open(CACHE_VERSION)
.then(cache => {
console.log(`[SW ${CACHE_VERSION}] Caching app shell essentials`);
return cache.addAll(APP_SHELL_URLS);
})
.then(() => {
console.log(`[SW ${CACHE_VERSION}] Skip waiting on install.`);
return self.skipWaiting();
})
);
});
self.addEventListener('activate', event => {
console.log(`[SW ${CACHE_VERSION}] Activate`);
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== CACHE_VERSION) {
console.log(`[SW ${CACHE_VERSION}] Deleting old cache: ${cacheName}`);
return caches.delete(cacheName);
}
})
);
}).then(() => {
console.log(`[SW ${CACHE_VERSION}] Clients claimed.`);
return self.clients.claim();
})
);
});
self.addEventListener('fetch', event => {
const { request } = event;
const url = new URL(request.url);
if (request.method !== 'GET' || !url.protocol.startsWith('http')) {
// console.log(`[SW ${CACHE_VERSION}] Ignoring non-GET or non-http(s) request: ${request.url}`);
return;
}
// Strategy 1: Network First for HTML (navigations or direct / request)
if (request.mode === 'navigate' || (request.destination === 'document' || url.pathname === '/')) {
// console.log(`[SW ${CACHE_VERSION}] NetworkFirst for: ${request.url}`);
event.respondWith(
fetch(request)
.then(response => {
if (response.ok) {
const responseClone = response.clone();
caches.open(CACHE_VERSION).then(cache => cache.put(request, responseClone));
}
return response;
})
.catch(async () => {
// console.warn(`[SW ${CACHE_VERSION}] Network fetch failed for ${request.url}, trying cache.`);
const cachedResponse = await caches.match(request);
if (cachedResponse) return cachedResponse;
// Fallback to root /index.html from cache if specific page not found offline
const rootCache = await caches.match('/');
if (rootCache) return rootCache;
// console.error(`[SW ${CACHE_VERSION}] Network and cache miss for navigation: ${request.url}`);
return new Response('Network error: You are offline and this page is not cached.', {
status: 404,
statusText: 'Not Found',
headers: { 'Content-Type': 'text/html' } // Important for SPA offline fallback
});
})
);
return;
}
// Strategy 2: Stale-While-Revalidate for assets (CSS, JS, images, fonts)
if (request.destination === 'style' ||
request.destination === 'script' ||
request.destination === 'worker' ||
request.destination === 'image' ||
request.destination === 'font') {
// console.log(`[SW ${CACHE_VERSION}] StaleWhileRevalidate for: ${request.url}`);
event.respondWith(
caches.open(CACHE_VERSION).then(cache => {
return cache.match(request).then(cachedResponse => {
const fetchPromise = fetch(request).then(networkResponse => {
if (networkResponse.ok) {
const responseToCache = networkResponse.clone();
cache.put(request, responseToCache);
} else {
// console.warn(`[SW ${CACHE_VERSION}] StaleWhileRevalidate: Network fetch for ${request.url} failed with status ${networkResponse.status}`);
}
return networkResponse;
}).catch(err => {
// console.warn(`[SW ${CACHE_VERSION}] StaleWhileRevalidate: Network fetch error for ${request.url}:`, err);
// If fetch fails, and we already served from cache, that's okay.
// If cache also missed (i.e., cachedResponse was null), then this error will propagate.
throw err;
});
return cachedResponse || fetchPromise;
}).catch(err => {
// This catch block handles errors from cache.match() or if fetchPromise was returned and rejected
// console.error(`[SW ${CACHE_VERSION}] StaleWhileRevalidate: Error for ${request.url}. Trying network fallback.`, err);
return fetch(request); // Final fallback to network if cache interactions fail
});
})
);
return;
}
// Strategy 3: Cache First for other types of requests (e.g., manifest.json if not in APP_SHELL_URLS)
// console.log(`[SW ${CACHE_VERSION}] CacheFirst for: ${request.url}`);
event.respondWith(
caches.match(request)
.then(response => {
return response || fetch(request).then(networkResponse => {
if(networkResponse.ok) {
const responseClone = networkResponse.clone();
caches.open(CACHE_VERSION).then(cache => cache.put(request, responseClone));
}
return networkResponse;
});
})
);
});
self.addEventListener('message', event => {
if (event.data && event.data.action === 'skipWaiting') {
console.log(`[SW ${CACHE_VERSION}] Received skipWaiting message, activating new SW.`);
self.skipWaiting();
}
});