mirror of
https://github.com/amir20/dozzle.git
synced 2026-06-23 04:10:12 +00:00
feat: add PWA support with dynamic base URL (#4608)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,3 +7,7 @@ Object.values(import.meta.glob<{ install: (app: VueApp) => void }>("./modules/*.
|
||||
i.install?.(app),
|
||||
);
|
||||
app.mount("#app");
|
||||
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.register(withBase("/sw.js"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (h *handler) manifest(w http.ResponseWriter, req *http.Request) {
|
||||
base := ""
|
||||
if h.config.Base != "/" {
|
||||
base = h.config.Base
|
||||
}
|
||||
|
||||
manifest := map[string]any{
|
||||
"name": "Dozzle",
|
||||
"short_name": "Dozzle",
|
||||
"start_url": base + "/",
|
||||
"display": "standalone",
|
||||
"lang": "en",
|
||||
"scope": base + "/",
|
||||
"description": "A log viewer for containers",
|
||||
"icons": []map[string]string{
|
||||
{"src": base + "/apple-touch-icon.png", "sizes": "512x512", "type": "image/png"},
|
||||
},
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/manifest+json")
|
||||
json.NewEncoder(w).Encode(manifest)
|
||||
}
|
||||
@@ -199,6 +199,8 @@ func createRouter(h *handler) *chi.Mux {
|
||||
})
|
||||
|
||||
r.Get("/healthcheck", h.healthcheck)
|
||||
r.Get("/manifest.webmanifest", h.manifest)
|
||||
r.Get("/sw.js", h.serviceWorker)
|
||||
|
||||
defaultHandler := http.StripPrefix(strings.Replace(base+"/", "//", "/", 1), http.HandlerFunc(h.index))
|
||||
r.With(Brotli).Get("/*", func(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const serviceWorkerTemplate = `
|
||||
const CACHE_NAME = "dozzle-%s";
|
||||
|
||||
self.addEventListener("install", (event) => {
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((names) =>
|
||||
Promise.all(
|
||||
names.filter((name) => name !== CACHE_NAME).map((name) => caches.delete(name))
|
||||
)
|
||||
)
|
||||
);
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
self.addEventListener("fetch", (event) => {
|
||||
const url = new URL(event.request.url);
|
||||
|
||||
// Cache immutable hashed assets (Vite adds hashes to filenames)
|
||||
if (url.pathname.match(/\/assets\/.*\.[a-f0-9]{8}\./)) {
|
||||
event.respondWith(
|
||||
caches.match(event.request).then((cached) => {
|
||||
if (cached) return cached;
|
||||
return fetch(event.request).then((response) => {
|
||||
if (response.ok) {
|
||||
const clone = response.clone();
|
||||
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
|
||||
}
|
||||
return response;
|
||||
});
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Network-first for everything else (API calls, HTML, etc.)
|
||||
});
|
||||
`
|
||||
|
||||
func (h *handler) serviceWorker(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/javascript")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
fmt.Fprintf(w, serviceWorkerTemplate, h.config.Version)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "Dozzle",
|
||||
"short_name": "Dozzle",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"lang": "en",
|
||||
"scope": "/",
|
||||
"description": "A log viewer for containers",
|
||||
"icons": [{ "src": "/apple-touch-icon.png", "sizes": "512x512", "type": "image/png" }]
|
||||
}
|
||||
Reference in New Issue
Block a user