mirror of
https://github.com/Dvorinka/beszel.git
synced 2026-06-03 13:02:55 +00:00
7727be166b
Introduces the ability for registered users to trigger Beszel container updates directly from the web interface. - Added `app_update` logic to the hub to pull the latest image from GHCR and recreate the container. - Implemented `/api/beszel/update` and `/api/beszel/update/apply` endpoints. - Added a new `AppUpdatePanel` in the settings UI to check for and apply updates. - Added update notifications in the navbar and settings. - Updated `docker-compose.yml` and `README.md` to include the required Docker socket mount for update functionality. - Added a new public status page route that bypasses authentication. - Refactored several TypeScript interfaces to replace `any` with `unknown` or specific types for better type safety. - Updated localization files to support new update-related strings.
157 lines
3.7 KiB
JavaScript
157 lines
3.7 KiB
JavaScript
// Beszel Service Worker
|
|
const CACHE_NAME = "beszel-v1"
|
|
const STATIC_ASSETS = ["/", "/index.html", "/manifest.json", "/favicon.ico", "/favicon.svg"]
|
|
|
|
// Install event - cache static assets
|
|
self.addEventListener("install", (event) => {
|
|
event.waitUntil(
|
|
caches
|
|
.open(CACHE_NAME)
|
|
.then((cache) => {
|
|
return cache.addAll(STATIC_ASSETS)
|
|
})
|
|
.then(() => self.skipWaiting())
|
|
)
|
|
})
|
|
|
|
// Activate event - clean up old caches
|
|
self.addEventListener("activate", (event) => {
|
|
event.waitUntil(
|
|
caches
|
|
.keys()
|
|
.then((cacheNames) => {
|
|
return Promise.all(cacheNames.filter((name) => name !== CACHE_NAME).map((name) => caches.delete(name)))
|
|
})
|
|
.then(() => self.clients.claim())
|
|
)
|
|
})
|
|
|
|
// Fetch event - serve from cache or network
|
|
self.addEventListener("fetch", (event) => {
|
|
const { request } = event
|
|
const url = new URL(request.url)
|
|
|
|
// Skip non-GET requests
|
|
if (request.method !== "GET") {
|
|
return
|
|
}
|
|
|
|
// Skip API requests
|
|
if (url.pathname.startsWith("/api/")) {
|
|
return
|
|
}
|
|
|
|
// Skip PocketBase API
|
|
if (url.pathname.startsWith("/_/")) {
|
|
return
|
|
}
|
|
|
|
event.respondWith(
|
|
caches
|
|
.match(request)
|
|
.then((cached) => {
|
|
if (cached) {
|
|
// Return cached version and update in background
|
|
fetch(request).then((response) => {
|
|
if (response.ok) {
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
cache.put(request, response)
|
|
})
|
|
}
|
|
})
|
|
return cached
|
|
}
|
|
|
|
// Fetch from network
|
|
return fetch(request).then((response) => {
|
|
if (!response || response.status !== 200 || response.type !== "basic") {
|
|
return response
|
|
}
|
|
|
|
const responseToCache = response.clone()
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
cache.put(request, responseToCache)
|
|
})
|
|
|
|
return response
|
|
})
|
|
})
|
|
.catch(() => {
|
|
// Return offline page if available
|
|
return caches.match("/offline.html")
|
|
})
|
|
)
|
|
})
|
|
|
|
// Push notification event
|
|
self.addEventListener("push", (event) => {
|
|
if (!event.data) {
|
|
return
|
|
}
|
|
|
|
const data = event.data.json()
|
|
const options = {
|
|
body: data.body || "New notification",
|
|
icon: data.icon || "/favicon-192x192.png",
|
|
badge: data.badge || "/favicon-72x72.png",
|
|
tag: data.tag || "default",
|
|
requireInteraction: data.requireInteraction || false,
|
|
data: data.data || {},
|
|
actions: data.actions || [
|
|
{ action: "open", title: "Open" },
|
|
{ action: "close", title: "Dismiss" },
|
|
],
|
|
}
|
|
|
|
event.waitUntil(self.registration.showNotification(data.title || "Beszel Alert", options))
|
|
})
|
|
|
|
// Notification click event
|
|
self.addEventListener("notificationclick", (event) => {
|
|
event.notification.close()
|
|
|
|
const { data } = event.notification
|
|
const urlToOpen = data?.url || "/"
|
|
|
|
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 not found
|
|
if (clients.openWindow) {
|
|
return clients.openWindow(urlToOpen)
|
|
}
|
|
})
|
|
)
|
|
})
|
|
|
|
// Background sync for offline support
|
|
self.addEventListener("sync", (event) => {
|
|
if (event.tag === "background-sync") {
|
|
event.waitUntil(doBackgroundSync())
|
|
}
|
|
})
|
|
|
|
function doBackgroundSync() {
|
|
// Retry any pending API requests stored in IndexedDB
|
|
// This is a placeholder - implement with actual pending request logic
|
|
console.log("Background sync executed")
|
|
}
|
|
|
|
// Periodic background sync (if supported)
|
|
self.addEventListener("periodicsync", (event) => {
|
|
if (event.tag === "update-check") {
|
|
event.waitUntil(checkForUpdates())
|
|
}
|
|
})
|
|
|
|
function checkForUpdates() {
|
|
// Check for new data and show notifications if needed
|
|
console.log("Periodic sync executed")
|
|
}
|