Files
ServerKit/frontend/public/sw.js
T
Juan DenisandClaude Opus 4.8 70027b9fea Revert panel de-branding — keep the ServerKit identity, keep only noindex
Restore the ServerKit name, favicon, manifest, auth-page headings, and
service-worker strings. Genericizing the panel's identity was the wrong
trade: hiding the title/favicon is security-by-obscurity that a real scanner
sees through, and it erased the product identity for almost no benefit. The
pages it touched (login/setup/register) require server or admin access to
reach anyway, so there is nothing to hide there.

The one durable, no-cost measure is kept: the `X-Robots-Tag: noindex`
header (from the prior commit) stays on both nginx server blocks so the
panel isn't indexed by search engines. Real protection lives in access
control + auth, not in the favicon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 12:40:21 -04:00

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);
})
);
});