package main import ( "crypto/sha256" "encoding/hex" "io/fs" "net/http" "strings" ) // staticFiles serves the embedded UI with an ETag per file (a hash of its // content, computed at startup). Pages, scripts and styles are revalidated on // every load ("no-cache"): an unchanged file costs a 304 without body, and the // files of a new version are always taken after an update. Fonts are kept for // 30 days. func staticFiles(static fs.FS) http.Handler { etags := map[string]string{} _ = fs.WalkDir(static, ".", func(path string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() { return err } b, err := fs.ReadFile(static, path) if err != nil { return err } sum := sha256.Sum256(b) etags["/"+path] = `"` + hex.EncodeToString(sum[:8]) + `"` return nil }) etags["/"] = etags["/index.html"] files := http.FileServer(http.FS(static)) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if tag, ok := etags[r.URL.Path]; ok { // http.FileServer answers If-None-Match with 304 from this header. w.Header().Set("ETag", tag) if strings.HasPrefix(r.URL.Path, "/fonts/") { w.Header().Set("Cache-Control", "public, max-age=2592000") } else { w.Header().Set("Cache-Control", "no-cache") } } files.ServeHTTP(w, r) }) }