fix (cache): improved caching

This commit is contained in:
MickaelK 2025-09-01 11:43:44 +10:00
parent ba5846375f
commit c6a3808957
3 changed files with 52 additions and 43 deletions

View file

@ -17,6 +17,7 @@ self.addEventListener("activate", (event) => {
self.addEventListener("fetch", async(event) => { self.addEventListener("fetch", async(event) => {
if (!event.request.url.startsWith(location.origin + "/assets/")) return; if (!event.request.url.startsWith(location.origin + "/assets/")) return;
else if (event.request.url.startsWith(location.origin + "/assets/bundle.js")) return;
event.respondWith((async() => { event.respondWith((async() => {
const cache = await caches.open(VERSION); const cache = await caches.open(VERSION);
@ -29,14 +30,13 @@ self.addEventListener("fetch", async(event) => {
self.addEventListener("message", (event) => { self.addEventListener("message", (event) => {
if (event.data.type === "preload") handlePreloadMessage( if (event.data.type === "preload") handlePreloadMessage(
event.data.payload, event.data.payload,
event.data.clear,
event.data.version, event.data.version,
() => event.source.postMessage({ type: "preload", status: "ok" }), () => event.source.postMessage({ type: "preload", status: "ok" }),
(err) => event.source.postMessage({ type: "preload", status: "error", msg: err.message }), (err) => event.source.postMessage({ type: "preload", status: "error", msg: err.message }),
); );
}); });
async function handlePreloadMessage(imports, clear, version, resolve, reject) { async function handlePreloadMessage(imports, version, resolve, reject) {
VERSION = version; VERSION = version;
try { try {
await caches.keys().then(async(names) => { await caches.keys().then(async(names) => {

View file

@ -71,7 +71,6 @@
"type": "preload", "type": "preload",
"payload": bundler.state(), "payload": bundler.state(),
"version": "{{ slice .version 0 7 }}::{{ .hash }}", "version": "{{ slice .version 0 7 }}::{{ .hash }}",
"clear": {{ .clear }},
}); });
await new Promise((resolve, reject) => navigator.serviceWorker.addEventListener("message", (event) => { await new Promise((resolve, reject) => navigator.serviceWorker.addEventListener("message", (event) => {
if (event.data && event.data.type === "preload") { if (event.data && event.data.type === "preload") {

View file

@ -47,11 +47,6 @@ func ServeBackofficeHandler(ctx *App, res http.ResponseWriter, req *http.Request
http.Redirect(res, req, URL_SETUP, http.StatusTemporaryRedirect) http.Redirect(res, req, URL_SETUP, http.StatusTemporaryRedirect)
return return
} }
head := res.Header()
head.Set("Cache-Control", "no-cache")
head.Set("Pragma", "no-cache")
head.Set("Expires", "0")
ServeIndex("index.backoffice.html")(ctx, res, req) ServeIndex("index.backoffice.html")(ctx, res, req)
return return
} }
@ -83,12 +78,6 @@ func ServeFrontofficeHandler(ctx *App, res http.ResponseWriter, req *http.Reques
http.Redirect(res, req, URL_SETUP, http.StatusTemporaryRedirect) http.Redirect(res, req, URL_SETUP, http.StatusTemporaryRedirect)
return return
} }
head := res.Header()
head.Set("Cache-Control", "no-cache")
head.Set("Pragma", "no-cache")
head.Set("Expires", "0")
ServeIndex("index.frontoffice.html")(ctx, res, req) ServeIndex("index.frontoffice.html")(ctx, res, req)
} }
@ -153,11 +142,9 @@ func ServeFile(chroot string) func(*App, http.ResponseWriter, *http.Request) {
), ),
) )
head := res.Header() head := res.Header()
head.Set("Cache-Control", "no-cache")
if f := applyPatch(filePath); f != nil { if f := applyPatch(filePath); f != nil {
head.Set("Content-Type", GetMimeType(filepath.Ext(filePath))) head.Set("Content-Type", GetMimeType(filepath.Ext(filePath)))
head.Set("Cache-Control", "no-cache")
head.Set("Pragma", "no-cache")
head.Set("Expires", "0")
res.WriteHeader(http.StatusOK) res.WriteHeader(http.StatusOK)
res.Write(f.Bytes()) res.Write(f.Bytes())
return return
@ -229,16 +216,14 @@ func ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request)
head := res.Header() head := res.Header()
sign := signature() sign := signature()
base := WithBase("/") base := WithBase("/")
clear := req.Header.Get("Cache-Control") == "no-cache"
templateData := map[string]any{ templateData := map[string]any{
"base": base, "base": base,
"version": BUILD_REF, "version": BUILD_REF,
"license": LICENSE, "license": LICENSE,
"clear": clear,
"hash": sign, "hash": sign,
"favicon": favicon(), "favicon": favicon(),
} }
calculatedEtag := QuickHash(base+BUILD_REF+LICENSE+fmt.Sprintf("%t", clear)+sign, 10) calculatedEtag := QuickHash(base+BUILD_REF+LICENSE+sign, 10)
head.Set("ETag", calculatedEtag) head.Set("ETag", calculatedEtag)
if etag := req.Header.Get("If-None-Match"); etag == calculatedEtag { if etag := req.Header.Get("If-None-Match"); etag == calculatedEtag {
res.WriteHeader(http.StatusNotModified) res.WriteHeader(http.StatusNotModified)
@ -252,6 +237,7 @@ func ServeIndex(indexPath string) func(*App, http.ResponseWriter, *http.Request)
out = gz out = gz
} }
head.Set("Content-Type", "text/html") head.Set("Content-Type", "text/html")
head.Set("Cache-Control", "no-cache")
tmpl.Execute(out, templateData) tmpl.Execute(out, templateData)
} }
} }
@ -376,7 +362,10 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
"/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.css", "/assets/" + BUILD_REF + "/pages/viewerpage/component_menubar.css",
} }
var bundlePlain bytes.Buffer var isDebug = os.Getenv("DEBUG") == "true"
build := func(quality int) (bundlePlain []byte, bundleBr []byte, etag string) {
var buf bytes.Buffer
for _, path := range paths { for _, path := range paths {
curPath := "/assets/" + strings.TrimPrefix(path, "/assets/"+BUILD_REF+"/") curPath := "/assets/" + strings.TrimPrefix(path, "/assets/"+BUILD_REF+"/")
f := applyPatch(curPath) f := applyPatch(curPath)
@ -398,18 +387,39 @@ func ServeBundle() func(*App, http.ResponseWriter, *http.Request) {
Log.Warning("static::bundle msg=marshal_failed path=%s err=%s", path, err.Error()) Log.Warning("static::bundle msg=marshal_failed path=%s err=%s", path, err.Error())
continue continue
} }
fmt.Fprintf(&bundlePlain, "bundler.register(%q, %s);\n", path, code) fmt.Fprintf(&buf, "bundler.register(%q, %s);\n", path, code)
} }
bundleBr, _ := cbrotli.Encode(bundlePlain.Bytes(), cbrotli.WriterOptions{Quality: 8}) etag = QuickHash(string(bundlePlain), 10)
bundlePlain = buf.Bytes()
if quality > 0 {
bundleBr, _ = cbrotli.Encode(bundlePlain, cbrotli.WriterOptions{Quality: quality})
}
return bundlePlain, bundleBr, etag
}
quality := 11
if isDebug {
quality = 8
}
bundlePlain, bundleBr, etag := build(quality)
return func(ctx *App, res http.ResponseWriter, req *http.Request) { return func(ctx *App, res http.ResponseWriter, req *http.Request) {
res.Header().Set("Content-Type", "application/javascript") if isDebug {
if strings.Contains(req.Header.Get("Accept-Encoding"), "br") && req.Header.Get("Cache-Control") != "no-cache" { bundlePlain, bundleBr, etag = build(quality)
res.Header().Set("Content-Encoding", "br") }
head := res.Header()
head.Set("Content-Type", "application/javascript")
head.Set("Cache-Control", "no-cache")
head.Set("Etag", etag)
if req.Header.Get("If-None-Match") == etag && etag != "" {
res.WriteHeader(http.StatusNotModified)
return
} else if strings.Contains(req.Header.Get("Accept-Encoding"), "br") && len(bundleBr) > 0 {
head.Set("Content-Encoding", "br")
res.Write(bundleBr) res.Write(bundleBr)
return return
} }
res.Write(bundlePlain.Bytes()) res.Write(bundlePlain)
} }
} }