mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-02-28 05:02:59 +00:00
Merge branch 'MHSanaei:main' into geofiles304update
This commit is contained in:
commit
0e986b97d2
37 changed files with 916 additions and 272 deletions
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
|
@ -89,7 +89,7 @@ jobs:
|
||||||
cd x-ui/bin
|
cd x-ui/bin
|
||||||
|
|
||||||
# Download dependencies
|
# Download dependencies
|
||||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v26.1.18/"
|
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v26.1.31/"
|
||||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||||
wget -q ${Xray_URL}Xray-linux-64.zip
|
wget -q ${Xray_URL}Xray-linux-64.zip
|
||||||
unzip Xray-linux-64.zip
|
unzip Xray-linux-64.zip
|
||||||
|
|
@ -187,7 +187,7 @@ jobs:
|
||||||
cd x-ui\bin
|
cd x-ui\bin
|
||||||
|
|
||||||
# Download Xray for Windows
|
# Download Xray for Windows
|
||||||
$Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v26.1.18/"
|
$Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v26.1.31/"
|
||||||
Invoke-WebRequest -Uri "${Xray_URL}Xray-windows-64.zip" -OutFile "Xray-windows-64.zip"
|
Invoke-WebRequest -Uri "${Xray_URL}Xray-windows-64.zip" -OutFile "Xray-windows-64.zip"
|
||||||
Expand-Archive -Path "Xray-windows-64.zip" -DestinationPath .
|
Expand-Archive -Path "Xray-windows-64.zip" -DestinationPath .
|
||||||
Remove-Item "Xray-windows-64.zip"
|
Remove-Item "Xray-windows-64.zip"
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ case $1 in
|
||||||
esac
|
esac
|
||||||
mkdir -p build/bin
|
mkdir -p build/bin
|
||||||
cd build/bin
|
cd build/bin
|
||||||
curl -sfLRO "https://github.com/XTLS/Xray-core/releases/download/v26.1.18/Xray-linux-${ARCH}.zip"
|
curl -sfLRO "https://github.com/XTLS/Xray-core/releases/download/v26.1.31/Xray-linux-${ARCH}.zip"
|
||||||
unzip "Xray-linux-${ARCH}.zip"
|
unzip "Xray-linux-${ARCH}.zip"
|
||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||||
mv xray "xray-linux-${FNAME}"
|
mv xray "xray-linux-${FNAME}"
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
2.8.8
|
2.8.9
|
||||||
24
go.mod
24
go.mod
|
|
@ -16,11 +16,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.2.4
|
github.com/pelletier/go-toml/v2 v2.2.4
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v4 v4.25.12
|
github.com/shirou/gopsutil/v4 v4.26.1
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||||
github.com/valyala/fasthttp v1.69.0
|
github.com/valyala/fasthttp v1.69.0
|
||||||
github.com/xlzd/gotp v0.1.0
|
github.com/xlzd/gotp v0.1.0
|
||||||
github.com/xtls/xray-core v1.260118.0
|
github.com/xtls/xray-core v1.260131.0
|
||||||
go.uber.org/atomic v1.11.0
|
go.uber.org/atomic v1.11.0
|
||||||
golang.org/x/crypto v0.47.0
|
golang.org/x/crypto v0.47.0
|
||||||
golang.org/x/sys v0.40.0
|
golang.org/x/sys v0.40.0
|
||||||
|
|
@ -35,11 +35,10 @@ require (
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 // indirect
|
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 // indirect
|
||||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||||
github.com/bytedance/sonic v1.14.2 // indirect
|
github.com/bytedance/sonic v1.15.0 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.4.0 // indirect
|
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||||
github.com/cloudflare/circl v1.6.2 // indirect
|
github.com/cloudflare/circl v1.6.3 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect
|
|
||||||
github.com/ebitengine/purego v0.9.1 // indirect
|
github.com/ebitengine/purego v0.9.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
|
|
@ -64,24 +63,21 @@ require (
|
||||||
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect
|
github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // 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.33 // indirect
|
github.com/mattn/go-sqlite3 v1.14.33 // indirect
|
||||||
github.com/miekg/dns v1.1.70 // indirect
|
github.com/miekg/dns v1.1.72 // 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/pires/go-proxyproto v0.8.1 // indirect
|
github.com/pires/go-proxyproto v0.9.2 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/quic-go/qpack v0.6.0 // indirect
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||||
github.com/refraction-networking/utls v1.8.2 // indirect
|
github.com/refraction-networking/utls v1.8.2 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
|
||||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
github.com/sagernet/sing v0.7.14 // indirect
|
github.com/sagernet/sing v0.7.18 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.9 // indirect
|
github.com/sagernet/sing-shadowsocks v0.2.9 // indirect
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
|
|
||||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fastjson v1.6.7 // indirect
|
github.com/valyala/fastjson v1.6.7 // indirect
|
||||||
github.com/vishvananda/netlink v1.3.1 // indirect
|
github.com/vishvananda/netlink v1.3.1 // indirect
|
||||||
|
|
@ -98,8 +94,8 @@ require (
|
||||||
golang.org/x/tools v0.41.0 // indirect
|
golang.org/x/tools v0.41.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-20250521234502-f333402bd9cb // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20260109181451-4be7c433dae2 // indirect
|
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 // indirect
|
||||||
lukechampine.com/blake3 v1.4.1 // indirect
|
lukechampine.com/blake3 v1.4.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
51
go.sum
51
go.sum
|
|
@ -10,20 +10,17 @@ github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 h1:bSq8n+gX4oO/
|
||||||
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178/go.mod h1:N1WIjPphkqs4efXWuyDNQ6OjjIK04vM3h+bEgwV+eVU=
|
github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178/go.mod h1:N1WIjPphkqs4efXWuyDNQ6OjjIK04vM3h+bEgwV+eVU=
|
||||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||||
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
|
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||||
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
|
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||||
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
|
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||||
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||||
github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ=
|
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||||
github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
|
||||||
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 h1:ucRHb6/lvW/+mTEIGbvhcYU3S8+uSNkuMjx/qZFfhtM=
|
|
||||||
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
|
||||||
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
github.com/ebitengine/purego v0.9.1 h1:a/k2f2HQU3Pi399RPW1MOaZyhKJL9w/xFpKAg4q1s0A=
|
||||||
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.9.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||||
|
|
@ -124,8 +121,8 @@ 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.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0=
|
github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0=
|
||||||
github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/miekg/dns v1.1.70 h1:DZ4u2AV35VJxdD9Fo9fIWm119BsQL5cZU1cQ9s0LkqA=
|
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||||
github.com/miekg/dns v1.1.70/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||||
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=
|
||||||
|
|
@ -141,8 +138,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
github.com/pires/go-proxyproto v0.9.2 h1:H1UdHn695zUVVmB0lQ354lOWHOy6TZSpzBl3tgN0s1U=
|
||||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
github.com/pires/go-proxyproto v0.9.2/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||||
|
|
@ -153,20 +150,16 @@ github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SA
|
||||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||||
github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo=
|
github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo=
|
||||||
github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/sagernet/sing v0.7.14 h1:5QQRDCUvYNOMyVp3LuK/hYEBAIv0VsbD3x/l9zH467s=
|
github.com/sagernet/sing v0.7.18 h1:iZHkaru1/MoHugx3G+9S3WG4owMewKO/KvieE2Pzk4E=
|
||||||
github.com/sagernet/sing v0.7.14/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.7.18/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.9 h1:Paep5zCszRKsEn8587O0MnhFWKJwDW1Y4zOYYlIxMkM=
|
github.com/sagernet/sing-shadowsocks v0.2.9 h1:Paep5zCszRKsEn8587O0MnhFWKJwDW1Y4zOYYlIxMkM=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.9/go.mod h1:TE/Z6401Pi8tgr0nBZcM/xawAI6u3F6TTbz4nH/qw+8=
|
github.com/sagernet/sing-shadowsocks v0.2.9/go.mod h1:TE/Z6401Pi8tgr0nBZcM/xawAI6u3F6TTbz4nH/qw+8=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY=
|
|
||||||
github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU=
|
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
|
@ -174,7 +167,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
|
@ -189,8 +181,6 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF8gHIiADmOVOV5LS43gt3ONnlEl3xkwI=
|
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
|
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
|
||||||
|
|
@ -205,8 +195,8 @@ github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
|
||||||
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
|
github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
|
||||||
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237 h1:UXjrmniKlY+ZbIqpN91lejB3pszQQQRVu1vqH/p/aGM=
|
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237 h1:UXjrmniKlY+ZbIqpN91lejB3pszQQQRVu1vqH/p/aGM=
|
||||||
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ=
|
github.com/xtls/reality v0.0.0-20251116175510-cd53f7d50237/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ=
|
||||||
github.com/xtls/xray-core v1.260118.0 h1:RJtgIbQ3ykFRcH1CKeoCgQ5WvhsMFu+lnvLF/fFHagE=
|
github.com/xtls/xray-core v1.260131.0 h1:gPBykLhUvRZ8sfubNerkwWqV3c15UtmSYQG2cgKqrV4=
|
||||||
github.com/xtls/xray-core v1.260118.0/go.mod h1:A5k7TXE2KfAjT8dAq6Ir4mMP1q0OTh+8VMmUdqWMQpg=
|
github.com/xtls/xray-core v1.260131.0/go.mod h1:cxzYFZrxu1B1NtPjHsqv4UzgDvRA71mV4rXYH4KtO7Q=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
|
@ -263,8 +253,8 @@ golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+Z
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
|
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
|
||||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 h1:C4WAdL+FbjnGlpp2S+HMVhBeCq2Lcib4xZqfPNF6OoQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||||
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||||
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
|
|
@ -275,14 +265,13 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||||
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
|
||||||
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
|
||||||
gvisor.dev/gvisor v0.0.0-20260109181451-4be7c433dae2 h1:fr6L00yGG2RP5NMea6njWpdC+bm+cMdFClrSpaicp1c=
|
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 h1:Lk6hARj5UPY47dBep70OD/TIMwikJ5fGUGX0Rm3Xigk=
|
||||||
gvisor.dev/gvisor v0.0.0-20260109181451-4be7c433dae2/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q=
|
gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q=
|
||||||
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
|
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
|
||||||
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
|
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ setup_ssl_certificate() {
|
||||||
echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
|
echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
|
||||||
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
|
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
|
||||||
|
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force >/dev/null 2>&1
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
|
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
|
@ -272,7 +272,7 @@ setup_ip_certificate() {
|
||||||
|
|
||||||
# Issue certificate with shortlived profile
|
# Issue certificate with shortlived profile
|
||||||
echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
|
echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force >/dev/null 2>&1
|
||||||
|
|
||||||
~/.acme.sh/acme.sh --issue \
|
~/.acme.sh/acme.sh --issue \
|
||||||
${domain_args} \
|
${domain_args} \
|
||||||
|
|
@ -414,7 +414,7 @@ ssl_cert_issue() {
|
||||||
systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null
|
systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null
|
||||||
|
|
||||||
# issue the certificate
|
# issue the certificate
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo -e "${red}Issuing certificate failed, please check logs.${plain}"
|
echo -e "${red}Issuing certificate failed, please check logs.${plain}"
|
||||||
|
|
|
||||||
28
sub/sub.go
28
sub/sub.go
|
|
@ -153,6 +153,31 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||||
SubTitle = ""
|
SubTitle = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SubSupportUrl, err := s.settingService.GetSubSupportUrl()
|
||||||
|
if err != nil {
|
||||||
|
SubSupportUrl = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
SubProfileUrl, err := s.settingService.GetSubProfileUrl()
|
||||||
|
if err != nil {
|
||||||
|
SubProfileUrl = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
SubAnnounce, err := s.settingService.GetSubAnnounce()
|
||||||
|
if err != nil {
|
||||||
|
SubAnnounce = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
SubEnableRouting, err := s.settingService.GetSubEnableRouting()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
SubRoutingRules, err := s.settingService.GetSubRoutingRules()
|
||||||
|
if err != nil {
|
||||||
|
SubRoutingRules = ""
|
||||||
|
}
|
||||||
|
|
||||||
// set per-request localizer from headers/cookies
|
// set per-request localizer from headers/cookies
|
||||||
engine.Use(locale.LocalizerMiddleware())
|
engine.Use(locale.LocalizerMiddleware())
|
||||||
|
|
||||||
|
|
@ -231,7 +256,8 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||||
|
|
||||||
s.sub = NewSUBController(
|
s.sub = NewSUBController(
|
||||||
g, LinksPath, JsonPath, subJsonEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates,
|
g, LinksPath, JsonPath, subJsonEnable, Encrypt, ShowInfo, RemarkModel, SubUpdates,
|
||||||
SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubTitle)
|
SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules, SubTitle, SubSupportUrl,
|
||||||
|
SubProfileUrl, SubAnnounce, SubEnableRouting, SubRoutingRules)
|
||||||
|
|
||||||
return engine, nil
|
return engine, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/mhsanaei/3x-ui/v2/config"
|
"github.com/mhsanaei/3x-ui/v2/config"
|
||||||
|
|
||||||
|
|
@ -12,12 +13,17 @@ import (
|
||||||
|
|
||||||
// SUBController handles HTTP requests for subscription links and JSON configurations.
|
// SUBController handles HTTP requests for subscription links and JSON configurations.
|
||||||
type SUBController struct {
|
type SUBController struct {
|
||||||
subTitle string
|
subTitle string
|
||||||
subPath string
|
subSupportUrl string
|
||||||
subJsonPath string
|
subProfileUrl string
|
||||||
jsonEnabled bool
|
subAnnounce string
|
||||||
subEncrypt bool
|
subEnableRouting bool
|
||||||
updateInterval string
|
subRoutingRules string
|
||||||
|
subPath string
|
||||||
|
subJsonPath string
|
||||||
|
jsonEnabled bool
|
||||||
|
subEncrypt bool
|
||||||
|
updateInterval string
|
||||||
|
|
||||||
subService *SubService
|
subService *SubService
|
||||||
subJsonService *SubJsonService
|
subJsonService *SubJsonService
|
||||||
|
|
@ -38,18 +44,28 @@ func NewSUBController(
|
||||||
jsonMux string,
|
jsonMux string,
|
||||||
jsonRules string,
|
jsonRules string,
|
||||||
subTitle string,
|
subTitle string,
|
||||||
|
subSupportUrl string,
|
||||||
|
subProfileUrl string,
|
||||||
|
subAnnounce string,
|
||||||
|
subEnableRouting bool,
|
||||||
|
subRoutingRules string,
|
||||||
) *SUBController {
|
) *SUBController {
|
||||||
sub := NewSubService(showInfo, rModel)
|
sub := NewSubService(showInfo, rModel)
|
||||||
a := &SUBController{
|
a := &SUBController{
|
||||||
subTitle: subTitle,
|
subTitle: subTitle,
|
||||||
subPath: subPath,
|
subSupportUrl: subSupportUrl,
|
||||||
subJsonPath: jsonPath,
|
subProfileUrl: subProfileUrl,
|
||||||
jsonEnabled: jsonEnabled,
|
subAnnounce: subAnnounce,
|
||||||
subEncrypt: encrypt,
|
subEnableRouting: subEnableRouting,
|
||||||
updateInterval: update,
|
subRoutingRules: subRoutingRules,
|
||||||
|
subPath: subPath,
|
||||||
|
subJsonPath: jsonPath,
|
||||||
|
jsonEnabled: jsonEnabled,
|
||||||
|
subEncrypt: encrypt,
|
||||||
|
updateInterval: update,
|
||||||
|
|
||||||
subService: sub,
|
subService: sub,
|
||||||
subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub),
|
subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub),
|
||||||
}
|
}
|
||||||
a.initRouter(g)
|
a.initRouter(g)
|
||||||
return a
|
return a
|
||||||
|
|
@ -127,7 +143,7 @@ func (a *SUBController) subs(c *gin.Context) {
|
||||||
|
|
||||||
// Add headers
|
// Add headers
|
||||||
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)
|
||||||
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle)
|
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, a.subProfileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules)
|
||||||
|
|
||||||
if a.subEncrypt {
|
if a.subEncrypt {
|
||||||
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
|
c.String(200, base64.StdEncoding.EncodeToString([]byte(result)))
|
||||||
|
|
@ -145,17 +161,31 @@ func (a *SUBController) subJsons(c *gin.Context) {
|
||||||
if err != nil || len(jsonSub) == 0 {
|
if err != nil || len(jsonSub) == 0 {
|
||||||
c.String(400, "Error!")
|
c.String(400, "Error!")
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Add headers
|
// Add headers
|
||||||
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle)
|
a.ApplyCommonHeaders(c, header, a.updateInterval, a.subTitle, a.subSupportUrl, a.subProfileUrl, a.subAnnounce, a.subEnableRouting, a.subRoutingRules)
|
||||||
|
|
||||||
c.String(200, jsonSub)
|
c.String(200, jsonSub)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyCommonHeaders sets common HTTP headers for subscription responses including user info, update interval, and profile title.
|
// ApplyCommonHeaders sets common HTTP headers for subscription responses including user info, update interval, and profile title.
|
||||||
func (a *SUBController) ApplyCommonHeaders(c *gin.Context, header, updateInterval, profileTitle string) {
|
func (a *SUBController) ApplyCommonHeaders(
|
||||||
|
c *gin.Context,
|
||||||
|
header,
|
||||||
|
updateInterval,
|
||||||
|
profileTitle string,
|
||||||
|
profileSupportUrl string,
|
||||||
|
profileUrl string,
|
||||||
|
profileAnnounce string,
|
||||||
|
profileEnableRouting bool,
|
||||||
|
profileRoutingRules string,
|
||||||
|
) {
|
||||||
c.Writer.Header().Set("Subscription-Userinfo", header)
|
c.Writer.Header().Set("Subscription-Userinfo", header)
|
||||||
c.Writer.Header().Set("Profile-Update-Interval", updateInterval)
|
c.Writer.Header().Set("Profile-Update-Interval", updateInterval)
|
||||||
c.Writer.Header().Set("Profile-Title", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileTitle)))
|
c.Writer.Header().Set("Profile-Title", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileTitle)))
|
||||||
|
c.Writer.Header().Set("Support-Url", profileSupportUrl)
|
||||||
|
c.Writer.Header().Set("Profile-Web-Page-Url", profileUrl)
|
||||||
|
c.Writer.Header().Set("Announce", "base64:"+base64.StdEncoding.EncodeToString([]byte(profileAnnounce)))
|
||||||
|
c.Writer.Header().Set("Routing-Enable", strconv.FormatBool(profileEnableRouting))
|
||||||
|
c.Writer.Header().Set("Routing", profileRoutingRules)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ setup_ssl_certificate() {
|
||||||
echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
|
echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
|
||||||
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
|
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
|
||||||
|
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force >/dev/null 2>&1
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
|
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport 80 --force
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
|
@ -297,7 +297,7 @@ setup_ip_certificate() {
|
||||||
|
|
||||||
# Issue certificate with shortlived profile
|
# Issue certificate with shortlived profile
|
||||||
echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
|
echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt >/dev/null 2>&1
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force >/dev/null 2>&1
|
||||||
|
|
||||||
~/.acme.sh/acme.sh --issue \
|
~/.acme.sh/acme.sh --issue \
|
||||||
${domain_args} \
|
${domain_args} \
|
||||||
|
|
@ -437,7 +437,7 @@ ssl_cert_issue() {
|
||||||
systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null
|
systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null
|
||||||
|
|
||||||
# issue the certificate
|
# issue the certificate
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo -e "${red}Issuing certificate failed, please check logs.${plain}"
|
echo -e "${red}Issuing certificate failed, please check logs.${plain}"
|
||||||
|
|
|
||||||
|
|
@ -318,15 +318,13 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass {
|
||||||
|
|
||||||
class KcpStreamSettings extends XrayCommonClass {
|
class KcpStreamSettings extends XrayCommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
mtu = 1250,
|
mtu = 1350,
|
||||||
tti = 50,
|
tti = 20,
|
||||||
uplinkCapacity = 5,
|
uplinkCapacity = 5,
|
||||||
downlinkCapacity = 20,
|
downlinkCapacity = 20,
|
||||||
congestion = false,
|
congestion = false,
|
||||||
readBufferSize = 2,
|
readBufferSize = 1,
|
||||||
writeBufferSize = 2,
|
writeBufferSize = 1,
|
||||||
type = 'none',
|
|
||||||
seed = RandomUtil.randomSeq(10),
|
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.mtu = mtu;
|
this.mtu = mtu;
|
||||||
|
|
@ -336,8 +334,6 @@ class KcpStreamSettings extends XrayCommonClass {
|
||||||
this.congestion = congestion;
|
this.congestion = congestion;
|
||||||
this.readBuffer = readBufferSize;
|
this.readBuffer = readBufferSize;
|
||||||
this.writeBuffer = writeBufferSize;
|
this.writeBuffer = writeBufferSize;
|
||||||
this.type = type;
|
|
||||||
this.seed = seed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
|
|
@ -349,8 +345,6 @@ class KcpStreamSettings extends XrayCommonClass {
|
||||||
json.congestion,
|
json.congestion,
|
||||||
json.readBufferSize,
|
json.readBufferSize,
|
||||||
json.writeBufferSize,
|
json.writeBufferSize,
|
||||||
ObjectUtil.isEmpty(json.header) ? 'none' : json.header.type,
|
|
||||||
json.seed,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,10 +357,6 @@ class KcpStreamSettings extends XrayCommonClass {
|
||||||
congestion: this.congestion,
|
congestion: this.congestion,
|
||||||
readBufferSize: this.readBuffer,
|
readBufferSize: this.readBuffer,
|
||||||
writeBufferSize: this.writeBuffer,
|
writeBufferSize: this.writeBuffer,
|
||||||
header: {
|
|
||||||
type: this.type,
|
|
||||||
},
|
|
||||||
seed: this.seed,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -497,6 +487,19 @@ class xHTTPStreamSettings extends XrayCommonClass {
|
||||||
noSSEHeader = false,
|
noSSEHeader = false,
|
||||||
xPaddingBytes = "100-1000",
|
xPaddingBytes = "100-1000",
|
||||||
mode = MODE_OPTION.AUTO,
|
mode = MODE_OPTION.AUTO,
|
||||||
|
xPaddingObfsMode = false,
|
||||||
|
xPaddingKey = '',
|
||||||
|
xPaddingHeader = '',
|
||||||
|
xPaddingPlacement = '',
|
||||||
|
xPaddingMethod = '',
|
||||||
|
uplinkHTTPMethod = '',
|
||||||
|
sessionPlacement = '',
|
||||||
|
sessionKey = '',
|
||||||
|
seqPlacement = '',
|
||||||
|
seqKey = '',
|
||||||
|
uplinkDataPlacement = '',
|
||||||
|
uplinkDataKey = '',
|
||||||
|
uplinkChunkSize = 0,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.path = path;
|
this.path = path;
|
||||||
|
|
@ -508,6 +511,19 @@ class xHTTPStreamSettings extends XrayCommonClass {
|
||||||
this.noSSEHeader = noSSEHeader;
|
this.noSSEHeader = noSSEHeader;
|
||||||
this.xPaddingBytes = xPaddingBytes;
|
this.xPaddingBytes = xPaddingBytes;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
|
this.xPaddingObfsMode = xPaddingObfsMode;
|
||||||
|
this.xPaddingKey = xPaddingKey;
|
||||||
|
this.xPaddingHeader = xPaddingHeader;
|
||||||
|
this.xPaddingPlacement = xPaddingPlacement;
|
||||||
|
this.xPaddingMethod = xPaddingMethod;
|
||||||
|
this.uplinkHTTPMethod = uplinkHTTPMethod;
|
||||||
|
this.sessionPlacement = sessionPlacement;
|
||||||
|
this.sessionKey = sessionKey;
|
||||||
|
this.seqPlacement = seqPlacement;
|
||||||
|
this.seqKey = seqKey;
|
||||||
|
this.uplinkDataPlacement = uplinkDataPlacement;
|
||||||
|
this.uplinkDataKey = uplinkDataKey;
|
||||||
|
this.uplinkChunkSize = uplinkChunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
addHeader(name, value) {
|
addHeader(name, value) {
|
||||||
|
|
@ -529,6 +545,19 @@ class xHTTPStreamSettings extends XrayCommonClass {
|
||||||
json.noSSEHeader,
|
json.noSSEHeader,
|
||||||
json.xPaddingBytes,
|
json.xPaddingBytes,
|
||||||
json.mode,
|
json.mode,
|
||||||
|
json.xPaddingObfsMode,
|
||||||
|
json.xPaddingKey,
|
||||||
|
json.xPaddingHeader,
|
||||||
|
json.xPaddingPlacement,
|
||||||
|
json.xPaddingMethod,
|
||||||
|
json.uplinkHTTPMethod,
|
||||||
|
json.sessionPlacement,
|
||||||
|
json.sessionKey,
|
||||||
|
json.seqPlacement,
|
||||||
|
json.seqKey,
|
||||||
|
json.uplinkDataPlacement,
|
||||||
|
json.uplinkDataKey,
|
||||||
|
json.uplinkChunkSize,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -543,6 +572,19 @@ class xHTTPStreamSettings extends XrayCommonClass {
|
||||||
noSSEHeader: this.noSSEHeader,
|
noSSEHeader: this.noSSEHeader,
|
||||||
xPaddingBytes: this.xPaddingBytes,
|
xPaddingBytes: this.xPaddingBytes,
|
||||||
mode: this.mode,
|
mode: this.mode,
|
||||||
|
xPaddingObfsMode: this.xPaddingObfsMode,
|
||||||
|
xPaddingKey: this.xPaddingKey,
|
||||||
|
xPaddingHeader: this.xPaddingHeader,
|
||||||
|
xPaddingPlacement: this.xPaddingPlacement,
|
||||||
|
xPaddingMethod: this.xPaddingMethod,
|
||||||
|
uplinkHTTPMethod: this.uplinkHTTPMethod,
|
||||||
|
sessionPlacement: this.sessionPlacement,
|
||||||
|
sessionKey: this.sessionKey,
|
||||||
|
seqPlacement: this.seqPlacement,
|
||||||
|
seqKey: this.seqKey,
|
||||||
|
uplinkDataPlacement: this.uplinkDataPlacement,
|
||||||
|
uplinkDataKey: this.uplinkDataKey,
|
||||||
|
uplinkChunkSize: this.uplinkChunkSize,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -554,7 +596,6 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||||
maxVersion = TLS_VERSION_OPTION.TLS13,
|
maxVersion = TLS_VERSION_OPTION.TLS13,
|
||||||
cipherSuites = '',
|
cipherSuites = '',
|
||||||
rejectUnknownSni = false,
|
rejectUnknownSni = false,
|
||||||
verifyPeerCertInNames = ['dns.google', 'cloudflare-dns.com'],
|
|
||||||
disableSystemRoot = false,
|
disableSystemRoot = false,
|
||||||
enableSessionResumption = false,
|
enableSessionResumption = false,
|
||||||
certificates = [new TlsStreamSettings.Cert()],
|
certificates = [new TlsStreamSettings.Cert()],
|
||||||
|
|
@ -569,7 +610,6 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||||
this.maxVersion = maxVersion;
|
this.maxVersion = maxVersion;
|
||||||
this.cipherSuites = cipherSuites;
|
this.cipherSuites = cipherSuites;
|
||||||
this.rejectUnknownSni = rejectUnknownSni;
|
this.rejectUnknownSni = rejectUnknownSni;
|
||||||
this.verifyPeerCertInNames = Array.isArray(verifyPeerCertInNames) ? verifyPeerCertInNames.join(",") : verifyPeerCertInNames;
|
|
||||||
this.disableSystemRoot = disableSystemRoot;
|
this.disableSystemRoot = disableSystemRoot;
|
||||||
this.enableSessionResumption = enableSessionResumption;
|
this.enableSessionResumption = enableSessionResumption;
|
||||||
this.certs = certificates;
|
this.certs = certificates;
|
||||||
|
|
@ -603,7 +643,6 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||||
json.maxVersion,
|
json.maxVersion,
|
||||||
json.cipherSuites,
|
json.cipherSuites,
|
||||||
json.rejectUnknownSni,
|
json.rejectUnknownSni,
|
||||||
json.verifyPeerCertInNames,
|
|
||||||
json.disableSystemRoot,
|
json.disableSystemRoot,
|
||||||
json.enableSessionResumption,
|
json.enableSessionResumption,
|
||||||
certs,
|
certs,
|
||||||
|
|
@ -621,7 +660,6 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||||
maxVersion: this.maxVersion,
|
maxVersion: this.maxVersion,
|
||||||
cipherSuites: this.cipherSuites,
|
cipherSuites: this.cipherSuites,
|
||||||
rejectUnknownSni: this.rejectUnknownSni,
|
rejectUnknownSni: this.rejectUnknownSni,
|
||||||
verifyPeerCertInNames: this.verifyPeerCertInNames.split(","),
|
|
||||||
disableSystemRoot: this.disableSystemRoot,
|
disableSystemRoot: this.disableSystemRoot,
|
||||||
enableSessionResumption: this.enableSessionResumption,
|
enableSessionResumption: this.enableSessionResumption,
|
||||||
certificates: TlsStreamSettings.toJsonArray(this.certs),
|
certificates: TlsStreamSettings.toJsonArray(this.certs),
|
||||||
|
|
@ -929,6 +967,51 @@ class SockoptStreamSettings extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FinalMask extends XrayCommonClass {
|
||||||
|
constructor(type = 'salamander', settings = {}) {
|
||||||
|
super();
|
||||||
|
this.type = type;
|
||||||
|
this.settings = this._getDefaultSettings(type, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getDefaultSettings(type, settings = {}) {
|
||||||
|
switch (type) {
|
||||||
|
case 'salamander':
|
||||||
|
case 'mkcp-aes128gcm':
|
||||||
|
return { password: settings.password || '' };
|
||||||
|
case 'header-dns':
|
||||||
|
case 'xdns':
|
||||||
|
return { domain: settings.domain || '' };
|
||||||
|
case 'mkcp-original':
|
||||||
|
case 'header-dtls':
|
||||||
|
case 'header-srtp':
|
||||||
|
case 'header-utp':
|
||||||
|
case 'header-wechat':
|
||||||
|
case 'header-wireguard':
|
||||||
|
return {};
|
||||||
|
default:
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json = {}) {
|
||||||
|
return new FinalMask(
|
||||||
|
json.type || 'salamander',
|
||||||
|
json.settings || {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson() {
|
||||||
|
const result = {
|
||||||
|
type: this.type
|
||||||
|
};
|
||||||
|
if (this.settings && Object.keys(this.settings).length > 0) {
|
||||||
|
result.settings = this.settings;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class StreamSettings extends XrayCommonClass {
|
class StreamSettings extends XrayCommonClass {
|
||||||
constructor(network = 'tcp',
|
constructor(network = 'tcp',
|
||||||
security = 'none',
|
security = 'none',
|
||||||
|
|
@ -941,6 +1024,7 @@ class StreamSettings extends XrayCommonClass {
|
||||||
grpcSettings = new GrpcStreamSettings(),
|
grpcSettings = new GrpcStreamSettings(),
|
||||||
httpupgradeSettings = new HTTPUpgradeStreamSettings(),
|
httpupgradeSettings = new HTTPUpgradeStreamSettings(),
|
||||||
xhttpSettings = new xHTTPStreamSettings(),
|
xhttpSettings = new xHTTPStreamSettings(),
|
||||||
|
finalmask = { udp: [] },
|
||||||
sockopt = undefined,
|
sockopt = undefined,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -955,9 +1039,23 @@ class StreamSettings extends XrayCommonClass {
|
||||||
this.grpc = grpcSettings;
|
this.grpc = grpcSettings;
|
||||||
this.httpupgrade = httpupgradeSettings;
|
this.httpupgrade = httpupgradeSettings;
|
||||||
this.xhttp = xhttpSettings;
|
this.xhttp = xhttpSettings;
|
||||||
|
this.finalmask = finalmask;
|
||||||
this.sockopt = sockopt;
|
this.sockopt = sockopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addUdpMask(type = 'salamander') {
|
||||||
|
if (!this.finalmask.udp) {
|
||||||
|
this.finalmask.udp = [];
|
||||||
|
}
|
||||||
|
this.finalmask.udp.push(new FinalMask(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
delUdpMask(index) {
|
||||||
|
if (this.finalmask.udp) {
|
||||||
|
this.finalmask.udp.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get isTls() {
|
get isTls() {
|
||||||
return this.security === "tls";
|
return this.security === "tls";
|
||||||
}
|
}
|
||||||
|
|
@ -992,6 +1090,14 @@ class StreamSettings extends XrayCommonClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
|
let finalmask = { udp: [] };
|
||||||
|
if (json.finalmask) {
|
||||||
|
if (Array.isArray(json.finalmask)) {
|
||||||
|
finalmask.udp = json.finalmask.map(mask => FinalMask.fromJson(mask));
|
||||||
|
} else if (json.finalmask.udp) {
|
||||||
|
finalmask.udp = json.finalmask.udp.map(mask => FinalMask.fromJson(mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
return new StreamSettings(
|
return new StreamSettings(
|
||||||
json.network,
|
json.network,
|
||||||
json.security,
|
json.security,
|
||||||
|
|
@ -1004,6 +1110,7 @@ class StreamSettings extends XrayCommonClass {
|
||||||
GrpcStreamSettings.fromJson(json.grpcSettings),
|
GrpcStreamSettings.fromJson(json.grpcSettings),
|
||||||
HTTPUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
HTTPUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
||||||
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
||||||
|
finalmask,
|
||||||
SockoptStreamSettings.fromJson(json.sockopt),
|
SockoptStreamSettings.fromJson(json.sockopt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1022,6 +1129,9 @@ class StreamSettings extends XrayCommonClass {
|
||||||
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
|
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
|
||||||
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
||||||
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
||||||
|
finalmask: (this.finalmask.udp && this.finalmask.udp.length > 0) ? {
|
||||||
|
udp: this.finalmask.udp.map(mask => mask.toJson())
|
||||||
|
} : undefined,
|
||||||
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1947,7 +2057,9 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
|
||||||
json.selectedAuth = this.selectedAuth;
|
json.selectedAuth = this.selectedAuth;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.testseed && this.testseed.length >= 4) {
|
// Only include testseed if at least one client has a flow set
|
||||||
|
const hasFlow = this.vlesses && this.vlesses.some(vless => vless.flow && vless.flow !== '');
|
||||||
|
if (hasFlow && this.testseed && this.testseed.length >= 4) {
|
||||||
json.testseed = this.testseed;
|
json.testseed = this.testseed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2509,7 +2621,7 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass {
|
||||||
Inbound.WireguardSettings = class extends XrayCommonClass {
|
Inbound.WireguardSettings = class extends XrayCommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
protocol,
|
protocol,
|
||||||
mtu = 1250,
|
mtu = 1420,
|
||||||
secretKey = Wireguard.generateKeypair().privateKey,
|
secretKey = Wireguard.generateKeypair().privateKey,
|
||||||
peers = [new Inbound.WireguardSettings.Peer()],
|
peers = [new Inbound.WireguardSettings.Peer()],
|
||||||
noKernelTun = false
|
noKernelTun = false
|
||||||
|
|
|
||||||
|
|
@ -165,15 +165,13 @@ class TcpStreamSettings extends CommonClass {
|
||||||
|
|
||||||
class KcpStreamSettings extends CommonClass {
|
class KcpStreamSettings extends CommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
mtu = 1250,
|
mtu = 1350,
|
||||||
tti = 50,
|
tti = 20,
|
||||||
uplinkCapacity = 5,
|
uplinkCapacity = 5,
|
||||||
downlinkCapacity = 20,
|
downlinkCapacity = 20,
|
||||||
congestion = false,
|
congestion = false,
|
||||||
readBufferSize = 2,
|
readBufferSize = 1,
|
||||||
writeBufferSize = 2,
|
writeBufferSize = 1,
|
||||||
type = 'none',
|
|
||||||
seed = '',
|
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.mtu = mtu;
|
this.mtu = mtu;
|
||||||
|
|
@ -183,8 +181,6 @@ class KcpStreamSettings extends CommonClass {
|
||||||
this.congestion = congestion;
|
this.congestion = congestion;
|
||||||
this.readBuffer = readBufferSize;
|
this.readBuffer = readBufferSize;
|
||||||
this.writeBuffer = writeBufferSize;
|
this.writeBuffer = writeBufferSize;
|
||||||
this.type = type;
|
|
||||||
this.seed = seed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
|
|
@ -196,8 +192,6 @@ class KcpStreamSettings extends CommonClass {
|
||||||
json.congestion,
|
json.congestion,
|
||||||
json.readBufferSize,
|
json.readBufferSize,
|
||||||
json.writeBufferSize,
|
json.writeBufferSize,
|
||||||
ObjectUtil.isEmpty(json.header) ? 'none' : json.header.type,
|
|
||||||
json.seed,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,10 +204,6 @@ class KcpStreamSettings extends CommonClass {
|
||||||
congestion: this.congestion,
|
congestion: this.congestion,
|
||||||
readBufferSize: this.readBuffer,
|
readBufferSize: this.readBuffer,
|
||||||
writeBufferSize: this.writeBuffer,
|
writeBufferSize: this.writeBuffer,
|
||||||
header: {
|
|
||||||
type: this.type,
|
|
||||||
},
|
|
||||||
seed: this.seed,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -357,6 +347,8 @@ class TlsStreamSettings extends CommonClass {
|
||||||
fingerprint = '',
|
fingerprint = '',
|
||||||
allowInsecure = false,
|
allowInsecure = false,
|
||||||
echConfigList = '',
|
echConfigList = '',
|
||||||
|
verifyPeerCertByName = 'cloudflare-dns.com',
|
||||||
|
pinnedPeerCertSha256 = '',
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.serverName = serverName;
|
this.serverName = serverName;
|
||||||
|
|
@ -364,6 +356,8 @@ class TlsStreamSettings extends CommonClass {
|
||||||
this.fingerprint = fingerprint;
|
this.fingerprint = fingerprint;
|
||||||
this.allowInsecure = allowInsecure;
|
this.allowInsecure = allowInsecure;
|
||||||
this.echConfigList = echConfigList;
|
this.echConfigList = echConfigList;
|
||||||
|
this.verifyPeerCertByName = verifyPeerCertByName;
|
||||||
|
this.pinnedPeerCertSha256 = pinnedPeerCertSha256;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
|
|
@ -373,6 +367,8 @@ class TlsStreamSettings extends CommonClass {
|
||||||
json.fingerprint,
|
json.fingerprint,
|
||||||
json.allowInsecure,
|
json.allowInsecure,
|
||||||
json.echConfigList,
|
json.echConfigList,
|
||||||
|
json.verifyPeerCertByName,
|
||||||
|
json.pinnedPeerCertSha256,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -382,7 +378,9 @@ class TlsStreamSettings extends CommonClass {
|
||||||
alpn: this.alpn,
|
alpn: this.alpn,
|
||||||
fingerprint: this.fingerprint,
|
fingerprint: this.fingerprint,
|
||||||
allowInsecure: this.allowInsecure,
|
allowInsecure: this.allowInsecure,
|
||||||
echConfigList: this.echConfigList
|
echConfigList: this.echConfigList,
|
||||||
|
verifyPeerCertByName: this.verifyPeerCertByName,
|
||||||
|
pinnedPeerCertSha256: this.pinnedPeerCertSha256
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -434,7 +432,8 @@ class HysteriaStreamSettings extends CommonClass {
|
||||||
up = '0',
|
up = '0',
|
||||||
down = '0',
|
down = '0',
|
||||||
udphopPort = '',
|
udphopPort = '',
|
||||||
udphopInterval = 30,
|
udphopIntervalMin = 30,
|
||||||
|
udphopIntervalMax = 30,
|
||||||
initStreamReceiveWindow = 8388608,
|
initStreamReceiveWindow = 8388608,
|
||||||
maxStreamReceiveWindow = 8388608,
|
maxStreamReceiveWindow = 8388608,
|
||||||
initConnectionReceiveWindow = 20971520,
|
initConnectionReceiveWindow = 20971520,
|
||||||
|
|
@ -450,7 +449,8 @@ class HysteriaStreamSettings extends CommonClass {
|
||||||
this.up = up;
|
this.up = up;
|
||||||
this.down = down;
|
this.down = down;
|
||||||
this.udphopPort = udphopPort;
|
this.udphopPort = udphopPort;
|
||||||
this.udphopInterval = udphopInterval;
|
this.udphopIntervalMin = udphopIntervalMin;
|
||||||
|
this.udphopIntervalMax = udphopIntervalMax;
|
||||||
this.initStreamReceiveWindow = initStreamReceiveWindow;
|
this.initStreamReceiveWindow = initStreamReceiveWindow;
|
||||||
this.maxStreamReceiveWindow = maxStreamReceiveWindow;
|
this.maxStreamReceiveWindow = maxStreamReceiveWindow;
|
||||||
this.initConnectionReceiveWindow = initConnectionReceiveWindow;
|
this.initConnectionReceiveWindow = initConnectionReceiveWindow;
|
||||||
|
|
@ -462,10 +462,18 @@ class HysteriaStreamSettings extends CommonClass {
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
let udphopPort = '';
|
let udphopPort = '';
|
||||||
let udphopInterval = 30;
|
let udphopIntervalMin = 30;
|
||||||
|
let udphopIntervalMax = 30;
|
||||||
if (json.udphop) {
|
if (json.udphop) {
|
||||||
udphopPort = json.udphop.port || '';
|
udphopPort = json.udphop.port || '';
|
||||||
udphopInterval = json.udphop.interval || 30;
|
// Backward compatibility: if old 'interval' exists, use it for both min/max
|
||||||
|
if (json.udphop.interval !== undefined) {
|
||||||
|
udphopIntervalMin = json.udphop.interval;
|
||||||
|
udphopIntervalMax = json.udphop.interval;
|
||||||
|
} else {
|
||||||
|
udphopIntervalMin = json.udphop.intervalMin || 30;
|
||||||
|
udphopIntervalMax = json.udphop.intervalMax || 30;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new HysteriaStreamSettings(
|
return new HysteriaStreamSettings(
|
||||||
json.version,
|
json.version,
|
||||||
|
|
@ -474,7 +482,8 @@ class HysteriaStreamSettings extends CommonClass {
|
||||||
json.up,
|
json.up,
|
||||||
json.down,
|
json.down,
|
||||||
udphopPort,
|
udphopPort,
|
||||||
udphopInterval,
|
udphopIntervalMin,
|
||||||
|
udphopIntervalMax,
|
||||||
json.initStreamReceiveWindow,
|
json.initStreamReceiveWindow,
|
||||||
json.maxStreamReceiveWindow,
|
json.maxStreamReceiveWindow,
|
||||||
json.initConnectionReceiveWindow,
|
json.initConnectionReceiveWindow,
|
||||||
|
|
@ -503,7 +512,8 @@ class HysteriaStreamSettings extends CommonClass {
|
||||||
if (this.udphopPort) {
|
if (this.udphopPort) {
|
||||||
result.udphop = {
|
result.udphop = {
|
||||||
port: this.udphopPort,
|
port: this.udphopPort,
|
||||||
interval: this.udphopInterval
|
intervalMin: this.udphopIntervalMin,
|
||||||
|
intervalMax: this.udphopIntervalMax
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -558,27 +568,49 @@ class SockoptStreamSettings extends CommonClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UdpMask extends CommonClass {
|
class FinalMask extends CommonClass {
|
||||||
constructor(type = 'salamander', password = '') {
|
constructor(type = 'salamander', settings = {}) {
|
||||||
super();
|
super();
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.password = password;
|
this.settings = this._getDefaultSettings(type, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getDefaultSettings(type, settings = {}) {
|
||||||
|
switch (type) {
|
||||||
|
case 'salamander':
|
||||||
|
case 'mkcp-aes128gcm':
|
||||||
|
return { password: settings.password || '' };
|
||||||
|
case 'header-dns':
|
||||||
|
case 'xdns':
|
||||||
|
return { domain: settings.domain || '' };
|
||||||
|
case 'mkcp-original':
|
||||||
|
case 'header-dtls':
|
||||||
|
case 'header-srtp':
|
||||||
|
case 'header-utp':
|
||||||
|
case 'header-wechat':
|
||||||
|
case 'header-wireguard':
|
||||||
|
return {}; // No settings needed
|
||||||
|
default:
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new UdpMask(
|
return new FinalMask(
|
||||||
json.type,
|
json.type || 'salamander',
|
||||||
json.settings?.password || ''
|
json.settings || {}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
const result = {
|
||||||
type: this.type,
|
type: this.type
|
||||||
settings: {
|
|
||||||
password: this.password
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
// Only include settings if they exist and are not empty
|
||||||
|
if (this.settings && Object.keys(this.settings).length > 0) {
|
||||||
|
result.settings = this.settings;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -595,7 +627,7 @@ class StreamSettings extends CommonClass {
|
||||||
httpupgradeSettings = new HttpUpgradeStreamSettings(),
|
httpupgradeSettings = new HttpUpgradeStreamSettings(),
|
||||||
xhttpSettings = new xHTTPStreamSettings(),
|
xhttpSettings = new xHTTPStreamSettings(),
|
||||||
hysteriaSettings = new HysteriaStreamSettings(),
|
hysteriaSettings = new HysteriaStreamSettings(),
|
||||||
udpmasks = [],
|
finalmask = { udp: [] },
|
||||||
sockopt = undefined,
|
sockopt = undefined,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -610,16 +642,21 @@ class StreamSettings extends CommonClass {
|
||||||
this.httpupgrade = httpupgradeSettings;
|
this.httpupgrade = httpupgradeSettings;
|
||||||
this.xhttp = xhttpSettings;
|
this.xhttp = xhttpSettings;
|
||||||
this.hysteria = hysteriaSettings;
|
this.hysteria = hysteriaSettings;
|
||||||
this.udpmasks = udpmasks;
|
this.finalmask = finalmask;
|
||||||
this.sockopt = sockopt;
|
this.sockopt = sockopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
addUdpMask() {
|
addUdpMask(type = 'salamander') {
|
||||||
this.udpmasks.push(new UdpMask());
|
if (!this.finalmask.udp) {
|
||||||
|
this.finalmask.udp = [];
|
||||||
|
}
|
||||||
|
this.finalmask.udp.push(new FinalMask(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
delUdpMask(index) {
|
delUdpMask(index) {
|
||||||
this.udpmasks.splice(index, 1);
|
if (this.finalmask.udp) {
|
||||||
|
this.finalmask.udp.splice(index, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isTls() {
|
get isTls() {
|
||||||
|
|
@ -639,7 +676,16 @@ class StreamSettings extends CommonClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
const udpmasks = json.udpmasks ? json.udpmasks.map(mask => UdpMask.fromJson(mask)) : [];
|
let finalmask = { udp: [] };
|
||||||
|
if (json.finalmask) {
|
||||||
|
if (Array.isArray(json.finalmask)) {
|
||||||
|
// Legacy format: direct array (backward compatibility)
|
||||||
|
finalmask.udp = json.finalmask.map(mask => FinalMask.fromJson(mask));
|
||||||
|
} else if (json.finalmask.udp) {
|
||||||
|
// New format: object with udp array
|
||||||
|
finalmask.udp = json.finalmask.udp.map(mask => FinalMask.fromJson(mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
return new StreamSettings(
|
return new StreamSettings(
|
||||||
json.network,
|
json.network,
|
||||||
json.security,
|
json.security,
|
||||||
|
|
@ -652,7 +698,7 @@ class StreamSettings extends CommonClass {
|
||||||
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
||||||
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
||||||
HysteriaStreamSettings.fromJson(json.hysteriaSettings),
|
HysteriaStreamSettings.fromJson(json.hysteriaSettings),
|
||||||
udpmasks,
|
finalmask,
|
||||||
SockoptStreamSettings.fromJson(json.sockopt),
|
SockoptStreamSettings.fromJson(json.sockopt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -671,7 +717,9 @@ class StreamSettings extends CommonClass {
|
||||||
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
||||||
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
||||||
hysteriaSettings: network === 'hysteria' ? this.hysteria.toJson() : undefined,
|
hysteriaSettings: network === 'hysteria' ? this.hysteria.toJson() : undefined,
|
||||||
udpmasks: this.udpmasks.length > 0 ? this.udpmasks.map(mask => mask.toJson()) : undefined,
|
finalmask: (this.finalmask.udp && this.finalmask.udp.length > 0) ? {
|
||||||
|
udp: this.finalmask.udp.map(mask => mask.toJson())
|
||||||
|
} : undefined,
|
||||||
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -996,7 +1044,15 @@ class Outbound extends CommonClass {
|
||||||
stream.hysteria.up = urlParams.get('up') ?? '0';
|
stream.hysteria.up = urlParams.get('up') ?? '0';
|
||||||
stream.hysteria.down = urlParams.get('down') ?? '0';
|
stream.hysteria.down = urlParams.get('down') ?? '0';
|
||||||
stream.hysteria.udphopPort = urlParams.get('udphopPort') ?? '';
|
stream.hysteria.udphopPort = urlParams.get('udphopPort') ?? '';
|
||||||
stream.hysteria.udphopInterval = parseInt(urlParams.get('udphopInterval') ?? '30');
|
// Support both old single interval and new min/max range
|
||||||
|
if (urlParams.has('udphopInterval')) {
|
||||||
|
const interval = parseInt(urlParams.get('udphopInterval'));
|
||||||
|
stream.hysteria.udphopIntervalMin = interval;
|
||||||
|
stream.hysteria.udphopIntervalMax = interval;
|
||||||
|
} else {
|
||||||
|
stream.hysteria.udphopIntervalMin = parseInt(urlParams.get('udphopIntervalMin') ?? '30');
|
||||||
|
stream.hysteria.udphopIntervalMax = parseInt(urlParams.get('udphopIntervalMax') ?? '30');
|
||||||
|
}
|
||||||
|
|
||||||
// Optional QUIC parameters
|
// Optional QUIC parameters
|
||||||
if (urlParams.has('initStreamReceiveWindow')) {
|
if (urlParams.has('initStreamReceiveWindow')) {
|
||||||
|
|
@ -1285,11 +1341,14 @@ Outbound.VLESSSettings = class extends CommonClass {
|
||||||
flow: this.flow,
|
flow: this.flow,
|
||||||
encryption: this.encryption,
|
encryption: this.encryption,
|
||||||
};
|
};
|
||||||
if (this.testpre > 0) {
|
// Only include Vision settings when flow is set
|
||||||
result.testpre = this.testpre;
|
if (this.flow && this.flow !== '') {
|
||||||
}
|
if (this.testpre > 0) {
|
||||||
if (this.testseed && this.testseed.length >= 4) {
|
result.testpre = this.testpre;
|
||||||
result.testseed = this.testseed;
|
}
|
||||||
|
if (this.testseed && this.testseed.length >= 4) {
|
||||||
|
result.testseed = this.testseed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -1422,7 +1481,7 @@ Outbound.HttpSettings = class extends CommonClass {
|
||||||
|
|
||||||
Outbound.WireguardSettings = class extends CommonClass {
|
Outbound.WireguardSettings = class extends CommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
mtu = 1250,
|
mtu = 1420,
|
||||||
secretKey = '',
|
secretKey = '',
|
||||||
address = [''],
|
address = [''],
|
||||||
workers = 2,
|
workers = 2,
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,15 @@
|
||||||
// List of popular services for VLESS Reality Target/SNI randomization
|
// List of popular services for VLESS Reality Target/SNI randomization
|
||||||
const REALITY_TARGETS = [
|
const REALITY_TARGETS = [
|
||||||
{ target: 'www.icloud.com:443', sni: 'www.icloud.com,icloud.com' },
|
{ target: 'www.apple.com:443', sni: 'www.apple.com' },
|
||||||
{ target: 'www.apple.com:443', sni: 'www.apple.com,apple.com' },
|
{ target: 'www.icloud.com:443', sni: 'www.icloud.com' },
|
||||||
{ target: 'www.tesla.com:443', sni: 'www.tesla.com,tesla.com' },
|
{ target: 'www.amazon.com:443', sni: 'www.amazon.com' },
|
||||||
{ target: 'www.sony.com:443', sni: 'www.sony.com,sony.com' },
|
{ target: 'aws.amazon.com:443', sni: 'aws.amazon.com' },
|
||||||
{ target: 'www.nvidia.com:443', sni: 'www.nvidia.com,nvidia.com' },
|
{ target: 'www.oracle.com:443', sni: 'www.oracle.com' },
|
||||||
{ target: 'www.amd.com:443', sni: 'www.amd.com,amd.com' },
|
{ target: 'www.nvidia.com:443', sni: 'www.nvidia.com' },
|
||||||
{ target: 'azure.microsoft.com:443', sni: 'azure.microsoft.com,www.azure.com' },
|
{ target: 'www.amd.com:443', sni: 'www.amd.com' },
|
||||||
{ target: 'aws.amazon.com:443', sni: 'aws.amazon.com,amazon.com' },
|
{ target: 'www.intel.com:443', sni: 'www.intel.com' },
|
||||||
{ target: 'www.bing.com:443', sni: 'www.bing.com,bing.com' },
|
{ target: 'www.tesla.com:443', sni: 'www.tesla.com' },
|
||||||
{ target: 'www.oracle.com:443', sni: 'www.oracle.com,oracle.com' },
|
{ target: 'www.sony.com:443', sni: 'www.sony.com' }
|
||||||
{ target: 'www.intel.com:443', sni: 'www.intel.com,intel.com' },
|
|
||||||
{ target: 'www.microsoft.com:443', sni: 'www.microsoft.com,microsoft.com' },
|
|
||||||
{ target: 'www.amazon.com:443', sni: 'www.amazon.com,amazon.com' }
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -28,4 +25,3 @@ function getRandomRealityTarget() {
|
||||||
sni: selected.sni
|
sni: selected.sni
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,11 @@ class AllSetting {
|
||||||
this.subEnable = true;
|
this.subEnable = true;
|
||||||
this.subJsonEnable = false;
|
this.subJsonEnable = false;
|
||||||
this.subTitle = "";
|
this.subTitle = "";
|
||||||
|
this.subSupportUrl = "";
|
||||||
|
this.subProfileUrl = "";
|
||||||
|
this.subAnnounce = "";
|
||||||
|
this.subEnableRouting = true;
|
||||||
|
this.subRoutingRules = "";
|
||||||
this.subListen = "";
|
this.subListen = "";
|
||||||
this.subPort = 2096;
|
this.subPort = 2096;
|
||||||
this.subPath = "/sub/";
|
this.subPath = "/sub/";
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@ type AllSetting struct {
|
||||||
SubEnable bool `json:"subEnable" form:"subEnable"` // Enable subscription server
|
SubEnable bool `json:"subEnable" form:"subEnable"` // Enable subscription server
|
||||||
SubJsonEnable bool `json:"subJsonEnable" form:"subJsonEnable"` // Enable JSON subscription endpoint
|
SubJsonEnable bool `json:"subJsonEnable" form:"subJsonEnable"` // Enable JSON subscription endpoint
|
||||||
SubTitle string `json:"subTitle" form:"subTitle"` // Subscription title
|
SubTitle string `json:"subTitle" form:"subTitle"` // Subscription title
|
||||||
|
SubSupportUrl string `json:"subSupportUrl" form:"subSupportUrl"` // Subscription support URL
|
||||||
|
SubProfileUrl string `json:"subProfileUrl" form:"subProfileUrl"` // Subscription profile URL
|
||||||
|
SubAnnounce string `json:"subAnnounce" form:"subAnnounce"` // Subscription announce
|
||||||
|
SubEnableRouting bool `json:"subEnableRouting" form:"subEnableRouting"` // Enable routing for subscription
|
||||||
|
SubRoutingRules string `json:"subRoutingRules" form:"subRoutingRules"` // Subscription global routing rules (Only for Happ)
|
||||||
SubListen string `json:"subListen" form:"subListen"` // Subscription server listen IP
|
SubListen string `json:"subListen" form:"subListen"` // Subscription server listen IP
|
||||||
SubPort int `json:"subPort" form:"subPort"` // Subscription server port
|
SubPort int `json:"subPort" form:"subPort"` // Subscription server port
|
||||||
SubPath string `json:"subPath" form:"subPath"` // Base path for subscription URLs
|
SubPath string `json:"subPath" form:"subPath"` // Base path for subscription URLs
|
||||||
|
|
|
||||||
|
|
@ -407,21 +407,6 @@
|
||||||
|
|
||||||
<!-- kcp -->
|
<!-- kcp -->
|
||||||
<template v-if="outbound.stream.network === 'kcp'">
|
<template v-if="outbound.stream.network === 'kcp'">
|
||||||
<a-form-item label='{{ i18n "camouflage" }}'>
|
|
||||||
<a-select v-model="outbound.stream.kcp.type"
|
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option value="none">None</a-select-option>
|
|
||||||
<a-select-option value="srtp">SRTP</a-select-option>
|
|
||||||
<a-select-option value="utp">uTP</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="wireguard">WireGuard</a-select-option>
|
|
||||||
<a-select-option value="dns">DNS</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='{{ i18n "password" }}'>
|
|
||||||
<a-input v-model="outbound.stream.kcp.seed"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='MTU'>
|
<a-form-item label='MTU'>
|
||||||
<a-input-number v-model.number="outbound.stream.kcp.mtu"
|
<a-input-number v-model.number="outbound.stream.kcp.mtu"
|
||||||
min="0"></a-input-number>
|
min="0"></a-input-number>
|
||||||
|
|
@ -546,8 +531,9 @@
|
||||||
<a-input v-model.trim="outbound.stream.hysteria.auth"></a-input>
|
<a-input v-model.trim="outbound.stream.hysteria.auth"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Congestion'>
|
<a-form-item label='Congestion'>
|
||||||
<a-select v-model="outbound.stream.hysteria.congestion" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="outbound.stream.hysteria.congestion"
|
||||||
<a-select-option value="">BBR (Auto)</a-select-option>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value>BBR (Auto)</a-select-option>
|
||||||
<a-select-option value="brutal">Brutal</a-select-option>
|
<a-select-option value="brutal">Brutal</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
@ -563,10 +549,16 @@
|
||||||
<a-input v-model.trim="outbound.stream.hysteria.udphopPort"
|
<a-input v-model.trim="outbound.stream.hysteria.udphopPort"
|
||||||
placeholder="e.g., 1145-1919 or 11,13,15-17"></a-input>
|
placeholder="e.g., 1145-1919 or 11,13,15-17"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='UDP Hop Interval (s)'
|
<a-form-item label='UDP Hop Interval Min (s)'
|
||||||
v-if="outbound.stream.hysteria.udphopPort">
|
v-if="outbound.stream.hysteria.udphopPort">
|
||||||
<a-input-number
|
<a-input-number
|
||||||
v-model.number="outbound.stream.hysteria.udphopInterval"
|
v-model.number="outbound.stream.hysteria.udphopIntervalMin"
|
||||||
|
:min="5"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='UDP Hop Interval Max (s)'
|
||||||
|
v-if="outbound.stream.hysteria.udphopPort">
|
||||||
|
<a-input-number
|
||||||
|
v-model.number="outbound.stream.hysteria.udphopIntervalMax"
|
||||||
:min="5"></a-input-number>
|
:min="5"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Init Stream Receive'>
|
<a-form-item label='Init Stream Receive'>
|
||||||
|
|
@ -602,25 +594,73 @@
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- udpmasks settings -->
|
<!-- finalmask settings -->
|
||||||
<template v-if="outbound.canEnableStream()">
|
<template v-if="outbound.canEnableStream()">
|
||||||
<a-form-item label="UDP Masks">
|
<a-form-item label="UDP Masks">
|
||||||
<a-button icon="plus" type="primary" size="small" @click="outbound.stream.addUdpMask()"></a-button>
|
<a-button icon="plus" type="primary" size="small"
|
||||||
|
@click="outbound.stream.addUdpMask(outbound.protocol === Protocols.Hysteria ? 'salamander' : (outbound.stream.network === 'kcp' ? 'mkcp-aes128gcm' : 'xdns'))"></a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<template v-if="outbound.stream.udpmasks.length > 0">
|
<template
|
||||||
<a-form v-for="(mask, index) in outbound.stream.udpmasks" :key="index" :colon="false"
|
v-if="outbound.stream.finalmask.udp && outbound.stream.finalmask.udp.length > 0">
|
||||||
|
<a-form v-for="(mask, index) in outbound.stream.finalmask.udp"
|
||||||
|
:key="index" :colon="false"
|
||||||
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider :style="{ margin: '0' }"> UDP Mask [[ index + 1 ]]
|
<a-divider :style="{ margin: '0' }"> UDP Mask [[ index + 1 ]]
|
||||||
<a-icon type="delete" @click="() => outbound.stream.delUdpMask(index)"
|
<a-icon type="delete"
|
||||||
|
@click="() => outbound.stream.delUdpMask(index)"
|
||||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<a-form-item label='Type'>
|
<a-form-item label='Type'>
|
||||||
<a-select v-model="mask.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="mask.type"
|
||||||
<a-select-option value="salamander">Salamander</a-select-option>
|
@change="(type) => mask.settings = mask._getDefaultSettings(type, {})"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<!-- Salamander for Hysteria2 only -->
|
||||||
|
<a-select-option v-if="outbound.protocol === Protocols.Hysteria"
|
||||||
|
value="salamander">
|
||||||
|
Salamander (Hysteria2)</a-select-option>
|
||||||
|
<!-- mKCP-specific masks -->
|
||||||
|
<a-select-option v-if="outbound.stream.network === 'kcp'"
|
||||||
|
value="mkcp-aes128gcm">
|
||||||
|
mKCP AES-128-GCM</a-select-option>
|
||||||
|
<a-select-option v-if="outbound.stream.network === 'kcp'"
|
||||||
|
value="header-dns">
|
||||||
|
Header DNS</a-select-option>
|
||||||
|
<a-select-option v-if="outbound.stream.network === 'kcp'"
|
||||||
|
value="header-dtls">
|
||||||
|
Header DTLS 1.2</a-select-option>
|
||||||
|
<a-select-option v-if="outbound.stream.network === 'kcp'"
|
||||||
|
value="header-srtp">
|
||||||
|
Header SRTP</a-select-option>
|
||||||
|
<a-select-option v-if="outbound.stream.network === 'kcp'"
|
||||||
|
value="header-utp">
|
||||||
|
Header uTP</a-select-option>
|
||||||
|
<a-select-option v-if="outbound.stream.network === 'kcp'"
|
||||||
|
value="header-wechat">
|
||||||
|
Header WeChat Video</a-select-option>
|
||||||
|
<a-select-option v-if="outbound.stream.network === 'kcp'"
|
||||||
|
value="header-wireguard">
|
||||||
|
Header WireGuard</a-select-option>
|
||||||
|
<a-select-option v-if="outbound.stream.network === 'kcp'"
|
||||||
|
value="mkcp-original">
|
||||||
|
mKCP Original</a-select-option>
|
||||||
|
<!-- xDNS for TCP/WS/HTTPUpgrade/XHTTP -->
|
||||||
|
<a-select-option
|
||||||
|
v-if="['tcp', 'ws', 'httpupgrade', 'xhttp'].includes(outbound.stream.network)"
|
||||||
|
value="xdns">
|
||||||
|
xDNS (Experimental)</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Password'>
|
<!-- Settings for password-based masks -->
|
||||||
<a-input v-model.trim="mask.password" placeholder="Obfuscation password"></a-input>
|
<a-form-item label='Password'
|
||||||
|
v-if="['salamander', 'mkcp-aes128gcm'].includes(mask.type)">
|
||||||
|
<a-input v-model.trim="mask.settings.password"
|
||||||
|
placeholder="Obfuscation password"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<!-- Settings for domain-based masks -->
|
||||||
|
<a-form-item label='Domain'
|
||||||
|
v-if="['header-dns', 'xdns'].includes(mask.type)">
|
||||||
|
<a-input v-model.trim="mask.settings.domain"
|
||||||
|
placeholder="e.g., www.example.com"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -663,6 +703,15 @@
|
||||||
<a-form-item label="Allow Insecure">
|
<a-form-item label="Allow Insecure">
|
||||||
<a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch>
|
<a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="verify Peer Cert By Name">
|
||||||
|
<a-input
|
||||||
|
v-model.trim="outbound.stream.tls.verifyPeerCertByName"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="pinned Peer Cert Sha256">
|
||||||
|
<a-input v-model.trim="outbound.stream.tls.pinnedPeerCertSha256"
|
||||||
|
placeholder="Enter SHA256 fingerprints (base64)">
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- reality settings -->
|
<!-- reality settings -->
|
||||||
|
|
|
||||||
70
web/html/form/stream/stream_finalmask.html
Normal file
70
web/html/form/stream/stream_finalmask.html
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
{{define "form/streamFinalMask"}}
|
||||||
|
<a-divider :style="{ margin: '5px 0 0' }"></a-divider>
|
||||||
|
<a-form :colon="false" :label-col="{ md: {span:8} }"
|
||||||
|
:wrapper-col="{ md: {span:14} }">
|
||||||
|
<a-form-item label="UDP Masks">
|
||||||
|
<a-button icon="plus" type="primary" size="small"
|
||||||
|
@click="inbound.stream.addUdpMask(inbound.stream.network === 'kcp' ? 'mkcp-aes128gcm' : 'xdns')"></a-button>
|
||||||
|
</a-form-item>
|
||||||
|
<template
|
||||||
|
v-if="inbound.stream.finalmask.udp && inbound.stream.finalmask.udp.length > 0">
|
||||||
|
<a-form v-for="(mask, index) in inbound.stream.finalmask.udp"
|
||||||
|
:key="index" :colon="false"
|
||||||
|
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
|
<a-divider :style="{ margin: '0' }"> UDP Mask [[ index + 1 ]]
|
||||||
|
<a-icon type="delete"
|
||||||
|
@click="() => inbound.stream.delUdpMask(index)"
|
||||||
|
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||||
|
</a-divider>
|
||||||
|
<a-form-item label='Type'>
|
||||||
|
<a-select v-model="mask.type"
|
||||||
|
@change="(type) => mask.settings = mask._getDefaultSettings(type, {})"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<!-- mKCP-specific masks -->
|
||||||
|
<a-select-option v-if="inbound.stream.network === 'kcp'"
|
||||||
|
value="mkcp-aes128gcm">
|
||||||
|
mKCP AES-128-GCM</a-select-option>
|
||||||
|
<a-select-option v-if="inbound.stream.network === 'kcp'"
|
||||||
|
value="header-dns">
|
||||||
|
Header DNS</a-select-option>
|
||||||
|
<a-select-option v-if="inbound.stream.network === 'kcp'"
|
||||||
|
value="header-dtls">
|
||||||
|
Header DTLS 1.2</a-select-option>
|
||||||
|
<a-select-option v-if="inbound.stream.network === 'kcp'"
|
||||||
|
value="header-srtp">
|
||||||
|
Header SRTP</a-select-option>
|
||||||
|
<a-select-option v-if="inbound.stream.network === 'kcp'"
|
||||||
|
value="header-utp">
|
||||||
|
Header uTP</a-select-option>
|
||||||
|
<a-select-option v-if="inbound.stream.network === 'kcp'"
|
||||||
|
value="header-wechat">
|
||||||
|
Header WeChat Video</a-select-option>
|
||||||
|
<a-select-option v-if="inbound.stream.network === 'kcp'"
|
||||||
|
value="header-wireguard">
|
||||||
|
Header WireGuard</a-select-option>
|
||||||
|
<a-select-option v-if="inbound.stream.network === 'kcp'"
|
||||||
|
value="mkcp-original">
|
||||||
|
mKCP Original</a-select-option>
|
||||||
|
<!-- xDNS for TCP/WS/HTTPUpgrade/XHTTP -->
|
||||||
|
<a-select-option
|
||||||
|
v-if="['tcp', 'ws', 'httpupgrade', 'xhttp'].includes(inbound.stream.network)"
|
||||||
|
value="xdns">
|
||||||
|
xDNS (Experimental)</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<!-- Settings for password-based masks -->
|
||||||
|
<a-form-item label='Password'
|
||||||
|
v-if="['mkcp-aes128gcm'].includes(mask.type)">
|
||||||
|
<a-input v-model.trim="mask.settings.password"
|
||||||
|
placeholder="Obfuscation password"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<!-- Settings for domain-based masks -->
|
||||||
|
<a-form-item label='Domain'
|
||||||
|
v-if="['header-dns', 'xdns'].includes(mask.type)">
|
||||||
|
<a-input v-model.trim="mask.settings.domain"
|
||||||
|
placeholder="e.g., www.example.com"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
</a-form>
|
||||||
|
{{end}}
|
||||||
|
|
@ -1,48 +1,32 @@
|
||||||
{{define "form/streamKCP"}}
|
{{define "form/streamKCP"}}
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }"
|
||||||
<a-form-item label='{{ i18n "camouflage" }}'>
|
:wrapper-col="{ md: {span:14} }">
|
||||||
<a-select v-model="inbound.stream.kcp.type" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option value="none">None</a-select-option>
|
|
||||||
<a-select-option value="srtp">SRTP</a-select-option>
|
|
||||||
<a-select-option value="utp">uTP</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="wireguard">WireGuard</a-select-option>
|
|
||||||
<a-select-option value="dns">DNS</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item>
|
|
||||||
<template slot="label">
|
|
||||||
<a-tooltip>
|
|
||||||
<template slot="title">
|
|
||||||
<span>{{ i18n "reset" }}</span>
|
|
||||||
</template>
|
|
||||||
{{ i18n "password" }}
|
|
||||||
<a-icon @click="inbound.stream.kcp.seed = RandomUtil.randomSeq(10)"type="sync"> </a-icon>
|
|
||||||
</a-tooltip>
|
|
||||||
</template>
|
|
||||||
<a-input v-model.trim="inbound.stream.kcp.seed"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='MTU'>
|
<a-form-item label='MTU'>
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.mtu" :min="576" :max="1460"></a-input-number>
|
<a-input-number v-model.number="inbound.stream.kcp.mtu" :min="576"
|
||||||
|
:max="1460"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='TTI (ms)'>
|
<a-form-item label='TTI (ms)'>
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.tti" :min="10" :max="100"></a-input-number>
|
<a-input-number v-model.number="inbound.stream.kcp.tti" :min="10"
|
||||||
|
:max="100"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Uplink (MB/s)'>
|
<a-form-item label='Uplink (MB/s)'>
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.upCap" :min="0"></a-input-number>
|
<a-input-number v-model.number="inbound.stream.kcp.upCap"
|
||||||
</a-form-item>
|
:min="0"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item label='Downlink (MB/s)'>
|
<a-form-item label='Downlink (MB/s)'>
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.downCap" :min="0"></a-input-number>
|
<a-input-number v-model.number="inbound.stream.kcp.downCap"
|
||||||
|
:min="0"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Congestion'>
|
<a-form-item label='Congestion'>
|
||||||
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
|
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Read Buffer (MB)'>
|
<a-form-item label='Read Buffer (MB)'>
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.readBuffer" :min="0"></a-input-number>
|
<a-input-number v-model.number="inbound.stream.kcp.readBuffer"
|
||||||
|
:min="0"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Write Buffer (MB)'>
|
<a-form-item label='Write Buffer (MB)'>
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.writeBuffer" :min="0"></a-input-number>
|
<a-input-number v-model.number="inbound.stream.kcp.writeBuffer"
|
||||||
|
:min="0"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
{{define "form/streamSettings"}}
|
{{define "form/streamSettings"}}
|
||||||
<!-- select stream network -->
|
<!-- select stream network -->
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }"
|
||||||
|
:wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label='{{ i18n "transmission" }}'>
|
<a-form-item label='{{ i18n "transmission" }}'>
|
||||||
<a-select v-model="inbound.stream.network" :style="{ width: '75%' }" @change="streamNetworkChange"
|
<a-select v-model="inbound.stream.network" :style="{ width: '75%' }"
|
||||||
|
@change="streamNetworkChange"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="tcp">TCP (RAW)</a-select-option>
|
<a-select-option value="tcp">TCP (RAW)</a-select-option>
|
||||||
<a-select-option value="kcp">mKCP</a-select-option>
|
<a-select-option value="kcp">mKCP</a-select-option>
|
||||||
|
|
@ -48,4 +50,10 @@
|
||||||
<template>
|
<template>
|
||||||
{{template "form/streamSockopt"}}
|
{{template "form/streamSockopt"}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- finalmask - only for TCP, WS, HTTPUpgrade, XHTTP, mKCP -->
|
||||||
|
<template
|
||||||
|
v-if="['tcp', 'ws', 'httpupgrade', 'xhttp', 'kcp'].includes(inbound.stream.network)">
|
||||||
|
{{template "form/streamFinalMask"}}
|
||||||
|
</template>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
{{define "form/streamXHTTP"}}
|
{{define "form/streamXHTTP"}}
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }"
|
||||||
|
:wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label='{{ i18n "host" }}'>
|
<a-form-item label='{{ i18n "host" }}'>
|
||||||
<a-input v-model.trim="inbound.stream.xhttp.host"></a-input>
|
<a-input v-model.trim="inbound.stream.xhttp.host"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
@ -7,38 +8,138 @@
|
||||||
<a-input v-model.trim="inbound.stream.xhttp.path"></a-input>
|
<a-input v-model.trim="inbound.stream.xhttp.path"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||||
<a-button icon="plus" size="small" @click="inbound.stream.xhttp.addHeader('', '')"></a-button>
|
<a-button icon="plus" size="small"
|
||||||
|
@click="inbound.stream.xhttp.addHeader('', '')"></a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :wrapper-col="{span:24}">
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.xhttp.headers">
|
<a-input-group compact
|
||||||
|
v-for="(header, index) in inbound.stream.xhttp.headers">
|
||||||
<a-input :style="{ width: '50%' }" v-model.trim="header.name"
|
<a-input :style="{ width: '50%' }" v-model.trim="header.name"
|
||||||
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
||||||
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1
|
||||||
|
]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input :style="{ width: '50%' }" v-model.trim="header.value"
|
<a-input :style="{ width: '50%' }" v-model.trim="header.value"
|
||||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
<a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.xhttp.removeHeader(index)"></a-button>
|
<a-button icon="minus" slot="addonAfter" size="small"
|
||||||
|
@click="inbound.stream.xhttp.removeHeader(index)"></a-button>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Mode'>
|
<a-form-item label='Mode'>
|
||||||
<a-select v-model="inbound.stream.xhttp.mode" :style="{ width: '50%' }"
|
<a-select v-model="inbound.stream.xhttp.mode" :style="{ width: '50%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key
|
||||||
|
]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Max Buffered Upload" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
<a-form-item label="Max Buffered Upload"
|
||||||
<a-input-number v-model.number="inbound.stream.xhttp.scMaxBufferedPosts"></a-input-number>
|
v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||||
|
<a-input-number
|
||||||
|
v-model.number="inbound.stream.xhttp.scMaxBufferedPosts"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Max Upload Size (Byte)" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
<a-form-item label="Max Upload Size (Byte)"
|
||||||
<a-input v-model.trim="inbound.stream.xhttp.scMaxEachPostBytes"></a-input>
|
v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||||
|
<a-input
|
||||||
|
v-model.trim="inbound.stream.xhttp.scMaxEachPostBytes"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Stream-Up Server" v-if="inbound.stream.xhttp.mode === 'stream-up'">
|
<a-form-item label="Stream-Up Server"
|
||||||
<a-input v-model.trim="inbound.stream.xhttp.scStreamUpServerSecs"></a-input>
|
v-if="inbound.stream.xhttp.mode === 'stream-up'">
|
||||||
|
<a-input
|
||||||
|
v-model.trim="inbound.stream.xhttp.scStreamUpServerSecs"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Padding Bytes">
|
<a-form-item label="Padding Bytes">
|
||||||
<a-input v-model.trim="inbound.stream.xhttp.xPaddingBytes"></a-input>
|
<a-input v-model.trim="inbound.stream.xhttp.xPaddingBytes"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label="Padding Obfs Mode">
|
||||||
|
<a-switch v-model="inbound.stream.xhttp.xPaddingObfsMode"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
<template v-if="inbound.stream.xhttp.xPaddingObfsMode">
|
||||||
|
<a-form-item label="Padding Key">
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.xPaddingKey"
|
||||||
|
placeholder="x_padding"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Padding Header">
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.xPaddingHeader"
|
||||||
|
placeholder="X-Padding"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Padding Placement">
|
||||||
|
<a-select v-model="inbound.stream.xhttp.xPaddingPlacement"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value>Default (queryInHeader)</a-select-option>
|
||||||
|
<a-select-option
|
||||||
|
value="queryInHeader">queryInHeader</a-select-option>
|
||||||
|
<a-select-option value="header">header</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Padding Method">
|
||||||
|
<a-select v-model="inbound.stream.xhttp.xPaddingMethod"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value>Default (repeat-x)</a-select-option>
|
||||||
|
<a-select-option value="repeat-x">repeat-x</a-select-option>
|
||||||
|
<a-select-option value="tokenish">tokenish</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
|
<a-form-item label="Uplink HTTP Method">
|
||||||
|
<a-select v-model="inbound.stream.xhttp.uplinkHTTPMethod"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value>Default (POST)</a-select-option>
|
||||||
|
<a-select-option value="POST">POST</a-select-option>
|
||||||
|
<a-select-option value="PUT">PUT</a-select-option>
|
||||||
|
<a-select-option value="GET">GET (packet-up only)</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Session Placement">
|
||||||
|
<a-select v-model="inbound.stream.xhttp.sessionPlacement"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value>Default (path)</a-select-option>
|
||||||
|
<a-select-option value="path">path</a-select-option>
|
||||||
|
<a-select-option value="header">header</a-select-option>
|
||||||
|
<a-select-option value="cookie">cookie</a-select-option>
|
||||||
|
<a-select-option value="query">query</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Session Key"
|
||||||
|
v-if="inbound.stream.xhttp.sessionPlacement && inbound.stream.xhttp.sessionPlacement !== 'path'">
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.sessionKey"
|
||||||
|
placeholder="x_session"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Sequence Placement">
|
||||||
|
<a-select v-model="inbound.stream.xhttp.seqPlacement"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value>Default (path)</a-select-option>
|
||||||
|
<a-select-option value="path">path</a-select-option>
|
||||||
|
<a-select-option value="header">header</a-select-option>
|
||||||
|
<a-select-option value="cookie">cookie</a-select-option>
|
||||||
|
<a-select-option value="query">query</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Sequence Key"
|
||||||
|
v-if="inbound.stream.xhttp.seqPlacement && inbound.stream.xhttp.seqPlacement !== 'path'">
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.seqKey"
|
||||||
|
placeholder="x_seq"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Uplink Data Placement"
|
||||||
|
v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||||
|
<a-select v-model="inbound.stream.xhttp.uplinkDataPlacement"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value>Default (body)</a-select-option>
|
||||||
|
<a-select-option value="body">body</a-select-option>
|
||||||
|
<a-select-option value="header">header</a-select-option>
|
||||||
|
<a-select-option value="query">query</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Uplink Data Key"
|
||||||
|
v-if="inbound.stream.xhttp.mode === 'packet-up' && inbound.stream.xhttp.uplinkDataPlacement && inbound.stream.xhttp.uplinkDataPlacement !== 'body'">
|
||||||
|
<a-input v-model.trim="inbound.stream.xhttp.uplinkDataKey"
|
||||||
|
placeholder="x_data"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Uplink Chunk Size"
|
||||||
|
v-if="inbound.stream.xhttp.mode === 'packet-up' && inbound.stream.xhttp.uplinkDataPlacement && inbound.stream.xhttp.uplinkDataPlacement !== 'body'">
|
||||||
|
<a-input-number v-model.number="inbound.stream.xhttp.uplinkChunkSize"
|
||||||
|
:min="0" placeholder="0 (unlimited)"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item label="No SSE Header">
|
<a-form-item label="No SSE Header">
|
||||||
<a-switch v-model="inbound.stream.xhttp.noSSEHeader"></a-switch>
|
<a-switch v-model="inbound.stream.xhttp.noSSEHeader"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
{{define "form/tlsSettings"}}
|
{{define "form/tlsSettings"}}
|
||||||
<!-- tls enable -->
|
<!-- tls enable -->
|
||||||
<a-form v-if="inbound.canEnableTls()" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form v-if="inbound.canEnableTls()" :colon="false"
|
||||||
|
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider :style="{ margin: '3px 0' }"></a-divider>
|
<a-divider :style="{ margin: '3px 0' }"></a-divider>
|
||||||
<a-form-item label='{{ i18n "security" }}'>
|
<a-form-item label='{{ i18n "security" }}'>
|
||||||
<a-radio-group v-model="inbound.stream.security" button-style="solid">
|
<a-radio-group v-model="inbound.stream.security" button-style="solid">
|
||||||
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
||||||
<a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
|
<a-radio-button v-if="inbound.canEnableReality()"
|
||||||
|
value="reality">Reality</a-radio-button>
|
||||||
<a-radio-button value="tls">TLS</a-radio-button>
|
<a-radio-button value="tls">TLS</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
@ -16,33 +18,44 @@
|
||||||
<a-input v-model.trim="inbound.stream.tls.sni"></a-input>
|
<a-input v-model.trim="inbound.stream.tls.sni"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Cipher Suites">
|
<a-form-item label="Cipher Suites">
|
||||||
<a-select v-model="inbound.stream.tls.cipherSuites" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="inbound.stream.tls.cipherSuites"
|
||||||
<a-select-option value="">Auto</a-select-option>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[ value ]]</a-select-option>
|
<a-select-option value>Auto</a-select-option>
|
||||||
|
<a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[
|
||||||
|
value ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Min/Max Version">
|
<a-form-item label="Min/Max Version">
|
||||||
<a-input-group compact>
|
<a-input-group compact>
|
||||||
<a-select v-model="inbound.stream.tls.minVersion" :style="{ width: '50%' }"
|
<a-select v-model="inbound.stream.tls.minVersion"
|
||||||
|
:style="{ width: '50%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key
|
||||||
|
]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select v-model="inbound.stream.tls.maxVersion" :style="{ width: '50%' }"
|
<a-select v-model="inbound.stream.tls.maxVersion"
|
||||||
|
:style="{ width: '50%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key
|
||||||
|
]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="uTLS">
|
<a-form-item label="uTLS">
|
||||||
<a-select v-model="inbound.stream.tls.settings.fingerprint" :style="{ width: '100%' }"
|
<a-select v-model="inbound.stream.tls.settings.fingerprint"
|
||||||
|
:style="{ width: '100%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value=''>None</a-select-option>
|
<a-select-option value>None</a-select-option>
|
||||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key
|
||||||
|
]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="ALPN">
|
<a-form-item label="ALPN">
|
||||||
<a-select mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme" v-model="inbound.stream.tls.alpn">
|
<a-select mode="multiple"
|
||||||
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
|
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
|
v-model="inbound.stream.tls.alpn">
|
||||||
|
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn
|
||||||
|
]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Allow Insecure">
|
<a-form-item label="Allow Insecure">
|
||||||
|
|
@ -57,21 +70,25 @@
|
||||||
<a-form-item label="Session Resumption">
|
<a-form-item label="Session Resumption">
|
||||||
<a-switch v-model="inbound.stream.tls.enableSessionResumption"></a-switch>
|
<a-switch v-model="inbound.stream.tls.enableSessionResumption"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="VerifyPeerCertInNames">
|
|
||||||
<a-input v-model.trim="inbound.stream.tls.verifyPeerCertInNames"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-divider :style="{ margin: '3px 0' }"></a-divider>
|
<a-divider :style="{ margin: '3px 0' }"></a-divider>
|
||||||
<template v-for="cert,index in inbound.stream.tls.certs">
|
<template v-for="cert,index in inbound.stream.tls.certs">
|
||||||
<a-form-item label='{{ i18n "certificate" }}'>
|
<a-form-item label='{{ i18n "certificate" }}'>
|
||||||
<a-radio-group v-model="cert.useFile" button-style="solid" :style="{ display: 'inline-flex', whiteSpace: 'nowrap', maxWidth: '100%' }">
|
<a-radio-group v-model="cert.useFile" button-style="solid"
|
||||||
<a-radio-button :value="true" :style="{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
:style="{ display: 'inline-flex', whiteSpace: 'nowrap', maxWidth: '100%' }">
|
||||||
<a-radio-button :value="false" :style="{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
<a-radio-button :value="true"
|
||||||
|
:style="{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }">{{
|
||||||
|
i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
||||||
|
<a-radio-button :value="false"
|
||||||
|
:style="{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }">{{
|
||||||
|
i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label=" ">
|
<a-form-item label=" ">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button icon="plus" v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()"></a-button>
|
<a-button icon="plus" v-if="index === 0" type="primary" size="small"
|
||||||
<a-button icon="minus" v-if="inbound.stream.tls.certs.length>1" type="primary" size="small"
|
@click="inbound.stream.tls.addCert()"></a-button>
|
||||||
|
<a-button icon="minus" v-if="inbound.stream.tls.certs.length>1"
|
||||||
|
type="primary" size="small"
|
||||||
@click="inbound.stream.tls.removeCert(index)"></a-button>
|
@click="inbound.stream.tls.removeCert(index)"></a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
@ -83,7 +100,8 @@
|
||||||
<a-input v-model.trim="cert.keyFile"></a-input>
|
<a-input v-model.trim="cert.keyFile"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label=" ">
|
<a-form-item label=" ">
|
||||||
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">
|
<a-button type="primary" icon="import"
|
||||||
|
@click="setDefaultCertData(index)">
|
||||||
{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
|
{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -99,8 +117,10 @@
|
||||||
<a-switch v-model="cert.oneTimeLoading"></a-switch>
|
<a-switch v-model="cert.oneTimeLoading"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Usage Option'>
|
<a-form-item label='Usage Option'>
|
||||||
<a-select v-model="cert.usage" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="cert.usage" :style="{ width: '50%' }"
|
||||||
<a-select-option v-for="key in USAGE_OPTION" :value="key">[[ key ]]</a-select-option>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="key in USAGE_OPTION" :value="key">[[ key
|
||||||
|
]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Build Chain" v-if="cert.usage === 'issue'">
|
<a-form-item label="Build Chain" v-if="cert.usage === 'issue'">
|
||||||
|
|
@ -108,20 +128,22 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
<a-form-item label='ECH key'>
|
<a-form-item label='ECH key'>
|
||||||
<a-input v-model="inbound.stream.tls.echServerKeys"></a-input>
|
<a-input v-model="inbound.stream.tls.echServerKeys"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='ECH config'>
|
<a-form-item label='ECH config'>
|
||||||
<a-input v-model="inbound.stream.tls.settings.echConfigList"></a-input>
|
<a-input v-model="inbound.stream.tls.settings.echConfigList"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='ECH force query'>
|
<a-form-item label='ECH force query'>
|
||||||
<a-select v-model="inbound.stream.tls.echForceQuery"
|
<a-select v-model="inbound.stream.tls.echForceQuery"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in ['none', 'half', 'full']" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in ['none', 'half', 'full']" :value="key">[[
|
||||||
</a-select>
|
key ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label=" ">
|
<a-form-item label=" ">
|
||||||
<a-space>
|
<a-space>
|
||||||
<a-button type="primary" icon="import" @click="getNewEchCert">Get New ECH Cert</a-button>
|
<a-button type="primary" icon="import" @click="getNewEchCert">Get New
|
||||||
|
ECH Cert</a-button>
|
||||||
<a-button danger @click="clearEchCert">Clear</a-button>
|
<a-button danger @click="clearEchCert">Clear</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,6 @@
|
||||||
<a-switch v-model="allSetting.subJsonEnable"></a-switch>
|
<a-switch v-model="allSetting.subJsonEnable"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.settings.subTitle"}}</template>
|
|
||||||
<template #description>{{ i18n "pages.settings.subTitleDesc"}}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-input type="text" v-model="allSetting.subTitle"></a-input>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
<template #title>{{ i18n "pages.settings.subListen"}}</template>
|
<template #title>{{ i18n "pages.settings.subListen"}}</template>
|
||||||
<template #description>{{ i18n "pages.settings.subListenDesc"}}</template>
|
<template #description>{{ i18n "pages.settings.subListenDesc"}}</template>
|
||||||
|
|
@ -78,6 +71,50 @@
|
||||||
<a-switch v-model="allSetting.subShowInfo"></a-switch>
|
<a-switch v-model="allSetting.subShowInfo"></a-switch>
|
||||||
</template>
|
</template>
|
||||||
</a-setting-list-item>
|
</a-setting-list-item>
|
||||||
|
<a-divider>{{ i18n "pages.xray.basicTemplate"}}</a-divider>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subTitle"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subTitleDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subTitle"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subSupportUrl"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subSupportUrlDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subSupportUrl" placeholder="https://example.com"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subProfileUrl"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subProfileUrlDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subProfileUrl" placeholder="https://example.com"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subAnnounce"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subAnnounceDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-textarea v-model="allSetting.subAnnounce"></a-textarea>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-divider>{{ i18n "pages.xray.advancedTemplate"}} (Happ)</a-divider>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subEnableRouting"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subEnableRoutingDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="allSetting.subEnableRouting"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subRoutingRules"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subRoutingRulesDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-textarea v-model="allSetting.subRoutingRules" placeholder="happ://routing/add/..."></a-textarea>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
<a-collapse-panel key="3" header='{{ i18n "pages.settings.certs" }}'>
|
<a-collapse-panel key="3" header='{{ i18n "pages.settings.certs" }}'>
|
||||||
<a-setting-list-item paddings="small">
|
<a-setting-list-item paddings="small">
|
||||||
|
|
|
||||||
|
|
@ -567,7 +567,7 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if major > 26 || (major == 26 && minor > 1) || (major == 26 && minor == 1 && patch >= 18) {
|
if major > 26 || (major == 26 && minor > 1) || (major == 26 && minor == 1 && patch >= 31) {
|
||||||
versions = append(versions, release.TagName)
|
versions = append(versions, release.TagName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,11 @@ var defaultValueMap = map[string]string{
|
||||||
"subEnable": "true",
|
"subEnable": "true",
|
||||||
"subJsonEnable": "false",
|
"subJsonEnable": "false",
|
||||||
"subTitle": "",
|
"subTitle": "",
|
||||||
|
"subSupportUrl": "",
|
||||||
|
"subProfileUrl": "",
|
||||||
|
"subAnnounce": "",
|
||||||
|
"subEnableRouting": "true",
|
||||||
|
"subRoutingRules": "",
|
||||||
"subListen": "",
|
"subListen": "",
|
||||||
"subPort": "2096",
|
"subPort": "2096",
|
||||||
"subPath": "/sub/",
|
"subPath": "/sub/",
|
||||||
|
|
@ -459,6 +464,26 @@ func (s *SettingService) GetSubTitle() (string, error) {
|
||||||
return s.getString("subTitle")
|
return s.getString("subTitle")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubSupportUrl() (string, error) {
|
||||||
|
return s.getString("subSupportUrl")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubProfileUrl() (string, error) {
|
||||||
|
return s.getString("subProfileUrl")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubAnnounce() (string, error) {
|
||||||
|
return s.getString("subAnnounce")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubEnableRouting() (bool, error) {
|
||||||
|
return s.getBool("subEnableRouting")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetSubRoutingRules() (string, error) {
|
||||||
|
return s.getString("subRoutingRules")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetSubListen() (string, error) {
|
func (s *SettingService) GetSubListen() (string, error) {
|
||||||
return s.getString("subListen")
|
return s.getString("subListen")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "تمكين/تعطيل نقطة نهاية اشتراك JSON بشكل مستقل."
|
"subJsonEnable" = "تمكين/تعطيل نقطة نهاية اشتراك JSON بشكل مستقل."
|
||||||
"subTitle" = "عنوان الاشتراك"
|
"subTitle" = "عنوان الاشتراك"
|
||||||
"subTitleDesc" = "العنوان اللي هيظهر في عميل VPN"
|
"subTitleDesc" = "العنوان اللي هيظهر في عميل VPN"
|
||||||
|
"subSupportUrl" = "رابط الدعم"
|
||||||
|
"subSupportUrlDesc" = "رابط الدعم الفني المعروض في عميل VPN"
|
||||||
|
"subProfileUrl" = "رابط الملف الشخصي"
|
||||||
|
"subProfileUrlDesc" = "رابط لموقعك الإلكتروني يظهر في عميل VPN"
|
||||||
|
"subAnnounce" = "إعلان"
|
||||||
|
"subAnnounceDesc" = "نص الإعلان المعروض في عميل VPN"
|
||||||
|
"subEnableRouting" = "تفعيل التوجيه"
|
||||||
|
"subEnableRoutingDesc" = "إعداد عام لتمكين التوجيه (Routing) في عميل VPN. (فقط لـ Happ)"
|
||||||
|
"subRoutingRules" = "قواعد التوجيه"
|
||||||
|
"subRoutingRulesDesc" = "قواعد التوجيه العامة لعميل VPN. (فقط لـ Happ)"
|
||||||
"subListen" = "IP الاستماع"
|
"subListen" = "IP الاستماع"
|
||||||
"subListenDesc" = "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)"
|
"subListenDesc" = "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)"
|
||||||
"subPort" = "بورت الاستماع"
|
"subPort" = "بورت الاستماع"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "Enable/Disable the JSON subscription endpoint independently."
|
"subJsonEnable" = "Enable/Disable the JSON subscription endpoint independently."
|
||||||
"subTitle" = "Subscription Title"
|
"subTitle" = "Subscription Title"
|
||||||
"subTitleDesc" = "Title shown in VPN client"
|
"subTitleDesc" = "Title shown in VPN client"
|
||||||
|
"subSupportUrl" = "Support URL"
|
||||||
|
"subSupportUrlDesc" = "Technical support link shown in the VPN client"
|
||||||
|
"subProfileUrl" = "Profile URL"
|
||||||
|
"subProfileUrlDesc" = "A link to your website displayed in the VPN client"
|
||||||
|
"subAnnounce" = "Announce"
|
||||||
|
"subAnnounceDesc" = "The text of the announce displayed in the VPN client"
|
||||||
|
"subEnableRouting" = "Enable routing"
|
||||||
|
"subEnableRoutingDesc" = "Global setting to enable routing in the VPN client. (Only for Happ)"
|
||||||
|
"subRoutingRules" = "Routing rules"
|
||||||
|
"subRoutingRulesDesc" = "Global routing rules for the VPN client. (Only for Happ)"
|
||||||
"subListen" = "Listen IP"
|
"subListen" = "Listen IP"
|
||||||
"subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)"
|
"subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)"
|
||||||
"subPort" = "Listen Port"
|
"subPort" = "Listen Port"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "Habilitar/Deshabilitar el endpoint de suscripción JSON de forma independiente."
|
"subJsonEnable" = "Habilitar/Deshabilitar el endpoint de suscripción JSON de forma independiente."
|
||||||
"subTitle" = "Título de la Suscripción"
|
"subTitle" = "Título de la Suscripción"
|
||||||
"subTitleDesc" = "Título mostrado en el cliente de VPN"
|
"subTitleDesc" = "Título mostrado en el cliente de VPN"
|
||||||
|
"subSupportUrl" = "URL de soporte"
|
||||||
|
"subSupportUrlDesc" = "Enlace de soporte técnico mostrado en el cliente VPN"
|
||||||
|
"subProfileUrl" = "URL del perfil"
|
||||||
|
"subProfileUrlDesc" = "Un enlace a tu sitio web mostrado en el cliente VPN"
|
||||||
|
"subAnnounce" = "Anuncio"
|
||||||
|
"subAnnounceDesc" = "El texto del anuncio mostrado en el cliente VPN"
|
||||||
|
"subEnableRouting" = "Habilitar enrutamiento"
|
||||||
|
"subEnableRoutingDesc" = "Configuración global para habilitar el enrutamiento en el cliente VPN. (Solo para Happ)"
|
||||||
|
"subRoutingRules" = "Reglas de enrutamiento"
|
||||||
|
"subRoutingRulesDesc" = "Reglas de enrutamiento globales para el cliente VPN. (Solo para Happ)"
|
||||||
"subListen" = "Listening IP"
|
"subListen" = "Listening IP"
|
||||||
"subListenDesc" = "Dejar en blanco por defecto para monitorear todas las IPs."
|
"subListenDesc" = "Dejar en blanco por defecto para monitorear todas las IPs."
|
||||||
"subPort" = "Puerto de Suscripción"
|
"subPort" = "Puerto de Suscripción"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "فعال/غیرفعالسازی مستقل نقطه دسترسی سابسکریپشن JSON."
|
"subJsonEnable" = "فعال/غیرفعالسازی مستقل نقطه دسترسی سابسکریپشن JSON."
|
||||||
"subTitle" = "عنوان اشتراک"
|
"subTitle" = "عنوان اشتراک"
|
||||||
"subTitleDesc" = "عنوان نمایش داده شده در کلاینت VPN"
|
"subTitleDesc" = "عنوان نمایش داده شده در کلاینت VPN"
|
||||||
|
"subSupportUrl" = "آدرس پشتیبانی"
|
||||||
|
"subSupportUrlDesc" = "لینک پشتیبانی فنی که در کلاینت VPN نمایش داده میشود"
|
||||||
|
"subProfileUrl" = "آدرس پروفایل"
|
||||||
|
"subProfileUrlDesc" = "لینک وبسایت شما که در کلاینت VPN نمایش داده میشود"
|
||||||
|
"subAnnounce" = "اعلان"
|
||||||
|
"subAnnounceDesc" = "متن اعلانی که در کلاینت VPN نمایش داده میشود"
|
||||||
|
"subEnableRouting" = "فعالسازی مسیریابی"
|
||||||
|
"subEnableRoutingDesc" = "تنظیمات سراسری برای فعالسازی مسیریابی در کلاینت VPN. (فقط برای Happ)"
|
||||||
|
"subRoutingRules" = "قوانین مسیریابی"
|
||||||
|
"subRoutingRulesDesc" = "قوانین مسیریابی سراسری برای کلاینت VPN. (فقط برای Happ)"
|
||||||
"subListen" = "آدرس آیپی"
|
"subListen" = "آدرس آیپی"
|
||||||
"subListenDesc" = "آدرس آیپی برای سرویس سابسکریپشن. برای گوش دادن بهتمام آیپیها خالیبگذارید"
|
"subListenDesc" = "آدرس آیپی برای سرویس سابسکریپشن. برای گوش دادن بهتمام آیپیها خالیبگذارید"
|
||||||
"subPort" = "پورت"
|
"subPort" = "پورت"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "Aktifkan/Nonaktifkan endpoint langganan JSON secara mandiri."
|
"subJsonEnable" = "Aktifkan/Nonaktifkan endpoint langganan JSON secara mandiri."
|
||||||
"subTitle" = "Judul Langganan"
|
"subTitle" = "Judul Langganan"
|
||||||
"subTitleDesc" = "Judul yang ditampilkan di klien VPN"
|
"subTitleDesc" = "Judul yang ditampilkan di klien VPN"
|
||||||
|
"subSupportUrl" = "URL Dukungan"
|
||||||
|
"subSupportUrlDesc" = "Tautan dukungan teknis yang ditampilkan di klien VPN"
|
||||||
|
"subProfileUrl" = "URL Profil"
|
||||||
|
"subProfileUrlDesc" = "Tautan ke situs web Anda yang ditampilkan di klien VPN"
|
||||||
|
"subAnnounce" = "Pengumuman"
|
||||||
|
"subAnnounceDesc" = "Teks pengumuman yang ditampilkan di klien VPN"
|
||||||
|
"subEnableRouting" = "Aktifkan perutean"
|
||||||
|
"subEnableRoutingDesc" = "Pengaturan global untuk mengaktifkan perutean (routing) di klien VPN. (Hanya untuk Happ)"
|
||||||
|
"subRoutingRules" = "Aturan routing"
|
||||||
|
"subRoutingRulesDesc" = "Aturan routing global untuk klien VPN. (Hanya untuk Happ)"
|
||||||
"subListen" = "IP Pendengar"
|
"subListen" = "IP Pendengar"
|
||||||
"subListenDesc" = "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)"
|
"subListenDesc" = "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)"
|
||||||
"subPort" = "Port Pendengar"
|
"subPort" = "Port Pendengar"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "JSON サブスクリプションのエンドポイントを個別に有効/無効にする。"
|
"subJsonEnable" = "JSON サブスクリプションのエンドポイントを個別に有効/無効にする。"
|
||||||
"subTitle" = "サブスクリプションタイトル"
|
"subTitle" = "サブスクリプションタイトル"
|
||||||
"subTitleDesc" = "VPNクライアントに表示されるタイトル"
|
"subTitleDesc" = "VPNクライアントに表示されるタイトル"
|
||||||
|
"subSupportUrl" = "サポートURL"
|
||||||
|
"subSupportUrlDesc" = "VPNクライアントに表示されるテクニカルサポートへのリンク"
|
||||||
|
"subProfileUrl" = "プロフィールURL"
|
||||||
|
"subProfileUrlDesc" = "VPNクライアントに表示されるWebサイトへのリンク"
|
||||||
|
"subAnnounce" = "お知らせ"
|
||||||
|
"subAnnounceDesc" = "VPNクライアントに表示されるお知らせのテキスト"
|
||||||
|
"subEnableRouting" = "ルーティングを有効化"
|
||||||
|
"subEnableRoutingDesc" = "VPNクライアントでルーティングを有効にするためのグローバル設定。(Happのみ)"
|
||||||
|
"subRoutingRules" = "ルーティングルール"
|
||||||
|
"subRoutingRulesDesc" = "VPNクライアントのグローバルルーティングルール。(Happのみ)"
|
||||||
"subListen" = "監視IP"
|
"subListen" = "監視IP"
|
||||||
"subListenDesc" = "サブスクリプションサービスが監視するIPアドレス(空白にするとすべてのIPを監視)"
|
"subListenDesc" = "サブスクリプションサービスが監視するIPアドレス(空白にするとすべてのIPを監視)"
|
||||||
"subPort" = "監視ポート"
|
"subPort" = "監視ポート"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "Ativar/Desativar o endpoint de assinatura JSON de forma independente."
|
"subJsonEnable" = "Ativar/Desativar o endpoint de assinatura JSON de forma independente."
|
||||||
"subTitle" = "Título da Assinatura"
|
"subTitle" = "Título da Assinatura"
|
||||||
"subTitleDesc" = "Título exibido no cliente VPN"
|
"subTitleDesc" = "Título exibido no cliente VPN"
|
||||||
|
"subSupportUrl" = "URL de Suporte"
|
||||||
|
"subSupportUrlDesc" = "Link de suporte técnico exibido no cliente VPN"
|
||||||
|
"subProfileUrl" = "URL de Perfil"
|
||||||
|
"subProfileUrlDesc" = "Um link para o seu site exibido no cliente VPN"
|
||||||
|
"subAnnounce" = "Anúncio"
|
||||||
|
"subAnnounceDesc" = "O texto do anúncio exibido no cliente VPN"
|
||||||
|
"subEnableRouting" = "Ativar roteamento"
|
||||||
|
"subEnableRoutingDesc" = "Configuração global para habilitar o roteamento no cliente VPN. (Apenas para Happ)"
|
||||||
|
"subRoutingRules" = "Regras de roteamento"
|
||||||
|
"subRoutingRulesDesc" = "Regras de roteamento globais para o cliente VPN. (Apenas para Happ)"
|
||||||
"subListen" = "IP de Escuta"
|
"subListen" = "IP de Escuta"
|
||||||
"subListenDesc" = "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)"
|
"subListenDesc" = "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)"
|
||||||
"subPort" = "Porta de Escuta"
|
"subPort" = "Porta de Escuta"
|
||||||
|
|
|
||||||
|
|
@ -373,7 +373,17 @@
|
||||||
"subEnableDesc" = "Функция подписки с отдельной конфигурацией"
|
"subEnableDesc" = "Функция подписки с отдельной конфигурацией"
|
||||||
"subJsonEnable" = "Включить/отключить JSON-эндпоинт подписки независимо."
|
"subJsonEnable" = "Включить/отключить JSON-эндпоинт подписки независимо."
|
||||||
"subTitle" = "Заголовок подписки"
|
"subTitle" = "Заголовок подписки"
|
||||||
"subTitleDesc" = "Название подписки, которое видит клиент в VPN клиенте"
|
"subTitleDesc" = "Название подписки, которое видит клиент в VPN-клиенте"
|
||||||
|
"subSupportUrl" = "URL поддержки"
|
||||||
|
"subSupportUrlDesc" = "Ссылка на техническую поддержку, отображаемая в VPN-клиенте"
|
||||||
|
"subProfileUrl" = "URL профиля"
|
||||||
|
"subProfileUrlDesc" = "Ссылка на ваш сайт, отображаемая в VPN-клиенте"
|
||||||
|
"subAnnounce" = "Объявление"
|
||||||
|
"subAnnounceDesc" = "Текст объявления, отображаемый в VPN-клиенте"
|
||||||
|
"subEnableRouting" = "Включить маршрутизацию"
|
||||||
|
"subEnableRoutingDesc" = "Глобальная настройка для включения маршрутизации в VPN-клиенте. (Только для Happ)"
|
||||||
|
"subRoutingRules" = "Правила маршрутизации"
|
||||||
|
"subRoutingRulesDesc" = "Глобальные правила маршрутизации для VPN-клиента. (Только для Happ)"
|
||||||
"subListen" = "Прослушивание IP"
|
"subListen" = "Прослушивание IP"
|
||||||
"subListenDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все IP-адреса"
|
"subListenDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все IP-адреса"
|
||||||
"subPort" = "Порт подписки"
|
"subPort" = "Порт подписки"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "JSON abonelik uç noktasını bağımsız olarak Etkinleştir/Devre Dışı bırak."
|
"subJsonEnable" = "JSON abonelik uç noktasını bağımsız olarak Etkinleştir/Devre Dışı bırak."
|
||||||
"subTitle" = "Abonelik Başlığı"
|
"subTitle" = "Abonelik Başlığı"
|
||||||
"subTitleDesc" = "VPN istemcisinde gösterilen başlık"
|
"subTitleDesc" = "VPN istemcisinde gösterilen başlık"
|
||||||
|
"subSupportUrl" = "Destek URL'si"
|
||||||
|
"subSupportUrlDesc" = "VPN istemcisinde gösterilen teknik destek bağlantısı"
|
||||||
|
"subProfileUrl" = "Profil URL'si"
|
||||||
|
"subProfileUrlDesc" = "VPN istemcisinde görüntülenen web sitenize giden bağlantı"
|
||||||
|
"subAnnounce" = "Duyuru"
|
||||||
|
"subAnnounceDesc" = "VPN istemcisinde görüntülenen duyuru metni"
|
||||||
|
"subEnableRouting" = "Yönlendirmeyi etkinleştir"
|
||||||
|
"subEnableRoutingDesc" = "VPN istemcisinde yönlendirmeyi etkinleştirmek için genel ayar. (Yalnızca Happ için)"
|
||||||
|
"subRoutingRules" = "Yönlendirme kuralları"
|
||||||
|
"subRoutingRulesDesc" = "VPN istemcisi için genel yönlendirme kuralları. (Yalnızca Happ için)"
|
||||||
"subListen" = "Dinleme IP"
|
"subListen" = "Dinleme IP"
|
||||||
"subListenDesc" = "Abonelik hizmeti için IP adresi. (tüm IP'leri dinlemek için boş bırakın)"
|
"subListenDesc" = "Abonelik hizmeti için IP adresi. (tüm IP'leri dinlemek için boş bırakın)"
|
||||||
"subPort" = "Dinleme Portu"
|
"subPort" = "Dinleme Portu"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "Увімкнути/вимкнути JSON-кінець підписки незалежно."
|
"subJsonEnable" = "Увімкнути/вимкнути JSON-кінець підписки незалежно."
|
||||||
"subTitle" = "Назва Підписки"
|
"subTitle" = "Назва Підписки"
|
||||||
"subTitleDesc" = "Назва, яка відображається у VPN-клієнті"
|
"subTitleDesc" = "Назва, яка відображається у VPN-клієнті"
|
||||||
|
"subSupportUrl" = "URL підтримки"
|
||||||
|
"subSupportUrlDesc" = "Посилання на технічну підтримку, що відображається у VPN-клієнті"
|
||||||
|
"subProfileUrl" = "URL профілю"
|
||||||
|
"subProfileUrlDesc" = "Посилання на ваш вебсайт, що відображається у VPN-клієнті"
|
||||||
|
"subAnnounce" = "Оголошення"
|
||||||
|
"subAnnounceDesc" = "Текст оголошення, що відображається у VPN-клієнті"
|
||||||
|
"subEnableRouting" = "Увімкнути маршрутизацію"
|
||||||
|
"subEnableRoutingDesc" = "Глобальне налаштування для увімкнення маршрутизації у VPN-клієнті. (Тільки для Happ)"
|
||||||
|
"subRoutingRules" = "Правила маршрутизації"
|
||||||
|
"subRoutingRulesDesc" = "Глобальні правила маршрутизації для VPN-клієнта. (Тільки для Happ)"
|
||||||
"subListen" = "Слухати IP"
|
"subListen" = "Слухати IP"
|
||||||
"subListenDesc" = "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)"
|
"subListenDesc" = "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)"
|
||||||
"subPort" = "Слухати порт"
|
"subPort" = "Слухати порт"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "Bật/Tắt điểm cuối đăng ký JSON độc lập."
|
"subJsonEnable" = "Bật/Tắt điểm cuối đăng ký JSON độc lập."
|
||||||
"subTitle" = "Tiêu đề Đăng ký"
|
"subTitle" = "Tiêu đề Đăng ký"
|
||||||
"subTitleDesc" = "Tiêu đề hiển thị trong ứng dụng VPN"
|
"subTitleDesc" = "Tiêu đề hiển thị trong ứng dụng VPN"
|
||||||
|
"subSupportUrl" = "URL Hỗ trợ"
|
||||||
|
"subSupportUrlDesc" = "Liên kết hỗ trợ kỹ thuật hiển thị trong ứng dụng VPN"
|
||||||
|
"subProfileUrl" = "URL Hồ sơ"
|
||||||
|
"subProfileUrlDesc" = "Liên kết đến trang web của bạn hiển thị trong ứng dụng VPN"
|
||||||
|
"subAnnounce" = "Thông báo"
|
||||||
|
"subAnnounceDesc" = "Văn bản thông báo hiển thị trong ứng dụng VPN"
|
||||||
|
"subEnableRouting" = "Bật định tuyến"
|
||||||
|
"subEnableRoutingDesc" = "Cài đặt toàn cục để bật định tuyến trong ứng dụng khách VPN. (Chỉ dành cho Happ)"
|
||||||
|
"subRoutingRules" = "Quy tắc định tuyến"
|
||||||
|
"subRoutingRulesDesc" = "Quy tắc định tuyến toàn cầu cho client VPN. (Chỉ dành cho Happ)"
|
||||||
"subListen" = "Listening IP"
|
"subListen" = "Listening IP"
|
||||||
"subListenDesc" = "Mặc định để trống để nghe tất cả các IP"
|
"subListenDesc" = "Mặc định để trống để nghe tất cả các IP"
|
||||||
"subPort" = "Cổng gói đăng ký"
|
"subPort" = "Cổng gói đăng ký"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "单独启用/禁用 JSON 订阅端点。"
|
"subJsonEnable" = "单独启用/禁用 JSON 订阅端点。"
|
||||||
"subTitle" = "订阅标题"
|
"subTitle" = "订阅标题"
|
||||||
"subTitleDesc" = "在VPN客户端中显示的标题"
|
"subTitleDesc" = "在VPN客户端中显示的标题"
|
||||||
|
"subSupportUrl" = "支持链接"
|
||||||
|
"subSupportUrlDesc" = "VPN 客户端中显示的技术支持链接"
|
||||||
|
"subProfileUrl" = "个人资料链接"
|
||||||
|
"subProfileUrlDesc" = "VPN 客户端中显示的网站链接"
|
||||||
|
"subAnnounce" = "公告"
|
||||||
|
"subAnnounceDesc" = "VPN 客户端中显示的公告文本"
|
||||||
|
"subEnableRouting" = "启用路由"
|
||||||
|
"subEnableRoutingDesc" = "在 VPN 客户端中启用路由的全局设置。(僅限 Happ)"
|
||||||
|
"subRoutingRules" = "路由規則"
|
||||||
|
"subRoutingRulesDesc" = "VPN 用戶端的全域路由規則。(僅限 Happ)"
|
||||||
"subListen" = "监听 IP"
|
"subListen" = "监听 IP"
|
||||||
"subListenDesc" = "订阅服务监听的 IP 地址(留空表示监听所有 IP)"
|
"subListenDesc" = "订阅服务监听的 IP 地址(留空表示监听所有 IP)"
|
||||||
"subPort" = "监听端口"
|
"subPort" = "监听端口"
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,16 @@
|
||||||
"subJsonEnable" = "獨立啟用/停用 JSON 訂閱端點。"
|
"subJsonEnable" = "獨立啟用/停用 JSON 訂閱端點。"
|
||||||
"subTitle" = "訂閱標題"
|
"subTitle" = "訂閱標題"
|
||||||
"subTitleDesc" = "在VPN客戶端中顯示的標題"
|
"subTitleDesc" = "在VPN客戶端中顯示的標題"
|
||||||
|
"subSupportUrl" = "支援連結"
|
||||||
|
"subSupportUrlDesc" = "VPN 用戶端中顯示的技術支援連結"
|
||||||
|
"subProfileUrl" = "個人資料連結"
|
||||||
|
"subProfileUrlDesc" = "VPN 用戶端中顯示的網站連結"
|
||||||
|
"subAnnounce" = "公告"
|
||||||
|
"subAnnounceDesc" = "VPN 用戶端中顯示的公告文字"
|
||||||
|
"subEnableRouting" = "啟用路由"
|
||||||
|
"subEnableRoutingDesc" = "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ)"
|
||||||
|
"subRoutingRules" = "路由規則"
|
||||||
|
"subRoutingRulesDesc" = "VPN 用戶端的全域路由規則。(僅限 Happ)"
|
||||||
"subListen" = "監聽 IP"
|
"subListen" = "監聽 IP"
|
||||||
"subListenDesc" = "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP)"
|
"subListenDesc" = "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP)"
|
||||||
"subPort" = "監聽埠"
|
"subPort" = "監聽埠"
|
||||||
|
|
|
||||||
6
x-ui.sh
6
x-ui.sh
|
|
@ -1226,7 +1226,7 @@ ssl_cert_issue_for_ip() {
|
||||||
local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null"
|
local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null"
|
||||||
|
|
||||||
# issue the certificate for IP with shortlived profile
|
# issue the certificate for IP with shortlived profile
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||||
~/.acme.sh/acme.sh --issue \
|
~/.acme.sh/acme.sh --issue \
|
||||||
${domain_args} \
|
${domain_args} \
|
||||||
--standalone \
|
--standalone \
|
||||||
|
|
@ -1391,7 +1391,7 @@ ssl_cert_issue() {
|
||||||
LOGI "Will use port: ${WebPort} to issue certificates. Please make sure this port is open."
|
LOGI "Will use port: ${WebPort} to issue certificates. Please make sure this port is open."
|
||||||
|
|
||||||
# issue the certificate
|
# issue the certificate
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Issuing certificate failed, please check logs."
|
LOGE "Issuing certificate failed, please check logs."
|
||||||
|
|
@ -1518,7 +1518,7 @@ ssl_cert_issue_CF() {
|
||||||
LOGD "Your registered email address is: ${CF_AccountEmail}"
|
LOGD "Your registered email address is: ${CF_AccountEmail}"
|
||||||
|
|
||||||
# Set the default CA to Let's Encrypt
|
# Set the default CA to Let's Encrypt
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Default CA, Let'sEncrypt fail, script exiting..."
|
LOGE "Default CA, Let'sEncrypt fail, script exiting..."
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue