mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-16 16:20:59 +00:00
feat(inbounds): improve multi-node online attribution (#6164)
This commit is contained in:
@@ -176,7 +176,12 @@ type process struct {
|
||||
// mutex guards this map, onlineClients, and localLastOnline above so the
|
||||
// online getters never see a torn read.
|
||||
nodeOnlineTrees map[int]map[string][]string
|
||||
onlineMu sync.RWMutex
|
||||
// nodeActiveInboundTrees mirrors nodeOnlineTrees for active inbound tags:
|
||||
// each direct node reports a GUID-keyed subtree of inbound tags that carried
|
||||
// traffic within its own grace window. The inbounds page combines this with
|
||||
// nodeOnlineTrees so a multi-inbound client is not shown on an idle inbound.
|
||||
nodeActiveInboundTrees map[int]map[string][]string
|
||||
onlineMu sync.RWMutex
|
||||
|
||||
// onlineAPISupport caches whether the running core implements the
|
||||
// online-stats RPCs (GetUsersStats). A new process is created on every
|
||||
@@ -398,10 +403,9 @@ func (p *Process) GetMergedNodeTrees() map[string][]string {
|
||||
}
|
||||
|
||||
// GetLocalActiveInbounds returns a copy of THIS panel's inbound tags that
|
||||
// carried traffic within the grace window. Only the local xray reports
|
||||
// per-inbound activity; remote-node snapshots don't carry it, so the service
|
||||
// layer keys these under the panel's own GUID and a node missing from the
|
||||
// active-inbounds map means "don't gate" (fall back to the email-only signal).
|
||||
// carried traffic within the grace window. The service layer keys these under
|
||||
// the panel's own GUID before merging them with remote-node active-inbound
|
||||
// subtrees.
|
||||
func (p *Process) GetLocalActiveInbounds() []string {
|
||||
p.onlineMu.RLock()
|
||||
defer p.onlineMu.RUnlock()
|
||||
@@ -413,6 +417,43 @@ func (p *Process) GetLocalActiveInbounds() []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// GetMergedActiveInboundTrees returns the union of every direct node's reported
|
||||
// active-inbound subtree, keyed by the panelGuid of the node that physically
|
||||
// hosts each inbound. Duplicate tags reported through multiple paths are
|
||||
// deduped per GUID.
|
||||
func (p *Process) GetMergedActiveInboundTrees() map[string][]string {
|
||||
p.onlineMu.RLock()
|
||||
defer p.onlineMu.RUnlock()
|
||||
if len(p.nodeActiveInboundTrees) == 0 {
|
||||
return map[string][]string{}
|
||||
}
|
||||
out := make(map[string][]string)
|
||||
seen := make(map[string]map[string]struct{})
|
||||
for _, tree := range p.nodeActiveInboundTrees {
|
||||
for guid, tags := range tree {
|
||||
if guid == "" || len(tags) == 0 {
|
||||
continue
|
||||
}
|
||||
dedup := seen[guid]
|
||||
if dedup == nil {
|
||||
dedup = make(map[string]struct{}, len(tags))
|
||||
seen[guid] = dedup
|
||||
}
|
||||
for _, tag := range tags {
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := dedup[tag]; ok {
|
||||
continue
|
||||
}
|
||||
dedup[tag] = struct{}{}
|
||||
out[guid] = append(out[guid], tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// RefreshLocalOnline records that each email in activeEmails and each tag in
|
||||
// activeInboundTags had local xray traffic at now, then rebuilds onlineClients
|
||||
// and localActiveInbounds from every entry seen within graceMs, pruning older
|
||||
@@ -462,12 +503,31 @@ func (p *Process) RefreshLocalOnline(activeEmails, activeInboundTags []string, n
|
||||
func (p *Process) SetNodeOnlineTree(nodeID int, tree map[string][]string) {
|
||||
p.onlineMu.Lock()
|
||||
defer p.onlineMu.Unlock()
|
||||
if len(tree) == 0 {
|
||||
delete(p.nodeOnlineTrees, nodeID)
|
||||
return
|
||||
}
|
||||
if p.nodeOnlineTrees == nil {
|
||||
p.nodeOnlineTrees = map[int]map[string][]string{}
|
||||
}
|
||||
p.nodeOnlineTrees[nodeID] = tree
|
||||
}
|
||||
|
||||
// SetNodeActiveInboundTree records the GUID-keyed active-inbound subtree one
|
||||
// direct remote node reported. Replaces any previous entry for that node.
|
||||
func (p *Process) SetNodeActiveInboundTree(nodeID int, tree map[string][]string) {
|
||||
p.onlineMu.Lock()
|
||||
defer p.onlineMu.Unlock()
|
||||
if len(tree) == 0 {
|
||||
delete(p.nodeActiveInboundTrees, nodeID)
|
||||
return
|
||||
}
|
||||
if p.nodeActiveInboundTrees == nil {
|
||||
p.nodeActiveInboundTrees = map[int]map[string][]string{}
|
||||
}
|
||||
p.nodeActiveInboundTrees[nodeID] = tree
|
||||
}
|
||||
|
||||
// ClearNodeOnlineClients drops a direct node's whole subtree contribution.
|
||||
// Called when a probe fails so a downed node — and everything behind it — doesn't
|
||||
// keep its clients listed as "online" until the next successful probe.
|
||||
@@ -475,6 +535,7 @@ func (p *Process) ClearNodeOnlineClients(nodeID int) {
|
||||
p.onlineMu.Lock()
|
||||
defer p.onlineMu.Unlock()
|
||||
delete(p.nodeOnlineTrees, nodeID)
|
||||
delete(p.nodeActiveInboundTrees, nodeID)
|
||||
}
|
||||
|
||||
// GetUptime returns the uptime of the Xray process in seconds.
|
||||
|
||||
Reference in New Issue
Block a user