diff --git a/internal/web/ui.go b/internal/web/ui.go index 3b5dec5..d778679 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -73,15 +73,16 @@ type PageData struct { } type ConfigStatusView struct { - OK bool `json:"ok"` - ConfigPath string `json:"config_path"` - Exists bool `json:"exists"` - Sha256 string `json:"sha256"` - Size int64 `json:"size"` - Metadata ConfigStatusMetadata `json:"metadata"` - Candidate *ConfigStatusLastGoodFile `json:"candidate"` - MediaServer ConfigStatusMediaServer `json:"media_server"` - LastGood *ConfigStatusLastGoodFile `json:"last_good"` + OK bool `json:"ok"` + ConfigPath string `json:"config_path"` + Exists bool `json:"exists"` + Sha256 string `json:"sha256"` + Size int64 `json:"size"` + Metadata ConfigStatusMetadata `json:"metadata"` + Candidate *ConfigStatusLastGoodFile `json:"candidate"` + MediaServer ConfigStatusMediaServer `json:"media_server"` + PreviousConfig *ConfigStatusLastGoodFile `json:"previous_config"` + PreviousConfigPath string `json:"previous_config_path"` } type ConfigStatusMetadata struct { diff --git a/internal/web/ui/templates/config_preview.html b/internal/web/ui/templates/config_preview.html index c888cea..af4b2a9 100644 --- a/internal/web/ui/templates/config_preview.html +++ b/internal/web/ui/templates/config_preview.html @@ -121,9 +121,9 @@
上一份配置
-
{{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}}
+
{{if and .ConfigStatus.PreviousConfig .ConfigStatus.PreviousConfig.Exists .ConfigStatus.PreviousConfig.Metadata.ConfigID}}{{.ConfigStatus.PreviousConfig.Metadata.ConfigID}} / {{if .ConfigStatus.PreviousConfig.Metadata.ConfigVersion}}{{.ConfigStatus.PreviousConfig.Metadata.ConfigVersion}}{{else}}未标记{{end}}{{else}}-{{end}}
+
{{if and .ConfigStatus.PreviousConfig .ConfigStatus.PreviousConfig.Metadata.Overlays}}{{range $i, $name := .ConfigStatus.PreviousConfig.Metadata.Overlays}}{{if $i}}, {{end}}{{$name}}{{end}}{{else}}-{{end}}
+
sha: {{if .ConfigStatus.PreviousConfig}}{{shortHash .ConfigStatus.PreviousConfig.Sha256}}{{end}}
candidate
@@ -134,7 +134,7 @@
{{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)}} + {{if and .ConfigStatus.PreviousConfig .ConfigStatus.Sha256 .ConfigStatus.PreviousConfig.Sha256 (eq .ConfigStatus.Metadata.ConfigID .ConfigStatus.PreviousConfig.Metadata.ConfigID) (eq .ConfigStatus.Metadata.ConfigVersion .ConfigStatus.PreviousConfig.Metadata.ConfigVersion) (ne .ConfigStatus.Sha256 .ConfigStatus.PreviousConfig.Sha256)}}
当前运行与上一份配置回滚点的 config_id/config_version 相同,但文件内容不同,请以 overlaysha 为准。
{{end}} diff --git a/internal/web/ui_test.go b/internal/web/ui_test.go index 0b41815..0a4bc9e 100644 --- a/internal/web/ui_test.go +++ b/internal/web/ui_test.go @@ -538,7 +538,7 @@ func TestUI_ConfigPreviewShowsApplySummaryAfterApplyResult(t *testing.T) { Exists: false, Path: "/opt/rk3588-media-server/etc/media-server.json.candidate.json", }, - LastGood: &ConfigStatusLastGoodFile{ + PreviousConfig: &ConfigStatusLastGoodFile{ Exists: true, Path: "/opt/rk3588-media-server/etc/media-server.json.last_good.json", Sha256: "07a37fabd73e98c1c131d31ddfa7b6c0e0949be854225bf2a6e990a09e60ddd3", @@ -633,7 +633,7 @@ func TestUI_ActionDeviceConfigCandidateApplyReloadsStatusAfterApply(t *testing.T "ok": true, "metadata": {"config_id": "preview_edge-01", "config_version": "v2"}, "candidate": {"exists": true, "path": "/opt/rk3588-media-server/etc/media-server.json.candidate.json"}, - "last_good": {"exists": true, "path": "/opt/rk3588-media-server/etc/media-server.json.last_good.json", "metadata": {"config_id": "local_3588_face_debug", "config_version": "20260419.120246"}}, + "previous_config": {"exists": true, "path": "/opt/rk3588-media-server/etc/media-server.json.last_good.json", "metadata": {"config_id": "local_3588_face_debug", "config_version": "20260419.120246"}}, "media_server": {"running": true, "pid": 1810489} }`)) return @@ -642,7 +642,7 @@ func TestUI_ActionDeviceConfigCandidateApplyReloadsStatusAfterApply(t *testing.T "ok": true, "metadata": {"config_id": "preview_edge-01", "config_version": "v2"}, "candidate": {"exists": false, "path": "/opt/rk3588-media-server/etc/media-server.json.candidate.json"}, - "last_good": {"exists": true, "path": "/opt/rk3588-media-server/etc/media-server.json.last_good.json", "metadata": {"config_id": "local_3588_face_debug", "config_version": "20260419.120246"}}, + "previous_config": {"exists": true, "path": "/opt/rk3588-media-server/etc/media-server.json.last_good.json", "metadata": {"config_id": "local_3588_face_debug", "config_version": "20260419.120246"}}, "media_server": {"running": true, "pid": 1810489} }`)) return @@ -708,6 +708,58 @@ func TestUI_ActionDeviceConfigCandidateApplyReloadsStatusAfterApply(t *testing.T } } +func TestUI_LoadConfigStatusPrefersPreviousConfigFields(t *testing.T) { + agentServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet || r.URL.Path != "/v1/config/status" { + t.Fatalf("unexpected request %s %s", r.Method, r.URL.Path) + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{ + "ok": true, + "metadata": {"config_id": "preview-edge", "config_version": "v2"}, + "previous_config_path": "/opt/rk3588-media-server/etc/media-server.json.last_good.json", + "previous_config": { + "exists": true, + "path": "/opt/rk3588-media-server/etc/media-server.json.last_good.json", + "sha256": "abc12345", + "metadata": {"config_id": "prev-edge", "config_version": "v1", "overlays": ["face_debug"]} + }, + "media_server": {"running": true, "pid": 1234} + }`)) + })) + defer agentServer.Close() + + host, portText, err := net.SplitHostPort(strings.TrimPrefix(agentServer.URL, "http://")) + if err != nil { + t.Fatalf("parse test server address: %v", err) + } + port, err := strconv.Atoi(portText) + if err != nil { + t.Fatalf("parse test server port: %v", err) + } + + cfg := &config.Config{Concurrency: 1, OfflineAfterMs: 1000000} + agent := service.NewAgentClient(cfg) + reg := service.NewRegistryService(cfg, agent) + reg.UpdateDevice(&models.Device{DeviceID: "edge-01", DeviceName: "入口识别节点", IP: host, AgentPort: port, MediaPort: 9000, Online: true}) + tasks := service.NewTaskService(cfg, agent, reg) + ui, err := NewUI(nil, reg, agent, tasks, nil) + if err != nil { + t.Fatalf("NewUI: %v", err) + } + + status, _, err := ui.loadConfigStatus(&models.Device{DeviceID: "edge-01", IP: host, AgentPort: port}) + if err != nil { + t.Fatalf("loadConfigStatus: %v", err) + } + if status == nil || status.PreviousConfig == nil { + t.Fatalf("expected previous config to be present, got %#v", status) + } + if status.PreviousConfig.Metadata.ConfigID != "prev-edge" { + t.Fatalf("previous config metadata = %#v", status.PreviousConfig.Metadata) + } +} + func TestUI_ModelDeploymentPageRendersDeviceActions(t *testing.T) { ui := newTestUI(t) req := httptest.NewRequest(http.MethodGet, "/ui/models", nil)