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