| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253 |
- //go:build embed || unit
- package web
- import (
- "net/http"
- "path"
- "strings"
- )
- // Vite emits content-hashed filenames under assets/, so the backend can apply
- // immutable caching without relying on a reverse proxy to classify paths.
- const staticAssetsCacheControl = "public, max-age=31536000, immutable"
- // isFingerprintedEmbeddedAssetPath reports whether a cleaned URL path refers to
- // a Vite asset whose filename contains the default eight-character build hash.
- func isFingerprintedEmbeddedAssetPath(cleanPath string) bool {
- cleanPath = strings.TrimPrefix(cleanPath, "/")
- if !strings.HasPrefix(cleanPath, "assets/") {
- return false
- }
- filename := path.Base(cleanPath)
- extension := path.Ext(filename)
- stem := strings.TrimSuffix(filename, extension)
- const fingerprintLength = 8
- delimiterIndex := len(stem) - fingerprintLength - 1
- if extension == "" || delimiterIndex < 1 || stem[delimiterIndex] != '-' {
- return false
- }
- // Vite hashes use URL-safe characters and are stable for immutable caching.
- fingerprint := stem[delimiterIndex+1:]
- for _, char := range fingerprint {
- if (char >= 'a' && char <= 'z') ||
- (char >= 'A' && char <= 'Z') ||
- (char >= '0' && char <= '9') ||
- char == '_' || char == '-' {
- continue
- }
- return false
- }
- return true
- }
- // applyStaticAssetCacheHeaders sets Cache-Control for long-cacheable static paths.
- // index.html / SPA routes must keep no-cache and are not handled here.
- func applyStaticAssetCacheHeaders(header http.Header, cleanPath string) {
- if header == nil || !isFingerprintedEmbeddedAssetPath(cleanPath) {
- return
- }
- header.Set("Cache-Control", staticAssetsCacheControl)
- }
|