diff --git a/internal/service/config_assets.go b/internal/service/config_assets.go index b3d3960..e2d21f1 100644 --- a/internal/service/config_assets.go +++ b/internal/service/config_assets.go @@ -80,6 +80,7 @@ type ConfigIntegrationServiceAsset struct { Enabled bool `json:"enabled"` AddressSummary string `json:"address_summary"` RefCount int `json:"ref_count"` + UpdatedAt string `json:"updated_at"` ObjectStorage *ObjectStorageConfig `json:"object_storage,omitempty"` TokenService *TokenServiceConfig `json:"token_service,omitempty"` AlarmService *AlarmServiceConfig `json:"alarm_service,omitempty"` @@ -1642,6 +1643,7 @@ func integrationServiceAssetFromRecord(record storage.IntegrationServiceRecord) Type: firstString(record.ServiceType, stringValue(raw["type"])), Description: firstString(raw["description"], record.Description), Enabled: boolValue(raw["enabled"], record.Enabled), + UpdatedAt: record.UpdatedAt, Raw: raw, } item.TypeLabel = integrationTypeLabel(item.Type) diff --git a/internal/web/ui.go b/internal/web/ui.go index 6f789d6..56d1836 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -187,9 +187,10 @@ type PageData struct { } type DeviceOverviewRow struct { - Device *models.Device - ConfigStatus *ConfigStatusView - ConfigStatusErr string + Device *models.Device + ConfigStatus *ConfigStatusView + ConfigStatusErr string + IntegrationStale bool } type TaskDeviceRow struct { @@ -3869,7 +3870,7 @@ func (u *UI) actionAssetIntegrationSave(w http.ResponseWriter, r *http.Request) http.Redirect(w, r, "/assets/integrations?error="+urlQueryEscape(err.Error())+"&name="+url.PathEscape(asset.Name), http.StatusFound) return } - http.Redirect(w, r, "/assets/integrations?msg="+urlQueryEscape("第三方服务已保存")+"&name="+url.PathEscape(asset.Name), http.StatusFound) + http.Redirect(w, r, "/assets/integrations?msg="+urlQueryEscape("第三方服务已保存,请通过部署向导重新下发生效")+"&name="+url.PathEscape(asset.Name), http.StatusFound) } func (u *UI) actionAssetIntegrationDelete(w http.ResponseWriter, r *http.Request) { @@ -5112,6 +5113,32 @@ func (u *UI) deviceOverviewPageData(r *http.Request, selectedIDs []string, errMs selectedIDs = selectedIDsFromQuery(r.URL.Query()["selected"]) } selectedIDs = filterSelectedDeviceIDs(devices, selectedIDs) + + // Check if integrations were updated after device's last deployment + if u.preview != nil { + if svcs, _ := u.preview.ListIntegrationServices(); len(svcs) > 0 { + maxUpdated := "" + for _, s := range svcs { + if s.UpdatedAt > maxUpdated { + maxUpdated = s.UpdatedAt + } + } + if maxUpdated != "" { + for i := range rows { + dev := rows[i].Device + if dev == nil || !dev.Online { + continue + } + status, _, _ := u.loadConfigStatus(dev) + if status == nil || status.Metadata.RenderedAt == "" { + continue + } + rows[i].IntegrationStale = maxUpdated > status.Metadata.RenderedAt + } + } + } + } + data := PageData{ Title: "设备管理", Devices: devices, diff --git a/internal/web/ui/templates/devices.html b/internal/web/ui/templates/devices.html index d4465cf..36aa659 100644 --- a/internal/web/ui/templates/devices.html +++ b/internal/web/ui/templates/devices.html @@ -94,6 +94,7 @@ {{else}} 未知 {{end}} + {{if .IntegrationStale}}需重新下发{{end}} diff --git a/safesightd-linux-arm64 b/safesightd-linux-arm64 index b4fee56..2ad19c4 100644 Binary files a/safesightd-linux-arm64 and b/safesightd-linux-arm64 differ