Browse Source

Change caching mechanism

Svilen Markov 1 year ago
parent
commit
122d05346b

+ 41 - 0
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)

+ 1 - 1
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"
         }

+ 5 - 5
internal/assets/templates/document.html

@@ -11,12 +11,12 @@
     <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
     <meta name="apple-mobile-web-app-title" content="Glance">
     <meta name="theme-color" content="{{ if ne nil .App.Config.Theme.BackgroundColor }}{{ .App.Config.Theme.BackgroundColor }}{{ else }}hsl(240, 8%, 9%){{ end }}">
-    <link rel="apple-touch-icon" sizes="512x512" href="/static/app-icon.png">
-    <link rel="icon" type="image/png" sizes="50x50" href="/static/favicon.png">
+    <link rel="apple-touch-icon" sizes="512x512" href="{{ .App.AssetPath "app-icon.png" }}">
+    <link rel="icon" type="image/png" sizes="50x50" href="{{ .App.AssetPath "favicon.png" }}">
     <link rel="manifest" href="/static/manifest.json">
-    <link rel="icon" type="image/png" href="/static/favicon.png" />
-    <link rel="stylesheet" href="/static/main.css?v={{ .App.Config.Server.StartedAt.Unix }}">
-    <script async src="/static/main.js?v={{ .App.Config.Server.StartedAt.Unix }}"></script>
+    <link rel="icon" type="image/png" href="{{ .App.AssetPath "favicon.png" }}" />
+    <link rel="stylesheet" href="{{ .App.AssetPath "main.css" }}">
+    <script async src="{{ .App.AssetPath "main.js" }}"></script>
     {{ block "document-head-after" . }}{{ end }}
 </head>
 <body>

+ 14 - 7
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()
 }