mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-03-19 17:15:49 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7417c52c8f | ||
|
|
4d4eef8d8a | ||
|
|
9de8b4acaf | ||
|
|
f21c293693 | ||
|
|
56159d9c52 | ||
|
|
6cbdf64013 | ||
|
|
bb9b9100a8 | ||
|
|
816adfc3ea | ||
|
|
0a95b0c7b2 | ||
|
|
d298f4ffbd | ||
|
|
315e8af025 | ||
|
|
de985263f5 | ||
|
|
dfe0bbd371 | ||
|
|
60cb328698 | ||
|
|
3d7f13225a | ||
|
|
76fdfb2ef2 | ||
|
|
8018023187 | ||
|
|
ea9d5dc2d5 | ||
|
|
96e43fa195 | ||
|
|
f1500a5d31 | ||
|
|
c9a218d060 | ||
|
|
6d18a15c4e | ||
|
|
c0f86d2f38 | ||
|
|
5227fefaeb | ||
|
|
7a51d2f2cc | ||
|
|
02ae61fe6b | ||
|
|
24b9e5bfa3 | ||
|
|
9ff7f14b6e | ||
|
|
c3b42b8ea4 | ||
|
|
5afb8d85fc | ||
|
|
767ee4ec2b | ||
|
|
21b64beb96 | ||
|
|
b84e3ef338 |
12
.github/workflows/release.yml
vendored
12
.github/workflows/release.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.22'
|
go-version-file: go.mod
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -83,7 +83,7 @@ jobs:
|
|||||||
cd x-ui/bin
|
cd x-ui/bin
|
||||||
|
|
||||||
# Download dependencies
|
# Download dependencies
|
||||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.16/"
|
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.19/"
|
||||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||||
wget ${Xray_URL}Xray-linux-64.zip
|
wget ${Xray_URL}Xray-linux-64.zip
|
||||||
unzip Xray-linux-64.zip
|
unzip Xray-linux-64.zip
|
||||||
@@ -125,7 +125,13 @@ jobs:
|
|||||||
|
|
||||||
- name: Package
|
- name: Package
|
||||||
run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
|
run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
|
||||||
|
|
||||||
|
- name: Upload files to Artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: x-ui-linux-${{ matrix.platform }}
|
||||||
|
path: ./x-ui-linux-${{ matrix.platform }}.tar.gz
|
||||||
|
|
||||||
- name: Upload files to GH release
|
- name: Upload files to GH release
|
||||||
uses: svenstaro/upload-release-action@v2
|
uses: svenstaro/upload-release-action@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ case $1 in
|
|||||||
esac
|
esac
|
||||||
mkdir -p build/bin
|
mkdir -p build/bin
|
||||||
cd build/bin
|
cd build/bin
|
||||||
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.16/Xray-linux-${ARCH}.zip"
|
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.19/Xray-linux-${ARCH}.zip"
|
||||||
unzip "Xray-linux-${ARCH}.zip"
|
unzip "Xray-linux-${ARCH}.zip"
|
||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||||
mv xray "xray-linux-${FNAME}"
|
mv xray "xray-linux-${FNAME}"
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
|||||||
|
|
||||||
## Install Custom Version
|
## Install Custom Version
|
||||||
|
|
||||||
To install your desired version, add the version to the end of the installation command. e.g., ver `v2.3.7`:
|
To install your desired version, add the version to the end of the installation command. e.g., ver `v2.3.9`:
|
||||||
|
|
||||||
```
|
```
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.3.7
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.3.9
|
||||||
```
|
```
|
||||||
|
|
||||||
## SSL Certificate
|
## SSL Certificate
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.3.7
|
2.3.9
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
@@ -18,54 +19,51 @@ import (
|
|||||||
|
|
||||||
var db *gorm.DB
|
var db *gorm.DB
|
||||||
|
|
||||||
var initializers = []func() error{
|
const (
|
||||||
initUser,
|
defaultUsername = "admin"
|
||||||
initInbound,
|
defaultPassword = "admin"
|
||||||
initOutbound,
|
defaultSecret = ""
|
||||||
initSetting,
|
)
|
||||||
initInboundClientIps,
|
|
||||||
initClientTraffic,
|
func initModels() error {
|
||||||
|
models := []interface{}{
|
||||||
|
&model.User{},
|
||||||
|
&model.Inbound{},
|
||||||
|
&model.OutboundTraffics{},
|
||||||
|
&model.Setting{},
|
||||||
|
&model.InboundClientIps{},
|
||||||
|
&xray.ClientTraffic{},
|
||||||
|
}
|
||||||
|
for _, model := range models {
|
||||||
|
if err := db.AutoMigrate(model); err != nil {
|
||||||
|
log.Printf("Error auto migrating model: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initUser() error {
|
func initUser() error {
|
||||||
err := db.AutoMigrate(&model.User{})
|
empty, err := isTableEmpty("users")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Error checking if users table is empty: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var count int64
|
if empty {
|
||||||
err = db.Model(&model.User{}).Count(&count).Error
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if count == 0 {
|
|
||||||
user := &model.User{
|
user := &model.User{
|
||||||
Username: "admin",
|
Username: defaultUsername,
|
||||||
Password: "admin",
|
Password: defaultPassword,
|
||||||
LoginSecret: "",
|
LoginSecret: defaultSecret,
|
||||||
}
|
}
|
||||||
return db.Create(user).Error
|
return db.Create(user).Error
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initInbound() error {
|
func isTableEmpty(tableName string) (bool, error) {
|
||||||
return db.AutoMigrate(&model.Inbound{})
|
var count int64
|
||||||
}
|
err := db.Table(tableName).Count(&count).Error
|
||||||
|
return count == 0, err
|
||||||
func initOutbound() error {
|
|
||||||
return db.AutoMigrate(&model.OutboundTraffics{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func initSetting() error {
|
|
||||||
return db.AutoMigrate(&model.Setting{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func initInboundClientIps() error {
|
|
||||||
return db.AutoMigrate(&model.InboundClientIps{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func initClientTraffic() error {
|
|
||||||
return db.AutoMigrate(&xray.ClientTraffic{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitDB(dbPath string) error {
|
func InitDB(dbPath string) error {
|
||||||
@@ -91,15 +89,27 @@ func InitDB(dbPath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, initialize := range initializers {
|
if err := initModels(); err != nil {
|
||||||
if err := initialize(); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if err := initUser(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CloseDB() error {
|
||||||
|
if db != nil {
|
||||||
|
sqlDB, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sqlDB.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetDB() *gorm.DB {
|
func GetDB() *gorm.DB {
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|||||||
54
go.mod
54
go.mod
@@ -1,25 +1,25 @@
|
|||||||
module x-ui
|
module x-ui
|
||||||
|
|
||||||
go 1.22.4
|
go 1.22.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/gzip v1.0.1
|
github.com/gin-contrib/gzip v1.0.1
|
||||||
github.com/gin-contrib/sessions v1.0.1
|
github.com/gin-contrib/sessions v1.0.1
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/goccy/go-json v0.10.3
|
github.com/goccy/go-json v0.10.3
|
||||||
github.com/mymmrac/telego v0.30.2
|
github.com/mymmrac/telego v0.31.0
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2
|
github.com/pelletier/go-toml/v2 v2.2.2
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v4 v4.24.6
|
github.com/shirou/gopsutil/v4 v4.24.6
|
||||||
github.com/valyala/fasthttp v1.55.0
|
github.com/valyala/fasthttp v1.55.0
|
||||||
github.com/xtls/xray-core v1.8.16
|
github.com/xtls/xray-core v1.8.19
|
||||||
go.uber.org/atomic v1.11.0
|
go.uber.org/atomic v1.11.0
|
||||||
golang.org/x/text v0.16.0
|
golang.org/x/text v0.16.0
|
||||||
google.golang.org/grpc v1.64.0
|
google.golang.org/grpc v1.65.0
|
||||||
gorm.io/driver/sqlite v1.5.6
|
gorm.io/driver/sqlite v1.5.6
|
||||||
gorm.io/gorm v1.25.10
|
gorm.io/gorm v1.25.11
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -30,7 +30,7 @@ require (
|
|||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||||
github.com/fasthttp/router v1.5.1 // indirect
|
github.com/fasthttp/router v1.5.2 // indirect
|
||||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
@@ -40,10 +40,10 @@ require (
|
|||||||
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.2 // indirect
|
||||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
|
github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da // indirect
|
||||||
github.com/gorilla/context v1.1.2 // indirect
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/sessions v1.2.2 // indirect
|
github.com/gorilla/sessions v1.3.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
github.com/grbit/go-json v0.11.0 // indirect
|
github.com/grbit/go-json v0.11.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
@@ -52,25 +52,26 @@ require (
|
|||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.9 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect
|
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
|
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
|
||||||
github.com/pires/go-proxyproto v0.7.0 // indirect
|
github.com/pires/go-proxyproto v0.7.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/quic-go/quic-go v0.45.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/refraction-networking/utls v1.6.6 // indirect
|
github.com/quic-go/quic-go v0.45.1 // indirect
|
||||||
|
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
github.com/sagernet/sing v0.4.1 // indirect
|
github.com/sagernet/sing v0.4.1 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect
|
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
||||||
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect
|
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
github.com/tklauser/numcpus v0.8.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
||||||
@@ -78,21 +79,22 @@ require (
|
|||||||
github.com/valyala/fastjson v1.6.4 // indirect
|
github.com/valyala/fastjson v1.6.4 // indirect
|
||||||
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect
|
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc // indirect
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.4.0 // indirect
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/crypto v0.24.0 // indirect
|
golang.org/x/crypto v0.25.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
|
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect
|
||||||
golang.org/x/mod v0.18.0 // indirect
|
golang.org/x/mod v0.19.0 // indirect
|
||||||
golang.org/x/net v0.26.0 // indirect
|
golang.org/x/net v0.27.0 // indirect
|
||||||
golang.org/x/sys v0.21.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.22.0 // indirect
|
golang.org/x/tools v0.23.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
|
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
|
||||||
|
|||||||
104
go.sum
104
go.sum
@@ -37,8 +37,8 @@ github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fp
|
|||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
|
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/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/fasthttp/router v1.5.1 h1:uViy8UYYhm5npJSKEZ4b/ozM//NGzVCfJbh6VJ0VKr8=
|
github.com/fasthttp/router v1.5.2 h1:ckJCCdV7hWkkrMeId3WfEhz+4Gyyf6QPwxi/RHIMZ6I=
|
||||||
github.com/fasthttp/router v1.5.1/go.mod h1:WrmsLo3mrerZP2VEXRV1E8nL8ymJFYCDTr4HmnB8+Zs=
|
github.com/fasthttp/router v1.5.2/go.mod h1:C8EY53ozOwpONyevc/V7Gr8pqnEjwnkFFqPo1alAGs0=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||||
@@ -97,8 +97,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
|||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
|
github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da h1:xRmpO92tb8y+Z85iUOMOicpCfaYcv7o3Cg3wKrIpg8g=
|
||||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
@@ -106,8 +106,8 @@ github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o
|
|||||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||||
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
|
||||||
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/grbit/go-json v0.11.0 h1:bAbyMdYrYl/OjYsSqLH99N2DyQ291mHy726Mx+sYrnc=
|
github.com/grbit/go-json v0.11.0 h1:bAbyMdYrYl/OjYsSqLH99N2DyQ291mHy726Mx+sYrnc=
|
||||||
@@ -139,8 +139,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0=
|
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
|
||||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
@@ -157,8 +157,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
|||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mymmrac/telego v0.30.2 h1:CqGlqX0hkgz9qMwdA3q+aZtSonqMOKQQrFLn/oUOTaw=
|
github.com/mymmrac/telego v0.31.0 h1:vsN+JCNkh7Z9vfL/2/AHZ2xBsRk2GCMj3zydjCxkgIc=
|
||||||
github.com/mymmrac/telego v0.30.2/go.mod h1:U6cWJBgRCzGt+s0q77x/Dh2+i+u56VTAAYKlMenhuFc=
|
github.com/mymmrac/telego v0.31.0/go.mod h1:MuqgVf2xXnIOWZs0prvsp3f4Yss80kCSjVEj4CRl7Ig=
|
||||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
|
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
|
||||||
@@ -179,31 +179,33 @@ github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP
|
|||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||||
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/quic-go/quic-go v0.45.0 h1:OHmkQGM37luZITyTSu6ff03HP/2IrwDX1ZFiNEhSFUE=
|
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||||
github.com/quic-go/quic-go v0.45.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
|
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||||
github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig=
|
github.com/quic-go/quic-go v0.45.1 h1:tPfeYCk+uZHjmDRwHHQmvHRYL2t44ROTujLeFVBmjCA=
|
||||||
github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
github.com/quic-go/quic-go v0.45.1/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
|
||||||
|
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||||
|
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
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/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 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk=
|
github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk=
|
||||||
github.com/sagernet/sing v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
github.com/sagernet/sing v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8=
|
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
|
||||||
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/shirou/gopsutil/v4 v4.24.6 h1:9qqCSYF2pgOU+t+NgJtp7Co5+5mHF/HyKBUckySQL64=
|
github.com/shirou/gopsutil/v4 v4.24.6 h1:9qqCSYF2pgOU+t+NgJtp7Co5+5mHF/HyKBUckySQL64=
|
||||||
github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA=
|
github.com/shirou/gopsutil/v4 v4.24.6/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA=
|
||||||
@@ -250,10 +252,10 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
|
|||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
|
||||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
@@ -273,10 +275,10 @@ github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3/go.mo
|
|||||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc h1:0Nj8T1n7F7+v4vRVroaJIvY6R0vNABLfPH+lzPHRJvI=
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d h1:+B97uD9uHLgAAulhigmys4BVwZZypzK7gPN3WtpgRJg=
|
||||||
github.com/xtls/reality v0.0.0-20240429224917-ecc4401070cc/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
||||||
github.com/xtls/xray-core v1.8.16 h1:PhbpdREAIvDS7xmxR6Sdpkx0h5ugmf6wIoWECWtJ0kE=
|
github.com/xtls/xray-core v1.8.19 h1:mml7smcO2FM5HyyKdqnf5F2BUvi3br2ldrqmeemEFRE=
|
||||||
github.com/xtls/xray-core v1.8.16/go.mod h1:tjzDQQJpFORuhf7fBsiswiexLVEeJpAfMsD0NE5xV7M=
|
github.com/xtls/xray-core v1.8.19/go.mod h1:0CwyMPNA5Cs+ukPXHbYQGgne/ug0PuXOSVqBu7zyXOc=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||||
@@ -294,16 +296,16 @@ golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+
|
|||||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
|
golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w=
|
||||||
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
|
||||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@@ -312,8 +314,8 @@ golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -337,10 +339,8 @@ golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
@@ -353,8 +353,8 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||||
@@ -371,14 +371,14 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
|
|||||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d h1:JU0iKnSg02Gmb5ZdV8nYsKEKsP6o/FGVWTrw4i1DA9A=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240711142825-46eb208f015d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
|
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||||
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
|
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -395,8 +395,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
|
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
|
||||||
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||||
gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
|
gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
|
||||||
gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 h1:ze1vwAdliUAr68RQ5NtufWaXaOg8WUO2OACzEV+TNdE=
|
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 h1:ze1vwAdliUAr68RQ5NtufWaXaOg8WUO2OACzEV+TNdE=
|
||||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
|
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ gen_random_string() {
|
|||||||
# This function will be called when user installed x-ui out of security
|
# This function will be called when user installed x-ui out of security
|
||||||
config_after_install() {
|
config_after_install() {
|
||||||
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
|
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
|
||||||
read -p "Do you want to continue with the modification [y/n]?": config_confirm
|
read -p "Would you like to customize the panel settings? (If not, random settings will be applied) [y/n]: " config_confirm
|
||||||
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
||||||
read -p "Please set up your username: " config_account
|
read -p "Please set up your username: " config_account
|
||||||
echo -e "${yellow}Your username will be: ${config_account}${plain}"
|
echo -e "${yellow}Your username will be: ${config_account}${plain}"
|
||||||
@@ -160,9 +160,9 @@ config_after_install() {
|
|||||||
echo -e "${green}Password: ${passwordTemp}${plain}"
|
echo -e "${green}Password: ${passwordTemp}${plain}"
|
||||||
echo -e "${green}WebBasePath: ${webBasePathTemp}${plain}"
|
echo -e "${green}WebBasePath: ${webBasePathTemp}${plain}"
|
||||||
echo -e "###############################################"
|
echo -e "###############################################"
|
||||||
echo -e "${red}If you forgot your login info, you can type x-ui and then type 8 to check after installation${plain}"
|
echo -e "${yellow}If you forgot your login info, you can type "x-ui settings" to check after installation${plain}"
|
||||||
else
|
else
|
||||||
echo -e "${red}This is your upgrade, will keep old settings. If you forgot your login info, you can type x-ui and then type 8 to check${plain}"
|
echo -e "${yellow}This is your upgrade, will keep old settings. If you forgot your login info, you can type "x-ui settings" to check${plain}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
/usr/local/x-ui/x-ui migrate
|
/usr/local/x-ui/x-ui migrate
|
||||||
|
|||||||
98
main.go
98
main.go
@@ -21,7 +21,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func runWebServer() {
|
func runWebServer() {
|
||||||
log.Printf("%v %v", config.GetName(), config.GetVersion())
|
log.Printf("Starting %v %v", config.GetName(), config.GetVersion())
|
||||||
|
|
||||||
switch config.GetLogLevel() {
|
switch config.GetLogLevel() {
|
||||||
case config.Debug:
|
case config.Debug:
|
||||||
@@ -35,31 +35,29 @@ func runWebServer() {
|
|||||||
case config.Error:
|
case config.Error:
|
||||||
logger.InitLogger(logging.ERROR)
|
logger.InitLogger(logging.ERROR)
|
||||||
default:
|
default:
|
||||||
log.Fatal("unknown log level:", config.GetLogLevel())
|
log.Fatalf("Unknown log level: %v", config.GetLogLevel())
|
||||||
}
|
}
|
||||||
|
|
||||||
err := database.InitDB(config.GetDBPath())
|
err := database.InitDB(config.GetDBPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatalf("Error initializing database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var server *web.Server
|
var server *web.Server
|
||||||
|
|
||||||
server = web.NewServer()
|
server = web.NewServer()
|
||||||
global.SetWebServer(server)
|
global.SetWebServer(server)
|
||||||
err = server.Start()
|
err = server.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Fatalf("Error starting web server: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var subServer *sub.Server
|
var subServer *sub.Server
|
||||||
subServer = sub.NewServer()
|
subServer = sub.NewServer()
|
||||||
global.SetSubServer(subServer)
|
global.SetSubServer(subServer)
|
||||||
|
|
||||||
err = subServer.Start()
|
err = subServer.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Fatalf("Error starting sub server: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,34 +69,39 @@ func runWebServer() {
|
|||||||
|
|
||||||
switch sig {
|
switch sig {
|
||||||
case syscall.SIGHUP:
|
case syscall.SIGHUP:
|
||||||
|
logger.Info("Received SIGHUP signal. Restarting servers...")
|
||||||
|
|
||||||
err := server.Stop()
|
err := server.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("stop server err:", err)
|
logger.Warning("Error stopping web server:", err)
|
||||||
}
|
}
|
||||||
err = subServer.Stop()
|
err = subServer.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("stop server err:", err)
|
logger.Warning("Error stopping sub server:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
server = web.NewServer()
|
server = web.NewServer()
|
||||||
global.SetWebServer(server)
|
global.SetWebServer(server)
|
||||||
err = server.Start()
|
err = server.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Fatalf("Error restarting web server: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Println("Web server restarted successfully.")
|
||||||
|
|
||||||
subServer = sub.NewServer()
|
subServer = sub.NewServer()
|
||||||
global.SetSubServer(subServer)
|
global.SetSubServer(subServer)
|
||||||
|
|
||||||
err = subServer.Start()
|
err = subServer.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Fatalf("Error restarting sub server: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Println("Sub server restarted successfully.")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
server.Stop()
|
server.Stop()
|
||||||
subServer.Stop()
|
subServer.Stop()
|
||||||
|
log.Println("Shutting down servers.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,16 +110,16 @@ func runWebServer() {
|
|||||||
func resetSetting() {
|
func resetSetting() {
|
||||||
err := database.InitDB(config.GetDBPath())
|
err := database.InitDB(config.GetDBPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println("Failed to initialize database:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
settingService := service.SettingService{}
|
settingService := service.SettingService{}
|
||||||
err = settingService.ResetSettings()
|
err = settingService.ResetSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("reset setting failed:", err)
|
fmt.Println("Failed to reset settings:", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("reset setting success")
|
fmt.Println("Settings successfully reset.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,19 +162,19 @@ func showSetting(show bool) {
|
|||||||
|
|
||||||
func updateTgbotEnableSts(status bool) {
|
func updateTgbotEnableSts(status bool) {
|
||||||
settingService := service.SettingService{}
|
settingService := service.SettingService{}
|
||||||
currentTgSts, err := settingService.GetTgbotenabled()
|
currentTgSts, err := settingService.GetTgbotEnabled()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Infof("current enabletgbot status[%v],need update to status[%v]", currentTgSts, status)
|
logger.Infof("current enabletgbot status[%v],need update to status[%v]", currentTgSts, status)
|
||||||
if currentTgSts != status {
|
if currentTgSts != status {
|
||||||
err := settingService.SetTgbotenabled(status)
|
err := settingService.SetTgbotEnabled(status)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
logger.Infof("SetTgbotenabled[%v] success", status)
|
logger.Infof("SetTgbotEnabled[%v] success", status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,7 +182,7 @@ func updateTgbotEnableSts(status bool) {
|
|||||||
func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime string) {
|
func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime string) {
|
||||||
err := database.InitDB(config.GetDBPath())
|
err := database.InitDB(config.GetDBPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println("Error initializing database:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,68 +191,65 @@ func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime stri
|
|||||||
if tgBotToken != "" {
|
if tgBotToken != "" {
|
||||||
err := settingService.SetTgBotToken(tgBotToken)
|
err := settingService.SetTgBotToken(tgBotToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Printf("Error setting Telegram bot token: %v\n", err)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
logger.Info("updateTgbotSetting tgBotToken success")
|
|
||||||
}
|
}
|
||||||
|
logger.Info("Successfully updated Telegram bot token.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if tgBotRuntime != "" {
|
if tgBotRuntime != "" {
|
||||||
err := settingService.SetTgbotRuntime(tgBotRuntime)
|
err := settingService.SetTgbotRuntime(tgBotRuntime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Printf("Error setting Telegram bot runtime: %v\n", err)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
logger.Infof("updateTgbotSetting tgBotRuntime[%s] success", tgBotRuntime)
|
|
||||||
}
|
}
|
||||||
|
logger.Infof("Successfully updated Telegram bot runtime to [%s].", tgBotRuntime)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tgBotChatid != "" {
|
if tgBotChatid != "" {
|
||||||
err := settingService.SetTgBotChatId(tgBotChatid)
|
err := settingService.SetTgBotChatId(tgBotChatid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Printf("Error setting Telegram bot chat ID: %v\n", err)
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
logger.Info("updateTgbotSetting tgBotChatid success")
|
|
||||||
}
|
}
|
||||||
|
logger.Info("Successfully updated Telegram bot chat ID.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSetting(port int, username string, password string, webBasePath string) {
|
func updateSetting(port int, username string, password string, webBasePath string) {
|
||||||
err := database.InitDB(config.GetDBPath())
|
err := database.InitDB(config.GetDBPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println("Database initialization failed:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
settingService := service.SettingService{}
|
settingService := service.SettingService{}
|
||||||
|
userService := service.UserService{}
|
||||||
|
|
||||||
if port > 0 {
|
if port > 0 {
|
||||||
err := settingService.SetPort(port)
|
err := settingService.SetPort(port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("set port failed:", err)
|
fmt.Println("Failed to set port:", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("set port %v success", port)
|
fmt.Printf("Port set successfully: %v\n", port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if username != "" || password != "" {
|
if username != "" || password != "" {
|
||||||
userService := service.UserService{}
|
|
||||||
err := userService.UpdateFirstUser(username, password)
|
err := userService.UpdateFirstUser(username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("set username and password failed:", err)
|
fmt.Println("Failed to update username and password:", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("set username and password success")
|
fmt.Println("Username and password updated successfully")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if webBasePath != "" {
|
if webBasePath != "" {
|
||||||
err := settingService.SetBasePath(webBasePath)
|
err := settingService.SetBasePath(webBasePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("set base URI path failed:", err)
|
fmt.Println("Failed to set base URI path:", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("set base URI path success")
|
fmt.Println("Base URI path set successfully")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,19 +348,19 @@ func main() {
|
|||||||
var reset bool
|
var reset bool
|
||||||
var show bool
|
var show bool
|
||||||
var remove_secret bool
|
var remove_secret bool
|
||||||
settingCmd.BoolVar(&reset, "reset", false, "reset all settings")
|
settingCmd.BoolVar(&reset, "reset", false, "Reset all settings")
|
||||||
settingCmd.BoolVar(&show, "show", false, "show current settings")
|
settingCmd.BoolVar(&show, "show", false, "Display current settings")
|
||||||
settingCmd.BoolVar(&remove_secret, "remove_secret", false, "remove secret")
|
settingCmd.BoolVar(&remove_secret, "remove_secret", false, "Remove secret key")
|
||||||
settingCmd.IntVar(&port, "port", 0, "set panel port")
|
settingCmd.IntVar(&port, "port", 0, "Set panel port number")
|
||||||
settingCmd.StringVar(&username, "username", "", "set login username")
|
settingCmd.StringVar(&username, "username", "", "Set login username")
|
||||||
settingCmd.StringVar(&password, "password", "", "set login password")
|
settingCmd.StringVar(&password, "password", "", "Set login password")
|
||||||
settingCmd.StringVar(&webBasePath, "webBasePath", "", "set web base path")
|
settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel")
|
||||||
settingCmd.StringVar(&webCertFile, "webCert", "", "set web public key path")
|
settingCmd.StringVar(&webCertFile, "webCert", "", "Set path to public key file for panel")
|
||||||
settingCmd.StringVar(&webKeyFile, "webCertKey", "", "set web private key path")
|
settingCmd.StringVar(&webKeyFile, "webCertKey", "", "Set path to private key file for panel")
|
||||||
settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "set telegram bot token")
|
settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "Set token for Telegram bot")
|
||||||
settingCmd.StringVar(&tgbotRuntime, "tgbotRuntime", "", "set telegram bot cron time")
|
settingCmd.StringVar(&tgbotRuntime, "tgbotRuntime", "", "Set cron time for Telegram bot notifications")
|
||||||
settingCmd.StringVar(&tgbotchatid, "tgbotchatid", "", "set telegram bot chat id")
|
settingCmd.StringVar(&tgbotchatid, "tgbotchatid", "", "Set chat ID for Telegram bot notifications")
|
||||||
settingCmd.BoolVar(&enabletgbot, "enabletgbot", false, "enable telegram bot notify")
|
settingCmd.BoolVar(&enabletgbot, "enabletgbot", false, "Enable notifications via Telegram bot")
|
||||||
|
|
||||||
oldUsage := flag.Usage
|
oldUsage := flag.Usage
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
|
|||||||
@@ -163,13 +163,13 @@ func (s *Server) Start() (err error) {
|
|||||||
}
|
}
|
||||||
listener = network.NewAutoHttpsListener(listener)
|
listener = network.NewAutoHttpsListener(listener)
|
||||||
listener = tls.NewListener(listener, c)
|
listener = tls.NewListener(listener, c)
|
||||||
logger.Info("sub server run https on", listener.Addr())
|
logger.Info("Sub server running HTTPS on", listener.Addr())
|
||||||
} else {
|
} else {
|
||||||
logger.Error("error in loading certificates: ", err)
|
logger.Error("Error loading certificates:", err)
|
||||||
logger.Info("sub server run http on", listener.Addr())
|
logger.Info("Sub server running HTTP on", listener.Addr())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.Info("sub server run http on", listener.Addr())
|
logger.Info("Sub server running HTTP on", listener.Addr())
|
||||||
}
|
}
|
||||||
s.listener = listener
|
s.listener = listener
|
||||||
|
|
||||||
|
|||||||
@@ -1023,10 +1023,9 @@ func (s *SubService) genRemark(inbound *model.Inbound, email string, extra strin
|
|||||||
remark = append(remark, fmt.Sprintf("%dM⏳", minutes))
|
remark = append(remark, fmt.Sprintf("%dM⏳", minutes))
|
||||||
}
|
}
|
||||||
case exp < 0:
|
case exp < 0:
|
||||||
passedSeconds := now - exp
|
days := exp / -86400
|
||||||
days := passedSeconds / 86400
|
hours := (exp % -86400) / 3600
|
||||||
hours := (passedSeconds % 86400) / 3600
|
minutes := (exp % -3600) / 60
|
||||||
minutes := (passedSeconds % 3600) / 60
|
|
||||||
if days > 0 {
|
if days > 0 {
|
||||||
if hours > 0 {
|
if hours > 0 {
|
||||||
remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours))
|
remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours))
|
||||||
|
|||||||
3
web/assets/axios/axios.min.js
vendored
3
web/assets/axios/axios.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -577,6 +577,10 @@ class Outbound extends CommonClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canEnableMux() {
|
canEnableMux() {
|
||||||
|
if (this.settings.flow && this.settings.flow != ''){
|
||||||
|
this.mux.enabled = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks, Protocols.HTTP, Protocols.Socks].includes(this.protocol);
|
return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks, Protocols.HTTP, Protocols.Socks].includes(this.protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -523,7 +523,7 @@ class HTTPUpgradeStreamSettings extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SplitHTTPStreamSettings extends XrayCommonClass {
|
class SplitHTTPStreamSettings extends XrayCommonClass {
|
||||||
constructor(path='/', host='', headers=[] , maxUploadSize= 1, maxConcurrentUploads= 10) {
|
constructor(path='/', host='', headers=[] , maxUploadSize= 1000000, maxConcurrentUploads= 10) {
|
||||||
super();
|
super();
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
@@ -570,7 +570,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||||||
disableSystemRoot = false,
|
disableSystemRoot = false,
|
||||||
enableSessionResumption = false,
|
enableSessionResumption = false,
|
||||||
certificates=[new TlsStreamSettings.Cert()],
|
certificates=[new TlsStreamSettings.Cert()],
|
||||||
alpn=[ALPN_OPTION.H2,ALPN_OPTION.HTTP1],
|
alpn=[ALPN_OPTION.H3,ALPN_OPTION.H2,ALPN_OPTION.HTTP1],
|
||||||
settings=new TlsStreamSettings.Settings()) {
|
settings=new TlsStreamSettings.Settings()) {
|
||||||
super();
|
super();
|
||||||
this.sni = serverName;
|
this.sni = serverName;
|
||||||
@@ -712,7 +712,7 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
|
|||||||
class XtlsStreamSettings extends XrayCommonClass {
|
class XtlsStreamSettings extends XrayCommonClass {
|
||||||
constructor(serverName='',
|
constructor(serverName='',
|
||||||
certificates=[new XtlsStreamSettings.Cert()],
|
certificates=[new XtlsStreamSettings.Cert()],
|
||||||
alpn=[ALPN_OPTION.H2,ALPN_OPTION.HTTP1],
|
alpn=[ALPN_OPTION.H3,ALPN_OPTION.H2,ALPN_OPTION.HTTP1],
|
||||||
settings=new XtlsStreamSettings.Settings()) {
|
settings=new XtlsStreamSettings.Settings()) {
|
||||||
super();
|
super();
|
||||||
this.sni = serverName;
|
this.sni = serverName;
|
||||||
@@ -892,7 +892,7 @@ class RealityStreamSettings extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
||||||
constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, serverName = '', spiderX= '/') {
|
constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_RANDOM, serverName = '', spiderX= '/') {
|
||||||
super();
|
super();
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
this.fingerprint = fingerprint;
|
this.fingerprint = fingerprint;
|
||||||
@@ -2608,4 +2608,4 @@ Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
|
|||||||
keepAlive: this.keepAlive?? undefined,
|
keepAlive: this.keepAlive?? undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ Date.prototype.formatDateTime = function (split = ' ') {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class DateUtil {
|
class DateUtil {
|
||||||
// String string to date object
|
// String to date object
|
||||||
static parseDate(str) {
|
static parseDate(str) {
|
||||||
return new Date(str.replace(/-/g, '/'));
|
return new Date(str.replace(/-/g, '/'));
|
||||||
}
|
}
|
||||||
@@ -143,4 +143,9 @@ class DateUtil {
|
|||||||
date.setMinTime();
|
date.setMinTime();
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static convertToJalalian(date) {
|
||||||
|
return date && moment.isMoment(date) ? date.format('jYYYY/jMM/jDD HH:mm:ss') : null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,57 @@
|
|||||||
class Msg {
|
class Msg {
|
||||||
constructor(success, msg, obj) {
|
constructor(success = false, msg = "", obj = null) {
|
||||||
this.success = false;
|
this.success = success;
|
||||||
this.msg = "";
|
this.msg = msg;
|
||||||
this.obj = null;
|
this.obj = obj;
|
||||||
|
|
||||||
if (success != null) {
|
|
||||||
this.success = success;
|
|
||||||
}
|
|
||||||
if (msg != null) {
|
|
||||||
this.msg = msg;
|
|
||||||
}
|
|
||||||
if (obj != null) {
|
|
||||||
this.obj = obj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class HttpUtil {
|
class HttpUtil {
|
||||||
static _handleMsg(msg) {
|
static _handleMsg(msg) {
|
||||||
if (!(msg instanceof Msg)) {
|
if (!(msg instanceof Msg) || msg.msg === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.msg === "") {
|
const messageType = msg.success ? 'success' : 'error';
|
||||||
return;
|
Vue.prototype.$message[messageType](msg.msg);
|
||||||
}
|
|
||||||
if (msg.success) {
|
|
||||||
Vue.prototype.$message.success(msg.msg);
|
|
||||||
} else {
|
|
||||||
Vue.prototype.$message.error(msg.msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static _respToMsg(resp) {
|
static _respToMsg(resp) {
|
||||||
const data = resp.data;
|
const { data } = resp;
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return new Msg(true);
|
return new Msg(true);
|
||||||
} else if (typeof data === 'object') {
|
|
||||||
if (data.hasOwnProperty('success')) {
|
|
||||||
return new Msg(data.success, data.msg, data.obj);
|
|
||||||
} else {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return new Msg(false, 'unknown data:', data);
|
|
||||||
}
|
}
|
||||||
|
if (typeof data === 'object' && 'success' in data) {
|
||||||
|
return new Msg(data.success, data.msg, data.obj);
|
||||||
|
}
|
||||||
|
return typeof data === 'object' ? data : new Msg(false, 'unknown data:', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async get(url, data, options) {
|
static async get(url, params, options = {}) {
|
||||||
let msg;
|
|
||||||
try {
|
try {
|
||||||
const resp = await axios.get(url, data, options);
|
const resp = await axios.get(url, { params, ...options });
|
||||||
msg = this._respToMsg(resp);
|
const msg = this._respToMsg(resp);
|
||||||
} catch (e) {
|
this._handleMsg(msg);
|
||||||
msg = new Msg(false, e.toString());
|
return msg;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('GET request failed:', error);
|
||||||
|
const errorMsg = new Msg(false, error.response?.data?.message || error.message);
|
||||||
|
this._handleMsg(errorMsg);
|
||||||
|
return errorMsg;
|
||||||
}
|
}
|
||||||
this._handleMsg(msg);
|
|
||||||
return msg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async post(url, data, options) {
|
static async post(url, data, options = {}) {
|
||||||
let msg;
|
|
||||||
try {
|
try {
|
||||||
const resp = await axios.post(url, data, options);
|
const resp = await axios.post(url, data, options);
|
||||||
msg = this._respToMsg(resp);
|
const msg = this._respToMsg(resp);
|
||||||
} catch (e) {
|
this._handleMsg(msg);
|
||||||
msg = new Msg(false, e.toString());
|
return msg;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('POST request failed:', error);
|
||||||
|
const errorMsg = new Msg(false, error.response?.data?.message || error.message);
|
||||||
|
this._handleMsg(errorMsg);
|
||||||
|
return errorMsg;
|
||||||
}
|
}
|
||||||
this._handleMsg(msg);
|
|
||||||
return msg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async postWithModal(url, data, modal) {
|
static async postWithModal(url, data, modal) {
|
||||||
|
|||||||
@@ -232,14 +232,12 @@ func (a *InboundController) resetClientTraffic(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
email := c.Param("email")
|
email := c.Param("email")
|
||||||
|
|
||||||
needRestart := true
|
needRestart, err := a.inboundService.ResetClientTraffic(id, email)
|
||||||
|
|
||||||
needRestart, err = a.inboundService.ResetClientTraffic(id, email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
jsonMsg(c, "Something went wrong!", err)
|
jsonMsg(c, "Something went wrong!", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jsonMsg(c, "traffic reseted", nil)
|
jsonMsg(c, "Traffic has been reset", nil)
|
||||||
if needRestart {
|
if needRestart {
|
||||||
a.xrayService.SetToNeedRestart()
|
a.xrayService.SetToNeedRestart()
|
||||||
}
|
}
|
||||||
@@ -253,7 +251,7 @@ func (a *InboundController) resetAllTraffics(c *gin.Context) {
|
|||||||
} else {
|
} else {
|
||||||
a.xrayService.SetToNeedRestart()
|
a.xrayService.SetToNeedRestart()
|
||||||
}
|
}
|
||||||
jsonMsg(c, "All traffics reseted", nil)
|
jsonMsg(c, "all traffic has been reset", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *InboundController) resetAllClientTraffics(c *gin.Context) {
|
func (a *InboundController) resetAllClientTraffics(c *gin.Context) {
|
||||||
@@ -270,7 +268,7 @@ func (a *InboundController) resetAllClientTraffics(c *gin.Context) {
|
|||||||
} else {
|
} else {
|
||||||
a.xrayService.SetToNeedRestart()
|
a.xrayService.SetToNeedRestart()
|
||||||
}
|
}
|
||||||
jsonMsg(c, "All traffics of client reseted", nil)
|
jsonMsg(c, "All traffic from the client has been reset.", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *InboundController) importInbound(c *gin.Context) {
|
func (a *InboundController) importInbound(c *gin.Context) {
|
||||||
@@ -313,9 +311,9 @@ func (a *InboundController) delDepletedClients(c *gin.Context) {
|
|||||||
jsonMsg(c, "Something went wrong!", err)
|
jsonMsg(c, "Something went wrong!", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jsonMsg(c, "All delpeted clients are deleted", nil)
|
jsonMsg(c, "All depleted clients are deleted", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *InboundController) onlines(c *gin.Context) {
|
func (a *InboundController) onlines(c *gin.Context) {
|
||||||
jsonObj(c, a.inboundService.GetOnlineClinets(), nil)
|
jsonObj(c, a.inboundService.GetOnlineClients(), nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
@@ -64,37 +65,40 @@ func (a *IndexController) login(c *gin.Context) {
|
|||||||
|
|
||||||
user := a.userService.CheckUser(form.Username, form.Password, form.LoginSecret)
|
user := a.userService.CheckUser(form.Username, form.Password, form.LoginSecret)
|
||||||
timeStr := time.Now().Format("2006-01-02 15:04:05")
|
timeStr := time.Now().Format("2006-01-02 15:04:05")
|
||||||
|
safeUser := template.HTMLEscapeString(form.Username)
|
||||||
|
safePass := template.HTMLEscapeString(form.Password)
|
||||||
|
safeSecret := template.HTMLEscapeString(form.LoginSecret)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
logger.Warningf("wrong username or password: \"%s\" \"%s\"", form.Username, form.Password)
|
logger.Warningf("wrong username or password or secret: \"%s\" \"%s\" \"%s\"", safeUser, safePass, safeSecret)
|
||||||
a.tgbot.UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0)
|
a.tgbot.UserLoginNotify(safeUser, safePass, getRemoteIp(c), timeStr, 0)
|
||||||
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword"))
|
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword"))
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
logger.Infof("%s login success, Ip Address: %s\n", form.Username, getRemoteIp(c))
|
logger.Infof("%s logged in successfully, Ip Address: %s\n", safeUser, getRemoteIp(c))
|
||||||
a.tgbot.UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 1)
|
a.tgbot.UserLoginNotify(safeUser, ``, getRemoteIp(c), timeStr, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionMaxAge, err := a.settingService.GetSessionMaxAge()
|
sessionMaxAge, err := a.settingService.GetSessionMaxAge()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warningf("Unable to get session's max age from DB")
|
logger.Warning("Unable to get session's max age from DB")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sessionMaxAge > 0 {
|
if sessionMaxAge > 0 {
|
||||||
err = session.SetMaxAge(c, sessionMaxAge*60)
|
err = session.SetMaxAge(c, sessionMaxAge*60)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warningf("Unable to set session's max age")
|
logger.Warning("Unable to set session's max age")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = session.SetLoginUser(c, user)
|
err = session.SetLoginUser(c, user)
|
||||||
logger.Info("user", user.Id, "login success")
|
logger.Infof("%s logged in successfully", user.Username)
|
||||||
jsonMsg(c, I18nWeb(c, "pages.login.toasts.successLogin"), err)
|
jsonMsg(c, I18nWeb(c, "pages.login.toasts.successLogin"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *IndexController) logout(c *gin.Context) {
|
func (a *IndexController) logout(c *gin.Context) {
|
||||||
user := session.GetLoginUser(c)
|
user := session.GetLoginUser(c)
|
||||||
if user != nil {
|
if user != nil {
|
||||||
logger.Info("user", user.Id, "logout")
|
logger.Infof("%s logged out successfully", user.Username)
|
||||||
}
|
}
|
||||||
session.ClearSession(c)
|
session.ClearSession(c)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
|
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ func (a *ServerController) stopXrayService(c *gin.Context) {
|
|||||||
jsonMsg(c, "", err)
|
jsonMsg(c, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
jsonMsg(c, "Xray stoped", err)
|
jsonMsg(c, "Xray stopped", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ServerController) restartXrayService(c *gin.Context) {
|
func (a *ServerController) restartXrayService(c *gin.Context) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ type XraySettingController struct {
|
|||||||
InboundService service.InboundService
|
InboundService service.InboundService
|
||||||
OutboundService service.OutboundService
|
OutboundService service.OutboundService
|
||||||
XrayService service.XrayService
|
XrayService service.XrayService
|
||||||
|
WarpService service.WarpService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewXraySettingController(g *gin.RouterGroup) *XraySettingController {
|
func NewXraySettingController(g *gin.RouterGroup) *XraySettingController {
|
||||||
@@ -72,16 +73,18 @@ func (a *XraySettingController) warp(c *gin.Context) {
|
|||||||
var err error
|
var err error
|
||||||
switch action {
|
switch action {
|
||||||
case "data":
|
case "data":
|
||||||
resp, err = a.XraySettingService.GetWarp()
|
resp, err = a.WarpService.GetWarpData()
|
||||||
|
case "del":
|
||||||
|
err = a.WarpService.DelWarpData()
|
||||||
case "config":
|
case "config":
|
||||||
resp, err = a.XraySettingService.GetWarpConfig()
|
resp, err = a.WarpService.GetWarpConfig()
|
||||||
case "reg":
|
case "reg":
|
||||||
skey := c.PostForm("privateKey")
|
skey := c.PostForm("privateKey")
|
||||||
pkey := c.PostForm("publicKey")
|
pkey := c.PostForm("publicKey")
|
||||||
resp, err = a.XraySettingService.RegWarp(skey, pkey)
|
resp, err = a.WarpService.RegWarp(skey, pkey)
|
||||||
case "license":
|
case "license":
|
||||||
license := c.PostForm("license")
|
license := c.PostForm("license")
|
||||||
resp, err = a.XraySettingService.SetWarpLicence(license)
|
resp, err = a.WarpService.SetWarpLicense(license)
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonObj(c, resp, err)
|
jsonObj(c, resp, err)
|
||||||
|
|||||||
@@ -79,8 +79,8 @@
|
|||||||
qrModal: qrModal,
|
qrModal: qrModal,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
copyToClipboard(elmentId, content) {
|
copyToClipboard(elementId, content) {
|
||||||
this.qrModal.clipboard = new ClipboardJS('#' + elmentId, {
|
this.qrModal.clipboard = new ClipboardJS('#' + elementId, {
|
||||||
text: () => content,
|
text: () => content,
|
||||||
});
|
});
|
||||||
this.qrModal.clipboard.on('success', () => {
|
this.qrModal.clipboard.on('success', () => {
|
||||||
@@ -88,9 +88,9 @@
|
|||||||
this.qrModal.clipboard.destroy();
|
this.qrModal.clipboard.destroy();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setQrCode(elmentId, content) {
|
setQrCode(elementId, content) {
|
||||||
new QRious({
|
new QRious({
|
||||||
element: document.querySelector('#' + elmentId),
|
element: document.querySelector('#' + elementId),
|
||||||
size: 400,
|
size: 400,
|
||||||
value: content,
|
value: content,
|
||||||
background: 'white',
|
background: 'white',
|
||||||
|
|||||||
@@ -108,15 +108,16 @@
|
|||||||
format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
|
format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
v-model="clientsBulkModal.expiryTime"></a-date-picker>
|
v-model="clientsBulkModal.expiryTime"></a-date-picker>
|
||||||
<persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
<persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
||||||
value="clientsBulkModal.expiryTime" v-model="clientsBulkModal.expiryTime"></persian-datepicker>
|
value="clientsBulkModal.expiryTime" v-model="clientsBulkModal.expiryTime">
|
||||||
|
</persian-datepicker>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="clientsBulkModal.expiryTime != 0">
|
<a-form-item v-if="clientsBulkModal.expiryTime != 0">
|
||||||
<template slot="label">
|
<template slot="label">
|
||||||
<span>{{ i18n "pages.client.renew" }}</span>
|
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.client.renewDesc" }}</span>
|
<span>{{ i18n "pages.client.renewDesc" }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
{{ i18n "pages.client.renew" }}
|
||||||
<a-icon type="question-circle"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -54,12 +54,13 @@
|
|||||||
<a-icon type="question-circle"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-date-picker style="width: 100%;" v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
<a-date-picker style="width: 100%;" v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
v-model="dbInbound._expiryTime"></a-date-picker>
|
v-model="dbInbound._expiryTime"></a-date-picker>
|
||||||
<persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
<persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
||||||
value="dbInbound._expiryTime" v-model="dbInbound._expiryTime"></persian-datepicker>
|
value="dbInbound._expiryTime" v-model="dbInbound._expiryTime">
|
||||||
</a-form-item>
|
</persian-datepicker>
|
||||||
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- vmess settings -->
|
<!-- vmess settings -->
|
||||||
|
|||||||
@@ -90,7 +90,14 @@
|
|||||||
<template slot="content">
|
<template slot="content">
|
||||||
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>[[ DateUtil.formatMillis(client._expiryTime) ]]</span>
|
<span v-else>
|
||||||
|
<template v-if="app.datepicker === 'gregorian'">
|
||||||
|
[[ DateUtil.formatMillis(client._expiryTime) ]]
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
[[ DateUtil.convertToJalalian(moment(client._expiryTime)) ]]
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<table>
|
<table>
|
||||||
<tr class="tr-table-box">
|
<tr class="tr-table-box">
|
||||||
@@ -108,7 +115,14 @@
|
|||||||
<template slot="content">
|
<template slot="content">
|
||||||
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>[[ DateUtil.formatMillis(client._expiryTime) ]]</span>
|
<span v-else>
|
||||||
|
<template v-if="app.datepicker === 'gregorian'">
|
||||||
|
[[ DateUtil.formatMillis(client._expiryTime) ]]
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
[[ DateUtil.convertToJalalian(moment(client._expiryTime)) ]]
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="min-width: 50px; border: none;" :color="userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
<a-tag style="min-width: 50px; border: none;" :color="userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
@@ -201,7 +215,14 @@
|
|||||||
<template slot="content">
|
<template slot="content">
|
||||||
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>[[ DateUtil.formatMillis(client._expiryTime) ]]</span>
|
<span v-else>
|
||||||
|
<template v-if="app.datepicker === 'gregorian'">
|
||||||
|
[[ DateUtil.formatMillis(client._expiryTime) ]]
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
[[ DateUtil.convertToJalalian(moment(client._expiryTime)) ]]
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-progress :show-info="false" :status="isClientEnabled(record, client.email)? 'exception' : ''" :percent="expireProgress(client.expiryTime, client.reset)" />
|
<a-progress :show-info="false" :status="isClientEnabled(record, client.email)? 'exception' : ''" :percent="expireProgress(client.expiryTime, client.reset)" />
|
||||||
</a-popover>
|
</a-popover>
|
||||||
@@ -214,7 +235,14 @@
|
|||||||
<template slot="content">
|
<template slot="content">
|
||||||
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else>[[ DateUtil.formatMillis(client._expiryTime) ]]</span>
|
<span v-else>
|
||||||
|
<template v-if="app.datepicker === 'gregorian'">
|
||||||
|
[[ DateUtil.formatMillis(client._expiryTime) ]]
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
[[ DateUtil.convertToJalalian(moment(client._expiryTime)) ]]
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="min-width: 50px; border: none;" :color="userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
<a-tag style="min-width: 50px; border: none;" :color="userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
|
|||||||
@@ -221,7 +221,14 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<template v-if="infoModal.clientSettings.expiryTime > 0">
|
<template v-if="infoModal.clientSettings.expiryTime > 0">
|
||||||
<a-tag :color="usageColor(new Date().getTime(), app.expireDiff, infoModal.clientSettings.expiryTime)"> [[ DateUtil.formatMillis(infoModal.clientSettings.expiryTime) ]] </a-tag>
|
<a-tag :color="usageColor(new Date().getTime(), app.expireDiff, infoModal.clientSettings.expiryTime)">
|
||||||
|
<template v-if="app.datepicker === 'gregorian'">
|
||||||
|
[[ DateUtil.formatMillis(infoModal.clientSettings.expiryTime) ]]
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
[[ DateUtil.convertToJalalian(moment(infoModal.clientSettings.expiryTime)) ]]
|
||||||
|
</template>
|
||||||
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
<a-tag v-else-if="infoModal.clientSettings.expiryTime < 0" color="green">[[ infoModal.clientSettings.expiryTime / -86400000 ]] {{ i18n "pages.client.days" }}
|
<a-tag v-else-if="infoModal.clientSettings.expiryTime < 0" color="green">[[ infoModal.clientSettings.expiryTime / -86400000 ]] {{ i18n "pages.client.days" }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
@@ -494,8 +501,8 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
copyToClipboard(elmentId, content) {
|
copyToClipboard(elementId, content) {
|
||||||
this.infoModal.clipboard = new ClipboardJS('#' + elmentId, {
|
this.infoModal.clipboard = new ClipboardJS('#' + elementId, {
|
||||||
text: () => content,
|
text: () => content,
|
||||||
});
|
});
|
||||||
this.infoModal.clipboard.on('success', () => {
|
this.infoModal.clipboard.on('success', () => {
|
||||||
|
|||||||
@@ -403,9 +403,12 @@
|
|||||||
</template>
|
</template>
|
||||||
<template slot="expiryTime" slot-scope="text, dbInbound">
|
<template slot="expiryTime" slot-scope="text, dbInbound">
|
||||||
<a-popover v-if="dbInbound.expiryTime > 0" :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover v-if="dbInbound.expiryTime > 0" :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content" v-if="app.datepicker === 'gregorian'">
|
||||||
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
|
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else slot="content">
|
||||||
|
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
|
||||||
|
</template>
|
||||||
<a-tag style="min-width: 50px;" :color="usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
|
<a-tag style="min-width: 50px;" :color="usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
|
||||||
[[ remainedDays(dbInbound._expiryTime) ]]
|
[[ remainedDays(dbInbound._expiryTime) ]]
|
||||||
</a-tag>
|
</a-tag>
|
||||||
@@ -498,8 +501,14 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "pages.inbounds.expireDate" }}</td>
|
<td>{{ i18n "pages.inbounds.expireDate" }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a-tag style="min-width: 50px; text-align: center;" v-if="dbInbound.expiryTime > 0" :color="dbInbound.isExpiry? 'red': 'blue'">
|
<a-tag style="min-width: 50px; text-align: center;" v-if="dbInbound.expiryTime > 0"
|
||||||
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
|
:color="dbInbound.isExpiry? 'red': 'blue'">
|
||||||
|
<template v-if="app.datepicker === 'gregorian'">
|
||||||
|
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
|
||||||
|
</template>
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<a-tag v-else style="text-align: center;" color="purple" class="infinite-tag">
|
<a-tag v-else style="text-align: center;" color="purple" class="infinite-tag">
|
||||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||||
|
|||||||
@@ -503,6 +503,7 @@
|
|||||||
this.loading(false);
|
this.loading(false);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
this.user = {};
|
this.user = {};
|
||||||
|
window.location.replace(basePath + "logout");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async restartPanel() {
|
async restartPanel() {
|
||||||
|
|||||||
@@ -24,19 +24,22 @@
|
|||||||
<td>[[ warpModal.warpData.private_key ]]</td>
|
<td>[[ warpModal.warpData.private_key ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
<a-button @click="delConfig" :loading="warpModal.confirmLoading" type="danger">{{ i18n "delete" }}</a-button>
|
||||||
<a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.settings" }}</a-divider>
|
<a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.settings" }}</a-divider>
|
||||||
<a-collapse style="margin: 10px 0;">
|
<a-collapse style="margin: 10px 0;">
|
||||||
<a-collapse-panel header='WARP/WARP+ License Key'>
|
<a-collapse-panel header='WARP/WARP+ License Key'>
|
||||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label="Key">
|
<a-form-item label="Key">
|
||||||
<a-input v-model="warpPlus"></a-input>
|
<a-input v-model="warpPlus"></a-input>
|
||||||
<a-button @click="updateLicense(warpPlus)" :disabled="warpPlus.length<26" :loading="warpModal.confirmLoading">{{ i18n "pages.inbounds.update" }}</a-button>
|
<a-button @click="updateLicense(warpPlus)" :disabled="warpPlus.length<26"
|
||||||
|
:loading="warpModal.confirmLoading">{{ i18n "pages.inbounds.update" }}</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
<a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.accountInfo" }}</a-divider>
|
<a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.accountInfo" }}</a-divider>
|
||||||
<a-button icon="sync" @click="getConfig" style="margin-top: 5px; margin-bottom: 10px;" :loading="warpModal.confirmLoading" type="primary">{{ i18n "info" }}</a-button>
|
<a-button icon="sync" @click="getConfig" style="margin-top: 5px; margin-bottom: 10px;"
|
||||||
|
:loading="warpModal.confirmLoading" type="primary">{{ i18n "info" }}</a-button>
|
||||||
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig)">
|
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig)">
|
||||||
<table style="width: 100%">
|
<table style="width: 100%">
|
||||||
<tr class="client-table-odd-row">
|
<tr class="client-table-odd-row">
|
||||||
@@ -51,39 +54,39 @@
|
|||||||
<td>Device Enabled</td>
|
<td>Device Enabled</td>
|
||||||
<td>[[ warpModal.warpConfig.enabled ]]</td>
|
<td>[[ warpModal.warpConfig.enabled ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account)">
|
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account)">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Account Type</td>
|
<td>Account Type</td>
|
||||||
<td>[[ warpModal.warpConfig.account.account_type ]]</td>
|
<td>[[ warpModal.warpConfig.account.account_type ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="client-table-odd-row">
|
<tr class="client-table-odd-row">
|
||||||
<td>Role</td>
|
<td>Role</td>
|
||||||
<td>[[ warpModal.warpConfig.account.role ]]</td>
|
<td>[[ warpModal.warpConfig.account.role ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>WARP+ Data</td>
|
<td>WARP+ Data</td>
|
||||||
<td>[[ sizeFormat(warpModal.warpConfig.account.premium_data) ]]</td>
|
<td>[[ sizeFormat(warpModal.warpConfig.account.premium_data) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="client-table-odd-row">
|
<tr class="client-table-odd-row">
|
||||||
<td>Quota</td>
|
<td>Quota</td>
|
||||||
<td>[[ sizeFormat(warpModal.warpConfig.account.quota) ]]</td>
|
<td>[[ sizeFormat(warpModal.warpConfig.account.quota) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account.usage)">
|
<tr v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account.usage)">
|
||||||
<td>Usage</td>
|
<td>Usage</td>
|
||||||
<td>[[ sizeFormat(warpModal.warpConfig.account.usage) ]]</td>
|
<td>[[ sizeFormat(warpModal.warpConfig.account.usage) ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</table>
|
</table>
|
||||||
<a-divider style="margin: 10px 0;">{{ i18n "pages.xray.outbound.outboundStatus" }}</a-divider>
|
<a-divider style="margin: 10px 0;">{{ i18n "pages.xray.outbound.outboundStatus" }}</a-divider>
|
||||||
<a-form :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<template v-if="warpOutboundIndex>=0">
|
<template v-if="warpOutboundIndex>=0">
|
||||||
<a-tag color="green" style="line-height: 31px;">{{ i18n "enabled" }}</a-tag>
|
<a-tag color="green" style="line-height: 31px;">{{ i18n "enabled" }}</a-tag>
|
||||||
<a-button @click="resetOutbound" :loading="warpModal.confirmLoading" type="danger">{{ i18n "reset" }}</a-button>
|
<a-button @click="resetOutbound" :loading="warpModal.confirmLoading" type="danger">{{ i18n "reset" }}</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-tag color="orange" style="line-height: 31px;">{{ i18n "disabled" }}</a-tag>
|
<a-tag color="orange" style="line-height: 31px;">{{ i18n "disabled" }}</a-tag>
|
||||||
<a-button @click="addOutbound" :loading="warpModal.confirmLoading" type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
<a-button @click="addOutbound" :loading="warpModal.confirmLoading" type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||||
</template>
|
</template>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</template>
|
</template>
|
||||||
@@ -101,21 +104,20 @@
|
|||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.warpConfig = null;
|
this.warpConfig = null;
|
||||||
this.getData();
|
this.getData();
|
||||||
|
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.loading(false);
|
this.loading(false);
|
||||||
},
|
},
|
||||||
loading(loading=true) {
|
loading(loading = true) {
|
||||||
this.confirmLoading = loading;
|
this.confirmLoading = loading;
|
||||||
},
|
},
|
||||||
async getData(){
|
async getData() {
|
||||||
this.loading(true);
|
this.loading(true);
|
||||||
const msg = await HttpUtil.post('/panel/xray/warp/data');
|
const msg = await HttpUtil.post('/panel/xray/warp/data');
|
||||||
this.loading(false);
|
this.loading(false);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
this.warpData = msg.obj.length>0 ? JSON.parse(msg.obj): null;
|
this.warpData = msg.obj.length > 0 ? JSON.parse(msg.obj) : null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -131,14 +133,15 @@
|
|||||||
collectConfig() {
|
collectConfig() {
|
||||||
config = warpModal.warpConfig.config;
|
config = warpModal.warpConfig.config;
|
||||||
peer = config.peers[0];
|
peer = config.peers[0];
|
||||||
if(config){
|
if (config) {
|
||||||
warpModal.warpOutbound = Outbound.fromJson({
|
warpModal.warpOutbound = Outbound.fromJson({
|
||||||
tag: 'warp',
|
tag: 'warp',
|
||||||
protocol: Protocols.Wireguard,
|
protocol: Protocols.Wireguard,
|
||||||
settings: {
|
settings: {
|
||||||
mtu: 1420,
|
mtu: 1420,
|
||||||
secretKey: warpModal.warpData.private_key,
|
secretKey: warpModal.warpData.private_key,
|
||||||
address: Object.values(config.interface.addresses),
|
address: this.getAddresses(config.interface.addresses),
|
||||||
|
reserved: this.getResolved(config.client_id),
|
||||||
domainStrategy: 'ForceIP',
|
domainStrategy: 'ForceIP',
|
||||||
peers: [{
|
peers: [{
|
||||||
publicKey: peer.public_key,
|
publicKey: peer.public_key,
|
||||||
@@ -149,10 +152,32 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async register(){
|
getAddresses(addrs) {
|
||||||
|
let addresses = [];
|
||||||
|
if (addrs.v4) addresses.push(addrs.v4 + "/32");
|
||||||
|
if (addrs.v6) addresses.push(addrs.v6 + "/128");
|
||||||
|
return addresses;
|
||||||
|
},
|
||||||
|
getResolved(client_id) {
|
||||||
|
let reserved = [];
|
||||||
|
let decoded = atob(client_id);
|
||||||
|
let hexString = '';
|
||||||
|
for (let i = 0; i < decoded.length; i++) {
|
||||||
|
let hex = decoded.charCodeAt(i).toString(16);
|
||||||
|
hexString += (hex.length === 1 ? '0' : '') + hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < hexString.length; i += 2) {
|
||||||
|
let hexByte = hexString.slice(i, i + 2);
|
||||||
|
let decValue = parseInt(hexByte, 16);
|
||||||
|
reserved.push(decValue);
|
||||||
|
}
|
||||||
|
return reserved;
|
||||||
|
},
|
||||||
|
async register() {
|
||||||
warpModal.loading(true);
|
warpModal.loading(true);
|
||||||
keys = Wireguard.generateKeypair();
|
keys = Wireguard.generateKeypair();
|
||||||
const msg = await HttpUtil.post('/panel/xray/warp/reg',keys);
|
const msg = await HttpUtil.post('/panel/xray/warp/reg', keys);
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
resp = JSON.parse(msg.obj);
|
resp = JSON.parse(msg.obj);
|
||||||
warpModal.warpData = resp.data;
|
warpModal.warpData = resp.data;
|
||||||
@@ -161,9 +186,9 @@
|
|||||||
}
|
}
|
||||||
warpModal.loading(false);
|
warpModal.loading(false);
|
||||||
},
|
},
|
||||||
async updateLicense(l){
|
async updateLicense(l) {
|
||||||
warpModal.loading(true);
|
warpModal.loading(true);
|
||||||
const msg = await HttpUtil.post('/panel/xray/warp/license',{license: l});
|
const msg = await HttpUtil.post('/panel/xray/warp/license', { license: l });
|
||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
warpModal.warpData = JSON.parse(msg.obj);
|
warpModal.warpData = JSON.parse(msg.obj);
|
||||||
warpModal.warpConfig = null;
|
warpModal.warpConfig = null;
|
||||||
@@ -171,7 +196,7 @@
|
|||||||
}
|
}
|
||||||
warpModal.loading(false);
|
warpModal.loading(false);
|
||||||
},
|
},
|
||||||
async getConfig(){
|
async getConfig() {
|
||||||
warpModal.loading(true);
|
warpModal.loading(true);
|
||||||
const msg = await HttpUtil.post('/panel/xray/warp/config');
|
const msg = await HttpUtil.post('/panel/xray/warp/config');
|
||||||
warpModal.loading(false);
|
warpModal.loading(false);
|
||||||
@@ -180,20 +205,37 @@
|
|||||||
this.collectConfig();
|
this.collectConfig();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addOutbound(){
|
async delConfig() {
|
||||||
|
warpModal.loading(true);
|
||||||
|
const msg = await HttpUtil.post('/panel/xray/warp/del');
|
||||||
|
warpModal.loading(false);
|
||||||
|
if (msg.success) {
|
||||||
|
warpModal.warpData = null;
|
||||||
|
warpModal.warpConfig = null;
|
||||||
|
this.delOutbound();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addOutbound() {
|
||||||
app.templateSettings.outbounds.push(warpModal.warpOutbound.toJson());
|
app.templateSettings.outbounds.push(warpModal.warpOutbound.toJson());
|
||||||
app.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
|
app.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
|
||||||
warpModal.close();
|
warpModal.close();
|
||||||
},
|
},
|
||||||
resetOutbound(){
|
resetOutbound() {
|
||||||
app.templateSettings.outbounds[this.warpOutboundIndex] = warpModal.warpOutbound.toJson();
|
app.templateSettings.outbounds[this.warpOutboundIndex] = warpModal.warpOutbound.toJson();
|
||||||
app.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
|
app.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
|
||||||
warpModal.close();
|
warpModal.close();
|
||||||
|
},
|
||||||
|
delOutbound() {
|
||||||
|
if (this.warpOutboundIndex != -1) {
|
||||||
|
app.templateSettings.outbounds.splice(this.warpOutboundIndex, 1);
|
||||||
|
app.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
|
||||||
|
}
|
||||||
|
warpModal.close();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
warpOutboundIndex: {
|
warpOutboundIndex: {
|
||||||
get: function() {
|
get: function () {
|
||||||
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag == 'warp') : -1;
|
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag == 'warp') : -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,4 +243,4 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1054,12 +1054,13 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
changeObsCode() {
|
changeObsCode() {
|
||||||
if (this.obsSettings == ''){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if(this.cm != null) {
|
if(this.cm != null) {
|
||||||
this.cm.toTextArea();
|
this.cm.toTextArea();
|
||||||
}
|
}
|
||||||
|
if (this.obsSettings == ''){
|
||||||
|
this.cm = null;
|
||||||
|
return
|
||||||
|
}
|
||||||
textAreaObj = document.getElementById('obsSetting');
|
textAreaObj = document.getElementById('obsSetting');
|
||||||
textAreaObj.value = this[this.obsSettings];
|
textAreaObj.value = this[this.obsSettings];
|
||||||
this.cm = CodeMirror.fromTextArea(textAreaObj, this.cmOptions);
|
this.cm = CodeMirror.fromTextArea(textAreaObj, this.cmOptions);
|
||||||
@@ -1267,7 +1268,8 @@
|
|||||||
balancer: {
|
balancer: {
|
||||||
tag: '',
|
tag: '',
|
||||||
strategy: 'random',
|
strategy: 'random',
|
||||||
selector: []
|
selector: [],
|
||||||
|
fallbackTag: ''
|
||||||
},
|
},
|
||||||
confirm: (balancer) => {
|
confirm: (balancer) => {
|
||||||
balancerModal.loading();
|
balancerModal.loading();
|
||||||
@@ -1277,27 +1279,18 @@
|
|||||||
}
|
}
|
||||||
let tmpBalancer = {
|
let tmpBalancer = {
|
||||||
'tag': balancer.tag,
|
'tag': balancer.tag,
|
||||||
'selector': balancer.selector
|
'selector': balancer.selector,
|
||||||
|
'fallbackTag': balancer.fallbackTag
|
||||||
};
|
};
|
||||||
if (balancer.strategy && balancer.strategy != 'random') {
|
if (balancer.strategy && balancer.strategy != 'random') {
|
||||||
tmpBalancer.strategy = {
|
tmpBalancer.strategy = {
|
||||||
'type': balancer.strategy
|
'type': balancer.strategy
|
||||||
};
|
};
|
||||||
if (balancer.strategy == 'leastPing'){
|
|
||||||
if (!newTemplateSettings.observatory)
|
|
||||||
newTemplateSettings.observatory = this.defaultObservatory;
|
|
||||||
if (!newTemplateSettings.observatory.subjectSelector.includes(balancer.tag))
|
|
||||||
newTemplateSettings.observatory.subjectSelector.push(balancer.tag);
|
|
||||||
}
|
|
||||||
if (balancer.strategy == 'leastLoad'){
|
|
||||||
if (!newTemplateSettings.burstObservatory)
|
|
||||||
newTemplateSettings.burstObservatory = this.defaultBurstObservatory;
|
|
||||||
if (!newTemplateSettings.burstObservatory.subjectSelector.includes(balancer.tag))
|
|
||||||
newTemplateSettings.burstObservatory.subjectSelector.push(balancer.tag);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
newTemplateSettings.routing.balancers.push(tmpBalancer);
|
newTemplateSettings.routing.balancers.push(tmpBalancer);
|
||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
|
if (balancer.strategy == 'leastPing' || balancer.strategy == 'leastLoad')
|
||||||
|
this.updateObservatorySelectors();
|
||||||
balancerModal.close();
|
balancerModal.close();
|
||||||
this.changeObsCode();
|
this.changeObsCode();
|
||||||
},
|
},
|
||||||
@@ -1317,7 +1310,8 @@
|
|||||||
|
|
||||||
let tmpBalancer = {
|
let tmpBalancer = {
|
||||||
'tag': balancer.tag,
|
'tag': balancer.tag,
|
||||||
'selector': balancer.selector
|
'selector': balancer.selector,
|
||||||
|
'fallbackTag': balancer.fallbackTag
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove old tag
|
// Remove old tag
|
||||||
@@ -1332,18 +1326,6 @@
|
|||||||
tmpBalancer.strategy = {
|
tmpBalancer.strategy = {
|
||||||
'type': balancer.strategy
|
'type': balancer.strategy
|
||||||
};
|
};
|
||||||
if (balancer.strategy == 'leastPing'){
|
|
||||||
if (!newTemplateSettings.observatory)
|
|
||||||
newTemplateSettings.observatory = this.defaultObservatory;
|
|
||||||
if (!newTemplateSettings.observatory.subjectSelector.includes(balancer.tag))
|
|
||||||
newTemplateSettings.observatory.subjectSelector.push(balancer.tag);
|
|
||||||
}
|
|
||||||
if (balancer.strategy == 'leastLoad'){
|
|
||||||
if (!newTemplateSettings.burstObservatory)
|
|
||||||
newTemplateSettings.burstObservatory = this.defaultBurstObservatory;
|
|
||||||
if (!newTemplateSettings.burstObservatory.subjectSelector.includes(balancer.tag))
|
|
||||||
newTemplateSettings.burstObservatory.subjectSelector.push(balancer.tag);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newTemplateSettings.routing.balancers[index] = tmpBalancer;
|
newTemplateSettings.routing.balancers[index] = tmpBalancer;
|
||||||
@@ -1356,14 +1338,49 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
|
if (balancer.strategy == 'leastPing' || balancer.strategy == 'leastLoad')
|
||||||
|
this.updateObservatorySelectors();
|
||||||
balancerModal.close();
|
balancerModal.close();
|
||||||
this.changeObsCode();
|
this.changeObsCode();
|
||||||
},
|
},
|
||||||
isEdit: true
|
isEdit: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
updateObservatorySelectors(){
|
||||||
|
newTemplateSettings = this.templateSettings;
|
||||||
|
const leastPings = this.balancersData.filter((b) => b.strategy == 'leastPing');
|
||||||
|
const leastLoads = this.balancersData.filter((b) => b.strategy == 'leastLoad');
|
||||||
|
if (leastPings.length>0){
|
||||||
|
if (!newTemplateSettings.observatory)
|
||||||
|
newTemplateSettings.observatory = this.defaultObservatory;
|
||||||
|
newTemplateSettings.observatory.subjectSelector = [];
|
||||||
|
leastPings.forEach((b) => {
|
||||||
|
b.selector.forEach((s) => {
|
||||||
|
if (!newTemplateSettings.observatory.subjectSelector.includes(s))
|
||||||
|
newTemplateSettings.observatory.subjectSelector.push(s);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
delete newTemplateSettings.observatory
|
||||||
|
}
|
||||||
|
if (leastLoads.length>0){
|
||||||
|
if (!newTemplateSettings.burstObservatory)
|
||||||
|
newTemplateSettings.burstObservatory = this.defaultBurstObservatory;
|
||||||
|
newTemplateSettings.burstObservatory.subjectSelector = [];
|
||||||
|
leastLoads.forEach((b) => {
|
||||||
|
b.selector.forEach((s) => {
|
||||||
|
if (!newTemplateSettings.burstObservatory.subjectSelector.includes(s))
|
||||||
|
newTemplateSettings.burstObservatory.subjectSelector.push(s);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
delete newTemplateSettings.burstObservatory
|
||||||
|
}
|
||||||
|
this.templateSettings = newTemplateSettings;
|
||||||
|
this.changeObsCode();
|
||||||
|
},
|
||||||
deleteBalancer(index) {
|
deleteBalancer(index) {
|
||||||
let newTemplateSettings = { ...this.templateSettings };
|
newTemplateSettings = this.templateSettings;
|
||||||
|
|
||||||
// Remove from balancers
|
// Remove from balancers
|
||||||
const removedBalancer = this.balancersData.splice(index, 1)[0];
|
const removedBalancer = this.balancersData.splice(index, 1)[0];
|
||||||
@@ -1371,27 +1388,14 @@
|
|||||||
// Remove from settings
|
// Remove from settings
|
||||||
let realIndex = newTemplateSettings.routing.balancers.findIndex((b) => b.tag === removedBalancer.tag);
|
let realIndex = newTemplateSettings.routing.balancers.findIndex((b) => b.tag === removedBalancer.tag);
|
||||||
newTemplateSettings.routing.balancers.splice(realIndex, 1);
|
newTemplateSettings.routing.balancers.splice(realIndex, 1);
|
||||||
|
|
||||||
// Remove tag from observatory
|
|
||||||
if (newTemplateSettings.observatory){
|
|
||||||
newTemplateSettings.observatory.subjectSelector = newTemplateSettings.observatory.subjectSelector.filter(s => s != removedBalancer.tag);
|
|
||||||
}
|
|
||||||
if (newTemplateSettings.burstObservatory){
|
|
||||||
newTemplateSettings.burstObservatory.subjectSelector = newTemplateSettings.burstObservatory.subjectSelector.filter(s => s != removedBalancer.tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove related routing rules
|
|
||||||
newTemplateSettings.routing.rules.forEach((rule) => {
|
|
||||||
if (rule.balancerTag === removedBalancer.tag) {
|
|
||||||
delete rule.balancerTag;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update balancers property to an empty array if there are no more balancers
|
// Update balancers property to an empty array if there are no more balancers
|
||||||
if (newTemplateSettings.routing.balancers.length === 0) {
|
if (newTemplateSettings.routing.balancers.length === 0) {
|
||||||
delete newTemplateSettings.routing.balancers;
|
delete newTemplateSettings.routing.balancers;
|
||||||
}
|
}
|
||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
|
this.updateObservatorySelectors();
|
||||||
|
this.obsSettings = '';
|
||||||
this.changeObsCode()
|
this.changeObsCode()
|
||||||
},
|
},
|
||||||
addDNSServer(){
|
addDNSServer(){
|
||||||
@@ -1622,7 +1626,8 @@
|
|||||||
'key': index,
|
'key': index,
|
||||||
'tag': o.tag ? o.tag : "",
|
'tag': o.tag ? o.tag : "",
|
||||||
'strategy': o.strategy?.type ?? "random",
|
'strategy': o.strategy?.type ?? "random",
|
||||||
'selector': o.selector ? o.selector : []
|
'selector': o.selector ? o.selector : [],
|
||||||
|
'fallbackTag': o.fallbackTag?? '',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1649,22 +1654,8 @@
|
|||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
observatoryEnable: {
|
observatoryEnable: function () { return this.templateSettings != null && this.templateSettings.observatory != undefined },
|
||||||
get: function () { return this.templateSettings != null && this.templateSettings.observatory },
|
burstObservatoryEnable: function () { return this.templateSettings != null && this.templateSettings.burstObservatory != undefined },
|
||||||
set: function (v) {
|
|
||||||
newTemplateSettings = this.templateSettings;
|
|
||||||
newTemplateSettings.observatory = v ? this.defaultObservatory : undefined;
|
|
||||||
this.templateSettings = newTemplateSettings;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
burstObservatoryEnable: {
|
|
||||||
get: function () { return this.templateSettings != null && this.templateSettings.burstObservatory },
|
|
||||||
set: function (v) {
|
|
||||||
newTemplateSettings = this.templateSettings;
|
|
||||||
newTemplateSettings.burstObservatory = v ? this.defaultBurstObservatory : undefined;
|
|
||||||
this.templateSettings = newTemplateSettings;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
freedomStrategy: {
|
freedomStrategy: {
|
||||||
get: function () {
|
get: function () {
|
||||||
if (!this.templateSettings) return "AsIs";
|
if (!this.templateSettings) return "AsIs";
|
||||||
|
|||||||
@@ -25,13 +25,19 @@
|
|||||||
<a-select-option value="leastPing">Least Ping</a-select-option>
|
<a-select-option value="leastPing">Least Ping</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.xray.balancer.balancerSelectors" }}' has-feedback
|
<a-form-item label='{{ i18n "pages.xray.balancer.balancerSelectors" }}' has-feedback
|
||||||
:validate-status="balancerModal.emptySelector? 'warning' : 'success'">
|
:validate-status="balancerModal.emptySelector? 'warning' : 'success'">
|
||||||
<a-select v-model="balancerModal.balancer.selector" mode="tags" @change="balancerModal.checkSelector()"
|
<a-select v-model="balancerModal.balancer.selector" mode="tags" @change="balancerModal.checkSelector()"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="tag in balancerModal.outboundTags" :value="tag">[[ tag ]]</a-select-option>
|
<a-select-option v-for="tag in balancerModal.outboundTags" :value="tag">[[ tag ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="Fallback">
|
||||||
|
<a-select v-model="balancerModal.balancer.fallbackTag" clearable
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="tag in [ '', ...balancerModal.outboundTags]" :value="tag">[[ tag ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
</table>
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@@ -48,7 +54,8 @@
|
|||||||
balancer: {
|
balancer: {
|
||||||
tag: '',
|
tag: '',
|
||||||
strategy: 'random',
|
strategy: 'random',
|
||||||
selector: []
|
selector: [],
|
||||||
|
fallbackTag: ''
|
||||||
},
|
},
|
||||||
outboundTags: [],
|
outboundTags: [],
|
||||||
balancerTags:[],
|
balancerTags:[],
|
||||||
@@ -71,7 +78,8 @@
|
|||||||
balancerModal.balancer = {
|
balancerModal.balancer = {
|
||||||
tag: '',
|
tag: '',
|
||||||
strategy: 'random',
|
strategy: 'random',
|
||||||
selector: []
|
selector: [],
|
||||||
|
fallbackTag: ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.balancerTags = balancerTags.filter((tag) => tag != balancer.tag);
|
this.balancerTags = balancerTags.filter((tag) => tag != balancer.tag);
|
||||||
|
|||||||
@@ -252,46 +252,55 @@ func (j *CheckClientIpJob) addInboundClientIps(clientEmail string, ips []string)
|
|||||||
|
|
||||||
func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmail string, ips []string) bool {
|
func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.InboundClientIps, clientEmail string, ips []string) bool {
|
||||||
jsonIps, err := json.Marshal(ips)
|
jsonIps, err := json.Marshal(ips)
|
||||||
j.checkError(err)
|
if err != nil {
|
||||||
|
logger.Error("failed to marshal IPs to JSON:", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
inboundClientIps.ClientEmail = clientEmail
|
inboundClientIps.ClientEmail = clientEmail
|
||||||
inboundClientIps.Ips = string(jsonIps)
|
inboundClientIps.Ips = string(jsonIps)
|
||||||
|
|
||||||
// check inbound limitation
|
// Fetch inbound settings by client email
|
||||||
inbound, err := j.getInboundByEmail(clientEmail)
|
inbound, err := j.getInboundByEmail(clientEmail)
|
||||||
j.checkError(err)
|
if err != nil {
|
||||||
|
logger.Errorf("failed to fetch inbound settings for email %s: %s", clientEmail, err)
|
||||||
if inbound.Settings == "" {
|
|
||||||
logger.Debug("wrong data ", inbound)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if inbound.Settings == "" {
|
||||||
|
logger.Debug("wrong data:", inbound)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal settings to get client limits
|
||||||
settings := map[string][]model.Client{}
|
settings := map[string][]model.Client{}
|
||||||
json.Unmarshal([]byte(inbound.Settings), &settings)
|
json.Unmarshal([]byte(inbound.Settings), &settings)
|
||||||
clients := settings["clients"]
|
clients := settings["clients"]
|
||||||
shouldCleanLog := false
|
shouldCleanLog := false
|
||||||
j.disAllowedIps = []string{}
|
j.disAllowedIps = []string{}
|
||||||
|
|
||||||
// create iplimit log file channel
|
// Open log file for IP limits
|
||||||
logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("failed to create or open ip limit log file: %s", err)
|
logger.Errorf("failed to open IP limit log file: %s", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
defer logIpFile.Close()
|
defer logIpFile.Close()
|
||||||
log.SetOutput(logIpFile)
|
log.SetOutput(logIpFile)
|
||||||
log.SetFlags(log.LstdFlags)
|
log.SetFlags(log.LstdFlags)
|
||||||
|
|
||||||
|
// Check client IP limits
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.Email == clientEmail {
|
if client.Email == clientEmail {
|
||||||
limitIp := client.LimitIP
|
limitIp := client.LimitIP
|
||||||
|
|
||||||
if limitIp != 0 {
|
if limitIp > 0 && inbound.Enable {
|
||||||
shouldCleanLog = true
|
shouldCleanLog = true
|
||||||
|
|
||||||
if limitIp < len(ips) && inbound.Enable {
|
if limitIp < len(ips) {
|
||||||
j.disAllowedIps = append(j.disAllowedIps, ips[limitIp:]...)
|
j.disAllowedIps = append(j.disAllowedIps, ips[limitIp:]...)
|
||||||
for i := limitIp; i < len(ips); i++ {
|
for i := limitIp; i < len(ips); i++ {
|
||||||
log.Printf("[LIMIT_IP] Email = %s || SRC = %s", clientEmail, ips[i])
|
logger.Debugf("[LIMIT_IP] Email = %s || SRC = %s", clientEmail, ips[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,12 +310,15 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
|
|||||||
sort.Strings(j.disAllowedIps)
|
sort.Strings(j.disAllowedIps)
|
||||||
|
|
||||||
if len(j.disAllowedIps) > 0 {
|
if len(j.disAllowedIps) > 0 {
|
||||||
logger.Debug("disAllowedIps ", j.disAllowedIps)
|
logger.Debug("disAllowedIps:", j.disAllowedIps)
|
||||||
}
|
}
|
||||||
|
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
err = db.Save(inboundClientIps).Error
|
err = db.Save(inboundClientIps).Error
|
||||||
j.checkError(err)
|
if err != nil {
|
||||||
|
logger.Error("failed to save inboundClientIps:", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return shouldCleanLog
|
return shouldCleanLog
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,8 @@ func (j *XrayTrafficJob) Run() {
|
|||||||
if !j.xrayService.IsXrayRunning() {
|
if !j.xrayService.IsXrayRunning() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
traffics, clientTraffics, err := j.xrayService.GetXrayTraffic()
|
traffics, clientTraffics, err := j.xrayService.GetXrayTraffic()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("get xray traffic failed:", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err, needRestart0 := j.inboundService.AddTraffic(traffics, clientTraffics)
|
err, needRestart0 := j.inboundService.AddTraffic(traffics, clientTraffics)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -14,12 +15,17 @@ func DomainValidatorMiddleware(domain string) gin.HandlerFunc {
|
|||||||
host = c.GetHeader("X-Real-IP")
|
host = c.GetHeader("X-Real-IP")
|
||||||
}
|
}
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host, _, _ := net.SplitHostPort(c.Request.Host)
|
host = c.Request.Host
|
||||||
if host != domain {
|
if colonIndex := strings.LastIndex(host, ":"); colonIndex != -1 {
|
||||||
c.AbortWithStatus(http.StatusForbidden)
|
host, _, _ = net.SplitHostPort(host)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
c.Next()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if host != domain {
|
||||||
|
c.AbortWithStatus(http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -595,7 +595,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
inerfaceClients := settings["clients"].([]interface{})
|
interfaceClients := settings["clients"].([]interface{})
|
||||||
|
|
||||||
oldInbound, err := s.GetInbound(data.Id)
|
oldInbound, err := s.GetInbound(data.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -650,7 +650,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
settingsClients := oldSettings["clients"].([]interface{})
|
settingsClients := oldSettings["clients"].([]interface{})
|
||||||
settingsClients[clientIndex] = inerfaceClients[0]
|
settingsClients[clientIndex] = interfaceClients[0]
|
||||||
oldSettings["clients"] = settingsClients
|
oldSettings["clients"] = settingsClients
|
||||||
|
|
||||||
newSettings, err := json.MarshalIndent(oldSettings, "", " ")
|
newSettings, err := json.MarshalIndent(oldSettings, "", " ")
|
||||||
@@ -1134,7 +1134,6 @@ func (s *InboundService) DelClientStat(tx *gorm.DB, email string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) DelClientIPs(tx *gorm.DB, email string) error {
|
func (s *InboundService) DelClientIPs(tx *gorm.DB, email string) error {
|
||||||
logger.Warning(email)
|
|
||||||
return tx.Where("client_email = ?", email).Delete(model.InboundClientIps{}).Error
|
return tx.Where("client_email = ?", email).Delete(model.InboundClientIps{}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1143,7 +1142,7 @@ func (s *InboundService) GetClientInboundByTrafficID(trafficId int) (traffic *xr
|
|||||||
var traffics []*xray.ClientTraffic
|
var traffics []*xray.ClientTraffic
|
||||||
err = db.Model(xray.ClientTraffic{}).Where("id = ?", trafficId).Find(&traffics).Error
|
err = db.Model(xray.ClientTraffic{}).Where("id = ?", trafficId).Find(&traffics).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning(err)
|
logger.Warningf("Error retrieving ClientTraffic with trafficId %d: %v", trafficId, err)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if len(traffics) > 0 {
|
if len(traffics) > 0 {
|
||||||
@@ -1158,7 +1157,7 @@ func (s *InboundService) GetClientInboundByEmail(email string) (traffic *xray.Cl
|
|||||||
var traffics []*xray.ClientTraffic
|
var traffics []*xray.ClientTraffic
|
||||||
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
|
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning(err)
|
logger.Warningf("Error retrieving ClientTraffic with email %s: %v", email, err)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if len(traffics) > 0 {
|
if len(traffics) > 0 {
|
||||||
@@ -1699,15 +1698,20 @@ func (s *InboundService) DelDepletedClients(id int) (err error) {
|
|||||||
func (s *InboundService) GetClientTrafficTgBot(tgId int64) ([]*xray.ClientTraffic, error) {
|
func (s *InboundService) GetClientTrafficTgBot(tgId int64) ([]*xray.ClientTraffic, error) {
|
||||||
db := database.GetDB()
|
db := database.GetDB()
|
||||||
var inbounds []*model.Inbound
|
var inbounds []*model.Inbound
|
||||||
err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": %d%%`, tgId)).Find(&inbounds).Error
|
|
||||||
|
// Retrieve inbounds where settings contain the given tgId
|
||||||
|
err := db.Model(model.Inbound{}).Where("settings LIKE ?", fmt.Sprintf(`%%"tgId": %d%%`, tgId)).Find(&inbounds).Error
|
||||||
if err != nil && err != gorm.ErrRecordNotFound {
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
|
logger.Errorf("Error retrieving inbounds with tgId %d: %v", tgId, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var emails []string
|
var emails []string
|
||||||
for _, inbound := range inbounds {
|
for _, inbound := range inbounds {
|
||||||
clients, err := s.GetClients(inbound)
|
clients, err := s.GetClients(inbound)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Unable to get clients from inbound")
|
logger.Errorf("Error retrieving clients for inbound %d: %v", inbound.Id, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.TgID == tgId {
|
if client.TgID == tgId {
|
||||||
@@ -1715,15 +1719,19 @@ func (s *InboundService) GetClientTrafficTgBot(tgId int64) ([]*xray.ClientTraffi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var traffics []*xray.ClientTraffic
|
var traffics []*xray.ClientTraffic
|
||||||
err = db.Model(xray.ClientTraffic{}).Where("email IN ?", emails).Find(&traffics).Error
|
err = db.Model(xray.ClientTraffic{}).Where("email IN ?", emails).Find(&traffics).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if err == gorm.ErrRecordNotFound {
|
||||||
logger.Warning(err)
|
logger.Warning("No ClientTraffic records found for emails:", emails)
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
logger.Errorf("Error retrieving ClientTraffic for emails %v: %v", emails, err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return traffics, err
|
|
||||||
|
return traffics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.ClientTraffic, err error) {
|
func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.ClientTraffic, err error) {
|
||||||
@@ -1732,7 +1740,7 @@ func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.Cl
|
|||||||
|
|
||||||
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
|
err = db.Model(xray.ClientTraffic{}).Where("email = ?", email).Find(&traffics).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning(err)
|
logger.Warningf("Error retrieving ClientTraffic with email %s: %v", email, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(traffics) > 0 {
|
if len(traffics) > 0 {
|
||||||
@@ -1747,38 +1755,51 @@ func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.Client
|
|||||||
inbound := &model.Inbound{}
|
inbound := &model.Inbound{}
|
||||||
traffic = &xray.ClientTraffic{}
|
traffic = &xray.ClientTraffic{}
|
||||||
|
|
||||||
err = db.Model(model.Inbound{}).Where("settings like ?", "%\""+query+"\"%").First(inbound).Error
|
// Search for inbound settings that contain the query
|
||||||
|
err = db.Model(model.Inbound{}).Where("settings LIKE ?", "%\""+query+"\"%").First(inbound).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if err == gorm.ErrRecordNotFound {
|
||||||
logger.Warning(err)
|
logger.Warningf("Inbound settings containing query %s not found: %v", query, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
logger.Errorf("Error searching for inbound settings with query %s: %v", query, err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
traffic.InboundId = inbound.Id
|
traffic.InboundId = inbound.Id
|
||||||
|
|
||||||
// get settings clients
|
// Unmarshal settings to get clients
|
||||||
settings := map[string][]model.Client{}
|
settings := map[string][]model.Client{}
|
||||||
json.Unmarshal([]byte(inbound.Settings), &settings)
|
if err := json.Unmarshal([]byte(inbound.Settings), &settings); err != nil {
|
||||||
|
logger.Errorf("Error unmarshalling inbound settings for inbound ID %d: %v", inbound.Id, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
clients := settings["clients"]
|
clients := settings["clients"]
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.ID == query && client.Email != "" {
|
if (client.ID == query || client.Password == query) && client.Email != "" {
|
||||||
traffic.Email = client.Email
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if client.Password == query && client.Email != "" {
|
|
||||||
traffic.Email = client.Email
|
traffic.Email = client.Email
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if traffic.Email == "" {
|
if traffic.Email == "" {
|
||||||
return nil, err
|
logger.Warningf("No client found with query %s in inbound ID %d", query, inbound.Id)
|
||||||
|
return nil, gorm.ErrRecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve ClientTraffic based on the found email
|
||||||
err = db.Model(xray.ClientTraffic{}).Where("email = ?", traffic.Email).First(traffic).Error
|
err = db.Model(xray.ClientTraffic{}).Where("email = ?", traffic.Email).First(traffic).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning(err)
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
logger.Warningf("ClientTraffic for email %s not found: %v", traffic.Email, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
logger.Errorf("Error retrieving ClientTraffic for email %s: %v", traffic.Email, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return traffic, err
|
|
||||||
|
return traffic, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetInboundClientIps(clientEmail string) (string, error) {
|
func (s *InboundService) GetInboundClientIps(clientEmail string) (string, error) {
|
||||||
@@ -1948,6 +1969,6 @@ func (s *InboundService) MigrateDB() {
|
|||||||
s.MigrationRemoveOrphanedTraffics()
|
s.MigrationRemoveOrphanedTraffics()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) GetOnlineClinets() []string {
|
func (s *InboundService) GetOnlineClients() []string {
|
||||||
return p.GetOnlineClients()
|
return p.GetOnlineClients()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func (s *OutboundService) GetOutboundsTraffic() ([]*model.OutboundTraffics, erro
|
|||||||
|
|
||||||
err := db.Model(model.OutboundTraffics{}).Find(&traffics).Error
|
err := db.Model(model.OutboundTraffics{}).Find(&traffics).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning(err)
|
logger.Warning("Error retrieving OutboundTraffics: ", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func (s *PanelService) RestartPanel(delay time.Duration) error {
|
|||||||
time.Sleep(delay)
|
time.Sleep(delay)
|
||||||
err := p.Signal(syscall.SIGHUP)
|
err := p.Signal(syscall.SIGHUP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("send signal SIGHUP failed:", err)
|
logger.Error("failed to send SIGHUP signal:", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -312,6 +312,16 @@ func (s *ServerService) downloadXRay(version string) (string, error) {
|
|||||||
arch = "64"
|
arch = "64"
|
||||||
case "arm64":
|
case "arm64":
|
||||||
arch = "arm64-v8a"
|
arch = "arm64-v8a"
|
||||||
|
case "armv7":
|
||||||
|
arch = "arm32-v7a"
|
||||||
|
case "armv6":
|
||||||
|
arch = "arm32-v6"
|
||||||
|
case "armv5":
|
||||||
|
arch = "arm32-v5"
|
||||||
|
case "386":
|
||||||
|
arch = "32"
|
||||||
|
case "s390x":
|
||||||
|
arch = "s390x"
|
||||||
}
|
}
|
||||||
|
|
||||||
fileName := fmt.Sprintf("Xray-%s-%s.zip", osName, arch)
|
fileName := fmt.Sprintf("Xray-%s-%s.zip", osName, arch)
|
||||||
|
|||||||
@@ -269,11 +269,11 @@ func (s *SettingService) SetTgBotChatId(chatIds string) error {
|
|||||||
return s.setString("tgBotChatId", chatIds)
|
return s.setString("tgBotChatId", chatIds)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetTgbotenabled() (bool, error) {
|
func (s *SettingService) GetTgbotEnabled() (bool, error) {
|
||||||
return s.getBool("tgBotEnable")
|
return s.getBool("tgBotEnable")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SettingService) SetTgbotenabled(value bool) error {
|
func (s *SettingService) SetTgbotEnabled(value bool) error {
|
||||||
return s.setBool("tgBotEnable", value)
|
return s.setBool("tgBotEnable", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,7 +524,7 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) {
|
|||||||
"pageSize": func() (interface{}, error) { return s.GetPageSize() },
|
"pageSize": func() (interface{}, error) { return s.GetPageSize() },
|
||||||
"defaultCert": func() (interface{}, error) { return s.GetCertFile() },
|
"defaultCert": func() (interface{}, error) { return s.GetCertFile() },
|
||||||
"defaultKey": func() (interface{}, error) { return s.GetKeyFile() },
|
"defaultKey": func() (interface{}, error) { return s.GetKeyFile() },
|
||||||
"tgBotEnable": func() (interface{}, error) { return s.GetTgbotenabled() },
|
"tgBotEnable": func() (interface{}, error) { return s.GetTgbotEnabled() },
|
||||||
"subEnable": func() (interface{}, error) { return s.GetSubEnable() },
|
"subEnable": func() (interface{}, error) { return s.GetSubEnable() },
|
||||||
"subURI": func() (interface{}, error) { return s.GetSubURI() },
|
"subURI": func() (interface{}, error) { return s.GetSubURI() },
|
||||||
"subJsonURI": func() (interface{}, error) { return s.GetSubJsonURI() },
|
"subJsonURI": func() (interface{}, error) { return s.GetSubJsonURI() },
|
||||||
|
|||||||
@@ -64,52 +64,59 @@ func (t *Tgbot) GetHashStorage() *global.HashStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tgbot) Start(i18nFS embed.FS) error {
|
func (t *Tgbot) Start(i18nFS embed.FS) error {
|
||||||
|
// Initialize localizer
|
||||||
err := locale.InitLocalizer(i18nFS, &t.settingService)
|
err := locale.InitLocalizer(i18nFS, &t.settingService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// init hash storage => store callback queries
|
// Initialize hash storage to store callback queries
|
||||||
hashStorage = global.NewHashStorage(20 * time.Minute)
|
hashStorage = global.NewHashStorage(20 * time.Minute)
|
||||||
|
|
||||||
t.SetHostname()
|
t.SetHostname()
|
||||||
tgBottoken, err := t.settingService.GetTgBotToken()
|
|
||||||
if err != nil || tgBottoken == "" {
|
// Get Telegram bot token
|
||||||
logger.Warning("Get TgBotToken failed:", err)
|
tgBotToken, err := t.settingService.GetTgBotToken()
|
||||||
|
if err != nil || tgBotToken == "" {
|
||||||
|
logger.Warning("Failed to get Telegram bot token:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tgBotid, err := t.settingService.GetTgBotChatId()
|
// Get Telegram bot chat ID(s)
|
||||||
|
tgBotID, err := t.settingService.GetTgBotChatId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("Get GetTgBotChatId failed:", err)
|
logger.Warning("Failed to get Telegram bot chat ID:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if tgBotid != "" {
|
// Parse admin IDs from comma-separated string
|
||||||
for _, adminId := range strings.Split(tgBotid, ",") {
|
if tgBotID != "" {
|
||||||
id, err := strconv.Atoi(adminId)
|
for _, adminID := range strings.Split(tgBotID, ",") {
|
||||||
|
id, err := strconv.Atoi(adminID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("Failed to get IDs from GetTgBotChatId:", err)
|
logger.Warning("Failed to parse admin ID from Telegram bot chat ID:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
adminIds = append(adminIds, int64(id))
|
adminIds = append(adminIds, int64(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get Telegram bot proxy URL
|
||||||
tgBotProxy, err := t.settingService.GetTgBotProxy()
|
tgBotProxy, err := t.settingService.GetTgBotProxy()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("Failed to get ProxyUrl:", err)
|
logger.Warning("Failed to get Telegram bot proxy URL:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bot, err = t.NewBot(tgBottoken, tgBotProxy)
|
// Create new Telegram bot instance
|
||||||
|
bot, err = t.NewBot(tgBotToken, tgBotProxy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Get tgbot's api error:", err)
|
logger.Error("Failed to initialize Telegram bot API:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// listen for TG bot income messages
|
// Start receiving Telegram bot messages
|
||||||
if !isRunning {
|
if !isRunning {
|
||||||
logger.Info("Starting Telegram receiver ...")
|
logger.Info("Telegram bot receiver started")
|
||||||
go t.OnReceive()
|
go t.OnReceive()
|
||||||
isRunning = true
|
isRunning = true
|
||||||
}
|
}
|
||||||
@@ -201,7 +208,7 @@ func (t *Tgbot) OnReceive() {
|
|||||||
}, th.AnyCommand())
|
}, th.AnyCommand())
|
||||||
|
|
||||||
botHandler.HandleCallbackQuery(func(_ *telego.Bot, query telego.CallbackQuery) {
|
botHandler.HandleCallbackQuery(func(_ *telego.Bot, query telego.CallbackQuery) {
|
||||||
t.asnwerCallback(&query, checkAdmin(query.From.ID))
|
t.answerCallback(&query, checkAdmin(query.From.ID))
|
||||||
}, th.AnyCallbackQueryWithMessage())
|
}, th.AnyCallbackQueryWithMessage())
|
||||||
|
|
||||||
botHandler.HandleMessage(func(_ *telego.Bot, message telego.Message) {
|
botHandler.HandleMessage(func(_ *telego.Bot, message telego.Message) {
|
||||||
@@ -286,7 +293,7 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tgbot) asnwerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool) {
|
func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool) {
|
||||||
chatId := callbackQuery.Message.GetChat().ID
|
chatId := callbackQuery.Message.GetChat().ID
|
||||||
|
|
||||||
if isAdmin {
|
if isAdmin {
|
||||||
@@ -964,7 +971,7 @@ func (t *Tgbot) getServerUsage(chatId int64, messageID ...int) string {
|
|||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send server usage without an inline keyborad
|
// Send server usage without an inline keyboard
|
||||||
func (t *Tgbot) sendServerUsage() string {
|
func (t *Tgbot) sendServerUsage() string {
|
||||||
info := t.prepareServerUsageInfo()
|
info := t.prepareServerUsageInfo()
|
||||||
return info
|
return info
|
||||||
@@ -1019,7 +1026,7 @@ func (t *Tgbot) prepareServerUsageInfo() string {
|
|||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tgbot) UserLoginNotify(username string, ip string, time string, status LoginStatus) {
|
func (t *Tgbot) UserLoginNotify(username string, password string, ip string, time string, status LoginStatus) {
|
||||||
if !t.IsRunning() {
|
if !t.IsRunning() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1037,11 +1044,12 @@ func (t *Tgbot) UserLoginNotify(username string, ip string, time string, status
|
|||||||
msg := ""
|
msg := ""
|
||||||
if status == LoginSuccess {
|
if status == LoginSuccess {
|
||||||
msg += t.I18nBot("tgbot.messages.loginSuccess")
|
msg += t.I18nBot("tgbot.messages.loginSuccess")
|
||||||
|
msg += t.I18nBot("tgbot.messages.hostname", "Hostname=="+hostname)
|
||||||
} else if status == LoginFail {
|
} else if status == LoginFail {
|
||||||
msg += t.I18nBot("tgbot.messages.loginFailed")
|
msg += t.I18nBot("tgbot.messages.loginFailed")
|
||||||
|
msg += t.I18nBot("tgbot.messages.hostname", "Hostname=="+hostname)
|
||||||
|
msg += t.I18nBot("tgbot.messages.password", "Password=="+password)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg += t.I18nBot("tgbot.messages.hostname", "Hostname=="+hostname)
|
|
||||||
msg += t.I18nBot("tgbot.messages.username", "Username=="+username)
|
msg += t.I18nBot("tgbot.messages.username", "Username=="+username)
|
||||||
msg += t.I18nBot("tgbot.messages.ip", "IP=="+ip)
|
msg += t.I18nBot("tgbot.messages.ip", "IP=="+ip)
|
||||||
msg += t.I18nBot("tgbot.messages.time", "Time=="+time)
|
msg += t.I18nBot("tgbot.messages.time", "Time=="+time)
|
||||||
@@ -1051,14 +1059,14 @@ func (t *Tgbot) UserLoginNotify(username string, ip string, time string, status
|
|||||||
func (t *Tgbot) getInboundUsages() string {
|
func (t *Tgbot) getInboundUsages() string {
|
||||||
info := ""
|
info := ""
|
||||||
// get traffic
|
// get traffic
|
||||||
inbouds, err := t.inboundService.GetAllInbounds()
|
inbounds, err := t.inboundService.GetAllInbounds()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("GetAllInbounds run failed:", err)
|
logger.Warning("GetAllInbounds run failed:", err)
|
||||||
info += t.I18nBot("tgbot.answers.getInboundsFailed")
|
info += t.I18nBot("tgbot.answers.getInboundsFailed")
|
||||||
} else {
|
} else {
|
||||||
// NOTE:If there no any sessions here,need to notify here
|
// NOTE:If there no any sessions here,need to notify here
|
||||||
// TODO:Sub-node push, automatic conversion format
|
// TODO:Sub-node push, automatic conversion format
|
||||||
for _, inbound := range inbouds {
|
for _, inbound := range inbounds {
|
||||||
info += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
|
info += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
|
||||||
info += t.I18nBot("tgbot.messages.port", "Port=="+strconv.Itoa(inbound.Port))
|
info += t.I18nBot("tgbot.messages.port", "Port=="+strconv.Itoa(inbound.Port))
|
||||||
info += t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic((inbound.Up+inbound.Down)), "Upload=="+common.FormatTraffic(inbound.Up), "Download=="+common.FormatTraffic(inbound.Down))
|
info += t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic((inbound.Up+inbound.Down)), "Upload=="+common.FormatTraffic(inbound.Up), "Download=="+common.FormatTraffic(inbound.Down))
|
||||||
@@ -1331,20 +1339,20 @@ func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tgbot) searchInbound(chatId int64, remark string) {
|
func (t *Tgbot) searchInbound(chatId int64, remark string) {
|
||||||
inbouds, err := t.inboundService.SearchInbounds(remark)
|
inbounds, err := t.inboundService.SearchInbounds(remark)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning(err)
|
logger.Warning(err)
|
||||||
msg := t.I18nBot("tgbot.wentWrong")
|
msg := t.I18nBot("tgbot.wentWrong")
|
||||||
t.SendMsgToTgbot(chatId, msg)
|
t.SendMsgToTgbot(chatId, msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(inbouds) == 0 {
|
if len(inbounds) == 0 {
|
||||||
msg := t.I18nBot("tgbot.noInbounds")
|
msg := t.I18nBot("tgbot.noInbounds")
|
||||||
t.SendMsgToTgbot(chatId, msg)
|
t.SendMsgToTgbot(chatId, msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, inbound := range inbouds {
|
for _, inbound := range inbounds {
|
||||||
info := ""
|
info := ""
|
||||||
info += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
|
info += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
|
||||||
info += t.I18nBot("tgbot.messages.port", "Port=="+strconv.Itoa(inbound.Port))
|
info += t.I18nBot("tgbot.messages.port", "Port=="+strconv.Itoa(inbound.Port))
|
||||||
|
|||||||
162
web/service/warp.go
Normal file
162
web/service/warp.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
"x-ui/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WarpService struct {
|
||||||
|
SettingService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WarpService) GetWarpData() (string, error) {
|
||||||
|
warp, err := s.SettingService.GetWarp()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return warp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WarpService) DelWarpData() error {
|
||||||
|
err := s.SettingService.SetWarp("")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WarpService) GetWarpConfig() (string, error) {
|
||||||
|
var warpData map[string]string
|
||||||
|
warp, err := s.SettingService.GetWarp()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal([]byte(warp), &warpData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s", warpData["device_id"])
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "Bearer "+warpData["access_token"])
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
buffer := bytes.NewBuffer(make([]byte, 8192))
|
||||||
|
buffer.Reset()
|
||||||
|
_, err = buffer.ReadFrom(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WarpService) RegWarp(secretKey string, publicKey string) (string, error) {
|
||||||
|
tos := time.Now().UTC().Format("2006-01-02T15:04:05.000Z")
|
||||||
|
hostName, _ := os.Hostname()
|
||||||
|
data := fmt.Sprintf(`{"key":"%s","tos":"%s","type": "PC","model": "x-ui", "name": "%s"}`, publicKey, tos, hostName)
|
||||||
|
|
||||||
|
url := "https://api.cloudflareclient.com/v0a2158/reg"
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(data)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("CF-Client-Version", "a-7.21-0721")
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
buffer := bytes.NewBuffer(make([]byte, 8192))
|
||||||
|
buffer.Reset()
|
||||||
|
_, err = buffer.ReadFrom(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rspData map[string]interface{}
|
||||||
|
err = json.Unmarshal(buffer.Bytes(), &rspData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceId := rspData["id"].(string)
|
||||||
|
token := rspData["token"].(string)
|
||||||
|
license, ok := rspData["account"].(map[string]interface{})["license"].(string)
|
||||||
|
if !ok {
|
||||||
|
logger.Debug("Error accessing license value.")
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
warpData := fmt.Sprintf("{\n \"access_token\": \"%s\",\n \"device_id\": \"%s\",", token, deviceId)
|
||||||
|
warpData += fmt.Sprintf("\n \"license_key\": \"%s\",\n \"private_key\": \"%s\"\n}", license, secretKey)
|
||||||
|
|
||||||
|
s.SettingService.SetWarp(warpData)
|
||||||
|
|
||||||
|
result := fmt.Sprintf("{\n \"data\": %s,\n \"config\": %s\n}", warpData, buffer.String())
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *WarpService) SetWarpLicense(license string) (string, error) {
|
||||||
|
var warpData map[string]string
|
||||||
|
warp, err := s.SettingService.GetWarp()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal([]byte(warp), &warpData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s/account", warpData["device_id"])
|
||||||
|
data := fmt.Sprintf(`{"license": "%s"}`, license)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("PUT", url, bytes.NewBuffer([]byte(data)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "Bearer "+warpData["access_token"])
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
buffer := bytes.NewBuffer(make([]byte, 8192))
|
||||||
|
buffer.Reset()
|
||||||
|
_, err = buffer.ReadFrom(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
warpData["license_key"] = license
|
||||||
|
newWarpData, err := json.MarshalIndent(warpData, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
s.SettingService.SetWarp(string(newWarpData))
|
||||||
|
println(string(newWarpData))
|
||||||
|
|
||||||
|
return string(newWarpData), nil
|
||||||
|
}
|
||||||
@@ -97,7 +97,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
|||||||
if !clientTraffic.Enable {
|
if !clientTraffic.Enable {
|
||||||
clients = RemoveIndex(clients, index-indexDecrease)
|
clients = RemoveIndex(clients, index-indexDecrease)
|
||||||
indexDecrease++
|
indexDecrease++
|
||||||
logger.Info("Remove Inbound User ", c["email"], " due the expire or traffic limit")
|
logger.Infof("Remove Inbound User %s due to expiration or traffic limit", c["email"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,11 +165,20 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
|||||||
|
|
||||||
func (s *XrayService) GetXrayTraffic() ([]*xray.Traffic, []*xray.ClientTraffic, error) {
|
func (s *XrayService) GetXrayTraffic() ([]*xray.Traffic, []*xray.ClientTraffic, error) {
|
||||||
if !s.IsXrayRunning() {
|
if !s.IsXrayRunning() {
|
||||||
return nil, nil, errors.New("xray is not running")
|
err := errors.New("xray is not running")
|
||||||
|
logger.Debug("Attempted to fetch Xray traffic, but Xray is not running:", err)
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
s.xrayAPI.Init(p.GetAPIPort())
|
apiPort := p.GetAPIPort()
|
||||||
|
s.xrayAPI.Init(apiPort)
|
||||||
defer s.xrayAPI.Close()
|
defer s.xrayAPI.Close()
|
||||||
return s.xrayAPI.GetTraffic(true)
|
|
||||||
|
traffic, clientTraffic, err := s.xrayAPI.GetTraffic(true)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug("Failed to fetch Xray traffic:", err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return traffic, clientTraffic, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *XrayService) RestartXray(isForce bool) error {
|
func (s *XrayService) RestartXray(isForce bool) error {
|
||||||
@@ -202,7 +211,7 @@ func (s *XrayService) RestartXray(isForce bool) error {
|
|||||||
func (s *XrayService) StopXray() error {
|
func (s *XrayService) StopXray() error {
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
logger.Debug("stop xray")
|
logger.Debug("Attempting to stop Xray...")
|
||||||
if s.IsXrayRunning() {
|
if s.IsXrayRunning() {
|
||||||
return p.Stop()
|
return p.Stop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
"x-ui/xray"
|
"x-ui/xray"
|
||||||
@@ -32,142 +27,3 @@ func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *XraySettingService) GetWarpData() (string, error) {
|
|
||||||
warp, err := s.SettingService.GetWarp()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return warp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *XraySettingService) GetWarpConfig() (string, error) {
|
|
||||||
var warpData map[string]string
|
|
||||||
warp, err := s.SettingService.GetWarp()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal([]byte(warp), &warpData)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s", warpData["device_id"])
|
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
req.Header.Set("Authorization", "Bearer "+warpData["access_token"])
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
buffer := bytes.NewBuffer(make([]byte, 8192))
|
|
||||||
buffer.Reset()
|
|
||||||
_, err = buffer.ReadFrom(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *XraySettingService) RegWarp(secretKey string, publicKey string) (string, error) {
|
|
||||||
tos := time.Now().UTC().Format("2006-01-02T15:04:05.000Z")
|
|
||||||
hostName, _ := os.Hostname()
|
|
||||||
data := fmt.Sprintf(`{"key":"%s","tos":"%s","type": "PC","model": "x-ui", "name": "%s"}`, publicKey, tos, hostName)
|
|
||||||
|
|
||||||
url := "https://api.cloudflareclient.com/v0a2158/reg"
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(data)))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("CF-Client-Version", "a-7.21-0721")
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
buffer := bytes.NewBuffer(make([]byte, 8192))
|
|
||||||
buffer.Reset()
|
|
||||||
_, err = buffer.ReadFrom(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var rspData map[string]interface{}
|
|
||||||
err = json.Unmarshal(buffer.Bytes(), &rspData)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceId := rspData["id"].(string)
|
|
||||||
token := rspData["token"].(string)
|
|
||||||
license, ok := rspData["account"].(map[string]interface{})["license"].(string)
|
|
||||||
if !ok {
|
|
||||||
fmt.Println("Error accessing license value.")
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
warpData := fmt.Sprintf("{\n \"access_token\": \"%s\",\n \"device_id\": \"%s\",", token, deviceId)
|
|
||||||
warpData += fmt.Sprintf("\n \"license_key\": \"%s\",\n \"private_key\": \"%s\"\n}", license, secretKey)
|
|
||||||
|
|
||||||
s.SettingService.SetWarp(warpData)
|
|
||||||
|
|
||||||
result := fmt.Sprintf("{\n \"data\": %s,\n \"config\": %s\n}", warpData, buffer.String())
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *XraySettingService) SetWarpLicence(license string) (string, error) {
|
|
||||||
var warpData map[string]string
|
|
||||||
warp, err := s.SettingService.GetWarp()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal([]byte(warp), &warpData)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s/account", warpData["device_id"])
|
|
||||||
data := fmt.Sprintf(`{"license": "%s"}`, license)
|
|
||||||
|
|
||||||
req, err := http.NewRequest("PUT", url, bytes.NewBuffer([]byte(data)))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
req.Header.Set("Authorization", "Bearer "+warpData["access_token"])
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
buffer := bytes.NewBuffer(make([]byte, 8192))
|
|
||||||
buffer.Reset()
|
|
||||||
_, err = buffer.ReadFrom(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
warpData["license_key"] = license
|
|
||||||
newWarpData, err := json.MarshalIndent(warpData, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
s.SettingService.SetWarp(string(newWarpData))
|
|
||||||
println(string(newWarpData))
|
|
||||||
|
|
||||||
return string(newWarpData), nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const loginUser = "LOGIN_USER"
|
||||||
loginUser = "LOGIN_USER"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
gob.Register(model.User{})
|
gob.Register(model.User{})
|
||||||
@@ -19,6 +17,10 @@ func init() {
|
|||||||
|
|
||||||
func SetLoginUser(c *gin.Context, user *model.User) error {
|
func SetLoginUser(c *gin.Context, user *model.User) error {
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
|
s.Options(sessions.Options{
|
||||||
|
Path: "/",
|
||||||
|
HttpOnly: true,
|
||||||
|
})
|
||||||
s.Set(loginUser, user)
|
s.Set(loginUser, user)
|
||||||
return s.Save()
|
return s.Save()
|
||||||
}
|
}
|
||||||
@@ -34,24 +36,28 @@ func SetMaxAge(c *gin.Context, maxAge int) error {
|
|||||||
|
|
||||||
func GetLoginUser(c *gin.Context) *model.User {
|
func GetLoginUser(c *gin.Context) *model.User {
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
obj := s.Get(loginUser)
|
if obj := s.Get(loginUser); obj != nil {
|
||||||
if obj == nil {
|
if user, ok := obj.(model.User); ok {
|
||||||
return nil
|
return &user
|
||||||
|
}
|
||||||
}
|
}
|
||||||
user := obj.(model.User)
|
return nil
|
||||||
return &user
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsLogin(c *gin.Context) bool {
|
func IsLogin(c *gin.Context) bool {
|
||||||
return GetLoginUser(c) != nil
|
return GetLoginUser(c) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClearSession(c *gin.Context) {
|
func ClearSession(c *gin.Context) error {
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
s.Clear()
|
s.Clear()
|
||||||
s.Options(sessions.Options{
|
s.Options(sessions.Options{
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: -1,
|
MaxAge: -1,
|
||||||
})
|
})
|
||||||
s.Save()
|
if err := s.Save(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.SetCookie("3x-ui", "", -1, "/", "", false, true)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
"monitor" = "Listen IP"
|
"monitor" = "Listen IP"
|
||||||
"certificate" = "Digital Certificate"
|
"certificate" = "Digital Certificate"
|
||||||
"fail" = " Failed"
|
"fail" = " Failed"
|
||||||
"success" = " Successful"
|
"success" = " Successfully"
|
||||||
"getVersion" = "Get Version"
|
"getVersion" = "Get Version"
|
||||||
"install" = "Install"
|
"install" = "Install"
|
||||||
"clients" = "Clients"
|
"clients" = "Clients"
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
"invalidFormData" = "The Input data format is invalid."
|
"invalidFormData" = "The Input data format is invalid."
|
||||||
"emptyUsername" = "Username is required"
|
"emptyUsername" = "Username is required"
|
||||||
"emptyPassword" = "Password is required"
|
"emptyPassword" = "Password is required"
|
||||||
"wrongUsernameOrPassword" = "Invalid username or password."
|
"wrongUsernameOrPassword" = "Invalid username or password or secret."
|
||||||
"successLogin" = "Login"
|
"successLogin" = "Login"
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
@@ -544,7 +544,7 @@
|
|||||||
"selectUserFailed" = "❌ Error in user selection!"
|
"selectUserFailed" = "❌ Error in user selection!"
|
||||||
"userSaved" = "✅ Telegram User saved."
|
"userSaved" = "✅ Telegram User saved."
|
||||||
"loginSuccess" = "✅ Logged in to the panel successfully.\r\n"
|
"loginSuccess" = "✅ Logged in to the panel successfully.\r\n"
|
||||||
"loginFailed" = "❗️ Log in to the panel failed.\r\n"
|
"loginFailed" = "❗️Login attempt to the panel failed.\r\n"
|
||||||
"report" = "🕰 Scheduled Reports: {{ .RunTime }}\r\n"
|
"report" = "🕰 Scheduled Reports: {{ .RunTime }}\r\n"
|
||||||
"datetime" = "⏰ Date&Time: {{ .DateTime }}\r\n"
|
"datetime" = "⏰ Date&Time: {{ .DateTime }}\r\n"
|
||||||
"hostname" = "💻 Host: {{ .Hostname }}\r\n"
|
"hostname" = "💻 Host: {{ .Hostname }}\r\n"
|
||||||
@@ -562,6 +562,7 @@
|
|||||||
"traffic" = "🚦 Traffic: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 Traffic: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ Status: {{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ Status: {{ .State }}\r\n"
|
||||||
"username" = "👤 Username: {{ .Username }}\r\n"
|
"username" = "👤 Username: {{ .Username }}\r\n"
|
||||||
|
"password" = "👤 Password: {{ .Password }}\r\n"
|
||||||
"time" = "⏰ Time: {{ .Time }}\r\n"
|
"time" = "⏰ Time: {{ .Time }}\r\n"
|
||||||
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
||||||
"port" = "🔌 Port: {{ .Port }}\r\n"
|
"port" = "🔌 Port: {{ .Port }}\r\n"
|
||||||
|
|||||||
@@ -560,6 +560,7 @@
|
|||||||
"traffic" = "🚦 Tráfico: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 Tráfico: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ Estado de Xray: {{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ Estado de Xray: {{ .State }}\r\n"
|
||||||
"username" = "👤 Nombre de usuario: {{ .Username }}\r\n"
|
"username" = "👤 Nombre de usuario: {{ .Username }}\r\n"
|
||||||
|
"password" = "👤 Contraseña: {{ .Password }}\r\n"
|
||||||
"time" = "⏰ Hora: {{ .Time }}\r\n"
|
"time" = "⏰ Hora: {{ .Time }}\r\n"
|
||||||
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
||||||
"port" = "🔌 Puerto: {{ .Port }}\r\n"
|
"port" = "🔌 Puerto: {{ .Port }}\r\n"
|
||||||
|
|||||||
@@ -562,6 +562,7 @@
|
|||||||
"traffic" = "🚦 ترافیک: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 ترافیک: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ وضعیتایکسری: {{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ وضعیتایکسری: {{ .State }}\r\n"
|
||||||
"username" = "👤 نامکاربری: {{ .Username }}\r\n"
|
"username" = "👤 نامکاربری: {{ .Username }}\r\n"
|
||||||
|
"password" = "👤 رمز عبور: {{ .Password }}\r\n"
|
||||||
"time" = "⏰ زمان: {{ .Time }}\r\n"
|
"time" = "⏰ زمان: {{ .Time }}\r\n"
|
||||||
"inbound" = "📍 نامورودی: {{ .Remark }}\r\n"
|
"inbound" = "📍 نامورودی: {{ .Remark }}\r\n"
|
||||||
"port" = "🔌 پورت: {{ .Port }}\r\n"
|
"port" = "🔌 پورت: {{ .Port }}\r\n"
|
||||||
|
|||||||
@@ -562,6 +562,7 @@
|
|||||||
"traffic" = "🚦 Lalu Lintas: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 Lalu Lintas: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ Status: {{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ Status: {{ .State }}\r\n"
|
||||||
"username" = "👤 Nama Pengguna: {{ .Username }}\r\n"
|
"username" = "👤 Nama Pengguna: {{ .Username }}\r\n"
|
||||||
|
"password" = "👤 Kata Sandi: {{ .Password }}\r\n"
|
||||||
"time" = "⏰ Waktu: {{ .Time }}\r\n"
|
"time" = "⏰ Waktu: {{ .Time }}\r\n"
|
||||||
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
||||||
"port" = "🔌 Port: {{ .Port }}\r\n"
|
"port" = "🔌 Port: {{ .Port }}\r\n"
|
||||||
|
|||||||
@@ -562,6 +562,7 @@
|
|||||||
"traffic" = "🚦 Трафик: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 Трафик: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ Состояние Xray: {{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ Состояние Xray: {{ .State }}\r\n"
|
||||||
"username" = "👤 Имя пользователя: {{ .Username }}\r\n"
|
"username" = "👤 Имя пользователя: {{ .Username }}\r\n"
|
||||||
|
"password" = "👤 Пароль: {{ .Password }}\r\n"
|
||||||
"time" = "⏰ Время: {{ .Time }}\r\n"
|
"time" = "⏰ Время: {{ .Time }}\r\n"
|
||||||
"inbound" = "📍 Входящий поток: {{ .Remark }}\r\n"
|
"inbound" = "📍 Входящий поток: {{ .Remark }}\r\n"
|
||||||
"port" = "🔌 Порт: {{ .Port }}\r\n"
|
"port" = "🔌 Порт: {{ .Port }}\r\n"
|
||||||
|
|||||||
@@ -562,6 +562,7 @@
|
|||||||
"traffic" = "🚦 Трафік: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 Трафік: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ Статус: {{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ Статус: {{ .State }}\r\n"
|
||||||
"username" = "👤 Ім'я користувача: {{ .Username }}\r\n"
|
"username" = "👤 Ім'я користувача: {{ .Username }}\r\n"
|
||||||
|
"password" = "👤 Пароль: {{ .Password }}\r\n"
|
||||||
"time" = "⏰ Час: {{ .Time }}\r\n"
|
"time" = "⏰ Час: {{ .Time }}\r\n"
|
||||||
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
||||||
"port" = "🔌 Порт: {{ .Port }}\r\n"
|
"port" = "🔌 Порт: {{ .Port }}\r\n"
|
||||||
|
|||||||
@@ -562,6 +562,7 @@
|
|||||||
"traffic" = "🚦 Lưu lượng: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 Lưu lượng: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ Trạng thái Xray: {{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ Trạng thái Xray: {{ .State }}\r\n"
|
||||||
"username" = "👤 Tên người dùng: {{ .Username }}\r\n"
|
"username" = "👤 Tên người dùng: {{ .Username }}\r\n"
|
||||||
|
"password" = "👤 Mật khẩu: {{ .Password }}\r\n"
|
||||||
"time" = "⏰ Thời gian: {{ .Time }}\r\n"
|
"time" = "⏰ Thời gian: {{ .Time }}\r\n"
|
||||||
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
||||||
"port" = "🔌 Cổng: {{ .Port }}\r\n"
|
"port" = "🔌 Cổng: {{ .Port }}\r\n"
|
||||||
|
|||||||
@@ -562,6 +562,7 @@
|
|||||||
"traffic" = "🚦 流量:{{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 流量:{{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ Xray 状态:{{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ Xray 状态:{{ .State }}\r\n"
|
||||||
"username" = "👤 用户名:{{ .Username }}\r\n"
|
"username" = "👤 用户名:{{ .Username }}\r\n"
|
||||||
|
"password" = "👤 密码: {{ .Password }}\r\n"
|
||||||
"time" = "⏰ 时间:{{ .Time }}\r\n"
|
"time" = "⏰ 时间:{{ .Time }}\r\n"
|
||||||
"inbound" = "📍 入站:{{ .Remark }}\r\n"
|
"inbound" = "📍 入站:{{ .Remark }}\r\n"
|
||||||
"port" = "🔌 端口:{{ .Port }}\r\n"
|
"port" = "🔌 端口:{{ .Port }}\r\n"
|
||||||
|
|||||||
14
web/web.go
14
web/web.go
@@ -180,7 +180,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
|||||||
assetsBasePath := basePath + "assets/"
|
assetsBasePath := basePath + "assets/"
|
||||||
|
|
||||||
store := cookie.NewStore(secret)
|
store := cookie.NewStore(secret)
|
||||||
engine.Use(sessions.Sessions("session", store))
|
engine.Use(sessions.Sessions("3x-ui", store))
|
||||||
engine.Use(func(c *gin.Context) {
|
engine.Use(func(c *gin.Context) {
|
||||||
c.Set("base_path", basePath)
|
c.Set("base_path", basePath)
|
||||||
})
|
})
|
||||||
@@ -268,7 +268,7 @@ func (s *Server) startTask() {
|
|||||||
|
|
||||||
// Make a traffic condition every day, 8:30
|
// Make a traffic condition every day, 8:30
|
||||||
var entry cron.EntryID
|
var entry cron.EntryID
|
||||||
isTgbotenabled, err := s.settingService.GetTgbotenabled()
|
isTgbotenabled, err := s.settingService.GetTgbotEnabled()
|
||||||
if (err == nil) && (isTgbotenabled) {
|
if (err == nil) && (isTgbotenabled) {
|
||||||
runtime, err := s.settingService.GetTgbotRuntime()
|
runtime, err := s.settingService.GetTgbotRuntime()
|
||||||
if err != nil || runtime == "" {
|
if err != nil || runtime == "" {
|
||||||
@@ -344,13 +344,13 @@ func (s *Server) Start() (err error) {
|
|||||||
}
|
}
|
||||||
listener = network.NewAutoHttpsListener(listener)
|
listener = network.NewAutoHttpsListener(listener)
|
||||||
listener = tls.NewListener(listener, c)
|
listener = tls.NewListener(listener, c)
|
||||||
logger.Info("web server run https on", listener.Addr())
|
logger.Info("Web server running HTTPS on", listener.Addr())
|
||||||
} else {
|
} else {
|
||||||
logger.Error("error in loading certificates: ", err)
|
logger.Error("Error loading certificates:", err)
|
||||||
logger.Info("web server run http on", listener.Addr())
|
logger.Info("Web server running HTTP on", listener.Addr())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.Info("web server run http on", listener.Addr())
|
logger.Info("Web server running HTTP on", listener.Addr())
|
||||||
}
|
}
|
||||||
s.listener = listener
|
s.listener = listener
|
||||||
|
|
||||||
@@ -364,7 +364,7 @@ func (s *Server) Start() (err error) {
|
|||||||
|
|
||||||
s.startTask()
|
s.startTask()
|
||||||
|
|
||||||
isTgbotenabled, err := s.settingService.GetTgbotenabled()
|
isTgbotenabled, err := s.settingService.GetTgbotEnabled()
|
||||||
if (err == nil) && (isTgbotenabled) {
|
if (err == nil) && (isTgbotenabled) {
|
||||||
tgBot := s.tgbotService.NewTgbot()
|
tgBot := s.tgbotService.NewTgbot()
|
||||||
tgBot.Start(i18nFS)
|
tgBot.Start(i18nFS)
|
||||||
|
|||||||
5
x-ui.sh
5
x-ui.sh
@@ -262,10 +262,9 @@ reset_webbasepath() {
|
|||||||
echo -e "${yellow}Resetting Web Base Path${plain}"
|
echo -e "${yellow}Resetting Web Base Path${plain}"
|
||||||
|
|
||||||
# Prompt user to set a new web base path
|
# Prompt user to set a new web base path
|
||||||
read -rp "Please set the new web base path [default is a random path]: " config_webBasePath
|
read -rp "Please set the new web base path [press 'y' for a random path]: " config_webBasePath
|
||||||
|
|
||||||
# If user input is empty, generate a random path
|
if [[ $config_webBasePath == "y" ]]; then
|
||||||
if [[ -z $config_webBasePath ]]; then
|
|
||||||
config_webBasePath=$(gen_random_string 10)
|
config_webBasePath=$(gen_random_string 10)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
174
xray/api.go
174
xray/api.go
@@ -31,24 +31,27 @@ type XrayAPI struct {
|
|||||||
isConnected bool
|
isConnected bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XrayAPI) Init(apiPort int) (err error) {
|
func (x *XrayAPI) Init(apiPort int) error {
|
||||||
if apiPort == 0 {
|
if apiPort <= 0 {
|
||||||
return common.NewError("xray api port wrong:", apiPort)
|
return fmt.Errorf("invalid Xray API port: %d", apiPort)
|
||||||
}
|
}
|
||||||
conn, err := grpc.NewClient(fmt.Sprintf("127.0.0.1:%v", apiPort), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
||||||
|
addr := fmt.Sprintf("127.0.0.1:%d", apiPort)
|
||||||
|
conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to connect to Xray API: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
x.grpcClient = conn
|
x.grpcClient = conn
|
||||||
x.isConnected = true
|
x.isConnected = true
|
||||||
|
|
||||||
hsClient := command.NewHandlerServiceClient(x.grpcClient)
|
hsClient := command.NewHandlerServiceClient(conn)
|
||||||
ssClient := statsService.NewStatsServiceClient(x.grpcClient)
|
ssClient := statsService.NewStatsServiceClient(conn)
|
||||||
|
|
||||||
x.HandlerServiceClient = &hsClient
|
x.HandlerServiceClient = &hsClient
|
||||||
x.StatsServiceClient = &ssClient
|
x.StatsServiceClient = &ssClient
|
||||||
|
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XrayAPI) Close() {
|
func (x *XrayAPI) Close() {
|
||||||
@@ -149,94 +152,101 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]in
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XrayAPI) RemoveUser(inboundTag string, email string) error {
|
func (x *XrayAPI) RemoveUser(inboundTag, email string) error {
|
||||||
client := *x.HandlerServiceClient
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
_, err := client.AlterInbound(context.Background(), &command.AlterInboundRequest{
|
defer cancel()
|
||||||
Tag: inboundTag,
|
|
||||||
Operation: serial.ToTypedMessage(&command.RemoveUserOperation{
|
op := &command.RemoveUserOperation{Email: email}
|
||||||
Email: email,
|
req := &command.AlterInboundRequest{
|
||||||
}),
|
Tag: inboundTag,
|
||||||
})
|
Operation: serial.ToTypedMessage(op),
|
||||||
return err
|
}
|
||||||
|
|
||||||
|
_, err := (*x.HandlerServiceClient).AlterInbound(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to remove user: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XrayAPI) GetTraffic(reset bool) ([]*Traffic, []*ClientTraffic, error) {
|
func (x *XrayAPI) GetTraffic(reset bool) ([]*Traffic, []*ClientTraffic, error) {
|
||||||
if x.grpcClient == nil {
|
if x.grpcClient == nil {
|
||||||
return nil, nil, common.NewError("xray api is not initialized")
|
return nil, nil, common.NewError("xray api is not initialized")
|
||||||
}
|
}
|
||||||
trafficRegex := regexp.MustCompile("(inbound|outbound)>>>([^>]+)>>>traffic>>>(downlink|uplink)")
|
|
||||||
ClientTrafficRegex := regexp.MustCompile("(user)>>>([^>]+)>>>traffic>>>(downlink|uplink)")
|
|
||||||
|
|
||||||
client := *x.StatsServiceClient
|
trafficRegex := regexp.MustCompile(`(inbound|outbound)>>>([^>]+)>>>traffic>>>(downlink|uplink)`)
|
||||||
|
clientTrafficRegex := regexp.MustCompile(`user>>>([^>]+)>>>traffic>>>(downlink|uplink)`)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
request := &statsService.QueryStatsRequest{
|
|
||||||
Reset_: reset,
|
resp, err := (*x.StatsServiceClient).QueryStats(ctx, &statsService.QueryStatsRequest{Reset_: reset})
|
||||||
}
|
|
||||||
resp, err := client.QueryStats(ctx, request)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logger.Debug("Failed to query Xray stats:", err)
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
tagTrafficMap := map[string]*Traffic{}
|
|
||||||
emailTrafficMap := map[string]*ClientTraffic{}
|
|
||||||
|
|
||||||
clientTraffics := make([]*ClientTraffic, 0)
|
tagTrafficMap := make(map[string]*Traffic)
|
||||||
traffics := make([]*Traffic, 0)
|
emailTrafficMap := make(map[string]*ClientTraffic)
|
||||||
|
|
||||||
for _, stat := range resp.GetStat() {
|
for _, stat := range resp.GetStat() {
|
||||||
matchs := trafficRegex.FindStringSubmatch(stat.Name)
|
if matches := trafficRegex.FindStringSubmatch(stat.Name); len(matches) == 4 {
|
||||||
if len(matchs) < 3 {
|
processTraffic(matches, stat.Value, tagTrafficMap)
|
||||||
|
} else if matches := clientTrafficRegex.FindStringSubmatch(stat.Name); len(matches) == 3 {
|
||||||
matchs := ClientTrafficRegex.FindStringSubmatch(stat.Name)
|
processClientTraffic(matches, stat.Value, emailTrafficMap)
|
||||||
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"
|
|
||||||
isOutbound := matchs[1] == "outbound"
|
|
||||||
tag := matchs[2]
|
|
||||||
isDown := matchs[3] == "downlink"
|
|
||||||
if tag == "api" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
traffic, ok := tagTrafficMap[tag]
|
|
||||||
if !ok {
|
|
||||||
traffic = &Traffic{
|
|
||||||
IsInbound: isInbound,
|
|
||||||
IsOutbound: isOutbound,
|
|
||||||
Tag: tag,
|
|
||||||
}
|
|
||||||
tagTrafficMap[tag] = traffic
|
|
||||||
traffics = append(traffics, traffic)
|
|
||||||
}
|
|
||||||
if isDown {
|
|
||||||
traffic.Down = stat.Value
|
|
||||||
} else {
|
|
||||||
traffic.Up = stat.Value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return mapToSlice(tagTrafficMap), mapToSlice(emailTrafficMap), nil
|
||||||
return traffics, clientTraffics, nil
|
}
|
||||||
|
|
||||||
|
func processTraffic(matches []string, value int64, trafficMap map[string]*Traffic) {
|
||||||
|
isInbound := matches[1] == "inbound"
|
||||||
|
tag := matches[2]
|
||||||
|
isDown := matches[3] == "downlink"
|
||||||
|
|
||||||
|
if tag == "api" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traffic, ok := trafficMap[tag]
|
||||||
|
if !ok {
|
||||||
|
traffic = &Traffic{
|
||||||
|
IsInbound: isInbound,
|
||||||
|
IsOutbound: !isInbound,
|
||||||
|
Tag: tag,
|
||||||
|
}
|
||||||
|
trafficMap[tag] = traffic
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDown {
|
||||||
|
traffic.Down = value
|
||||||
|
} else {
|
||||||
|
traffic.Up = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processClientTraffic(matches []string, value int64, clientTrafficMap map[string]*ClientTraffic) {
|
||||||
|
email := matches[1]
|
||||||
|
isDown := matches[2] == "downlink"
|
||||||
|
|
||||||
|
traffic, ok := clientTrafficMap[email]
|
||||||
|
if !ok {
|
||||||
|
traffic = &ClientTraffic{Email: email}
|
||||||
|
clientTrafficMap[email] = traffic
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDown {
|
||||||
|
traffic.Down = value
|
||||||
|
} else {
|
||||||
|
traffic.Up = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapToSlice[T any](m map[string]*T) []*T {
|
||||||
|
result := make([]*T, 0, len(m))
|
||||||
|
for _, v := range m {
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,14 +60,14 @@ func GetAccessPersistentPrevLogPath() string {
|
|||||||
func GetAccessLogPath() (string, error) {
|
func GetAccessLogPath() (string, error) {
|
||||||
config, err := os.ReadFile(GetConfigPath())
|
config, err := os.ReadFile(GetConfigPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warningf("Something went wrong: %s", err)
|
logger.Warningf("Failed to read configuration file: %s", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonConfig := map[string]interface{}{}
|
jsonConfig := map[string]interface{}{}
|
||||||
err = json.Unmarshal([]byte(config), &jsonConfig)
|
err = json.Unmarshal([]byte(config), &jsonConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warningf("Something went wrong: %s", err)
|
logger.Warningf("Failed to parse JSON configuration: %s", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ func (p *process) Start() (err error) {
|
|||||||
|
|
||||||
err = os.MkdirAll(config.GetLogFolder(), 0o770)
|
err = os.MkdirAll(config.GetLogFolder(), 0o770)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warningf("Something went wrong: %s", err)
|
logger.Warningf("Failed to create log folder: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
configPath := GetConfigPath()
|
configPath := GetConfigPath()
|
||||||
@@ -224,7 +224,7 @@ func (p *process) Start() (err error) {
|
|||||||
go func() {
|
go func() {
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failure in running xray-core: ", err)
|
logger.Error("Failure in running xray-core:", err)
|
||||||
p.exitErr = err
|
p.exitErr = err
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
Reference in New Issue
Block a user