From 70f250dfe1e24249a4cc8102e3fef65959dfb15a Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Mon, 5 Jun 2023 00:32:19 +0330 Subject: [PATCH] [feature] using xray api and more Improve DB performance [api] backward compatibility: add client by update Co-Authored-By: Alireza Ahmadi --- go.mod | 9 +- go.sum | 18 +- web/controller/inbound.go | 29 ++- web/job/check_inbound_job.go | 14 +- web/service/inbound.go | 355 +++++++++++++++++++++++++++++------ web/service/xray.go | 13 +- xray/api.go | 182 ++++++++++++++++++ xray/process.go | 91 --------- 8 files changed, 533 insertions(+), 178 deletions(-) create mode 100644 xray/api.go diff --git a/go.mod b/go.mod index f4dda6dc..533d9933 100644 --- a/go.mod +++ b/go.mod @@ -27,13 +27,15 @@ require ( github.com/andybalholm/brotli v1.0.5 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/fasthttp/router v1.4.19 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gaukas/godicttls v0.0.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-playground/validator/v10 v10.14.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect @@ -51,14 +53,19 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect + github.com/refraction-networking/utls v1.3.2 // indirect + github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect + github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/tklauser/go-sysconf v0.3.11 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect + github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.47.0 // indirect + github.com/xtls/reality v0.0.0-20230331223127-176a94313eda // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.9.0 // indirect diff --git a/go.sum b/go.sum index 3ab60d1e..ad0a1921 100644 --- a/go.sum +++ b/go.sum @@ -18,13 +18,16 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583j github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0= +github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/fasthttp/router v1.4.19 h1:RLE539IU/S4kfb4MP56zgP0TIBU9kEg0ID9GpWO0vqk= github.com/fasthttp/router v1.4.19/go.mod h1:+Fh3YOd8x1+he6ZS+d2iUDBH9MGGZ1xQFUor0DE9rKE= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gaukas/godicttls v0.0.3 h1:YNDIf0d9adcxOijiLrEzpfZGAkNwLRzPaG6OjU7EITk= +github.com/gaukas/godicttls v0.0.3/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= github.com/gin-contrib/sessions v0.0.4 h1:gq4fNa1Zmp564iHP5G6EBuktilEos8VKhe2sza1KMgo= github.com/gin-contrib/sessions v0.0.4/go.mod h1:pQ3sIyviBBGcxgyR8mkeJuXbeV3h3NYmhJADQTq5+Vo= @@ -47,8 +50,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= -github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= -github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= @@ -86,8 +89,6 @@ github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nV github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= @@ -134,7 +135,9 @@ github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc8 github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= github.com/refraction-networking/utls v1.3.2 h1:o+AkWB57mkcoW36ET7uJ002CpBWHu0KPxi6vzxvPnv8= +github.com/refraction-networking/utls v1.3.2/go.mod h1:fmoaOww2bxzzEpIKOebIsnBvjQpqP7L2vcm/9KUfm/E= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= +github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/sagernet/sing v0.2.3 h1:V50MvZ4c3Iij2lYFWPlzL1PyipwSzjGeN9x+Ox89vpk= @@ -143,6 +146,7 @@ github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aW github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= +github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= github.com/shirou/gopsutil/v3 v3.23.5 h1:5SgDCeQ0KW0S4N0znjeM/eFHXXOKyv2dVNgRq/c9P6Y= github.com/shirou/gopsutil/v3 v3.23.5/go.mod h1:Ng3Maa27Q2KARVJ0SPZF5NdrQSC3XHKP8IIWrHgMeLY= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -154,6 +158,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -164,7 +169,6 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= -github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= @@ -176,11 +180,13 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= +github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/xtls/reality v0.0.0-20230331223127-176a94313eda h1:psRJD2RrZbnI0OWyHvXfgYCPqlRM5q5SPDcjDoDBWhE= +github.com/xtls/reality v0.0.0-20230331223127-176a94313eda/go.mod h1:rkuAY1S9F8eI8gDiPDYvACE8e2uwkyg8qoOTuwWov7Y= github.com/xtls/xray-core v1.8.1 h1:iSTTqXj82ZdwC1ah+eV331X4JTcnrDz+WuKuB/EB3P4= github.com/xtls/xray-core v1.8.1/go.mod h1:AXxSso0MZwUE4NhRocCfHCg73BtJ+T2dSpQVo1Cg9VM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -223,7 +229,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -264,6 +269,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/sqlite v1.5.1 h1:hYyrLkAWE71bcarJDPdZNTLWtr8XrSjOWyjUYI6xdL4= diff --git a/web/controller/inbound.go b/web/controller/inbound.go index 815f1788..461c4b27 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -79,7 +79,6 @@ func (a *InboundController) getInbound(c *gin.Context) { } jsonObj(c, inbound, nil) } - func (a *InboundController) getClientTraffics(c *gin.Context) { email := c.Param("email") clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email) @@ -178,13 +177,15 @@ func (a *InboundController) addInboundClient(c *gin.Context) { return } - err = a.inboundService.AddInboundClient(data) + needRestart := false + + needRestart, err = a.inboundService.AddInboundClient(data) if err != nil { jsonMsg(c, "Something went wrong!", err) return } jsonMsg(c, "Client(s) added", nil) - if err == nil { + if err == nil && needRestart { a.xrayService.SetToNeedRestart() } } @@ -197,13 +198,15 @@ func (a *InboundController) delInboundClient(c *gin.Context) { } clientId := c.Param("clientId") - err = a.inboundService.DelInboundClient(id, clientId) + needRestart := false + + needRestart, err = a.inboundService.DelInboundClient(id, clientId) if err != nil { jsonMsg(c, "Something went wrong!", err) return } jsonMsg(c, "Client deleted", nil) - if err == nil { + if err == nil && needRestart { a.xrayService.SetToNeedRestart() } } @@ -218,13 +221,15 @@ func (a *InboundController) updateInboundClient(c *gin.Context) { return } - err = a.inboundService.UpdateInboundClient(inbound, clientId) + needRestart := false + + needRestart, err = a.inboundService.UpdateInboundClient(inbound, clientId) if err != nil { jsonMsg(c, "Something went wrong!", err) return } jsonMsg(c, "Client updated", nil) - if err == nil { + if err == nil && needRestart { a.xrayService.SetToNeedRestart() } } @@ -237,13 +242,15 @@ func (a *InboundController) resetClientTraffic(c *gin.Context) { } email := c.Param("email") - err = a.inboundService.ResetClientTraffic(id, email) + needRestart := false + + needRestart, err = a.inboundService.ResetClientTraffic(id, email) if err != nil { jsonMsg(c, "Something went wrong!", err) return } jsonMsg(c, "traffic reseted", nil) - if err == nil { + if err == nil && needRestart { a.xrayService.SetToNeedRestart() } } @@ -253,6 +260,8 @@ func (a *InboundController) resetAllTraffics(c *gin.Context) { if err != nil { jsonMsg(c, "Something went wrong!", err) return + } else { + a.xrayService.SetToNeedRestart() } jsonMsg(c, "All traffics reseted", nil) } @@ -268,6 +277,8 @@ func (a *InboundController) resetAllClientTraffics(c *gin.Context) { if err != nil { jsonMsg(c, "Something went wrong!", err) return + } else { + a.xrayService.SetToNeedRestart() } jsonMsg(c, "All traffics of client reseted", nil) } diff --git a/web/job/check_inbound_job.go b/web/job/check_inbound_job.go index 2b24afb0..cb8bd331 100644 --- a/web/job/check_inbound_job.go +++ b/web/job/check_inbound_job.go @@ -15,19 +15,21 @@ func NewCheckInboundJob() *CheckInboundJob { } func (j *CheckInboundJob) Run() { - count, err := j.inboundService.DisableInvalidClients() + needRestart, count, err := j.inboundService.DisableInvalidClients() if err != nil { - logger.Warning("disable invalid Client err:", err) + logger.Warning("Error in disabling invalid clients:", err) } else if count > 0 { - logger.Debugf("disabled %v Client", count) - j.xrayService.SetToNeedRestart() + logger.Debugf("%v clients disabled", count) + if needRestart { + j.xrayService.SetToNeedRestart() + } } count, err = j.inboundService.DisableInvalidInbounds() if err != nil { - logger.Warning("disable invalid inbounds err:", err) + logger.Warning("Error in disabling invalid inbounds:", err) } else if count > 0 { - logger.Debugf("disabled %v inbounds", count) + logger.Debugf("%v inbounds disabled", count) j.xrayService.SetToNeedRestart() } } diff --git a/web/service/inbound.go b/web/service/inbound.go index 11522ad2..84ce9bd8 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -15,6 +15,7 @@ import ( ) type InboundService struct { + xrayApi xray.XrayAPI } func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error) { @@ -156,11 +157,19 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, err } db := database.GetDB() + tx := db.Begin() + defer func() { + if err == nil { + tx.Commit() + } else { + tx.Rollback() + } + }() - err = db.Save(inbound).Error + err = tx.Save(inbound).Error if err == nil { for _, client := range clients { - s.AddClientStat(inbound.Id, &client) + s.AddClientStat(tx, inbound.Id, &client) } } return inbound, err @@ -244,6 +253,12 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, if err != nil { return inbound, err } + + err = s.updateClientTraffics(oldInbound, inbound) + if err != nil { + return inbound, err + } + oldInbound.Up = inbound.Up oldInbound.Down = inbound.Down oldInbound.Total = inbound.Total @@ -262,36 +277,92 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, return inbound, db.Save(oldInbound).Error } -func (s *InboundService) AddInboundClient(data *model.Inbound) error { - clients, err := s.GetClients(data) +func (s *InboundService) updateClientTraffics(oldInbound *model.Inbound, newInbound *model.Inbound) error { + oldClients, err := s.GetClients(oldInbound) if err != nil { return err } + newClients, err := s.GetClients(newInbound) + if err != nil { + return err + } + + db := database.GetDB() + tx := db.Begin() + + defer func() { + if err != nil { + tx.Rollback() + } else { + tx.Commit() + } + }() + + var emailExists bool + + for _, oldClient := range oldClients { + emailExists = false + for _, newClient := range newClients { + if oldClient.Email == newClient.Email { + emailExists = true + break + } + } + if !emailExists { + err = s.DelClientStat(tx, oldClient.Email) + if err != nil { + return err + } + } + } + for _, newClient := range newClients { + emailExists = false + for _, oldClient := range oldClients { + if newClient.Email == oldClient.Email { + emailExists = true + break + } + } + if !emailExists { + err = s.AddClientStat(tx, oldInbound.Id, &newClient) + if err != nil { + return err + } + } + } + return nil +} + +func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) { + clients, err := s.GetClients(data) + if err != nil { + return false, err + } var settings map[string]interface{} err = json.Unmarshal([]byte(data.Settings), &settings) if err != nil { - return err + return false, err } interfaceClients := settings["clients"].([]interface{}) existEmail, err := s.checkEmailsExistForClients(clients) if err != nil { - return err + return false, err } if existEmail != "" { - return common.NewError("Duplicate email:", existEmail) + return false, common.NewError("Duplicate email:", existEmail) } oldInbound, err := s.GetInbound(data.Id) if err != nil { - return err + return false, err } var oldSettings map[string]interface{} err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings) if err != nil { - return err + return false, err } oldClients := oldSettings["clients"].([]interface{}) @@ -301,30 +372,58 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) error { newSettings, err := json.MarshalIndent(oldSettings, "", " ") if err != nil { - return err + return false, err } oldInbound.Settings = string(newSettings) + db := database.GetDB() + tx := db.Begin() + + defer func() { + if err != nil { + tx.Rollback() + } else { + tx.Commit() + } + }() + + needRestart := false + s.xrayApi.Init(p.GetAPIPort()) for _, client := range clients { if len(client.Email) > 0 { - s.AddClientStat(data.Id, &client) + s.AddClientStat(tx, data.Id, &client) + err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{ + "email": client.Email, + "id": client.ID, + "alterId": client.AlterIds, + "flow": client.Flow, + "password": client.Password, + }) + if err1 == nil { + logger.Debug("Client added by api:", client.Email) + } else { + needRestart = true + } + } else { + needRestart = true } } - db := database.GetDB() - return db.Save(oldInbound).Error + s.xrayApi.Close() + + return needRestart, tx.Save(oldInbound).Error } -func (s *InboundService) DelInboundClient(inboundId int, clientId string) error { +func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool, error) { oldInbound, err := s.GetInbound(inboundId) if err != nil { logger.Error("Load Old Data Error") - return err + return false, err } var settings map[string]interface{} err = json.Unmarshal([]byte(oldInbound.Settings), &settings) if err != nil { - return err + return false, err } email := "" @@ -351,7 +450,7 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) error settings["clients"] = newClients newSettings, err := json.MarshalIndent(settings, "", " ") if err != nil { - return err + return false, err } oldInbound.Settings = string(newSettings) @@ -360,39 +459,49 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) error err = s.DelClientStat(db, email) if err != nil { logger.Error("Delete stats Data Error") - return err + return false, err } err = s.DelClientIPs(db, email) if err != nil { logger.Error("Error in delete client IPs") - return err + return false, err } - return db.Save(oldInbound).Error + needRestart := true + s.xrayApi.Init(p.GetAPIPort()) + if len(email) > 0 { + err = s.xrayApi.RemoveUser(oldInbound.Tag, email) + if err == nil { + logger.Debug("Client deleted by api:", email) + needRestart = false + } + } + s.xrayApi.Close() + return needRestart, db.Save(oldInbound).Error } -func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId string) error { +func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId string) (bool, error) { clients, err := s.GetClients(data) if err != nil { - return err + return false, err } var settings map[string]interface{} err = json.Unmarshal([]byte(data.Settings), &settings) if err != nil { - return err + return false, err } inerfaceClients := settings["clients"].([]interface{}) oldInbound, err := s.GetInbound(data.Id) if err != nil { - return err + return false, err } oldClients, err := s.GetClients(oldInbound) if err != nil { - return err + return false, err } oldEmail := "" @@ -416,17 +525,17 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin if len(clients[0].Email) > 0 && clients[0].Email != oldEmail { existEmail, err := s.checkEmailsExistForClients(clients) if err != nil { - return err + return false, err } if existEmail != "" { - return common.NewError("Duplicate email:", existEmail) + return false, common.NewError("Duplicate email:", existEmail) } } var oldSettings map[string]interface{} err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings) if err != nil { - return err + return false, err } settingsClients := oldSettings["clients"].([]interface{}) settingsClients[clientIndex] = inerfaceClients[0] @@ -434,36 +543,67 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin newSettings, err := json.MarshalIndent(oldSettings, "", " ") if err != nil { - return err + return false, err } oldInbound.Settings = string(newSettings) db := database.GetDB() + tx := db.Begin() + + defer func() { + if err != nil { + tx.Rollback() + } else { + tx.Commit() + } + }() if len(clients[0].Email) > 0 { if len(oldEmail) > 0 { err = s.UpdateClientStat(oldEmail, &clients[0]) if err != nil { - return err + return false, err } err = s.UpdateClientIPs(db, oldEmail, clients[0].Email) if err != nil { - return err + return false, err } } else { - s.AddClientStat(data.Id, &clients[0]) + s.AddClientStat(tx, data.Id, &clients[0]) } } else { - err = s.DelClientStat(db, oldEmail) + err = s.DelClientStat(tx, oldEmail) if err != nil { - return err + return false, err } err = s.DelClientIPs(db, oldEmail) if err != nil { - return err + return false, err } } - return db.Save(oldInbound).Error + needRestart := true + s.xrayApi.Init(p.GetAPIPort()) + if len(oldEmail) > 0 { + s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail) + if clients[0].Enable { + err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{ + "email": clients[0].Email, + "id": clients[0].ID, + "alterId": clients[0].AlterIds, + "flow": clients[0].Flow, + "password": clients[0].Password, + }) + if err1 == nil { + logger.Debug("Client edited by api:", clients[0].Email) + needRestart = false + } + } else { + logger.Debug("Client disabled by api:", clients[0].Email) + needRestart = false + } + } + s.xrayApi.Close() + return needRestart, tx.Save(oldInbound).Error } func (s *InboundService) AddTraffic(traffics []*xray.Traffic) error { @@ -489,6 +629,7 @@ func (s *InboundService) AddTraffic(traffics []*xray.Traffic) error { return err } + func (s *InboundService) AddClientTraffic(traffics []*xray.ClientTraffic) (err error) { if len(traffics) == 0 { return nil @@ -601,15 +742,42 @@ func (s *InboundService) DisableInvalidInbounds() (int64, error) { return count, err } -func (s *InboundService) DisableInvalidClients() (int64, error) { +func (s *InboundService) DisableInvalidClients() (bool, int64, error) { db := database.GetDB() now := time.Now().Unix() * 1000 + needRestart := false + + if p != nil { + var results []struct { + Tag string + Email string + } + + err := db.Table("inbounds"). + Select("inbounds.tag, client_traffics.email"). + Joins("JOIN client_traffics ON inbounds.id = client_traffics.inbound_id"). + Where("((client_traffics.total > 0 AND client_traffics.up + client_traffics.down >= client_traffics.total) OR (client_traffics.expiry_time > 0 AND client_traffics.expiry_time <= ?)) AND client_traffics.enable = ?", now, true). + Scan(&results).Error + if err != nil { + return false, 0, err + } + s.xrayApi.Init(p.GetAPIPort()) + for _, result := range results { + err = s.xrayApi.RemoveUser(result.Tag, result.Email) + if err == nil { + logger.Debug("Client deleted by api:", result.Email) + } else { + needRestart = true + } + } + s.xrayApi.Close() + } result := db.Model(xray.ClientTraffic{}). Where("((total > 0 and up + down >= total) or (expiry_time > 0 and expiry_time <= ?)) and enable = ?", now, true). Update("enable", false) err := result.Error count := result.RowsAffected - return count, err + return needRestart, count, err } func (s *InboundService) MigrationRemoveOrphanedTraffics() { @@ -624,9 +792,7 @@ func (s *InboundService) MigrationRemoveOrphanedTraffics() { `) } -func (s *InboundService) AddClientStat(inboundId int, client *model.Client) error { - db := database.GetDB() - +func (s *InboundService) AddClientStat(tx *gorm.DB, inboundId int, client *model.Client) error { clientTraffic := xray.ClientTraffic{} clientTraffic.InboundId = inboundId clientTraffic.Email = client.Email @@ -635,7 +801,7 @@ func (s *InboundService) AddClientStat(inboundId int, client *model.Client) erro clientTraffic.Enable = true clientTraffic.Up = 0 clientTraffic.Down = 0 - result := db.Create(&clientTraffic) + result := tx.Create(&clientTraffic) err := result.Error if err != nil { return err @@ -779,7 +945,11 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId string) err return err } inbound.Settings = string(modifiedSettings) - return s.UpdateInboundClient(inbound, clientId) + _, err = s.UpdateInboundClient(inbound, clientId) + if err != nil { + return err + } + return nil } func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, error) { @@ -835,7 +1005,13 @@ func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, er return false, err } inbound.Settings = string(modifiedSettings) - return !clientOldEnabled, s.UpdateInboundClient(inbound, clientId) + + _, err = s.UpdateInboundClient(inbound, clientId) + if err != nil { + return false, err + } + + return !clientOldEnabled, nil } func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int) error { @@ -889,9 +1065,13 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int return err } inbound.Settings = string(modifiedSettings) - return s.UpdateInboundClient(inbound, clientId) -} + _, err = s.UpdateInboundClient(inbound, clientId) + if err != nil { + return err + } + return nil +} func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry_time int64) error { _, inbound, err := s.GetClientInboundByEmail(clientEmail) if err != nil { @@ -943,7 +1123,12 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry return err } inbound.Settings = string(modifiedSettings) - return s.UpdateInboundClient(inbound, clientId) + _, err = s.UpdateInboundClient(inbound, clientId) + if err != nil { + return err + } + return nil + } func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error { @@ -961,19 +1146,55 @@ func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error { return nil } -func (s *InboundService) ResetClientTraffic(id int, clientEmail string) error { - db := database.GetDB() - - result := db.Model(xray.ClientTraffic{}). - Where("inbound_id = ? and email = ?", id, clientEmail). - Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0}) - - err := result.Error +func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, error) { + needRestart := false + traffic, err := s.GetClientTrafficByEmail(clientEmail) if err != nil { - return err + return false, err } - return nil + + if !traffic.Enable { + inbound, err := s.GetInbound(id) + if err != nil { + return false, err + } + clients, err := s.GetClients(inbound) + if err != nil { + return false, err + } + for _, client := range clients { + if client.Email == clientEmail { + s.xrayApi.Init(p.GetAPIPort()) + err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{ + "email": client.Email, + "id": client.ID, + "alterId": client.AlterIds, + "flow": client.Flow, + "password": client.Password, + }) + if err1 == nil { + logger.Debug("Client enabled due to reset traffic:", clientEmail) + } else { + needRestart = true + } + s.xrayApi.Close() + break + } + } + } + + traffic.Up = 0 + traffic.Down = 0 + traffic.Enable = true + + db := database.GetDB() + err = db.Save(traffic).Error + if err != nil { + return false, err + } + + return needRestart, nil } func (s *InboundService) ResetAllClientTraffics(id int) error { @@ -1212,10 +1433,19 @@ func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error) func (s *InboundService) MigrationRequirements() { db := database.GetDB() + tx := db.Begin() + var err error + defer func() { + if err == nil { + tx.Commit() + } else { + tx.Rollback() + } + }() // Fix inbounds based problems var inbounds []*model.Inbound - err := db.Model(model.Inbound{}).Where("protocol IN (?)", []string{"vmess", "vless", "trojan"}).Find(&inbounds).Error + err = tx.Model(model.Inbound{}).Where("protocol IN (?)", []string{"vmess", "vless", "trojan"}).Find(&inbounds).Error if err != nil && err != gorm.ErrRecordNotFound { return } @@ -1250,6 +1480,7 @@ func (s *InboundService) MigrationRequirements() { inbounds[inbound_index].Settings = string(modifiedSettings) } + // Add client traffic row for all clients which has email modelClients, err := s.GetClients(inbounds[inbound_index]) if err != nil { @@ -1258,17 +1489,17 @@ func (s *InboundService) MigrationRequirements() { for _, modelClient := range modelClients { if len(modelClient.Email) > 0 { var count int64 - db.Model(xray.ClientTraffic{}).Where("email = ?", modelClient.Email).Count(&count) + tx.Model(xray.ClientTraffic{}).Where("email = ?", modelClient.Email).Count(&count) if count == 0 { - s.AddClientStat(inbounds[inbound_index].Id, &modelClient) + s.AddClientStat(tx, inbounds[inbound_index].Id, &modelClient) } } } } - db.Save(inbounds) + tx.Save(inbounds) // Remove orphaned traffics - db.Where("inbound_id = 0").Delete(xray.ClientTraffic{}) + tx.Where("inbound_id = 0").Delete(xray.ClientTraffic{}) } func (s *InboundService) MigrateDB() { diff --git a/web/service/xray.go b/web/service/xray.go index bcc886fe..5475891f 100644 --- a/web/service/xray.go +++ b/web/service/xray.go @@ -18,6 +18,7 @@ var result string type XrayService struct { inboundService InboundService settingService SettingService + xrayAPI xray.XrayAPI } func (s *XrayService) IsXrayRunning() bool { @@ -143,7 +144,9 @@ func (s *XrayService) GetXrayTraffic() ([]*xray.Traffic, []*xray.ClientTraffic, if !s.IsXrayRunning() { return nil, nil, errors.New("xray is not running") } - return p.GetTraffic(true) + s.xrayAPI.Init(p.GetAPIPort()) + defer s.xrayAPI.Close() + return s.xrayAPI.GetTraffic(true) } func (s *XrayService) RestartXray(isForce bool) error { @@ -158,7 +161,7 @@ func (s *XrayService) RestartXray(isForce bool) error { if p != nil && p.IsRunning() { if !isForce && p.GetConfig().Equals(xrayConfig) { - logger.Debug("not need to restart xray") + logger.Debug("It does not need to restart xray") return nil } p.Stop() @@ -166,7 +169,11 @@ func (s *XrayService) RestartXray(isForce bool) error { p = xray.NewProcess(xrayConfig) result = "" - return p.Start() + err = p.Start() + if err != nil { + return err + } + return nil } func (s *XrayService) StopXray() error { diff --git a/xray/api.go b/xray/api.go new file mode 100644 index 00000000..a84435f9 --- /dev/null +++ b/xray/api.go @@ -0,0 +1,182 @@ +package xray + +import ( + "context" + "fmt" + "regexp" + "time" + "x-ui/util/common" + + "github.com/xtls/xray-core/app/proxyman/command" + statsService "github.com/xtls/xray-core/app/stats/command" + "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/serial" + "github.com/xtls/xray-core/proxy/shadowsocks" + "github.com/xtls/xray-core/proxy/trojan" + "github.com/xtls/xray-core/proxy/vless" + "github.com/xtls/xray-core/proxy/vmess" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +type XrayAPI struct { + HandlerServiceClient *command.HandlerServiceClient + StatsServiceClient *statsService.StatsServiceClient + grpcClient *grpc.ClientConn + isConnected bool +} + +func (x *XrayAPI) Init(apiPort int) (err error) { + if apiPort == 0 { + return common.NewError("xray api port wrong:", apiPort) + } + x.grpcClient, err = grpc.Dial(fmt.Sprintf("127.0.0.1:%v", apiPort), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return err + } + x.isConnected = true + + hsClient := command.NewHandlerServiceClient(x.grpcClient) + ssClient := statsService.NewStatsServiceClient(x.grpcClient) + + x.HandlerServiceClient = &hsClient + x.StatsServiceClient = &ssClient + + return +} + +func (x *XrayAPI) Close() { + x.grpcClient.Close() + x.HandlerServiceClient = nil + x.StatsServiceClient = nil + x.isConnected = false +} + +func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]interface{}) error { + var account *serial.TypedMessage + switch Protocol { + case "vmess": + account = serial.ToTypedMessage(&vmess.Account{ + Id: user["id"].(string), + AlterId: uint32(user["alterId"].(uint16)), + }) + case "vless": + account = serial.ToTypedMessage(&vless.Account{ + Id: user["id"].(string), + Flow: user["flow"].(string), + }) + case "trojan": + account = serial.ToTypedMessage(&trojan.Account{ + Password: user["password"].(string), + }) + case "shadowsocks": + account = serial.ToTypedMessage(&shadowsocks.Account{ + Password: user["password"].(string), + }) + default: + return nil + } + + client := *x.HandlerServiceClient + + _, err := client.AlterInbound(context.Background(), &command.AlterInboundRequest{ + Tag: inboundTag, + Operation: serial.ToTypedMessage(&command.AddUserOperation{ + User: &protocol.User{ + Email: user["email"].(string), + Account: account, + }, + }), + }) + return err +} + +func (x *XrayAPI) RemoveUser(inboundTag string, email string) error { + client := *x.HandlerServiceClient + _, err := client.AlterInbound(context.Background(), &command.AlterInboundRequest{ + Tag: inboundTag, + Operation: serial.ToTypedMessage(&command.RemoveUserOperation{ + Email: email, + }), + }) + return err +} + +func (x *XrayAPI) GetTraffic(reset bool) ([]*Traffic, []*ClientTraffic, error) { + if x.grpcClient == nil { + return nil, nil, common.NewError("xray api is not initialized") + } + var trafficRegex = regexp.MustCompile("(inbound|outbound)>>>([^>]+)>>>traffic>>>(downlink|uplink)") + var ClientTrafficRegex = regexp.MustCompile("(user)>>>([^>]+)>>>traffic>>>(downlink|uplink)") + + client := *x.StatsServiceClient + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + request := &statsService.QueryStatsRequest{ + Reset_: reset, + } + resp, err := client.QueryStats(ctx, request) + if err != nil { + return nil, nil, err + } + tagTrafficMap := map[string]*Traffic{} + emailTrafficMap := map[string]*ClientTraffic{} + + clientTraffics := make([]*ClientTraffic, 0) + traffics := make([]*Traffic, 0) + for _, stat := range resp.GetStat() { + matchs := trafficRegex.FindStringSubmatch(stat.Name) + if len(matchs) < 3 { + + matchs := ClientTrafficRegex.FindStringSubmatch(stat.Name) + if len(matchs) < 3 { + continue + } else { + + isUser := matchs[1] == "user" + email := matchs[2] + isDown := matchs[3] == "downlink" + if !isUser { + continue + } + traffic, ok := emailTrafficMap[email] + if !ok { + traffic = &ClientTraffic{ + Email: email, + } + emailTrafficMap[email] = traffic + clientTraffics = append(clientTraffics, traffic) + } + if isDown { + traffic.Down = stat.Value + } else { + traffic.Up = stat.Value + } + + } + continue + } + isInbound := matchs[1] == "inbound" + tag := matchs[2] + isDown := matchs[3] == "downlink" + if tag == "api" { + continue + } + traffic, ok := tagTrafficMap[tag] + if !ok { + traffic = &Traffic{ + IsInbound: isInbound, + Tag: tag, + } + tagTrafficMap[tag] = traffic + traffics = append(traffics, traffic) + } + if isDown { + traffic.Down = stat.Value + } else { + traffic.Up = stat.Value + } + } + + return traffics, clientTraffics, nil +} diff --git a/xray/process.go b/xray/process.go index d0117324..60c53320 100644 --- a/xray/process.go +++ b/xray/process.go @@ -3,30 +3,21 @@ package xray import ( "bufio" "bytes" - "context" "encoding/json" "errors" "fmt" "io/fs" "os" "os/exec" - "regexp" "runtime" "strings" "sync" - "time" "x-ui/config" "x-ui/util/common" "github.com/Workiva/go-datastructures/queue" - statsservice "github.com/xtls/xray-core/app/stats/command" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" ) -var trafficRegex = regexp.MustCompile("(inbound|outbound)>>>([^>]+)>>>traffic>>>(downlink|uplink)") -var ClientTrafficRegex = regexp.MustCompile("(user)>>>([^>]+)>>>traffic>>>(downlink|uplink)") - func GetBinaryName() string { return fmt.Sprintf("xray-%s-%s", runtime.GOOS, runtime.GOARCH) } @@ -238,85 +229,3 @@ func (p *process) Stop() error { } return p.cmd.Process.Kill() } - -func (p *process) GetTraffic(reset bool) ([]*Traffic, []*ClientTraffic, error) { - if p.apiPort == 0 { - return nil, nil, common.NewError("xray api port wrong:", p.apiPort) - } - conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1:%v", p.apiPort), grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - return nil, nil, err - } - defer conn.Close() - - client := statsservice.NewStatsServiceClient(conn) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) - defer cancel() - request := &statsservice.QueryStatsRequest{ - Reset_: reset, - } - resp, err := client.QueryStats(ctx, request) - if err != nil { - return nil, nil, err - } - tagTrafficMap := map[string]*Traffic{} - emailTrafficMap := map[string]*ClientTraffic{} - - clientTraffics := make([]*ClientTraffic, 0) - traffics := make([]*Traffic, 0) - for _, stat := range resp.GetStat() { - matchs := trafficRegex.FindStringSubmatch(stat.Name) - if len(matchs) < 3 { - - matchs := ClientTrafficRegex.FindStringSubmatch(stat.Name) - if len(matchs) < 3 { - continue - } else { - - isUser := matchs[1] == "user" - email := matchs[2] - isDown := matchs[3] == "downlink" - if !isUser { - continue - } - traffic, ok := emailTrafficMap[email] - if !ok { - traffic = &ClientTraffic{ - Email: email, - } - emailTrafficMap[email] = traffic - clientTraffics = append(clientTraffics, traffic) - } - if isDown { - traffic.Down = stat.Value - } else { - traffic.Up = stat.Value - } - - } - continue - } - isInbound := matchs[1] == "inbound" - tag := matchs[2] - isDown := matchs[3] == "downlink" - if tag == "api" { - continue - } - traffic, ok := tagTrafficMap[tag] - if !ok { - traffic = &Traffic{ - IsInbound: isInbound, - Tag: tag, - } - tagTrafficMap[tag] = traffic - traffics = append(traffics, traffic) - } - if isDown { - traffic.Down = stat.Value - } else { - traffic.Up = stat.Value - } - } - - return traffics, clientTraffics, nil -}