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

offline mode

docs: update documentation to reflect current codebase and MQTT features

- Update README.md with global MQTT commands
- Enhance architecture.md with comprehensive data model and MQTT state
- Update development.md with project structure and workflow
- Remove redundant script listings
- Fix formatting and organization

rebase
This commit is contained in:
cpu
2025-05-08 15:36:17 +02:00
parent d741efa62d
commit ca3ba141a7
51 changed files with 7140 additions and 2 deletions

166
src/sw.js Normal file
View File

@@ -0,0 +1,166 @@
const CACHE_VERSION = typeof __APP_CACHE_VERSION__ !== 'undefined'
? __APP_CACHE_VERSION__
: 'nexus-timer-cache-fallback-dev-vManual';
const APP_SHELL_URLS = [
// Precache the root (index.html) explicitly for better offline fallback
'/',
'/manifest.json',
'/favicon.ico',
'/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',
];
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')) {
return;
}
// Strategy 1: Network First, then Cache for Navigation/HTML requests
if (request.mode === 'navigate' || request.destination === 'document' || url.pathname === '/') {
// console.log(`[SW ${CACHE_VERSION}] NetworkFirst for navigation/document: ${request.url}`);
event.respondWith(
fetch(request)
.then(networkResponse => {
// If successful, cache the response and return it
if (networkResponse.ok) {
const responseToCache = networkResponse.clone();
caches.open(CACHE_VERSION).then(cache => {
// For navigations, it's often best to cache the specific URL requested
// as well as potentially updating the '/' cache if this is the root.
cache.put(request, responseToCache);
if (url.pathname === '/') { // Also update root cache if it's the index
const rootResponseClone = networkResponse.clone(); // Need another clone
cache.put('/', rootResponseClone);
}
});
}
return networkResponse;
})
.catch(async () => {
// Network failed. Try to serve from cache.
// console.warn(`[SW ${CACHE_VERSION}] Network fetch failed for ${request.url}. Attempting cache.`);
// 1. Try matching the specific request first (e.g. /info, /game)
const cachedResponse = await caches.match(request);
if (cachedResponse) {
// console.log(`[SW ${CACHE_VERSION}] Serving from cache (specific request): ${request.url}`);
return cachedResponse;
}
// 2. If specific request not found, try serving the app shell ('/')
// This is crucial for SPAs to work offline.
const appShellResponse = await caches.match('/');
if (appShellResponse) {
// console.log(`[SW ${CACHE_VERSION}] Serving app shell ('/') from cache for: ${request.url}`);
return appShellResponse;
}
// 3. If even the app shell is not in cache (shouldn't happen if install was successful)
console.error(`[SW ${CACHE_VERSION}] CRITICAL: Network and cache miss for navigation AND app shell ('/') for: ${request.url}`);
// Return a very basic offline message, but ideally this state is avoided.
return new Response(
`<h1>Offline</h1><p>The application is currently offline and the requested page could not be loaded from the cache. Please check your connection.</p>`,
{ headers: { 'Content-Type': 'text/html' } }
);
})
);
return;
}
// Strategy 2: Stale-While-Revalidate for assets (CSS, JS, images, fonts, workers)
if (request.destination === 'style' ||
request.destination === 'script' ||
request.destination === 'worker' ||
request.destination === 'image' ||
request.destination === 'font') {
// console.log(`[SW ${CACHE_VERSION}] StaleWhileRevalidate for asset: ${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);
}
return networkResponse;
}).catch(err => {
// If fetch fails, and we served from cache, it's fine.
// If cache also missed, this error will propagate.
// console.warn(`[SW ${CACHE_VERSION}] SWR: Network fetch error for ${request.url}`, err);
throw err;
});
return cachedResponse || fetchPromise;
}).catch(() => {
// Fallback to network if cache.match fails
// console.warn(`[SW ${CACHE_VERSION}] SWR: Cache match error for ${request.url}, trying network directly.`);
return fetch(request);
});
})
);
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();
}
});