From 468db800cd05f351a646087f35b738af39378cc1 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sun, 19 Apr 2026 14:14:56 +0800 Subject: [PATCH] Clarify config identity in preview UI --- internal/web/ui.go | 7 +++ internal/web/ui/templates/config_preview.html | 14 +++++ internal/web/ui_test.go | 51 +++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/internal/web/ui.go b/internal/web/ui.go index f7dbe28..3b5dec5 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -121,6 +121,13 @@ func NewUI(discovery *service.DiscoveryService, registry *service.RegistryServic } return false }, + "shortHash": func(v string) string { + v = strings.TrimSpace(v) + if len(v) > 8 { + return v[:8] + } + return v + }, "ago": func(ms int64) string { if ms <= 0 { return "-" diff --git a/internal/web/ui/templates/config_preview.html b/internal/web/ui/templates/config_preview.html index 993aed1..862583e 100644 --- a/internal/web/ui/templates/config_preview.html +++ b/internal/web/ui/templates/config_preview.html @@ -13,6 +13,9 @@

生成配置预览

{{if .ConfigSources.Root}}
Media 仓库:{{.ConfigSources.Root}}
{{end}} +
+ config_id 是配置名,默认会带上 device_id(当前设备:{{.Device.DeviceID}});config_version 表示本次生成版本;SHA256 是最终文件内容指纹。 +
@@ -74,6 +77,10 @@
Profile
{{index .ConfigPreview.Metadata "profile"}}
大小
{{.ConfigPreview.Size}} bytes
+
+
Overlay
+
{{if index .ConfigPreview.Metadata "overlays"}}{{range $i, $name := index .ConfigPreview.Metadata "overlays"}}{{if $i}}, {{end}}{{$name}}{{end}}{{else}}-{{end}}
+
SHA256
{{.ConfigPreview.Sha256}}
@@ -109,10 +116,14 @@
当前运行
{{if .ConfigStatus.Metadata.ConfigID}}{{.ConfigStatus.Metadata.ConfigID}} / {{if .ConfigStatus.Metadata.ConfigVersion}}{{.ConfigStatus.Metadata.ConfigVersion}}{{else}}未标记{{end}}{{else}}未标记{{end}}
+
{{if .ConfigStatus.Metadata.Overlays}}{{range $i, $name := .ConfigStatus.Metadata.Overlays}}{{if $i}}, {{end}}{{$name}}{{end}}{{else}}-{{end}}
+
sha: {{shortHash .ConfigStatus.Sha256}}
last_good
{{if and .ConfigStatus.LastGood .ConfigStatus.LastGood.Exists .ConfigStatus.LastGood.Metadata.ConfigID}}{{.ConfigStatus.LastGood.Metadata.ConfigID}} / {{if .ConfigStatus.LastGood.Metadata.ConfigVersion}}{{.ConfigStatus.LastGood.Metadata.ConfigVersion}}{{else}}未标记{{end}}{{else}}-{{end}}
+
{{if and .ConfigStatus.LastGood .ConfigStatus.LastGood.Metadata.Overlays}}{{range $i, $name := .ConfigStatus.LastGood.Metadata.Overlays}}{{if $i}}, {{end}}{{$name}}{{end}}{{else}}-{{end}}
+
sha: {{if .ConfigStatus.LastGood}}{{shortHash .ConfigStatus.LastGood.Sha256}}{{end}}
candidate
@@ -123,6 +134,9 @@
{{if .ConfigStatus.MediaServer.Running}}运行中{{else}}未运行{{end}}
+ {{if and .ConfigStatus.LastGood .ConfigStatus.Sha256 .ConfigStatus.LastGood.Sha256 (eq .ConfigStatus.Metadata.ConfigID .ConfigStatus.LastGood.Metadata.ConfigID) (eq .ConfigStatus.Metadata.ConfigVersion .ConfigStatus.LastGood.Metadata.ConfigVersion) (ne .ConfigStatus.Sha256 .ConfigStatus.LastGood.Sha256)}} +
当前运行与 last_good 的 config_id/config_version 相同,但文件内容不同,请以 overlaysha 为准。
+ {{end}}
{{end}} diff --git a/internal/web/ui_test.go b/internal/web/ui_test.go index 6fd65fd..1921787 100644 --- a/internal/web/ui_test.go +++ b/internal/web/ui_test.go @@ -522,7 +522,9 @@ func TestUI_ConfigPreviewShowsApplySummaryAfterApplyResult(t *testing.T) { Metadata: map[string]any{ "config_id": "preview_edge-01", "config_version": "v2", + "overlays": []any{"face_test_sensitive", "production_quiet"}, }, + Sha256: "eecdf8d422705f3affa0f892199604f037f60ea8fe578fe2a65527e1800044c5", Size: 64, }, ConfigStatus: &ConfigStatusView{ @@ -530,6 +532,7 @@ func TestUI_ConfigPreviewShowsApplySummaryAfterApplyResult(t *testing.T) { Metadata: ConfigStatusMetadata{ ConfigID: "preview_edge-01", ConfigVersion: "v2", + Overlays: []string{"face_test_sensitive", "production_quiet"}, }, Candidate: &ConfigStatusLastGoodFile{ Exists: false, @@ -538,9 +541,11 @@ func TestUI_ConfigPreviewShowsApplySummaryAfterApplyResult(t *testing.T) { LastGood: &ConfigStatusLastGoodFile{ Exists: true, Path: "/opt/rk3588-media-server/etc/media-server.json.last_good.json", + Sha256: "07a37fabd73e98c1c131d31ddfa7b6c0e0949be854225bf2a6e990a09e60ddd3", Metadata: ConfigStatusMetadata{ ConfigID: "local_3588_face_debug", ConfigVersion: "20260419.120246", + Overlays: []string{"face_debug"}, }, }, MediaServer: ConfigStatusMediaServer{ @@ -559,6 +564,10 @@ func TestUI_ConfigPreviewShowsApplySummaryAfterApplyResult(t *testing.T) { "preview_edge-01 / v2", "last_good", "local_3588_face_debug / 20260419.120246", + "face_test_sensitive, production_quiet", + "face_debug", + "eecdf8d4", + "07a37fab", "candidate", "已清空", "media-server", @@ -570,6 +579,48 @@ func TestUI_ConfigPreviewShowsApplySummaryAfterApplyResult(t *testing.T) { } } +func TestUI_ConfigPreviewExplainsConfigIdentityFields(t *testing.T) { + ui := newTestUI(t) + req := httptest.NewRequest(http.MethodGet, "/ui/devices/edge-01/config-preview", nil) + rr := httptest.NewRecorder() + + ui.render(rr, req, "config_preview", PageData{ + Title: "配置预览", + Device: &models.Device{DeviceID: "edge-01", DeviceName: "入口识别节点", IP: "127.0.0.1", AgentPort: 9100}, + SelectedTemplate: "workshop_face_shoe_alarm", + SelectedProfile: "local_3588_test", + SelectedOverlays: []string{"face_debug"}, + SelectedConfigID: "preview_edge-01", + ConfigPreview: &service.ConfigPreviewResult{ + JSON: `{"templates":{"tpl":{"nodes":[],"edges":[]}},"instances":[],"metadata":{"config_id":"preview_edge-01","config_version":"v2","overlays":["face_debug"]}}`, + Metadata: map[string]any{ + "config_id": "preview_edge-01", + "config_version": "v2", + "template": "workshop_face_shoe_alarm", + "profile": "local_3588_test", + "overlays": []any{"face_debug"}, + }, + Sha256: "eecdf8d422705f3affa0f892199604f037f60ea8fe578fe2a65527e1800044c5", + Size: 64, + }, + }) + + body := rr.Body.String() + for _, want := range []string{ + "config_id 是配置名,默认会带上 device_id", + "config_version 表示本次生成版本", + "device_id", + "edge-01", + "SHA256 是最终文件内容指纹", + "Overlay", + "face_debug", + } { + if !strings.Contains(body, want) { + t.Fatalf("expected preview explanation HTML to contain %q, got:\n%s", want, body) + } + } +} + func TestUI_ActionDeviceConfigCandidateApplyReloadsStatusAfterApply(t *testing.T) { statusCalls := 0 agentServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {