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(YSAG&#Ts#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