Compare commits

...

31 Commits

Author SHA1 Message Date
mhsanaei
4cd89f4379 v2.3.11 2024-07-29 13:17:55 +02:00
mhsanaei
fdfc29f6cd new - splithttp (noSSEHeader) 2024-07-29 13:16:07 +02:00
mhsanaei
4ec104c5ee new - splithttp (scMinPostsIntervalMs) 2024-07-29 13:05:05 +02:00
mhsanaei
bae89272b0 Xray Core v1.8.23 2024-07-29 12:33:46 +02:00
mhsanaei
8408a45eff update dependencies 2024-07-27 14:56:37 +02:00
mhsanaei
a37b1bde4c update pictures 2024-07-24 13:33:36 +02:00
mhsanaei
953b5d3dea buy me a coffee 2024-07-24 12:47:35 +02:00
mhsanaei
c7906e8598 API - Get client's traffic By ID
Co-Authored-By: Hassan Ali Gilani <mr.ajaxian@gmail.com>
2024-07-23 11:28:28 +02:00
yeer
e1bc43da5f fix bug for nil pointer (#2438) 2024-07-23 11:11:28 +02:00
mhsanaei
0630642849 v2.3.10 2024-07-22 00:14:48 +02:00
mhsanaei
8bd3827b41 Xray Core v1.8.21 2024-07-22 00:12:58 +02:00
mhsanaei
24b367b82f fix 2024-07-18 23:03:48 +02:00
mhsanaei
011443bfc1 update donate wallet 2024-07-17 18:13:12 +02:00
mhsanaei
7417c52c8f fixed - sub show time when "Start After First Use" 2024-07-17 16:39:53 +02:00
mhsanaei
4d4eef8d8a Xray Core v1.8.19 2024-07-17 16:21:03 +02:00
mhsanaei
9de8b4acaf v2.3.9 2024-07-17 13:51:11 +02:00
mhsanaei
f21c293693 Xray core v1.8.18 2024-07-16 01:33:24 +02:00
mhsanaei
56159d9c52 Upload files to Artifacts 2024-07-15 11:09:24 +02:00
mhsanaei
6cbdf64013 update dependencies 2024-07-15 10:28:39 +02:00
mhsanaei
bb9b9100a8 [warp] enhanced + delete option
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-07-15 00:21:54 +02:00
mhsanaei
816adfc3ea fallback outbound in balancer
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-07-15 00:09:31 +02:00
mhsanaei
0a95b0c7b2 disable mux for vision flow
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-07-14 23:55:56 +02:00
mhsanaei
d298f4ffbd fix domain validator
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-07-14 23:55:04 +02:00
mhsanaei
315e8af025 fix observatory data
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-07-14 23:44:31 +02:00
mhsanaei
de985263f5 safe login
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-07-14 23:37:43 +02:00
mhsanaei
dfe0bbd371 Refactor database initialization 2024-07-14 01:22:51 +02:00
mhsanaei
60cb328698 default - alpn h3, utls random 2024-07-14 00:03:07 +02:00
mhsanaei
3d7f13225a Refactor HttpUtil and Msg: optimize methods 2024-07-14 00:01:13 +02:00
mhsanaei
76fdfb2ef2 date format - jalalian 2024-07-13 01:38:51 +02:00
mhsanaei
8018023187 Xray core v1.8.17 + GO v1.22.5 2024-07-12 21:42:32 +02:00
mmmray
ea9d5dc2d5 fix wrong splithttp default (#2433)
This default is defined as 1MB, but maxUploadSize is to be specified in
bytes. This confusion could've come from poorly written documentation in
xray, but it has been updated.

in general I wish that panels would not set defaults at all and instead
just omit parameters (in sharelinks, inbounds, ...) that the user didn't
set explicitly. If I want to change the defaults in xray's codebase, it
seems that all the panels will have to update the default too.

I see marzban doing the same kind of things.
2024-07-09 15:24:06 +02:00
45 changed files with 696 additions and 476 deletions

14
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: mhsanaei
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -26,7 +26,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
go-version-file: go.mod
- name: Install dependencies
run: |
@@ -83,7 +83,7 @@ jobs:
cd x-ui/bin
# 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.23/"
if [ "${{ matrix.platform }}" == "amd64" ]; then
wget ${Xray_URL}Xray-linux-64.zip
unzip Xray-linux-64.zip
@@ -125,7 +125,13 @@ jobs:
- name: Package
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
uses: svenstaro/upload-release-action@v2
with:

View File

@@ -27,7 +27,7 @@ case $1 in
esac
mkdir -p 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.23/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
mv xray "xray-linux-${FNAME}"

View File

@@ -14,9 +14,15 @@
**Si este proyecto te es útil, podrías considerar darle una**:star2:
<p align="left"><a href="#"><img width="125" src="https://github.com/MHSanaei/3x-ui/assets/115543613/7aa895dd-048a-42e7-989b-afd41a74e2e1" alt="Image"></a></p>
<p align="left">
<a href="https://buymeacoffee.com/mhsanaei" target="_blank">
<img src="./media/buymeacoffe.png" alt="Image">
</a>
</p>
- USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC`
- MATIC (polygon): `0x41C9548675D044c6Bfb425786C765bc37427256A`
- LTC (Litecoin): `ltc1q2ach7x6d2zq0n4l0t4zl7d7xe2s6fs7a3vspwv`
## Instalar y Actualizar

View File

@@ -14,9 +14,15 @@
**If this project is helpful to you, you may wish to give it a**:star2:
<p align="left"><a href="#"><img width="125" src="https://github.com/MHSanaei/3x-ui/assets/115543613/7aa895dd-048a-42e7-989b-afd41a74e2e1" alt="Image"></a></p>
<p align="left">
<a href="https://buymeacoffee.com/mhsanaei" target="_blank">
<img src="./media/buymeacoffe.png" alt="Image">
</a>
</p>
- USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC`
- MATIC (polygon): `0x41C9548675D044c6Bfb425786C765bc37427256A`
- LTC (Litecoin): `ltc1q2ach7x6d2zq0n4l0t4zl7d7xe2s6fs7a3vspwv`
## Install & Upgrade
@@ -26,10 +32,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## 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.11`:
```
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.11
```
## SSL Certificate
@@ -453,6 +459,7 @@ Enter the user ID in input field number 4. The Telegram accounts with this id wi
| `GET` | `"/list"` | Get all inbounds |
| `GET` | `"/get/:id"` | Get inbound with inbound.id |
| `GET` | `"/getClientTraffics/:email"` | Get Client Traffics with email |
| `GET` | `"/getClientTrafficsById/:id"` | Get client's traffic By ID |
| `GET` | `"/createbackup"` | Telegram bot sends backup to admins |
| `POST` | `"/add"` | Add inbound |
| `POST` | `"/del/:id"` | Delete Inbound |

View File

@@ -14,9 +14,15 @@
**如果此项目对你有用,请给一个**:star2:
<p align="left"><a href="#"><img width="125" src="https://github.com/MHSanaei/3x-ui/assets/115543613/7aa895dd-048a-42e7-989b-afd41a74e2e1" alt="Image"></a></p>
<p align="left">
<a href="https://buymeacoffee.com/mhsanaei" target="_blank">
<img src="./media/buymeacoffe.png" alt="Image">
</a>
</p>
- USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC`
- MATIC (polygon): `0x41C9548675D044c6Bfb425786C765bc37427256A`
- LTC (Litecoin): `ltc1q2ach7x6d2zq0n4l0t4zl7d7xe2s6fs7a3vspwv`
## 安装 & 升级

View File

@@ -1 +1 @@
2.3.8
2.3.11

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"io"
"io/fs"
"log"
"os"
"path"
@@ -18,54 +19,51 @@ import (
var db *gorm.DB
var initializers = []func() error{
initUser,
initInbound,
initOutbound,
initSetting,
initInboundClientIps,
initClientTraffic,
const (
defaultUsername = "admin"
defaultPassword = "admin"
defaultSecret = ""
)
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 {
err := db.AutoMigrate(&model.User{})
empty, err := isTableEmpty("users")
if err != nil {
log.Printf("Error checking if users table is empty: %v", err)
return err
}
var count int64
err = db.Model(&model.User{}).Count(&count).Error
if err != nil {
return err
}
if count == 0 {
if empty {
user := &model.User{
Username: "admin",
Password: "admin",
LoginSecret: "",
Username: defaultUsername,
Password: defaultPassword,
LoginSecret: defaultSecret,
}
return db.Create(user).Error
}
return nil
}
func initInbound() error {
return db.AutoMigrate(&model.Inbound{})
}
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 isTableEmpty(tableName string) (bool, error) {
var count int64
err := db.Table(tableName).Count(&count).Error
return count == 0, err
}
func InitDB(dbPath string) error {
@@ -91,15 +89,27 @@ func InitDB(dbPath string) error {
return err
}
for _, initialize := range initializers {
if err := initialize(); err != nil {
return err
}
if err := initModels(); err != nil {
return err
}
if err := initUser(); err != nil {
return err
}
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 {
return db
}

46
go.mod
View File

@@ -1,6 +1,6 @@
module x-ui
go 1.22.4
go 1.22.5
require (
github.com/gin-contrib/gzip v1.0.1
@@ -14,25 +14,25 @@ require (
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v4 v4.24.6
github.com/valyala/fasthttp v1.55.0
github.com/xtls/xray-core v1.8.16
github.com/xtls/xray-core v1.8.23
go.uber.org/atomic v1.11.0
golang.org/x/text v0.16.0
google.golang.org/grpc v1.65.0
gorm.io/driver/sqlite v1.5.6
gorm.io/gorm v1.25.10
gorm.io/gorm v1.25.11
)
require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/bytedance/sonic v1.11.9 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cloudflare/circl v1.3.9 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/fasthttp/router v1.5.2 // 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.5 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
@@ -40,7 +40,7 @@ require (
github.com/go-playground/validator/v10 v10.22.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // 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-20240727154555-813a5fbdbec8 // indirect
github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.2.2 // indirect
@@ -52,25 +52,26 @@ require (
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // 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-sqlite3 v1.14.22 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 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.1 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/quic-go/quic-go v0.45.0 // indirect
github.com/refraction-networking/utls v1.6.6 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/quic-go/qpack v0.4.0 // 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/rogpeppe/go-internal v1.11.0 // indirect
github.com/sagernet/sing v0.4.1 // indirect
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/sagernet/sing v0.4.2 // indirect
github.com/sagernet/sing-shadowsocks v0.2.7 // 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/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // 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/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // 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
go.uber.org/mock v0.4.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.27.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/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/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-20240725223205-93522f1f2a9f // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect

95
go.sum
View File

@@ -20,8 +20,9 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bytedance/sonic v1.11.9 h1:LFHENlIY/SLzDWverzdOvgMztTxcfcF+cqNsz9pK5zg=
github.com/bytedance/sonic v1.11.9/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
@@ -43,8 +44,8 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
@@ -58,8 +59,8 @@ github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
@@ -97,8 +98,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/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-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/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 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -139,8 +140,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/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/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0=
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
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/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -163,10 +164,10 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
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/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0=
github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA=
github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os=
github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
@@ -179,31 +180,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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig=
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 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
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_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/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/quic-go v0.45.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig=
github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.45.1 h1:tPfeYCk+uZHjmDRwHHQmvHRYL2t44ROTujLeFVBmjCA=
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/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
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/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-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
github.com/sagernet/sing v0.4.2 h1:jzGNJdZVRI0xlAfFugsIQUPvyB9SuWvbJK7zQCXc4QM=
github.com/sagernet/sing v0.4.2/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
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-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
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/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=
@@ -250,10 +253,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/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
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/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
@@ -273,10 +276,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.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
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-20240429224917-ecc4401070cc/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
github.com/xtls/xray-core v1.8.16 h1:PhbpdREAIvDS7xmxR6Sdpkx0h5ugmf6wIoWECWtJ0kE=
github.com/xtls/xray-core v1.8.16/go.mod h1:tjzDQQJpFORuhf7fBsiswiexLVEeJpAfMsD0NE5xV7M=
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d h1:+B97uD9uHLgAAulhigmys4BVwZZypzK7gPN3WtpgRJg=
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
github.com/xtls/xray-core v1.8.23 h1:A8Wr50ildMYLpaNu3EiK+Stg/tps6i0h7z5Hr4f9H2k=
github.com/xtls/xray-core v1.8.23/go.mod h1:0CwyMPNA5Cs+ukPXHbYQGgne/ug0PuXOSVqBu7zyXOc=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
@@ -297,13 +300,13 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
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-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/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-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
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.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -337,8 +340,6 @@ 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.5.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.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -353,8 +354,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-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.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
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/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
@@ -371,8 +372,8 @@ 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-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/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
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-20240725223205-93522f1f2a9f h1:RARaIm8pxYuxyNPbBQf5igT7XdOyCNtat1qAT2ZxjU4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240725223205-93522f1f2a9f/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
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.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
@@ -395,8 +396,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
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.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
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/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=

View File

@@ -123,7 +123,7 @@ install_base() {
gen_random_string() {
local length="$1"
local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w "$length" | head -n 1)
local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' </dev/urandom | fold -w "$length" | head -n 1)
echo "$random_string"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

After

Width:  |  Height:  |  Size: 60 KiB

BIN
media/buymeacoffe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -1023,10 +1023,9 @@ func (s *SubService) genRemark(inbound *model.Inbound, email string, extra strin
remark = append(remark, fmt.Sprintf("%dM⏳", minutes))
}
case exp < 0:
passedSeconds := now - exp
days := passedSeconds / 86400
hours := (passedSeconds % 86400) / 3600
minutes := (passedSeconds % 3600) / 60
days := exp / -86400
hours := (exp % -86400) / 3600
minutes := (exp % -3600) / 60
if days > 0 {
if hours > 0 {
remark = append(remark, fmt.Sprintf("%dD,%dH⏳", days, hours))

View File

@@ -577,6 +577,10 @@ class Outbound extends CommonClass {
}
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);
}

View File

@@ -523,13 +523,23 @@ class HTTPUpgradeStreamSettings extends XrayCommonClass {
}
class SplitHTTPStreamSettings extends XrayCommonClass {
constructor(path='/', host='', headers=[] , maxUploadSize= 1, maxConcurrentUploads= 10) {
constructor(
path = '/',
host = '',
headers = [],
scMaxConcurrentPosts = 100,
scMaxEachPostBytes = 1000000,
scMinPostsIntervalMs = 30,
noSSEHeader = false,
) {
super();
this.path = path;
this.host = host;
this.headers = headers;
this.maxUploadSize = maxUploadSize;
this.maxConcurrentUploads = maxConcurrentUploads;
this.scMaxConcurrentPosts = scMaxConcurrentPosts;
this.scMaxEachPostBytes = scMaxEachPostBytes;
this.scMinPostsIntervalMs = scMinPostsIntervalMs;
this.noSSEHeader = noSSEHeader;
}
addHeader(name, value) {
@@ -540,13 +550,15 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
this.headers.splice(index, 1);
}
static fromJson(json={}) {
static fromJson(json = {}) {
return new SplitHTTPStreamSettings(
json.path,
json.host,
XrayCommonClass.toHeaders(json.headers),
json.maxUploadSize,
json.maxConcurrentUploads,
json.scMaxConcurrentPosts,
json.scMaxEachPostBytes,
json.scMinPostsIntervalMs,
json.noSSEHeader,
);
}
@@ -555,8 +567,10 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
path: this.path,
host: this.host,
headers: XrayCommonClass.toV2Headers(this.headers, false),
maxUploadSize: this.maxUploadSize,
maxConcurrentUploads: this.maxConcurrentUploads,
scMaxConcurrentPosts: this.scMaxConcurrentPosts,
scMaxEachPostBytes: this.scMaxEachPostBytes,
scMinPostsIntervalMs: this.scMinPostsIntervalMs,
noSSEHeader: this.noSSEHeader,
};
}
}
@@ -570,7 +584,7 @@ class TlsStreamSettings extends XrayCommonClass {
disableSystemRoot = false,
enableSessionResumption = false,
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()) {
super();
this.sni = serverName;
@@ -712,7 +726,7 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
class XtlsStreamSettings extends XrayCommonClass {
constructor(serverName='',
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()) {
super();
this.sni = serverName;
@@ -892,7 +906,7 @@ class RealityStreamSettings 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();
this.publicKey = publicKey;
this.fingerprint = fingerprint;
@@ -2608,4 +2622,4 @@ Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
keepAlive: this.keepAlive?? undefined,
};
}
};
};

View File

@@ -128,7 +128,7 @@ Date.prototype.formatDateTime = function (split = ' ') {
};
class DateUtil {
// String string to date object
// String to date object
static parseDate(str) {
return new Date(str.replace(/-/g, '/'));
}
@@ -143,4 +143,9 @@ class DateUtil {
date.setMinTime();
return date;
}
static convertToJalalian(date) {
return date && moment.isMoment(date) ? date.format('jYYYY/jMM/jDD HH:mm:ss') : null;
}
}

View File

@@ -1,73 +1,57 @@
class Msg {
constructor(success, msg, obj) {
this.success = false;
this.msg = "";
this.obj = null;
if (success != null) {
this.success = success;
}
if (msg != null) {
this.msg = msg;
}
if (obj != null) {
this.obj = obj;
}
constructor(success = false, msg = "", obj = null) {
this.success = success;
this.msg = msg;
this.obj = obj;
}
}
class HttpUtil {
static _handleMsg(msg) {
if (!(msg instanceof Msg)) {
if (!(msg instanceof Msg) || msg.msg === "") {
return;
}
if (msg.msg === "") {
return;
}
if (msg.success) {
Vue.prototype.$message.success(msg.msg);
} else {
Vue.prototype.$message.error(msg.msg);
}
const messageType = msg.success ? 'success' : 'error';
Vue.prototype.$message[messageType](msg.msg);
}
static _respToMsg(resp) {
const data = resp.data;
const { data } = resp;
if (data == null) {
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) {
let msg;
static async get(url, params, options = {}) {
try {
const resp = await axios.get(url, data, options);
msg = this._respToMsg(resp);
} catch (e) {
msg = new Msg(false, e.toString());
const resp = await axios.get(url, { params, ...options });
const msg = this._respToMsg(resp);
this._handleMsg(msg);
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) {
let msg;
static async post(url, data, options = {}) {
try {
const resp = await axios.post(url, data, options);
msg = this._respToMsg(resp);
} catch (e) {
msg = new Msg(false, e.toString());
const msg = this._respToMsg(resp);
this._handleMsg(msg);
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) {

View File

@@ -33,6 +33,7 @@ func (a *APIController) initRouter(g *gin.RouterGroup) {
{"GET", "/list", a.inboundController.getInbounds},
{"GET", "/get/:id", a.inboundController.getInbound},
{"GET", "/getClientTraffics/:email", a.inboundController.getClientTraffics},
{"GET", "/getClientTrafficsById/:id", a.inboundController.getClientTrafficsById},
{"POST", "/add", a.inboundController.addInbound},
{"POST", "/del/:id", a.inboundController.delInbound},
{"POST", "/update/:id", a.inboundController.updateInbound},

View File

@@ -77,6 +77,16 @@ func (a *InboundController) getClientTraffics(c *gin.Context) {
jsonObj(c, clientTraffics, nil)
}
func (a *InboundController) getClientTrafficsById(c *gin.Context) {
id := c.Param("id")
clientTraffics, err := a.inboundService.GetClientTrafficByID(id)
if err != nil {
jsonMsg(c, "Error getting traffics", err)
return
}
jsonObj(c, clientTraffics, nil)
}
func (a *InboundController) addInbound(c *gin.Context) {
inbound := &model.Inbound{}
err := c.ShouldBind(inbound)

View File

@@ -2,6 +2,7 @@ package controller
import (
"net/http"
"text/template"
"time"
"x-ui/logger"
@@ -64,14 +65,17 @@ func (a *IndexController) login(c *gin.Context) {
user := a.userService.CheckUser(form.Username, form.Password, form.LoginSecret)
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 {
logger.Warningf("wrong username or password or secret: \"%s\" \"%s\" \"%s\"", form.Username, form.Password, form.LoginSecret)
a.tgbot.UserLoginNotify(form.Username, form.Password, getRemoteIp(c), timeStr, 0)
logger.Warningf("wrong username or password or secret: \"%s\" \"%s\" \"%s\"", safeUser, safePass, safeSecret)
a.tgbot.UserLoginNotify(safeUser, safePass, getRemoteIp(c), timeStr, 0)
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword"))
return
} else {
logger.Infof("%s logged in successfully, Ip Address: %s\n", form.Username, getRemoteIp(c))
a.tgbot.UserLoginNotify(form.Username, ``, getRemoteIp(c), timeStr, 1)
logger.Infof("%s logged in successfully, Ip Address: %s\n", safeUser, getRemoteIp(c))
a.tgbot.UserLoginNotify(safeUser, ``, getRemoteIp(c), timeStr, 1)
}
sessionMaxAge, err := a.settingService.GetSessionMaxAge()

View File

@@ -12,6 +12,7 @@ type XraySettingController struct {
InboundService service.InboundService
OutboundService service.OutboundService
XrayService service.XrayService
WarpService service.WarpService
}
func NewXraySettingController(g *gin.RouterGroup) *XraySettingController {
@@ -72,16 +73,18 @@ func (a *XraySettingController) warp(c *gin.Context) {
var err error
switch action {
case "data":
resp, err = a.XraySettingService.GetWarp()
resp, err = a.WarpService.GetWarpData()
case "del":
err = a.WarpService.DelWarpData()
case "config":
resp, err = a.XraySettingService.GetWarpConfig()
resp, err = a.WarpService.GetWarpConfig()
case "reg":
skey := c.PostForm("privateKey")
pkey := c.PostForm("publicKey")
resp, err = a.XraySettingService.RegWarp(skey, pkey)
resp, err = a.WarpService.RegWarp(skey, pkey)
case "license":
license := c.PostForm("license")
resp, err = a.XraySettingService.SetWarpLicense(license)
resp, err = a.WarpService.SetWarpLicense(license)
}
jsonObj(c, resp, err)

View File

@@ -108,15 +108,16 @@
format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
v-model="clientsBulkModal.expiryTime"></a-date-picker>
<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 v-if="clientsBulkModal.expiryTime != 0">
<template slot="label">
<span>{{ i18n "pages.client.renew" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.client.renewDesc" }}</span>
</template>
{{ i18n "pages.client.renew" }}
<a-icon type="question-circle"></a-icon>
</a-tooltip>
</template>

View File

@@ -54,12 +54,13 @@
<a-icon type="question-circle"></a-icon>
</a-tooltip>
</template>
<a-date-picker style="width: 100%;" v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="themeSwitcher.currentTheme"
v-model="dbInbound._expiryTime"></a-date-picker>
<persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
value="dbInbound._expiryTime" v-model="dbInbound._expiryTime"></persian-datepicker>
</a-form-item>
<a-date-picker style="width: 100%;" v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }"
format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
v-model="dbInbound._expiryTime"></a-date-picker>
<persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
value="dbInbound._expiryTime" v-model="dbInbound._expiryTime">
</persian-datepicker>
</a-form-item>
</a-form>
<!-- vmess settings -->

View File

@@ -19,11 +19,17 @@
</a-input>
</a-input-group>
</a-form-item>
<a-form-item label="Max Upload Size (MB)">
<a-input-number v-model="inbound.stream.splithttp.maxUploadSize" :min="0"></a-input-number>
</a-form-item>
<a-form-item label="Max Concurrent Upload">
<a-input-number v-model="inbound.stream.splithttp.maxConcurrentUploads" :min="0"></a-input-number>
<a-input-number v-model="inbound.stream.splithttp.scMaxConcurrentPosts" :min="0"></a-input-number>
</a-form-item>
<a-form-item label="Max Upload Size (Byte)">
<a-input-number v-model="inbound.stream.splithttp.scMaxEachPostBytes" :min="0"></a-input-number>
</a-form-item>
<a-form-item label="Min Upload Interval (Ms)">
<a-input-number v-model="inbound.stream.splithttp.scMinPostsIntervalMs" :min="0"></a-input-number>
</a-form-item>
<a-form-item label="No SSE Header">
<a-switch v-model="inbound.stream.splithttp.noSSEHeader"></a-switch>
</a-form-item>
</a-form>
{{end}}

View File

@@ -90,7 +90,14 @@
<template slot="content">
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
</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>
<table>
<tr class="tr-table-box">
@@ -108,7 +115,14 @@
<template slot="content">
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
</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>
<a-tag style="min-width: 50px; border: none;" :color="userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
</a-popover>
@@ -201,7 +215,14 @@
<template slot="content">
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
</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>
<a-progress :show-info="false" :status="isClientEnabled(record, client.email)? 'exception' : ''" :percent="expireProgress(client.expiryTime, client.reset)" />
</a-popover>
@@ -214,7 +235,14 @@
<template slot="content">
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
</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>
<a-tag style="min-width: 50px; border: none;" :color="userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
</a-popover>

View File

@@ -221,7 +221,14 @@
</td>
<td>
<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>
<a-tag v-else-if="infoModal.clientSettings.expiryTime < 0" color="green">[[ infoModal.clientSettings.expiryTime / -86400000 ]] {{ i18n "pages.client.days" }}
</a-tag>

View File

@@ -403,9 +403,12 @@
</template>
<template slot="expiryTime" slot-scope="text, dbInbound">
<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) ]]
</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)">
[[ remainedDays(dbInbound._expiryTime) ]]
</a-tag>
@@ -498,8 +501,14 @@
<tr>
<td>{{ i18n "pages.inbounds.expireDate" }}</td>
<td>
<a-tag style="min-width: 50px; text-align: center;" v-if="dbInbound.expiryTime > 0" :color="dbInbound.isExpiry? 'red': 'blue'">
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
<a-tag style="min-width: 50px; text-align: center;" v-if="dbInbound.expiryTime > 0"
: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 v-else style="text-align: center;" color="purple" class="infinite-tag">
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">

View File

@@ -24,19 +24,22 @@
<td>[[ warpModal.warpData.private_key ]]</td>
</tr>
</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-collapse style="margin: 10px 0;">
<a-collapse-panel header='WARP/WARP+ License Key'>
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label="Key">
<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>
</a-collapse-panel>
</a-collapse>
<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)">
<table style="width: 100%">
<tr class="client-table-odd-row">
@@ -51,39 +54,39 @@
<td>Device Enabled</td>
<td>[[ warpModal.warpConfig.enabled ]]</td>
</tr>
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account)">
<tr>
<td>Account Type</td>
<td>[[ warpModal.warpConfig.account.account_type ]]</td>
</tr>
<tr class="client-table-odd-row">
<td>Role</td>
<td>[[ warpModal.warpConfig.account.role ]]</td>
</tr>
<tr>
<td>WARP+ Data</td>
<td>[[ sizeFormat(warpModal.warpConfig.account.premium_data) ]]</td>
</tr>
<tr class="client-table-odd-row">
<td>Quota</td>
<td>[[ sizeFormat(warpModal.warpConfig.account.quota) ]]</td>
</tr>
<tr v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account.usage)">
<td>Usage</td>
<td>[[ sizeFormat(warpModal.warpConfig.account.usage) ]]</td>
</tr>
</template>
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account)">
<tr>
<td>Account Type</td>
<td>[[ warpModal.warpConfig.account.account_type ]]</td>
</tr>
<tr class="client-table-odd-row">
<td>Role</td>
<td>[[ warpModal.warpConfig.account.role ]]</td>
</tr>
<tr>
<td>WARP+ Data</td>
<td>[[ sizeFormat(warpModal.warpConfig.account.premium_data) ]]</td>
</tr>
<tr class="client-table-odd-row">
<td>Quota</td>
<td>[[ sizeFormat(warpModal.warpConfig.account.quota) ]]</td>
</tr>
<tr v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account.usage)">
<td>Usage</td>
<td>[[ sizeFormat(warpModal.warpConfig.account.usage) ]]</td>
</tr>
</template>
</table>
<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} }">
<template v-if="warpOutboundIndex>=0">
<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>
</template>
<template v-else>
<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>
</template>
<template v-if="warpOutboundIndex>=0">
<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>
</template>
<template v-else>
<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>
</template>
</a-form-item>
</a-form>
</template>
@@ -101,21 +104,20 @@
this.visible = true;
this.warpConfig = null;
this.getData();
},
close() {
this.visible = false;
this.loading(false);
},
loading(loading=true) {
loading(loading = true) {
this.confirmLoading = loading;
},
async getData(){
async getData() {
this.loading(true);
const msg = await HttpUtil.post('/panel/xray/warp/data');
this.loading(false);
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() {
config = warpModal.warpConfig.config;
peer = config.peers[0];
if(config){
if (config) {
warpModal.warpOutbound = Outbound.fromJson({
tag: 'warp',
protocol: Protocols.Wireguard,
settings: {
mtu: 1420,
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',
peers: [{
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);
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) {
resp = JSON.parse(msg.obj);
warpModal.warpData = resp.data;
@@ -161,9 +186,9 @@
}
warpModal.loading(false);
},
async updateLicense(l){
async updateLicense(l) {
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) {
warpModal.warpData = JSON.parse(msg.obj);
warpModal.warpConfig = null;
@@ -171,7 +196,7 @@
}
warpModal.loading(false);
},
async getConfig(){
async getConfig() {
warpModal.loading(true);
const msg = await HttpUtil.post('/panel/xray/warp/config');
warpModal.loading(false);
@@ -180,20 +205,37 @@
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.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
warpModal.close();
},
resetOutbound(){
resetOutbound() {
app.templateSettings.outbounds[this.warpOutboundIndex] = warpModal.warpOutbound.toJson();
app.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
warpModal.close();
},
delOutbound() {
if (this.warpOutboundIndex != -1) {
app.templateSettings.outbounds.splice(this.warpOutboundIndex, 1);
app.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
}
warpModal.close();
}
},
computed: {
warpOutboundIndex: {
get: function() {
get: function () {
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag == 'warp') : -1;
}
}
@@ -201,4 +243,4 @@
});
</script>
{{end}}
{{end}}

View File

@@ -1054,12 +1054,13 @@
});
},
changeObsCode() {
if (this.obsSettings == ''){
return
}
if(this.cm != null) {
this.cm.toTextArea();
}
if (this.obsSettings == ''){
this.cm = null;
return
}
textAreaObj = document.getElementById('obsSetting');
textAreaObj.value = this[this.obsSettings];
this.cm = CodeMirror.fromTextArea(textAreaObj, this.cmOptions);
@@ -1267,7 +1268,8 @@
balancer: {
tag: '',
strategy: 'random',
selector: []
selector: [],
fallbackTag: ''
},
confirm: (balancer) => {
balancerModal.loading();
@@ -1277,27 +1279,18 @@
}
let tmpBalancer = {
'tag': balancer.tag,
'selector': balancer.selector
'selector': balancer.selector,
'fallbackTag': balancer.fallbackTag
};
if (balancer.strategy && balancer.strategy != 'random') {
tmpBalancer.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);
this.templateSettings = newTemplateSettings;
if (balancer.strategy == 'leastPing' || balancer.strategy == 'leastLoad')
this.updateObservatorySelectors();
balancerModal.close();
this.changeObsCode();
},
@@ -1317,7 +1310,8 @@
let tmpBalancer = {
'tag': balancer.tag,
'selector': balancer.selector
'selector': balancer.selector,
'fallbackTag': balancer.fallbackTag
};
// Remove old tag
@@ -1332,18 +1326,6 @@
tmpBalancer.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;
@@ -1356,14 +1338,49 @@
});
}
this.templateSettings = newTemplateSettings;
if (balancer.strategy == 'leastPing' || balancer.strategy == 'leastLoad')
this.updateObservatorySelectors();
balancerModal.close();
this.changeObsCode();
},
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) {
let newTemplateSettings = { ...this.templateSettings };
newTemplateSettings = this.templateSettings;
// Remove from balancers
const removedBalancer = this.balancersData.splice(index, 1)[0];
@@ -1371,27 +1388,14 @@
// Remove from settings
let realIndex = newTemplateSettings.routing.balancers.findIndex((b) => b.tag === removedBalancer.tag);
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
if (newTemplateSettings.routing.balancers.length === 0) {
delete newTemplateSettings.routing.balancers;
}
this.templateSettings = newTemplateSettings;
this.updateObservatorySelectors();
this.obsSettings = '';
this.changeObsCode()
},
addDNSServer(){
@@ -1622,7 +1626,8 @@
'key': index,
'tag': o.tag ? o.tag : "",
'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;
},
},
observatoryEnable: {
get: function () { return this.templateSettings != null && this.templateSettings.observatory },
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;
}
},
observatoryEnable: function () { return this.templateSettings != null && this.templateSettings.observatory != undefined },
burstObservatoryEnable: function () { return this.templateSettings != null && this.templateSettings.burstObservatory != undefined },
freedomStrategy: {
get: function () {
if (!this.templateSettings) return "AsIs";

View File

@@ -25,13 +25,19 @@
<a-select-option value="leastPing">Least Ping</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='{{ i18n "pages.xray.balancer.balancerSelectors" }}' has-feedback
:validate-status="balancerModal.emptySelector? 'warning' : 'success'">
<a-form-item label='{{ i18n "pages.xray.balancer.balancerSelectors" }}' has-feedback
:validate-status="balancerModal.emptySelector? 'warning' : 'success'">
<a-select v-model="balancerModal.balancer.selector" mode="tags" @change="balancerModal.checkSelector()"
: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>
<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>
</a-form>
</a-modal>
@@ -48,7 +54,8 @@
balancer: {
tag: '',
strategy: 'random',
selector: []
selector: [],
fallbackTag: ''
},
outboundTags: [],
balancerTags:[],
@@ -71,7 +78,8 @@
balancerModal.balancer = {
tag: '',
strategy: 'random',
selector: []
selector: [],
fallbackTag: ''
};
}
this.balancerTags = balancerTags.filter((tag) => tag != balancer.tag);

View File

@@ -300,7 +300,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
if limitIp < len(ips) {
j.disAllowedIps = append(j.disAllowedIps, ips[limitIp:]...)
for i := limitIp; i < len(ips); i++ {
logger.Debugf("[LIMIT_IP] Email = %s || SRC = %s", clientEmail, ips[i])
log.Printf("[LIMIT_IP] Email = %s || SRC = %s", clientEmail, ips[i])
}
}
}

View File

@@ -3,6 +3,7 @@ package middleware
import (
"net"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
@@ -14,12 +15,17 @@ func DomainValidatorMiddleware(domain string) gin.HandlerFunc {
host = c.GetHeader("X-Real-IP")
}
if host == "" {
host, _, _ := net.SplitHostPort(c.Request.Host)
if host != domain {
c.AbortWithStatus(http.StatusForbidden)
return
host = c.Request.Host
if colonIndex := strings.LastIndex(host, ":"); colonIndex != -1 {
host, _, _ = net.SplitHostPort(host)
}
c.Next()
}
if host != domain {
c.AbortWithStatus(http.StatusForbidden)
return
}
c.Next()
}
}

View File

@@ -1750,6 +1750,25 @@ func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.Cl
return nil, nil
}
func (s *InboundService) GetClientTrafficByID(id string) ([]xray.ClientTraffic, error) {
db := database.GetDB()
var traffics []xray.ClientTraffic
err := db.Model(xray.ClientTraffic{}).Where(`email IN(
SELECT JSON_EXTRACT(client.value, '$.email') as email
FROM inbounds,
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
WHERE
JSON_EXTRACT(client.value, '$.id') in (?)
)`, id).Find(&traffics).Error
if err != nil {
logger.Debug(err)
return nil, err
}
return traffics, err
}
func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.ClientTraffic, err error) {
db := database.GetDB()
inbound := &model.Inbound{}

162
web/service/warp.go Normal file
View 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
}

View File

@@ -1,13 +1,8 @@
package service
import (
"bytes"
_ "embed"
"encoding/json"
"fmt"
"net/http"
"os"
"time"
"x-ui/util/common"
"x-ui/xray"
@@ -32,142 +27,3 @@ func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error {
}
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) 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
}

View File

@@ -17,6 +17,10 @@ func init() {
func SetLoginUser(c *gin.Context, user *model.User) error {
s := sessions.Default(c)
s.Options(sessions.Options{
Path: "/",
HttpOnly: true,
})
s.Set(loginUser, user)
return s.Save()
}

View File

@@ -254,7 +254,7 @@ reset_user() {
gen_random_string() {
local length="$1"
local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w "$length" | head -n 1)
local random_string=$(LC_ALL=C tr -dc 'a-zA-Z0-9' </dev/urandom | fold -w "$length" | head -n 1)
echo "$random_string"
}

View File

@@ -181,6 +181,10 @@ func (x *XrayAPI) GetTraffic(reset bool) ([]*Traffic, []*ClientTraffic, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
if x.StatsServiceClient == nil {
return nil, nil, common.NewError("xray StatusServiceClient is not initialized")
}
resp, err := (*x.StatsServiceClient).QueryStats(ctx, &statsService.QueryStatsRequest{Reset_: reset})
if err != nil {
logger.Debug("Failed to query Xray stats:", err)