Merge branch 'main' into main

This commit is contained in:
Ho3ein 2024-02-28 01:26:24 +03:30 committed by GitHub
commit 5da569b3e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 1172 additions and 325 deletions

View file

@ -11,15 +11,15 @@ jobs:
steps: steps:
- name: Check out the code - name: Check out the code
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3
- name: Login to GHCR - name: Login to GHCR
uses: docker/login-action@v3.0.0 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@ -27,12 +27,12 @@ jobs:
- name: Docker meta - name: Docker meta
id: meta id: meta
uses: docker/metadata-action@v5.5.1 uses: docker/metadata-action@v5
with: with:
images: ghcr.io/${{ github.repository }} images: ghcr.io/${{ github.repository }}
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v5.1.0 uses: docker/build-push-action@v5
with: with:
context: . context: .
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}

View file

@ -20,10 +20,10 @@ jobs:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5.0.0 uses: actions/setup-go@v5
with: with:
go-version: '1.22' go-version: '1.22'
@ -77,7 +77,7 @@ jobs:
cd x-ui/bin cd x-ui/bin
# Download dependencies # Download dependencies
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.7/" Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.8/"
if [ "${{ matrix.platform }}" == "amd64" ]; then if [ "${{ matrix.platform }}" == "amd64" ]; then
wget ${Xray_URL}Xray-linux-64.zip wget ${Xray_URL}Xray-linux-64.zip
unzip Xray-linux-64.zip unzip Xray-linux-64.zip
@ -117,7 +117,7 @@ jobs:
run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
- name: Upload files to GH release - name: Upload files to GH release
uses: svenstaro/upload-release-action@2.9.0 uses: svenstaro/upload-release-action@v2
with: with:
repo_token: ${{ secrets.GITHUB_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }} tag: ${{ github.ref }}

View file

@ -27,7 +27,7 @@ case $1 in
esac esac
mkdir -p build/bin mkdir -p build/bin
cd build/bin cd build/bin
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.7/Xray-linux-${ARCH}.zip" wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.8/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip" unzip "Xray-linux-${ARCH}.zip"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
mv xray "xray-linux-${FNAME}" mv xray "xray-linux-${FNAME}"

25
go.mod
View file

@ -14,7 +14,7 @@ require (
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v3 v3.24.1 github.com/shirou/gopsutil/v3 v3.24.1
github.com/valyala/fasthttp v1.52.0 github.com/valyala/fasthttp v1.52.0
github.com/xtls/xray-core v1.8.7 github.com/xtls/xray-core v1.8.8
go.uber.org/atomic v1.11.0 go.uber.org/atomic v1.11.0
golang.org/x/text v0.14.0 golang.org/x/text v0.14.0
google.golang.org/grpc v1.62.0 google.golang.org/grpc v1.62.0
@ -40,7 +40,7 @@ require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect github.com/google/pprof v0.0.0-20240225044709-fd706174c886 // indirect
github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/context v1.1.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.2.2 // indirect github.com/gorilla/sessions v1.2.2 // indirect
@ -49,23 +49,22 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.6 // indirect github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.19 // indirect github.com/mattn/go-sqlite3 v1.14.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.13.2 // indirect github.com/onsi/ginkgo/v2 v2.15.0 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/quic-go/quic-go v0.41.0 // indirect
github.com/quic-go/quic-go v0.40.1 // indirect github.com/refraction-networking/utls v1.6.3 // indirect
github.com/refraction-networking/utls v1.6.0 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sagernet/sing v0.3.0 // indirect github.com/sagernet/sing v0.3.2 // indirect
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect github.com/sagernet/sing-shadowsocks v0.2.6 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
@ -85,15 +84,15 @@ require (
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.6.0 // indirect golang.org/x/arch v0.6.0 // indirect
golang.org/x/crypto v0.19.0 // indirect golang.org/x/crypto v0.19.0 // indirect
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/mod v0.14.0 // indirect golang.org/x/mod v0.15.0 // indirect
golang.org/x/net v0.21.0 // indirect golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect golang.org/x/sys v0.17.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.16.1 // indirect golang.org/x/tools v0.18.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
google.golang.org/protobuf v1.32.0 // indirect google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect

62
go.sum
View file

@ -88,8 +88,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@ -111,8 +111,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8= github.com/google/pprof v0.0.0-20240225044709-fd706174c886 h1:JSJUTZTQT1Gzb2ROdAKOY3HwzBYcclS2GgumhMfHqjw=
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20240225044709-fd706174c886/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@ -138,11 +138,11 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@ -169,8 +169,8 @@ github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbW
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -183,10 +183,10 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
@ -208,12 +208,10 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/quic-go v0.41.0 h1:aD8MmHfgqTURWNJy48IYFg2OnxwHT3JL7ahGs73lb4k=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/quic-go/quic-go v0.41.0/go.mod h1:qCkNjqczPEvgsOnxZ0eCD14lv+B2LHlFAB++CNOh9hA=
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q= github.com/refraction-networking/utls v1.6.3 h1:MFOfRN35sSx6K5AZNIoESsBuBxS2LCgRilRIdHb6fDc=
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= github.com/refraction-networking/utls v1.6.3/go.mod h1:yil9+7qSl+gBwJqztoQseO6Pr3h62pQoY1lXiNR/FPs=
github.com/refraction-networking/utls v1.6.0 h1:X5vQMqVx7dY7ehxxqkFER/W6DSjy8TMqSItXm8hRDYQ=
github.com/refraction-networking/utls v1.6.0/go.mod h1:kHJ6R9DFFA0WsRgBM35iiDku4O7AqPR6y79iuzW7b10=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
@ -223,8 +221,8 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sagernet/sing v0.3.0 h1:PIDVFZHnQAAYRL1UYqNM+0k5s8f/tb1lUW6UDcQiOc8= github.com/sagernet/sing v0.3.2 h1:CwWcxUBPkMvwgfe2/zUgY5oHG9qOL8Aob/evIFYK9jo=
github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g= github.com/sagernet/sing v0.3.2/go.mod h1:qHySJ7u8po9DABtMYEkNBcOumx7ZZJf/fbv2sfTkNHE=
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s= github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM= github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
@ -303,8 +301,8 @@ github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1Y
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI=
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
github.com/xtls/xray-core v1.8.7 h1:lb8O1l3/eAg3YAXA6tLm5M6N7BsX8wxW9sJLjU3dHkA= github.com/xtls/xray-core v1.8.8 h1:6ApBa5pNkPZ+I7jyJDZod3v5sadizS/BZr0pW7zcs8o=
github.com/xtls/xray-core v1.8.7/go.mod h1:9rFpflfQbgFeH1VKJw7yUmEy7myOyDCgNXXl0bmmyOo= github.com/xtls/xray-core v1.8.8/go.mod h1:Zp33A8cxnhP5Kt6nguQrMgNH4A/tgq7LE8cBedeNje8=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
@ -326,13 +324,13 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -392,8 +390,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
@ -411,8 +409,8 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=

View file

@ -50,7 +50,6 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) {
} }
func (a *SUBController) subs(c *gin.Context) { func (a *SUBController) subs(c *gin.Context) {
println(c.Request.Header["User-Agent"][0])
subId := c.Param("subid") subId := c.Param("subid")
host := strings.Split(c.Request.Host, ":")[0] host := strings.Split(c.Request.Host, ":")[0]
subs, header, err := a.subService.GetSubs(subId, host) subs, header, err := a.subService.GetSubs(subId, host)
@ -76,7 +75,6 @@ func (a *SUBController) subs(c *gin.Context) {
} }
func (a *SUBController) subJsons(c *gin.Context) { func (a *SUBController) subJsons(c *gin.Context) {
println(c.Request.Header["User-Agent"][0])
subId := c.Param("subid") subId := c.Param("subid")
host := strings.Split(c.Request.Host, ":")[0] host := strings.Split(c.Request.Host, ":")[0]
jsonSub, header, err := a.subJsonService.GetJson(subId, host) jsonSub, header, err := a.subJsonService.GetJson(subId, host)

View file

@ -189,7 +189,7 @@ func (s *SubJsonService) streamData(stream string) map[string]interface{} {
delete(streamSettings, "sockopt") delete(streamSettings, "sockopt")
if s.fragmanet != "" { if s.fragmanet != "" {
streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "TcpNoDelay": true}`) streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "tcpNoDelay": true}`)
} }
// remove proxy protocol // remove proxy protocol
@ -218,7 +218,7 @@ func (s *SubJsonService) tlsData(tData map[string]interface{}) map[string]interf
tlsData["serverName"] = tData["serverName"] tlsData["serverName"] = tData["serverName"]
tlsData["alpn"] = tData["alpn"] tlsData["alpn"] = tData["alpn"]
if allowInsecure, ok := tlsClientSettings["allowInsecure"].(string); ok { if allowInsecure, ok := tlsClientSettings["allowInsecure"].(bool); ok {
tlsData["allowInsecure"] = allowInsecure tlsData["allowInsecure"] = allowInsecure
} }
if fingerprint, ok := tlsClientSettings["fingerprint"].(string); ok { if fingerprint, ok := tlsClientSettings["fingerprint"].(string); ok {

View file

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

View file

@ -34,6 +34,11 @@ const supportLangs = [
value: 'id-ID', value: 'id-ID',
icon: '🇮🇩', icon: '🇮🇩',
}, },
{
name: 'Український',
value: 'uk-UA',
icon: '🇺🇦',
},
]; ];
function getLang() { function getLang() {

View file

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

View file

@ -71,7 +71,7 @@
background-color: #0f2d32; background-color: #0f2d32;
} }
.dark #login { .dark #login {
background-color: var(--dark-color-surface-100); background-color: #101113;
} }
.dark h1 { .dark h1 {
color: rgba(255, 255, 255); color: rgba(255, 255, 255);
@ -199,7 +199,7 @@
z-index: -1; z-index: -1;
} }
.dark .waves-header { .dark .waves-header {
background-color: var(--dark-color-background); background-color: #0a2227;
} }
.waves-inner-header { .waves-inner-header {
height: 50vh; height: 50vh;
@ -374,92 +374,96 @@
</style> </style>
<body> <body>
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme"> <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
<transition name="list" appear> <transition name="list" appear>
<a-layout-content class="under" style="min-height: 0;"> <a-layout-content class="under" style="min-height: 0;">
<div class="waves-header"> <div class="waves-header">
<div class="waves-inner-header"></div> <div class="waves-inner-header"></div>
<svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"> <svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
<defs> viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
<path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" /> <defs>
</defs> <path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />
<g class="parallax"> </defs>
<use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(0, 135, 113, 0.08)" /> <g class="parallax">
<use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(0, 135, 113, 0.08)" /> <use xlink:href="#gentle-wave" x="48" y="0" fill="rgba(0, 135, 113, 0.08)" />
<use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(0, 135, 113, 0.08)" /> <use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(0, 135, 113, 0.08)" />
<use xlink:href="#gentle-wave" x="48" y="7" fill="#c7ebe2" /> <use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(0, 135, 113, 0.08)" />
</g> <use xlink:href="#gentle-wave" x="48" y="7" fill="#c7ebe2" />
</svg> </g>
</div> </svg>
<a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto;"> </div>
<a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;"> <a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto;">
<a-row type="flex" justify="center"> <a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;">
<a-col style="width: 100%;"> <a-row type="flex" justify="center">
<h1 class="title headline zoom"> <a-col style="width: 100%;">
<span class="words-wrapper"> <h1 class="title headline zoom">
<b class="is-visible">{{ i18n "pages.login.title" }}</b> <span class="words-wrapper">
<b>3X-UI</b> <b class="is-visible">{{ i18n "pages.login.title" }}</b>
</span> <b>Hello!</b>
</h1> </span>
</a-col> </h1>
</a-row>
<a-row type="flex" justify="center">
<a-col span="24">
<a-form>
<a-form-item>
<a-input v-model.trim="user.username" placeholder='{{ i18n "username" }}'
@keydown.enter.native="login" autofocus>
<a-icon slot="prefix" type="user" style="font-size: 16px;"/>
</a-input>
</a-form-item>
<a-form-item>
<password-input icon="lock" v-model.trim="user.password"
placeholder='{{ i18n "password" }}' @keydown.enter.native="login">
</password-input>
</a-form-item>
<a-form-item v-if="secretEnable">
<password-input icon="key" v-model.trim="user.loginSecret"
placeholder='{{ i18n "secretToken" }}' @keydown.enter.native="login">
</password-input>
</a-input>
</a-form-item>
<a-form-item>
<a-row justify="center" class="centered">
<div class="wave-btn-bg wave-btn-bg-cl" :style="loading ? { width: '52px' } : { display: 'inline-block' }">
<a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined">
[[ loading ? '' : '{{ i18n "login" }}' ]]
</a-button>
</div>
</a-row>
</a-form-item>
<a-form-item>
<a-row justify="center" class="centered">
<a-col :span="24">
<a-select ref="selectLang" v-model="lang" @change="setLang(lang)" style="width: 150px;" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option :value="l.value" label="English" v-for="l in supportLangs">
<span role="img" aria-label="l.name" v-text="l.icon"></span>
&nbsp;&nbsp;<span v-text="l.name"></span>
</a-select-option>
</a-select>
</a-col>
</a-row>
</a-form-item>
<a-form-item>
<a-row justify="center" class="centered">
<a-col>
<a-icon :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>&nbsp;
</a-col>
<a-col>
<theme-switch />
</a-col>
</a-row>
</a-form-item>
</a-form>
</a-col>
</a-row>
</a-col> </a-col>
</a-row> </a-row>
</a-layout-content> <a-row type="flex" justify="center">
</transition> <a-col span="24">
<a-form>
<a-form-item>
<a-input v-model.trim="user.username" placeholder='{{ i18n "username" }}'
@keydown.enter.native="login" autofocus>
<a-icon slot="prefix" type="user" style="font-size: 16px;" />
</a-input>
</a-form-item>
<a-form-item>
<password-input icon="lock" v-model.trim="user.password" placeholder='{{ i18n "password" }}'
@keydown.enter.native="login">
</password-input>
</a-form-item>
<a-form-item v-if="secretEnable">
<password-input icon="key" v-model.trim="user.loginSecret" placeholder='{{ i18n "secretToken" }}'
@keydown.enter.native="login">
</password-input>
</a-input>
</a-form-item>
<a-form-item>
<a-row justify="center" class="centered">
<div class="wave-btn-bg wave-btn-bg-cl"
:style="loading ? { width: '52px' } : { display: 'inline-block' }">
<a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login"
:icon="loading ? 'poweroff' : undefined">
[[ loading ? '' : '{{ i18n "login" }}' ]]
</a-button>
</div>
</a-row>
</a-form-item>
<a-form-item>
<a-row justify="center" class="centered">
<a-col :span="24">
<a-select ref="selectLang" v-model="lang" @change="setLang(lang)" style="width: 150px;"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option :value="l.value" label="English" v-for="l in supportLangs">
<span role="img" aria-label="l.name" v-text="l.icon"></span>
&nbsp;&nbsp;<span v-text="l.name"></span>
</a-select-option>
</a-select>
</a-col>
</a-row>
</a-form-item>
<a-form-item>
<a-row justify="center" class="centered">
<a-col>
<a-icon :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>&nbsp;
</a-col>
<a-col>
<theme-switch />
</a-col>
</a-row>
</a-form-item>
</a-form>
</a-col>
</a-row>
</a-col>
</a-row>
</a-layout-content>
</transition>
</a-layout> </a-layout>
{{template "js" .}} {{template "js" .}}
{{template "component/themeSwitcher" .}} {{template "component/themeSwitcher" .}}

View file

@ -0,0 +1,218 @@
{{define "component/sortableTableTrigger"}}
<a-icon type="drag"
style="cursor: move;"
@mouseup="mouseUpHandler"
@mousedown="mouseDownHandler"
@click="clickHandler" />
{{end}}
{{define "component/sortableTable"}}
<script>
const DRAGGABLE_ROW_CLASS = 'draggable-row';
const findParentRowElement = (el) => {
if (!el || !el.tagName) {
return null;
} else if (el.classList.contains(DRAGGABLE_ROW_CLASS)) {
return el;
} else if (el.parentNode) {
return findParentRowElement(el.parentNode);
} else {
return null;
}
}
Vue.component('a-table-sortable', {
data() {
return {
sortingElementIndex: null,
newElementIndex: null,
};
},
props: ['data-source', 'customRow'],
inheritAttrs: false,
provide() {
const sortable = {}
Object.defineProperty(sortable, "setSortableIndex", {
enumerable: true,
get: () => this.setCurrentSortableIndex,
});
Object.defineProperty(sortable, "resetSortableIndex", {
enumerable: true,
get: () => this.resetSortableIndex,
});
return {
sortable,
}
},
render: function (createElement) {
return createElement(
'a-table',
{
class: {
'ant-table-is-sorting': this.isDragging(),
},
props: {
...this.$attrs,
'data-source': this.records,
customRow: (record, index) => this.customRowRender(record, index),
},
on: this.$listeners,
nativeOn: {
drop: (e) => this.dropHandler(e),
},
scopedSlots: this.$scopedSlots,
},
this.$slots.default,
)
},
created() {
this.$memoSort = {};
},
methods: {
isDragging() {
const currentIndex = this.sortingElementIndex;
return currentIndex !== null && currentIndex !== undefined;
},
resetSortableIndex(e, index) {
this.sortingElementIndex = null;
this.newElementIndex = null;
this.$memoSort = {};
},
setCurrentSortableIndex(e, index) {
this.sortingElementIndex = index;
},
dragStartHandler(e, index) {
if (!this.isDragging()) {
e.preventDefault();
return;
}
},
dragStopHandler(e, index) {
this.resetSortableIndex(e, index);
},
dragOverHandler(e, index) {
if (!this.isDragging()) {
return;
}
e.preventDefault();
const currentIndex = this.sortingElementIndex;
if (index === currentIndex) {
this.newElementIndex = null;
return;
}
const row = findParentRowElement(e.target);
if (!row) {
return;
}
const rect = row.getBoundingClientRect();
const offsetTop = e.pageY - rect.top;
if (offsetTop < rect.height / 2) {
this.newElementIndex = Math.max(index - 1, 0);
} else {
this.newElementIndex = index;
}
},
dropHandler(e) {
if (this.isDragging()) {
this.$emit('onsort', this.sortingElementIndex, this.newElementIndex);
}
},
customRowRender(record, index) {
const parentMethodResult = this.customRow?.(record, index) || {};
const newIndex = this.newElementIndex;
const currentIndex = this.sortingElementIndex;
return {
...parentMethodResult,
attrs: {
...(parentMethodResult?.attrs || {}),
draggable: true,
},
on: {
...(parentMethodResult?.on || {}),
dragstart: (e) => this.dragStartHandler(e, index),
dragend: (e) => this.dragStopHandler(e, index),
dragover: (e) => this.dragOverHandler(e, index),
},
class: {
...(parentMethodResult?.class || {}),
[DRAGGABLE_ROW_CLASS]: true,
['dragging']: this.isDragging()
? (newIndex === null ? index === currentIndex : index === newIndex)
: false,
},
};
}
},
computed: {
records() {
const newIndex = this.newElementIndex;
const currentIndex = this.sortingElementIndex;
if (!this.isDragging() || newIndex === null || currentIndex === newIndex) {
return this.dataSource;
}
if (this.$memoSort.newIndex === newIndex) {
return this.$memoSort.list;
}
let list = [...this.dataSource];
list.splice(newIndex, 0, list.splice(currentIndex, 1)[0]);
this.$memoSort = {
newIndex,
list,
};
return list;
}
}
});
Vue.component('table-sort-trigger', {
template: `{{template "component/sortableTableTrigger"}}`,
props: ['item-index'],
inject: ['sortable'],
methods: {
mouseDownHandler(e) {
if (this.sortable) {
this.sortable.setSortableIndex(e, this.itemIndex);
}
},
mouseUpHandler(e) {
if (this.sortable) {
this.sortable.resetSortableIndex(e, this.itemIndex);
}
},
clickHandler(e) {
e.preventDefault();
},
}
})
</script>
<style>
.ant-table-is-sorting .draggable-row td {
background-color: white !important;
}
.dark .ant-table-is-sorting .draggable-row td {
background-color: var(--dark-color-surface-100) !important;
}
.ant-table-is-sorting .dragging {
opacity: 0.5;
}
.ant-table-is-sorting .dragging .ant-table-row-index {
opacity: 0;
}
</style>
{{end}}

View file

@ -2,7 +2,7 @@
<a-modal id="dns-modal" v-model="dnsModal.visible" :title="dnsModal.title" @ok="dnsModal.ok" <a-modal id="dns-modal" v-model="dnsModal.visible" :title="dnsModal.title" @ok="dnsModal.ok"
:closable="true" :mask-closable="false" :closable="true" :mask-closable="false"
:ok-text="dnsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme"> :ok-text="dnsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.xray.outbound.address" }}'> <a-form-item label='{{ i18n "pages.xray.outbound.address" }}'>
<a-input v-model.trim="dnsModal.dnsServer.address"></a-input> <a-input v-model.trim="dnsModal.dnsServer.address"></a-input>
</a-form-item> </a-form-item>

View file

@ -2,7 +2,7 @@
<a-modal id="fakedns-modal" v-model="fakednsModal.visible" :title="fakednsModal.title" @ok="fakednsModal.ok" <a-modal id="fakedns-modal" v-model="fakednsModal.visible" :title="fakednsModal.title" @ok="fakednsModal.ok"
:closable="true" :mask-closable="false" :closable="true" :mask-closable="false"
:ok-text="fakednsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme"> :ok-text="fakednsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.xray.fakedns.ipPool" }}'> <a-form-item label='{{ i18n "pages.xray.fakedns.ipPool" }}'>
<a-input v-model.trim="fakednsModal.fakeDns.ipPool"></a-input> <a-input v-model.trim="fakednsModal.fakeDns.ipPool"></a-input>
</a-form-item> </a-form-item>

View file

@ -1,6 +1,6 @@
{{define "form/inbound"}} {{define "form/inbound"}}
<!-- base --> <!-- base -->
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "enable" }}'> <a-form-item label='{{ i18n "enable" }}'>
<a-switch v-model="dbInbound.enable"></a-switch> <a-switch v-model="dbInbound.enable"></a-switch>
</a-form-item> </a-form-item>

View file

@ -1,5 +1,5 @@
{{define "form/dokodemo"}} {{define "form/dokodemo"}}
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'> <a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'>
<a-input v-model.trim="inbound.settings.address"></a-input> <a-input v-model.trim="inbound.settings.address"></a-input>
</a-form-item> </a-form-item>

View file

@ -1,5 +1,5 @@
{{define "form/http"}} {{define "form/http"}}
<a-form> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<table style="width: 100%; text-align: center; margin-bottom: 10px;"> <table style="width: 100%; text-align: center; margin-bottom: 10px;">
<tr> <tr>
<td width="45%">{{ i18n "username" }}</td> <td width="45%">{{ i18n "username" }}</td>

View file

@ -20,7 +20,7 @@
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
</template> </template>
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "encryption" }}'> <a-form-item label='{{ i18n "encryption" }}'>
<a-select v-model="inbound.settings.method" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.settings.method" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option> <a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option>

View file

@ -1,5 +1,5 @@
{{define "form/socks"}} {{define "form/socks"}}
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.inbounds.enable" }} UDP'> <a-form-item label='{{ i18n "pages.inbounds.enable" }} UDP'>
<a-switch v-model="inbound.settings.udp"></a-switch> <a-switch v-model="inbound.settings.udp"></a-switch>
</a-form-item> </a-form-item>

View file

@ -19,27 +19,23 @@
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
<template v-if="inbound.isTcp && !inbound.stream.isReality"> <template v-if="inbound.isTcp && !inbound.stream.isReality">
<a-form layout="inline"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label="Fallbacks"> <a-form-item label="Fallbacks">
<a-row> <a-button type="primary" size="small" @click="inbound.settings.addFallback()">+</a-button>
<a-button type="primary" size="small"
@click="inbound.settings.addFallback()">
+
</a-button>
</a-row>
</a-form-item> </a-form-item>
</a-form> </a-form>
<!-- trojan fallbacks --> <!-- trojan fallbacks -->
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:8} }"
:wrapper-col="{ md: {span:14} }">
<a-divider style="margin:0;"> <a-divider style="margin:0;">
Fallback [[ index + 1 ]] Fallback [[ index + 1 ]]
<a-icon type="delete" @click="() => inbound.settings.delFallback(index)" <a-icon type="delete" @click="() => inbound.settings.delFallback(index)"
style="color: rgb(255, 77, 79);cursor: pointer;"/> style="color: rgb(255, 77, 79);cursor: pointer;" />
</a-divider> </a-divider>
<a-form-item label='SNI'> <a-form-item label='SNI'>
<a-input v-model="fallback.name"></a-input> <a-input v-model="fallback.name"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='ALPN'> <a-form-item label='ALPN'>
<a-input v-model="fallback.alpn"></a-input> <a-input v-model="fallback.alpn"></a-input>
</a-form-item> </a-form-item>
@ -53,6 +49,6 @@
<a-input-number v-model="fallback.xver" :min="0" :max="2"></a-input-number> <a-input-number v-model="fallback.xver" :min="0" :max="2"></a-input-number>
</a-form-item> </a-form-item>
</a-form> </a-form>
<a-divider style="margin:0;"></a-divider> <a-divider style="margin:5px 0;"></a-divider>
</template> </template>
{{end}} {{end}}

View file

@ -21,27 +21,23 @@
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
<template v-if="inbound.isTcp && !inbound.stream.isReality"> <template v-if="inbound.isTcp && !inbound.stream.isReality">
<a-form layout="inline"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label="Fallbacks"> <a-form-item label="Fallbacks">
<a-row> <a-button type="primary" size="small" @click="inbound.settings.addFallback()">+</a-button>
<a-button type="primary" size="small"
@click="inbound.settings.addFallback()">
+
</a-button>
</a-row>
</a-form-item> </a-form-item>
</a-form> </a-form>
<!-- vless fallbacks --> <!-- vless fallbacks -->
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:8} }"
:wrapper-col="{ md: {span:14} }">
<a-divider style="margin:0;"> <a-divider style="margin:0;">
Fallback [[ index + 1 ]] Fallback [[ index + 1 ]]
<a-icon type="delete" @click="() => inbound.settings.delFallback(index)" <a-icon type="delete" @click="() => inbound.settings.delFallback(index)"
style="color: rgb(255, 77, 79);cursor: pointer;"/> style="color: rgb(255, 77, 79);cursor: pointer;" />
</a-divider> </a-divider>
<a-form-item label='SNI'> <a-form-item label='SNI'>
<a-input v-model="fallback.name"></a-input> <a-input v-model="fallback.name"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='ALPN'> <a-form-item label='ALPN'>
<a-input v-model="fallback.alpn"></a-input> <a-input v-model="fallback.alpn"></a-input>
</a-form-item> </a-form-item>

View file

@ -1,5 +1,5 @@
{{define "form/wireguard"}} {{define "form/wireguard"}}
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item> <a-form-item>
<template slot="label"> <template slot="label">
<a-tooltip> <a-tooltip>
@ -26,7 +26,7 @@
<a-form-item label="Peers"> <a-form-item label="Peers">
<a-button type="primary" size="small" @click="inbound.settings.addPeer()">+</a-button> <a-button type="primary" size="small" @click="inbound.settings.addPeer()">+</a-button>
</a-form-item> </a-form-item>
<a-form v-for="(peer, index) in inbound.settings.peers" :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form v-for="(peer, index) in inbound.settings.peers" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-divider style="margin:0;"> <a-divider style="margin:0;">
Peer [[ index + 1 ]] Peer [[ index + 1 ]]
<a-icon v-if="inbound.settings.peers.length>1" type="delete" @click="() => inbound.settings.delPeer(index)" <a-icon v-if="inbound.settings.peers.length>1" type="delete" @click="() => inbound.settings.delPeer(index)"

View file

@ -1,6 +1,6 @@
{{define "form/sniffing"}} {{define "form/sniffing"}}
<a-divider style="margin:5px 0 0;"></a-divider> <a-divider style="margin:5px 0 0;"></a-divider>
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item> <a-form-item>
<span slot="label"> <span slot="label">
Sniffing Sniffing

View file

@ -1,5 +1,5 @@
{{define "form/streamGRPC"}} {{define "form/streamGRPC"}}
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label="Service Name"> <a-form-item label="Service Name">
<a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input> <a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input>
</a-form-item> </a-form-item>

View file

@ -1,5 +1,5 @@
{{define "form/streamHTTP"}} {{define "form/streamHTTP"}}
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "path" }}'> <a-form-item label='{{ i18n "path" }}'>
<a-input v-model.trim="inbound.stream.http.path"></a-input> <a-input v-model.trim="inbound.stream.http.path"></a-input>
</a-form-item> </a-form-item>

View file

@ -1,7 +1,7 @@
{{define "form/streamKCP"}} {{define "form/streamKCP"}}
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "camouflage" }}'> <a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="inbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.kcp.type" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option> <a-select-option value="none">None</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option> <a-select-option value="srtp">SRTP</a-select-option>
<a-select-option value="utp">uTP</a-select-option> <a-select-option value="utp">uTP</a-select-option>

View file

@ -1,5 +1,5 @@
{{define "form/streamQUIC"}} {{define "form/streamQUIC"}}
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'> <a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
<a-select v-model="inbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option> <a-select-option value="none">None</a-select-option>
@ -20,7 +20,7 @@
<a-input v-model.trim="inbound.stream.quic.key"></a-input> <a-input v-model.trim="inbound.stream.quic.key"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "camouflage" }}'> <a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="inbound.stream.quic.type" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.quic.type" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option> <a-select-option value="none">None</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option> <a-select-option value="srtp">SRTP</a-select-option>
<a-select-option value="utp">uTP</a-select-option> <a-select-option value="utp">uTP</a-select-option>

View file

@ -1,8 +1,8 @@
{{define "form/streamSettings"}} {{define "form/streamSettings"}}
<!-- select stream network --> <!-- select stream network -->
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "transmission" }}'> <a-form-item label='{{ i18n "transmission" }}'>
<a-select v-model="inbound.stream.network" @change="streamNetworkChange" <a-select v-model="inbound.stream.network" style="width: 50%" @change="streamNetworkChange"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="tcp">TCP</a-select-option> <a-select-option value="tcp">TCP</a-select-option>
<a-select-option value="kcp">mKCP</a-select-option> <a-select-option value="kcp">mKCP</a-select-option>

View file

@ -34,16 +34,16 @@
</a-form-item> </a-form-item>
<a-form-item label="Min/Max Version"> <a-form-item label="Min/Max Version">
<a-input-group compact> <a-input-group compact>
<a-select v-model="inbound.stream.tls.minVersion" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.tls.minVersion" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option> <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
</a-select> </a-select>
<a-select v-model="inbound.stream.tls.maxVersion" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.tls.maxVersion" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option> <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
</a-select> </a-select>
</a-input-group> </a-input-group>
</a-form-item> </a-form-item>
<a-form-item label="uTLS"> <a-form-item label="uTLS">
<a-select v-model="inbound.stream.tls.settings.fingerprint" <a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 50%"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value=''>None</a-select-option> <a-select-option value=''>None</a-select-option>
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option> <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
@ -73,10 +73,10 @@
@click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px">-</a-button> @click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px">-</a-button>
</a-form-item> </a-form-item>
<template v-if="cert.useFile"> <template v-if="cert.useFile">
<a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'> <a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
<a-input v-model.trim="cert.certFile"></a-input> <a-input v-model.trim="cert.certFile"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'> <a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
<a-input v-model.trim="cert.keyFile"></a-input> <a-input v-model.trim="cert.keyFile"></a-input>
</a-form-item> </a-form-item>
<a-form-item label=" "> <a-form-item label=" ">
@ -85,10 +85,10 @@
</a-form-item> </a-form-item>
</template> </template>
<template v-else> <template v-else>
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'> <a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
<a-input type="textarea" :rows="3" v-model="cert.cert"></a-input> <a-input type="textarea" :rows="3" v-model="cert.cert"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'> <a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
<a-input type="textarea" :rows="3" v-model="cert.key"></a-input> <a-input type="textarea" :rows="3" v-model="cert.key"></a-input>
</a-form-item> </a-form-item>
</template> </template>
@ -124,10 +124,10 @@
@click="inbound.stream.xtls.removeCert(index)" style="margin-left: 10px">-</a-button> @click="inbound.stream.xtls.removeCert(index)" style="margin-left: 10px">-</a-button>
</a-form-item> </a-form-item>
<template v-if="cert.useFile"> <template v-if="cert.useFile">
<a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'> <a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
<a-input v-model.trim="cert.certFile"></a-input> <a-input v-model.trim="cert.certFile"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'> <a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
<a-input v-model.trim="cert.keyFile"></a-input> <a-input v-model.trim="cert.keyFile"></a-input>
</a-form-item> </a-form-item>
<a-form-item label=" "> <a-form-item label=" ">
@ -136,10 +136,10 @@
</a-form-item> </a-form-item>
</template> </template>
<template v-else> <template v-else>
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'> <a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
<a-input type="textarea" :rows="3" v-model="cert.cert"></a-input> <a-input type="textarea" :rows="3" v-model="cert.cert"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'> <a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
<a-input type="textarea" :rows="3" v-model="cert.key"></a-input> <a-input type="textarea" :rows="3" v-model="cert.key"></a-input>
</a-form-item> </a-form-item>
</template> </template>
@ -154,7 +154,7 @@
<a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number> <a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='uTLS'> <a-form-item label='uTLS'>
<a-select v-model="inbound.stream.reality.settings.fingerprint" <a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 50%"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option> <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-select> </a-select>
@ -180,10 +180,10 @@
<a-form-item label='SpiderX'> <a-form-item label='SpiderX'>
<a-input v-model.trim="inbound.stream.reality.settings.spiderX"></a-input> <a-input v-model.trim="inbound.stream.reality.settings.spiderX"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='Private Key'> <a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
<a-input v-model.trim="inbound.stream.reality.privateKey"></a-input> <a-input v-model.trim="inbound.stream.reality.privateKey"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='Public Key'> <a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
<a-input v-model.trim="inbound.stream.reality.settings.publicKey"></a-input> <a-input v-model.trim="inbound.stream.reality.settings.publicKey"></a-input>
</a-form-item> </a-form-item>
<a-form-item label=" "> <a-form-item label=" ">

View file

@ -848,9 +848,7 @@
okText: '{{ i18n "pages.inbounds.create"}}', okText: '{{ i18n "pages.inbounds.create"}}',
cancelText: '{{ i18n "close" }}', cancelText: '{{ i18n "close" }}',
confirm: async (inbound, dbInbound) => { confirm: async (inbound, dbInbound) => {
inModal.loading(); await this.addInbound(inbound, dbInbound, inModal);
await this.addInbound(inbound, dbInbound);
inModal.close();
}, },
isEdit: false isEdit: false
}); });
@ -865,9 +863,7 @@
inbound: inbound, inbound: inbound,
dbInbound: dbInbound, dbInbound: dbInbound,
confirm: async (inbound, dbInbound) => { confirm: async (inbound, dbInbound) => {
inModal.loading();
await this.updateInbound(inbound, dbInbound); await this.updateInbound(inbound, dbInbound);
inModal.close();
}, },
isEdit: true isEdit: true
}); });
@ -917,9 +913,7 @@
okText: '{{ i18n "pages.client.submitAdd"}}', okText: '{{ i18n "pages.client.submitAdd"}}',
dbInbound: dbInbound, dbInbound: dbInbound,
confirm: async (clients, dbInboundId) => { confirm: async (clients, dbInboundId) => {
clientModal.loading(); await this.addClient(clients, dbInboundId, clientModal);
await this.addClient(clients, dbInboundId);
clientModal.close();
}, },
isEdit: false isEdit: false
}); });
@ -931,9 +925,7 @@
okText: '{{ i18n "pages.client.bulk"}}', okText: '{{ i18n "pages.client.bulk"}}',
dbInbound: dbInbound, dbInbound: dbInbound,
confirm: async (clients, dbInboundId) => { confirm: async (clients, dbInboundId) => {
clientsBulkModal.loading(); await this.addClient(clients, dbInboundId, clientsBulkModal);
await this.addClient(clients, dbInboundId);
clientsBulkModal.close();
}, },
}); });
}, },
@ -962,19 +954,19 @@
default: return clients.findIndex(item => item.id === client.id && item.email === client.email); default: return clients.findIndex(item => item.id === client.id && item.email === client.email);
} }
}, },
async addClient(clients, dbInboundId) { async addClient(clients, dbInboundId, modal) {
const data = { const data = {
id: dbInboundId, id: dbInboundId,
settings: '{"clients": [' + clients.toString() + ']}', settings: '{"clients": [' + clients.toString() + ']}',
}; };
await this.submit(`/panel/inbound/addClient`, data); await this.submit(`/panel/inbound/addClient`, data, modal);
}, },
async updateClient(client, dbInboundId, clientId) { async updateClient(client, dbInboundId, clientId) {
const data = { const data = {
id: dbInboundId, id: dbInboundId,
settings: '{"clients": [' + client.toString() + ']}', settings: '{"clients": [' + client.toString() + ']}',
}; };
await this.submit(`/panel/inbound/updateClient/${clientId}`, data); await this.submit(`/panel/inbound/updateClient/${clientId}`, data, clientModal);
}, },
resetTraffic(dbInboundId) { resetTraffic(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
@ -1077,8 +1069,8 @@
await this.updateClient(clients[index], dbInboundId, clientId); await this.updateClient(clients[index], dbInboundId, clientId);
this.loading(false); this.loading(false);
}, },
async submit(url, data) { async submit(url, data, modal) {
const msg = await HttpUtil.postWithModal(url, data); const msg = await HttpUtil.postWithModal(url, data, modal);
if (msg.success) { if (msg.success) {
await this.getDBInbounds(); await this.getDBInbounds();
} }
@ -1237,7 +1229,6 @@
okText: '{{ i18n "pages.inbounds.import" }}', okText: '{{ i18n "pages.inbounds.import" }}',
confirm: async (dbInboundText) => { confirm: async (dbInboundText) => {
await this.submit('/panel/inbound/import', {data: dbInboundText}, promptModal); await this.submit('/panel/inbound/import', {data: dbInboundText}, promptModal);
promptModal.close();
}, },
}); });
}, },

View file

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

View file

@ -76,23 +76,17 @@
<a-layout-content> <a-layout-content>
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'> <a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
<transition name="list" appear> <transition name="list" appear>
<a-alert type="error" v-if="showAlert" style="margin-bottom: 10px" <a-alert type="error" v-if="confAlerts.length>0" style="margin: 10px 5px;"
message='{{ i18n "secAlertTitle" }}' message='{{ i18n "secAlertTitle" }}'
color="red" color="red"
description='{{ i18n "secAlertSsl" }}' show-icon
show-icon closable closable
> >
</a-alert> <template slot="description">
<a-alert type="error" v-if="confAlerts.length>0" style="margin-bottom: 10px" <b>{{ i18n "secAlertConf" }}</b>
message='{{ i18n "secAlertTitle" }}' <ul><li v-for="a in confAlerts">[[ a ]]</li></ul>
color="red" </template>
show-icon closable </a-alert>
>
<template slot="description">
{{ i18n "secAlertConf" }}
<li v-for="a in confAlerts">- [[ a ]]</li>
</template>
</a-alert>
</transition> </transition>
<a-space direction="vertical"> <a-space direction="vertical">
<a-card hoverable style="margin-bottom: .5rem; overflow-x: hidden;"> <a-card hoverable style="margin-bottom: .5rem; overflow-x: hidden;">
@ -263,7 +257,6 @@
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<a-list-item-meta title="Telegram Bot Language" /> <a-list-item-meta title="Telegram Bot Language" />
</a-col> </a-col>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<template> <template>
<a-select <a-select
@ -333,7 +326,6 @@
saveBtnDisable: true, saveBtnDisable: true,
user: {}, user: {},
lang: getLang(), lang: getLang(),
showAlert: false,
remarkModels: {i:'Inbound',e:'Email',o:'Other'}, remarkModels: {i:'Inbound',e:'Email',o:'Other'},
remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'], remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'],
datepickerList: [{name:'Gregorian (Standard)', value: 'gregorian'}, {name:'Jalalian (شمسی)', value: 'jalalian'}], datepickerList: [{name:'Gregorian (Standard)', value: 'gregorian'}, {name:'Jalalian (شمسی)', value: 'jalalian'}],
@ -352,7 +344,7 @@
streamSettings: { streamSettings: {
sockopt: { sockopt: {
tcpKeepAliveIdle: 100, tcpKeepAliveIdle: 100,
TcpNoDelay: true tcpNoDelay: true
} }
} }
}, },
@ -515,23 +507,21 @@
get: function() { get: function() {
if (!this.allSetting) return []; if (!this.allSetting) return [];
var alerts = [] var alerts = []
if (this.allSetting.port == 54321) alerts.push('{{ i18n "pages.settings.panelPort"}}'); if (window.location.protocol !== "https:") alerts.push('{{ i18n "secAlertSSL" }}');
if (this.allSetting.webPort == 54321) alerts.push('{{ i18n "secAlertPanelPort" }}');
panelPath = window.location.pathname.split('/').length<4 panelPath = window.location.pathname.split('/').length<4
if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "pages.settings.panelSettings"}} {{ i18n "pages.settings.panelUrlPath"}}'); if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "secAlertPanelURI" }}');
if (this.allSetting.subEnable) { if (this.allSetting.subEnable) {
subPath = this.allSetting.subURI.length >0 ? new URL(this.allSetting.subURI).pathname : this.allSetting.subPath; subPath = this.allSetting.subURI.length >0 ? new URL(this.allSetting.subURI).pathname : this.allSetting.subPath;
if (subPath == '/sub/') alerts.push('{{ i18n "pages.settings.subSettings"}} {{ i18n "pages.settings.subPath"}}'); if (subPath == '/sub/') alerts.push('{{ i18n "secAlertSubURI" }}');
subJsonPath = this.allSetting.subJsonURI.length >0 ? new URL(this.allSetting.subJsonURI).pathname : this.allSetting.subJsonPath; subJsonPath = this.allSetting.subJsonURI.length >0 ? new URL(this.allSetting.subJsonURI).pathname : this.allSetting.subJsonPath;
if (subJsonPath == '/json/') alerts.push('JSON {{ i18n "pages.settings.subPath"}}'); if (subJsonPath == '/json/') alerts.push('{{ i18n "secAlertSubJsonURI" }}');
} }
return alerts return alerts
} }
} }
}, },
async mounted() { async mounted() {
if (window.location.protocol !== "https:") {
this.showAlert = true;
}
await this.getAllSetting(); await this.getAllSetting();
while (true) { while (true) {
await PromiseUtil.sleep(1000); await PromiseUtil.sleep(1000);
@ -541,4 +531,4 @@
}); });
</script> </script>
</body> </body>
</html> </html>

View file

@ -27,7 +27,7 @@
<a-divider style="margin: 0;">{{ i18n "pages.settings.toasts.modifySettings" }}</a-divider> <a-divider style="margin: 0;">{{ i18n "pages.settings.toasts.modifySettings" }}</a-divider>
<a-collapse style="margin: 10px 0;"> <a-collapse style="margin: 10px 0;">
<a-collapse-panel header='WARP/WARP+ License Key'> <a-collapse-panel header='WARP/WARP+ License Key'>
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label="License Key"> <a-form-item label="License Key">
<a-input v-model="warpPlus"></a-input> <a-input v-model="warpPlus"></a-input>
<a-button @click="updateLicense(warpPlus)" :disabled="warpPlus.length<26" :loading="warpModal.confirmLoading">{{ i18n "pages.inbounds.update" }}</a-button> <a-button @click="updateLicense(warpPlus)" :disabled="warpPlus.length<26" :loading="warpModal.confirmLoading">{{ i18n "pages.inbounds.update" }}</a-button>

View file

@ -1,14 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
{{template "head" .}} {{template "head" .}}
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/codemirror.css"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/codemirror.css?{{ .cur_ver }}">
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/fold/foldgutter.css"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/fold/foldgutter.css">
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.css?{{ .cur_ver }}"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.css?{{ .cur_ver }}">
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css">
<script src="{{ .base_path }}assets/base64/base64.min.js"></script> <script src="{{ .base_path }}assets/base64/base64.min.js"></script>
<script src="{{ .base_path }}assets/js/model/outbound.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/outbound.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/codemirror/codemirror.js"></script> <script src="{{ .base_path }}assets/codemirror/codemirror.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/codemirror/javascript.js"></script> <script src="{{ .base_path }}assets/codemirror/javascript.js"></script>
<script src="{{ .base_path }}assets/codemirror/jshint.js"></script> <script src="{{ .base_path }}assets/codemirror/jshint.js"></script>
<script src="{{ .base_path }}assets/codemirror/jsonlint.js"></script> <script src="{{ .base_path }}assets/codemirror/jsonlint.js"></script>
@ -290,15 +290,19 @@
<a-alert type="warning" style="margin-bottom: 10px; width: fit-content" <a-alert type="warning" style="margin-bottom: 10px; width: fit-content"
message='{{ i18n "pages.xray.RoutingsDesc"}}' show-icon></a-alert> message='{{ i18n "pages.xray.RoutingsDesc"}}' show-icon></a-alert>
<a-button type="primary" icon="plus" @click="addRule">{{ i18n "pages.xray.rules.add" }}</a-button> <a-button type="primary" icon="plus" @click="addRule">{{ i18n "pages.xray.rules.add" }}</a-button>
<a-table :columns="isMobile ? rulesMobileColumns : rulesColumns" bordered <a-table-sortable :columns="isMobile ? rulesMobileColumns : rulesColumns" bordered
:row-key="r => r.key" :row-key="r => r.key"
:data-source="routingRuleData" :data-source="routingRuleData"
:scroll="isMobile ? {} : { x: 1000 }" :scroll="isMobile ? {} : { x: 1000 }"
:pagination="false" :pagination="false"
:indent-size="0" :indent-size="0"
:style="isMobile ? 'padding: 5px 0' : 'margin-top: 10px;'"> :style="isMobile ? 'padding: 5px 0' : 'margin-top: 10px;'"
v-on:onSort="replaceRule">
<template slot="action" slot-scope="text, rule, index"> <template slot="action" slot-scope="text, rule, index">
[[ index+1 ]] <table-sort-trigger :item-index="index"></table-sort-trigger>
<span class="ant-table-row-index">
[[ index+1 ]]
</span>
<a-dropdown :trigger="['click']"> <a-dropdown :trigger="['click']">
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon> <a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme"> <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
@ -404,7 +408,7 @@
</a-button> </a-button>
</a-popover> </a-popover>
</template> </template>
</a-table> </a-table-sortable>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-3" tab='{{ i18n "pages.xray.Outbounds"}}' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-3" tab='{{ i18n "pages.xray.Outbounds"}}' style="padding-top: 20px;" force-render="true">
<a-row> <a-row>
@ -530,7 +534,7 @@
<template slot="selector" slot-scope="text, balancer, index"> <template slot="selector" slot-scope="text, balancer, index">
<a-tag class="info-large-tag" style="margin:1;" v-for="sel in balancer.selector">[[ sel ]]</a-tag> <a-tag class="info-large-tag" style="margin:1;" v-for="sel in balancer.selector">[[ sel ]]</a-tag>
</template> </template>
</a-table> </a-table>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-6" tab='DNS' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-6" tab='DNS' style="padding-top: 20px;" force-render="true">
<setting-list-item type="switch" title='{{ i18n "pages.xray.dns.enable" }}' desc='{{ i18n "pages.xray.dns.enableDesc" }}' v-model="enableDNS"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.dns.enable" }}' desc='{{ i18n "pages.xray.dns.enableDesc" }}' v-model="enableDNS"></setting-list-item>
@ -630,6 +634,7 @@
</a-layout> </a-layout>
{{template "js" .}} {{template "js" .}}
{{template "component/themeSwitcher" .}} {{template "component/themeSwitcher" .}}
{{template "component/sortableTable" .}}
{{template "component/setting"}} {{template "component/setting"}}
{{template "ruleModal"}} {{template "ruleModal"}}
{{template "outModal"}} {{template "outModal"}}
@ -1269,7 +1274,7 @@
newRules = newTemplateSettings.routing.rules.filter(r => r.outboundTag != oldData.tag); newRules = newTemplateSettings.routing.rules.filter(r => r.outboundTag != oldData.tag);
} }
newTemplateSettings.routing.rules = newRules; newTemplateSettings.routing.rules = newRules;
this.templateSettings = newTemplateSettings; this.templateSettings = newTemplateSettings;
}, },
addDNSServer(){ addDNSServer(){

View file

@ -11,7 +11,7 @@
:ok-text="balancerModal.okText" :ok-text="balancerModal.okText"
cancel-text='{{ i18n "close" }}' cancel-text='{{ i18n "close" }}'
:class="themeSwitcher.currentTheme"> :class="themeSwitcher.currentTheme">
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.xray.balancer.tag" }}' has-feedback <a-form-item label='{{ i18n "pages.xray.balancer.tag" }}' has-feedback
:validate-status="balancerModal.duplicateTag? 'warning' : 'success'"> :validate-status="balancerModal.duplicateTag? 'warning' : 'success'">
<a-input v-model.trim="balancerModal.balancer.tag" @change="balancerModal.check()" <a-input v-model.trim="balancerModal.balancer.tag" @change="balancerModal.check()"

View file

@ -2,7 +2,7 @@
<a-modal id="reverse-modal" v-model="reverseModal.visible" :title="reverseModal.title" @ok="reverseModal.ok" <a-modal id="reverse-modal" v-model="reverseModal.visible" :title="reverseModal.title" @ok="reverseModal.ok"
:confirm-loading="reverseModal.confirmLoading" :closable="true" :mask-closable="false" :confirm-loading="reverseModal.confirmLoading" :closable="true" :mask-closable="false"
:ok-text="reverseModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme"> :ok-text="reverseModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'> <a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
<a-select v-model="reverseModal.reverse.type" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="reverseModal.reverse.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="x,y in reverseTypes" :value="y">[[ x ]]</a-select-option> <a-select-option v-for="x,y in reverseTypes" :value="y">[[ x ]]</a-select-option>

View file

@ -2,7 +2,7 @@
<a-modal id="rule-modal" v-model="ruleModal.visible" :title="ruleModal.title" @ok="ruleModal.ok" <a-modal id="rule-modal" v-model="ruleModal.visible" :title="ruleModal.title" @ok="ruleModal.ok"
:confirm-loading="ruleModal.confirmLoading" :closable="true" :mask-closable="false" :confirm-loading="ruleModal.confirmLoading" :closable="true" :mask-closable="false"
:ok-text="ruleModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme"> :ok-text="ruleModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='Domain Matcher'> <a-form-item label='Domain Matcher'>
<a-select v-model="ruleModal.rule.domainMatcher" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="ruleModal.rule.domainMatcher" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="dm in ['','hybrid','linear']" :value="dm">[[ dm ]]</a-select-option> <a-select-option v-for="dm in ['','hybrid','linear']" :value="dm">[[ dm ]]</a-select-option>

View file

@ -1814,7 +1814,7 @@ func (s *InboundService) MigrationRequirements() {
json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings) json.Unmarshal([]byte(inbounds[inbound_index].Settings), &settings)
clients, ok := settings["clients"].([]interface{}) clients, ok := settings["clients"].([]interface{})
if ok { if ok {
// Fix Clinet configuration problems // Fix Client configuration problems
var newClients []interface{} var newClients []interface{}
for client_index := range clients { for client_index := range clients {
c := clients[client_index].(map[string]interface{}) c := clients[client_index].(map[string]interface{})

View file

@ -42,7 +42,7 @@
"online" = "Online" "online" = "Online"
"domainName" = "Domain Name" "domainName" = "Domain Name"
"monitor" = "Listen IP" "monitor" = "Listen IP"
"certificate" = "Certificate" "certificate" = "Digital Certificate"
"fail" = " Failed" "fail" = " Failed"
"success" = " Successful" "success" = " Successful"
"getVersion" = "Get Version" "getVersion" = "Get Version"
@ -54,7 +54,12 @@
"security" = "Security" "security" = "Security"
"secAlertTitle" = "Security Alert" "secAlertTitle" = "Security Alert"
"secAlertSsl" = "This connection is not secure. Please avoid entering sensitive information until TLS is activated for data protection." "secAlertSsl" = "This connection is not secure. Please avoid entering sensitive information until TLS is activated for data protection."
"secAlertConf" = "Certain configurations have been identified as susceptible to attacks, prompting immediate action to reinforce security protocols and safeguard against potential security breaches." "secAlertConf" = "Certain settings are vulnerable to attacks. It is recommended to reinforce security protocols to prevent potential breaches."
"secAlertSSL" = "Panel lacks secure connection. Please install TLS certificate for data protection."
"secAlertPanelPort" = "Panel default port is vulnerable. Please configure a random or specific port."
"secAlertPanelURI" = "Panel default URI path is insecure. Please configure a complex URI path."
"secAlertSubURI" = "Subscription default URI path is insecure. Please configure a complex URI path."
"secAlertSubJsonURI" = "Subscription JSON default URI path is insecure. Please configure a complex URI path."
[menu] [menu]
"dashboard" = "Overview" "dashboard" = "Overview"
@ -143,10 +148,8 @@
"noRecommendKeepDefault" = "It is recommended to keep the default" "noRecommendKeepDefault" = "It is recommended to keep the default"
"certificatePath" = "File Path" "certificatePath" = "File Path"
"certificateContent" = "File Content" "certificateContent" = "File Content"
"publicKeyPath" = "Public Key Path" "publicKey" = "Public Key"
"publicKeyContent" = "Public Key Content" "privatekey" = "Private Key"
"keyPath" = "Private Key Path"
"keyContent" = "Private Key Content"
"clickOnQRcode" = "Click on QR Code to Copy" "clickOnQRcode" = "Click on QR Code to Copy"
"client" = "Client" "client" = "Client"
"export" = "Export All URLs" "export" = "Export All URLs"

View file

@ -42,7 +42,7 @@
"online" = "en línea" "online" = "en línea"
"domainName" = "Nombre de dominio" "domainName" = "Nombre de dominio"
"monitor" = "Listening IP" "monitor" = "Listening IP"
"certificate" = "Certificado" "certificate" = "Certificado Digital"
"fail" = "Falló" "fail" = "Falló"
"success" = "Éxito" "success" = "Éxito"
"getVersion" = "Obtener versión" "getVersion" = "Obtener versión"
@ -54,7 +54,12 @@
"security" = "Seguridad" "security" = "Seguridad"
"secAlertTitle" = "Alerta de seguridad" "secAlertTitle" = "Alerta de seguridad"
"secAlertSsl" = "Esta conexión no es segura. Evite ingresar información confidencial hasta que TLS esté activado para la protección de datos." "secAlertSsl" = "Esta conexión no es segura. Evite ingresar información confidencial hasta que TLS esté activado para la protección de datos."
"secAlertConf" = "Se han identificado ciertas configuraciones como susceptibles a ataques, lo que genera acciones inmediatas para reforzar los protocolos de seguridad y proteger contra posibles violaciones de seguridad." "secAlertConf" = "Certae occasus vulnerabiles sunt impetus. Commendatur ad securitatem protocolla roboranda ne interrupta potentiale."
"secAlertSSL" = "La panel carece de conexión segura. Por favor, instale un certificado TLS para la protección de datos."
"secAlertPanelPort" = "La puerto predeterminado del panel es vulnerable. Por favor, configure un puerto aleatorio o específico."
"secAlertPanelURI" = "La ruta URI predeterminada del panel no es segura. Por favor, configure una ruta URI compleja."
"secAlertSubURI" = "La ruta URI predeterminada de la suscripción no es segura. Por favor, configure una ruta URI compleja."
"secAlertSubJsonURI" = "La ruta URI predeterminada de la suscripción JSON no es segura. Por favor, configure una ruta URI compleja."
[menu] [menu]
"dashboard" = "Estado del Sistema" "dashboard" = "Estado del Sistema"
@ -143,10 +148,8 @@
"noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada" "noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada"
"certificatePath" = "Ruta del Archivo" "certificatePath" = "Ruta del Archivo"
"certificateContent" = "Contenido del Archivo" "certificateContent" = "Contenido del Archivo"
"publicKeyPath" = "Ruta de la Clave Pública" "publicKey" = "llave Pública"
"publicKeyContent" = "Contenido de la Clave Pública" "privatekey" = "llave Privada"
"keyPath" = "Ruta de la Clave Privada"
"keyContent" = "Contenido de la Clave Privada"
"clickOnQRcode" = "Haz clic en el Código QR para Copiar" "clickOnQRcode" = "Haz clic en el Código QR para Copiar"
"client" = "Cliente" "client" = "Cliente"
"export" = "Exportar Enlaces" "export" = "Exportar Enlaces"
@ -197,7 +200,7 @@
"last" = "Último" "last" = "Último"
"prefix" = "Prefijo" "prefix" = "Prefijo"
"postfix" = "Sufijo" "postfix" = "Sufijo"
"delayedStart" = "Iniciar después del primer uso" "delayedStart" = "Inicio Inicial"
"expireDays" = "Duratio" "expireDays" = "Duratio"
"days" = "día(s)" "days" = "día(s)"
"renew" = "Renovación automática" "renew" = "Renovación automática"

View file

@ -42,7 +42,7 @@
"online" = "آنلاین" "online" = "آنلاین"
"domainName" = "آدرس دامنه" "domainName" = "آدرس دامنه"
"monitor" = "آی‌پی اتصال" "monitor" = "آی‌پی اتصال"
"certificate" = "گواهی" "certificate" = "گواهی دیجیتال"
"fail" = "ناموفق" "fail" = "ناموفق"
"success" = " موفق" "success" = " موفق"
"getVersion" = "دریافت نسخه" "getVersion" = "دریافت نسخه"
@ -54,7 +54,12 @@
"security" = "امنیت" "security" = "امنیت"
"secAlertTitle" = "هشدار‌امنیتی" "secAlertTitle" = "هشدار‌امنیتی"
"secAlertSsl" = "این‌اتصال‌امن نیست. لطفا‌ تازمانی‌که تی‌ال‌اس برای محافظت از‌ داده‌ها فعال نشده‌است، از وارد کردن اطلاعات حساس خودداری کنید" "secAlertSsl" = "این‌اتصال‌امن نیست. لطفا‌ تازمانی‌که تی‌ال‌اس برای محافظت از‌ داده‌ها فعال نشده‌است، از وارد کردن اطلاعات حساس خودداری کنید"
"secAlertConf" = "پیکربندی‌های خاصی مستعد حملات سایبری شناسایی شده‌اند، اقدام فوری برای تقویت پروتکل‌های امنیتی و محافظت در برابر نقض‌های امنیتی لازم است" "secAlertConf" = "تنظیمات خاصی در برابر حملات آسیب پذیر هستند. توصیه می‌شود پروتکل‌های امنیتی را برای جلوگیری از نفوذ احتمالی تقویت کنید"
"secAlertSSL" = "پنل فاقد ارتباط امن است. لطفاً یک گواهینامه تی‌ال‌اس برای محافظت از داده‌ها نصب کنید"
"secAlertPanelPort" = "استفاده از پورت پیش‌فرض پنل ناامن است. لطفاً یک پورت تصادفی یا خاص تنظیم کنید"
"secAlertPanelURI" = "مسیر پیش‌فرض لینک پنل ناامن است. لطفاً یک مسیر پیچیده تنظیم کنید"
"secAlertSubURI" = "مسیر پیش‌فرض لینک سابسکریپشن ناامن است. لطفاً یک مسیر پیچیده تنظیم کنید"
"secAlertSubJsonURI" = "مسیر پیش‌فرض لینک سابسکریپشن جیسون ناامن است. لطفاً یک مسیر پیچیده تنظیم کنید"
[menu] [menu]
"dashboard" = "نمای کلی" "dashboard" = "نمای کلی"
@ -137,16 +142,14 @@
"destinationPort" = "پورت مقصد" "destinationPort" = "پورت مقصد"
"targetAddress" = "آدرس مقصد" "targetAddress" = "آدرس مقصد"
"monitorDesc" = "به‌طور پیش‌فرض خالی‌بگذارید" "monitorDesc" = "به‌طور پیش‌فرض خالی‌بگذارید"
"meansNoLimit" = " = واحد: گیگابایت) نامحدود)" "meansNoLimit" = "0 = واحد: گیگابایت) نامحدود)"
"totalFlow" = "ترافیک کل" "totalFlow" = "ترافیک کل"
"leaveBlankToNeverExpire" = "برای منقضی‌نشدن خالی‌بگذارید" "leaveBlankToNeverExpire" = "برای منقضی‌نشدن خالی‌بگذارید"
"noRecommendKeepDefault" = "توصیه‌می‌شود به‌طور پیش‌فرض حفظ‌شود" "noRecommendKeepDefault" = "توصیه‌می‌شود به‌طور پیش‌فرض حفظ‌شود"
"certificatePath" = "مسیر فایل" "certificatePath" = "مسیر فایل"
"certificateContent" = "محتوای فایل" "certificateContent" = "محتوای فایل"
"publicKeyPath" = "مسیر کلید عمومی" "publicKey" = "کلید عمومی"
"publicKeyContent" = "محتوای کلید عمومی" "privatekey" = "کلید خصوصی"
"keyPath" = "مسیر کلید خصوصی"
"keyContent" = "محتوای کلید خصوصی"
"clickOnQRcode" = "برای کپی بر روی کدتصویری کلیک کنید" "clickOnQRcode" = "برای کپی بر روی کدتصویری کلیک کنید"
"client" = "کاربر" "client" = "کاربر"
"export" = "استخراج لینک‌ها" "export" = "استخراج لینک‌ها"

View file

@ -42,7 +42,7 @@
"online" = "Online" "online" = "Online"
"domainName" = "Nama Domain" "domainName" = "Nama Domain"
"monitor" = "IP Pemantauan" "monitor" = "IP Pemantauan"
"certificate" = "Sertifikat" "certificate" = "Sertifikat Digital"
"fail" = "Gagal" "fail" = "Gagal"
"success" = "Berhasil" "success" = "Berhasil"
"getVersion" = "Dapatkan Versi" "getVersion" = "Dapatkan Versi"
@ -54,7 +54,12 @@
"security" = "Keamanan" "security" = "Keamanan"
"secAlertTitle" = "Peringatan keamanan" "secAlertTitle" = "Peringatan keamanan"
"secAlertSsl" = "Koneksi ini tidak aman. Harap hindari memasukkan informasi sensitif sampai TLS diaktifkan untuk perlindungan data." "secAlertSsl" = "Koneksi ini tidak aman. Harap hindari memasukkan informasi sensitif sampai TLS diaktifkan untuk perlindungan data."
"secAlertConf" = "Konfigurasi tertentu telah diidentifikasi rentan terhadap serangan, sehingga mendorong tindakan segera untuk memperkuat protokol keamanan dan melindungi dari potensi pelanggaran keamanan." "secAlertConf" = "Beberapa pengaturan rentan terhadap serangan. Disarankan untuk memperkuat protokol keamanan guna mencegah pelanggaran potensial."
"secAlertSSL" = "Panel kekurangan koneksi yang aman. Harap instal sertifikat TLS untuk perlindungan data."
"secAlertPanelPort" = "Port default panel rentan. Harap konfigurasi port acak atau tertentu."
"secAlertPanelURI" = "Jalur URI default panel tidak aman. Harap konfigurasi jalur URI kompleks."
"secAlertSubURI" = "Jalur URI default langganan tidak aman. Harap konfigurasi jalur URI kompleks."
"secAlertSubJsonURI" = "Jalur URI default JSON langganan tidak aman. Harap konfigurasikan jalur URI kompleks."
[menu] [menu]
"dashboard" = "Ikhtisar" "dashboard" = "Ikhtisar"
@ -143,10 +148,8 @@
"noRecommendKeepDefault" = "Disarankan untuk tetap menggunakan pengaturan default" "noRecommendKeepDefault" = "Disarankan untuk tetap menggunakan pengaturan default"
"certificatePath" = "Path Berkas" "certificatePath" = "Path Berkas"
"certificateContent" = "Konten Berkas" "certificateContent" = "Konten Berkas"
"publicKeyPath" = "Path Kunci Publik" "publicKey" = "Kunci Publik"
"publicKeyContent" = "Konten Kunci Publik" "privatekey" = "Kunci Pribadi"
"keyPath" = "Path Kunci Privat"
"keyContent" = "Konten Kunci Privat"
"clickOnQRcode" = "Klik pada Kode QR untuk Menyalin" "clickOnQRcode" = "Klik pada Kode QR untuk Menyalin"
"client" = "Klien" "client" = "Klien"
"export" = "Ekspor Semua URL" "export" = "Ekspor Semua URL"
@ -197,7 +200,7 @@
"last" = "Terakhir" "last" = "Terakhir"
"prefix" = "Awalan" "prefix" = "Awalan"
"postfix" = "Akhiran" "postfix" = "Akhiran"
"delayedStart" = "Mulai saat Penggunaan Awal" "delayedStart" = "Mulai Awal"
"expireDays" = "Durasi" "expireDays" = "Durasi"
"days" = "Hari" "days" = "Hari"
"renew" = "Perpanjang Otomatis" "renew" = "Perpanjang Otomatis"

View file

@ -42,7 +42,7 @@
"online" = "Онлайн" "online" = "Онлайн"
"domainName" = "Домен" "domainName" = "Домен"
"monitor" = "Порт IP" "monitor" = "Порт IP"
"certificate" = "Сертификат" "certificate" = "Цифровой сертификат"
"fail" = "Неудачно" "fail" = "Неудачно"
"success" = "Успешно" "success" = "Успешно"
"getVersion" = "Узнать версию" "getVersion" = "Узнать версию"
@ -54,7 +54,12 @@
"security" = "Безопасность" "security" = "Безопасность"
"secAlertTitle" = "Предупреждение системы безопасности" "secAlertTitle" = "Предупреждение системы безопасности"
"secAlertSsl" = "Это соединение не защищено. Пожалуйста, воздержитесь от ввода конфиденциальной информации до тех пор, пока не будет активирован TLS для защиты данных" "secAlertSsl" = "Это соединение не защищено. Пожалуйста, воздержитесь от ввода конфиденциальной информации до тех пор, пока не будет активирован TLS для защиты данных"
"secAlertConf" = "Некоторые конфигурации были определены как уязвимые для атак, что требует немедленных действий по усилению протоколов безопасности и защите от потенциальных нарушений безопасности." "secAlertConf" = "Некоторые настройки уязвимы для атак. Рекомендуется усилить протоколы безопасности, чтобы предотвратить потенциальные нарушения."
"secAlertSSL" = "В панели отсутствует безопасное соединение. Пожалуйста, установите сертификат TLS для защиты данных."
"secAlertPanelPort" = "Порт по умолчанию панели небезопасен. Пожалуйста, настройте случайный или определенный порт."
"secAlertPanelURI" = "URI-путь по умолчанию панели небезопасен. Пожалуйста, настройте сложный URI-путь."
"secAlertSubURI" = "URI-путь по умолчанию подписки небезопасен. Пожалуйста, настройте сложный URI-путь."
"secAlertSubJsonURI" = "URI-путь по умолчанию для JSON подписки небезопасен. Пожалуйста, настройте сложный URI-путь."
[menu] [menu]
"dashboard" = "Статус системы" "dashboard" = "Статус системы"
@ -143,10 +148,8 @@
"noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию" "noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию"
"certificatePath" = "Путь файла" "certificatePath" = "Путь файла"
"certificateContent" = "Содержимое файла" "certificateContent" = "Содержимое файла"
"publicKeyPath" = "Путь к публичному ключу" "publicKey" = "Публичный ключ"
"publicKeyContent" = "Содержимое публичного ключа" "privatekey" = "Приватный ключ"
"keyPath" = "Путь к приватному ключу"
"keyContent" = "Содержимое приватного ключа"
"clickOnQRcode" = "Нажмите на QR-код, чтобы скопировать" "clickOnQRcode" = "Нажмите на QR-код, чтобы скопировать"
"client" = "Клиент" "client" = "Клиент"
"export" = "Экспорт ключей" "export" = "Экспорт ключей"
@ -197,7 +200,7 @@
"last" = "Последний" "last" = "Последний"
"prefix" = "Префикс" "prefix" = "Префикс"
"postfix" = "Постфикс" "postfix" = "Постфикс"
"delayedStart" = "Начать с момента первого подключения" "delayedStart" = "Начало использования"
"expireDays" = "Длительность" "expireDays" = "Длительность"
"days" = "дней" "days" = "дней"
"renew" = "Автопродление" "renew" = "Автопродление"
@ -454,7 +457,7 @@
[pages.xray.wireguard] [pages.xray.wireguard]
"secretKey" = "Секретный ключ" "secretKey" = "Секретный ключ"
"publicKey" = "Открытый ключ" "publicKey" = "Публичный ключ"
"allowedIPs" = "Разрешенные IP-адреса" "allowedIPs" = "Разрешенные IP-адреса"
"endpoint" = "Конечная точка" "endpoint" = "Конечная точка"
"psk" = "Общий ключ" "psk" = "Общий ключ"

View file

@ -0,0 +1,624 @@
"username" = "Ім'я користувача"
"password" = "Пароль"
"login" = "Увійти"
"confirm" = "Підтвердити"
"cancel" = "Скасувати"
"close" = "Закрити"
"copy" = "Копіювати"
"copied" = "Скопійовано"
"download" = "Завантажити"
"remark" = "Примітка"
"enable" = "Увімкнути"
"protocol" = "Протокол"
"search" = "Пошук"
"filter" = "Фільтр"
"loading" = "Завантаження..."
"second" = "Секунда"
"minute" = "Хвилина"
"hour" = "Година"
"day" = "День"
"check" = "Перевірка"
"indefinite" = "Безстроково"
"unlimited" = "Безлімітний"
"none" = "Немає"
"qrCode" = "QR-Код"
"info" = "Більше інформації"
"edit" = "Редагувати"
"delete" = "Видалити"
"reset" = "Скидання"
"copySuccess" = "Скопійовано успішно"
"sure" = "Звичайно"
"encryption" = "Шифрування"
"transmission" = "Протокол передачи"
"host" = "Хост"
"path" = "Шлях"
"camouflage" = "Маскування"
"status" = "Статус"
"enabled" = "Увімкнено"
"disabled" = "Вимкнено"
"depleted" = "Вичерпано"
"depletingSoon" = "Вичерпується"
"offline" = "Офлайн"
"online" = "Онлайн"
"domainName" = "Доменне ім`я"
"monitor" = "Слухати IP"
"certificate" = "Цифровий сертифікат"
"fail" = " Помилка"
"success" = " Успішно"
"getVersion" = "Отримати версію"
"install" = "Встановити"
"clients" = "Клієнти"
"usage" = "Використання"
"secretToken" = "Секретний маркер"
"remained" = "Залишилося"
"security" = "Беспека"
"secAlertTitle" = "Попередження системи безпеки"
"secAlertSsl" = "Це з'єднання не є безпечним. Будь ласка, уникайте введення конфіденційної інформації, поки TLS не буде активовано для захисту даних."
"secAlertConf" = "Деякі налаштування вразливі до атак. Рекомендується посилити протоколи безпеки, щоб запобігти можливим порушенням."
"secAlertSSL" = "Панель не має безпечного з'єднання. Будь ласка, встановіть сертифікат TLS для захисту даних."
"secAlertPanelPort" = "Стандартний порт панелі вразливий. Будь ласка, сконфігуруйте випадковий або конкретний порт."
"secAlertPanelURI" = "Стандартний URI-шлях панелі небезпечний. Будь ласка, сконфігуруйте складний URI-шлях."
"secAlertSubURI" = "Стандартний URI-шлях підписки небезпечний. Будь ласка, сконфігуруйте складний URI-шлях."
"secAlertSubJsonURI" = "Стандартний URI-шлях JSON підписки небезпечний. Будь ласка, сконфігуруйте складний URI-шлях."
[menu]
"dashboard" = "Огляд"
"inbounds" = "Вхідні"
"settings" = "Параметри панелі"
"xray" = "Конфігурації Xray"
"logout" = "Вийти"
"link" = "Керувати"
[pages.login]
"title" = "Ласкаво просимо"
"loginAgain" = "Ваш сеанс закінчився, увійдіть знову"
[pages.login.toasts]
"invalidFormData" = "Формат вхідних даних недійсний."
"emptyUsername" = "Потрібне ім'я користувача"
"emptyPassword" = "Потрібен пароль"
"wrongUsernameOrPassword" = "Невірне ім'я користувача або пароль."
"successLogin" = "Вхід"
[pages.index]
"title" = "Огляд"
"memory" = "Пам'ять"
"hard" = "Диск"
"xrayStatus" = "Xray"
"stopXray" = "Зупинити"
"restartXray" = "Перезапустити"
"xraySwitch" = "Версія"
"xraySwitchClick" = "Виберіть версію, на яку ви хочете перейти."
"xraySwitchClickDesk" = "Вибирайте уважно, оскільки старіші версії можуть бути несумісними з поточними конфігураціями."
"operationHours" = "Час роботи"
"systemLoad" = "Завантаження системи"
"systemLoadDesc" = "Середнє завантаження системи за останні 1, 5 і 15 хвилин"
"connectionTcpCountDesc" = "Загальна кількість TCP-з'єднань у системі"
"connectionUdpCountDesc" = "Загальна кількість UDP-з'єднань у системі"
"connectionCount" = "Статистика з'єднання"
"upSpeed" = "Загальна швидкість завантаження в системі"
"downSpeed" = "Загальна швидкість завантаження в системі"
"totalSent" = "Загальна кількість даних, надісланих через систему з моменту запуску ОС"
"totalReceive" = "Загальна кількість даних, отриманих системою з моменту запуску ОС"
"xraySwitchVersionDialog" = "Змінити версію Xray"
"xraySwitchVersionDialogDesc" = "Ви впевнені, що бажаєте змінити версію Xray на"
"dontRefresh" = "Інсталяція триває, будь ласка, не оновлюйте цю сторінку"
"logs" = "Журнали"
"config" = "Конфігурація"
"backup" = "Резервне копіювання та відновлення"
"backupTitle" = "Резервне копіювання та відновлення бази даних"
"backupDescription" = "Рекомендується зробити резервну копію перед відновленням бази даних."
"exportDatabase" = "Резервне копіювання"
"importDatabase" = "Відновити"
[pages.inbounds]
"title" = "Вхідні"
"totalDownUp" = "Всього надісланих/отриманих"
"totalUsage" = "Всього використанно"
"inboundCount" = "Загальна кількість вхідних"
"operate" = "Меню"
"enable" = "Увімкнено"
"remark" = "Примітка"
"protocol" = "Протокол"
"port" = "Порт"
"traffic" = "Трафік"
"details" = "Деталі"
"transportConfig" = "Транспорт"
"expireDate" = "Тривалість"
"resetTraffic" = "Скинути трафік"
"addInbound" = "Додати вхідний"
"generalActions" = "Загальні дії"
"create" = "Створити"
"update" = "Оновити"
"modifyInbound" = "Змінити вхідний"
"deleteInbound" = "Видалити вхідні"
"deleteInboundContent" = "Ви впевнені, що хочете видалити вхідні?"
"deleteClient" = "Видалити клієнта"
"deleteClientContent" = "Ви впевнені, що хочете видалити клієнт?"
"resetTrafficContent" = "Ви впевнені, що хочете скинути трафік?"
"copyLink" = "Копіювати URL"
"address" = "Адреса"
"network" = "Мережа"
"destinationPort" = "Порт призначення"
"targetAddress" = "Цільова адреса"
"monitorDesc" = "Залиште порожнім, щоб слухати всі IP-адреси"
"meansNoLimit" = " = Необмежено. (одиниця: ГБ)"
"totalFlow" = "Загальна витрата"
"leaveBlankToNeverExpire" = "Залиште порожнім, щоб ніколи не закінчувався"
"noRecommendKeepDefault" = "Рекомендується зберегти значення за замовчуванням"
"certificatePath" = "Шлях до файлу"
"certificateContent" = "Вміст файлу"
"publicKey" = "Публічний ключ"
"privatekey" = "Закритий ключ"
"clickOnQRcode" = "Натисніть QR-код, щоб скопіювати"
"client" = "Клієнт"
"export" = "Експортувати всі URL-адреси"
"clone" = "Клон"
"cloneInbound" = "Клонувати"
"cloneInboundContent" = "Усі налаштування цього вхідного потоку, крім порту, IP-адреси прослуховування та клієнтів, будуть застосовані до клону."
"cloneInboundOk" = "Клонувати"
"resetAllTraffic" = "Скинути весь вхідний трафік"
"resetAllTrafficTitle" = "Скинути весь вхідний трафік"
"resetAllTrafficContent" = "Ви впевнені, що бажаєте скинути трафік усіх вхідних?"
"resetInboundClientTraffics" = "Скинути трафік клієнтів"
"resetInboundClientTrafficTitle" = "Скинути трафік клієнтів"
"resetInboundClientTrafficContent" = "Ви впевнені, що бажаєте скинути трафік клієнтів цього вхідного потоку?"
"resetAllClientTraffics" = "Скинути весь трафік клієнтів"
"resetAllClientTrafficTitle" = "Скинути весь трафік клієнтів"
"resetAllClientTrafficContent" = "Ви впевнені, що бажаєте скинути трафік усіх клієнтів?"
"delDepletedClients" = "Видалити вичерпані клієнти"
"delDepletedClientsTitle" = "Видалити вичерпані клієнти"
"delDepletedClientsContent" = "Ви впевнені, що хочете видалити всі вичерпані клієнти?"
"email" = "Електронна пошта"
"emailDesc" = "Будь ласка, надайте унікальну адресу електронної пошти."
"IPLimit" = "Обмеження IP"
"IPLimitDesc" = "Вимикає вхідний, якщо кількість перевищує встановлене значення. (0 = вимкнено)"
"IPLimitlog" = "Журнал IP"
"IPLimitlogDesc" = "Журнал історії IP-адрес. (щоб увімкнути вхідну після вимкнення, очистіть журнал)"
"IPLimitlogclear" = "Очистити журнал"
"setDefaultCert" = "Установити сертифікат з панелі"
"xtlsDesc" = "Xray має бути v1.7.5"
"realityDesc" = "Xray має бути v1.8.0+"
"telegramDesc" = "Будь ласка, надайте ідентифікатори Telegram або чату без використання '@'. (отримайте його тут @userinfobot) або (використайте команду '/id' у боті)"
"subscriptionDesc" = "Щоб знайти URL-адресу вашої підписки, перейдіть до «Деталі». Крім того, ви можете використовувати одне ім'я для кількох клієнтів."
"info" = "Інформація"
"same" = "Те саме"
"inboundData" = "Вхідні дані"
"exportInbound" = "Експортувати вхідні"
"import" = "Імпорт"
"importInbound" = "Імпортувати вхідний"
[pages.client]
"add" = "Додати клієнта"
"edit" = "Редагувати клієнта"
"submitAdd" = "Додати клієнта"
"submitEdit" = "Зберегти зміни"
"clientCount" = "Кількість клієнтів"
"bulk" = "Додати групу"
"method" = "Метод"
"first" = "Перший"
"last" = "Останній"
"prefix" = "Префікс"
"postfix" = "Постфікс"
"delayedStart" = "Початок використання"
"expireDays" = "Тривалість"
"days" = "Дні(в)"
"renew" = "Автоматичне оновлення"
"renewDesc" = "Автоматичне поновлення після закінчення терміну дії. (0 = вимкнено)(одиниця: день)"
[pages.inbounds.toasts]
"obtain" = "Отримати"
[pages.inbounds.stream.general]
"request" = "Запит"
"response" = "Відповідь"
"name" = "Ім'я"
"value" = "Значення"
[pages.inbounds.stream.tcp]
"version" = "Версія"
"method" = "Метод"
"path" = "Шлях"
"status" = "Статус"
"statusDescription" = "Опис стану"
"requestHeader" = "Заголовок запиту"
"responseHeader" = "Заголовок відповіді"
[pages.inbounds.stream.quic]
"encryption" = "Шифрування"
[pages.settings]
"title" = "Параметри панелі"
"save" = "Зберегти"
"infoDesc" = "Кожна внесена тут зміна повинна бути збережена. Перезапустіть панель, щоб застосувати зміни."
"restartPanel" = "Перезапустити панель"
"restartPanelDesc" = "Ви впевнені, що бажаєте перезапустити панель? Якщо ви не можете отримати доступ до панелі після перезапуску, будь ласка, перегляньте інформацію журналу панелі на сервері."
"actions" = "Дії"
"resetDefaultConfig" = "Відновити значення за замовчуванням"
"panelSettings" = "Загальні"
"securitySettings" = "Автентифікація"
"TGBotSettings" = "Telegram Бот"
"panelListeningIP" = "Слухати IP"
"panelListeningIPDesc" = "IP-адреса для веб-панелі. (залиште порожнім, щоб слухати всі IP-адреси)"
"panelListeningDomain" = "Домен прослуховування"
"panelListeningDomainDesc" = "Доменне ім'я для веб-панелі. (залиште порожнім, щоб слухати всі домени та IP-адреси)"
"panelPort" = "Порт прослуховування"
"panelPortDesc" = "Номер порту для веб-панелі. (має бути невикористаний порт)"
"publicKeyPath" = "Шлях відкритого ключа"
"publicKeyPathDesc" = "Шлях до файлу відкритого ключа для веб-панелі. (починається з /)"
"privateKeyPath" = "Шлях приватного ключа"
"privateKeyPathDesc" = "Шлях до файлу приватного ключа для веб-панелі. (починається з /)"
"panelUrlPath" = "Шлях URL"
"panelUrlPathDesc" = "Шлях URL для веб-панелі. (починається з / і закінчується /)"
"pageSize" = "Розмір сторінки"
"pageSizeDesc" = "Визначити розмір сторінки для вхідної таблиці. (0 = вимкнено)"
"remarkModel" = "Модель зауваження та роздільний символ"
"datepicker" = "Тип календаря"
"datepickerPlaceholder" = "Виберіть дату"
"datepickerDescription" = "Заплановані завдання виконуватимуться на основі цього календаря."
"sampleRemark" = "Зразок зауваження"
"oldUsername" = "Поточне ім'я користувача"
"currentPassword" = "Поточний пароль"
"newUsername" = "Нове ім'я користувача"
"newPassword" = "Новий пароль"
"telegramBotEnable" = "Увімкнути Telegram Bot"
"telegramBotEnableDesc" = "Вмикає бота Telegram."
"telegramToken" = "Telegram Токен"
"telegramTokenDesc" = "Токен бота Telegram, отриманий від '@BotFather'."
"telegramProxy" = "SOCKS Проксі"
"telegramProxyDesc" = "Вмикає проксі-сервер SOCKS5 для підключення до Telegram. (відкоригуйте параметри відповідно до посібника)"
"telegramChatId" = "Ідентифікатор чату адміністратора"
"telegramChatIdDesc" = "Ідентифікатори чату адміністратора Telegram. (розділені комами) (отримайте тут @userinfobot) або (використовуйте команду '/id' у боті)"
"telegramNotifyTime" = "Час сповіщення"
"telegramNotifyTimeDesc" = "Час повідомлення бота Telegram, встановлений для періодичних звітів. (використовуйте формат часу crontab)"
"tgNotifyBackup" = "Резервне копіювання бази даних"
"tgNotifyBackupDesc" = "Надіслати файл резервної копії бази даних зі звітом."
"tgNotifyLogin" = "Сповіщення про вхід"
"tgNotifyLoginDesc" = "Отримувати сповіщення про ім'я користувача, IP-адресу та час щоразу, коли хтось намагається увійти у вашу веб-панель."
"sessionMaxAge" = "Тривалість сеансу"
"sessionMaxAgeDesc" = "Тривалість, протягом якої ви можете залишатися в системі. (одиниця: хвилина)"
"expireTimeDiff" = "Повідомлення про дату закінчення"
"expireTimeDiffDesc" = "Отримувати сповіщення про термін дії при досягненні цього порогу. (одиниця: день)"
"trafficDiff" = "Повідомлення про обмеження трафіку"
"trafficDiffDesc" = "Отримувати сповіщення про обмеження трафіку при досягненні цього порогу. (одиниця: ГБ)"
"tgNotifyCpu" = "Сповіщення про завантаження ЦП"
"tgNotifyCpuDesc" = "Отримувати сповіщення, якщо навантаження ЦП перевищує це порогове значення. (одиниця: %)"
"timeZone" = "Часовий пояс"
"timeZoneDesc" = "Заплановані завдання виконуватимуться на основі цього часового поясу."
"subSettings" = "Підписка"
"subEnable" = "Увімкнути службу підписки"
"subEnableDesc" = "Вмикає службу підписки."
"subListen" = "Слухати IP"
"subListenDesc" = "IP-адреса для служби підписки. (залиште порожнім, щоб слухати всі IP-адреси)"
"subPort" = "Слухати порт"
"subPortDesc" = "Номер порту для служби підписки. (має бути невикористаний порт)"
"subCertPath" = "Шлях відкритого ключа"
"subCertPathDesc" = "Шлях до файлу відкритого ключа для служби підписки. (починається з /)"
"subKeyPath" = "Шлях приватного ключа"
"subKeyPathDesc" = "Шлях до файлу приватного ключа для служби підписки. (починається з /)"
"subPath" = "Шлях URI"
"subPathDesc" = "Шлях URI для служби підписки. (починається з / і закінчується /)"
"subDomain" = "Домен прослуховування"
"subDomainDesc" = "Ім'я домену для служби підписки. (залиште порожнім, щоб слухати всі домени та IP-адреси)"
"subUpdates" = "Інтервали оновлення"
"subUpdatesDesc" = "Інтервали оновлення URL-адреси підписки в клієнтських програмах. (одиниця: година)"
"subEncrypt" = "Закодувати"
"subEncryptDesc" = "Повернений вміст послуги підписки матиме кодування Base64."
"subShowInfo" = "Показати інформацію про використання"
"subShowInfoDesc" = "Залишок трафіку та дата відображатимуться в клієнтських програмах."
"subURI" = "URI зворотного проксі"
"subURIDesc" = "URI до URL-адреси підписки для використання за проксі."
"fragment" = "Фрагментація"
"fragmentDesc" = "Увімкнути фрагментацію для пакету привітання TLS"
[pages.xray]
"title" = "Xray конфігурації"
"save" = "Зберегти"
"restart" = "Перезапустити Xray"
"basicTemplate" = "Базовий шаблон"
"advancedTemplate" = "Додатково"
"generalConfigs" = "Загальні конфігурації"
"generalConfigsDesc" = "Ці параметри визначатимуть загальні налаштування."
"logConfigs" = "Журнал"
"logConfigsDesc" = "Журнали можуть вплинути на ефективність вашого сервера. Рекомендується вмикати його з розумом лише у випадку ваших потреб"
"blockConfigs" = "Захисний екран"
"blockConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретних запитуваних протоколів і веб-сайтів."
"blockCountryConfigs" = "Заблокувати країну"
"blockCountryConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретної запитуваної країни."
"directCountryConfigs" = "Пряма країна"
"directCountryConfigsDesc" = "Ці параметри безпосередньо перенаправлятимуть трафік на основі конкретної запитуваної країни."
"ipv4Configs" = "Маршрутизація IPv4"
"ipv4ConfigsDesc" = "Ці параметри спрямовуватимуть трафік на основі певного призначення через IPv4."
"warpConfigs" = "WARP маршрутизація"
"warpConfigsDesc" = "Ці параметри маршрутизуватимуть трафік на основі певного пункту призначення через WARP."
"Template" = "Шаблон розширеної конфігурації Xray"
"TemplateDesc" = "Остаточний конфігураційний файл Xray буде створено на основі цього шаблону."
"FreedomStrategy" = "Стратегія протоколу свободи"
"FreedomStrategyDesc" = "Установити стратегію виведення для мережі в протоколі свободи."
"RoutingStrategy" = "Загальна стратегія маршрутизації"
"RoutingStrategyDesc" = "Установити загальну стратегію маршрутизації трафіку для вирішення всіх запитів."
"Torrent" = "Блокувати протокол BitTorrent"
"TorrentDesc" = "Блокує протокол BitTorrent."
"PrivateIp" = "Блокувати підключення до приватних IP-адрес"
"PrivateIpDesc" = "Блокує встановлення підключень до приватних діапазонів IP."
"Ads" = "Блокувати рекламу"
"AdsDesc" = "Блокує рекламні веб-сайти."
"Family" = "Захист сім'ї"
"FamilyDesc" = "Блокує вміст для дорослих і веб-сайти з шкідливими програмами."
"Security" = "Щит безпеки"
"SecurityDesc" = "Блокує веб-сайти шкідливого програмного забезпечення, фішингу та майнерів."
"Speedtest" = "Заблокувати Speedtest"
"SpeedtestDesc" = "Блокує підключення до веб-сайтів для тестування швидкості."
"IRIp" = "Блокувати підключення до IP-адрес Ірану"
"IRIpDesc" = "Блокує встановлення з'єднань з діапазонами IP Ірану."
"IRDomain" = "Блокувати підключення до доменів Ірану"
"IRDomainDesc" = "Блокує встановлення з'єднань з доменами Ірану."
"ChinaIp" = "Блокувати підключення до IP-адрес Китаю"
"ChinaIpDesc" = "Блокує встановлення з'єднань із діапазонами IP-адрес Китаю."
"ChinaDomain" = "Блокувати підключення до китайських доменів"
"ChinaDomainDesc" = "Блокує встановлення підключень до доменів Китаю."
"RussiaIp" = "Блокувати підключення до російських IP-адрес"
"RussiaIpDesc" = "Блокує встановлення з'єднань з діапазонами IP-адрес Росії."
"RussiaDomain" = "Блокувати підключення до російських доменів"
"RussiaDomainDesc" = "Блокує встановлення з'єднань з російськими доменами."
"VNIp" = "Блокувати підключення до IP-адрес В'єтнаму"
"VNIpDesc" = "Блокує встановлення з'єднань із діапазонами IP-адрес В'єтнаму."
"VNDomain" = "Блокувати підключення до доменів В'єтнаму"
"VNDomainDesc" = "Блокує встановлення з'єднань із доменами В'єтнаму."
"DirectIRIp" = "Пряме підключення до IP-адрес Ірану"
"DirectIRIpDesc" = "Безпосередньо встановлює з'єднання з діапазонами IP Ірану."
"DirectIRDomain" = "Пряме підключення до доменів Ірану"
"DirectIRDomainDesc" = "Безпосередньо встановлює підключення до доменів Ірану."
"DirectChinaIp" = "Пряме підключення до китайських IP-адрес"
"DirectChinaIpDesc" = "Безпосередньо встановлює підключення до IP-діапазонів Китаю."
"DirectChinaDomain" = "Пряме підключення до китайських доменів"
"DirectChinaDomainDesc" = "Безпосередньо встановлює підключення до доменів Китаю."
"DirectRussiaIp" = "Пряме підключення до IP-адрес Росії"
"DirectRussiaIpDesc" = "Безпосередньо встановлює з'єднання з діапазонами IP-адрес Росії."
"DirectRussiaDomain" = "Пряме підключення до російських доменів"
"DirectRussiaDomainDesc" = "Безпосередньо встановлює підключення до російських доменів."
"DirectVNIp" = "Пряме підключення до IP-адрес В'єтнаму"
"DirectVNIpDesc" = "Безпосередньо встановлює з'єднання з діапазонами IP-адрес В'єтнаму."
"DirectVNDomain" = "Пряме підключення до доменів В'єтнаму"
"DirectVNDomainDesc" = "Безпосередньо встановлює з'єднання з доменами В'єтнаму."
"GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "Направляє трафік до Google через IPv4."
"NetflixIPv4" = "Netflix"
"NetflixIPv4Desc" = "Направляє трафік до Netflix через IPv4."
"GoogleWARP" = "Google"
"GoogleWARPDesc" = "Додати маршрутизацію для Google через WARP."
"OpenAIWARP" = "ChatGPT"
"OpenAIWARPDesc" = "Направляє трафік до ChatGPT через WARP."
"NetflixWARP" = "Netflix"
"NetflixWARPDesc" = "Направляє трафік до Netflix через WARP."
"MetaWARP" = "Meta"
"MetaWARPDesc" = "Направляє трафік до Meta (Instagram, Facebook, WhatsApp, Threads,...) через WARP."
"AppleWARP" = "Apple"
"AppleWARPDesc" = "Направляє трафік до Apple через WARP."
"RedditWARP" = "Reddit"
"RedditWARPDesc" = "Направляє трафік до Reddit через WARP."
"SpotifyWARP" = "Spotify"
"SpotifyWARPDesc" = "Направляє трафік до Spotify через WARP."
"IRWARP" = "Іранські домени"
"IRWARPDesc" = "Направляє трафік до доменів Ірану через WARP"
"Inbounds" = "Вхідні"
"InboundsDesc" = "Прийняття певних клієнтів."
"Outbounds" = "Вихід"
"Balancers" = "Балансери"
"OutboundsDesc" = "Встановити шлях вихідного трафіку."
"Routings" = "Правила маршрутизації"
"RoutingsDesc" = "Пріоритет кожного правила важливий!"
"completeTemplate" = "Усі"
"logLevel" = "Рівень журналу"
"logLevelDesc" = "Рівень журналу для журналів помилок із зазначенням інформації, яку потрібно записати."
"accessLog" = "Журнал доступу"
"accessLogDesc" = "Шлях до файлу журналу доступу. Спеціальне значення 'none' вимикає журнали доступу"
"errorLog" = "Журнал помилок"
"errorLogDesc" = "Шлях до файлу журналу помилок. Спеціальне значення 'none' вимикає журнали помилок"
[pages.xray.rules]
"first" = "Перший"
"last" = "Останній"
"up" = "Вгору"
"down" = "Вниз"
"source" = "Джерело"
"dest" = "Пункт призначення"
"inbound" = "Вхідний"
"outbound" = "Вихідний"
"balancer" = "Балансувальник"
"info" = "Інформація"
"add" = "Додати правило"
"edit" = "Редагувати правило"
"useComma" = "Елементи, розділені комами"
[pages.xray.outbound]
"addOutbound" = "Додати вихідний"
"addReverse" = "Додати реверс"
"editOutbound" = "Редагувати вихідні"
"editReverse" = "Редагувати реверс"
"tag" = "Тег"
"tagDesc" = "Унікальний тег"
"address" = "Адреса"
"reverse" = "Зворотний"
"domain" = "Домен"
"type" = "Тип"
"bridge" = "Міст"
"portal" = "Портал"
"intercon" = "Взаємозв'язок"
[pages.xray.balancer]
"addBalancer" = "Додати балансир"
"editBalancer" = "Редагувати балансир"
"balancerStrategy" = "Стратегія"
"balancerSelectors" = "Селектори"
"tag" = "Тег"
"tagDesc" = "Унікальний тег"
"balancerDesc" = "Неможливо використовувати balancerTag і outboundTag одночасно. Якщо використовувати одночасно, працюватиме лише outboundTag."
[pages.xray.wireguard]
"secretKey" = "Приватний ключ"
"publicKey" = "Публічний ключ"
"allowedIPs" = "Дозволені IP-адреси"
"endpoint" = "Кінцева точка"
"psk" = "Спільний ключ"
"domainStrategy" = "Стратегія домену"
[pages.xray.dns]
"enable" = "Увімкнути DNS"
"enableDesc" = "Увімкнути вбудований DNS-сервер"
"strategy" = "Стратегія запиту"
"strategyDesc" = "Загальна стратегія вирішення доменних імен"
"add" = "Додати сервер"
"edit" = "Редагувати сервер"
"domains" = "Домени"
[pages.xray.fakedns]
"add" = "Додати підроблений DNS"
"edit" = "Редагувати підроблений DNS"
"ipPool" = "Підмережа IP-пулу"
"poolSize" = "Розмір пулу"
[pages.settings.security]
"admin" = "Адміністратор"
"secret" = "Секретний маркер"
"loginSecurity" = "Безпечний вхід"
"loginSecurityDesc" = "Додає додатковий рівень автентифікації для забезпечення більшої безпеки."
"secretToken" = "Секретний маркер"
"secretTokenDesc" = "Будь ласка, надійно зберігайте цей маркер у безпечному місці. Цей маркер потрібен для входу, і його неможливо відновити."
[pages.settings.toasts]
"modifySettings" = "Змінити налаштування"
"getSettings" = "Отримати налаштування"
"modifyUser" = "Змінити адміністратора"
"originalUserPassIncorrect" = "Поточне ім'я користувача або пароль недійсні"
"userPassMustBeNotEmpty" = "Нове ім'я користувача та пароль порожні"
[tgbot]
"keyboardClosed" = "❌ Спеціальна клавіатура закрита!"
"noResult" = "❗ Немає результату!"
"noQuery" = "❌ Запит не знайдено! Скористайтеся командою ще раз!"
"wentWrong" = "❌ Щось пішло не так!"
"noIpRecord" = "❗ Немає IP-запису!"
"noInbounds" = "❗ Вхідних не знайдено!"
"unlimited" = "♾ Необмежений (скинути)"
"add" = "Додати"
"month" = "Місяць"
"months" = "Місяці"
"day" = "День"
"days" = "Дні"
"hours" = "Годинник"
"unknown" = "Невідомо"
"inbounds" = "Вхідні"
"clients" = "Клієнти"
"offline" = "🔴 Офлайн"
"online" = "🟢 Онлайн"
[tgbot.commands]
"unknown" = "❗ Невідома команда."
"pleaseChoose" = "👇 Будь ласка, виберіть:\r\n"
"help" = "🤖 Ласкаво просимо до цього бота! Він розроблений, щоб надавати певні дані з веб-панелі та дозволяє вносити зміни за потреби.\r\n\r\n"
"start" = "👋 Привіт <i>{{ .Firstname }}</i>.\r\n"
"welcome" = "🤖 Ласкаво просимо до <b>{{ .Hostname }}</b> бота керування.\r\n"
"status" = "✅ Бот в порядку!"
"usage" = "❗ Введіть текст для пошуку!"
"getID" = "🆔 Ваш ідентифікатор: <code>{{ .ID }}</code>"
"helpAdminCommands" = "Для пошуку електронної пошти клієнта:\r\n<code>/usage [Email]</code>\r\n\r\nДля пошуку вхідних листів (зі статистикою клієнта):\r\n<code>/inbound [Примітка]</code>"
"helpClientCommands" = "Для пошуку статистики скористайтеся такою командою:\r\n\r\n<code>/usage [Email]</code>"
[tgbot.messages]
"cpuThreshold" = "🔴 Навантаження ЦП {{ .Percent }}% перевищує порогове значення {{ .Threshold }}%"
"selectUserFailed" = "❌ Помилка під час вибору користувача!"
"userSaved" = "✅ Користувача Telegram збережено."
"loginSuccess" = "✅ Успішно ввійшли в панель\r\n"
"loginFailed" = "❗️ Помилка входу в панель.\r\n"
"report" = "🕰 Заплановані звіти: {{ .RunTime }}\r\n"
"datetime" = "⏰ Дата й час: {{ .DateTime }}\r\n"
"hostname" = "💻 Хост: {{ .Hostname }}\r\n"
"version" = "🚀 3X-UI Версія: {{ .Version }}\r\n"
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
"ip" = "🌐 IP: {{ .IP }}\r\n"
"ips" = "🔢 IP-адреси:\r\n{{ .IPs }}\r\n"
"serverUpTime" = "⏳ Час роботи: {{ .UpTime }} {{ .Unit }}\r\n"
"serverLoad" = "📈 Завантаження системи: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n"
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
"udpCount" = "🔸 UDP: {{ .Count }}\r\n"
"traffic" = "🚦 Трафік: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
"xrayStatus" = " Статус: {{ .State }}\r\n"
"username" = "👤 Ім'я користувача: {{ .Username }}\r\n"
"time" = "⏰ Час: {{ .Time }}\r\n"
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
"port" = "🔌 Порт: {{ .Port }}\r\n"
"expire" = "📅 Дата закінчення: {{ .Time }}\r\n"
"expireIn" = "📅 Термін дії: {{ .Time }}\r\n"
"active" = "💡 Активний: {{ .Enable }}\r\n"
"enabled" = "🚨 Увімкнено: {{ .Enable }}\r\n"
"online" = "🌐 Стан підключення: {{ .Status }}\r\n"
"email" = "📧 Електронна пошта: {{ .Email }}\r\n"
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
"download" = "🔽 Download: ↓{{ .Download }}\r\n"
"total" = "📊 Всього: ↑↓{{ .UpDown }} / {{ .Total }}\r\n"
"TGUser" = "👤 Користувач Telegram: {{ .TelegramID }}\r\n"
"exhaustedMsg" = "🚨 Вичерпано {{ .Type }}:\r\n"
"exhaustedCount" = "🚨 Вичерпано кількість {{ .Type }} count:\r\n"
"onlinesCount" = "🌐 Онлайн-клієнти: {{ .Count }}\r\n"
"disabled" = "🛑 Вимкнено: {{ .Disabled }}\r\n"
"depleteSoon" = "🔜 Скоро вичерпається: {{ .Deplete }}\r\n\r\n"
"backupTime" = "🗄 Час резервного копіювання: {{ .Time }}\r\n"
"refreshedOn" = "\r\n📋🔄 Оновлено: {{ .Time }}\r\n\r\n"
"yes" = "✅ Так"
"no" = "❌ Ні"
[tgbot.buttons]
"closeKeyboard" = "❌ Закрити клавіатуру"
"cancel" = "❌ Скасувати"
"cancelReset" = "❌ Скасувати скидання"
"cancelIpLimit" = "❌ Скасувати обмеження IP"
"confirmResetTraffic" = "✅ Підтвердити скидання трафіку?"
"confirmClearIps" = "✅ Підтвердити очищення IP-адрес?"
"confirmRemoveTGUser" = "✅ Підтвердити видалення користувача Telegram?"
"confirmToggle" = "✅ Підтвердити ввімкнути/вимкнути користувача?"
"dbBackup" = "Отримати резервну копію БД"
"serverUsage" = "Використання сервера"
"getInbounds" = "Отримати вхідні"
"depleteSoon" = "Скоро вичерпати"
"clientUsage" = "Отримати використання"
"onlines" = "Онлайн-клієнти"
"commands" = "Команди"
"refresh" = "🔄 Оновити"
"clearIPs" = "❌ Очистити IP-адреси"
"removeTGUser" = "❌ Видалити користувача Telegram"
"selectTGUser" = "👤 Виберіть користувача Telegram"
"selectOneTGUser" = "👤 Виберіть користувача Telegram:"
"resetTraffic" = "📈 Скинути трафік"
"resetExpire" = "📅 Змінити термін дії"
"ipLog" = "🔢 IP журнал"
"ipLimit" = "🔢 IP Ліміт"
"setTGUser" = "👤 Встановити користувача Telegram"
"toggle" = "🔘 Увімкнути / Вимкнути"
"custom" = "🔢 Custom"
"confirmNumber" = "✅ Підтвердити: {{ .Num }}"
"confirmNumberAdd" = "✅ Підтвердити додавання: {{ .Num }}"
"limitTraffic" = "🚧 Ліміт трафіку"
"getBanLogs" = "Отримати журнали заборон"
[tgbot.answers]
"successfulOperation" = "✅ Операція успішна!"
"errorOperation" = "❗ Помилка в роботі."
"getInboundsFailed" = "❌ Не вдалося отримати вхідні повідомлення."
"canceled" = "❌ {{ .Email }}: Операцію скасовано."
"clientRefreshSuccess" = "✅ {{ .Email }}: Клієнт успішно оновлено."
"IpRefreshSuccess" = "✅ {{ .Email }}: IP-адреси успішно оновлено."
"TGIdRefreshSuccess" = "✅ {{ .Email }}: Користувач Telegram клієнта успішно оновлено."
"resetTrafficSuccess" = "✅ {{ .Email }}: Трафік скинуто успішно."
"setTrafficLimitSuccess" = "✅ {{ .Email }}: Ліміт трафіку успішно збережено."
"expireResetSuccess" = "✅ {{ .Email }}: Успішно скинуто дні закінчення терміну дії."
"resetIpSuccess" = "✅ {{ .Email }}: IP обмеження {{ .Count }} успішно збережено."
"clearIpSuccess" = "✅ {{ .Email }}: IP успішно очищено."
"getIpLog" = "✅ {{ .Email }}: Отримати IP-журнал."
"getUserInfo" = "✅ {{ .Email }}: Отримати інформацію про користувача Telegram."
"removedTGUserSuccess" = "✅ {{ .Email }}: Користувача Telegram видалено успішно."
"enableSuccess" = "✅ {{ .Email }}: Увімкнути успішно."
"disableSuccess" = "✅ {{ .Email }}: Успішно вимкнено."
"askToAddUserId" = "Вашу конфігурацію не знайдено!\r\nБудь ласка, попросіть свого адміністратора використовувати ваш ідентифікатор Telegram у вашій конфігурації.\r\n\r\nВаш ідентифікатор користувача: <code>{{ .TgUserID }}</code>"

View file

@ -42,7 +42,7 @@
"online" = "Trực tuyến" "online" = "Trực tuyến"
"domainName" = "Tên miền" "domainName" = "Tên miền"
"monitor" = "Listening IP" "monitor" = "Listening IP"
"certificate" = "Chứng chỉ" "certificate" = "Chứng chỉ số"
"fail" = "Thất bại" "fail" = "Thất bại"
"success" = "Thành công" "success" = "Thành công"
"getVersion" = "Lấy phiên bản" "getVersion" = "Lấy phiên bản"
@ -54,7 +54,12 @@
"security" = "Bảo vệ" "security" = "Bảo vệ"
"secAlertTitle" = "Cảnh báo an ninh-Tiếng Việt by Ohoang7" "secAlertTitle" = "Cảnh báo an ninh-Tiếng Việt by Ohoang7"
"secAlertSsl" = "Kết nối này không an toàn; Vui lòng không nhập thông tin nhạy cảm cho đến khi TLS được kích hoạt để bảo vệ dữ liệu của Bạn" "secAlertSsl" = "Kết nối này không an toàn; Vui lòng không nhập thông tin nhạy cảm cho đến khi TLS được kích hoạt để bảo vệ dữ liệu của Bạn"
"secAlertConf" = "Một số cấu hình nhất định đã được xác định là dễ bị tấn công, thúc đẩy hành động ngay lập tức để củng cố các giao thức bảo mật và bảo vệ chống lại các vi phạm bảo mật tiềm ẩn." "secAlertConf" = "Một số cài đặt có thể dễ bị tấn công. Đề xuất tăng cường các giao thức bảo mật để ngăn chặn các vi phạm tiềm ẩn."
"secAlertSSL" = "Bảng điều khiển thiếu kết nối an toàn. Vui lòng cài đặt chứng chỉ TLS để bảo vệ dữ liệu."
"secAlertPanelPort" = "Cổng mặc định của bảng điều khiển có thể dễ bị tấn công. Vui lòng cấu hình một cổng ngẫu nhiên hoặc cụ thể."
"secAlertPanelURI" = "Đường dẫn URI mặc định của bảng điều khiển không an toàn. Vui lòng cấu hình một đường dẫn URI phức tạp."
"secAlertSubURI" = "Đường dẫn URI mặc định của đăng ký không an toàn. Vui lòng cấu hình một đường dẫn URI phức tạp."
"secAlertSubJsonURI" = "Đường dẫn URI JSON mặc định của đăng ký không an toàn. Vui lòng cấu hình một đường dẫn URI phức tạp."
[menu] [menu]
"dashboard" = "Trạng thái hệ thống" "dashboard" = "Trạng thái hệ thống"
@ -143,10 +148,8 @@
"noRecommendKeepDefault" = "Không yêu cầu đặc biệt để giữ nguyên cài đặt mặc định" "noRecommendKeepDefault" = "Không yêu cầu đặc biệt để giữ nguyên cài đặt mặc định"
"certificatePath" = "Đường dẫn tập" "certificatePath" = "Đường dẫn tập"
"certificateContent" = "Nội dung tập" "certificateContent" = "Nội dung tập"
"publicKeyPath" = "Đường dẫn khóa công khai" "publicKey" = "Khóa công khai"
"publicKeyContent" = "Nội dung khóa công khai" "privatekey" = "Khóa cá nhân"
"keyPath" = "Đường dẫn khóa riêng tư"
"keyContent" = "Nội dung khóa riêng tư"
"clickOnQRcode" = "Nhấn vào Mã QR để sao chép" "clickOnQRcode" = "Nhấn vào Mã QR để sao chép"
"client" = "Người dùng" "client" = "Người dùng"
"export" = "Xuất liên kết" "export" = "Xuất liên kết"
@ -197,7 +200,7 @@
"last" = "Cuối cùng" "last" = "Cuối cùng"
"prefix" = "Tiền tố" "prefix" = "Tiền tố"
"postfix" = "Hậu tố" "postfix" = "Hậu tố"
"delayedStart" = "Bắt đầu sau khi sử dụng lần đầu" "delayedStart" = "Bắt đầu ở Lần Đầu"
"expireDays" = "Khoảng thời gian" "expireDays" = "Khoảng thời gian"
"days" = "ngày" "days" = "ngày"
"renew" = "Tự động gia hạn" "renew" = "Tự động gia hạn"

View file

@ -42,7 +42,7 @@
"online" = "在线" "online" = "在线"
"domainName" = "域名" "domainName" = "域名"
"monitor" = "监听" "monitor" = "监听"
"certificate" = "证书" "certificate" = "Digitales Zertifikat"
"fail" = "失败" "fail" = "失败"
"success" = "成功" "success" = "成功"
"getVersion" = "获取版本" "getVersion" = "获取版本"
@ -54,7 +54,12 @@
"security" = "安全" "security" = "安全"
"secAlertTitle" = "安全警报" "secAlertTitle" = "安全警报"
"secAlertSsl" = "此连接不安全;在激活 TLS 进行数据保护之前,请勿输入敏感信息" "secAlertSsl" = "此连接不安全;在激活 TLS 进行数据保护之前,请勿输入敏感信息"
"secAlertConf" = "某些配置已被确定为容易受到攻击,促使立即采取行动以加强安全协议并防范潜在的安全漏洞。" "secAlertConf" = "某些设置容易受到攻击。建议加强安全协议以防止潜在的违规行为。"
"secAlertSSL" = "面板缺乏安全连接。请安装 TLS 证书以保护数据。"
"secAlertPanelPort" = "面板默认端口存在漏洞。请配置随机或特定端口。"
"secAlertPanelURI" = "面板默认 URI 路径不安全。请配置复杂的 URI 路径。"
"secAlertSubURI" = "订阅默认 URI 路径不安全。请配置复杂的 URI 路径。"
"secAlertSubJsonURI" = "订阅 JSON 默认 URI 路径不安全。请配置复杂的 URI 路径。"
[menu] [menu]
"dashboard" = "系统状态" "dashboard" = "系统状态"
@ -143,10 +148,8 @@
"noRecommendKeepDefault" = "没有特殊需求保持默认即可" "noRecommendKeepDefault" = "没有特殊需求保持默认即可"
"certificatePath" = "文件路径" "certificatePath" = "文件路径"
"certificateContent" = "文件内容" "certificateContent" = "文件内容"
"publicKeyPath" = "公钥文件路径" "publicKey" = "公钥"
"publicKeyContent" = "公钥内容" "privatekey" = "私钥"
"keyPath" = "密钥文件路径"
"keyContent" = "密钥内容"
"clickOnQRcode" = "点击二维码复制" "clickOnQRcode" = "点击二维码复制"
"client" = "客户" "client" = "客户"
"export" = "导出链接" "export" = "导出链接"

View file

@ -337,19 +337,17 @@ func (s *Server) Start() (err error) {
} }
if certFile != "" || keyFile != "" { if certFile != "" || keyFile != "" {
cert, err := tls.LoadX509KeyPair(certFile, keyFile) cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil { if err == nil {
listener.Close() c := &tls.Config{
return err Certificates: []tls.Certificate{cert},
}
listener = network.NewAutoHttpsListener(listener)
listener = tls.NewListener(listener, c)
logger.Info("web server run https on", listener.Addr())
} else {
logger.Error("error in loading certificates: ", err)
logger.Info("web server run http on", listener.Addr())
} }
c := &tls.Config{
Certificates: []tls.Certificate{cert},
}
listener = network.NewAutoHttpsListener(listener)
listener = tls.NewListener(listener, c)
}
if certFile != "" || keyFile != "" {
logger.Info("web server run https on", listener.Addr())
} else { } else {
logger.Info("web server run http on", listener.Addr()) logger.Info("web server run http on", listener.Addr())
} }

10
x-ui.sh
View file

@ -947,8 +947,8 @@ run_speedtest() {
} }
create_iplimit_jails() { create_iplimit_jails() {
# Use default bantime if not passed => 30 minutes # Use default bantime if not passed => 15 minutes
local bantime="${1:-30}" local bantime="${1:-15}"
# Uncomment 'allowipv6 = auto' in fail2ban.conf # Uncomment 'allowipv6 = auto' in fail2ban.conf
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
@ -959,8 +959,8 @@ enabled=true
filter=3x-ipl filter=3x-ipl
action=3x-ipl action=3x-ipl
logpath=${iplimit_log_path} logpath=${iplimit_log_path}
maxretry=4 maxretry=2
findtime=60 findtime=32
bantime=${bantime}m bantime=${bantime}m
EOF EOF
@ -973,7 +973,7 @@ EOF
cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
[INCLUDES] [INCLUDES]
before = iptables-common.conf before = iptables-allports.conf
[Definition] [Definition]
actionstart = <iptables> -N f2b-<name> actionstart = <iptables> -N f2b-<name>