marky

Lightweight bookmarking + plaintext archive
Log | Files | Refs | README | LICENSE

commit 5563d32bc82884394ef7bfff647d21d632a6f178
parent ec9b947472ab9436481f43599a924b110e2ba5f5
Author: David Eisinger <[email protected]>
Date:   Thu,  9 Oct 2025 14:32:36 -0400

Cache busting

Diffstat:
Minternal/api.go | 2++
Minternal/static.go | 40++++++++++++++++++++++++++++++++++++++++
2 files changed, 42 insertions(+), 0 deletions(-)

diff --git a/internal/api.go b/internal/api.go @@ -362,6 +362,7 @@ func NewHandlerWithConfig(db *DB, cfg HandlerConfig) (http.Handler, error) { html = out } } + html = applyCacheBust(html) w.Header().Set("Content-Type", "text/html; charset=utf-8") _, _ = w.Write(html) return @@ -441,6 +442,7 @@ func NewHandlerWithConfig(db *DB, cfg HandlerConfig) (http.Handler, error) { } } } + html = applyCacheBust(html) w.Header().Set("Content-Type", "text/html; charset=utf-8") _, _ = w.Write(html) return diff --git a/internal/static.go b/internal/static.go @@ -3,6 +3,8 @@ package internal import ( "bytes" "embed" + "crypto/sha256" + "fmt" "io/fs" "net/http" "path" @@ -46,6 +48,8 @@ func StaticHandlerWithConfig(cfg HandlerConfig) (http.Handler, error) { indexBytes = nb } } + // Apply cache-busting query params to static asset URLs + indexBytes = applyCacheBust(indexBytes) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { p := strings.TrimPrefix(r.URL.Path, "/") if p == "" { @@ -94,3 +98,39 @@ func RenderPage(name string, cfg HandlerConfig) ([]byte, error) { } return pageBytes, nil } + +// applyCacheBust appends a short content hash to known asset URLs in HTML, +// e.g., /app.js?v=abcd1234. This helps CDNs like Cloudflare pick up changes +// without manual purges. +func applyCacheBust(html []byte) []byte { + sub, err := fs.Sub(webAssets, "web") + if err != nil { return html } + // Build fingerprint map for assets we serve locally + assets := []string{ + "app.js", + "styles.css", + "vendor/vue.global.prod.js", + "vendor/marked.min.js", + "vendor/purify.min.js", + } + bust := map[string]string{} + for _, p := range assets { + if b, err := fs.ReadFile(sub, p); err == nil { + sum := sha256.Sum256(b) + bust[p] = fmt.Sprintf("%x", sum)[:10] + } + } + // Replace src/href occurrences + out := html + for p, v := range bust { + // src="/path" + from := []byte("src=\"/"+p+"\"") + to := []byte("src=\"/"+p+"?v="+v+"\"") + out = bytes.ReplaceAll(out, from, to) + // href="/path" + fromH := []byte("href=\"/"+p+"\"") + toH := []byte("href=\"/"+p+"?v="+v+"\"") + out = bytes.ReplaceAll(out, fromH, toH) + } + return out +}