diff --git a/internal/assets/files.go b/internal/assets/files.go index bfb2b4c..2c7c09e 100644 --- a/internal/assets/files.go +++ b/internal/assets/files.go @@ -1,8 +1,14 @@ package assets import ( + "crypto/md5" "embed" + "encoding/hex" + "io" "io/fs" + "log/slog" + "strconv" + "time" ) //go:embed static @@ -13,3 +19,38 @@ var _templateFS embed.FS var PublicFS, _ = fs.Sub(_publicFS, "static") var TemplateFS, _ = fs.Sub(_templateFS, "templates") + +func getFSHash(files fs.FS) string { + hash := md5.New() + + err := fs.WalkDir(files, ".", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + if d.IsDir() { + return nil + } + + file, err := files.Open(path) + + if err != nil { + return err + } + + if _, err := io.Copy(hash, file); err != nil { + return err + } + + return nil + }) + + if err == nil { + return hex.EncodeToString(hash.Sum(nil))[:10] + } + + slog.Warn("Could not compute assets cache", "err", err) + return strconv.FormatInt(time.Now().Unix(), 10) +} + +var PublicFSHash = getFSHash(PublicFS) diff --git a/internal/assets/static/manifest.json b/internal/assets/static/manifest.json index 668b289..42e8213 100644 --- a/internal/assets/static/manifest.json +++ b/internal/assets/static/manifest.json @@ -6,7 +6,7 @@ "start_url": "/", "icons": [ { - "src": "/static/app-icon.png", + "src": "app-icon.png", "type": "image/png", "sizes": "512x512" } diff --git a/internal/assets/templates/document.html b/internal/assets/templates/document.html index d126d8b..e044733 100644 --- a/internal/assets/templates/document.html +++ b/internal/assets/templates/document.html @@ -11,12 +11,12 @@ - - + + - - - + + + {{ block "document-head-after" . }}{{ end }} diff --git a/internal/glance/glance.go b/internal/glance/glance.go index 653be75..c1ce3e7 100644 --- a/internal/glance/glance.go +++ b/internal/glance/glance.go @@ -38,10 +38,10 @@ type Theme struct { } type Server struct { - Host string `yaml:"host"` - Port uint16 `yaml:"port"` - AssetsPath string `yaml:"assets-path"` - StartedAt time.Time `yaml:"-"` + Host string `yaml:"host"` + Port uint16 `yaml:"port"` + AssetsPath string `yaml:"assets-path"` + AssetsHash string `yaml:"-"` } type Column struct { @@ -189,7 +189,13 @@ func FileServerWithCache(fs http.FileSystem, cacheDuration time.Duration) http.H }) } +func (a *Application) AssetPath(asset string) string { + return "/static/" + a.Config.Server.AssetsHash + "/" + asset +} + func (a *Application) Serve() error { + a.Config.Server.AssetsHash = assets.PublicFSHash + // TODO: add gzip support, static files must have their gzipped contents cached // TODO: add HTTPS support mux := http.NewServeMux() @@ -197,7 +203,10 @@ func (a *Application) Serve() error { mux.HandleFunc("GET /{$}", a.HandlePageRequest) mux.HandleFunc("GET /{page}", a.HandlePageRequest) mux.HandleFunc("GET /api/pages/{page}/content/{$}", a.HandlePageContentRequest) - mux.Handle("GET /static/{path...}", http.StripPrefix("/static/", FileServerWithCache(http.FS(assets.PublicFS), 2*time.Hour))) + mux.Handle( + fmt.Sprintf("GET /static/%s/{path...}", a.Config.Server.AssetsHash), + http.StripPrefix("/static/"+a.Config.Server.AssetsHash, FileServerWithCache(http.FS(assets.PublicFS), 8*time.Hour)), + ) if a.Config.Server.AssetsPath != "" { absAssetsPath, err := filepath.Abs(a.Config.Server.AssetsPath) @@ -216,8 +225,6 @@ func (a *Application) Serve() error { Handler: mux, } - a.Config.Server.StartedAt = time.Now() - slog.Info("Starting server", "host", a.Config.Server.Host, "port", a.Config.Server.Port) return server.ListenAndServe() }