UI files: ETag and revalidation on each load

The embedded UI was served without any cache header, so a browser or a
proxy could keep an old app.js after an update (seen with the API token
display). Each file now has an ETag (hash of its content, computed at
startup); the page, app.js and style.css are sent with "no-cache", so
each load costs a 304 without body until the next version. Fonts are
cached for 30 days.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
cedricandClaude Opus 5.5 committed 2026-11-01 16:11:41 +01:00
1 parent 367420961e
commit 3e703c8792
5 files changed
+89 -1

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
package main
import (
"net/http"
"net/http/httptest"
"testing"
"testing/fstest"
)
func TestStaticFilesCache(t *testing.T) {
h := staticFiles(fstest.MapFS{
"index.html": {Data: []byte("<p>v1</p>")},
"app.js": {Data: []byte("v1")},
"fonts/mono.woff2": {Data: []byte("font")},
})
get := func(path, etag string) *httptest.ResponseRecorder {
req := httptest.NewRequest("GET", path, nil)
if etag != "" {
req.Header.Set("If-None-Match", etag)
}
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
return rec
}
rec := get("/app.js", "")
tag := rec.Header().Get("ETag")
if rec.Code != http.StatusOK || tag == "" || rec.Header().Get("Cache-Control") != "no-cache" {
t.Fatalf("app.js: %d, ETag %q, Cache-Control %q", rec.Code, tag, rec.Header().Get("Cache-Control"))
}
if rec := get("/app.js", tag); rec.Code != http.StatusNotModified || rec.Body.Len() != 0 {
t.Fatalf("revalidation: %d with %d bytes, want 304 without body", rec.Code, rec.Body.Len())
}
if rec := get("/app.js", `"old"`); rec.Code != http.StatusOK || rec.Body.String() != "v1" {
t.Fatalf("stale ETag: %d %q", rec.Code, rec.Body)
}
if rec := get("/", ""); rec.Code != http.StatusOK || rec.Header().Get("ETag") == "" || rec.Header().Get("Cache-Control") != "no-cache" {
t.Fatalf("index: %d %v", rec.Code, rec.Header())
}
if rec := get("/fonts/mono.woff2", ""); rec.Header().Get("Cache-Control") != "public, max-age=2592000" {
t.Fatalf("font Cache-Control %q", rec.Header().Get("Cache-Control"))
}
}