mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-03 18:22:57 +00:00
249 lines
6.2 KiB
JavaScript
249 lines
6.2 KiB
JavaScript
/* eslint-disable no-restricted-globals */
|
|
// Service Worker for PWA support and offline functionality
|
|
|
|
const CACHE_VERSION = 'v1.0.0';
|
|
const CACHE_NAME = `fotbal-club-cache-${CACHE_VERSION}`;
|
|
|
|
// Assets to cache on install
|
|
const STATIC_ASSETS = [
|
|
'/',
|
|
'/index.html',
|
|
'/static/css/main.css',
|
|
'/static/js/main.js',
|
|
'/manifest.json',
|
|
'/favicon.ico',
|
|
'/logo192.png',
|
|
'/logo512.png',
|
|
];
|
|
|
|
// API endpoints to cache
|
|
const API_CACHE_ENDPOINTS = [
|
|
'/api/v1/settings/public',
|
|
'/api/v1/seo',
|
|
];
|
|
|
|
// Install event - cache static assets
|
|
self.addEventListener('install', (event) => {
|
|
console.log('[SW] Installing service worker...');
|
|
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
console.log('[SW] Caching static assets');
|
|
return cache.addAll(STATIC_ASSETS.map(url => new Request(url, { cache: 'reload' })));
|
|
}).catch((error) => {
|
|
console.error('[SW] Failed to cache static assets:', error);
|
|
})
|
|
);
|
|
|
|
// Activate immediately
|
|
self.skipWaiting();
|
|
});
|
|
|
|
// Activate event - clean up old caches
|
|
self.addEventListener('activate', (event) => {
|
|
console.log('[SW] Activating service worker...');
|
|
|
|
event.waitUntil(
|
|
caches.keys().then((cacheNames) => {
|
|
return Promise.all(
|
|
cacheNames
|
|
.filter((name) => name !== CACHE_NAME)
|
|
.map((name) => {
|
|
console.log('[SW] Deleting old cache:', name);
|
|
return caches.delete(name);
|
|
})
|
|
);
|
|
})
|
|
);
|
|
|
|
// Take control immediately
|
|
return self.clients.claim();
|
|
});
|
|
|
|
// Fetch event - serve from cache, fall back to network
|
|
self.addEventListener('fetch', (event) => {
|
|
const { request } = event;
|
|
const url = new URL(request.url);
|
|
|
|
// Skip non-GET requests
|
|
if (request.method !== 'GET') {
|
|
return;
|
|
}
|
|
|
|
// Skip Chrome extensions and non-http(s) requests
|
|
if (!url.protocol.startsWith('http')) {
|
|
return;
|
|
}
|
|
|
|
// Skip admin routes
|
|
if (url.pathname.startsWith('/admin')) {
|
|
return;
|
|
}
|
|
|
|
// Handle API requests
|
|
if (url.pathname.startsWith('/api/')) {
|
|
event.respondWith(handleAPIRequest(request));
|
|
return;
|
|
}
|
|
|
|
// Handle static assets and pages
|
|
event.respondWith(handleStaticRequest(request));
|
|
});
|
|
|
|
// Handle static requests - Cache First strategy
|
|
async function handleStaticRequest(request) {
|
|
try {
|
|
// Try cache first
|
|
const cachedResponse = await caches.match(request);
|
|
if (cachedResponse) {
|
|
// Return cached response and update in background
|
|
fetchAndUpdateCache(request);
|
|
return cachedResponse;
|
|
}
|
|
|
|
// Not in cache - fetch from network
|
|
const networkResponse = await fetch(request);
|
|
|
|
// Cache successful responses
|
|
if (networkResponse.ok) {
|
|
const cache = await caches.open(CACHE_NAME);
|
|
cache.put(request, networkResponse.clone());
|
|
}
|
|
|
|
return networkResponse;
|
|
} catch (error) {
|
|
console.error('[SW] Fetch failed:', error);
|
|
|
|
// Return offline page if available
|
|
const cachedOffline = await caches.match('/offline.html');
|
|
if (cachedOffline) {
|
|
return cachedOffline;
|
|
}
|
|
|
|
// Return basic offline response
|
|
return new Response('Offline - Please check your connection', {
|
|
status: 503,
|
|
statusText: 'Service Unavailable',
|
|
headers: { 'Content-Type': 'text/plain' },
|
|
});
|
|
}
|
|
}
|
|
|
|
// Handle API requests - Network First strategy with cache fallback
|
|
async function handleAPIRequest(request) {
|
|
try {
|
|
// Try network first for fresh data
|
|
const networkResponse = await fetch(request);
|
|
|
|
// Cache successful responses
|
|
if (networkResponse.ok) {
|
|
const cache = await caches.open(CACHE_NAME);
|
|
cache.put(request, networkResponse.clone());
|
|
}
|
|
|
|
return networkResponse;
|
|
} catch (error) {
|
|
console.log('[SW] Network failed, trying cache:', request.url);
|
|
|
|
// Fall back to cache
|
|
const cachedResponse = await caches.match(request);
|
|
if (cachedResponse) {
|
|
return cachedResponse;
|
|
}
|
|
|
|
// Return error response
|
|
return new Response(
|
|
JSON.stringify({ error: 'Offline - cached data not available' }),
|
|
{
|
|
status: 503,
|
|
statusText: 'Service Unavailable',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
// Update cache in background
|
|
async function fetchAndUpdateCache(request) {
|
|
try {
|
|
const response = await fetch(request);
|
|
if (response.ok) {
|
|
const cache = await caches.open(CACHE_NAME);
|
|
cache.put(request, response);
|
|
}
|
|
} catch (error) {
|
|
// Silent fail - we already returned cached version
|
|
}
|
|
}
|
|
|
|
// Handle background sync for offline actions
|
|
self.addEventListener('sync', (event) => {
|
|
console.log('[SW] Background sync:', event.tag);
|
|
|
|
if (event.tag === 'sync-data') {
|
|
event.waitUntil(syncOfflineData());
|
|
}
|
|
});
|
|
|
|
async function syncOfflineData() {
|
|
// Implement offline data sync logic here
|
|
// For example, sync form submissions, votes, etc.
|
|
console.log('[SW] Syncing offline data...');
|
|
}
|
|
|
|
// Handle push notifications
|
|
self.addEventListener('push', (event) => {
|
|
const data = event.data ? event.data.json() : {};
|
|
|
|
const title = data.title || 'Fotbal Club';
|
|
const options = {
|
|
body: data.body || 'Nová notifikace',
|
|
icon: '/logo192.png',
|
|
badge: '/logo192.png',
|
|
data: data.url || '/',
|
|
};
|
|
|
|
event.waitUntil(
|
|
self.registration.showNotification(title, options)
|
|
);
|
|
});
|
|
|
|
// Handle notification clicks
|
|
self.addEventListener('notificationclick', (event) => {
|
|
event.notification.close();
|
|
|
|
const urlToOpen = event.notification.data || '/';
|
|
|
|
event.waitUntil(
|
|
clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
|
|
// Check if there's already a window open
|
|
for (const client of clientList) {
|
|
if (client.url === urlToOpen && 'focus' in client) {
|
|
return client.focus();
|
|
}
|
|
}
|
|
// Open new window
|
|
if (clients.openWindow) {
|
|
return clients.openWindow(urlToOpen);
|
|
}
|
|
})
|
|
);
|
|
});
|
|
|
|
// Message handler for manual cache updates
|
|
self.addEventListener('message', (event) => {
|
|
if (event.data && event.data.type === 'SKIP_WAITING') {
|
|
self.skipWaiting();
|
|
}
|
|
|
|
if (event.data && event.data.type === 'CLEAR_CACHE') {
|
|
event.waitUntil(
|
|
caches.delete(CACHE_NAME).then(() => {
|
|
return caches.open(CACHE_NAME);
|
|
})
|
|
);
|
|
}
|
|
});
|
|
|
|
console.log('[SW] Service Worker loaded successfully');
|