From 426bedda65f940562187b4ae086e3687729b117d Mon Sep 17 00:00:00 2001 From: Victor_Jay Date: Tue, 30 Jun 2026 19:10:11 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20ComputeAssetHashes=20=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E5=AE=89=E5=85=A8=20+=20theme=20=E9=A1=B5=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=20JS=20=E5=AF=BC=E8=87=B4=E5=8F=8C=E5=86=99?= =?UTF-8?q?=20panic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - helper.go: 用 sync.RWMutex 保护 assetHashes,构建本地 map 后原子替换 - theme.html: 删除硬编码 script 标签(footer ExtraJS 已加载), 修复 site-settings-theme.js 执行两次 → applyTheme 触发两次 → 并发写 map panic --- internal/common/helper.go | 35 +++++++++++++------ templates/admin/html/site-settings/theme.html | 1 - 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/internal/common/helper.go b/internal/common/helper.go index 6b33823..896b714 100644 --- a/internal/common/helper.go +++ b/internal/common/helper.go @@ -11,6 +11,7 @@ import ( "path/filepath" "regexp" "strconv" + "sync" "time" "metazone.cc/mce/internal/model" @@ -19,22 +20,31 @@ import ( ) // assetHashes 静态资源文件哈希表(URL 路径 → 短哈希,文件不变哈希不变) -var assetHashes map[string]string +var ( + assetHashes map[string]string + assetMu sync.RWMutex +) // ComputeAssetHashes 遍历 static 目录,计算每个文件的 CRC32 作为版本号 +// 构建本地 map 后原子替换,避免并发读写冲突 func ComputeAssetHashes(templateDir string) { - assetHashes = make(map[string]string) + mu := make(map[string]string) // 前端主题:templates/MetaLab-2026/static → URL /static/ - walkStaticDir(filepath.Join(templateDir, "static"), "/static") + walkStaticDirInto(mu, filepath.Join(templateDir, "static"), "/static") // 共享资源:templates/shared/static → URL /shared/static/ - walkStaticDir(filepath.Join(filepath.Dir(templateDir), "shared", "static"), "/shared/static") + walkStaticDirInto(mu, filepath.Join(filepath.Dir(templateDir), "shared", "static"), "/shared/static") // 管理后台:templates/admin/static → URL /admin/static/ - walkStaticDir(filepath.Join(filepath.Dir(templateDir), "admin", "static"), "/admin/static") - log.Printf("[AssetHashes] computed %d file hashes", len(assetHashes)) + walkStaticDirInto(mu, filepath.Join(filepath.Dir(templateDir), "admin", "static"), "/admin/static") + + assetMu.Lock() + assetHashes = mu + assetMu.Unlock() + + log.Printf("[AssetHashes] computed %d file hashes", len(mu)) } -// walkStaticDir 遍历目录,计算每个 .js/.css 的 CRC32,存入 assetHashes -func walkStaticDir(dir, urlPrefix string) { +// walkStaticDirInto 遍历目录,计算每个 .js/.css 的 CRC32,存入目标 map +func walkStaticDirInto(dst map[string]string, dir, urlPrefix string) { _ = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { if err != nil || d.IsDir() { return nil @@ -52,14 +62,17 @@ func walkStaticDir(dir, urlPrefix string) { // 生成 URL 路径:urlPrefix + 相对于 dir 的子路径 rel, _ := filepath.Rel(dir, path) urlPath := urlPrefix + "/" + filepath.ToSlash(rel) - assetHashes[urlPath] = shortHash + dst[urlPath] = shortHash return nil }) } -// AssetV 返回指定静态资源的哈希版本号(模板函数) +// AssetV 返回指定静态资源的哈希版本号(模板函数,并发安全) func AssetV(path string) string { - if h, ok := assetHashes[path]; ok { + assetMu.RLock() + h, ok := assetHashes[path] + assetMu.RUnlock() + if ok { return h } return "0" diff --git a/templates/admin/html/site-settings/theme.html b/templates/admin/html/site-settings/theme.html index 02665a3..bed80c1 100644 --- a/templates/admin/html/site-settings/theme.html +++ b/templates/admin/html/site-settings/theme.html @@ -41,4 +41,3 @@ {{template "admin/layout/footer.html" .}} -