Logo für Webseiten-Tab implementiert
Dieser Commit ist enthalten in:
committet von
Server Deploy
Ursprung
ef153789cc
Commit
5b1f8b1cfe
128
frontend/sw.js
128
frontend/sw.js
@ -4,7 +4,7 @@
|
||||
* Offline support and caching
|
||||
*/
|
||||
|
||||
const CACHE_VERSION = '292';
|
||||
const CACHE_VERSION = '297';
|
||||
const CACHE_NAME = 'taskmate-v' + CACHE_VERSION;
|
||||
const STATIC_CACHE_NAME = 'taskmate-static-v' + CACHE_VERSION;
|
||||
const DYNAMIC_CACHE_NAME = 'taskmate-dynamic-v' + CACHE_VERSION;
|
||||
@ -43,6 +43,7 @@ const STATIC_ASSETS = [
|
||||
'/js/mobile.js',
|
||||
'/js/reminders.js',
|
||||
'/js/contacts.js',
|
||||
'/js/pwa.js',
|
||||
'/css/list.css',
|
||||
'/css/mobile.css',
|
||||
'/css/admin.css',
|
||||
@ -52,13 +53,31 @@ const STATIC_ASSETS = [
|
||||
'/css/knowledge.css',
|
||||
'/css/coding.css',
|
||||
'/css/reminders.css',
|
||||
'/css/contacts.css'
|
||||
'/css/contacts.css',
|
||||
'/css/pwa.css',
|
||||
'/manifest.json'
|
||||
];
|
||||
|
||||
// API routes to cache
|
||||
const API_CACHE_ROUTES = [
|
||||
'/api/projects',
|
||||
'/api/auth/users'
|
||||
'/api/auth/users',
|
||||
'/api/columns',
|
||||
'/api/tasks',
|
||||
'/api/labels',
|
||||
'/api/proposals',
|
||||
'/api/knowledge',
|
||||
'/api/reminders',
|
||||
'/api/contacts'
|
||||
];
|
||||
|
||||
// Assets to skip caching
|
||||
const SKIP_CACHE_PATTERNS = [
|
||||
/\/api\/auth\/login/,
|
||||
/\/api\/auth\/logout/,
|
||||
/\/api\/files\//,
|
||||
/\/uploads\//,
|
||||
/socket\.io/
|
||||
];
|
||||
|
||||
// Install event - cache static assets
|
||||
@ -121,42 +140,88 @@ self.addEventListener('fetch', (event) => {
|
||||
event.respondWith(handleStaticRequest(event.request));
|
||||
});
|
||||
|
||||
// Handle static asset requests - Network First strategy
|
||||
// Handle static asset requests - Cache First for assets, Network First for HTML
|
||||
async function handleStaticRequest(request) {
|
||||
// Try network first to always get fresh content
|
||||
try {
|
||||
const networkResponse = await fetch(request);
|
||||
|
||||
// Cache successful responses
|
||||
if (networkResponse.ok) {
|
||||
const cache = await caches.open(STATIC_CACHE_NAME);
|
||||
cache.put(request, networkResponse.clone());
|
||||
}
|
||||
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
// If network fails, try cache
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
console.log('[SW] Serving from cache (offline):', request.url);
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// Return offline page if available for navigation
|
||||
if (request.mode === 'navigate') {
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Check if should skip caching
|
||||
if (SKIP_CACHE_PATTERNS.some(pattern => pattern.test(url.pathname))) {
|
||||
return fetch(request);
|
||||
}
|
||||
|
||||
// For HTML files, use Network First
|
||||
if (request.mode === 'navigate' || url.pathname.endsWith('.html') || url.pathname === '/') {
|
||||
try {
|
||||
const networkResponse = await fetch(request);
|
||||
|
||||
if (networkResponse.ok) {
|
||||
const cache = await caches.open(STATIC_CACHE_NAME);
|
||||
cache.put(request, networkResponse.clone());
|
||||
}
|
||||
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
console.log('[SW] Serving HTML from cache (offline):', request.url);
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// Return offline page
|
||||
const offlinePage = await caches.match('/index.html');
|
||||
if (offlinePage) {
|
||||
return offlinePage;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// For static assets (CSS, JS, images), use Cache First
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
// Update cache in background
|
||||
fetchAndCache(request);
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// If not in cache, fetch from network
|
||||
try {
|
||||
const networkResponse = await fetch(request);
|
||||
|
||||
if (networkResponse.ok) {
|
||||
const cache = await caches.open(STATIC_CACHE_NAME);
|
||||
cache.put(request, networkResponse.clone());
|
||||
}
|
||||
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
console.error('[SW] Network failed for:', request.url);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Background cache update
|
||||
async function fetchAndCache(request) {
|
||||
try {
|
||||
const networkResponse = await fetch(request);
|
||||
if (networkResponse.ok) {
|
||||
const cache = await caches.open(STATIC_CACHE_NAME);
|
||||
cache.put(request, networkResponse);
|
||||
}
|
||||
} catch (error) {
|
||||
// Silently fail - we already served from cache
|
||||
}
|
||||
}
|
||||
|
||||
// Handle API requests
|
||||
async function handleApiRequest(request) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Check if should skip caching
|
||||
if (SKIP_CACHE_PATTERNS.some(pattern => pattern.test(url.pathname))) {
|
||||
return fetch(request);
|
||||
}
|
||||
|
||||
// Check if this is a cacheable API route
|
||||
const isCacheable = API_CACHE_ROUTES.some(route =>
|
||||
@ -180,13 +245,22 @@ async function handleApiRequest(request) {
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
console.log('[SW] Serving cached API response:', request.url);
|
||||
return cachedResponse;
|
||||
// Add offline header to indicate cached response
|
||||
const headers = new Headers(cachedResponse.headers);
|
||||
headers.set('X-From-Cache', 'true');
|
||||
|
||||
return new Response(cachedResponse.body, {
|
||||
status: cachedResponse.status,
|
||||
statusText: cachedResponse.statusText,
|
||||
headers: headers
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Return error response
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
error: 'Keine Internetverbindung',
|
||||
offline: true
|
||||
}),
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren