mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-19 15:25:49 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa541b4719 | ||
|
|
b6fe0c9342 | ||
|
|
00e3db0caa | ||
|
|
ed3a6c36e6 | ||
|
|
ada0287dc9 | ||
|
|
4f9ec33393 | ||
|
|
fd98ff6fad | ||
|
|
ecf89e430d | ||
|
|
af881a8fe4 | ||
|
|
490ed0ab96 | ||
|
|
29d348bd67 | ||
|
|
258101efae | ||
|
|
cec900b596 | ||
|
|
463a120d58 | ||
|
|
cf521367f7 | ||
|
|
2012dd0c0a | ||
|
|
b22d1e082a | ||
|
|
dc332b09fa | ||
|
|
a30043d8ae | ||
|
|
482e04e614 | ||
|
|
5ae613c188 | ||
|
|
1cc72ae6af | ||
|
|
b6ccb2e076 | ||
|
|
e3c2b42048 | ||
|
|
029df32b71 | ||
|
|
629d0da33c | ||
|
|
80c3f84eac | ||
|
|
2a6ed18163 | ||
|
|
271a85e9b5 | ||
|
|
bbb0663198 | ||
|
|
f5c7b03e80 | ||
|
|
e4b9b70c26 | ||
|
|
53fcf4da15 | ||
|
|
bb8f3dab36 | ||
|
|
c834e4cc35 | ||
|
|
225b3fc3a5 | ||
|
|
6b174762e2 | ||
|
|
1c660bbea1 | ||
|
|
50d4e67e59 | ||
|
|
1025eeebf9 | ||
|
|
fe00a61963 | ||
|
|
64e2e62292 | ||
|
|
0f6c8f1ec5 | ||
|
|
5bc6f7518c | ||
|
|
79f2d34541 | ||
|
|
0aec9b6cb7 | ||
|
|
eb36faced9 | ||
|
|
99e9c27d2f | ||
|
|
b3bb31c98c | ||
|
|
09407710a7 | ||
|
|
7a38a2474e | ||
|
|
afd072a430 | ||
|
|
3bbf83924c | ||
|
|
1329b2bb61 | ||
|
|
d80b26f241 |
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -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
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
|||||||
.cache
|
.cache
|
||||||
.sync*
|
.sync*
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
|
*.log
|
||||||
access.log
|
access.log
|
||||||
error.log
|
error.log
|
||||||
tmp
|
tmp
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
@@ -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"
|
[](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
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.7.2
|
1.8.0
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
36
go.mod
36
go.mod
@@ -11,11 +11,11 @@ require (
|
|||||||
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.1.1
|
||||||
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.7
|
||||||
)
|
)
|
||||||
@@ -37,9 +37,9 @@ require (
|
|||||||
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.18.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
|
||||||
|
|||||||
93
go.sum
93
go.sum
@@ -59,8 +59,8 @@ 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=
|
||||||
@@ -88,13 +88,13 @@ 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.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
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=
|
||||||
@@ -111,8 +111,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,11 +136,11 @@ 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/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.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
@@ -164,12 +164,10 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||||||
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 +178,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=
|
||||||
@@ -207,8 +205,8 @@ 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=
|
||||||
@@ -217,15 +215,15 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
|
|||||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
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/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=
|
||||||
@@ -264,8 +262,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=
|
||||||
@@ -290,9 +289,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=
|
||||||
@@ -311,16 +309,16 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
|
|||||||
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.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||||
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=
|
||||||
@@ -330,8 +328,8 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
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.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||||
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=
|
||||||
@@ -361,9 +359,9 @@ 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/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||||
|
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
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=
|
||||||
@@ -380,8 +378,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
golang.org/x/tools v0.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,19 +397,18 @@ 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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.33.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/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 h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
@@ -434,8 +431,6 @@ gorm.io/gorm v1.25.7/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=
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
3
main.go
3
main.go
@@ -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()
|
||||||
|
|||||||
@@ -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": {}
|
||||||
}
|
}
|
||||||
41
sub/sub.go
41
sub/sub.go
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,47 @@ 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...)
|
||||||
|
fmt.Printf("routing: %#v\n\nRules: %#v\n\n", routing, 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 +71,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 +83,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 +91,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 +127,15 @@ 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...)
|
finalJson, _ := json.MarshalIndent(configArray, "", " ")
|
||||||
var outboundStrings []json_util.RawMessage
|
|
||||||
for _, outbound := range outbounds {
|
|
||||||
outboundStrings = append(outboundStrings, outbound)
|
|
||||||
}
|
|
||||||
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 +145,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 +170,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 +206,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 +217,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 +234,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 +249,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 +273,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 +292,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 +305,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 +331,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 +348,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"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package common
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -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,7 +659,6 @@ class Outbound extends CommonClass {
|
|||||||
json.allowInsecure);
|
json.allowInsecure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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, json.port, json.id), stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,7 +688,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'){
|
||||||
@@ -586,7 +716,7 @@ class Outbound extends CommonClass {
|
|||||||
let data = link.split('?');
|
let data = link.split('?');
|
||||||
if(data.length != 2) return null;
|
if(data.length != 2) return null;
|
||||||
|
|
||||||
const regex = /([^@]+):\/\/([^@]+)@([^:]+):(\d+)\?(.*)$/;
|
const regex = /([^@]+):\/\/([^@]+)@(.+):(\d+)\?(.*)$/;
|
||||||
const match = link.match(regex);
|
const match = link.match(regex);
|
||||||
|
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
@@ -955,7 +1085,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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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') {
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
13
web/html/xui/form/stream/stream_httpupgrade.html
Normal file
13
web/html/xui/form/stream/stream_httpupgrade.html
Normal 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}}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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"}}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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) : '-';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -31,5 +31,4 @@ func (j *XrayTrafficJob) Run() {
|
|||||||
if needRestart {
|
if needRestart {
|
||||||
j.xrayService.SetToNeedRestart()
|
j.xrayService.SetToNeedRestart()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -238,7 +254,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 +297,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())
|
||||||
@@ -533,15 +553,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 +574,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 +950,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 +1030,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 +1041,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 +1127,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 +1138,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 +1337,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{})
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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" = "دامنهها"
|
||||||
|
|||||||
@@ -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" = "Использование сервера"
|
||||||
|
|||||||
@@ -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" = "Tổng 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ủ"
|
||||||
|
|||||||
@@ -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" = "将OpenAI(ChatGPT)路由添加到WARP"
|
"OpenAIWARPDesc" = "将 ChatGPT 路由添加到WARP"
|
||||||
"NetflixWARP" = "将 Netflix 路由到 WARP"
|
"NetflixWARP" = "Netflix"
|
||||||
"NetflixWARPDesc" = "为Netflix添加路由到WARP"
|
"NetflixWARPDesc" = "为Netflix添加路由到WARP"
|
||||||
"MetaWARP"="元"
|
"MetaWARP"="Meta"
|
||||||
"MetaWARPDesc" = "通过 WARP 将流量路由到 Meta(Instagram、Facebook、WhatsApp、Threads...)"
|
"MetaWARPDesc" = "通过 WARP 将流量路由到 Meta(Instagram, 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" = "添加服务器"
|
||||||
|
|||||||
27
web/web.go
27
web/web.go
@@ -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
258
x-ui.sh
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package xray
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
"x-ui/util/json_util"
|
"x-ui/util/json_util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package xray
|
|||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user