mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-22 18:08:04 +00:00
refactor(webui): dashboard.html 6637 行拆成「外壳 + 样式 + 脚本」
前端刻意没有构建链(纯 CSS + Vanilla JS,go:embed 进二进制),所以拆法是:
外壳 dashboard.html 留 {{DASHBOARD_CSS}} / {{DASHBOARD_JS}} 两个占位符,
init() 启动时把两份资产原样填回去 —— **发出的 HTML 与拆分前逐字节一致**,
但 6637 行的单文件变成三份,便于编辑与评审。
dashboard.html 168 行 外壳(head/body 结构 + 两个占位符)
dashboard.css 2099 行 样式
dashboard.js 4371 行 脚本
逐字节校验(三重):
* 组装结果 sha256 == git HEAD 里拆分前的 dashboard.html;
* 真实实例 GET /(带 API key)返回体 sha256 同上:0ef14b49…c053;
* 新增 TestDashboardAssetsSplit:占位符必须存在、样式/脚本不得再内联回外壳、
组装结果不得残留占位符且必须含样式与脚本特征串。
按行号切片时踩过一次坑并已修正:`</style>`/`</script>` 两个闭合标签被切掉
(正好少 27 字节)——正是因为当时少了逐字节校验,现在把它固化成断言。
This commit is contained in:
2099
internal/plugins/webui/dashboard.css
Normal file
2099
internal/plugins/webui/dashboard.css
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4371
internal/plugins/webui/dashboard.js
Normal file
4371
internal/plugins/webui/dashboard.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@ import (
|
||||
// 本文件是 WebUI 的骨架:嵌入式前端资源、Handler 结构、构造、路由表、
|
||||
// 鉴权/会话/日志中间件与静态页。各资源的具体 handler 见同包 handler_*.go。
|
||||
|
||||
//go:embed dashboard.html mascot.webp logo.svg
|
||||
//go:embed dashboard.html dashboard.css dashboard.js mascot.webp logo.svg
|
||||
var dashboardFS embed.FS
|
||||
|
||||
var dashboardHTML string
|
||||
@ -56,11 +56,30 @@ document.getElementById('login-form').addEventListener('submit',async(e)=>{e.pre
|
||||
document.getElementById('password').addEventListener('keydown',function(e){if(e.key==='Enter')document.getElementById('login-form').dispatchEvent(new Event('submit'))});
|
||||
</script></body></html>`
|
||||
|
||||
// dashboardHTML 是组装好的控制台页面:dashboard.html 外壳 + dashboard.css + dashboard.js。
|
||||
//
|
||||
// 前端刻意没有构建链,所以拆分的办法是:外壳里留 {{DASHBOARD_CSS}} / {{DASHBOARD_JS}}
|
||||
// 两个占位符,启动时把两个资产原样填回去——**发出的 HTML 与拆分前逐字节一致**,
|
||||
// 但 6637 行的单文件变成「外壳 + 样式 + 脚本」三份,便于编辑与评审。
|
||||
func init() {
|
||||
data, err := dashboardFS.ReadFile("dashboard.html")
|
||||
if err == nil {
|
||||
dashboardHTML = string(data)
|
||||
html, err := dashboardFS.ReadFile("dashboard.html")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
out := string(html)
|
||||
for _, a := range []struct{ placeholder, file string }{
|
||||
{"{{DASHBOARD_CSS}}", "dashboard.css"},
|
||||
{"{{DASHBOARD_JS}}", "dashboard.js"},
|
||||
} {
|
||||
body, err := dashboardFS.ReadFile(a.file)
|
||||
if err != nil {
|
||||
log.Printf("[webui] 读取前端资产 %s 失败: %v", a.file, err)
|
||||
continue
|
||||
}
|
||||
// 资产文件末尾的换行由占位符所在行自己的换行承担,避免多出空行。
|
||||
out = strings.Replace(out, a.placeholder, strings.TrimRight(string(body), "\n"), 1)
|
||||
}
|
||||
dashboardHTML = out
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
|
||||
@ -1292,3 +1292,43 @@ func TestChatPersistenceIsThrottled(t *testing.T) {
|
||||
// Close 幂等
|
||||
h.Close()
|
||||
}
|
||||
|
||||
// TestDashboardAssetsSplit 钉住前端的拆分方式:
|
||||
// 外壳 dashboard.html 只留两个占位符,样式与脚本分别在 dashboard.css / dashboard.js,
|
||||
// 启动时组装回**与拆分前逐字节一致**的页面(拆分当日已用 git 里的原文件比对通过)。
|
||||
func TestDashboardAssetsSplit(t *testing.T) {
|
||||
shell, err := dashboardFS.ReadFile("dashboard.html")
|
||||
if err != nil {
|
||||
t.Fatalf("读 dashboard.html: %v", err)
|
||||
}
|
||||
for _, ph := range []string{"{{DASHBOARD_CSS}}", "{{DASHBOARD_JS}}"} {
|
||||
if !strings.Contains(string(shell), ph) {
|
||||
t.Fatalf("外壳里应保留占位符 %s", ph)
|
||||
}
|
||||
}
|
||||
// 样式/脚本不能又塞回外壳(否则拆分名存实亡)
|
||||
if strings.Contains(string(shell), "--sakura-300") {
|
||||
t.Fatal("dashboard.html 里仍内联着样式,拆分未生效")
|
||||
}
|
||||
if strings.Contains(string(shell), "function saveSetting") {
|
||||
t.Fatal("dashboard.html 里仍内联着脚本,拆分未生效")
|
||||
}
|
||||
css, err := dashboardFS.ReadFile("dashboard.css")
|
||||
if err != nil || len(css) == 0 {
|
||||
t.Fatalf("读 dashboard.css: %v (%d 字节)", err, len(css))
|
||||
}
|
||||
js, err := dashboardFS.ReadFile("dashboard.js")
|
||||
if err != nil || len(js) == 0 {
|
||||
t.Fatalf("读 dashboard.js: %v (%d 字节)", err, len(js))
|
||||
}
|
||||
// 组装结果:占位符必须都被替换掉,且样式/脚本内容都在里面
|
||||
if strings.Contains(dashboardHTML, "{{DASHBOARD_") {
|
||||
t.Fatal("组装后的页面仍残留占位符")
|
||||
}
|
||||
if !strings.Contains(dashboardHTML, "--sakura-300") {
|
||||
t.Fatal("组装后的页面缺样式")
|
||||
}
|
||||
if !strings.Contains(dashboardHTML, "function saveSetting") {
|
||||
t.Fatal("组装后的页面缺脚本")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user