From 4605f00a157cd404f5e41a252d99b8619f65dae0 Mon Sep 17 00:00:00 2001 From: Sanaei Date: Tue, 28 Jul 2026 17:38:46 +0200 Subject: [PATCH] fix(nodes): keep the credential-presence flag on the node heartbeat push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Nodes page cache is overwritten wholesale by the heartbeat websocket push, but the job broadcast a raw []*model.Node while the REST list returns []*service.NodeView. model.Node tags the api token json:"-" and carries no hasApiToken field, so every push stripped the flag the edit form reads to decide whether a token is already stored. One 5s tick after the page loaded, editing any non-mTLS node then failed with "Name, address, port and API token are required" — and stayed failed, because setQueryData refreshes dataUpdatedAt, so the query never goes stale and never refetches the intact REST payload. Broadcast the NodeView read contract instead. --- internal/web/job/node_heartbeat_job.go | 2 +- internal/web/websocket/notifier.go | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/web/job/node_heartbeat_job.go b/internal/web/job/node_heartbeat_job.go index c8c14b419..bb37d7cf7 100644 --- a/internal/web/job/node_heartbeat_job.go +++ b/internal/web/job/node_heartbeat_job.go @@ -63,7 +63,7 @@ func (j *NodeHeartbeatJob) Run() { if !websocket.HasClients() { return } - updated, err := j.nodeService.GetNodeTree() + updated, err := j.nodeService.GetNodeTreeView() if err != nil { logger.Warning("node heartbeat: load nodes for broadcast failed:", err) return diff --git a/internal/web/websocket/notifier.go b/internal/web/websocket/notifier.go index 897047e5d..7b5eff898 100644 --- a/internal/web/websocket/notifier.go +++ b/internal/web/websocket/notifier.go @@ -63,9 +63,6 @@ func BroadcastInbounds(inbounds any) { } } -// BroadcastNodes broadcasts the fresh node list to all connected clients. -// Pushed by NodeHeartbeatJob at the end of each 10s tick so the Nodes page -// reflects status / latency / cpu / mem updates without polling. func BroadcastNodes(nodes any) { if hub := GetHub(); hub != nil { hub.Broadcast(MessageTypeNodes, nodes)