Files
sub2api/backend/internal/web/static_cache.go
T

54 lines
1.6 KiB
Go
Raw Normal View History

//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)
}