MHSanaei 2 anni fa
parent
commit
fe9844b51b

+ 1 - 1
.github/workflows/release.yml

@@ -39,4 +39,4 @@ jobs:
           tag: ${{ github.ref }}
           file: x-ui-linux-amd64.tar.gz
           asset_name: x-ui-linux-amd64.tar.gz
-          prerelease: true
+          prerelease: true

+ 1 - 1
config/version

@@ -1 +1 @@
-1.0.4
+1.0.5

+ 5 - 11
database/db.go

@@ -1,15 +1,16 @@
 package database
 
 import (
-	"gorm.io/driver/sqlite"
-	"gorm.io/gorm"
-	"gorm.io/gorm/logger"
 	"io/fs"
 	"os"
 	"path"
 	"x-ui/config"
-	"x-ui/xray"
 	"x-ui/database/model"
+	"x-ui/xray"
+
+	"gorm.io/driver/sqlite"
+	"gorm.io/gorm"
+	"gorm.io/gorm/logger"
 )
 
 var db *gorm.DB
@@ -41,9 +42,6 @@ func initInbound() error {
 func initSetting() error {
 	return db.AutoMigrate(&model.Setting{})
 }
-func initInboundClientIps() error {
-	return db.AutoMigrate(&model.InboundClientIps{})
-}
 func initClientTraffic() error {
 	return db.AutoMigrate(&xray.ClientTraffic{})
 }
@@ -83,10 +81,6 @@ func InitDB(dbPath string) error {
 	if err != nil {
 		return err
 	}
-	err = initInboundClientIps()
-	if err != nil {
-		return err
-	}
 	err = initClientTraffic()
 	if err != nil {
 		return err

+ 0 - 6
database/model/model.go

@@ -43,11 +43,6 @@ type Inbound struct {
 	Tag            string   `json:"tag" form:"tag" gorm:"unique"`
 	Sniffing       string   `json:"sniffing" form:"sniffing"`
 }
-type InboundClientIps struct {
-	Id       int    `json:"id" gorm:"primaryKey;autoIncrement"`
-	ClientEmail string `json:"clientEmail" form:"clientEmail" gorm:"unique"`
-	Ips string `json:"ips" form:"ips"`
-}
 
 func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
 	listen := i.Listen
@@ -74,7 +69,6 @@ type Client struct {
 	ID       string `json:"id"`
 	AlterIds uint16 `json:"alterId"`
 	Email string `json:"email"`
-	LimitIP int `json:"limitIp"`
 	Security string `json:"security"`
 	TotalGB      int64  `json:"totalGB" form:"totalGB"`
 	ExpiryTime int64  `json:"expiryTime" form:"expiryTime"`

+ 7 - 3
go.mod

@@ -11,13 +11,14 @@ require (
 	github.com/nicksnyder/go-i18n/v2 v2.2.1
 	github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
 	github.com/robfig/cron/v3 v3.0.1
-	github.com/shirou/gopsutil v3.21.11+incompatible
+	github.com/shirou/gopsutil/v3 v3.23.1
 	github.com/xtls/xray-core v1.7.5
 	go.uber.org/atomic v1.10.0
 	golang.org/x/text v0.7.0
 	google.golang.org/grpc v1.53.0
 	gorm.io/driver/sqlite v1.3.6
 	gorm.io/gorm v1.23.8
+
 )
 
 require (
@@ -35,14 +36,16 @@ require (
 	github.com/jinzhu/now v1.1.5 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/mattn/go-sqlite3 v1.14.16 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.1 // indirect
 	github.com/pires/go-proxyproto v0.6.2 // indirect
-	github.com/tklauser/go-sysconf v0.3.5 // indirect
-	github.com/tklauser/numcpus v0.2.2 // indirect
+	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
+	github.com/tklauser/go-sysconf v0.3.11 // indirect
+	github.com/tklauser/numcpus v0.6.0 // indirect
 	github.com/ugorji/go/codec v1.2.7 // indirect
 	github.com/yusufpapurcu/wmi v1.2.2 // indirect
 	golang.org/x/crypto v0.5.0 // indirect
@@ -51,4 +54,5 @@ require (
 	google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
+
 )

+ 20 - 7
go.sum

@@ -49,7 +49,9 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
 github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
 github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
@@ -84,6 +86,8 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
 github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
 github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
 github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
@@ -112,6 +116,8 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
 github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
 github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U=
 github.com/quic-go/qtls-go1-19 v0.2.0 h1:Cvn2WdhyViFUHoOqK52i51k4nDX8EwIh5VJiVM4nttk=
@@ -128,20 +134,24 @@ github.com/sagernet/sing v0.1.6 h1:Qy63OUfKpcqKjfd5rPmUlj0RGjHZSK/PJn0duyCCsRg=
 github.com/sagernet/sing-shadowsocks v0.1.1-0.20230202035033-e3123545f2f7 h1:Plup6oEiyLzY3HDqQ+QsUBzgBGdVmcsgf3t8h940z9U=
 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo=
 github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
-github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
-github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4=
+github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+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=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg=
-github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
-github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
-github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
-github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
+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/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
 github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
 github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
@@ -187,12 +197,14 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -236,6 +248,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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.3.6 h1:Fi8xNYCUplOqWiPa3/GuCeowRNBRGTf62DEmhMDHeQQ=
 gorm.io/driver/sqlite v1.3.6/go.mod h1:Sg1/pvnKtbQ7jLXxfZa+jSHvoX8hoZA8cn4xllOMTgE=
 gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=

+ 0 - 1
install.sh

@@ -163,7 +163,6 @@ install_x-ui() {
     echo -e "x-ui enable       - Enable    x-ui on system startup"
     echo -e "x-ui disable      - Disable   x-ui on system startup"
     echo -e "x-ui log          - Check     x-ui logs"
-    echo -e "x-ui v2-ui        - Migrate   v2-ui Account data to x-ui"
     echo -e "x-ui update       - Update    x-ui"
     echo -e "x-ui install      - Install   x-ui"
     echo -e "x-ui uninstall    - Uninstall x-ui"

+ 1 - 1
util/sys/psutil.go

@@ -4,5 +4,5 @@ import (
 	_ "unsafe"
 )
 
-//go:linkname HostProc github.com/shirou/gopsutil/internal/common.HostProc
+//go:linkname HostProc github.com/shirou/gopsutil/v3/internal/common.HostProc
 func HostProc(combineWith ...string) string

+ 1 - 1
util/sys/sys_darwin.go

@@ -3,7 +3,7 @@
 package sys
 
 import (
-	"github.com/shirou/gopsutil/net"
+	"github.com/shirou/gopsutil/v3/net"
 )
 
 func GetTCPCount() (int, error) {

+ 1 - 1
web/assets/js/model/xray.js

@@ -694,7 +694,7 @@ class Inbound extends XrayCommonClass {
         this._protocol = protocol;
         this.settings = Inbound.Settings.getSettings(protocol);
         if (protocol === Protocols.TROJAN) {
-            this.tls = true;
+            this.tls = false;
         }
     }
     get tls() {

+ 2 - 2
web/html/xui/form/protocol/trojan.html

@@ -9,10 +9,10 @@
     <a-form layout="inline">
         <a-form-item>
             <span slot="label">
-                Email
+                Username
                 <a-tooltip>
                     <template slot="title">
-                        The Email Must Be Completely Unique
+                        The Username Must Be Completely Unique
                     </template>
                     <!--Renew Svg Icon-->
                     <svg 

+ 2 - 2
web/html/xui/form/protocol/vless.html

@@ -9,10 +9,10 @@
         <a-form layout="inline">
             <a-form-item>
                 <span slot="label">
-                    Email
+                    Username
                     <a-tooltip>
                         <template slot="title">
-                            The Email Must Be Completely Unique
+                            The Username Must Be Completely Unique
                         </template>
                         <!--Renew Svg Icon-->
                         <svg 

+ 2 - 2
web/html/xui/form/protocol/vmess.html

@@ -8,10 +8,10 @@
         <a-form layout="inline">
             <a-form-item>
                 <span slot="label">
-                    Email
+                    Username
                     <a-tooltip>
                         <template slot="title">
-                            The Email Must Be Completely Unique
+                            The Username Must Be Completely Unique
                         </template>
                         <!--Renew Svg Icon-->
                         <svg 

+ 70 - 13
web/html/xui/inbound_info_modal.html

@@ -1,9 +1,11 @@
 {{define "inboundInfoModal"}}
-<a-modal id="inbound-info-modal" v-model="infoModal.visible" title='{{ i18n "pages.inbounds.details"}}'
-         :closable="true"
-         :mask-closable="true"
-         :footer="null"
-         >
+<a-modal id="inbound-info-modal"
+    v-model="infoModal.visible" title='{{ i18n "pages.inbounds.details"}}'
+    :closable="true"
+    :mask-closable="true"
+    :footer="null"
+    width="600px"
+    >
     <table style="margin-bottom: 10px; width: 100%;">
         <tr><td>
             <table>
@@ -55,11 +57,47 @@
         </tr>
     </table>
     <a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
-    <template v-if="dbInbound.hasLink()">
-        <p>Client URL:</p>
+    <table style="margin-bottom: 10px; width: 100%;">
+        <tr><th>[[ Object.keys(infoModal.clientSettings)[0] ]]</th><th>[[ Object.keys(infoModal.clientSettings)[1] ]]</th><th>[[ Object.keys(infoModal.clientSettings)[2] ]]</th></tr>
+        <tr>
+            <td><a-tag color="green">[[ Object.values(infoModal.clientSettings)[0] ]]</a-tag></td>
+            <td><a-tag color="green">[[ Object.values(infoModal.clientSettings)[1] ]]</a-tag></td>
+            <td><a-tag color="green">[[ Object.values(infoModal.clientSettings)[2] ]]</a-tag></td>
+        </tr>
+    </table>
+    <table style="margin-bottom: 10px; width: 100%;">
+            <tr><th>{{ i18n "usage" }}</th><th>{{ i18n "pages.inbounds.totalFlow" }}</th><th>{{ i18n "pages.inbounds.expireDate" }}</th><th>{{ i18n "enable" }}</th></tr>    
+        <tr>
+            <td>
+                <a-tag :color="statsColor(infoModal.clientStats)">
+                    [[ sizeFormat(infoModal.clientStats['up']) ]] / 
+                    [[ sizeFormat(infoModal.clientStats['down']) ]]
+                    ([[ sizeFormat(infoModal.clientStats['up'] + infoModal.clientStats['down']) ]])
+                </a-tag>
+            </td>
+            <td>
+                <a-tag v-if="infoModal.clientSettings.totalGB > 0" :color="statsColor(infoModal.clientStats)">[[ sizeFormat(infoModal.clientSettings.totalGB) ]]</a-tag>
+                <a-tag v-else color="green">{{ i18n "indefinite" }}</a-tag>
+            </td>
+            <td>
+                <template v-if="infoModal.clientSettings.expiryTime > 0">
+                    <a-tag :color="infoModal.isExpired ? 'red' : 'blue'">
+                        [[ DateUtil.formatMillis(infoModal.clientSettings.expiryTime) ]]
+                    </a-tag>
+                </template>
+                <a-tag v-else color="green">{{ i18n "indefinite" }}</a-tag>
+            </td>
+            <td>
+                <a-tag v-if="infoModal.clientStats.enable" color="blue">{{ i18n "enabled" }}</a-tag>
+                <a-tag v-else color="red">{{ i18n "disabled" }}</a-tag>
+            </td>
+        </tr>
+    </table>
+    <div v-if="dbInbound.hasLink()">
+        <a-divider>URL</a-divider>
         <p>[[ infoModal.link ]]</p>
-        <button class="btn" id="copy-url-link"><a-icon type="snippets"></a-icon>{{ i18n "copy" }}</button>
-    </template>
+        <button class="ant-btn ant-btn-primary" id="copy-url-link"><a-icon type="snippets"></a-icon>{{ i18n "copy" }}</button>
+    </div>
 </a-modal>
 <script>
 
@@ -67,14 +105,32 @@
         visible: false,
         inbound: new Inbound(),
         dbInbound: new DBInbound(),
+        clientSettings: new Inbound.Settings(),
+        clientStats: [],
+        upStats: 0,
+        downStats: 0,
         clipboard: null,
         link: null,
         index: 0,
+        isExpired: false,
         show(dbInbound, index=0) {
             this.index = index;
             this.inbound = dbInbound.toInbound();
             this.dbInbound = new DBInbound(dbInbound);
             this.link = dbInbound.genLink(index);
+            this.clientSettings = Object.values(JSON.parse(this.inbound.settings).clients)[index];
+            this.clientStats = dbInbound.clientStats;
+            this.isExpired = this.inbound.isExpiry(index);
+            if(dbInbound.clientStats.length > 0)
+            {
+                for (const key in dbInbound.clientStats) {
+                    if (Object.hasOwnProperty.call(dbInbound.clientStats, key)) {
+                        if(dbInbound.clientStats[key]['email'] == this.clientSettings.email)
+                            this.clientStats = dbInbound.clientStats[key];
+
+                    }
+                }
+            }
             this.visible = true;
             infoModalApp.$nextTick(() => {
                 if (this.clipboard === null) {
@@ -89,7 +145,6 @@
             infoModal.visible = false;
         },
     };
-
     const infoModalApp = new Vue({
         delimiters: ['[[', ']]'],
         el: '#inbound-info-modal',
@@ -105,7 +160,6 @@
         methods: {
             setQrCode(elmentId,index) {
                 content = infoModal.inbound.genLink(infoModal.dbInbound.address,infoModal.dbInbound.remark,index)
-
                 new QRious({
                         element: document.querySelector('#'+elmentId),
                         size: 260,
@@ -120,10 +174,13 @@
                     app.$message.success('{{ i18n "copySuccess" }}')
                     this.infoModal.clipboard.destroy();
                 });
+            },
+            statsColor(stats) {
+                if(stats['total'] === 0) return 'blue'
+                else if(stats['total'] > 0 && (stats['down']+stats['up']) < stats['total']) return 'cyan'
+                else return 'red'
             }
         },
-        
     });
-
 </script>
 {{end}}

+ 2 - 2
web/html/xui/inbound_modal.html

@@ -165,11 +165,11 @@
             getNewEmail(client) {
                 var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
                 var string = '';
-                var len = 6 + Math.floor(Math.random() * 5)
+                var len = 7 + Math.floor(Math.random() * 5)
                 for(var ii=0; ii<len; ii++){
                     string += chars[Math.floor(Math.random() * chars.length)];
                 }
-                client.email = string + "@gmail.com"
+                client.email = string
             }
         },
     });

+ 19 - 12
web/html/xui/inbounds.html

@@ -159,11 +159,6 @@
         align: 'center',
         width: 40,
         scopedSlots: { customRender: 'enable' },
-    }, {
-        title: "Id",
-        align: 'center',
-        dataIndex: "id",
-        width: 30,
     }, {
         title: '{{ i18n "pages.inbounds.remark" }}',
         align: 'center',
@@ -197,18 +192,19 @@
     }];
 
     const innerColumns = [
-        { title: '', width: 50, scopedSlots: { customRender: 'actions' } },
+        { title: '', width: 20, scopedSlots: { customRender: 'actions' } },
         { title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
-        { title: '{{ i18n "pages.inbounds.traffic" }}', width: 100, scopedSlots: { customRender: 'traffic' } },
-        { title: '{{ i18n "pages.inbounds.expireDate" }}', width: 80, scopedSlots: { customRender: 'expiryTime' } },
+        { title: '{{ i18n "pages.inbounds.traffic" }}↑|↓', width: 80, scopedSlots: { customRender: 'traffic' } },
+        { title: '{{ i18n "pages.inbounds.expireDate" }}', width: 70, scopedSlots: { customRender: 'expiryTime' } },
         { title: 'UID', width: 150, dataIndex: "id" },
+		
     ];
 
     const innerTrojanColumns = [
-        { title: '', width: 50, scopedSlots: { customRender: 'actions' } },
+        { title: '', width: 20, scopedSlots: { customRender: 'actions' } },
         { title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
-        { title: '{{ i18n "pages.inbounds.traffic" }}', width: 100, scopedSlots: { customRender: 'traffic' } },
-        { title: '{{ i18n "pages.inbounds.expireDate" }}', width: 80, scopedSlots: { customRender: 'expiryTime' } },
+        { title: '{{ i18n "pages.inbounds.traffic" }}↑|↓', width: 80, scopedSlots: { customRender: 'traffic' } },
+        { title: '{{ i18n "pages.inbounds.expireDate" }}', width: 70, scopedSlots: { customRender: 'expiryTime' } },
         { title: 'Password', width: 150, dataIndex: "password" },
     ];
 
@@ -258,7 +254,18 @@
                     this.searchedInbounds.splice(0, this.searchedInbounds.length);
                     this.dbInbounds.forEach(inbound => {
                         if (ObjectUtil.deepSearch(inbound, key)) {
-                            this.searchedInbounds.push(inbound);
+                            const newInbound = new DBInbound(inbound);
+                            const inboundSettings = JSON.parse(inbound.settings);
+                            if (inboundSettings.hasOwnProperty('clients')){
+                                const searchedSettings = { "clients": [] };
+                                inboundSettings.clients.forEach(client => {
+                                    if (ObjectUtil.deepSearch(client, key)){
+                                        searchedSettings.clients.push(client);
+                                    }
+                                });
+                                newInbound.settings = Inbound.Settings.fromJson(inbound.protocol, searchedSettings);
+                            }
+                            this.searchedInbounds.push(newInbound);
                         }
                     });
                 }

+ 8 - 8
web/html/xui/inbounds_client_row.html

@@ -16,16 +16,16 @@
     <a-tag v-if="!isClientEnabled(record, client.email)" color="red">{{ i18n "disabled" }}</a-tag>
 </template>                                  
 <template slot="traffic" slot-scope="text, client">
-    <a-tag v-if="client._totalGB === 0" color="blue">{{ i18n "used" }}: [[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]]</a-tag>
-    <a-tag v-if="client._totalGB > 0 && !isTrafficExhausted(record, client.email)" color="green">{{ i18n "used" }}: [[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] / [[client._totalGB]]GB</a-tag>
-    <a-tag v-if="client._totalGB > 0 && isTrafficExhausted(record, client.email)" color="red">{{ i18n "used" }}: [[ sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] / [[client._totalGB]]GB</a-tag>
-</template>
+  <a-tag color="blue">[[ sizeFormat(getUpStats(record, client.email)) ]] / [[ sizeFormat(getDownStats(record, client.email)) ]]</a-tag>
+    <template v-if="client._totalGB > 0">
+        <a-tag v-if="isTrafficExhausted(record, client.email)" color="red">[[client._totalGB]]GB</a-tag>
+        <a-tag v-else color="cyan">[[client._totalGB]]GB</a-tag>
+    </template>
+    <a-tag v-else color="green">{{ i18n "indefinite" }}</a-tag>
+</template>                                    
 <template slot="expiryTime" slot-scope="text, client, index">
     <template v-if="client._expiryTime > 0">
-        <a-tag v-if="isExpiry(record, index)" color="red">
-            [[ DateUtil.formatMillis(client._expiryTime) ]]
-        </a-tag>
-        <a-tag v-else color="blue">
+        <a-tag :color="isExpiry(record, index)? 'red' : 'blue'">
             [[ DateUtil.formatMillis(client._expiryTime) ]]
         </a-tag>
     </template>

+ 8 - 8
web/service/server.go

@@ -15,12 +15,12 @@ import (
 	"x-ui/util/sys"
 	"x-ui/xray"
 
-	"github.com/shirou/gopsutil/cpu"
-	"github.com/shirou/gopsutil/disk"
-	"github.com/shirou/gopsutil/host"
-	"github.com/shirou/gopsutil/load"
-	"github.com/shirou/gopsutil/mem"
-	"github.com/shirou/gopsutil/net"
+	"github.com/shirou/gopsutil/v3/cpu"
+	"github.com/shirou/gopsutil/v3/disk"
+	"github.com/shirou/gopsutil/v3/host"
+	"github.com/shirou/gopsutil/v3/load"
+	"github.com/shirou/gopsutil/v3/mem"
+	"github.com/shirou/gopsutil/v3/net"
 )
 
 type ProcessState string
@@ -143,7 +143,7 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
 	} else {
 		logger.Warning("can not find io counters")
 	}
-
+	
 	status.TcpCount, err = sys.GetTCPCount()
 	if err != nil {
 		logger.Warning("get tcp connections failed:", err)
@@ -153,7 +153,7 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
 	if err != nil {
 		logger.Warning("get udp connections failed:", err)
 	}
-
+	
 	if s.xrayService.IsXrayRunning() {
 		status.Xray.State = Running
 		status.Xray.ErrorMsg = ""