Bläddra i källkod

refactor(ui): use consistent cache-busted URL pattern for all static assets

Move JS and CSS routes to /js/{checksum}/{filename} and
/stylesheets/{checksum}/{filename}, matching the icon route pattern.
Use full filenames (e.g. "app.js", "light_serif.css") as bundle map
keys so handlers can use PathValue directly without suffix stripping.
Frédéric Guillot 2 veckor sedan
förälder
incheckning
0162c467f2

+ 3 - 3
internal/template/templates/common/layout.html

@@ -27,8 +27,8 @@
 
     {{ $cspNonce := nonce }}
     {{ csp .user $cspNonce | safeHTML }}
-    <link rel="stylesheet" nonce="{{ $cspNonce }}" type="text/css" href="{{ routePath "/stylesheets/%s.%s.css" .theme .theme_checksum }}">
-    <script nonce="{{ $cspNonce }}" src="{{ routePath "/%s.%s.js" "app" .app_js_checksum }}" type="module"></script>
+    <link rel="stylesheet" nonce="{{ $cspNonce }}" type="text/css" href="{{ routePath "/stylesheets/%s/%s.css" .theme_checksum .theme }}">
+    <script nonce="{{ $cspNonce }}" src="{{ routePath "/js/%s/%s.js" .app_js_checksum "app" }}" type="module"></script>
     {{ if .user -}}
         {{ if .user.Stylesheet -}}
         <style nonce="{{ $cspNonce }}">{{ .user.Stylesheet | safeCSS }}</style>
@@ -39,7 +39,7 @@
     {{ end -}}
 </head>
 <body
-    data-service-worker-url="{{ routePath "/%s.%s.js" "service-worker" .sw_js_checksum }}"
+    data-service-worker-url="{{ routePath "/js/%s/%s.js" .sw_js_checksum "service-worker" }}"
     {{ if .csrf }}data-csrf-token="{{ .csrf }}"{{ end }}
     data-add-subscription-url="{{ routePath "/subscribe" }}"
     data-entries-status-url="{{ routePath "/entry/status" }}"

+ 1 - 1
internal/ui/middleware.go

@@ -173,7 +173,7 @@ func isPublicRoute(r *http.Request) bool {
 		strings.HasSuffix(path, "/callback") && strings.HasPrefix(path, "/oauth2/") ||
 		strings.HasPrefix(path, "/share/") ||
 		strings.HasPrefix(path, "/proxy/") ||
-		strings.HasSuffix(path, ".js") {
+		strings.HasPrefix(path, "/js/") {
 		return true
 	}
 

+ 4 - 4
internal/ui/static/static.go

@@ -91,7 +91,7 @@ func GenerateStylesheetsBundles() error {
 	minifier := minify.New()
 	minifier.AddFunc("text/css", css.Minify)
 
-	for bundle, srcFiles := range bundles {
+	for bundleName, srcFiles := range bundles {
 		var buffer bytes.Buffer
 
 		for _, srcFile := range srcFiles {
@@ -108,7 +108,7 @@ func GenerateStylesheetsBundles() error {
 			return err
 		}
 
-		StylesheetBundles[bundle] = asset{
+		StylesheetBundles[bundleName+".css"] = asset{
 			Data:     minifiedData,
 			Checksum: crypto.HashFromBytes(minifiedData),
 		}
@@ -141,7 +141,7 @@ func GenerateJavascriptBundles(webauthnEnabled bool) error {
 	minifier := minify.New()
 	minifier.AddFunc("text/javascript", jsMinifier.Minify)
 
-	for bundle, srcFiles := range bundles {
+	for bundleName, srcFiles := range bundles {
 		var buffer bytes.Buffer
 
 		for _, srcFile := range srcFiles {
@@ -158,7 +158,7 @@ func GenerateJavascriptBundles(webauthnEnabled bool) error {
 			return err
 		}
 
-		JavascriptBundles[bundle] = asset{
+		JavascriptBundles[bundleName+".js"] = asset{
 			Data:     minifiedData,
 			Checksum: crypto.HashFromBytes(minifiedData),
 		}

+ 5 - 12
internal/ui/static_javascript.go

@@ -18,24 +18,17 @@ const licensePrefix = "//@license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d3
 const licenseSuffix = "\n//@license-end"
 
 func (h *handler) showJavascript(w http.ResponseWriter, r *http.Request) {
-	// The filename path value contains "name.checksum.js"; reject non-JS requests
-	// and extract the name portion.
-	rawFilename := r.PathValue("filename")
-	if !strings.HasSuffix(rawFilename, ".js") {
-		response.HTMLNotFound(w, r)
-		return
-	}
-	filename, _, _ := strings.Cut(strings.TrimSuffix(rawFilename, ".js"), ".")
-	js, found := static.JavascriptBundles[filename]
+	filename := r.PathValue("filename")
+	javascriptBundle, found := static.JavascriptBundles[filename]
 	if !found {
 		response.HTMLNotFound(w, r)
 		return
 	}
 
-	response.NewBuilder(w, r).WithCaching(js.Checksum, 48*time.Hour, func(b *response.Builder) {
-		contents := js.Data
+	response.NewBuilder(w, r).WithCaching(javascriptBundle.Checksum, 48*time.Hour, func(b *response.Builder) {
+		contents := javascriptBundle.Data
 
-		if filename == "service-worker" {
+		if filename == "service-worker.js" {
 			variables := fmt.Sprintf(`const OFFLINE_URL=%q;`, h.routePath("/offline"))
 			contents = append([]byte(variables), contents...)
 		}

+ 3 - 6
internal/ui/static_stylesheet.go

@@ -5,7 +5,6 @@ package ui // import "miniflux.app/v2/internal/ui"
 
 import (
 	"net/http"
-	"strings"
 	"time"
 
 	"miniflux.app/v2/internal/http/response"
@@ -14,17 +13,15 @@ import (
 )
 
 func (h *handler) showStylesheet(w http.ResponseWriter, r *http.Request) {
-	// The filename path value contains "name.checksum.css"; extract the name portion.
-	filename, _, _ := strings.Cut(r.PathValue("filename"), ".")
-	m, found := static.StylesheetBundles[filename]
+	stylesheetBundle, found := static.StylesheetBundles[r.PathValue("filename")]
 	if !found {
 		response.HTMLNotFound(w, r)
 		return
 	}
 
-	response.NewBuilder(w, r).WithCaching(m.Checksum, 48*time.Hour, func(b *response.Builder) {
+	response.NewBuilder(w, r).WithCaching(stylesheetBundle.Checksum, 48*time.Hour, func(b *response.Builder) {
 		b.WithHeader("Content-Type", "text/css; charset=utf-8")
-		b.WithBodyAsBytes(m.Data)
+		b.WithBodyAsBytes(stylesheetBundle.Data)
 		b.Write()
 	})
 }

+ 3 - 3
internal/ui/ui.go

@@ -26,10 +26,10 @@ func Serve(store *storage.Storage, pool *worker.Pool) http.Handler {
 	mux := http.NewServeMux()
 
 	// Static assets.
-	mux.HandleFunc("GET /stylesheets/{filename}", handler.showStylesheet)
-	mux.HandleFunc("GET /{filename}", handler.showJavascript)
-	mux.HandleFunc("GET /favicon.ico", handler.showFavicon)
+	mux.HandleFunc("GET /stylesheets/{checksum}/{filename}", handler.showStylesheet)
+	mux.HandleFunc("GET /js/{checksum}/{filename}", handler.showJavascript)
 	mux.HandleFunc("GET /icon/{checksum}/{filename}", handler.showAppIcon)
+	mux.HandleFunc("GET /favicon.ico", handler.showFavicon)
 	mux.HandleFunc("GET /manifest.json", handler.showWebManifest)
 
 	// New subscription pages.

+ 3 - 3
internal/ui/view/view.go

@@ -41,9 +41,9 @@ func New(tpl *template.Engine, r *http.Request, sess *session.Session) *view {
 		"flashErrorMessage": sess.FlashErrorMessage(request.FlashErrorMessage(r)),
 		"theme":             theme,
 		"language":          request.UserLanguage(r),
-		"theme_checksum":    static.StylesheetBundles[theme].Checksum,
-		"app_js_checksum":   static.JavascriptBundles["app"].Checksum,
-		"sw_js_checksum":    static.JavascriptBundles["service-worker"].Checksum,
+		"theme_checksum":    static.StylesheetBundles[theme+".css"].Checksum,
+		"app_js_checksum":   static.JavascriptBundles["app.js"].Checksum,
+		"sw_js_checksum":    static.JavascriptBundles["service-worker.js"].Checksum,
 		"webAuthnEnabled":   config.Opts.WebAuthn(),
 	}}
 }