Compare commits

...

68 Commits
1.7.2 ... 1.8.1

Author SHA1 Message Date
Alireza Ahmadi
76f255f140 v1.8.1 2024-03-28 14:20:01 +01:00
Alireza Ahmadi
a090c6d6e7 [subjson] avoid array for one config 2024-03-28 14:12:16 +01:00
Alireza Ahmadi
de6c768149 Merge pull request #1121 from alireza0/dependabot/go_modules/github.com/gin-contrib/gzip-1.0.0
Bump github.com/gin-contrib/gzip from 0.0.6 to 1.0.0
2024-03-27 11:28:13 +01:00
dependabot[bot]
38e2bc3618 Bump github.com/gin-contrib/gzip from 0.0.6 to 1.0.0
Bumps [github.com/gin-contrib/gzip](https://github.com/gin-contrib/gzip) from 0.0.6 to 1.0.0.
- [Release notes](https://github.com/gin-contrib/gzip/releases)
- [Changelog](https://github.com/gin-contrib/gzip/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/gin-contrib/gzip/compare/v0.0.6...v1.0.0)

---
updated-dependencies:
- dependency-name: github.com/gin-contrib/gzip
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 21:38:12 +00:00
Alireza Ahmadi
03dd392a39 Merge pull request #1112 from alireza0/dependabot/go_modules/github.com/pelletier/go-toml/v2-2.2.0
Bump github.com/pelletier/go-toml/v2 from 2.1.1 to 2.2.0
2024-03-23 16:40:16 +01:00
Alireza Ahmadi
a338ab8808 [log] add exit code #1114 2024-03-23 16:09:07 +01:00
dependabot[bot]
115635660a Bump github.com/pelletier/go-toml/v2 from 2.1.1 to 2.2.0
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.1.1 to 2.2.0.
- [Release notes](https://github.com/pelletier/go-toml/releases)
- [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml)
- [Commits](https://github.com/pelletier/go-toml/compare/v2.1.1...v2.2.0)

---
updated-dependencies:
- dependency-name: github.com/pelletier/go-toml/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-19 21:58:22 +00:00
Alireza Ahmadi
040edf5c01 Merge pull request #1109 from alireza0/dependabot/go_modules/gorm.io/gorm-1.25.8
Bump gorm.io/gorm from 1.25.7 to 1.25.8
2024-03-19 10:39:41 +01:00
Alireza Ahmadi
56928a22c7 [xray] fix: convert link to json #1107 2024-03-19 10:39:19 +01:00
dependabot[bot]
e6ce891883 Bump gorm.io/gorm from 1.25.7 to 1.25.8
Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.25.7 to 1.25.8.
- [Release notes](https://github.com/go-gorm/gorm/releases)
- [Commits](https://github.com/go-gorm/gorm/compare/v1.25.7...v1.25.8)

---
updated-dependencies:
- dependency-name: gorm.io/gorm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 21:49:53 +00:00
Alireza Ahmadi
3b7738a6c9 small fixes 2024-03-17 20:21:51 +01:00
Alireza Ahmadi
7e4f6e5131 [sub] fix grpc authority failure 2024-03-17 20:21:40 +01:00
Alireza Ahmadi
c6ce96a63e avoid empty client id #1099 2024-03-15 19:03:02 +01:00
Alireza Ahmadi
fa541b4719 v1.8.0 2024-03-13 02:17:30 +01:00
Alireza Ahmadi
b6fe0c9342 [xray] balancer leastPing leastLoad 2024-03-13 02:02:55 +01:00
Alireza Ahmadi
00e3db0caa remain rules on delete balancer 2024-03-12 23:54:04 +01:00
Alireza Ahmadi
ed3a6c36e6 fix remained traffic in info #1083 2024-03-12 23:34:42 +01:00
Alireza Ahmadi
ada0287dc9 [ui] remove online animation #1086 2024-03-12 20:32:07 +01:00
Alireza Ahmadi
4f9ec33393 support IPv6 address in Link #1085 2024-03-12 20:23:33 +01:00
Alireza Ahmadi
fd98ff6fad improve null handling
Co-Authored-By: MHSanaei <ho3ein.sanaei@gmail.com>
2024-03-12 19:29:21 +01:00
Alireza Ahmadi
ecf89e430d add dns obf to outbound kcp 2024-03-12 19:13:46 +01:00
Alireza Ahmadi
af881a8fe4 limit mux to connection base outbounds 2024-03-12 19:13:14 +01:00
Alireza Ahmadi
490ed0ab96 [feature] add grpc authority
Co-Authored-By: MHSanaei <ho3ein.sanaei@gmail.com>
2024-03-12 19:12:07 +01:00
Alireza Ahmadi
29d348bd67 [feature] add httpUpgrade transport
Co-Authored-By: MHSanaei <ho3ein.sanaei@gmail.com>
2024-03-12 15:40:41 +01:00
Alireza Ahmadi
258101efae build with xray-core 1.8.9 2024-03-12 14:15:09 +01:00
Alireza Ahmadi
cec900b596 [xray] add outbound sendThrough #1017 2024-03-12 14:07:53 +01:00
Alireza Ahmadi
463a120d58 fix sockopt in all protocols 2024-03-12 13:58:31 +01:00
Alireza Ahmadi
cf521367f7 Merge pull request #1081 from alireza0/dependabot/go_modules/github.com/xtls/xray-core-1.8.9
Bump github.com/xtls/xray-core from 1.8.8 to 1.8.9
2024-03-12 12:24:38 +01:00
Alireza Ahmadi
2012dd0c0a small changes 2024-03-12 12:24:26 +01:00
Alireza Ahmadi
b22d1e082a [subJson] add mux and direct 2024-03-12 12:24:01 +01:00
Alireza Ahmadi
dc332b09fa add dns obf type to kcp 2024-03-12 12:16:34 +01:00
dependabot[bot]
a30043d8ae Bump github.com/xtls/xray-core from 1.8.8 to 1.8.9
Bumps [github.com/xtls/xray-core](https://github.com/xtls/xray-core) from 1.8.8 to 1.8.9.
- [Release notes](https://github.com/xtls/xray-core/releases)
- [Commits](https://github.com/xtls/xray-core/compare/v1.8.8...v1.8.9)

---
updated-dependencies:
- dependency-name: github.com/xtls/xray-core
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 21:10:53 +00:00
Alireza Ahmadi
482e04e614 [xray] add mux to outbound 2024-03-11 16:24:04 +01:00
Alireza Ahmadi
5ae613c188 [sub] JSON sub enhancement #1029 2024-03-11 11:56:28 +01:00
Alireza Ahmadi
1cc72ae6af [xray] add sockopt to all outbounds #1017 2024-03-11 11:53:17 +01:00
Hamidreza
b6ccb2e076 Some fixes and improvements (#1077)
* [refactor] api controller

* [update] use status code for jsonMsg and 401 to unauthorize

* [update] handle response status code via axios

* [lint] all .go files
2024-03-10 18:00:44 +01:00
Alireza Ahmadi
e3c2b42048 [xray] add sockopt #1017 2024-03-10 17:46:51 +01:00
Alireza Ahmadi
029df32b71 [xray] add dns tag #1018 2024-03-10 17:05:21 +01:00
Alireza Ahmadi
629d0da33c avoid empty client id #1072 2024-03-10 16:02:31 +01:00
Alireza Ahmadi
80c3f84eac small changes 2024-03-10 15:29:03 +01:00
Alireza Ahmadi
2a6ed18163 [xray] add BurstObservatory #1031 2024-03-10 15:26:20 +01:00
Alireza Ahmadi
271a85e9b5 [xray] add new dns strategies #1017 2024-03-10 15:24:23 +01:00
Alireza Ahmadi
bbb0663198 run sub on http if https failed 2024-03-10 15:18:28 +01:00
Alireza Ahmadi
f5c7b03e80 Merge pull request #1062 from alireza0/dependabot/go_modules/google.golang.org/grpc-1.62.1
Bump google.golang.org/grpc from 1.62.0 to 1.62.1
2024-03-08 15:07:07 +01:00
dependabot[bot]
e4b9b70c26 Bump google.golang.org/grpc from 1.62.0 to 1.62.1
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.62.0 to 1.62.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.62.0...v1.62.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-05 21:54:45 +00:00
Alireza Ahmadi
53fcf4da15 Merge pull request #1048 from alireza0/dependabot/go_modules/github.com/shirou/gopsutil/v3-3.24.2
Bump github.com/shirou/gopsutil/v3 from 3.24.1 to 3.24.2
2024-03-02 00:03:57 +01:00
dependabot[bot]
bb8f3dab36 Bump github.com/shirou/gopsutil/v3 from 3.24.1 to 3.24.2
Bumps [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) from 3.24.1 to 3.24.2.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v3.24.1...v3.24.2)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-01 21:42:01 +00:00
Shahin
c834e4cc35 [i18n] add security alert translation (#1036)
* [ui] fix external proxy input field

* [settings] improve security alert

* [i18n] add security alert translation

* [i18n] fix vietnamese lang
2024-02-27 11:31:13 +01:00
Alireza Ahmadi
225b3fc3a5 change cookie name 2024-02-27 00:59:12 +01:00
Alireza Ahmadi
6b174762e2 close modal only after success msg 2024-02-26 23:39:45 +01:00
Alireza Ahmadi
1c660bbea1 small changes 2024-02-26 19:33:50 +01:00
Mohammadi, Erfan
50d4e67e59 Add sorting to client and traffic columns, fix typo (#1005)
* Enable sorting for client and traffic columns in innerColumns and client column in innerMobileColumns.
Fix typo: mobileColums -> mobileColumns.

* Refactored sorter functions in inbounds.html

* Fix sorting issue in inbounds.html

* Refactor email sorting function in inbounds.html

* Add traffic sorting to inbounds table

* small changes

* Add hover effect to table column sorters

* Update background color in dark mode

* Fix hover background color in dark mode

* Remove unnecessary CSS styles for table column sorters

---------

Co-authored-by: Alireza Ahmadi <alireza7@gmail.com>
2024-02-26 16:02:16 +01:00
Alireza Ahmadi
1025eeebf9 Merge pull request #1028 from MHSanaei/main
upgrade xray to v1.8.8 + minor changes
2024-02-26 15:49:25 +01:00
MHSanaei
fe00a61963 update dependencies 2024-02-25 22:35:33 +03:30
MHSanaei
64e2e62292 i think its better to remove 2024-02-25 19:46:06 +03:30
MHSanaei
0f6c8f1ec5 fix select with mouse 2024-02-25 19:45:24 +03:30
MHSanaei
5bc6f7518c upgrade xray to v1.8.8 2024-02-25 19:45:06 +03:30
Alireza Ahmadi
79f2d34541 run panel on http if https failed 2024-02-25 11:30:36 +01:00
Alireza Ahmadi
0aec9b6cb7 Merge pull request #1011 from alireza0/dependabot/go_modules/google.golang.org/grpc-1.62.0
Bump google.golang.org/grpc from 1.61.1 to 1.62.0
2024-02-24 00:22:56 +01:00
Alireza Ahmadi
eb36faced9 [log] fix download format 2024-02-24 00:20:40 +01:00
Alireza Ahmadi
99e9c27d2f small changes 2024-02-24 00:08:25 +01:00
dependabot[bot]
b3bb31c98c Bump google.golang.org/grpc from 1.61.1 to 1.62.0
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.61.1 to 1.62.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.61.1...v1.62.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-22 21:43:59 +00:00
Alireza Ahmadi
09407710a7 small changes 2024-02-22 20:35:19 +01:00
Ho3ein
7a38a2474e bash - firewall Management + Speedtest by Ookla + Enable or Disable BBR (#1000)
* bash - firewall Management + Speedtest by Ookla + Enable or Disable BBR

* svenstaro + release to draft
2024-02-22 18:35:37 +01:00
Shahin
afd072a430 [xray] balancerTag & logs translation (#1008) 2024-02-22 18:34:22 +01:00
Alireza Ahmadi
3bbf83924c [rule] clearable outbound & balancer #996 2024-02-22 14:43:19 +01:00
Alireza Ahmadi
1329b2bb61 support multi address-port #997 2024-02-22 14:18:31 +01:00
Alireza Ahmadi
d80b26f241 [bug] fix wg reserved #995 2024-02-22 12:04:26 +01:00
75 changed files with 1658 additions and 697 deletions

View File

@@ -18,10 +18,10 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5.0.0 uses: actions/setup-go@v5
with: with:
go-version: '1.22' go-version: '1.22'
@@ -63,7 +63,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.7/" Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.9/"
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
@@ -93,7 +93,7 @@ jobs:
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 GH release - name: Upload files to GH release
uses: MHSanaei/upload-release-action@2.8.0 uses: svenstaro/upload-release-action@v2
with: with:
repo_token: ${{ secrets.GITHUB_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }} tag: ${{ github.ref }}

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
.cache .cache
.sync* .sync*
*.tar.gz *.tar.gz
*.log
access.log access.log
error.log error.log
tmp tmp

View File

@@ -23,7 +23,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.7/Xray-linux-${ARCH}.zip" wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.9/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip" unzip "Xray-linux-${ARCH}.zip"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat LICENSE README.md rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat LICENSE README.md
mv xray "xray-linux-${FNAME}" mv xray "xray-linux-${FNAME}"

View File

@@ -11,8 +11,7 @@
**If you think this project is helpful to you, you may wish to give a**:star2: **If you think this project is helpful to you, you may wish to give a**:star2:
<img width="125" alt="image" [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/alireza7)
src="https://github.com/alireza0/x-ui/assets/115543613/dd4f10dd-8bb0-40cf-846f-1fe1de7a6275">
- USDT (TRC20): `TYTq73Gj6dJ67qe58JVPD9zpjW2cc9XgVz` - USDT (TRC20): `TYTq73Gj6dJ67qe58JVPD9zpjW2cc9XgVz`
- Tezos (XTZ): - Tezos (XTZ):
@@ -43,10 +42,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.s
## Install Custom Version ## Install Custom Version
**Step 1:** To install your desired version, add the version to the end of the installation command. e.g., ver `1.7.1`: **Step 1:** To install your desired version, add the version to the end of the installation command. e.g., ver `1.8.0`:
```sh ```sh
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh) 1.7.1 bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh) 1.8.0
``` ```
## Manual Install & Upgrade ## Manual Install & Upgrade

View File

@@ -1 +1 @@
1.7.2 1.8.1

View File

@@ -6,6 +6,7 @@ import (
"io/fs" "io/fs"
"os" "os"
"path" "path"
"x-ui/config" "x-ui/config"
"x-ui/database/model" "x-ui/database/model"
"x-ui/xray" "x-ui/xray"

View File

@@ -2,6 +2,7 @@ package model
import ( import (
"fmt" "fmt"
"x-ui/util/json_util" "x-ui/util/json_util"
"x-ui/xray" "x-ui/xray"
) )
@@ -36,7 +37,7 @@ type Inbound struct {
// config part // config part
Listen string `json:"listen" form:"listen"` Listen string `json:"listen" form:"listen"`
Port int `json:"port" form:"port" gorm:"unique"` Port int `json:"port" form:"port"`
Protocol Protocol `json:"protocol" form:"protocol"` Protocol Protocol `json:"protocol" form:"protocol"`
Settings string `json:"settings" form:"settings"` Settings string `json:"settings" form:"settings"`
StreamSettings string `json:"streamSettings" form:"streamSettings"` StreamSettings string `json:"streamSettings" form:"streamSettings"`

46
go.mod
View File

@@ -9,37 +9,37 @@ require (
github.com/goccy/go-json v0.10.2 github.com/goccy/go-json v0.10.2
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.1.1 github.com/pelletier/go-toml/v2 v2.2.0
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v3 v3.24.1 github.com/shirou/gopsutil/v3 v3.24.2
github.com/xtls/xray-core v1.8.7 github.com/xtls/xray-core v1.8.9
go.uber.org/atomic v1.11.0 go.uber.org/atomic v1.11.0
golang.org/x/text v0.14.0 golang.org/x/text v0.14.0
google.golang.org/grpc v1.61.1 google.golang.org/grpc v1.62.1
gorm.io/driver/sqlite v1.5.5 gorm.io/driver/sqlite v1.5.5
gorm.io/gorm v1.25.7 gorm.io/gorm v1.25.8
) )
require github.com/chenzhuoyu/iasm v0.9.1 // indirect require github.com/chenzhuoyu/iasm v0.9.1 // indirect
require ( require (
github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect
github.com/bytedance/sonic v1.11.0 // indirect github.com/bytedance/sonic v1.11.3 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/cloudflare/circl v1.3.7 // indirect github.com/cloudflare/circl v1.3.7 // 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/francoispqt/gojay v1.2.13 // indirect github.com/francoispqt/gojay v1.2.13 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/gzip v0.0.6 github.com/gin-contrib/gzip v1.0.0
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.18.0 // indirect github.com/go-playground/validator/v10 v10.19.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // 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.2.2 // indirect
@@ -47,21 +47,21 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.6 // indirect github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // 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-20231016141302-07b5767bb0ed // 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.15.0 // indirect github.com/onsi/ginkgo/v2 v2.16.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-20240219145905-2259734c190a // indirect github.com/power-devops/perfstat v0.0.0-20240219145905-2259734c190a // indirect
github.com/quic-go/quic-go v0.41.0 // indirect github.com/quic-go/quic-go v0.41.0 // indirect
github.com/refraction-networking/utls v1.6.2 // indirect github.com/refraction-networking/utls v1.6.3 // 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/sagernet/sing v0.3.0 // indirect github.com/sagernet/sing v0.3.6 // indirect
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect github.com/sagernet/sing-shadowsocks v0.2.6 // indirect
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect
@@ -77,17 +77,17 @@ require (
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.7.0 // indirect golang.org/x/arch v0.7.0 // indirect
golang.org/x/crypto v0.19.0 // indirect golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/mod v0.15.0 // indirect golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.21.0 // indirect golang.org/x/net v0.22.0 // indirect
golang.org/x/sys v0.17.0 // indirect golang.org/x/sys v0.18.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.18.0 // indirect golang.org/x/tools v0.19.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-20240213162025-012b6fc9bca9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 // indirect
google.golang.org/protobuf v1.32.0 // indirect google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect
lukechampine.com/blake3 v1.2.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect

156
go.sum
View File

@@ -20,8 +20,8 @@ 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/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.11.0 h1:FwNNv6Vu4z2Onf1++LNzxB/QhitD8wuTdpZzMTGITWo= github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA=
github.com/bytedance/sonic v1.11.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
@@ -33,7 +33,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -50,37 +49,31 @@ github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcP
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= github.com/gin-contrib/gzip v1.0.0 h1:UKN586Po/92IDX6ie5CWLgMI81obiIp5nSP85T3wlTk=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= github.com/gin-contrib/gzip v1.0.0/go.mod h1:CtG7tQrPB3vIBo6Gat9FVUsis+1emjvQqd66ME5TdnE=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= 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-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1/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.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 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8= github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -88,18 +81,16 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@@ -111,8 +102,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-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 h1:y3N7Bm7Y9/CtpiVkw/ZWj6lSlDF3F74SfKwfTCer72Q=
github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
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=
@@ -136,22 +127,18 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
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-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
@@ -159,17 +146,14 @@ github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIg
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
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=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -180,8 +164,8 @@ 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/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=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM=
github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
@@ -189,12 +173,10 @@ github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWEr
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
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=
@@ -207,25 +189,22 @@ github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7q
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.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k= github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA= github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
github.com/refraction-networking/utls v1.6.2 h1:iTeeGY0o6nMNcGyirxkD5bFIsVctP5InGZ3E0HrzS7k= github.com/refraction-networking/utls v1.6.3 h1:MFOfRN35sSx6K5AZNIoESsBuBxS2LCgRilRIdHb6fDc=
github.com/refraction-networking/utls v1.6.2/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs= github.com/refraction-networking/utls v1.6.3/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs=
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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
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.3.0 h1:PIDVFZHnQAAYRL1UYqNM+0k5s8f/tb1lUW6UDcQiOc8= github.com/sagernet/sing v0.3.6 h1:dsEdYLKBQlrxUfw1a92x0VdPvR1/BOxQ+HIMyaoEJsQ=
github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g= github.com/sagernet/sing v0.3.6/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI=
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s= 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-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U= 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-20220411075957-e3b120b3f5fb/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/v3 v3.24.1 h1:R3t6ondCEvmARp3wxODhXMTLC/klMa87h2PHUw5m7QI= github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y=
github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU= github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -257,6 +236,7 @@ github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -264,8 +244,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
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/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
@@ -275,8 +256,6 @@ github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
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 v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI= github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
@@ -290,9 +269,8 @@ github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1Y
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-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI=
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
github.com/xtls/xray-core v1.8.7 h1:lb8O1l3/eAg3YAXA6tLm5M6N7BsX8wxW9sJLjU3dHkA= github.com/xtls/xray-core v1.8.9 h1:wefcON0behu4DoQvCKJYZKsJlSvNhyq2I7vC2fxLFcY=
github.com/xtls/xray-core v1.8.7/go.mod h1:9rFpflfQbgFeH1VKJw7yUmEy7myOyDCgNXXl0bmmyOo= github.com/xtls/xray-core v1.8.9/go.mod h1:XDE4f422qJKAU3hNDSNZyWrOHvn9kF8UHVdyOzU38rc=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
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=
@@ -310,17 +288,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.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
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-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
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.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.16.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=
@@ -329,9 +306,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.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
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=
@@ -350,25 +326,18 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.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.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -376,12 +345,11 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-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.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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=
@@ -399,24 +367,19 @@ 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-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7 h1:em/y72n4XlYRtayY/cVj6pnVzHa//BDA1BdoO+z9mdE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk= google.golang.org/genproto/googleapis/rpc v0.0.0-20240308144416-29370a3891b7/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
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.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -424,18 +387,15 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 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.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E= gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE= gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A= gorm.io/gorm v1.25.8 h1:WAGEZ/aEcznN4D03laj8DKnehe1e9gYQAjW8xyPRdeo=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.8/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
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-20231104011432-48a6d7d5bd0b h1:yqkg3pTifuKukuWanp8spDsL4irJkHF5WI0J47hU87o= gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h1:yqkg3pTifuKukuWanp8spDsL4irJkHF5WI0J47hU87o=
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk= gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=
gvisor.dev/gvisor v0.0.0-20240216214558-53d2b511e78e h1:BAmOamSGzOqvWRcyoAZvud9SuInvUn2qYIK495rjF+4=
gvisor.dev/gvisor v0.0.0-20240216214558-53d2b511e78e/go.mod h1:YcCCAniKhCIGGvWxOobcre6euvNQON7nZCtMcVYO9rA=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -8,12 +8,14 @@ import (
"github.com/op/go-logging" "github.com/op/go-logging"
) )
var logger *logging.Logger var (
var logBuffer []struct { logger *logging.Logger
time string logBuffer []struct {
level logging.Level time string
log string level logging.Level
} log string
}
)
func init() { func init() {
InitLogger(logging.INFO) InitLogger(logging.INFO)

View File

@@ -8,6 +8,7 @@ import (
"os/signal" "os/signal"
"syscall" "syscall"
_ "unsafe" _ "unsafe"
"x-ui/config" "x-ui/config"
"x-ui/database" "x-ui/database"
"x-ui/logger" "x-ui/logger"
@@ -318,7 +319,7 @@ func main() {
updateTgbotEnableSts(enabletgbot) updateTgbotEnableSts(enabletgbot)
} }
default: default:
fmt.Println("except 'run' or 'setting' subcommands") fmt.Println("Invalid subcommands")
fmt.Println() fmt.Println()
runCmd.Usage() runCmd.Usage()
fmt.Println() fmt.Println()

View File

@@ -1,4 +1,5 @@
{ {
"remarks": "",
"dns": { "dns": {
"tag": "dns_out", "tag": "dns_out",
"queryStrategy": "UseIP", "queryStrategy": "UseIP",
@@ -73,33 +74,14 @@
} }
}, },
"routing": { "routing": {
"domainStrategy": "AsIs", "domainStrategy": "IPIfNonMatch",
"rules": [ "rules": [
{ {
"type": "field", "type": "field",
"network": "tcp,udp", "network": "tcp,udp",
"balancerTag": "all" "outboundTag": "proxy"
}
],
"balancers": [
{
"tag": "all",
"selector": [
"proxy"
],
"strategy": {
"type": "leastPing"
}
} }
] ]
}, },
"observatory": {
"probeInterval": "5m",
"probeURL": "https://api.github.com/_private/browser/stats",
"subjectSelector": [
"proxy"
],
"EnableConcurrency": true
},
"stats": {} "stats": {}
} }

View File

@@ -7,6 +7,7 @@ import (
"net" "net"
"net/http" "net/http"
"strconv" "strconv"
"x-ui/config" "x-ui/config"
"x-ui/logger" "x-ui/logger"
"x-ui/util/common" "x-ui/util/common"
@@ -91,15 +92,27 @@ func (s *Server) initRouter() (*gin.Engine, error) {
SubJsonFragment = "" SubJsonFragment = ""
} }
SubJsonMux, err := s.settingService.GetSubJsonMux()
if err != nil {
SubJsonMux = ""
}
SubJsonRules, err := s.settingService.GetSubJsonRules()
if err != nil {
SubJsonRules = ""
}
g := engine.Group("/") g := engine.Group("/")
s.sub = NewSUBController(g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates, SubJsonFragment) s.sub = NewSUBController(
g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates,
SubJsonFragment, SubJsonMux, SubJsonRules)
return engine, nil return engine, nil
} }
func (s *Server) Start() (err error) { func (s *Server) Start() (err error) {
//This is an anonymous function, no function name // This is an anonymous function, no function name
defer func() { defer func() {
if err != nil { if err != nil {
s.Stop() s.Stop()
@@ -144,21 +157,19 @@ func (s *Server) Start() (err error) {
if certFile != "" || keyFile != "" { if certFile != "" || keyFile != "" {
cert, err := tls.LoadX509KeyPair(certFile, keyFile) cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil { if err == nil {
listener.Close() c := &tls.Config{
return err Certificates: []tls.Certificate{cert},
}
listener = network.NewAutoHttpsListener(listener)
listener = tls.NewListener(listener, c)
logger.Info("sub server run https on", listener.Addr())
} else {
logger.Error("error in loading certificates: ", err)
logger.Info("sub server run http on", listener.Addr())
} }
c := &tls.Config{
Certificates: []tls.Certificate{cert},
}
listener = network.NewAutoHttpsListener(listener)
listener = tls.NewListener(listener, c)
}
if certFile != "" || keyFile != "" {
logger.Info("Sub server run https on", listener.Addr())
} else { } else {
logger.Info("Sub server run http on", listener.Addr()) logger.Info("sub server run http on", listener.Addr())
} }
s.listener = listener s.listener = listener

View File

@@ -25,16 +25,19 @@ func NewSUBController(
showInfo bool, showInfo bool,
rModel string, rModel string,
update string, update string,
jsonFragment string) *SUBController { jsonFragment string,
jsonMux string,
jsonRules string,
) *SUBController {
sub := NewSubService(showInfo, rModel)
a := &SUBController{ a := &SUBController{
subPath: subPath, subPath: subPath,
subJsonPath: jsonPath, subJsonPath: jsonPath,
subEncrypt: encrypt, subEncrypt: encrypt,
updateInterval: update, updateInterval: update,
subService: NewSubService(showInfo, rModel), subService: sub,
subJsonService: NewSubJsonService(jsonFragment), subJsonService: NewSubJsonService(jsonFragment, jsonMux, jsonRules, sub),
} }
a.initRouter(g) a.initRouter(g)
return a return a
@@ -50,7 +53,6 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) {
} }
func (a *SUBController) subs(c *gin.Context) { func (a *SUBController) subs(c *gin.Context) {
println(c.Request.Header["User-Agent"][0])
subId := c.Param("subid") subId := c.Param("subid")
host := strings.Split(c.Request.Host, ":")[0] host := strings.Split(c.Request.Host, ":")[0]
subs, header, err := a.subService.GetSubs(subId, host) subs, header, err := a.subService.GetSubs(subId, host)
@@ -76,7 +78,6 @@ func (a *SUBController) subs(c *gin.Context) {
} }
func (a *SUBController) subJsons(c *gin.Context) { func (a *SUBController) subJsons(c *gin.Context) {
println(c.Request.Header["User-Agent"][0])
subId := c.Param("subid") subId := c.Param("subid")
host := strings.Split(c.Request.Host, ":")[0] host := strings.Split(c.Request.Host, ":")[0]
jsonSub, header, err := a.subJsonService.GetJson(subId, host) jsonSub, header, err := a.subJsonService.GetJson(subId, host)

View File

@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings" "strings"
"x-ui/database/model" "x-ui/database/model"
"x-ui/logger" "x-ui/logger"
"x-ui/util/json_util" "x-ui/util/json_util"
@@ -17,15 +18,46 @@ import (
var defaultJson string var defaultJson string
type SubJsonService struct { type SubJsonService struct {
fragmanet string configJson map[string]interface{}
defaultOutbounds []json_util.RawMessage
fragment string
mux string
inboundService service.InboundService inboundService service.InboundService
SubService SubService *SubService
} }
func NewSubJsonService(fragment string) *SubJsonService { func NewSubJsonService(fragment string, mux string, rules string, subService *SubService) *SubJsonService {
var configJson map[string]interface{}
var defaultOutbounds []json_util.RawMessage
json.Unmarshal([]byte(defaultJson), &configJson)
if outboundSlices, ok := configJson["outbounds"].([]interface{}); ok {
for _, defaultOutbound := range outboundSlices {
jsonBytes, _ := json.Marshal(defaultOutbound)
defaultOutbounds = append(defaultOutbounds, jsonBytes)
}
}
if rules != "" {
var newRules []interface{}
routing, _ := configJson["routing"].(map[string]interface{})
defaultRules, _ := routing["rules"].([]interface{})
json.Unmarshal([]byte(rules), &newRules)
defaultRules = append(newRules, defaultRules...)
routing["rules"] = defaultRules
configJson["routing"] = routing
}
if fragment != "" {
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment))
}
return &SubJsonService{ return &SubJsonService{
fragmanet: fragment, configJson: configJson,
defaultOutbounds: defaultOutbounds,
fragment: fragment,
mux: mux,
SubService: subService,
} }
} }
@@ -38,19 +70,8 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
var header string var header string
var traffic xray.ClientTraffic var traffic xray.ClientTraffic
var clientTraffics []xray.ClientTraffic var clientTraffics []xray.ClientTraffic
var configJson map[string]interface{} var configArray []json_util.RawMessage
var defaultOutbounds []json_util.RawMessage
json.Unmarshal([]byte(defaultJson), &configJson)
if outboundSlices, ok := configJson["outbounds"].([]interface{}); ok {
for _, defaultOutbound := range outboundSlices {
jsonBytes, _ := json.Marshal(defaultOutbound)
defaultOutbounds = append(defaultOutbounds, jsonBytes)
}
}
outbounds := []json_util.RawMessage{}
startIndex := 0
// Prepare Inbounds // Prepare Inbounds
for _, inbound := range inbounds { for _, inbound := range inbounds {
clients, err := s.inboundService.GetClients(inbound) clients, err := s.inboundService.GetClients(inbound)
@@ -61,7 +82,7 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
continue continue
} }
if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' { if len(inbound.Listen) > 0 && inbound.Listen[0] == '@' {
listen, port, streamSettings, err := s.getFallbackMaster(inbound.Listen, inbound.StreamSettings) listen, port, streamSettings, err := s.SubService.getFallbackMaster(inbound.Listen, inbound.StreamSettings)
if err == nil { if err == nil {
inbound.Listen = listen inbound.Listen = listen
inbound.Port = port inbound.Port = port
@@ -69,22 +90,16 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
} }
} }
var subClients []model.Client
for _, client := range clients { for _, client := range clients {
if client.Enable && client.SubID == subId { if client.Enable && client.SubID == subId {
subClients = append(subClients, client)
clientTraffics = append(clientTraffics, s.SubService.getClientTraffics(inbound.ClientStats, client.Email)) clientTraffics = append(clientTraffics, s.SubService.getClientTraffics(inbound.ClientStats, client.Email))
newConfigs := s.getConfig(inbound, client, host)
configArray = append(configArray, newConfigs...)
} }
} }
outbound := s.getOutbound(inbound, subClients, host, startIndex)
if outbound != nil {
outbounds = append(outbounds, outbound...)
startIndex += len(outbound)
}
} }
if len(outbounds) == 0 { if len(configArray) == 0 {
return "", "", nil return "", "", nil
} }
@@ -111,25 +126,20 @@ func (s *SubJsonService) GetJson(subId string, host string) (string, string, err
} }
} }
if s.fragmanet != "" {
outbounds = append(outbounds, json_util.RawMessage(s.fragmanet))
}
// Combile outbounds // Combile outbounds
outbounds = append(outbounds, defaultOutbounds...) var finalJson []byte
var outboundStrings []json_util.RawMessage if len(configArray) == 1 {
for _, outbound := range outbounds { finalJson, _ = json.MarshalIndent(configArray[0], "", " ")
outboundStrings = append(outboundStrings, outbound) } else {
finalJson, _ = json.MarshalIndent(configArray, "", " ")
} }
configJson["outbounds"] = outboundStrings
finalJson, _ := json.MarshalIndent(configJson, "", " ")
header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000) header = fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000)
return string(finalJson), header, nil return string(finalJson), header, nil
} }
func (s *SubJsonService) getOutbound(inbound *model.Inbound, clients []model.Client, host string, startIndex int) []json_util.RawMessage { func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client, host string) []json_util.RawMessage {
var newOutbounds []json_util.RawMessage var newJsonArray []json_util.RawMessage
stream := s.streamData(inbound.StreamSettings) stream := s.streamData(inbound.StreamSettings)
externalProxies, ok := stream["externalProxy"].([]interface{}) externalProxies, ok := stream["externalProxy"].([]interface{})
@@ -139,13 +149,13 @@ func (s *SubJsonService) getOutbound(inbound *model.Inbound, clients []model.Cli
"forceTls": "same", "forceTls": "same",
"dest": host, "dest": host,
"port": float64(inbound.Port), "port": float64(inbound.Port),
"remark": "",
}, },
} }
} }
delete(stream, "externalProxy") delete(stream, "externalProxy")
config_index := startIndex
for _, ep := range externalProxies { for _, ep := range externalProxies {
extPrxy := ep.(map[string]interface{}) extPrxy := ep.(map[string]interface{})
inbound.Listen = extPrxy["dest"].(string) inbound.Listen = extPrxy["dest"].(string)
@@ -164,21 +174,29 @@ func (s *SubJsonService) getOutbound(inbound *model.Inbound, clients []model.Cli
} }
} }
streamSettings, _ := json.MarshalIndent(newStream, "", " ") streamSettings, _ := json.MarshalIndent(newStream, "", " ")
inbound.StreamSettings = string(streamSettings)
for _, client := range clients { var newOutbounds []json_util.RawMessage
inbound.Tag = fmt.Sprintf("proxy_%d", config_index)
switch inbound.Protocol { switch inbound.Protocol {
case "vmess", "vless": case "vmess", "vless":
newOutbounds = append(newOutbounds, s.genVnext(inbound, client)) newOutbounds = append(newOutbounds, s.genVnext(inbound, streamSettings, client))
case "trojan", "shadowsocks": case "trojan", "shadowsocks":
newOutbounds = append(newOutbounds, s.genServer(inbound, client)) newOutbounds = append(newOutbounds, s.genServer(inbound, streamSettings, client))
}
config_index += 1
} }
newOutbounds = append(newOutbounds, s.defaultOutbounds...)
newConfigJson := make(map[string]interface{})
for key, value := range s.configJson {
newConfigJson[key] = value
}
newConfigJson["outbounds"] = newOutbounds
newConfigJson["remarks"] = s.SubService.genRemark(inbound, client.Email, extPrxy["remark"].(string))
newConfig, _ := json.MarshalIndent(newConfigJson, "", " ")
newJsonArray = append(newJsonArray, newConfig)
} }
return newOutbounds return newJsonArray
} }
func (s *SubJsonService) streamData(stream string) map[string]interface{} { func (s *SubJsonService) streamData(stream string) map[string]interface{} {
@@ -192,8 +210,8 @@ func (s *SubJsonService) streamData(stream string) map[string]interface{} {
} }
delete(streamSettings, "sockopt") delete(streamSettings, "sockopt")
if s.fragmanet != "" { if s.fragment != "" {
streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "TcpNoDelay": true}`) streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "tcpNoDelay": true}`)
} }
// remove proxy protocol // remove proxy protocol
@@ -203,6 +221,8 @@ func (s *SubJsonService) streamData(stream string) map[string]interface{} {
streamSettings["tcpSettings"] = s.removeAcceptProxy(streamSettings["tcpSettings"]) streamSettings["tcpSettings"] = s.removeAcceptProxy(streamSettings["tcpSettings"])
case "ws": case "ws":
streamSettings["wsSettings"] = s.removeAcceptProxy(streamSettings["wsSettings"]) streamSettings["wsSettings"] = s.removeAcceptProxy(streamSettings["wsSettings"])
case "httpupgrade":
streamSettings["httpupgradeSettings"] = s.removeAcceptProxy(streamSettings["httpupgradeSettings"])
} }
return streamSettings return streamSettings
@@ -218,11 +238,11 @@ func (s *SubJsonService) removeAcceptProxy(setting interface{}) map[string]inter
func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interface{} { func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interface{} {
tlsData := make(map[string]interface{}, 1) tlsData := make(map[string]interface{}, 1)
tlsClientSettings := tData["settings"].(map[string]interface{}) tlsClientSettings, _ := tData["settings"].(map[string]interface{})
tlsData["serverName"] = tData["serverName"] tlsData["serverName"] = tData["serverName"]
tlsData["alpn"] = tData["alpn"] tlsData["alpn"] = tData["alpn"]
if allowInsecure, ok := tlsClientSettings["allowInsecure"].(string); ok { if allowInsecure, ok := tlsClientSettings["allowInsecure"].(bool); ok {
tlsData["allowInsecure"] = allowInsecure tlsData["allowInsecure"] = allowInsecure
} }
if fingerprint, ok := tlsClientSettings["fingerprint"].(string); ok { if fingerprint, ok := tlsClientSettings["fingerprint"].(string); ok {
@@ -233,7 +253,7 @@ func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interf
func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]interface{} { func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]interface{} {
rltyData := make(map[string]interface{}, 1) rltyData := make(map[string]interface{}, 1)
rltyClientSettings := rData["settings"].(map[string]interface{}) rltyClientSettings, _ := rData["settings"].(map[string]interface{})
rltyData["show"] = false rltyData["show"] = false
rltyData["publicKey"] = rltyClientSettings["publicKey"] rltyData["publicKey"] = rltyClientSettings["publicKey"]
@@ -257,7 +277,7 @@ func (s *SubJsonService) realityData(rData map[string]interface{}) map[string]in
return rltyData return rltyData
} }
func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) json_util.RawMessage { func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage {
outbound := Outbound{} outbound := Outbound{}
usersData := make([]UserVnext, 1) usersData := make([]UserVnext, 1)
@@ -276,8 +296,11 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) j
} }
outbound.Protocol = string(inbound.Protocol) outbound.Protocol = string(inbound.Protocol)
outbound.Tag = inbound.Tag outbound.Tag = "proxy"
outbound.StreamSettings = json_util.RawMessage(inbound.StreamSettings) if s.mux != "" {
outbound.Mux = json_util.RawMessage(s.mux)
}
outbound.StreamSettings = streamSettings
outbound.Settings = OutboundSettings{ outbound.Settings = OutboundSettings{
Vnext: vnextData, Vnext: vnextData,
} }
@@ -286,7 +309,7 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, client model.Client) j
return result return result
} }
func (s *SubJsonService) genServer(inbound *model.Inbound, client model.Client) json_util.RawMessage { func (s *SubJsonService) genServer(inbound *model.Inbound, streamSettings json_util.RawMessage, client model.Client) json_util.RawMessage {
outbound := Outbound{} outbound := Outbound{}
serverData := make([]ServerSetting, 1) serverData := make([]ServerSetting, 1)
@@ -312,8 +335,11 @@ func (s *SubJsonService) genServer(inbound *model.Inbound, client model.Client)
} }
outbound.Protocol = string(inbound.Protocol) outbound.Protocol = string(inbound.Protocol)
outbound.Tag = inbound.Tag outbound.Tag = "proxy"
outbound.StreamSettings = json_util.RawMessage(inbound.StreamSettings) if s.mux != "" {
outbound.Mux = json_util.RawMessage(s.mux)
}
outbound.StreamSettings = streamSettings
outbound.Settings = OutboundSettings{ outbound.Settings = OutboundSettings{
Servers: serverData, Servers: serverData,
} }
@@ -326,7 +352,7 @@ type Outbound struct {
Protocol string `json:"protocol"` Protocol string `json:"protocol"`
Tag string `json:"tag"` Tag string `json:"tag"`
StreamSettings json_util.RawMessage `json:"streamSettings"` StreamSettings json_util.RawMessage `json:"streamSettings"`
Mux map[string]interface{} `json:"mux,omitempty"` Mux json_util.RawMessage `json:"mux,omitempty"`
ProxySettings map[string]interface{} `json:"proxySettings,omitempty"` ProxySettings map[string]interface{} `json:"proxySettings,omitempty"`
Settings OutboundSettings `json:"settings,omitempty"` Settings OutboundSettings `json:"settings,omitempty"`
} }

View File

@@ -6,6 +6,7 @@ import (
"net/url" "net/url"
"strings" "strings"
"time" "time"
"x-ui/database" "x-ui/database"
"x-ui/database/model" "x-ui/database/model"
"x-ui/logger" "x-ui/logger"
@@ -208,10 +209,15 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
obj["path"], _ = quic["key"].(string) obj["path"], _ = quic["key"].(string)
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
obj["path"] = grpc["serviceName"].(string) obj["path"], _ = grpc["serviceName"].(string)
obj["authority"], _ = grpc["authority"].(string)
if grpc["multiMode"].(bool) { if grpc["multiMode"].(bool) {
obj["type"] = "multi" obj["type"] = "multi"
} }
case "httpupgrade":
httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{})
obj["path"] = httpupgrade["path"].(string)
obj["host"] = httpupgrade["host"].(string)
} }
security, _ := stream["security"].(string) security, _ := stream["security"].(string)
@@ -343,9 +349,14 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
params["serviceName"] = grpc["serviceName"].(string) params["serviceName"] = grpc["serviceName"].(string)
params["authority"], _ = grpc["authority"].(string)
if grpc["multiMode"].(bool) { if grpc["multiMode"].(bool) {
params["mode"] = "multi" params["mode"] = "multi"
} }
case "httpupgrade":
httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{})
params["path"] = httpupgrade["path"].(string)
params["host"] = httpupgrade["host"].(string)
} }
security, _ := stream["security"].(string) security, _ := stream["security"].(string)
@@ -524,9 +535,14 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
params["serviceName"] = grpc["serviceName"].(string) params["serviceName"] = grpc["serviceName"].(string)
params["authority"], _ = grpc["authority"].(string)
if grpc["multiMode"].(bool) { if grpc["multiMode"].(bool) {
params["mode"] = "multi" params["mode"] = "multi"
} }
case "httpupgrade":
httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{})
params["path"] = httpupgrade["path"].(string)
params["host"] = httpupgrade["host"].(string)
} }
security, _ := stream["security"].(string) security, _ := stream["security"].(string)
@@ -701,9 +717,14 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
params["serviceName"] = grpc["serviceName"].(string) params["serviceName"] = grpc["serviceName"].(string)
params["authority"], _ = grpc["authority"].(string)
if grpc["multiMode"].(bool) { if grpc["multiMode"].(bool) {
params["mode"] = "multi" params["mode"] = "multi"
} }
case "httpupgrade":
httpupgrade, _ := stream["httpupgradeSettings"].(map[string]interface{})
params["path"] = httpupgrade["path"].(string)
params["host"] = httpupgrade["host"].(string)
} }
security, _ := stream["security"].(string) security, _ := stream["security"].(string)

View File

@@ -3,6 +3,7 @@ package common
import ( import (
"errors" "errors"
"fmt" "fmt"
"x-ui/logger" "x-ui/logger"
) )

View File

@@ -5,12 +5,14 @@ import (
"time" "time"
) )
var numSeq [10]rune var (
var lowerSeq [26]rune numSeq [10]rune
var upperSeq [26]rune lowerSeq [26]rune
var numLowerSeq [36]rune upperSeq [26]rune
var numUpperSeq [36]rune numLowerSeq [36]rune
var allSeq [62]rune numUpperSeq [36]rune
allSeq [62]rune
)
func init() { func init() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())

View File

@@ -538,7 +538,7 @@
var on = function(emitter, type, f) { var on = function(emitter, type, f) {
if (emitter.addEventListener) { if (emitter.addEventListener) {
emitter.addEventListener(type, f, false); emitter.addEventListener(type, f, { passive: false });
} else if (emitter.attachEvent) { } else if (emitter.attachEvent) {
emitter.attachEvent("on" + type, f); emitter.attachEvent("on" + type, f);
} else { } else {

View File

@@ -683,7 +683,8 @@ style attribute {
border-color: #9ea2a8; border-color: #9ea2a8;
} }
.dark .ant-table-row-expand-icon:hover { .dark .ant-table-row-expand-icon:hover,
.dark .ant-table-thead>tr>th.ant-table-column-has-actions.ant-table-column-has-sorters:hover {
color: #0e49b5; color: #0e49b5;
background-color: #fff0; background-color: #fff0;
border-color: #0e49b5; border-color: #0e49b5;

View File

@@ -14,3 +14,17 @@ axios.interceptors.request.use(
}, },
(error) => Promise.reject(error), (error) => Promise.reject(error),
); );
axios.interceptors.response.use(
(response) => response,
(error) => {
if (error.response) {
const statusCode = error.response.status;
// Check the status code
if (statusCode === 401) { // Unauthorized
return window.location.reload();
}
}
return Promise.reject(error);
}
);

View File

@@ -51,7 +51,14 @@ const OutboundDomainStrategies = [
"AsIs", "AsIs",
"UseIP", "UseIP",
"UseIPv4", "UseIPv4",
"UseIPv6" "UseIPv6",
"UseIPv6v4",
"UseIPv4v6",
"ForceIP",
"ForceIPv6v4",
"ForceIPv6",
"ForceIPv4v6",
"ForceIPv4"
]; ];
const WireguardDomainStrategy = [ const WireguardDomainStrategy = [
@@ -250,24 +257,48 @@ class QuicStreamSettings extends CommonClass {
} }
class GrpcStreamSettings extends CommonClass { class GrpcStreamSettings extends CommonClass {
constructor(serviceName="", multiMode=false) { constructor(serviceName="", authority="", multiMode=false) {
super(); super();
this.serviceName = serviceName; this.serviceName = serviceName;
this.authority = authority;
this.multiMode = multiMode; this.multiMode = multiMode;
} }
static fromJson(json={}) { static fromJson(json={}) {
return new GrpcStreamSettings(json.serviceName, json.multiMode); return new GrpcStreamSettings(json.serviceName, json.authority, json.multiMode);
} }
toJson() { toJson() {
return { return {
serviceName: this.serviceName, serviceName: this.serviceName,
authority: this.authority,
multiMode: this.multiMode, multiMode: this.multiMode,
} }
} }
} }
class HttpUpgradeStreamSettings extends CommonClass {
constructor(path='/', host='') {
super();
this.path = path;
this.host = host;
}
static fromJson(json={}) {
return new HttpUpgradeStreamSettings(
json.path,
json.Host,
);
}
toJson() {
return {
path: this.path,
host: this.host,
};
}
}
class TlsStreamSettings extends CommonClass { class TlsStreamSettings extends CommonClass {
constructor(serverName='', constructor(serverName='',
alpn=[], alpn=[],
@@ -326,7 +357,36 @@ class RealityStreamSettings extends CommonClass {
spiderX: this.spiderX, spiderX: this.spiderX,
}; };
} }
}; }
class SockoptStreamSettings extends CommonClass {
constructor(dialerProxy = "", tcpFastOpen = false, tcpKeepAliveInterval = 0, tcpNoDelay = false) {
super();
this.dialerProxy = dialerProxy;
this.tcpFastOpen = tcpFastOpen;
this.tcpKeepAliveInterval = tcpKeepAliveInterval;
this.tcpNoDelay = tcpNoDelay;
}
static fromJson(json = {}) {
if (Object.keys(json).length === 0) return undefined;
return new SockoptStreamSettings(
json.dialerProxy,
json.tcpFastOpen,
json.tcpKeepAliveInterval,
json.tcpNoDelay,
);
}
toJson() {
return {
dialerProxy: this.dialerProxy,
tcpFastOpen: this.tcpFastOpen,
tcpKeepAliveInterval: this.tcpKeepAliveInterval,
tcpNoDelay: this.tcpNoDelay,
};
}
}
class StreamSettings extends CommonClass { class StreamSettings extends CommonClass {
constructor(network='tcp', constructor(network='tcp',
@@ -339,6 +399,8 @@ class StreamSettings extends CommonClass {
httpSettings=new HttpStreamSettings(), httpSettings=new HttpStreamSettings(),
quicSettings=new QuicStreamSettings(), quicSettings=new QuicStreamSettings(),
grpcSettings=new GrpcStreamSettings(), grpcSettings=new GrpcStreamSettings(),
httpupgradeSettings=new HttpUpgradeStreamSettings(),
sockopt = undefined,
) { ) {
super(); super();
this.network = network; this.network = network;
@@ -351,6 +413,8 @@ class StreamSettings extends CommonClass {
this.http = httpSettings; this.http = httpSettings;
this.quic = quicSettings; this.quic = quicSettings;
this.grpc = grpcSettings; this.grpc = grpcSettings;
this.httpupgrade = httpupgradeSettings;
this.sockopt = sockopt;
} }
get isTls() { get isTls() {
@@ -361,6 +425,14 @@ class StreamSettings extends CommonClass {
return this.security === "reality"; return this.security === "reality";
} }
get sockoptSwitch() {
return this.sockopt != undefined;
}
set sockoptSwitch(value) {
this.sockopt = value ? new SockoptStreamSettings() : undefined;
}
static fromJson(json={}) { static fromJson(json={}) {
return new StreamSettings( return new StreamSettings(
json.network, json.network,
@@ -373,6 +445,8 @@ class StreamSettings extends CommonClass {
HttpStreamSettings.fromJson(json.httpSettings), HttpStreamSettings.fromJson(json.httpSettings),
QuicStreamSettings.fromJson(json.quicSettings), QuicStreamSettings.fromJson(json.quicSettings),
GrpcStreamSettings.fromJson(json.grpcSettings), GrpcStreamSettings.fromJson(json.grpcSettings),
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
SockoptStreamSettings.fromJson(json.sockopt),
); );
} }
@@ -389,6 +463,37 @@ class StreamSettings extends CommonClass {
httpSettings: network === 'http' ? this.http.toJson() : undefined, httpSettings: network === 'http' ? this.http.toJson() : undefined,
quicSettings: network === 'quic' ? this.quic.toJson() : undefined, quicSettings: network === 'quic' ? this.quic.toJson() : undefined,
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
};
}
}
class Mux extends CommonClass {
constructor(enabled = false, concurrency = 8, xudpConcurrency = 16, xudpProxyUDP443 = "reject") {
super();
this.enabled = enabled;
this.concurrency = concurrency;
this.xudpConcurrency = xudpConcurrency;
this.xudpProxyUDP443 = xudpProxyUDP443;
}
static fromJson(json = {}) {
if (Object.keys(json).length === 0) return undefined;
return new Mux(
json.enabled,
json.concurrency,
json.xudpConcurrency,
json.xudpProxyUDP443,
);
}
toJson() {
return {
enabled: this.enabled,
concurrency: this.concurrency,
xudpConcurrency: this.xudpConcurrency,
xudpProxyUDP443: this.xudpProxyUDP443,
}; };
} }
} }
@@ -399,12 +504,16 @@ class Outbound extends CommonClass {
protocol=Protocols.VMess, protocol=Protocols.VMess,
settings=null, settings=null,
streamSettings = new StreamSettings(), streamSettings = new StreamSettings(),
sendThrough,
mux = new Mux(),
) { ) {
super(); super();
this.tag = tag; this.tag = tag;
this._protocol = protocol; this._protocol = protocol;
this.settings = settings == null ? Outbound.Settings.getSettings(protocol) : settings; this.settings = settings == null ? Outbound.Settings.getSettings(protocol) : settings;
this.stream = streamSettings; this.stream = streamSettings;
this.sendThrough = sendThrough;
this.mux = mux;
} }
get protocol() { get protocol() {
@@ -419,7 +528,7 @@ class Outbound extends CommonClass {
canEnableTls() { canEnableTls() {
if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false; if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false;
return ["tcp", "ws", "http", "quic", "grpc"].includes(this.stream.network); return ["tcp", "ws", "http", "quic", "grpc", "httpupgrade"].includes(this.stream.network);
} }
//this is used for xtls-rprx-vision //this is used for xtls-rprx-vision
@@ -439,6 +548,10 @@ class Outbound extends CommonClass {
return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol); return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol);
} }
canEnableMux() {
return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks, Protocols.HTTP, Protocols.Socks].includes(this.protocol);
}
hasVnext() { hasVnext() {
return [Protocols.VMess, Protocols.VLESS].includes(this.protocol); return [Protocols.VMess, Protocols.VLESS].includes(this.protocol);
} }
@@ -469,15 +582,26 @@ class Outbound extends CommonClass {
json.protocol, json.protocol,
Outbound.Settings.fromJson(json.protocol, json.settings), Outbound.Settings.fromJson(json.protocol, json.settings),
StreamSettings.fromJson(json.streamSettings), StreamSettings.fromJson(json.streamSettings),
json.sendThrough,
Mux.fromJson(json.mux),
) )
} }
toJson() { toJson() {
var stream;
if (this.canEnableStream()) {
stream = this.stream.toJson();
} else {
if (this.stream?.sockopt)
stream = { sockopt: this.stream.sockopt.toJson() };
}
return { return {
tag: this.tag == '' ? undefined : this.tag, tag: this.tag == '' ? undefined : this.tag,
protocol: this.protocol, protocol: this.protocol,
settings: this.settings instanceof CommonClass ? this.settings.toJson() : this.settings, settings: this.settings instanceof CommonClass ? this.settings.toJson() : this.settings,
streamSettings: this.canEnableStream() ? this.stream.toJson() : undefined, streamSettings: stream,
sendThrough: this.sendThrough != "" ? this.sendThrough : undefined,
mux: this.mux?.enabled ? this.mux : undefined,
}; };
} }
@@ -522,7 +646,9 @@ class Outbound extends CommonClass {
json.path, json.path,
json.type ? json.type : 'none'); json.type ? json.type : 'none');
} else if (network === 'grpc') { } else if (network === 'grpc') {
stream.grpc = new GrpcStreamSettings(json.path, json.type == 'multi'); stream.grpc = new GrpcStreamSettings(json.path, json.authority, json.type == 'multi');
} else if (network === 'httpupgrade') {
stream.httpupgrade = new HttpUpgradeStreamSettings(json.path,json.host);
} }
if(json.tls && json.tls == 'tls'){ if(json.tls && json.tls == 'tls'){
@@ -533,21 +659,22 @@ class Outbound extends CommonClass {
json.allowInsecure); json.allowInsecure);
} }
const port = json.port * 1;
return new Outbound(json.ps, Protocols.VMess, new Outbound.VmessSettings(json.add, json.port, json.id), stream); return new Outbound(json.ps, Protocols.VMess, new Outbound.VmessSettings(json.add, port, json.id), stream);
} }
static fromParamLink(link){ static fromParamLink(link){
const url = new URL(link); const url = new URL(link);
let type = url.searchParams.get('type'); let type = url.searchParams.get('type') ?? 'tcp';
let security = url.searchParams.get('security') ?? 'none'; let security = url.searchParams.get('security') ?? 'none';
let stream = new StreamSettings(type, security); let stream = new StreamSettings(type, security);
let headerType = url.searchParams.get('headerType'); let headerType = url.searchParams.get('headerType') ?? undefined;
let host = url.searchParams.get('host'); let host = url.searchParams.get('host') ?? undefined;
let path = url.searchParams.get('path'); let path = url.searchParams.get('path') ?? undefined;
if (type === 'tcp') { if (type === 'tcp' || type === 'none') {
stream.tcp = new TcpStreamSettings(headerType ?? 'none', host, path); stream.tcp = new TcpStreamSettings(headerType ?? 'none', host, path);
} else if (type === 'kcp') { } else if (type === 'kcp') {
stream.kcp = new KcpStreamSettings(); stream.kcp = new KcpStreamSettings();
@@ -563,7 +690,12 @@ class Outbound extends CommonClass {
url.searchParams.get('key') ?? '', url.searchParams.get('key') ?? '',
headerType ?? 'none'); headerType ?? 'none');
} else if (type === 'grpc') { } else if (type === 'grpc') {
stream.grpc = new GrpcStreamSettings(url.searchParams.get('serviceName') ?? '', url.searchParams.get('mode') == 'multi'); stream.grpc = new GrpcStreamSettings(
url.searchParams.get('serviceName') ?? '',
url.searchParams.get('authority') ?? '',
url.searchParams.get('mode') == 'multi');
} else if (type === 'httpupgrade') {
stream.httpupgrade = new HttpUpgradeStreamSettings(path,host);
} }
if(security == 'tls'){ if(security == 'tls'){
@@ -583,10 +715,7 @@ class Outbound extends CommonClass {
stream.reality = new RealityStreamSettings(pbk, fp, sni, sid, spx); stream.reality = new RealityStreamSettings(pbk, fp, sni, sid, spx);
} }
let data = link.split('?'); const regex = /([^@]+):\/\/([^@]+)@(.+):(\d+)(.*)$/;
if(data.length != 2) return null;
const regex = /([^@]+):\/\/([^@]+)@([^:]+):(\d+)\?(.*)$/;
const match = link.match(regex); const match = link.match(regex);
if (!match) return null; if (!match) return null;
@@ -955,7 +1084,7 @@ Outbound.WireguardSettings = class extends CommonClass {
address: this.address ? this.address.split(",") : [], address: this.address ? this.address.split(",") : [],
workers: this.workers?? undefined, workers: this.workers?? undefined,
domainStrategy: WireguardDomainStrategy.includes(this.domainStrategy) ? this.domainStrategy : undefined, domainStrategy: WireguardDomainStrategy.includes(this.domainStrategy) ? this.domainStrategy : undefined,
reserved: this.reserved ? this.reserved.split(",") : undefined, reserved: this.reserved ? this.reserved.split(",").map(Number) : undefined,
peers: Outbound.WireguardSettings.Peer.toJsonArray(this.peers), peers: Outbound.WireguardSettings.Peer.toJsonArray(this.peers),
kernelMode: this.kernelMode, kernelMode: this.kernelMode,
}; };

View File

@@ -31,9 +31,11 @@ class AllSetting {
this.subUpdates = 0; this.subUpdates = 0;
this.subEncrypt = true; this.subEncrypt = true;
this.subShowInfo = false; this.subShowInfo = false;
this.subURI = ''; this.subURI = "";
this.subJsonURI = ''; this.subJsonURI = "";
this.subJsonFragment = ''; this.subJsonFragment = "";
this.subJsonMux = "";
this.subJsonRules = "";
this.timeLocation = "Asia/Tehran"; this.timeLocation = "Asia/Tehran";

View File

@@ -435,24 +435,51 @@ class QuicStreamSettings extends XrayCommonClass {
} }
class GrpcStreamSettings extends XrayCommonClass { class GrpcStreamSettings extends XrayCommonClass {
constructor(serviceName="", multiMode=false) { constructor(serviceName="", authority="", multiMode=false) {
super(); super();
this.serviceName = serviceName; this.serviceName = serviceName;
this.authority = authority;
this.multiMode = multiMode; this.multiMode = multiMode;
} }
static fromJson(json={}) { static fromJson(json={}) {
return new GrpcStreamSettings(json.serviceName, json.multiMode); return new GrpcStreamSettings(json.serviceName, json.authority, json.multiMode);
} }
toJson() { toJson() {
return { return {
serviceName: this.serviceName, serviceName: this.serviceName,
authority: this.authority,
multiMode: this.multiMode, multiMode: this.multiMode,
} }
} }
} }
class HttpUpgradeStreamSettings extends XrayCommonClass {
constructor(acceptProxyProtocol=false, path='/', host='') {
super();
this.acceptProxyProtocol = acceptProxyProtocol;
this.path = path;
this.host = host;
}
static fromJson(json={}) {
return new HttpUpgradeStreamSettings(
json.acceptProxyProtocol,
json.path,
json.host,
);
}
toJson() {
return {
acceptProxyProtocol: this.acceptProxyProtocol,
path: this.path,
host: this.host,
};
}
}
class TlsStreamSettings extends XrayCommonClass { class TlsStreamSettings extends XrayCommonClass {
constructor(serverName='', constructor(serverName='',
minVersion = TLS_VERSION_OPTION.TLS12, minVersion = TLS_VERSION_OPTION.TLS12,
@@ -705,6 +732,7 @@ class StreamSettings extends XrayCommonClass {
httpSettings=new HttpStreamSettings(), httpSettings=new HttpStreamSettings(),
quicSettings=new QuicStreamSettings(), quicSettings=new QuicStreamSettings(),
grpcSettings=new GrpcStreamSettings(), grpcSettings=new GrpcStreamSettings(),
httpupgradeSettings=new HttpUpgradeStreamSettings(),
sockopt = undefined, sockopt = undefined,
) { ) {
super(); super();
@@ -719,6 +747,7 @@ class StreamSettings extends XrayCommonClass {
this.http = httpSettings; this.http = httpSettings;
this.quic = quicSettings; this.quic = quicSettings;
this.grpc = grpcSettings; this.grpc = grpcSettings;
this.httpupgrade = httpupgradeSettings;
this.sockopt = sockopt; this.sockopt = sockopt;
} }
@@ -767,6 +796,7 @@ class StreamSettings extends XrayCommonClass {
HttpStreamSettings.fromJson(json.httpSettings), HttpStreamSettings.fromJson(json.httpSettings),
QuicStreamSettings.fromJson(json.quicSettings), QuicStreamSettings.fromJson(json.quicSettings),
GrpcStreamSettings.fromJson(json.grpcSettings), GrpcStreamSettings.fromJson(json.grpcSettings),
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
SockoptStreamSettings.fromJson(json.sockopt), SockoptStreamSettings.fromJson(json.sockopt),
); );
} }
@@ -785,6 +815,7 @@ class StreamSettings extends XrayCommonClass {
httpSettings: network === 'http' ? this.http.toJson() : undefined, httpSettings: network === 'http' ? this.http.toJson() : undefined,
quicSettings: network === 'quic' ? this.quic.toJson() : undefined, quicSettings: network === 'quic' ? this.quic.toJson() : undefined,
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined, sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
}; };
} }
@@ -889,6 +920,10 @@ class Inbound extends XrayCommonClass {
return this.network === "http"; return this.network === "http";
} }
get isHttpupgrade() {
return this.network === "httpupgrade";
}
// Shadowsocks // Shadowsocks
get method() { get method() {
switch (this.protocol) { switch (this.protocol) {
@@ -918,6 +953,8 @@ class Inbound extends XrayCommonClass {
return this.stream.ws.getHeader("Host"); return this.stream.ws.getHeader("Host");
} else if (this.isH2) { } else if (this.isH2) {
return this.stream.http.host[0]; return this.stream.http.host[0];
} else if (this.isHttpupgrade) {
return this.stream.httpupgrade.host;
} }
return null; return null;
} }
@@ -929,6 +966,8 @@ class Inbound extends XrayCommonClass {
return this.stream.ws.path; return this.stream.ws.path;
} else if (this.isH2) { } else if (this.isH2) {
return this.stream.http.path; return this.stream.http.path;
} else if (this.isHttpupgrade) {
return this.stream.httpupgrade.path;
} }
return null; return null;
} }
@@ -964,7 +1003,7 @@ class Inbound extends XrayCommonClass {
canEnableTls() { canEnableTls() {
if(![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false; if(![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false;
return ["tcp", "ws", "http", "quic", "grpc"].includes(this.network); return ["tcp", "ws", "http", "quic", "grpc", "httpupgrade"].includes(this.network);
} }
//this is used for xtls-rprx-vision //this is used for xtls-rprx-vision
@@ -1042,9 +1081,14 @@ class Inbound extends XrayCommonClass {
obj.path = this.stream.quic.key; obj.path = this.stream.quic.key;
} else if (network === 'grpc') { } else if (network === 'grpc') {
obj.path = this.stream.grpc.serviceName; obj.path = this.stream.grpc.serviceName;
obj.authority = this.stream.grpc.authority;
if (this.stream.grpc.multiMode){ if (this.stream.grpc.multiMode){
obj.type = 'multi' obj.type = 'multi'
} }
} else if (network === 'httpupgrade') {
let httpupgrade = this.stream.httpupgrade;
obj.path = httpupgrade.path;
obj.host = httpupgrade.host;
} }
if (security === 'tls') { if (security === 'tls') {
@@ -1113,10 +1157,16 @@ class Inbound extends XrayCommonClass {
case "grpc": case "grpc":
const grpc = this.stream.grpc; const grpc = this.stream.grpc;
params.set("serviceName", grpc.serviceName); params.set("serviceName", grpc.serviceName);
params.set("authority", grpc.authority);
if(grpc.multiMode){ if(grpc.multiMode){
params.set("mode", "multi"); params.set("mode", "multi");
} }
break; break;
case "httpupgrade":
const httpupgrade = this.stream.httpupgrade;
params.set("path", httpupgrade.path);
params.set("host", httpupgrade.host);
break;
} }
if (security === 'tls') { if (security === 'tls') {
@@ -1215,10 +1265,16 @@ class Inbound extends XrayCommonClass {
case "grpc": case "grpc":
const grpc = this.stream.grpc; const grpc = this.stream.grpc;
params.set("serviceName", grpc.serviceName); params.set("serviceName", grpc.serviceName);
params.set("authority", grpc.authority);
if(grpc.multiMode){ if(grpc.multiMode){
params.set("mode", "multi"); params.set("mode", "multi");
} }
break; break;
case "httpupgrade":
const httpupgrade = this.stream.httpupgrade;
params.set("path", httpupgrade.path);
params.set("host", httpupgrade.host);
break;
} }
if (security === 'tls') { if (security === 'tls') {
@@ -1296,10 +1352,16 @@ class Inbound extends XrayCommonClass {
case "grpc": case "grpc":
const grpc = this.stream.grpc; const grpc = this.stream.grpc;
params.set("serviceName", grpc.serviceName); params.set("serviceName", grpc.serviceName);
params.set("authority", grpc.authority);
if(grpc.multiMode){ if(grpc.multiMode){
params.set("mode", "multi"); params.set("mode", "multi");
} }
break; break;
case "httpupgrade":
const httpupgrade = this.stream.httpupgrade;
params.set("path", httpupgrade.path);
params.set("host", httpupgrade.host);
break;
} }
if (security === 'tls') { if (security === 'tls') {

View File

@@ -22,81 +22,35 @@ func (a *APIController) initRouter(g *gin.RouterGroup) {
g = g.Group("/xui/API/inbounds") g = g.Group("/xui/API/inbounds")
g.Use(a.checkLogin) g.Use(a.checkLogin)
g.GET("/", a.inbounds)
g.GET("/get/:id", a.inbound)
g.GET("/getClientTraffics/:email", a.getClientTraffics)
g.POST("/add", a.addInbound)
g.POST("/del/:id", a.delInbound)
g.POST("/update/:id", a.updateInbound)
g.POST("/addClient", a.addInboundClient)
g.POST("/:id/delClient/:clientId", a.delInboundClient)
g.POST("/updateClient/:clientId", a.updateInboundClient)
g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic)
g.POST("/resetAllTraffics", a.resetAllTraffics)
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
g.POST("/delDepletedClients/:id", a.delDepletedClients)
g.GET("/createbackup", a.createBackup)
g.POST("/onlines", a.onlines)
a.inboundController = NewInboundController(g) a.inboundController = NewInboundController(g)
}
func (a *APIController) inbounds(c *gin.Context) { inboundRoutes := []struct {
a.inboundController.getInbounds(c) Method string
} Path string
Handler gin.HandlerFunc
}{
{"GET", "/createbackup", a.createBackup},
{"GET", "/", a.inboundController.getInbounds},
{"GET", "/get/:id", a.inboundController.getInbound},
{"GET", "/getClientTraffics/:email", a.inboundController.getClientTraffics},
{"POST", "/add", a.inboundController.addInbound},
{"POST", "/del/:id", a.inboundController.delInbound},
{"POST", "/update/:id", a.inboundController.updateInbound},
{"POST", "/addClient", a.inboundController.addInboundClient},
{"POST", "/:id/delClient/:clientId", a.inboundController.delInboundClient},
{"POST", "/updateClient/:clientId", a.inboundController.updateInboundClient},
{"POST", "/:id/resetClientTraffic/:email", a.inboundController.resetClientTraffic},
{"POST", "/resetAllTraffics", a.inboundController.resetAllTraffics},
{"POST", "/resetAllClientTraffics/:id", a.inboundController.resetAllClientTraffics},
{"POST", "/delDepletedClients/:id", a.inboundController.delDepletedClients},
{"POST", "/onlines", a.inboundController.onlines},
}
func (a *APIController) inbound(c *gin.Context) { for _, route := range inboundRoutes {
a.inboundController.getInbound(c) g.Handle(route.Method, route.Path, route.Handler)
} }
func (a *APIController) getClientTraffics(c *gin.Context) {
a.inboundController.getClientTraffics(c)
}
func (a *APIController) addInbound(c *gin.Context) {
a.inboundController.addInbound(c)
}
func (a *APIController) delInbound(c *gin.Context) {
a.inboundController.delInbound(c)
}
func (a *APIController) updateInbound(c *gin.Context) {
a.inboundController.updateInbound(c)
}
func (a *APIController) addInboundClient(c *gin.Context) {
a.inboundController.addInboundClient(c)
}
func (a *APIController) delInboundClient(c *gin.Context) {
a.inboundController.delInboundClient(c)
}
func (a *APIController) updateInboundClient(c *gin.Context) {
a.inboundController.updateInboundClient(c)
}
func (a *APIController) resetClientTraffic(c *gin.Context) {
a.inboundController.resetClientTraffic(c)
}
func (a *APIController) resetAllTraffics(c *gin.Context) {
a.inboundController.resetAllTraffics(c)
}
func (a *APIController) resetAllClientTraffics(c *gin.Context) {
a.inboundController.resetAllClientTraffics(c)
}
func (a *APIController) delDepletedClients(c *gin.Context) {
a.inboundController.delDepletedClients(c)
} }
func (a *APIController) createBackup(c *gin.Context) { func (a *APIController) createBackup(c *gin.Context) {
a.Tgbot.SendBackupToAdmins() a.Tgbot.SendBackupToAdmins()
} }
func (a *APIController) onlines(c *gin.Context) {
a.inboundController.onlines(c)
}

View File

@@ -2,6 +2,7 @@ package controller
import ( import (
"net/http" "net/http"
"x-ui/logger" "x-ui/logger"
"x-ui/web/locale" "x-ui/web/locale"
"x-ui/web/session" "x-ui/web/session"
@@ -9,13 +10,12 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
type BaseController struct { type BaseController struct{}
}
func (a *BaseController) checkLogin(c *gin.Context) { func (a *BaseController) checkLogin(c *gin.Context) {
if !session.IsLogin(c) { if !session.IsLogin(c) {
if isAjax(c) { if isAjax(c) {
pureJsonMsg(c, false, I18nWeb(c, "pages.login.loginAgain")) pureJsonMsg(c, http.StatusUnauthorized, false, I18nWeb(c, "pages.login.loginAgain"))
} else { } else {
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path")) c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
} }

View File

@@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"strconv" "strconv"
"x-ui/database/model" "x-ui/database/model"
"x-ui/web/service" "x-ui/web/service"
"x-ui/web/session" "x-ui/web/session"
@@ -63,6 +64,7 @@ func (a *InboundController) getInbound(c *gin.Context) {
} }
jsonObj(c, inbound, nil) jsonObj(c, inbound, nil)
} }
func (a *InboundController) getClientTraffics(c *gin.Context) { func (a *InboundController) getClientTraffics(c *gin.Context) {
email := c.Param("email") email := c.Param("email")
clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email) clientTraffics, err := a.inboundService.GetClientTrafficByEmail(email)
@@ -82,7 +84,11 @@ func (a *InboundController) addInbound(c *gin.Context) {
} }
user := session.GetLoginUser(c) user := session.GetLoginUser(c)
inbound.UserId = user.Id inbound.UserId = user.Id
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
} else {
inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
}
needRestart := false needRestart := false
inbound, needRestart, err = a.inboundService.AddInbound(inbound) inbound, needRestart, err = a.inboundService.AddInbound(inbound)
@@ -144,7 +150,7 @@ func (a *InboundController) addInboundClient(c *gin.Context) {
return return
} }
jsonMsg(c, "Client(s) added", nil) jsonMsg(c, "Client(s) added", nil)
if err == nil && needRestart { if needRestart {
a.xrayService.SetToNeedRestart() a.xrayService.SetToNeedRestart()
} }
} }
@@ -165,7 +171,7 @@ func (a *InboundController) delInboundClient(c *gin.Context) {
return return
} }
jsonMsg(c, "Client deleted", nil) jsonMsg(c, "Client deleted", nil)
if err == nil && needRestart { if needRestart {
a.xrayService.SetToNeedRestart() a.xrayService.SetToNeedRestart()
} }
} }
@@ -188,7 +194,7 @@ func (a *InboundController) updateInboundClient(c *gin.Context) {
return return
} }
jsonMsg(c, "Client updated", nil) jsonMsg(c, "Client updated", nil)
if err == nil && needRestart { if needRestart {
a.xrayService.SetToNeedRestart() a.xrayService.SetToNeedRestart()
} }
} }
@@ -209,7 +215,7 @@ func (a *InboundController) resetClientTraffic(c *gin.Context) {
return return
} }
jsonMsg(c, "traffic reseted", nil) jsonMsg(c, "traffic reseted", nil)
if err == nil && needRestart { if needRestart {
a.xrayService.SetToNeedRestart() a.xrayService.SetToNeedRestart()
} }
} }
@@ -266,7 +272,11 @@ func (a *InboundController) importInbound(c *gin.Context) {
user := session.GetLoginUser(c) user := session.GetLoginUser(c)
inbound.Id = 0 inbound.Id = 0
inbound.UserId = user.Id inbound.UserId = user.Id
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
} else {
inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
}
for index := range inbound.ClientStats { for index := range inbound.ClientStats {
inbound.ClientStats[index].Id = 0 inbound.ClientStats[index].Id = 0

View File

@@ -3,6 +3,7 @@ package controller
import ( import (
"net/http" "net/http"
"time" "time"
"x-ui/logger" "x-ui/logger"
"x-ui/web/service" "x-ui/web/service"
"x-ui/web/session" "x-ui/web/session"
@@ -47,15 +48,15 @@ func (a *IndexController) login(c *gin.Context) {
var form LoginForm var form LoginForm
err := c.ShouldBind(&form) err := c.ShouldBind(&form)
if err != nil { if err != nil {
pureJsonMsg(c, false, I18nWeb(c, "pages.login.toasts.invalidFormData")) pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.invalidFormData"))
return return
} }
if form.Username == "" { if form.Username == "" {
pureJsonMsg(c, false, I18nWeb(c, "pages.login.toasts.emptyUsername")) pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.emptyUsername"))
return return
} }
if form.Password == "" { if form.Password == "" {
pureJsonMsg(c, false, I18nWeb(c, "pages.login.toasts.emptyPassword")) pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.emptyPassword"))
return return
} }
@@ -64,7 +65,7 @@ func (a *IndexController) login(c *gin.Context) {
if user == nil { if user == nil {
logger.Infof("wrong username or password: \"%s\" \"%s\"", form.Username, form.Password) logger.Infof("wrong username or password: \"%s\" \"%s\"", form.Username, form.Password)
a.tgbot.UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0) a.tgbot.UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0)
pureJsonMsg(c, 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 login success ,Ip Address: %s\n", form.Username, getRemoteIp(c))

View File

@@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"regexp" "regexp"
"time" "time"
"x-ui/web/global" "x-ui/web/global"
"x-ui/web/service" "x-ui/web/service"

View File

@@ -3,6 +3,7 @@ package controller
import ( import (
"errors" "errors"
"time" "time"
"x-ui/web/entity" "x-ui/web/entity"
"x-ui/web/service" "x-ui/web/service"
"x-ui/web/session" "x-ui/web/session"

View File

@@ -4,6 +4,7 @@ import (
"net" "net"
"net/http" "net/http"
"strings" "strings"
"x-ui/config" "x-ui/config"
"x-ui/logger" "x-ui/logger"
"x-ui/web/entity" "x-ui/web/entity"
@@ -48,18 +49,11 @@ func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) {
c.JSON(http.StatusOK, m) c.JSON(http.StatusOK, m)
} }
func pureJsonMsg(c *gin.Context, success bool, msg string) { func pureJsonMsg(c *gin.Context, statusCode int, success bool, msg string) {
if success { c.JSON(statusCode, entity.Msg{
c.JSON(http.StatusOK, entity.Msg{ Success: success,
Success: true, Msg: msg,
Msg: msg, })
})
} else {
c.JSON(http.StatusOK, entity.Msg{
Success: false,
Msg: msg,
})
}
} }
func html(c *gin.Context, name string, title string, data gin.H) { func html(c *gin.Context, name string, title string, data gin.H) {

View File

@@ -5,6 +5,7 @@ import (
"net" "net"
"strings" "strings"
"time" "time"
"x-ui/util/common" "x-ui/util/common"
) )
@@ -49,6 +50,8 @@ type AllSetting struct {
SubJsonPath string `json:"subJsonPath" form:"subJsonPath"` SubJsonPath string `json:"subJsonPath" form:"subJsonPath"`
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"` SubJsonURI string `json:"subJsonURI" form:"subJsonURI"`
SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"` SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"`
SubJsonMux string `json:"subJsonMux" form:"subJsonMux"`
SubJsonRules string `json:"subJsonRules" form:"subJsonRules"`
} }
func (s *AllSetting) CheckValid() error { func (s *AllSetting) CheckValid() error {

View File

@@ -7,8 +7,10 @@ import (
"github.com/robfig/cron/v3" "github.com/robfig/cron/v3"
) )
var webServer WebServer var (
var subServer SubServer webServer WebServer
subServer SubServer
)
type WebServer interface { type WebServer interface {
GetCron() *cron.Cron GetCron() *cron.Cron

View File

@@ -1,6 +1,7 @@
{{define "promptModal"}} {{define "promptModal"}}
<a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title" <a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title"
:closable="true" @ok="promptModal.ok" :mask-closable="false" :closable="true" @ok="promptModal.ok" :mask-closable="false"
:confirm-loading="promptModal.confirmLoading"
:ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}' :class="themeSwitcher.currentTheme"> :ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}' :class="themeSwitcher.currentTheme">
<a-input id="prompt-modal-input" :type="promptModal.type" <a-input id="prompt-modal-input" :type="promptModal.type"
v-model="promptModal.value" v-model="promptModal.value"
@@ -17,6 +18,7 @@
value: '', value: '',
okText: '{{ i18n "sure"}}', okText: '{{ i18n "sure"}}',
visible: false, visible: false,
confirmLoading: false,
keyEnter(e) { keyEnter(e) {
if (this.type !== 'textarea') { if (this.type !== 'textarea') {
e.preventDefault(); e.preventDefault();
@@ -30,7 +32,6 @@
} }
}, },
ok() { ok() {
promptModal.close();
promptModal.confirm(promptModal.value); promptModal.confirm(promptModal.value);
}, },
confirm() {}, confirm() {},
@@ -53,7 +54,10 @@
}, },
close() { close() {
this.visible = false; this.visible = false;
} },
loading(loading=true) {
this.confirmLoading = loading;
},
}; };
const promptModalApp = new Vue({ const promptModalApp = new Vue({

View File

@@ -97,7 +97,6 @@
<a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;"> <a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;">
<a-row type="flex" justify="center"> <a-row type="flex" justify="center">
<a-col> <a-col>
<h1 class="title" style="margin-bottom: 5px; font-size: 24px;">X-UI</h1>
<h2 class="title" style="text-align: center;">{{ i18n "pages.login.title" }}</h2> <h2 class="title" style="text-align: center;">{{ i18n "pages.login.title" }}</h2>
</a-col> </a-col>
</a-row> </a-row>

View File

@@ -15,7 +15,7 @@
<a-input :value="value" @input="$emit('input', $event.target.value)" :placeholder="placeholder"></a-input> <a-input :value="value" @input="$emit('input', $event.target.value)" :placeholder="placeholder"></a-input>
</template> </template>
<template v-else-if="type === 'number'"> <template v-else-if="type === 'number'">
<a-input-number :value="value" @change="value => $emit('input', value)" :min="min" :step="step" style="width: 100%;"></a-input-number> <a-input-number :value="value" @change="value => $emit('input', value)" :min="min" :max="max" :step="step" style="width: 100%;"></a-input-number>
</template> </template>
<template v-else-if="type === 'switch'"> <template v-else-if="type === 'switch'">
<a-switch :checked="value" @change="value => $emit('input', value)"></a-switch> <a-switch :checked="value" @change="value => $emit('input', value)"></a-switch>
@@ -28,7 +28,7 @@
{{define "component/setting"}} {{define "component/setting"}}
<script> <script>
Vue.component('setting-list-item', { Vue.component('setting-list-item', {
props: ["type", "title", "desc", "value", "min", "step", "placeholder"], props: ["type", "title", "desc", "value", "min", "max", "step", "placeholder"],
template: `{{template "component/settingListItem"}}`, template: `{{template "component/settingListItem"}}`,
}); });
</script> </script>

View File

@@ -11,6 +11,9 @@
<a-form-item label='{{ i18n "pages.xray.outbound.tag" }}' has-feedback :validate-status="outModal.duplicateTag? 'warning' : 'success'"> <a-form-item label='{{ i18n "pages.xray.outbound.tag" }}' has-feedback :validate-status="outModal.duplicateTag? 'warning' : 'success'">
<a-input v-model.trim="outbound.tag" @change="outModal.check()" placeholder='{{ i18n "pages.xray.outbound.tagDesc" }}'></a-input> <a-input v-model.trim="outbound.tag" @change="outModal.check()" placeholder='{{ i18n "pages.xray.outbound.tagDesc" }}'></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.xray.outbound.sendThrough" }}'>
<a-input v-model="outbound.sendThrough"></a-input>
</a-form-item>
<!-- freedom settings--> <!-- freedom settings-->
<template v-if="outbound.protocol === Protocols.Freedom"> <template v-if="outbound.protocol === Protocols.Freedom">
@@ -214,31 +217,32 @@
<!-- stream settings --> <!-- stream settings -->
<template v-if="outbound.canEnableStream()"> <template v-if="outbound.canEnableStream()">
<a-form-item label='{{ i18n "transmission" }}'> <a-form-item label='{{ i18n "transmission" }}'>
<a-select v-model="outbound.stream.network" @change="streamNetworkChange" <a-select v-model="outbound.stream.network" @change="streamNetworkChange"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="tcp">TCP</a-select-option> <a-select-option value="tcp">TCP</a-select-option>
<a-select-option value="kcp">mKCP</a-select-option> <a-select-option value="kcp">mKCP</a-select-option>
<a-select-option value="ws">WebSocket</a-select-option> <a-select-option value="ws">WebSocket</a-select-option>
<a-select-option value="http">HTTP/2</a-select-option> <a-select-option value="http">HTTP/2</a-select-option>
<a-select-option value="quic">QUIC</a-select-option> <a-select-option value="quic">QUIC</a-select-option>
<a-select-option value="grpc">gRPC</a-select-option> <a-select-option value="grpc">gRPC</a-select-option>
</a-select> <a-select-option value="httpupgrade">HttpUpgrade</a-select-option>
</a-form-item> </a-select>
</a-select>
</a-form-item>
<template v-if="outbound.stream.network === 'tcp'"> <template v-if="outbound.stream.network === 'tcp'">
<a-form-item label='HTTP {{ i18n "camouflage" }}'> <a-form-item label='HTTP {{ i18n "camouflage" }}'>
<a-switch <a-switch :checked="outbound.stream.tcp.type === 'http'"
:checked="outbound.stream.tcp.type === 'http'"
@change="checked => outbound.stream.tcp.type = checked ? 'http' : 'none'"> @change="checked => outbound.stream.tcp.type = checked ? 'http' : 'none'">
</a-switch> </a-switch>
</a-form-item> </a-form-item>
<template v-if="outbound.stream.tcp.type == 'http'"> <template v-if="outbound.stream.tcp.type == 'http'">
<a-form-item label='{{ i18n "host" }}'> <a-form-item label='{{ i18n "host" }}'>
<a-input v-model.trim="outbound.stream.tcp.host"></a-input> <a-input v-model.trim="outbound.stream.tcp.host"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "path" }}'> <a-form-item label='{{ i18n "path" }}'>
<a-input v-model.trim="outbound.stream.tcp.path"></a-input> <a-input v-model.trim="outbound.stream.tcp.path"></a-input>
</a-form-item> </a-form-item>
</template> </template>
</template> </template>
@@ -252,6 +256,7 @@
<a-select-option value="wechat-video">WeChat</a-select-option> <a-select-option value="wechat-video">WeChat</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option> <a-select-option value="dtls">DTLS 1.2</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option> <a-select-option value="wireguard">WireGuard</a-select-option>
<a-select-option value="dns">DNS</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "password" }}'> <a-form-item label='{{ i18n "password" }}'>
@@ -329,10 +334,23 @@
<a-form-item label='Service Name'> <a-form-item label='Service Name'>
<a-input v-model.trim="outbound.stream.grpc.serviceName"></a-input> <a-input v-model.trim="outbound.stream.grpc.serviceName"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='Authority'>
<a-input v-model.trim="outbound.stream.grpc.authority"></a-input>
</a-form-item>
<a-form-item label='Multi Mode'> <a-form-item label='Multi Mode'>
<a-switch v-model="outbound.stream.grpc.multiMode"></a-switch> <a-switch v-model="outbound.stream.grpc.multiMode"></a-switch>
</a-form-item> </a-form-item>
</template> </template>
<!-- httpupgrade -->
<template v-if="outbound.stream.network === 'httpupgrade'">
<a-form-item label='{{ i18n "host" }}'>
<a-input v-model="outbound.stream.httpupgrade.host"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "path" }}'>
<a-form-item><a-input v-model.trim="outbound.stream.httpupgrade.path"></a-input>
</a-form-item>
</template>
</template> </template>
<!-- tls settings --> <!-- tls settings -->
@@ -391,6 +409,48 @@
</a-form-item> </a-form-item>
</template> </template>
</template> </template>
<!-- sockopt settings -->
<a-form-item label="Sockopts">
<a-switch v-model="outbound.stream.sockoptSwitch"></a-switch>
</a-form-item>
<template v-if="outbound.stream.sockoptSwitch">
<a-form-item label="Dialer Proxy">
<a-select v-model="outbound.stream.sockopt.dialerProxy" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="tag in ['', ...outModal.tags]" :value="tag">[[ tag ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="TCP Fast Open">
<a-switch v-model="outbound.stream.sockopt.tcpFastOpen"></a-switch>
</a-form-item>
<a-form-item label="Keep Alive Interval">
<a-input-number v-model="outbound.stream.sockopt.tcpKeepAliveInterval" :min="0"></a-input-number>
</a-form-item>
<a-form-item label="TCP No-Delay">
<a-switch v-model="outbound.stream.sockopt.tcpNoDelay"></a-switch>
</a-form-item>
</template>
<!-- mux settings -->
<template v-if="outbound.canEnableMux()">
<a-form-item label="Mux">
<a-switch v-model="outbound.mux.enabled"></a-switch>
</a-form-item>
<template v-if="outbound.mux.enabled">
<a-form-item label="Concurrency">
<a-input-number v-model="outbound.mux.concurrency" :min="-1" :max="1024"></a-input-number>
</a-form-item>
<a-form-item label="xudp Concurrency">
<a-input-number v-model="outbound.mux.xudpConcurrency" :min="-1" :max="1024"></a-input-number>
</a-form-item>
<a-form-item label="xudp UDP 443">
<a-select v-model="outbound.mux.xudpProxyUDP443" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="c in ['reject', 'allow', 'skip']" :value="c">[[ c ]]</a-select-option>
</a-select>
</a-form-item>
</template>
</template>
</a-form> </a-form>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="2" tab="JSON" force-render="true"> <a-tab-pane key="2" tab="JSON" force-render="true">

View File

@@ -15,11 +15,11 @@
</a-select> </a-select>
</a-tooltip> </a-tooltip>
</template> </template>
<a-input style="width: 35%" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input> <a-input style="width: 35%; border-radius: 0;" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input>
<a-tooltip title='{{ i18n "pages.inbounds.port" }}'> <a-tooltip title='{{ i18n "pages.inbounds.port" }}'>
<a-input-number style="width: 15%;" v-model.number="row.port" min="1" max="65531"></a-input-number> <a-input-number style="width: 15%;" v-model.number="row.port" min="1" max="65531"></a-input-number>
</a-tooltip> </a-tooltip>
<a-input style="width: 20%" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'></a-input> <a-input style="width: 20%; border-radius: 0;" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'></a-input>
<a-button style="width: 10%; margin: 0px" @click="inbound.stream.externalProxy.splice(index, 1)">-</a-button> <a-button style="width: 10%; margin: 0px" @click="inbound.stream.externalProxy.splice(index, 1)">-</a-button>
</a-input-group> </a-input-group>
</a-form> </a-form>

View File

@@ -3,6 +3,9 @@
<a-form-item label="Service Name"> <a-form-item label="Service Name">
<a-input v-model.trim="inbound.stream.grpc.serviceName" style="width: 250px;"></a-input> <a-input v-model.trim="inbound.stream.grpc.serviceName" style="width: 250px;"></a-input>
</a-form-item> </a-form-item>
<a-form-item label="Authority">
<a-input v-model.trim="inbound.stream.grpc.authority"></a-input>
</a-form-item>
<a-form-item label="Multi Mode"> <a-form-item label="Multi Mode">
<a-switch v-model="inbound.stream.grpc.multiMode"></a-switch> <a-switch v-model="inbound.stream.grpc.multiMode"></a-switch>
</a-form-item> </a-form-item>

View File

@@ -0,0 +1,13 @@
{{define "form/streamHTTPUPGRADE"}}
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label="PROXY Protocol">
<a-switch v-model="inbound.stream.httpupgrade.acceptProxyProtocol"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "path" }}'>
<a-input v-model.trim="inbound.stream.httpupgrade.path"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "host" }}'>
<a-input v-model.trim="inbound.stream.httpupgrade.host"></a-input>
</a-form-item>
</a-form>
{{end}}

View File

@@ -8,6 +8,7 @@
<a-select-option value="wechat-video">WeChat</a-select-option> <a-select-option value="wechat-video">WeChat</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option> <a-select-option value="dtls">DTLS 1.2</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option> <a-select-option value="wireguard">WireGuard</a-select-option>
<a-select-option value="dns">DNS</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>

View File

@@ -10,6 +10,7 @@
<a-select-option value="http">HTTP/2</a-select-option> <a-select-option value="http">HTTP/2</a-select-option>
<a-select-option value="quic">QUIC</a-select-option> <a-select-option value="quic">QUIC</a-select-option>
<a-select-option value="grpc">gRPC</a-select-option> <a-select-option value="grpc">gRPC</a-select-option>
<a-select-option value="httpupgrade">HttpUpgrade</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-form> </a-form>
@@ -43,6 +44,12 @@
<template v-if="inbound.stream.network === 'grpc'"> <template v-if="inbound.stream.network === 'grpc'">
{{template "form/streamGRPC"}} {{template "form/streamGRPC"}}
</template> </template>
<!-- httpupgrade -->
<template v-if="inbound.stream.network === 'httpupgrade'">
{{template "form/streamHTTPUPGRADE"}}
</template>
<!-- sockopt --> <!-- sockopt -->
<template> <template>
{{template "form/streamSockopt"}} {{template "form/streamSockopt"}}

View File

@@ -54,7 +54,7 @@
<template v-else-if="!client.enable">{{ i18n "disabled" }}</template> <template v-else-if="!client.enable">{{ i18n "disabled" }}</template>
<template v-else-if="isClientOnline(client.email)">{{ i18n "online" }}</template> <template v-else-if="isClientOnline(client.email)">{{ i18n "online" }}</template>
</template> </template>
<a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"> <a-badge :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'">
</a-badge> </a-badge>
</a-tooltip> </a-tooltip>
[[ client.email ]] [[ client.email ]]
@@ -86,13 +86,12 @@
<td width="120px" v-else-if="client.totalGB > 0"> <td width="120px" v-else-if="client.totalGB > 0">
<a-progress :stroke-color="clientStatsColor(record, client.email)" <a-progress :stroke-color="clientStatsColor(record, client.email)"
:show-info="false" :show-info="false"
:status="isClientOnline(client.email)? 'active' : isClientEnabled(record, client.email)? 'exception' : ''" :status="isClientEnabled(record, client.email)? 'exception' : ''"
:percent="statsProgress(record, client.email)"/> :percent="statsProgress(record, client.email)"/>
</td> </td>
<td width="120px" v-else class="infinite-bar"> <td width="120px" v-else class="infinite-bar">
<a-progress <a-progress
:show-info="false" :show-info="false"
:status="isClientOnline(client.email)? 'active' : ''"
:percent="100"></a-progress> :percent="100"></a-progress>
</td> </td>
<td width="60px"> <td width="60px">
@@ -117,7 +116,7 @@
</td> </td>
<td width="120px" class="infinite-bar"> <td width="120px" class="infinite-bar">
<a-progress :show-info="false" <a-progress :show-info="false"
:status="isClientOnline(client.email)? 'active' : isClientEnabled(record, client.email)? 'exception' : ''" :status="isClientEnabled(record, client.email)? 'exception' : ''"
:percent="expireProgress(client.expiryTime, client.reset)"/> :percent="expireProgress(client.expiryTime, client.reset)"/>
</td> </td>
<td width="60px">[[ client.reset + "d" ]]</td> <td width="60px">[[ client.reset + "d" ]]</td>
@@ -202,14 +201,13 @@
</template> </template>
<a-progress :stroke-color="clientStatsColor(record, client.email)" <a-progress :stroke-color="clientStatsColor(record, client.email)"
:show-info="false" :show-info="false"
:status="isClientOnline(client.email)? 'active' : isClientEnabled(record, client.email)? 'exception' : ''" :status="isClientEnabled(record, client.email)? 'exception' : ''"
:percent="statsProgress(record, client.email)"/> :percent="statsProgress(record, client.email)"/>
</a-popover> </a-popover>
</td> </td>
<td width="120px" v-else class="infinite-bar"> <td width="120px" v-else class="infinite-bar">
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? '#2c1e32':'#F2EAF1'" <a-progress :stroke-color="themeSwitcher.isDarkTheme ? '#2c1e32':'#F2EAF1'"
:show-info="false" :show-info="false"
:status="isClientOnline(client.email)? 'active' : ''"
:percent="100"></a-progress> :percent="100"></a-progress>
</td> </td>
<td width="80px"> <td width="80px">
@@ -235,7 +233,7 @@
<span v-else>[[ DateUtil.formatMillis(client._expiryTime) ]]</span> <span v-else>[[ DateUtil.formatMillis(client._expiryTime) ]]</span>
</template> </template>
<a-progress :show-info="false" <a-progress :show-info="false"
:status="isClientOnline(client.email)? 'active' : isClientEnabled(record, client.email)? 'exception' : ''" :status="isClientEnabled(record, client.email)? 'exception' : ''"
:percent="expireProgress(client.expiryTime, client.reset)"/> :percent="expireProgress(client.expiryTime, client.reset)"/>
</a-popover> </a-popover>
</td> </td>

View File

@@ -25,7 +25,7 @@
<tr> <tr>
<td>{{ i18n "transmission" }}</td><td><a-tag color="blue">[[ inbound.network ]]</a-tag></td> <td>{{ i18n "transmission" }}</td><td><a-tag color="blue">[[ inbound.network ]]</a-tag></td>
</tr> </tr>
<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2"> <template v-if="inbound.isTcp || inbound.isWs || inbound.isH2 || inbound.isHttpupgrade">
<tr> <tr>
<td>{{ i18n "host" }}</td> <td>{{ i18n "host" }}</td>
<td v-if="inbound.host"> <td v-if="inbound.host">
@@ -139,7 +139,7 @@
<tr> <tr>
<td> <td>
<a-tag v-if="infoModal.clientStats && infoModal.clientSettings.totalGB > 0" :color="statsColor(infoModal.clientStats)"> <a-tag v-if="infoModal.clientStats && infoModal.clientSettings.totalGB > 0" :color="statsColor(infoModal.clientStats)">
[[ sizeFormat(infoModal.clientSettings.totalGB - infoModal.clientStats.up - infoModal.clientStats.down) ]] [[ getRemStats() ]]
</a-tag> </a-tag>
</td> </td>
<td> <td>
@@ -423,7 +423,11 @@
}, },
statsColor(stats) { statsColor(stats) {
return usageColor(stats.up + stats.down, app.trafficDiff, stats.total); return usageColor(stats.up + stats.down, app.trafficDiff, stats.total);
} },
getRemStats() {
remained = this.infoModal.clientStats.total - this.infoModal.clientStats.up - this.infoModal.clientStats.down;
return remained>0 ? sizeFormat(remained) : '-';
},
}, },
}); });

View File

@@ -36,13 +36,6 @@
.ant-collapse { .ant-collapse {
margin: 5px 0; margin: 5px 0;
} }
.online-animation .ant-badge-status-dot {
animation: 1.2s ease infinite normal none running onlineAnimation;
}
@keyframes onlineAnimation {
0%, 50%, 100% { transform: scale(1); opacity: 1; }
10% { transform: scale(1.5); opacity: .2; }
}
.info-large-tag { .info-large-tag {
max-width: 200px; max-width: 200px;
overflow: hidden; overflow: hidden;
@@ -185,7 +178,7 @@
</a-radio-group> </a-radio-group>
</div> </div>
<a-back-top></a-back-top> <a-back-top></a-back-top>
<a-table :columns="isMobile ? mobileColums : columns" :row-key="dbInbound => dbInbound.id" <a-table :columns="isMobile ? mobileColumns : columns" :row-key="dbInbound => dbInbound.id"
:data-source="searchedInbounds" :data-source="searchedInbounds"
:scroll="isMobile ? {} : { x: 1000 }" :scroll="isMobile ? {} : { x: 1000 }"
:pagination=pagination(searchedInbounds) :pagination=pagination(searchedInbounds)
@@ -502,7 +495,7 @@
scopedSlots: { customRender: 'expiryTime' }, scopedSlots: { customRender: 'expiryTime' },
}]; }];
const mobileColums = [{ const mobileColumns = [{
title: "ID", title: "ID",
align: 'right', align: 'right',
dataIndex: "id", dataIndex: "id",
@@ -526,19 +519,73 @@
}]; }];
const innerColumns = [ const innerColumns = [
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 50, scopedSlots: { customRender: 'actions' } }, {
{ title: '{{ i18n "pages.inbounds.enable" }}', width: 20, scopedSlots: { customRender: 'enable' } }, title: '{{ i18n "pages.inbounds.operate" }}',
{ title: '{{ i18n "online" }}', width: 20, scopedSlots: { customRender: 'online' } }, width: 50,
{ title: '{{ i18n "pages.inbounds.client" }}', width: 70, scopedSlots: { customRender: 'client' } }, scopedSlots: { customRender: 'actions' },
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 80, align: 'center', scopedSlots: { customRender: 'traffic' } }, },
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 70, align: 'center', scopedSlots: { customRender: 'expiryTime' } }, {
]; title: '{{ i18n "pages.inbounds.enable" }}',
width: 20,
scopedSlots: { customRender: 'enable' },
},
{
title: '{{ i18n "online" }}',
width: 20,
scopedSlots: { customRender: 'online' },
},
{
title: '{{ i18n "pages.inbounds.client" }}',
dataIndex: 'email',
width: 70,
scopedSlots: { customRender: 'client' },
sorter: (a, b) => {
const clientA = a.email || '';
const clientB = b.email || '';
return clientA.localeCompare(clientB, undefined, { sensitivity: 'base' });
},
},
{
title: '{{ i18n "pages.inbounds.traffic" }}',
dataIndex: 'traffic',
width: 80,
align: 'center',
scopedSlots: { customRender: 'traffic' },
},
{
title: '{{ i18n "pages.inbounds.expireDate" }}',
width: 70,
align: 'center',
scopedSlots: { customRender: 'expiryTime' },
},
];
const innerMobileColumns = [ const innerMobileColumns = [
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 10, align: 'center', scopedSlots: { customRender: 'actionMenu' } }, {
{ title: '{{ i18n "pages.inbounds.client" }}', width: 90, align: 'left', scopedSlots: { customRender: 'client' } }, title: '{{ i18n "pages.inbounds.operate" }}',
{ title: '{{ i18n "pages.inbounds.info" }}', width: 10, align: 'center', scopedSlots: { customRender: 'info' } }, width: 10,
]; align: 'center',
scopedSlots: { customRender: 'actionMenu' },
},
{
title: '{{ i18n "pages.inbounds.client" }}',
dataIndex: 'email',
width: 90,
align: 'left',
scopedSlots: { customRender: 'client' },
sorter: (a, b) => {
const clientA = a.email || '';
const clientB = b.email || '';
return clientA.localeCompare(clientB, undefined, { sensitivity: 'base' });
},
},
{
title: '{{ i18n "pages.inbounds.info" }}',
width: 10,
align: 'center',
scopedSlots: { customRender: 'info' },
},
];
const app = new Vue({ const app = new Vue({
delimiters: ['[[', ']]'], delimiters: ['[[', ']]'],
@@ -804,9 +851,7 @@
okText: '{{ i18n "pages.inbounds.create"}}', okText: '{{ i18n "pages.inbounds.create"}}',
cancelText: '{{ i18n "close" }}', cancelText: '{{ i18n "close" }}',
confirm: async (inbound, dbInbound) => { confirm: async (inbound, dbInbound) => {
inModal.loading(); await this.addInbound(inbound, dbInbound, inModal);
await this.addInbound(inbound, dbInbound);
inModal.close();
}, },
isEdit: false isEdit: false
}); });
@@ -821,9 +866,7 @@
inbound: inbound, inbound: inbound,
dbInbound: dbInbound, dbInbound: dbInbound,
confirm: async (inbound, dbInbound) => { confirm: async (inbound, dbInbound) => {
inModal.loading();
await this.updateInbound(inbound, dbInbound); await this.updateInbound(inbound, dbInbound);
inModal.close();
}, },
isEdit: true isEdit: true
}); });
@@ -906,9 +949,7 @@
okText: '{{ i18n "pages.client.submitAdd"}}', okText: '{{ i18n "pages.client.submitAdd"}}',
dbInbound: dbInbound, dbInbound: dbInbound,
confirm: async (clients, dbInboundId) => { confirm: async (clients, dbInboundId) => {
clientModal.loading(); await this.addClient(clients, dbInboundId, clientModal);
await this.addClient(clients, dbInboundId);
clientModal.close();
}, },
isEdit: false isEdit: false
}); });
@@ -920,9 +961,7 @@
okText: '{{ i18n "pages.client.bulk"}}', okText: '{{ i18n "pages.client.bulk"}}',
dbInbound: dbInbound, dbInbound: dbInbound,
confirm: async (clients, dbInboundId) => { confirm: async (clients, dbInboundId) => {
clientsBulkModal.loading(); await this.addClient(clients, dbInboundId, clientsBulkModal);
await this.addClient(clients, dbInboundId);
clientsBulkModal.close();
}, },
}); });
}, },
@@ -951,19 +990,19 @@
default: return clients.findIndex(item => item.id === client.id && item.email === client.email); default: return clients.findIndex(item => item.id === client.id && item.email === client.email);
} }
}, },
async addClient(clients, dbInboundId) { async addClient(clients, dbInboundId, modal) {
const data = { const data = {
id: dbInboundId, id: dbInboundId,
settings: '{"clients": [' + clients.toString() + ']}', settings: '{"clients": [' + clients.toString() + ']}',
}; };
await this.submit(`/xui/inbound/addClient`, data); await this.submit(`/xui/inbound/addClient`, data, modal);
}, },
async updateClient(client, dbInboundId, clientId) { async updateClient(client, dbInboundId, clientId) {
const data = { const data = {
id: dbInboundId, id: dbInboundId,
settings: '{"clients": [' + client.toString() + ']}', settings: '{"clients": [' + client.toString() + ']}',
}; };
await this.submit(`/xui/inbound/updateClient/${clientId}`, data); await this.submit(`/xui/inbound/updateClient/${clientId}`, data, clientModal);
}, },
resetTraffic(dbInboundId) { resetTraffic(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
@@ -1066,8 +1105,8 @@
await this.updateClient(clients[index], dbInboundId, clientId); await this.updateClient(clients[index], dbInboundId, clientId);
this.loading(false); this.loading(false);
}, },
async submit(url, data) { async submit(url, data, modal) {
const msg = await HttpUtil.postWithModal(url, data); const msg = await HttpUtil.postWithModal(url, data, modal);
if (msg.success) { if (msg.success) {
await this.getDBInbounds(); await this.getDBInbounds();
} }
@@ -1226,7 +1265,6 @@
okText: '{{ i18n "pages.inbounds.import" }}', okText: '{{ i18n "pages.inbounds.import" }}',
confirm: async (dbInboundText) => { confirm: async (dbInboundText) => {
await this.submit('/xui/inbound/import', {data: dbInboundText}, promptModal); await this.submit('/xui/inbound/import', {data: dbInboundText}, promptModal);
promptModal.close();
}, },
}); });
}, },

View File

@@ -311,7 +311,7 @@
</a-form-item> </a-form-item>
<a-form-item style="float: right;"> <a-form-item style="float: right;">
<a-button type="primary" icon="download" <a-button type="primary" icon="download"
:href="'data:application/text;charset=utf-8,' + encodeURIComponent(logModal.logs)" download="x-ui.log"> :href="'data:application/text;charset=utf-8,' + encodeURIComponent(logModal.logs?.join('\n'))" download="x-ui.log">
</a-button> </a-button>
</a-form-item> </a-form-item>
</a-form> </a-form>
@@ -440,7 +440,7 @@
const logModal = { const logModal = {
visible: false, visible: false,
logs: '', logs: [],
rows: 20, rows: 20,
level: 'info', level: 'info',
syslog: false, syslog: false,

View File

@@ -44,23 +44,17 @@
<a-layout-content> <a-layout-content>
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'> <a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
<transition name="list" appear> <transition name="list" appear>
<a-alert type="error" v-if="showAlert" style="margin-bottom: 10px" <a-alert type="error" v-if="confAlerts.length>0" style="margin: 10px 5px;"
message='{{ i18n "secAlertTitle" }}' message='{{ i18n "secAlertTitle" }}'
color="red" color="red"
description='{{ i18n "secAlertSsl" }}' show-icon
show-icon closable closable
>
</a-alert>
<a-alert type="error" v-if="confAlerts.length>0" style="margin-bottom: 10px"
message='{{ i18n "secAlertTitle" }}'
color="red"
show-icon closable
> >
<template slot="description"> <template slot="description">
{{ i18n "secAlertConf" }} <b>{{ i18n "secAlertConf" }}</b>
<li v-for="a in confAlerts">- [[ a ]]</li> <ul><li v-for="a in confAlerts">[[ a ]]</li></ul>
</template> </template>
</a-alert> </a-alert>
</transition> </transition>
<a-space direction="vertical"> <a-space direction="vertical">
<a-card hoverable style="margin-bottom: .5rem;"> <a-card hoverable style="margin-bottom: .5rem;">
@@ -142,7 +136,7 @@
</a-col> </a-col>
</a-row> </a-row>
</a-list-item> </a-list-item>
</a-list> </a-list>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="2" tab='{{ i18n "pages.settings.userSettings"}}'> <a-tab-pane key="2" tab='{{ i18n "pages.settings.userSettings"}}'>
<a-form layout="horizontal" :colon="false" style="float: left; margin: 10px 0;" :label-col="{ md: {span:10} }" :wrapper-col="{ md: {span:14} }"> <a-form layout="horizontal" :colon="false" style="float: left; margin: 10px 0;" :label-col="{ md: {span:10} }" :wrapper-col="{ md: {span:14} }">
@@ -177,7 +171,6 @@
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<a-list-item-meta title="Telegram Bot Language" /> <a-list-item-meta title="Telegram Bot Language" />
</a-col> </a-col>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<template> <template>
<a-select <a-select
@@ -216,11 +209,62 @@
<setting-list-item type="text" title='{{ i18n "pages.settings.subPath"}}' desc='{{ i18n "pages.settings.subPathDesc"}}' v-model="allSetting.subJsonPath"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.subPath"}}' desc='{{ i18n "pages.settings.subPathDesc"}}' v-model="allSetting.subJsonPath"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.subURI"}}' desc='{{ i18n "pages.settings.subURIDesc"}}' v-model="allSetting.subJsonURI" placeholder="(http|https)://domain[:port]/path/"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.subURI"}}' desc='{{ i18n "pages.settings.subURIDesc"}}' v-model="allSetting.subJsonURI" placeholder="(http|https)://domain[:port]/path/"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.settings.fragment"}}' desc='{{ i18n "pages.settings.fragmentDesc"}}' v-model="fragment"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.settings.fragment"}}' desc='{{ i18n "pages.settings.fragmentDesc"}}' v-model="fragment"></setting-list-item>
<template v-if="fragment"> <setting-list-item type="switch" title='Mux' v-model="enableMux"></setting-list-item>
<setting-list-item type="text" title='length' v-model="fragmentLength" placeholder="100-200"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.directCountryConfigs"}}' desc='{{ i18n "pages.xray.directCountryConfigsDesc"}}' v-model="enableDirect"></setting-list-item>
<setting-list-item type="text" title='Interval' v-model="fragmentInterval" placeholder="10-20"></setting-list-item>
</template>
</a-list> </a-list>
<a-collapse>
<a-collapse-panel header='{{ i18n "pages.settings.fragment"}}' v-if="fragment">
<a-list-item style="padding: 20px">
<a-row>
<a-col :lg="24" :xl="12">
<a-list-item-meta title='Packets'/>
</a-col>
<a-col :lg="24" :xl="12">
<a-select
v-model="fragmentPackets"
style="width: 100%"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option :value="p" :label="p" v-for="p in ['1-1', '1-3', 'tlshello']">
[[ p ]]
</a-select-option>
</a-select>
</a-col>
</a-row>
</a-list-item>
<setting-list-item type="text" title='Length' v-model="fragmentLength" placeholder="100-200"></setting-list-item>
<setting-list-item type="text" title='Interval' v-model="fragmentInterval" placeholder="10-20"></setting-list-item>
</a-collapse-panel>
<a-collapse-panel header='Mux' v-if="enableMux">
<setting-list-item type="number" title='Concurrency' v-model="muxConcurrency" :min="-1" :max="1024"></setting-list-item>
<setting-list-item type="number" title='xudp Concurrency' v-model="muxXudpConcurrency" :min="-1" :max="1024"></setting-list-item>
<a-list-item style="padding: 20px">
<a-row>
<a-col :lg="24" :xl="12">
<a-list-item-meta title='xudp UDP 443'/>
</a-col>
<a-col :lg="24" :xl="12">
<a-select
v-model="muxXudpProxyUDP443"
style="width: 100%"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option :value="p" :label="p" v-for="p in ['reject', 'allow', 'skip']">
[[ p ]]
</a-select-option>
</a-select>
</a-col>
</a-row>
</a-list-item>
</a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.directCountryConfigs"}}' v-if="enableDirect">
<a-list-item style="padding: 20px">
<a-checkbox-group
v-model="directCountries"
name="Countries"
:options="countryOptions"
/>
</a-list-item>
</a-collapse-panel>
</a-collapse>
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
</a-space> </a-space>
@@ -247,7 +291,6 @@
saveBtnDisable: true, saveBtnDisable: true,
user: {}, user: {},
lang: getLang(), lang: getLang(),
showAlert: false,
remarkModels: {i:'Inbound',e:'Email',o:'Other'}, remarkModels: {i:'Inbound',e:'Email',o:'Other'},
remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'], remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'],
remarkSample: '', remarkSample: '',
@@ -265,10 +308,44 @@
streamSettings: { streamSettings: {
sockopt: { sockopt: {
tcpKeepAliveIdle: 100, tcpKeepAliveIdle: 100,
TcpNoDelay: true tcpNoDelay: true
} }
} }
}, },
defaultMux: {
enabled: true,
concurrency: 8,
xudpConcurrency: 16,
xudpProxyUDP443: "reject"
},
defaultRules: [
{
type: "field",
outboundTag: "direct",
domain: [
"geosite:category-ir",
"geosite:cn"
],
"enabled": true
},
{
type: "field",
outboundTag: "direct",
ip: [
"geoip:private",
"geoip:ir",
"geoip:cn"
],
enabled: true
},
],
countryOptions: [
{ label: 'Private IP/Domain', value: 'private' },
{ label: '🇮🇷 Iran', value: 'ir' },
{ label: '🇨🇳 China', value: 'cn' },
{ label: '🇷🇺 Russia', value: 'ru' },
{ label: '🇻🇳 Vietnam', value: 'vn' },
],
get remarkModel() { get remarkModel() {
rm = this.allSetting.remarkModel; rm = this.allSetting.remarkModel;
return rm.length>1 ? rm.substring(1).split('') : []; return rm.length>1 ? rm.substring(1).split('') : [];
@@ -356,6 +433,16 @@
this.allSetting.subJsonFragment = v ? JSON.stringify(this.defaultFragment) : ""; this.allSetting.subJsonFragment = v ? JSON.stringify(this.defaultFragment) : "";
} }
}, },
fragmentPackets: {
get: function() { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.packets : ""; },
set: function(v) {
if (v != ""){
newFragment = JSON.parse(this.allSetting.subJsonFragment);
newFragment.settings.fragment.packets = v;
this.allSetting.subJsonFragment = JSON.stringify(newFragment);
}
}
},
fragmentLength: { fragmentLength: {
get: function() { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.length : ""; }, get: function() { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.length : ""; },
set: function(v) { set: function(v) {
@@ -376,27 +463,80 @@
} }
} }
}, },
enableMux: {
get: function() { return this.allSetting?.subJsonMux != ""; },
set: function (v) {
this.allSetting.subJsonMux = v ? JSON.stringify(this.defaultMux) : "";
}
},
muxConcurrency: {
get: function() { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).concurrency : -1; },
set: function(v) {
newMux = JSON.parse(this.allSetting.subJsonMux);
newMux.concurrency = v;
this.allSetting.subJsonMux = JSON.stringify(newMux);
}
},
muxXudpConcurrency: {
get: function() { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpConcurrency : -1; },
set: function(v) {
newMux = JSON.parse(this.allSetting.subJsonMux);
newMux.xudpConcurrency = v;
this.allSetting.subJsonMux = JSON.stringify(newMux);
}
},
muxXudpProxyUDP443: {
get: function() { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpProxyUDP443 : "reject"; },
set: function(v) {
newMux = JSON.parse(this.allSetting.subJsonMux);
newMux.xudpProxyUDP443 = v;
this.allSetting.subJsonMux = JSON.stringify(newMux);
}
},
enableDirect: {
get: function() { return this.allSetting?.subJsonRules != ""; },
set: function (v) {
this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
}
},
directCountries: {
get: function() {
if (!this.enableDirect) return [];
rules = JSON.parse(this.allSetting.subJsonRules);
return Array.isArray(rules) ? rules[1].ip.map(d => d.replace("geoip:","")) : [];
},
set: function (v) {
rules = JSON.parse(this.allSetting.subJsonRules);
if (!Array.isArray(rules)) return;
rules[0].domain = [];
rules[1].ip = [];
v.forEach(d => {
category = ["cn","private"].includes(d) ? "" : "category-";
rules[0].domain.push("geosite:"+category+d);
rules[1].ip.push("geoip:"+d);
});
this.allSetting.subJsonRules = JSON.stringify(rules);
}
},
confAlerts: { confAlerts: {
get: function() { get: function() {
if (!this.allSetting) return []; if (!this.allSetting) return [];
var alerts = [] var alerts = []
if (this.allSetting.port == 54321) alerts.push('{{ i18n "pages.settings.panelPort"}}'); if (window.location.protocol !== "https:") alerts.push('{{ i18n "secAlertSSL" }}');
if (this.allSetting.webPort == 54321) alerts.push('{{ i18n "secAlertPanelPort" }}');
panelPath = window.location.pathname.split('/').length<4 panelPath = window.location.pathname.split('/').length<4
if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "pages.settings.panelConfig"}} {{ i18n "pages.settings.panelUrlPath"}}'); if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "secAlertPanelURI" }}');
if (this.allSetting.subEnable) { if (this.allSetting.subEnable) {
subPath = this.allSetting.subURI.length >0 ? new URL(this.allSetting.subURI).pathname : this.allSetting.subPath; subPath = this.allSetting.subURI.length >0 ? new URL(this.allSetting.subURI).pathname : this.allSetting.subPath;
if (subPath == '/sub/') alerts.push('{{ i18n "pages.settings.subSettings"}} {{ i18n "pages.settings.subPath"}}'); if (subPath == '/sub/') alerts.push('{{ i18n "secAlertSubURI" }}');
subJsonPath = this.allSetting.subJsonURI.length >0 ? new URL(this.allSetting.subJsonURI).pathname : this.allSetting.subJsonPath; subJsonPath = this.allSetting.subJsonURI.length >0 ? new URL(this.allSetting.subJsonURI).pathname : this.allSetting.subJsonPath;
if (subJsonPath == '/json/') alerts.push('JSON {{ i18n "pages.settings.subPath"}}'); if (subJsonPath == '/json/') alerts.push('{{ i18n "secAlertSubJsonURI" }}');
} }
return alerts return alerts
} }
} }
}, },
async mounted() { async mounted() {
if (window.location.protocol !== "https:") {
this.showAlert = true;
}
await this.getAllSetting(); await this.getAllSetting();
while (true) { while (true) {
await PromiseUtil.sleep(1000); await PromiseUtil.sleep(1000);

View File

@@ -1,14 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
{{template "head" .}} {{template "head" .}}
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/codemirror.css"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/codemirror.css?{{ .cur_ver }}">
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/fold/foldgutter.css"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/fold/foldgutter.css">
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.css?{{ .cur_ver }}"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.css?{{ .cur_ver }}">
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css">
<script src="{{ .base_path }}assets/base64/base64.min.js"></script> <script src="{{ .base_path }}assets/base64/base64.min.js"></script>
<script src="{{ .base_path }}assets/js/model/outbound.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/outbound.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/codemirror/codemirror.js"></script> <script src="{{ .base_path }}assets/codemirror/codemirror.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/codemirror/javascript.js"></script> <script src="{{ .base_path }}assets/codemirror/javascript.js"></script>
<script src="{{ .base_path }}assets/codemirror/jshint.js"></script> <script src="{{ .base_path }}assets/codemirror/jshint.js"></script>
<script src="{{ .base_path }}assets/codemirror/jsonlint.js"></script> <script src="{{ .base_path }}assets/codemirror/jsonlint.js"></script>
@@ -104,8 +104,8 @@
</a-col> </a-col>
</a-row> </a-row>
</a-card> </a-card>
<a-tabs default-active-key="1" @change="(activeKey) => { if(activeKey == 'tpl-advanced') this.changeCode(); }"> <a-tabs default-active-key="1" @change="(activeKey) => { this.changePage(activeKey); }">
<a-tab-pane key="tpl-1" tab='{{ i18n "pages.xray.basicTemplate"}}' style="padding-top: 20px;"> <a-tab-pane key="tpl-basic" tab='{{ i18n "pages.xray.basicTemplate"}}' style="padding-top: 20px;">
<a-collapse> <a-collapse>
<a-collapse-panel header='{{ i18n "pages.xray.generalConfigs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.generalConfigs"}}'>
<a-row :xs="24" :sm="24" :lg="12"> <a-row :xs="24" :sm="24" :lg="12">
@@ -162,7 +162,8 @@
<a-list-item> <a-list-item>
<a-row style="padding: 20px"> <a-row style="padding: 20px">
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<a-list-item-meta title='Level'/> <a-list-item-meta title='{{ i18n "pages.xray.logLevel" }}'
description='{{ i18n "pages.xray.logLevelDesc" }}' />
</a-col> </a-col>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<template> <template>
@@ -177,7 +178,8 @@
</a-list-item> </a-list-item>
<a-row style="padding: 20px"> <a-row style="padding: 20px">
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<a-list-item-meta title='Access Logs' /> <a-list-item-meta title='{{ i18n "pages.xray.accessLog" }}'
description='{{ i18n "pages.xray.accessLogDesc" }}' />
</a-col> </a-col>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<a-input v-model="logAccess"></a-input> <a-input v-model="logAccess"></a-input>
@@ -185,7 +187,8 @@
</a-row> </a-row>
<a-row style="padding: 20px"> <a-row style="padding: 20px">
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<a-list-item-meta title='Error Logs' /> <a-list-item-meta title='{{ i18n "pages.xray.errorLog" }}'
description='{{ i18n "pages.xray.errorLogDesc" }}' />
</a-col> </a-col>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<a-input v-model="logError"></a-input> <a-input v-model="logError"></a-input>
@@ -267,18 +270,16 @@
<setting-list-item type="switch" title='{{ i18n "pages.xray.SpotifyWARP"}}' desc='{{ i18n "pages.xray.SpotifyWARPDesc"}}' v-model="SpotifyWARPSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.SpotifyWARP"}}' desc='{{ i18n "pages.xray.SpotifyWARPDesc"}}' v-model="SpotifyWARPSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.MetaWARP"}}' desc='{{ i18n "pages.xray.MetaWARPDesc"}}' v-model="MetaWARPSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.MetaWARP"}}' desc='{{ i18n "pages.xray.MetaWARPDesc"}}' v-model="MetaWARPSettings"></setting-list-item>
</template> </template>
<a-button v-else style="margin: 10px 0;" @click="showWarp">WARP {{ i18n "pages.xray.rules.outbound" }}</a-button> <a-button v-else type="primary" icon="cloud" style="margin: 15px 20px;" @click="showWarp()">WARP</a-button>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.settings.resetDefaultConfig"}}'> <a-collapse-panel header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
<a-space direction="horizontal" style="padding: 0 20px"> <a-space direction="horizontal" style="padding: 0 20px">
<a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button> <a-button type="danger" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
</a-space> </a-space>
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-2" tab='{{ i18n "pages.xray.Routings"}}' style="padding-top: 20px;"> <a-tab-pane key="tpl-routing" tab='{{ i18n "pages.xray.Routings"}}' style="padding-top: 20px;">
<a-alert type="warning" style="margin-bottom: 10px; width: fit-content"
message='{{ i18n "pages.xray.RoutingsDesc"}}' show-icon></a-alert>
<a-button type="primary" icon="plus" @click="addRule">{{ i18n "pages.xray.rules.add" }}</a-button> <a-button type="primary" icon="plus" @click="addRule">{{ i18n "pages.xray.rules.add" }}</a-button>
<a-table :columns="isMobile ? rulesMobileColumns : rulesColumns" bordered <a-table :columns="isMobile ? rulesMobileColumns : rulesColumns" bordered
:row-key="r => r.key" :row-key="r => r.key"
@@ -396,9 +397,9 @@
</template> </template>
</a-table> </a-table>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-3" tab='{{ i18n "pages.xray.Outbounds"}}' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-outbound" tab='{{ i18n "pages.xray.Outbounds"}}' style="padding-top: 20px;" force-render="true">
<a-button type="primary" icon="plus" @click="addOutbound()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button> <a-button type="primary" icon="plus" @click="addOutbound()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
<a-button type="primary" @click="showWarp()" style="margin-bottom: 10px;">WARP</a-button> <a-button type="primary" icon="cloud" @click="showWarp()" style="margin-bottom: 10px;">WARP</a-button>
<a-table :columns="outboundColumns" bordered <a-table :columns="outboundColumns" bordered
:row-key="r => r.key" :row-key="r => r.key"
:data-source="outboundData" :data-source="outboundData"
@@ -440,9 +441,9 @@
</template> </template>
</a-table> </a-table>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-4" tab='{{ i18n "pages.xray.outbound.reverse"}}' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-reverse" tab='{{ i18n "pages.xray.outbound.reverse"}}' style="padding-top: 20px;" force-render="true">
<a-button type="primary" icon="plus" @click="addReverse()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addReverse" }}</a-button> <a-button type="primary" icon="plus" @click="addReverse()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addReverse" }}</a-button>
<a-table :columns="reverseColumns" bordered <a-table :columns="reverseColumns" bordered v-if="reverseData.length>0"
:row-key="r => r.key" :row-key="r => r.key"
:data-source="reverseData" :data-source="reverseData"
:scroll="isMobile ? {} : { x: 200 }" :scroll="isMobile ? {} : { x: 200 }"
@@ -468,7 +469,7 @@
</template> </template>
</a-table> </a-table>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-5" tab='{{ i18n "pages.xray.Balancers"}}' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-balancer" tab='{{ i18n "pages.xray.Balancers"}}' style="padding-top: 20px;" force-render="true">
<a-button type="primary" icon="plus" @click="addBalancer()" style="margin-bottom: 10px;">{{ i18n "pages.xray.balancer.addBalancer"}}</a-button> <a-button type="primary" icon="plus" @click="addBalancer()" style="margin-bottom: 10px;">{{ i18n "pages.xray.balancer.addBalancer"}}</a-button>
<a-table :columns="balancerColumns" bordered v-if="balancersData.length>0" <a-table :columns="balancerColumns" bordered v-if="balancersData.length>0"
:row-key="r => r.key" :row-key="r => r.key"
@@ -495,17 +496,31 @@
</a-dropdown> </a-dropdown>
</template> </template>
<template slot="strategy" slot-scope="text, balancer, index"> <template slot="strategy" slot-scope="text, balancer, index">
<a-tag style="margin:0;" v-if="balancer.strategy=='random'" color="purple">Random</a-tag> <a-tag style="margin:0;" v-if="balancer.strategy=='random'" color="blue">Random</a-tag>
<a-tag style="margin:0;" v-if="balancer.strategy=='roundRobin'" color="green">Round Robin</a-tag> <a-tag style="margin:0;" v-if="balancer.strategy=='roundRobin'" color="blue">Round Robin</a-tag>
<a-tag style="margin:0;" v-if="balancer.strategy=='leastPing'" color="green">Least Ping</a-tag>
<a-tag style="margin:0;" v-if="balancer.strategy=='leastLoad'" color="purple">Least Load</a-tag>
</template> </template>
<template slot="selector" slot-scope="text, balancer, index"> <template slot="selector" slot-scope="text, balancer, index">
<a-tag class="info-large-tag" style="margin:1;" v-for="sel in balancer.selector">[[ sel ]]</a-tag> <a-tag class="info-large-tag" style="margin:1;" v-for="sel in balancer.selector">[[ sel ]]</a-tag>
</template> </template>
</a-table> </a-table>
<a-radio-group
v-model="obsSettings"
v-if="observatoryEnable || burstObservatoryEnable"
@change="changeObsCode"
button-style="solid"
style="margin: 10px 0;"
:size="isMobile ? 'small' : ''">
<a-radio-button value="observatory" v-if="observatoryEnable">Observatory</a-radio-button>
<a-radio-button value="burstObservatory" v-if="burstObservatoryEnable">Burst Observatory</a-radio-button>
</a-radio-group>
<textarea style="position:absolute; left: -800px;" id="obsSetting"></textarea>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-6" tab='DNS' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-dns" tab='DNS' style="padding-top: 20px;" force-render="true">
<setting-list-item type="switch" title='{{ i18n "pages.xray.dns.enable" }}' desc='{{ i18n "pages.xray.dns.enableDesc" }}' v-model="enableDNS"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.dns.enable" }}' desc='{{ i18n "pages.xray.dns.enableDesc" }}' v-model="enableDNS"></setting-list-item>
<template v-if="enableDNS"> <template v-if="enableDNS">
<setting-list-item type="text" title='{{ i18n "pages.xray.dns.tag" }}' desc='{{ i18n "pages.xray.dns.tagDesc" }}' v-model="dnsTag"></setting-list-item>
<a-list-item> <a-list-item>
<a-row style="padding: 20px"> <a-row style="padding: 20px">
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
@@ -689,6 +704,7 @@
showAlert: false, showAlert: false,
isMobile: window.innerWidth <= 768, isMobile: window.innerWidth <= 768,
advSettings: 'xraySetting', advSettings: 'xraySetting',
obsSettings: '',
cm: null, cm: null,
cmOptions: { cmOptions: {
lineNumbers: true, lineNumbers: true,
@@ -766,6 +782,22 @@
], ],
"queryStrategy": "UseIP" "queryStrategy": "UseIP"
}, },
},
defaultObservatory: {
subjectSelector: [],
probeURL: "http://www.google.com/gen_204",
probeInterval: "10m",
enableConcurrency: true
},
defaultBurstObservatory: {
subjectSelector: [],
pingConfig: {
destination: "http://www.google.com/gen_204",
interval: "30m",
connectivity: "http://connectivitycheck.platform.hicloud.com/generate_204",
timeout: "10s",
sampling: 2
}
} }
}, },
methods: { methods: {
@@ -818,6 +850,10 @@
this.saveBtnDisable = true; this.saveBtnDisable = true;
} }
}, },
changePage(pageKey) {
if(pageKey == 'tpl-advanced') this.changeCode();
if(pageKey == 'tpl-balancer') this.changeObsCode();
},
syncRulesWithOutbound(tag, setting) { syncRulesWithOutbound(tag, setting) {
const newTemplateSettings = this.templateSettings; const newTemplateSettings = this.templateSettings;
const haveRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === tag); const haveRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === tag);
@@ -900,6 +936,23 @@
} }
}); });
}, },
changeObsCode() {
if (this.obsSettings == ''){
return
}
if(this.cm != null) {
this.cm.toTextArea();
}
textAreaObj = document.getElementById('obsSetting');
textAreaObj.value = this[this.obsSettings];
this.cm = CodeMirror.fromTextArea(textAreaObj, this.cmOptions);
this.cm.on('change',editor => {
value = editor.getValue();
if(this.isJsonString(value)){
this[this.obsSettings] = value;
}
});
},
isJsonString(str) { isJsonString(str) {
try { try {
JSON.parse(str); JSON.parse(str);
@@ -1075,14 +1128,27 @@
'tag': balancer.tag, 'tag': balancer.tag,
'selector': balancer.selector 'selector': balancer.selector
}; };
if (balancer.strategy == 'roundRobin') { 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;
balancerModal.close(); balancerModal.close();
this.changeObsCode();
}, },
isEdit: false isEdit: false
}); });
@@ -1102,10 +1168,31 @@
'tag': balancer.tag, 'tag': balancer.tag,
'selector': balancer.selector 'selector': balancer.selector
}; };
if (balancer.strategy == 'roundRobin') {
// Remove old tag
if (newTemplateSettings.observatory){
newTemplateSettings.observatory.subjectSelector = newTemplateSettings.observatory.subjectSelector.filter(s => s != oldTag);
}
if (newTemplateSettings.burstObservatory){
newTemplateSettings.burstObservatory.subjectSelector = newTemplateSettings.burstObservatory.subjectSelector.filter(s => s != oldTag);
}
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[index] = tmpBalancer; newTemplateSettings.routing.balancers[index] = tmpBalancer;
@@ -1119,6 +1206,7 @@
} }
this.templateSettings = newTemplateSettings; this.templateSettings = newTemplateSettings;
balancerModal.close(); balancerModal.close();
this.changeObsCode();
}, },
isEdit: true isEdit: true
}); });
@@ -1127,22 +1215,26 @@
newTemplateSettings = this.templateSettings; newTemplateSettings = this.templateSettings;
//remove from balancers //remove from balancers
const oldTag = this.balancersData[index].tag; const removedBalancer = this.balancersData.splice(index, 1)[0];
this.balancersData.splice(index, 1);
// remove from settings // Remove from settings
let realIndex = newTemplateSettings.routing.balancers.findIndex((b) => b.tag == oldTag); let realIndex = newTemplateSettings.routing.balancers.findIndex((b) => b.tag === removedBalancer.tag);
newTemplateSettings.routing.balancers.splice(realIndex, 1); newTemplateSettings.routing.balancers.splice(realIndex, 1);
// remove related routing rules // Remove tag from observatory
let rules = []; if (newTemplateSettings.observatory){
newTemplateSettings.routing.rules.forEach((r) => { newTemplateSettings.observatory.subjectSelector = newTemplateSettings.observatory.subjectSelector.filter(s => s != removedBalancer.tag);
if (!r.balancerTag || r.balancerTag != oldTag) { }
rules.push(r); if (newTemplateSettings.burstObservatory){
} newTemplateSettings.burstObservatory.subjectSelector = newTemplateSettings.burstObservatory.subjectSelector.filter(s => s != removedBalancer.tag);
}); }
newTemplateSettings.routing.rules = rules;
// 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.templateSettings = newTemplateSettings;
this.changeObsCode()
}, },
addDNSServer(){ addDNSServer(){
dnsModal.show({ dnsModal.show({
@@ -1279,7 +1371,7 @@
newLogSettings = this.logSettings; newLogSettings = this.logSettings;
newLogSettings.loglevel = newValue; newLogSettings.loglevel = newValue;
this.logSettings = newLogSettings; this.logSettings = newLogSettings;
}, },
}, },
logAccess: { logAccess: {
get: function () { return this.logSettings?.access?? ''; }, get: function () { return this.logSettings?.access?? ''; },
@@ -1287,7 +1379,7 @@
newLogSettings = this.logSettings; newLogSettings = this.logSettings;
newValue == "" ? delete newLogSettings.access : newLogSettings.access = newValue; newValue == "" ? delete newLogSettings.access : newLogSettings.access = newValue;
this.logSettings = newLogSettings; this.logSettings = newLogSettings;
}, },
}, },
logError: { logError: {
get: function () { return this.logSettings?.error?? ''; }, get: function () { return this.logSettings?.error?? ''; },
@@ -1295,7 +1387,7 @@
newLogSettings = this.logSettings; newLogSettings = this.logSettings;
newValue == "" ? delete newLogSettings.error : newLogSettings.error = newValue; newValue == "" ? delete newLogSettings.error : newLogSettings.error = newValue;
this.logSettings = newLogSettings; this.logSettings = newLogSettings;
}, },
}, },
inboundSettings: { inboundSettings: {
get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.inbounds, null, 2) : null; }, get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.inbounds, null, 2) : null; },
@@ -1376,15 +1468,10 @@
data = [] data = []
if (this.templateSettings != null && this.templateSettings.routing != null && this.templateSettings.routing.balancers != null) { if (this.templateSettings != null && this.templateSettings.routing != null && this.templateSettings.routing.balancers != null) {
this.templateSettings.routing.balancers.forEach((o, index) => { this.templateSettings.routing.balancers.forEach((o, index) => {
let strategy = "random"
if (o.strategy && o.strategy.type == "roundRobin") {
strategy = o.strategy.type
}
data.push({ data.push({
'key': index, 'key': index,
'tag': o.tag ? o.tag : "", 'tag': o.tag ? o.tag : "",
'strategy': strategy, 'strategy': o.strategy?.type ?? "random",
'selector': o.selector ? o.selector : [] 'selector': o.selector ? o.selector : []
}); });
}); });
@@ -1392,6 +1479,42 @@
return data; return data;
} }
}, },
observatory: {
get: function () {
return this.templateSettings?.observatory ? JSON.stringify(this.templateSettings.observatory, null, 2) : null;
},
set: function (newValue) {
newTemplateSettings = this.templateSettings;
newTemplateSettings.observatory = JSON.parse(newValue);
this.templateSettings = newTemplateSettings;
},
},
burstObservatory: {
get: function () {
return this.templateSettings?.burstObservatory ? JSON.stringify(this.templateSettings.burstObservatory, null, 2) : null;
},
set: function (newValue) {
newTemplateSettings = this.templateSettings;
newTemplateSettings.burstObservatory = JSON.parse(newValue);
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;
}
},
freedomStrategy: { freedomStrategy: {
get: function () { get: function () {
if (!this.templateSettings) return "AsIs"; if (!this.templateSettings) return "AsIs";
@@ -1773,7 +1896,23 @@
}, },
set: function (newValue) { set: function (newValue) {
newTemplateSettings = this.templateSettings; newTemplateSettings = this.templateSettings;
newTemplateSettings.dns = newValue ? { servers: [], queryStrategy: "UseIP" } : null; if (newValue) {
newTemplateSettings.dns = { servers: [], queryStrategy: "UseIP", tag: "dns_inbound" };
newTemplateSettings.fakedns = null;
} else {
delete newTemplateSettings.dns;
delete newTemplateSettings.fakedns;
}
this.templateSettings = newTemplateSettings;
}
},
dnsTag: {
get: function () {
return this.enableDNS ? this.templateSettings.dns.tag : "";
},
set: function (newValue) {
newTemplateSettings = this.templateSettings;
newTemplateSettings.dns.tag = newValue;
this.templateSettings = newTemplateSettings; this.templateSettings = newTemplateSettings;
} }
}, },

View File

@@ -21,6 +21,8 @@
<a-select v-model="balancerModal.balancer.strategy" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="balancerModal.balancer.strategy" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="random">Random</a-select-option> <a-select-option value="random">Random</a-select-option>
<a-select-option value="roundRobin">Round Robin</a-select-option> <a-select-option value="roundRobin">Round Robin</a-select-option>
<a-select-option value="leastLoad">Least Load</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 :validate-status="balancerModal.emptySelector? 'warning' : 'success'"> <a-form-item label='{{ i18n "pages.xray.balancer.balancerSelectors" }}' has-feedback :validate-status="balancerModal.emptySelector? 'warning' : 'success'">
@@ -74,17 +76,18 @@
this.balancerTags = balancerTags.filter((tag) => tag != balancer.tag); this.balancerTags = balancerTags.filter((tag) => tag != balancer.tag);
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag); this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag);
this.isEdit = isEdit; this.isEdit = isEdit;
this.check() this.check();
this.checkSelector();
}, },
close() { close() {
balancerModal.visible = false; this.visible = false;
balancerModal.loading(false); this.loading(false);
}, },
loading(loading=true) { loading(loading=true) {
balancerModal.confirmLoading = loading; this.confirmLoading = loading;
}, },
check() { check() {
if (balancerModal.balancer.tag == '' || balancerModal.balancerTags.includes(balancerModal.balancer.tag)) { if (this.balancer.tag == '' || this.balancerTags.includes(this.balancer.tag)) {
this.duplicateTag = true; this.duplicateTag = true;
this.isValid = false; this.isValid = false;
} else { } else {
@@ -93,7 +96,7 @@
} }
}, },
checkSelector() { checkSelector() {
balancerModal.emptySelector = balancerModal.balancer.selector.length == 0; this.emptySelector = this.balancer.selector.length == 0;
} }
}; };

View File

@@ -113,6 +113,7 @@
this.isEdit = isEdit; this.isEdit = isEdit;
this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj => obj.tag); this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj => obj.tag);
this.inboundTags.push(...app.inboundTags); this.inboundTags.push(...app.inboundTags);
if (app.enableDNS && !ObjectUtil.isEmpty(app.dnsTag)) this.inboundTags.push(app.dnsTag)
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag); this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag);
}, },
close() { close() {

View File

@@ -195,7 +195,8 @@
this.isEdit = isEdit; this.isEdit = isEdit;
this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj => obj.tag); this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj => obj.tag);
this.inboundTags.push(...app.inboundTags); this.inboundTags.push(...app.inboundTags);
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag); if (app.enableDNS && !ObjectUtil.isEmpty(app.dnsTag)) this.inboundTags.push(app.dnsTag)
this.outboundTags = ["", ...app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag)];
if(app.templateSettings.reverse){ if(app.templateSettings.reverse){
if(app.templateSettings.reverse.bridges) { if(app.templateSettings.reverse.bridges) {
this.inboundTags.push(...app.templateSettings.reverse.bridges.map(b => b.tag)); this.inboundTags.push(...app.templateSettings.reverse.bridges.map(b => b.tag));
@@ -203,7 +204,7 @@
if(app.templateSettings.reverse.portals) this.outboundTags.push(...app.templateSettings.reverse.portals.map(b => b.tag)); if(app.templateSettings.reverse.portals) this.outboundTags.push(...app.templateSettings.reverse.portals.map(b => b.tag));
} }
if (app.templateSettings.routing && app.templateSettings.routing.balancers) { if (app.templateSettings.routing && app.templateSettings.routing.balancers) {
this.balancerTags = app.templateSettings.routing.balancers.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag) this.balancerTags = [ "", ...app.templateSettings.routing.balancers.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag)];
} }
}, },
close() { close() {
@@ -229,8 +230,8 @@
rule.inboundTag = value.inboundTag; rule.inboundTag = value.inboundTag;
rule.protocol = value.protocol; rule.protocol = value.protocol;
rule.attrs = Object.fromEntries(value.attrs); rule.attrs = Object.fromEntries(value.attrs);
rule.outboundTag = value.outboundTag; rule.outboundTag = value.outboundTag == "" ? undefined : value.outboundTag;
rule.balancerTag = value.balancerTag; rule.balancerTag = value.balancerTag == "" ? undefined : value.balancerTag;
for (const [key, value] of Object.entries(rule)) { for (const [key, value] of Object.entries(rule)) {
if ( if (

View File

@@ -3,6 +3,7 @@ package job
import ( import (
"strconv" "strconv"
"time" "time"
"x-ui/web/service" "x-ui/web/service"
"github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/cpu"

View File

@@ -31,5 +31,4 @@ func (j *XrayTrafficJob) Run() {
if needRestart { if needRestart {
j.xrayService.SetToNeedRestart() j.xrayService.SetToNeedRestart()
} }
} }

View File

@@ -4,6 +4,7 @@ import (
"embed" "embed"
"io/fs" "io/fs"
"strings" "strings"
"x-ui/logger" "x-ui/logger"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -12,9 +13,11 @@ import (
"golang.org/x/text/language" "golang.org/x/text/language"
) )
var i18nBundle *i18n.Bundle var (
var LocalizerWeb *i18n.Localizer i18nBundle *i18n.Bundle
var LocalizerBot *i18n.Localizer LocalizerWeb *i18n.Localizer
LocalizerBot *i18n.Localizer
)
type I18nType string type I18nType string
@@ -79,7 +82,6 @@ func I18n(i18nType I18nType, key string, params ...string) string {
MessageID: key, MessageID: key,
TemplateData: templateData, TemplateData: templateData,
}) })
if err != nil { if err != nil {
logger.Errorf("Failed to localize message: %v", err) logger.Errorf("Failed to localize message: %v", err)
return "" return ""
@@ -135,7 +137,6 @@ func parseTranslationFiles(i18nFS embed.FS, i18nBundle *i18n.Bundle) error {
_, err = i18nBundle.ParseMessageFileBytes(data, path) _, err = i18nBundle.ParseMessageFileBytes(data, path)
return err return err
}) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"time" "time"
"x-ui/database" "x-ui/database"
"x-ui/database/model" "x-ui/database/model"
"x-ui/logger" "x-ui/logger"
@@ -38,9 +39,25 @@ func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
return inbounds, nil return inbounds, nil
} }
func (s *InboundService) checkPortExist(port int, ignoreId int) (bool, error) { func (s *InboundService) checkPortExist(listen string, port int, ignoreId int) (bool, error) {
db := database.GetDB() db := database.GetDB()
db = db.Model(model.Inbound{}).Where("port = ?", port) if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" {
db = db.Model(model.Inbound{}).Where("port = ?", port)
} else {
db = db.Model(model.Inbound{}).
Where("port = ?", port).
Where(
db.Model(model.Inbound{}).Where(
"listen = ?", listen,
).Or(
"listen = \"\"",
).Or(
"listen = \"0.0.0.0\"",
).Or(
"listen = \"::\"",
).Or(
"listen = \"::0\""))
}
if ignoreId > 0 { if ignoreId > 0 {
db = db.Where("id != ?", ignoreId) db = db.Where("id != ?", ignoreId)
} }
@@ -74,7 +91,6 @@ func (s *InboundService) getAllEmails() ([]string, error) {
FROM inbounds, FROM inbounds,
JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client JSON_EACH(JSON_EXTRACT(inbounds.settings, '$.clients')) AS client
`).Scan(&emails).Error `).Scan(&emails).Error
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -135,7 +151,7 @@ func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbound) (stri
} }
func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error) { func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
exist, err := s.checkPortExist(inbound.Port, 0) exist, err := s.checkPortExist(inbound.Listen, inbound.Port, 0)
if err != nil { if err != nil {
return inbound, false, err return inbound, false, err
} }
@@ -156,6 +172,23 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, boo
return inbound, false, err return inbound, false, err
} }
// Secure client ID
for _, client := range clients {
if inbound.Protocol == "trojan" {
if client.Password == "" {
return inbound, false, common.NewError("empty client ID")
}
} else if inbound.Protocol == "shadowsocks" {
if client.Email == "" {
return inbound, false, common.NewError("empty client ID")
}
} else {
if client.ID == "" {
return inbound, false, common.NewError("empty client ID")
}
}
}
db := database.GetDB() db := database.GetDB()
tx := db.Begin() tx := db.Begin()
defer func() { defer func() {
@@ -238,7 +271,7 @@ func (s *InboundService) GetInbound(id int) (*model.Inbound, error) {
} }
func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, bool, error) { func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
exist, err := s.checkPortExist(inbound.Port, inbound.Id) exist, err := s.checkPortExist(inbound.Listen, inbound.Port, inbound.Id)
if err != nil { if err != nil {
return inbound, false, err return inbound, false, err
} }
@@ -281,7 +314,11 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
oldInbound.Settings = inbound.Settings oldInbound.Settings = inbound.Settings
oldInbound.StreamSettings = inbound.StreamSettings oldInbound.StreamSettings = inbound.StreamSettings
oldInbound.Sniffing = inbound.Sniffing oldInbound.Sniffing = inbound.Sniffing
oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
} else {
oldInbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
}
needRestart := false needRestart := false
s.xrayApi.Init(p.GetAPIPort()) s.xrayApi.Init(p.GetAPIPort())
@@ -379,6 +416,23 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
return false, err return false, err
} }
// Secure client ID
for _, client := range clients {
if oldInbound.Protocol == "trojan" {
if client.Password == "" {
return false, common.NewError("empty client ID")
}
} else if oldInbound.Protocol == "shadowsocks" {
if client.Email == "" {
return false, common.NewError("empty client ID")
}
} else {
if client.ID == "" {
return false, common.NewError("empty client ID")
}
}
}
var oldSettings map[string]interface{} var oldSettings map[string]interface{}
err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings) err = json.Unmarshal([]byte(oldInbound.Settings), &oldSettings)
if err != nil { if err != nil {
@@ -462,9 +516,9 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
client_key = "email" client_key = "email"
} }
inerfaceClients := settings["clients"].([]interface{}) interfaceClients := settings["clients"].([]interface{})
var newClients []interface{} var newClients []interface{}
for _, client := range inerfaceClients { for _, client := range interfaceClients {
c := client.(map[string]interface{}) c := client.(map[string]interface{})
c_id := c[client_key].(string) c_id := c[client_key].(string)
if c_id == clientId { if c_id == clientId {
@@ -533,15 +587,19 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
} }
oldEmail := "" oldEmail := ""
newClientId := ""
clientIndex := 0 clientIndex := 0
for index, oldClient := range oldClients { for index, oldClient := range oldClients {
oldClientId := "" oldClientId := ""
if oldInbound.Protocol == "trojan" { if oldInbound.Protocol == "trojan" {
oldClientId = oldClient.Password oldClientId = oldClient.Password
newClientId = clients[0].Password
} else if oldInbound.Protocol == "shadowsocks" { } else if oldInbound.Protocol == "shadowsocks" {
oldClientId = oldClient.Email oldClientId = oldClient.Email
newClientId = clients[0].Email
} else { } else {
oldClientId = oldClient.ID oldClientId = oldClient.ID
newClientId = clients[0].ID
} }
if clientId == oldClientId { if clientId == oldClientId {
oldEmail = oldClient.Email oldEmail = oldClient.Email
@@ -550,6 +608,11 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
} }
} }
// Validate new client ID
if newClientId == "" {
return false, common.NewError("empty client ID")
}
if len(clients[0].Email) > 0 && clients[0].Email != oldEmail { if len(clients[0].Email) > 0 && clients[0].Email != oldEmail {
existEmail, err := s.checkEmailsExistForClients(clients) existEmail, err := s.checkEmailsExistForClients(clients)
if err != nil { if err != nil {
@@ -921,7 +984,7 @@ func (s *InboundService) disableInvalidInbounds(tx *gorm.DB) (bool, int64, error
s.xrayApi.Init(p.GetAPIPort()) s.xrayApi.Init(p.GetAPIPort())
for _, tag := range tags { for _, tag := range tags {
err1 := s.xrayApi.DelInbound(tag) err1 := s.xrayApi.DelInbound(tag)
if err == nil { if err1 == nil {
logger.Debug("Inbound disabled by api:", tag) logger.Debug("Inbound disabled by api:", tag)
} else { } else {
logger.Debug("Error in disabling inbound by api:", err1) logger.Debug("Error in disabling inbound by api:", err1)
@@ -1001,10 +1064,7 @@ func (s *InboundService) AddClientStat(tx *gorm.DB, inboundId int, client *model
clientTraffic.Reset = client.Reset clientTraffic.Reset = client.Reset
result := tx.Create(&clientTraffic) result := tx.Create(&clientTraffic)
err := result.Error err := result.Error
if err != nil { return err
return err
}
return nil
} }
func (s *InboundService) UpdateClientStat(tx *gorm.DB, email string, client *model.Client) error { func (s *InboundService) UpdateClientStat(tx *gorm.DB, email string, client *model.Client) error {
@@ -1015,13 +1075,12 @@ func (s *InboundService) UpdateClientStat(tx *gorm.DB, email string, client *mod
"email": client.Email, "email": client.Email,
"total": client.TotalGB, "total": client.TotalGB,
"expiry_time": client.ExpiryTime, "expiry_time": client.ExpiryTime,
"reset": client.Reset}) "reset": client.Reset,
})
err := result.Error err := result.Error
if err != nil { return err
return err
}
return nil
} }
func (s *InboundService) DelClientStat(tx *gorm.DB, email string) error { func (s *InboundService) DelClientStat(tx *gorm.DB, email string) error {
return tx.Where("email = ?", email).Delete(xray.ClientTraffic{}).Error return tx.Where("email = ?", email).Delete(xray.ClientTraffic{}).Error
} }
@@ -1102,11 +1161,7 @@ func (s *InboundService) ResetAllClientTraffics(id int) error {
Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0}) Updates(map[string]interface{}{"enable": true, "up": 0, "down": 0})
err := result.Error err := result.Error
return err
if err != nil {
return err
}
return nil
} }
func (s *InboundService) ResetAllTraffics() error { func (s *InboundService) ResetAllTraffics() error {
@@ -1117,11 +1172,7 @@ func (s *InboundService) ResetAllTraffics() error {
Updates(map[string]interface{}{"up": 0, "down": 0}) Updates(map[string]interface{}{"up": 0, "down": 0})
err := result.Error err := result.Error
return err
if err != nil {
return err
}
return nil
} }
func (s *InboundService) DelDepletedClients(id int) (err error) { func (s *InboundService) DelDepletedClients(id int) (err error) {
@@ -1320,7 +1371,7 @@ func (s *InboundService) MigrationRequirements() {
json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings) json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
clients, ok := settings["clients"].([]interface{}) clients, ok := settings["clients"].([]interface{})
if ok { if ok {
// Fix Clinet configuration problems // Fix Client configuration problems
var newClients []interface{} var newClients []interface{}
for client_index := range clients { for client_index := range clients {
c := clients[client_index].(map[string]interface{}) c := clients[client_index].(map[string]interface{})

View File

@@ -4,11 +4,11 @@ import (
"os" "os"
"syscall" "syscall"
"time" "time"
"x-ui/logger" "x-ui/logger"
) )
type PanelService struct { type PanelService struct{}
}
func (s *PanelService) RestartPanel(delay time.Duration) error { func (s *PanelService) RestartPanel(delay time.Duration) error {
p, err := os.FindProcess(syscall.Getpid()) p, err := os.FindProcess(syscall.Getpid())

View File

@@ -15,6 +15,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"x-ui/config" "x-ui/config"
"x-ui/database" "x-ui/database"
"x-ui/logger" "x-ui/logger"
@@ -250,7 +251,6 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
} }
func (s *ServerService) StopXrayService() (string error) { func (s *ServerService) StopXrayService() (string error) {
err := s.xrayService.StopXray() err := s.xrayService.StopXray()
if err != nil { if err != nil {
logger.Error("stop xray failed:", err) logger.Error("stop xray failed:", err)
@@ -261,7 +261,6 @@ func (s *ServerService) StopXrayService() (string error) {
} }
func (s *ServerService) RestartXrayService() (string error) { func (s *ServerService) RestartXrayService() (string error) {
s.xrayService.StopXray() s.xrayService.StopXray()
defer func() { defer func() {
err := s.xrayService.RestartXray(true) err := s.xrayService.RestartXray(true)
@@ -365,7 +364,6 @@ func (s *ServerService) UpdateXray(version string) error {
} }
return nil return nil
} }
func (s *ServerService) GetLogs(count string, level string, syslog string) []string { func (s *ServerService) GetLogs(count string, level string, syslog string) []string {

View File

@@ -9,6 +9,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"x-ui/database" "x-ui/database"
"x-ui/database/model" "x-ui/database/model"
"x-ui/logger" "x-ui/logger"
@@ -58,11 +59,12 @@ var defaultValueMap = map[string]string{
"subJsonPath": "/json/", "subJsonPath": "/json/",
"subJsonURI": "", "subJsonURI": "",
"subJsonFragment": "", "subJsonFragment": "",
"subJsonMux": "",
"subJsonRules": "",
"warp": "", "warp": "",
} }
type SettingService struct { type SettingService struct{}
}
func (s *SettingService) GetAllSetting() (*entity.AllSetting, error) { func (s *SettingService) GetAllSetting() (*entity.AllSetting, error) {
db := database.GetDB() db := database.GetDB()
@@ -403,9 +405,18 @@ func (s *SettingService) GetSubJsonFragment() (string, error) {
return s.getString("subJsonFragment") return s.getString("subJsonFragment")
} }
func (s *SettingService) GetSubJsonMux() (string, error) {
return s.getString("subJsonMux")
}
func (s *SettingService) GetSubJsonRules() (string, error) {
return s.getString("subJsonRules")
}
func (s *SettingService) GetWarp() (string, error) { func (s *SettingService) GetWarp() (string, error) {
return s.getString("warp") return s.getString("warp")
} }
func (s *SettingService) SetWarp(data string) error { func (s *SettingService) SetWarp(data string) error {
return s.setString("warp", data) return s.setString("warp", data)
} }

View File

@@ -8,6 +8,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"x-ui/config" "x-ui/config"
"x-ui/database" "x-ui/database"
"x-ui/database/model" "x-ui/database/model"
@@ -19,10 +20,12 @@ import (
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
) )
var bot *tgbotapi.BotAPI var (
var adminIds []int64 bot *tgbotapi.BotAPI
var isRunning bool adminIds []int64
var hostname string isRunning bool
hostname string
)
type LoginStatus byte type LoginStatus byte
@@ -132,7 +135,7 @@ func (t *Tgbot) OnReceive() {
isAdmin := checkAdmin(tgId) isAdmin := checkAdmin(tgId)
if update.Message == nil { if update.Message == nil {
if update.CallbackQuery != nil { if update.CallbackQuery != nil {
t.asnwerCallback(update.CallbackQuery, isAdmin) t.asnwerCallback(update.CallbackQuery)
} }
} else { } else {
if update.Message.IsCommand() { if update.Message.IsCommand() {
@@ -193,7 +196,7 @@ func (t *Tgbot) answerCommand(message *tgbotapi.Message, chatId int64, isAdmin b
t.SendAnswer(chatId, msg, isAdmin) t.SendAnswer(chatId, msg, isAdmin)
} }
func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery, isAdmin bool) { func (t *Tgbot) asnwerCallback(callbackQuery *tgbotapi.CallbackQuery) {
// Respond to the callback query, telling Telegram to show the user // Respond to the callback query, telling Telegram to show the user
// a message with the data received. // a message with the data received.
callback := tgbotapi.NewCallback(callbackQuery.ID, callbackQuery.Data) callback := tgbotapi.NewCallback(callbackQuery.ID, callbackQuery.Data)

View File

@@ -2,6 +2,7 @@ package service
import ( import (
"errors" "errors"
"x-ui/database" "x-ui/database"
"x-ui/database/model" "x-ui/database/model"
"x-ui/logger" "x-ui/logger"
@@ -9,8 +10,7 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
type UserService struct { type UserService struct{}
}
func (s *UserService) GetFirstUser() (*model.User, error) { func (s *UserService) GetFirstUser() (*model.User, error) {
db := database.GetDB() db := database.GetDB()

View File

@@ -4,16 +4,19 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"sync" "sync"
"x-ui/logger" "x-ui/logger"
"x-ui/xray" "x-ui/xray"
"go.uber.org/atomic" "go.uber.org/atomic"
) )
var p *xray.Process var (
var lock sync.Mutex p *xray.Process
var isNeedXrayRestart atomic.Bool lock sync.Mutex
var result string isNeedXrayRestart atomic.Bool
result string
)
type XrayService struct { type XrayService struct {
inboundService InboundService inboundService InboundService
@@ -87,7 +90,6 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
// check users active or not // check users active or not
clientStats := inbound.ClientStats clientStats := inbound.ClientStats
for _, clientTraffic := range clientStats { for _, clientTraffic := range clientStats {
indexDecrease := 0 indexDecrease := 0
for index, client := range clients { for index, client := range clients {
c := client.(map[string]interface{}) c := client.(map[string]interface{})
@@ -96,10 +98,8 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
clients = RemoveIndex(clients, index-indexDecrease) clients = RemoveIndex(clients, index-indexDecrease)
indexDecrease++ indexDecrease++
} }
} }
} }
} }
// clear client config for additional parameters // clear client config for additional parameters

View File

@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"os" "os"
"time" "time"
"x-ui/util/common" "x-ui/util/common"
"x-ui/xray" "x-ui/xray"
) )

View File

@@ -2,6 +2,7 @@ package session
import ( import (
"encoding/gob" "encoding/gob"
"x-ui/database/model" "x-ui/database/model"
sessions "github.com/Calidity/gin-sessions" sessions "github.com/Calidity/gin-sessions"

View File

@@ -52,7 +52,12 @@
"remained" = "Remaining" "remained" = "Remaining"
"secAlertTitle" = "Security Alert" "secAlertTitle" = "Security Alert"
"secAlertSsl" = "This connection is not secure. Please avoid entering sensitive information until TLS is activated for data protection." "secAlertSsl" = "This connection is not secure. Please avoid entering sensitive information until TLS is activated for data protection."
"secAlertConf" = "Certain configurations have been identified as susceptible to attacks, prompting immediate action to reinforce security protocols and safeguard against potential security breaches." "secAlertConf" = "Certain settings are vulnerable to attacks. It is recommended to reinforce security protocols to prevent potential breaches."
"secAlertSSL" = "Panel lacks secure connection. Please install TLS certificate for data protection."
"secAlertPanelPort" = "Panel default port is vulnerable. Please configure a random or specific port."
"secAlertPanelURI" = "Panel default URI path is insecure. Please configure a complex URI path."
"secAlertSubURI" = "Subscription default URI path is insecure. Please configure a complex URI path."
"secAlertSubJsonURI" = "Subscription JSON default URI path is insecure. Please configure a complex URI path."
"security" = "Security" "security" = "Security"
[menu] [menu]
@@ -312,8 +317,8 @@
"advancedTemplate" = "Advanced" "advancedTemplate" = "Advanced"
"generalConfigs" = "General Strategy" "generalConfigs" = "General Strategy"
"generalConfigsDesc" = "These options will determine general strategy adjustments." "generalConfigsDesc" = "These options will determine general strategy adjustments."
"logConfigs" = "Log" "logConfigs" = "Logs Settings"
"logConfigsDesc" = "Logs may affect your server's efficiency. It is recommended to enable it wisely only in case of your needs" "logConfigsDesc" = "Enabling logs may affect your server's efficiency. It is recommended to enable it wisely only when necessary."
"blockConfigs" = "Protection Shield" "blockConfigs" = "Protection Shield"
"blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites." "blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites."
"blockCountryConfigs" = "Block Country" "blockCountryConfigs" = "Block Country"
@@ -321,7 +326,7 @@
"directCountryConfigs" = "Direct Country" "directCountryConfigs" = "Direct Country"
"directCountryConfigsDesc" = "These options will directly forward traffic based on the specific requested country." "directCountryConfigsDesc" = "These options will directly forward traffic based on the specific requested country."
"ipv4Configs" = "IPv4 Routing" "ipv4Configs" = "IPv4 Routing"
"ipv4ConfigsDesc" = "These options will route traffic based on specific requested destination via system's IPv4." "ipv4ConfigsDesc" = "These options will route traffic based on specific requested destination via server's IPv4."
"warpConfigs" = "WARP Config" "warpConfigs" = "WARP Config"
"warpConfigsDesc" = "These options will route traffic based on specific requested destination via WARP." "warpConfigsDesc" = "These options will route traffic based on specific requested destination via WARP."
"Template" = "Advanced Xray Configuration Template" "Template" = "Advanced Xray Configuration Template"
@@ -382,6 +387,12 @@
"Routings" = "Routing Rules" "Routings" = "Routing Rules"
"RoutingsDesc" = "The priority of each rule is important!" "RoutingsDesc" = "The priority of each rule is important!"
"Balancers" = "Balancers" "Balancers" = "Balancers"
"logLevel" = "Log Level"
"logLevelDesc" = "The log level will determine the severity of issues that should be recorded."
"accessLog" = "Access Log"
"accessLogDesc" = "The file path for the access log."
"errorLog" = "Error Log"
"errorLogDesc" = "The file path for the error log."
[pages.xray.rules] [pages.xray.rules]
"first" = "First" "first" = "First"
@@ -415,6 +426,7 @@
"settings" = "Settings" "settings" = "Settings"
"accountInfo" = "Account Information" "accountInfo" = "Account Information"
"outboundStatus" = "Outbound Status" "outboundStatus" = "Outbound Status"
"sendThrough" = "Send Through"
[pages.xray.balancer] [pages.xray.balancer]
"addBalancer" = "Add Balancer" "addBalancer" = "Add Balancer"
@@ -423,7 +435,7 @@
"balancerSelectors" = "Selectors" "balancerSelectors" = "Selectors"
"tag" = "Tag" "tag" = "Tag"
"tagDesc" = "Unique Tag" "tagDesc" = "Unique Tag"
"balancerDesc" = "It is not possible to use balancerTag and outboundTag at the same time. If used at the same time, only outboundTag will work." "balancerDesc" = "It is not possible to use both balancerTag and outboundTag simultaneously. If both are used together, only outboundTag will function."
[pages.xray.wireguard] [pages.xray.wireguard]
"secretKey" = "Secret Key" "secretKey" = "Secret Key"
@@ -435,9 +447,11 @@
[pages.xray.dns] [pages.xray.dns]
"enable" = "Enable DNS" "enable" = "Enable DNS"
"enableDesc" = "Enable built-in DNS server" "enableDesc" = "Enables built-in DNS server."
"tag" = "DNS Inbound Tag"
"tagDesc" = "This tag will be available as an Inbound tag in routing rules."
"strategy" = "Query Strategy" "strategy" = "Query Strategy"
"strategyDesc" = "Overall strategy to resolve domain names" "strategyDesc" = "Overall strategy to resolve domain names."
"add" = "Add Server" "add" = "Add Server"
"edit" = "Edit Server" "edit" = "Edit Server"
"domains" = "Domains" "domains" = "Domains"

View File

@@ -50,9 +50,14 @@
"clients" = "کاربران" "clients" = "کاربران"
"usage" = "استفاده" "usage" = "استفاده"
"remained" = "باقی‌مانده" "remained" = "باقی‌مانده"
"secAlertTitle" = "هشدار‌امنیتی" "secAlertTitle" = "هشدار‌ امنیتی"
"secAlertSsl" = "این‌اتصال‌امن نیست. لطفا‌ تازمانی‌که تی‌ال‌اس برای محافظت از‌ داده‌ها فعال نشده‌است، از وارد کردن اطلاعات حساس خودداری کنید" "secAlertSsl" = "این‌اتصال‌امن نیست. لطفا‌ تازمانی‌که تی‌ال‌اس برای محافظت از‌ داده‌ها فعال نشده‌است، از وارد کردن اطلاعات حساس خودداری کنید"
"secAlertConf" = "پیکربندی‌های خاصی مستعد حملات سایبری شناسایی شده‌اند، اقدام فوری برای تقویت پروتکل‌های امنیتی و محافظت در برابر نقض‌های امنیتی لازم است" "secAlertConf" = "تنظیمات خاصی در برابر حملات آسیب پذیر هستند. توصیه می‌شود پروتکل‌های امنیتی را برای جلوگیری از نفوذ احتمالی تقویت کنید"
"secAlertSSL" = "پنل فاقد ارتباط امن است. لطفاً یک گواهینامه تی‌ال‌اس برای محافظت از داده‌ها نصب کنید"
"secAlertPanelPort" = "استفاده از پورت پیش‌فرض پنل ناامن است. لطفاً یک پورت تصادفی یا خاص تنظیم کنید"
"secAlertPanelURI" = "مسیر پیش‌فرض لینک پنل ناامن است. لطفاً یک مسیر پیچیده تنظیم کنید"
"secAlertSubURI" = "مسیر پیش‌فرض لینک سابسکریپشن ناامن است. لطفاً یک مسیر پیچیده تنظیم کنید"
"secAlertSubJsonURI" = "مسیر پیش‌فرض لینک سابسکریپشن جیسون ناامن است. لطفاً یک مسیر پیچیده تنظیم کنید"
"security" = "امنیت" "security" = "امنیت"
[menu] [menu]
@@ -105,7 +110,7 @@
"backup" = "پشتیبان‌گیری و بازیابی" "backup" = "پشتیبان‌گیری و بازیابی"
"backupTitle" = "پشتیبان‌گیری و بازیابی دیتابیس" "backupTitle" = "پشتیبان‌گیری و بازیابی دیتابیس"
"backupDescription" = "توصیه‌می‌شود قبل‌از بازیابی دیتابیس، یک نسخه پشتیبان تهیه ‌کنید" "backupDescription" = "توصیه‌می‌شود قبل‌از بازیابی دیتابیس، یک نسخه پشتیبان تهیه ‌کنید"
"exportDatabase" = "پشتیبان‌گیری" "exportDatabase" = "دریافت پشتیبان"
"importDatabase" = "بازیابی" "importDatabase" = "بازیابی"
[pages.inbounds] [pages.inbounds]
@@ -262,7 +267,7 @@
"tgNotifyLoginDesc" = "هر زمان کسی سعی به ورود به وب پنل شما را داشت. درباره نام‌کاربری، آی‌پی و زمان، مطلع می‌شوید" "tgNotifyLoginDesc" = "هر زمان کسی سعی به ورود به وب پنل شما را داشت. درباره نام‌کاربری، آی‌پی و زمان، مطلع می‌شوید"
"sessionMaxAge" = "مدت جلسه" "sessionMaxAge" = "مدت جلسه"
"sessionMaxAgeDesc" = "بیشینه مدت زمانی‌که می‌توانید لاگین بمانید. واحد: دقیقه" "sessionMaxAgeDesc" = "بیشینه مدت زمانی‌که می‌توانید لاگین بمانید. واحد: دقیقه"
"expireTimeDiff" = "اطلاع‌رسانی زمانانقضا" "expireTimeDiff" = "اطلاع‌رسانی زمان انقضا"
"expireTimeDiffDesc" = "وقتی زمان باقی‌مانده به‌آستانه تعیین‌شده رسید، مطلع می‌شوید. واحد: روز" "expireTimeDiffDesc" = "وقتی زمان باقی‌مانده به‌آستانه تعیین‌شده رسید، مطلع می‌شوید. واحد: روز"
"trafficDiff" = "اطلاع‌رسانی ترافیک باقی‌مانده" "trafficDiff" = "اطلاع‌رسانی ترافیک باقی‌مانده"
"trafficDiffDesc" = "وقتی‌ ترافیک باقی‌مانده به‌آستانه تعیین‌شده رسید، مطلع می‌شوید. واحد: گیگابایت" "trafficDiffDesc" = "وقتی‌ ترافیک باقی‌مانده به‌آستانه تعیین‌شده رسید، مطلع می‌شوید. واحد: گیگابایت"
@@ -311,8 +316,8 @@
"advancedTemplate" = "پیشرفته" "advancedTemplate" = "پیشرفته"
"generalConfigs" = "استراتژی‌ کلی" "generalConfigs" = "استراتژی‌ کلی"
"generalConfigsDesc" = "این گزینه‌ها استراتژی کلی ترافیک را تعیین می‌کنند" "generalConfigsDesc" = "این گزینه‌ها استراتژی کلی ترافیک را تعیین می‌کنند"
"logConfigs" = "گزارش‌ها" "logConfigs" = "تنظیمات گزارش‌ها"
"logConfigsDesc" = "گزارشها ممکن است بر کارایی سرور شما تأثیر بگذارند. توصیه می شود فقط در صورت نیاز آن را آگاهانه فعال کنید" "logConfigsDesc" = "فعال کردن گزارش ممکن است بر عملکرد سرور شما تأثیر بگذارد. توصیه میشود فقط در صورت لزوم آن را با دقت فعال کنید"
"blockConfigs" = "سپر محافظ" "blockConfigs" = "سپر محافظ"
"blockConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس پروتکل‌های درخواستی خاص، و وب سایت‌ها مسدود می‌کند" "blockConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس پروتکل‌های درخواستی خاص، و وب سایت‌ها مسدود می‌کند"
"blockCountryConfigs" = "مسدودسازی کشور" "blockCountryConfigs" = "مسدودسازی کشور"
@@ -320,7 +325,7 @@
"directCountryConfigs" = "اتصال مستقیم کشور" "directCountryConfigs" = "اتصال مستقیم کشور"
"directCountryConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس کشور درخواستی خاص بصورت مستقیم ارسال می‌کند" "directCountryConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس کشور درخواستی خاص بصورت مستقیم ارسال می‌کند"
"ipv4Configs" = "IPv4 مسیریابی" "ipv4Configs" = "IPv4 مسیریابی"
"ipv4ConfigsDesc" = "این گزینه‌ها ترافیک را از طریق آی‌پی نسخه4 ماشین، به مقصد هدایت می‌کند" "ipv4ConfigsDesc" = "این گزینه‌ها ترافیک را از طریق آی‌پی نسخه4 سرور، به مقصد هدایت می‌کند"
"warpConfigs" = "WARP تنظمیات" "warpConfigs" = "WARP تنظمیات"
"warpConfigsDesc" = "این گزینه‌ها ترافیک‌ را از طریق وارپ کلادفلر به مقصد هدایت می‌کند" "warpConfigsDesc" = "این گزینه‌ها ترافیک‌ را از طریق وارپ کلادفلر به مقصد هدایت می‌کند"
"Template" = "‌پیکربندی پیشرفته الگو ایکس‌ری" "Template" = "‌پیکربندی پیشرفته الگو ایکس‌ری"
@@ -359,7 +364,7 @@
"DirectChinaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور چین" "DirectChinaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور چین"
"DirectRussiaIp" = "ارتباط مستقیم آی‌پی‌های روسیه" "DirectRussiaIp" = "ارتباط مستقیم آی‌پی‌های روسیه"
"DirectRussiaIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور روسیه" "DirectRussiaIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور روسیه"
"DirectRussiaDomain" = "ارتباط مستقیم دامنه های روسیه" "DirectRussiaDomain" = "ارتباط مستقیم دامنههای روسیه"
"DirectRussiaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور روسیه" "DirectRussiaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور روسیه"
"GoogleIPv4" = "گوگل" "GoogleIPv4" = "گوگل"
"GoogleIPv4Desc" = "ترافیک را از طریق آی‌پی نسخه4، به گوگل هدایت می‌کند" "GoogleIPv4Desc" = "ترافیک را از طریق آی‌پی نسخه4، به گوگل هدایت می‌کند"
@@ -381,6 +386,12 @@
"Routings" = "قوانین مسیریابی" "Routings" = "قوانین مسیریابی"
"RoutingsDesc" = "اولویت هر قانون مهم است" "RoutingsDesc" = "اولویت هر قانون مهم است"
"Balancers" = "بالانسرها" "Balancers" = "بالانسرها"
"logLevel" = "سطح گزارش"
"logLevelDesc" = "سطح گزارش، شدت مسائلی را که باید ثبت شوند، تعیین می‌کند"
"accessLog" = "گزارش دسترسی"
"accessLogDesc" = "مسیر فایل گزارش دسترسی"
"errorLog" = "گزارش خطا"
"errorLogDesc" = "مسیر فایل گزارش خطا"
[pages.xray.rules] [pages.xray.rules]
"first" = "اولین" "first" = "اولین"
@@ -414,6 +425,7 @@
"settings" = "تنظیمات" "settings" = "تنظیمات"
"accountInfo" = "اطلاعات حساب" "accountInfo" = "اطلاعات حساب"
"outboundStatus" = "وضعیت خروجی" "outboundStatus" = "وضعیت خروجی"
"sendThrough" = "ارسال با"
[pages.xray.balancer] [pages.xray.balancer]
"addBalancer" = "افزودن بالانسر" "addBalancer" = "افزودن بالانسر"
@@ -422,7 +434,7 @@
"balancerSelectors" = "انتخاب‌گرها" "balancerSelectors" = "انتخاب‌گرها"
"tag" = "برچسب" "tag" = "برچسب"
"tagDesc" = "برچسب یگانه" "tagDesc" = "برچسب یگانه"
"balancerDesc" = "امکان استفاده همزمان برچسب خروجی و برچسب بالانسر باهم وجود ندارد. درصورت استفاده همزمان فقط برجسب خروجی عمل خواهد کرد." "balancerDesc" = "امکان استفاده همزمان برچسب خروجی و برچسب بالانسر باهم وجود ندارد. درصورت استفاده همزمان فقط برچسب خروجی عمل خواهد کرد"
[pages.xray.wireguard] [pages.xray.wireguard]
"secretKey" = "کلید شخصی" "secretKey" = "کلید شخصی"
@@ -434,9 +446,11 @@
[pages.xray.dns] [pages.xray.dns]
"enable" = "فعال کردن حل دامنه" "enable" = "فعال کردن حل دامنه"
"enableDesc" = "سرور حل دامنه داخلی را فعال کنید" "enableDesc" = "سرور حل دامنه داخلی را فعال می‌کند"
"tag" = "برچسب"
"tagDesc" = "این برچسب در قوانین مسیریابی به عنوان یک برچسب ورودی قابل استفاده خواهد بود"
"strategy" = "استراتژی پرس‌وجو" "strategy" = "استراتژی پرس‌وجو"
"strategyDesc" = "استراتژی کلی برای حل نام دامنه" "strategyDesc" = "استراتژی کلی برای حل نامدامنه"
"add" = "افزودن سرور" "add" = "افزودن سرور"
"edit" = "ویرایش سرور" "edit" = "ویرایش سرور"
"domains" = "دامنه‌ها" "domains" = "دامنه‌ها"

View File

@@ -52,14 +52,19 @@
"remained" = "Осталось" "remained" = "Осталось"
"secAlertTitle" = "Предупреждение системы безопасности" "secAlertTitle" = "Предупреждение системы безопасности"
"secAlertSsl" = "Это соединение не защищено. Пожалуйста, воздержитесь от ввода конфиденциальной информации до тех пор, пока не будет активирован TLS для защиты данных" "secAlertSsl" = "Это соединение не защищено. Пожалуйста, воздержитесь от ввода конфиденциальной информации до тех пор, пока не будет активирован TLS для защиты данных"
"secAlertConf" = "Некоторые конфигурации были определены как уязвимые для атак, что требует немедленных действий по усилению протоколов безопасности и защите от потенциальных нарушений безопасности." "secAlertConf" = "Некоторые настройки уязвимы для атак. Рекомендуется усилить протоколы безопасности, чтобы предотвратить потенциальные нарушения."
"secAlertSSL" = "В панели отсутствует безопасное соединение. Пожалуйста, установите сертификат TLS для защиты данных."
"secAlertPanelPort" = "Порт по умолчанию панели небезопасен. Пожалуйста, настройте случайный или определенный порт."
"secAlertPanelURI" = "URI-путь по умолчанию панели небезопасен. Пожалуйста, настройте сложный URI-путь."
"secAlertSubURI" = "URI-путь по умолчанию подписки небезопасен. Пожалуйста, настройте сложный URI-путь."
"secAlertSubJsonURI" = "URI-путь по умолчанию для JSON подписки небезопасен. Пожалуйста, настройте сложный URI-путь."
"security" = "Безопасность" "security" = "Безопасность"
[menu] [menu]
"dashboard" = "Статус системы" "dashboard" = "Обзор"
"inbounds" = "Подключения" "inbounds" = "Подключения"
"settings" = "Настройки" "settings" = "Настройки"
"xray" = "Xray Настройки" "xray" = "Xray"
"logout" = "Выйти" "logout" = "Выйти"
"link" = "Менеджмент" "link" = "Менеджмент"
@@ -312,8 +317,8 @@
"advancedTemplate" = "Расширенные шаблоны" "advancedTemplate" = "Расширенные шаблоны"
"generalConfigs" = "Основные настройки" "generalConfigs" = "Основные настройки"
"generalConfigsDesc" = "Общие настройки" "generalConfigsDesc" = "Общие настройки"
"logConfigs" = "Журнал" "logConfigs" = "Настройки журналов"
"logConfigsDesc" = "Журналы могут повлиять на эффективность вашего сервера. Рекомендуется включать их с умом только в случае ваших нужд!" "logConfigsDesc" = "Включение журнала может повлиять на эффективность вашего сервера. Рекомендуется включать его разумно, только когда это необходимо."
"blockConfigs" = "Блокирующие конфигурации" "blockConfigs" = "Блокирующие конфигурации"
"blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам." "blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам."
"blockCountryConfigs" = "Конфигурация блокировки стран" "blockCountryConfigs" = "Конфигурация блокировки стран"
@@ -362,19 +367,19 @@
"DirectRussiaIpDesc" = "Изменить шаблон конфигурации для прямого подключения к диапазонам IP-адресов России" "DirectRussiaIpDesc" = "Изменить шаблон конфигурации для прямого подключения к диапазонам IP-адресов России"
"DirectRussiaDomain" = "Прямое подключение к доменам России" "DirectRussiaDomain" = "Прямое подключение к доменам России"
"DirectRussiaDomainDesc" = "Изменить шаблон конфигурации для прямого подключения к доменам России" "DirectRussiaDomainDesc" = "Изменить шаблон конфигурации для прямого подключения к доменам России"
"GoogleIPv4" = "Использовать IPv4 для Google" "GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "Применить маршрутизацию Google для подключения к IPv4." "GoogleIPv4Desc" = "Применить маршрутизацию Google для подключения к IPv4."
"NetflixIPv4" = "Использовать IPv4 для Netflix" "NetflixIPv4" = "Netflix"
"NetflixIPv4Desc" = "Применить маршрутизацию Netflix для подключения к IPv4." "NetflixIPv4Desc" = "Применить маршрутизацию Netflix для подключения к IPv4."
"GoogleWARP" = "Маршрутизация Google через WARP" "GoogleWARP" = "Google"
"GoogleWARPDesc" = "Добавить маршрутизацию для Google через WARP" "GoogleWARPDesc" = "Добавить маршрутизацию для Google через WARP"
"OpenAIWARP" = "Маршрутизация OpenAI (ChatGPT) через WARP" "OpenAIWARP" = "ChatGPT"
"OpenAIWARPDesc" = "Добавить маршрутизацию для OpenAI (ChatGPT) через WARP" "OpenAIWARPDesc" = "Добавить маршрутизацию для ChatGPT через WARP"
"NetflixWARP" = "Маршрутизация Netflix через WARP" "NetflixWARP" = "Netflix"
"NetflixWARPDesc" = "Добавить маршрутизацию для Netflix через WARP" "NetflixWARPDesc" = "Добавить маршрутизацию для Netflix через WARP"
"MetaWARP" = "Мета" "MetaWARP" = "Meta"
"MetaWARPDesc" = "Направляет трафик в Meta (Instagram, Facebook, WhatsApp, Threads...) через WARP." "MetaWARPDesc" = "Направляет трафик в Meta (Instagram, Facebook, WhatsApp, Threads...) через WARP."
"SpotifyWARP" = "Маршрутизация Spotify через WARP" "SpotifyWARP" = "Spotify"
"SpotifyWARPDesc" = "Добавить маршрутизацию для Spotify через WARP" "SpotifyWARPDesc" = "Добавить маршрутизацию для Spotify через WARP"
"completeTemplate" = "Все" "completeTemplate" = "Все"
"Inbounds" = "Входящие" "Inbounds" = "Входящие"
@@ -382,6 +387,12 @@
"Routings" = "Правила маршрутизации" "Routings" = "Правила маршрутизации"
"RoutingsDesc" = "Важен приоритет каждого правила!" "RoutingsDesc" = "Важен приоритет каждого правила!"
"Balancers" = "Балансиры" "Balancers" = "Балансиры"
"logLevel" = "Уровень журнала"
"logLevelDesc" = "Уровень журнала ошибок определяет серьезность проблем, которые следует фиксировать."
"accessLog" = "Журнал доступа"
"accessLogDesc" = "Путь к файлу журнала доступа."
"errorLog" = "Журнал ошибок"
"errorLogDesc" = "Путь к файлу журнала ошибок."
[pages.xray.rules] [pages.xray.rules]
"first" = "Первый" "first" = "Первый"
@@ -415,6 +426,7 @@
"settings" = "Настройки" "settings" = "Настройки"
"accountInfo" = "Информация Об Учетной Записи" "accountInfo" = "Информация Об Учетной Записи"
"outboundStatus" = "Исходящий статус" "outboundStatus" = "Исходящий статус"
"sendThrough" = "Отправить через"
[pages.xray.balancer] [pages.xray.balancer]
"addBalancer" = "Добавить балансир" "addBalancer" = "Добавить балансир"
@@ -436,6 +448,8 @@
[pages.xray.dns] [pages.xray.dns]
"enable" = "Включить DNS" "enable" = "Включить DNS"
"enableDesc" = "Включить встроенный DNS-сервер" "enableDesc" = "Включить встроенный DNS-сервер"
"tag" = "Входящий тег DNS"
"tagDesc" = "Этот тег будет доступен как входящий тег в правилах маршрутизации."
"strategy" = "Стратегия запроса" "strategy" = "Стратегия запроса"
"strategyDesc" = "Общая стратегия разрешения доменных имен" "strategyDesc" = "Общая стратегия разрешения доменных имен"
"add" = "Добавить сервер" "add" = "Добавить сервер"
@@ -509,7 +523,6 @@
"backupTime" = "🗄 Время резервного копирования: {{ .Time }}\r\n" "backupTime" = "🗄 Время резервного копирования: {{ .Time }}\r\n"
"yes" = "✅ Да" "yes" = "✅ Да"
"no" = "❌ Нет" "no" = "❌ Нет"
[tgbot.buttons] [tgbot.buttons]
"dbBackup" = "Получить резервную копию базы данных" "dbBackup" = "Получить резервную копию базы данных"
"serverUsage" = "Использование сервера" "serverUsage" = "Использование сервера"

View File

@@ -52,13 +52,18 @@
"remained" = "Còn lại" "remained" = "Còn lại"
"secAlertTitle" = "Cảnh báo an ninh-Tiếng Việt by Ohoang7" "secAlertTitle" = "Cảnh báo an ninh-Tiếng Việt by Ohoang7"
"secAlertSsl" = "Kết nối này không an toàn; Vui lòng không nhập thông tin nhạy cảm cho đến khi TLS được kích hoạt để bảo vệ dữ liệu của Bạn" "secAlertSsl" = "Kết nối này không an toàn; Vui lòng không nhập thông tin nhạy cảm cho đến khi TLS được kích hoạt để bảo vệ dữ liệu của Bạn"
"secAlertConf" = "Một số cấu hình nhất định đã được xác định là dễ bị tấn công, thúc đẩy hành động ngay lập tức để củng cố các giao thức bảo mật và bảo vệ chống lại các vi phạm bảo mật tiềm ẩn." "secAlertConf" = "Một số cài đặt có thể dễ bị tấn công. Đề xuất tăng cường các giao thức bảo mật để ngăn chặn các vi phạm tiềm ẩn."
"secAlertSSL" = "Bảng điều khiển thiếu kết nối an toàn. Vui lòng cài đặt chứng chỉ TLS để bảo vệ dữ liệu."
"secAlertPanelPort" = "Cổng mặc định của bảng điều khiển có thể dễ bị tấn công. Vui lòng cấu hình một cổng ngẫu nhiên hoặc cụ thể."
"secAlertPanelURI" = "Đường dẫn URI mặc định của bảng điều khiển không an toàn. Vui lòng cấu hình một đường dẫn URI phức tạp."
"secAlertSubURI" = "Đường dẫn URI mặc định của đăng ký không an toàn. Vui lòng cấu hình một đường dẫn URI phức tạp."
"secAlertSubJsonURI" = "Đường dẫn URI JSON mặc định của đăng ký không an toàn. Vui lòng cấu hình một đường dẫn URI phức tạp."
"security" = "Bảo vệ" "security" = "Bảo vệ"
[menu] [menu]
"dashboard" = "Trạng thái hệ thống" "dashboard" = "Tng quan"
"inbounds" = "Đầu Vào khách hàng" "inbounds" = "Đầu Vào khách hàng"
"settings" = "Cài đặt bảng điều khiển" "settings" = "Cài đặt X-UI"
"xray" = "Cài đặt Xray" "xray" = "Cài đặt Xray"
"logout" = "Đăng xuất" "logout" = "Đăng xuất"
"link" = "Sự quản lý" "link" = "Sự quản lý"
@@ -312,8 +317,8 @@
"advancedTemplate" = "Mẫu nâng cao" "advancedTemplate" = "Mẫu nâng cao"
"generalConfigs" = "Cấu hình Chung" "generalConfigs" = "Cấu hình Chung"
"generalConfigsDesc" = "Những tùy chọn này sẽ cung cấp điều chỉnh tổng quát." "generalConfigsDesc" = "Những tùy chọn này sẽ cung cấp điều chỉnh tổng quát."
"logConfigs" = "Nhật ký" "logConfigs" = "Cài đặt nhật ký"
"logConfigsDesc" = "Nhật ký có thể ảnh hưởng đến hiệu suất máy chủ của bạn. Bạn chỉ nên kích hoạt nó một cách khôn ngoan trong trường hợp bạn cần" "logConfigsDesc" = "Bật nhật ký có thể ảnh hưởng đến hiệu suất của máy chủ của bạn. Đề xuất chỉ nên bật khi cần thiết và một cách sáng suốt."
"blockConfigs" = "Cấu hình Chặn" "blockConfigs" = "Cấu hình Chặn"
"blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể." "blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể."
"blockCountryConfigs" = "Cấu hình Chặn Quốc gia" "blockCountryConfigs" = "Cấu hình Chặn Quốc gia"
@@ -362,19 +367,19 @@
"DirectRussiaIpDesc" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với dải IP của Nga." "DirectRussiaIpDesc" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với dải IP của Nga."
"DirectRussiaDomain" = "Kết nối trực tiếp tới các miền của Nga" "DirectRussiaDomain" = "Kết nối trực tiếp tới các miền của Nga"
"DirectRussiaDomainDesc" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với miền Nga." "DirectRussiaDomainDesc" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với miền Nga."
"GoogleIPv4" = "Sử dụng IPv4 cho Google" "GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "Thêm định tuyến để Google kết nối với IPv4." "GoogleIPv4Desc" = "Thêm định tuyến để Google kết nối với IPv4."
"NetflixIPv4" = "Sử dụng IPv4 cho Netflix" "NetflixIPv4" = "Netflix"
"NetflixIPv4Desc" = "Thêm định tuyến cho Netflix để kết nối với IPv4." "NetflixIPv4Desc" = "Thêm định tuyến cho Netflix để kết nối với IPv4."
"GoogleWARP" = "Định tuyến Google qua WARP." "GoogleWARP" = "Google"
"GoogleWARPDesc" = "Thêm định tuyến cho Google qua WARP." "GoogleWARPDesc" = "Thêm định tuyến cho Google qua WARP."
"OpenAIWARP" = "Định tuyến OpenAI (ChatGPT) qua WARP." "OpenAIWARP" = "ChatGPT"
"OpenAIWARPDesc" = "Thêm định tuyến cho OpenAI (ChatGPT) qua WARP." "OpenAIWARPDesc" = "Thêm định tuyến cho ChatGPT qua WARP."
"NetflixWARP" = "Định tuyến Netflix qua WARP." "NetflixWARP" = "Netflix"
"NetflixWARPDesc" = "Thêm định tuyến cho Netflix qua WARP." "NetflixWARPDesc" = "Thêm định tuyến cho Netflix qua WARP."
"MetaWARP" = "Meta" "MetaWARP" = "Meta"
"MetaWARPDesc" = "Định tuyến lưu lượng truy cập tới Meta (Instagram, Facebook, WhatsApp, Threads,...) thông qua WARP." "MetaWARPDesc" = "Định tuyến lưu lượng truy cập tới Meta (Instagram, Facebook, WhatsApp, Threads,...) thông qua WARP."
"SpotifyWARP" = "Định tuyến Spotify qua WARP." "SpotifyWARP" = "Spotify"
"SpotifyWARPDesc" = "Thêm định tuyến cho Spotify qua WARP." "SpotifyWARPDesc" = "Thêm định tuyến cho Spotify qua WARP."
"completeTemplate" = "Tất cả" "completeTemplate" = "Tất cả"
"Inbounds" = "Đầu vào" "Inbounds" = "Đầu vào"
@@ -382,6 +387,12 @@
"Routings" = "Quy tắc định tuyến" "Routings" = "Quy tắc định tuyến"
"RoutingsDesc" = "Mức độ ưu tiên của mỗi quy tắc là quan trọng!" "RoutingsDesc" = "Mức độ ưu tiên của mỗi quy tắc là quan trọng!"
"Balancers" = "Cân bằng" "Balancers" = "Cân bằng"
"logLevel" = "Mức đăng nhập"
"logLevelDesc" = "Cấp độ nhật ký cho nhật ký lỗi xác định mức độ nghiêm trọng của sự cố cần được ghi lại."
"accessLog" = "Nhật ký truy cập"
"accessLogDesc" = "Đường dẫn tệp cho nhật ký truy cập."
"errorLog" = "Nhật ký lỗi"
"errorLogDesc" = "Đường dẫn tệp cho nhật ký lỗi."
[pages.xray.rules] [pages.xray.rules]
"first" = "Đầu tiên" "first" = "Đầu tiên"
@@ -415,6 +426,7 @@
"settings" = "cài đặt" "settings" = "cài đặt"
"accountInfo" = "Thông tin tài khoản" "accountInfo" = "Thông tin tài khoản"
"outboundStatus" = "Trạng thái đầu ra" "outboundStatus" = "Trạng thái đầu ra"
"sendThrough" = "Gửi qua"
[pages.xray.balancer] [pages.xray.balancer]
"addBalancer" = "Thêm cân bằng" "addBalancer" = "Thêm cân bằng"
@@ -436,6 +448,8 @@
[pages.xray.dns] [pages.xray.dns]
"enable" = "Kích hoạt DNS" "enable" = "Kích hoạt DNS"
"enableDesc" = "Kích hoạt máy chủ DNS tích hợp" "enableDesc" = "Kích hoạt máy chủ DNS tích hợp"
"tag" = "Thẻ gửi đến DNS"
"tagDesc" = "Thẻ này sẽ có sẵn dưới dạng thẻ Gửi đến trong quy tắc định tuyến."
"strategy" = "Chiến lược truy vấn" "strategy" = "Chiến lược truy vấn"
"strategyDesc" = "Chiến lược tổng thể để phân giải tên miền" "strategyDesc" = "Chiến lược tổng thể để phân giải tên miền"
"add" = "Thêm máy chủ" "add" = "Thêm máy chủ"

View File

@@ -52,14 +52,19 @@
"remained" = "仍然存在" "remained" = "仍然存在"
"secAlertTitle" = "安全警报" "secAlertTitle" = "安全警报"
"secAlertSsl" = "此连接不安全;在激活 TLS 进行数据保护之前,请勿输入敏感信息" "secAlertSsl" = "此连接不安全;在激活 TLS 进行数据保护之前,请勿输入敏感信息"
"secAlertConf" = "某些配置已被确定为容易受到攻击,促使立即采取行动以加强安全协议并防范潜在的安全漏洞。" "secAlertConf" = "某些设置容易受到攻击。建议加强安全协议以防止潜在的违规行为。"
"secAlertSSL" = "面板缺乏安全连接。请安装 TLS 证书以保护数据。"
"secAlertPanelPort" = "面板默认端口存在漏洞。请配置随机或特定端口。"
"secAlertPanelURI" = "面板默认 URI 路径不安全。请配置复杂的 URI 路径。"
"secAlertSubURI" = "订阅默认 URI 路径不安全。请配置复杂的 URI 路径。"
"secAlertSubJsonURI" = "订阅 JSON 默认 URI 路径不安全。请配置复杂的 URI 路径。"
"security" = "安全" "security" = "安全"
[menu] [menu]
"dashboard" = "系统状态" "dashboard" = "概述"
"inbounds" = "入站列表" "inbounds" = "入站列表"
"settings" = "面板设置" "settings" = "面板设置"
"xray" = "Xray 设置" "xray" = "Xray"
"logout" = "退出登录" "logout" = "退出登录"
"link" = "管理" "link" = "管理"
@@ -80,7 +85,7 @@
"hard" = "硬盘" "hard" = "硬盘"
"serverInfo" = "服务器" "serverInfo" = "服务器"
"hostname" = "主机名" "hostname" = "主机名"
"xrayStatus" = "Xray" "xrayStatus" = "Xray 状态"
"stopXray" = "停止" "stopXray" = "停止"
"restartXray" = "重启" "restartXray" = "重启"
"xraySwitch" = "切换版本" "xraySwitch" = "切换版本"
@@ -312,8 +317,8 @@
"advancedTemplate" = "高级模板部件" "advancedTemplate" = "高级模板部件"
"generalConfigs" = "通用配置" "generalConfigs" = "通用配置"
"generalConfigsDesc" = "这些选项提供通用设置调整" "generalConfigsDesc" = "这些选项提供通用设置调整"
"logConfigs"="日志" "logConfigs"="日志设置"
"logConfigsDesc" = "日志可能会影响服务器的效率。建议仅在您需要时明智地启用它" "logConfigsDesc" = "启用日志可能会影响服务器的效率。建议仅在要时明智地启用它"
"blockConfigs" = "阻塞配置" "blockConfigs" = "阻塞配置"
"blockConfigsDesc" = "这些选项将禁止用户连接到特定协议和网站" "blockConfigsDesc" = "这些选项将禁止用户连接到特定协议和网站"
"blockCountryConfigs" = "禁连国家配置" "blockCountryConfigs" = "禁连国家配置"
@@ -362,26 +367,32 @@
"DirectRussiaIpDesc" = "更改直接连接到俄罗斯 IP 范围的配置模板" "DirectRussiaIpDesc" = "更改直接连接到俄罗斯 IP 范围的配置模板"
"DirectRussiaDomain" = "直接连接到俄罗斯域" "DirectRussiaDomain" = "直接连接到俄罗斯域"
"DirectRussiaDomainDesc" = "更改直接连接到俄罗斯域的配置模板" "DirectRussiaDomainDesc" = "更改直接连接到俄罗斯域的配置模板"
"GoogleIPv4" = "为谷歌使用 IPv4" "GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "添加谷歌连接IPv4的路由" "GoogleIPv4Desc" = "添加谷歌连接IPv4的路由"
"NetflixIPv4" = "Netflix 使用 IPv4" "NetflixIPv4" = "Netflix"
"NetflixIPv4Desc" = "添加Netflix连接IPv4的路由" "NetflixIPv4Desc" = "添加Netflix连接IPv4的路由"
"GoogleWARP" = "将谷歌路由到 WARP" "GoogleWARP" = "将谷歌路由到 WARP"
"GoogleWARPDesc" = "为谷歌添加路由到WARP" "GoogleWARPDesc" = "为谷歌添加路由到WARP"
"OpenAIWARP" = "将 OpenAI (ChatGPT) 路由到 WARP" "OpenAIWARP" = "ChatGPT"
"OpenAIWARPDesc" = "将OpenAIChatGPT路由添加到WARP" "OpenAIWARPDesc" = "将 ChatGPT 路由添加到WARP"
"NetflixWARP" = "Netflix 路由到 WARP" "NetflixWARP" = "Netflix"
"NetflixWARPDesc" = "为Netflix添加路由到WARP" "NetflixWARPDesc" = "为Netflix添加路由到WARP"
"MetaWARP"="" "MetaWARP"="Meta"
"MetaWARPDesc" = "通过 WARP 将流量路由到 MetaInstagramFacebookWhatsAppThreads..." "MetaWARPDesc" = "通过 WARP 将流量路由到 MetaInstagram, Facebook, WhatsApp, Threads,..."
"SpotifyWARP" = "Spotify 路由到 WARP" "SpotifyWARP" = "Spotify"
"SpotifyWARPDesc" = "Spotify添加路由到WARP" "SpotifyWARPDesc" = "Spotify添加路由到WARP"
"completeTemplate" = "全部" "completeTemplate" = "全部"
"Inbounds" = "界内" "Inbounds" = "界内"
"Outbounds" = "出站" "Outbounds" = "出站"
"Routings" = "路由规则" "Routings" = "路由规则"
"RoutingsDesc" = "每条规则的优先级都很重要" "RoutingsDesc" = "每条规则的优先级都很重要"
"Balancers" = "平衡器" "Balancers" = "平衡器"
"logLevel" = "日志级别"
"logLevelDesc" = "错误日志的日志级别决定了应记录的问题的严重性。"
"accessLog" = "访问日志"
"accessLogDesc" = "访问日志的文件路径。"
"errorLog" = "错误日志"
"errorLogDesc" = "错误日志的文件路径。"
[pages.xray.rules] [pages.xray.rules]
"first" = "第一个" "first" = "第一个"
@@ -415,6 +426,7 @@
"settings" = "设置" "settings" = "设置"
"accountInfo" = "帐户信息" "accountInfo" = "帐户信息"
"outboundStatus" = "出站状态" "outboundStatus" = "出站状态"
"sendThrough" = "发送通过"
[pages.xray.balancer] [pages.xray.balancer]
"addBalancer" = "添加平衡器" "addBalancer" = "添加平衡器"
@@ -436,6 +448,8 @@
[pages.xray.dns] [pages.xray.dns]
"enable" = "启用 DNS" "enable" = "启用 DNS"
"enableDesc" = "启用内置 DNS 服务器" "enableDesc" = "启用内置 DNS 服务器"
"tag" = "DNS 入站标签"
"tagDesc" = "此标记将在路由规则中用作入站标记。"
"strategy" = "查询策略" "strategy" = "查询策略"
"strategyDesc" = "解析域名的总体策略" "strategyDesc" = "解析域名的总体策略"
"add" = "添加服务器" "add" = "添加服务器"

View File

@@ -13,6 +13,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"x-ui/config" "x-ui/config"
"x-ui/logger" "x-ui/logger"
"x-ui/util/common" "x-ui/util/common"
@@ -179,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("x-ui", store))
engine.Use(func(c *gin.Context) { engine.Use(func(c *gin.Context) {
c.Set("base_path", basePath) c.Set("base_path", basePath)
}) })
@@ -283,7 +284,7 @@ func (s *Server) startTask() {
} }
func (s *Server) Start() (err error) { func (s *Server) Start() (err error) {
//This is an anonymous function, no function name // This is an anonymous function, no function name
defer func() { defer func() {
if err != nil { if err != nil {
s.Stop() s.Stop()
@@ -325,19 +326,17 @@ func (s *Server) Start() (err error) {
} }
if certFile != "" || keyFile != "" { if certFile != "" || keyFile != "" {
cert, err := tls.LoadX509KeyPair(certFile, keyFile) cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil { if err == nil {
listener.Close() c := &tls.Config{
return err Certificates: []tls.Certificate{cert},
}
listener = network.NewAutoHttpsListener(listener)
listener = tls.NewListener(listener, c)
logger.Info("web server run https on", listener.Addr())
} else {
logger.Error("error in loading certificates: ", err)
logger.Info("web server run http on", listener.Addr())
} }
c := &tls.Config{
Certificates: []tls.Certificate{cert},
}
listener = network.NewAutoHttpsListener(listener)
listener = tls.NewListener(listener, c)
}
if certFile != "" || keyFile != "" {
logger.Info("web server run https on", listener.Addr())
} else { } else {
logger.Info("web server run http on", listener.Addr()) logger.Info("web server run http on", listener.Addr())
} }

258
x-ui.sh
View File

@@ -307,13 +307,6 @@ show_log() {
fi fi
} }
install_bbr() {
# temporary workaround for installing bbr
bash <(curl -L -s https://raw.githubusercontent.com/teddysun/across/master/bbr.sh)
echo ""
before_show_menu
}
update_shell() { update_shell() {
wget -O /usr/bin/x-ui -N --no-check-certificate https://github.com/alireza0/x-ui/raw/main/x-ui.sh wget -O /usr/bin/x-ui -N --no-check-certificate https://github.com/alireza0/x-ui/raw/main/x-ui.sh
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
@@ -630,6 +623,195 @@ ssl_cert_issue_CF() {
fi fi
} }
firewall_menu() {
echo -e "${green}\t1.${plain} Install Firewall & open ports"
echo -e "${green}\t2.${plain} Allowed List"
echo -e "${green}\t3.${plain} Delete Ports from List"
echo -e "${green}\t4.${plain} Disable Firewall"
echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice
case "$choice" in
0)
show_menu
;;
1)
open_ports
;;
2)
sudo ufw status
;;
3)
delete_ports
;;
4)
sudo ufw disable
;;
*) echo "Invalid choice" ;;
esac
}
open_ports() {
if ! command -v ufw &>/dev/null; then
echo "ufw firewall is not installed. Installing now..."
apt-get update
apt-get install -y ufw
else
echo "ufw firewall is already installed"
fi
# Check if the firewall is inactive
if ufw status | grep -q "Status: active"; then
echo "firewall is already active"
else
# Open the necessary ports
ufw allow ssh
ufw allow http
ufw allow https
ufw allow 54321/tcp
# Enable the firewall
ufw --force enable
fi
# Prompt the user to enter a list of ports
read -p "Enter the ports you want to open (e.g. 80,443,2053 or range 400-500): " ports
# Check if the input is valid
if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
exit 1
fi
# Open the specified ports using ufw
IFS=',' read -ra PORT_LIST <<<"$ports"
for port in "${PORT_LIST[@]}"; do
if [[ $port == *-* ]]; then
# Split the range into start and end ports
start_port=$(echo $port | cut -d'-' -f1)
end_port=$(echo $port | cut -d'-' -f2)
# Loop through the range and open each port
for ((i = start_port; i <= end_port; i++)); do
ufw allow $i
done
else
ufw allow "$port"
fi
done
# Confirm that the ports are open
ufw status | grep $ports
}
delete_ports() {
# Prompt the user to enter the ports they want to delete
read -p "Enter the ports you want to delete (e.g. 80,443,2053 or range 400-500): " ports
# Check if the input is valid
if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
exit 1
fi
# Delete the specified ports using ufw
IFS=',' read -ra PORT_LIST <<<"$ports"
for port in "${PORT_LIST[@]}"; do
if [[ $port == *-* ]]; then
# Split the range into start and end ports
start_port=$(echo $port | cut -d'-' -f1)
end_port=$(echo $port | cut -d'-' -f2)
# Loop through the range and delete each port
for ((i = start_port; i <= end_port; i++)); do
ufw delete allow $i
done
else
ufw delete allow "$port"
fi
done
# Confirm that the ports are deleted
echo "Deleted the specified ports:"
ufw status | grep $ports
}
bbr_menu() {
echo -e "${green}\t1.${plain} Enable BBR"
echo -e "${green}\t2.${plain} Disable BBR"
echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice
case "$choice" in
0)
show_menu
;;
1)
enable_bbr
;;
2)
disable_bbr
;;
*) echo "Invalid choice" ;;
esac
}
disable_bbr() {
if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf || ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
echo -e "${yellow}BBR is not currently enabled.${plain}"
exit 0
fi
# Replace BBR with CUBIC configurations
sed -i 's/net.core.default_qdisc=fq/net.core.default_qdisc=pfifo_fast/' /etc/sysctl.conf
sed -i 's/net.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf
# Apply changes
sysctl -p
# Verify that BBR is replaced with CUBIC
if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "cubic" ]]; then
echo -e "${green}BBR has been replaced with CUBIC successfully.${plain}"
else
echo -e "${red}Failed to replace BBR with CUBIC. Please check your system configuration.${plain}"
fi
}
enable_bbr() {
if grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf && grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
echo -e "${green}BBR is already enabled!${plain}"
exit 0
fi
# Check the OS and install necessary packages
case "${release}" in
ubuntu | debian)
apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
;;
centos | almalinux | rocky)
yum -y update && yum -y install ca-certificates
;;
fedora)
dnf -y update && dnf -y install ca-certificates
;;
*)
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
exit 1
;;
esac
# Enable BBR
echo "net.core.default_qdisc=fq" | tee -a /etc/sysctl.conf
echo "net.ipv4.tcp_congestion_control=bbr" | tee -a /etc/sysctl.conf
# Apply changes
sysctl -p
# Verify that BBR is enabled
if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then
echo -e "${green}BBR has been enabled successfully.${plain}"
else
echo -e "${red}Failed to enable BBR. Please check your system configuration.${plain}"
fi
}
update_geo() { update_geo() {
cd /usr/local/x-ui/bin cd /usr/local/x-ui/bin
echo -e "${green}\t1.${plain} Update Geofiles [Recommended choice] " echo -e "${green}\t1.${plain} Update Geofiles [Recommended choice] "
@@ -667,6 +849,40 @@ update_geo() {
esac esac
} }
run_speedtest() {
# Check if Speedtest is already installed
if ! command -v speedtest &>/dev/null; then
# If not installed, install it
local pkg_manager=""
local speedtest_install_script=""
if command -v dnf &>/dev/null; then
pkg_manager="dnf"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
elif command -v yum &>/dev/null; then
pkg_manager="yum"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
elif command -v apt-get &>/dev/null; then
pkg_manager="apt-get"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
elif command -v apt &>/dev/null; then
pkg_manager="apt"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
fi
if [[ -z $pkg_manager ]]; then
echo "Error: Package manager not found. You may need to install Speedtest manually."
return 1
else
curl -s $speedtest_install_script | bash
$pkg_manager install -y speedtest
fi
fi
# Run Speedtest
speedtest
}
show_usage() { show_usage() {
echo "X-UI Control Menu Usage" echo "X-UI Control Menu Usage"
echo "------------------------------------------" echo "------------------------------------------"
@@ -711,14 +927,16 @@ show_menu() {
${green}14.${plain} Enable Autostart ${green}14.${plain} Enable Autostart
${green}15.${plain} Disable Autostart ${green}15.${plain} Disable Autostart
———————————————— ————————————————
${green}16.${plain} A Key Installation BBR (latest kernel) ${green}16.${plain} SSL Certificate Management
${green}17.${plain} SSL Certificate Management ${green}17.${plain} Cloudflare SSL Certificate
${green}18.${plain} Cloudflare SSL Certificate ${green}18.${plain} Firewall Management
${green}19.${plain} Update Geo Files
———————————————— ————————————————
${green}19.${plain} Enable or Disable BBR
${green}20.${plain} Update Geo Files
${green}21.${plain} Speedtest by Ookla
" "
show_status show_status
echo && read -p "Please enter your selection [0-19]: " num echo && read -p "Please enter your selection [0-21]: " num
case "${num}" in case "${num}" in
0) 0)
@@ -770,19 +988,25 @@ show_menu() {
check_install && disable check_install && disable
;; ;;
16) 16)
install_bbr
;;
17)
ssl_cert_issue_main ssl_cert_issue_main
;; ;;
18) 17)
ssl_cert_issue_CF ssl_cert_issue_CF
;; ;;
18)
firewall_menu
;;
19) 19)
bbr_menu
;;
20)
update_geo update_geo
;; ;;
21)
run_speedtest
;;
*) *)
LOGE "Please enter the correct number [0-19]" LOGE "Please enter the correct number [0-21]"
;; ;;
esac esac
} }

View File

@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"regexp" "regexp"
"time" "time"
"x-ui/logger" "x-ui/logger"
"x-ui/util/common" "x-ui/util/common"
@@ -159,8 +160,8 @@ 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")
} }
var trafficRegex = regexp.MustCompile("(inbound|outbound)>>>([^>]+)>>>traffic>>>(downlink|uplink)") trafficRegex := regexp.MustCompile("(inbound|outbound)>>>([^>]+)>>>traffic>>>(downlink|uplink)")
var ClientTrafficRegex = regexp.MustCompile("(user)>>>([^>]+)>>>traffic>>>(downlink|uplink)") ClientTrafficRegex := regexp.MustCompile("(user)>>>([^>]+)>>>traffic>>>(downlink|uplink)")
client := *x.StatsServiceClient client := *x.StatsServiceClient
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)

View File

@@ -2,22 +2,24 @@ package xray
import ( import (
"bytes" "bytes"
"x-ui/util/json_util" "x-ui/util/json_util"
) )
type Config struct { type Config struct {
LogConfig json_util.RawMessage `json:"log"` LogConfig json_util.RawMessage `json:"log"`
RouterConfig json_util.RawMessage `json:"routing"` RouterConfig json_util.RawMessage `json:"routing"`
DNSConfig json_util.RawMessage `json:"dns"` DNSConfig json_util.RawMessage `json:"dns"`
InboundConfigs []InboundConfig `json:"inbounds"` InboundConfigs []InboundConfig `json:"inbounds"`
OutboundConfigs json_util.RawMessage `json:"outbounds"` OutboundConfigs json_util.RawMessage `json:"outbounds"`
Transport json_util.RawMessage `json:"transport"` Transport json_util.RawMessage `json:"transport"`
Policy json_util.RawMessage `json:"policy"` Policy json_util.RawMessage `json:"policy"`
API json_util.RawMessage `json:"api"` API json_util.RawMessage `json:"api"`
Stats json_util.RawMessage `json:"stats"` Stats json_util.RawMessage `json:"stats"`
Reverse json_util.RawMessage `json:"reverse"` Reverse json_util.RawMessage `json:"reverse"`
FakeDNS json_util.RawMessage `json:"fakedns"` FakeDNS json_util.RawMessage `json:"fakedns"`
Observatory json_util.RawMessage `json:"observatory"` Observatory json_util.RawMessage `json:"observatory"`
BurstObservatory json_util.RawMessage `json:"burstObservatory"`
} }
func (c *Config) Equals(other *Config) bool { func (c *Config) Equals(other *Config) bool {

View File

@@ -2,6 +2,7 @@ package xray
import ( import (
"bytes" "bytes"
"x-ui/util/json_util" "x-ui/util/json_util"
) )

View File

@@ -3,6 +3,7 @@ package xray
import ( import (
"regexp" "regexp"
"strings" "strings"
"x-ui/logger" "x-ui/logger"
) )

View File

@@ -11,7 +11,9 @@ import (
"runtime" "runtime"
"syscall" "syscall"
"time" "time"
"x-ui/config" "x-ui/config"
"x-ui/logger"
"x-ui/util/common" "x-ui/util/common"
) )
@@ -148,13 +150,14 @@ func (p *process) Start() (err error) {
defer func() { defer func() {
if err != nil { if err != nil {
logger.Error("Failure in running xray-core process: ", err)
p.exitErr = err p.exitErr = err
} }
}() }()
data, err := json.MarshalIndent(p.config, "", " ") data, err := json.MarshalIndent(p.config, "", " ")
if err != nil { if err != nil {
return common.NewErrorf("Failure to generate XRAY configuration files: %v", err) return common.NewErrorf("Failed to generate XRAY configuration files: %v", err)
} }
configPath := GetConfigPath() configPath := GetConfigPath()
err = os.WriteFile(configPath, data, fs.ModePerm) err = os.WriteFile(configPath, data, fs.ModePerm)
@@ -171,6 +174,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)
p.exitErr = err p.exitErr = err
} }
}() }()