diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e77af2c3..6a54d399 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,7 @@ on: - 'go.mod' - 'go.sum' - 'x-ui.service.debian' + - 'x-ui.service.arch' - 'x-ui.service.rhel' jobs: @@ -80,6 +81,7 @@ jobs: mkdir x-ui cp xui-release x-ui/ cp x-ui.service.debian x-ui/ + cp x-ui.service.arch x-ui/ cp x-ui.service.rhel x-ui/ cp x-ui.sh x-ui/ cp -r lib x-ui/ @@ -88,7 +90,7 @@ jobs: cd x-ui/bin # Download dependencies - Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.12.8/" + Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v26.1.18/" if [ "${{ matrix.platform }}" == "amd64" ]; then wget -q ${Xray_URL}Xray-linux-64.zip unzip Xray-linux-64.zip @@ -186,7 +188,7 @@ jobs: cd x-ui\bin # Download Xray for Windows - $Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v25.12.8/" + $Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v26.1.18/" 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" @@ -224,4 +226,4 @@ jobs: file: x-ui-windows-amd64.zip asset_name: x-ui-windows-amd64.zip overwrite: true - prerelease: true \ No newline at end of file + prerelease: true diff --git a/config/version b/config/version index d45e55c2..c3e2bd4c 100644 --- a/config/version +++ b/config/version @@ -1 +1 @@ -2.8.7 \ No newline at end of file +2.8.8 \ No newline at end of file diff --git a/go.mod b/go.mod index 126a109b..7d602b11 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/mhsanaei/3x-ui/v2 -go 1.25.5 +go 1.25.6 require ( github.com/gin-contrib/gzip v1.2.5 @@ -11,7 +11,7 @@ require ( github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 github.com/joho/godotenv v1.5.1 - github.com/mymmrac/telego v1.4.0 + github.com/mymmrac/telego v1.5.0 github.com/nicksnyder/go-i18n/v2 v2.6.1 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/pelletier/go-toml/v2 v2.2.4 @@ -20,11 +20,11 @@ require ( 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.251208.0 + github.com/xtls/xray-core v1.260118.0 go.uber.org/atomic v1.11.0 - golang.org/x/crypto v0.46.0 - golang.org/x/sys v0.39.0 - golang.org/x/text v0.32.0 + golang.org/x/crypto v0.47.0 + golang.org/x/sys v0.40.0 + golang.org/x/text v0.33.0 google.golang.org/grpc v1.78.0 gorm.io/driver/sqlite v1.6.0 gorm.io/gorm v1.31.1 @@ -33,6 +33,7 @@ require ( require ( github.com/Azure/go-ntlmssp v0.1.0 // indirect 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 @@ -47,7 +48,7 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.30.1 // indirect - github.com/goccy/go-yaml v1.19.1 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect github.com/google/btree v1.1.3 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect @@ -57,20 +58,20 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/juju/ratelimit v1.0.2 // indirect - github.com/klauspost/compress v1.18.2 // indirect + github.com/klauspost/compress v1.18.3 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect 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.69 // indirect + github.com/miekg/dns v1.1.70 // 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/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.58.0 // indirect - github.com/refraction-networking/utls v1.8.1 // 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 @@ -89,15 +90,16 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect golang.org/x/arch v0.23.0 // indirect - golang.org/x/mod v0.31.0 // indirect - golang.org/x/net v0.48.0 // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect + golang.org/x/mod v0.32.0 // indirect + golang.org/x/net v0.49.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.40.0 // indirect + 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-20251222181119-0a764e51fe1b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260114163908-3f89685c29c3 // indirect google.golang.org/protobuf v1.36.11 // indirect - gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c // indirect + gvisor.dev/gvisor v0.0.0-20260109181451-4be7c433dae2 // indirect lukechampine.com/blake3 v1.4.1 // indirect ) diff --git a/go.sum b/go.sum index 70c47d08..e711c95d 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktp github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 h1:bSq8n+gX4oO/qnM3MKf4kroW75n+phO9Qp6nigJKZ1E= +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= @@ -57,8 +59,8 @@ github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy0 github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE= -github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -106,8 +108,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI= github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= -github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= -github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= +github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -122,15 +124,15 @@ 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.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= -github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= +github.com/miekg/dns v1.1.70 h1:DZ4u2AV35VJxdD9Fo9fIWm119BsQL5cZU1cQ9s0LkqA= +github.com/miekg/dns v1.1.70/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= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mymmrac/telego v1.4.0 h1:z74W5lfOTgLplQXuZPjDsRvvvI0iQatO2gp/XZz7s3I= -github.com/mymmrac/telego v1.4.0/go.mod h1:u9fKXZSOCOdMj6K0U69fQqeAvDE+2RGkHKkDksijp3o= +github.com/mymmrac/telego v1.5.0 h1:VjBDZcSpEQim1Y3JX2WCsF/PJqOA2DKfZknXUvtKCnw= +github.com/mymmrac/telego v1.5.0/go.mod h1:MDYHIeT68tURdcwH4SNCQQ+0xBC3u6wOcH2hBpa4Ip0= github.com/nicksnyder/go-i18n/v2 v2.6.1 h1:JDEJraFsQE17Dut9HFDHzCoAWGEQJom5s0TRd17NIEQ= github.com/nicksnyder/go-i18n/v2 v2.6.1/go.mod h1:Vee0/9RD3Quc/NmwEjzzD7VTZ+Ir7QbXocrkhOzmUKA= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= @@ -147,10 +149,10 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug= -github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= -github.com/refraction-networking/utls v1.8.1 h1:yNY1kapmQU8JeM1sSw2H2asfTIwWxIkrMJI0pRUOCAo= -github.com/refraction-networking/utls v1.8.1/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +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= @@ -203,8 +205,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.251208.0 h1:9jIXi+9KXnfmT5esSYNf9VAQlQkaAP8bG413B0eyAes= -github.com/xtls/xray-core v1.251208.0/go.mod h1:kclzboEF0g6VBrp9/NXm8C0Aj64SDBt52OfthH1LSr4= +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/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= @@ -231,12 +233,14 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= -golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= -golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -245,22 +249,22 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= -golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= -golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A= 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-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +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/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= @@ -278,7 +282,7 @@ 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-20250503011706-39ed1f5ac29c h1:m/r7OM+Y2Ty1sgBQ7Qb27VgIMBW8ZZhT4gLnUyDIhzI= -gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g= +gvisor.dev/gvisor v0.0.0-20260109181451-4be7c433dae2 h1:fr6L00yGG2RP5NMea6njWpdC+bm+cMdFClrSpaicp1c= +gvisor.dev/gvisor v0.0.0-20260109181451-4be7c433dae2/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 00111845..80663c0f 100644 --- a/install.sh +++ b/install.sh @@ -825,6 +825,15 @@ install_x-ui() { fi fi ;; + arch | manjaro | parch) + if [ -f "x-ui.service.arch" ]; then + echo -e "${green}Found x-ui.service.arch in extracted files, installing...${plain}" + cp -f x-ui.service.arch ${xui_service}/x-ui.service >/dev/null 2>&1 + if [[ $? -eq 0 ]]; then + service_installed=true + fi + fi + ;; *) if [ -f "x-ui.service.rhel" ]; then echo -e "${green}Found x-ui.service.rhel in extracted files, installing...${plain}" @@ -844,6 +853,9 @@ install_x-ui() { ubuntu | debian | armbian) curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.debian >/dev/null 2>&1 ;; + arch | manjaro | parch) + curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.arch >/dev/null 2>&1 + ;; *) curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.rhel >/dev/null 2>&1 ;; diff --git a/update.sh b/update.sh index 91c37c37..58206984 100755 --- a/update.sh +++ b/update.sh @@ -737,6 +737,7 @@ update_x-ui() { rm ${xui_folder} -f >/dev/null 2>&1 rm ${xui_folder}/x-ui.service -f >/dev/null 2>&1 rm ${xui_folder}/x-ui.service.debian -f >/dev/null 2>&1 + rm ${xui_folder}/x-ui.service.arch -f >/dev/null 2>&1 rm ${xui_folder}/x-ui.service.rhel -f >/dev/null 2>&1 rm ${xui_folder}/x-ui -f >/dev/null 2>&1 rm ${xui_folder}/x-ui.sh -f >/dev/null 2>&1 @@ -819,6 +820,15 @@ update_x-ui() { fi fi ;; + arch | manjaro | parch) + if [ -f "x-ui.service.arch" ]; then + echo -e "${green}Installing arch-like systemd unit...${plain}" + cp -f x-ui.service.arch ${xui_service}/x-ui.service >/dev/null 2>&1 + if [[ $? -eq 0 ]]; then + service_installed=true + fi + fi + ;; *) if [ -f "x-ui.service.rhel" ]; then echo -e "${green}Installing rhel-like systemd unit...${plain}" @@ -837,6 +847,9 @@ update_x-ui() { ubuntu | debian | armbian) ${curl_bin} -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.debian >/dev/null 2>&1 ;; + arch | manjaro | parch) + ${curl_bin} -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.arch >/dev/null 2>&1 + ;; *) ${curl_bin} -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.rhel >/dev/null 2>&1 ;; diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js index 8d4b6819..e5fe368f 100644 --- a/web/assets/js/model/inbound.js +++ b/web/assets/js/model/inbound.js @@ -7,6 +7,7 @@ const Protocols = { MIXED: 'mixed', HTTP: 'http', WIREGUARD: 'wireguard', + TUN: 'tun', }; const SSMethods = { @@ -1739,6 +1740,7 @@ Inbound.Settings = class extends XrayCommonClass { case Protocols.MIXED: return new Inbound.MixedSettings(protocol); case Protocols.HTTP: return new Inbound.HttpSettings(protocol); case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol); + case Protocols.TUN: return new Inbound.TunSettings(protocol); default: return null; } } @@ -1753,6 +1755,7 @@ Inbound.Settings = class extends XrayCommonClass { case Protocols.MIXED: return Inbound.MixedSettings.fromJson(json); case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json); case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json); + case Protocols.TUN: return Inbound.TunSettings.fromJson(json); default: return null; } } @@ -2586,3 +2589,34 @@ Inbound.WireguardSettings.Peer = class extends XrayCommonClass { }; } }; + +Inbound.TunSettings = class extends Inbound.Settings { + constructor( + protocol, + name = 'xray0', + mtu = 1500, + userLevel = 0 + ) { + super(protocol); + this.name = name; + this.mtu = mtu; + this.userLevel = userLevel; + } + + static fromJson(json = {}) { + return new Inbound.TunSettings( + Protocols.TUN, + json.name ?? 'xray0', + json.mtu ?? json.MTU ?? 1500, + json.userLevel ?? 0 + ); + } + + toJson() { + return { + name: this.name || 'xray0', + mtu: this.mtu || 1500, + userLevel: this.userLevel || 0, + }; + } +}; diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js index 6fe34982..c6529560 100644 --- a/web/assets/js/model/outbound.js +++ b/web/assets/js/model/outbound.js @@ -8,7 +8,8 @@ const Protocols = { Shadowsocks: "shadowsocks", Socks: "socks", HTTP: "http", - Wireguard: "wireguard" + Wireguard: "wireguard", + Hysteria: "hysteria" }; const SSMethods = { @@ -424,6 +425,90 @@ class RealityStreamSettings extends CommonClass { }; } }; + +class HysteriaStreamSettings extends CommonClass { + constructor( + version = 2, + auth = '', + congestion = '', + up = '0', + down = '0', + udphopPort = '', + udphopInterval = 30, + initStreamReceiveWindow = 8388608, + maxStreamReceiveWindow = 8388608, + initConnectionReceiveWindow = 20971520, + maxConnectionReceiveWindow = 20971520, + maxIdleTimeout = 30, + keepAlivePeriod = 0, + disablePathMTUDiscovery = false + ) { + super(); + this.version = version; + this.auth = auth; + this.congestion = congestion; + this.up = up; + this.down = down; + this.udphopPort = udphopPort; + this.udphopInterval = udphopInterval; + this.initStreamReceiveWindow = initStreamReceiveWindow; + this.maxStreamReceiveWindow = maxStreamReceiveWindow; + this.initConnectionReceiveWindow = initConnectionReceiveWindow; + this.maxConnectionReceiveWindow = maxConnectionReceiveWindow; + this.maxIdleTimeout = maxIdleTimeout; + this.keepAlivePeriod = keepAlivePeriod; + this.disablePathMTUDiscovery = disablePathMTUDiscovery; + } + + static fromJson(json = {}) { + let udphopPort = ''; + let udphopInterval = 30; + if (json.udphop) { + udphopPort = json.udphop.port || ''; + udphopInterval = json.udphop.interval || 30; + } + return new HysteriaStreamSettings( + json.version, + json.auth, + json.congestion, + json.up, + json.down, + udphopPort, + udphopInterval, + json.initStreamReceiveWindow, + json.maxStreamReceiveWindow, + json.initConnectionReceiveWindow, + json.maxConnectionReceiveWindow, + json.maxIdleTimeout, + json.keepAlivePeriod, + json.disablePathMTUDiscovery + ); + } + + toJson() { + const result = { + version: this.version, + auth: this.auth, + congestion: this.congestion, + up: this.up, + down: this.down, + initStreamReceiveWindow: this.initStreamReceiveWindow, + maxStreamReceiveWindow: this.maxStreamReceiveWindow, + initConnectionReceiveWindow: this.initConnectionReceiveWindow, + maxConnectionReceiveWindow: this.maxConnectionReceiveWindow, + maxIdleTimeout: this.maxIdleTimeout, + keepAlivePeriod: this.keepAlivePeriod, + disablePathMTUDiscovery: this.disablePathMTUDiscovery + }; + if (this.udphopPort) { + result.udphop = { + port: this.udphopPort, + interval: this.udphopInterval + }; + } + return result; + } +}; class SockoptStreamSettings extends CommonClass { constructor( dialerProxy = "", @@ -473,6 +558,30 @@ class SockoptStreamSettings extends CommonClass { } } +class UdpMask extends CommonClass { + constructor(type = 'salamander', password = '') { + super(); + this.type = type; + this.password = password; + } + + static fromJson(json = {}) { + return new UdpMask( + json.type, + json.settings?.password || '' + ); + } + + toJson() { + return { + type: this.type, + settings: { + password: this.password + } + }; + } +} + class StreamSettings extends CommonClass { constructor( network = 'tcp', @@ -485,6 +594,8 @@ class StreamSettings extends CommonClass { grpcSettings = new GrpcStreamSettings(), httpupgradeSettings = new HttpUpgradeStreamSettings(), xhttpSettings = new xHTTPStreamSettings(), + hysteriaSettings = new HysteriaStreamSettings(), + udpmasks = [], sockopt = undefined, ) { super(); @@ -498,9 +609,19 @@ class StreamSettings extends CommonClass { this.grpc = grpcSettings; this.httpupgrade = httpupgradeSettings; this.xhttp = xhttpSettings; + this.hysteria = hysteriaSettings; + this.udpmasks = udpmasks; this.sockopt = sockopt; } + addUdpMask() { + this.udpmasks.push(new UdpMask()); + } + + delUdpMask(index) { + this.udpmasks.splice(index, 1); + } + get isTls() { return this.security === 'tls'; } @@ -518,6 +639,7 @@ class StreamSettings extends CommonClass { } static fromJson(json = {}) { + const udpmasks = json.udpmasks ? json.udpmasks.map(mask => UdpMask.fromJson(mask)) : []; return new StreamSettings( json.network, json.security, @@ -529,6 +651,8 @@ class StreamSettings extends CommonClass { GrpcStreamSettings.fromJson(json.grpcSettings), HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings), xHTTPStreamSettings.fromJson(json.xhttpSettings), + HysteriaStreamSettings.fromJson(json.hysteriaSettings), + udpmasks, SockoptStreamSettings.fromJson(json.sockopt), ); } @@ -546,6 +670,8 @@ class StreamSettings extends CommonClass { grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, 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, sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined, }; } @@ -609,7 +735,8 @@ class Outbound extends CommonClass { } canEnableTls() { - if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false; + if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks, Protocols.Hysteria].includes(this.protocol)) return false; + if (this.protocol === Protocols.Hysteria) return this.stream.network === 'hysteria'; return ["tcp", "ws", "http", "grpc", "httpupgrade", "xhttp"].includes(this.stream.network); } @@ -634,7 +761,7 @@ class Outbound extends CommonClass { } canEnableStream() { - return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol); + return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks, Protocols.Hysteria].includes(this.protocol); } canEnableMux() { @@ -673,7 +800,8 @@ class Outbound extends CommonClass { Protocols.Trojan, Protocols.Shadowsocks, Protocols.Socks, - Protocols.HTTP + Protocols.HTTP, + Protocols.Hysteria ].includes(this.protocol); } @@ -722,6 +850,9 @@ class Outbound extends CommonClass { case Protocols.Trojan: case 'ss': return this.fromParamLink(link); + case 'hysteria2': + case Protocols.Hysteria: + return this.fromHysteriaLink(link); default: return null; } @@ -842,6 +973,62 @@ class Outbound extends CommonClass { remark = remark.length > 0 ? remark.substring(1) : 'out-' + protocol + '-' + port; return new Outbound(remark, protocol, settings, stream); } + + static fromHysteriaLink(link) { + // Parse hysteria2://password@address:port[?param1=value1¶m2=value2...][#remarks] + const regex = /^hysteria2?:\/\/([^@]+)@([^:?#]+):(\d+)([^#]*)(#.*)?$/; + const match = link.match(regex); + + if (!match) return null; + + let [, password, address, port, params, hash] = match; + port = parseInt(port); + + // Parse URL parameters if present + let urlParams = new URLSearchParams(params); + + // Create stream settings with hysteria network + let stream = new StreamSettings('hysteria', 'none'); + + // Set hysteria stream settings + stream.hysteria.auth = password; + stream.hysteria.congestion = urlParams.get('congestion') ?? ''; + 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'); + + // Optional QUIC parameters + if (urlParams.has('initStreamReceiveWindow')) { + stream.hysteria.initStreamReceiveWindow = parseInt(urlParams.get('initStreamReceiveWindow')); + } + if (urlParams.has('maxStreamReceiveWindow')) { + stream.hysteria.maxStreamReceiveWindow = parseInt(urlParams.get('maxStreamReceiveWindow')); + } + if (urlParams.has('initConnectionReceiveWindow')) { + stream.hysteria.initConnectionReceiveWindow = parseInt(urlParams.get('initConnectionReceiveWindow')); + } + if (urlParams.has('maxConnectionReceiveWindow')) { + stream.hysteria.maxConnectionReceiveWindow = parseInt(urlParams.get('maxConnectionReceiveWindow')); + } + if (urlParams.has('maxIdleTimeout')) { + stream.hysteria.maxIdleTimeout = parseInt(urlParams.get('maxIdleTimeout')); + } + if (urlParams.has('keepAlivePeriod')) { + stream.hysteria.keepAlivePeriod = parseInt(urlParams.get('keepAlivePeriod')); + } + if (urlParams.has('disablePathMTUDiscovery')) { + stream.hysteria.disablePathMTUDiscovery = urlParams.get('disablePathMTUDiscovery') === 'true'; + } + + // Create settings + let settings = new Outbound.HysteriaSettings(address, port, 2); + + // Extract remark from hash + let remark = hash ? decodeURIComponent(hash.substring(1)) : `out-hysteria-${port}`; + + return new Outbound(remark, Protocols.Hysteria, settings, stream); + } } Outbound.Settings = class extends CommonClass { @@ -862,6 +1049,7 @@ Outbound.Settings = class extends CommonClass { case Protocols.Socks: return new Outbound.SocksSettings(); case Protocols.HTTP: return new Outbound.HttpSettings(); case Protocols.Wireguard: return new Outbound.WireguardSettings(); + case Protocols.Hysteria: return new Outbound.HysteriaSettings(); default: return null; } } @@ -878,6 +1066,7 @@ Outbound.Settings = class extends CommonClass { case Protocols.Socks: return Outbound.SocksSettings.fromJson(json); case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json); case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json); + case Protocols.Hysteria: return Outbound.HysteriaSettings.fromJson(json); default: return null; } } @@ -1324,4 +1513,30 @@ Outbound.WireguardSettings.Peer = class extends CommonClass { keepAlive: this.keepAlive ?? undefined, }; } +}; + +Outbound.HysteriaSettings = class extends CommonClass { + constructor(address = '', port = 443, version = 2) { + super(); + this.address = address; + this.port = port; + this.version = version; + } + + static fromJson(json = {}) { + if (Object.keys(json).length === 0) return new Outbound.HysteriaSettings(); + return new Outbound.HysteriaSettings( + json.address, + json.port, + json.version + ); + } + + toJson() { + return { + address: this.address, + port: this.port, + version: this.version + }; + } }; \ No newline at end of file diff --git a/web/assets/js/websocket.js b/web/assets/js/websocket.js index 5b8a3948..ccafef87 100644 --- a/web/assets/js/websocket.js +++ b/web/assets/js/websocket.js @@ -14,10 +14,12 @@ class WebSocketClient { } connect() { - if (this.ws && this.ws.readyState === WebSocket.OPEN) { + if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { return; } + this.shouldReconnect = true; + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; // Ensure basePath ends with '/' for proper URL construction let basePath = this.basePath || ''; @@ -97,7 +99,10 @@ class WebSocketClient { if (!this.listeners.has(event)) { this.listeners.set(event, []); } - this.listeners.get(event).push(callback); + const callbacks = this.listeners.get(event); + if (!callbacks.includes(callback)) { + callbacks.push(callback); + } } off(event, callback) { diff --git a/web/html/form/inbound.html b/web/html/form/inbound.html index fdd381b0..8b59dc28 100644 --- a/web/html/form/inbound.html +++ b/web/html/form/inbound.html @@ -1,6 +1,7 @@ {{define "form/inbound"}} - + @@ -9,8 +10,10 @@ - - [[ p ]] + + [[ p + ]] @@ -28,7 +31,8 @@ - + @@ -41,29 +45,42 @@ - + - - {{ i18n "pages.inbounds.periodicTrafficReset.never" }} - {{ i18n "pages.inbounds.periodicTrafficReset.daily" }} - {{ i18n "pages.inbounds.periodicTrafficReset.weekly" }} - {{ i18n "pages.inbounds.periodicTrafficReset.monthly" }} + + {{ i18n + "pages.inbounds.periodicTrafficReset.never" }} + {{ i18n + "pages.inbounds.periodicTrafficReset.daily" }} + {{ i18n + "pages.inbounds.periodicTrafficReset.weekly" + }} + {{ i18n + "pages.inbounds.periodicTrafficReset.monthly" + }} @@ -71,16 +88,20 @@ - - @@ -126,6 +147,11 @@ {{template "form/wireguard"}} + + +