Clarify config identity in preview UI
This commit is contained in:
parent
03ccac2230
commit
468db800cd
@ -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 "-"
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
<div class="card">
|
||||
<h2>生成配置预览</h2>
|
||||
{{if .ConfigSources.Root}}<div class="muted small">Media 仓库:<span class="mono">{{.ConfigSources.Root}}</span></div>{{end}}
|
||||
<div class="muted small" style="margin-top:8px">
|
||||
<span class="mono">config_id</span> 是配置名,默认会带上 <span class="mono">device_id</span>(当前设备:<span class="mono">{{.Device.DeviceID}}</span>);<span class="mono">config_version</span> 表示本次生成版本;<span class="mono">SHA256</span> 是最终文件内容指纹。
|
||||
</div>
|
||||
<form method="post" action="/ui/devices/{{.Device.DeviceID}}/config-preview" style="margin-top:12px">
|
||||
<div class="row">
|
||||
<div>
|
||||
@ -74,6 +77,10 @@
|
||||
<div><div class="muted small">Profile</div><div class="mono">{{index .ConfigPreview.Metadata "profile"}}</div></div>
|
||||
<div><div class="muted small">大小</div><div class="mono">{{.ConfigPreview.Size}} bytes</div></div>
|
||||
</div>
|
||||
<div style="margin-top:10px">
|
||||
<div class="muted small">Overlay</div>
|
||||
<div class="mono">{{if index .ConfigPreview.Metadata "overlays"}}{{range $i, $name := index .ConfigPreview.Metadata "overlays"}}{{if $i}}, {{end}}{{$name}}{{end}}{{else}}-{{end}}</div>
|
||||
</div>
|
||||
<div style="margin-top:10px">
|
||||
<div class="muted small">SHA256</div>
|
||||
<div class="mono small">{{.ConfigPreview.Sha256}}</div>
|
||||
@ -109,10 +116,14 @@
|
||||
<div>
|
||||
<div class="muted small">当前运行</div>
|
||||
<div class="mono">{{if .ConfigStatus.Metadata.ConfigID}}{{.ConfigStatus.Metadata.ConfigID}} / {{if .ConfigStatus.Metadata.ConfigVersion}}{{.ConfigStatus.Metadata.ConfigVersion}}{{else}}未标记{{end}}{{else}}未标记{{end}}</div>
|
||||
<div class="muted small mono" style="margin-top:6px">{{if .ConfigStatus.Metadata.Overlays}}{{range $i, $name := .ConfigStatus.Metadata.Overlays}}{{if $i}}, {{end}}{{$name}}{{end}}{{else}}-{{end}}</div>
|
||||
<div class="muted small mono" style="margin-top:6px">sha: {{shortHash .ConfigStatus.Sha256}}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="muted small">last_good</div>
|
||||
<div class="mono">{{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}}</div>
|
||||
<div class="muted small mono" style="margin-top:6px">{{if and .ConfigStatus.LastGood .ConfigStatus.LastGood.Metadata.Overlays}}{{range $i, $name := .ConfigStatus.LastGood.Metadata.Overlays}}{{if $i}}, {{end}}{{$name}}{{end}}{{else}}-{{end}}</div>
|
||||
<div class="muted small mono" style="margin-top:6px">sha: {{if .ConfigStatus.LastGood}}{{shortHash .ConfigStatus.LastGood.Sha256}}{{end}}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="muted small">candidate</div>
|
||||
@ -123,6 +134,9 @@
|
||||
<div>{{if .ConfigStatus.MediaServer.Running}}<span class="pill ok">运行中</span>{{else}}<span class="pill bad">未运行</span>{{end}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{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)}}
|
||||
<div class="muted small" style="margin-top:10px">当前运行与 last_good 的 <span class="mono">config_id/config_version</span> 相同,但文件内容不同,请以 <span class="mono">overlay</span> 和 <span class="mono">sha</span> 为准。</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
||||
@ -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</span> 是配置名,默认会带上 <span class=\"mono\">device_id",
|
||||
"config_version</span> 表示本次生成版本",
|
||||
"device_id",
|
||||
"edge-01",
|
||||
"SHA256</span> 是最终文件内容指纹",
|
||||
"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) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user