Add v2ray user stats api

This commit is contained in:
世界 2023-02-08 16:50:15 +08:00
parent 7ea9d48987
commit 39514b3ca0
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
6 changed files with 39 additions and 8 deletions

View file

@ -45,6 +45,6 @@ type V2RayServer interface {
} }
type V2RayStatsService interface { type V2RayStatsService interface {
RoutedConnection(inbound string, outbound string, conn net.Conn) net.Conn RoutedConnection(inbound string, outbound string, user string, conn net.Conn) net.Conn
RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn
} }

View file

@ -25,6 +25,9 @@
"outbounds": [ "outbounds": [
"proxy", "proxy",
"direct" "direct"
],
"users": [
"sekai"
] ]
} }
} }
@ -109,3 +112,7 @@ Inbound list to count traffic.
#### stats.outbounds #### stats.outbounds
Outbound list to count traffic. Outbound list to count traffic.
#### stats.users
User list to count traffic.

View file

@ -25,6 +25,9 @@
"outbounds": [ "outbounds": [
"proxy", "proxy",
"direct" "direct"
],
"users": [
"sekai"
] ]
} }
} }
@ -107,3 +110,7 @@ gRPC API 监听地址。如果为空,则禁用 V2Ray API。
#### stats.outbounds #### stats.outbounds
统计流量的出站列表。 统计流量的出站列表。
#### stats.users
统计流量的用户列表。

View file

@ -31,6 +31,7 @@ type StatsService struct {
createdAt time.Time createdAt time.Time
inbounds map[string]bool inbounds map[string]bool
outbounds map[string]bool outbounds map[string]bool
users map[string]bool
access sync.Mutex access sync.Mutex
counters map[string]*atomic.Int64 counters map[string]*atomic.Int64
} }
@ -41,26 +42,32 @@ func NewStatsService(options option.V2RayStatsServiceOptions) *StatsService {
} }
inbounds := make(map[string]bool) inbounds := make(map[string]bool)
outbounds := make(map[string]bool) outbounds := make(map[string]bool)
users := make(map[string]bool)
for _, inbound := range options.Inbounds { for _, inbound := range options.Inbounds {
inbounds[inbound] = true inbounds[inbound] = true
} }
for _, outbound := range options.Outbounds { for _, outbound := range options.Outbounds {
outbounds[outbound] = true outbounds[outbound] = true
} }
for _, user := range options.Users {
users[user] = true
}
return &StatsService{ return &StatsService{
createdAt: time.Now(), createdAt: time.Now(),
inbounds: inbounds, inbounds: inbounds,
outbounds: outbounds, outbounds: outbounds,
users: users,
counters: make(map[string]*atomic.Int64), counters: make(map[string]*atomic.Int64),
} }
} }
func (s *StatsService) RoutedConnection(inbound string, outbound string, conn net.Conn) net.Conn { func (s *StatsService) RoutedConnection(inbound string, outbound string, user string, conn net.Conn) net.Conn {
var readCounter []*atomic.Int64 var readCounter []*atomic.Int64
var writeCounter []*atomic.Int64 var writeCounter []*atomic.Int64
countInbound := inbound != "" && s.inbounds[inbound] countInbound := inbound != "" && s.inbounds[inbound]
countOutbound := outbound != "" && s.outbounds[outbound] countOutbound := outbound != "" && s.outbounds[outbound]
if !countInbound && !countOutbound { countUser := user != "" && s.users[user]
if !countInbound && !countOutbound && !countUser {
return conn return conn
} }
s.access.Lock() s.access.Lock()
@ -72,16 +79,21 @@ func (s *StatsService) RoutedConnection(inbound string, outbound string, conn ne
readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink")) readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink")) writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
} }
if countUser {
readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
}
s.access.Unlock() s.access.Unlock()
return trackerconn.New(conn, readCounter, writeCounter) return trackerconn.New(conn, readCounter, writeCounter)
} }
func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, conn N.PacketConn) N.PacketConn { func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, user string, conn N.PacketConn) N.PacketConn {
var readCounter []*atomic.Int64 var readCounter []*atomic.Int64
var writeCounter []*atomic.Int64 var writeCounter []*atomic.Int64
countInbound := inbound != "" && s.inbounds[inbound] countInbound := inbound != "" && s.inbounds[inbound]
countOutbound := outbound != "" && s.outbounds[outbound] countOutbound := outbound != "" && s.outbounds[outbound]
if !countInbound && !countOutbound { countUser := user != "" && s.users[user]
if !countInbound && !countOutbound && !countUser {
return conn return conn
} }
s.access.Lock() s.access.Lock()
@ -93,6 +105,10 @@ func (s *StatsService) RoutedPacketConnection(inbound string, outbound string, c
readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink")) readCounter = append(readCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>uplink"))
writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink")) writeCounter = append(writeCounter, s.loadOrCreateCounter("outbound>>>"+outbound+">>>traffic>>>downlink"))
} }
if countUser {
readCounter = append(readCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>uplink"))
writeCounter = append(writeCounter, s.loadOrCreateCounter("user>>>"+user+">>>traffic>>>downlink"))
}
s.access.Unlock() s.access.Unlock()
return trackerconn.NewPacket(conn, readCounter, writeCounter) return trackerconn.NewPacket(conn, readCounter, writeCounter)
} }

View file

@ -9,4 +9,5 @@ type V2RayStatsServiceOptions struct {
Enabled bool `json:"enabled,omitempty"` Enabled bool `json:"enabled,omitempty"`
Inbounds []string `json:"inbounds,omitempty"` Inbounds []string `json:"inbounds,omitempty"`
Outbounds []string `json:"outbounds,omitempty"` Outbounds []string `json:"outbounds,omitempty"`
Users []string `json:"users,omitempty"`
} }

View file

@ -600,7 +600,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
} }
if r.v2rayServer != nil { if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil { if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), conn) conn = statsService.RoutedConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
} }
} }
return detour.NewConnection(ctx, conn, metadata) return detour.NewConnection(ctx, conn, metadata)
@ -678,7 +678,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
} }
if r.v2rayServer != nil { if r.v2rayServer != nil {
if statsService := r.v2rayServer.StatsService(); statsService != nil { if statsService := r.v2rayServer.StatsService(); statsService != nil {
conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), conn) conn = statsService.RoutedPacketConnection(metadata.Inbound, detour.Tag(), metadata.User, conn)
} }
} }
return detour.NewPacketConnection(ctx, conn, metadata) return detour.NewPacketConnection(ctx, conn, metadata)