From d3e5cbe18b38c1cc1ec69a960596b0e90e482033 Mon Sep 17 00:00:00 2001 From: Victor_Jay Date: Sun, 31 May 2026 01:53:17 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=9D=99=E6=80=81=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E7=BC=93=E5=AD=98=E7=A0=B4=E5=9D=8F=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=BA=A7=E5=93=88=E5=B8=8C=EF=BC=8C=E9=87=8D?= =?UTF-8?q?=E5=90=AF=E4=B8=8D=E5=88=B7=E6=96=B0=E6=9C=AA=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=9A=84=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 启动时计算每个 JS/CSS 的 SHA-1 前 8 位作为版本号 - 文件不变则哈希不变,浏览器继续用缓存,重启零压力 - 替换全局 AssetVersion 为 assetV 模板函数,按文件路径查询哈希 - 移除 BuildPageData 中的全局版本号注入 --- cmd/server/main.go | 4 + internal/common/helper.go | 96 +++++++++++++++++-- internal/theme/loader.go | 3 + templates/MetaLab-2026/html/auth/login.html | 4 +- .../MetaLab-2026/html/auth/register.html | 4 +- templates/MetaLab-2026/html/home/index.html | 2 +- .../MetaLab-2026/html/layout/header.html | 6 +- .../MetaLab-2026/html/messages/index.html | 2 +- templates/MetaLab-2026/html/posts/404.html | 2 +- templates/MetaLab-2026/html/posts/index.html | 2 +- templates/MetaLab-2026/html/posts/new.html | 8 +- templates/MetaLab-2026/html/posts/show.html | 8 +- .../MetaLab-2026/html/settings/index.html | 2 +- templates/MetaLab-2026/html/space/index.html | 2 +- templates/MetaLab-2026/html/studio/write.html | 8 +- templates/admin/html/layout/base.html | 2 +- templates/admin/html/layout/footer.html | 4 +- templates/admin/html/posts/index.html | 2 +- 18 files changed, 124 insertions(+), 37 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index c571ebd..9d4c35a 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -3,6 +3,7 @@ package main import ( "log" + "metazone.cc/metalab/internal/common" "metazone.cc/metalab/internal/config" "metazone.cc/metalab/internal/model" "metazone.cc/metalab/internal/router" @@ -48,6 +49,9 @@ func main() { r := gin.Default() + // 计算静态资源哈希(用于缓存破坏,文件不变哈希不变) + common.ComputeAssetHashes("templates/MetaLab-2026") + // 模板加载(前端主题 + 管理后台) tmpl, err := theme.LoadTemplates( theme.TemplateRoot{Dir: "templates/MetaLab-2026/html", Prefix: ""}, diff --git a/internal/common/helper.go b/internal/common/helper.go index 96377c0..fd85e94 100644 --- a/internal/common/helper.go +++ b/internal/common/helper.go @@ -1,23 +1,105 @@ package common import ( + "crypto/sha1" + "encoding/hex" "fmt" + "io/fs" + "log" "net/http" "net/url" + "os" + "path/filepath" "regexp" - "strconv" - "time" + "sync" "metazone.cc/metalab/internal/model" "github.com/gin-gonic/gin" ) -// AssetVersion 静态资源版本号(服务启动时生成,重启即刷新缓存) -var AssetVersion string +// assetHashes 静态资源文件哈希表(URL 路径 → 短哈希,文件不变哈希不变) +var ( + assetHashes map[string]string + assetOnce sync.Once +) -func init() { - AssetVersion = strconv.FormatInt(time.Now().Unix(), 36) +// ComputeAssetHashes 遍历 static 目录,计算每个文件的 SHA-1 前 8 位作为版本号 +func ComputeAssetHashes(templateDir string) { + assetHashes = make(map[string]string) + staticDir := filepath.Join(templateDir, "static") + filepath.WalkDir(staticDir, func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() { + return nil + } + // 只处理 .js 和 .css + ext := filepath.Ext(path) + if ext != ".js" && ext != ".css" { + return nil + } + data, err := os.ReadFile(path) + if err != nil { + return nil + } + h := sha1.Sum(data) + shortHash := hex.EncodeToString(h[:])[:8] + // 生成 URL 路径:/static/... + rel, _ := filepath.Rel(filepath.Dir(templateDir), path) + urlPath := "/" + filepath.ToSlash(rel) + assetHashes[urlPath] = shortHash + return nil + }) + // 也处理 shared/static 目录 + sharedDir := filepath.Join(filepath.Dir(templateDir), "shared", "static") + filepath.WalkDir(sharedDir, func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() { + return nil + } + ext := filepath.Ext(path) + if ext != ".js" && ext != ".css" { + return nil + } + data, err := os.ReadFile(path) + if err != nil { + return nil + } + h := sha1.Sum(data) + shortHash := hex.EncodeToString(h[:])[:8] + rel, _ := filepath.Rel(filepath.Dir(templateDir), path) + urlPath := "/" + filepath.ToSlash(rel) + assetHashes[urlPath] = shortHash + return nil + }) + // 也处理 admin/static 目录 + adminDir := filepath.Join(filepath.Dir(templateDir), "admin", "static") + filepath.WalkDir(adminDir, func(path string, d fs.DirEntry, err error) error { + if err != nil || d.IsDir() { + return nil + } + ext := filepath.Ext(path) + if ext != ".js" && ext != ".css" { + return nil + } + data, err := os.ReadFile(path) + if err != nil { + return nil + } + h := sha1.Sum(data) + shortHash := hex.EncodeToString(h[:])[:8] + rel, _ := filepath.Rel(filepath.Dir(templateDir), path) + urlPath := "/" + filepath.ToSlash(rel) + assetHashes[urlPath] = shortHash + return nil + }) + log.Printf("[AssetHashes] computed %d file hashes", len(assetHashes)) +} + +// AssetV 返回指定静态资源的哈希版本号(模板函数) +func AssetV(path string) string { + if h, ok := assetHashes[path]; ok { + return h + } + return "0" } // mdImageRe 匹配 Markdown 图片语法 ![alt](url) @@ -57,8 +139,6 @@ func BuildPageData(c *gin.Context, extra gin.H) gin.H { if isMaintenance, exists := c.Get("is_maintenance"); exists { data["IsMaintenance"] = isMaintenance } - // 注入静态资源版本号 - data["V"] = AssetVersion return data } diff --git a/internal/theme/loader.go b/internal/theme/loader.go index 564c89b..f1e8fc5 100644 --- a/internal/theme/loader.go +++ b/internal/theme/loader.go @@ -6,6 +6,8 @@ import ( "os" "path/filepath" "strings" + + "metazone.cc/metalab/internal/common" ) // TemplateRoot 模板根目录配置 @@ -21,6 +23,7 @@ func LoadTemplates(roots ...TemplateRoot) (*template.Template, error) { "add": func(a, b int) int { return a + b }, "subtract": func(a, b int) int { return a - b }, "formatTTL": formatTTL, + "assetV": common.AssetV, } t := template.New("").Funcs(funcMap) for _, root := range roots { diff --git a/templates/MetaLab-2026/html/auth/login.html b/templates/MetaLab-2026/html/auth/login.html index ce5aaa2..f3249fa 100644 --- a/templates/MetaLab-2026/html/auth/login.html +++ b/templates/MetaLab-2026/html/auth/login.html @@ -42,7 +42,7 @@ - - + + diff --git a/templates/MetaLab-2026/html/auth/register.html b/templates/MetaLab-2026/html/auth/register.html index e728504..e1c1350 100644 --- a/templates/MetaLab-2026/html/auth/register.html +++ b/templates/MetaLab-2026/html/auth/register.html @@ -89,7 +89,7 @@ - - + + diff --git a/templates/MetaLab-2026/html/home/index.html b/templates/MetaLab-2026/html/home/index.html index 2fa49ca..786b0e7 100644 --- a/templates/MetaLab-2026/html/home/index.html +++ b/templates/MetaLab-2026/html/home/index.html @@ -37,6 +37,6 @@ {{template "layout/footer.html" .}} - + diff --git a/templates/MetaLab-2026/html/layout/header.html b/templates/MetaLab-2026/html/layout/header.html index 3a186bb..67c71e9 100644 --- a/templates/MetaLab-2026/html/layout/header.html +++ b/templates/MetaLab-2026/html/layout/header.html @@ -13,9 +13,9 @@ {{if .OgURL}}{{end}} {{end}} {{.Title}} - MetaLab - + {{if .ExtraCSS}} - + {{end}} - + diff --git a/templates/MetaLab-2026/html/messages/index.html b/templates/MetaLab-2026/html/messages/index.html index 0dceca0..fe81667 100644 --- a/templates/MetaLab-2026/html/messages/index.html +++ b/templates/MetaLab-2026/html/messages/index.html @@ -202,6 +202,6 @@ } })(); - + diff --git a/templates/MetaLab-2026/html/posts/404.html b/templates/MetaLab-2026/html/posts/404.html index a18218c..8b35b83 100644 --- a/templates/MetaLab-2026/html/posts/404.html +++ b/templates/MetaLab-2026/html/posts/404.html @@ -9,6 +9,6 @@ {{template "layout/footer.html" .}} - + diff --git a/templates/MetaLab-2026/html/posts/index.html b/templates/MetaLab-2026/html/posts/index.html index 6c4da28..14bea35 100644 --- a/templates/MetaLab-2026/html/posts/index.html +++ b/templates/MetaLab-2026/html/posts/index.html @@ -53,6 +53,6 @@ {{template "layout/footer.html" .}} - + diff --git a/templates/MetaLab-2026/html/posts/new.html b/templates/MetaLab-2026/html/posts/new.html index 2181988..904c16c 100644 --- a/templates/MetaLab-2026/html/posts/new.html +++ b/templates/MetaLab-2026/html/posts/new.html @@ -62,9 +62,9 @@ {{template "layout/footer.html" .}} - - - - + + + + diff --git a/templates/MetaLab-2026/html/posts/show.html b/templates/MetaLab-2026/html/posts/show.html index 40f6b02..f16a9f9 100644 --- a/templates/MetaLab-2026/html/posts/show.html +++ b/templates/MetaLab-2026/html/posts/show.html @@ -48,10 +48,10 @@ {{/* MD 内容安全传递给 JS */}} - - - - + + + + + + diff --git a/templates/MetaLab-2026/html/studio/write.html b/templates/MetaLab-2026/html/studio/write.html index 88eaf5d..da4ec38 100644 --- a/templates/MetaLab-2026/html/studio/write.html +++ b/templates/MetaLab-2026/html/studio/write.html @@ -64,9 +64,9 @@ {{template "layout/footer.html" .}} - - - - + + + + diff --git a/templates/admin/html/layout/base.html b/templates/admin/html/layout/base.html index 3d3fb0d..f949fe8 100644 --- a/templates/admin/html/layout/base.html +++ b/templates/admin/html/layout/base.html @@ -5,7 +5,7 @@ {{if .CSRFToken}}{{end}} {{.Title}} - MetaLab 管理面板 - + {{if .ExtraCSS}} {{end}} diff --git a/templates/admin/html/layout/footer.html b/templates/admin/html/layout/footer.html index 36ec005..0888180 100644 --- a/templates/admin/html/layout/footer.html +++ b/templates/admin/html/layout/footer.html @@ -1,7 +1,7 @@ - - + + {{if .ExtraJS}} {{end}} diff --git a/templates/admin/html/posts/index.html b/templates/admin/html/posts/index.html index ecd0b21..55b237f 100644 --- a/templates/admin/html/posts/index.html +++ b/templates/admin/html/posts/index.html @@ -82,5 +82,5 @@ {{end}} - + {{template "admin/layout/footer.html" .}}