mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-27 07:14:08 +09:00
Phase 37: Progressive Web App manifest, service worker for offline caching, push notification registration, quick actions API, mobile summary endpoint, and offline data caching for server status. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
77 lines
2.0 KiB
JavaScript
77 lines
2.0 KiB
JavaScript
// ServerKit Service Worker for PWA / Offline support
|
|
|
|
const CACHE_NAME = 'serverkit-v1';
|
|
const OFFLINE_URL = '/';
|
|
|
|
// Assets to cache on install
|
|
const PRECACHE_ASSETS = [
|
|
'/',
|
|
'/favicon.svg',
|
|
'/manifest.json',
|
|
];
|
|
|
|
self.addEventListener('install', (event) => {
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
return cache.addAll(PRECACHE_ASSETS);
|
|
})
|
|
);
|
|
self.skipWaiting();
|
|
});
|
|
|
|
self.addEventListener('activate', (event) => {
|
|
event.waitUntil(
|
|
caches.keys().then((keys) => {
|
|
return Promise.all(
|
|
keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))
|
|
);
|
|
})
|
|
);
|
|
self.clients.claim();
|
|
});
|
|
|
|
self.addEventListener('fetch', (event) => {
|
|
// Skip API requests — always go to network
|
|
if (event.request.url.includes('/api/')) {
|
|
return;
|
|
}
|
|
|
|
event.respondWith(
|
|
fetch(event.request).catch(() => {
|
|
return caches.match(event.request).then((cached) => {
|
|
return cached || caches.match(OFFLINE_URL);
|
|
});
|
|
})
|
|
);
|
|
});
|
|
|
|
// Push notification handler
|
|
self.addEventListener('push', (event) => {
|
|
const data = event.data ? event.data.json() : {};
|
|
const title = data.title || 'ServerKit';
|
|
const options = {
|
|
body: data.body || 'New notification',
|
|
icon: '/favicon.svg',
|
|
badge: '/favicon.svg',
|
|
data: data.url || '/',
|
|
actions: data.actions || [],
|
|
};
|
|
|
|
event.waitUntil(self.registration.showNotification(title, options));
|
|
});
|
|
|
|
self.addEventListener('notificationclick', (event) => {
|
|
event.notification.close();
|
|
const url = event.notification.data || '/';
|
|
event.waitUntil(
|
|
self.clients.matchAll({ type: 'window' }).then((clients) => {
|
|
for (const client of clients) {
|
|
if (client.url === url && 'focus' in client) {
|
|
return client.focus();
|
|
}
|
|
}
|
|
return self.clients.openWindow(url);
|
|
})
|
|
);
|
|
});
|