From 17433c39f4106190d4b3eee87f59a42811755980 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sun, 17 May 2026 13:59:40 +0200 Subject: [PATCH] feat(nodes): per-node client roll-up and panel version Added transient inboundCount / clientCount / onlineCount / depletedCount fields to model.Node, populated by NodeService.GetAll via aggregated queries (one join across inbounds + client_inbounds, one over client_traffics intersected with the in-memory online emails). The Nodes list renders these as colored chips on a new "Clients" column so an operator can see at a glance how many users each node carries and how many are currently online or depleted. Also exposes the remote panel's version. The central panel adds panelVersion to its /api/server/status payload (sourced from config.GetVersion). Probe reads that field and persists it on the node row, mirroring how xrayVersion already flows. NodesPage gets a new column next to Xray Version, in both desktop and mobile views, with English and Persian strings. Co-Authored-By: Claude Opus 4.7 --- database/model/model.go | 6 ++ frontend/src/pages/nodes/NodeList.vue | 34 +++++++ frontend/src/pages/nodes/useNodes.js | 14 ++- web/service/node.go | 134 +++++++++++++++++++++++--- web/service/server.go | 2 + web/translation/en-US.json | 1 + web/translation/fa-IR.json | 1 + 7 files changed, 175 insertions(+), 17 deletions(-) diff --git a/database/model/model.go b/database/model/model.go index d300c6cf..f38a9acd 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -149,11 +149,17 @@ type Node struct { LastHeartbeat int64 `json:"lastHeartbeat"` // unix seconds, 0 = never LatencyMs int `json:"latencyMs"` XrayVersion string `json:"xrayVersion"` + PanelVersion string `json:"panelVersion" gorm:"column:panel_version"` CpuPct float64 `json:"cpuPct"` MemPct float64 `json:"memPct"` UptimeSecs uint64 `json:"uptimeSecs"` LastError string `json:"lastError"` + InboundCount int `json:"inboundCount" gorm:"-"` + ClientCount int `json:"clientCount" gorm:"-"` + OnlineCount int `json:"onlineCount" gorm:"-"` + DepletedCount int `json:"depletedCount" gorm:"-"` + CreatedAt int64 `json:"createdAt" gorm:"autoCreateTime"` UpdatedAt int64 `json:"updatedAt" gorm:"autoUpdateTime"` } diff --git a/frontend/src/pages/nodes/NodeList.vue b/frontend/src/pages/nodes/NodeList.vue index 73dc6236..434aa80b 100644 --- a/frontend/src/pages/nodes/NodeList.vue +++ b/frontend/src/pages/nodes/NodeList.vue @@ -184,6 +184,10 @@ function isExpanded(id) { {{ t('pages.nodes.xrayVersion') }} {{ statsNode.xrayVersion || '-' }} +
+ {{ t('pages.nodes.panelVersion') || 'Panel version' }} + {{ statsNode.panelVersion || '-' }} +
{{ t('pages.nodes.uptime') }} {{ formatUptime(statsNode.uptimeSecs) }} @@ -195,6 +199,16 @@ function isExpanded(id) {
+
+ {{ t('clients') }} + {{ statsNode.clientCount || 0 }} + + {{ statsNode.onlineCount }} {{ t('online') }} + + + {{ statsNode.depletedCount }} {{ t('depleted') }} + +
{{ t('pages.nodes.lastHeartbeat') }} {{ relativeTime(statsNode.lastHeartbeat) }} @@ -260,10 +274,30 @@ function isExpanded(id) { + + + + + + + +