From 8a59f665723d87b9b41d275625561f900c41b2ec Mon Sep 17 00:00:00 2001
From: tian <11429339@qq.com>
Date: Fri, 24 Jul 2026 19:28:18 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=9D=E5=AD=98=E9=9B=86=E6=88=90?=
=?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=90=8E=E6=8F=90=E7=A4=BA=E9=87=8D=E6=96=B0?=
=?UTF-8?q?=E4=B8=8B=E5=8F=91=EF=BC=8C=E8=AE=BE=E5=A4=87=E5=88=97=E8=A1=A8?=
=?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=9C=80=E9=87=8D=E6=96=B0=E4=B8=8B=E5=8F=91?=
=?UTF-8?q?=E8=A7=92=E6=A0=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
internal/service/config_assets.go | 2 ++
internal/web/ui.go | 35 ++++++++++++++++++++++---
internal/web/ui/templates/devices.html | 1 +
safesightd-linux-arm64 | Bin 15270050 -> 15270050 bytes
4 files changed, 34 insertions(+), 4 deletions(-)
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 b4fee56efbcf0155b0f608c5f1b25df5fc1f78a0..2ad19c47e799fc3881d8cfb8bcca0d1865d71d2e 100644
GIT binary patch
delta 1153
zcmb`_Nn8{J0L9?}0h1Q~m6BGbtturl!?0OWV1*S(DT`8>BLgxBLLdmXm{bp2u}~>n
zEV8m)E2Y(F(e|Q^Y$00Ax^$>h-g?JQojQD%_j|YZ`qyn6+iyG8r_5+6UtE{lQC1vV
zUC~mxys9p%KC5(AVM#?*`-=MJl8nZJ!ZoX!=9LzOdUvLz=7sBXI!oKygJo@1#){^b2R3#0ZJD-D@r}KIOq!6tH+|^9qWmsIoQM}=M1n{ZNn)%x
zOpFuB;&5?P*ak;ocTq%}_tHe?f
z5z9oih>99fE0&8o5fk-dg=i3sVx?#j&Ejg&B3i{N(I(o(YSAGTs#qxK>;zt`}>?
z4Pu?>5;uyQ#LYsYTdWtih+D;N;&ySzZtmRkC%Kv7nbp}@dBJF~CQ_3Z*|l|aW5V=*
zC*h1ps5+-Ml9ini4u-Qs!Q4nRGn$cI9Sqg>ZAj{w63(5S8LmoR))s4sj%%-OO>b<9
z#%g0V(K)q|hSr+oL!r4rO`O^i=qjWjb^l*JPax1elGI}x>@K_8dTpa^vdz|K_t?F*
z#qP8F?E!nxw%SAXu=U#`w#^>3$Lw+2Zco^g_LMzs&)Bo}ob9lk_Po7dFWN5KZ3Fg_
zy=<@8tM;0`ZhPzv8?-m=EqmMEvAwp>-nIAaefz*Zw2$m#`@}xAA^Xfew=e8V`^tvx
zYuj(%*thnbeQzTJBS}9LABz7wxo>G)&yxRR!pZ+RVc+J&(X`&?xWw+k#8LavezKqK
d7dv3T4h$w9+&&c7(H$E;Xul1_hJVjX`v=9?v`GK}
delta 1143
zcmci9*;f<<0EKZ-z@&w*Qraxb78OMq1_sBnqQVIwLO?9dWrksJ6tY+ZDKo7uW(%1V
zEw+)B?b0HdWwh90Wwz0tv{|Qz&groS&*?t)7xeIZ`tIB9Teo#opKafeHMzK=CbzjN
z7U^g&%PlLaDQTaxqH=mww7RmiGg4b#-Za0sp(roh5SqKWKPx+2vaEASQ*FE~5ec-0
z8%s;;!+}IfT1raF7#nGkig_thTekf9*WO+6JG$G
zVy(DdtP|_S4dO;|laS~V8^q0Gqqs%fDsJ1s?YsZZ4i*&T=I7;y0`X`(910|+#FNdf
zs|WU_cK4)B`fn(lk)Md>#{-3-P@*mt3I~JHyih0<3kLIIy&E&S$BzrnEGSrz8EZ>6
z#z(KJYnj%xGM-E%>*KRpo7;Lfr5_mEHJs6Hci1Mo(>7a=-DP)Muiaz!+I@DvZLtUJ
zL3_v^wnwbb9<{CZm_2S!*fx98p0cOy8GF{Av*&HQ_1g>fqP=7<+YTGBS8S)fYP;+;
zd);>18#ZWf+FSOvy<>aqU3<^ow-0QueP|!q$F|QtvHkX`eP*BA7xtwM*;n?pePiF+
zclN#gFfg3)W9dI54rTT(P3>O%f8LhUHJEr>^Nr4jr`L
L2a-d7gmeA{nv}7x