From aa4c8ad5da57087502a63e29326af713dee90776 Mon Sep 17 00:00:00 2001 From: hossinasaadi Date: Sun, 4 Aug 2024 17:36:05 +0400 Subject: [PATCH 1/7] add statsUserOnline bool to policy --- app/policy/config.go | 1 + app/policy/config.pb.go | 112 +++++++++++++++++++++----------------- app/policy/config.proto | 1 + features/policy/policy.go | 3 + infra/conf/policy.go | 2 + 5 files changed, 68 insertions(+), 51 deletions(-) diff --git a/app/policy/config.go b/app/policy/config.go index 267307b7906a..9e5ee1c2b447 100644 --- a/app/policy/config.go +++ b/app/policy/config.go @@ -73,6 +73,7 @@ func (p *Policy) ToCorePolicy() policy.Session { if p.Stats != nil { cp.Stats.UserUplink = p.Stats.UserUplink cp.Stats.UserDownlink = p.Stats.UserDownlink + cp.Stats.UserOnline = p.Stats.UserOnline } if p.Buffer != nil { cp.Buffer.PerConnection = p.Buffer.Connection diff --git a/app/policy/config.pb.go b/app/policy/config.pb.go index adcb363ad28f..e4294953d3b1 100644 --- a/app/policy/config.pb.go +++ b/app/policy/config.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 -// protoc v5.27.0 +// protoc-gen-go v1.34.2 +// protoc v4.25.3 // source: app/policy/config.proto package policy @@ -311,6 +311,7 @@ type Policy_Stats struct { UserUplink bool `protobuf:"varint,1,opt,name=user_uplink,json=userUplink,proto3" json:"user_uplink,omitempty"` UserDownlink bool `protobuf:"varint,2,opt,name=user_downlink,json=userDownlink,proto3" json:"user_downlink,omitempty"` + UserOnline bool `protobuf:"varint,3,opt,name=user_online,json=userOnline,proto3" json:"user_online,omitempty"` } func (x *Policy_Stats) Reset() { @@ -359,6 +360,13 @@ func (x *Policy_Stats) GetUserDownlink() bool { return false } +func (x *Policy_Stats) GetUserOnline() bool { + if x != nil { + return x.UserOnline + } + return false +} + type Policy_Buffer struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -485,7 +493,7 @@ var file_app_policy_config_proto_rawDesc = []byte{ 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x1e, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xa6, 0x04, 0x0a, 0x06, 0x50, + 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xc7, 0x04, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, @@ -512,49 +520,51 @@ var file_app_policy_config_proto_rawDesc = []byte{ 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x52, 0x0c, 0x64, 0x6f, - 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x4d, 0x0a, 0x05, 0x53, 0x74, + 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x4f, 0x6e, 0x6c, 0x79, 0x1a, 0x6e, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65, - 0x72, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x1a, 0x28, 0x0a, 0x06, 0x42, 0x75, 0x66, - 0x66, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0xfb, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x1a, - 0xaf, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, 0x62, - 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, 0x6c, 0x69, 0x6e, 0x6b, - 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, - 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x62, 0x6f, - 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x27, 0x0a, 0x0f, 0x6f, - 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, - 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, - 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, - 0x6b, 0x22, 0xcc, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a, 0x05, - 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, 0x72, - 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, - 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x1a, 0x51, 0x0a, - 0x0a, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x78, - 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, - 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xaa, - 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x73, 0x65, + 0x72, 0x5f, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x75, 0x73, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x1a, 0x28, 0x0a, 0x06, 0x42, 0x75, + 0x66, 0x66, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xfb, 0x01, 0x0a, 0x0c, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x39, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, + 0x1a, 0xaf, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x69, 0x6e, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, 0x70, 0x6c, 0x69, 0x6e, + 0x6b, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x64, 0x6f, 0x77, + 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x6e, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x27, 0x0a, 0x0f, + 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x5f, 0x75, 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x55, + 0x70, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x2b, 0x0a, 0x11, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x10, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x69, + 0x6e, 0x6b, 0x22, 0xcc, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x0a, + 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, + 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x1a, 0x51, + 0x0a, 0x0a, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x42, 0x4f, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, + 0x70, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x01, 0x5a, 0x24, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, + 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0xaa, 0x02, 0x0f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -570,7 +580,7 @@ func file_app_policy_config_proto_rawDescGZIP() []byte { } var file_app_policy_config_proto_msgTypes = make([]protoimpl.MessageInfo, 9) -var file_app_policy_config_proto_goTypes = []interface{}{ +var file_app_policy_config_proto_goTypes = []any{ (*Second)(nil), // 0: xray.app.policy.Second (*Policy)(nil), // 1: xray.app.policy.Policy (*SystemPolicy)(nil), // 2: xray.app.policy.SystemPolicy @@ -606,7 +616,7 @@ func file_app_policy_config_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_app_policy_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_app_policy_config_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Second); i { case 0: return &v.state @@ -618,7 +628,7 @@ func file_app_policy_config_proto_init() { return nil } } - file_app_policy_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_app_policy_config_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*Policy); i { case 0: return &v.state @@ -630,7 +640,7 @@ func file_app_policy_config_proto_init() { return nil } } - file_app_policy_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_app_policy_config_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*SystemPolicy); i { case 0: return &v.state @@ -642,7 +652,7 @@ func file_app_policy_config_proto_init() { return nil } } - file_app_policy_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_app_policy_config_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*Config); i { case 0: return &v.state @@ -654,7 +664,7 @@ func file_app_policy_config_proto_init() { return nil } } - file_app_policy_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_app_policy_config_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*Policy_Timeout); i { case 0: return &v.state @@ -666,7 +676,7 @@ func file_app_policy_config_proto_init() { return nil } } - file_app_policy_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_app_policy_config_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*Policy_Stats); i { case 0: return &v.state @@ -678,7 +688,7 @@ func file_app_policy_config_proto_init() { return nil } } - file_app_policy_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_app_policy_config_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*Policy_Buffer); i { case 0: return &v.state @@ -690,7 +700,7 @@ func file_app_policy_config_proto_init() { return nil } } - file_app_policy_config_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_app_policy_config_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*SystemPolicy_Stats); i { case 0: return &v.state diff --git a/app/policy/config.proto b/app/policy/config.proto index e5f2954723e8..eefcc17f3ad7 100644 --- a/app/policy/config.proto +++ b/app/policy/config.proto @@ -22,6 +22,7 @@ message Policy { message Stats { bool user_uplink = 1; bool user_downlink = 2; + bool user_online = 3; } message Buffer { diff --git a/features/policy/policy.go b/features/policy/policy.go index 4d3f7ecf32a8..d6fd20d06b82 100644 --- a/features/policy/policy.go +++ b/features/policy/policy.go @@ -27,6 +27,8 @@ type Stats struct { UserUplink bool // Whether or not to enable stat counter for user downlink traffic. UserDownlink bool + // Whether or not to enable online map for user. + UserOnline bool } // Buffer contains settings for internal buffer. @@ -123,6 +125,7 @@ func SessionDefault() Session { Stats: Stats{ UserUplink: false, UserDownlink: false, + UserOnline: false, }, Buffer: defaultBufferPolicy(), } diff --git a/infra/conf/policy.go b/infra/conf/policy.go index 5fbf01e672be..1182766cc854 100644 --- a/infra/conf/policy.go +++ b/infra/conf/policy.go @@ -11,6 +11,7 @@ type Policy struct { DownlinkOnly *uint32 `json:"downlinkOnly"` StatsUserUplink bool `json:"statsUserUplink"` StatsUserDownlink bool `json:"statsUserDownlink"` + StatsUserOnline bool `json:"statsUserOnline"` BufferSize *int32 `json:"bufferSize"` } @@ -34,6 +35,7 @@ func (t *Policy) Build() (*policy.Policy, error) { Stats: &policy.Policy_Stats{ UserUplink: t.StatsUserUplink, UserDownlink: t.StatsUserDownlink, + UserOnline: t.StatsUserOnline, }, } From 0b8ad84c2ba213f79d2552de896da8b79d68fa3c Mon Sep 17 00:00:00 2001 From: hossinasaadi Date: Sun, 4 Aug 2024 17:48:01 +0400 Subject: [PATCH 2/7] add OnlineMap struct to stats --- app/stats/online_map.go | 79 +++++++++++++++++++++++++++++++++++++++++ app/stats/stats.go | 51 ++++++++++++++++++++++---- features/stats/stats.go | 44 +++++++++++++++++++++++ 3 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 app/stats/online_map.go diff --git a/app/stats/online_map.go b/app/stats/online_map.go new file mode 100644 index 000000000000..5a325d0caeaa --- /dev/null +++ b/app/stats/online_map.go @@ -0,0 +1,79 @@ +package stats + +import ( + "sync" + "time" +) + +// OnlineMap is an implementation of stats.OnlineMap. +type OnlineMap struct { + value int + ipList map[string]time.Time + access sync.RWMutex +} + +// Count implements stats.OnlineMap. +func (c *OnlineMap) Count() int { + return c.value +} + +// List implements stats.OnlineMap. +func (c *OnlineMap) List() []string { + return c.GetKeys() +} + +// AddIP implements stats.OnlineMap. +func (c *OnlineMap) AddIP(ip string) { + list := c.ipList + + keys := c.GetKeys() + if list == nil { + list = map[string]time.Time{} + } + if ip == "127.0.0.1" { + return + } + if !contains(keys, ip) { + c.access.Lock() + list[ip] = time.Now() + c.access.Unlock() + } + list = c.RemoveExpiredIPs(list) + + c.value = len(list) + c.ipList = list +} + +func (c *OnlineMap) GetKeys() []string { + c.access.RLock() + defer c.access.RUnlock() + + keys := []string{} + for k := range c.ipList { + keys = append(keys, k) + } + return keys +} + +func (c *OnlineMap) RemoveExpiredIPs(list map[string]time.Time) map[string]time.Time { + c.access.Lock() + defer c.access.Unlock() + + now := time.Now() + for k, t := range list { + diff := now.Sub(t) + if diff.Seconds() > 20 { + delete(list, k) + } + } + return list +} + +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + return false +} diff --git a/app/stats/stats.go b/app/stats/stats.go index 95d5ae9c5d6d..4b2658dad0f2 100644 --- a/app/stats/stats.go +++ b/app/stats/stats.go @@ -13,17 +13,19 @@ import ( // Manager is an implementation of stats.Manager. type Manager struct { - access sync.RWMutex - counters map[string]*Counter - channels map[string]*Channel - running bool + access sync.RWMutex + counters map[string]*Counter + onlineMap map[string]*OnlineMap + channels map[string]*Channel + running bool } // NewManager creates an instance of Statistics Manager. func NewManager(ctx context.Context, config *Config) (*Manager, error) { m := &Manager{ - counters: make(map[string]*Counter), - channels: make(map[string]*Channel), + counters: make(map[string]*Counter), + onlineMap: make(map[string]*OnlineMap), + channels: make(map[string]*Channel), } return m, nil @@ -83,6 +85,43 @@ func (m *Manager) VisitCounters(visitor func(string, stats.Counter) bool) { } } +// RegisterOnlineMap implements stats.Manager. +func (m *Manager) RegisterOnlineMap(name string) (stats.OnlineMap, error) { + m.access.Lock() + defer m.access.Unlock() + + if _, found := m.onlineMap[name]; found { + return nil, errors.New("onlineMap ", name, " already registered.") + } + errors.LogDebug(context.Background(), "create new onlineMap ", name) + om := new(OnlineMap) + m.onlineMap[name] = om + return om, nil +} + +// UnregisterOnlineMap implements stats.Manager. +func (m *Manager) UnregisterOnlineMap(name string) error { + m.access.Lock() + defer m.access.Unlock() + + if _, found := m.onlineMap[name]; found { + errors.LogDebug(context.Background(), "remove onlineMap ", name) + delete(m.onlineMap, name) + } + return nil +} + +// GetOnlineMap implements stats.Manager. +func (m *Manager) GetOnlineMap(name string) stats.OnlineMap { + m.access.RLock() + defer m.access.RUnlock() + + if om, found := m.onlineMap[name]; found { + return om + } + return nil +} + // RegisterChannel implements stats.Manager. func (m *Manager) RegisterChannel(name string) (stats.Channel, error) { m.access.Lock() diff --git a/features/stats/stats.go b/features/stats/stats.go index a6c1f6beb803..f47cc6ff7ea8 100644 --- a/features/stats/stats.go +++ b/features/stats/stats.go @@ -22,6 +22,18 @@ type Counter interface { Add(int64) int64 } +// OnlineMap is the interface for stats. +// +// xray:api:stable +type OnlineMap interface { + // Count is the current value of the OnlineMap. + Count() int + // AddIP adds a ip to the current OnlineMap. + AddIP(string) + // List is the current OnlineMap ip list. + List() []string +} + // Channel is the interface for stats channel. // // xray:api:stable @@ -72,6 +84,13 @@ type Manager interface { // GetCounter returns a counter by its identifier. GetCounter(string) Counter + // RegisterCounter registers a new counter to the manager. The identifier string must not be empty, and unique among other counters. + RegisterOnlineMap(string) (OnlineMap, error) + // UnregisterCounter unregisters a counter from the manager by its identifier. + UnregisterOnlineMap(string) error + // GetCounter returns a counter by its identifier. + GetOnlineMap(string) OnlineMap + // RegisterChannel registers a new channel to the manager. The identifier string must not be empty, and unique among other channels. RegisterChannel(string) (Channel, error) // UnregisterChannel unregisters a channel from the manager by its identifier. @@ -90,6 +109,16 @@ func GetOrRegisterCounter(m Manager, name string) (Counter, error) { return m.RegisterCounter(name) } +// GetOrRegisterCounter tries to get the StatCounter first. If not exist, it then tries to create a new counter. +func GetOrRegisterOnlineMap(m Manager, name string) (OnlineMap, error) { + onlineMap := m.GetOnlineMap(name) + if onlineMap != nil { + return onlineMap, nil + } + + return m.RegisterOnlineMap(name) +} + // GetOrRegisterChannel tries to get the StatChannel first. If not exist, it then tries to create a new channel. func GetOrRegisterChannel(m Manager, name string) (Channel, error) { channel := m.GetChannel(name) @@ -130,6 +159,21 @@ func (NoopManager) GetCounter(string) Counter { return nil } +// RegisterCounter implements Manager. +func (NoopManager) RegisterOnlineMap(string) (OnlineMap, error) { + return nil, errors.New("not implemented") +} + +// UnregisterCounter implements Manager. +func (NoopManager) UnregisterOnlineMap(string) error { + return nil +} + +// GetCounter implements Manager. +func (NoopManager) GetOnlineMap(string) OnlineMap { + return nil +} + // RegisterChannel implements Manager. func (NoopManager) RegisterChannel(string) (Channel, error) { return nil, errors.New("not implemented") From 233231091c5d1e2eaac01a97a4956f028b56fe4e Mon Sep 17 00:00:00 2001 From: hossinasaadi Date: Sun, 4 Aug 2024 17:49:00 +0400 Subject: [PATCH 3/7] apply UserOnline functionality to dispatcher --- app/dispatcher/default.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 34119ac2a348..36009a6c3a03 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -10,8 +10,8 @@ import ( "time" "github.com/xtls/xray-core/common" - "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/log" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/protocol" @@ -177,6 +177,18 @@ func (d *DefaultDispatcher) getLink(ctx context.Context) (*transport.Link, *tran } } } + + if p.Stats.UserOnline { + name := "user>>>" + user.Email + ">>>online" + if om, _ := stats.GetOrRegisterOnlineMap(d.stats, name); om != nil { + sessionInbounds := session.InboundFromContext(ctx) + userIP := sessionInbounds.Source.Address.String() + om.AddIP(userIP) + // log Online user with ips + // errors.LogDebug(ctx, "user>>>" + user.Email + ">>>online", om.Count(), om.List()) + + } + } } return inboundLink, outboundLink From 67a31b1b19d49710eca47ab5fa03d3b5c805078e Mon Sep 17 00:00:00 2001 From: hossinasaadi Date: Sun, 4 Aug 2024 18:05:49 +0400 Subject: [PATCH 4/7] add statsonline api command --- app/stats/command/command.go | 14 +++++ app/stats/command/command.pb.go | 84 +++++++++++++++------------ app/stats/command/command.proto | 1 + app/stats/command/command_grpc.pb.go | 58 +++++++++++++----- main/commands/all/api/api.go | 1 + main/commands/all/api/stats_online.go | 47 +++++++++++++++ 6 files changed, 153 insertions(+), 52 deletions(-) create mode 100644 main/commands/all/api/stats_online.go diff --git a/app/stats/command/command.go b/app/stats/command/command.go index 2c603ca2e74c..dc99ee5676ff 100644 --- a/app/stats/command/command.go +++ b/app/stats/command/command.go @@ -48,6 +48,20 @@ func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (* }, nil } +func (s *statsServer) GetStatsOnline(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) { + c := s.stats.GetOnlineMap(request.Name) + if c == nil { + return nil, errors.New(request.Name, " not found.") + } + value := int64(c.Count()) + return &GetStatsResponse{ + Stat: &Stat{ + Name: request.Name, + Value: value, + }, + }, nil +} + func (s *statsServer) QueryStats(ctx context.Context, request *QueryStatsRequest) (*QueryStatsResponse, error) { matcher, err := strmatcher.Substr.New(request.Pattern) if err != nil { diff --git a/app/stats/command/command.pb.go b/app/stats/command/command.pb.go index 418e7fb1e890..36fb23d7230c 100644 --- a/app/stats/command/command.pb.go +++ b/app/stats/command/command.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 -// protoc v5.27.0 +// protoc-gen-go v1.34.2 +// protoc v4.25.3 // source: app/stats/command/command.proto package command @@ -522,7 +522,7 @@ var file_app_stats_command_command_proto_rawDesc = []byte{ 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x50, 0x61, 0x75, 0x73, 0x65, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x55, 0x70, 0x74, - 0x69, 0x6d, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xba, 0x02, + 0x69, 0x6d, 0x65, 0x22, 0x08, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x32, 0xa1, 0x03, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, @@ -530,26 +530,32 @@ var file_app_stats_command_command_proto_rawDesc = []byte{ 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x65, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x29, 0x2e, - 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, - 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, - 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, + 0x65, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, + 0x65, 0x12, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, + 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, + 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x65, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, + 0x74, 0x61, 0x74, 0x73, 0x12, 0x29, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, + 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, + 0x0b, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x78, + 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, - 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, - 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2e, 0x53, 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, - 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, - 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, - 0x70, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x79, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x42, 0x64, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, + 0x70, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x50, + 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, + 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, + 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0xaa, 0x02, + 0x16, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x2e, + 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -565,7 +571,7 @@ func file_app_stats_command_command_proto_rawDescGZIP() []byte { } var file_app_stats_command_command_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_app_stats_command_command_proto_goTypes = []interface{}{ +var file_app_stats_command_command_proto_goTypes = []any{ (*GetStatsRequest)(nil), // 0: xray.app.stats.command.GetStatsRequest (*Stat)(nil), // 1: xray.app.stats.command.Stat (*GetStatsResponse)(nil), // 2: xray.app.stats.command.GetStatsResponse @@ -579,13 +585,15 @@ var file_app_stats_command_command_proto_depIdxs = []int32{ 1, // 0: xray.app.stats.command.GetStatsResponse.stat:type_name -> xray.app.stats.command.Stat 1, // 1: xray.app.stats.command.QueryStatsResponse.stat:type_name -> xray.app.stats.command.Stat 0, // 2: xray.app.stats.command.StatsService.GetStats:input_type -> xray.app.stats.command.GetStatsRequest - 3, // 3: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest - 5, // 4: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest - 2, // 5: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse - 4, // 6: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse - 6, // 7: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse - 5, // [5:8] is the sub-list for method output_type - 2, // [2:5] is the sub-list for method input_type + 0, // 3: xray.app.stats.command.StatsService.GetStatsOnline:input_type -> xray.app.stats.command.GetStatsRequest + 3, // 4: xray.app.stats.command.StatsService.QueryStats:input_type -> xray.app.stats.command.QueryStatsRequest + 5, // 5: xray.app.stats.command.StatsService.GetSysStats:input_type -> xray.app.stats.command.SysStatsRequest + 2, // 6: xray.app.stats.command.StatsService.GetStats:output_type -> xray.app.stats.command.GetStatsResponse + 2, // 7: xray.app.stats.command.StatsService.GetStatsOnline:output_type -> xray.app.stats.command.GetStatsResponse + 4, // 8: xray.app.stats.command.StatsService.QueryStats:output_type -> xray.app.stats.command.QueryStatsResponse + 6, // 9: xray.app.stats.command.StatsService.GetSysStats:output_type -> xray.app.stats.command.SysStatsResponse + 6, // [6:10] is the sub-list for method output_type + 2, // [2:6] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name @@ -597,7 +605,7 @@ func file_app_stats_command_command_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_app_stats_command_command_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_app_stats_command_command_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*GetStatsRequest); i { case 0: return &v.state @@ -609,7 +617,7 @@ func file_app_stats_command_command_proto_init() { return nil } } - file_app_stats_command_command_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_app_stats_command_command_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*Stat); i { case 0: return &v.state @@ -621,7 +629,7 @@ func file_app_stats_command_command_proto_init() { return nil } } - file_app_stats_command_command_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_app_stats_command_command_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*GetStatsResponse); i { case 0: return &v.state @@ -633,7 +641,7 @@ func file_app_stats_command_command_proto_init() { return nil } } - file_app_stats_command_command_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_app_stats_command_command_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*QueryStatsRequest); i { case 0: return &v.state @@ -645,7 +653,7 @@ func file_app_stats_command_command_proto_init() { return nil } } - file_app_stats_command_command_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_app_stats_command_command_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*QueryStatsResponse); i { case 0: return &v.state @@ -657,7 +665,7 @@ func file_app_stats_command_command_proto_init() { return nil } } - file_app_stats_command_command_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_app_stats_command_command_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*SysStatsRequest); i { case 0: return &v.state @@ -669,7 +677,7 @@ func file_app_stats_command_command_proto_init() { return nil } } - file_app_stats_command_command_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_app_stats_command_command_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*SysStatsResponse); i { case 0: return &v.state @@ -681,7 +689,7 @@ func file_app_stats_command_command_proto_init() { return nil } } - file_app_stats_command_command_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_app_stats_command_command_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*Config); i { case 0: return &v.state diff --git a/app/stats/command/command.proto b/app/stats/command/command.proto index bd1f9781d973..1d2ed867540c 100644 --- a/app/stats/command/command.proto +++ b/app/stats/command/command.proto @@ -48,6 +48,7 @@ message SysStatsResponse { service StatsService { rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {} + rpc GetStatsOnline(GetStatsRequest) returns (GetStatsResponse) {} rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {} rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {} } diff --git a/app/stats/command/command_grpc.pb.go b/app/stats/command/command_grpc.pb.go index e9420299b74e..bb650e7589f5 100644 --- a/app/stats/command/command_grpc.pb.go +++ b/app/stats/command/command_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 -// - protoc v5.27.0 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v4.25.3 // source: app/stats/command/command.proto package command @@ -18,17 +18,12 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 -const ( - StatsService_GetStats_FullMethodName = "/xray.app.stats.command.StatsService/GetStats" - StatsService_QueryStats_FullMethodName = "/xray.app.stats.command.StatsService/QueryStats" - StatsService_GetSysStats_FullMethodName = "/xray.app.stats.command.StatsService/GetSysStats" -) - // StatsServiceClient is the client API for StatsService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type StatsServiceClient interface { GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) + GetStatsOnline(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) } @@ -43,7 +38,16 @@ func NewStatsServiceClient(cc grpc.ClientConnInterface) StatsServiceClient { func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) { out := new(GetStatsResponse) - err := c.cc.Invoke(ctx, StatsService_GetStats_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/GetStats", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *statsServiceClient) GetStatsOnline(ctx context.Context, in *GetStatsRequest, opts ...grpc.CallOption) (*GetStatsResponse, error) { + out := new(GetStatsResponse) + err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/GetStatsOnline", in, out, opts...) if err != nil { return nil, err } @@ -52,7 +56,7 @@ func (c *statsServiceClient) GetStats(ctx context.Context, in *GetStatsRequest, func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) { out := new(QueryStatsResponse) - err := c.cc.Invoke(ctx, StatsService_QueryStats_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/QueryStats", in, out, opts...) if err != nil { return nil, err } @@ -61,7 +65,7 @@ func (c *statsServiceClient) QueryStats(ctx context.Context, in *QueryStatsReque func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsRequest, opts ...grpc.CallOption) (*SysStatsResponse, error) { out := new(SysStatsResponse) - err := c.cc.Invoke(ctx, StatsService_GetSysStats_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/xray.app.stats.command.StatsService/GetSysStats", in, out, opts...) if err != nil { return nil, err } @@ -73,6 +77,7 @@ func (c *statsServiceClient) GetSysStats(ctx context.Context, in *SysStatsReques // for forward compatibility type StatsServiceServer interface { GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) + GetStatsOnline(context.Context, *GetStatsRequest) (*GetStatsResponse, error) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) GetSysStats(context.Context, *SysStatsRequest) (*SysStatsResponse, error) mustEmbedUnimplementedStatsServiceServer() @@ -85,6 +90,9 @@ type UnimplementedStatsServiceServer struct { func (UnimplementedStatsServiceServer) GetStats(context.Context, *GetStatsRequest) (*GetStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetStats not implemented") } +func (UnimplementedStatsServiceServer) GetStatsOnline(context.Context, *GetStatsRequest) (*GetStatsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetStatsOnline not implemented") +} func (UnimplementedStatsServiceServer) QueryStats(context.Context, *QueryStatsRequest) (*QueryStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryStats not implemented") } @@ -114,7 +122,7 @@ func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec fu } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: StatsService_GetStats_FullMethodName, + FullMethod: "/xray.app.stats.command.StatsService/GetStats", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StatsServiceServer).GetStats(ctx, req.(*GetStatsRequest)) @@ -122,6 +130,24 @@ func _StatsService_GetStats_Handler(srv interface{}, ctx context.Context, dec fu return interceptor(ctx, in, info, handler) } +func _StatsService_GetStatsOnline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetStatsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StatsServiceServer).GetStatsOnline(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/xray.app.stats.command.StatsService/GetStatsOnline", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StatsServiceServer).GetStatsOnline(ctx, req.(*GetStatsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _StatsService_QueryStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryStatsRequest) if err := dec(in); err != nil { @@ -132,7 +158,7 @@ func _StatsService_QueryStats_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: StatsService_QueryStats_FullMethodName, + FullMethod: "/xray.app.stats.command.StatsService/QueryStats", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StatsServiceServer).QueryStats(ctx, req.(*QueryStatsRequest)) @@ -150,7 +176,7 @@ func _StatsService_GetSysStats_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: StatsService_GetSysStats_FullMethodName, + FullMethod: "/xray.app.stats.command.StatsService/GetSysStats", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StatsServiceServer).GetSysStats(ctx, req.(*SysStatsRequest)) @@ -169,6 +195,10 @@ var StatsService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetStats", Handler: _StatsService_GetStats_Handler, }, + { + MethodName: "GetStatsOnline", + Handler: _StatsService_GetStatsOnline_Handler, + }, { MethodName: "QueryStats", Handler: _StatsService_QueryStats_Handler, diff --git a/main/commands/all/api/api.go b/main/commands/all/api/api.go index ea43d1070c2d..859f6d121592 100644 --- a/main/commands/all/api/api.go +++ b/main/commands/all/api/api.go @@ -24,5 +24,6 @@ var CmdAPI = &base.Command{ cmdAddRules, cmdRemoveRules, cmdSourceIpBlock, + cmdOnlineStats, }, } diff --git a/main/commands/all/api/stats_online.go b/main/commands/all/api/stats_online.go new file mode 100644 index 000000000000..935928075302 --- /dev/null +++ b/main/commands/all/api/stats_online.go @@ -0,0 +1,47 @@ +package api + +import ( + statsService "github.com/xtls/xray-core/app/stats/command" + "github.com/xtls/xray-core/main/commands/base" +) + +var cmdOnlineStats = &base.Command{ + CustomFlags: true, + UsageLine: "{{.Exec}} api statsonline [--server=127.0.0.1:8080] [-name '']", + Short: "Get online user", + Long: ` +Get statistics from Xray. +Arguments: + -s, -server + The API server address. Default 127.0.0.1:8080 + -t, -timeout + Timeout seconds to call API. Default 3 + -email + email of the user. + -reset + Reset the counter to fetching its value. +Example: + {{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -email "user1@test.com" +`, + Run: executeOnlineStats, +} + +func executeOnlineStats(cmd *base.Command, args []string) { + setSharedFlags(cmd) + email := cmd.Flag.String("email", "", "") + cmd.Flag.Parse(args) + statName := "user>>>" + *email + ">>>online" + conn, ctx, close := dialAPIServer() + defer close() + + client := statsService.NewStatsServiceClient(conn) + r := &statsService.GetStatsRequest{ + Name: statName, + Reset_: false, + } + resp, err := client.GetStatsOnline(ctx, r) + if err != nil { + base.Fatalf("failed to get stats: %s", err) + } + showJSONResponse(resp) +} From f8a5732bffe25cd0ed4bd273dc49ab7adaac0caf Mon Sep 17 00:00:00 2001 From: hossinasaadi Date: Sun, 4 Aug 2024 18:33:02 +0400 Subject: [PATCH 5/7] fix comments --- features/stats/stats.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/features/stats/stats.go b/features/stats/stats.go index f47cc6ff7ea8..915a7fc15199 100644 --- a/features/stats/stats.go +++ b/features/stats/stats.go @@ -84,11 +84,11 @@ type Manager interface { // GetCounter returns a counter by its identifier. GetCounter(string) Counter - // RegisterCounter registers a new counter to the manager. The identifier string must not be empty, and unique among other counters. + // RegisterOnlineMap registers a new onlinemap to the manager. The identifier string must not be empty, and unique among other onlinemaps. RegisterOnlineMap(string) (OnlineMap, error) - // UnregisterCounter unregisters a counter from the manager by its identifier. + // UnregisterOnlineMap unregisters a onlinemap from the manager by its identifier. UnregisterOnlineMap(string) error - // GetCounter returns a counter by its identifier. + // GetOnlineMap returns a onlinemap by its identifier. GetOnlineMap(string) OnlineMap // RegisterChannel registers a new channel to the manager. The identifier string must not be empty, and unique among other channels. @@ -109,7 +109,7 @@ func GetOrRegisterCounter(m Manager, name string) (Counter, error) { return m.RegisterCounter(name) } -// GetOrRegisterCounter tries to get the StatCounter first. If not exist, it then tries to create a new counter. +// GetOrRegisterOnlineMap tries to get the OnlineMap first. If not exist, it then tries to create a new onlinemap. func GetOrRegisterOnlineMap(m Manager, name string) (OnlineMap, error) { onlineMap := m.GetOnlineMap(name) if onlineMap != nil { @@ -159,17 +159,17 @@ func (NoopManager) GetCounter(string) Counter { return nil } -// RegisterCounter implements Manager. +// RegisterOnlineMap implements Manager. func (NoopManager) RegisterOnlineMap(string) (OnlineMap, error) { return nil, errors.New("not implemented") } -// UnregisterCounter implements Manager. +// UnregisterOnlineMap implements Manager. func (NoopManager) UnregisterOnlineMap(string) error { return nil } -// GetCounter implements Manager. +// GetOnlineMap implements Manager. func (NoopManager) GetOnlineMap(string) OnlineMap { return nil } From f2d3d0d6d8818dcbffdd44a36e9a1d4b71715f49 Mon Sep 17 00:00:00 2001 From: Hossin Asaadi Date: Mon, 5 Aug 2024 09:37:34 +0400 Subject: [PATCH 6/7] Update app/stats/online_map.go Co-authored-by: mmmray <142015632+mmmray@users.noreply.github.com> --- app/stats/online_map.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/stats/online_map.go b/app/stats/online_map.go index 5a325d0caeaa..72a0c3a1c830 100644 --- a/app/stats/online_map.go +++ b/app/stats/online_map.go @@ -33,7 +33,7 @@ func (c *OnlineMap) AddIP(ip string) { if ip == "127.0.0.1" { return } - if !contains(keys, ip) { + if _, ok := list[ip]; !ok { c.access.Lock() list[ip] = time.Now() c.access.Unlock() From ac7d3e2b3c8d70b1b0e22e18fa51fedf2f2991b4 Mon Sep 17 00:00:00 2001 From: hossinasaadi Date: Mon, 5 Aug 2024 11:22:35 +0400 Subject: [PATCH 7/7] improve AddIP --- app/stats/online_map.go | 35 ++++++++++++++++++----------------- app/stats/stats.go | 2 +- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/stats/online_map.go b/app/stats/online_map.go index 72a0c3a1c830..0ba2b924a084 100644 --- a/app/stats/online_map.go +++ b/app/stats/online_map.go @@ -7,9 +7,20 @@ import ( // OnlineMap is an implementation of stats.OnlineMap. type OnlineMap struct { - value int - ipList map[string]time.Time - access sync.RWMutex + value int + ipList map[string]time.Time + access sync.RWMutex + lastCleanup time.Time + cleanupPeriod time.Duration +} + +// NewOnlineMap creates a new instance of OnlineMap. +func NewOnlineMap() *OnlineMap { + return &OnlineMap{ + ipList: make(map[string]time.Time), + lastCleanup: time.Now(), + cleanupPeriod: 10 * time.Second, + } } // Count implements stats.OnlineMap. @@ -26,10 +37,6 @@ func (c *OnlineMap) List() []string { func (c *OnlineMap) AddIP(ip string) { list := c.ipList - keys := c.GetKeys() - if list == nil { - list = map[string]time.Time{} - } if ip == "127.0.0.1" { return } @@ -38,7 +45,10 @@ func (c *OnlineMap) AddIP(ip string) { list[ip] = time.Now() c.access.Unlock() } - list = c.RemoveExpiredIPs(list) + if time.Since(c.lastCleanup) > c.cleanupPeriod { + list = c.RemoveExpiredIPs(list) + c.lastCleanup = time.Now() + } c.value = len(list) c.ipList = list @@ -68,12 +78,3 @@ func (c *OnlineMap) RemoveExpiredIPs(list map[string]time.Time) map[string]time. } return list } - -func contains(s []string, str string) bool { - for _, v := range s { - if v == str { - return true - } - } - return false -} diff --git a/app/stats/stats.go b/app/stats/stats.go index 4b2658dad0f2..c7aaecc41967 100644 --- a/app/stats/stats.go +++ b/app/stats/stats.go @@ -94,7 +94,7 @@ func (m *Manager) RegisterOnlineMap(name string) (stats.OnlineMap, error) { return nil, errors.New("onlineMap ", name, " already registered.") } errors.LogDebug(context.Background(), "create new onlineMap ", name) - om := new(OnlineMap) + om := NewOnlineMap() m.onlineMap[name] = om return om, nil }