Files
SingboxForPanel/service/xboard/tracker.go
2026-04-16 10:29:41 +08:00

100 lines
2.2 KiB
Go

package xboard
import (
"net"
"sort"
"strconv"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/service/ssmapi"
N "github.com/sagernet/sing/common/network"
)
const aliveIPRetention = 5 * time.Minute
var _ adapter.SSMTracker = (*xboardTracker)(nil)
type xboardTracker struct {
service *Service
traffic *ssmapi.TrafficManager
}
func newXboardTracker(service *Service) *xboardTracker {
return &xboardTracker{
service: service,
traffic: ssmapi.NewTrafficManager(),
}
}
func (t *xboardTracker) TrafficManager() *ssmapi.TrafficManager {
return t.traffic
}
func (t *xboardTracker) TrackConnection(conn net.Conn, metadata adapter.InboundContext) net.Conn {
t.service.recordAliveIP(metadata)
return t.traffic.TrackConnection(conn, metadata)
}
func (t *xboardTracker) TrackPacketConnection(conn N.PacketConn, metadata adapter.InboundContext) N.PacketConn {
t.service.recordAliveIP(metadata)
return t.traffic.TrackPacketConnection(conn, metadata)
}
func (s *Service) recordAliveIP(metadata adapter.InboundContext) {
if metadata.User == "" || !metadata.Source.IsValid() || !metadata.Source.Addr.IsValid() {
return
}
clientIP := metadata.Source.Addr.Unmap().String()
if clientIP == "" {
return
}
s.access.Lock()
defer s.access.Unlock()
ipSet, exists := s.aliveUsers[metadata.User]
if !exists {
ipSet = make(map[string]time.Time)
s.aliveUsers[metadata.User] = ipSet
}
ipSet[clientIP] = time.Now()
}
func (s *Service) buildAlivePayload() map[string][]string {
now := time.Now()
s.access.Lock()
defer s.access.Unlock()
payload := make(map[string][]string)
for userName, ipSet := range s.aliveUsers {
userMeta, exists := s.localUsers[userName]
if !exists || userMeta.ID == 0 {
delete(s.aliveUsers, userName)
continue
}
activeIPs := make([]string, 0, len(ipSet))
for ip, lastSeen := range ipSet {
if now.Sub(lastSeen) > aliveIPRetention {
delete(ipSet, ip)
continue
}
activeIPs = append(activeIPs, ip)
}
if len(ipSet) == 0 {
delete(s.aliveUsers, userName)
}
if len(activeIPs) == 0 {
continue
}
sort.Strings(activeIPs)
payload[strconv.Itoa(userMeta.ID)] = activeIPs
}
return payload
}