diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a744b9f3..bf332fb3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,7 +89,7 @@ jobs: cd x-ui/bin # 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 wget -q ${Xray_URL}Xray-linux-64.zip unzip Xray-linux-64.zip @@ -187,7 +187,7 @@ jobs: cd x-ui\bin # 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" Expand-Archive -Path "Xray-windows-64.zip" -DestinationPath . Remove-Item "Xray-windows-64.zip" diff --git a/DockerInit.sh b/DockerInit.sh index 176e1261..d8acad77 100755 --- a/DockerInit.sh +++ b/DockerInit.sh @@ -27,7 +27,7 @@ case $1 in esac mkdir -p 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" rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat mv xray "xray-linux-${FNAME}" diff --git a/config/version b/config/version index c3e2bd4c..0d367c86 100644 --- a/config/version +++ b/config/version @@ -1 +1 @@ -2.8.8 \ No newline at end of file +2.8.9 \ No newline at end of file diff --git a/go.mod b/go.mod index 7d602b11..4f031534 100644 --- a/go.mod +++ b/go.mod @@ -16,11 +16,11 @@ require ( github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/pelletier/go-toml/v2 v2.2.4 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/valyala/fasthttp v1.69.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 golang.org/x/crypto v0.47.0 golang.org/x/sys v0.40.0 @@ -35,11 +35,10 @@ require ( github.com/andybalholm/brotli v1.2.0 // indirect github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 // indirect github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.14.2 // indirect - github.com/bytedance/sonic/loader v0.4.0 // indirect - github.com/cloudflare/circl v1.6.2 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect + github.com/cloudflare/circl v1.6.3 // 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/gabriel-vasile/mimetype v1.4.12 // 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/mattn/go-isatty v0.0.20 // 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/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/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // 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/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/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect github.com/tklauser/go-sysconf v0.3.16 // indirect github.com/tklauser/numcpus v0.11.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.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/fastjson v1.6.7 // indirect github.com/vishvananda/netlink v1.3.1 // indirect @@ -98,8 +94,8 @@ require ( golang.org/x/tools v0.41.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // 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 - 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 ) diff --git a/go.sum b/go.sum index e711c95d..59e6c7f3 100644 --- a/go.sum +++ b/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/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/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= -github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= -github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= -github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= -github.com/cloudflare/circl v1.6.2 h1:hL7VBpHHKzrV5WTfHCaBsgx/HGbBYlgrwvNXEVDYYsQ= -github.com/cloudflare/circl v1.6.2/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= +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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= 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-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= 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.70/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 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/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/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= -github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= +github.com/pires/go-proxyproto v0.9.2 h1:H1UdHn695zUVVmB0lQ354lOWHOy6TZSpzBl3tgN0s1U= +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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/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/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= -github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 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.14/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= +github.com/sagernet/sing v0.7.18 h1:iZHkaru1/MoHugx3G+9S3WG4owMewKO/KvieE2Pzk4E= +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/go.mod h1:TE/Z6401Pi8tgr0nBZcM/xawAI6u3F6TTbz4nH/qw+8= -github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4= -github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= -github.com/shirou/gopsutil/v4 v4.25.12 h1:e7PvW/0RmJ8p8vPGJH4jvNkOyLmbkXgXW4m6ZPic6CY= -github.com/shirou/gopsutil/v4 v4.25.12/go.mod h1:EivAfP5x2EhLp2ovdpKSozecVXn1TmuG7SMzs/Wh4PU= +github.com/shirou/gopsutil/v4 v4.26.1 h1:TOkEyriIXk2HX9d4isZJtbjXbEjf5qyKPAzbzY0JWSo= +github.com/shirou/gopsutil/v4 v4.26.1/go.mod h1:medLI9/UNAb0dOI9Q3/7yWSqKkj00u+1tgY8nvv41pc= 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/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.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.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.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 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/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= 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/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 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/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/xray-core v1.260118.0 h1:RJtgIbQ3ykFRcH1CKeoCgQ5WvhsMFu+lnvLF/fFHagE= -github.com/xtls/xray-core v1.260118.0/go.mod h1:A5k7TXE2KfAjT8dAq6Ir4mMP1q0OTh+8VMmUdqWMQpg= +github.com/xtls/xray-core v1.260131.0 h1:gPBykLhUvRZ8sfubNerkwWqV3c15UtmSYQG2cgKqrV4= +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/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= 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= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= 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-20260114163908-3f89685c29c3/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= +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/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= 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/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-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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= 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/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= -gvisor.dev/gvisor v0.0.0-20260109181451-4be7c433dae2 h1:fr6L00yGG2RP5NMea6njWpdC+bm+cMdFClrSpaicp1c= -gvisor.dev/gvisor v0.0.0-20260109181451-4be7c433dae2/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q= +gvisor.dev/gvisor v0.0.0-20260122175437-89a5d21be8f0 h1:Lk6hARj5UPY47dBep70OD/TIMwikJ5fGUGX0Rm3Xigk= +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/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= diff --git a/install.sh b/install.sh index 62c8ea37..852e128a 100644 --- a/install.sh +++ b/install.sh @@ -147,7 +147,7 @@ setup_ssl_certificate() { echo -e "${green}Issuing SSL certificate for ${domain}...${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 if [ $? -ne 0 ]; then @@ -272,7 +272,7 @@ setup_ip_certificate() { # Issue certificate with shortlived profile 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 \ ${domain_args} \ @@ -414,7 +414,7 @@ ssl_cert_issue() { systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null # 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 if [ $? -ne 0 ]; then echo -e "${red}Issuing certificate failed, please check logs.${plain}" diff --git a/sub/sub.go b/sub/sub.go index 0605c8b9..1dcd9601 100644 --- a/sub/sub.go +++ b/sub/sub.go @@ -153,6 +153,31 @@ func (s *Server) initRouter() (*gin.Engine, error) { 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 engine.Use(locale.LocalizerMiddleware()) @@ -231,7 +256,8 @@ func (s *Server) initRouter() (*gin.Engine, error) { s.sub = NewSUBController( 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 } diff --git a/sub/subController.go b/sub/subController.go index ec574d6e..7653a4e1 100644 --- a/sub/subController.go +++ b/sub/subController.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "fmt" "strings" + "strconv" "github.com/mhsanaei/3x-ui/v2/config" @@ -12,12 +13,17 @@ import ( // SUBController handles HTTP requests for subscription links and JSON configurations. type SUBController struct { - subTitle string - subPath string - subJsonPath string - jsonEnabled bool - subEncrypt bool - updateInterval string + subTitle string + subSupportUrl string + subProfileUrl string + subAnnounce string + subEnableRouting bool + subRoutingRules string + subPath string + subJsonPath string + jsonEnabled bool + subEncrypt bool + updateInterval string subService *SubService subJsonService *SubJsonService @@ -38,18 +44,28 @@ func NewSUBController( jsonMux string, jsonRules string, subTitle string, + subSupportUrl string, + subProfileUrl string, + subAnnounce string, + subEnableRouting bool, + subRoutingRules string, ) *SUBController { sub := NewSubService(showInfo, rModel) a := &SUBController{ - subTitle: subTitle, - subPath: subPath, - subJsonPath: jsonPath, - jsonEnabled: jsonEnabled, - subEncrypt: encrypt, - updateInterval: update, + subTitle: subTitle, + subSupportUrl: subSupportUrl, + subProfileUrl: subProfileUrl, + subAnnounce: subAnnounce, + subEnableRouting: subEnableRouting, + subRoutingRules: subRoutingRules, + subPath: subPath, + subJsonPath: jsonPath, + jsonEnabled: jsonEnabled, + subEncrypt: encrypt, + updateInterval: update, - subService: sub, - subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub), + subService: sub, + subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub), } a.initRouter(g) return a @@ -127,7 +143,7 @@ func (a *SUBController) subs(c *gin.Context) { // Add headers 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 { 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 { c.String(400, "Error!") } else { - // 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) } } // 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("Profile-Update-Interval", updateInterval) 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) } diff --git a/update.sh b/update.sh index 3781c365..0c4bb725 100755 --- a/update.sh +++ b/update.sh @@ -173,7 +173,7 @@ setup_ssl_certificate() { echo -e "${green}Issuing SSL certificate for ${domain}...${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 if [ $? -ne 0 ]; then @@ -297,7 +297,7 @@ setup_ip_certificate() { # Issue certificate with shortlived profile 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 \ ${domain_args} \ @@ -437,7 +437,7 @@ ssl_cert_issue() { systemctl stop x-ui 2>/dev/null || rc-service x-ui stop 2>/dev/null # 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 if [ $? -ne 0 ]; then echo -e "${red}Issuing certificate failed, please check logs.${plain}" diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js index e5fe368f..3f3f8831 100644 --- a/web/assets/js/model/inbound.js +++ b/web/assets/js/model/inbound.js @@ -318,15 +318,13 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass { class KcpStreamSettings extends XrayCommonClass { constructor( - mtu = 1250, - tti = 50, + mtu = 1350, + tti = 20, uplinkCapacity = 5, downlinkCapacity = 20, congestion = false, - readBufferSize = 2, - writeBufferSize = 2, - type = 'none', - seed = RandomUtil.randomSeq(10), + readBufferSize = 1, + writeBufferSize = 1, ) { super(); this.mtu = mtu; @@ -336,8 +334,6 @@ class KcpStreamSettings extends XrayCommonClass { this.congestion = congestion; this.readBuffer = readBufferSize; this.writeBuffer = writeBufferSize; - this.type = type; - this.seed = seed; } static fromJson(json = {}) { @@ -349,8 +345,6 @@ class KcpStreamSettings extends XrayCommonClass { json.congestion, json.readBufferSize, json.writeBufferSize, - ObjectUtil.isEmpty(json.header) ? 'none' : json.header.type, - json.seed, ); } @@ -363,10 +357,6 @@ class KcpStreamSettings extends XrayCommonClass { congestion: this.congestion, readBufferSize: this.readBuffer, writeBufferSize: this.writeBuffer, - header: { - type: this.type, - }, - seed: this.seed, }; } } @@ -497,6 +487,19 @@ class xHTTPStreamSettings extends XrayCommonClass { noSSEHeader = false, xPaddingBytes = "100-1000", mode = MODE_OPTION.AUTO, + xPaddingObfsMode = false, + xPaddingKey = '', + xPaddingHeader = '', + xPaddingPlacement = '', + xPaddingMethod = '', + uplinkHTTPMethod = '', + sessionPlacement = '', + sessionKey = '', + seqPlacement = '', + seqKey = '', + uplinkDataPlacement = '', + uplinkDataKey = '', + uplinkChunkSize = 0, ) { super(); this.path = path; @@ -508,6 +511,19 @@ class xHTTPStreamSettings extends XrayCommonClass { this.noSSEHeader = noSSEHeader; this.xPaddingBytes = xPaddingBytes; 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) { @@ -529,6 +545,19 @@ class xHTTPStreamSettings extends XrayCommonClass { json.noSSEHeader, json.xPaddingBytes, 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, xPaddingBytes: this.xPaddingBytes, 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, cipherSuites = '', rejectUnknownSni = false, - verifyPeerCertInNames = ['dns.google', 'cloudflare-dns.com'], disableSystemRoot = false, enableSessionResumption = false, certificates = [new TlsStreamSettings.Cert()], @@ -569,7 +610,6 @@ class TlsStreamSettings extends XrayCommonClass { this.maxVersion = maxVersion; this.cipherSuites = cipherSuites; this.rejectUnknownSni = rejectUnknownSni; - this.verifyPeerCertInNames = Array.isArray(verifyPeerCertInNames) ? verifyPeerCertInNames.join(",") : verifyPeerCertInNames; this.disableSystemRoot = disableSystemRoot; this.enableSessionResumption = enableSessionResumption; this.certs = certificates; @@ -603,7 +643,6 @@ class TlsStreamSettings extends XrayCommonClass { json.maxVersion, json.cipherSuites, json.rejectUnknownSni, - json.verifyPeerCertInNames, json.disableSystemRoot, json.enableSessionResumption, certs, @@ -621,7 +660,6 @@ class TlsStreamSettings extends XrayCommonClass { maxVersion: this.maxVersion, cipherSuites: this.cipherSuites, rejectUnknownSni: this.rejectUnknownSni, - verifyPeerCertInNames: this.verifyPeerCertInNames.split(","), disableSystemRoot: this.disableSystemRoot, enableSessionResumption: this.enableSessionResumption, 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 { constructor(network = 'tcp', security = 'none', @@ -941,6 +1024,7 @@ class StreamSettings extends XrayCommonClass { grpcSettings = new GrpcStreamSettings(), httpupgradeSettings = new HTTPUpgradeStreamSettings(), xhttpSettings = new xHTTPStreamSettings(), + finalmask = { udp: [] }, sockopt = undefined, ) { super(); @@ -955,9 +1039,23 @@ class StreamSettings extends XrayCommonClass { this.grpc = grpcSettings; this.httpupgrade = httpupgradeSettings; this.xhttp = xhttpSettings; + this.finalmask = finalmask; 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() { return this.security === "tls"; } @@ -992,6 +1090,14 @@ class StreamSettings extends XrayCommonClass { } 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( json.network, json.security, @@ -1004,6 +1110,7 @@ class StreamSettings extends XrayCommonClass { GrpcStreamSettings.fromJson(json.grpcSettings), HTTPUpgradeStreamSettings.fromJson(json.httpupgradeSettings), xHTTPStreamSettings.fromJson(json.xhttpSettings), + finalmask, SockoptStreamSettings.fromJson(json.sockopt), ); } @@ -1022,6 +1129,9 @@ class StreamSettings extends XrayCommonClass { grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.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, }; } @@ -1947,7 +2057,9 @@ Inbound.VLESSSettings = class extends Inbound.Settings { 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; } @@ -2509,7 +2621,7 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass { Inbound.WireguardSettings = class extends XrayCommonClass { constructor( protocol, - mtu = 1250, + mtu = 1420, secretKey = Wireguard.generateKeypair().privateKey, peers = [new Inbound.WireguardSettings.Peer()], noKernelTun = false diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js index c6529560..3e0dd0d4 100644 --- a/web/assets/js/model/outbound.js +++ b/web/assets/js/model/outbound.js @@ -165,15 +165,13 @@ class TcpStreamSettings extends CommonClass { class KcpStreamSettings extends CommonClass { constructor( - mtu = 1250, - tti = 50, + mtu = 1350, + tti = 20, uplinkCapacity = 5, downlinkCapacity = 20, congestion = false, - readBufferSize = 2, - writeBufferSize = 2, - type = 'none', - seed = '', + readBufferSize = 1, + writeBufferSize = 1, ) { super(); this.mtu = mtu; @@ -183,8 +181,6 @@ class KcpStreamSettings extends CommonClass { this.congestion = congestion; this.readBuffer = readBufferSize; this.writeBuffer = writeBufferSize; - this.type = type; - this.seed = seed; } static fromJson(json = {}) { @@ -196,8 +192,6 @@ class KcpStreamSettings extends CommonClass { json.congestion, json.readBufferSize, json.writeBufferSize, - ObjectUtil.isEmpty(json.header) ? 'none' : json.header.type, - json.seed, ); } @@ -210,10 +204,6 @@ class KcpStreamSettings extends CommonClass { congestion: this.congestion, readBufferSize: this.readBuffer, writeBufferSize: this.writeBuffer, - header: { - type: this.type, - }, - seed: this.seed, }; } } @@ -357,6 +347,8 @@ class TlsStreamSettings extends CommonClass { fingerprint = '', allowInsecure = false, echConfigList = '', + verifyPeerCertByName = 'cloudflare-dns.com', + pinnedPeerCertSha256 = '', ) { super(); this.serverName = serverName; @@ -364,6 +356,8 @@ class TlsStreamSettings extends CommonClass { this.fingerprint = fingerprint; this.allowInsecure = allowInsecure; this.echConfigList = echConfigList; + this.verifyPeerCertByName = verifyPeerCertByName; + this.pinnedPeerCertSha256 = pinnedPeerCertSha256; } static fromJson(json = {}) { @@ -373,6 +367,8 @@ class TlsStreamSettings extends CommonClass { json.fingerprint, json.allowInsecure, json.echConfigList, + json.verifyPeerCertByName, + json.pinnedPeerCertSha256, ); } @@ -382,7 +378,9 @@ class TlsStreamSettings extends CommonClass { alpn: this.alpn, fingerprint: this.fingerprint, 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', down = '0', udphopPort = '', - udphopInterval = 30, + udphopIntervalMin = 30, + udphopIntervalMax = 30, initStreamReceiveWindow = 8388608, maxStreamReceiveWindow = 8388608, initConnectionReceiveWindow = 20971520, @@ -450,7 +449,8 @@ class HysteriaStreamSettings extends CommonClass { this.up = up; this.down = down; this.udphopPort = udphopPort; - this.udphopInterval = udphopInterval; + this.udphopIntervalMin = udphopIntervalMin; + this.udphopIntervalMax = udphopIntervalMax; this.initStreamReceiveWindow = initStreamReceiveWindow; this.maxStreamReceiveWindow = maxStreamReceiveWindow; this.initConnectionReceiveWindow = initConnectionReceiveWindow; @@ -462,10 +462,18 @@ class HysteriaStreamSettings extends CommonClass { static fromJson(json = {}) { let udphopPort = ''; - let udphopInterval = 30; + let udphopIntervalMin = 30; + let udphopIntervalMax = 30; if (json.udphop) { 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( json.version, @@ -474,7 +482,8 @@ class HysteriaStreamSettings extends CommonClass { json.up, json.down, udphopPort, - udphopInterval, + udphopIntervalMin, + udphopIntervalMax, json.initStreamReceiveWindow, json.maxStreamReceiveWindow, json.initConnectionReceiveWindow, @@ -503,7 +512,8 @@ class HysteriaStreamSettings extends CommonClass { if (this.udphopPort) { result.udphop = { port: this.udphopPort, - interval: this.udphopInterval + intervalMin: this.udphopIntervalMin, + intervalMax: this.udphopIntervalMax }; } return result; @@ -558,27 +568,49 @@ class SockoptStreamSettings extends CommonClass { } } -class UdpMask extends CommonClass { - constructor(type = 'salamander', password = '') { +class FinalMask extends CommonClass { + constructor(type = 'salamander', settings = {}) { super(); 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 = {}) { - return new UdpMask( - json.type, - json.settings?.password || '' + return new FinalMask( + json.type || 'salamander', + json.settings || {} ); } toJson() { - return { - type: this.type, - settings: { - password: this.password - } + const result = { + type: this.type }; + // 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(), xhttpSettings = new xHTTPStreamSettings(), hysteriaSettings = new HysteriaStreamSettings(), - udpmasks = [], + finalmask = { udp: [] }, sockopt = undefined, ) { super(); @@ -610,16 +642,21 @@ class StreamSettings extends CommonClass { this.httpupgrade = httpupgradeSettings; this.xhttp = xhttpSettings; this.hysteria = hysteriaSettings; - this.udpmasks = udpmasks; + this.finalmask = finalmask; this.sockopt = sockopt; } - addUdpMask() { - this.udpmasks.push(new UdpMask()); + addUdpMask(type = 'salamander') { + if (!this.finalmask.udp) { + this.finalmask.udp = []; + } + this.finalmask.udp.push(new FinalMask(type)); } delUdpMask(index) { - this.udpmasks.splice(index, 1); + if (this.finalmask.udp) { + this.finalmask.udp.splice(index, 1); + } } get isTls() { @@ -639,7 +676,16 @@ class StreamSettings extends CommonClass { } 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( json.network, json.security, @@ -652,7 +698,7 @@ class StreamSettings extends CommonClass { HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings), xHTTPStreamSettings.fromJson(json.xhttpSettings), HysteriaStreamSettings.fromJson(json.hysteriaSettings), - udpmasks, + finalmask, SockoptStreamSettings.fromJson(json.sockopt), ); } @@ -671,7 +717,9 @@ class StreamSettings extends CommonClass { httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, xhttpSettings: network === 'xhttp' ? this.xhttp.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, }; } @@ -996,7 +1044,15 @@ class Outbound extends CommonClass { stream.hysteria.up = urlParams.get('up') ?? '0'; stream.hysteria.down = urlParams.get('down') ?? '0'; 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 if (urlParams.has('initStreamReceiveWindow')) { @@ -1285,11 +1341,14 @@ Outbound.VLESSSettings = class extends CommonClass { flow: this.flow, encryption: this.encryption, }; - if (this.testpre > 0) { - result.testpre = this.testpre; - } - if (this.testseed && this.testseed.length >= 4) { - result.testseed = this.testseed; + // Only include Vision settings when flow is set + if (this.flow && this.flow !== '') { + if (this.testpre > 0) { + result.testpre = this.testpre; + } + if (this.testseed && this.testseed.length >= 4) { + result.testseed = this.testseed; + } } return result; } @@ -1422,7 +1481,7 @@ Outbound.HttpSettings = class extends CommonClass { Outbound.WireguardSettings = class extends CommonClass { constructor( - mtu = 1250, + mtu = 1420, secretKey = '', address = [''], workers = 2, diff --git a/web/assets/js/model/reality_targets.js b/web/assets/js/model/reality_targets.js index cfe65afc..d17bcfc1 100644 --- a/web/assets/js/model/reality_targets.js +++ b/web/assets/js/model/reality_targets.js @@ -1,18 +1,15 @@ // List of popular services for VLESS Reality Target/SNI randomization const REALITY_TARGETS = [ - { target: 'www.icloud.com:443', sni: 'www.icloud.com,icloud.com' }, - { target: 'www.apple.com:443', sni: 'www.apple.com,apple.com' }, - { target: 'www.tesla.com:443', sni: 'www.tesla.com,tesla.com' }, - { target: 'www.sony.com:443', sni: 'www.sony.com,sony.com' }, - { target: 'www.nvidia.com:443', sni: 'www.nvidia.com,nvidia.com' }, - { target: 'www.amd.com:443', sni: 'www.amd.com,amd.com' }, - { target: 'azure.microsoft.com:443', sni: 'azure.microsoft.com,www.azure.com' }, - { target: 'aws.amazon.com:443', sni: 'aws.amazon.com,amazon.com' }, - { target: 'www.bing.com:443', sni: 'www.bing.com,bing.com' }, - { target: 'www.oracle.com:443', sni: 'www.oracle.com,oracle.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' } + { target: 'www.apple.com:443', sni: 'www.apple.com' }, + { target: 'www.icloud.com:443', sni: 'www.icloud.com' }, + { target: 'www.amazon.com:443', sni: 'www.amazon.com' }, + { target: 'aws.amazon.com:443', sni: 'aws.amazon.com' }, + { target: 'www.oracle.com:443', sni: 'www.oracle.com' }, + { target: 'www.nvidia.com:443', sni: 'www.nvidia.com' }, + { target: 'www.amd.com:443', sni: 'www.amd.com' }, + { target: 'www.intel.com:443', sni: 'www.intel.com' }, + { target: 'www.tesla.com:443', sni: 'www.tesla.com' }, + { target: 'www.sony.com:443', sni: 'www.sony.com' } ]; /** @@ -28,4 +25,3 @@ function getRandomRealityTarget() { sni: selected.sni }; } - diff --git a/web/assets/js/model/setting.js b/web/assets/js/model/setting.js index 53ffae1a..af80a63e 100644 --- a/web/assets/js/model/setting.js +++ b/web/assets/js/model/setting.js @@ -29,6 +29,11 @@ class AllSetting { this.subEnable = true; this.subJsonEnable = false; this.subTitle = ""; + this.subSupportUrl = ""; + this.subProfileUrl = ""; + this.subAnnounce = ""; + this.subEnableRouting = true; + this.subRoutingRules = ""; this.subListen = ""; this.subPort = 2096; this.subPath = "/sub/"; diff --git a/web/entity/entity.go b/web/entity/entity.go index 42e2df85..40294925 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -57,6 +57,11 @@ type AllSetting struct { SubEnable bool `json:"subEnable" form:"subEnable"` // Enable subscription server SubJsonEnable bool `json:"subJsonEnable" form:"subJsonEnable"` // Enable JSON subscription endpoint 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 SubPort int `json:"subPort" form:"subPort"` // Subscription server port SubPath string `json:"subPath" form:"subPath"` // Base path for subscription URLs diff --git a/web/html/form/outbound.html b/web/html/form/outbound.html index 688a3564..4df095d4 100644 --- a/web/html/form/outbound.html +++ b/web/html/form/outbound.html @@ -407,21 +407,6 @@ - + diff --git a/web/html/form/stream/stream_finalmask.html b/web/html/form/stream/stream_finalmask.html new file mode 100644 index 00000000..4ed7d6a1 --- /dev/null +++ b/web/html/form/stream/stream_finalmask.html @@ -0,0 +1,70 @@ +{{define "form/streamFinalMask"}} + + + + + + + +{{end}} diff --git a/web/html/form/stream/stream_kcp.html b/web/html/form/stream/stream_kcp.html index 50794574..11f89ebd 100644 --- a/web/html/form/stream/stream_kcp.html +++ b/web/html/form/stream/stream_kcp.html @@ -1,48 +1,32 @@ {{define "form/streamKCP"}} - - - - None - SRTP - uTP - WeChat - DTLS 1.2 - WireGuard - DNS - - - - - - + - + - + - - + + - + - + - + {{end}} diff --git a/web/html/form/stream/stream_settings.html b/web/html/form/stream/stream_settings.html index f6b17cc6..5b00ef25 100644 --- a/web/html/form/stream/stream_settings.html +++ b/web/html/form/stream/stream_settings.html @@ -1,8 +1,10 @@ {{define "form/streamSettings"}} - + - TCP (RAW) mKCP @@ -48,4 +50,10 @@ + + + {{end}} diff --git a/web/html/form/stream/stream_xhttp.html b/web/html/form/stream/stream_xhttp.html index 4b3052b6..447612c9 100644 --- a/web/html/form/stream/stream_xhttp.html +++ b/web/html/form/stream/stream_xhttp.html @@ -1,5 +1,6 @@ {{define "form/streamXHTTP"}} - + @@ -7,38 +8,138 @@ - + - + - + - + - [[ key ]] + [[ key + ]] - - + + - - + + - - + + + + + + + + + Default (POST) + POST + PUT + GET (packet-up only) + + + + + Default (path) + path + header + cookie + query + + + + + + + + Default (path) + path + header + cookie + query + + + + + + + + Default (body) + body + header + query + + + + + + + + diff --git a/web/html/form/tls_settings.html b/web/html/form/tls_settings.html index 3723130e..b2368d4f 100644 --- a/web/html/form/tls_settings.html +++ b/web/html/form/tls_settings.html @@ -1,11 +1,13 @@ {{define "form/tlsSettings"}} - + {{ i18n "none" }} - Reality + Reality TLS @@ -16,33 +18,44 @@ - - Auto - [[ value ]] + + Auto + [[ + value ]] - - [[ key ]] + [[ key + ]] - - [[ key ]] + [[ key + ]] - - None - [[ key ]] + None + [[ key + ]] - - [[ alpn ]] + + [[ alpn + ]] @@ -57,21 +70,25 @@ - - - @@ -99,8 +117,10 @@ - - [[ key ]] + + [[ key + ]] @@ -108,20 +128,22 @@ - + - + - - [[ key ]] - + + [[ + key ]] + - Get New ECH Cert + Get New + ECH Cert Clear diff --git a/web/html/settings/panel/subscription/general.html b/web/html/settings/panel/subscription/general.html index e65b2738..5d83aa37 100644 --- a/web/html/settings/panel/subscription/general.html +++ b/web/html/settings/panel/subscription/general.html @@ -15,13 +15,6 @@ - - - - - @@ -78,6 +71,50 @@ + {{ i18n "pages.xray.basicTemplate"}} + + + + + + + + + + + + + + + + + + + + + {{ i18n "pages.xray.advancedTemplate"}} (Happ) + + + + + + + + + + diff --git a/web/service/server.go b/web/service/server.go index fae1c66a..793f541b 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -567,7 +567,7 @@ func (s *ServerService) GetXrayVersions() ([]string, error) { 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) } } diff --git a/web/service/setting.go b/web/service/setting.go index 56db346d..3fa37f44 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -53,6 +53,11 @@ var defaultValueMap = map[string]string{ "subEnable": "true", "subJsonEnable": "false", "subTitle": "", + "subSupportUrl": "", + "subProfileUrl": "", + "subAnnounce": "", + "subEnableRouting": "true", + "subRoutingRules": "", "subListen": "", "subPort": "2096", "subPath": "/sub/", @@ -459,6 +464,26 @@ func (s *SettingService) GetSubTitle() (string, error) { 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) { return s.getString("subListen") } diff --git a/web/translation/translate.ar_EG.toml b/web/translation/translate.ar_EG.toml index 06a3e937..6d75d196 100644 --- a/web/translation/translate.ar_EG.toml +++ b/web/translation/translate.ar_EG.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "تمكين/تعطيل نقطة نهاية اشتراك JSON بشكل مستقل." "subTitle" = "عنوان الاشتراك" "subTitleDesc" = "العنوان اللي هيظهر في عميل VPN" +"subSupportUrl" = "رابط الدعم" +"subSupportUrlDesc" = "رابط الدعم الفني المعروض في عميل VPN" +"subProfileUrl" = "رابط الملف الشخصي" +"subProfileUrlDesc" = "رابط لموقعك الإلكتروني يظهر في عميل VPN" +"subAnnounce" = "إعلان" +"subAnnounceDesc" = "نص الإعلان المعروض في عميل VPN" +"subEnableRouting" = "تفعيل التوجيه" +"subEnableRoutingDesc" = "إعداد عام لتمكين التوجيه (Routing) في عميل VPN. (فقط لـ Happ)" +"subRoutingRules" = "قواعد التوجيه" +"subRoutingRulesDesc" = "قواعد التوجيه العامة لعميل VPN. (فقط لـ Happ)" "subListen" = "IP الاستماع" "subListenDesc" = "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)" "subPort" = "بورت الاستماع" diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 6da9185b..244e6f2c 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "Enable/Disable the JSON subscription endpoint independently." "subTitle" = "Subscription Title" "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" "subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)" "subPort" = "Listen Port" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index ccd5beff..b0dde898 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "Habilitar/Deshabilitar el endpoint de suscripción JSON de forma independiente." "subTitle" = "Título de la Suscripción" "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" "subListenDesc" = "Dejar en blanco por defecto para monitorear todas las IPs." "subPort" = "Puerto de Suscripción" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index a25484d1..1eda5fb5 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "فعال/غیرفعال‌سازی مستقل نقطه دسترسی سابسکریپشن JSON." "subTitle" = "عنوان اشتراک" "subTitleDesc" = "عنوان نمایش داده شده در کلاینت VPN" +"subSupportUrl" = "آدرس پشتیبانی" +"subSupportUrlDesc" = "لینک پشتیبانی فنی که در کلاینت VPN نمایش داده می‌شود" +"subProfileUrl" = "آدرس پروفایل" +"subProfileUrlDesc" = "لینک وب‌سایت شما که در کلاینت VPN نمایش داده می‌شود" +"subAnnounce" = "اعلان" +"subAnnounceDesc" = "متن اعلانی که در کلاینت VPN نمایش داده می‌شود" +"subEnableRouting" = "فعال‌سازی مسیریابی" +"subEnableRoutingDesc" = "تنظیمات سراسری برای فعال‌سازی مسیریابی در کلاینت VPN. (فقط برای Happ)" +"subRoutingRules" = "قوانین مسیریابی" +"subRoutingRulesDesc" = "قوانین مسیریابی سراسری برای کلاینت VPN. (فقط برای Happ)" "subListen" = "آدرس آی‌پی" "subListenDesc" = "آدرس آی‌پی برای سرویس سابسکریپشن. برای گوش دادن به‌تمام آی‌پی‌ها خالی‌بگذارید" "subPort" = "پورت" diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml index 7bd7aae0..8804ef04 100644 --- a/web/translation/translate.id_ID.toml +++ b/web/translation/translate.id_ID.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "Aktifkan/Nonaktifkan endpoint langganan JSON secara mandiri." "subTitle" = "Judul Langganan" "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" "subListenDesc" = "Alamat IP untuk layanan langganan. (biarkan kosong untuk mendengarkan semua IP)" "subPort" = "Port Pendengar" diff --git a/web/translation/translate.ja_JP.toml b/web/translation/translate.ja_JP.toml index bc9c294a..8bba24c0 100644 --- a/web/translation/translate.ja_JP.toml +++ b/web/translation/translate.ja_JP.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "JSON サブスクリプションのエンドポイントを個別に有効/無効にする。" "subTitle" = "サブスクリプションタイトル" "subTitleDesc" = "VPNクライアントに表示されるタイトル" +"subSupportUrl" = "サポートURL" +"subSupportUrlDesc" = "VPNクライアントに表示されるテクニカルサポートへのリンク" +"subProfileUrl" = "プロフィールURL" +"subProfileUrlDesc" = "VPNクライアントに表示されるWebサイトへのリンク" +"subAnnounce" = "お知らせ" +"subAnnounceDesc" = "VPNクライアントに表示されるお知らせのテキスト" +"subEnableRouting" = "ルーティングを有効化" +"subEnableRoutingDesc" = "VPNクライアントでルーティングを有効にするためのグローバル設定。(Happのみ)" +"subRoutingRules" = "ルーティングルール" +"subRoutingRulesDesc" = "VPNクライアントのグローバルルーティングルール。(Happのみ)" "subListen" = "監視IP" "subListenDesc" = "サブスクリプションサービスが監視するIPアドレス(空白にするとすべてのIPを監視)" "subPort" = "監視ポート" diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml index 86b3b97b..1b173e6e 100644 --- a/web/translation/translate.pt_BR.toml +++ b/web/translation/translate.pt_BR.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "Ativar/Desativar o endpoint de assinatura JSON de forma independente." "subTitle" = "Título da Assinatura" "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" "subListenDesc" = "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)" "subPort" = "Porta de Escuta" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index 63fbbbb6..895734f5 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -373,7 +373,17 @@ "subEnableDesc" = "Функция подписки с отдельной конфигурацией" "subJsonEnable" = "Включить/отключить JSON-эндпоинт подписки независимо." "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" "subListenDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все IP-адреса" "subPort" = "Порт подписки" diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml index 25b8de9b..50639358 100644 --- a/web/translation/translate.tr_TR.toml +++ b/web/translation/translate.tr_TR.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "JSON abonelik uç noktasını bağımsız olarak Etkinleştir/Devre Dışı bırak." "subTitle" = "Abonelik Başlığı" "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" "subListenDesc" = "Abonelik hizmeti için IP adresi. (tüm IP'leri dinlemek için boş bırakın)" "subPort" = "Dinleme Portu" diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml index c32854ae..54d45889 100644 --- a/web/translation/translate.uk_UA.toml +++ b/web/translation/translate.uk_UA.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "Увімкнути/вимкнути JSON-кінець підписки незалежно." "subTitle" = "Назва Підписки" "subTitleDesc" = "Назва, яка відображається у VPN-клієнті" +"subSupportUrl" = "URL підтримки" +"subSupportUrlDesc" = "Посилання на технічну підтримку, що відображається у VPN-клієнті" +"subProfileUrl" = "URL профілю" +"subProfileUrlDesc" = "Посилання на ваш вебсайт, що відображається у VPN-клієнті" +"subAnnounce" = "Оголошення" +"subAnnounceDesc" = "Текст оголошення, що відображається у VPN-клієнті" +"subEnableRouting" = "Увімкнути маршрутизацію" +"subEnableRoutingDesc" = "Глобальне налаштування для увімкнення маршрутизації у VPN-клієнті. (Тільки для Happ)" +"subRoutingRules" = "Правила маршрутизації" +"subRoutingRulesDesc" = "Глобальні правила маршрутизації для VPN-клієнта. (Тільки для Happ)" "subListen" = "Слухати IP" "subListenDesc" = "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)" "subPort" = "Слухати порт" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index 1187548e..3fa63bb1 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "Bật/Tắt điểm cuối đăng ký JSON độc lập." "subTitle" = "Tiêu đề Đăng ký" "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" "subListenDesc" = "Mặc định để trống để nghe tất cả các IP" "subPort" = "Cổng gói đăng ký" diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml index 923cc21b..d6b82b93 100644 --- a/web/translation/translate.zh_CN.toml +++ b/web/translation/translate.zh_CN.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "单独启用/禁用 JSON 订阅端点。" "subTitle" = "订阅标题" "subTitleDesc" = "在VPN客户端中显示的标题" +"subSupportUrl" = "支持链接" +"subSupportUrlDesc" = "VPN 客户端中显示的技术支持链接" +"subProfileUrl" = "个人资料链接" +"subProfileUrlDesc" = "VPN 客户端中显示的网站链接" +"subAnnounce" = "公告" +"subAnnounceDesc" = "VPN 客户端中显示的公告文本" +"subEnableRouting" = "启用路由" +"subEnableRoutingDesc" = "在 VPN 客户端中启用路由的全局设置。(僅限 Happ)" +"subRoutingRules" = "路由規則" +"subRoutingRulesDesc" = "VPN 用戶端的全域路由規則。(僅限 Happ)" "subListen" = "监听 IP" "subListenDesc" = "订阅服务监听的 IP 地址(留空表示监听所有 IP)" "subPort" = "监听端口" diff --git a/web/translation/translate.zh_TW.toml b/web/translation/translate.zh_TW.toml index df32d742..616f2322 100644 --- a/web/translation/translate.zh_TW.toml +++ b/web/translation/translate.zh_TW.toml @@ -374,6 +374,16 @@ "subJsonEnable" = "獨立啟用/停用 JSON 訂閱端點。" "subTitle" = "訂閱標題" "subTitleDesc" = "在VPN客戶端中顯示的標題" +"subSupportUrl" = "支援連結" +"subSupportUrlDesc" = "VPN 用戶端中顯示的技術支援連結" +"subProfileUrl" = "個人資料連結" +"subProfileUrlDesc" = "VPN 用戶端中顯示的網站連結" +"subAnnounce" = "公告" +"subAnnounceDesc" = "VPN 用戶端中顯示的公告文字" +"subEnableRouting" = "啟用路由" +"subEnableRoutingDesc" = "在 VPN 用戶端中啟用路由的全域設定。(僅限 Happ)" +"subRoutingRules" = "路由規則" +"subRoutingRulesDesc" = "VPN 用戶端的全域路由規則。(僅限 Happ)" "subListen" = "監聽 IP" "subListenDesc" = "訂閱服務監聽的 IP 地址(留空表示監聽所有 IP)" "subPort" = "監聽埠" diff --git a/x-ui.sh b/x-ui.sh index 42dbb601..22d02358 100644 --- a/x-ui.sh +++ b/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" # 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 \ ${domain_args} \ --standalone \ @@ -1391,7 +1391,7 @@ ssl_cert_issue() { LOGI "Will use port: ${WebPort} to issue certificates. Please make sure this port is open." # 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 if [ $? -ne 0 ]; then LOGE "Issuing certificate failed, please check logs." @@ -1518,7 +1518,7 @@ ssl_cert_issue_CF() { LOGD "Your registered email address is: ${CF_AccountEmail}" # 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 LOGE "Default CA, Let'sEncrypt fail, script exiting..." exit 1