Compare commits
119 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a078335d4c | ||
|
|
025545f0a4 | ||
|
|
85c2d2ecd7 | ||
|
|
2026bd7546 | ||
|
|
f286080edd | ||
|
|
d36118fd95 | ||
|
|
1ee6e39bc3 | ||
|
|
d8e4f85e03 | ||
|
|
dd2ce332a6 | ||
|
|
41b0fe4af4 | ||
|
|
c841c1f148 | ||
|
|
3b3d70aeaa | ||
|
|
2a311287bf | ||
|
|
dbb17f5829 | ||
|
|
93412f8a21 | ||
|
|
b63f305737 | ||
|
|
2cd6305fe8 | ||
|
|
37b7c89d5e | ||
|
|
df84b2fa46 | ||
|
|
d0a99a469b | ||
|
|
47792cdbd3 | ||
|
|
4df056f6a2 | ||
|
|
94ac29ba7f | ||
|
|
3a174144c4 | ||
|
|
8b64976eef | ||
|
|
56336cbcef | ||
|
|
43bc53a10e | ||
|
|
babe3e8788 | ||
|
|
c247cd60b8 | ||
|
|
06507f0e1f | ||
|
|
6a39ee037d | ||
|
|
96f9c73f5f | ||
|
|
15f84de363 | ||
|
|
2d07b64839 | ||
|
|
1ef299951e | ||
|
|
83fed835e1 | ||
|
|
6e29022998 | ||
|
|
5e77dcb198 | ||
|
|
2d7607165b | ||
|
|
c9e4918494 | ||
|
|
63b166ce40 | ||
|
|
2ab1726131 | ||
|
|
7dd5449cf4 | ||
|
|
ded780b11a | ||
|
|
2e44501573 | ||
|
|
3e7770ad97 | ||
|
|
0c380aebdd | ||
|
|
93f6ba897c | ||
|
|
b93adb7435 | ||
|
|
538fdb9860 | ||
|
|
c4ed595a1c | ||
|
|
5acfdf584e | ||
|
|
62ceaeba8f | ||
|
|
3c688d85e7 | ||
|
|
99a4cf4179 | ||
|
|
7aa242b64e | ||
|
|
cbb3c009cc | ||
|
|
d8e8195271 | ||
|
|
e31ec2cade | ||
|
|
af42723213 | ||
|
|
94117f94e8 | ||
|
|
b946f7a1d4 | ||
|
|
aaa0f66a37 | ||
|
|
ebbe2c8e5b | ||
|
|
0fdd4dcfb9 | ||
|
|
78fb3844fe | ||
|
|
9452f4fc15 | ||
|
|
5996f92fe5 | ||
|
|
4cabe80462 | ||
|
|
1cf0d52433 | ||
|
|
3cef5142d9 | ||
|
|
ac8af25cef | ||
|
|
aee2107f30 | ||
|
|
7971082beb | ||
|
|
d60290d0a1 | ||
|
|
0b1bc1908b | ||
|
|
a342ef9e90 | ||
|
|
317e4f1fdd | ||
|
|
fd82e701b6 | ||
|
|
046be5dd0f | ||
|
|
fa4c954444 | ||
|
|
03e1434051 | ||
|
|
5725420197 | ||
|
|
fd64ae5c85 | ||
|
|
ff59bb60ce | ||
|
|
7ec646fd0d | ||
|
|
902368fc03 | ||
|
|
57827225b4 | ||
|
|
d406d2925a | ||
|
|
2f2876ec90 | ||
|
|
20d00d31a1 | ||
|
|
48b327ccb5 | ||
|
|
9cc893a396 | ||
|
|
91e9c2d6b2 | ||
|
|
6bfaad48fd | ||
|
|
10779201f5 | ||
|
|
5b20505515 | ||
|
|
ed5db9390d | ||
|
|
5ea3333367 | ||
|
|
9a95530742 | ||
|
|
b5eb368b89 | ||
|
|
1f026d0039 | ||
|
|
056de927da | ||
|
|
4596a981d6 | ||
|
|
5cebf8faef | ||
|
|
2cf2da8ae2 | ||
|
|
da82d14c18 | ||
|
|
8da966a7f7 | ||
|
|
81b96507d0 | ||
|
|
0986791fb3 | ||
|
|
d4317a0a62 | ||
|
|
5fc7f132a1 | ||
|
|
bdb371367e | ||
|
|
407bebad05 | ||
|
|
10d85f9855 | ||
|
|
98c11dd83e | ||
|
|
86f6050697 | ||
|
|
e488dc18b1 | ||
|
|
7ed106a0f0 |
2
.github/workflows/docker.yml
vendored
@@ -50,6 +50,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
platforms: linux/amd64,linux/arm64/v8
|
platforms: linux/amd64,linux/arm64/v8, linux/arm/v7
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
|||||||
170
.github/workflows/release.yml
vendored
@@ -1,4 +1,5 @@
|
|||||||
name: Release X-ui
|
name: Release X-UI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
@@ -6,124 +7,81 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linuxamd64build:
|
build:
|
||||||
name: build x-ui amd64 version
|
strategy:
|
||||||
|
matrix:
|
||||||
|
platform: [amd64, arm64, arm]
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout repository
|
||||||
- name: Set up Go
|
uses: actions/checkout@v4.1.1
|
||||||
uses: actions/setup-go@v4
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5.0.0
|
||||||
with:
|
with:
|
||||||
go-version: '1.21'
|
go-version: '1.21'
|
||||||
- name: build linux amd64 version
|
|
||||||
run: |
|
- name: Install dependencies for arm64 and arm
|
||||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o xui-release -v main.go
|
if: matrix.platform == 'arm64' || matrix.platform == 'arm'
|
||||||
mkdir x-ui
|
|
||||||
cp xui-release x-ui/xui-release
|
|
||||||
cp x-ui.service x-ui/x-ui.service
|
|
||||||
cp x-ui.sh x-ui/x-ui.sh
|
|
||||||
cd x-ui
|
|
||||||
mv xui-release x-ui
|
|
||||||
mkdir bin
|
|
||||||
cd bin
|
|
||||||
wget https://github.com/XTLS/Xray-core/releases/download/v1.8.6/Xray-linux-64.zip
|
|
||||||
unzip Xray-linux-64.zip
|
|
||||||
rm -f Xray-linux-64.zip geoip.dat geosite.dat iran.dat
|
|
||||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
|
||||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
|
||||||
wget https://github.com/MasterKia/iran-hosted-domains/releases/latest/download/iran.dat
|
|
||||||
mv xray xray-linux-amd64
|
|
||||||
cd ..
|
|
||||||
cd ..
|
|
||||||
- name: package
|
|
||||||
run: tar -zcvf x-ui-linux-amd64.tar.gz x-ui
|
|
||||||
- name: upload
|
|
||||||
uses: svenstaro/upload-release-action@2.7.0
|
|
||||||
with:
|
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
tag: ${{ github.ref }}
|
|
||||||
file: x-ui-linux-amd64.tar.gz
|
|
||||||
asset_name: x-ui-linux-amd64.tar.gz
|
|
||||||
prerelease: true
|
|
||||||
linuxarm64build:
|
|
||||||
name: build x-ui arm64 version
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version: '1.21'
|
|
||||||
- name: build linux arm64 version
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt install gcc-aarch64-linux-gnu
|
sudo apt install gcc-aarch64-linux-gnu
|
||||||
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc go build -o xui-release -v main.go
|
if [ "${{ matrix.platform }}" == "arm" ]; then
|
||||||
mkdir x-ui
|
sudo apt install gcc-arm-linux-gnueabihf
|
||||||
cp xui-release x-ui/xui-release
|
fi
|
||||||
cp x-ui.service x-ui/x-ui.service
|
|
||||||
cp x-ui.sh x-ui/x-ui.sh
|
- name: Build x-ui
|
||||||
cd x-ui
|
|
||||||
mv xui-release x-ui
|
|
||||||
mkdir bin
|
|
||||||
cd bin
|
|
||||||
wget https://github.com/xtls/xray-core/releases/download/v1.8.6/Xray-linux-arm64-v8a.zip
|
|
||||||
unzip Xray-linux-arm64-v8a.zip
|
|
||||||
rm -f Xray-linux-64.zip geoip.dat geosite.dat iran.dat
|
|
||||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
|
||||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
|
||||||
wget https://github.com/Masterkia/iran-hosted-domains/releases/latest/download/iran.dat
|
|
||||||
mv xray xray-linux-arm64
|
|
||||||
cd ..
|
|
||||||
cd ..
|
|
||||||
- name: package
|
|
||||||
run: tar -zcvf x-ui-linux-arm64.tar.gz x-ui
|
|
||||||
- name: upload
|
|
||||||
uses: svenstaro/upload-release-action@2.7.0
|
|
||||||
with:
|
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
tag: ${{ github.ref }}
|
|
||||||
file: x-ui-linux-arm64.tar.gz
|
|
||||||
asset_name: x-ui-linux-arm64.tar.gz
|
|
||||||
prerelease: true
|
|
||||||
linuxs390xbuild:
|
|
||||||
name: build x-ui s390x version
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v4
|
|
||||||
with:
|
|
||||||
go-version: '1.21'
|
|
||||||
- name: build linux s390x version
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
export CGO_ENABLED=1
|
||||||
sudo apt install gcc-s390x-linux-gnu -y
|
export GOOS=linux
|
||||||
CGO_ENABLED=1 GOOS=linux GOARCH=s390x CC=s390x-linux-gnu-gcc go build -o xui-release -v main.go
|
export GOARCH=${{ matrix.platform }}
|
||||||
|
if [ "${{ matrix.platform }}" == "arm64" ]; then
|
||||||
|
export CC=aarch64-linux-gnu-gcc
|
||||||
|
elif [ "${{ matrix.platform }}" == "arm" ]; then
|
||||||
|
export CC=arm-linux-gnueabihf-gcc
|
||||||
|
fi
|
||||||
|
go build -o xui-release -v main.go
|
||||||
|
|
||||||
mkdir x-ui
|
mkdir x-ui
|
||||||
cp xui-release x-ui/xui-release
|
cp xui-release x-ui/
|
||||||
cp x-ui.service x-ui/x-ui.service
|
cp x-ui.service x-ui/
|
||||||
cp x-ui.sh x-ui/x-ui.sh
|
cp x-ui.sh x-ui/
|
||||||
cd x-ui
|
mv x-ui/xui-release x-ui/x-ui
|
||||||
mv xui-release x-ui
|
mkdir x-ui/bin
|
||||||
mkdir bin
|
cd x-ui/bin
|
||||||
cd bin
|
|
||||||
wget https://github.com/xtls/xray-core/releases/download/v1.8.6/Xray-linux-s390x.zip
|
# Download dependencies
|
||||||
unzip Xray-linux-s390x.zip
|
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.7/"
|
||||||
rm -f Xray-linux-64.zip geoip.dat geosite.dat iran.dat
|
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||||
|
wget ${Xray_URL}Xray-linux-64.zip
|
||||||
|
unzip Xray-linux-64.zip
|
||||||
|
rm -f Xray-linux-64.zip
|
||||||
|
elif [ "${{ matrix.platform }}" == "arm64" ]; then
|
||||||
|
wget ${Xray_URL}Xray-linux-arm64-v8a.zip
|
||||||
|
unzip Xray-linux-arm64-v8a.zip
|
||||||
|
rm -f Xray-linux-arm64-v8a.zip
|
||||||
|
else
|
||||||
|
wget ${Xray_URL}Xray-linux-arm32-v7a.zip
|
||||||
|
unzip Xray-linux-arm32-v7a.zip
|
||||||
|
rm -f Xray-linux-arm32-v7a.zip
|
||||||
|
fi
|
||||||
|
rm -f geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat
|
||||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||||
wget https://github.com/Masterkia/iran-hosted-domains/releases/latest/download/iran.dat
|
wget -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||||
mv xray xray-linux-s390x
|
wget -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||||
cd ..
|
mv xray xray-linux-${{ matrix.platform }}
|
||||||
cd ..
|
cd ../..
|
||||||
- name: package
|
|
||||||
run: tar -zcvf x-ui-linux-s390x.tar.gz x-ui
|
- name: Package
|
||||||
- name: upload
|
run: tar -zcvf x-ui-linux-${{ matrix.platform }}.tar.gz x-ui
|
||||||
|
|
||||||
|
- name: Upload
|
||||||
uses: svenstaro/upload-release-action@2.7.0
|
uses: svenstaro/upload-release-action@2.7.0
|
||||||
with:
|
with:
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: ${{ github.ref }}
|
tag: ${{ github.ref }}
|
||||||
file: x-ui-linux-s390x.tar.gz
|
file: x-ui-linux-${{ matrix.platform }}.tar.gz
|
||||||
asset_name: x-ui-linux-s390x.tar.gz
|
asset_name: x-ui-linux-${{ matrix.platform }}.tar.gz
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
overwrite: true
|
||||||
|
|||||||
@@ -1,21 +1,30 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
if [ $1 == "amd64" ]; then
|
case $1 in
|
||||||
ARCH="64";
|
amd64)
|
||||||
FNAME="amd64";
|
ARCH="64"
|
||||||
elif [ $1 == "arm64" ]; then
|
FNAME="amd64"
|
||||||
ARCH="arm64-v8a"
|
;;
|
||||||
FNAME="arm64";
|
armv8 | arm64 | aarch64)
|
||||||
else
|
ARCH="arm64-v8a"
|
||||||
ARCH="64";
|
FNAME="arm64"
|
||||||
FNAME="amd64";
|
;;
|
||||||
fi
|
armv7 | arm | arm32)
|
||||||
|
ARCH="arm32-v7a"
|
||||||
|
FNAME="arm32"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ARCH="64"
|
||||||
|
FNAME="amd64"
|
||||||
|
;;
|
||||||
|
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.6/Xray-linux-${ARCH}.zip"
|
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.7/Xray-linux-${ARCH}.zip"
|
||||||
unzip "Xray-linux-${ARCH}.zip"
|
unzip "Xray-linux-${ARCH}.zip"
|
||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat iran.dat
|
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat LICENSE README.md
|
||||||
mv xray "xray-linux-${FNAME}"
|
mv xray "xray-linux-${FNAME}"
|
||||||
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
|
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat"
|
||||||
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
|
wget "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
|
||||||
wget "https://github.com/Masterkia/iran-hosted-domains/releases/latest/download/iran.dat"
|
wget -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||||
|
wget -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ WORKDIR /app
|
|||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
RUN apk --no-cache --update add build-base gcc wget unzip
|
RUN apk --no-cache --update add build-base gcc wget unzip
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN env CGO_ENABLED=1 go build -o build/x-ui main.go
|
ENV CGO_ENABLED=1
|
||||||
|
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
|
||||||
|
RUN go build -o build/x-ui main.go
|
||||||
RUN ./DockerInitFiles.sh "$TARGETARCH"
|
RUN ./DockerInitFiles.sh "$TARGETARCH"
|
||||||
|
|
||||||
FROM alpine
|
FROM alpine
|
||||||
|
|||||||
335
README.md
@@ -1,4 +1,5 @@
|
|||||||
# x-ui
|
# X-UI
|
||||||
|
**An Advanced Web Panel • Built on Xray Core**
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
@@ -6,52 +7,60 @@
|
|||||||
[](https://img.shields.io/github/downloads/alireza0/x-ui/total.svg)
|
[](https://img.shields.io/github/downloads/alireza0/x-ui/total.svg)
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||||
|
|
||||||
> **Disclaimer: This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment**
|
> **Disclaimer:** This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment
|
||||||
|
|
||||||
xray panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese,Russian)**
|
**If you think this project is helpful to you, you may wish to give a**:star2:
|
||||||
|
|
||||||
| Features | Enable? |
|
<img width="125" alt="image"
|
||||||
| ------------------------------------ | :----------------: |
|
src="https://github.com/alireza0/x-ui/assets/115543613/dd4f10dd-8bb0-40cf-846f-1fe1de7a6275">
|
||||||
| Multi-lang | :heavy_check_mark: |
|
|
||||||
| Dark/Light Theme | :heavy_check_mark: |
|
- USDT (TRC20): `TYTq73Gj6dJ67qe58JVPD9zpjW2cc9XgVz`
|
||||||
| Search in deep | :heavy_check_mark: |
|
- Tezos (XTZ):
|
||||||
| Inbound Multi User | :heavy_check_mark: |
|
`tz2Wnh2SsY1eezXrcLChu6idWpgdHzUFQcts`
|
||||||
| Multi User Traffic & Expiration time | :heavy_check_mark: |
|
|
||||||
| REST API | :heavy_check_mark: |
|
|
||||||
| Telegram BOT (admin + clients) | :heavy_check_mark: |
|
|
||||||
| Backup database using Telegram BOT | :heavy_check_mark: |
|
|
||||||
| Subscription link + userInfo | :heavy_check_mark: |
|
|
||||||
| Calculate expire date on first usage | :heavy_check_mark: |
|
|
||||||
| Show Online Clients | :heavy_check_mark: |
|
|
||||||
|
|
||||||
|
|
||||||
**If you think this project is helpful to you, you may wish to give a** :star2:
|
## Quick Overview
|
||||||
|
| Features | Enable? |
|
||||||
|
| -------------------------------------- | :----------------: |
|
||||||
|
| Multi-Protocol | :heavy_check_mark: |
|
||||||
|
| Multi-Language | :heavy_check_mark: |
|
||||||
|
| Multi-Client/Inbound | :heavy_check_mark: |
|
||||||
|
| Advanced Traffic Routing Interface | :heavy_check_mark: |
|
||||||
|
| Client & Traffic & System Status | :heavy_check_mark: |
|
||||||
|
| Date & Traffic Cap Based on First Use | :heavy_check_mark: |
|
||||||
|
| REST API | :heavy_check_mark: |
|
||||||
|
| TG Bot (DB backup + admin + client) | :heavy_check_mark: |
|
||||||
|
| Subscription Service (link + info) | :heavy_check_mark: |
|
||||||
|
| Search in Deep | :heavy_check_mark: |
|
||||||
|
| Dark/Light Theme | :heavy_check_mark: |
|
||||||
|
|
||||||
**Buy Me a Coffee :**
|
|
||||||
|
## Install & Upgrade to Latest Version
|
||||||
- Tron USDT (TRC20): `TYTq73Gj6dJ67qe58JVPD9zpjW2cc9XgVz`
|
|
||||||
- Tezos (XTZ): tz2Wnh2SsY1eezXrcLChu6idWpgdHzUFQcts
|
|
||||||
|
|
||||||
# Install & Upgrade to latest version
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install custom version
|
## Install Custom Version
|
||||||
|
|
||||||
To install your desired version you can add the version to the end of install command. Example for ver `0.5.2`:
|
**Step 1:** To install your desired version, add the version to the end of the installation command. e.g., ver `1.6.4`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh) 0.5.2
|
bash <(curl -Ls https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh) 1.6.4
|
||||||
```
|
```
|
||||||
|
|
||||||
## Manual install & upgrade
|
## Manual Install & Upgrade
|
||||||
|
|
||||||
1. First download the latest compressed package from https://github.com/alireza0/x-ui/releases, generally choose Architecture `amd64`
|
<details>
|
||||||
2. Then upload the compressed package to the server's `/root/` directory and login to the server with user `root`
|
<summary>Click for details</summary>
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
> If your server cpu architecture is not `amd64` replace another architecture
|
**Step 1:** First download the latest compressed package from https://github.com/alireza0/x-ui/releases, generally choose Architecture `amd64`
|
||||||
|
|
||||||
|
**Step 2:** Then upload the compressed package to the server's `/root/` directory and login to the server with user `root`
|
||||||
|
|
||||||
|
> If your server CPU architecture is not `amd64` replace it with the appropriate architecture
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ARCH=$(uname -m)
|
ARCH=$(uname -m)
|
||||||
@@ -68,15 +77,22 @@ systemctl enable x-ui
|
|||||||
systemctl restart x-ui
|
systemctl restart x-ui
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install using docker
|
</details>
|
||||||
|
|
||||||
1. install docker
|
## Install using Docker
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Click for details</summary>
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
**Step 1:** Install Docker
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -fsSL https://get.docker.com | sh
|
curl -fsSL https://get.docker.com | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
2. install x-ui
|
**Step 2:** Install X-UI
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
mkdir x-ui && cd x-ui
|
mkdir x-ui && cd x-ui
|
||||||
@@ -95,33 +111,56 @@ docker run -itd \
|
|||||||
docker build -t x-ui .
|
docker build -t x-ui .
|
||||||
```
|
```
|
||||||
|
|
||||||
# Features
|
</details>
|
||||||
|
|
||||||
- System Status Monitoring
|
## Languages
|
||||||
- Search within all inbounds and clients
|
|
||||||
- Support Dark/Light theme UI
|
|
||||||
- Support multi-user multi-protocol, web page visualization operation
|
|
||||||
- Support multi-domain configuration and multi-certificate inbounds
|
|
||||||
- Supported protocols: vmess, vless, trojan, shadowsocks, dokodemo-door, socks, http
|
|
||||||
- Support for configuring more transport configurations
|
|
||||||
- Traffic statistics, limit traffic, limit expiration time
|
|
||||||
- Customizable xray configuration templates
|
|
||||||
- Support subscription ( multi ) link
|
|
||||||
- Detect users which are expiring or exceed traffic limit soon
|
|
||||||
- Support https access panel (self-provided domain name + ssl certificate)
|
|
||||||
- Support one-click SSL certificate application and automatic renewal
|
|
||||||
- For more advanced configuration items, please refer to the panel
|
|
||||||
- Support export/import database from panel
|
|
||||||
- Show online users
|
|
||||||
|
|
||||||
## suggestion system
|
- English
|
||||||
|
- Chinese
|
||||||
|
- Farsi
|
||||||
|
- Russian
|
||||||
|
- Vietnamese
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Supports protocols including VLESS, VMess, Trojan, Shadowsocks, Dokodemo-door, SOCKS, HTTP, Wireguard
|
||||||
|
- Supports XTLS protocols, including Vision and REALITY
|
||||||
|
- An advanced interface for routing traffic, incorporating PROXY Protocol, Reverse, External, and Transparent Proxy, along with Multi-Domain, SSL Certificate, and Port
|
||||||
|
- Support auto generate Cloudflare WARP using Wireguard outbound
|
||||||
|
- An interactive JSON interface for Xray template configuration
|
||||||
|
- An advanced interface for inbound and outbound configuration
|
||||||
|
- Clients’ traffic cap and expiration date based on first use
|
||||||
|
- Displays online clients, traffic statistics, and system status monitoring
|
||||||
|
- Deep database search
|
||||||
|
- Displays depleted clients with expired dates or exceeded traffic cap
|
||||||
|
- Subscription service with (multi)link
|
||||||
|
- Importing and exporting databases
|
||||||
|
- One-Click SSL certificate application and automatic renewal
|
||||||
|
- HTTPS for secure access to the web panel and subscription service (self-provided domain + SSL certificate)
|
||||||
|
- Dark/Light theme
|
||||||
|
|
||||||
|
## Recommended OS
|
||||||
|
|
||||||
- CentOS 8+
|
- CentOS 8+
|
||||||
- Ubuntu 20+
|
- Ubuntu 20+
|
||||||
- Debian 10+
|
- Debian 10+
|
||||||
- Fedora 36+
|
- Fedora 36+
|
||||||
|
|
||||||
## API routes
|
## Preview
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## API Routes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Click for details</summary>
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
- `/login` with `PUSH` user data: `{username: '', password: ''}` for login
|
- `/login` with `PUSH` user data: `{username: '', password: ''}` for login
|
||||||
- `/xui/API/inbounds` base for following actions:
|
- `/xui/API/inbounds` base for following actions:
|
||||||
@@ -132,25 +171,32 @@ docker build -t x-ui .
|
|||||||
| `GET` | `"/get/:id"` | Get inbound with inbound.id |
|
| `GET` | `"/get/:id"` | Get inbound with inbound.id |
|
||||||
| `GET` | `"/createbackup"` | Telegram bot sends backup to admins |
|
| `GET` | `"/createbackup"` | Telegram bot sends backup to admins |
|
||||||
| `POST` | `"/add"` | Add inbound |
|
| `POST` | `"/add"` | Add inbound |
|
||||||
| `POST` | `"/del/:id"` | Delete Inbound |
|
| `POST` | `"/del/:id"` | Delete inbound |
|
||||||
| `POST` | `"/update/:id"` | Update Inbound |
|
| `POST` | `"/update/:id"` | Update inbound |
|
||||||
| `POST` | `"/addClient/"` | Add Client to inbound |
|
| `POST` | `"/addClient/"` | Add client to inbound |
|
||||||
| `POST` | `"/:id/delClient/:clientId"` | Delete Client by clientId\* |
|
| `POST` | `"/:id/delClient/:clientId"` | Delete client by clientId\* |
|
||||||
| `POST` | `"/updateClient/:clientId"` | Update Client by clientId\* |
|
| `POST` | `"/updateClient/:clientId"` | Update client by clientId\* |
|
||||||
| `GET` | `"/getClientTraffics/:email"` | Get Client's Traffic |
|
| `GET` | `"/getClientTraffics/:email"` | Get client's traffic |
|
||||||
| `POST` | `"/:id/resetClientTraffic/:email"` | Reset Client's Traffic |
|
| `POST` | `"/:id/resetClientTraffic/:email"` | Reset client's traffic |
|
||||||
| `POST` | `"/resetAllTraffics"` | Reset traffics of all inbounds |
|
| `POST` | `"/resetAllTraffics"` | Reset traffics of all inbounds |
|
||||||
| `POST` | `"/resetAllClientTraffics/:id"` | Reset inbound clients traffics (-1: all) |
|
| `POST` | `"/resetAllClientTraffics/:id"` | Reset inbound clients traffics (-1: all) |
|
||||||
| `POST` | `"/delDepletedClients/:id"` | Delete inbound depleted clients (-1: all) |
|
| `POST` | `"/delDepletedClients/:id"` | Delete inbound depleted clients (-1: all) |
|
||||||
| `POST` | `"/onlines"` | Get Online users ( list of emails ) |
|
| `POST` | `"/onlines"` | Get online users ( list of emails ) |
|
||||||
|
|
||||||
\*- The field `clientId` should be filled by:
|
\*- The field `clientId` should be filled by:
|
||||||
|
|
||||||
- `client.id` for VMESS and VLESS
|
- `client.id` for VMess and VLESS
|
||||||
- `client.password` for TROJAN
|
- `client.password` for Trojan
|
||||||
- `client.email` for Shadowsocks
|
- `client.email` for Shadowsocks
|
||||||
|
|
||||||
# Environment Variables
|
</details>
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Click for details</summary>
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
| Variable | Type | Default |
|
| Variable | Type | Default |
|
||||||
| -------------- | :--------------------------------------------: | :------------ |
|
| -------------- | :--------------------------------------------: | :------------ |
|
||||||
@@ -159,18 +205,24 @@ docker build -t x-ui .
|
|||||||
| XUI_BIN_FOLDER | `string` | `"bin"` |
|
| XUI_BIN_FOLDER | `string` | `"bin"` |
|
||||||
| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` |
|
| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` |
|
||||||
|
|
||||||
# Screenshots
|
</details>
|
||||||
|
|
||||||

|
## SSL Certificate
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
## SSL certificate application
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Click for details</summary>
|
<summary>Click for details</summary>
|
||||||
|
|
||||||
|
### Cloudflare
|
||||||
|
|
||||||
|
The admin management script has a built-in SSL certificate application for Cloudflare. To use this script to apply for a certificate, you need the following:
|
||||||
|
|
||||||
|
- Cloudflare registered email
|
||||||
|
- Cloudflare Global API Key
|
||||||
|
- The domain name has been resolved to the current server through cloudflare
|
||||||
|
|
||||||
|
**Step 1:** Run the`x-ui`command on the server's terminal and then choose `17`. Then enter the information as requested.
|
||||||
|
|
||||||
|
|
||||||
### Certbot
|
### Certbot
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -183,102 +235,119 @@ certbot certonly --standalone --register-unsafely-without-email --non-interactiv
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Tg robot use
|
## Telegram Bot
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Click for details</summary>
|
<summary>Click for details</summary>
|
||||||
|
|
||||||
X-UI supports daily traffic notification, panel login reminder and other functions through the Tg robot. To use the Tg robot, you need to apply for the specific application tutorial. You can refer to the [blog](https://coderfan.net/how-to-use-telegram-bot-to-alarm-you-when-someone-login-into-your-vps.html)
|
### Usage
|
||||||
Set the robot-related parameters in the panel background, including:
|
|
||||||
|
|
||||||
- Tg robot Token
|
The web panel supports daily traffic, panel login, database backup, system status, client info, and other notification and functions through the Telegram Bot. To use the bot, you need to set the bot-related parameters in the panel, including:
|
||||||
- Tg robot ChatId
|
|
||||||
- Tg robot cycle runtime, in crontab syntax
|
- Telegram Token
|
||||||
- Tg robot Expiration threshold
|
- Admin Chat ID(s)
|
||||||
- Tg robot Traffic threshold
|
- Notification Time (in cron syntax)
|
||||||
- Tg robot Enable send backup in cycle runtime
|
- Database Backup
|
||||||
- Tg robot Enable CPU usage alarm threshold
|
- CPU Load Threshold Notification
|
||||||
|
|
||||||
|
**Crontab Time Format**
|
||||||
|
|
||||||
Reference syntax:
|
Reference syntax:
|
||||||
|
|
||||||
- 30 \* \* \* \* \* //Notify at the 30s of each point
|
- `*/30 * * * *` - Notify every 30 minutes, every hour
|
||||||
- 0 \*/10 \* \* \* \* //Notify at the first second of each 10 minutes
|
- `30 * * * * *` - Notify at the 30th second of each minute
|
||||||
- @hourly // hourly notification
|
- `0 */10 * * * *` - Notify at the start of every 10 minutes
|
||||||
- @daily // Daily notification (00:00 in the morning)
|
- `@hourly` - Hourly notification
|
||||||
- @every 8h // notify every 8 hours
|
- `@daily` - Daily notification (00:00 AM)
|
||||||
|
- `@every 8h` - Notify every 8 hours
|
||||||
|
|
||||||
### Telegram Bot Features
|
For more info about [Crontab](https://acquia.my.site.com/s/article/360004224494-Cron-time-string-format)
|
||||||
|
|
||||||
- Report periodic
|
### Features
|
||||||
- Login notification
|
|
||||||
- CPU threshold notification
|
- Periodic reporting
|
||||||
- Threshold for Expiration time and Traffic to report in advance
|
- Login notifications
|
||||||
- Support client report menu if client's telegram ID or telegram UserName added to the user's configurations
|
- CPU load threshold notifications
|
||||||
- Support telegram traffic report searched with UUID (VMESS/VLESS) or Password (TROJAN) - anonymously
|
- Advance notifications for expiration time and traffic
|
||||||
- Menu based bot
|
- Client reporting menu with Telegram ID or username in configurations
|
||||||
- Search client by email ( only admin )
|
- Anonymous traffic reports, search by UUID (VLESS/VMess) or Password (Trojan/Shadowsocks)
|
||||||
- Check all inbounds
|
- Menu-based bot
|
||||||
- Check server status
|
- Client search by email (admin only)
|
||||||
- Check depleted users
|
- Inbound checks
|
||||||
- Receive backup by request and in periodic reports
|
- System status check
|
||||||
- Multi language bot
|
- Depleted client checks
|
||||||
|
- Backup on request and in periodic reports
|
||||||
|
- Multilingual support
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
# T-Shoots:
|
## Troubleshoots
|
||||||
|
|
||||||
**If you upgrade from an old version or other forks, for enable traffic for users you should do :**
|
<details>
|
||||||
|
<summary>Click for details</summary>
|
||||||
|
|
||||||
find this in config :
|
### Enable Traffic Usage
|
||||||
|
|
||||||
```json
|
If you are upgrading from an older version or other forks and find that data traffic usage for clients may not work by default, follow the steps below to enable it:
|
||||||
"policy": {
|
|
||||||
"system": {
|
|
||||||
```
|
|
||||||
|
|
||||||
**and add this just after ` "policy": {` :**
|
**Step 1: Locate the Configuration Section**
|
||||||
|
|
||||||
```json
|
Find the following section in the config file:
|
||||||
"levels": {
|
|
||||||
"0": {
|
|
||||||
"statsUserUplink": true,
|
|
||||||
"statsUserDownlink": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
```
|
|
||||||
|
|
||||||
**the final output is like :**
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"policy": {
|
"policy": {
|
||||||
"levels": {
|
|
||||||
"0": {
|
|
||||||
"statsUserUplink": true,
|
|
||||||
"statsUserDownlink": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"system": {
|
"system": {
|
||||||
"statsInboundDownlink": true,
|
// Other policy configurations
|
||||||
"statsInboundUplink": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"routing": {
|
|
||||||
```
|
```
|
||||||
|
**Step 2: Add the Required Configuration**
|
||||||
|
|
||||||
restart panel
|
Add the following section just after `"policy": {`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"levels": {
|
||||||
|
"0": {
|
||||||
|
"statsUserUplink": true,
|
||||||
|
"statsUserDownlink": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
**Step 3: Final Configuration**
|
||||||
|
|
||||||
|
Your final config should look like this:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"policy": {
|
||||||
|
"levels": {
|
||||||
|
"0": {
|
||||||
|
"statsUserUplink": true,
|
||||||
|
"statsUserDownlink": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"system": {
|
||||||
|
"statsInboundDownlink": true,
|
||||||
|
"statsInboundUplink": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"routing": {
|
||||||
|
// Other routing configurations
|
||||||
|
},
|
||||||
|
```
|
||||||
|
**Step 4: Save and Restart**
|
||||||
|
|
||||||
|
Save your changes and restart the Xray Service
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
# a special thanks to
|
## A Special Thanks to
|
||||||
|
|
||||||
- [HexaSoftwareTech](https://github.com/HexaSoftwareTech/)
|
- [HexaSoftwareTech](https://github.com/HexaSoftwareTech/)
|
||||||
- [MHSanaei](https://github.com/MHSanaei)
|
- [MHSanaei](https://github.com/MHSanaei)
|
||||||
|
|
||||||
# Acknowledgment
|
## Acknowledgment
|
||||||
|
|
||||||
- [Iran Hosted Domains](https://github.com/bootmortis/iran-hosted-domains) (License: **MIT**): _A comprehensive list of Iranian domains and services that are hosted within the country._
|
- [Loyalsoldier](https://github.com/Loyalsoldier/v2ray-rules-dat) (License: **GPL-3.0**): _The enhanced version of V2Ray routing rule._
|
||||||
- [PersianBlocker](https://github.com/MasterKia/PersianBlocker) (License: **AGPLv3**): _An optimal and extensive list to block ads and trackers on Persian websites._
|
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._
|
||||||
|
|
||||||
## Stargazers over time
|
## Stargazers over Time
|
||||||
|
|
||||||
[](https://starchart.cc/alireza0/x-ui)
|
[](https://starchart.cc/alireza0/x-ui)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
1.6.1
|
1.7.0
|
||||||
@@ -110,3 +110,12 @@ func IsSQLiteDB(file io.Reader) (bool, error) {
|
|||||||
}
|
}
|
||||||
return bytes.Equal(buf, signature), nil
|
return bytes.Equal(buf, signature), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Checkpoint() error {
|
||||||
|
// Update WAL
|
||||||
|
err := db.Exec("PRAGMA wal_checkpoint;").Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
50
go.mod
@@ -3,20 +3,19 @@ module x-ui
|
|||||||
go 1.21.4
|
go 1.21.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Workiva/go-datastructures v1.1.1
|
|
||||||
github.com/gin-contrib/sessions v0.0.4
|
github.com/gin-contrib/sessions v0.0.4
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||||
github.com/goccy/go-json v0.10.2
|
github.com/goccy/go-json v0.10.2
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.2.2
|
github.com/nicksnyder/go-i18n/v2 v2.3.0
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0
|
github.com/pelletier/go-toml/v2 v2.1.1
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v3 v3.23.11
|
github.com/shirou/gopsutil/v3 v3.23.12
|
||||||
github.com/xtls/xray-core v1.8.6
|
github.com/xtls/xray-core v1.8.7
|
||||||
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.59.0
|
google.golang.org/grpc v1.60.1
|
||||||
gorm.io/driver/sqlite v1.5.4
|
gorm.io/driver/sqlite v1.5.4
|
||||||
gorm.io/gorm v1.25.5
|
gorm.io/gorm v1.25.5
|
||||||
)
|
)
|
||||||
@@ -25,11 +24,10 @@ require (
|
|||||||
github.com/andybalholm/brotli v1.0.6 // indirect
|
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||||
github.com/bytedance/sonic v1.9.1 // indirect
|
github.com/bytedance/sonic v1.9.1 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/cloudflare/circl v1.3.6 // indirect
|
github.com/cloudflare/circl v1.3.7 // indirect
|
||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
||||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
github.com/gaukas/godicttls v0.0.4 // indirect
|
|
||||||
github.com/gin-contrib/gzip v0.0.6
|
github.com/gin-contrib/gzip v0.0.6
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
@@ -39,7 +37,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-20231101202521-4ca4178f5c7a // indirect
|
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect
|
||||||
github.com/gorilla/context v1.1.1 // indirect
|
github.com/gorilla/context v1.1.1 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||||
github.com/gorilla/sessions v1.2.1 // indirect
|
github.com/gorilla/sessions v1.2.1 // indirect
|
||||||
@@ -47,7 +45,7 @@ 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.2 // indirect
|
github.com/klauspost/compress v1.17.4 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.6 // 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-20230326075908-cb1d2100619a // indirect
|
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect
|
||||||
@@ -55,15 +53,15 @@ require (
|
|||||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
github.com/mattn/go-sqlite3 v1.14.17 // 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.1 // indirect
|
github.com/onsi/ginkgo/v2 v2.13.2 // 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/qtls-go1-20 v0.4.1 // indirect
|
||||||
github.com/quic-go/quic-go v0.40.0 // indirect
|
github.com/quic-go/quic-go v0.40.1 // indirect
|
||||||
github.com/refraction-networking/utls v1.5.4 // 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/sagernet/sing v0.2.17 // indirect
|
github.com/sagernet/sing v0.3.0 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.5 // indirect
|
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||||
@@ -75,20 +73,20 @@ require (
|
|||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 // indirect
|
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||||
go.uber.org/mock v0.3.0 // indirect
|
go.uber.org/mock v0.4.0 // indirect
|
||||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/crypto v0.15.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
golang.org/x/mod v0.14.0 // indirect
|
||||||
golang.org/x/net v0.18.0 // indirect
|
golang.org/x/net v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.15.0 // indirect
|
golang.org/x/sys v0.16.0 // indirect
|
||||||
golang.org/x/time v0.4.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.15.0 // indirect
|
golang.org/x/tools v0.16.1 // 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-20231022001213-2e0774f246fb // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||||
google.golang.org/protobuf v1.31.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
|
||||||
lukechampine.com/blake3 v1.2.1 // indirect
|
lukechampine.com/blake3 v1.2.1 // indirect
|
||||||
|
|||||||
136
go.sum
@@ -8,11 +8,8 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1
|
|||||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
|
||||||
github.com/Workiva/go-datastructures v1.1.1 h1:9G5u1UqKt6ABseAffHGNfbNQd7omRlWE5QaxNruzhE0=
|
|
||||||
github.com/Workiva/go-datastructures v1.1.1/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
|
|
||||||
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
@@ -30,8 +27,8 @@ github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F
|
|||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -47,8 +44,6 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD
|
|||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
|
|
||||||
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
|
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||||
@@ -119,8 +114,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
|
|||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.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-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8=
|
||||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/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=
|
||||||
@@ -148,8 +143,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
|||||||
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/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
|
github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
|
||||||
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.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||||
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
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.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
@@ -192,10 +187,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||||
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.2.2 h1:Iv/FL6pvYmDqybEZkr4TrOv8jSHezwpE77K68kcaft8=
|
github.com/nicksnyder/go-i18n/v2 v2.3.0 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.2.2/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
|
github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
|
||||||
github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU=
|
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
||||||
github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
||||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
github.com/onsi/gomega v1.29.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=
|
||||||
@@ -204,9 +199,8 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm
|
|||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
|
||||||
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
|
||||||
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
@@ -224,10 +218,10 @@ github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R
|
|||||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
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.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw=
|
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q=
|
||||||
github.com/quic-go/quic-go v0.40.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
|
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
|
||||||
github.com/refraction-networking/utls v1.5.4 h1:9k6EO2b8TaOGsQ7Pl7p9w6PUhx18/ZCeT0WNTZ7Uw4o=
|
github.com/refraction-networking/utls v1.6.0 h1:X5vQMqVx7dY7ehxxqkFER/W6DSjy8TMqSItXm8hRDYQ=
|
||||||
github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw=
|
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=
|
||||||
@@ -236,15 +230,15 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
|
|||||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sagernet/sing v0.2.17 h1:vMPKb3MV0Aa5ws4dCJkRI8XEjrsUcDn810czd0FwmzI=
|
github.com/sagernet/sing v0.3.0 h1:PIDVFZHnQAAYRL1UYqNM+0k5s8f/tb1lUW6UDcQiOc8=
|
||||||
github.com/sagernet/sing v0.2.17/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY=
|
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A=
|
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ=
|
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||||
github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||||
@@ -288,12 +282,10 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
|||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||||
github.com/tinylib/msgp v1.1.5/go.mod h1:eQsjooMTnV42mHu917E26IogZ2930nFyBQdofk10Udg=
|
|
||||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
|
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
@@ -313,20 +305,18 @@ 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.6 h1:tr3nk/fZnFfCsmgZv7B3RC72N5qUC88oMGVLlybDey8=
|
github.com/xtls/xray-core v1.8.7 h1:lb8O1l3/eAg3YAXA6tLm5M6N7BsX8wxW9sJLjU3dHkA=
|
||||||
github.com/xtls/xray-core v1.8.6/go.mod h1:hj2EB8rtcLdlTC8zxiWm5xL+C0k2Aie9Pk0mXtDEP6U=
|
github.com/xtls/xray-core v1.8.7/go.mod h1:9rFpflfQbgFeH1VKJw7yUmEy7myOyDCgNXXl0bmmyOo=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
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=
|
||||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
@@ -334,20 +324,16 @@ golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+
|
|||||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
|
||||||
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-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
|
||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
|
||||||
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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.14.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=
|
||||||
@@ -359,12 +345,9 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@@ -374,11 +357,8 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
|
||||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -388,54 +368,43 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
|
||||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||||
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-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.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||||
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
|
||||||
golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
|
|
||||||
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
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.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb h1:c5tyN8sSp8jSDxdCCDXVOpJwYXXhmTkNMt+g0zTSOic=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||||
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||||
@@ -448,19 +417,19 @@ 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-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 h1:6G8oQ016D88m1xAKljMlBOOGWDZkes4kMhgGFlf8WcQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU=
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
@@ -470,7 +439,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
|||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
78
install.sh
@@ -23,25 +23,15 @@ else
|
|||||||
fi
|
fi
|
||||||
echo "The OS release is: $release"
|
echo "The OS release is: $release"
|
||||||
|
|
||||||
arch=$(arch)
|
arch() {
|
||||||
|
case "$(uname -m)" in
|
||||||
if [[ $arch == "x86_64" || $arch == "x64" || $arch == "amd64" ]]; then
|
x86_64 | x64 | amd64) echo 'amd64' ;;
|
||||||
arch="amd64"
|
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
|
||||||
elif [[ $arch == "aarch64" || $arch == "arm64" ]]; then
|
armv7* | armv7 | arm | arm32 ) echo 'arm' ;;
|
||||||
arch="arm64"
|
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
|
||||||
elif [[ $arch == "s390x" ]]; then
|
esac
|
||||||
arch="s390x"
|
}
|
||||||
else
|
echo "arch: $(arch)"
|
||||||
arch="amd64"
|
|
||||||
echo -e "${red} Failed to check system arch, will use default arch: ${arch}${plain}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "arch: ${arch}"
|
|
||||||
|
|
||||||
if [ $(getconf WORD_BIT) != '32' ] && [ $(getconf LONG_BIT) != '64' ]; then
|
|
||||||
echo "x-ui dosen't support 32-bit(x86) system, please use 64 bit operating system(x86_64) instead, if there is something wrong, please get in touch with me!"
|
|
||||||
exit -1
|
|
||||||
fi
|
|
||||||
|
|
||||||
os_version=""
|
os_version=""
|
||||||
os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1)
|
os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1)
|
||||||
@@ -122,18 +112,18 @@ install_x-ui() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo -e "Got x-ui latest version: ${last_version}, beginning the installation..."
|
echo -e "Got x-ui latest version: ${last_version}, beginning the installation..."
|
||||||
wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz https://github.com/alireza0/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz
|
wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/alireza0/x-ui/releases/download/${last_version}/x-ui-linux-$(arch).tar.gz
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
echo -e "${red}Dowanloading x-ui failed, please be sure that your server can access Github ${plain}"
|
echo -e "${red}Downloading x-ui failed, please be sure that your server can access Github ${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
last_version=$1
|
last_version=$1
|
||||||
url="https://github.com/alireza0/x-ui/releases/download/${last_version}/x-ui-linux-${arch}.tar.gz"
|
url="https://github.com/alireza0/x-ui/releases/download/${last_version}/x-ui-linux-$(arch).tar.gz"
|
||||||
echo -e "Begining to install x-ui v$1"
|
echo -e "Beginning to install x-ui v$1"
|
||||||
wget -N --no-check-certificate -O /usr/local/x-ui-linux-${arch}.tar.gz ${url}
|
wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch).tar.gz ${url}
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
echo -e "${red}dowanload x-ui v$1 failed,please check the verison exists${plain}"
|
echo -e "${red}download x-ui v$1 failed,please check the version exists${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -143,10 +133,10 @@ install_x-ui() {
|
|||||||
rm /usr/local/x-ui/ -rf
|
rm /usr/local/x-ui/ -rf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tar zxvf x-ui-linux-${arch}.tar.gz
|
tar zxvf x-ui-linux-$(arch).tar.gz
|
||||||
rm x-ui-linux-${arch}.tar.gz -f
|
rm x-ui-linux-$(arch).tar.gz -f
|
||||||
cd x-ui
|
cd x-ui
|
||||||
chmod +x x-ui bin/xray-linux-${arch}
|
chmod +x x-ui bin/xray-linux-$(arch)
|
||||||
cp -f x-ui.service /etc/systemd/system/
|
cp -f x-ui.service /etc/systemd/system/
|
||||||
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/alireza0/x-ui/main/x-ui.sh
|
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/alireza0/x-ui/main/x-ui.sh
|
||||||
chmod +x /usr/local/x-ui/x-ui.sh
|
chmod +x /usr/local/x-ui/x-ui.sh
|
||||||
@@ -163,22 +153,24 @@ install_x-ui() {
|
|||||||
systemctl start x-ui
|
systemctl start x-ui
|
||||||
echo -e "${green}x-ui v${last_version}${plain} installation finished, it is up and running now..."
|
echo -e "${green}x-ui v${last_version}${plain} installation finished, it is up and running now..."
|
||||||
echo -e ""
|
echo -e ""
|
||||||
echo -e "x-ui control menu usages: "
|
echo "X-UI Control Menu Usage"
|
||||||
echo -e "----------------------------------------------"
|
echo "------------------------------------------"
|
||||||
echo -e "x-ui - Enter Admin menu"
|
echo "SUBCOMMANDS:"
|
||||||
echo -e "x-ui start - Start x-ui"
|
echo "x-ui - Admin Management Script"
|
||||||
echo -e "x-ui stop - Stop x-ui"
|
echo "x-ui start - Start"
|
||||||
echo -e "x-ui restart - Restart x-ui"
|
echo "x-ui stop - Stop"
|
||||||
echo -e "x-ui status - Show x-ui status"
|
echo "x-ui restart - Restart"
|
||||||
echo -e "x-ui enable - Enable x-ui on system startup"
|
echo "x-ui status - Current Status"
|
||||||
echo -e "x-ui disable - Disable x-ui on system startup"
|
echo "x-ui enable - Enable Autostart on OS Startup"
|
||||||
echo -e "x-ui log - Check x-ui logs"
|
echo "x-ui disable - Disable Autostart on OS Startup"
|
||||||
echo -e "x-ui update - Update x-ui"
|
echo "x-ui log - Check Logs"
|
||||||
echo -e "x-ui install - Install x-ui"
|
echo "x-ui update - Update"
|
||||||
echo -e "x-ui uninstall - Uninstall x-ui"
|
echo "x-ui install - Install"
|
||||||
echo -e "----------------------------------------------"
|
echo "x-ui uninstall - Uninstall"
|
||||||
|
echo "x-ui help - Control Menu Usage"
|
||||||
|
echo "------------------------------------------"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo -e "${green}Excuting...${plain}"
|
echo -e "${green}Running...${plain}"
|
||||||
install_base
|
install_base
|
||||||
install_x-ui $1
|
install_x-ui $1
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 172 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 96 KiB |
BIN
media/warp.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
@@ -19,8 +19,9 @@ import (
|
|||||||
type SubService struct {
|
type SubService struct {
|
||||||
address string
|
address string
|
||||||
showInfo bool
|
showInfo bool
|
||||||
|
remarkModel string
|
||||||
inboundService service.InboundService
|
inboundService service.InboundService
|
||||||
settingServics service.SettingService
|
settingService service.SettingService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string, []string, error) {
|
func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string, []string, error) {
|
||||||
@@ -34,6 +35,10 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
s.remarkModel, err = s.settingService.GetRemarkModel()
|
||||||
|
if err != nil {
|
||||||
|
s.remarkModel = "-ieo"
|
||||||
|
}
|
||||||
for _, inbound := range inbounds {
|
for _, inbound := range inbounds {
|
||||||
clients, err := s.inboundService.GetClients(inbound)
|
clients, err := s.inboundService.GetClients(inbound)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -88,7 +93,7 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000))
|
headers = append(headers, fmt.Sprintf("upload=%d; download=%d; total=%d; expire=%d", traffic.Up, traffic.Down, traffic.Total, traffic.ExpiryTime/1000))
|
||||||
updateInterval, _ := s.settingServics.GetSubUpdates()
|
updateInterval, _ := s.settingService.GetSubUpdates()
|
||||||
headers = append(headers, fmt.Sprintf("%d", updateInterval))
|
headers = append(headers, fmt.Sprintf("%d", updateInterval))
|
||||||
headers = append(headers, subId)
|
headers = append(headers, subId)
|
||||||
return result, headers, nil
|
return result, headers, nil
|
||||||
@@ -396,11 +401,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
|||||||
params["spx"] = spx
|
params["spx"] = spx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if serverName, ok := searchKey(realitySettings, "serverName"); ok {
|
|
||||||
if sname, ok := serverName.(string); ok && len(sname) > 0 {
|
|
||||||
address = sname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
||||||
@@ -408,6 +408,10 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if security != "tls" && security != "reality" {
|
||||||
|
params["security"] = "none"
|
||||||
|
}
|
||||||
|
|
||||||
externalProxies, _ := stream["externalProxy"].([]interface{})
|
externalProxies, _ := stream["externalProxy"].([]interface{})
|
||||||
|
|
||||||
if len(externalProxies) > 0 {
|
if len(externalProxies) > 0 {
|
||||||
@@ -578,14 +582,13 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
|||||||
params["spx"] = spx
|
params["spx"] = spx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if serverName, ok := searchKey(realitySettings, "serverName"); ok {
|
|
||||||
if sname, ok := serverName.(string); ok && len(sname) > 0 {
|
|
||||||
address = sname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if security != "tls" && security != "reality" {
|
||||||
|
params["security"] = "none"
|
||||||
|
}
|
||||||
|
|
||||||
externalProxies, _ := stream["externalProxy"].([]interface{})
|
externalProxies, _ := stream["externalProxy"].([]interface{})
|
||||||
|
|
||||||
if len(externalProxies) > 0 {
|
if len(externalProxies) > 0 {
|
||||||
@@ -793,17 +796,30 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string {
|
func (s *SubService) genRemark(inbound *model.Inbound, email string, extra string) string {
|
||||||
var remark []string
|
separationChar := string(s.remarkModel[0])
|
||||||
|
orderChars := s.remarkModel[1:]
|
||||||
|
orders := map[byte]string{
|
||||||
|
'i': "",
|
||||||
|
'e': "",
|
||||||
|
'o': "",
|
||||||
|
}
|
||||||
if len(email) > 0 {
|
if len(email) > 0 {
|
||||||
if len(inbound.Remark) > 0 {
|
orders['e'] = email
|
||||||
remark = append(remark, inbound.Remark)
|
}
|
||||||
|
if len(inbound.Remark) > 0 {
|
||||||
|
orders['i'] = inbound.Remark
|
||||||
|
}
|
||||||
|
if len(extra) > 0 {
|
||||||
|
orders['o'] = extra
|
||||||
|
}
|
||||||
|
|
||||||
|
var remark []string
|
||||||
|
for i := 0; i < len(orderChars); i++ {
|
||||||
|
char := orderChars[i]
|
||||||
|
order, exists := orders[char]
|
||||||
|
if exists && order != "" {
|
||||||
|
remark = append(remark, order)
|
||||||
}
|
}
|
||||||
remark = append(remark, email)
|
|
||||||
if len(extra) > 0 {
|
|
||||||
remark = append(remark, extra)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return inbound.Remark
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.showInfo {
|
if s.showInfo {
|
||||||
@@ -820,7 +836,7 @@ func (s *SubService) genRemark(inbound *model.Inbound, email string, extra strin
|
|||||||
// Get remained days
|
// Get remained days
|
||||||
if statsExist {
|
if statsExist {
|
||||||
if !stats.Enable {
|
if !stats.Enable {
|
||||||
return fmt.Sprintf("⛔️N/A-%s", strings.Join(remark, "-"))
|
return fmt.Sprintf("⛔️N/A%s%s", separationChar, strings.Join(remark, separationChar))
|
||||||
}
|
}
|
||||||
if vol := stats.Total - (stats.Up + stats.Down); vol > 0 {
|
if vol := stats.Total - (stats.Up + stats.Down); vol > 0 {
|
||||||
remark = append(remark, fmt.Sprintf("%s%s", common.FormatTraffic(vol), "📊"))
|
remark = append(remark, fmt.Sprintf("%s%s", common.FormatTraffic(vol), "📊"))
|
||||||
@@ -834,7 +850,7 @@ func (s *SubService) genRemark(inbound *model.Inbound, email string, extra strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return strings.Join(remark, "-")
|
return strings.Join(remark, separationChar)
|
||||||
}
|
}
|
||||||
|
|
||||||
func searchKey(data interface{}, key string) (interface{}, bool) {
|
func searchKey(data interface{}, key string) (interface{}, bool) {
|
||||||
|
|||||||
@@ -1741,7 +1741,7 @@
|
|||||||
// is needed on Webkit to be able to get line-level bounding
|
// is needed on Webkit to be able to get line-level bounding
|
||||||
// rectangles for it (in measureChar).
|
// rectangles for it (in measureChar).
|
||||||
var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
|
var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
|
||||||
var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
|
var builder = {pre: eltP("pre", [content], "CodeMirror-line Line-Hover"), content: content,
|
||||||
col: 0, pos: 0, cm: cm,
|
col: 0, pos: 0, cm: cm,
|
||||||
trailingSpace: false,
|
trailingSpace: false,
|
||||||
splitSpaces: cm.getOption("lineWrapping")};
|
splitSpaces: cm.getOption("lineWrapping")};
|
||||||
|
|||||||
@@ -21,17 +21,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
.cm-s-xq.CodeMirror { border-radius: 1.5rem; border: 1px solid #d9d9d9; height: auto; }
|
.cm-s-xq.CodeMirror { border-radius: 1.5rem; border: 1px solid #d9d9d9; height: auto; }
|
||||||
|
.cm-s-xq.CodeMirror:hover { background-color: #edf4fa; border-color: #2f67c2; transition: all .3s; }
|
||||||
|
.cm-s-xq .CodeMirror-gutters { border-right: 1px solid #ddd; background-color: rgb(221 221 221 / 20%); white-space: nowrap; }
|
||||||
.cm-s-xq span.cm-keyword { line-height: 1em; font-weight: bold; color: #5A5CAD; }
|
.cm-s-xq span.cm-keyword { line-height: 1em; font-weight: bold; color: #5A5CAD; }
|
||||||
.cm-s-xq span.cm-atom { color: #6C8CD5; }
|
.cm-s-xq span.cm-atom { color: #7A316F; font-weight:bold; }
|
||||||
.cm-s-xq span.cm-number { color: #164; }
|
.cm-s-xq span.cm-number { color: #389E0D; }
|
||||||
.cm-s-xq span.cm-def { text-decoration:underline; }
|
.cm-s-xq span.cm-def { text-decoration:underline; }
|
||||||
.cm-s-xq span.cm-variable { color: black; }
|
.cm-s-xq span.cm-variable { color: black; }
|
||||||
.cm-s-xq span.cm-variable-2 { color:black; }
|
.cm-s-xq span.cm-variable-2 { color:black; }
|
||||||
.cm-s-xq span.cm-variable-3, .cm-s-xq span.cm-type { color: black; }
|
.cm-s-xq span.cm-variable-3, .cm-s-xq span.cm-type { color: black; }
|
||||||
.cm-s-xq span.cm-property {}
|
.cm-s-xq span.cm-property { color: #0e49b5; }
|
||||||
.cm-s-xq span.cm-operator {}
|
.cm-s-xq span.cm-operator {}
|
||||||
.cm-s-xq span.cm-comment { color: #0080FF; font-style: italic; }
|
.cm-s-xq span.cm-comment { color: #bbbbbb; font-style: italic; }
|
||||||
.cm-s-xq span.cm-string { color: #e04141; }
|
.cm-s-xq span.cm-string {}
|
||||||
.cm-s-xq span.cm-meta { color: yellow; }
|
.cm-s-xq span.cm-meta { color: yellow; }
|
||||||
.cm-s-xq span.cm-qualifier { color: grey; }
|
.cm-s-xq span.cm-qualifier { color: grey; }
|
||||||
.cm-s-xq span.cm-builtin { color: #7EA656; }
|
.cm-s-xq span.cm-builtin { color: #7EA656; }
|
||||||
@@ -44,26 +46,27 @@ THE SOFTWARE.
|
|||||||
.cm-s-xq .CodeMirror-matchingbracket { outline:1px solid grey;color:black !important;background:yellow; }
|
.cm-s-xq .CodeMirror-matchingbracket { outline:1px solid grey;color:black !important;background:yellow; }
|
||||||
|
|
||||||
.dark .cm-s-xq.CodeMirror { background-color: #222D42; border-color: #2c3950; color: rgb(255 255 255 / 65%); }
|
.dark .cm-s-xq.CodeMirror { background-color: #222D42; border-color: #2c3950; color: rgb(255 255 255 / 65%); }
|
||||||
.dark .cm-s-xq div.CodeMirror-selected { background: #27007A; }
|
.dark .cm-s-xq.CodeMirror:hover { background-color: #0e2040; border-color: #0e49b5; transition: all .3s; }
|
||||||
|
.dark .cm-s-xq div.CodeMirror-selected { background: rgba(0, 0, 0, 0.5); }
|
||||||
.dark .cm-s-xq .CodeMirror-line::selection, .dark .cm-s-xq .CodeMirror-line > span::selection, .dark .cm-s-xq .CodeMirror-line > span > span::selection { background: rgba(39, 0, 122, 0.99); }
|
.dark .cm-s-xq .CodeMirror-line::selection, .dark .cm-s-xq .CodeMirror-line > span::selection, .dark .cm-s-xq .CodeMirror-line > span > span::selection { background: rgba(39, 0, 122, 0.99); }
|
||||||
.dark .cm-s-xq .CodeMirror-line::-moz-selection, .dark .cm-s-xq .CodeMirror-line > span::-moz-selection, .dark .cm-s-xq .CodeMirror-line > span > span::-moz-selection { background: rgba(39, 0, 122, 0.99); }
|
.dark .cm-s-xq .CodeMirror-line::-moz-selection, .dark .cm-s-xq .CodeMirror-line > span::-moz-selection, .dark .cm-s-xq .CodeMirror-line > span > span::-moz-selection { background: rgba(39, 0, 122, 0.99); }
|
||||||
.dark .cm-s-xq .CodeMirror-gutters { background: #222D42; border-right: 1px solid #2c3950; }
|
.dark .cm-s-xq .CodeMirror-gutters { background: rgb(0 0 0 / 30%); border-right: 1px solid #2c3950; }
|
||||||
.dark .cm-s-xq .CodeMirror-guttermarker { color: #FFBD40; }
|
.dark .cm-s-xq .CodeMirror-guttermarker { color: #FFBD40; }
|
||||||
.dark .cm-s-xq .CodeMirror-guttermarker-subtle { color: #f8f8f8; }
|
.dark .cm-s-xq .CodeMirror-guttermarker-subtle { color: rgb(255 255 255 / 70%); }
|
||||||
.dark .cm-s-xq .CodeMirror-linenumber { color: #f8f8f8; }
|
.dark .cm-s-xq .CodeMirror-linenumber { color: rgb(255 255 255 / 50%); }
|
||||||
.dark .cm-s-xq .CodeMirror-cursor { border-left: 1px solid white; }
|
.dark .cm-s-xq .CodeMirror-cursor { border-left: 1px solid white; }
|
||||||
|
|
||||||
.dark .cm-s-xq span.cm-keyword { color: #FFBD40; }
|
.dark .cm-s-xq span.cm-keyword { color: #FFBD40; }
|
||||||
.dark .cm-s-xq span.cm-atom { color: #6C8CD5; }
|
.dark .cm-s-xq span.cm-atom { color: #c099ff; }
|
||||||
.dark .cm-s-xq span.cm-number { color: #164; }
|
.dark .cm-s-xq span.cm-number { color: #9ccfd8; }
|
||||||
.dark .cm-s-xq span.cm-def { color: #FFF; text-decoration:underline; }
|
.dark .cm-s-xq span.cm-def { color: #FFF; text-decoration:underline; }
|
||||||
.dark .cm-s-xq span.cm-variable { color: #FFF; }
|
.dark .cm-s-xq span.cm-variable { color: #FFF; }
|
||||||
.dark .cm-s-xq span.cm-variable-2 { color: #EEE; }
|
.dark .cm-s-xq span.cm-variable-2 { color: #EEE; }
|
||||||
.dark .cm-s-xq span.cm-variable-3, .dark .cm-s-xq span.cm-type { color: #DDD; }
|
.dark .cm-s-xq span.cm-variable-3, .dark .cm-s-xq span.cm-type { color: #DDD; }
|
||||||
.dark .cm-s-xq span.cm-property {}
|
.dark .cm-s-xq span.cm-property { color: #f6c177 }
|
||||||
.dark .cm-s-xq span.cm-operator {}
|
.dark .cm-s-xq span.cm-operator {}
|
||||||
.dark .cm-s-xq span.cm-comment { color: gray; }
|
.dark .cm-s-xq span.cm-comment { color: gray; }
|
||||||
.dark .cm-s-xq span.cm-string { color: #9FEE00; }
|
.dark .cm-s-xq span.cm-string {}
|
||||||
.dark .cm-s-xq span.cm-meta { color: yellow; }
|
.dark .cm-s-xq span.cm-meta { color: yellow; }
|
||||||
.dark .cm-s-xq span.cm-qualifier { color: #FFF700; }
|
.dark .cm-s-xq span.cm-qualifier { color: #FFF700; }
|
||||||
.dark .cm-s-xq span.cm-builtin { color: #30a; }
|
.dark .cm-s-xq span.cm-builtin { color: #30a; }
|
||||||
@@ -73,4 +76,11 @@ THE SOFTWARE.
|
|||||||
.dark .cm-s-xq span.cm-error { color: #e04141; }
|
.dark .cm-s-xq span.cm-error { color: #e04141; }
|
||||||
|
|
||||||
.dark .cm-s-xq .CodeMirror-activeline-background { background: #27282E; }
|
.dark .cm-s-xq .CodeMirror-activeline-background { background: #27282E; }
|
||||||
.dark .cm-s-xq .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }
|
.dark .cm-s-xq .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }
|
||||||
|
|
||||||
|
.Line-Hover{transition: all .2s;}
|
||||||
|
.Line-Hover:hover{ background-color: rgb(4 48 143 / 5%) !important; }
|
||||||
|
.dark .Line-Hover:hover{ background-color: rgb(0 0 0 / 20%) !important; }
|
||||||
|
|
||||||
|
.CodeMirror-foldmarker { color: #fc8800; text-shadow: #ffd8aa 1px 1px 2px, #ffd8aa -1px -1px 2px, #ffd8aa 1px -1px 2px, #ffd8aa -1px 1px 2px; font-family: arial; line-height: .3; cursor: pointer; }
|
||||||
|
.dark .CodeMirror-foldmarker { color: #ffffff; text-shadow: #bbb 1px 1px 2px, #bbb -1px -1px 2px, #bbb 1px -1px 2px, #bbb -1px 1px 2px; font-family: arial; line-height: .3; cursor: pointer; }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
color: rgba(0,0,0,.65);
|
color: rgba(0, 0, 0, 0.65);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-variant: tabular-nums;
|
font-variant: tabular-nums;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@@ -21,12 +21,12 @@ html {
|
|||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
-ms-text-size-adjust: 100%;
|
-ms-text-size-adjust: 100%;
|
||||||
-ms-overflow-style: scrollbar;
|
-ms-overflow-style: scrollbar;
|
||||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
color: #0e49b5;
|
color: #0e49b5;
|
||||||
background-color: #0e49b530;
|
background-color: #d2ddf1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
@@ -41,7 +41,8 @@ html {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-layout, .ant-layout * {
|
.ant-layout,
|
||||||
|
.ant-layout * {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,16 +53,17 @@ html {
|
|||||||
style attribute {
|
style attribute {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.ant-table-tbody>tr>td, .ant-table-thead>tr>th {
|
.ant-table-tbody > tr > td,
|
||||||
padding: 16px;
|
.ant-table-thead > tr > th {
|
||||||
|
padding: 12px 8px;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
.ant-table-thead>tr>th {
|
.ant-table-thead > tr > th {
|
||||||
color: rgba(0,0,0,.85);
|
color: rgba(0, 0, 0, 0.85);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-bottom: 1px solid #e8e8e8;
|
border-bottom: 1px solid #e8e8e8;
|
||||||
transition: background .3s ease;
|
transition: background 0.3s ease;
|
||||||
}
|
}
|
||||||
.ant-table-row-cell-break-word {
|
.ant-table-row-cell-break-word {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@@ -79,7 +81,7 @@ style attribute {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: rgba(0,0,0,.65);
|
color: rgba(0, 0, 0, 0.65);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-variant: tabular-nums;
|
font-variant: tabular-nums;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
@@ -88,6 +90,9 @@ style attribute {
|
|||||||
position: relative;
|
position: relative;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
.ant-table-body {
|
||||||
|
overflow-x: auto !important;
|
||||||
|
}
|
||||||
.ant-card-hoverable {
|
.ant-card-hoverable {
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -96,16 +101,16 @@ style attribute {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: rgba(0,0,0,.65);
|
color: rgba(0, 0, 0, 0.65);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-variant: tabular-nums;
|
font-variant: tabular-nums;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
font-feature-settings: "tnum";
|
font-feature-settings: "tnum";
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #fff;
|
background-color: #fff;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
transition: all .3s;
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-space {
|
.ant-space {
|
||||||
@@ -121,11 +126,18 @@ style attribute {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.ant-card {
|
.ant-card {
|
||||||
margin: .5rem;
|
margin: 0.5rem;
|
||||||
}
|
}
|
||||||
.ant-tabs {
|
.ant-tabs {
|
||||||
margin: .5rem;
|
margin: 0.5rem;
|
||||||
padding: .5rem;
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
.ant-modal-body {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.ant-form-item-label {
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 8px 0 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +154,7 @@ style attribute {
|
|||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-card+.ant-card {
|
.ant-card + .ant-card {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +171,7 @@ style attribute {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #fff;
|
background-color: #fff;
|
||||||
right: -40px;
|
right: -40px;
|
||||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
|
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
|
||||||
border-radius: 0 4px 4px 0;
|
border-radius: 0 4px 4px 0;
|
||||||
@@ -167,27 +179,45 @@ style attribute {
|
|||||||
|
|
||||||
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
|
||||||
background-color: #04308f !important;
|
background-color: #04308f !important;
|
||||||
background-image: linear-gradient( 270deg, rgba(123, 199, 77, 0) 30%, #2f67c2, rgba(123, 199, 77, 0) 100% );
|
background-image: linear-gradient(
|
||||||
|
270deg,
|
||||||
|
rgba(123, 199, 77, 0) 30%,
|
||||||
|
#2f67c2,
|
||||||
|
rgba(123, 199, 77, 0) 100%
|
||||||
|
);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
animation: ma-bg-move linear 6.6s infinite;
|
animation: ma-bg-move linear 6.6s infinite;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-radius: 0.5rem
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes ma-bg-move {
|
@-webkit-keyframes ma-bg-move {
|
||||||
0% {background-position: -500px 0;}
|
0% {
|
||||||
100% {background-position: 1000px 0;}
|
background-position: -500px 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 1000px 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes ma-bg-move {
|
@keyframes ma-bg-move {
|
||||||
0% {background-position: -500px 0;}
|
0% {
|
||||||
50% {background-position: 1000px 0;}
|
background-position: -500px 0;
|
||||||
100% {background-position: 1000px 0;}
|
}
|
||||||
|
50% {
|
||||||
|
background-position: 1000px 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 1000px 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu-item-active,
|
.ant-menu-item-active,
|
||||||
.ant-menu-item:hover,
|
.ant-menu-item:hover,
|
||||||
.ant-menu-submenu-active,
|
.ant-menu-submenu-active,
|
||||||
.ant-menu-submenu-title:hover,
|
.ant-menu-submenu-title:hover,
|
||||||
.ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open{
|
.ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open {
|
||||||
color:#0e49b5;
|
color: #0e49b5;
|
||||||
background-color: #dce9f5;
|
background-color: #dce9f5;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -202,13 +232,13 @@ style attribute {
|
|||||||
}
|
}
|
||||||
.ant-layout-sider-children,
|
.ant-layout-sider-children,
|
||||||
.ant-pagination ul {
|
.ant-pagination ul {
|
||||||
margin-top:-.1px;
|
margin-top: -0.1px;
|
||||||
padding:0.5rem
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-dropdown-menu,
|
.ant-dropdown-menu,
|
||||||
.ant-select-dropdown-menu {
|
.ant-select-dropdown-menu {
|
||||||
padding: .5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
.ant-dropdown-menu-item,
|
.ant-dropdown-menu-item,
|
||||||
.ant-dropdown-menu-item:hover,
|
.ant-dropdown-menu-item:hover,
|
||||||
@@ -216,7 +246,7 @@ style attribute {
|
|||||||
.ant-select-dropdown-menu-item:hover,
|
.ant-select-dropdown-menu-item:hover,
|
||||||
.ant-select-dropdown-menu-item-selected,
|
.ant-select-dropdown-menu-item-selected,
|
||||||
.ant-select-selection--multiple .ant-select-selection__choice {
|
.ant-select-selection--multiple .ant-select-selection__choice {
|
||||||
border-radius: .5rem;
|
border-radius: 0.5rem;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,98 +267,130 @@ style attribute {
|
|||||||
.fade-in-linear-enter,
|
.fade-in-linear-enter,
|
||||||
.fade-in-linear-leave,
|
.fade-in-linear-leave,
|
||||||
.fade-in-linear-leave-active {
|
.fade-in-linear-leave-active {
|
||||||
opacity: 0
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-in-linear-enter-active, .fade-in-linear-leave-active {
|
.fade-in-linear-enter-active,
|
||||||
-webkit-transition: opacity .2s linear;
|
.fade-in-linear-leave-active {
|
||||||
transition: opacity .2s linear
|
-webkit-transition: opacity 0.2s linear;
|
||||||
|
transition: opacity 0.2s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-in-linear-enter-active, .fade-in-linear-leave-active {
|
.fade-in-linear-enter-active,
|
||||||
-webkit-transition: opacity .2s linear;
|
.fade-in-linear-leave-active {
|
||||||
transition: opacity .2s linear
|
-webkit-transition: opacity 0.2s linear;
|
||||||
|
transition: opacity 0.2s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-in-enter-active, .fade-in-leave-active {
|
.fade-in-enter-active,
|
||||||
-webkit-transition: all .3s cubic-bezier(.55, 0, .1, 1);
|
.fade-in-leave-active {
|
||||||
transition: all .3s cubic-bezier(.55, 0, .1, 1)
|
-webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
|
||||||
|
transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-in-center-enter-active, .zoom-in-center-leave-active {
|
.zoom-in-center-enter-active,
|
||||||
-webkit-transition: all .3s cubic-bezier(.55, 0, .1, 1);
|
.zoom-in-center-leave-active {
|
||||||
transition: all .3s cubic-bezier(.55, 0, .1, 1)
|
-webkit-transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
|
||||||
|
transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-in-center-enter, .zoom-in-center-leave-active {
|
.zoom-in-center-enter,
|
||||||
|
.zoom-in-center-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transform: scaleX(0);
|
-webkit-transform: scaleX(0);
|
||||||
transform: scaleX(0)
|
transform: scaleX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-in-top-enter-active, .zoom-in-top-leave-active {
|
.zoom-in-top-enter-active,
|
||||||
|
.zoom-in-top-leave-active {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
-webkit-transform: scaleY(1);
|
-webkit-transform: scaleY(1);
|
||||||
transform: scaleY(1);
|
transform: scaleY(1);
|
||||||
-webkit-transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1);
|
-webkit-transition: opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1);
|
-webkit-transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1);
|
transition: opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1);
|
-webkit-transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
|
opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
|
opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
|
-webkit-transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
-webkit-transform-origin: center top;
|
-webkit-transform-origin: center top;
|
||||||
transform-origin: center top
|
transform-origin: center top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-in-top-enter, .zoom-in-top-leave-active {
|
.zoom-in-top-enter,
|
||||||
|
.zoom-in-top-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transform: scaleY(0);
|
-webkit-transform: scaleY(0);
|
||||||
transform: scaleY(0)
|
transform: scaleY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-in-bottom-enter-active, .zoom-in-bottom-leave-active {
|
.zoom-in-bottom-enter-active,
|
||||||
|
.zoom-in-bottom-leave-active {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
-webkit-transform: scaleY(1);
|
-webkit-transform: scaleY(1);
|
||||||
transform: scaleY(1);
|
transform: scaleY(1);
|
||||||
-webkit-transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1);
|
-webkit-transition: opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1);
|
-webkit-transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1);
|
transition: opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1);
|
-webkit-transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
|
opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
|
opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
|
-webkit-transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
-webkit-transform-origin: center bottom;
|
-webkit-transform-origin: center bottom;
|
||||||
transform-origin: center bottom
|
transform-origin: center bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-in-bottom-enter, .zoom-in-bottom-leave-active {
|
.zoom-in-bottom-enter,
|
||||||
|
.zoom-in-bottom-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transform: scaleY(0);
|
-webkit-transform: scaleY(0);
|
||||||
transform: scaleY(0)
|
transform: scaleY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-in-left-enter-active, .zoom-in-left-leave-active {
|
.zoom-in-left-enter-active,
|
||||||
|
.zoom-in-left-leave-active {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
-webkit-transform: scale(1, 1);
|
-webkit-transform: scale(1, 1);
|
||||||
transform: scale(1, 1);
|
transform: scale(1, 1);
|
||||||
-webkit-transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1);
|
-webkit-transition: opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
transition: opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1);
|
-webkit-transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1);
|
transition: opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
transition: transform .3s cubic-bezier(.23, 1, .32, 1), opacity .3s cubic-bezier(.23, 1, .32, 1), -webkit-transform .3s cubic-bezier(.23, 1, .32, 1);
|
-webkit-transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
|
opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
|
opacity 0.3s cubic-bezier(0.23, 1, 0.32, 1),
|
||||||
|
-webkit-transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
-webkit-transform-origin: top left;
|
-webkit-transform-origin: top left;
|
||||||
transform-origin: top left
|
transform-origin: top left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zoom-in-left-enter, .zoom-in-left-leave-active {
|
.zoom-in-left-enter,
|
||||||
|
.zoom-in-left-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transform: scale(.45, .45);
|
-webkit-transform: scale(0.45, 0.45);
|
||||||
transform: scale(.45, .45)
|
transform: scale(0.45, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-enter-active, .list-leave-active {
|
.list-enter-active,
|
||||||
-webkit-transition: all .3s;
|
.list-leave-active {
|
||||||
transition: all .3s
|
-webkit-transition: all 0.3s;
|
||||||
|
transition: all 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-enter, .list-leave-active {
|
.list-enter,
|
||||||
|
.list-leave-active {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transform: translateY(-30px);
|
-webkit-transform: translateY(-30px);
|
||||||
transform: translateY(-30px)
|
transform: translateY(-30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-tooltip-inner {
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-list-item-meta-title {
|
.ant-list-item-meta-title {
|
||||||
@@ -336,19 +398,14 @@ style attribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-progress-inner {
|
.ant-progress-inner {
|
||||||
background-color: #EBEEF5;
|
background-color: #ebeef5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.deactive-client .ant-collapse-header{
|
.deactive-client .ant-collapse-header {
|
||||||
color:rgb(255, 255, 255) !important;
|
color: rgb(255, 255, 255) !important;
|
||||||
background-color: rgb(255, 127, 127);
|
background-color: rgb(255, 127, 127);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-table-tbody>tr>td,
|
|
||||||
.ant-table-thead>tr>th{
|
|
||||||
padding:16px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-table-expand-icon-th,
|
.ant-table-expand-icon-th,
|
||||||
.ant-table-row-expand-icon-cell {
|
.ant-table-row-expand-icon-cell {
|
||||||
width: 30px;
|
width: 30px;
|
||||||
@@ -359,6 +416,10 @@ style attribute {
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-form-item {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.ant-setting-textarea {
|
.ant-setting-textarea {
|
||||||
margin-top: 1.5rem;
|
margin-top: 1.5rem;
|
||||||
}
|
}
|
||||||
@@ -393,18 +454,18 @@ style attribute {
|
|||||||
}
|
}
|
||||||
.ant-tag-orange,
|
.ant-tag-orange,
|
||||||
.ant-alert-warning {
|
.ant-alert-warning {
|
||||||
background-color:#fff6E6;
|
background-color: #fff6e6;
|
||||||
border-color: #ffd98c;
|
border-color: #ffd98c;
|
||||||
color: #ffa031;
|
color: #ffa031;
|
||||||
}
|
}
|
||||||
.ant-tag-red,
|
.ant-tag-red,
|
||||||
.ant-alert-error {
|
.ant-alert-error {
|
||||||
background-color:#fff0f0;
|
background-color: #fff0f0;
|
||||||
border-color: #fb9d9d;
|
border-color: #fb9d9d;
|
||||||
color: #e04141;
|
color: #e04141;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-input::placeholder{
|
.ant-input::placeholder {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,11 +475,11 @@ style attribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.delete-icon:hover {
|
.delete-icon:hover {
|
||||||
color: #E04141;
|
color: #e04141;
|
||||||
}
|
}
|
||||||
|
|
||||||
.normal-icon:hover {
|
.normal-icon:hover {
|
||||||
color: #0E49B5;
|
color: #0e49b5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DARK THEME */
|
/* DARK THEME */
|
||||||
@@ -440,18 +501,18 @@ style attribute {
|
|||||||
.dark .ant-table,
|
.dark .ant-table,
|
||||||
.dark .ant-collapse-content,
|
.dark .ant-collapse-content,
|
||||||
.dark .ant-tabs {
|
.dark .ant-tabs {
|
||||||
background-color: #151F31;
|
background-color: #151f31;
|
||||||
color: #ffffffa6;
|
color: #ffffffa6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-card-hoverable:hover,
|
.dark .ant-card-hoverable:hover,
|
||||||
.dark .ant-space-item>.ant-tabs:hover {
|
.dark .ant-space-item > .ant-tabs:hover {
|
||||||
box-shadow: 0 1px 10px -1px rgb(154 175 238 / 80%);
|
box-shadow: 0 1px 10px -1px rgb(154 175 238 / 80%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark>.ant-layout,
|
.dark > .ant-layout,
|
||||||
.dark .drawer-handle,
|
.dark .drawer-handle,
|
||||||
.dark .ant-table-thead>tr>th,
|
.dark .ant-table-thead > tr > th,
|
||||||
.dark .ant-table-expanded-row,
|
.dark .ant-table-expanded-row,
|
||||||
.dark .ant-table-expanded-row:hover,
|
.dark .ant-table-expanded-row:hover,
|
||||||
.dark .ant-table-expanded-row .ant-table-tbody,
|
.dark .ant-table-expanded-row .ant-table-tbody,
|
||||||
@@ -460,7 +521,7 @@ style attribute {
|
|||||||
color: rgb(255 255 255 /65%);
|
color: rgb(255 255 255 /65%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-table-expanded-row .ant-table-thead>tr:first-child>th {
|
.dark .ant-table-expanded-row .ant-table-thead > tr:first-child > th {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,27 +532,27 @@ style attribute {
|
|||||||
|
|
||||||
.dark .ant-table-bordered,
|
.dark .ant-table-bordered,
|
||||||
.dark .ant-table-bordered.ant-table-empty .ant-table-placeholder,
|
.dark .ant-table-bordered.ant-table-empty .ant-table-placeholder,
|
||||||
.dark .ant-table-bordered .ant-table-body>table,
|
.dark .ant-table-bordered .ant-table-body > table,
|
||||||
.dark .ant-table-bordered .ant-table-fixed-left table,
|
.dark .ant-table-bordered .ant-table-fixed-left table,
|
||||||
.dark .ant-table-bordered .ant-table-fixed-right table,
|
.dark .ant-table-bordered .ant-table-fixed-right table,
|
||||||
.dark .ant-table-bordered .ant-table-header>table,
|
.dark .ant-table-bordered .ant-table-header > table,
|
||||||
.dark .ant-table-bordered .ant-table-thead>tr:not(:last-child)>th,
|
.dark .ant-table-bordered .ant-table-thead > tr:not(:last-child) > th,
|
||||||
.dark .ant-table-bordered .ant-table-tbody>tr>td,
|
.dark .ant-table-bordered .ant-table-tbody > tr > td,
|
||||||
.dark .ant-table-bordered .ant-table-thead>tr>th {
|
.dark .ant-table-bordered .ant-table-thead > tr > th {
|
||||||
border-color: #2C3950;
|
border-color: #2c3950;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-table-tbody>tr>td,
|
.dark .ant-table-tbody > tr > td,
|
||||||
.dark .ant-table-thead>tr>th,
|
.dark .ant-table-thead > tr > th,
|
||||||
.dark .ant-card-head,
|
.dark .ant-card-head,
|
||||||
.dark .ant-modal-header,
|
.dark .ant-modal-header,
|
||||||
.dark .ant-collapse>.ant-collapse-item,
|
.dark .ant-collapse > .ant-collapse-item,
|
||||||
.dark .ant-tabs-bar,
|
.dark .ant-tabs-bar,
|
||||||
.dark .ant-list-split .ant-list-item,
|
.dark .ant-list-split .ant-list-item,
|
||||||
.dark .ant-popover-title,
|
.dark .ant-popover-title,
|
||||||
.dark .ant-calendar-header,
|
.dark .ant-calendar-header,
|
||||||
.dark .ant-calendar-input-wrap {
|
.dark .ant-calendar-input-wrap {
|
||||||
border-bottom-color: #2C3950;
|
border-bottom-color: #2c3950;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-modal-footer,
|
.dark .ant-modal-footer,
|
||||||
@@ -505,8 +566,7 @@ style attribute {
|
|||||||
.dark .ant-progress-text,
|
.dark .ant-progress-text,
|
||||||
.dark .ant-card-head,
|
.dark .ant-card-head,
|
||||||
.dark .ant-form,
|
.dark .ant-form,
|
||||||
.dark .ant-collapse>.ant-collapse-item>.ant-collapse-header,
|
.dark .ant-collapse > .ant-collapse-item > .ant-collapse-header,
|
||||||
.dark .ant-form-item i,
|
|
||||||
.dark .ant-modal-close-x,
|
.dark .ant-modal-close-x,
|
||||||
.dark .ant-pagination-item a,
|
.dark .ant-pagination-item a,
|
||||||
.dark li:not(.ant-pagination-disabled) i,
|
.dark li:not(.ant-pagination-disabled) i,
|
||||||
@@ -522,17 +582,30 @@ style attribute {
|
|||||||
.dark .ant-divider-inner-text,
|
.dark .ant-divider-inner-text,
|
||||||
.dark .ant-popover-title,
|
.dark .ant-popover-title,
|
||||||
.dark .ant-popover-inner-content,
|
.dark .ant-popover-inner-content,
|
||||||
.dark h2 {
|
.dark h2,
|
||||||
color: rgb(255 255 255 / 65%);
|
.dark .ant-modal-title,
|
||||||
|
.dark .ant-form-item-label > label,
|
||||||
|
.dark .ant-checkbox-wrapper,
|
||||||
|
.dark .ant-form-item,
|
||||||
|
.dark .ant-calendar-footer .ant-calendar-today-btn,
|
||||||
|
.dark .ant-calendar-footer .ant-calendar-time-picker-btn,
|
||||||
|
.dark .ant-calendar-day-select,
|
||||||
|
.dark .ant-calendar-month-select,
|
||||||
|
.dark .ant-calendar-year-select,
|
||||||
|
.dark .ant-calendar-date,
|
||||||
|
.dark .ant-calendar-year-panel-year,
|
||||||
|
.dark .ant-calendar-month-panel-month,
|
||||||
|
.dark .ant-calendar-decade-panel-decade {
|
||||||
|
color: rgba(255, 255, 255, 0.65);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-list-item-meta-description {
|
.dark .ant-list-item-meta-description {
|
||||||
color: rgb(255 255 255 / 45%);
|
color: rgba(255, 255, 255, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-pagination-disabled i,
|
.dark .ant-pagination-disabled i,
|
||||||
.dark .ant-tabs-tab-btn-disabled {
|
.dark .ant-tabs-tab-btn-disabled {
|
||||||
color: rgb(255 255 255 / 25%);
|
color: rgba(255, 255, 255, 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-input,
|
.dark .ant-input,
|
||||||
@@ -553,9 +626,9 @@ style attribute {
|
|||||||
.dark .client-table-header,
|
.dark .client-table-header,
|
||||||
.dark .ant-select-selection--multiple .ant-select-selection__choice,
|
.dark .ant-select-selection--multiple .ant-select-selection__choice,
|
||||||
.dark .ant-calendar-time-picker-inner {
|
.dark .ant-calendar-time-picker-inner {
|
||||||
background-color: #222D42;
|
background-color: #222d42;
|
||||||
border-color: #2c3950;
|
border-color: #2c3950;
|
||||||
color: rgb(255 255 255 / 65%);
|
color: rgba(255, 255, 255, 0.65);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-select-selection:hover,
|
.dark .ant-select-selection:hover,
|
||||||
@@ -564,13 +637,13 @@ style attribute {
|
|||||||
.dark .ant-input-number:focus,
|
.dark .ant-input-number:focus,
|
||||||
.dark .ant-input:hover,
|
.dark .ant-input:hover,
|
||||||
.dark .ant-input:focus {
|
.dark .ant-input:focus {
|
||||||
background-color: rgb(14 73 181 / 30%);
|
background-color: rgba(14, 73, 181, 0.3);
|
||||||
border-color: #0E49B5;
|
border-color: #0e49b5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-btn:not(.ant-btn-primary):not(.ant-btn-danger) {
|
.dark .ant-btn:not(.ant-btn-primary):not(.ant-btn-danger) {
|
||||||
color: rgb(255 255 255 / 65%);
|
color: rgba(255, 255, 255, 0.65);
|
||||||
background-color: rgb(14 73 181 / 30%);
|
background-color: rgba(14, 73, 181, 0.3);
|
||||||
border: 1px solid #0e49b5;
|
border: 1px solid #0e49b5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,21 +654,25 @@ style attribute {
|
|||||||
border-color: #0e49b5;
|
border-color: #0e49b5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger) ,
|
.dark .ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),
|
||||||
.dark .ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger) {
|
.dark .ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger) {
|
||||||
color: #ffffff;
|
color: #fff;
|
||||||
background-color: rgb(14 73 181 / 50%);
|
background-color: rgb(14 73 181 / 50%);
|
||||||
border-color: #0e49b5;
|
border-color: #0e49b5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-btn-primary[disabled],
|
.dark .ant-btn-primary[disabled],
|
||||||
|
.dark .ant-btn-danger[disabled],
|
||||||
.dark .ant-calendar-ok-btn-disabled {
|
.dark .ant-calendar-ok-btn-disabled {
|
||||||
color: rgb(255 255 255 / 35%);
|
color: rgb(255 255 255 / 35%);
|
||||||
background-color: #2c3950;
|
background-color: #2c3950;
|
||||||
border-color: #42516c;
|
border-color: #42516c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-table-tbody>tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)>td,
|
.dark
|
||||||
|
.ant-table-tbody
|
||||||
|
> tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected)
|
||||||
|
> td,
|
||||||
.dark .client-table-odd-row {
|
.dark .client-table-odd-row {
|
||||||
background-color: #122444;
|
background-color: #122444;
|
||||||
}
|
}
|
||||||
@@ -612,10 +689,7 @@ style attribute {
|
|||||||
border-color: #0e49b5;
|
border-color: #0e49b5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-switch:not(.ant-switch-checked) {
|
.dark .ant-switch:not(.ant-switch-checked),
|
||||||
background-color: #2C3950;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .ant-progress-line .ant-progress-inner {
|
.dark .ant-progress-line .ant-progress-inner {
|
||||||
background-color: #2c3950;
|
background-color: #2c3950;
|
||||||
}
|
}
|
||||||
@@ -626,11 +700,11 @@ style attribute {
|
|||||||
|
|
||||||
.ant-dropdown-menu-dark,
|
.ant-dropdown-menu-dark,
|
||||||
.dark .ant-popover-inner {
|
.dark .ant-popover-inner {
|
||||||
background-color: #222D42;
|
background-color: #222d42;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark>.ant-popover-content>.ant-popover-arrow {
|
.dark > .ant-popover-content > .ant-popover-arrow {
|
||||||
border-color: #222D42;
|
border-color: #222d42;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-dropdown-menu-dark .ant-dropdown-menu-item:hover,
|
.ant-dropdown-menu-dark .ant-dropdown-menu-item:hover,
|
||||||
@@ -645,11 +719,11 @@ style attribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-alert-message {
|
.dark .ant-alert-message {
|
||||||
color: rgb(255 255 255 /85%);
|
color: rgba(255, 255, 255, 0.85);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-tag {
|
.dark .ant-tag {
|
||||||
color: rgb(255 255 255 / 65%);
|
color: rgba(255, 255, 255, 0.65);
|
||||||
background-color: #ffffff0a;
|
background-color: #ffffff0a;
|
||||||
border-color: #344461;
|
border-color: #344461;
|
||||||
}
|
}
|
||||||
@@ -663,7 +737,7 @@ style attribute {
|
|||||||
.dark .ant-tag-red,
|
.dark .ant-tag-red,
|
||||||
.dark .ant-alert-error {
|
.dark .ant-alert-error {
|
||||||
background-color: #291515;
|
background-color: #291515;
|
||||||
border-color: #5C2626;
|
border-color: #5c2626;
|
||||||
color: #e04141;
|
color: #e04141;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,7 +757,7 @@ style attribute {
|
|||||||
.dark .ant-tag-purple {
|
.dark .ant-tag-purple {
|
||||||
background-color: #2c1e32;
|
background-color: #2c1e32;
|
||||||
border-color: #49394e;
|
border-color: #49394e;
|
||||||
color: #f2eaf1;
|
color: #cfb9cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-modal-content,
|
.dark .ant-modal-content,
|
||||||
@@ -691,19 +765,6 @@ style attribute {
|
|||||||
background-color: #181f2c;
|
background-color: #181f2c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-modal-title,
|
|
||||||
.dark .ant-form-item-label>label,
|
|
||||||
.dark .ant-checkbox-wrapper,
|
|
||||||
.dark .ant-form-item,
|
|
||||||
.dark .ant-calendar-footer .ant-calendar-today-btn,
|
|
||||||
.dark .ant-calendar-footer .ant-calendar-time-picker-btn,
|
|
||||||
.dark .ant-calendar-day-select,
|
|
||||||
.dark .ant-calendar-month-select,
|
|
||||||
.dark .ant-calendar-year-select,
|
|
||||||
.dark .ant-calendar-date {
|
|
||||||
color: rgb(255 255 255 / 65%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .ant-calendar-next-month-btn-day .ant-calendar-date,
|
.dark .ant-calendar-next-month-btn-day .ant-calendar-date,
|
||||||
.dark .ant-calendar-last-month-cell .ant-calendar-date {
|
.dark .ant-calendar-last-month-cell .ant-calendar-date {
|
||||||
color: #2c3950;
|
color: #2c3950;
|
||||||
@@ -727,28 +788,264 @@ style attribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-calendar-time-picker-select li:focus {
|
.dark .ant-calendar-time-picker-select li:focus {
|
||||||
color: #ffffff;
|
color: #fff;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
outline: none;
|
outline: none;
|
||||||
background-color: #0e49b5;
|
background-color: #0e49b5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-calendar-time-picker-select {
|
.dark .ant-calendar-time-picker-select {
|
||||||
border-right-color: #2C3950;
|
border-right-color: #2c3950;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-warning .ant-input,
|
||||||
|
.has-warning .ant-input:hover {
|
||||||
|
background-color: #fff6e6;
|
||||||
|
border-color: #ffd98c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-warning .ant-input::placeholder {
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
|
||||||
|
.has-warning .ant-input:not([disabled]):hover {
|
||||||
|
border-color: #ffd98c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .has-warning .ant-input,
|
||||||
|
.dark .has-warning .ant-input:hover {
|
||||||
|
border-color: #784e1d;
|
||||||
|
background: rgb(49, 35, 19);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .has-warning .ant-input::placeholder {
|
||||||
|
color: rgb(255 160 49 / 70%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .has-warning .anticon {
|
||||||
|
color: #ffa031;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .has-success .anticon {
|
||||||
|
color: #61bf39;
|
||||||
|
animation-name: diffZoomIn1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .anticon-close-circle {
|
.dark .anticon-close-circle {
|
||||||
color: #E04141;
|
color: #e04141;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-spin-nested-loading>div>.ant-spin .ant-spin-text {
|
.dark .ant-spin-nested-loading > div > .ant-spin .ant-spin-text {
|
||||||
text-shadow: 0 1px 2px #00000077;
|
text-shadow: 0 1px 2px #00000077;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-spin {
|
.dark .ant-spin {
|
||||||
color: #ffffff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-spin-dot-item {
|
.dark .ant-spin-dot-item {
|
||||||
background-color: #ffffffff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-menu,
|
||||||
|
.ant-radio-button-wrapper {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-calendar-date:hover {
|
||||||
|
background-color: #dae9f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-calendar-date:active {
|
||||||
|
background-color: #dae9f5;
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-calendar-today .ant-calendar-date {
|
||||||
|
color: #0e49b5;
|
||||||
|
font-weight: 700;
|
||||||
|
border-color: #0e49b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-today .ant-calendar-date {
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
border-color: #0e49b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-calendar-selected-day .ant-calendar-date {
|
||||||
|
background: #0e49b5;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.ant-select-dropdown-menu-item:empty:after {
|
||||||
|
content: "None";
|
||||||
|
font-weight: normal;
|
||||||
|
color: rgba(0, 0, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark li.ant-select-dropdown-menu-item:empty:after {
|
||||||
|
content: "None";
|
||||||
|
font-weight: normal;
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select-dropdown.ant-select-dropdown--multiple
|
||||||
|
.ant-select-dropdown-menu-item:hover
|
||||||
|
.ant-select-selected-icon {
|
||||||
|
color: rgba(0, 0, 0, 0.87);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark.ant-select-dropdown.ant-select-dropdown--multiple
|
||||||
|
.ant-select-dropdown-menu-item:hover
|
||||||
|
.ant-select-selected-icon {
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-select-dropdown.ant-select-dropdown--multiple
|
||||||
|
.ant-select-dropdown-menu-item-selected
|
||||||
|
.ant-select-selected-icon,
|
||||||
|
.ant-select-dropdown.ant-select-dropdown--multiple
|
||||||
|
.ant-select-dropdown-menu-item-selected:hover
|
||||||
|
.ant-select-selected-icon {
|
||||||
|
color: #3c89e8;
|
||||||
|
}
|
||||||
|
.ant-select-selection:hover,
|
||||||
|
.ant-input-number-focused,
|
||||||
|
.ant-input-number:hover {
|
||||||
|
background-color: #edf4fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-input-number-handler:active {
|
||||||
|
background-color: #0e49b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-input-number-handler:hover .ant-input-number-handler-down-inner,
|
||||||
|
.dark .ant-input-number-handler:hover .ant-input-number-handler-up-inner {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-input-number-handler-down {
|
||||||
|
border-top: 1px solid rgba(217, 217, 217, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-year-panel-header .ant-calendar-year-panel-century-select,
|
||||||
|
.dark .ant-calendar-year-panel-header .ant-calendar-year-panel-decade-select,
|
||||||
|
.dark .ant-calendar-year-panel-header .ant-calendar-year-panel-month-select,
|
||||||
|
.dark
|
||||||
|
.ant-calendar-year-panel-header
|
||||||
|
.ant-calendar-year-panel-year-select
|
||||||
|
.dark
|
||||||
|
.ant-calendar-month-panel-header
|
||||||
|
.ant-calendar-month-panel-century-select,
|
||||||
|
.dark .ant-calendar-month-panel-header .ant-calendar-month-panel-decade-select,
|
||||||
|
.dark .ant-calendar-month-panel-header .ant-calendar-month-panel-month-select,
|
||||||
|
.dark .ant-calendar-month-panel-header .ant-calendar-month-panel-year-select {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-year-panel-header {
|
||||||
|
border-bottom: 1px solid #222d42;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-year-panel-last-decade-cell .ant-calendar-year-panel-year,
|
||||||
|
.dark .ant-calendar-year-panel-next-decade-cell .ant-calendar-year-panel-year {
|
||||||
|
color: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-year-panel-year:hover,
|
||||||
|
.dark .ant-calendar-month-panel-month:hover,
|
||||||
|
.dark .ant-calendar-decade-panel-decade:hover {
|
||||||
|
background-color: #222d42;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-header a:hover {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-month-panel-header {
|
||||||
|
background-color: #101828;
|
||||||
|
border-bottom: 1px solid #222d42;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-year-panel,
|
||||||
|
.dark .ant-calendar table {
|
||||||
|
background-color: #101828;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-year-panel-selected-cell .ant-calendar-year-panel-year,
|
||||||
|
.dark
|
||||||
|
.ant-calendar-year-panel-selected-cell
|
||||||
|
.ant-calendar-year-panel-year:hover,
|
||||||
|
.dark .ant-calendar-month-panel-selected-cell .ant-calendar-month-panel-month,
|
||||||
|
.dark
|
||||||
|
.ant-calendar-month-panel-selected-cell
|
||||||
|
.ant-calendar-month-panel-month:hover,
|
||||||
|
.dark
|
||||||
|
.ant-calendar-decade-panel-selected-cell
|
||||||
|
.ant-calendar-decade-panel-decade,
|
||||||
|
.dark
|
||||||
|
.ant-calendar-decade-panel-selected-cell
|
||||||
|
.ant-calendar-decade-panel-decade:hover {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #0e49b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-last-month-cell .ant-calendar-date,
|
||||||
|
.dark .ant-calendar-last-month-cell .ant-calendar-date:hover,
|
||||||
|
.dark .ant-calendar-next-month-btn-day .ant-calendar-date,
|
||||||
|
.dark .ant-calendar-next-month-btn-day .ant-calendar-date:hover {
|
||||||
|
color: rgb(255 255 255 / 25%);
|
||||||
|
background: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-today .ant-calendar-date:hover {
|
||||||
|
color: #fff;
|
||||||
|
border-color: #0e49b5;
|
||||||
|
background-color: #0e49b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark
|
||||||
|
.ant-calendar-decade-panel-last-century-cell
|
||||||
|
.ant-calendar-decade-panel-decade,
|
||||||
|
.dark
|
||||||
|
.ant-calendar-decade-panel-next-century-cell
|
||||||
|
.ant-calendar-decade-panel-decade {
|
||||||
|
color: rgb(255 255 255 / 25%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-decade-panel-header {
|
||||||
|
border-bottom: 1px solid #222d42;
|
||||||
|
background-color: #101828;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-checkbox-inner {
|
||||||
|
background-color: rgba(14, 73, 181, 0.3);
|
||||||
|
border-color: rgba(14, 73, 181, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-checkbox-checked .ant-checkbox-inner {
|
||||||
|
background-color: #0e49b5;
|
||||||
|
border-color: #0e49b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-input {
|
||||||
|
background-color: #101828;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .ant-calendar-input::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-input-number-handler-wrap {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-input-number-handler {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-input-number {
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ const supportLangs = [
|
|||||||
value: 'ru-RU',
|
value: 'ru-RU',
|
||||||
icon: '🇷🇺',
|
icon: '🇷🇺',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Tiếng Việt',
|
||||||
|
value: 'vi-VN',
|
||||||
|
icon: '🇻🇳',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function getLang() {
|
function getLang() {
|
||||||
@@ -60,4 +65,4 @@ function isSupportLang(lang) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ class DBInbound {
|
|||||||
return this.protocol === Protocols.HTTP;
|
return this.protocol === Protocols.HTTP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isWireguard() {
|
||||||
|
return this.protocol === Protocols.WIREGUARD;
|
||||||
|
}
|
||||||
|
|
||||||
get address() {
|
get address() {
|
||||||
let address = location.hostname;
|
let address = location.hostname;
|
||||||
if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") {
|
if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") {
|
||||||
@@ -137,8 +141,8 @@ class DBInbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get genInboundLinks() {
|
genInboundLinks(remarkModel) {
|
||||||
const inbound = this.toInbound();
|
const inbound = this.toInbound();
|
||||||
return inbound.genInboundLinks(this.remark);
|
return inbound.genInboundLinks(this.remark,remarkModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ const Protocols = {
|
|||||||
Shadowsocks: "shadowsocks",
|
Shadowsocks: "shadowsocks",
|
||||||
Socks: "socks",
|
Socks: "socks",
|
||||||
HTTP: "http",
|
HTTP: "http",
|
||||||
|
Wireguard: "wireguard"
|
||||||
};
|
};
|
||||||
|
|
||||||
const SSMethods = {
|
const SSMethods = {
|
||||||
@@ -46,18 +47,27 @@ const ALPN_OPTION = {
|
|||||||
HTTP1: "http/1.1",
|
HTTP1: "http/1.1",
|
||||||
};
|
};
|
||||||
|
|
||||||
const outboundDomainStrategies = [
|
const OutboundDomainStrategies = [
|
||||||
"AsIs",
|
"AsIs",
|
||||||
"UseIP",
|
"UseIP",
|
||||||
"UseIPv4",
|
"UseIPv4",
|
||||||
"UseIPv6"
|
"UseIPv6"
|
||||||
]
|
];
|
||||||
|
|
||||||
|
const WireguardDomainStrategy = [
|
||||||
|
"ForceIP",
|
||||||
|
"ForceIPv4",
|
||||||
|
"ForceIPv4v6",
|
||||||
|
"ForceIPv6",
|
||||||
|
"ForceIPv6v4"
|
||||||
|
];
|
||||||
|
|
||||||
Object.freeze(Protocols);
|
Object.freeze(Protocols);
|
||||||
Object.freeze(SSMethods);
|
Object.freeze(SSMethods);
|
||||||
Object.freeze(TLS_FLOW_CONTROL);
|
Object.freeze(TLS_FLOW_CONTROL);
|
||||||
Object.freeze(ALPN_OPTION);
|
Object.freeze(ALPN_OPTION);
|
||||||
Object.freeze(outboundDomainStrategies);
|
Object.freeze(OutboundDomainStrategies);
|
||||||
|
Object.freeze(WireguardDomainStrategy);
|
||||||
|
|
||||||
class CommonClass {
|
class CommonClass {
|
||||||
|
|
||||||
@@ -476,7 +486,7 @@ class Outbound extends CommonClass {
|
|||||||
if(data.length !=2) return null;
|
if(data.length !=2) return null;
|
||||||
switch(data[0].toLowerCase()){
|
switch(data[0].toLowerCase()){
|
||||||
case Protocols.VMess:
|
case Protocols.VMess:
|
||||||
return this.fromVmessLink(JSON.parse(atob(data[1])));
|
return this.fromVmessLink(JSON.parse(Base64.decode(data[1])));
|
||||||
case Protocols.VLESS:
|
case Protocols.VLESS:
|
||||||
case Protocols.Trojan:
|
case Protocols.Trojan:
|
||||||
case 'ss':
|
case 'ss':
|
||||||
@@ -493,8 +503,8 @@ class Outbound extends CommonClass {
|
|||||||
if (network === 'tcp') {
|
if (network === 'tcp') {
|
||||||
stream.tcp = new TcpStreamSettings(
|
stream.tcp = new TcpStreamSettings(
|
||||||
json.type,
|
json.type,
|
||||||
json.host ? json.host.split(','): [],
|
json.host ?? '',
|
||||||
json.path ? json.path.split(','): []);
|
json.path ?? '');
|
||||||
} else if (network === 'kcp') {
|
} else if (network === 'kcp') {
|
||||||
stream.kcp = new KcpStreamSettings();
|
stream.kcp = new KcpStreamSettings();
|
||||||
stream.type = json.type;
|
stream.type = json.type;
|
||||||
@@ -505,7 +515,7 @@ class Outbound extends CommonClass {
|
|||||||
stream.network = 'http'
|
stream.network = 'http'
|
||||||
stream.http = new HttpStreamSettings(
|
stream.http = new HttpStreamSettings(
|
||||||
json.path,
|
json.path,
|
||||||
json.host ? json.host.split(',') : []);
|
json.host);
|
||||||
} else if (network === 'quic') {
|
} else if (network === 'quic') {
|
||||||
stream.quic = new QuicStreamSettings(
|
stream.quic = new QuicStreamSettings(
|
||||||
json.host ? json.host : 'none',
|
json.host ? json.host : 'none',
|
||||||
@@ -570,7 +580,7 @@ class Outbound extends CommonClass {
|
|||||||
let sni=url.searchParams.get('sni') ?? '';
|
let sni=url.searchParams.get('sni') ?? '';
|
||||||
let sid=url.searchParams.get('sid') ?? '';
|
let sid=url.searchParams.get('sid') ?? '';
|
||||||
let spx=url.searchParams.get('spx') ?? '';
|
let spx=url.searchParams.get('spx') ?? '';
|
||||||
stream.tls = new RealityStreamSettings(pbk, fp, sni, sid, spx);
|
stream.reality = new RealityStreamSettings(pbk, fp, sni, sid, spx);
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = link.split('?');
|
let data = link.split('?');
|
||||||
@@ -581,6 +591,7 @@ class Outbound extends CommonClass {
|
|||||||
|
|
||||||
if (!match) return null;
|
if (!match) return null;
|
||||||
let [, protocol, userData, address, port, ] = match;
|
let [, protocol, userData, address, port, ] = match;
|
||||||
|
port *= 1;
|
||||||
if(protocol == 'ss') {
|
if(protocol == 'ss') {
|
||||||
protocol = 'shadowsocks';
|
protocol = 'shadowsocks';
|
||||||
userData = atob(userData).split(':');
|
userData = atob(userData).split(':');
|
||||||
@@ -624,6 +635,7 @@ Outbound.Settings = class extends CommonClass {
|
|||||||
case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings();
|
case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings();
|
||||||
case Protocols.Socks: return new Outbound.SocksSettings();
|
case Protocols.Socks: return new Outbound.SocksSettings();
|
||||||
case Protocols.HTTP: return new Outbound.HttpSettings();
|
case Protocols.HTTP: return new Outbound.HttpSettings();
|
||||||
|
case Protocols.Wireguard: return new Outbound.WireguardSettings();
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -639,6 +651,7 @@ Outbound.Settings = class extends CommonClass {
|
|||||||
case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json);
|
case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json);
|
||||||
case Protocols.Socks: return Outbound.SocksSettings.fromJson(json);
|
case Protocols.Socks: return Outbound.SocksSettings.fromJson(json);
|
||||||
case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json);
|
case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json);
|
||||||
|
case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json);
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -746,12 +759,13 @@ Outbound.VmessSettings = class extends CommonClass {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
Outbound.VLESSSettings = class extends CommonClass {
|
Outbound.VLESSSettings = class extends CommonClass {
|
||||||
constructor(address, port, id, flow) {
|
constructor(address, port, id, flow, encryption='none') {
|
||||||
super();
|
super();
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.flow = flow;
|
this.flow = flow;
|
||||||
|
this.encryption = encryption
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json={}) {
|
static fromJson(json={}) {
|
||||||
@@ -761,6 +775,7 @@ Outbound.VLESSSettings = class extends CommonClass {
|
|||||||
json.vnext[0].port,
|
json.vnext[0].port,
|
||||||
json.vnext[0].users[0].id,
|
json.vnext[0].users[0].id,
|
||||||
json.vnext[0].users[0].flow,
|
json.vnext[0].users[0].flow,
|
||||||
|
json.vnext[0].users[0].encryption,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,7 +784,7 @@ Outbound.VLESSSettings = class extends CommonClass {
|
|||||||
vnext: [{
|
vnext: [{
|
||||||
address: this.address,
|
address: this.address,
|
||||||
port: this.port,
|
port: this.port,
|
||||||
users: [{id: this.id, flow: this.flow}],
|
users: [{id: this.id, flow: this.flow, encryption: 'none',}],
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -812,7 +827,7 @@ Outbound.ShadowsocksSettings = class extends CommonClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json={}) {
|
static fromJson(json={}) {
|
||||||
servers = json.servers;
|
let servers = json.servers;
|
||||||
if(ObjectUtil.isArrEmpty(servers)) servers=[{}];
|
if(ObjectUtil.isArrEmpty(servers)) servers=[{}];
|
||||||
return new Outbound.ShadowsocksSettings(
|
return new Outbound.ShadowsocksSettings(
|
||||||
servers[0].address,
|
servers[0].address,
|
||||||
@@ -836,12 +851,12 @@ Outbound.ShadowsocksSettings = class extends CommonClass {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
Outbound.SocksSettings = class extends CommonClass {
|
Outbound.SocksSettings = class extends CommonClass {
|
||||||
constructor(address, port, user, password) {
|
constructor(address, port, user, pass) {
|
||||||
super();
|
super();
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.password = password;
|
this.pass = pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json={}) {
|
static fromJson(json={}) {
|
||||||
@@ -851,7 +866,7 @@ Outbound.SocksSettings = class extends CommonClass {
|
|||||||
servers[0].address,
|
servers[0].address,
|
||||||
servers[0].port,
|
servers[0].port,
|
||||||
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
|
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
|
||||||
ObjectUtil.isArrEmpty(servers[0].password) ? '' : servers[0].users[0].password,
|
ObjectUtil.isArrEmpty(servers[0].pass) ? '' : servers[0].users[0].pass,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,18 +875,18 @@ Outbound.SocksSettings = class extends CommonClass {
|
|||||||
servers: [{
|
servers: [{
|
||||||
address: this.address,
|
address: this.address,
|
||||||
port: this.port,
|
port: this.port,
|
||||||
users: ObjectUtil.isEmpty(this.user) ? [] : [{user: this.user, password: this.password}],
|
users: ObjectUtil.isEmpty(this.user) ? [] : [{user: this.user, pass: this.pass}],
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Outbound.HttpSettings = class extends CommonClass {
|
Outbound.HttpSettings = class extends CommonClass {
|
||||||
constructor(address, port, user, password) {
|
constructor(address, port, user, pass) {
|
||||||
super();
|
super();
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.password = password;
|
this.pass = pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json={}) {
|
static fromJson(json={}) {
|
||||||
@@ -881,7 +896,7 @@ Outbound.HttpSettings = class extends CommonClass {
|
|||||||
servers[0].address,
|
servers[0].address,
|
||||||
servers[0].port,
|
servers[0].port,
|
||||||
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
|
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
|
||||||
ObjectUtil.isArrEmpty(servers[0].password) ? '' : servers[0].users[0].password,
|
ObjectUtil.isArrEmpty(servers[0].pass) ? '' : servers[0].users[0].pass,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -890,8 +905,89 @@ Outbound.HttpSettings = class extends CommonClass {
|
|||||||
servers: [{
|
servers: [{
|
||||||
address: this.address,
|
address: this.address,
|
||||||
port: this.port,
|
port: this.port,
|
||||||
users: ObjectUtil.isEmpty(this.user) ? [] : [{user: this.user, password: this.password}],
|
users: ObjectUtil.isEmpty(this.user) ? [] : [{user: this.user, pass: this.pass}],
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
Outbound.WireguardSettings = class extends CommonClass {
|
||||||
|
constructor(
|
||||||
|
mtu=1420, secretKey=Wireguard.generateKeypair().privateKey,
|
||||||
|
address='', workers=2, domainStrategy='', reserved='',
|
||||||
|
peers=[new Outbound.WireguardSettings.Peer()], kernelMode=false) {
|
||||||
|
super();
|
||||||
|
this.mtu = mtu;
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
this.pubKey = secretKey.length>0 ? Wireguard.generateKeypair(secretKey).publicKey : '';
|
||||||
|
this.address = address instanceof Array ? address.join(',') : address;
|
||||||
|
this.workers = workers;
|
||||||
|
this.domainStrategy = domainStrategy;
|
||||||
|
this.reserved = reserved instanceof Array ? reserved.join(',') : reserved;
|
||||||
|
this.peers = peers;
|
||||||
|
this.kernelMode = kernelMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPeer() {
|
||||||
|
this.peers.push(new Outbound.WireguardSettings.Peer());
|
||||||
|
}
|
||||||
|
|
||||||
|
delPeer(index) {
|
||||||
|
this.peers.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json={}){
|
||||||
|
return new Outbound.WireguardSettings(
|
||||||
|
json.mtu,
|
||||||
|
json.secretKey,
|
||||||
|
json.address,
|
||||||
|
json.workers,
|
||||||
|
json.domainStrategy,
|
||||||
|
json.reserved,
|
||||||
|
json.peers.map(peer => Outbound.WireguardSettings.Peer.fromJson(peer)),
|
||||||
|
json.kernelMode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson() {
|
||||||
|
return {
|
||||||
|
mtu: this.mtu?? undefined,
|
||||||
|
secretKey: this.secretKey,
|
||||||
|
address: this.address ? this.address.split(",") : [],
|
||||||
|
workers: this.workers?? undefined,
|
||||||
|
domainStrategy: WireguardDomainStrategy.includes(this.domainStrategy) ? this.domainStrategy : undefined,
|
||||||
|
reserved: this.reserved ? this.reserved.split(",") : undefined,
|
||||||
|
peers: Outbound.WireguardSettings.Peer.toJsonArray(this.peers),
|
||||||
|
kernelMode: this.kernelMode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Outbound.WireguardSettings.Peer = class extends CommonClass {
|
||||||
|
constructor(publicKey='', psk='', allowedIPs=['0.0.0.0/0','::/0'], endpoint='', keepAlive=0) {
|
||||||
|
super();
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
this.psk = psk;
|
||||||
|
this.allowedIPs = allowedIPs;
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
this.keepAlive = keepAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json={}){
|
||||||
|
return new Outbound.WireguardSettings.Peer(
|
||||||
|
json.publicKey,
|
||||||
|
json.preSharedKey,
|
||||||
|
json.allowedIPs,
|
||||||
|
json.endpoint,
|
||||||
|
json.keepAlive
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson() {
|
||||||
|
return {
|
||||||
|
publicKey: this.publicKey,
|
||||||
|
preSharedKey: this.psk.length>0 ? this.psk : undefined,
|
||||||
|
allowedIPs: this.allowedIPs ? this.allowedIPs : undefined,
|
||||||
|
endpoint: this.endpoint,
|
||||||
|
keepAlive: this.keepAlive?? undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@@ -11,6 +11,7 @@ class AllSetting {
|
|||||||
this.pageSize = 0;
|
this.pageSize = 0;
|
||||||
this.expireDiff = "";
|
this.expireDiff = "";
|
||||||
this.trafficDiff = "";
|
this.trafficDiff = "";
|
||||||
|
this.remarkModel = "-ieo";
|
||||||
this.tgBotEnable = false;
|
this.tgBotEnable = false;
|
||||||
this.tgBotToken = "";
|
this.tgBotToken = "";
|
||||||
this.tgBotChatId = "";
|
this.tgBotChatId = "";
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const Protocols = {
|
|||||||
DOKODEMO: 'dokodemo-door',
|
DOKODEMO: 'dokodemo-door',
|
||||||
SOCKS: 'socks',
|
SOCKS: 'socks',
|
||||||
HTTP: 'http',
|
HTTP: 'http',
|
||||||
|
WIREGUARD: 'wireguard',
|
||||||
};
|
};
|
||||||
|
|
||||||
const SSMethods = {
|
const SSMethods = {
|
||||||
@@ -229,6 +230,7 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
|
|||||||
|
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
return {
|
||||||
|
version: this.version,
|
||||||
method: this.method,
|
method: this.method,
|
||||||
path: ObjectUtil.clone(this.path),
|
path: ObjectUtil.clone(this.path),
|
||||||
headers: XrayCommonClass.toV2Headers(this.headers),
|
headers: XrayCommonClass.toV2Headers(this.headers),
|
||||||
@@ -406,7 +408,7 @@ class HttpStreamSettings extends XrayCommonClass {
|
|||||||
|
|
||||||
class QuicStreamSettings extends XrayCommonClass {
|
class QuicStreamSettings extends XrayCommonClass {
|
||||||
constructor(security='none',
|
constructor(security='none',
|
||||||
key='', type='none') {
|
key=RandomUtil.randomSeq(10), type='none') {
|
||||||
super();
|
super();
|
||||||
this.security = security;
|
this.security = security;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
@@ -458,7 +460,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
|||||||
cipherSuites = '',
|
cipherSuites = '',
|
||||||
rejectUnknownSni = false,
|
rejectUnknownSni = false,
|
||||||
certificates=[new TlsStreamSettings.Cert()],
|
certificates=[new TlsStreamSettings.Cert()],
|
||||||
alpn=[],
|
alpn=[ALPN_OPTION.H2,ALPN_OPTION.HTTP1],
|
||||||
settings=new TlsStreamSettings.Settings()) {
|
settings=new TlsStreamSettings.Settings()) {
|
||||||
super();
|
super();
|
||||||
this.sni = serverName;
|
this.sni = serverName;
|
||||||
@@ -812,7 +814,7 @@ class Sniffing extends XrayCommonClass {
|
|||||||
class Inbound extends XrayCommonClass {
|
class Inbound extends XrayCommonClass {
|
||||||
constructor(port=RandomUtil.randomIntRange(10000, 60000),
|
constructor(port=RandomUtil.randomIntRange(10000, 60000),
|
||||||
listen='',
|
listen='',
|
||||||
protocol=Protocols.VMESS,
|
protocol=Protocols.VLESS,
|
||||||
settings=null,
|
settings=null,
|
||||||
streamSettings=new StreamSettings(),
|
streamSettings=new StreamSettings(),
|
||||||
tag='',
|
tag='',
|
||||||
@@ -1138,7 +1140,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (security === 'reality') {
|
else if (security === 'reality') {
|
||||||
params.set("security", "reality");
|
params.set("security", "reality");
|
||||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||||
@@ -1156,6 +1158,10 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
params.set("security", "none");
|
||||||
|
}
|
||||||
|
|
||||||
const link = `vless://${uuid}@${address}:${port}`;
|
const link = `vless://${uuid}@${address}:${port}`;
|
||||||
const url = new URL(link);
|
const url = new URL(link);
|
||||||
for (const [key, value] of params) {
|
for (const [key, value] of params) {
|
||||||
@@ -1314,7 +1320,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (security === 'reality') {
|
else if (security === 'reality') {
|
||||||
params.set("security", "reality");
|
params.set("security", "reality");
|
||||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||||
@@ -1328,6 +1334,9 @@ class Inbound extends XrayCommonClass {
|
|||||||
params.set("spx", this.stream.reality.settings.spiderX);
|
params.set("spx", this.stream.reality.settings.spiderX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
params.set("security", "none");
|
||||||
|
}
|
||||||
|
|
||||||
const link = `trojan://${clientPassword}@${address}:${port}`;
|
const link = `trojan://${clientPassword}@${address}:${port}`;
|
||||||
const url = new URL(link);
|
const url = new URL(link);
|
||||||
@@ -1352,20 +1361,28 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genAllLinks(remark='', client){
|
genAllLinks(remark='', remarkModel = '-ieo', client){
|
||||||
let result = [];
|
let result = [];
|
||||||
let email = client ? client.email : '';
|
let email = client ? client.email : '';
|
||||||
let addr = !ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0" ? this.listen : location.hostname;
|
let addr = !ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0" ? this.listen : location.hostname;
|
||||||
let port = this.port
|
let port = this.port;
|
||||||
|
const separationChar = remarkModel.charAt(0);
|
||||||
|
const orderChars = remarkModel.slice(1);
|
||||||
|
let orders = {
|
||||||
|
'i': remark,
|
||||||
|
'e': client ? client.email : '',
|
||||||
|
'o': '',
|
||||||
|
};
|
||||||
if(ObjectUtil.isArrEmpty(this.stream.externalProxy)){
|
if(ObjectUtil.isArrEmpty(this.stream.externalProxy)){
|
||||||
let r = [remark, email].filter(x => x.length > 0).join('-');
|
let r = orderChars.split('').map(char => orders[char]).filter(x => x.length > 0).join(separationChar);
|
||||||
result.push({
|
result.push({
|
||||||
remark: r,
|
remark: r,
|
||||||
link: this.genLink(addr, port, 'same', r, client)
|
link: this.genLink(addr, port, 'same', r, client)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.stream.externalProxy.forEach((ep) => {
|
this.stream.externalProxy.forEach((ep) => {
|
||||||
let r = [remark, email, ep.remark].filter(x => x.length > 0).join('-')
|
orders['o'] = ep.remark;
|
||||||
|
let r = orderChars.split('').map(char => orders[char]).filter(x => x.length > 0).join(separationChar);
|
||||||
result.push({
|
result.push({
|
||||||
remark: r,
|
remark: r,
|
||||||
link: this.genLink(ep.dest, ep.port, ep.forceTls, r, client)
|
link: this.genLink(ep.dest, ep.port, ep.forceTls, r, client)
|
||||||
@@ -1375,11 +1392,11 @@ class Inbound extends XrayCommonClass {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
genInboundLinks(remark = '') {
|
genInboundLinks(remark = '', remarkModel = '-ieo') {
|
||||||
if(this.clients){
|
if(this.clients){
|
||||||
let links = [];
|
let links = [];
|
||||||
this.clients.forEach((client) => {
|
this.clients.forEach((client) => {
|
||||||
this.genAllLinks(remark,client).forEach(l => {
|
this.genAllLinks(remark,remarkModel,client).forEach(l => {
|
||||||
links.push(l.link);
|
links.push(l.link);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -1436,6 +1453,7 @@ Inbound.Settings = class extends XrayCommonClass {
|
|||||||
case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol);
|
case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol);
|
||||||
case Protocols.SOCKS: return new Inbound.SocksSettings(protocol);
|
case Protocols.SOCKS: return new Inbound.SocksSettings(protocol);
|
||||||
case Protocols.HTTP: return new Inbound.HttpSettings(protocol);
|
case Protocols.HTTP: return new Inbound.HttpSettings(protocol);
|
||||||
|
case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol);
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1449,6 +1467,7 @@ Inbound.Settings = class extends XrayCommonClass {
|
|||||||
case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json);
|
case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json);
|
||||||
case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json);
|
case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json);
|
||||||
case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json);
|
case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json);
|
||||||
|
case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json);
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1555,7 +1574,7 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
|
|||||||
fallbacks=[],) {
|
fallbacks=[],) {
|
||||||
super(protocol);
|
super(protocol);
|
||||||
this.vlesses = vlesses;
|
this.vlesses = vlesses;
|
||||||
this.decryption = 'none'; // Using decryption is not implemented here
|
this.decryption = decryption;
|
||||||
this.fallbacks = fallbacks;
|
this.fallbacks = fallbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1687,11 +1706,11 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
|
|||||||
this.fallbacks = fallbacks;
|
this.fallbacks = fallbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
addTrojanFallback() {
|
addFallback() {
|
||||||
this.fallbacks.push(new Inbound.TrojanSettings.Fallback());
|
this.fallbacks.push(new Inbound.TrojanSettings.Fallback());
|
||||||
}
|
}
|
||||||
|
|
||||||
delTrojanFallback(index) {
|
delFallback(index) {
|
||||||
this.fallbacks.splice(index, 1);
|
this.fallbacks.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2039,3 +2058,69 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass {
|
|||||||
return new Inbound.HttpSettings.HttpAccount(json.user, json.pass);
|
return new Inbound.HttpSettings.HttpAccount(json.user, json.pass);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Inbound.WireguardSettings = class extends XrayCommonClass {
|
||||||
|
constructor(protocol, mtu=1420, secretKey=Wireguard.generateKeypair().privateKey, peers=[new Inbound.WireguardSettings.Peer()], kernelMode=false) {
|
||||||
|
super(protocol);
|
||||||
|
this.mtu = mtu;
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
this.pubKey = secretKey.length>0 ? Wireguard.generateKeypair(secretKey).publicKey : '';
|
||||||
|
this.peers = peers;
|
||||||
|
this.kernelMode = kernelMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPeer() {
|
||||||
|
this.peers.push(new Inbound.WireguardSettings.Peer());
|
||||||
|
}
|
||||||
|
|
||||||
|
delPeer(index) {
|
||||||
|
this.peers.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json={}){
|
||||||
|
return new Inbound.WireguardSettings(
|
||||||
|
Protocols.WIREGUARD,
|
||||||
|
json.mtu,
|
||||||
|
json.secretKey,
|
||||||
|
json.peers.map(peer => Inbound.WireguardSettings.Peer.fromJson(peer)),
|
||||||
|
json.kernelMode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson() {
|
||||||
|
return {
|
||||||
|
mtu: this.mtu?? undefined,
|
||||||
|
secretKey: this.secretKey,
|
||||||
|
peers: Inbound.WireguardSettings.Peer.toJsonArray(this.peers),
|
||||||
|
kernelMode: this.kernelMode,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
|
||||||
|
constructor(publicKey='', psk='', allowedIPs=['0.0.0.0/0','::/0'], keepAlive=0) {
|
||||||
|
super();
|
||||||
|
this.publicKey = publicKey;
|
||||||
|
this.psk = psk;
|
||||||
|
this.allowedIPs = allowedIPs;
|
||||||
|
this.keepAlive = keepAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json={}){
|
||||||
|
return new Inbound.WireguardSettings.Peer(
|
||||||
|
json.publicKey,
|
||||||
|
json.preSharedKey,
|
||||||
|
json.allowedIPs,
|
||||||
|
json.keepAlive
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toJson() {
|
||||||
|
return {
|
||||||
|
publicKey: this.publicKey,
|
||||||
|
preSharedKey: this.psk.length>0 ? this.psk : undefined,
|
||||||
|
allowedIPs: this.allowedIPs,
|
||||||
|
keepAlive: this.keepAlive?? undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -20,6 +20,14 @@ function sizeFormat(size) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cpuCoreFormat(cores) {
|
||||||
|
if (cores === 1) {
|
||||||
|
return "1 Core";
|
||||||
|
} else {
|
||||||
|
return cores + " Cores";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function base64(str) {
|
function base64(str) {
|
||||||
return Base64.encode(str);
|
return Base64.encode(str);
|
||||||
}
|
}
|
||||||
@@ -113,35 +121,35 @@ function usageColor(data, threshold, total) {
|
|||||||
function clientUsageColor(clientStats, trafficDiff) {
|
function clientUsageColor(clientStats, trafficDiff) {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case !clientStats || clientStats.total == 0:
|
case !clientStats || clientStats.total == 0:
|
||||||
return "#7a316f";
|
return "#7a316f"; // Purple
|
||||||
case clientStats.up + clientStats.down < clientStats.total - trafficDiff:
|
case clientStats.up + clientStats.down < clientStats.total - trafficDiff:
|
||||||
return "#0e49b5";
|
return "#0e49b5"; // Blue
|
||||||
case clientStats.up + clientStats.down < clientStats.total:
|
case clientStats.up + clientStats.down < clientStats.total:
|
||||||
return "#ffa031";
|
return "#f37b24"; // Orange
|
||||||
default:
|
default:
|
||||||
return "#e04141";
|
return "#e04141"; // red
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function userExpiryColor(threshold, client, isDark = false) {
|
function userExpiryColor(threshold, client, isDark = false) {
|
||||||
if (!client.enable) {
|
if (!client.enable) {
|
||||||
return isDark ? '#2c3950' : '#bcbcbc';
|
return isDark ? '#2c3950' : '#bcbcbc'; // Gray
|
||||||
}
|
}
|
||||||
now = new Date().getTime(),
|
now = new Date().getTime(),
|
||||||
expiry = client.expiryTime;
|
expiry = client.expiryTime;
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case expiry === null:
|
case expiry === null:
|
||||||
return "#389e0d";
|
return "#7a316f"; // Purple
|
||||||
case expiry < 0:
|
case expiry < 0:
|
||||||
return "#0e49b5";
|
return "#0e49b5"; // Blue
|
||||||
case expiry == 0:
|
case expiry == 0:
|
||||||
return "#7a316f";
|
return "#7a316f"; // Purple
|
||||||
case now < expiry - threshold:
|
case now < expiry - threshold:
|
||||||
return "#0e49b5";
|
return "#0e49b5"; // Blue
|
||||||
case now < expiry:
|
case now < expiry:
|
||||||
return "#ffa031";
|
return "#f37b24"; // Orange
|
||||||
default:
|
default:
|
||||||
return "#e04141";
|
return "#e04141"; // red
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ class ObjectUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return obj.toString().toLowerCase().indexOf(key.toLowerCase()) >= 0;
|
return this.isEmpty(obj) ? false : obj.toString().toLowerCase().indexOf(key.toLowerCase()) >= 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -296,3 +296,190 @@ class ObjectUtil {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Wireguard {
|
||||||
|
static gf(init) {
|
||||||
|
var r = new Float64Array(16);
|
||||||
|
if (init) {
|
||||||
|
for (var i = 0; i < init.length; ++i)
|
||||||
|
r[i] = init[i];
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pack(o, n) {
|
||||||
|
var b, m = this.gf(), t = this.gf();
|
||||||
|
for (var i = 0; i < 16; ++i)
|
||||||
|
t[i] = n[i];
|
||||||
|
this.carry(t);
|
||||||
|
this.carry(t);
|
||||||
|
this.carry(t);
|
||||||
|
for (var j = 0; j < 2; ++j) {
|
||||||
|
m[0] = t[0] - 0xffed;
|
||||||
|
for (var i = 1; i < 15; ++i) {
|
||||||
|
m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
|
||||||
|
m[i - 1] &= 0xffff;
|
||||||
|
}
|
||||||
|
m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
|
||||||
|
b = (m[15] >> 16) & 1;
|
||||||
|
m[14] &= 0xffff;
|
||||||
|
this.cswap(t, m, 1 - b);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < 16; ++i) {
|
||||||
|
o[2 * i] = t[i] & 0xff;
|
||||||
|
o[2 * i + 1] = t[i] >> 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static carry(o) {
|
||||||
|
var c;
|
||||||
|
for (var i = 0; i < 16; ++i) {
|
||||||
|
o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536);
|
||||||
|
o[i] &= 0xffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static cswap(p, q, b) {
|
||||||
|
var t, c = ~(b - 1);
|
||||||
|
for (var i = 0; i < 16; ++i) {
|
||||||
|
t = c & (p[i] ^ q[i]);
|
||||||
|
p[i] ^= t;
|
||||||
|
q[i] ^= t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static add(o, a, b) {
|
||||||
|
for (var i = 0; i < 16; ++i)
|
||||||
|
o[i] = (a[i] + b[i]) | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static subtract(o, a, b) {
|
||||||
|
for (var i = 0; i < 16; ++i)
|
||||||
|
o[i] = (a[i] - b[i]) | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static multmod(o, a, b) {
|
||||||
|
var t = new Float64Array(31);
|
||||||
|
for (var i = 0; i < 16; ++i) {
|
||||||
|
for (var j = 0; j < 16; ++j)
|
||||||
|
t[i + j] += a[i] * b[j];
|
||||||
|
}
|
||||||
|
for (var i = 0; i < 15; ++i)
|
||||||
|
t[i] += 38 * t[i + 16];
|
||||||
|
for (var i = 0; i < 16; ++i)
|
||||||
|
o[i] = t[i];
|
||||||
|
this.carry(o);
|
||||||
|
this.carry(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
static invert(o, i) {
|
||||||
|
var c = this.gf();
|
||||||
|
for (var a = 0; a < 16; ++a)
|
||||||
|
c[a] = i[a];
|
||||||
|
for (var a = 253; a >= 0; --a) {
|
||||||
|
this.multmod(c, c, c);
|
||||||
|
if (a !== 2 && a !== 4)
|
||||||
|
this.multmod(c, c, i);
|
||||||
|
}
|
||||||
|
for (var a = 0; a < 16; ++a)
|
||||||
|
o[a] = c[a];
|
||||||
|
}
|
||||||
|
|
||||||
|
static clamp(z) {
|
||||||
|
z[31] = (z[31] & 127) | 64;
|
||||||
|
z[0] &= 248;
|
||||||
|
}
|
||||||
|
|
||||||
|
static generatePublicKey(privateKey) {
|
||||||
|
var r, z = new Uint8Array(32);
|
||||||
|
var a = this.gf([1]),
|
||||||
|
b = this.gf([9]),
|
||||||
|
c = this.gf(),
|
||||||
|
d = this.gf([1]),
|
||||||
|
e = this.gf(),
|
||||||
|
f = this.gf(),
|
||||||
|
_121665 = this.gf([0xdb41, 1]),
|
||||||
|
_9 = this.gf([9]);
|
||||||
|
for (var i = 0; i < 32; ++i)
|
||||||
|
z[i] = privateKey[i];
|
||||||
|
this.clamp(z);
|
||||||
|
for (var i = 254; i >= 0; --i) {
|
||||||
|
r = (z[i >>> 3] >>> (i & 7)) & 1;
|
||||||
|
this.cswap(a, b, r);
|
||||||
|
this.cswap(c, d, r);
|
||||||
|
this.add(e, a, c);
|
||||||
|
this.subtract(a, a, c);
|
||||||
|
this.add(c, b, d);
|
||||||
|
this.subtract(b, b, d);
|
||||||
|
this.multmod(d, e, e);
|
||||||
|
this.multmod(f, a, a);
|
||||||
|
this.multmod(a, c, a);
|
||||||
|
this.multmod(c, b, e);
|
||||||
|
this.add(e, a, c);
|
||||||
|
this.subtract(a, a, c);
|
||||||
|
this.multmod(b, a, a);
|
||||||
|
this.subtract(c, d, f);
|
||||||
|
this.multmod(a, c, _121665);
|
||||||
|
this.add(a, a, d);
|
||||||
|
this.multmod(c, c, a);
|
||||||
|
this.multmod(a, d, f);
|
||||||
|
this.multmod(d, b, _9);
|
||||||
|
this.multmod(b, e, e);
|
||||||
|
this.cswap(a, b, r);
|
||||||
|
this.cswap(c, d, r);
|
||||||
|
}
|
||||||
|
this.invert(c, c);
|
||||||
|
this.multmod(a, a, c);
|
||||||
|
this.pack(z, a);
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
static generatePresharedKey() {
|
||||||
|
var privateKey = new Uint8Array(32);
|
||||||
|
window.crypto.getRandomValues(privateKey);
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
static generatePrivateKey() {
|
||||||
|
var privateKey = this.generatePresharedKey();
|
||||||
|
this.clamp(privateKey);
|
||||||
|
return privateKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
static encodeBase64(dest, src) {
|
||||||
|
var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]);
|
||||||
|
for (var i = 0; i < 4; ++i)
|
||||||
|
dest[i] = input[i] + 65 +
|
||||||
|
(((25 - input[i]) >> 8) & 6) -
|
||||||
|
(((51 - input[i]) >> 8) & 75) -
|
||||||
|
(((61 - input[i]) >> 8) & 15) +
|
||||||
|
(((62 - input[i]) >> 8) & 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static keyToBase64(key) {
|
||||||
|
var i, base64 = new Uint8Array(44);
|
||||||
|
for (i = 0; i < 32 / 3; ++i)
|
||||||
|
this.encodeBase64(base64.subarray(i * 4), key.subarray(i * 3));
|
||||||
|
this.encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0]));
|
||||||
|
base64[43] = 61;
|
||||||
|
return String.fromCharCode.apply(null, base64);
|
||||||
|
}
|
||||||
|
|
||||||
|
static keyFromBase64(encoded) {
|
||||||
|
const binaryStr = atob(encoded);
|
||||||
|
const bytes = new Uint8Array(binaryStr.length);
|
||||||
|
for (let i = 0; i < binaryStr.length; i++) {
|
||||||
|
bytes[i] = binaryStr.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static generateKeypair(secretKey='') {
|
||||||
|
var privateKey = secretKey.length>0 ? this.keyFromBase64(secretKey) : this.generatePrivateKey();
|
||||||
|
var publicKey = this.generatePublicKey(privateKey);
|
||||||
|
return {
|
||||||
|
publicKey: this.keyToBase64(publicKey),
|
||||||
|
privateKey: secretKey.length>0 ? secretKey : this.keyToBase64(privateKey)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"x-ui/database/model"
|
"x-ui/database/model"
|
||||||
@@ -35,6 +36,7 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
|
|||||||
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
g.POST("/resetAllTraffics", a.resetAllTraffics)
|
||||||
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
|
g.POST("/resetAllClientTraffics/:id", a.resetAllClientTraffics)
|
||||||
g.POST("/delDepletedClients/:id", a.delDepletedClients)
|
g.POST("/delDepletedClients/:id", a.delDepletedClients)
|
||||||
|
g.POST("/import", a.importInbound)
|
||||||
g.POST("/onlines", a.onlines)
|
g.POST("/onlines", a.onlines)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +256,31 @@ func (a *InboundController) delDepletedClients(c *gin.Context) {
|
|||||||
jsonMsg(c, "All delpeted clients are deleted", nil)
|
jsonMsg(c, "All delpeted clients are deleted", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *InboundController) importInbound(c *gin.Context) {
|
||||||
|
inbound := &model.Inbound{}
|
||||||
|
err := json.Unmarshal([]byte(c.PostForm("data")), inbound)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "Something went wrong!", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user := session.GetLoginUser(c)
|
||||||
|
inbound.Id = 0
|
||||||
|
inbound.UserId = user.Id
|
||||||
|
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
|
||||||
|
|
||||||
|
for index := range inbound.ClientStats {
|
||||||
|
inbound.ClientStats[index].Id = 0
|
||||||
|
inbound.ClientStats[index].Enable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
needRestart := false
|
||||||
|
inbound, needRestart, err = a.inboundService.AddInbound(inbound)
|
||||||
|
jsonMsgObj(c, I18nWeb(c, "pages.inbounds.create"), inbound, err)
|
||||||
|
if err == nil && needRestart {
|
||||||
|
a.xrayService.SetToNeedRestart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *InboundController) onlines(c *gin.Context) {
|
func (a *InboundController) onlines(c *gin.Context) {
|
||||||
jsonObj(c, a.inboundService.GetOnlineClinets(), nil)
|
jsonObj(c, a.inboundService.GetOnlineClinets(), nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type XraySettingController struct {
|
|||||||
XraySettingService service.XraySettingService
|
XraySettingService service.XraySettingService
|
||||||
SettingService service.SettingService
|
SettingService service.SettingService
|
||||||
InboundService service.InboundService
|
InboundService service.InboundService
|
||||||
|
XrayService service.XrayService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewXraySettingController(g *gin.RouterGroup) *XraySettingController {
|
func NewXraySettingController(g *gin.RouterGroup) *XraySettingController {
|
||||||
@@ -23,7 +24,9 @@ func (a *XraySettingController) initRouter(g *gin.RouterGroup) {
|
|||||||
|
|
||||||
g.POST("/", a.getXraySetting)
|
g.POST("/", a.getXraySetting)
|
||||||
g.POST("/update", a.updateSetting)
|
g.POST("/update", a.updateSetting)
|
||||||
|
g.GET("/getXrayResult", a.getXrayResult)
|
||||||
g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig)
|
g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig)
|
||||||
|
g.POST("/warp/:action", a.warp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *XraySettingController) getXraySetting(c *gin.Context) {
|
func (a *XraySettingController) getXraySetting(c *gin.Context) {
|
||||||
@@ -55,3 +58,29 @@ func (a *XraySettingController) getDefaultXrayConfig(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
jsonObj(c, defaultJsonConfig, nil)
|
jsonObj(c, defaultJsonConfig, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *XraySettingController) getXrayResult(c *gin.Context) {
|
||||||
|
jsonObj(c, a.XrayService.GetXrayResult(), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *XraySettingController) warp(c *gin.Context) {
|
||||||
|
action := c.Param("action")
|
||||||
|
var resp string
|
||||||
|
var err error
|
||||||
|
switch action {
|
||||||
|
case "data":
|
||||||
|
resp, err = a.XraySettingService.GetWarp()
|
||||||
|
case "config":
|
||||||
|
resp, err = a.XraySettingService.GetWarpConfig()
|
||||||
|
case "reg":
|
||||||
|
skey := c.PostForm("privateKey")
|
||||||
|
pkey := c.PostForm("publicKey")
|
||||||
|
resp, err = a.XraySettingService.RegWarp(skey, pkey)
|
||||||
|
case "license":
|
||||||
|
license := c.PostForm("license")
|
||||||
|
println(license)
|
||||||
|
resp, err = a.XraySettingService.SetWarpLicence(license)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonObj(c, resp, err)
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ type AllSetting struct {
|
|||||||
PageSize int `json:"pageSize" form:"pageSize"`
|
PageSize int `json:"pageSize" form:"pageSize"`
|
||||||
ExpireDiff int `json:"expireDiff" form:"expireDiff"`
|
ExpireDiff int `json:"expireDiff" form:"expireDiff"`
|
||||||
TrafficDiff int `json:"trafficDiff" form:"trafficDiff"`
|
TrafficDiff int `json:"trafficDiff" form:"trafficDiff"`
|
||||||
|
RemarkModel string `json:"remarkModel" form:"remarkModel"`
|
||||||
TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"`
|
TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"`
|
||||||
TgBotToken string `json:"tgBotToken" form:"tgBotToken"`
|
TgBotToken string `json:"tgBotToken" form:"tgBotToken"`
|
||||||
TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"`
|
TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"`
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
this.client = client;
|
this.client = client;
|
||||||
this.subId = '';
|
this.subId = '';
|
||||||
this.qrcodes = [];
|
this.qrcodes = [];
|
||||||
this.inbound.genAllLinks(this.dbInbound.remark, client).forEach(l => {
|
this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, client).forEach(l => {
|
||||||
this.qrcodes.push({
|
this.qrcodes.push({
|
||||||
remark: l.remark,
|
remark: l.remark,
|
||||||
link: l.link
|
link: l.link
|
||||||
|
|||||||
@@ -30,7 +30,10 @@
|
|||||||
this.clipboard = new ClipboardJS('#txt-modal-ok-btn', {
|
this.clipboard = new ClipboardJS('#txt-modal-ok-btn', {
|
||||||
text: () => this.content,
|
text: () => this.content,
|
||||||
});
|
});
|
||||||
this.clipboard.on('success', () => app.$message.success('{{ i18n "copied" }}'));
|
this.clipboard.on('success', () => {
|
||||||
|
app.$message.success('{{ i18n "copied" }}')
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,112 +2,99 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
{{template "head" .}}
|
{{template "head" .}}
|
||||||
<style>
|
<style>
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 20px 0 50px 0;
|
margin: 20px 0 50px 0;
|
||||||
}
|
}
|
||||||
|
.ant-btn,
|
||||||
.ant-btn, .ant-input {
|
.ant-input {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
}
|
}
|
||||||
|
.ant-input-group-addon {
|
||||||
.ant-input-group-addon {
|
border-radius: 0 30px 30px 0;
|
||||||
border-radius: 0 30px 30px 0;
|
width: 50px;
|
||||||
width: 50px;
|
font-size: 18px;
|
||||||
font-size: 18px;
|
}
|
||||||
}
|
.ant-input-affix-wrapper .ant-input-prefix {
|
||||||
|
left: 23px;
|
||||||
.ant-input-affix-wrapper .ant-input-prefix {
|
}
|
||||||
left: 23px;
|
.ant-input-affix-wrapper .ant-input:not(:first-child) {
|
||||||
}
|
padding-left: 50px;
|
||||||
|
}
|
||||||
.ant-input-affix-wrapper .ant-input:not(:first-child) {
|
.centered {
|
||||||
padding-left: 50px;
|
display: flex;
|
||||||
}
|
text-align: center;
|
||||||
|
align-items: center;
|
||||||
.centered {
|
justify-content: center;
|
||||||
display: flex;
|
width: 100%;
|
||||||
text-align: center;
|
}
|
||||||
align-items: center;
|
.title {
|
||||||
justify-content: center;
|
font-size: 32px;
|
||||||
}
|
font-weight: bold;
|
||||||
|
}
|
||||||
.title {
|
#app {
|
||||||
font-size: 32px;
|
overflow: hidden;
|
||||||
font-weight: bold;
|
}
|
||||||
}
|
#login {
|
||||||
|
background-color: #fff;
|
||||||
#app {
|
border-radius: 2rem;
|
||||||
overflow: hidden;
|
padding: 3rem;
|
||||||
}
|
transition: all 0.3s;
|
||||||
#login {
|
}
|
||||||
animation: charge .5s both;
|
#login:hover {
|
||||||
background-color: #fff;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
|
||||||
border-radius: 2rem;
|
}
|
||||||
padding: 3rem;
|
.wave {
|
||||||
}
|
opacity: 0.6;
|
||||||
#login:hover {
|
position: absolute;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,.09);
|
bottom: 40%;
|
||||||
}
|
left: 50%;
|
||||||
@keyframes charge {
|
width: 6000px;
|
||||||
from {transform: translateY(5rem);opacity: 0}
|
height: 6000px;
|
||||||
to {transform: translateY(0);opacity: 1}
|
background-color: rgba(0, 135, 113, 0.08);
|
||||||
}
|
margin-left: -3000px;
|
||||||
@keyframes wave {
|
transform-origin: 50% 48%;
|
||||||
from {transform: rotate(0deg);}
|
border-radius: 46%;
|
||||||
to {transform: rotate(360deg);}
|
pointer-events: none;
|
||||||
}
|
rotate: 125deg;
|
||||||
.wave {
|
}
|
||||||
opacity: .6;
|
.wave2 {
|
||||||
position: absolute;
|
opacity: 0.4;
|
||||||
bottom: 40%;
|
rotate: 70deg;
|
||||||
left: 50%;
|
}
|
||||||
width: 6000px;
|
.wave3 {
|
||||||
height: 6000px;
|
opacity: 0.2;
|
||||||
background: #000;
|
rotate: 90deg;
|
||||||
margin-left: -3000px;
|
}
|
||||||
transform-origin: 50% 48%;
|
.under {
|
||||||
border-radius: 46%;
|
background-color: #dce9f5;
|
||||||
animation: wave 72s infinite linear;
|
}
|
||||||
pointer-events: none;
|
.dark .wave {
|
||||||
}
|
background: rgba(14, 73, 181, 0.2);
|
||||||
.wave2 {
|
}
|
||||||
animation: wave 88s infinite linear;
|
.dark .under {
|
||||||
opacity: .3;
|
background-color: #101828;
|
||||||
}
|
}
|
||||||
.wave3 {
|
.dark #login {
|
||||||
animation: wave 80s infinite linear;
|
background-color: #151f31;
|
||||||
opacity: .1;
|
}
|
||||||
}
|
.dark h1 {
|
||||||
.wave {
|
color: rgba(255, 255, 255, 0.85);
|
||||||
background: #0e49b515;
|
}
|
||||||
}
|
.ant-form-item {
|
||||||
.under {
|
margin-bottom: 16px;
|
||||||
background-color: #dce9f5;
|
}
|
||||||
}
|
|
||||||
.dark .wave {
|
|
||||||
background: rgb(14 73 181 / 20%);
|
|
||||||
}
|
|
||||||
.dark .under {
|
|
||||||
background-color: #101828;
|
|
||||||
}
|
|
||||||
.dark #login {
|
|
||||||
background-color: #151F31;
|
|
||||||
}
|
|
||||||
.dark h1 {
|
|
||||||
color: rgb(255 255 255 / 85%);
|
|
||||||
}
|
|
||||||
</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">
|
<a-layout-content class="under" style="min-height: 0;">
|
||||||
<div class='wave'></div>
|
<div class='wave'></div>
|
||||||
<div class='wave wave2'></div>
|
<div class='wave wave2'></div>
|
||||||
<div class='wave wave3'></div>
|
<div class='wave wave3'></div>
|
||||||
<a-row type="flex" justify="center" align="middle" style="height: 100%;">
|
<a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto;">
|
||||||
<a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="6" id="login">
|
<a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;">
|
||||||
<a-row type="flex" justify="center">
|
<a-row type="flex" justify="center">
|
||||||
<a-col>
|
<a-col>
|
||||||
<h1 class="title">{{ i18n "pages.login.title" }}</h1>
|
<h1 class="title">{{ i18n "pages.login.title" }}</h1>
|
||||||
@@ -130,7 +117,7 @@
|
|||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-row justify="center" class="centered">
|
<a-row justify="center" class="centered">
|
||||||
<a-button type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined"
|
<a-button type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined"
|
||||||
:style="loading ? { width: '50px' } : { display: 'block', width: '100%' }">
|
:style="loading ? { width: '50px' } : { display: 'inline-block', width: '100%' }">
|
||||||
[[ loading ? '' : '{{ i18n "login" }}' ]]
|
[[ loading ? '' : '{{ i18n "login" }}' ]]
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -175,11 +162,6 @@
|
|||||||
this.password = "";
|
this.password = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const State = {
|
|
||||||
Running: "running",
|
|
||||||
Stop: "stop",
|
|
||||||
Error: "error",
|
|
||||||
}
|
|
||||||
|
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
@@ -206,4 +188,4 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -2,170 +2,105 @@
|
|||||||
<a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title" @ok="clientsBulkModal.ok"
|
<a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title" @ok="clientsBulkModal.ok"
|
||||||
:confirm-loading="clientsBulkModal.confirmLoading" :closable="true" :mask-closable="false"
|
:confirm-loading="clientsBulkModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||||
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "pages.client.method" }}'>
|
||||||
<tr>
|
<a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>{{ i18n "pages.client.method" }}</td>
|
<a-select-option :value="0">Random</a-select-option>
|
||||||
<td>
|
<a-select-option :value="1">Random+Prefix</a-select-option>
|
||||||
<a-form-item>
|
<a-select-option :value="2">Random+Prefix+Num</a-select-option>
|
||||||
<a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select-option :value="3">Random+Prefix+Num+Postfix</a-select-option>
|
||||||
<a-select-option :value="0">Random</a-select-option>
|
<a-select-option :value="4">Prefix+Num+Postfix</a-select-option>
|
||||||
<a-select-option :value="1">Random+Prefix</a-select-option>
|
</a-select>
|
||||||
<a-select-option :value="2">Random+Prefix+Num</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option :value="3">Random+Prefix+Num+Postfix</a-select-option>
|
<a-form-item label='{{ i18n "pages.client.first" }}' v-if="clientsBulkModal.emailMethod>1">
|
||||||
<a-select-option :value="4">Prefix+Num+Postfix</a-select-option>
|
<a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
|
||||||
</a-select>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label='{{ i18n "pages.client.last" }}' v-if="clientsBulkModal.emailMethod>1">
|
||||||
</td>
|
<a-input-number v-model="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr v-if="clientsBulkModal.emailMethod>1">
|
<a-form-item label='{{ i18n "pages.client.prefix" }}' v-if="clientsBulkModal.emailMethod>0">
|
||||||
<td>{{ i18n "pages.client.first" }}</td>
|
<a-input v-model="clientsBulkModal.emailPrefix"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label='{{ i18n "pages.client.postfix" }}' v-if="clientsBulkModal.emailMethod>2">
|
||||||
<a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
|
<a-input v-model="clientsBulkModal.emailPostfix"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item label='{{ i18n "pages.client.clientCount" }}' v-if="clientsBulkModal.emailMethod < 2">
|
||||||
</tr>
|
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
|
||||||
<tr v-if="clientsBulkModal.emailMethod>1">
|
</a-form-item>
|
||||||
<td>{{ i18n "pages.client.last" }}</td>
|
<a-form-item label='Flow' v-if="clientsBulkModal.inbound.canEnableTlsFlow()">
|
||||||
<td>
|
<a-select v-model="clientsBulkModal.flow" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-form-item>
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
<a-input-number v-model="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-form-item>
|
</a-select>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item v-if="app.subSettings.enable">
|
||||||
<tr v-if="clientsBulkModal.emailMethod>0">
|
<template slot="label">
|
||||||
<td>{{ i18n "pages.client.prefix" }}</td>
|
<a-tooltip>
|
||||||
<td>
|
<template slot="title">
|
||||||
<a-form-item>
|
<span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
|
||||||
<a-input v-model="clientsBulkModal.emailPrefix" style="width: 250px"></a-input>
|
</template>
|
||||||
</a-form-item>
|
Subscription
|
||||||
</td>
|
<a-icon @click="clientsBulkModal.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon>
|
||||||
</tr>
|
</a-tooltip>
|
||||||
<tr v-if="clientsBulkModal.emailMethod>2">
|
</template>
|
||||||
<td>{{ i18n "pages.client.postfix" }}</td>
|
<a-input v-model.trim="clientsBulkModal.subId"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item v-if="app.tgBotEnable">
|
||||||
<a-input v-model="clientsBulkModal.emailPostfix" style="width: 250px"></a-input>
|
<template slot="label">
|
||||||
</a-form-item>
|
<a-tooltip>
|
||||||
</td>
|
<template slot="title">
|
||||||
</tr>
|
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
|
||||||
<tr v-if="clientsBulkModal.emailMethod < 2">
|
</template>
|
||||||
<td>{{ i18n "pages.client.clientCount" }}</td>
|
Telegram ID
|
||||||
<td>
|
<a-icon type="question-circle"></a-icon>
|
||||||
<a-form-item>
|
</a-tooltip>
|
||||||
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
|
</template>
|
||||||
</a-form-item>
|
<a-input v-model.trim="clientsBulkModal.tgId"></a-input>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item>
|
||||||
<tr v-if="clientsBulkModal.inbound.canEnableTlsFlow()">
|
<template slot="label">
|
||||||
<td>Flow</td>
|
<a-tooltip>
|
||||||
<td>
|
<template slot="title">
|
||||||
<a-form-item>
|
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||||
<a-select v-model="clientsBulkModal.flow" style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme">
|
</template>
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
{{ i18n "pages.inbounds.totalFlow" }} (GB)
|
||||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-select>
|
</a-tooltip>
|
||||||
</a-form-item>
|
</template>
|
||||||
</td>
|
<a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr v-if="app.subSettings.enable">
|
<a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
|
||||||
<td>Subscription
|
<a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
|
||||||
<a-tooltip>
|
</a-form-item>
|
||||||
<template slot="title">
|
<a-form-item label='{{ i18n "pages.client.expireDays" }}' v-if="clientsBulkModal.delayedStart">
|
||||||
<span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
|
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
||||||
</template>
|
</a-form-item>
|
||||||
<a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon>
|
<a-form-item v-else>
|
||||||
</a-tooltip>
|
<template slot="label">
|
||||||
</td>
|
<a-tooltip>
|
||||||
<td>
|
<template slot="title">
|
||||||
<a-form-item>
|
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
||||||
<a-input v-model.trim="clientsBulkModal.subId" style="width: 250px"></a-input>
|
</template>
|
||||||
</a-form-item>
|
{{ i18n "pages.inbounds.expireDate" }}
|
||||||
</td>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</tr>
|
</a-tooltip>
|
||||||
<tr v-if="app.tgBotEnable">
|
</template>
|
||||||
<td>Telegram ID
|
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
||||||
<a-tooltip>
|
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
<template slot="title">
|
v-model="clientsBulkModal.expiryTime"></a-date-picker>
|
||||||
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
|
</a-form-item>
|
||||||
</template>
|
<a-form-item v-if="clientsBulkModal.expiryTime != 0">
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<template slot="label">
|
||||||
</a-tooltip>
|
<span>{{ i18n "pages.client.renew" }}</span>
|
||||||
</td>
|
<a-tooltip>
|
||||||
<td>
|
<template slot="title">
|
||||||
<a-form-item>
|
<span>{{ i18n "pages.client.renewDesc" }}</span>
|
||||||
<a-input v-model.trim="clientsBulkModal.tgId" style="width: 250px"></a-input>
|
</template>
|
||||||
</a-form-item>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</td>
|
</a-tooltip>
|
||||||
</tr>
|
</template>
|
||||||
<tr>
|
<a-input-number v-model.number="clientsBulkModal.reset" :min="0"></a-input-number>
|
||||||
<td>
|
</a-form-item>
|
||||||
<span>{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
|
||||||
<a-tooltip>
|
|
||||||
<template slot="title">
|
|
||||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
|
||||||
</template>
|
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
|
||||||
</a-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.client.delayedStart" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="clientsBulkModal.delayedStart">
|
|
||||||
<td>{{ i18n "pages.client.expireDays" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-else>
|
|
||||||
<td>
|
|
||||||
<span>{{ i18n "pages.inbounds.expireDate" }}</span>
|
|
||||||
<a-tooltip>
|
|
||||||
<template slot="title">
|
|
||||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
|
||||||
</template>
|
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
|
||||||
</a-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
|
||||||
v-model="clientsBulkModal.expiryTime" style="width: 250px;"></a-date-picker>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="clientsBulkModal.expiryTime != 0">
|
|
||||||
<td>
|
|
||||||
<span>{{ i18n "pages.client.renew" }}</span>
|
|
||||||
<a-tooltip>
|
|
||||||
<template slot="title">
|
|
||||||
<span>{{ i18n "pages.client.renewDesc" }}</span>
|
|
||||||
</template>
|
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
|
||||||
</a-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="clientsBulkModal.reset" :min="0"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<script>
|
<script>
|
||||||
@@ -276,4 +211,4 @@
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,170 +1,125 @@
|
|||||||
{{define "form/client"}}
|
{{define "form/client"}}
|
||||||
<a-form layout="inline" v-if="client">
|
<a-form layout="horizontal" v-if="client" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "pages.inbounds.enable" }}'>
|
||||||
<tr>
|
<a-switch v-model="client.enable"></a-switch>
|
||||||
<td>{{ i18n "pages.inbounds.enable" }}</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item>
|
||||||
<a-form-item>
|
<template slot="label">
|
||||||
<a-switch v-model="client.enable"></a-switch>
|
<a-tooltip>
|
||||||
</a-form-item>
|
<template slot="title">
|
||||||
</td>
|
<span>{{ i18n "pages.inbounds.emailDesc" }}</span>
|
||||||
</tr>
|
</template>
|
||||||
<tr>
|
{{ i18n "pages.inbounds.email" }}
|
||||||
<td>
|
<a-icon type="sync" @click="client.email = RandomUtil.randomLowerAndNum(9)"></a-icon>
|
||||||
<span>{{ i18n "pages.inbounds.email" }}</span>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
</template>
|
||||||
<template slot="title">
|
<a-input v-model.trim="client.email"></a-input>
|
||||||
<span>{{ i18n "pages.inbounds.emailDesc" }}</span>
|
</a-form-item>
|
||||||
</template>
|
<a-form-item v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS">
|
||||||
<a-icon type="sync" @click="client.email = RandomUtil.randomLowerAndNum(9)"></a-icon>
|
<template slot="label">
|
||||||
</a-tooltip>
|
<a-tooltip>
|
||||||
</td>
|
<template slot="title">
|
||||||
<td>
|
<span>{{ i18n "reset" }}</span>
|
||||||
<a-form-item>
|
</template>
|
||||||
<a-input v-model.trim="client.email" style="width: 250px"></a-input>
|
{{ i18n "password" }}
|
||||||
</a-form-item>
|
<a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS" @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
|
||||||
</td>
|
<a-icon v-if="inbound.protocol === Protocols.TROJAN" @click="client.password = RandomUtil.randomSeq(10)" type="sync"></a-icon>
|
||||||
</tr>
|
</a-tooltip>
|
||||||
<tr v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS">
|
</template>
|
||||||
<td>password
|
<a-input v-model.trim="client.password"></a-input>
|
||||||
<a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS" @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
|
||||||
<td>
|
<template slot="label">
|
||||||
<a-form-item>
|
<a-tooltip>
|
||||||
<a-input v-model.trim="client.password" style="width: 250px"></a-input>
|
<template slot="title">
|
||||||
</a-form-item>
|
<span>{{ i18n "reset" }}</span>
|
||||||
</td>
|
</template>
|
||||||
</tr>
|
ID <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"></a-icon>
|
||||||
<tr v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
|
</a-tooltip>
|
||||||
<td>ID <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"></a-icon></td>
|
</template>
|
||||||
<td>
|
<a-input v-model.trim="client.id"></a-input>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input v-model.trim="client.id" style="width: 250px"></a-input>
|
<a-form-item v-if="client.email && app.subSettings.enable">
|
||||||
</a-form-item>
|
<template slot="label">
|
||||||
</td>
|
<a-tooltip>
|
||||||
</tr>
|
<template slot="title">
|
||||||
<tr v-if="client.email && app.subSettings.enable">
|
<span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
|
||||||
<td>Subscription
|
</template>
|
||||||
<a-tooltip>
|
Subscription
|
||||||
<template slot="title">
|
<a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon>
|
||||||
<span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon>
|
<a-input v-model.trim="client.subId"></a-input>
|
||||||
</a-tooltip>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item v-if="client.email && app.tgBotEnable">
|
||||||
<td>
|
<template slot="label">
|
||||||
<a-form-item>
|
<a-tooltip>
|
||||||
<a-input v-model.trim="client.subId" style="width: 250px"></a-input>
|
<template slot="title">
|
||||||
</a-form-item>
|
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
|
||||||
</td>
|
</template>
|
||||||
</tr>
|
Telegram ID
|
||||||
<tr v-if="client.email && app.tgBotEnable">
|
<a-icon type="question-circle"></a-icon>
|
||||||
<td>Telegram ID
|
</a-tooltip>
|
||||||
<a-tooltip>
|
</template>
|
||||||
<template slot="title">
|
<a-input v-model.trim="client.tgId"></a-input>
|
||||||
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
|
</a-form-item>
|
||||||
</template>
|
<a-form-item v-if="inbound.canEnableTlsFlow()" label='Flow'>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-select v-model="client.flow" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</a-tooltip>
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
</td>
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
<td>
|
</a-select>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input v-model.trim="client.tgId" style="width: 250px"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<template slot="label">
|
||||||
</td>
|
<a-tooltip>
|
||||||
</tr>
|
<template slot="title">
|
||||||
<tr v-if="inbound.canEnableTlsFlow()">
|
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||||
<td>Flow</td>
|
</template>
|
||||||
<td>
|
{{ i18n "pages.inbounds.totalFlow" }} (GB)
|
||||||
<a-form-item>
|
<a-icon type="question-circle"></a-icon>
|
||||||
<a-select v-model="client.flow" style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-tooltip>
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
</template>
|
||||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||||
</a-select>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item v-if="isEdit && clientStats" label='{{ i18n "usage" }}'>
|
||||||
</td>
|
<a-tag :color="clientUsageColor(clientStats, app.trafficDiff)">
|
||||||
</tr>
|
[[ sizeFormat(clientStats.up) ]] /
|
||||||
<tr>
|
[[ sizeFormat(clientStats.down) ]]
|
||||||
<td>
|
([[ sizeFormat(clientStats.up + clientStats.down) ]])
|
||||||
<span>{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
</a-tag>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
<a-icon type="retweet" @click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)" v-if="client.email.length > 0"></a-icon>
|
||||||
</template>
|
</a-tooltip>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
</a-form-item>
|
||||||
</a-tooltip>
|
<a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
|
||||||
</td>
|
<a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item v-if="delayedStart" label='{{ i18n "pages.client.expireDays" }}'>
|
||||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item v-else>
|
||||||
</tr>
|
<template slot="label">
|
||||||
<tr v-if="isEdit && clientStats">
|
<a-tooltip>
|
||||||
<td>{{ i18n "usage" }}</td>
|
<template slot="title">{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</template>
|
||||||
<td>
|
{{ i18n "pages.inbounds.expireDate" }}
|
||||||
<a-tag :color="clientUsageColor(clientStats, app.trafficDiff)">
|
<a-icon type="question-circle"></a-icon>
|
||||||
[[ sizeFormat(clientStats.up) ]] /
|
</a-tooltip>
|
||||||
[[ sizeFormat(clientStats.down) ]]
|
</template>
|
||||||
([[ sizeFormat(clientStats.up + clientStats.down) ]])
|
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
||||||
</a-tag>
|
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
<a-tooltip>
|
v-model="client._expiryTime"></a-date-picker>
|
||||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
<a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
|
||||||
<a-icon type="retweet" @click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)" v-if="client.email.length > 0"></a-icon>
|
</a-form-item>
|
||||||
</a-tooltip>
|
<a-form-item v-if="client.expiryTime != 0">
|
||||||
</td>
|
<template slot="label">
|
||||||
</tr>
|
<a-tooltip>
|
||||||
<tr>
|
<template slot="title">{{ i18n "pages.client.renewDesc" }}</template>
|
||||||
<td>{{ i18n "pages.client.delayedStart" }}</td>
|
{{ i18n "pages.client.renew" }}
|
||||||
<td>
|
<a-icon type="question-circle"></a-icon>
|
||||||
<a-form-item>
|
</a-tooltip>
|
||||||
<a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
|
</template>
|
||||||
</a-form-item>
|
<a-input-number v-model.number="client.reset" :min="0"></a-input-number>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
|
||||||
<tr v-if="delayedStart">
|
|
||||||
<td>{{ i18n "pages.client.expireDays" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-else>
|
|
||||||
<td>
|
|
||||||
<span>{{ i18n "pages.inbounds.expireDate" }}</span>
|
|
||||||
<a-tooltip>
|
|
||||||
<template slot="title">
|
|
||||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
|
||||||
</template>
|
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
|
||||||
</a-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
|
||||||
v-model="client._expiryTime" style="width: 250px;"></a-date-picker>
|
|
||||||
<a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="client.expiryTime != 0">
|
|
||||||
<td>
|
|
||||||
<span>{{ i18n "pages.client.renew" }}</span>
|
|
||||||
<a-tooltip>
|
|
||||||
<template slot="title">
|
|
||||||
<span>{{ i18n "pages.client.renewDesc" }}</span>
|
|
||||||
</template>
|
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
|
||||||
</a-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="client.reset" :min="0"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,91 +1,63 @@
|
|||||||
{{define "form/inbound"}}
|
{{define "form/inbound"}}
|
||||||
<!-- base -->
|
<!-- base -->
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "enable" }}'>
|
||||||
<tr>
|
<a-switch v-model="dbInbound.enable"></a-switch>
|
||||||
<td>{{ i18n "enable" }}</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item label='{{ i18n "remark" }}'>
|
||||||
<a-form-item>
|
<a-input v-model.trim="dbInbound.remark"></a-input>
|
||||||
<a-switch v-model="dbInbound.enable"></a-switch>
|
</a-form-item>
|
||||||
</a-form-item>
|
|
||||||
</td>
|
<a-form-item label='{{ i18n "protocol" }}'>
|
||||||
</tr>
|
<a-select v-model="inbound.protocol" :disabled="isEdit" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<tr>
|
<a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
|
||||||
<td>{{ i18n "remark" }}</td>
|
</a-select>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="dbInbound.remark" style="width: 250px;"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<template slot="label">
|
||||||
</td>
|
<a-tooltip>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "protocol" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-select v-model="inbound.protocol" style="width: 250px;" :disabled="isEdit" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "monitor" }}
|
|
||||||
<a-tooltip>
|
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.inbounds.monitorDesc" }}</span>
|
<span>{{ i18n "pages.inbounds.monitorDesc" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
{{ i18n "monitor" }}
|
||||||
</a-tooltip>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</td>
|
</a-tooltip>
|
||||||
<td>
|
</template>
|
||||||
<a-form-item>
|
<a-input v-model.trim="inbound.listen"></a-input>
|
||||||
<a-input v-model.trim="inbound.listen" style="width: 250px;"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
|
||||||
</td>
|
<a-form-item label='{{ i18n "pages.inbounds.port" }}'>
|
||||||
</tr>
|
<a-input-number v-model.number="inbound.port"></a-input-number>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>{{ i18n "pages.inbounds.port" }}</td>
|
|
||||||
<td>
|
<a-form-item>
|
||||||
<a-form-item>
|
<template slot="label">
|
||||||
<a-input-number v-model.number="inbound.port"></a-input-number>
|
<a-tooltip>
|
||||||
</a-form-item>
|
<template slot="title">
|
||||||
</td>
|
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||||
</tr>
|
</template>
|
||||||
<tr>
|
{{ i18n "pages.inbounds.totalFlow" }} (GB)
|
||||||
<td>
|
<a-icon type="question-circle"></a-icon>
|
||||||
<span>{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
|
</a-tooltip>
|
||||||
<a-tooltip>
|
</template>
|
||||||
<template slot="title">
|
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
|
||||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
</a-form-item>
|
||||||
</template>
|
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-form-item>
|
||||||
</a-tooltip>
|
<template slot="label">
|
||||||
</td>
|
<a-tooltip>
|
||||||
<td>
|
<template slot="title">
|
||||||
<a-form-item>
|
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
||||||
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
|
</template>
|
||||||
</a-form-item>
|
{{ i18n "pages.inbounds.expireDate" }}
|
||||||
</td>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</tr>
|
</a-tooltip>
|
||||||
<tr>
|
</template>
|
||||||
<td>
|
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
||||||
<span>{{ i18n "pages.inbounds.expireDate" }}</span>
|
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
<a-tooltip>
|
v-model="dbInbound._expiryTime"></a-date-picker>
|
||||||
<template slot="title">
|
</a-form-item>
|
||||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
|
||||||
</template>
|
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
|
||||||
</a-tooltip>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
|
||||||
v-model="dbInbound._expiryTime" style="width: 250px;"></a-date-picker>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- vmess settings -->
|
<!-- vmess settings -->
|
||||||
@@ -123,6 +95,11 @@
|
|||||||
{{template "form/http"}}
|
{{template "form/http"}}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- wireguard -->
|
||||||
|
<template v-if="inbound.protocol === Protocols.WIREGUARD">
|
||||||
|
{{template "form/wireguard"}}
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- stream settings -->
|
<!-- stream settings -->
|
||||||
<template v-if="inbound.canEnableStream()">
|
<template v-if="inbound.canEnableStream()">
|
||||||
{{template "form/streamSettings"}}
|
{{template "form/streamSettings"}}
|
||||||
@@ -138,4 +115,4 @@
|
|||||||
<template v-if="inbound.canSniffing()">
|
<template v-if="inbound.canSniffing()">
|
||||||
{{template "form/sniffing"}}
|
{{template "form/sniffing"}}
|
||||||
</template>
|
</template>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -2,536 +2,389 @@
|
|||||||
<!-- base -->
|
<!-- base -->
|
||||||
<a-tabs :active-key="outModal.activeKey" style="padding: 0; background-color: transparent;" @change="(activeKey) => {outModal.toggleJson(activeKey == '2'); }">
|
<a-tabs :active-key="outModal.activeKey" style="padding: 0; background-color: transparent;" @change="(activeKey) => {outModal.toggleJson(activeKey == '2'); }">
|
||||||
<a-tab-pane key="1" tab="Form">
|
<a-tab-pane key="1" tab="Form">
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "protocol" }}'>
|
||||||
<tr>
|
<a-select v-model="outbound.protocol" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>{{ i18n "protocol" }}</td>
|
<a-select-option v-for="x,y in Protocols" :value="x">[[ y ]]</a-select-option>
|
||||||
<td>
|
</a-select>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-select v-model="outbound.protocol" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-form-item label='{{ i18n "pages.xray.outbound.tag" }}' has-feedback :validate-status="outModal.duplicateTag? 'warning' : 'success'">
|
||||||
<a-select-option v-for="x,y in Protocols" :value="x">[[ y ]]</a-select-option>
|
<a-input v-model.trim="outbound.tag" @change="outModal.check()" placeholder='{{ i18n "pages.xray.outbound.tagDesc" }}'></a-input>
|
||||||
</a-select>
|
</a-form-item>
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.xray.outbound.tag" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="outbound.tag" style="width: 250px" @change="outModal.check()" :style="outModal.duplicateTag? 'border-color: red;' : ''"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- freedom settings-->
|
<!-- freedom settings-->
|
||||||
<template v-if="outbound.protocol === Protocols.Freedom">
|
<template v-if="outbound.protocol === Protocols.Freedom">
|
||||||
<tr>
|
<a-form-item label='Strategy'>
|
||||||
<td>Strategy</td>
|
<a-select
|
||||||
<td>
|
v-model="outbound.settings.domainStrategy"
|
||||||
<a-form-item>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select
|
<a-select-option v-for="s in OutboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
|
||||||
v-model="outbound.settings.domainStrategy"
|
</a-select>
|
||||||
style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-form-item>
|
||||||
<a-select-option v-for="s in outboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
|
<a-form-item label='Fragment'>
|
||||||
</a-select>
|
<a-switch
|
||||||
</a-form-item>
|
:checked="Object.keys(outbound.settings.fragment).length >0"
|
||||||
</td>
|
@change="checked => outbound.settings.fragment = checked ? new Outbound.FreedomSettings.Fragment() : {}">
|
||||||
</tr>
|
</a-switch>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>Fragment</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch
|
|
||||||
:checked="Object.keys(outbound.settings.fragment).length >0"
|
|
||||||
@change="checked => outbound.settings.fragment = checked ? new Outbound.FreedomSettings.Fragment() : {}">
|
|
||||||
</a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<template v-if="Object.keys(outbound.settings.fragment).length >0">
|
<template v-if="Object.keys(outbound.settings.fragment).length >0">
|
||||||
<tr>
|
<a-form-item label='Packets'>
|
||||||
<td>Packets</td>
|
<a-select
|
||||||
<td>
|
v-model="outbound.settings.fragment.packets"
|
||||||
<a-form-item>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select
|
<a-select-option v-for="s in ['1-3','tlshello']" :value="s">[[ s ]]</a-select-option>
|
||||||
v-model="outbound.settings.fragment.packets"
|
</a-select>
|
||||||
style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-form-item>
|
||||||
<a-select-option v-for="s in ['1-3','tlshello']" :value="s">[[ s ]]</a-select-option>
|
<a-form-item label='Length'>
|
||||||
</a-select>
|
<a-input v-model.trim="outbound.settings.fragment.length"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item label='Interval'>
|
||||||
</tr>
|
<a-input v-model.trim="outbound.settings.fragment.interval"></a-input>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>Length</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="outbound.settings.fragment.length" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Interval</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="outbound.settings.fragment.interval" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- blackhole settings -->
|
<!-- blackhole settings -->
|
||||||
<template v-if="outbound.protocol === Protocols.Blackhole">
|
<template v-if="outbound.protocol === Protocols.Blackhole">
|
||||||
<tr>
|
<a-form-item label='Response Type'>
|
||||||
<td>Response Type</td>
|
<a-select
|
||||||
<td>
|
v-model="outbound.settings.type"
|
||||||
<a-form-item>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select
|
<a-select-option v-for="s in ['', 'none','http']" :value="s">[[ s ]]</a-select-option>
|
||||||
v-model="outbound.settings.type"
|
</a-select>
|
||||||
style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-form-item>
|
||||||
<a-select-option v-for="s in ['', 'none','http']" :value="s">[[ s ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- dns settings -->
|
<!-- dns settings -->
|
||||||
<template v-if="outbound.protocol === Protocols.DNS">
|
<template v-if="outbound.protocol === Protocols.DNS">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
||||||
<td>{{ i18n "pages.inbounds.network" }}</td>
|
<a-select
|
||||||
<td>
|
v-model="outbound.settings.network"
|
||||||
<a-form-item>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select
|
<a-select-option v-for="s in ['udp','tcp']" :value="s">[[ s ]]</a-select-option>
|
||||||
v-model="outbound.settings.network"
|
</a-select>
|
||||||
style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-form-item>
|
||||||
<a-select-option v-for="s in ['udp','tcp']" :value="s">[[ s ]]</a-select-option>
|
</template>
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
<!-- wireguard settings -->
|
||||||
</td>
|
<template v-if="outbound.protocol === Protocols.Wireguard">
|
||||||
</tr>
|
<a-form-item>
|
||||||
|
<template slot="label">
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||||
|
</template>
|
||||||
|
{{ i18n "pages.xray.outbound.address" }} <a-icon type="question-circle"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-input v-model.trim="outbound.settings.address"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<template slot="label">
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
<span>{{ i18n "reset" }}</span>
|
||||||
|
</template>
|
||||||
|
{{ i18n "pages.xray.wireguard.secretKey" }}
|
||||||
|
<a-icon type="sync"
|
||||||
|
@click="[outbound.settings.pubKey, outbound.settings.secretKey] = Object.values(Wireguard.generateKeypair())">
|
||||||
|
</a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-input v-model.trim="outbound.settings.secretKey"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.wireguard.publicKey" }}'>
|
||||||
|
<a-input disabled v-model="outbound.settings.pubKey"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.wireguard.domainStrategy" }}'>
|
||||||
|
<a-select v-model="outbound.settings.domainStrategy" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="wds in ['', ...WireguardDomainStrategy]" :value="wds">[[ wds ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='MTU'>
|
||||||
|
<a-input-number v-model.number="outbound.settings.mtu"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Workers'>
|
||||||
|
<a-input-number min="0" v-model.number="outbound.settings.workers"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Kernel Mode'>
|
||||||
|
<a-switch v-model="outbound.settings.kernelMode"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<template slot="label">
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||||
|
</template>
|
||||||
|
Reserved <a-icon type="question-circle"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-input v-model="outbound.settings.reserved"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Peers">
|
||||||
|
<a-button type="primary" size="small" @click="outbound.settings.addPeer()">+</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form v-for="(peer, index) in outbound.settings.peers" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
|
<a-divider style="margin:0;">
|
||||||
|
Peer [[ index + 1 ]]
|
||||||
|
<a-icon v-if="outbound.settings.peers.length>1" type="delete" @click="() => outbound.settings.delPeer(index)"
|
||||||
|
style="color: rgb(255, 77, 79);cursor: pointer;"/>
|
||||||
|
</a-divider>
|
||||||
|
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.wireguard.endpoint" }}'>
|
||||||
|
<a-input v-model.trim="peer.endpoint"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.wireguard.publicKey" }}'>
|
||||||
|
<a-input v-model.trim="peer.publicKey"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.wireguard.psk" }}'>
|
||||||
|
<a-input v-model.trim="peer.psk"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<template slot="label">
|
||||||
|
{{ i18n "pages.xray.wireguard.allowedIPs" }} <a-button type="primary" size="small" @click="peer.allowedIPs.push('')">+</a-button>
|
||||||
|
</template>
|
||||||
|
<template v-for="(aip, index) in peer.allowedIPs" style="margin-bottom: 10px;">
|
||||||
|
<a-input v-model.trim="peer.allowedIPs[index]">
|
||||||
|
<a-button v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)">-</a-button>
|
||||||
|
</a-input>
|
||||||
|
</template>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Keep Alive'>
|
||||||
|
<a-input-number v-model.number="peer.keepAlive" :min="0"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Address + Port -->
|
<!-- Address + Port -->
|
||||||
<template v-if="outbound.hasAddressPort()">
|
<template v-if="outbound.hasAddressPort()">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "pages.inbounds.address" }}'>
|
||||||
<td>{{ i18n "pages.inbounds.address" }}</td>
|
<a-input v-model.trim="outbound.settings.address"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label='{{ i18n "pages.inbounds.port" }}'>
|
||||||
<a-input v-model.trim="outbound.settings.address" style="width: 250px"></a-input>
|
<a-input-number v-model.number="outbound.settings.port" :min="1" :max="65532"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.inbounds.port" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="outbound.settings.port"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Vnext (vless/vmess) settings -->
|
<!-- Vnext (vless/vmess) settings -->
|
||||||
<template v-if="[Protocols.VMess, Protocols.VLESS].includes(outbound.protocol)">
|
<template v-if="[Protocols.VMess, Protocols.VLESS].includes(outbound.protocol)">
|
||||||
<tr>
|
<a-form-item label='ID'>
|
||||||
<td>ID</td>
|
<a-input v-model.trim="outbound.settings.id"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="outbound.settings.id" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<!-- vless settings -->
|
<!-- vless settings -->
|
||||||
<template v-if="outbound.canEnableTlsFlow()">
|
<template v-if="outbound.canEnableTlsFlow()">
|
||||||
<tr>
|
<a-form-item label='Flow'>
|
||||||
<td>Flow</td>
|
<a-select v-model="outbound.settings.flow" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>
|
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||||
<a-form-item>
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
<a-select v-model="outbound.settings.flow" style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-select>
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Servers (trojan/shadowsocks/socks/http) settings -->
|
<!-- Servers (trojan/shadowsocks/socks/http) settings -->
|
||||||
<template v-if="outbound.hasServers()">
|
<template v-if="outbound.hasServers()">
|
||||||
<tr v-if="outbound.hasUsername()">
|
<!-- http / socks -->
|
||||||
<td>{{ i18n "username" }}</td>
|
<template v-if="outbound.hasUsername()">
|
||||||
<td>
|
<a-form-item label='{{ i18n "username" }}'>
|
||||||
<a-form-item>
|
<a-input v-model.trim="outbound.settings.user"></a-input>
|
||||||
<a-input v-model.trim="outbound.settings.user" style="width: 250px"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label='{{ i18n "password" }}'>
|
||||||
</td>
|
<a-input v-model.trim="outbound.settings.pass"></a-input>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
</template>
|
||||||
<td>{{ i18n "password" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="outbound.settings.password" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<!-- shadowsocks -->
|
<!-- shadowsocks -->
|
||||||
<template v-if="outbound.protocol === Protocols.Shadowsocks">
|
<template v-if="outbound.protocol === Protocols.Shadowsocks">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "encryption" }}'>
|
||||||
<td>{{ i18n "encryption" }}</td>
|
<a-select v-model="outbound.settings.method" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>
|
<a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option>
|
||||||
<a-form-item>
|
</a-select>
|
||||||
<a-select v-model="outbound.settings.method" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-form-item>
|
||||||
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
|
<a-form-item label='UDP over TCP'>
|
||||||
</a-select>
|
<a-switch v-model="outbound.settings.uot"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>UDP over TCP</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="outbound.settings.uot"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- stream settings -->
|
<!-- stream settings -->
|
||||||
<template v-if="outbound.canEnableStream()">
|
<template v-if="outbound.canEnableStream()">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "transmission" }}'>
|
||||||
<td>{{ i18n "transmission" }}</td>
|
<a-select v-model="outbound.stream.network" @change="streamNetworkChange"
|
||||||
<td>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-form-item>
|
<a-select-option value="tcp">TCP</a-select-option>
|
||||||
<a-select v-model="outbound.stream.network" @change="streamNetworkChange"
|
<a-select-option value="kcp">mKCP</a-select-option>
|
||||||
style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select-option value="ws">WebSocket</a-select-option>
|
||||||
<a-select-option value="tcp">TCP</a-select-option>
|
<a-select-option value="http">HTTP/2</a-select-option>
|
||||||
<a-select-option value="kcp">KCP</a-select-option>
|
<a-select-option value="quic">QUIC</a-select-option>
|
||||||
<a-select-option value="ws">WebSocket</a-select-option>
|
<a-select-option value="grpc">gRPC</a-select-option>
|
||||||
<a-select-option value="http">HTTP2</a-select-option>
|
</a-select>
|
||||||
<a-select-option value="quic">QUIC</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option value="grpc">gRPC</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<template v-if="outbound.stream.network === 'tcp'">
|
<template v-if="outbound.stream.network === 'tcp'">
|
||||||
<tr>
|
<a-form-item label='HTTP {{ i18n "camouflage" }}'>
|
||||||
<td>http {{ i18n "camouflage" }}</td>
|
<a-switch
|
||||||
<td>
|
:checked="outbound.stream.tcp.type === 'http'"
|
||||||
<a-form-item>
|
@change="checked => outbound.stream.tcp.type = checked ? 'http' : 'none'">
|
||||||
<a-switch
|
</a-switch>
|
||||||
:checked="outbound.stream.tcp.type === 'http'"
|
</a-form-item>
|
||||||
@change="checked => outbound.stream.tcp.type = checked ? 'http' : 'none'">
|
|
||||||
</a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<template v-if="outbound.stream.tcp.type == 'http'">
|
<template v-if="outbound.stream.tcp.type == 'http'">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "host" }}'>
|
||||||
<td>{{ i18n "host" }}</td>
|
<a-input v-model.trim="outbound.stream.tcp.host"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label='{{ i18n "path" }}'>
|
||||||
<a-input style="width: 250px;" v-model.trim="outbound.stream.tcp.host"></a-input>
|
<a-input v-model.trim="outbound.stream.tcp.path"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "path" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input style="width: 250px;" v-model.trim="outbound.stream.tcp.path"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- kcp -->
|
<!-- kcp -->
|
||||||
<template v-if="outbound.stream.network === 'kcp'">
|
<template v-if="outbound.stream.network === 'kcp'">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "camouflage" }}'>
|
||||||
<td>{{ i18n "camouflage" }}</td>
|
<a-select v-model="outbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>
|
<a-select-option value="none">None</a-select-option>
|
||||||
<a-form-item>
|
<a-select-option value="srtp">SRTP</a-select-option>
|
||||||
<a-select v-model="outbound.stream.kcp.type" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select-option value="utp">uTP</a-select-option>
|
||||||
<a-select-option value="none">none (not camouflage)</a-select-option>
|
<a-select-option value="wechat-video">WeChat</a-select-option>
|
||||||
<a-select-option value="srtp">srtp (video call)</a-select-option>
|
<a-select-option value="dtls">DTLS 1.2</a-select-option>
|
||||||
<a-select-option value="utp">utp (BT download)</a-select-option>
|
<a-select-option value="wireguard">WireGuard</a-select-option>
|
||||||
<a-select-option value="wechat-video">wechat-video (WeChat video)</a-select-option>
|
</a-select>
|
||||||
<a-select-option value="dtls">dtls (DTLS 1.2 packages)</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option value="wireguard">wireguard (wireguard packages)</a-select-option>
|
<a-form-item label='{{ i18n "password" }}'>
|
||||||
</a-select>
|
<a-input v-model="outbound.stream.kcp.seed"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item label='MTU'>
|
||||||
</tr>
|
<a-input-number v-model.number="outbound.stream.kcp.mtu"></a-input-number>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>{{ i18n "password" }}</td>
|
<a-form-item label='TTI (ms)'>
|
||||||
<td>
|
<a-input-number v-model.number="outbound.stream.kcp.tti"></a-input-number>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input v-model="outbound.stream.kcp.seed" style="width: 250px;"></a-input>
|
<a-form-item label='Uplink (MB/s)'>
|
||||||
</a-form-item>
|
<a-input-number v-model.number="outbound.stream.kcp.upCap"></a-input-number>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item label='Downlink (MB/s)'>
|
||||||
<tr>
|
<a-input-number v-model.number="outbound.stream.kcp.downCap"></a-input-number>
|
||||||
<td>mtu</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item label='Congestion'>
|
||||||
<a-form-item>
|
<a-switch v-model="outbound.stream.kcp.congestion"></a-switch>
|
||||||
<a-input-number v-model.number="outbound.stream.kcp.mtu"></a-input-number>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label='Read Buffer (MB)'>
|
||||||
</td>
|
<a-input-number v-model.number="outbound.stream.kcp.readBuffer"></a-input-number>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
<a-form-item label='Write Buffer (MB)'>
|
||||||
<td>tti (ms)</td>
|
<a-input-number v-model.number="outbound.stream.kcp.writeBuffer"></a-input-number>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="outbound.stream.kcp.tti"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>uplink capacity (MB/S)</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="outbound.stream.kcp.upCap"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>downlink capacity (MB/S)</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="outbound.stream.kcp.downCap"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>congestion</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="outbound.stream.kcp.congestion"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>read buffer size (MB)</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="outbound.stream.kcp.readBuffer"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>write buffer size (MB)</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="outbound.stream.kcp.writeBuffer"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- ws -->
|
<!-- ws -->
|
||||||
<template v-if="outbound.stream.network === 'ws'">
|
<template v-if="outbound.stream.network === 'ws'">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "host" }}'>
|
||||||
<td>{{ i18n "host" }}</td>
|
<a-input v-model="outbound.stream.ws.host"></a-input>
|
||||||
<td><a-form-item><a-input style="width: 250px" v-model="outbound.stream.ws.host"></a-input></a-form-item></td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item label='{{ i18n "path" }}'>
|
||||||
<tr>
|
<a-form-item><a-input v-model.trim="outbound.stream.ws.path"></a-input>
|
||||||
<td>{{ i18n "path" }}</td>
|
</a-form-item>
|
||||||
<td><a-form-item><a-input style="width: 250px;" v-model.trim="outbound.stream.ws.path"></a-input></a-form-item></td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- http -->
|
<!-- http -->
|
||||||
<template v-if="outbound.stream.network === 'http'">
|
<template v-if="outbound.stream.network === 'http'">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "host" }}'>
|
||||||
<td>{{ i18n "host" }}</td>
|
<a-input v-model.trim="outbound.stream.http.host"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label='{{ i18n "path" }}'>
|
||||||
<a-input v-model.trim="outbound.stream.http.host" style="width: 250px;"></a-input>
|
<a-input v-model.trim="outbound.stream.http.path"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "path" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="outbound.stream.http.path" style="width: 250px;"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- quic -->
|
<!-- quic -->
|
||||||
<template v-if="outbound.stream.network === 'quic'">
|
<template v-if="outbound.stream.network === 'quic'">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
|
||||||
<td>{{ i18n "pages.inbounds.stream.quic.encryption" }}</td>
|
<a-select v-model="outbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>
|
<a-select-option value="none">None</a-select-option>
|
||||||
<a-form-item>
|
<a-select-option value="aes-128-gcm">AES-128-GCM</a-select-option>
|
||||||
<a-select v-model="outbound.stream.quic.security" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select-option value="chacha20-poly1305">CHACHA20-POLY1305</a-select-option>
|
||||||
<a-select-option value="none">none</a-select-option>
|
</a-select>
|
||||||
<a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
|
<a-form-item label='{{ i18n "password" }}'>
|
||||||
</a-select>
|
<a-input v-model.trim="outbound.stream.quic.key"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item label='{{ i18n "camouflage" }}'>
|
||||||
</tr>
|
<a-select v-model="outbound.stream.quic.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<tr>
|
<a-select-option value="none">None</a-select-option>
|
||||||
<td>{{ i18n "password" }}</td>
|
<a-select-option value="srtp">SRTP</a-select-option>
|
||||||
<td>
|
<a-select-option value="utp">uTP</a-select-option>
|
||||||
<a-form-item>
|
<a-select-option value="wechat-video">WeChat</a-select-option>
|
||||||
<a-input v-model.trim="outbound.stream.quic.key" style="width: 250px;"></a-input>
|
<a-select-option value="dtls">DTLS 1.2</a-select-option>
|
||||||
</a-form-item>
|
<a-select-option value="wireguard">WireGuard</a-select-option>
|
||||||
</td>
|
</a-select>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "camouflage" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-select v-model="outbound.stream.quic.type" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option value="none">none (not camouflage)</a-select-option>
|
|
||||||
<a-select-option value="srtp">srtp (video call)</a-select-option>
|
|
||||||
<a-select-option value="utp">utp (BT download)</a-select-option>
|
|
||||||
<a-select-option value="wechat-video">wechat-video (WeChat video)</a-select-option>
|
|
||||||
<a-select-option value="dtls">dtls (DTLS 1.2 packages)</a-select-option>
|
|
||||||
<a-select-option value="wireguard">wireguard (wireguard packages)</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- grpc -->
|
<!-- grpc -->
|
||||||
<template v-if="outbound.stream.network === 'grpc'">
|
<template v-if="outbound.stream.network === 'grpc'">
|
||||||
<tr>
|
<a-form-item label='Service Name'>
|
||||||
<td>serviceName</td>
|
<a-input v-model.trim="outbound.stream.grpc.serviceName"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label='Multi Mode'>
|
||||||
<a-input v-model.trim="outbound.stream.grpc.serviceName" style="width: 250px;"></a-input>
|
<a-switch v-model="outbound.stream.grpc.multiMode"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>MultiMode</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="outbound.stream.grpc.multiMode"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- tls settings -->
|
<!-- tls settings -->
|
||||||
<template v-if="outbound.canEnableTls()">
|
<template v-if="outbound.canEnableTls()">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "security" }}'>
|
||||||
<td>{{ i18n "security" }}</td>
|
<a-radio-group v-model="outbound.stream.security" button-style="solid">
|
||||||
<td>
|
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
||||||
<a-form-item>
|
<a-radio-button value="tls">TLS</a-radio-button>
|
||||||
<a-radio-group v-model="outbound.stream.security" button-style="solid">
|
<a-radio-button v-if="outbound.canEnableReality()" value="reality">Reality</a-radio-button>
|
||||||
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
</a-radio-group>
|
||||||
<a-radio-button value="tls">TLS</a-radio-button>
|
</a-form-item>
|
||||||
<a-radio-button v-if="outbound.canEnableReality()" value="reality">Reality</a-radio-button>
|
|
||||||
</a-radio-group>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<template v-if="outbound.stream.isTls">
|
<template v-if="outbound.stream.isTls">
|
||||||
<tr>
|
<a-form-item label="SNI" placeholder="Server Name Indication">
|
||||||
<td>SNI</td>
|
<a-input v-model.trim="outbound.stream.tls.serverName"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item placeholder="Server Name Indication">
|
<a-form-item label="uTLS">
|
||||||
<a-input v-model.trim="outbound.stream.tls.serverName" style="width: 250px"></a-input>
|
<a-select v-model="outbound.stream.tls.fingerprint"
|
||||||
</a-form-item>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</td>
|
<a-select-option value=''>None</a-select-option>
|
||||||
</tr>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
<tr>
|
</a-select>
|
||||||
<td>uTLS</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item label="ALPN">
|
||||||
<a-form-item>
|
<a-select
|
||||||
<a-select v-model="outbound.stream.tls.fingerprint"
|
mode="multiple"
|
||||||
style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option value=''>None</a-select-option>
|
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
v-model="outbound.stream.tls.alpn">
|
||||||
</a-select>
|
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
|
||||||
</a-form-item>
|
</a-select>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item label="Allow Insecure">
|
||||||
<tr>
|
<a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch>
|
||||||
<td>ALPN</td>
|
</a-form-item>
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-select
|
|
||||||
mode="multiple"
|
|
||||||
style="width: 250px"
|
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
|
||||||
v-model="outbound.stream.tls.alpn">
|
|
||||||
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Allow insecure</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- reality settings -->
|
<!-- reality settings -->
|
||||||
<template v-if="outbound.stream.isReality">
|
<template v-if="outbound.stream.isReality">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "domainName" }}'>
|
||||||
<td>{{ i18n "domainName" }}</td>
|
<a-input v-model.trim="outbound.stream.reality.serverName"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label="uTLS">
|
||||||
<a-input v-model.trim="outbound.stream.reality.serverName" style="width: 250px"></a-input>
|
<a-select v-model="outbound.stream.reality.fingerprint"
|
||||||
</a-form-item>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</td>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
</tr>
|
</a-select>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>uTLS</td>
|
<a-form-item label="Short ID">
|
||||||
<td>
|
<a-input v-model.trim="outbound.stream.reality.shortId" style="width:250px"></a-input>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-select v-model="outbound.stream.reality.fingerprint"
|
<a-form-item label="SpiderX">
|
||||||
style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-input v-model.trim="outbound.stream.reality.spiderX" style="width:250px"></a-input>
|
||||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
</a-form-item>
|
||||||
</a-select>
|
<a-form-item label="Public Key">
|
||||||
</a-form-item>
|
<a-input v-model.trim="outbound.stream.reality.publicKey"></a-input>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Short Id</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="outbound.stream.reality.shortId" style="width:250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>SpiderX</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="outbound.stream.reality.spiderX" style="width:250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Public Key</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="outbound.stream.reality.publicKey" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="2" tab="JSON" force-render="true">
|
<a-tab-pane key="2" tab="JSON" force-render="true">
|
||||||
@@ -542,4 +395,4 @@
|
|||||||
<textarea style="position:absolute; left: -800px;" id="outboundJson"></textarea>
|
<textarea style="position:absolute; left: -800px;" id="outboundJson"></textarea>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,42 +1,20 @@
|
|||||||
{{define "form/dokodemo"}}
|
{{define "form/dokodemo"}}
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'>
|
||||||
<tr>
|
<a-input v-model.trim="inbound.settings.address"></a-input>
|
||||||
<td>{{ i18n "pages.inbounds.targetAddress"}}</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item label='{{ i18n "pages.inbounds.destinationPort"}}'>
|
||||||
<a-form-item>
|
<a-input-number v-model.number="inbound.settings.port"></a-input-number>
|
||||||
<a-input v-model.trim="inbound.settings.address"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label='{{ i18n "pages.inbounds.network"}}'>
|
||||||
</td>
|
<a-select v-model="inbound.settings.network" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</tr>
|
<a-select-option value="tcp,udp">TCP+UDP</a-select-option>
|
||||||
<tr>
|
<a-select-option value="tcp">TCP</a-select-option>
|
||||||
<td>{{ i18n "pages.inbounds.destinationPort"}}</td>
|
<a-select-option value="udp">UDP</a-select-option>
|
||||||
<td>
|
</a-select>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input-number v-model.number="inbound.settings.port"></a-input-number>
|
<a-form-item label='Follow Redirect'>
|
||||||
</a-form-item>
|
<a-switch v-model="inbound.settings.followRedirect"></a-switch>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.inbounds.network"}}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option value="tcp,udp">tcp+udp</a-select-option>
|
|
||||||
<a-select-option value="tcp">tcp</a-select-option>
|
|
||||||
<a-select-option value="udp">udp</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>FollowRedirect</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="inbound.settings.followRedirect"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{{define "form/http"}}
|
{{define "form/http"}}
|
||||||
<a-form layout="inline">
|
<a-form>
|
||||||
<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>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{{define "form/shadowsocks"}}
|
{{define "form/shadowsocks"}}
|
||||||
<a-form layout="inline">
|
<template v-if="inbound.isSSMultiUser">
|
||||||
<template v-if="inbound.isSSMultiUser">
|
|
||||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.shadowsockses.slice(0,1)" v-if="!isEdit">
|
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.shadowsockses.slice(0,1)" v-if="!isEdit">
|
||||||
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
||||||
{{template "form/client"}}
|
{{template "form/client"}}
|
||||||
@@ -20,40 +19,32 @@
|
|||||||
</table>
|
</table>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
</template>
|
</template>
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "encryption" }}'>
|
||||||
<td>{{ i18n "encryption" }}</td>
|
<a-select v-model="inbound.settings.method" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>
|
<a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option>
|
||||||
<a-form-item>
|
</a-select>
|
||||||
<a-select v-model="inbound.settings.method" style="width: 250px;" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-form-item>
|
||||||
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
|
<a-form-item v-if="inbound.isSS2022">
|
||||||
</a-select>
|
<template slot="label">
|
||||||
</a-form-item>
|
<a-tooltip>
|
||||||
</td>
|
<template slot="title">
|
||||||
</tr>
|
<span>{{ i18n "reset" }}</span>
|
||||||
<tr v-if="inbound.isSS2022">
|
</template>
|
||||||
<td>{{ i18n "password" }}
|
{{ i18n "password" }}
|
||||||
<a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
|
<a-icon @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
|
||||||
</td>
|
</a-tooltip>
|
||||||
<td>
|
</template>
|
||||||
<a-form-item>
|
<a-input v-model.trim="inbound.settings.password"></a-input>
|
||||||
<a-input v-model.trim="inbound.settings.password" style="width: 250px"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
||||||
</td>
|
<a-select v-model="inbound.settings.network" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</tr>
|
<a-select-option value="tcp,udp">TCP+UDP</a-select-option>
|
||||||
<tr>
|
<a-select-option value="tcp">TCP</a-select-option>
|
||||||
<td>{{ i18n "pages.inbounds.network" }}</td>
|
<a-select-option value="udp">UDP</a-select-option>
|
||||||
<td>
|
</a-select>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option value="tcp,udp">tcp+udp</a-select-option>
|
|
||||||
<a-select-option value="tcp">tcp</a-select-option>
|
|
||||||
<a-select-option value="udp">udp</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
<a-divider style="margin:0;"></a-divider>
|
||||||
|
{{end}}
|
||||||
|
|||||||
@@ -1,52 +1,33 @@
|
|||||||
{{define "form/socks"}}
|
{{define "form/socks"}}
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "pages.inbounds.enable" }} UDP'>
|
||||||
<tr>
|
<a-switch v-model="inbound.settings.udp"></a-switch>
|
||||||
<td style="width: 30%;">{{ i18n "password" }}</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item label="IP" v-if="inbound.settings.udp">
|
||||||
<a-form-item>
|
<a-input v-model.trim="inbound.settings.ip"></a-input>
|
||||||
<a-switch :checked="inbound.settings.auth === 'password'"
|
</a-form-item>
|
||||||
@change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
|
<a-form-item label='{{ i18n "password" }}'>
|
||||||
</a-form-item>
|
<a-switch :checked="inbound.settings.auth === 'password'"
|
||||||
</td>
|
@change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr v-if="inbound.settings.auth === 'password'">
|
<template v-if="inbound.settings.auth === 'password'">
|
||||||
<td colspan="2">
|
<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>
|
<td width="45%">{{ i18n "password" }}</td>
|
||||||
<td width="45%">{{ i18n "password" }}</td>
|
<td><a-button size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button></td>
|
||||||
<td><a-button size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button></td>
|
</tr>
|
||||||
</tr>
|
</table>
|
||||||
</table>
|
<a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
|
||||||
<a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
|
<a-input style="width: 50%" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
|
||||||
<a-input style="width: 50%" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
|
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
</a-input>
|
||||||
</a-input>
|
<a-input style="width: 50%" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
|
||||||
<a-input style="width: 50%" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
|
<template slot="addonAfter">
|
||||||
<template slot="addonAfter">
|
<a-button size="small" @click="inbound.settings.delAccount(index)">-</a-button>
|
||||||
<a-button size="small" @click="inbound.settings.delAccount(index)">-</a-button>
|
</template>
|
||||||
</template>
|
</a-input>
|
||||||
</a-input>
|
</a-input-group>
|
||||||
</a-input-group>
|
</template>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.inbounds.enable" }} udp</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="inbound.settings.udp"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="inbound.settings.udp">
|
|
||||||
<td>IP</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="inbound.settings.ip"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{{define "form/trojan"}}
|
{{define "form/trojan"}}
|
||||||
<a-form layout="inline">
|
|
||||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.trojans.slice(0,1)" v-if="!isEdit">
|
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.trojans.slice(0,1)" v-if="!isEdit">
|
||||||
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
||||||
{{template "form/client"}}
|
{{template "form/client"}}
|
||||||
@@ -24,7 +23,7 @@
|
|||||||
<a-form-item label="Fallbacks">
|
<a-form-item label="Fallbacks">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-button type="primary" size="small"
|
<a-button type="primary" size="small"
|
||||||
@click="inbound.settings.addTrojanFallback()">
|
@click="inbound.settings.addFallback()">
|
||||||
+
|
+
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -32,28 +31,28 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- trojan fallbacks -->
|
<!-- trojan fallbacks -->
|
||||||
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" layout="inline">
|
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:6} }" :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.delTrojanFallback(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="name">
|
<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>
|
||||||
<a-form-item label="path">
|
<a-form-item label='Path'>
|
||||||
<a-input v-model="fallback.path"></a-input>
|
<a-input v-model="fallback.path"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="dest">
|
<a-form-item label='Dest'>
|
||||||
<a-input v-model="fallback.dest"></a-input>
|
<a-input v-model="fallback.dest"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="xver">
|
<a-form-item label='xVer'>
|
||||||
<a-input-number v-model.number="fallback.xver"></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:0;"></a-divider>
|
||||||
</template>
|
</template>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{{define "form/vless"}}
|
{{define "form/vless"}}
|
||||||
<a-form layout="inline">
|
|
||||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.vlesses.slice(0,1)" v-if="!isEdit">
|
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.vlesses.slice(0,1)" v-if="!isEdit">
|
||||||
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
||||||
{{template "form/client"}}
|
{{template "form/client"}}
|
||||||
@@ -34,26 +33,26 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- vless fallbacks -->
|
<!-- vless fallbacks -->
|
||||||
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" layout="inline">
|
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:6} }" :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="name">
|
<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>
|
||||||
<a-form-item label="path">
|
<a-form-item label='Path'>
|
||||||
<a-input v-model="fallback.path"></a-input>
|
<a-input v-model="fallback.path"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="dest">
|
<a-form-item label='Dest'>
|
||||||
<a-input v-model="fallback.dest"></a-input>
|
<a-input v-model="fallback.dest"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="xver">
|
<a-form-item label='xVer'>
|
||||||
<a-input-number v-model.number="fallback.xver"></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:0;"></a-divider>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{{define "form/vmess"}}
|
{{define "form/vmess"}}
|
||||||
<a-form layout="inline">
|
|
||||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.vmesses.slice(0,1)" v-if="!isEdit">
|
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.vmesses.slice(0,1)" v-if="!isEdit">
|
||||||
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
||||||
{{template "form/client"}}
|
{{template "form/client"}}
|
||||||
|
|||||||
56
web/html/xui/form/protocol/wireguard.html
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{{define "form/wireguard"}}
|
||||||
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
|
<a-form-item>
|
||||||
|
<template slot="label">
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
<span>{{ i18n "reset" }}</span>
|
||||||
|
</template>
|
||||||
|
{{ i18n "pages.xray.wireguard.secretKey" }}
|
||||||
|
<a-icon type="sync"
|
||||||
|
@click="[inbound.settings.pubKey, inbound.settings.secretKey] = Object.values(Wireguard.generateKeypair())">
|
||||||
|
</a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-input v-model.trim="inbound.settings.secretKey"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.wireguard.publicKey" }}'>
|
||||||
|
<a-input disabled v-model="inbound.settings.pubKey"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='MTU'>
|
||||||
|
<a-input-number v-model.number="inbound.settings.mtu"></a-input-number>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Kernel Mode'>
|
||||||
|
<a-switch v-model="inbound.settings.kernelMode"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="Peers">
|
||||||
|
<a-button type="primary" size="small" @click="inbound.settings.addPeer()">+</a-button>
|
||||||
|
</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-divider style="margin:0;">
|
||||||
|
Peer [[ index + 1 ]]
|
||||||
|
<a-icon v-if="inbound.settings.peers.length>1" type="delete" @click="() => inbound.settings.delPeer(index)"
|
||||||
|
style="color: rgb(255, 77, 79);cursor: pointer;"/>
|
||||||
|
</a-divider>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.wireguard.publicKey" }}'>
|
||||||
|
<a-input v-model.trim="peer.publicKey"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='{{ i18n "pages.xray.wireguard.psk" }}'>
|
||||||
|
<a-input v-model.trim="peer.psk"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<template slot="label">
|
||||||
|
{{ i18n "pages.xray.wireguard.allowedIPs" }} <a-button type="primary" size="small" @click="peer.allowedIPs.push('')">+</a-button>
|
||||||
|
</template>
|
||||||
|
<template v-for="(aip, index) in peer.allowedIPs" style="margin-bottom: 10px;">
|
||||||
|
<a-input v-model.trim="peer.allowedIPs[index]">
|
||||||
|
<a-button v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)">-</a-button>
|
||||||
|
</a-input>
|
||||||
|
</template>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label='Keep Alive'>
|
||||||
|
<a-input-number v-model.number="peer.keepAlive" :min="0"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-form>
|
||||||
|
{{end}}
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
{{define "form/sniffing"}}
|
{{define "form/sniffing"}}
|
||||||
<a-divider style="margin:0;"></a-divider>
|
<a-divider style="margin:0;"></a-divider>
|
||||||
<a-form layout="inline">
|
<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
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.inbounds.noRecommendKeepDefault" }}</span>
|
<span>{{ i18n "pages.inbounds.noRecommendKeepDefault" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
<a-switch v-model="inbound.sniffing.enabled"></a-switch>
|
<a-switch v-model="inbound.sniffing.enabled"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
<a-checkbox-group v-model="inbound.sniffing.destOverride" v-if="inbound.sniffing.enabled">
|
<a-checkbox-group v-model="inbound.sniffing.destOverride" v-if="inbound.sniffing.enabled">
|
||||||
<a-checkbox v-for="key,value in SNIFFING_OPTION" :value="key">[[ value ]]</a-checkbox>
|
<a-checkbox v-for="key,value in SNIFFING_OPTION" :value="key">[[ value ]]</a-checkbox>
|
||||||
</a-checkbox-group>
|
</a-checkbox-group>
|
||||||
|
|||||||
@@ -1,32 +1,26 @@
|
|||||||
{{define "form/externalProxy"}}
|
{{define "form/externalProxy"}}
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider style="margin:0;"></a-divider>
|
<a-divider style="margin:0;"></a-divider>
|
||||||
<a-form-item label="External Proxy">
|
<a-form-item label="External Proxy">
|
||||||
<a-switch v-model="externalProxy"></a-switch>
|
<a-switch v-model="externalProxy"></a-switch>
|
||||||
<a-button v-if="externalProxy" type="primary" style="margin-left: 10px" size="small" @click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', port: 443, remark: ''})">+</a-button>
|
<a-button v-if="externalProxy" type="primary" style="margin-left: 10px" size="small" @click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', port: 443, remark: ''})">+</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<table width="100%" class="ant-table-tbody" v-if="externalProxy">
|
<a-input-group style="margin: 5px 0;" compact v-for="(row, index) in inbound.stream.externalProxy">
|
||||||
<tr style="line-height: 40px;">
|
<template>
|
||||||
<td width="100%">
|
<a-tooltip title="Force TLS">
|
||||||
<a-input-group style="margin-top:5px;" compact v-for="(row, index) in inbound.stream.externalProxy">
|
<a-select v-model="row.forceTls" style="width:20%; margin: 0px" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<template>
|
<a-select-option value="same">{{ i18n "pages.inbounds.same" }}</a-select-option>
|
||||||
<a-tooltip title="Force TLS">
|
<a-select-option value="none">{{ i18n "none" }}</a-select-option>
|
||||||
<a-select v-model="row.forceTls" style="width:20%; margin: 0px" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select-option value="tls">TLS</a-select-option>
|
||||||
<a-select-option value="same">{{ i18n "pages.inbounds.same" }}</a-select-option>
|
</a-select>
|
||||||
<a-select-option value="none">{{ i18n "none" }}</a-select-option>
|
</a-tooltip>
|
||||||
<a-select-option value="tls">TLS</a-select-option>
|
</template>
|
||||||
</a-select>
|
<a-input style="width: 35%" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input>
|
||||||
</a-tooltip>
|
<a-tooltip title='{{ i18n "pages.inbounds.port" }}'>
|
||||||
</template>
|
<a-input-number style="width: 15%;" v-model.number="row.port" min="1" max="65531"></a-input-number>
|
||||||
<a-input style="width: 35%" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input>
|
</a-tooltip>
|
||||||
<a-tooltip title='{{ i18n "pages.inbounds.port" }}'>
|
<a-input style="width: 20%" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'></a-input>
|
||||||
<a-input-number style="width: 15%;" v-model.number="row.port" min="1" max="65531"></a-input-number>
|
<a-button style="width: 10%; margin: 0px" @click="inbound.stream.externalProxy.splice(index, 1)">-</a-button>
|
||||||
</a-tooltip>
|
</a-input-group>
|
||||||
<a-input style="width: 20%" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'></a-input>
|
|
||||||
<a-button style="width: 10%; margin: 0px" @click="inbound.stream.externalProxy.splice(index, 1)">-</a-button>
|
|
||||||
</a-input-group>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,22 +1,10 @@
|
|||||||
{{define "form/streamGRPC"}}
|
{{define "form/streamGRPC"}}
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label="Service Name">
|
||||||
<tr>
|
<a-input v-model.trim="inbound.stream.grpc.serviceName" style="width: 250px;"></a-input>
|
||||||
<td>serviceName</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item label="Multi Mode">
|
||||||
<a-form-item>
|
<a-switch v-model="inbound.stream.grpc.multiMode"></a-switch>
|
||||||
<a-input v-model.trim="inbound.stream.grpc.serviceName" style="width: 250px;"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>MultiMode</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="inbound.stream.grpc.multiMode"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,24 +1,19 @@
|
|||||||
{{define "form/streamHTTP"}}
|
{{define "form/streamHTTP"}}
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "path" }}'>
|
||||||
<tr>
|
<a-input v-model.trim="inbound.stream.http.path"></a-input>
|
||||||
<td>{{ i18n "path" }}</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item>
|
||||||
<a-form-item>
|
<template slot="label">{{ i18n "host" }}
|
||||||
<a-input v-model.trim="inbound.stream.http.path" style="width: 250px;"></a-input>
|
<a-button size="small" @click="inbound.stream.http.addHost()">+</a-button>
|
||||||
</a-form-item>
|
</template>
|
||||||
</td>
|
<template v-for="(host, index) in inbound.stream.http.host">
|
||||||
</tr>
|
<a-input v-model.trim="inbound.stream.http.host[index]">
|
||||||
<tr>
|
<a-button size="small" slot="addonAfter"
|
||||||
<td>host</td>
|
@click="inbound.stream.http.removeHost(index)"
|
||||||
<td>
|
v-if="inbound.stream.http.host.length>1">-</a-button>
|
||||||
<a-form-item>
|
</a-input>
|
||||||
<a-row v-for="(host, index) in inbound.stream.http.host">
|
</template>
|
||||||
<a-input v-model.trim="inbound.stream.http.host[index]" style="width: 250px;"></a-input>
|
</a-form-item>
|
||||||
</a-row>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,85 +1,47 @@
|
|||||||
{{define "form/streamKCP"}}
|
{{define "form/streamKCP"}}
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "camouflage" }}'>
|
||||||
<tr>
|
<a-select v-model="inbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>{{ i18n "camouflage" }}</td>
|
<a-select-option value="none">None</a-select-option>
|
||||||
<td>
|
<a-select-option value="srtp">SRTP</a-select-option>
|
||||||
<a-form-item>
|
<a-select-option value="utp">uTP</a-select-option>
|
||||||
<a-select v-model="inbound.stream.kcp.type" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select-option value="wechat-video">WeChat</a-select-option>
|
||||||
<a-select-option value="none">none (not camouflage)</a-select-option>
|
<a-select-option value="dtls">DTLS 1.2</a-select-option>
|
||||||
<a-select-option value="srtp">srtp (video call)</a-select-option>
|
<a-select-option value="wireguard">WireGuard</a-select-option>
|
||||||
<a-select-option value="utp">utp (BT download)</a-select-option>
|
</a-select>
|
||||||
<a-select-option value="wechat-video">wechat-video (WeChat video)</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option value="dtls">dtls (DTLS 1.2 packages)</a-select-option>
|
<a-form-item>
|
||||||
<a-select-option value="wireguard">wireguard (wireguard packages)</a-select-option>
|
<template slot="label">
|
||||||
</a-select>
|
<a-tooltip>
|
||||||
</a-form-item>
|
<template slot="title">
|
||||||
</td>
|
<span>{{ i18n "reset" }}</span>
|
||||||
</tr>
|
</template>
|
||||||
<tr>
|
{{ i18n "password" }}
|
||||||
<td>{{ i18n "password" }}</td>
|
<a-icon @click="inbound.stream.kcp.seed = RandomUtil.randomSeq(10)"type="sync"> </a-icon>
|
||||||
<td>
|
</a-tooltip>
|
||||||
<a-form-item>
|
</template>
|
||||||
<a-input v-model="inbound.stream.kcp.seed" style="width: 250px;"></a-input>
|
<a-input v-model.trim="inbound.stream.kcp.seed"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item label='MTU'>
|
||||||
</tr>
|
<a-input-number v-model.number="inbound.stream.kcp.mtu"></a-input-number>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>mtu</td>
|
<a-form-item label='TTI (ms)'>
|
||||||
<td>
|
<a-input-number v-model.number="inbound.stream.kcp.tti"></a-input-number>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.mtu"></a-input-number>
|
<a-form-item label='Uplink (MB/s)'>
|
||||||
</a-form-item>
|
<a-input-number v-model.number="inbound.stream.kcp.upCap"></a-input-number>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item label='Downlink (MB/s)'>
|
||||||
<tr>
|
<a-input-number v-model.number="inbound.stream.kcp.downCap"></a-input-number>
|
||||||
<td>tti (ms)</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item label='Congestion'>
|
||||||
<a-form-item>
|
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.tti"></a-input-number>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label='Read Buffer (MB)'>
|
||||||
</td>
|
<a-input-number v-model.number="inbound.stream.kcp.readBuffer"></a-input-number>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
<a-form-item label='Write Buffer (MB)'>
|
||||||
<td>uplink capacity (MB/S)</td>
|
<a-input-number v-model.number="inbound.stream.kcp.writeBuffer"></a-input-number>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.upCap"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>downlink capacity (MB/S)</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.downCap"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>congestion</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>read buffer size (MB)</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.readBuffer"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>write buffer size (MB)</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="inbound.stream.kcp.writeBuffer"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,41 +1,33 @@
|
|||||||
{{define "form/streamQUIC"}}
|
{{define "form/streamQUIC"}}
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
|
||||||
<tr>
|
<a-select v-model="inbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>{{ i18n "pages.inbounds.stream.quic.encryption" }}</td>
|
<a-select-option value="none">None</a-select-option>
|
||||||
<td>
|
<a-select-option value="aes-128-gcm">AES-128-GCM</a-select-option>
|
||||||
<a-form-item>
|
<a-select-option value="chacha20-poly1305">CHACHA20-POLY1305</a-select-option>
|
||||||
<a-select v-model="inbound.stream.quic.security" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-select>
|
||||||
<a-select-option value="none">none</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
|
<a-form-item>
|
||||||
<a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
|
<template slot="label">
|
||||||
</a-select>
|
<a-tooltip>
|
||||||
</a-form-item>
|
<template slot="title">
|
||||||
</td>
|
<span>{{ i18n "reset" }}</span>
|
||||||
</tr>
|
</template>
|
||||||
<tr>
|
{{ i18n "password" }}
|
||||||
<td>{{ i18n "password" }}</td>
|
<a-icon @click="inbound.stream.quic.key = RandomUtil.randomSeq(10)"type="sync"> </a-icon>
|
||||||
<td>
|
</a-tooltip>
|
||||||
<a-form-item>
|
</template>
|
||||||
<a-input v-model.trim="inbound.stream.quic.key" style="width: 250px;"></a-input>
|
<a-input v-model.trim="inbound.stream.quic.key"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item label='{{ i18n "camouflage" }}'>
|
||||||
</tr>
|
<a-select v-model="inbound.stream.quic.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<tr>
|
<a-select-option value="none">None</a-select-option>
|
||||||
<td>{{ i18n "camouflage" }}</td>
|
<a-select-option value="srtp">SRTP</a-select-option>
|
||||||
<td>
|
<a-select-option value="utp">uTP</a-select-option>
|
||||||
<a-form-item>
|
<a-select-option value="wechat-video">WeChat</a-select-option>
|
||||||
<a-select v-model="inbound.stream.quic.type" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select-option value="dtls">DTLS 1.2</a-select-option>
|
||||||
<a-select-option value="none">none (not camouflage)</a-select-option>
|
<a-select-option value="wireguard">WireGuard</a-select-option>
|
||||||
<a-select-option value="srtp">srtp (video call)</a-select-option>
|
</a-select>
|
||||||
<a-select-option value="utp">utp (BT download)</a-select-option>
|
</a-form-item>
|
||||||
<a-select-option value="wechat-video">wechat-video (WeChat video)</a-select-option>
|
|
||||||
<a-select-option value="dtls">dtls (DTLS 1.2 packages)</a-select-option>
|
|
||||||
<a-select-option value="wireguard">wireguard (wireguard packages)</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{{define "form/streamSettings"}}
|
{{define "form/streamSettings"}}
|
||||||
<!-- select stream network -->
|
<!-- select stream network -->
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :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" @change="streamNetworkChange"
|
||||||
style="width: 150px;" :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">KCP</a-select-option>
|
<a-select-option value="kcp">mKCP</a-select-option>
|
||||||
<a-select-option value="ws">WebSocket</a-select-option>
|
<a-select-option value="ws">WebSocket</a-select-option>
|
||||||
<a-select-option value="http">HTTP2</a-select-option>
|
<a-select-option value="http">HTTP/2</a-select-option>
|
||||||
<a-select-option value="quic">QUIC</a-select-option>
|
<a-select-option value="quic">QUIC</a-select-option>
|
||||||
<a-select-option value="grpc">gRPC</a-select-option>
|
<a-select-option value="grpc">gRPC</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
@@ -47,4 +47,4 @@
|
|||||||
<template>
|
<template>
|
||||||
{{template "form/streamSockopt"}}
|
{{template "form/streamSockopt"}}
|
||||||
</template>
|
</template>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,46 +1,26 @@
|
|||||||
{{define "form/streamSockopt"}}
|
{{define "form/streamSockopt"}}
|
||||||
<a-divider style="margin:0;"></a-divider>
|
<a-divider style="margin:0;"></a-divider>
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label="Transparent Proxy">
|
<a-form-item label="TPROXY">
|
||||||
<a-switch v-model="inbound.stream.sockoptSwitch"></a-switch>
|
<a-switch v-model="inbound.stream.sockoptSwitch"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<table width="100%" class="ant-table-tbody" v-if="inbound.stream.sockoptSwitch">
|
<template v-if="inbound.stream.sockoptSwitch">
|
||||||
<tr>
|
<a-form-item label="PROXY Protocol">
|
||||||
<td>Accept Proxy Protocol</td>
|
<a-switch v-model="inbound.stream.sockopt.acceptProxyProtocol"></a-switch>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label="TCP Fast Open">
|
||||||
<a-switch v-model="inbound.stream.sockopt.acceptProxyProtocol"></a-switch>
|
<a-switch v-model.trim="inbound.stream.sockopt.tcpFastOpen"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item label="Route Mark">
|
||||||
</tr>
|
<a-input-number v-model="inbound.stream.sockopt.mark" :min="0"></a-input-number>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>TCP FastOpen</td>
|
<a-form-item label="TPROXY">
|
||||||
<td>
|
<a-select v-model="inbound.stream.sockopt.tproxy" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-form-item>
|
<a-select-option value="off">Off</a-select-option>
|
||||||
<a-switch v-model.trim="inbound.stream.sockopt.tcpFastOpen"></a-switch>
|
<a-select-option value="redirect">Redirect</a-select-option>
|
||||||
</a-form-item>
|
<a-select-option value="tproxy">TPROXY</a-select-option>
|
||||||
</td>
|
</a-select>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
</template>
|
||||||
<td>Route Mark</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model="inbound.stream.sockopt.mark" :min="0"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>T-Proxy</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-select v-model="inbound.stream.sockopt.tproxy" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option value="off">OFF</a-select-option>
|
|
||||||
<a-select-option value="redirect">Redirect</a-select-option>
|
|
||||||
<a-select-option value="tproxy">T-Proxy</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,113 +1,78 @@
|
|||||||
{{define "form/streamTCP"}}
|
{{define "form/streamTCP"}}
|
||||||
<!-- tcp type -->
|
<!-- tcp type -->
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label="Accept Proxy Protocol" v-if="inbound.canEnableTls()">
|
<a-form-item label="PROXY Protocol" v-if="inbound.canEnableTls()">
|
||||||
<a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch>
|
<a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="http {{ i18n "camouflage" }}">
|
<a-form-item label="HTTP {{ i18n "camouflage" }}">
|
||||||
<a-switch
|
<a-switch
|
||||||
:checked="inbound.stream.tcp.type === 'http'"
|
:checked="inbound.stream.tcp.type === 'http'"
|
||||||
@change="checked => inbound.stream.tcp.type = checked ? 'http' : 'none'">
|
@change="checked => inbound.stream.tcp.type = checked ? 'http' : 'none'">
|
||||||
</a-switch>
|
</a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
<!-- tcp request -->
|
<a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form v-if="inbound.stream.tcp.type === 'http'" layout="inline">
|
<!-- tcp request -->
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.requestHeader" }}</a-divider>
|
||||||
<tr>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}'>
|
||||||
<td>{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}</td>
|
<a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}'>
|
||||||
<a-input v-model.trim="inbound.stream.tcp.request.version" style="width: 200px;"></a-input>
|
<a-input v-model.trim="inbound.stream.tcp.request.method"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item>
|
||||||
</tr>
|
<template slot="label">{{ i18n "pages.inbounds.stream.tcp.requestPath" }}
|
||||||
<tr>
|
<a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button>
|
||||||
<td>{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}</td>
|
</template>
|
||||||
<td>
|
<template v-for="(path, index) in inbound.stream.tcp.request.path">
|
||||||
<a-form-item>
|
<a-input v-model.trim="inbound.stream.tcp.request.path[index]">
|
||||||
<a-input v-model.trim="inbound.stream.tcp.request.method" style="width: 200px;"></a-input>
|
<a-button size="small" slot="addonAfter"
|
||||||
</a-form-item>
|
@click="inbound.stream.tcp.request.removePath(index)"
|
||||||
</td>
|
v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
|
||||||
</tr>
|
</a-input>
|
||||||
<tr>
|
</template>
|
||||||
<td>{{ i18n "pages.inbounds.stream.tcp.requestPath" }}
|
</a-form-item>
|
||||||
<a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
||||||
</td>
|
<a-button size="small" @click="inbound.stream.tcp.request.addHeader('', '')">+</a-button>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
<a-row v-for="(path, index) in inbound.stream.tcp.request.path">
|
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
|
||||||
<a-input v-model.trim="inbound.stream.tcp.request.path[index]" style="width: 200px;">
|
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||||
<a-button size="small" slot="addonAfter"
|
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||||
@click="inbound.stream.tcp.request.removePath(index)"
|
</a-input>
|
||||||
v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
|
<a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
</a-input>
|
<a-button slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
|
||||||
</a-row>
|
</a-input>
|
||||||
</a-form-item>
|
</a-input-group>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<!-- tcp response -->
|
||||||
<tr>
|
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}</a-divider>
|
||||||
<td colspan="2" width="100%">
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}'>
|
||||||
<a-form-item>
|
<a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
|
||||||
<span>{{ i18n "pages.inbounds.stream.general.requestHeader" }}:</span>
|
</a-form-item>
|
||||||
<a-button size="small" style="margin-left: 10px" @click="inbound.stream.tcp.request.addHeader('', '')">+</a-button>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}'>
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
|
<a-input v-model.trim="inbound.stream.tcp.response.status"></a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
</a-form-item>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}'>
|
||||||
</a-input>
|
<a-input v-model.trim="inbound.stream.tcp.response.reason"></a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
</a-form-item>
|
||||||
<a-button slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}'>
|
||||||
</a-input>
|
<a-button size="small"
|
||||||
</a-input-group>
|
@click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">+</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
</tr>
|
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers">
|
||||||
<!-- tcp response -->
|
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||||
<tr>
|
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||||
<td>{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}</td>
|
</a-input>
|
||||||
<td>
|
<a-input style="width: 50%" v-model.trim="header.value"
|
||||||
<a-form-item>
|
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
<a-input v-model.trim="inbound.stream.tcp.response.version" style="width: 200px;"></a-input>
|
<template slot="addonAfter">
|
||||||
</a-form-item>
|
<a-button size="small" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button>
|
||||||
</td>
|
</template>
|
||||||
</tr>
|
</a-input>
|
||||||
<tr>
|
</a-input-group>
|
||||||
<td>{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}</td>
|
</a-form-item>
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="inbound.stream.tcp.response.status" style="width: 200px;"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="inbound.stream.tcp.response.reason" style="width: 200px;"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2" width="100%">
|
|
||||||
<a-form-item>
|
|
||||||
<span>{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}:</span>
|
|
||||||
<a-button size="small" style="margin-left: 10px"
|
|
||||||
@click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">+</a-button>
|
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers">
|
|
||||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
|
||||||
</a-input>
|
|
||||||
<a-input style="width: 50%" v-model.trim="header.value"
|
|
||||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
|
||||||
<template slot="addonAfter">
|
|
||||||
<a-button size="small" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</a-input-group>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
{{define "form/streamWS"}}
|
{{define "form/streamWS"}}
|
||||||
<a-form layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label="AcceptProxyProtocol">
|
<a-form-item label="PROXY Protocol">
|
||||||
<a-switch v-model="inbound.stream.ws.acceptProxyProtocol"></a-switch>
|
<a-switch v-model="inbound.stream.ws.acceptProxyProtocol"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<br>
|
|
||||||
<a-form-item label='{{ i18n "path" }}'>
|
<a-form-item label='{{ i18n "path" }}'>
|
||||||
<a-input v-model.trim="inbound.stream.ws.path"></a-input>
|
<a-input v-model.trim="inbound.stream.ws.path"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<br>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
||||||
<a-form-item style="width: 100%;">
|
<a-button size="small" @click="inbound.stream.ws.addHeader()">+</a-button>
|
||||||
<span>{{ i18n "pages.inbounds.stream.general.requestHeader" }}:</span>
|
</a-form-item>
|
||||||
<a-button size="small" style="margin-left: 10px" @click="inbound.stream.ws.addHeader()">+</a-button>
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.ws.headers">
|
<a-input-group compact v-for="(header, index) in inbound.stream.ws.headers">
|
||||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||||
@@ -21,4 +20,4 @@
|
|||||||
</a-input-group>
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -1,251 +1,135 @@
|
|||||||
{{define "form/tlsSettings"}}
|
{{define "form/tlsSettings"}}
|
||||||
<!-- tls enable -->
|
<!-- tls enable -->
|
||||||
<a-form v-if="inbound.canEnableTls()" layout="inline">
|
<a-form v-if="inbound.canEnableTls()" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider style="margin:0;"></a-divider>
|
<a-divider style="margin:0;"></a-divider>
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "security" }}'>
|
||||||
<tr>
|
<a-radio-group v-model="inbound.stream.security" button-style="solid">
|
||||||
<td colspan="2">
|
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
||||||
<a-form-item label='{{ i18n "security" }}'>
|
<a-radio-button value="tls">TLS</a-radio-button>
|
||||||
<a-radio-group v-model="inbound.stream.security" button-style="solid">
|
<a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
|
||||||
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
</a-radio-group>
|
||||||
<a-radio-button value="tls">TLS</a-radio-button>
|
</a-form-item>
|
||||||
<a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
|
|
||||||
</a-radio-group>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- tls settings -->
|
<!-- tls settings -->
|
||||||
<template v-if="inbound.stream.isTls">
|
<template v-if="inbound.stream.isTls">
|
||||||
<tr>
|
<a-form-item label="SNI" placeholder="Server Name Indication">
|
||||||
<td>SNI</td>
|
<a-input v-model.trim="inbound.stream.tls.sni"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item placeholder="Server Name Indication">
|
<a-form-item label="Cipher Suites">
|
||||||
<a-input v-model.trim="inbound.stream.tls.sni" style="width: 250px"></a-input>
|
<a-select v-model="inbound.stream.tls.cipherSuites" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</a-form-item>
|
<a-select-option value="">Auto</a-select-option>
|
||||||
</td>
|
<a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[ value ]]</a-select-option>
|
||||||
</tr>
|
</a-select>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>CipherSuites</td>
|
<a-form-item label="Min/Max Version">
|
||||||
<td>
|
<a-input-group compact>
|
||||||
<a-form-item>
|
<a-select v-model="inbound.stream.tls.minVersion" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select v-model="inbound.stream.tls.cipherSuites" style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
<a-select-option value="">auto</a-select-option>
|
</a-select>
|
||||||
<a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[ value ]]</a-select-option>
|
<a-select v-model="inbound.stream.tls.maxVersion" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</a-select>
|
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-form-item>
|
</a-select>
|
||||||
</td>
|
</a-input-group>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
<a-form-item label="uTLS">
|
||||||
<td>Min/Max Version</td>
|
<a-select v-model="inbound.stream.tls.settings.fingerprint"
|
||||||
<td>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-form-item>
|
<a-select-option value=''>None</a-select-option>
|
||||||
<a-input-group compact>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
<a-select style="width: 125px" v-model="inbound.stream.tls.minVersion" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-select>
|
||||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
</a-form-item>
|
||||||
</a-select>
|
<a-form-item label="ALPN">
|
||||||
<a-select style="width: 125px" v-model="inbound.stream.tls.maxVersion" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select
|
||||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
mode="multiple"
|
||||||
</a-select>
|
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
</a-input-group>
|
v-model="inbound.stream.tls.alpn">
|
||||||
</a-form-item>
|
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
|
||||||
</td>
|
</a-select>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
<a-form-item label="Allow Insecure">
|
||||||
<td>uTLS</td>
|
<a-switch v-model="inbound.stream.tls.settings.allowInsecure"></a-switch>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label="Reject Unknown SNI">
|
||||||
<a-select v-model="inbound.stream.tls.settings.fingerprint"
|
<a-switch v-model="inbound.stream.tls.rejectUnknownSni"></a-switch>
|
||||||
style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-form-item>
|
||||||
<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>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>ALPN</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-select
|
|
||||||
mode="multiple"
|
|
||||||
style="width: 250px"
|
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
|
||||||
v-model="inbound.stream.tls.alpn">
|
|
||||||
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Allow insecure</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="inbound.stream.tls.settings.allowInsecure"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Reject Unknown SNI</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-switch v-model="inbound.stream.tls.rejectUnknownSni"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<template v-for="cert,index in inbound.stream.tls.certs">
|
<template v-for="cert,index in inbound.stream.tls.certs">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "certificate" }}'>
|
||||||
<td>{{ i18n "certificate" }}</td>
|
<a-radio-group v-model="cert.useFile" button-style="solid">
|
||||||
<td>
|
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
||||||
<a-form-item>
|
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
||||||
<a-radio-group v-model="cert.useFile" button-style="solid">
|
</a-radio-group>
|
||||||
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
<a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()" style="margin-left: 10px">+</a-button>
|
||||||
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
<a-button v-if="inbound.stream.tls.certs.length>1" type="primary" size="small" @click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px">-</a-button>
|
||||||
</a-radio-group>
|
</a-form-item>
|
||||||
<a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()" style="margin-left: 10px">+</a-button>
|
<template v-if="cert.useFile">
|
||||||
<a-button v-if="inbound.stream.tls.certs.length>1" type="primary" size="small" @click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px">-</a-button>
|
<a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'>
|
||||||
</a-form-item>
|
<a-input v-model.trim="cert.certFile" style="width:250px;"></a-input>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'>
|
||||||
<template v-if="cert.useFile">
|
<a-input v-model.trim="cert.keyFile" style="width:250px;"></a-input>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>{{ i18n "pages.inbounds.publicKeyPath" }}</td>
|
<a-form-item label=" ">
|
||||||
<td>
|
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input v-model.trim="cert.certFile" style="width:250px;"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.inbounds.keyPath" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="cert.keyFile" style="width:250px;"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td>
|
|
||||||
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<tr>
|
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
|
||||||
<td>{{ i18n "pages.inbounds.publicKeyContent" }}</td>
|
<a-input type="textarea" :rows="3" style="width:250px;" v-model="cert.cert"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'>
|
||||||
<a-input type="textarea" :rows="3" style="width:250px;" v-model="cert.cert"></a-input>
|
<a-input type="textarea" :rows="3" style="width:250px;" v-model="cert.key"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.inbounds.keyContent" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input type="textarea" :rows="3" style="width:250px;" v-model="cert.key"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
<tr>
|
<a-form-item label='OCSP stapling'>
|
||||||
<td>ocspStapling</td>
|
<a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
|
||||||
<a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- reality settings -->
|
<!-- reality settings -->
|
||||||
<template v-if="inbound.stream.isReality">
|
<template v-if="inbound.stream.isReality">
|
||||||
<tr>
|
<a-form-item label='Show'>
|
||||||
<td>Show</td>
|
<a-switch v-model="inbound.stream.reality.show"></a-switch>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label='Xver'>
|
||||||
<a-switch v-model="inbound.stream.reality.show"></a-switch>
|
<a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item label='uTLS'>
|
||||||
</tr>
|
<a-select v-model="inbound.stream.reality.settings.fingerprint"
|
||||||
<tr>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>Xver</td>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
<td>
|
</a-select>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number>
|
<a-form-item label='Dest'>
|
||||||
</a-form-item>
|
<a-input v-model.trim="inbound.stream.reality.dest"></a-input>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item label='SNI'>
|
||||||
<tr>
|
<a-input v-model.trim="inbound.stream.reality.serverNames"></a-input>
|
||||||
<td>uTLS</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item>
|
||||||
<a-form-item>
|
<template slot="label">
|
||||||
<a-select v-model="inbound.stream.reality.settings.fingerprint"
|
<a-tooltip>
|
||||||
style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme">
|
<template slot="title">
|
||||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
<span>{{ i18n "reset" }}</span>
|
||||||
</a-select>
|
</template>
|
||||||
</a-form-item>
|
Short IDs
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Dest</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="inbound.stream.reality.dest" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Server Names</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="inbound.stream.reality.serverNames" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Short Ids
|
|
||||||
<a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId().join(',')" type="sync">
|
<a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId().join(',')" type="sync">
|
||||||
</td>
|
</a-icon>
|
||||||
<td>
|
</template>
|
||||||
<a-form-item>
|
<a-input v-model.trim="inbound.stream.reality.shortIds" style="width:250px"></a-input>
|
||||||
<a-input v-model.trim="inbound.stream.reality.shortIds" style="width:250px"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label='SpiderX'>
|
||||||
</td>
|
<a-input v-model.trim="inbound.stream.reality.settings.spiderX" style="width:250px"></a-input>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
<a-form-item label='Private Key'>
|
||||||
<td>SpiderX</td>
|
<a-input v-model.trim="inbound.stream.reality.privateKey"></a-input>
|
||||||
<td>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label='Public Key'>
|
||||||
<a-input v-model.trim="inbound.stream.reality.settings.spiderX" style="width:250px"></a-input>
|
<a-input v-model.trim="inbound.stream.reality.settings.publicKey"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item label=" ">
|
||||||
</tr>
|
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>Private Key</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="inbound.stream.reality.privateKey" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Public Key</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="inbound.stream.reality.settings.publicKey" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td></td>
|
|
||||||
<td>
|
|
||||||
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get new cert</a-button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
</table>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
:overlay-class-name="themeSwitcher.currentTheme"
|
:overlay-class-name="themeSwitcher.currentTheme"
|
||||||
ok-text='{{ i18n "reset"}}'
|
ok-text='{{ i18n "reset"}}'
|
||||||
cancel-text='{{ i18n "cancel"}}'>
|
cancel-text='{{ i18n "cancel"}}'>
|
||||||
<a-icon slot="icon" type="question-circle-o" style="color: blue"></a-icon>
|
<a-icon slot="icon" type="question-circle-o" :style="themeSwitcher.isDarkTheme ? 'color: #3c89e8' : 'color: blue'"></a-icon>
|
||||||
<a-icon style="font-size: 24px;" class="normal-icon" type="retweet" v-if="client.email.length > 0"></a-icon>
|
<a-icon style="font-size: 24px; cursor: pointer;" class="normal-icon" type="retweet" v-if="client.email.length > 0"></a-icon>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
ok-type="danger"
|
ok-type="danger"
|
||||||
cancel-text='{{ i18n "cancel"}}'>
|
cancel-text='{{ i18n "cancel"}}'>
|
||||||
<a-icon slot="icon" type="question-circle-o" style="color: #e04141"></a-icon>
|
<a-icon slot="icon" type="question-circle-o" style="color: #e04141"></a-icon>
|
||||||
<a-icon style="font-size: 24px" class="delete-icon" type="delete" v-if="isRemovable(record.id)"></a-icon>
|
<a-icon style="font-size: 24px; cursor: pointer;" class="delete-icon" type="delete" v-if="isRemovable(record.id)"></a-icon>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
@@ -265,4 +265,4 @@
|
|||||||
</a-badge>
|
</a-badge>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -7,15 +7,20 @@
|
|||||||
width="600px"
|
width="600px"
|
||||||
:class="themeSwitcher.currentTheme"
|
:class="themeSwitcher.currentTheme"
|
||||||
>
|
>
|
||||||
<table style="margin-bottom: 10px; width: 100%;">
|
<a-row>
|
||||||
<tr><td>
|
<a-col :xs="24" :md="12">
|
||||||
<table>
|
<table>
|
||||||
<tr><td>{{ i18n "protocol" }}</td><td><a-tag color="purple">[[ dbInbound.protocol ]]</a-tag></td></tr>
|
<tr><td>{{ i18n "protocol" }}</td><td><a-tag color="purple">[[ dbInbound.protocol ]]</a-tag></td></tr>
|
||||||
<tr><td>{{ i18n "pages.inbounds.address" }}</td><td><a-tag>[[ dbInbound.address ]]</a-tag></td></tr>
|
<tr><td>{{ i18n "pages.inbounds.address" }}</td><td>
|
||||||
|
<a-tooltip :title="[[ dbInbound.address ]]">
|
||||||
|
<a-tag class="info-large-tag">[[ dbInbound.address ]]</a-tag>
|
||||||
|
</a-tooltip>
|
||||||
|
</td></tr>
|
||||||
<tr><td>{{ i18n "pages.inbounds.port" }}</td><td><a-tag>[[ dbInbound.port ]]</a-tag></td></tr>
|
<tr><td>{{ i18n "pages.inbounds.port" }}</td><td><a-tag>[[ dbInbound.port ]]</a-tag></td></tr>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</a-col>
|
||||||
<td v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
<a-col :xs="24" :md="12">
|
||||||
|
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "transmission" }}</td><td><a-tag color="blue">[[ inbound.network ]]</a-tag></td>
|
<td>{{ i18n "transmission" }}</td><td><a-tag color="blue">[[ inbound.network ]]</a-tag></td>
|
||||||
@@ -23,12 +28,19 @@
|
|||||||
<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2">
|
<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2">
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "host" }}</td>
|
<td>{{ i18n "host" }}</td>
|
||||||
<td v-if="inbound.host"><a-tag>[[ inbound.host ]]</a-tag></td>
|
<td v-if="inbound.host">
|
||||||
|
<a-tooltip :title="[[ inbound.host ]]">
|
||||||
|
<a-tag class="info-large-tag">[[ inbound.host ]]</a-tag>
|
||||||
|
</a-tooltip>
|
||||||
|
</td>
|
||||||
<td v-else><a-tag color="orange">{{ i18n "none" }}</a-tag></td></tr>
|
<td v-else><a-tag color="orange">{{ i18n "none" }}</a-tag></td></tr>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "path" }}</td>
|
<td>{{ i18n "path" }}</td>
|
||||||
<td v-if="inbound.path"><a-tag>[[ inbound.path ]]</a-tag></td>
|
<td v-if="inbound.path">
|
||||||
|
<a-tooltip :title="[[ inbound.path ]]">
|
||||||
|
<a-tag class="info-large-tag">[[ inbound.path ]]</a-tag>
|
||||||
|
</a-tooltip>
|
||||||
<td v-else><a-tag color="orange">{{ i18n "none" }}</a-tag></td>
|
<td v-else><a-tag color="orange">{{ i18n "none" }}</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
@@ -45,30 +57,35 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="inbound.isGrpc">
|
<template v-if="inbound.isGrpc">
|
||||||
<tr><td>grpc serviceName</td><td><a-tag>[[ inbound.serviceName ]]</a-tag></td></tr>
|
<tr><td>grpc serviceName</td><td>
|
||||||
|
<a-tooltip :title="[[ inbound.serviceName ]]">
|
||||||
|
<a-tag class="info-large-tag">[[ inbound.serviceName ]]</a-tag>
|
||||||
|
</a-tooltip>
|
||||||
<tr><td>grpc multiMode</td><td><a-tag>[[ inbound.stream.grpc.multiMode ]]</a-tag></td></tr>
|
<tr><td>grpc multiMode</td><td><a-tag>[[ inbound.stream.grpc.multiMode ]]</a-tag></td></tr>
|
||||||
</template>
|
</template>
|
||||||
</table>
|
</table>
|
||||||
</td></tr>
|
</template>
|
||||||
<tr colspan="2" v-if="dbInbound.hasLink()">
|
</a-col>
|
||||||
<td>
|
<template v-if="dbInbound.hasLink()">
|
||||||
{{ i18n "security" }}
|
{{ i18n "security" }}
|
||||||
<a-tag :color="inbound.stream.security == 'none' ? 'red' : 'green'">[[ inbound.stream.security ]]</a-tag>
|
<a-tag :color="inbound.stream.security == 'none' ? 'red' : 'green'">[[ inbound.stream.security ]]</a-tag>
|
||||||
<br />
|
<br />
|
||||||
<template v-if="inbound.stream.security != 'none'">
|
<template v-if="inbound.stream.security != 'none'">
|
||||||
{{ i18n "domainName" }}
|
{{ i18n "domainName" }}
|
||||||
<a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
|
<a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</template>
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<table v-if="dbInbound.isSS" style="margin-bottom: 10px; width: 100%;">
|
<table v-if="dbInbound.isSS" style="margin-bottom: 10px; width: 100%;">
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "encryption" }}</td>
|
<td>{{ i18n "encryption" }}</td>
|
||||||
<td><a-tag color="blue">[[ inbound.settings.method ]]</a-tag></td>
|
<td><a-tag color="blue">[[ inbound.settings.method ]]</a-tag></td>
|
||||||
</tr><tr v-if="inbound.isSS2022">
|
</tr><tr v-if="inbound.isSS2022">
|
||||||
<td>{{ i18n "password" }}</td>
|
<td>{{ i18n "password" }}</td>
|
||||||
<td><a-tag>[[ inbound.settings.password ]]</a-tag></td>
|
<td>
|
||||||
|
<a-tooltip :title="[[ inbound.settings.password ]]">
|
||||||
|
<a-tag class="info-large-tag">[[ inbound.settings.password ]]</a-tag>
|
||||||
|
</a-tooltip>
|
||||||
|
</td>
|
||||||
</tr><tr>
|
</tr><tr>
|
||||||
<td>{{ i18n "pages.inbounds.network" }}</td>
|
<td>{{ i18n "pages.inbounds.network" }}</td>
|
||||||
<td><a-tag color="blue">[[ inbound.settings.network ]]</a-tag></td>
|
<td><a-tag color="blue">[[ inbound.settings.network ]]</a-tag></td>
|
||||||
@@ -90,8 +107,12 @@
|
|||||||
<td><a-tag>[[ infoModal.clientSettings.flow ]]</a-tag></td>
|
<td><a-tag>[[ infoModal.clientSettings.flow ]]</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="infoModal.clientSettings.password">
|
<tr v-if="infoModal.clientSettings.password">
|
||||||
<td>Password</td>
|
<td>{{ i18n "password" }}</td>
|
||||||
<td><a-tag>[[ infoModal.clientSettings.password ]]</a-tag></td>
|
<td>
|
||||||
|
<a-tooltip :title="[[ infoModal.clientSettings.password ]]">
|
||||||
|
<a-tag class="info-large-tag">[[ infoModal.clientSettings.password ]]</a-tag>
|
||||||
|
</a-tooltip>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "status" }}</td>
|
<td>{{ i18n "status" }}</td>
|
||||||
@@ -139,10 +160,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<template v-if="app.subSettings.enable && infoModal.clientSettings.subId">
|
<template v-if="app.subSettings.enable && infoModal.clientSettings.subId">
|
||||||
<a-divider>Subscription link</a-divider>
|
<a-divider>Subscription URL</a-divider>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="22"><a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a></a-col>
|
<a-col :sx="24" :md="22"><a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a></a-col>
|
||||||
<a-col :span="2">
|
<a-col :sx="24" :md="2" style="text-align: right;">
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<button class="ant-btn ant-btn-primary" id="copy-sub-link" @click="copyToClipboard('copy-sub-link', infoModal.subLink)">
|
<button class="ant-btn ant-btn-primary" id="copy-sub-link" @click="copyToClipboard('copy-sub-link', infoModal.subLink)">
|
||||||
<a-icon type="snippets"></a-icon>
|
<a-icon type="snippets"></a-icon>
|
||||||
@@ -154,8 +175,8 @@
|
|||||||
<template v-if="app.tgBotEnable && infoModal.clientSettings.tgId">
|
<template v-if="app.tgBotEnable && infoModal.clientSettings.tgId">
|
||||||
<a-divider>Telegram ID</a-divider>
|
<a-divider>Telegram ID</a-divider>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="22"><a :href="[[ infoModal.tgLink ]]" target="_blank">@[[ infoModal.clientSettings.tgId ]]</a></a-col>
|
<a-col :sx="24" :md="22"><a :href="[[ infoModal.tgLink ]]" target="_blank">@[[ infoModal.clientSettings.tgId ]]</a></a-col>
|
||||||
<a-col :span="2">
|
<a-col :sx="24" :md="2" style="text-align: right;">
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<button class="ant-btn ant-btn-primary" id="copy-tg-link" @click="copyToClipboard('copy-tg-link', '@' + infoModal.clientSettings.tgId)">
|
<button class="ant-btn ant-btn-primary" id="copy-tg-link" @click="copyToClipboard('copy-tg-link', '@' + infoModal.clientSettings.tgId)">
|
||||||
<a-icon type="snippets"></a-icon>
|
<a-icon type="snippets"></a-icon>
|
||||||
@@ -167,8 +188,8 @@
|
|||||||
<template v-if="dbInbound.hasLink()">
|
<template v-if="dbInbound.hasLink()">
|
||||||
<a-divider>URL</a-divider>
|
<a-divider>URL</a-divider>
|
||||||
<a-row v-for="(link,index) in infoModal.links">
|
<a-row v-for="(link,index) in infoModal.links">
|
||||||
<a-col :span="22"><a-tag color="blue">[[ link.remark ]]</a-tag><br />[[ link.link ]]</a-col>
|
<a-col :sx="24" :md="22"><a-tag color="blue">[[ link.remark ]]</a-tag><br />[[ link.link ]]</a-col>
|
||||||
<a-col :span="2" style="text-align: right;">
|
<a-col :sx="24" :md="2" style="text-align: right;">
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<button class="ant-btn ant-btn-primary" :id="'copy-url-link-'+index" @click="copyToClipboard('copy-url-link-'+index, link.link)">
|
<button class="ant-btn ant-btn-primary" :id="'copy-url-link-'+index" @click="copyToClipboard('copy-url-link-'+index, link.link)">
|
||||||
<a-icon type="snippets"></a-icon>
|
<a-icon type="snippets"></a-icon>
|
||||||
@@ -239,6 +260,45 @@
|
|||||||
<td><a-tag color="blue">[[ account.pass ]]</a-tag></td>
|
<td><a-tag color="blue">[[ account.pass ]]</a-tag></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
<table v-if="dbInbound.isWireguard" style="margin-bottom: 10px; width: 100%;">
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>{{ i18n "pages.xray.wireguard.secretKey" }}</td>
|
||||||
|
<td>[[ inbound.settings.secretKey ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.xray.wireguard.publicKey" }}</td>
|
||||||
|
<td>[[ inbound.settings.pubKey ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>MTU</td>
|
||||||
|
<td>[[ inbound.settings.mtu ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Kernel Mode</td>
|
||||||
|
<td>[[ inbound.settings.kernelMode ]]</td>
|
||||||
|
</tr>
|
||||||
|
<template v-for="(peer, index) in inbound.settings.peers">
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"><a-tag>Peer [[ index + 1 ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>{{ i18n "pages.xray.wireguard.publicKey" }}</td>
|
||||||
|
<td>[[ peer.publicKey ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ i18n "pages.xray.wireguard.psk" }}</td>
|
||||||
|
<td>[[ peer.psk ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>{{ i18n "pages.xray.wireguard.allowedIPs" }}</td>
|
||||||
|
<td>[[ peer.allowedIPs.join(",") ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Keep Alive</td>
|
||||||
|
<td>[[ peer.keepAlive ]]</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<script>
|
<script>
|
||||||
@@ -263,7 +323,7 @@
|
|||||||
this.clientSettings = this.inbound.clients ? this.inbound.clients[index] : null;
|
this.clientSettings = this.inbound.clients ? this.inbound.clients[index] : null;
|
||||||
this.isExpired = this.inbound.clients ? this.inbound.isExpiry(index): this.dbInbound.isExpiry;
|
this.isExpired = this.inbound.clients ? this.inbound.isExpiry(index): this.dbInbound.isExpiry;
|
||||||
this.clientStats = this.inbound.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
this.clientStats = this.inbound.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
||||||
this.links = this.inbound.genAllLinks(this.dbInbound.remark, this.clientSettings);
|
this.links = this.inbound.genAllLinks(this.dbInbound.remark, app.remarkModel, this.clientSettings);
|
||||||
if (this.clientSettings) {
|
if (this.clientSettings) {
|
||||||
if (this.clientSettings.subId) {
|
if (this.clientSettings.subId) {
|
||||||
this.subLink = this.genSubLink(this.clientSettings.subId);
|
this.subLink = this.genSubLink(this.clientSettings.subId);
|
||||||
@@ -324,4 +384,4 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -43,6 +43,10 @@
|
|||||||
0%, 50%, 100% { transform: scale(1); opacity: 1; }
|
0%, 50%, 100% { transform: scale(1); opacity: 1; }
|
||||||
10% { transform: scale(1.5); opacity: .2; }
|
10% { transform: scale(1.5); opacity: .2; }
|
||||||
}
|
}
|
||||||
|
.info-large-tag {
|
||||||
|
max-width: 200px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -125,6 +129,10 @@
|
|||||||
<template v-if="!isMobile">{{ i18n "pages.inbounds.generalActions" }}</template>
|
<template v-if="!isMobile">{{ i18n "pages.inbounds.generalActions" }}</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-menu slot="overlay" @click="a => generalActions(a)" :theme="themeSwitcher.currentTheme">
|
<a-menu slot="overlay" @click="a => generalActions(a)" :theme="themeSwitcher.currentTheme">
|
||||||
|
<a-menu-item key="import">
|
||||||
|
<a-icon type="import"></a-icon>
|
||||||
|
{{ i18n "pages.inbounds.importInbound" }}
|
||||||
|
</a-menu-item>
|
||||||
<a-menu-item key="export">
|
<a-menu-item key="export">
|
||||||
<a-icon type="export"></a-icon>
|
<a-icon type="export"></a-icon>
|
||||||
{{ i18n "pages.inbounds.export" }}
|
{{ i18n "pages.inbounds.export" }}
|
||||||
@@ -156,9 +164,9 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; align-items: center; justify-content: flex-start;">
|
<div :style="isMobile ? '' : 'display: flex; align-items: center; justify-content: flex-start;'">
|
||||||
<a-switch v-model="enableFilter"
|
<a-switch v-model="enableFilter"
|
||||||
style="margin-right: .5rem;"
|
:style="isMobile ? 'margin-bottom: .5rem; display: flex;' : 'margin-right: .5rem;'"
|
||||||
@change="toggleFilter">
|
@change="toggleFilter">
|
||||||
<a-icon slot="checkedChildren" type="search"></a-icon>
|
<a-icon slot="checkedChildren" type="search"></a-icon>
|
||||||
<a-icon slot="unCheckedChildren" type="filter"></a-icon>
|
<a-icon slot="unCheckedChildren" type="filter"></a-icon>
|
||||||
@@ -226,6 +234,10 @@
|
|||||||
<a-menu-item key="resetTraffic">
|
<a-menu-item key="resetTraffic">
|
||||||
<a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }}
|
<a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
|
<a-menu-item key="clipboard">
|
||||||
|
<a-icon type="copy"></a-icon>
|
||||||
|
{{ i18n "pages.inbounds.copyToClipboard" }}
|
||||||
|
</a-menu-item>
|
||||||
<a-menu-item key="clone">
|
<a-menu-item key="clone">
|
||||||
<a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}}
|
<a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
@@ -415,7 +427,7 @@
|
|||||||
:columns="isMobile ? innerMobileColumns : innerColumns"
|
:columns="isMobile ? innerMobileColumns : innerColumns"
|
||||||
:data-source="getInboundClients(record)"
|
:data-source="getInboundClients(record)"
|
||||||
:pagination=pagination(getInboundClients(record))
|
:pagination=pagination(getInboundClients(record))
|
||||||
:style="isMobile ? 'margin: -16px -5px -17px;' : 'margin-left: 10px;'">
|
style="margin: -12px -6px -13px;">
|
||||||
{{template "client_table"}}
|
{{template "client_table"}}
|
||||||
</a-table>
|
</a-table>
|
||||||
</template>
|
</template>
|
||||||
@@ -509,9 +521,9 @@
|
|||||||
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 50, scopedSlots: { customRender: 'actions' } },
|
{ title: '{{ i18n "pages.inbounds.operate" }}', width: 50, scopedSlots: { customRender: 'actions' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.enable" }}', width: 20, scopedSlots: { customRender: 'enable' } },
|
{ title: '{{ i18n "pages.inbounds.enable" }}', width: 20, scopedSlots: { customRender: 'enable' } },
|
||||||
{ title: '{{ i18n "online" }}', width: 20, scopedSlots: { customRender: 'online' } },
|
{ title: '{{ i18n "online" }}', width: 20, scopedSlots: { customRender: 'online' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.client" }}', width: 80, scopedSlots: { customRender: 'client' } },
|
{ title: '{{ i18n "pages.inbounds.client" }}', width: 70, scopedSlots: { customRender: 'client' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 80, align: 'center', scopedSlots: { customRender: 'traffic' } },
|
{ title: '{{ i18n "pages.inbounds.traffic" }}', width: 80, align: 'center', scopedSlots: { customRender: 'traffic' } },
|
||||||
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 80, align: 'center', scopedSlots: { customRender: 'expiryTime' } },
|
{ title: '{{ i18n "pages.inbounds.expireDate" }}', width: 70, align: 'center', scopedSlots: { customRender: 'expiryTime' } },
|
||||||
];
|
];
|
||||||
|
|
||||||
const innerMobileColumns = [
|
const innerMobileColumns = [
|
||||||
@@ -546,6 +558,7 @@
|
|||||||
enable : false,
|
enable : false,
|
||||||
subURI : ''
|
subURI : ''
|
||||||
},
|
},
|
||||||
|
remarkModel: '-ieo',
|
||||||
tgBotEnable: false,
|
tgBotEnable: false,
|
||||||
showAlert: false,
|
showAlert: false,
|
||||||
pageSize: 0,
|
pageSize: 0,
|
||||||
@@ -591,6 +604,7 @@
|
|||||||
subURI: subURI
|
subURI: subURI
|
||||||
};
|
};
|
||||||
this.pageSize = pageSize;
|
this.pageSize = pageSize;
|
||||||
|
this.remarkModel = remarkModel;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setInbounds(dbInbounds) {
|
setInbounds(dbInbounds) {
|
||||||
@@ -707,6 +721,9 @@
|
|||||||
},
|
},
|
||||||
generalActions(action) {
|
generalActions(action) {
|
||||||
switch (action.key) {
|
switch (action.key) {
|
||||||
|
case "import":
|
||||||
|
this.importInbound();
|
||||||
|
break;
|
||||||
case "export":
|
case "export":
|
||||||
this.exportAllLinks();
|
this.exportAllLinks();
|
||||||
break;
|
break;
|
||||||
@@ -741,6 +758,9 @@
|
|||||||
case "export":
|
case "export":
|
||||||
this.inboundLinks(dbInbound.id);
|
this.inboundLinks(dbInbound.id);
|
||||||
break;
|
break;
|
||||||
|
case "clipboard":
|
||||||
|
this.copyToClipboard(dbInbound.id);
|
||||||
|
break;
|
||||||
case "resetTraffic":
|
case "resetTraffic":
|
||||||
this.resetTraffic(dbInbound.id);
|
this.resetTraffic(dbInbound.id);
|
||||||
break;
|
break;
|
||||||
@@ -792,7 +812,7 @@
|
|||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.cloneInbound"}} \"' + dbInbound.remark + '\"',
|
title: '{{ i18n "pages.inbounds.cloneInbound"}} \"' + dbInbound.remark + '\"',
|
||||||
content: '{{ i18n "pages.inbounds.cloneInboundContent"}}',
|
content: '{{ i18n "pages.inbounds.cloneInboundContent"}}',
|
||||||
okText: '{{ i18n "pages.inbounds.update"}}',
|
okText: '{{ i18n "pages.inbounds.clone"}}',
|
||||||
class: themeSwitcher.currentTheme,
|
class: themeSwitcher.currentTheme,
|
||||||
cancelText: '{{ i18n "cancel" }}',
|
cancelText: '{{ i18n "cancel" }}',
|
||||||
onOk: () => {
|
onOk: () => {
|
||||||
@@ -928,7 +948,7 @@
|
|||||||
resetTraffic(dbInboundId) {
|
resetTraffic(dbInboundId) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
|
title: '{{ i18n "pages.inbounds.resetTraffic"}}' + ' #' + dbInboundId,
|
||||||
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
||||||
class: themeSwitcher.currentTheme,
|
class: themeSwitcher.currentTheme,
|
||||||
okText: '{{ i18n "reset"}}',
|
okText: '{{ i18n "reset"}}',
|
||||||
@@ -943,7 +963,7 @@
|
|||||||
},
|
},
|
||||||
delInbound(dbInboundId) {
|
delInbound(dbInboundId) {
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.deleteInbound"}}',
|
title: '{{ i18n "pages.inbounds.deleteInbound"}}' + ' #' + dbInboundId,
|
||||||
content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
|
content: '{{ i18n "pages.inbounds.deleteInboundContent"}}',
|
||||||
class: themeSwitcher.currentTheme,
|
class: themeSwitcher.currentTheme,
|
||||||
okText: '{{ i18n "delete"}}',
|
okText: '{{ i18n "delete"}}',
|
||||||
@@ -956,7 +976,7 @@
|
|||||||
clientId = this.getClientId(dbInbound.protocol, client);
|
clientId = this.getClientId(dbInbound.protocol, client);
|
||||||
if (confirmation){
|
if (confirmation){
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.deleteClient"}}',
|
title: '{{ i18n "pages.inbounds.deleteClient"}}' + ' ' + client.email,
|
||||||
content: '{{ i18n "pages.inbounds.deleteClientContent"}}',
|
content: '{{ i18n "pages.inbounds.deleteClientContent"}}',
|
||||||
class: themeSwitcher.currentTheme,
|
class: themeSwitcher.currentTheme,
|
||||||
okText: '{{ i18n "delete"}}',
|
okText: '{{ i18n "delete"}}',
|
||||||
@@ -978,7 +998,7 @@
|
|||||||
newDbInbound = new DBInbound(dbInbound);
|
newDbInbound = new DBInbound(dbInbound);
|
||||||
if (dbInbound.listen.startsWith("@")){
|
if (dbInbound.listen.startsWith("@")){
|
||||||
rootInbound = this.inbounds.find((i) =>
|
rootInbound = this.inbounds.find((i) =>
|
||||||
i.stream.isTls &&
|
i.isTcp &&
|
||||||
['trojan','vless'].includes(i.protocol) &&
|
['trojan','vless'].includes(i.protocol) &&
|
||||||
i.settings.fallbacks.find(f => f.dest === dbInbound.listen)
|
i.settings.fallbacks.find(f => f.dest === dbInbound.listen)
|
||||||
);
|
);
|
||||||
@@ -1012,6 +1032,7 @@
|
|||||||
},
|
},
|
||||||
switchEnable(dbInboundId) {
|
switchEnable(dbInboundId) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
|
dbInbound.enable = !dbInbound.enable;
|
||||||
this.submit(`/xui/inbound/update/${dbInboundId}`, dbInbound);
|
this.submit(`/xui/inbound/update/${dbInboundId}`, dbInbound);
|
||||||
},
|
},
|
||||||
async switchEnableClient(dbInboundId, client) {
|
async switchEnableClient(dbInboundId, client) {
|
||||||
@@ -1037,7 +1058,7 @@
|
|||||||
resetClientTraffic(client, dbInboundId, confirmation = true) {
|
resetClientTraffic(client, dbInboundId, confirmation = true) {
|
||||||
if (confirmation){
|
if (confirmation){
|
||||||
this.$confirm({
|
this.$confirm({
|
||||||
title: '{{ i18n "pages.inbounds.resetTraffic"}}',
|
title: '{{ i18n "pages.inbounds.resetTraffic"}}' + ' ' + client.email,
|
||||||
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
content: '{{ i18n "pages.inbounds.resetTrafficContent"}}',
|
||||||
class: themeSwitcher.currentTheme,
|
class: themeSwitcher.currentTheme,
|
||||||
okText: '{{ i18n "reset"}}',
|
okText: '{{ i18n "reset"}}',
|
||||||
@@ -1073,7 +1094,7 @@
|
|||||||
title: '{{ i18n "pages.inbounds.delDepletedClientsTitle"}}',
|
title: '{{ i18n "pages.inbounds.delDepletedClientsTitle"}}',
|
||||||
content: '{{ i18n "pages.inbounds.delDepletedClientsContent"}}',
|
content: '{{ i18n "pages.inbounds.delDepletedClientsContent"}}',
|
||||||
class: themeSwitcher.currentTheme,
|
class: themeSwitcher.currentTheme,
|
||||||
okText: '{{ i18n "reset"}}',
|
okText: '{{ i18n "delete"}}',
|
||||||
cancelText: '{{ i18n "cancel"}}',
|
cancelText: '{{ i18n "cancel"}}',
|
||||||
onOk: () => this.submit('/xui/inbound/delDepletedClients/' + dbInboundId),
|
onOk: () => this.submit('/xui/inbound/delDepletedClients/' + dbInboundId),
|
||||||
})
|
})
|
||||||
@@ -1159,15 +1180,31 @@
|
|||||||
inboundLinks(dbInboundId) {
|
inboundLinks(dbInboundId) {
|
||||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
newDbInbound = this.checkFallback(dbInbound);
|
newDbInbound = this.checkFallback(dbInbound);
|
||||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', newDbInbound.genInboundLinks, newDbInbound.remark);
|
txtModal.show('{{ i18n "pages.inbounds.export"}}', newDbInbound.genInboundLinks(this.remarkModel), newDbInbound.remark);
|
||||||
|
},
|
||||||
|
importInbound() {
|
||||||
|
promptModal.open({
|
||||||
|
title: '{{ i18n "pages.inbounds.importInbound" }}',
|
||||||
|
type: 'textarea',
|
||||||
|
value: '',
|
||||||
|
okText: '{{ i18n "pages.inbounds.import" }}',
|
||||||
|
confirm: async (dbInboundText) => {
|
||||||
|
await this.submit('/xui/inbound/import', {data: dbInboundText}, promptModal);
|
||||||
|
promptModal.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
exportAllLinks() {
|
exportAllLinks() {
|
||||||
let copyText = [];
|
let copyText = [];
|
||||||
for (const dbInbound of this.dbInbounds) {
|
for (const dbInbound of this.dbInbounds) {
|
||||||
copyText.push(dbInbound.genInboundLinks);
|
copyText.push(dbInbound.genInboundLinks(this.remarkModel));
|
||||||
}
|
}
|
||||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText.join('\r\n'), 'All-Inbounds');
|
txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText.join('\r\n'), 'All-Inbounds');
|
||||||
},
|
},
|
||||||
|
copyToClipboard(dbInboundId) {
|
||||||
|
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||||
|
txtModal.show('{{ i18n "pages.inbounds.inboundData" }}', JSON.stringify(dbInbound, null, 2));
|
||||||
|
},
|
||||||
async startDataRefreshLoop() {
|
async startDataRefreshLoop() {
|
||||||
while (this.isRefreshEnabled) {
|
while (this.isRefreshEnabled) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
<a-progress type="dashboard" status="normal"
|
<a-progress type="dashboard" status="normal"
|
||||||
:stroke-color="status.cpu.color"
|
:stroke-color="status.cpu.color"
|
||||||
:percent="status.cpu.percent"></a-progress>
|
:percent="status.cpu.percent"></a-progress>
|
||||||
<div>CPU: ([[ status.cpuCount ]]core)</div>
|
<div>CPU: [[ cpuCoreFormat(status.cpuCount) ]]</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12" style="text-align: center">
|
<a-col :span="12" style="text-align: center">
|
||||||
<a-progress type="dashboard" status="normal"
|
<a-progress type="dashboard" status="normal"
|
||||||
@@ -83,9 +83,12 @@
|
|||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
<a-card hoverable>
|
<a-card hoverable>
|
||||||
X-UI: <a href="https://github.com/alireza0/x-ui/releases" target="_blank"><a-tag color="blue">{{ .cur_ver }}</a-tag></a>
|
X-UI <a href="https://github.com/alireza0/x-ui/releases" target="_blank"><a-tag color="blue">{{ .cur_ver }}</a-tag></a>
|
||||||
Xray: <a-tag color="blue" style="cursor: pointer;" @click="openSelectV2rayVersion">[[ status.xray.version ]]</a-tag>
|
Xray
|
||||||
|
<a-tooltip title='{{ i18n "pages.index.xraySwitch" }}'>
|
||||||
|
<a-tag color="blue" style="cursor: pointer;" @click="openSelectV2rayVersion">[[ status.xray.version ]]</a-tag>
|
||||||
|
</a-tooltip>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
@@ -98,7 +101,7 @@
|
|||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.operationHoursDesc" }}
|
{{ i18n "pages.index.operationHoursDesc" }}
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tag color="blue">[[ formatSecond(status.uptime) ]]</a-tag>
|
<a-tag color="blue">[[ formatSecond(status.uptime) ]]</a-tag>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -107,15 +110,18 @@
|
|||||||
<a-card hoverable>
|
<a-card hoverable>
|
||||||
{{ i18n "pages.index.xrayStatus" }}:
|
{{ i18n "pages.index.xrayStatus" }}:
|
||||||
<a-tag :color="status.xray.color">[[ status.xray.state ]]</a-tag>
|
<a-tag :color="status.xray.color">[[ status.xray.state ]]</a-tag>
|
||||||
<a-tooltip v-if="status.xray.state === State.Error">
|
<a-popover v-if="status.xray.state === State.Error"
|
||||||
<template slot="title">
|
:overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<p v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</p>
|
<span slot="title" style="font-size: 12pt">An error occurred while running Xray
|
||||||
|
<a-tag color="purple" style="cursor: pointer; float: right;" @click="openLogs()">{{ i18n "pages.index.logs" }}</a-tag>
|
||||||
|
</span>
|
||||||
|
<template slot="content">
|
||||||
|
<p style="max-width: 400px" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</p>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-popover>
|
||||||
<a-tag color="purple" style="cursor: pointer;" @click="stopXrayService">{{ i18n "pages.index.stopXray" }}</a-tag>
|
<a-tag color="purple" style="cursor: pointer;" @click="stopXrayService">{{ i18n "pages.index.stopXray" }}</a-tag>
|
||||||
<a-tag color="purple" style="cursor: pointer;" @click="restartXrayService">{{ i18n "pages.index.restartXray" }}</a-tag>
|
<a-tag color="purple" style="cursor: pointer;" @click="restartXrayService">{{ i18n "pages.index.restartXray" }}</a-tag>
|
||||||
<a-tag color="purple" style="cursor: pointer;" @click="openSelectV2rayVersion">{{ i18n "pages.index.xraySwitch" }}</a-tag>
|
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
@@ -134,7 +140,7 @@
|
|||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
<a-card hoverable>
|
<a-card hoverable>
|
||||||
{{ i18n "usage"}}:
|
{{ i18n "usage"}}:
|
||||||
Memory: [[ sizeFormat(status.appStats.mem) ]] -
|
RAM: [[ sizeFormat(status.appStats.mem) ]] -
|
||||||
Threads: [[ status.appStats.threads ]]
|
Threads: [[ status.appStats.threads ]]
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -147,7 +153,7 @@
|
|||||||
<template slot="title">
|
<template slot="title">
|
||||||
[[ status.hostInfo.ipv4 ]]
|
[[ status.hostInfo.ipv4 ]]
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="status.hostInfo.ipv6">IPv6:
|
<template v-if="status.hostInfo.ipv6">IPv6:
|
||||||
@@ -155,20 +161,33 @@
|
|||||||
<template slot="title">
|
<template slot="title">
|
||||||
[[ status.hostInfo.ipv6 ]]
|
[[ status.hostInfo.ipv6 ]]
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
<a-card hoverable>
|
<a-card hoverable>
|
||||||
{{ i18n "pages.index.connectionCount" }}: TCP: [[ status.tcpCount ]] UDP: [[ status.udpCount ]]
|
<a-row>
|
||||||
<a-tooltip>
|
<a-col :span="12">
|
||||||
<template slot="title">
|
TCP: [[ status.tcpCount ]]
|
||||||
{{ i18n "pages.index.connectionCountDesc" }}
|
<a-tooltip>
|
||||||
</template>
|
<template slot="title">
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
{{ i18n "pages.index.connectionTcpCountDesc" }}
|
||||||
</a-tooltip>
|
</template>
|
||||||
|
<a-icon type="question-circle"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
UDP: [[ status.udpCount ]]
|
||||||
|
<a-tooltip>
|
||||||
|
<template slot="title">
|
||||||
|
{{ i18n "pages.index.connectionUdpCountDesc" }}
|
||||||
|
</template>
|
||||||
|
<a-icon type="question-circle"></a-icon>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
@@ -176,22 +195,22 @@
|
|||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-icon type="arrow-up"></a-icon>
|
<a-icon type="arrow-up"></a-icon>
|
||||||
[[ sizeFormat(status.netIO.up) ]] / S
|
[[ sizeFormat(status.netIO.up) ]]/s
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.upSpeed" }}
|
{{ i18n "pages.index.upSpeed" }}
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-icon type="arrow-down"></a-icon>
|
<a-icon type="arrow-down"></a-icon>
|
||||||
[[ sizeFormat(status.netIO.down) ]] / S
|
[[ sizeFormat(status.netIO.down) ]]/s
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.downSpeed" }}
|
{{ i18n "pages.index.downSpeed" }}
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -207,7 +226,7 @@
|
|||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.totalSent" }}
|
{{ i18n "pages.index.totalSent" }}
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
@@ -217,7 +236,7 @@
|
|||||||
<template slot="title">
|
<template slot="title">
|
||||||
{{ i18n "pages.index.totalReceive" }}
|
{{ i18n "pages.index.totalReceive" }}
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle" theme="filled"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -232,8 +251,10 @@
|
|||||||
:closable="true" @ok="() => versionModal.visible = false"
|
:closable="true" @ok="() => versionModal.visible = false"
|
||||||
:class="themeSwitcher.currentTheme"
|
:class="themeSwitcher.currentTheme"
|
||||||
footer="">
|
footer="">
|
||||||
<h2>{{ i18n "pages.index.xraySwitchClick"}}</h2>
|
<a-alert type="warning" style="margin-bottom: 12px; width: fit-content"
|
||||||
<h2>{{ i18n "pages.index.xraySwitchClickDesk"}}</h2>
|
message='{{ i18n "pages.index.xraySwitchClickDesk" }}'
|
||||||
|
show-icon
|
||||||
|
></a-alert>
|
||||||
<template v-for="version, index in versionModal.versions">
|
<template v-for="version, index in versionModal.versions">
|
||||||
<a-tag :color="index % 2 == 0 ? 'purple' : 'blue'"
|
<a-tag :color="index % 2 == 0 ? 'purple' : 'blue'"
|
||||||
style="margin: 10px" @click="switchV2rayVersion(version)">
|
style="margin: 10px" @click="switchV2rayVersion(version)">
|
||||||
@@ -242,7 +263,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<a-modal id="log-modal" v-model="logModal.visible" title="X-UI logs"
|
<a-modal id="log-modal" v-model="logModal.visible" title="Logs"
|
||||||
:closable="true" @ok="() => logModal.visible = false" @cancel="() => logModal.visible = false"
|
:closable="true" @ok="() => logModal.visible = false" @cancel="() => logModal.visible = false"
|
||||||
:class="themeSwitcher.currentTheme"
|
:class="themeSwitcher.currentTheme"
|
||||||
width="800px"
|
width="800px"
|
||||||
@@ -272,7 +293,7 @@
|
|||||||
<a-checkbox v-model="logModal.syslog" @change="openLogs()"></a-checkbox>
|
<a-checkbox v-model="logModal.syslog" @change="openLogs()"></a-checkbox>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<button class="ant-btn ant-btn-primary" @click="openLogs()"><a-icon type="sync"></a-icon> Reload</button>
|
<a-button class="ant-btn ant-btn-primary" :loading="logModal.loading" @click="openLogs()"><a-icon :spin="logModal.loading" type="sync"></a-icon> Reload</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-button type="primary" style="margin-bottom: 10px;"
|
<a-button type="primary" style="margin-bottom: 10px;"
|
||||||
@@ -281,7 +302,7 @@
|
|||||||
</a-button>
|
</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
<div v-model="logModal.logs" class="ant-input" style="height: 400px; overflow: scroll;"><pre>[[ logModal.logs ]]</pre></div>
|
<div class="ant-input" style="height: auto; max-height: 500px; overflow: auto;" v-html="logModal.logs"></div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<a-modal id="backup-modal" v-model="backupModal.visible" :title="backupModal.title"
|
<a-modal id="backup-modal" v-model="backupModal.visible" :title="backupModal.title"
|
||||||
@@ -411,9 +432,46 @@
|
|||||||
rows: 20,
|
rows: 20,
|
||||||
level: 'info',
|
level: 'info',
|
||||||
syslog: false,
|
syslog: false,
|
||||||
|
loading: false,
|
||||||
show(logs) {
|
show(logs) {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.logs = logs? logs.join("\n"): "No Record...";
|
this.logs = logs? this.formatLogs(logs) : "No Record...";
|
||||||
|
},
|
||||||
|
formatLogs(logs) {
|
||||||
|
let formattedLogs = '';
|
||||||
|
const levels = ["DEBUG","INFO","WARNING","ERROR"];
|
||||||
|
const levelColors = ["#3c89e8","#008771","#f37b24","#e04141","#bcbcbc"];
|
||||||
|
|
||||||
|
logs.forEach((log, index) => {
|
||||||
|
let [data, message] = log.split(" - ",2);
|
||||||
|
const parts = data.split(" ")
|
||||||
|
if(index>0) formattedLogs += '<br>';
|
||||||
|
|
||||||
|
if (parts.length === 3) {
|
||||||
|
const d = parts[0];
|
||||||
|
const t = parts[1];
|
||||||
|
const level = parts[2];
|
||||||
|
const levelIndex = levels.indexOf(level,levels) || 4;
|
||||||
|
|
||||||
|
//formattedLogs += `<span style="color: gray;">${index + 1}.</span>`;
|
||||||
|
formattedLogs += `<span style="color: ${levelColors[0]};">${d} ${t}</span> `;
|
||||||
|
formattedLogs += `<span style="color: ${levelColors[levelIndex]}">${level}</span>`;
|
||||||
|
} else {
|
||||||
|
const levelIndex = levels.indexOf(data,levels) || 4;
|
||||||
|
formattedLogs += `<span style="color: ${levelColors[levelIndex]}">${data}</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message){
|
||||||
|
if(message.startsWith("XRAY:"))
|
||||||
|
message = "<b>XRAY: </b>" + message.substring(5);
|
||||||
|
else
|
||||||
|
message = "<b>X-UI: </b>" + message;
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedLogs += message ? ' - ' + message : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
return formattedLogs;
|
||||||
},
|
},
|
||||||
hide() {
|
hide() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
@@ -512,13 +570,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async openLogs(){
|
async openLogs(){
|
||||||
this.loading(true);
|
logModal.loading = true;
|
||||||
const msg = await HttpUtil.post('server/logs/'+logModal.rows,{level: logModal.level, syslog: logModal.syslog});
|
const msg = await HttpUtil.post('server/logs/'+logModal.rows,{level: logModal.level, syslog: logModal.syslog});
|
||||||
this.loading(false);
|
|
||||||
if (!msg.success) {
|
if (!msg.success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logModal.show(msg.obj);
|
logModal.show(msg.obj);
|
||||||
|
await PromiseUtil.sleep(500);
|
||||||
|
logModal.loading = false;
|
||||||
},
|
},
|
||||||
async openConfig() {
|
async openConfig() {
|
||||||
this.loading(true);
|
this.loading(true);
|
||||||
|
|||||||
@@ -77,6 +77,28 @@
|
|||||||
<a-tabs default-active-key="1">
|
<a-tabs default-active-key="1">
|
||||||
<a-tab-pane key="1" tab='{{ i18n "pages.settings.panelConfig"}}'>
|
<a-tab-pane key="1" tab='{{ i18n "pages.settings.panelConfig"}}'>
|
||||||
<a-list item-layout="horizontal">
|
<a-list item-layout="horizontal">
|
||||||
|
<a-list-item>
|
||||||
|
<a-row style="padding: 20px">
|
||||||
|
<a-col :lg="24" :xl="12">
|
||||||
|
<a-list-item-meta title='{{ i18n "pages.settings.remarkModel"}}'>
|
||||||
|
<template slot="description">{{ i18n "pages.settings.sampleRemark"}}: <i>#[[ remarkSample ]]</i></template>
|
||||||
|
</a-list-item-meta>
|
||||||
|
</a-col>
|
||||||
|
<a-col :lg="24" :xl="12">
|
||||||
|
<a-input-group style="width: 100%;">
|
||||||
|
<a-select style="padding-right: .5rem; min-width: 80%; width: auto;"
|
||||||
|
mode="multiple"
|
||||||
|
v-model="remarkModel"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="(value, key) in remarkModels" :value="key">[[ value ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-select style="width: 20%;" v-model="remarkSeparator" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="key in remarkSeparators" :value="key">[[ key ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-input-group>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-list-item>
|
||||||
<setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningIP"}}' desc='{{ i18n "pages.settings.panelListeningIPDesc"}}' v-model="allSetting.webListen"></setting-list-item>
|
<setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningIP"}}' desc='{{ i18n "pages.settings.panelListeningIPDesc"}}' v-model="allSetting.webListen"></setting-list-item>
|
||||||
<setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningDomain"}}' desc='{{ i18n "pages.settings.panelListeningDomainDesc"}}' v-model="allSetting.webDomain"></setting-list-item>
|
<setting-list-item type="text" title='{{ i18n "pages.settings.panelListeningDomain"}}' desc='{{ i18n "pages.settings.panelListeningDomainDesc"}}' v-model="allSetting.webDomain"></setting-list-item>
|
||||||
<setting-list-item type="number" title='{{ i18n "pages.settings.panelPort"}}' desc='{{ i18n "pages.settings.panelPortDesc"}}' v-model.number="allSetting.webPort"></setting-list-item>
|
<setting-list-item type="number" title='{{ i18n "pages.settings.panelPort"}}' desc='{{ i18n "pages.settings.panelPortDesc"}}' v-model.number="allSetting.webPort"></setting-list-item>
|
||||||
@@ -93,7 +115,6 @@
|
|||||||
<a-col :lg="24" :xl="12">
|
<a-col :lg="24" :xl="12">
|
||||||
<a-list-item-meta title="Language"/>
|
<a-list-item-meta title="Language"/>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
|
||||||
<a-col :lg="24" :xl="12">
|
<a-col :lg="24" :xl="12">
|
||||||
<template>
|
<template>
|
||||||
<a-select
|
<a-select
|
||||||
@@ -113,20 +134,20 @@
|
|||||||
</a-list>
|
</a-list>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="2" tab='{{ i18n "pages.settings.userSettings"}}'>
|
<a-tab-pane key="2" tab='{{ i18n "pages.settings.userSettings"}}'>
|
||||||
<a-form style="padding: 20px;">
|
<a-form layout="horizontal" :colon="false" style="float: left; margin: 10px 0;" :label-col="{ md: {span:10} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label='{{ i18n "pages.settings.oldUsername"}}'>
|
<a-form-item label='{{ i18n "pages.settings.oldUsername"}}'>
|
||||||
<a-input v-model="user.oldUsername" style="max-width: 300px"></a-input>
|
<a-input v-model="user.oldUsername"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.settings.currentPassword"}}'>
|
<a-form-item label='{{ i18n "pages.settings.currentPassword"}}'>
|
||||||
<password-input v-model="user.oldPassword" style="max-width: 300px"></password-input>
|
<password-input v-model="user.oldPassword"></password-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.settings.newUsername"}}'>
|
<a-form-item label='{{ i18n "pages.settings.newUsername"}}'>
|
||||||
<a-input v-model="user.newUsername" style="max-width: 300px"></a-input>
|
<a-input v-model="user.newUsername"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.settings.newPassword"}}'>
|
<a-form-item label='{{ i18n "pages.settings.newPassword"}}'>
|
||||||
<password-input v-model="user.newPassword" style="max-width: 300px"></password-input>
|
<password-input v-model="user.newPassword"></password-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item label=" ">
|
||||||
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
@@ -152,8 +173,7 @@
|
|||||||
ref="selectBotLang"
|
ref="selectBotLang"
|
||||||
v-model="allSetting.tgLang"
|
v-model="allSetting.tgLang"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
>
|
|
||||||
<a-select-option :value="l.value" :label="l.value" v-for="l in supportLangs">
|
<a-select-option :value="l.value" :label="l.value" v-for="l in supportLangs">
|
||||||
<span role="img" aria-label="l.name" v-text="l.icon"></span>
|
<span role="img" aria-label="l.name" v-text="l.icon"></span>
|
||||||
<span v-text="l.name"></span>
|
<span v-text="l.name"></span>
|
||||||
@@ -205,7 +225,31 @@
|
|||||||
saveBtnDisable: true,
|
saveBtnDisable: true,
|
||||||
user: {},
|
user: {},
|
||||||
lang: getLang(),
|
lang: getLang(),
|
||||||
showAlert: false
|
showAlert: false,
|
||||||
|
remarkModels: {i:'Inbound',e:'Email',o:'Other'},
|
||||||
|
remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'],
|
||||||
|
remarkSample: '',
|
||||||
|
get remarkModel() {
|
||||||
|
rm = this.allSetting.remarkModel;
|
||||||
|
return rm.length>1 ? rm.substring(1).split('') : [];
|
||||||
|
},
|
||||||
|
set remarkModel(value) {
|
||||||
|
rs = this.allSetting.remarkModel[0];
|
||||||
|
this.allSetting.remarkModel = rs + value.join('');
|
||||||
|
this.changeRemarkSample();
|
||||||
|
},
|
||||||
|
get remarkSeparator() {
|
||||||
|
return this.allSetting.remarkModel.length > 1 ? this.allSetting.remarkModel.charAt(0) : '-';
|
||||||
|
},
|
||||||
|
set remarkSeparator(value) {
|
||||||
|
this.allSetting.remarkModel = value + this.allSetting.remarkModel.substring(1);
|
||||||
|
this.changeRemarkSample();
|
||||||
|
},
|
||||||
|
changeRemarkSample(){
|
||||||
|
sample = []
|
||||||
|
this.remarkModel.forEach(r => sample.push(this.remarkModels[r]));
|
||||||
|
this.remarkSample = sample.length == 0 ? '' : sample.join(this.remarkSeparator);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loading(spinning = true) {
|
loading(spinning = true) {
|
||||||
@@ -218,6 +262,7 @@
|
|||||||
if (msg.success) {
|
if (msg.success) {
|
||||||
this.oldAllSetting = new AllSetting(msg.obj);
|
this.oldAllSetting = new AllSetting(msg.obj);
|
||||||
this.allSetting = new AllSetting(msg.obj);
|
this.allSetting = new AllSetting(msg.obj);
|
||||||
|
app.changeRemarkSample();
|
||||||
this.saveBtnDisable = true;
|
this.saveBtnDisable = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
204
web/html/xui/warp_modal.html
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
{{define "warpModal"}}
|
||||||
|
<a-modal id="warp-modal" v-model="warpModal.visible" title="Cloudflare WARP"
|
||||||
|
:confirm-loading="warpModal.confirmLoading" :closable="true" :mask-closable="true"
|
||||||
|
:footer="null" :class="themeSwitcher.currentTheme">
|
||||||
|
<template v-if="ObjectUtil.isEmpty(warpModal.warpData)">
|
||||||
|
<a-button icon="api" @click="register" :loading="warpModal.confirmLoading">{{ i18n "pages.inbounds.create" }}</a-button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<table style="margin: 5px 0; width: 100%;">
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>Access Token</td>
|
||||||
|
<td>[[ warpModal.warpData.access_token ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Devide ID</td>
|
||||||
|
<td>[[ warpModal.warpData.device_id ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>License Key</td>
|
||||||
|
<td>[[ warpModal.warpData.license_key ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Private Key</td>
|
||||||
|
<td>[[ warpModal.warpData.private_key ]]</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<a-divider style="margin: 0;">{{ i18n "pages.settings.toasts.modifySettings" }}</a-divider>
|
||||||
|
<a-collapse style="margin: 10px 0;">
|
||||||
|
<a-collapse-panel header='WARP/WARP+ License Key'>
|
||||||
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
|
<a-form-item label="License Key">
|
||||||
|
<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-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
<a-divider style="margin: 0;">{{ i18n "pages.settings.toasts.getSettings" }}</a-divider>
|
||||||
|
<a-button icon="sync" @click="getConfig" style="margin-bottom: 10px;" :loading="warpModal.confirmLoading">{{ i18n "info" }}</a-button>
|
||||||
|
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig)">
|
||||||
|
<table style="width: 100%">
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>Device Name</td>
|
||||||
|
<td>[[ warpModal.warpConfig.name ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Device Model</td>
|
||||||
|
<td>[[ warpModal.warpConfig.model ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>Device Active</td>
|
||||||
|
<td>[[ warpModal.warpConfig.enabled ]]</td>
|
||||||
|
</tr>
|
||||||
|
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account)">
|
||||||
|
<tr>
|
||||||
|
<td>Account Type</td>
|
||||||
|
<td>[[ warpModal.warpConfig.account.account_type ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>Role</td>
|
||||||
|
<td>[[ warpModal.warpConfig.account.role ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Premium Data</td>
|
||||||
|
<td>[[ sizeFormat(warpModal.warpConfig.account.premium_data) ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="client-table-odd-row">
|
||||||
|
<td>Quota</td>
|
||||||
|
<td>[[ sizeFormat(warpModal.warpConfig.account.quota) ]]</td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="!ObjectUtil.isEmpty(warpModal.warpConfig.account.usage)">
|
||||||
|
<td>Usage</td>
|
||||||
|
<td>[[ sizeFormat(warpModal.warpConfig.account.usage) ]]</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</table>
|
||||||
|
<a-divider style="margin: 10px 0;">WARP {{ i18n "pages.xray.rules.outbound" }}</a-divider>
|
||||||
|
<a-form :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
|
<a-form-item label="{{ i18n "status" }}">
|
||||||
|
<template v-if="warpOutboundIndex>=0">
|
||||||
|
<a-tag color="green">{{ i18n "enabled" }}</a-tag>
|
||||||
|
<a-button @click="resetOutbound" :loading="warpModal.confirmLoading">{{ i18n "reset" }}</a-button>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-tag color="orange">{{ i18n "disabled" }}</a-tag>
|
||||||
|
<a-button @click="addOutbound" :loading="warpModal.confirmLoading">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||||
|
</template>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const warpModal = {
|
||||||
|
visible: false,
|
||||||
|
confirmLoading: false,
|
||||||
|
warpData: null,
|
||||||
|
warpConfig: null,
|
||||||
|
warpOutbound: null,
|
||||||
|
show() {
|
||||||
|
this.visible = true;
|
||||||
|
this.warpConfig = null;
|
||||||
|
this.getData();
|
||||||
|
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.visible = false;
|
||||||
|
this.loading(false);
|
||||||
|
},
|
||||||
|
loading(loading) {
|
||||||
|
this.confirmLoading = loading;
|
||||||
|
},
|
||||||
|
async getData(){
|
||||||
|
this.loading(true);
|
||||||
|
const msg = await HttpUtil.post('/xui/xray/warp/data');
|
||||||
|
this.loading(false);
|
||||||
|
if (msg.success) {
|
||||||
|
this.warpData = msg.obj.length>0 ? JSON.parse(msg.obj): null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
delimiters: ['[[', ']]'],
|
||||||
|
el: '#warp-modal',
|
||||||
|
data: {
|
||||||
|
warpModal: warpModal,
|
||||||
|
warpPlus: '',
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
collectConfig() {
|
||||||
|
config = warpModal.warpConfig.config;
|
||||||
|
peer = config.peers[0];
|
||||||
|
if(config){
|
||||||
|
warpModal.warpOutbound = Outbound.fromJson({
|
||||||
|
tag: 'warp',
|
||||||
|
protocol: Protocols.Wireguard,
|
||||||
|
settings: {
|
||||||
|
mtu: 1420,
|
||||||
|
secretKey: warpModal.warpData.private_key,
|
||||||
|
address: Object.values(config.interface.addresses),
|
||||||
|
peers: [{
|
||||||
|
publicKey: peer.public_key,
|
||||||
|
endpoint: peer.endpoint.host,
|
||||||
|
}],
|
||||||
|
kernelMode: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async register(){
|
||||||
|
warpModal.loading(true);
|
||||||
|
keys = Wireguard.generateKeypair();
|
||||||
|
const msg = await HttpUtil.post('/xui/xray/warp/reg',keys);
|
||||||
|
if (msg.success) {
|
||||||
|
resp = JSON.parse(msg.obj);
|
||||||
|
warpModal.warpData = resp.data;
|
||||||
|
warpModal.warpConfig = resp.config;
|
||||||
|
this.collectConfig();
|
||||||
|
}
|
||||||
|
warpModal.loading(false);
|
||||||
|
},
|
||||||
|
async updateLicense(l){
|
||||||
|
warpModal.loading(true);
|
||||||
|
const msg = await HttpUtil.post('/xui/xray/warp/license',{license: l});
|
||||||
|
if (msg.success) {
|
||||||
|
warpModal.warpData = JSON.parse(msg.obj);
|
||||||
|
warpModal.warpConfig = null;
|
||||||
|
this.warpPlus = '';
|
||||||
|
}
|
||||||
|
warpModal.loading(false);
|
||||||
|
},
|
||||||
|
async getConfig(){
|
||||||
|
warpModal.loading(true);
|
||||||
|
const msg = await HttpUtil.post('/xui/xray/warp/config');
|
||||||
|
warpModal.loading(false);
|
||||||
|
if (msg.success) {
|
||||||
|
warpModal.warpConfig = JSON.parse(msg.obj);
|
||||||
|
this.collectConfig();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addOutbound(){
|
||||||
|
app.templateSettings.outbounds.push(warpModal.warpOutbound.toJson());
|
||||||
|
app.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
|
||||||
|
warpModal.close();
|
||||||
|
},
|
||||||
|
resetOutbound(){
|
||||||
|
app.templateSettings.outbounds[this.warpOutboundIndex] = warpModal.warpOutbound.toJson();
|
||||||
|
app.outboundSettings = JSON.stringify(app.templateSettings.outbounds);
|
||||||
|
warpModal.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
warpOutboundIndex: {
|
||||||
|
get: function() {
|
||||||
|
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag == 'warp') : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{{end}}
|
||||||
@@ -3,10 +3,11 @@
|
|||||||
{{template "head" .}}
|
{{template "head" .}}
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/codemirror.css">
|
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/codemirror.css">
|
||||||
<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">
|
<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/js/model/outbound.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/codemirror/codemirror.js"></script>
|
<script src="{{ .base_path }}assets/codemirror/codemirror.js"></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>
|
||||||
@@ -29,6 +30,10 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 12px .5rem;
|
padding: 12px .5rem;
|
||||||
}
|
}
|
||||||
|
.ant-table-thead > tr > th,
|
||||||
|
.ant-table-tbody > tr > td {
|
||||||
|
padding: 10px 0px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-tabs-bar {
|
.ant-tabs-bar {
|
||||||
@@ -74,6 +79,14 @@
|
|||||||
<a-space direction="horizontal">
|
<a-space direction="horizontal">
|
||||||
<a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button>
|
<a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button>
|
||||||
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button>
|
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button>
|
||||||
|
<a-popover v-if="restartResult"
|
||||||
|
:overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<span slot="title" style="font-size: 12pt">Error in running xray-core</span>
|
||||||
|
<template slot="content">
|
||||||
|
<p style="max-width: 400px" v-for="line in restartResult.split('\n')">[[ line ]]</p>
|
||||||
|
</template>
|
||||||
|
<a-icon type="question-circle"></a-icon>
|
||||||
|
</a-popover>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :sm="16">
|
<a-col :xs="24" :sm="16">
|
||||||
@@ -90,7 +103,7 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-card>
|
</a-card>
|
||||||
<a-tabs default-active-key="1" @change="(activeKey) => { if(activeKey == 'tpl-4') this.changeCode(); }">
|
<a-tabs default-active-key="1" @change="(activeKey) => { if(activeKey == 'tpl-advanced') this.changeCode(); }">
|
||||||
<a-tab-pane key="tpl-1" tab='{{ i18n "pages.xray.basicTemplate"}}' style="padding-top: 20px;">
|
<a-tab-pane key="tpl-1" tab='{{ i18n "pages.xray.basicTemplate"}}' style="padding-top: 20px;">
|
||||||
<a-collapse>
|
<a-collapse>
|
||||||
<a-collapse-panel header='{{ i18n "pages.xray.generalConfigs"}}'>
|
<a-collapse-panel header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||||
@@ -114,7 +127,7 @@
|
|||||||
<a-select
|
<a-select
|
||||||
v-model="freedomStrategy"
|
v-model="freedomStrategy"
|
||||||
style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme">
|
style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="s in outboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
|
<a-select-option v-for="s in OutboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</template>
|
</template>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -143,7 +156,7 @@
|
|||||||
<a-alert type="warning" style="text-align: center;">
|
<a-alert type="warning" style="text-align: center;">
|
||||||
<template slot="message">
|
<template slot="message">
|
||||||
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
||||||
{{ i18n "pages.xray.generalConfigsDesc" }}
|
{{ i18n "pages.xray.blockConfigsDesc" }}
|
||||||
</template>
|
</template>
|
||||||
</a-alert>
|
</a-alert>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -196,6 +209,23 @@
|
|||||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.GoogleIPv4"}}' desc='{{ i18n "pages.xray.GoogleIPv4Desc"}}' v-model="GoogleIPv4Settings"></setting-list-item>
|
<setting-list-item type="switch" title='{{ i18n "pages.xray.GoogleIPv4"}}' desc='{{ i18n "pages.xray.GoogleIPv4Desc"}}' v-model="GoogleIPv4Settings"></setting-list-item>
|
||||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.NetflixIPv4"}}' desc='{{ i18n "pages.xray.NetflixIPv4Desc"}}' v-model="NetflixIPv4Settings"></setting-list-item>
|
<setting-list-item type="switch" title='{{ i18n "pages.xray.NetflixIPv4"}}' desc='{{ i18n "pages.xray.NetflixIPv4Desc"}}' v-model="NetflixIPv4Settings"></setting-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel header='{{ i18n "pages.xray.warpConfigs"}}'>
|
||||||
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
|
<a-alert type="warning" style="text-align: center;">
|
||||||
|
<template slot="message">
|
||||||
|
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
||||||
|
{{ i18n "pages.xray.warpConfigsDesc" }}
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</a-row>
|
||||||
|
<template v-if="WarpExist">
|
||||||
|
<setting-list-item type="switch" title='{{ i18n "pages.xray.GoogleWARP"}}' desc='{{ i18n "pages.xray.GoogleWARPDesc"}}' v-model="GoogleWARPSettings"></setting-list-item>
|
||||||
|
<setting-list-item type="switch" title='{{ i18n "pages.xray.OpenAIWARP"}}' desc='{{ i18n "pages.xray.OpenAIWARPDesc"}}' v-model="OpenAIWARPSettings"></setting-list-item>
|
||||||
|
<setting-list-item type="switch" title='{{ i18n "pages.xray.NetflixWARP"}}' desc='{{ i18n "pages.xray.NetflixWARPDesc"}}' v-model="NetflixWARPSettings"></setting-list-item>
|
||||||
|
<setting-list-item type="switch" title='{{ i18n "pages.xray.SpotifyWARP"}}' desc='{{ i18n "pages.xray.SpotifyWARPDesc"}}' v-model="SpotifyWARPSettings"></setting-list-item>
|
||||||
|
</template>
|
||||||
|
<a-button v-else style="margin: 10px 0;" @click="showWarp">WARP {{ i18n "pages.xray.rules.outbound" }}</a-button>
|
||||||
|
</a-collapse-panel>
|
||||||
<a-collapse-panel header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
|
<a-collapse-panel header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
|
||||||
<a-space direction="horizontal" style="padding: 0 20px">
|
<a-space direction="horizontal" style="padding: 0 20px">
|
||||||
<a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
|
<a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
|
||||||
@@ -312,79 +342,74 @@
|
|||||||
</a-table>
|
</a-table>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-3" tab='{{ i18n "pages.xray.Outbounds"}}' style="padding-top: 20px;" force-render="true">
|
<a-tab-pane key="tpl-3" tab='{{ i18n "pages.xray.Outbounds"}}' style="padding-top: 20px;" force-render="true">
|
||||||
<a-button type="primary" icon="plus" @click="addOutbound()">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
<a-button type="primary" icon="plus" @click="addOutbound()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||||
<a-button type="primary" icon="plus" @click="addReverse()">{{ i18n "pages.xray.outbound.addReverse" }}</a-button>
|
<a-button type="primary" @click="showWarp()" style="margin-bottom: 10px;">WARP</a-button>
|
||||||
<a-row>
|
<a-table :columns="outboundColumns" bordered
|
||||||
<a-col :sm="24" :md="12">
|
:row-key="r => r.key"
|
||||||
<p style="margin: 10px;">{{ i18n "pages.xray.Outbounds"}}</p>
|
:data-source="outboundData"
|
||||||
<a-table :columns="outboundColumns" bordered
|
:scroll="isMobile ? {} : { x: 200 }"
|
||||||
:row-key="r => r.key"
|
:pagination="false"
|
||||||
:data-source="outboundData"
|
:indent-size="0"
|
||||||
:scroll="isMobile ? {} : { x: 200 }"
|
:style="isMobile ? 'padding: 5px 5px' : 'margin-right: 1px;'">
|
||||||
:pagination="false"
|
<template slot="action" slot-scope="text, outbound, index">
|
||||||
:indent-size="0"
|
[[ index+1 ]]
|
||||||
:style="isMobile ? 'padding: 5px 5px' : 'margin-right: 1px;'">
|
<a-dropdown :trigger="['click']">
|
||||||
<template slot="action" slot-scope="text, outbound, index">
|
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
|
||||||
[[ index+1 ]]
|
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||||
<a-dropdown :trigger="['click']">
|
<a-menu-item @click="editOutbound(index)">
|
||||||
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
|
<a-icon type="edit"></a-icon>
|
||||||
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
{{ i18n "edit" }}
|
||||||
<a-menu-item @click="editOutbound(index)">
|
</a-menu-item>
|
||||||
<a-icon type="edit"></a-icon>
|
<a-menu-item @click="deleteOutbound(index)">
|
||||||
{{ i18n "edit" }}
|
<span style="color: #FF4D4F">
|
||||||
</a-menu-item>
|
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||||
<a-menu-item @click="deleteOutbound(index)">
|
</span>
|
||||||
<span style="color: #FF4D4F">
|
</a-menu-item>
|
||||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
</a-menu>
|
||||||
</span>
|
</a-dropdown>
|
||||||
</a-menu-item>
|
</template>
|
||||||
</a-menu>
|
<template slot="address" slot-scope="text, outbound, index">
|
||||||
</a-dropdown>
|
<p style="margin: 0 5px;" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
|
||||||
</template>
|
</template>
|
||||||
<template slot="address" slot-scope="text, outbound, index">
|
<template slot="protocol" slot-scope="text, outbound, index">
|
||||||
<p style="margin: 0 5px;" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
|
<a-tag style="margin:0;" color="purple">[[ outbound.protocol ]]</a-tag>
|
||||||
</template>
|
<template v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
|
||||||
<template slot="protocol" slot-scope="text, outbound, index">
|
<a-tag style="margin:0;" color="blue">[[ outbound.streamSettings.network ]]</a-tag>
|
||||||
<a-tag style="margin:0;" color="purple">[[ outbound.protocol ]]</a-tag>
|
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
|
||||||
<template v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
|
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='reality'" color="green">reality</a-tag>
|
||||||
<a-tag style="margin:0;" color="blue">[[ outbound.streamSettings.network ]]</a-tag>
|
</template>
|
||||||
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
|
</template>
|
||||||
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='reality'" color="green">reality</a-tag>
|
</a-table>
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
</a-col>
|
|
||||||
<a-col :sm="24" :md="12" v-if="reverseData.length>0">
|
|
||||||
<p style="margin: 10px;">{{ i18n "pages.xray.outbound.reverse"}}</p>
|
|
||||||
<a-table :columns="reverseColumns" bordered
|
|
||||||
:row-key="r => r.key"
|
|
||||||
:data-source="reverseData"
|
|
||||||
:scroll="isMobile ? {} : { x: 200 }"
|
|
||||||
:pagination="false"
|
|
||||||
:indent-size="0"
|
|
||||||
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
|
|
||||||
<template slot="action" slot-scope="text, reverse, index">
|
|
||||||
[[ index+1 ]]
|
|
||||||
<a-dropdown :trigger="['click']">
|
|
||||||
<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-item @click="editReverse(index)">
|
|
||||||
<a-icon type="edit"></a-icon>
|
|
||||||
{{ i18n "edit" }}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item @click="deleteReverse(index)">
|
|
||||||
<span style="color: #FF4D4F">
|
|
||||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
|
||||||
</span>
|
|
||||||
</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</a-dropdown>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-4" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;" force-render="true">
|
<a-tab-pane key="tpl-4" tab='{{ i18n "pages.xray.outbound.reverse"}}' style="padding-top: 20px;" force-render="true">
|
||||||
|
<a-button type="primary" icon="plus" @click="addReverse()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addReverse" }}</a-button>
|
||||||
|
<a-table :columns="reverseColumns" bordered
|
||||||
|
:row-key="r => r.key"
|
||||||
|
:data-source="reverseData"
|
||||||
|
:scroll="isMobile ? {} : { x: 200 }"
|
||||||
|
:pagination="false"
|
||||||
|
:indent-size="0"
|
||||||
|
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
|
||||||
|
<template slot="action" slot-scope="text, reverse, index">
|
||||||
|
[[ index+1 ]]
|
||||||
|
<a-dropdown :trigger="['click']">
|
||||||
|
<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-item @click="editReverse(index)">
|
||||||
|
<a-icon type="edit"></a-icon>
|
||||||
|
{{ i18n "edit" }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="deleteReverse(index)">
|
||||||
|
<span style="color: #FF4D4F">
|
||||||
|
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||||
|
</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;" force-render="true">
|
||||||
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}' description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
|
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}' description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
|
||||||
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;" :size="isMobile ? 'small' : ''">
|
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;" :size="isMobile ? 'small' : ''">
|
||||||
<a-radio-button value="xraySetting">{{ i18n "pages.xray.completeTemplate"}}</a-radio-button>
|
<a-radio-button value="xraySetting">{{ i18n "pages.xray.completeTemplate"}}</a-radio-button>
|
||||||
@@ -406,12 +431,13 @@
|
|||||||
{{template "ruleModal"}}
|
{{template "ruleModal"}}
|
||||||
{{template "outModal"}}
|
{{template "outModal"}}
|
||||||
{{template "reverseModal"}}
|
{{template "reverseModal"}}
|
||||||
|
{{template "warpModal"}}
|
||||||
<script>
|
<script>
|
||||||
const rulesColumns = [
|
const rulesColumns = [
|
||||||
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
|
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
|
||||||
{ title: '{{ i18n "pages.xray.rules.source"}}', children: [
|
{ title: '{{ i18n "pages.xray.rules.source"}}', children: [
|
||||||
{ title: 'IP', dataIndex: "source", align: 'center', width: 20, ellipsis: true },
|
{ title: 'IP', dataIndex: "source", align: 'center', width: 20, ellipsis: true },
|
||||||
{ title: 'port', dataIndex: 'sourcePort', align: 'center', width: 10, ellipsis: true } ]},
|
{ title: 'Port', dataIndex: 'sourcePort', align: 'center', width: 10, ellipsis: true } ]},
|
||||||
{ title: '{{ i18n "pages.inbounds.network"}}', children: [
|
{ title: '{{ i18n "pages.inbounds.network"}}', children: [
|
||||||
{ title: 'L4', dataIndex: 'network', align: 'center', width: 10 },
|
{ title: 'L4', dataIndex: 'network', align: 'center', width: 10 },
|
||||||
{ title: 'Protocol', dataIndex: 'protocol', align: 'center', width: 10, ellipsis: true },
|
{ title: 'Protocol', dataIndex: 'protocol', align: 'center', width: 10, ellipsis: true },
|
||||||
@@ -422,7 +448,7 @@
|
|||||||
{ title: 'Port', dataIndex: 'port', align: 'center', width: 10, ellipsis: true }]},
|
{ title: 'Port', dataIndex: 'port', align: 'center', width: 10, ellipsis: true }]},
|
||||||
{ title: '{{ i18n "pages.xray.rules.inbound"}}', children: [
|
{ title: '{{ i18n "pages.xray.rules.inbound"}}', children: [
|
||||||
{ title: 'Inbound Tag', dataIndex: 'inboundTag', align: 'center', width: 20, ellipsis: true },
|
{ title: 'Inbound Tag', dataIndex: 'inboundTag', align: 'center', width: 20, ellipsis: true },
|
||||||
{ title: 'User email', dataIndex: 'user', align: 'center', width: 20, ellipsis: true }]},
|
{ title: 'Client Email', dataIndex: 'user', align: 'center', width: 20, ellipsis: true }]},
|
||||||
{ title: '{{ i18n "pages.xray.rules.outbound"}}', dataIndex: 'outboundTag', align: 'center', width: 20 },
|
{ title: '{{ i18n "pages.xray.rules.outbound"}}', dataIndex: 'outboundTag', align: 'center', width: 20 },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -459,6 +485,7 @@
|
|||||||
xraySetting: '',
|
xraySetting: '',
|
||||||
inboundTags: [],
|
inboundTags: [],
|
||||||
saveBtnDisable: true,
|
saveBtnDisable: true,
|
||||||
|
restartResult: '',
|
||||||
showAlert: false,
|
showAlert: false,
|
||||||
isMobile: window.innerWidth <= 768,
|
isMobile: window.innerWidth <= 768,
|
||||||
advSettings: 'xraySetting',
|
advSettings: 'xraySetting',
|
||||||
@@ -503,15 +530,17 @@
|
|||||||
ips: {
|
ips: {
|
||||||
local: ["geoip:private"],
|
local: ["geoip:private"],
|
||||||
cn: ["geoip:cn"],
|
cn: ["geoip:cn"],
|
||||||
ir: ["geoip:ir"],
|
ir: ["ext:geoip_IR.dat:ir"],
|
||||||
ru: ["geoip:ru"],
|
ru: ["geoip:ru"],
|
||||||
},
|
},
|
||||||
domains: {
|
domains: {
|
||||||
ads: [
|
ads: [
|
||||||
"geosite:category-ads-all",
|
"geosite:category-ads-all",
|
||||||
"ext:iran.dat:ads"
|
"ext:geosite_IR.dat:category-ads-all"
|
||||||
],
|
],
|
||||||
|
openai: ["geosite:openai"],
|
||||||
google: ["geosite:google"],
|
google: ["geosite:google"],
|
||||||
|
spotify: ["geosite:spotify"],
|
||||||
netflix: ["geosite:netflix"],
|
netflix: ["geosite:netflix"],
|
||||||
cn: [
|
cn: [
|
||||||
"geosite:cn",
|
"geosite:cn",
|
||||||
@@ -523,9 +552,8 @@
|
|||||||
],
|
],
|
||||||
ir: [
|
ir: [
|
||||||
"regexp:.*\\.ir$",
|
"regexp:.*\\.ir$",
|
||||||
"ext:iran.dat:ir",
|
"regexp:.*\\.xn--mgba3a4f16a$", // .ایران
|
||||||
"ext:iran.dat:other",
|
"ext:geosite_IR.dat:ir" // have rules to bypass all .ir domains.
|
||||||
"geosite:category-ir"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
familyProtectDNS: {
|
familyProtectDNS: {
|
||||||
@@ -567,8 +595,19 @@
|
|||||||
async restartXray() {
|
async restartXray() {
|
||||||
this.loading(true);
|
this.loading(true);
|
||||||
const msg = await HttpUtil.post("server/restartXrayService");
|
const msg = await HttpUtil.post("server/restartXrayService");
|
||||||
|
if (msg.success) {
|
||||||
|
await PromiseUtil.sleep(500);
|
||||||
|
await this.getXrayResult();
|
||||||
|
}
|
||||||
this.loading(false);
|
this.loading(false);
|
||||||
},
|
},
|
||||||
|
async getXrayResult() {
|
||||||
|
const msg = await HttpUtil.get("/xui/xray/getXrayResult");
|
||||||
|
if(msg.success){
|
||||||
|
this.restartResult=msg.obj;
|
||||||
|
if(msg.obj.length > 1) Vue.prototype.$message.error(msg.obj);
|
||||||
|
}
|
||||||
|
},
|
||||||
async resetXrayConfigToDefault() {
|
async resetXrayConfigToDefault() {
|
||||||
this.loading(true);
|
this.loading(true);
|
||||||
const msg = await HttpUtil.get("/xui/xray/getDefaultJsonConfig");
|
const msg = await HttpUtil.get("/xui/xray/getDefaultJsonConfig");
|
||||||
@@ -683,6 +722,8 @@
|
|||||||
break;
|
break;
|
||||||
case Protocols.DNS:
|
case Protocols.DNS:
|
||||||
return [o.settings.address + ':' + o.settings.port];
|
return [o.settings.address + ':' + o.settings.port];
|
||||||
|
case Protocols.Wireguard:
|
||||||
|
return o.settings.peers.map(peer => peer.endpoint);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -700,7 +741,8 @@
|
|||||||
}
|
}
|
||||||
outModal.close();
|
outModal.close();
|
||||||
},
|
},
|
||||||
isEdit: false
|
isEdit: false,
|
||||||
|
tags: this.templateSettings.outbounds.map(obj => obj.tag)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
editOutbound(index){
|
editOutbound(index){
|
||||||
@@ -713,7 +755,8 @@
|
|||||||
this.outboundSettings = JSON.stringify(this.templateSettings.outbounds);
|
this.outboundSettings = JSON.stringify(this.templateSettings.outbounds);
|
||||||
outModal.close();
|
outModal.close();
|
||||||
},
|
},
|
||||||
isEdit: true
|
isEdit: true,
|
||||||
|
tags: this.outboundData.filter((o) => o.key != index ).map(obj => obj.tag)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
deleteOutbound(index){
|
deleteOutbound(index){
|
||||||
@@ -756,17 +799,27 @@
|
|||||||
confirm: (reverse, rules) => {
|
confirm: (reverse, rules) => {
|
||||||
reverseModal.loading();
|
reverseModal.loading();
|
||||||
if(reverse.tag.length > 0){
|
if(reverse.tag.length > 0){
|
||||||
oldtag = this.reverseData[index].tag;
|
oldData = this.reverseData[index];
|
||||||
this.deleteReverse(index);
|
|
||||||
newTemplateSettings = this.templateSettings;
|
newTemplateSettings = this.templateSettings;
|
||||||
if(newTemplateSettings.reverse == undefined) newTemplateSettings.reverse = {};
|
oldReverseIndex = newTemplateSettings.reverse[oldData.type+'s'].findIndex(rs => rs.tag == oldData.tag);
|
||||||
if(newTemplateSettings.reverse[reverse.type+'s'] == undefined) newTemplateSettings.reverse[reverse.type+'s'] = [];
|
oldRuleIndex0 = oldRules.length>0 ? newTemplateSettings.routing.rules.findIndex(r => JSON.stringify(r) == JSON.stringify(oldRules[0])) : -1;
|
||||||
newTemplateSettings.reverse[reverse.type+'s'].push({ tag: reverse.tag, domain: reverse.domain });
|
oldRuleIndex1 = oldRules.length==2 ? newTemplateSettings.routing.rules.findIndex(r => JSON.stringify(r) == JSON.stringify(oldRules[1])) : -1;
|
||||||
|
if(oldData.type == reverse.type){
|
||||||
|
newTemplateSettings.reverse[oldData.type + 's'][oldReverseIndex] = { tag: reverse.tag, domain: reverse.domain };
|
||||||
|
} else {
|
||||||
|
newTemplateSettings.reverse[oldData.type+'s'].splice(oldReverseIndex,1);
|
||||||
|
// delete empty object
|
||||||
|
if(newTemplateSettings.reverse[oldData.type+'s'].length == 0) Reflect.deleteProperty(newTemplateSettings.reverse, oldData.type+'s');
|
||||||
|
// add other type of reverse if it is not exist
|
||||||
|
if(!newTemplateSettings.reverse[reverse.type+'s']) newTemplateSettings.reverse[reverse.type+'s'] = [];
|
||||||
|
newTemplateSettings.reverse[reverse.type+'s'].push({ tag: reverse.tag, domain: reverse.domain });
|
||||||
|
}
|
||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
|
|
||||||
// Adjust Rules
|
// Adjust Rules
|
||||||
newRules = this.templateSettings.routing.rules.filter(r => r.outboundTag != oldtag && (r.inboundTag && !r.inboundTag.includes(oldtag)));
|
newRules = this.templateSettings.routing.rules;
|
||||||
newRules.push(...rules)
|
oldRuleIndex0 != -1 ? newRules[oldRuleIndex0] = rules[0] : newRules.push(rules[0]);
|
||||||
|
oldRuleIndex1 != -1 ? newRules[oldRuleIndex1] = rules[1] : newRules.push(rules[1]);
|
||||||
this.routingRuleSettings = JSON.stringify(newRules);
|
this.routingRuleSettings = JSON.stringify(newRules);
|
||||||
}
|
}
|
||||||
reverseModal.close();
|
reverseModal.close();
|
||||||
@@ -781,10 +834,17 @@
|
|||||||
realIndex = reverseTypeObj.findIndex(r => r.tag==oldData.tag && r.domain==oldData.domain);
|
realIndex = reverseTypeObj.findIndex(r => r.tag==oldData.tag && r.domain==oldData.domain);
|
||||||
newTemplateSettings.reverse[oldData.type+'s'].splice(realIndex,1);
|
newTemplateSettings.reverse[oldData.type+'s'].splice(realIndex,1);
|
||||||
|
|
||||||
|
// delete empty objects
|
||||||
if(reverseTypeObj.length == 0) Reflect.deleteProperty(newTemplateSettings.reverse, oldData.type+'s');
|
if(reverseTypeObj.length == 0) Reflect.deleteProperty(newTemplateSettings.reverse, oldData.type+'s');
|
||||||
if(Object.keys(newTemplateSettings.reverse).length === 0) Reflect.deleteProperty(newTemplateSettings, 'reverse');
|
if(Object.keys(newTemplateSettings.reverse).length === 0) Reflect.deleteProperty(newTemplateSettings, 'reverse');
|
||||||
|
|
||||||
newRules = newTemplateSettings.routing.rules.filter(r => r.outboundTag != oldData.tag && (r.inboundTag && !r.inboundTag.includes(oldData.tag)));
|
// delete related routing rules
|
||||||
|
newRules = newTemplateSettings.routing.rules;
|
||||||
|
if(oldData.type == "bridge"){
|
||||||
|
newRules = newTemplateSettings.routing.rules.filter(r => !( r.inboundTag && r.inboundTag.length == 1 && r.inboundTag[0] == oldData.tag));
|
||||||
|
} else if(oldData.type == "portal"){
|
||||||
|
newRules = newTemplateSettings.routing.rules.filter(r => r.outboundTag != oldData.tag);
|
||||||
|
}
|
||||||
newTemplateSettings.routing.rules = newRules;
|
newTemplateSettings.routing.rules = newRules;
|
||||||
|
|
||||||
this.templateSettings = newTemplateSettings;
|
this.templateSettings = newTemplateSettings;
|
||||||
@@ -829,6 +889,9 @@
|
|||||||
rules = this.templateSettings.routing.rules;
|
rules = this.templateSettings.routing.rules;
|
||||||
rules.splice(index,1);
|
rules.splice(index,1);
|
||||||
this.routingRuleSettings = JSON.stringify(rules);
|
this.routingRuleSettings = JSON.stringify(rules);
|
||||||
|
},
|
||||||
|
showWarp(){
|
||||||
|
warpModal.show();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
@@ -836,6 +899,7 @@
|
|||||||
this.showAlert = true;
|
this.showAlert = true;
|
||||||
}
|
}
|
||||||
await this.getXraySetting();
|
await this.getXraySetting();
|
||||||
|
await this.getXrayResult();
|
||||||
while (true) {
|
while (true) {
|
||||||
await PromiseUtil.sleep(1000);
|
await PromiseUtil.sleep(1000);
|
||||||
this.saveBtnDisable = this.oldXraySetting === this.xraySetting;
|
this.saveBtnDisable = this.oldXraySetting === this.xraySetting;
|
||||||
@@ -923,15 +987,17 @@
|
|||||||
freedomStrategy: {
|
freedomStrategy: {
|
||||||
get: function () {
|
get: function () {
|
||||||
if (!this.templateSettings) return "AsIs";
|
if (!this.templateSettings) return "AsIs";
|
||||||
freedomOutbound = this.templateSettings.outbounds.find((o) => o.protocol === "freedom" && !o.tag);
|
freedomOutbound = this.templateSettings.outbounds.find((o) => o.protocol === "freedom" && o.tag == "direct");
|
||||||
if (!freedomOutbound) return "AsIs";
|
if (!freedomOutbound) return "AsIs";
|
||||||
if (!freedomOutbound.settings || !freedomOutbound.settings.domainStrategy) return "AsIs";
|
if (!freedomOutbound.settings || !freedomOutbound.settings.domainStrategy) return "AsIs";
|
||||||
return freedomOutbound.settings.domainStrategy;
|
return freedomOutbound.settings.domainStrategy;
|
||||||
},
|
},
|
||||||
set: function (newValue) {
|
set: function (newValue) {
|
||||||
newTemplateSettings = this.templateSettings;
|
newTemplateSettings = this.templateSettings;
|
||||||
freedomOutboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.protocol === "freedom" && !o.tag);
|
freedomOutboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.protocol === "freedom" && o.tag == "direct");
|
||||||
if (!newTemplateSettings.outbounds[freedomOutboundIndex].settings) {
|
if(freedomOutboundIndex == -1){
|
||||||
|
newTemplateSettings.outbounds.push({protocol: "freedom", tag: "direct", settings: { "domainStrategy": newValue }});
|
||||||
|
} else if (!newTemplateSettings.outbounds[freedomOutboundIndex].settings) {
|
||||||
newTemplateSettings.outbounds[freedomOutboundIndex].settings = { "domainStrategy": newValue };
|
newTemplateSettings.outbounds[freedomOutboundIndex].settings = { "domainStrategy": newValue };
|
||||||
} else {
|
} else {
|
||||||
newTemplateSettings.outbounds[freedomOutboundIndex].settings.domainStrategy = newValue;
|
newTemplateSettings.outbounds[freedomOutboundIndex].settings.domainStrategy = newValue;
|
||||||
@@ -1001,6 +1067,14 @@
|
|||||||
this.syncRulesWithOutbound("IPv4", this.ipv4Settings);
|
this.syncRulesWithOutbound("IPv4", this.ipv4Settings);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
warpDomains: {
|
||||||
|
get: function () {
|
||||||
|
return this.templateRuleGetter({ outboundTag: "warp", property: "domain" });
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
this.templateRuleSetter({ outboundTag: "warp", property: "domain", data: newValue });
|
||||||
|
}
|
||||||
|
},
|
||||||
torrentSettings: {
|
torrentSettings: {
|
||||||
get: function () {
|
get: function () {
|
||||||
return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols);
|
return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols);
|
||||||
@@ -1220,8 +1294,61 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
WarpExist: {
|
||||||
|
get: function() {
|
||||||
|
return this.templateSettings ? this.templateSettings.outbounds.findIndex((o) => o.tag == "warp")>=0 : false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GoogleWARPSettings: {
|
||||||
|
get: function () {
|
||||||
|
return doAllItemsExist(this.settingsData.domains.google, this.warpDomains);
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.google];
|
||||||
|
} else {
|
||||||
|
this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.google.includes(data));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OpenAIWARPSettings: {
|
||||||
|
get: function () {
|
||||||
|
return doAllItemsExist(this.settingsData.domains.openai, this.warpDomains);
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.openai];
|
||||||
|
} else {
|
||||||
|
this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.openai.includes(data));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NetflixWARPSettings: {
|
||||||
|
get: function () {
|
||||||
|
return doAllItemsExist(this.settingsData.domains.netflix, this.warpDomains);
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.netflix];
|
||||||
|
} else {
|
||||||
|
this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.netflix.includes(data));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SpotifyWARPSettings: {
|
||||||
|
get: function () {
|
||||||
|
return doAllItemsExist(this.settingsData.domains.spotify, this.warpDomains);
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.spotify];
|
||||||
|
} else {
|
||||||
|
this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.spotify.includes(data));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -21,10 +21,11 @@
|
|||||||
duplicateTag: false,
|
duplicateTag: false,
|
||||||
isValid: true,
|
isValid: true,
|
||||||
activeKey: '1',
|
activeKey: '1',
|
||||||
|
tags: [],
|
||||||
ok() {
|
ok() {
|
||||||
ObjectUtil.execute(outModal.confirm, outModal.outbound.toJson());
|
ObjectUtil.execute(outModal.confirm, outModal.outbound.toJson());
|
||||||
},
|
},
|
||||||
show({ title='', okText='{{ i18n "sure" }}', outbound, confirm=(outbound)=>{}, isEdit=false }) {
|
show({ title='', okText='{{ i18n "sure" }}', outbound, confirm=(outbound)=>{}, isEdit=false, tags=[] }) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.okText = okText;
|
this.okText = okText;
|
||||||
this.confirm = confirm;
|
this.confirm = confirm;
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
this.visible = true;
|
this.visible = true;
|
||||||
this.outbound = isEdit ? Outbound.fromJson(outbound) : new Outbound();
|
this.outbound = isEdit ? Outbound.fromJson(outbound) : new Outbound();
|
||||||
this.isEdit = isEdit;
|
this.isEdit = isEdit;
|
||||||
|
this.tags = tags;
|
||||||
this.check()
|
this.check()
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
@@ -44,7 +46,7 @@
|
|||||||
outModal.confirmLoading = loading;
|
outModal.confirmLoading = loading;
|
||||||
},
|
},
|
||||||
check(){
|
check(){
|
||||||
if(outModal.outbound.tag == ''){
|
if(outModal.outbound.tag == '' || outModal.tags.includes(outModal.outbound.tag)){
|
||||||
this.duplicateTag = true;
|
this.duplicateTag = true;
|
||||||
this.isValid = false;
|
this.isValid = false;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,77 +2,41 @@
|
|||||||
<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 layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
|
||||||
<tr>
|
<a-select v-model="reverseModal.reverse.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>{{ i18n "pages.xray.outbound.type" }}</td>
|
<a-select-option v-for="x,y in reverseTypes" :value="y">[[ x ]]</a-select-option>
|
||||||
<td>
|
</a-select>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-select v-model="reverseModal.reverse.type" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-form-item label='{{ i18n "pages.xray.outbound.tag" }}'>
|
||||||
<a-select-option v-for="x,y in reverseTypes" :value="y">[[ x ]]</a-select-option>
|
<a-input v-model.trim="reverseModal.reverse.tag"></a-input>
|
||||||
</a-select>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label='{{ i18n "pages.xray.outbound.domain" }}'>
|
||||||
</td>
|
<a-input v-model.trim="reverseModal.reverse.domain"></a-input>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.xray.outbound.tag" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="reverseModal.reverse.tag" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.xray.outbound.domain" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-input v-model.trim="reverseModal.reverse.domain" style="width: 250px"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<template v-if="reverseModal.reverse.type=='bridge'">
|
<template v-if="reverseModal.reverse.type=='bridge'">
|
||||||
<tr>
|
<a-form-item label='{{ i18n "pages.xray.outbound.intercon" }}'>
|
||||||
<td>{{ i18n "pages.xray.outbound.intercon" }}</td>
|
<a-select v-model="reverseModal.rules[0].outboundTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>
|
<a-select-option v-for="x in reverseModal.outboundTags" :value="x">[[ x ]]</a-select-option>
|
||||||
<a-form-item>
|
</a-select>
|
||||||
<a-select v-model="reverseModal.rules[0].outboundTag" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-form-item>
|
||||||
<a-select-option v-for="x in reverseModal.outboundTags" :value="x">[[ x ]]</a-select-option>
|
<a-form-item label='{{ i18n "pages.xray.rules.outbound" }}'>
|
||||||
</a-select>
|
<a-select v-model="reverseModal.rules[1].outboundTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</a-form-item>
|
<a-select-option v-for="x in reverseModal.outboundTags" :value="x">[[ x ]]</a-select-option>
|
||||||
</td>
|
</a-select>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.xray.rules.outbound" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-select v-model="reverseModal.rules[1].outboundTag" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option v-for="x in reverseModal.outboundTags" :value="x">[[ x ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<tr>
|
<a-form-item label='{{ i18n "pages.xray.outbound.intercon" }}'>
|
||||||
<td>{{ i18n "pages.xray.outbound.intercon" }}</td>
|
<a-checkbox-group
|
||||||
<td>
|
v-model="reverseModal.rules[0].inboundTag"
|
||||||
<a-form-item>
|
:options="reverseModal.inboundTags"></a-checkbox-group>
|
||||||
<a-checkbox-group
|
</a-form-item>
|
||||||
v-model="reverseModal.rules[0].inboundTag"
|
<a-form-item label='{{ i18n "pages.xray.rules.inbound" }}'>
|
||||||
:options="reverseModal.inboundTags"></a-checkbox-group>
|
<a-checkbox-group
|
||||||
</a-form-item>
|
v-model="reverseModal.rules[1].inboundTag"
|
||||||
</td>
|
:options="reverseModal.inboundTags"></a-checkbox-group>
|
||||||
</tr>
|
</a-form-item>
|
||||||
<tr>
|
|
||||||
<td>{{ i18n "pages.xray.rules.inbound" }}</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-checkbox-group
|
|
||||||
v-model="reverseModal.rules[1].inboundTag"
|
|
||||||
:options="reverseModal.inboundTags"></a-checkbox-group>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</template>
|
</template>
|
||||||
</table>
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
@@ -148,9 +112,9 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
this.isEdit = isEdit;
|
this.isEdit = isEdit;
|
||||||
this.inboundTags = app.templateSettings.inbounds.map(obj => obj.tag);
|
this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj => obj.tag);
|
||||||
this.inboundTags.push(...app.inboundTags);
|
this.inboundTags.push(...app.inboundTags);
|
||||||
this.outboundTags = app.templateSettings.outbounds.map(obj => obj.tag);
|
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag);
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
reverseModal.visible = false;
|
reverseModal.visible = false;
|
||||||
|
|||||||
@@ -2,149 +2,111 @@
|
|||||||
<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 layout="inline">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table width="100%" class="ant-table-tbody">
|
<a-form-item label='Domain Matcher'>
|
||||||
<tr>
|
<a-select v-model="ruleModal.rule.domainMatcher" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<td>Domain Matcher</td>
|
|
||||||
<td>
|
|
||||||
<a-form-item>
|
|
||||||
<a-select v-model="ruleModal.rule.domainMatcher" style="width: 250px;" :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>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</td>
|
<a-form-item>
|
||||||
</tr>
|
<template slot="label">
|
||||||
<tr>
|
|
||||||
<td>Source IPs
|
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle"></a-icon>
|
Source IPs <a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</td>
|
</template>
|
||||||
<td>
|
<a-input v-model.trim="ruleModal.rule.source"></a-input>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input v-model.trim="ruleModal.rule.source" style="width: 250px"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<template slot="label">
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Source Port
|
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle"></a-icon>
|
Source Port <a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</td>
|
</template>
|
||||||
<td>
|
<a-input v-model.trim="ruleModal.rule.sourcePort"></a-input>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input v-model.trim="ruleModal.rule.sourcePort" style="width: 250px"></a-input>
|
<a-form-item label='Network'>
|
||||||
</a-form-item>
|
<a-select v-model="ruleModal.rule.network" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</td>
|
<a-select-option v-for="x in ['','tcp','udp','tcp,udp']" :value="x">[[ x ]]</a-select-option>
|
||||||
</tr>
|
</a-select>
|
||||||
<tr>
|
</a-form-item>
|
||||||
<td>Network</td>
|
<a-form-item label='Protocol'>
|
||||||
<td>
|
<a-select v-model="ruleModal.rule.protocol" mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-form-item>
|
<a-select-option v-for="x in ['http','tls','bittorrent']" :value="x">[[ x ]]</a-select-option>
|
||||||
<a-select v-model="ruleModal.rule.network" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
</a-select>
|
||||||
<a-select-option v-for="x in ['','tcp','tdp','tcp,udp']" :value="x">[[ x ]]</a-select-option>
|
</a-form-item>
|
||||||
</a-select>
|
<a-form-item label='Attributes'>
|
||||||
</a-form-item>
|
<a-button size="small" style="margin-left: 10px" @click="ruleModal.rule.attrs.push(['', ''])">+</a-button>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item :wrapper-col="{span: 24}">
|
||||||
<tr>
|
<a-input-group compact v-for="(attr,index) in ruleModal.rule.attrs">
|
||||||
<td>Protocol</td>
|
<a-input style="width: 50%" v-model="attr[0]" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||||
<td>
|
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||||
<a-form-item>
|
</a-input>
|
||||||
<a-select v-model="ruleModal.rule.protocol" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-input style="width: 50%" v-model="attr[1]" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
<a-select-option v-for="x in ['','http','tls','bittorrent']" :value="x">[[ x ]]</a-select-option>
|
<a-button slot="addonAfter" size="small" @click="ruleModal.rule.attrs.splice(index,1)">-</a-button>
|
||||||
</a-select>
|
</a-input>
|
||||||
</a-form-item>
|
</a-input-group>
|
||||||
</td>
|
</a-form-item>
|
||||||
</tr>
|
<a-form-item>
|
||||||
<tr>
|
<template slot="label">
|
||||||
<td colspan="2">
|
|
||||||
<a-form-item>
|
|
||||||
<span>Attributes</span>
|
|
||||||
<a-button size="small" style="margin-left: 10px" @click="ruleModal.rule.attrs.push(['', ''])">+</a-button>
|
|
||||||
<a-input-group compact v-for="(attr,index) in ruleModal.rule.attrs">
|
|
||||||
<a-input style="width: 50%" v-model="attr[0]" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
|
||||||
</a-input>
|
|
||||||
<a-input style="width: 50%" v-model="attr[1]" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
|
||||||
<a-button slot="addonAfter" size="small" @click="ruleModal.rule.attrs.splice(index,1)">-</a-button>
|
|
||||||
</a-input>
|
|
||||||
</a-input-group>
|
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>IP
|
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle"></a-icon>
|
IP <a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</td>
|
</template>
|
||||||
<td>
|
<a-input v-model.trim="ruleModal.rule.ip"></a-input>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input v-model.trim="ruleModal.rule.ip" style="width: 250px"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<template slot="label">
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Domain
|
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle"></a-icon>
|
Domain <a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</td>
|
</template>
|
||||||
<td>
|
<a-input v-model.trim="ruleModal.rule.domain"></a-input>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input v-model.trim="ruleModal.rule.domain" style="width: 250px"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<template slot="label">
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Port
|
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle"></a-icon>
|
User <a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</td>
|
</template>
|
||||||
<td>
|
<a-input v-model.trim="ruleModal.rule.user"></a-input>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-input v-model.trim="ruleModal.rule.port" style="width: 250px"></a-input>
|
<a-form-item>
|
||||||
</a-form-item>
|
<template slot="label">
|
||||||
</td>
|
<a-tooltip>
|
||||||
</tr>
|
<template slot="title">
|
||||||
<tr>
|
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||||
<td>Inbound Tags</td>
|
</template>
|
||||||
<td>
|
Port <a-icon type="question-circle"></a-icon>
|
||||||
<a-form-item>
|
</a-tooltip>
|
||||||
<a-select v-model="ruleModal.rule.inboundTag" mode="multiple" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
</template>
|
||||||
<a-select-option v-for="tag in ruleModal.inboundTags" :value="tag">[[ tag ]]</a-select-option>
|
<a-input v-model.trim="ruleModal.rule.port"></a-input>
|
||||||
</a-select>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label='Inbound Tags'>
|
||||||
</td>
|
<a-select v-model="ruleModal.rule.inboundTag" mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
</tr>
|
<a-select-option v-for="tag in ruleModal.inboundTags" :value="tag">[[ tag ]]</a-select-option>
|
||||||
<tr>
|
</a-select>
|
||||||
<td>Outbound Tag</td>
|
</a-form-item>
|
||||||
<td>
|
<a-form-item label='Outbound Tag'>
|
||||||
<a-form-item>
|
<a-select v-model="ruleModal.rule.outboundTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select v-model="ruleModal.rule.outboundTag" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select-option v-for="tag in ruleModal.outboundTags" :value="tag">[[ tag ]]</a-select-option>
|
||||||
<a-select-option v-for="tag in ruleModal.outboundTags" :value="tag">[[ tag ]]</a-select-option>
|
</a-select>
|
||||||
</a-select>
|
</a-form-item>
|
||||||
</a-form-item>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
</table>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@@ -158,6 +120,7 @@
|
|||||||
isEdit: false,
|
isEdit: false,
|
||||||
confirm: null,
|
confirm: null,
|
||||||
rule: {
|
rule: {
|
||||||
|
type: "field",
|
||||||
domainMatcher: "",
|
domainMatcher: "",
|
||||||
domain: "",
|
domain: "",
|
||||||
ip: "",
|
ip: "",
|
||||||
@@ -214,9 +177,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.isEdit = isEdit;
|
this.isEdit = isEdit;
|
||||||
this.inboundTags = app.templateSettings.inbounds.map(obj => obj.tag);
|
this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj => obj.tag);
|
||||||
this.inboundTags.push(...app.inboundTags);
|
this.inboundTags.push(...app.inboundTags);
|
||||||
this.outboundTags = app.templateSettings.outbounds.map(obj => obj.tag);
|
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag);
|
||||||
if(app.templateSettings.reverse){
|
if(app.templateSettings.reverse){
|
||||||
if(app.templateSettings.reverse.bridges) {
|
if(app.templateSettings.reverse.bridges) {
|
||||||
this.inboundTags.push(...app.templateSettings.reverse.bridges.map(b => b.tag));
|
this.inboundTags.push(...app.templateSettings.reverse.bridges.map(b => b.tag));
|
||||||
@@ -235,6 +198,7 @@
|
|||||||
value = ruleModal.rule;
|
value = ruleModal.rule;
|
||||||
rule = {};
|
rule = {};
|
||||||
newRule = {};
|
newRule = {};
|
||||||
|
rule.type = "field";
|
||||||
rule.domainMatcher = value.domainMatcher;
|
rule.domainMatcher = value.domainMatcher;
|
||||||
rule.domain = value.domain.length>0 ? value.domain.split(',') : [];
|
rule.domain = value.domain.length>0 ? value.domain.split(',') : [];
|
||||||
rule.ip = value.ip.length>0 ? value.ip.split(',') : [];
|
rule.ip = value.ip.length>0 ? value.ip.split(',') : [];
|
||||||
|
|||||||
@@ -168,9 +168,13 @@ func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, boo
|
|||||||
|
|
||||||
err = tx.Save(inbound).Error
|
err = tx.Save(inbound).Error
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, client := range clients {
|
if len(inbound.ClientStats) == 0 {
|
||||||
s.AddClientStat(tx, inbound.Id, &client)
|
for _, client := range clients {
|
||||||
|
s.AddClientStat(tx, inbound.Id, &client)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return inbound, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
needRestart := false
|
needRestart := false
|
||||||
@@ -1285,7 +1289,8 @@ func (s *InboundService) GetInboundTags() (string, error) {
|
|||||||
if err != nil && err != gorm.ErrRecordNotFound {
|
if err != nil && err != gorm.ErrRecordNotFound {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return "[\"" + strings.Join(inboundTags, "\", \"") + "\"]", nil
|
tags, _ := json.Marshal(inboundTags)
|
||||||
|
return string(tags), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *InboundService) MigrationRequirements() {
|
func (s *InboundService) MigrationRequirements() {
|
||||||
|
|||||||
@@ -363,14 +363,6 @@ func (s *ServerService) UpdateXray(version string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = copyZipFile("geosite.dat", xray.GetGeositePath())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = copyZipFile("geoip.dat", xray.GetGeoipPath())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
@@ -418,6 +410,11 @@ func (s *ServerService) GetConfigJson() (interface{}, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetDb() ([]byte, error) {
|
func (s *ServerService) GetDb() ([]byte, error) {
|
||||||
|
// Update by manually trigger a checkpoint operation
|
||||||
|
err := database.Checkpoint()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
// Open the file for reading
|
// Open the file for reading
|
||||||
file, err := os.Open(config.GetDBPath())
|
file, err := os.Open(config.GetDBPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ var defaultValueMap = map[string]string{
|
|||||||
"pageSize": "0",
|
"pageSize": "0",
|
||||||
"expireDiff": "0",
|
"expireDiff": "0",
|
||||||
"trafficDiff": "0",
|
"trafficDiff": "0",
|
||||||
|
"remarkModel": "-ieo",
|
||||||
"timeLocation": "Asia/Tehran",
|
"timeLocation": "Asia/Tehran",
|
||||||
"tgBotEnable": "false",
|
"tgBotEnable": "false",
|
||||||
"tgBotToken": "",
|
"tgBotToken": "",
|
||||||
@@ -54,6 +55,7 @@ var defaultValueMap = map[string]string{
|
|||||||
"subEncrypt": "true",
|
"subEncrypt": "true",
|
||||||
"subShowInfo": "false",
|
"subShowInfo": "false",
|
||||||
"subURI": "",
|
"subURI": "",
|
||||||
|
"warp": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
type SettingService struct {
|
type SettingService struct {
|
||||||
@@ -295,6 +297,10 @@ func (s *SettingService) GetSessionMaxAge() (int, error) {
|
|||||||
return s.getInt("sessionMaxAge")
|
return s.getInt("sessionMaxAge")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetRemarkModel() (string, error) {
|
||||||
|
return s.getString("remarkModel")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetSecret() ([]byte, error) {
|
func (s *SettingService) GetSecret() ([]byte, error) {
|
||||||
secret, err := s.getString("secret")
|
secret, err := s.getString("secret")
|
||||||
if secret == defaultValueMap["secret"] {
|
if secret == defaultValueMap["secret"] {
|
||||||
@@ -392,6 +398,13 @@ func (s *SettingService) GetSubURI() (string, error) {
|
|||||||
return s.getString("subURI")
|
return s.getString("subURI")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetWarp() (string, error) {
|
||||||
|
return s.getString("warp")
|
||||||
|
}
|
||||||
|
func (s *SettingService) SetWarp(data string) error {
|
||||||
|
return s.setString("warp", data)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {
|
func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {
|
||||||
if err := allSetting.CheckValid(); err != nil {
|
if err := allSetting.CheckValid(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -433,6 +446,7 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) {
|
|||||||
"tgBotEnable": func() (interface{}, error) { return s.GetTgbotenabled() },
|
"tgBotEnable": func() (interface{}, error) { return s.GetTgbotenabled() },
|
||||||
"subEnable": func() (interface{}, error) { return s.GetSubEnable() },
|
"subEnable": func() (interface{}, error) { return s.GetSubEnable() },
|
||||||
"subURI": func() (interface{}, error) { return s.GetSubURI() },
|
"subURI": func() (interface{}, error) { return s.GetSubURI() },
|
||||||
|
"remarkModel": func() (interface{}, error) { return s.GetRemarkModel() },
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
|
"x-ui/database"
|
||||||
"x-ui/database/model"
|
"x-ui/database/model"
|
||||||
"x-ui/logger"
|
"x-ui/logger"
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
@@ -697,12 +698,18 @@ func (t *Tgbot) sendBackup(chatId int64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update by manually trigger a checkpoint operation
|
||||||
|
err := database.Checkpoint()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warning("Error in trigger a checkpoint operation: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
output := t.I18nBot("tgbot.messages.backupTime", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
|
output := t.I18nBot("tgbot.messages.backupTime", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
|
||||||
t.SendMsgToTgbot(chatId, output)
|
t.SendMsgToTgbot(chatId, output)
|
||||||
|
|
||||||
file := tgbotapi.FilePath(config.GetDBPath())
|
file := tgbotapi.FilePath(config.GetDBPath())
|
||||||
msg := tgbotapi.NewDocument(chatId, file)
|
msg := tgbotapi.NewDocument(chatId, file)
|
||||||
_, err := bot.Send(msg)
|
_, err = bot.Send(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("Error in uploading backup: ", err)
|
logger.Warning("Error in uploading backup: ", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"encoding/json"
|
|
||||||
"x-ui/util/common"
|
|
||||||
"x-ui/xray"
|
|
||||||
)
|
|
||||||
|
|
||||||
type XraySettingService struct {
|
|
||||||
SettingService
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *XraySettingService) SaveXraySetting(newXraySettings string) error {
|
|
||||||
if err := s.CheckXrayConfig(newXraySettings); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return s.SettingService.saveSetting("xrayTemplateConfig", newXraySettings)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error {
|
|
||||||
xrayConfig := &xray.Config{}
|
|
||||||
err := json.Unmarshal([]byte(XrayTemplateConfig), xrayConfig)
|
|
||||||
if err != nil {
|
|
||||||
return common.NewError("xray template config invalid:", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
172
web/service/xray_setting.go
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
"x-ui/util/common"
|
||||||
|
"x-ui/xray"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XraySettingService struct {
|
||||||
|
SettingService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *XraySettingService) SaveXraySetting(newXraySettings string) error {
|
||||||
|
if err := s.CheckXrayConfig(newXraySettings); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return s.SettingService.saveSetting("xrayTemplateConfig", newXraySettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error {
|
||||||
|
xrayConfig := &xray.Config{}
|
||||||
|
err := json.Unmarshal([]byte(XrayTemplateConfig), xrayConfig)
|
||||||
|
if err != nil {
|
||||||
|
return common.NewError("xray template config invalid:", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *XraySettingService) GetWarpData() (string, error) {
|
||||||
|
warp, err := s.SettingService.GetWarp()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return warp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *XraySettingService) GetWarpConfig() (string, error) {
|
||||||
|
var warpData map[string]string
|
||||||
|
warp, err := s.SettingService.GetWarp()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal([]byte(warp), &warpData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s", warpData["device_id"])
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "Bearer "+warpData["access_token"])
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
buffer := bytes.NewBuffer(make([]byte, 8192))
|
||||||
|
buffer.Reset()
|
||||||
|
_, err = buffer.ReadFrom(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *XraySettingService) RegWarp(secretKey string, publicKey string) (string, error) {
|
||||||
|
tos := time.Now().UTC().Format("2006-01-02T15:04:05.000Z")
|
||||||
|
hostName, _ := os.Hostname()
|
||||||
|
data := fmt.Sprintf(`{"key":"%s","tos":"%s","type": "PC","model": "x-ui", "name": "%s"}`, publicKey, tos, hostName)
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg")
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(data)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("CF-Client-Version", "a-7.21-0721")
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
buffer := bytes.NewBuffer(make([]byte, 8192))
|
||||||
|
buffer.Reset()
|
||||||
|
_, err = buffer.ReadFrom(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rspData map[string]interface{}
|
||||||
|
err = json.Unmarshal(buffer.Bytes(), &rspData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceId := rspData["id"].(string)
|
||||||
|
token := rspData["token"].(string)
|
||||||
|
license, ok := rspData["account"].(map[string]interface{})["license"].(string)
|
||||||
|
if !ok {
|
||||||
|
fmt.Println("Error accessing license value.")
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
warpData := fmt.Sprintf("{\n \"access_token\": \"%s\",\n \"device_id\": \"%s\",", token, deviceId)
|
||||||
|
warpData += fmt.Sprintf("\n \"license_key\": \"%s\",\n \"private_key\": \"%s\"\n}", license, secretKey)
|
||||||
|
|
||||||
|
s.SettingService.SetWarp(warpData)
|
||||||
|
|
||||||
|
result := fmt.Sprintf("{\n \"data\": %s,\n \"config\": %s\n}", warpData, buffer.String())
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *XraySettingService) SetWarpLicence(license string) (string, error) {
|
||||||
|
var warpData map[string]string
|
||||||
|
warp, err := s.SettingService.GetWarp()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal([]byte(warp), &warpData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://api.cloudflareclient.com/v0a2158/reg/%s/account", warpData["device_id"])
|
||||||
|
data := fmt.Sprintf(`{"license": "%s"}`, license)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("PUT", url, bytes.NewBuffer([]byte(data)))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", "Bearer "+warpData["access_token"])
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
buffer := bytes.NewBuffer(make([]byte, 8192))
|
||||||
|
buffer.Reset()
|
||||||
|
_, err = buffer.ReadFrom(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
warpData["license_key"] = license
|
||||||
|
newWarpData, err := json.MarshalIndent(warpData, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
s.SettingService.SetWarp(string(newWarpData))
|
||||||
|
println(string(newWarpData))
|
||||||
|
|
||||||
|
return string(newWarpData), nil
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
"username" = "Username"
|
"username" = "Username"
|
||||||
"password" = "Password"
|
"password" = "Password"
|
||||||
"login" = "Login"
|
"login" = "Log In"
|
||||||
"confirm" = "Confirm"
|
"confirm" = "Confirm"
|
||||||
"cancel" = "Cancel"
|
"cancel" = "Cancel"
|
||||||
"close" = "Close"
|
"close" = "Close"
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
"copied" = "Copied"
|
"copied" = "Copied"
|
||||||
"download" = "Download"
|
"download" = "Download"
|
||||||
"remark" = "Remark"
|
"remark" = "Remark"
|
||||||
"enable" = "Enable"
|
"enable" = "Enabled"
|
||||||
"protocol" = "Protocol"
|
"protocol" = "Protocol"
|
||||||
"search" = "Search"
|
"search" = "Search"
|
||||||
"filter" = "Filter"
|
"filter" = "Filter"
|
||||||
@@ -26,13 +26,13 @@
|
|||||||
"edit" = "Edit"
|
"edit" = "Edit"
|
||||||
"delete" = "Delete"
|
"delete" = "Delete"
|
||||||
"reset" = "Reset"
|
"reset" = "Reset"
|
||||||
"copySuccess" = "Copied successfully"
|
"copySuccess" = "Copied Successful"
|
||||||
"sure" = "Sure"
|
"sure" = "Sure"
|
||||||
"encryption" = "Encryption"
|
"encryption" = "Encryption"
|
||||||
"transmission" = "Transmission"
|
"transmission" = "Transmission"
|
||||||
"host" = "Host"
|
"host" = "Host"
|
||||||
"path" = "Path"
|
"path" = "Path"
|
||||||
"camouflage" = "Camouflage"
|
"camouflage" = "Obfuscation"
|
||||||
"status" = "Status"
|
"status" = "Status"
|
||||||
"enabled" = "Enabled"
|
"enabled" = "Enabled"
|
||||||
"disabled" = "Disabled"
|
"disabled" = "Disabled"
|
||||||
@@ -40,83 +40,83 @@
|
|||||||
"depletingSoon" = "Depleting"
|
"depletingSoon" = "Depleting"
|
||||||
"offline" = "Offline"
|
"offline" = "Offline"
|
||||||
"online" = "Online"
|
"online" = "Online"
|
||||||
"domainName" = "Domain name"
|
"domainName" = "Domain Name"
|
||||||
"monitor" = "Listen IP"
|
"monitor" = "Listen IP"
|
||||||
"certificate" = "Certificate"
|
"certificate" = "Certificate"
|
||||||
"fail" = " Fail"
|
"fail" = " Fail"
|
||||||
"success" = " Success"
|
"success" = " Successful"
|
||||||
"getVersion" = "Get version"
|
"getVersion" = "Get Version"
|
||||||
"install" = "Install"
|
"install" = "Install"
|
||||||
"clients" = "Clients"
|
"clients" = "Clients"
|
||||||
"usage" = "Usage"
|
"usage" = "Usage"
|
||||||
"remained" = "Remained"
|
"remained" = "Remained"
|
||||||
"secAlertTitle" = "Security Alert"
|
"secAlertTitle" = "Security Alert"
|
||||||
"secAlertSsl" = "This connection is not secure; Please refrain from 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."
|
||||||
"security" = "Security"
|
"security" = "Security"
|
||||||
|
|
||||||
[menu]
|
[menu]
|
||||||
"dashboard" = "System Status"
|
"dashboard" = "OVERVIEW"
|
||||||
"inbounds" = "Inbounds"
|
"inbounds" = "INBOUNDS"
|
||||||
"settings" = "Panel Settings"
|
"settings" = "PANEL SETTINGS"
|
||||||
"xray" = "Xray Settings"
|
"xray" = "XRAY CONFIGS"
|
||||||
"logout" = "Logout"
|
"logout" = "LOG OUT"
|
||||||
"link" = "Other"
|
"link" = "Management"
|
||||||
|
|
||||||
[pages.login]
|
[pages.login]
|
||||||
"title" = "Login"
|
"title" = "Welcome"
|
||||||
"loginAgain" = "The login time limit has expired, please log in again"
|
"loginAgain" = "Your session has expired, please log in again"
|
||||||
|
|
||||||
[pages.login.toasts]
|
[pages.login.toasts]
|
||||||
"invalidFormData" = "Input data format is invalid."
|
"invalidFormData" = "The input data format is invalid"
|
||||||
"emptyUsername" = "Please enter username."
|
"emptyUsername" = "Username is required"
|
||||||
"emptyPassword" = "Please enter password."
|
"emptyPassword" = "Password is required"
|
||||||
"wrongUsernameOrPassword" = "Invalid username or password."
|
"wrongUsernameOrPassword" = "The username or password is incorrect"
|
||||||
"successLogin" = "Login"
|
"successLogin" = "Login"
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "System Status"
|
"title" = "Overview"
|
||||||
"memory" = "Memory"
|
"memory" = "RAM"
|
||||||
"hard" = "Hard Disk"
|
"hard" = "Disk"
|
||||||
"xrayStatus" = "Xray Status"
|
"xrayStatus" = "Status"
|
||||||
"stopXray" = "Stop"
|
"stopXray" = "Stop"
|
||||||
"restartXray" = "Restart"
|
"restartXray" = "Restart"
|
||||||
"xraySwitch" = "Switch Version"
|
"xraySwitch" = "Change Xray Version"
|
||||||
"xraySwitchClick" = "Choose the version you want to switch to."
|
"xraySwitchClick" = "Choose the version you want to switch."
|
||||||
"xraySwitchClickDesk" = "Choose wisely, as older versions may not be compatible with current configurations."
|
"xraySwitchClickDesk" = "Choose carefully, as older versions may not be compatible with the current configurations."
|
||||||
"operationHours" = "Operation Hours"
|
"operationHours" = "Uptime"
|
||||||
"operationHoursDesc" = "System uptime: time since startup."
|
"operationHoursDesc" = "Time since startup"
|
||||||
"systemLoad" = "System Load"
|
"systemLoad" = "System Load"
|
||||||
"connectionCount" = "Connection Count"
|
"connectionTcpCountDesc" = "Total TCP connections across all networks"
|
||||||
"connectionCountDesc" = "Total connections across all network cards."
|
"connectionUdpCountDesc" = "Total UDP connections across all networks"
|
||||||
"upSpeed" = "Total upload speed for all network cards."
|
"upSpeed" = "Overall upload speed across all networks"
|
||||||
"downSpeed" = "Total download speed for all network cards."
|
"downSpeed" = "Overall download speed across all networks"
|
||||||
"totalSent" = "Total upload traffic of all network cards since system startup."
|
"totalSent" = "Total traffic sent across all networks since OS startup"
|
||||||
"totalReceive" = "Total download data across all network cards since system startup."
|
"totalReceive" = "Total traffic received across all networks since OS startup"
|
||||||
"xraySwitchVersionDialog" = "Switch Xray Version"
|
"xraySwitchVersionDialog" = "Change Xray Version"
|
||||||
"xraySwitchVersionDialogDesc" = "Are you sure you want to switch the Xray version to"
|
"xraySwitchVersionDialogDesc" = "Are you sure you want to change the Xray version to"
|
||||||
"dontRefresh" = "Installation is in progress, please do not refresh this page."
|
"dontRefresh" = "Installation is in progress, please do not refresh this page."
|
||||||
"logs" = "Logs"
|
"logs" = "Logs"
|
||||||
"config" = "Config"
|
"config" = "Config"
|
||||||
"backup" = "Backup & Restore"
|
"backup" = "Backup & Restore"
|
||||||
"backupTitle" = "Backup & Restore Database"
|
"backupTitle" = "Backup & Restore Database"
|
||||||
"backupDescription" = "Remember to backup before importing a new database."
|
"backupDescription" = "It is recommended to make a backup before importing a new database."
|
||||||
"exportDatabase" = "Download Database"
|
"exportDatabase" = "Backup"
|
||||||
"importDatabase" = "Upload Database"
|
"importDatabase" = "Restore"
|
||||||
|
|
||||||
[pages.inbounds]
|
[pages.inbounds]
|
||||||
"title" = "Inbounds"
|
"title" = "Inbounds"
|
||||||
"totalDownUp" = "Total Uploads/Downloads"
|
"totalDownUp" = "Total Sent/Received"
|
||||||
"totalUsage" = "Total Usage"
|
"totalUsage" = "Total Usage"
|
||||||
"inboundCount" = "Number of Inbounds"
|
"inboundCount" = "Total Inbounds"
|
||||||
"operate" = "Menu"
|
"operate" = "Menu"
|
||||||
"enable" = "Enable"
|
"enable" = "Enabled"
|
||||||
"remark" = "Remark"
|
"remark" = "Remark"
|
||||||
"protocol" = "Protocol"
|
"protocol" = "Protocol"
|
||||||
"port" = "Port"
|
"port" = "Port"
|
||||||
"traffic" = "Traffic"
|
"traffic" = "Traffic"
|
||||||
"details" = "Details"
|
"details" = "Details"
|
||||||
"transportConfig" = "Transport Config"
|
"transportConfig" = "Transport Config"
|
||||||
"expireDate" = "Expire Date"
|
"expireDate" = "Expiry Date"
|
||||||
"resetTraffic" = "Reset Traffic"
|
"resetTraffic" = "Reset Traffic"
|
||||||
"addInbound" = "Add Inbound"
|
"addInbound" = "Add Inbound"
|
||||||
"generalActions" = "General Actions"
|
"generalActions" = "General Actions"
|
||||||
@@ -128,16 +128,16 @@
|
|||||||
"deleteClient" = "Delete Client"
|
"deleteClient" = "Delete Client"
|
||||||
"deleteClientContent" = "Are you sure you want to delete client?"
|
"deleteClientContent" = "Are you sure you want to delete client?"
|
||||||
"resetTrafficContent" = "Are you sure you want to reset traffic?"
|
"resetTrafficContent" = "Are you sure you want to reset traffic?"
|
||||||
"copyLink" = "Copy Link"
|
"copyLink" = "Copy URL"
|
||||||
"address" = "Address"
|
"address" = "Address"
|
||||||
"network" = "Network"
|
"network" = "Network"
|
||||||
"destinationPort" = "Destination Port"
|
"destinationPort" = "Destination Port"
|
||||||
"targetAddress" = "Target Address"
|
"targetAddress" = "Target Address"
|
||||||
"monitorDesc" = "Leave blank by default"
|
"monitorDesc" = "Leave blank to listen on all IPs"
|
||||||
"meansNoLimit" = "Means No Limit"
|
"meansNoLimit" = "Means no limit"
|
||||||
"totalFlow" = "Total Flow"
|
"totalFlow" = "Total Flow"
|
||||||
"leaveBlankToNeverExpire" = "Leave blank to never expire"
|
"leaveBlankToNeverExpire" = "Leave blank to never expire"
|
||||||
"noRecommendKeepDefault" = "No special requirements 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"
|
"publicKeyPath" = "Public Key Path"
|
||||||
@@ -146,36 +146,40 @@
|
|||||||
"keyContent" = "Private Key Content"
|
"keyContent" = "Private Key Content"
|
||||||
"clickOnQRcode" = "Click on QR Code to Copy"
|
"clickOnQRcode" = "Click on QR Code to Copy"
|
||||||
"client" = "Client"
|
"client" = "Client"
|
||||||
"export" = "Export Links"
|
"export" = "Export All URLs"
|
||||||
"clone" = "Clone"
|
"clone" = "Clone"
|
||||||
"cloneInbound" = "Clone"
|
"cloneInbound" = "Clone"
|
||||||
"cloneInboundContent" = "All settings of this inbound, except for Port, Listening IP, and Clients, will be applied to the clone."
|
"cloneInboundContent" = "All settings for this inbound, except Port, Listening IP, and Clients, will be applied to the clone."
|
||||||
"cloneInboundOk" = "Clone"
|
"cloneInboundOk" = "Clone"
|
||||||
"resetAllTraffic" = "Reset All Inbounds Traffic"
|
"resetAllTraffic" = "Reset All Inbounds Traffic"
|
||||||
"resetAllTrafficTitle" = "Reset all inbounds traffic"
|
"resetAllTrafficTitle" = "Reset All Inbounds Traffic"
|
||||||
"resetAllTrafficContent" = "Are you sure you want to reset all inbounds traffic?"
|
"resetAllTrafficContent" = "Are you sure you want to reset the traffic of all inbounds?"
|
||||||
"resetInboundClientTraffics" = "Reset Clients Traffic"
|
"resetInboundClientTraffics" = "Reset Clients Traffic"
|
||||||
"resetInboundClientTrafficTitle" = "Reset all clients traffic"
|
"resetInboundClientTrafficTitle" = "Reset Clients Traffic"
|
||||||
"resetInboundClientTrafficContent" = "Are you sure you want to reset all traffic for this inbound's clients?"
|
"resetInboundClientTrafficContent" = "Are you sure you want to reset the traffic of this inbound's clients?"
|
||||||
"resetAllClientTraffics" = "Reset All Clients Traffic"
|
"resetAllClientTraffics" = "Reset All Clients Traffic"
|
||||||
"resetAllClientTrafficTitle" = "Reset all clients traffic"
|
"resetAllClientTrafficTitle" = "Reset All Clients Traffic"
|
||||||
"resetAllClientTrafficContent" = "Are you sure you want to reset all traffics for all clients?"
|
"resetAllClientTrafficContent" = "Are you sure you want to reset the traffic of all clients?"
|
||||||
"delDepletedClients" = "Delete Depleted Clients"
|
"delDepletedClients" = "Delete Depleted Clients"
|
||||||
"delDepletedClientsTitle" = "Delete depleted clients"
|
"delDepletedClientsTitle" = "Delete Depleted Clients"
|
||||||
"delDepletedClientsContent" = "Are you sure you want to delete all depleted clients?"
|
"delDepletedClientsContent" = "Are you sure you want to delete all the depleted clients?"
|
||||||
"email" = "Email"
|
"email" = "Email"
|
||||||
"emailDesc" = "Please provide a unique email address."
|
"emailDesc" = "Please provide a unique email address"
|
||||||
"setDefaultCert" = "Set cert from panel"
|
"setDefaultCert" = "Set Cert from Panel"
|
||||||
"telegramDesc" = "Use Telegram ID without @ or chat IDs ( you can get it here @userinfobot or use '/id' command in bot )"
|
"telegramDesc" = "Please provide Telegram or Chat ID(s) without using the '@'. (get it here @userinfobot) or (use '/id' command in the bot)"
|
||||||
"subscriptionDesc" = "You can find your sub link on Details, also you can use the same name for several configurations"
|
"subscriptionDesc" = "To find your subscription URL, navigate to the 'Details'. Additionally, you can use the same name for several clients."
|
||||||
"info" = "Info"
|
"info" = "Info"
|
||||||
"same" = "Same"
|
"same" = "Same"
|
||||||
|
"inboundData" = "Inbound's Data"
|
||||||
|
"copyToClipboard" = "Copy to Clipboard"
|
||||||
|
"import" = "Import"
|
||||||
|
"importInbound" = "Import an Inbound"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Add Client"
|
"add" = "Add Client"
|
||||||
"edit" = "Edit Client"
|
"edit" = "Edit Client"
|
||||||
"submitAdd" = "Add Client"
|
"submitAdd" = "Add Client"
|
||||||
"submitEdit" = "Save changes"
|
"submitEdit" = "Save Changes"
|
||||||
"clientCount" = "Number of Clients"
|
"clientCount" = "Number of Clients"
|
||||||
"bulk" = "Add Bulk"
|
"bulk" = "Add Bulk"
|
||||||
"method" = "Method"
|
"method" = "Method"
|
||||||
@@ -183,175 +187,187 @@
|
|||||||
"last" = "Last"
|
"last" = "Last"
|
||||||
"prefix" = "Prefix"
|
"prefix" = "Prefix"
|
||||||
"postfix" = "Postfix"
|
"postfix" = "Postfix"
|
||||||
"delayedStart" = "Start after first use"
|
"delayedStart" = "Start after First Use"
|
||||||
"expireDays" = "Expire days"
|
"expireDays" = "Duration"
|
||||||
"days" = "day(s)"
|
"days" = "Day(s)"
|
||||||
"renew" = "Auto renew"
|
"renew" = "Auto Renew"
|
||||||
"renewDesc" = "Auto renew days after expiration. 0 = disable"
|
"renewDesc" = "Auto-renewal after expiration. (0 = disable)(unit: day)"
|
||||||
|
|
||||||
[pages.inbounds.toasts]
|
[pages.inbounds.toasts]
|
||||||
"obtain" = "Obtain"
|
"obtain" = "Obtain"
|
||||||
|
|
||||||
[pages.inbounds.stream.general]
|
[pages.inbounds.stream.general]
|
||||||
"requestHeader" = "Request header"
|
"requestHeader" = "Request Header"
|
||||||
"name" = "Name"
|
"name" = "Name"
|
||||||
"value" = "Value"
|
"value" = "Value"
|
||||||
|
|
||||||
[pages.inbounds.stream.tcp]
|
[pages.inbounds.stream.tcp]
|
||||||
"requestVersion" = "Request version"
|
"requestVersion" = "Request Version"
|
||||||
"requestMethod" = "Request method"
|
"requestMethod" = "Request Method"
|
||||||
"requestPath" = "Request path"
|
"requestPath" = "Request Path"
|
||||||
"responseVersion" = "Response version"
|
"responseVersion" = "Response Version"
|
||||||
"responseStatus" = "Response status"
|
"responseStatus" = "Response Status"
|
||||||
"responseStatusDescription" = "Response status description"
|
"responseStatusDescription" = "Response Status Description"
|
||||||
"responseHeader" = "Response header"
|
"responseHeader" = "Response Header"
|
||||||
|
|
||||||
[pages.inbounds.stream.quic]
|
[pages.inbounds.stream.quic]
|
||||||
"encryption" = "Encryption"
|
"encryption" = "Encryption"
|
||||||
|
|
||||||
[pages.settings]
|
[pages.settings]
|
||||||
"title" = "Settings"
|
"title" = "Panel Settings"
|
||||||
"save" = "Save"
|
"save" = "Save"
|
||||||
"infoDesc" = "Every change made here needs to be saved. Please restart the panel for the changes to take effect."
|
"infoDesc" = "Every change made here needs to be saved. Please restart the panel for the changes to take effect."
|
||||||
"restartPanel" = "Restart Panel "
|
"restartPanel" = "Restart Panel"
|
||||||
"restartPanelDesc" = "Are you sure you want to restart the panel? Click OK to restart after 3 seconds. If you cannot access the panel after restarting, please view the panel log information on the server."
|
"restartPanelDesc" = "Are you sure you want to restart the panel? If you are unable to access the panel after restarting, please check the logs in the terminal script"
|
||||||
"resetDefaultConfig" = "Reset to default config"
|
"resetDefaultConfig" = "Reset to Default"
|
||||||
"panelConfig" = "Panel Configurations"
|
"panelConfig" = "Configuration"
|
||||||
"userSettings" = "User Settings"
|
"userSettings" = "Authentication"
|
||||||
"TGBotSettings" = "Telegram Bot Settings"
|
"TGBotSettings" = "Telegram Bot"
|
||||||
"panelListeningIP" = "Panel Listening IP"
|
"panelListeningIP" = "Listen IP"
|
||||||
"panelListeningIPDesc" = "Leave blank by default to monitor all IPs."
|
"panelListeningIPDesc" = "The IP address for the web panel. (leave blank to listen on all IPs)"
|
||||||
"panelListeningDomain" = "Panel Listening Domain"
|
"panelListeningDomain" = "Listen Domain"
|
||||||
"panelListeningDomainDesc" = "Leave blank by default to monitor all domains and IPs"
|
"panelListeningDomainDesc" = "The domain name for the web panel. (leave blank to listen on all domains and IPs)"
|
||||||
"panelPort" = "Panel Port"
|
"panelPort" = "Listen Port"
|
||||||
"panelPortDesc" = "Port number for serving the panel."
|
"panelPortDesc" = "The port number for the web panel. (must be an unused port)"
|
||||||
"publicKeyPath" = "Panel Certificate Public Key File Path"
|
"publicKeyPath" = "Public Key Path"
|
||||||
"publicKeyPathDesc" = "Fill in an absolute path starting with '/'"
|
"publicKeyPathDesc" = "The public key file path for the web panel. (begins with ‘/‘)"
|
||||||
"privateKeyPath" = "Panel Certificate Private Key File Path"
|
"privateKeyPath" = "Private Key Path"
|
||||||
"privateKeyPathDesc" = "Fill in an absolute path starting with '/'"
|
"privateKeyPathDesc" = "The private key file path for the web panel. (begins with ‘/‘)"
|
||||||
"panelUrlPath" = "Panel URL Root Path"
|
"panelUrlPath" = "URI Path"
|
||||||
"panelUrlPathDesc" = "Must start with '/' and end with '/'"
|
"panelUrlPathDesc" = "The URI path for the web panel. (begins with ‘/‘ and concludes with ‘/‘)"
|
||||||
"pageSize" = "Pagination size"
|
"pageSize" = "Pagination Size"
|
||||||
"pageSizeDesc" = "Define page size for inbounds table. Set 0 to disable"
|
"pageSizeDesc" = "Define page size for the inbounds table. (0 = disable)"
|
||||||
|
"remarkModel" = "Remark Model & Separation Character"
|
||||||
|
"sampleRemark" = "Sample Remark"
|
||||||
"oldUsername" = "Current Username"
|
"oldUsername" = "Current Username"
|
||||||
"currentPassword" = "Current Password"
|
"currentPassword" = "Current Password"
|
||||||
"newUsername" = "New Username"
|
"newUsername" = "New Username"
|
||||||
"newPassword" = "New Password"
|
"newPassword" = "New Password"
|
||||||
"telegramBotEnable" = "Enable Telegram bot"
|
"telegramBotEnable" = "Enable Telegram Bot"
|
||||||
"telegramBotEnableDesc" = "Your telegram bot will interact with the panel"
|
"telegramBotEnableDesc" = "Enables the Telegram bot."
|
||||||
"telegramToken" = "Telegram Token"
|
"telegramToken" = "Telegram Token"
|
||||||
"telegramTokenDesc" = "The Token you have got from @BotFather"
|
"telegramTokenDesc" = "The Telegram bot token obtained from '@BotFather'."
|
||||||
"telegramChatId" = "Telegram Admin ChatIDs"
|
"telegramChatId" = "Admin Chat ID"
|
||||||
"telegramChatIdDesc" = "Multiple Chat IDs separated by comma. use @userinfobot or use '/id' command in bot to get your Chat IDs."
|
"telegramChatIdDesc" = "The Telegram Admin Chat ID(s). (comma-separated)(use @userinfobot) or (use '/id' command in the bot)"
|
||||||
"telegramNotifyTime" = "Telegram bot notification time"
|
"telegramNotifyTime" = "Notification Time"
|
||||||
"telegramNotifyTimeDesc" = "Use Crontab timing format."
|
"telegramNotifyTimeDesc" = "The Telegram bot notification time set for periodic reports. (use the crontab time format)"
|
||||||
"tgNotifyBackup" = "Database Backup"
|
"tgNotifyBackup" = "Database Backup"
|
||||||
"tgNotifyBackupDesc" = "Send database backup file with report notification"
|
"tgNotifyBackupDesc" = "Send a database backup file with a report."
|
||||||
"tgNotifyLogin" = "Login Notification"
|
"tgNotifyLogin" = "Login Notification"
|
||||||
"tgNotifyLoginDesc" = "Displays the username, IP address, and time when someone tries to log into your panel."
|
"tgNotifyLoginDesc" = "Get notified about the username, IP address, and time whenever someone attempts to log into your web panel."
|
||||||
"sessionMaxAge" = "Session maximum age"
|
"sessionMaxAge" = "Session Duration"
|
||||||
"sessionMaxAgeDesc" = "The time that you can stay login (unit: minute)"
|
"sessionMaxAgeDesc" = "The duration for which you can stay logged in. (unit: minute)"
|
||||||
"expireTimeDiff" = "Expiration threshold for notification"
|
"expireTimeDiff" = "Client Expiration Threshold Notification"
|
||||||
"expireTimeDiffDesc" = "Get notified about account expiration before the threshold (unit: day)"
|
"expireTimeDiffDesc" = "Get notified about client expiration when reaching this threshold. (unit: day)"
|
||||||
"trafficDiff" = "Traffic threshold for notification"
|
"trafficDiff" = "Traffic Exhaustion Threshold Notification"
|
||||||
"trafficDiffDesc" = "Get notified about traffic exhaustion before reaching the threshold (unit: GB)"
|
"trafficDiffDesc" = "Get notified about traffic exhaustion when reaching this threshold. (unit: GB)"
|
||||||
"tgNotifyCpu" = "CPU percentage alert threshold"
|
"tgNotifyCpu" = "CPU Load Threshold Notification"
|
||||||
"tgNotifyCpuDesc" = "Receive notification if CPU usage exceeds this threshold (unit: %)"
|
"tgNotifyCpuDesc" = "Get notified if CPU usage exceeds this threshold. (unit: %)"
|
||||||
"timeZone" = "Time Zone"
|
"timeZone" = "Time Zone"
|
||||||
"timeZoneDesc" = "Scheduled tasks run according to the time in this time zone."
|
"timeZoneDesc" = "Scheduled tasks run based on this time zone."
|
||||||
"subSettings" = "Subscription"
|
"subSettings" = "Subscription"
|
||||||
"subEnable" = "Enable service"
|
"subEnable" = "Enable Subscription Service"
|
||||||
"subEnableDesc" = "Subscription feature with separate configuration"
|
"subEnableDesc" = "Enables the subscription service."
|
||||||
"subListen" = "Listening IP"
|
"subListen" = "Listen IP"
|
||||||
"subListenDesc" = "Leave blank by default to monitor all IPs"
|
"subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)"
|
||||||
"subPort" = "Subscription Port"
|
"subPort" = "Listen Port"
|
||||||
"subPortDesc" = "Port number for serving the subscription service must be unused in server"
|
"subPortDesc" = "The port number for the subscription service. (must be an unused port)"
|
||||||
"subCertPath" = "Subscription Certificate Public Key File Path"
|
"subCertPath" = "Public Key Path"
|
||||||
"subCertPathDesc" = "Fill in an absolute path starting with '/'"
|
"subCertPathDesc" = "The public key file path for the subscription service. (begins with ‘/‘)"
|
||||||
"subKeyPath" = "Subscription Certificate Private Key File Path"
|
"subKeyPath" = "Private Key Path"
|
||||||
"subKeyPathDesc" = "Fill in an absolute path starting with '/'"
|
"subKeyPathDesc" = "The private key file path for the subscription service. (begins with ‘/‘)"
|
||||||
"subPath" = "Subscription URL Root Path"
|
"subPath" = "URI Path"
|
||||||
"subPathDesc" = "Must start with '/' and end with '/'"
|
"subPathDesc" = "The URI path for the subscription service. (begins with ‘/‘ and concludes with ‘/‘)"
|
||||||
"subDomain" = "Listening Domain"
|
"subDomain" = "Listen Domain"
|
||||||
"subDomainDesc" = "Leave blank by default to monitor all domains and IPs"
|
"subDomainDesc" = "The domain name for the subscription service. (leave blank to listen on all domains and IPs)"
|
||||||
"subUpdates" = "Subscription update intervals"
|
"subUpdates" = "Update Intervals"
|
||||||
"subUpdatesDesc" = "Interval hours between updates in client application"
|
"subUpdatesDesc" = "The update intervals of the subscription URL in the client apps. (unit: hour)"
|
||||||
"subEncrypt" = "Encrypt configs"
|
"subEncrypt" = "Encode"
|
||||||
"subEncryptDesc" = "Encrypt the returned configs in subscription"
|
"subEncryptDesc" = "The returned content of subscription service will be Base64 encoded."
|
||||||
"subShowInfo" = "Show usage info"
|
"subShowInfo" = "Show Usage Info"
|
||||||
"subShowInfoDesc" = "Show remianed traffic and date after config name"
|
"subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps."
|
||||||
"subURI" = "Reverse Proxy URI"
|
"subURI" = "Reverse Proxy URI"
|
||||||
"subURIDesc" = "Change base URI of subscription URL for using on behind of proxies"
|
"subURIDesc" = "The URI path of the subscription URL for use behind proxies."
|
||||||
|
|
||||||
[pages.settings.toasts]
|
[pages.settings.toasts]
|
||||||
"modifySettings" = "Modify Settings "
|
"modifySettings" = "Modify Settings"
|
||||||
"getSettings" = "Get Settings "
|
"getSettings" = "Get Settings"
|
||||||
"modifyUser" = "Modify User "
|
"modifyUser" = "Modify User"
|
||||||
"originalUserPassIncorrect" = "Incorrect original username or password"
|
"originalUserPassIncorrect" = "The current username or password is incorrect"
|
||||||
"userPassMustBeNotEmpty" = "New username and new password cannot be empty"
|
"userPassMustBeNotEmpty" = "The new username or password is required"
|
||||||
|
|
||||||
[pages.xray]
|
[pages.xray]
|
||||||
"title" = "Xray Settings"
|
"title" = "Xray Configs"
|
||||||
"save" = "Save Settings"
|
"save" = "Save"
|
||||||
"restart" = "Reastart Xray"
|
"restart" = "Restart Xray"
|
||||||
"basicTemplate" = "Basic Template"
|
"basicTemplate" = "Basic"
|
||||||
"advancedTemplate" = "Advanced Template"
|
"advancedTemplate" = "Advanced"
|
||||||
"generalConfigs" = "General Configs"
|
"generalConfigs" = "General Strategy"
|
||||||
"generalConfigsDesc" = "These options will provide general adjustments."
|
"generalConfigsDesc" = "These options will determine general strategy adjustments."
|
||||||
"blockConfigs" = "Blocking Configs"
|
"blockConfigs" = "Protection Shield"
|
||||||
"blockConfigsDesc" = "These options will prevent users from connecting to specific protocols and websites."
|
"blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites."
|
||||||
"blockCountryConfigs" = "Block Country Configs"
|
"blockCountryConfigs" = "Block Country"
|
||||||
"blockCountryConfigsDesc" = "These options will prevent users from connecting to specific country domains."
|
"blockCountryConfigsDesc" = "These options will block traffic based on the specific requested country."
|
||||||
"directCountryConfigs" = "Direct Country Configs"
|
"directCountryConfigs" = "Direct Country"
|
||||||
"directCountryConfigsDesc" = "These options will connect users directly to specific country domains."
|
"directCountryConfigsDesc" = "These options will directly forward traffic based on the specific requested country."
|
||||||
"ipv4Configs" = "IPv4 Configs"
|
"ipv4Configs" = "IPv4 Routing"
|
||||||
"ipv4ConfigsDesc" = "These options will route to target domains only via IPv4."
|
"ipv4ConfigsDesc" = "These options will route requests to destination only via IPv4."
|
||||||
"Template" = "Xray Configuration Template"
|
"warpConfigs" = "WARP Routing"
|
||||||
"TemplateDesc" = "Generate the final Xray configuration file based on this template."
|
"warpConfigsDesc" = "WARP will route traffic to websites through Cloudflare servers."
|
||||||
"FreedomStrategy" = "Configure Strategy for Freedom Protocol"
|
"Template" = "Advanced Xray Configuration Template"
|
||||||
"FreedomStrategyDesc" = "Set the output strategy of the network in the Freedom Protocol."
|
"TemplateDesc" = "The final Xray configuration file will be generated based on this template."
|
||||||
"RoutingStrategy" = "Configure Domains Routing Strategy"
|
"FreedomStrategy" = "Freedom Protocol Strategy"
|
||||||
"RoutingStrategyDesc" = "Set the overall routing strategy for DNS resolving."
|
"FreedomStrategyDesc" = "Set the output strategy for the network in the Freedom Protocol."
|
||||||
"Torrent" = "Ban BitTorrent Usage"
|
"RoutingStrategy" = "Overall Routing Strategy"
|
||||||
"TorrentDesc" = "Change the configuration template to avoid using BitTorrent by users."
|
"RoutingStrategyDesc" = "Set the overall traffic routing strategy for resolving all requests."
|
||||||
"PrivateIp" = "Ban Private IP Ranges to Connect"
|
"Torrent" = "Block BitTorrent Protocol"
|
||||||
"PrivateIpDesc" = "Change the configuration template to avoid connecting to private IP ranges."
|
"TorrentDesc" = "Blocks BitTorrent protocol."
|
||||||
|
"PrivateIp" = "Block Connection to Private IPs"
|
||||||
|
"PrivateIpDesc" = "Blocks establishing connections to private IP ranges."
|
||||||
"Ads" = "Block Ads"
|
"Ads" = "Block Ads"
|
||||||
"AdsDesc" = "Change the configuration template to block ads"
|
"AdsDesc" = "Blocks advertising websites."
|
||||||
"Family" = "Enable Family-Friendly Configuration"
|
"Family" = "Family Protection"
|
||||||
"FamilyDesc" = "Avoid connecting to unsafe websites for family protection."
|
"FamilyDesc" = "Blocks adult content, and malware websites."
|
||||||
"IRIp" = "Disable connection to Iran IP ranges"
|
"IRIp" = "Block Connection to Iran IPs"
|
||||||
"IRIpDesc" = "Change the configuration template to avoid connecting to Iran IP ranges."
|
"IRIpDesc" = "Blocks establishing connections to Iran IP ranges."
|
||||||
"IRDomain" = "Disable connection to Iran domains"
|
"IRDomain" = "Block Connection to Iran Domains"
|
||||||
"IRDomainDesc" = "Change the configuration template to avoid connecting to Iran domains."
|
"IRDomainDesc" = "Blocks establishing connections to Iran domains."
|
||||||
"ChinaIp" = "Disable connection to China IP ranges"
|
"ChinaIp" = "Block Connection to China IPs"
|
||||||
"ChinaIpDesc" = "Change the configuration template to avoid connecting to China IP ranges."
|
"ChinaIpDesc" = "Blocks establishing connections to China IP ranges."
|
||||||
"ChinaDomain" = "Disable connection to China domains"
|
"ChinaDomain" = "Block Connection to China Domains"
|
||||||
"ChinaDomainDesc" = "Change the configuration template to avoid connecting to China domains."
|
"ChinaDomainDesc" = "Blocks establishing connections to China domains."
|
||||||
"RussiaIp" = "Disable connection to Russia IP ranges"
|
"RussiaIp" = "Block Connection to Russia IPs"
|
||||||
"RussiaIpDesc" = "Change the configuration template to avoid connecting to Russia IP ranges."
|
"RussiaIpDesc" = "Blocks establishing connections to Russia IP ranges."
|
||||||
"RussiaDomain" = "Disable connection to Russia domains"
|
"RussiaDomain" = "Block Connection to Russia Domains"
|
||||||
"RussiaDomainDesc" = "Change the configuration template to avoid connecting to Russia domains."
|
"RussiaDomainDesc" = "Blocks establishing connections to Russia domains."
|
||||||
"DirectIRIp" = "Direct connection to Iran IP ranges"
|
"DirectIRIp" = "Direct Connection to Iran IPs"
|
||||||
"DirectIRIpDesc" = "Change the configuration template for direct connecting to Iran IP ranges."
|
"DirectIRIpDesc" = "Directly establishes connections to Iran IP ranges."
|
||||||
"DirectIRDomain" = "Direct connection to Iran domains"
|
"DirectIRDomain" = "Direct Connection to Iran Domains"
|
||||||
"DirectIRDomainDesc" = "Change the configuration template for direct connecting to Iran domains."
|
"DirectIRDomainDesc" = "Directly establishes connections to Iran domains."
|
||||||
"DirectChinaIp" = "Direct connection to China IP ranges"
|
"DirectChinaIp" = "Direct Connection to China IPs"
|
||||||
"DirectChinaIpDesc" = "Change the configuration template for direct connecting to China IP ranges."
|
"DirectChinaIpDesc" = "Directly establishes connections to China IP ranges."
|
||||||
"DirectChinaDomain" = "Direct connection to China domains"
|
"DirectChinaDomain" = "Direct Connection to China Domains"
|
||||||
"DirectChinaDomainDesc" = "Change the configuration template for direct connecting to China domains."
|
"DirectChinaDomainDesc" = "Directly establishes connections to China domains."
|
||||||
"DirectRussiaIp" = "Direct connection to Russia IP ranges"
|
"DirectRussiaIp" = "Direct Connection to Russia IPs"
|
||||||
"DirectRussiaIpDesc" = "Change the configuration template for direct connecting to Russia IP ranges."
|
"DirectRussiaIpDesc" = "Directly establishes connections to Russia IP ranges."
|
||||||
"DirectRussiaDomain" = "Direct connection to Russia domains"
|
"DirectRussiaDomain" = "Direct Connection to Russia Domains"
|
||||||
"DirectRussiaDomainDesc" = "Change the configuration template for direct connecting to Russia domains."
|
"DirectRussiaDomainDesc" = "Directly establishes connections to Russia domains."
|
||||||
"GoogleIPv4" = "Use IPv4 for Google"
|
"GoogleIPv4" = "Google"
|
||||||
"GoogleIPv4Desc" = "Add routing for Google to connect with IPv4."
|
"GoogleIPv4Desc" = "Routes traffic to Google via IPv4."
|
||||||
"NetflixIPv4" = "Use IPv4 for Netflix"
|
"NetflixIPv4" = "Netflix"
|
||||||
"NetflixIPv4Desc" = "Add routing for Netflix to connect with IPv4."
|
"NetflixIPv4Desc" = "Routes traffic to Netflix via IPv4."
|
||||||
|
"GoogleWARP" = "Google"
|
||||||
|
"GoogleWARPDesc" = "Routes traffic to Google via WARP."
|
||||||
|
"OpenAIWARP" = "ChatGPT"
|
||||||
|
"OpenAIWARPDesc" = "Routes traffic to OpenAI (ChatGPT) via WARP."
|
||||||
|
"NetflixWARP" = "Netflix"
|
||||||
|
"NetflixWARPDesc" = "Routes traffic to Netflix via WARP."
|
||||||
|
"SpotifyWARP" = "Spotify"
|
||||||
|
"SpotifyWARPDesc" = "Routes traffic to Spotify via WARP."
|
||||||
"completeTemplate" = "All"
|
"completeTemplate" = "All"
|
||||||
"Inbounds" = "Inbounds"
|
"Inbounds" = "Inbounds"
|
||||||
"Outbounds" = "Outbounds"
|
"Outbounds" = "Outbounds"
|
||||||
"Routings" = "Routing rules"
|
"Routings" = "Routing Rules"
|
||||||
"RoutingsDesc" = "The priority of each rule is important!"
|
"RoutingsDesc" = "The priority of each rule is important!"
|
||||||
|
|
||||||
[pages.xray.rules]
|
[pages.xray.rules]
|
||||||
@@ -366,14 +382,15 @@
|
|||||||
"info" = "Info"
|
"info" = "Info"
|
||||||
"add" = "Add Rule"
|
"add" = "Add Rule"
|
||||||
"edit" = "Edit Rule"
|
"edit" = "Edit Rule"
|
||||||
"useComma" = "Comma separated items"
|
"useComma" = "Comma-separated items"
|
||||||
|
|
||||||
[pages.xray.outbound]
|
[pages.xray.outbound]
|
||||||
"addOutbound" = "Add outbound"
|
"addOutbound" = "Add Outbound"
|
||||||
"addReverse" = "Add reverse"
|
"addReverse" = "Add Reverse"
|
||||||
"editOutbound" = "Edit outbound"
|
"editOutbound" = "Edit Outbound"
|
||||||
"editReverse" = "Edit reverse"
|
"editReverse" = "Edit Reverse"
|
||||||
"tag" = "Tag"
|
"tag" = "Tag"
|
||||||
|
"tagDesc" = "Unique Tag"
|
||||||
"address" = "Address"
|
"address" = "Address"
|
||||||
"reverse" = "Reverse"
|
"reverse" = "Reverse"
|
||||||
"domain" = "Domain"
|
"domain" = "Domain"
|
||||||
@@ -382,6 +399,14 @@
|
|||||||
"portal" = "Portal"
|
"portal" = "Portal"
|
||||||
"intercon" = "Interconnection"
|
"intercon" = "Interconnection"
|
||||||
|
|
||||||
|
[pages.xray.wireguard]
|
||||||
|
"secretKey" = "Secret Key"
|
||||||
|
"publicKey" = "Public Key"
|
||||||
|
"allowedIPs" = "Allowed IPs"
|
||||||
|
"endpoint" = "Endpoint"
|
||||||
|
"psk" = "PreShared Key"
|
||||||
|
"domainStrategy" = "Domain Strategy"
|
||||||
|
|
||||||
[tgbot]
|
[tgbot]
|
||||||
"noResult" = "❗ No result!"
|
"noResult" = "❗ No result!"
|
||||||
"wentWrong" = "❌ Something went wrong!"
|
"wentWrong" = "❌ Something went wrong!"
|
||||||
@@ -394,41 +419,41 @@
|
|||||||
"clients" = "Clients"
|
"clients" = "Clients"
|
||||||
|
|
||||||
[tgbot.commands]
|
[tgbot.commands]
|
||||||
"unknown" = "❗ Unknown command"
|
"unknown" = "❗️Unknown command"
|
||||||
"pleaseChoose" = "👇 Please choose:\r\n"
|
"pleaseChoose" = "👇 Please choose:\r\n"
|
||||||
"help" = "🤖 Welcome to this bot! It's designed to offer you specific data from the server, and it allows you to make modifications as needed.\r\n\r\n"
|
"help" = "🤖 Welcome to this bot! It's designed to provide you with specific data from the web panel and allows you to make modifications as needed.\r\n\r\n"
|
||||||
"start" = "👋 Hello <i>{{ .Firstname }}</i>.\r\n"
|
"start" = "👋 Hello <i>{{ .Firstname }}</i>.\r\n"
|
||||||
"welcome" = "🤖 Welcome to <b>{{ .Hostname }}</b> management bot.\r\n"
|
"welcome" = "🤖 Welcome to <b>{{ .Hostname }}</b> management bot.\r\n"
|
||||||
"status" = "✅ Bot is ok!"
|
"status" = "✅ Bot is OK!"
|
||||||
"usage" = "❗ Please provide a text to search!"
|
"usage" = "❗️ Please provide a text to search!"
|
||||||
"getID" = "🆔 Your ID: <code>{{ .ID }}</code>"
|
"getID" = "🆔 Your ID: <code>{{ .ID }}</code>"
|
||||||
"helpAdminCommands" = "Search for a client email:\r\n<code>/usage [Email]</code>\r\n \r\nSearch for inbounds (with client stats):\r\n<code>/inbound [Remark]</code>"
|
"helpAdminCommands" = "Search for a client email:\r\n<code>/usage [Email]</code>\r\n\r\nSearch for inbounds (with client stats):\r\n<code>/inbound [Remark]</code>"
|
||||||
"helpClientCommands" = "To search for statistics, just use folowing command:\r\n \r\n<code>/usage [UUID|Password]</code>\r\n \r\nUse UUID for vmess/vless and Password for Trojan."
|
"helpClientCommands" = "To search for statistics, simply use the following command:\r\n\r\n<code>/usage [UUID|Password]</code>\r\n\r\nUse UUID for VMess/VLESS and password for Trojan/Shadowsocks."
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 The CPU usage {{ .Percent }}% is more than threshold {{ .Threshold }}%"
|
"cpuThreshold" = "🔴 CPU load {{ .Percent }}% = CPU load {{ .Percent }}% is more than the threshold of {{ .Threshold }}%"
|
||||||
"loginSuccess" = "✅ Successfully logged-in to the panel.\r\n"
|
"loginSuccess" = "✅ Logged in to the web panel successfully.\r\n"
|
||||||
"loginFailed" = "❗️ Login to the panel failed.\r\n"
|
"loginFailed" = "❗Log in to the web panel failed.\r\n"
|
||||||
"report" = "🕰 Scheduled Reports: {{ .RunTime }}\r\n"
|
"report" = "🕰 Scheduled reports: {{ .RunTime }}\r\n"
|
||||||
"datetime" = "⏰ Date-Time: {{ .DateTime }}\r\n"
|
"datetime" = "⏰ Date&Time: {{ .DateTime }}\r\n"
|
||||||
"hostname" = "💻 Hostname: {{ .Hostname }}\r\n"
|
"hostname" = "💻 Host: {{ .Hostname }}\r\n"
|
||||||
"version" = "🚀 X-UI Version: {{ .Version }}\r\n"
|
"version" = "🚀 X-UI: {{ .Version }}\r\n"
|
||||||
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
||||||
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
||||||
"ip" = "🌐 IP: {{ .IP }}\r\n"
|
"ip" = "🌐 IP: {{ .IP }}\r\n"
|
||||||
"serverUpTime" = "⏳ Server Uptime: {{ .UpTime }} {{ .Unit }}\r\n"
|
"serverUpTime" = "⏳ Uptime: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||||
"serverLoad" = "📈 Server Load: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
"serverLoad" = "📈 System load: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||||
"serverMemory" = "📋 Server Memory: {{ .Current }}/{{ .Total }}\r\n"
|
"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n"
|
||||||
"tcpCount" = "🔹 TcpCount: {{ .Count }}\r\n"
|
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
|
||||||
"udpCount" = "🔸 UdpCount: {{ .Count }}\r\n"
|
"udpCount" = "🔸 UDP: {{ .Count }}\r\n"
|
||||||
"traffic" = "🚦 Traffic: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 Traffic: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ Xray Status: {{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ Xray status: {{ .State }}\r\n"
|
||||||
"username" = "👤 Username: {{ .Username }}\r\n"
|
"username" = "👤 Username: {{ .Username }}\r\n"
|
||||||
"time" = "⏰ Time: {{ .Time }}\r\n"
|
"time" = "⏰ Time: {{ .Time }}\r\n"
|
||||||
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
||||||
"port" = "🔌 Port: {{ .Port }}\r\n"
|
"port" = "🔌 Port: {{ .Port }}\r\n"
|
||||||
"expire" = "📅 Expire Date: {{ .DateTime }}\r\n \r\n"
|
"expire" = "📅 Expire date: {{ .DateTime }}\r\n \r\n"
|
||||||
"expireIn" = "📅 Expire In: {{ .Time }}\r\n \r\n"
|
"expireIn" = "📅 Expire in: {{ .Time }}\r\n \r\n"
|
||||||
"active" = "💡 Active: {{ .Enable }}\r\n"
|
"active" = "💡 Active: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 Connection status: {{ .Status }}\r\n"
|
"online" = "🌐 Connection status: {{ .Status }}\r\n"
|
||||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||||
@@ -437,10 +462,10 @@
|
|||||||
"total" = "🔄 Total: {{ .UpDown }} / {{ .Total }}\r\n"
|
"total" = "🔄 Total: {{ .UpDown }} / {{ .Total }}\r\n"
|
||||||
"exhaustedMsg" = "🚨 Exhausted {{ .Type }}:\r\n"
|
"exhaustedMsg" = "🚨 Exhausted {{ .Type }}:\r\n"
|
||||||
"exhaustedCount" = "🚨 Exhausted {{ .Type }} count:\r\n"
|
"exhaustedCount" = "🚨 Exhausted {{ .Type }} count:\r\n"
|
||||||
"onlinesCount" = "🌐 Online clients count: {{ .Count }}\r\n"
|
"onlinesCount" = "🌐 Online clients: {{ .Count }}\r\n"
|
||||||
"disabled" = "🛑 Disabled: {{ .Disabled }}\r\n"
|
"disabled" = "🛑 Disabled: {{ .Disabled }}\r\n"
|
||||||
"depleteSoon" = "🔜 Deplete soon: {{ .Deplete }}\r\n \r\n"
|
"depleteSoon" = "🔜 Deplete soon: {{ .Deplete }}\r\n \r\n"
|
||||||
"backupTime" = "🗄 Backup Time: {{ .Time }}\r\n"
|
"backupTime" = "🗄 Backup time: {{ .Time }}\r\n"
|
||||||
"yes" = "✅ Yes"
|
"yes" = "✅ Yes"
|
||||||
"no" = "❌ No"
|
"no" = "❌ No"
|
||||||
|
|
||||||
@@ -448,12 +473,12 @@
|
|||||||
"dbBackup" = "Get DB Backup"
|
"dbBackup" = "Get DB Backup"
|
||||||
"serverUsage" = "Server Usage"
|
"serverUsage" = "Server Usage"
|
||||||
"getInbounds" = "Get Inbounds"
|
"getInbounds" = "Get Inbounds"
|
||||||
"depleteSoon" = "Deplete soon"
|
"depleteSoon" = "Deplete Soon"
|
||||||
"clientUsage" = "Get Usage"
|
"clientUsage" = "Get Usage"
|
||||||
"onlines" = "Online Clients"
|
"onlines" = "Online Clients"
|
||||||
"commands" = "Commands"
|
"commands" = "Commands"
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"getInboundsFailed" = "❌ Failed to get inbounds"
|
"getInboundsFailed" = "❌ Failed to get inbounds"
|
||||||
"askToAddUser" = "Your configuration is not found!\r\nYou should configure your telegram username and ask your Admin to add it to your configuration."
|
"askToAddUser" = "Your configuration is not found!\r\nYou should configure your Telegram username and ask your Admin to add it to your configuration(s)."
|
||||||
"askToAddUserName" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram username in your configuration(s).\r\n\r\nYour username: <b>@{{ .TgUserName }}</b>"
|
"askToAddUserName" = "Your configuration is not found!\r\nPlease ask your Admin to use your Telegram username in your configuration(s).\r\n\r\nYour username: <b>@{{ .TgUserName }}</b>"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"username" = "نام کاربری"
|
"username" = "نامکاربری"
|
||||||
"password" = "رمز عبور"
|
"password" = "رمزعبور"
|
||||||
"login" = "ورود"
|
"login" = "ورود"
|
||||||
"confirm" = "تایید"
|
"confirm" = "تایید"
|
||||||
"cancel" = "انصراف"
|
"cancel" = "انصراف"
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
"protocol" = "پروتکل"
|
"protocol" = "پروتکل"
|
||||||
"search" = "جستجو"
|
"search" = "جستجو"
|
||||||
"filter" = "فیلتر"
|
"filter" = "فیلتر"
|
||||||
"loading" = "در حال بروزرسانی..."
|
"loading" = "...در حال بارگذاری"
|
||||||
"second" = "ثانیه"
|
"second" = "ثانیه"
|
||||||
"minute" = "دقیقه"
|
"minute" = "دقیقه"
|
||||||
"hour" = "ساعت"
|
"hour" = "ساعت"
|
||||||
@@ -21,93 +21,93 @@
|
|||||||
"indefinite" = "نامحدود"
|
"indefinite" = "نامحدود"
|
||||||
"unlimited" = "نامحدود"
|
"unlimited" = "نامحدود"
|
||||||
"none" = "هیچ"
|
"none" = "هیچ"
|
||||||
"qrCode" = "QR کد"
|
"qrCode" = "QRکد"
|
||||||
"info" = "اطلاعات بیشتر"
|
"info" = "اطلاعات بیشتر"
|
||||||
"edit" = "ویرایش"
|
"edit" = "ویرایش"
|
||||||
"delete" = "حذف"
|
"delete" = "حذف"
|
||||||
"reset" = "ریست"
|
"reset" = "ریست"
|
||||||
"copySuccess" = "با موفقیت کپی شد"
|
"copySuccess" = "باموفقیت کپیشد"
|
||||||
"sure" = "مطمئن"
|
"sure" = "مطمئن"
|
||||||
"encryption" = "رمزگذاری"
|
"encryption" = "رمزگذاری"
|
||||||
"transmission" = "راه اتصال"
|
"transmission" = "راهاتصال"
|
||||||
"host" = "آدرس"
|
"host" = "آدرس"
|
||||||
"path" = "مسیر"
|
"path" = "مسیر"
|
||||||
"camouflage" = "استتار"
|
"camouflage" = "مبهمسازی"
|
||||||
"status" = "وضعیت"
|
"status" = "وضعیت"
|
||||||
"enabled" = "فعال"
|
"enabled" = "فعال"
|
||||||
"disabled" = "غیرفعال"
|
"disabled" = "غیرفعال"
|
||||||
"depleted" = "منقضی"
|
"depleted" = "منقضی"
|
||||||
"depletingSoon" = "در حال انقضا"
|
"depletingSoon" = "درحالانقضا"
|
||||||
"offline" = "آفلاین"
|
"offline" = "آفلاین"
|
||||||
"online" = "آنلاین"
|
"online" = "آنلاین"
|
||||||
"domainName" = "آدرس دامنه"
|
"domainName" = "آدرس دامنه"
|
||||||
"monitor" = "آی پی اتصال"
|
"monitor" = "آیپی اتصال"
|
||||||
"certificate" = "گواهی دیجیتال"
|
"certificate" = "گواهی"
|
||||||
"fail" = "خطا"
|
"fail" = "ناموفق"
|
||||||
"success" = " موفق"
|
"success" = " موفق"
|
||||||
"getVersion" = "دریافت ورژن"
|
"getVersion" = "دریافت نسخه"
|
||||||
"install" = "نصب"
|
"install" = "نصب"
|
||||||
"clients" = "کاربران"
|
"clients" = "کاربران"
|
||||||
"usage" = "استفاده"
|
"usage" = "استفاده"
|
||||||
"remained" = "باقیمانده"
|
"remained" = "باقیمانده"
|
||||||
"secAlertTitle" = "هشدار امنیتی"
|
"secAlertTitle" = "هشدارامنیتی"
|
||||||
"secAlertSsl" = "این اتصال امن نیست؛ لطفا تا زمانی که تیالاس برای حفاظت از داده ها فعال نشده است از وارد کردن اطلاعات حساس خودداری کنید"
|
"secAlertSsl" = "ایناتصالامن نیست. لطفا تازمانیکه تیالاس برای محافظت از دادهها فعال نشدهاست، از وارد کردن اطلاعات حساس خودداریکنید"
|
||||||
"security" = "امنیت"
|
"security" = "امنیت"
|
||||||
|
|
||||||
[menu]
|
[menu]
|
||||||
"dashboard" = "وضعیت سیستم"
|
"dashboard" = "نمای کلی"
|
||||||
"inbounds" = "سرویس ها"
|
"inbounds" = "ورودیها"
|
||||||
"settings" = "تنظیمات پنل"
|
"settings" = "تنظیمات پنل"
|
||||||
"xray" = "الگوی ایکسری"
|
"xray" = "پیکربندی ایکسری"
|
||||||
"logout" = "خروج"
|
"logout" = "خروج"
|
||||||
"link" = "دیگر"
|
"link" = "مدیریت"
|
||||||
|
|
||||||
[pages.login]
|
[pages.login]
|
||||||
"title" = "ورود به سیستم"
|
"title" = "خوشآمدید"
|
||||||
"loginAgain" = "مدت زمان استفاده به اتمام رسیده ، لطفا دوباره وارد شوید"
|
"loginAgain" = "مدت زمان استفاده بهاتمامرسیده، لطفا دوباره وارد شوید"
|
||||||
|
|
||||||
[pages.login.toasts]
|
[pages.login.toasts]
|
||||||
"invalidFormData" = "اطلاعات وارد شده به صورت درست وارد نشده است"
|
"invalidFormData" = "اطلاعات بهدرستی وارد نشدهاست"
|
||||||
"emptyUsername" = "نام کاربری خالی میباشد"
|
"emptyUsername" = "لطفا یک نامکاربری وارد کنید"
|
||||||
"emptyPassword" = "رمز عبور خالی میباشد"
|
"emptyPassword" = "لطفا یک رمزعبور وارد کنید"
|
||||||
"wrongUsernameOrPassword" = "نام کاربری و رمز عبور اشتباه میباشد"
|
"wrongUsernameOrPassword" = "نامکاربری یا رمزعبوراشتباهاست"
|
||||||
"successLogin" = "خوش آمدید"
|
"successLogin" = "ورود"
|
||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "وضعیت سیستم"
|
"title" = "نمای کلی"
|
||||||
"memory" = "حافظه رم"
|
"memory" = "RAM"
|
||||||
"hard" = "حافظه دیسک"
|
"hard" = "Disk"
|
||||||
"xrayStatus" = "وضعیت Xray"
|
"xrayStatus" = "وضعیتایکسری"
|
||||||
"stopXray" = "توقف"
|
"stopXray" = "توقف"
|
||||||
"restartXray" = "شروع مجدد"
|
"restartXray" = "شروعمجدد"
|
||||||
"xraySwitch" = "تغییر ورژن"
|
"xraySwitch" = "تغییرنسخه"
|
||||||
"xraySwitchClick" = "ورژن مورد نظر را انتخاب کنید"
|
"xraySwitchClick" = "نسخه مورد نظر را انتخاب کنید"
|
||||||
"xraySwitchClickDesk" = "لطفا با دقت انتخاب کنید ، در صورت انتخاب اشتباه امکان قطعی سیستم وجود دارد "
|
"xraySwitchClickDesk" = "لطفا بادقت انتخاب کنید. درصورت انتخاب نسخه قدیمیتر، امکان ناهماهنگی با پیکربندی فعلی وجود دارد"
|
||||||
"operationHours" = "مدت فعالیت"
|
"operationHours" = "مدتکارکرد"
|
||||||
"operationHoursDesc" = "مدت فعالیت سیستم بعد از روشن شدن"
|
"operationHoursDesc" = "مدت فعالیت سیستمعامل پساز شروع بهکار"
|
||||||
"systemLoad" = "بار روی سیستم"
|
"systemLoad" = "بارسیستم"
|
||||||
"connectionCount" = "تعداد کانکشن ها"
|
"connectionTcpCountDesc" = "در تمامشبکهها TCP مجموعاتصالات"
|
||||||
"connectionCountDesc" = "تعداد کانکشن ها برای کل شبکه"
|
"connectionUdpCountDesc" = "در تمامشبکهها UDP مجموعاتصالات"
|
||||||
"upSpeed" = "سرعت آپلود در حال حاضر سیستم"
|
"upSpeed" = "سرعت کلی آپلود در تمامشبکهها"
|
||||||
"downSpeed" = "سرعت دانلود در حال حاضر سیستم"
|
"downSpeed" = "سرعت کلی دانلود در تمامشبکهها"
|
||||||
"totalSent" = "جمع کل ترافیک آپلود مصرفی"
|
"totalSent" = "مجموع ترافیک ارسالشده پساز شروعبهکار سیستمعامل"
|
||||||
"totalReceive" = "جمع کل ترافیک دانلود مصرفی"
|
"totalReceive" = "مجموع ترافیک دریافتشده پساز شروعبهکار سیستمعامل"
|
||||||
"xraySwitchVersionDialog" = "تغییر ورژن Xray"
|
"xraySwitchVersionDialog" = "تغییرنسخهایکسری"
|
||||||
"xraySwitchVersionDialogDesc" = "آیا از تغییر ورژن مطمئن هستین"
|
"xraySwitchVersionDialogDesc" = "آیا از تغییر نسخه مطمئن هستید؟"
|
||||||
"dontRefresh" = "در حال نصب ، لطفا رفرش نکنید "
|
"dontRefresh" = "در حال نصب، لطفا صفحه را رفرش نکنید"
|
||||||
"logs" = "گزارش ها"
|
"logs" = "گزارشها"
|
||||||
"config" = "تنظیمات"
|
"config" = "پیکربندی"
|
||||||
"backup" = "پشتیبان گیری"
|
"backup" = "پشتیبانگیری"
|
||||||
"backupTitle" = "پشتیبان گیری دیتابیس"
|
"backupTitle" = "پشتیبانگیری دیتابیس"
|
||||||
"backupDescription" = "به یاد داشته باشید که قبل از وارد کردن یک دیتابیس جدید، نسخه پشتیبان تهیه کنید"
|
"backupDescription" = "توصیهمیشود قبلاز واردکردن یک دیتابیس جدید، نسخه پشتیبان تهیه کنید"
|
||||||
"exportDatabase" = "دانلود دیتابیس"
|
"exportDatabase" = "پشتیبانگیری"
|
||||||
"importDatabase" = "آپلود دیتابیس"
|
"importDatabase" = "بازگرداندن"
|
||||||
|
|
||||||
[pages.inbounds]
|
[pages.inbounds]
|
||||||
"title" = "کاربران"
|
"title" = "کاربران"
|
||||||
"totalDownUp" = "جمع آپلود/دانلود"
|
"totalDownUp" = "دریافت/ارسال کل"
|
||||||
"totalUsage" = "جمع کل"
|
"totalUsage" = "مصرف کل"
|
||||||
"inboundCount" = "تعداد سرویس ها"
|
"inboundCount" = "کل ورودیها"
|
||||||
"operate" = "عملیات"
|
"operate" = "عملیات"
|
||||||
"enable" = "فعال"
|
"enable" = "فعال"
|
||||||
"remark" = "نام"
|
"remark" = "نام"
|
||||||
@@ -118,57 +118,61 @@
|
|||||||
"transportConfig" = "نحوه اتصال"
|
"transportConfig" = "نحوه اتصال"
|
||||||
"expireDate" = "تاریخ انقضا"
|
"expireDate" = "تاریخ انقضا"
|
||||||
"resetTraffic" = "ریست ترافیک"
|
"resetTraffic" = "ریست ترافیک"
|
||||||
"addInbound" = "اضافه کردن سرویس"
|
"addInbound" = "افزودن ورودی"
|
||||||
"generalActions" = "عملیات کلی"
|
"generalActions" = "عملیات کلی"
|
||||||
"create" = "اضافه کردن"
|
"create" = "افزودن"
|
||||||
"update" = "ویرایش"
|
"update" = "ویرایش"
|
||||||
"modifyInbound" = "ویرایش سرویس"
|
"modifyInbound" = "ویرایش ورودی"
|
||||||
"deleteInbound" = "حذف سرویس"
|
"deleteInbound" = "حذف ورودی"
|
||||||
"deleteInboundContent" = "آیا مطمئن به حذف سرویس هستید ؟"
|
"deleteInboundContent" = "آیا مطمئن به حذف ورودی هستید؟"
|
||||||
"deleteClient" = "حذف کاربر"
|
"deleteClient" = "حذف کاربر"
|
||||||
"deleteClientContent" = "آیا مطمئن به حذف کاربر هستید ؟"
|
"deleteClientContent" = "آیا مطمئن به حذف کاربر هستید؟"
|
||||||
"resetTrafficContent" = "آیا مطمئن به ریست ترافیک هستید ؟"
|
"resetTrafficContent" = "آیا مطمئن به ریست ترافیک هستید؟"
|
||||||
"copyLink" = "کپی لینک"
|
"copyLink" = "کپی لینک"
|
||||||
"address" = "آدرس"
|
"address" = "آدرس"
|
||||||
"network" = "شبکه"
|
"network" = "شبکه"
|
||||||
"destinationPort" = "پورت مقصد"
|
"destinationPort" = "پورت مقصد"
|
||||||
"targetAddress" = "آدرس مقصد"
|
"targetAddress" = "آدرس مقصد"
|
||||||
"monitorDesc" = "به طور پیش فرض خالی بگذارید"
|
"monitorDesc" = "بهطور پیشفرض خالیبگذارید"
|
||||||
"meansNoLimit" = "یعنی بدون محدودیت"
|
"meansNoLimit" = "یعنیبدونمحدودیت"
|
||||||
"totalFlow" = "کل ترافیک"
|
"totalFlow" = "ترافیک کل"
|
||||||
"leaveBlankToNeverExpire" = "خالی بگذارید تا هرگز منقضی نشود"
|
"leaveBlankToNeverExpire" = "برای منقضینشدن خالیبگذارید"
|
||||||
"noRecommendKeepDefault" = "توصیه می شود به عنوان پیش فرض حفظ شود"
|
"noRecommendKeepDefault" = "توصیهمیشود بهطور پیشفرض حفظشود"
|
||||||
"certificatePath" = "مسیر فایل"
|
"certificatePath" = "مسیر فایل"
|
||||||
"certificateContent" = "محتوای فایل"
|
"certificateContent" = "محتوای فایل"
|
||||||
"publicKeyPath" = "مسیر کلید عمومی"
|
"publicKeyPath" = "مسیر کلید عمومی"
|
||||||
"publicKeyContent" = "محتوای کلید عمومی"
|
"publicKeyContent" = "محتوای کلید عمومی"
|
||||||
"keyPath" = "مسیر کلید خصوصی"
|
"keyPath" = "مسیر کلید خصوصی"
|
||||||
"keyContent" = "محتوای کلید خصوصی"
|
"keyContent" = "محتوای کلید خصوصی"
|
||||||
"clickOnQRcode" = "برای کپی بر روی کد تصویری کلیک کنید"
|
"clickOnQRcode" = "برای کپی بر روی کدتصویری کلیک کنید"
|
||||||
"client" = "کاربر"
|
"client" = "کاربر"
|
||||||
"export" = "استخراج لینکها"
|
"export" = "استخراج لینکها"
|
||||||
"clone" = "شبیه سازی"
|
"clone" = "شبیهسازی"
|
||||||
"cloneInbound" = "ایجاد"
|
"cloneInbound" = "شبیهسازی ورودی"
|
||||||
"cloneInboundContent" = "همه موارد این ورودی بجز پورت ، ای پی و کلاینت ها شبیه سازی خواهند شد"
|
"cloneInboundContent" = "همه موارد این ورودی بجز پورت، آیپی و کاربرها شبیهسازی خواهند شد"
|
||||||
"resetAllTraffic" = "ریست ترافیک کل سرویس ها"
|
"resetAllTraffic" = "ریست ترافیک کل ورودیها"
|
||||||
"resetAllTrafficTitle" = "ریست ترافیک کل سرویس ها"
|
"resetAllTrafficTitle" = "ریست ترافیک کل ورودیها"
|
||||||
"resetAllTrafficContent" = "آیا مطمئن هستید که میخواهید تمام ترافیک سرویس ها را ریست کنید؟"
|
"resetAllTrafficContent" = "آیا مطمئن به ریست ترافیک تمام ورودیها هستید؟"
|
||||||
"resetInboundClientTraffics" = "ریست ترافیک کاربران"
|
"resetInboundClientTraffics" = "ریست ترافیک کاربران"
|
||||||
"resetInboundClientTrafficTitle" = "ریست ترافیک کل کاربران"
|
"resetInboundClientTrafficTitle" = "ریست ترافیک کاربران"
|
||||||
"resetInboundClientTrafficContent" = "آیا مطمئن هستید که میخواهید تمام ترافیک کاربران این سرویس را ریست کنید؟"
|
"resetInboundClientTrafficContent" = "آیا مطمئن به ریست ترافیک تمام کاربران این ورودی هستید؟"
|
||||||
"resetAllClientTraffics" = "ریست ترافیک کاربران"
|
"resetAllClientTraffics" = "ریست ترافیک کل کاربران"
|
||||||
"resetAllClientTrafficTitle" = "ریست ترافیک کل کاربران"
|
"resetAllClientTrafficTitle" = "ریست ترافیک کل کاربران"
|
||||||
"resetAllClientTrafficContent" = "آیا مطمئن هستید که میخواهید تمام ترافیک کاربران را ریست کنید؟"
|
"resetAllClientTrafficContent" = "آیا مطمئن به ریست ترافیک تمام کاربران هستید؟"
|
||||||
"delDepletedClients" = "حذف کاربران منقضی"
|
"delDepletedClients" = "حذف کاربران منقضی"
|
||||||
"delDepletedClientsTitle" = "حذف کاربران منقضی"
|
"delDepletedClientsTitle" = "حذف کاربران منقضی"
|
||||||
"delDepletedClientsContent" = "آیا مطمئن هستید مه میخواهید تمامی کاربران منقضی شده را حذف کنید؟"
|
"delDepletedClientsContent" = "آیا مطمئن به حذف تمام کاربران منقضیشده هستید؟"
|
||||||
"email" = "ایمیل"
|
"email" = "ایمیل"
|
||||||
"emailDesc" = "ایمیل باید کاملا منحصر به فرد باشد"
|
"emailDesc" = "باید یک ایمیل یکتا باشد"
|
||||||
"setDefaultCert" = "استفاده از گواهی پنل"
|
"setDefaultCert" = "استفاده از گواهی پنل"
|
||||||
"telegramDesc" = "از آیدی تلگرام بدون @ یا آیدی چت استفاده کنید (می توانید آن را از اینجا دریافت کنید @userinfobot یا در ربات دستور '/id' را وارد کنید)"
|
"telegramDesc" = " استفاده کنید'/id'یااز دستور @userinfobot آنرا اینجا دریافت کنید .از آیدی(های) چت تلگرام بدون '@' استفاده کنید"
|
||||||
"subscriptionDesc" = "می توانید ساب لینک خود را در جزئیات پیدا کنید، همچنین می توانید از همین نام برای چندین کانفیگ استفاده کنید"
|
"subscriptionDesc" = "شما میتوانید لینک سابسکربپشن خودرا در 'جزئیات' پیدا کنید، همچنین میتوانید از همین نام برای چندین کاربر استفادهکنید"
|
||||||
"info" = "اطلاعات"
|
"info" = "اطلاعات"
|
||||||
"same" = "همسان"
|
"same" = "همسان"
|
||||||
|
"inboundData" = "دادههای ورودی"
|
||||||
|
"copyToClipboard" = "کپی در حافظه"
|
||||||
|
"import" = "افزودن"
|
||||||
|
"importInbound" = "افزودن یک ورودی"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "کاربر جدید"
|
"add" = "کاربر جدید"
|
||||||
@@ -176,20 +180,20 @@
|
|||||||
"submitAdd" = "اضافه کردن"
|
"submitAdd" = "اضافه کردن"
|
||||||
"submitEdit" = "ذخیره تغییرات"
|
"submitEdit" = "ذخیره تغییرات"
|
||||||
"clientCount" = "تعداد کاربران"
|
"clientCount" = "تعداد کاربران"
|
||||||
"bulk" = "انبوه سازی"
|
"bulk" = "انبوهسازی"
|
||||||
"method" = "روش"
|
"method" = "روش"
|
||||||
"first" = "از"
|
"first" = "از"
|
||||||
"last" = "تا"
|
"last" = "تا"
|
||||||
"prefix" = "پیشوند"
|
"prefix" = "پیشوند"
|
||||||
"postfix" = "پسوند"
|
"postfix" = "پسوند"
|
||||||
"delayedStart" = "شروع بعد از اولین استفاده"
|
"delayedStart" = "شروعپسازاولیناستفاده"
|
||||||
"expireDays" = "روزهای اعتبار"
|
"expireDays" = "مدت زمان"
|
||||||
"days" = "(روز)"
|
"days" = "(روز)"
|
||||||
"renew" = "تمدید خودکار"
|
"renew" = "تمدید خودکار"
|
||||||
"renewDesc" = "روزهای تمدید خودکار پس از انقضا. 0 = غیرفعال"
|
"renewDesc" = "تمدید خودکار پساز انقضا. 0 = غیرفعال - واحد: روز"
|
||||||
|
|
||||||
[pages.inbounds.toasts]
|
[pages.inbounds.toasts]
|
||||||
"obtain" = "Obtain"
|
"obtain" = "فراهمسازی"
|
||||||
|
|
||||||
[pages.inbounds.stream.general]
|
[pages.inbounds.stream.general]
|
||||||
"requestHeader" = "درخواست سربرگ"
|
"requestHeader" = "درخواست سربرگ"
|
||||||
@@ -197,10 +201,10 @@
|
|||||||
"value" = "مقدار"
|
"value" = "مقدار"
|
||||||
|
|
||||||
[pages.inbounds.stream.tcp]
|
[pages.inbounds.stream.tcp]
|
||||||
"requestVersion" = "ورژن درخواست"
|
"requestVersion" = "نسخه درخواست"
|
||||||
"requestMethod" = "متد درخواست"
|
"requestMethod" = "متد درخواست"
|
||||||
"requestPath" = "مسیر درخواست"
|
"requestPath" = "مسیر درخواست"
|
||||||
"responseVersion" = "ورژن پاسخ"
|
"responseVersion" = "نسخه پاسخ"
|
||||||
"responseStatus" = "وضعیت پاسخ"
|
"responseStatus" = "وضعیت پاسخ"
|
||||||
"responseStatusDescription" = "توضیحات وضعیت پاسخ"
|
"responseStatusDescription" = "توضیحات وضعیت پاسخ"
|
||||||
"responseHeader" = "سربرگ پاسخ"
|
"responseHeader" = "سربرگ پاسخ"
|
||||||
@@ -209,149 +213,161 @@
|
|||||||
"encryption" = "رمزنگاری"
|
"encryption" = "رمزنگاری"
|
||||||
|
|
||||||
[pages.settings]
|
[pages.settings]
|
||||||
"title" = "تنظیمات"
|
"title" = "تنظیمات پنل"
|
||||||
"save" = "ذخیره"
|
"save" = "ذخیره"
|
||||||
"infoDesc" = "برای اعمال تغییرات در این بخش باید پس از ذخیره کردن، پنل را ریستارت کنید"
|
"infoDesc" = "برای اعمال تغییرات در این بخش باید پس از ذخیره کردن، پنل را ریستارت کنید"
|
||||||
"restartPanel" = "ریستارت پنل"
|
"restartPanel" = "ریستارت پنل"
|
||||||
"restartPanelDesc" = "آیا مطمئن هستید که می خواهید پنل را دوباره راه اندازی کنید؟ برای راه اندازی مجدد روی OK کلیک کنید. اگر بعد از 3 ثانیه نمی توانید به پنل دسترسی پیدا کنید، لطفاً برای مشاهده اطلاعات گزارش پانل به سرور برگردید"
|
"restartPanelDesc" = "آیا مطمئن به ریستارت پنل هستید؟ اگر پساز ریستارت نمیتوانید به پنل دسترسی پیدا کنید، لطفاً گزارشهای موجود در اسکریپت پنل را بررسی کنید"
|
||||||
"resetDefaultConfig" = "برگشت به تنظیمات پیشفرض"
|
"resetDefaultConfig" = "برگشت به پیشفرض"
|
||||||
"panelConfig" = "تنظیمات پنل"
|
"panelConfig" = "پیکربندی"
|
||||||
"userSettings" = "تنظیمات مدیر"
|
"userSettings" = "احرازهویت"
|
||||||
"TGBotSettings" = "تنظیمات ربات تلگرام"
|
"TGBotSettings" = "ربات تلگرام"
|
||||||
"panelListeningIP" = "محدودیت آی پی پنل"
|
"panelListeningIP" = "آدرس آیپی"
|
||||||
"panelListeningIPDesc" = "برای استفاده از تمام آیپیها به طور پیش فرض خالی بگذارید"
|
"panelListeningIPDesc" = "آدرس آیپی برای وب پنل. برای گوشدادن بهتمام آیپیها خالیبگذارید"
|
||||||
"panelListeningDomain" = "محدودیت دامین پنل"
|
"panelListeningDomain" = "نام دامنه"
|
||||||
"panelListeningDomainDesc" = "برای استفاده از تمام دامنهها و آیپیها به طور پیش فرض خالی بگذارید"
|
"panelListeningDomainDesc" = "آدرس دامنه برای وب پنل. برای گوش دادن بهتمام دامنهها و آیپیها خالیبگذارید"
|
||||||
"panelPort" = "پورت پنل"
|
"panelPort" = "پورت"
|
||||||
"panelPortDesc" = "پورت مورد استفاده برای نمایش این پنل"
|
"panelPortDesc" = "شماره پورت برای وب پنل. باید پورت استفاده نشدهباشد"
|
||||||
"publicKeyPath" = "مسیر فایل گواهی کلید عمومی پنل"
|
"publicKeyPath" = "مسیر کلید عمومی"
|
||||||
"publicKeyPathDesc" = "باید یک مسیر مطلق باشد که با / شروع می شود "
|
"publicKeyPathDesc" = "مسیر فایل کلیدعمومی برای وب پنل. با '/' شروعمیشود"
|
||||||
"privateKeyPath" = "مسیر فایل گواهی کلید خصوصی پنل"
|
"privateKeyPath" = "مسیر کلید خصوصی"
|
||||||
"privateKeyPathDesc" = "باید یک مسیر مطلق باشد که با / شروع می شود "
|
"privateKeyPathDesc" = "مسیر فایل کلیدخصوصی برای وب پنل. با '/' شروعمیشود"
|
||||||
"panelUrlPath" = "آدرس روت پنل"
|
"panelUrlPath" = "URI مسیر"
|
||||||
"panelUrlPathDesc" = "باید با '/' شروع شود و با '/' تمام شود"
|
"panelUrlPathDesc" = "برای وب پنل. با '/' شروع و با '/' خاتمه مییابد URI مسیر"
|
||||||
"pageSize" = "اندازه صفحه بندی جدول"
|
"pageSize" = "اندازه صفحه بندی جدول"
|
||||||
"pageSizeDesc" = "اندازه صفحه را برای جدول سرویس ها تعریف کنید. 0: غیرفعال"
|
"pageSizeDesc" = "اندازه صفحه برای جدول ورودیها. 0 = غیرفعال"
|
||||||
"oldUsername" = "نام کاربری فعلی"
|
"remarkModel" = "نامکانفیگ و جداکننده"
|
||||||
"currentPassword" = "رمز عبور فعلی"
|
"sampleRemark" = "نمونهنام"
|
||||||
"newUsername" = "نام کاربری جدید"
|
"oldUsername" = "نامکاربری فعلی"
|
||||||
"newPassword" = "رمز عبور جدید"
|
"currentPassword" = "رمزعبور فعلی"
|
||||||
"telegramBotEnable" = "فعالسازی ربات تلگرام"
|
"newUsername" = "نامکاربری جدید"
|
||||||
"telegramBotEnableDesc" = "از طریق بات تلگرام به امکانات ابن پنل متصل شوید"
|
"newPassword" = "رمزعبور جدید"
|
||||||
|
"telegramBotEnable" = "فعالسازی ربات تلگرام"
|
||||||
|
"telegramBotEnableDesc" = "ربات تلگرام را فعال میکند"
|
||||||
"telegramToken" = "توکن تلگرام"
|
"telegramToken" = "توکن تلگرام"
|
||||||
"telegramTokenDesc" = "توکن را باید از مدیر بات های تلگرام دریافت کنید @botfather"
|
"telegramTokenDesc" = "دریافت کنید @botfather توکن را میتوانید از"
|
||||||
"telegramChatId" = "آی دی تلگرام مدیریت"
|
"telegramChatId" = "آیدی چت مدیر"
|
||||||
"telegramChatIdDesc" = "از @userinfobot یا دستور '/id' در ربات برای دریافت شناسه های چت خود استفاده کنید. با استفاده از کاما میتونید چند آی دی را از هم جدا کنید. "
|
"telegramChatIdDesc" = "استفادهکنید'/id'یا دستور @userinfobot آیدی(های) چت تلگرام مدیر، برای دریافت شناسههای چت خود از"
|
||||||
"telegramNotifyTime" = "مدت زمان نوتیفیکیشن ربات تلگرام"
|
"telegramNotifyTime" = "زمان نوتیفیکیشن"
|
||||||
"telegramNotifyTimeDesc" = "از فرمت زمان بندی لینوکس استفاده کنید "
|
"telegramNotifyTimeDesc" = "زماناطلاعرسانی ربات تلگرام برای گزارش های دورهای. از فرمت زمانبندی لینوکس استفادهکنید"
|
||||||
"tgNotifyBackup" = "پشتیبان گیری از پایگاه داده"
|
"tgNotifyBackup" = "پشتیبانگیری از دیتابیس"
|
||||||
"tgNotifyBackupDesc" = "ارسال کپی فایل پایگاه داده به همراه گزارش دوره ای"
|
"tgNotifyBackupDesc" = "فایل پشتیباندیتابیس را بههمراه گزارش ارسال میکند"
|
||||||
"tgNotifyLogin" = "اعلان ورود"
|
"tgNotifyLogin" = "اعلان ورود"
|
||||||
"tgNotifyLoginDesc" = "نام کاربری، آدرس ای پی، و زمان وقتی که فردی سعی میکند به پنل شما وارد شود نمایش میدهد"
|
"tgNotifyLoginDesc" = "نامکاربری، آدرس آیپی، و زمان ورود، فردی که سعی میکند وارد پنل شود را نمایش میدهد"
|
||||||
"sessionMaxAge" = "بیشینه زمان جلسه وب"
|
"sessionMaxAge" = "بیشینه زمان جلسه وب"
|
||||||
"sessionMaxAgeDesc" = "بیشینه زمانی که میتوانید لاگین بمانید (واحد: دقیقه)"
|
"sessionMaxAgeDesc" = "بیشینه زمانی که میتوانید لاگین بمانید. واحد: دقیقه"
|
||||||
"expireTimeDiff" = "آستانه زمان باقی مانده"
|
"expireTimeDiff" = "آستانه زمان باقی مانده"
|
||||||
"expireTimeDiffDesc" = "فاصله زمانی هشدار تا رسیدن به زمان انقضا (واحد: روز)"
|
"expireTimeDiffDesc" = "فاصله زمانی هشدار تا رسیدن به زمان انقضا. واحد: روز"
|
||||||
"trafficDiff" = "آستانه ترافیک باقی مانده"
|
"trafficDiff" = "آستانه ترافیک باقی مانده"
|
||||||
"trafficDiffDesc" = "فاصله زمانی هشدار تا رسیدن به اتمام ترافیک (واحد: گیگابایت)"
|
"trafficDiffDesc" = "فاصله زمانی هشدار تا رسیدن به اتمام ترافیک. واحد: گیگابایت"
|
||||||
"tgNotifyCpu" = "آستانه هشدار درصد پردازنده"
|
"tgNotifyCpu" = "آستانه هشدار بار پردازنده"
|
||||||
"tgNotifyCpuDesc" = "این ربات تلگرام در صورت استفاده پردازنده بیشتر از این درصد برای شما پیام ارسال می کند.(واحد: درصد)"
|
"tgNotifyCpuDesc" = "اگر بار روی پردازنده ازاین آستانه فراتر رفت، برای شما پیام ارسال میشود. واحد: درصد"
|
||||||
"timeZone" = "منظقه زمانی"
|
"timeZone" = "منطقه زمانی"
|
||||||
"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه زمانی اجرا می شوند. پنل را مجدداً راه اندازی می کند تا اعمال شود"
|
"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقهزمانی اجرا میشود"
|
||||||
"subSettings" = "سابسکریپشن"
|
"subSettings" = "سابسکریپشن"
|
||||||
"subEnable" = "فعال کردن سرویس"
|
"subEnable" = "فعالسازی سرویس سابسکریپشن"
|
||||||
"subEnableDesc" = "ویژگی سابسکریپشن با پیکربندی جداگانه"
|
"subEnableDesc" = " سرویس سابسکریپشن را فعالمیکند"
|
||||||
"subListen" = "محدودیت آیپی"
|
"subListen" = "آدرس آیپی"
|
||||||
"subListenDesc" = "برای استفاده از همه آیپی ها به طور پیش فرض خالی بگذارید"
|
"subListenDesc" = "آدرس آیپی برای سرویس سابسکریپشن. برای گوش دادن بهتمام آیپیها خالیبگذارید"
|
||||||
"subPort" = "پورت سرویس"
|
"subPort" = "پورت"
|
||||||
"subPortDesc" = "شماره پورت برای ارائه خدمات سابسکریپشن"
|
"subPortDesc" = "شماره پورت برای سرویس سابسکریپشن. باید پورت استفاده نشدهباشد"
|
||||||
"subCertPath" = "مسیر فایل کلید عمومی گواهی سابسکریپشن"
|
"subCertPath" = "مسیر کلید عمومی"
|
||||||
"subCertPathDesc" = "یک مسیر مطلق که با '/' شروع می شود را پر کنید."
|
"subCertPathDesc" = "مسیر فایل کلیدعمومی برای سرویس سابیکریپشن. با '/' شروعمیشود"
|
||||||
"subKeyPath" = "مسیر فایل کلید خصوصی گواهی سابسکریپشن"
|
"subKeyPath" = "مسیر کلید خصوصی"
|
||||||
"subKeyPathDesc" = "یک مسیر مطلق که با '/' شروع می شود را پر کنید."
|
"subKeyPathDesc" = "مسیر فایل کلیدخصوصی برای سرویس سابسکریپشن. با '/' شروعمیشود"
|
||||||
"subPath" = "مسیر ریشه سابسکریپشن"
|
"subPath" = "URI مسیر"
|
||||||
"subPathDesc" = "باید با '/' شروع شود و با '/' ختم شود."
|
"subPathDesc" = "برای سرویس سابسکریپشن. با '/' شروع و با '/' خاتمه مییابد URI مسیر"
|
||||||
"subDomain" = "دامنه مخصوص سابسکریپشن"
|
"subDomain" = "نام دامنه"
|
||||||
"subDomainDesc" = "برای نظارت بر همه دامنه ها و آیپی ها به طور پیش فرض خالی بگذارید"
|
"subDomainDesc" = "آدرس دامنه برای سرویس سابسکریپشن. برای گوش دادن به تمام دامنهها و آیپیها خالیبگذارید"
|
||||||
"subUpdates" = "فاصله به روز رسانی های سابسکریپشن"
|
"subUpdates" = "فاصله بروزرسانی سابسکریپشن"
|
||||||
"subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر"
|
"subUpdatesDesc" = "فاصله مابین بروزرسانی در برنامههای کاربری - واحد: ساعت"
|
||||||
"subEncrypt" = "رمزگذاری کانفیگ ها"
|
"subEncrypt" = "کدگذاری"
|
||||||
"subEncryptDesc" = "رمزگذاری کانفیگ های بازگشتی سابسکریپشن"
|
"subEncryptDesc" = "کدگذاری خواهدشد Base64 محتوای برگشتی سرویس سابسکریپشن برپایه"
|
||||||
"subShowInfo" = "نمایش اطلاعات مصرف"
|
"subShowInfo" = "نمایش اطلاعات مصرف"
|
||||||
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد"
|
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در برنامههای کاربری نمایش میدهد"
|
||||||
"subURI" = "آدرس پایه پروکسی معکوس"
|
"subURI" = "پروکسی معکوس URI مسیر"
|
||||||
"subURIDesc" = "آدرس پایه سابسکریپشن را برای استفاده در پشت پراکسی ها تغییر میدهد"
|
"subURIDesc" = "سابسکریپشن را برای استفاده در پشت پراکسیها تغییر میدهد URI مسیر"
|
||||||
|
|
||||||
[pages.settings.toasts]
|
[pages.settings.toasts]
|
||||||
"modifySettings" = "ویرایش تنظیمات"
|
"modifySettings" = "ویرایش تنظیمات"
|
||||||
"getSettings" = "دریافت تنظیمات"
|
"getSettings" = "دریافت تنظیمات"
|
||||||
"modifyUser" = "ویرایش کاربر"
|
"modifyUser" = "ویرایش مدیر"
|
||||||
"originalUserPassIncorrect" = "نام کاربری و رمز عبور فعلی اشتباه می باشد "
|
"originalUserPassIncorrect" = "نامکاربری یا رمزعبور فعلی اشتباهاست"
|
||||||
"userPassMustBeNotEmpty" = "نام کاربری و رمز عبور جدید نمیتواند خالی باشد "
|
"userPassMustBeNotEmpty" = "نامکاربری یا رمزعبور جدید خالیاست"
|
||||||
|
|
||||||
[pages.xray]
|
[pages.xray]
|
||||||
"title" = "تنظیمات Xray"
|
"title" = "پیکربندی ایکسری"
|
||||||
"save" = "ذخیره تنظیمات"
|
"save" = "ذخیره"
|
||||||
"restart" = "ریستارت ایکسری"
|
"restart" = "ریستارت ایکسری"
|
||||||
"basicTemplate" = "بخش الگو پایه"
|
"basicTemplate" = "پایه"
|
||||||
"advancedTemplate" = "بخش الگو پیشرفته"
|
"advancedTemplate" = "پیشرفته"
|
||||||
"generalConfigs" = "تنظیمات عمومی"
|
"generalConfigs" = "استراتژی کلی"
|
||||||
"generalConfigsDesc" = "این تنظیمات میتواند ترافیک کلی سرویس را متاثر کند"
|
"generalConfigsDesc" = "این گزینهها استراتژی کلی ترافیک را تعیین میکنند"
|
||||||
"blockConfigs" = "مسدود سازی"
|
"blockConfigs" = "سپر محافظ"
|
||||||
"blockConfigsDesc" = "این گزینه ها از اتصال کاربران به پروتکل ها و وب سایت های خاص جلوگیری می کند"
|
"blockConfigsDesc" = "این گزینهها ترافیک را بر اساس پروتکلهای درخواستی خاص، و وب سایتها مسدود میکند"
|
||||||
"blockCountryConfigs" = "تنظیمات برای مسدودسازی کشورها"
|
"blockCountryConfigs" = "مسدودسازی کشور"
|
||||||
"blockCountryConfigsDesc" = "این گزینه اتصال کاربران به دامنه های کشوری خاص را مسدود می کند"
|
"blockCountryConfigsDesc" = "این گزینهها ترافیک را بر اساس کشور درخواستی خاص مسدود میکند"
|
||||||
"directCountryConfigs" = "تنظیمات برای اتصال مستقیم کشورها"
|
"directCountryConfigs" = "اتصال مستقیم کشور"
|
||||||
"directCountryConfigsDesc" = "این گزینه کاربران را به دامنه های کشوری خاص را به طور مستقیم، متصل می کند"
|
"directCountryConfigsDesc" = "این گزینهها ترافیک را بر اساس کشور درخواستی خاص بصورت مستقیم ارسال میکند"
|
||||||
"ipv4Configs" = "تنظیمات برای IPv4"
|
"ipv4Configs" = "IPv4 مسیریابی"
|
||||||
"ipv4ConfigsDesc" = "این گزینه فقط از طریق آیپی ورژن ۴ به دامنه های هدف هدایت می شود"
|
"ipv4ConfigsDesc" = "این گزینهها درخواستها را فقط از طریق آیپینسخه4 به مقصد هدایت میکند"
|
||||||
"Template" = "تنظیمات الگو ایکس ری"
|
"warpConfigs" = "تنظیمات برای وارپ"
|
||||||
"TemplateDesc" = "فایل پیکربندی ایکس ری نهایی بر اساس این الگو ایجاد میشود. لطفاً این را تغییر ندهید مگر اینکه دقیقاً بدانید که چه کاری انجام می دهید!"
|
"warpConfigsDesc" = ".وارپ ترافیک را از طریق سرورهای کلادفلر به وب سایت ها هدایت می کند"
|
||||||
"FreedomStrategy" = "روش استفاده از شبکه خروجی مستقیم"
|
"Template" = "پیکربندی پیشرفته الگو ایکسری"
|
||||||
"FreedomStrategyDesc" = "تعیین روش استفاده از خروجی برای پرتکل مستقیم"
|
"TemplateDesc" = "فایل پیکربندی نهایی ایکسری بر اساس این الگو ایجاد میشود"
|
||||||
"RoutingStrategy" = "پیکربندی استراتژی حل دامنه در مسیریابی"
|
"FreedomStrategy" = "Freedom استراتژی پروتکل"
|
||||||
"RoutingStrategyDesc" = "تعیین استراتژی مسیریابی کلی برای پیدا کردن دامنه"
|
"FreedomStrategyDesc" = "تعیین میکند Freedom استراتژی خروجی شبکه را برای پروتکل"
|
||||||
"Torrent" = "فیلتر کردن بیت تورنت"
|
"RoutingStrategy" = "استراتژی کلی مسیریابی"
|
||||||
"TorrentDesc" = "الگوی تنظیمات را برای فیلتر کردن پروتکل بیت تورنت برای کاربران تغییر میدهد"
|
"RoutingStrategyDesc" = "استراتژی کلی مسیریابی برای حل تمام درخواستها را تعیین میکند"
|
||||||
"PrivateIp" = "جلوگیری از اتصال آیپی های خصوصی یا محلی"
|
"Torrent" = "مسدودسازی پروتکل بیتتورنت"
|
||||||
"PrivateIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های خصوصی یا محلی و بسته های سرگردان تغییر میدهد"
|
"TorrentDesc" = "پروتکل بیت تورنت را مسدود میکند"
|
||||||
"Ads" = "مسدود کردن تبلیغات"
|
"PrivateIp" = "مسدودسازی اتصال آیپیهای خصوصی"
|
||||||
"AdsDesc" = "الگوی تنظیمات را برای مسدود کردن تبلیغات تغییر میدهد"
|
"PrivateIpDesc" = "اتصال به آیپیهای رنج خصوصی را مسدود میکند"
|
||||||
"Family" = "فعال کردن حالت خانواده"
|
"Ads" = "مسدودسازی تبلیغات"
|
||||||
"FamilyDesc" = "برای جلوگیری از ارتباط با وبسایت های ناامن"
|
"AdsDesc" = "وبسایتهای تبلیغاتی را مسدود میکند"
|
||||||
"IRIp" = "جلوگیری از اتصال آیپی های ایران"
|
"Family" = "محافظت خانواده"
|
||||||
"IRIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های ایران تغییر میدهد"
|
"FamilyDesc" = "محتوای مخصوص بزرگسالان، و وبسایتهای ناامن را مسدود میکند"
|
||||||
"IRDomain" = "جلوگیری از اتصال دامنه های ایران"
|
"IRIp" = "مسدودسازی اتصال به آیپیهای ایران"
|
||||||
"IRDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های ایران تغییر میدهد"
|
"IRIpDesc" = "اتصال به آیپیهای کشور ایران را مسدود میکند"
|
||||||
"ChinaIp" = "جلوگیری از اتصال آیپی های چین"
|
"IRDomain" = "مسدودسازی اتصال به دامنههای ایران"
|
||||||
"ChinaIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های چین تغییر میدهد"
|
"IRDomainDesc" = "اتصال به دامنههای کشور ایران را مسدود میکند"
|
||||||
"ChinaDomain" = "جلوگیری از اتصال دامنه های چین"
|
"ChinaIp" = "مسدودسازی اتصال به آیپیهای چین"
|
||||||
"ChinaDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های چین تغییر میدهد"
|
"ChinaIpDesc" = "اتصال به آیپیهای کشور چین را مسدود میکند"
|
||||||
"RussiaIp" = "جلوگیری از اتصال آیپی های روسیه"
|
"ChinaDomain" = "مسدودسازی اتصال به دامنههای چین"
|
||||||
"RussiaIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های روسیه تغییر میدهد"
|
"ChinaDomainDesc" = "اتصال به دامنههای کشور چین را مسدود میکند"
|
||||||
"RussiaDomain" = "جلوگیری از اتصال دامنه های روسیه"
|
"RussiaIp" = "مسدودسازی اتصال به آیپیهای روسیه"
|
||||||
"RussiaDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های روسیه تغییر میدهد"
|
"RussiaIpDesc" = "اتصال به آیپیهای کشور روسیه را مسدود میکند"
|
||||||
"DirectIRIp" = "ارتباط مستقیم به آیپی های ایران"
|
"RussiaDomain" = "مسدودسازی اتصال به دامنههای روسیه"
|
||||||
"DirectIRIpDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به آیپی های ایران تغییر میدهد"
|
"RussiaDomainDesc" = "اتصال به دامنههای کشور روسیه را مسدود میکند"
|
||||||
"DirectIRDomain" = "ارتباط مستقیم به دامنه های ایران"
|
"DirectIRIp" = "اتصال مستقیم آیپیهای ایران"
|
||||||
"DirectIRDomainDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به دامنه های ایران تغییر میدهد"
|
"DirectIRIpDesc" = "اتصال مستقیم به آیپیهای کشور ایران"
|
||||||
"DirectChinaIp" = "ارتباط مستقیم به آیپی های چین"
|
"DirectIRDomain" = "اتصال مستقیم دامنههای ایران"
|
||||||
"DirectChinaIpDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به آیپی های چین تغییر میدهد"
|
"DirectIRDomainDesc" = "اتصال مستقیم به دامنههای کشور ایران"
|
||||||
"DirectChinaDomain" = "ارتباط مستقیم به دامنه های چین"
|
"DirectChinaIp" = "اتصال مستقیم آیپیهای چین"
|
||||||
"DirectChinaDomainDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به دامنه های چین تغییر میدهد"
|
"DirectChinaIpDesc" = "اتصال مستقیم به آیپیهای کشور چین"
|
||||||
"DirectRussiaIp" = "ارتباط مستقیم به آیپی های روسیه"
|
"DirectChinaDomain" = "ارتباط مستقیم دامنههای چین"
|
||||||
"DirectRussiaIpDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به آیپی های روسیه تغییر میدهد"
|
"DirectChinaDomainDesc" = "اتصال مستقیم به دامنههای کشور چین"
|
||||||
"DirectRussiaDomain" = "ارتباط مستقیم به دامنه های روسیه"
|
"DirectRussiaIp" = "ارتباط مستقیم آیپیهای روسیه"
|
||||||
"DirectRussiaDomainDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به دامنه های روسیه تغییر میدهد"
|
"DirectRussiaIpDesc" = "اتصال مستقیم به آیپیهای کشور روسیه"
|
||||||
"GoogleIPv4" = "استفاده از آیپی ورژن 4 برای اتصال به گوگل"
|
"DirectRussiaDomain" = "ارتباط مستقیم دامنه های روسیه"
|
||||||
"GoogleIPv4Desc" = "مسیردهی جدید برای اتصال به گوگل با آیپی ورژن 4 اضافه میکند"
|
"DirectRussiaDomainDesc" = "اتصال مستقیم به دامنههای کشور روسیه"
|
||||||
"NetflixIPv4" = "استفاده از آیپی ورژن 4 برای اتصال به نتفلیکس"
|
"GoogleIPv4" = "گوگل"
|
||||||
"NetflixIPv4Desc" = "مسیردهی جدید برای اتصال به نتفلیکس با آیپی ورژن 4 اضافه میکند"
|
"GoogleIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به گوگل هدایت میکند"
|
||||||
|
"NetflixIPv4" = "نتفلیکس"
|
||||||
|
"NetflixIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به نتفلیکس هدایت میکند"
|
||||||
"completeTemplate" = "کامل"
|
"completeTemplate" = "کامل"
|
||||||
|
"GoogleWARP" = "گوگل"
|
||||||
|
"GoogleWARPDesc" = "ترافیک را از طریق وارپ به گوگل هدایت میکند"
|
||||||
|
"OpenAIWARP" = "چت جیپیتی"
|
||||||
|
"OpenAIWARPDesc" = "ترافیک را از طریق وارپ به چت جیپیتی هدایت میکند"
|
||||||
|
"NetflixWARP" = "نتفلیکس"
|
||||||
|
"NetflixWARPDesc" = "ترافیک را از طریق وارپ به نتفلیکس هدایت میکند"
|
||||||
|
"SpotifyWARP" = "اسپاتیفای"
|
||||||
|
"SpotifyWARPDesc" = " ترافیک را از طریق وارپ به اسپاتیفای هدایت میکند"
|
||||||
"Inbounds" = "ورودیها"
|
"Inbounds" = "ورودیها"
|
||||||
"Outbounds" = "خروجیها"
|
"Outbounds" = "خروجیها"
|
||||||
"Routings" = "قوانین مسیریابی"
|
"Routings" = "قوانین مسیریابی"
|
||||||
"RoutingsDesc" = "اولویت هر قانون مهم است!"
|
"RoutingsDesc" = "اولویت هر قانون مهم است"
|
||||||
|
|
||||||
[pages.xray.rules]
|
[pages.xray.rules]
|
||||||
"first" = "اولین"
|
"first" = "اولین"
|
||||||
@@ -365,7 +381,7 @@
|
|||||||
"info" = "اطلاعات"
|
"info" = "اطلاعات"
|
||||||
"add" = "افزودن قانون"
|
"add" = "افزودن قانون"
|
||||||
"edit" = "ویرایش قانون"
|
"edit" = "ویرایش قانون"
|
||||||
"useComma" = "موارد جدا شده با کاما"
|
"useComma" = "موارد جداشده با کاما"
|
||||||
|
|
||||||
[pages.xray.outbound]
|
[pages.xray.outbound]
|
||||||
"addOutbound" = "افزودن خروجی"
|
"addOutbound" = "افزودن خروجی"
|
||||||
@@ -373,87 +389,95 @@
|
|||||||
"editOutbound" = "ویرایش خروجی"
|
"editOutbound" = "ویرایش خروجی"
|
||||||
"editReverse" = "ویرایش معکوس"
|
"editReverse" = "ویرایش معکوس"
|
||||||
"tag" = "برچسب"
|
"tag" = "برچسب"
|
||||||
|
"tagDesc" = "برچسب یگانه"
|
||||||
"address" = "آدرس"
|
"address" = "آدرس"
|
||||||
"reverse" = "معکوس"
|
"reverse" = "معکوس"
|
||||||
"domain" = "دامنه"
|
"domain" = "دامنه"
|
||||||
"type" = "نوع"
|
"type" = "نوع"
|
||||||
"bridge" = "پل"
|
"bridge" = "پل"
|
||||||
"portal" = "پرتال"
|
"portal" = "پورتال"
|
||||||
"intercon" = "اتصال میانی"
|
"intercon" = "اتصال میانی"
|
||||||
|
|
||||||
|
[pages.xray.wireguard]
|
||||||
|
"secretKey" = "کلید شخصی"
|
||||||
|
"publicKey" = "کلید عمومی"
|
||||||
|
"allowedIPs" = "آیپیهای مجاز"
|
||||||
|
"endpoint" = "نقطه پایانی"
|
||||||
|
"psk" = "کلید مشترک"
|
||||||
|
"domainStrategy" = "استراتژی حل دامنه"
|
||||||
|
|
||||||
[tgbot]
|
[tgbot]
|
||||||
"noResult" = "❗ نتیجهای یافت نشد!"
|
"noResult" = "❗نتیجهای یافت نشد"
|
||||||
"wentWrong" = "❌ مشکلی رخ داده است!"
|
"wentWrong" = "❌ مشکلی رخ دادهاست"
|
||||||
"noInbounds" = "❗ هیچ ورودی یافت نشد!"
|
"noInbounds" = " هیچ ورودی یافت نشد"
|
||||||
"unlimited" = "♾ نامحدود"
|
"unlimited" = "♾ نامحدود"
|
||||||
"day" = "روز"
|
"day" = "روز"
|
||||||
"days" = "روزها"
|
"days" = "روزها"
|
||||||
"unknown" = "نامشخص"
|
"unknown" = "نامشخص"
|
||||||
"inbounds" = "ورودیها"
|
"inbounds" = "ورودیها"
|
||||||
"clients" = "کلاینتها"
|
"clients" = "کاربران"
|
||||||
|
|
||||||
[tgbot.commands]
|
[tgbot.commands]
|
||||||
"unknown" = "❗ دستور ناشناخته"
|
"unknown" = "❗ دستور ناشناخته"
|
||||||
"pleaseChoose" = "👇 لطفاً انتخاب کنید:\r\n"
|
"pleaseChoose" = "👇 لطفاًانتخاب کنید:\r\n"
|
||||||
"help" = "🤖 به این ربات خوش آمدید! این ربات برای ارائه دادههای خاص از سرور طراحی شده است و به شما امکان تغییرات لازم را میدهد.\r\n\r\n"
|
"help" = "🤖 به این ربات خوشآمدید! این ربات برای ارائه دادههای خاص از وب پنل طراحی شدهاست و بهشما امکان تغییرات لازم را میدهد\r\n\r\n"
|
||||||
"start" = "👋 سلام <i>{{ .Firstname }}</i>.\r\n"
|
"start" = "👋 سلام <i>{{ .Firstname }}</i>.\r\n"
|
||||||
"welcome" = "🤖 به ربات مدیریت <b>{{ .Hostname }}</b> خوش آمدید.\r\n"
|
"welcome" = "🤖 بهربات مدیریت <b>{{ .Hostname }}</b> خوشآمدید\r\n"
|
||||||
"status" = "✅ ربات در حالت عادی است!"
|
"status" = "✅ رباتدرحالتعادیاست"
|
||||||
"usage" = "❗ لطفاً یک متن برای جستجو وارد کنید!"
|
"usage" = "❗ لطفا یک متن برای جستجو واردکنید"
|
||||||
"getID" = "🆔 شناسه شما: <code>{{ .ID }}</code>"
|
"getID" = "🆔 شناسهشما: <code>{{ .ID }}</code>"
|
||||||
"helpAdminCommands" = "برای جستجوی ایمیل مشتری:\r\n<code>/usage [ایمیل]</code>\r\n \r\nبرای جستجوی ورودیها (با آمار مشتری):\r\n<code>/inbound [توضیح]</code>"
|
"helpAdminCommands" = "برای جستجوی ایمیل کاربر:\r\n<code>/usage [ایمیل]</code>\r\n \r\nبرای جستجوی ورودیها (با آمار کاربر):\r\n<code>/inbound [توضیح]</code>"
|
||||||
"helpClientCommands" = "برای جستجوی آمار، فقط از دستور زیر استفاده کنید:\r\n \r\n<code>/usage [UUID|رمز عبور]</code>\r\n \r\nاز UUID برای vmess/vless و از رمز عبور برای Trojan استفاده کنید."
|
"helpClientCommands" = "برای جستجوی آمار، فقط از دستور زیر استفادهکنید:\r\n \r\n<code>/usage [UUID|رمز عبور]</code>\r\n \r\nاز رمزعبور استفاده کنید Trojan/Shadowsocks و برای UUID از VMess/VLESS برای"
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 میزان استفاده از CPU {{ .Percent }}% بیشتر از آستانه {{ .Threshold }}% است."
|
"cpuThreshold" = "🔴 بار پردازنده {{ .Percent }}% بیشتر از آستانه است {{ .Threshold }}%"
|
||||||
"loginSuccess" = "✅ با موفقیت به پنل وارد شدید.\r\n"
|
"loginSuccess" = "✅ باموفقیت به پنل واردشدید \r\n"
|
||||||
"loginFailed" = "❗️ ورود به پنل ناموفق بود.\r\n"
|
"loginFailed" = "❗️ ورود به پنل ناموفقبود \r\n"
|
||||||
"report" = "🕰 گزارشات زمانبندی شده: {{ .RunTime }}\r\n"
|
"report" = "🕰 گزارشاتزمانبندیشده: {{ .RunTime }}\r\n"
|
||||||
"datetime" = "⏰ تاریخ-زمان: {{ .DateTime }}\r\n"
|
"datetime" = "⏰ تاریخوزمان: {{ .DateTime }}\r\n"
|
||||||
"hostname" = "💻 نام میزبان: {{ .Hostname }}\r\n"
|
"hostname" = "💻 ناممیزبان: {{ .Hostname }}\r\n"
|
||||||
"version" = "🚀 نسخه X-UI: {{ .Version }}\r\n"
|
"version" = "🚀 نسخهپنل: {{ .Version }}\r\n"
|
||||||
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
||||||
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
||||||
"ip" = "🌐 آدرس IP: {{ .IP }}\r\n"
|
"ip" = "🌐 آدرسآیپی: {{ .IP }}\r\n"
|
||||||
"serverUpTime" = "⏳ زمان کارکرد سرور: {{ .UpTime }} {{ .Unit }}\r\n"
|
"serverUpTime" = "⏳ مدتکارکردسیستم: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||||
"serverLoad" = "📈 بار سرور: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
"serverLoad" = "📈 بارسیستم: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||||
"serverMemory" = "📋 حافظه سرور: {{ .Current }}/{{ .Total }}\r\n"
|
"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n"
|
||||||
"tcpCount" = "🔹 تعداد ترافیک TCP: {{ .Count }}\r\n"
|
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
|
||||||
"udpCount" = "🔸 تعداد ترافیک UDP: {{ .Count }}\r\n"
|
"udpCount" = "🔸 UDP: {{ .Count }}\r\n"
|
||||||
"traffic" = "🚦 ترافیک: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
"traffic" = "🚦 ترافیک: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
"xrayStatus" = "ℹ️ وضعیت Xray: {{ .State }}\r\n"
|
"xrayStatus" = "ℹ️ وضعیتایکسری: {{ .State }}\r\n"
|
||||||
"username" = "👤 نام کاربری: {{ .Username }}\r\n"
|
"username" = "👤 نامکاربری: {{ .Username }}\r\n"
|
||||||
"time" = "⏰ زمان: {{ .Time }}\r\n"
|
"time" = "⏰ زمان: {{ .Time }}\r\n"
|
||||||
"inbound" = "📍 ورودی: {{ .Remark }}\r\n"
|
"inbound" = "📍 نامورودی: {{ .Remark }}\r\n"
|
||||||
"port" = "🔌 پورت: {{ .Port }}\r\n"
|
"port" = "🔌 پورت: {{ .Port }}\r\n"
|
||||||
"expire" = "📅 تاریخ انقضا: {{ .DateTime }}\r\n \r\n"
|
"expire" = "📅 تاریخانقضا: {{ .DateTime }}\r\n \r\n"
|
||||||
"expireIn" = "📅 باقیمانده از انقضا: {{ .Time }}\r\n \r\n"
|
"expireIn" = "📅 باقیماندهتاانقضا: {{ .Time }}\r\n \r\n"
|
||||||
"active" = "💡 فعال: {{ .Enable }}\r\n"
|
"active" = "💡 فعال: {{ .Enable }}\r\n"
|
||||||
"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
|
"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
|
||||||
"email" = "📧 ایمیل: {{ .Email }}\r\n"
|
"email" = "📧 ایمیل: {{ .Email }}\r\n"
|
||||||
"upload" = "🔼 آپلود↑: {{ .Upload }}\r\n"
|
"upload" = "🔼 آپلود↑: {{ .Upload }}\r\n"
|
||||||
"download" = "🔽 دانلود↓: {{ .Download }}\r\n"
|
"download" = "🔽 دانلود↓: {{ .Download }}\r\n"
|
||||||
"total" = "🔄 کل: {{ .UpDown }} / {{ .Total }}\r\n"
|
"total" = "🔄 کل: {{ .UpDown }} / {{ .Total }}\r\n"
|
||||||
"exhaustedMsg" = "🚨 {{ .Type }} به اتمام رسیده است:\r\n"
|
"exhaustedMsg" = "🚨 {{ .Type }} بهاتمامرسیدهاست:\r\n"
|
||||||
"exhaustedCount" = "🚨 تعداد {{ .Type }} به اتمام رسیده:\r\n"
|
"exhaustedCount" = "🚨 تعداد {{ .Type }} بهاتمامرسیدهاست:\r\n"
|
||||||
"onlinesCount" = "🌐 تعداد کاربران آنلاین: {{ .Count }}\r\n"
|
"onlinesCount" = "🌐 کاربرانآنلاین: {{ .Count }}\r\n"
|
||||||
"disabled" = "🛑 غیرفعال: {{ .Disabled }}\r\n"
|
"disabled" = "🛑 غیرفعال: {{ .Disabled }}\r\n"
|
||||||
"depleteSoon" = "🔜 به زودی به پایان خواهد رسید: {{ .Deplete }}\r\n \r\n"
|
"depleteSoon" = "🔜 بهزودیبهپایانخواهدرسید: {{ .Deplete }}\r\n \r\n"
|
||||||
"backupTime" = "🗄 زمان پشتیبانگیری: {{ .Time }}\r\n"
|
"backupTime" = "🗄 زمانپشتیبانگیری: {{ .Time }}\r\n"
|
||||||
"yes" = "✅ بله"
|
"yes" = "✅ بله"
|
||||||
"no" = "❌ خیر"
|
"no" = "❌ خیر"
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"dbBackup" = "دریافت پشتیبان پایگاه داده"
|
"dbBackup" = "دریافت پشتیبان دیتابیس"
|
||||||
"serverUsage" = "استفاده از سرور"
|
"serverUsage" = "استفاده از سیستم"
|
||||||
"getInbounds" = "دریافت ورودیها"
|
"getInbounds" = "دریافت ورودیها"
|
||||||
"depleteSoon" = "به زودی به پایان خواهد رسید"
|
"depleteSoon" = "بهزودی به پایان خواهد رسید"
|
||||||
"clientUsage" = "دریافت آمار کاربر"
|
"clientUsage" = "دریافت آمار کاربر"
|
||||||
"onlines" = "کاربران آنلاین"
|
"onlines" = "کاربران آنلاین"
|
||||||
"commands" = "دستورات"
|
"commands" = "دستورات"
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"getInboundsFailed" = "❌ دریافت ورودیها با خطا مواجه شد."
|
"getInboundsFailed" = "❌ دریافت ورودیها باخطا مواجه شد"
|
||||||
"askToAddUser" = "پیکربندی شما یافت نشد!\r\nشما باید نام کاربری تلگرام خود را پیکربندی کنید و از مدیر خود درخواست اضافه کردن آن به پیکربندی خود بکنید."
|
"askToAddUser" = "پیکربندی شما پیدا نشد!\r\nشما باید نامکاربری تلگرام خود را پیکربندی کنید و از مدیر پنل خود بخواهید که آن را به پیکربندی شما اضافه کند"
|
||||||
"askToAddUserName" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود درخواست استفاده از نام کاربری تلگرام خود در پیکربندی (ها) خود را بکنید.\r\n\r\nنام کاربری شما: <b>@{{ .TgUserName }}</b>"
|
"askToAddUserName" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر پنل درخواست کنید اطلاعات تلگرام شما را در پیکربندی(های) مربوط بهشما تنظیمکند \r\n\r\nنامکاربری شما: @{{ .TgUserName }}"
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"transmission" = "Протокол передачи"
|
"transmission" = "Протокол передачи"
|
||||||
"host" = "Хост"
|
"host" = "Хост"
|
||||||
"path" = "Путь"
|
"path" = "Путь"
|
||||||
"camouflage" = "Маскировка"
|
"camouflage" = "Затемнение"
|
||||||
"status" = "Статус"
|
"status" = "Статус"
|
||||||
"enabled" = "Включено"
|
"enabled" = "Включено"
|
||||||
"disabled" = "Отключено"
|
"disabled" = "Отключено"
|
||||||
@@ -60,10 +60,10 @@
|
|||||||
"settings" = "Настройки"
|
"settings" = "Настройки"
|
||||||
"xray" = "Xray Настройки"
|
"xray" = "Xray Настройки"
|
||||||
"logout" = "Выйти"
|
"logout" = "Выйти"
|
||||||
"link" = "Другое"
|
"link" = "Менеджмент"
|
||||||
|
|
||||||
[pages.login]
|
[pages.login]
|
||||||
"title" = "Войти"
|
"title" = "Добро пожаловать"
|
||||||
"loginAgain" = "Время сессии истекло. Пожалуйста, войдите в систему снова"
|
"loginAgain" = "Время сессии истекло. Пожалуйста, войдите в систему снова"
|
||||||
|
|
||||||
[pages.login.toasts]
|
[pages.login.toasts]
|
||||||
@@ -86,8 +86,8 @@
|
|||||||
"operationHours" = "Время работы"
|
"operationHours" = "Время работы"
|
||||||
"operationHoursDesc" = "Время работы системы: время с момента запуска."
|
"operationHoursDesc" = "Время работы системы: время с момента запуска."
|
||||||
"systemLoad" = "Системная нагрузка"
|
"systemLoad" = "Системная нагрузка"
|
||||||
"connectionCount" = "Количество соединений"
|
"connectionTcpCountDesc" = "Всего подключений TCP по всем сетевым картам."
|
||||||
"connectionCountDesc" = "Всего подключений по всем сетям»"
|
"connectionUdpCountDesc" = "Общее количество подключений UDP по всем сетевым картам."
|
||||||
"upSpeed" = "Общая скорость отдачи"
|
"upSpeed" = "Общая скорость отдачи"
|
||||||
"downSpeed" = "Общая скорость получения"
|
"downSpeed" = "Общая скорость получения"
|
||||||
"totalSent" = "Общий объем загруженных данных с момента запуска системы"
|
"totalSent" = "Общий объем загруженных данных с момента запуска системы"
|
||||||
@@ -167,9 +167,13 @@
|
|||||||
"emailDesc" = "Пожалуйста, укажите уникальный Email"
|
"emailDesc" = "Пожалуйста, укажите уникальный Email"
|
||||||
"setDefaultCert" = "Установить сертификат с панели"
|
"setDefaultCert" = "Установить сертификат с панели"
|
||||||
"telegramDesc" = "Используйте идентификатор Telegram без символа @ или идентификатора чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
|
"telegramDesc" = "Используйте идентификатор Telegram без символа @ или идентификатора чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
|
||||||
"subscriptionDesc" = "вы можете найти свою ссылку подписки в разделе «Подробнее», также вы можете использовать одно и то же имя для нескольких конфигов"
|
"subscriptionDesc" = "вы можете найти свою ссылку подписки в разделе «Подробнее», также вы можете использовать одно и то же имя для нескольких клиенты"
|
||||||
"info" = "Информация"
|
"info" = "Информация"
|
||||||
"same" = "Тот же"
|
"same" = "Тот же"
|
||||||
|
"inboundData" = "Входящие данные"
|
||||||
|
"copyToClipboard" = "Копировать в буфер обмена"
|
||||||
|
"import" = "Импортировать"
|
||||||
|
"importInbound" = "Импортировать входящее сообщение"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Добавить клиента"
|
"add" = "Добавить клиента"
|
||||||
@@ -184,10 +188,10 @@
|
|||||||
"prefix" = "Префикс"
|
"prefix" = "Префикс"
|
||||||
"postfix" = "Постфикс"
|
"postfix" = "Постфикс"
|
||||||
"delayedStart" = "Начать со времени первого подключения"
|
"delayedStart" = "Начать со времени первого подключения"
|
||||||
"expireDays" = "Срок действия"
|
"expireDays" = "Длительность"
|
||||||
"days" = "дней"
|
"days" = "дней"
|
||||||
"renew" = "Автопродление"
|
"renew" = "Автопродление"
|
||||||
"renewDesc" = "Автоматическое продление через несколько дней после истечения срока действия. 0 = отключить"
|
"renewDesc" = "Автопродление после истечения срока действия. (0 = отключить)(единица: день) "
|
||||||
|
|
||||||
[pages.inbounds.toasts]
|
[pages.inbounds.toasts]
|
||||||
"obtain" = "Получить"
|
"obtain" = "Получить"
|
||||||
@@ -233,6 +237,8 @@
|
|||||||
"panelUrlPathDesc" = "Должен начинаться с «/» и заканчиваться на «/»."
|
"panelUrlPathDesc" = "Должен начинаться с «/» и заканчиваться на «/»."
|
||||||
"pageSize" = "Размер нумерации страниц"
|
"pageSize" = "Размер нумерации страниц"
|
||||||
"pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить"
|
"pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить"
|
||||||
|
"remarkModel" = "Модель примечания и символ разделения"
|
||||||
|
"sampleRemark" = "Пример замечания"
|
||||||
"oldUsername" = "Текущее имя пользователя"
|
"oldUsername" = "Текущее имя пользователя"
|
||||||
"currentPassword" = "Текущий пароль"
|
"currentPassword" = "Текущий пароль"
|
||||||
"newUsername" = "Новое имя пользователя"
|
"newUsername" = "Новое имя пользователя"
|
||||||
@@ -306,6 +312,8 @@
|
|||||||
"directCountryConfigsDesc" = "Эти параметры будут подключать пользователей напрямую к доменам определенной страны."
|
"directCountryConfigsDesc" = "Эти параметры будут подключать пользователей напрямую к доменам определенной страны."
|
||||||
"ipv4Configs" = "Настройки IPv4"
|
"ipv4Configs" = "Настройки IPv4"
|
||||||
"ipv4ConfigsDesc" = "Эти параметры будут маршрутизироваться к целевым доменам только через IPv4"
|
"ipv4ConfigsDesc" = "Эти параметры будут маршрутизироваться к целевым доменам только через IPv4"
|
||||||
|
"warpConfigs" = "Настройки WARP"
|
||||||
|
"warpConfigsDesc" = "WARP будет направлять трафик на веб-сайты через серверы Cloudflare"
|
||||||
"Template" = "Шаблон конфигурации Xray"
|
"Template" = "Шаблон конфигурации Xray"
|
||||||
"TemplateDesc" = "Создание файла конфигурации Xray на основе этого шаблона."
|
"TemplateDesc" = "Создание файла конфигурации Xray на основе этого шаблона."
|
||||||
"FreedomStrategy" = "Настроить стратегию протокола Freedom"
|
"FreedomStrategy" = "Настроить стратегию протокола Freedom"
|
||||||
@@ -348,6 +356,14 @@
|
|||||||
"GoogleIPv4Desc" = "Применить маршрутизацию Google для подключения к IPv4."
|
"GoogleIPv4Desc" = "Применить маршрутизацию Google для подключения к IPv4."
|
||||||
"NetflixIPv4" = "Использовать IPv4 для Netflix"
|
"NetflixIPv4" = "Использовать IPv4 для Netflix"
|
||||||
"NetflixIPv4Desc" = "Применить маршрутизацию Netflix для подключения к IPv4."
|
"NetflixIPv4Desc" = "Применить маршрутизацию Netflix для подключения к IPv4."
|
||||||
|
"GoogleWARP" = "Маршрутизация Google через WARP"
|
||||||
|
"GoogleWARPDesc" = "Добавить маршрутизацию для Google через WARP"
|
||||||
|
"OpenAIWARP" = "Маршрутизация OpenAI (ChatGPT) через WARP"
|
||||||
|
"OpenAIWARPDesc" = "Добавить маршрутизацию для OpenAI (ChatGPT) через WARP"
|
||||||
|
"NetflixWARP" = "Маршрутизация Netflix через WARP"
|
||||||
|
"NetflixWARPDesc" = "Добавить маршрутизацию для Netflix через WARP"
|
||||||
|
"SpotifyWARP" = "Маршрутизация Spotify через WARP"
|
||||||
|
"SpotifyWARPDesc" = "Добавить маршрутизацию для Spotify через WARP"
|
||||||
"completeTemplate" = "Все"
|
"completeTemplate" = "Все"
|
||||||
"Inbounds" = "Входящие"
|
"Inbounds" = "Входящие"
|
||||||
"Outbounds" = "Исходящие"
|
"Outbounds" = "Исходящие"
|
||||||
@@ -362,7 +378,7 @@
|
|||||||
"source" = "Источник"
|
"source" = "Источник"
|
||||||
"dest" = "Пункт назначения"
|
"dest" = "Пункт назначения"
|
||||||
"inbound" = "Входящий"
|
"inbound" = "Входящий"
|
||||||
"outboun" = "Исходящий"
|
"outbound" = "Исходящий"
|
||||||
"info" = "Информация"
|
"info" = "Информация"
|
||||||
"add" = "Добавить правило"
|
"add" = "Добавить правило"
|
||||||
"edit" = "Редактировать правило"
|
"edit" = "Редактировать правило"
|
||||||
@@ -374,6 +390,7 @@
|
|||||||
"editOutbound" = "Изменить исходящий"
|
"editOutbound" = "Изменить исходящий"
|
||||||
"editReverse" = "Редактировать реверс"
|
"editReverse" = "Редактировать реверс"
|
||||||
"tag" = "Тег"
|
"tag" = "Тег"
|
||||||
|
"tagDesc" = "уникальный тег"
|
||||||
"address" = "Адрес"
|
"address" = "Адрес"
|
||||||
"reverse" = "Обратный"
|
"reverse" = "Обратный"
|
||||||
"domain" = "Домен"
|
"domain" = "Домен"
|
||||||
@@ -382,6 +399,14 @@
|
|||||||
"portal" = "Портал"
|
"portal" = "Портал"
|
||||||
"intercon" = "Соединение"
|
"intercon" = "Соединение"
|
||||||
|
|
||||||
|
[pages.xray.wireguard]
|
||||||
|
"secretKey" = "Секретный ключ"
|
||||||
|
"publicKey" = "Открытый ключ"
|
||||||
|
"allowedIPs" = "Разрешенные IP-адреса"
|
||||||
|
"endpoint" = "Конечная точка"
|
||||||
|
"psk" = "Общий ключ"
|
||||||
|
"domainStrategy" = "Стратегия домена"
|
||||||
|
|
||||||
[tgbot]
|
[tgbot]
|
||||||
"noResult" = "❗ Нет результатов!"
|
"noResult" = "❗ Нет результатов!"
|
||||||
"wentWrong" = "❌ Что-то пошло не так!"
|
"wentWrong" = "❌ Что-то пошло не так!"
|
||||||
|
|||||||
484
web/translation/translate.vi_VN.toml
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
"username" = "Tên người dùng"
|
||||||
|
"password" = "Mật khẩu"
|
||||||
|
"login" = "Đăng nhập"
|
||||||
|
"confirm" = "Xác nhận"
|
||||||
|
"cancel" = "Hủy bỏ"
|
||||||
|
"close" = "Đóng"
|
||||||
|
"copy" = "Sao chép"
|
||||||
|
"copied" = "Đã sao chép"
|
||||||
|
"download" = "Tải xuống"
|
||||||
|
"remark" = "Ghi chú"
|
||||||
|
"enable" = "Kích hoạt"
|
||||||
|
"protocol" = "Giao thức"
|
||||||
|
"search" = "Tìm kiếm"
|
||||||
|
"filter" = "Bộ lọc"
|
||||||
|
"loading" = "Đang tải..."
|
||||||
|
"second" = "Giây"
|
||||||
|
"minute" = "Phút"
|
||||||
|
"hour" = "Giờ"
|
||||||
|
"day" = "Ngày"
|
||||||
|
"check" = "Kiểm tra"
|
||||||
|
"indefinite" = "Không xác định"
|
||||||
|
"unlimited" = "Không giới hạn"
|
||||||
|
"none" = "Không có"
|
||||||
|
"qrCode" = "Mã QR"
|
||||||
|
"info" = "Thông tin thêm"
|
||||||
|
"edit" = "Chỉnh sửa"
|
||||||
|
"delete" = "Xóa"
|
||||||
|
"reset" = "Đặt lại"
|
||||||
|
"copySuccess" = "Đã sao chép thành công"
|
||||||
|
"sure" = "Chắc chắn"
|
||||||
|
"encryption" = "Mã hóa"
|
||||||
|
"transmission" = "Truyền tải"
|
||||||
|
"host" = "Máy chủ"
|
||||||
|
"path" = "Đường dẫn"
|
||||||
|
"camouflage" = "Sự làm xáo trộn"
|
||||||
|
"status" = "Trạng thái"
|
||||||
|
"enabled" = "Đã kích hoạt"
|
||||||
|
"disabled" = "Đã tắt"
|
||||||
|
"depleted" = "Đã Dùng hết"
|
||||||
|
"depletingSoon" = "Sắp dùng hết"
|
||||||
|
"offline" = "Ngoại tuyến"
|
||||||
|
"online" = "Trực tuyến"
|
||||||
|
"domainName" = "Tên miền"
|
||||||
|
"monitor" = "Nghe IP"
|
||||||
|
"certificate" = "Chứng chỉ"
|
||||||
|
"fail" = " Thất bại"
|
||||||
|
"success" = " Thành công"
|
||||||
|
"getVersion" = "Lấy phiên bản"
|
||||||
|
"install" = "Cài đặt"
|
||||||
|
"clients" = "Khách hàng"
|
||||||
|
"usage" = "Sử dụng"
|
||||||
|
"remained" = "Còn lại"
|
||||||
|
"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"
|
||||||
|
"security" = "Bảo vệ"
|
||||||
|
|
||||||
|
[menu]
|
||||||
|
"dashboard" = "Trạng thái hệ thống"
|
||||||
|
"inbounds" = "Đầu Vào khách hàng"
|
||||||
|
"settings" = "Cài đặt bảng điều khiển"
|
||||||
|
"xray" = "Cài đặt Xray"
|
||||||
|
"logout" = "Đăng xuất"
|
||||||
|
"link" = "Sự quản lý"
|
||||||
|
|
||||||
|
[pages.login]
|
||||||
|
"title" = "Chào mừng"
|
||||||
|
"loginAgain" = "Thời hạn đăng nhập đã hết, Vui lòng đăng nhập lại."
|
||||||
|
|
||||||
|
[pages.login.toasts]
|
||||||
|
"invalidFormData" = "Dạng dữ liệu nhập không hợp lệ."
|
||||||
|
"emptyUsername" = "Vui lòng nhập tên người dùng."
|
||||||
|
"emptyPassword" = "Vui lòng nhập mật khẩu."
|
||||||
|
"wrongUsernameOrPassword" = "Tên người dùng hoặc mật khẩu không đúng."
|
||||||
|
"successLogin" = "Đăng nhập thành công."
|
||||||
|
|
||||||
|
[pages.index]
|
||||||
|
"title" = "Trạng thái hệ thống"
|
||||||
|
"memory" = "Bộ nhớ"
|
||||||
|
"hard" = "Ổ cứng"
|
||||||
|
"xrayStatus" = "Trạng thái của Xray"
|
||||||
|
"stopXray" = "Dừng Xray"
|
||||||
|
"restartXray" = "Khởi động lại Xray"
|
||||||
|
"xraySwitch" = "Chuyển đổi phiên bản"
|
||||||
|
"xraySwitchClick" = "Chọn phiên bản mà bạn muốn chuyển đổi sang."
|
||||||
|
"xraySwitchClickDesk" = "Hãy lựa chọn thận trọng, vì các phiên bản cũ có thể không tương thích với các cấu hình hiện tại, của Bạn"
|
||||||
|
"operationHours" = "Thời gian hoạt động"
|
||||||
|
"operationHoursDesc" = "Thời gian hoạt động của hệ thống: thời gian kể từ khi khởi động."
|
||||||
|
"systemLoad" = "Tải hệ thống"
|
||||||
|
"connectionTcpCountDesc" = "Tổng số kết nối TCP trên tất cả các card mạng."
|
||||||
|
"connectionUdpCountDesc" = "Tổng số kết nối UDP trên tất cả các card mạng."
|
||||||
|
"upSpeed" = "Tổng tốc độ tải lên cho tất cả các thẻ mạng."
|
||||||
|
"downSpeed" = "Tổng tốc độ tải xuống cho tất cả các thẻ mạng."
|
||||||
|
"totalSent" = "Tổng lưu lượng tải lên của tất cả các thẻ mạng kể từ khi khởi động hệ thống."
|
||||||
|
"totalReceive" = "Tổng số dữ liệu tải xuống trên tất cả các thẻ mạng kể từ khi khởi động hệ thống."
|
||||||
|
"xraySwitchVersionDialog" = "Chuyển đổi phiên bản Xray"
|
||||||
|
"xraySwitchVersionDialogDesc" = "Bạn có chắc chắn muốn chuyển phiên bản Xray sang phiên bản khác"
|
||||||
|
"dontRefresh" = "Quá trình cài đặt đang diễn ra, vui lòng không làm mới trang này."
|
||||||
|
"logs" = "Nhật ký"
|
||||||
|
"config" = "Cấu hình"
|
||||||
|
"backup" = "Phục hồi dữ liệu đã lưu"
|
||||||
|
"backupTitle" = "Sao lưu và Khôi phục cơ sở dữ liệu"
|
||||||
|
"backupDescription" = "Bạn hãy nhớ sao lưu trước khi nhập cơ sở dữ liệu mới."
|
||||||
|
"exportDatabase" = "Sao lưu"
|
||||||
|
"importDatabase" = "Khôi phục"
|
||||||
|
|
||||||
|
[pages.inbounds]
|
||||||
|
"title" = "Điểm vào (Inbounds)"
|
||||||
|
"totalDownUp" = "Tổng tải lên/tải xuống"
|
||||||
|
"totalUsage" = "Tổng sử dụng"
|
||||||
|
"inboundCount" = "Số lần vào"
|
||||||
|
"operate" = "Bảng Chọn"
|
||||||
|
"enable" = "Cho phép"
|
||||||
|
"remark" = "Nhận xét"
|
||||||
|
"protocol" = "Giao thức"
|
||||||
|
"port" = "Cổng"
|
||||||
|
"traffic" = "Lưu lượng"
|
||||||
|
"details" = "Chi tiết"
|
||||||
|
"transportConfig" = "Cấu hình vận chuyển"
|
||||||
|
"expireDate" = "Ngày hết hạn"
|
||||||
|
"resetTraffic" = "Đặt lại lưu lượng"
|
||||||
|
"addInbound" = "Thêm điểm vào"
|
||||||
|
"generalActions" = "Hành động chung"
|
||||||
|
"create" = "Tạo mới"
|
||||||
|
"update" = "Cập nhật"
|
||||||
|
"modifyInbound" = "Chỉnh sửa điểm vào (Inbound)"
|
||||||
|
"deleteInbound" = "Xóa điểm vào (Inbound)"
|
||||||
|
"deleteInboundContent" = "Xác nhận xóa điểm vào? (Inbound)"
|
||||||
|
"deleteClient" = "Xóa khách hàng"
|
||||||
|
"deleteClientContent" = "Bạn có chắc chắn muốn xóa khách hàng không?"
|
||||||
|
"resetTrafficContent" = "Bạn có chắc chắn muốn đặt lại lưu lượng truy cập không?"
|
||||||
|
"copyLink" = "Sao chép liên kết"
|
||||||
|
"address" = "Địa chỉ"
|
||||||
|
"network" = "Mạng"
|
||||||
|
"destinationPort" = "Cổng đích"
|
||||||
|
"targetAddress" = "Địa chỉ mục tiêu"
|
||||||
|
"monitorDesc" = "Mặc định để trống"
|
||||||
|
"meansNoLimit" = "Nghĩa là không giới hạn"
|
||||||
|
"totalFlow" = "Tổng lưu lượng"
|
||||||
|
"leaveBlankToNeverExpire" = "Để trống để không bao giờ hết hạn"
|
||||||
|
"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 tin chứng chỉ"
|
||||||
|
"certificateContent" = "Nội dung tập tin chứng chỉ"
|
||||||
|
"publicKeyPath" = "Đường dẫn khóa công khai"
|
||||||
|
"publicKeyContent" = "Nội dung khóa công khai"
|
||||||
|
"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"
|
||||||
|
"client" = "Khách hàng"
|
||||||
|
"export" = "Xuất liên kết"
|
||||||
|
"clone" = "Bản sao"
|
||||||
|
"cloneInbound" = "Nhân bản"
|
||||||
|
"cloneInboundContent" = "Tất cả cài đặt của điểm vào này, ngoại trừ Cổng, IP nghe và Máy khách, sẽ được áp dụng cho bản sao."
|
||||||
|
"cloneInboundOk" = "Bản sao"
|
||||||
|
"resetAllTraffic" = "Đặt lại tất cả lưu lượng truy cập"
|
||||||
|
"resetAllTrafficTitle" = "Đặt lại tất cả lưu lượng truy cập"
|
||||||
|
"resetAllTrafficContent" = "Bạn có chắc chắn muốn đặt lại lưu lượng cho tất cả điểm vào không?"
|
||||||
|
"resetInboundClientTraffics" = "Đặt lại lưu lượng cho các khách hàng của điểm vào"
|
||||||
|
"resetInboundClientTrafficTitle" = "Đặt lại tất cả lưu lượng truy cập của khách hàng"
|
||||||
|
"resetInboundClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho các khách hàng của điểm vào này không?"
|
||||||
|
"resetAllClientTraffics" = "Đặt lại lưu lượng cho tất cả máy khách"
|
||||||
|
"resetAllClientTrafficTitle" = "Đặt lại lưu lượng cho tất cả các khách hàng"
|
||||||
|
"resetAllClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho tất cả khách hàng không?"
|
||||||
|
"delDepletedClients" = "Xóa các máy khách đã cạn kiệt"
|
||||||
|
"delDepletedClientsTitle" = "Xóa các khách khách đã cạn kiệt"
|
||||||
|
"delDepletedClientsContent" = "Bạn có chắc chắn muốn xóa tất cả các máy khách đã cạn kiệt không??"
|
||||||
|
"email" = "Email"
|
||||||
|
"emailDesc" = "vui lòng cung cấp một địa chỉ email duy nhất."
|
||||||
|
"setDefaultCert" = "Đặt chứng chỉ từ bảng điều khiển"
|
||||||
|
"telegramDesc" = "Sử dụng ID Telegram không có @ hoặc ID trò chuyện (bạn có thể lấy nó tại đây @userinfobot hoặc sử dụng lệnh '/id' trong bot)"
|
||||||
|
"subscriptionDesc" = "Bạn có thể tìm thấy liên kết phụ của mình trên Chi tiết, bạn cũng có thể sử dụng cùng tên cho một số khách hàng"
|
||||||
|
"info" = "Thông tin"
|
||||||
|
"same" = "Như nhau"
|
||||||
|
"inboundData" = "Dữ liệu gửi đến"
|
||||||
|
"copyToClipboard" = "Sao chép vào bảng nhớ tạm"
|
||||||
|
"import" = "Nhập"
|
||||||
|
"importInbound" = "Nhập hàng gửi về"
|
||||||
|
|
||||||
|
[pages.client]
|
||||||
|
"add" = "Thêm máy khách"
|
||||||
|
"edit" = "Chỉnh sửa Máy khách"
|
||||||
|
"submitAdd" = "Thêm máy khách"
|
||||||
|
"submitEdit" = "Lưu thay đổi"
|
||||||
|
"clientCount" = "Số lượng khách hàng"
|
||||||
|
"bulk" = "Thêm số lượng lớn"
|
||||||
|
"method" = "Các thức"
|
||||||
|
"first" = "Ban đầu"
|
||||||
|
"last" = "Đến cùng"
|
||||||
|
"prefix" = "Tiền Tố (Được ưu đãi)"
|
||||||
|
"postfix" = "Hậu tố"
|
||||||
|
"delayedStart" = "Bắt đầu sau lần sử dụng đầu tiên"
|
||||||
|
"expireDays" = "Khoảng thời gian"
|
||||||
|
"days" = "Ngày(s)"
|
||||||
|
"renew" = "Tự động gia hạn"
|
||||||
|
"renewDesc" = "Tự động gia hạn sau khi hết hạn. (0 = tắt)(đơn vị: ngày)"
|
||||||
|
|
||||||
|
[pages.inbounds.toasts]
|
||||||
|
"obtain" = "Nhận được"
|
||||||
|
|
||||||
|
[pages.inbounds.stream.general]
|
||||||
|
"requestHeader" = "Tiêu đề yêu cầu"
|
||||||
|
"name" = "Tên"
|
||||||
|
"value" = "Giá trị"
|
||||||
|
|
||||||
|
[pages.inbounds.stream.tcp]
|
||||||
|
"requestVersion" = "Phiên bản yêu cầu"
|
||||||
|
"requestMethod" = "Phương thức yêu cầu"
|
||||||
|
"requestPath" = "Đường dẫn yêu cầu"
|
||||||
|
"responseVersion" = "Phiên bản phản hồi"
|
||||||
|
"responseStatus" = "Trạng thái phản hồi"
|
||||||
|
"responseStatusDescription" = "Mô tả trạng thái phản hồi"
|
||||||
|
"responseHeader" = "Tiêu đề phản hồi"
|
||||||
|
|
||||||
|
[pages.inbounds.stream.quic]
|
||||||
|
"encryption" = "Mã hóa"
|
||||||
|
|
||||||
|
[pages.settings]
|
||||||
|
"title" = "Cài đặt"
|
||||||
|
"save" = "Lưu"
|
||||||
|
"infoDesc" = "Mọi thay đổi được thực hiện ở đây cần phải được lưu. Vui lòng khởi động lại bảng điều khiển để áp dụng các thay đổi. bản dich bởi Ohoang7"
|
||||||
|
"restartPanel" = "Khởi động lại Bảng điều khiển"
|
||||||
|
"restartPanelDesc" = "Bạn có chắc chắn muốn khởi động lại bảng điều khiển? Nhấn OK để khởi động lại sau 3 giây. Nếu bạn không thể truy cập bảng điều khiển sau khi khởi động lại, vui lòng xem thông tin nhật ký của bảng điều khiển trên máy chủ."
|
||||||
|
"resetDefaultConfig" = "Đặt lại Cấu hình Mặc định"
|
||||||
|
"panelConfig" = "Cấu hình bảng điều khiển"
|
||||||
|
"userSettings" = "Thiết lập người dùng"
|
||||||
|
"TGBotSettings" = "Cài đặt Bot Telegram"
|
||||||
|
"panelListeningIP" = "IP Nghe của Bảng điều khiển"
|
||||||
|
"panelListeningIPDesc" = "Mặc định để trống để nghe tất cả các IP."
|
||||||
|
"panelListeningDomain" = "Tên miền của nghe Bảng điều khiển"
|
||||||
|
"panelListeningDomainDesc" = "Mặc định để trống để nghe tất cả các tên miền và IP"
|
||||||
|
"panelPort" = "Cổng Bảng điều khiển"
|
||||||
|
"panelPortDesc" = "Cổng được sử dụng để hiển thị bảng điều khiển này"
|
||||||
|
"publicKeyPath" = "Đường dẫn tập tin khóa công khai Chứng chỉ Bảng điều khiển"
|
||||||
|
"publicKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với."
|
||||||
|
"privateKeyPath" = "Đường dẫn tập tin khóa riêng tư Chứng chỉ Bảng điều khiển"
|
||||||
|
"privateKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với."
|
||||||
|
"panelUrlPath" = "Đường dẫn gốc URL Bảng điều khiển"
|
||||||
|
"panelUrlPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng."
|
||||||
|
"pageSize" = "Kích thước phân trang"
|
||||||
|
"pageSizeDesc" = "Xác định kích thước trang cho bảng gửi đến. Đặt 0 để tắt"
|
||||||
|
"remarkModel" = "Ghi chú mô hình và ký tự phân tách"
|
||||||
|
"sampleRemark" = "Nhận xét mẫu"
|
||||||
|
"oldUsername" = "Tên người dùng hiện tại"
|
||||||
|
"currentPassword" = "Mật khẩu hiện tại"
|
||||||
|
"newUsername" = "Tên người dùng mới"
|
||||||
|
"newPassword" = "Mật khẩu mới"
|
||||||
|
"telegramBotEnable" = "Bật Bot Telegram"
|
||||||
|
"telegramBotEnableDesc" = "Kết nối với các tính năng của bảng điều khiển này thông qua bot Telegram"
|
||||||
|
"telegramToken" = "Token Telegram"
|
||||||
|
"telegramTokenDesc" = "Bạn phải nhận token từ quản lý bot Telegram @botfather"
|
||||||
|
"telegramChatId" = "Chat ID Telegram của quản trị viên"
|
||||||
|
"telegramChatIdDesc" = "Nhiều Chat ID phân tách bằng dấu phẩy. Sử dụng @userinfobot hoặc sử dụng lệnh '/id' trong bot để lấy Chat ID của bạn."
|
||||||
|
"telegramNotifyTime" = "Thời gian thông báo của bot Telegram"
|
||||||
|
"telegramNotifyTimeDesc" = "Sử dụng định dạng thời gian Crontab."
|
||||||
|
"tgNotifyBackup" = "Sao lưu Cơ sở dữ liệu"
|
||||||
|
"tgNotifyBackupDesc" = "Bao gồm tệp sao lưu cơ sở dữ liệu với thông báo báo cáo."
|
||||||
|
"tgNotifyLogin" = "Thông báo Đăng nhập"
|
||||||
|
"tgNotifyLoginDesc" = "Hiển thị tên người dùng, địa chỉ IP và thời gian khi ai đó cố gắng đăng nhập vào bảng điều khiển của bạn."
|
||||||
|
"sessionMaxAge" = "Tuổi tối đa của phiên"
|
||||||
|
"sessionMaxAgeDesc" = "Thời gian của phiên đăng nhập (đơn vị: phút)"
|
||||||
|
"expireTimeDiff" = "Ngưỡng hết hạn cho thông báo"
|
||||||
|
"expireTimeDiffDesc" = "Nhận thông báo về việc hết hạn tài khoản trước ngưỡng này (đơn vị: ngày)"
|
||||||
|
"trafficDiff" = "Ngưỡng lưu lượng cho thông báo"
|
||||||
|
"trafficDiffDesc" = "Nhận thông báo về việc cạn kiệt lưu lượng trước khi đạt đến ngưỡng này (đơn vị: GB)"
|
||||||
|
"tgNotifyCpu" = "Ngưỡng cảnh báo tỷ lệ CPU"
|
||||||
|
"tgNotifyCpuDesc" = "Nhận thông báo nếu tỷ lệ sử dụng CPU vượt quá ngưỡng này (đơn vị: %)"
|
||||||
|
"timeZone" = "Múi giờ"
|
||||||
|
"timeZoneDesc" = "Các tác vụ được lên lịch chạy theo thời gian trong múi giờ này."
|
||||||
|
"subSettings" = "Đăng ký"
|
||||||
|
"subEnable" = "Bật dịch vụ"
|
||||||
|
"subEnableDesc" = "Tính năng đăng ký với cấu hình riêng"
|
||||||
|
"subListen" = "Listening IP"
|
||||||
|
"subListenDesc" = "Mặc định để trống để nghe tất cả các IP"
|
||||||
|
"subPort" = "Cổng Đăng ký"
|
||||||
|
"subPortDesc" = "Số cổng dịch vụ đăng ký phải chưa được sử dụng trên máy chủ"
|
||||||
|
"subCertPath" = "Đường dẫn tập tin khóa công khai Chứng chỉ Đăng ký"
|
||||||
|
"subCertPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với '/'"
|
||||||
|
"subKeyPath" = "Đường dẫn tập tin khóa riêng tư Chứng chỉ Đăng ký"
|
||||||
|
"subKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với '/'"
|
||||||
|
"subPath" = "Đường dẫn gốc URL Đăng ký"
|
||||||
|
"subPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng '/'"
|
||||||
|
"subDomain" = "Tên miền con"
|
||||||
|
"subDomainDesc" = "Mặc định để trống để nghe tất cả các tên miền và IP"
|
||||||
|
"subUpdates" = "Khoảng thời gian cập nhật đăng ký"
|
||||||
|
"subUpdatesDesc" = "Số giờ giữa các cập nhật trong ứng dụng khách"
|
||||||
|
"subEncrypt" = "Mã hóa cấu hình"
|
||||||
|
"subEncryptDesc" = "Mã hóa các cấu hình được trả về trong đăng ký"
|
||||||
|
"subShowInfo" = "Hiển thị thông tin sử dụng"
|
||||||
|
"subShowInfoDesc" = "Hiển thị lưu lượng truy cập còn lại và ngày sau tên cấu hình"
|
||||||
|
"subURI" = "URI proxy ngược"
|
||||||
|
"subURIDesc" = "Thay đổi URI cơ sở của URL đăng ký để sử dụng ở phía sau proxy"
|
||||||
|
|
||||||
|
[pages.settings.toasts]
|
||||||
|
"modifySettings" = "Sửa đổi cài đặt"
|
||||||
|
"getSettings" = "Nhận cài đặt "
|
||||||
|
"modifyUser" = "Sửa đổi người dùng"
|
||||||
|
"originalUserPassIncorrect" = "Tên người dùng hoặc mật khẩu ban đầu không chính xác"
|
||||||
|
"userPassMustBeNotEmpty" = "Tên người dùng mới và mật khẩu mới không được để trống"
|
||||||
|
|
||||||
|
[pages.xray]
|
||||||
|
"title" = "Cài đặt Xray"
|
||||||
|
"save" = "Lưu các thiết lập"
|
||||||
|
"restart" = "Khởi động lại Xray"
|
||||||
|
"basicTemplate" = "Mẫu cơ bản"
|
||||||
|
"advancedTemplate" = "Mẫu nâng cao"
|
||||||
|
"generalConfigs" = "Cấu hình Chung"
|
||||||
|
"generalConfigsDesc" = "Những tùy chọn này sẽ cung cấp điều chỉnh tổng quát."
|
||||||
|
"blockConfigs" = "Cấu hình Chặn"
|
||||||
|
"blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể."
|
||||||
|
"blockCountryConfigs" = "Cấu hình Chặn Quốc gia"
|
||||||
|
"blockCountryConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các tên miền quốc gia cụ thể."
|
||||||
|
"directCountryConfigs" = "Cấu hình Kết nối Trực tiếp Quốc gia"
|
||||||
|
"directCountryConfigsDesc" = "Những tùy chọn này sẽ kết nối người dùng trực tiếp đến các tên miền quốc gia cụ thể."
|
||||||
|
"ipv4Configs" = "Cấu hình IPv4"
|
||||||
|
"ipv4ConfigsDesc" = "Những tùy chọn này sẽ chỉ định kết nối đến các tên miền mục tiêu qua IPv4."
|
||||||
|
"warpConfigs" = "Cấu hình WARP"
|
||||||
|
"warpConfigsDesc" = "WARP sẽ định tuyến lưu lượng đến các trang web qua máy chủ Cloudflare."
|
||||||
|
"Template" = "Mẫu cấu hình Xray"
|
||||||
|
"TemplateDesc" = "Tạo tệp cấu hình Xray cuối cùng dựa trên mẫu này."
|
||||||
|
"FreedomStrategy" = "Cấu hình chiến lược cho giao thức tự do"
|
||||||
|
"FreedomStrategyDesc" = "Đặt chiến lược đầu ra của mạng trong Giao thức Tự do."
|
||||||
|
"RoutingStrategy" = "Định cấu hình chiến lược định tuyến tên miền"
|
||||||
|
"RoutingStrategyDesc" = "Đặt chiến lược định tuyến tổng thể để phân giải DNS."
|
||||||
|
"Torrent" = "Cấm sử dụng BitTorrent"
|
||||||
|
"TorrentDesc" = "Thay đổi mẫu cấu hình để tránh việc người dùng sử dụng BitTorrent."
|
||||||
|
"PrivateIp" = "Cấm dãy IP riêng để kết nối"
|
||||||
|
"PrivateIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối với dải IP riêng."
|
||||||
|
"Ads" = "Chặn quảng cáo"
|
||||||
|
"AdsDesc" = "Thay đổi mẫu cấu hình để chặn quảng cáo"
|
||||||
|
"Family" = "Kích hoạt cấu hình thân thiện với gia đình"
|
||||||
|
"FamilyDesc" = "Tránh kết nối đến các trang web không an toàn để bảo vệ gia đình."
|
||||||
|
"IRIp" = "Vô hiệu hóa kết nối với dải IP Iran"
|
||||||
|
"IRIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối với dãy IP Iran."
|
||||||
|
"IRDomain" = "Vô hiệu hóa kết nối với tên miền Iran"
|
||||||
|
"IRDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối với tên miền Iran."
|
||||||
|
"ChinaIp" = "Vô hiệu hóa kết nối với dải IP Trung Quốc"
|
||||||
|
"ChinaIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối tới dãy IP Trung Quốc."
|
||||||
|
"ChinaDomain" = "Vô hiệu hóa kết nối với tên miền Trung Quốc"
|
||||||
|
"ChinaDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối với miền Trung Quốc."
|
||||||
|
"RussiaIp" = "Vô hiệu hóa kết nối với dải IP của Nga"
|
||||||
|
"RussiaIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối với dãy IP của Nga."
|
||||||
|
"RussiaDomain" = "Vô hiệu hóa kết nối với tên miền của Nga"
|
||||||
|
"RussiaDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối với miền Nga."
|
||||||
|
"DirectIRIp" = "Kết nối trực tiếp tới dãy IP Iran"
|
||||||
|
"DirectIRIpDesc" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với dải IP Iran."
|
||||||
|
"DirectIRDomain" = "Kết nối trực tiếp tới các miền của Iran"
|
||||||
|
"DirectIRDomainDesc" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với miền Iran."
|
||||||
|
"DirectChinaIp" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với miền Iran."
|
||||||
|
"DirectChinaIpDesc" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với dải IP Trung Quốc."
|
||||||
|
"DirectChinaDomain" = "Kết nối trực tiếp tới các miền Trung Quốc"
|
||||||
|
"DirectChinaDomainDesc" = "Kết nối trực tiếp tới các miền Trung Quốc"
|
||||||
|
"DirectRussiaIp" = "Kết nối trực tiếp tới dãy IP của Nga"
|
||||||
|
"DirectRussiaIpDesc" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với dải IP của Nga."
|
||||||
|
"DirectRussiaDomain" = "Kết nối trực tiếp tới các miền của Nga"
|
||||||
|
"DirectRussiaDomainDesc" = "Thay đổi mẫu cấu hình để kết nối trực tiếp với miền Nga."
|
||||||
|
"GoogleIPv4" = "Sử dụng IPv4 cho Google"
|
||||||
|
"GoogleIPv4Desc" = "Thêm định tuyến để Google kết nối với IPv4."
|
||||||
|
"NetflixIPv4" = "Sử dụng IPv4 cho Netflix"
|
||||||
|
"NetflixIPv4Desc" = "Thêm định tuyến cho Netflix để kết nối với IPv4."
|
||||||
|
"GoogleWARP" = "Định tuyến Google qua WARP."
|
||||||
|
"GoogleWARPDesc" = "Thêm định tuyến cho Google qua WARP."
|
||||||
|
"OpenAIWARP" = "Định tuyến OpenAI (ChatGPT) qua WARP."
|
||||||
|
"OpenAIWARPDesc" = "Thêm định tuyến cho OpenAI (ChatGPT) qua WARP."
|
||||||
|
"NetflixWARP" = "Định tuyến Netflix qua WARP."
|
||||||
|
"NetflixWARPDesc" = "Thêm định tuyến cho Netflix qua WARP."
|
||||||
|
"SpotifyWARP" = "Định tuyến Spotify qua WARP."
|
||||||
|
"SpotifyWARPDesc" = "Thêm định tuyến cho Spotify qua WARP."
|
||||||
|
"completeTemplate" = "Tất cả"
|
||||||
|
"Inbounds" = "Đầu vào"
|
||||||
|
"Outbounds" = "Đầu ra"
|
||||||
|
"Routings" = "Quy tắc định tuyến"
|
||||||
|
"RoutingsDesc" = "Mức độ ưu tiên của mỗi quy tắc là quan trọng!"
|
||||||
|
|
||||||
|
[pages.xray.rules]
|
||||||
|
"first" = "Đầu tiên"
|
||||||
|
"last" = "Cuối cùng"
|
||||||
|
"up" = "hướng lên"
|
||||||
|
"down" = "Xuống"
|
||||||
|
"source" = "Nguồn"
|
||||||
|
"dest" = "Điểm đến"
|
||||||
|
"inbound" = "Đầu vào"
|
||||||
|
"outbound" = "Đầu ra"
|
||||||
|
"info" = "Thông tin"
|
||||||
|
"add" = "Thêm quy tắc"
|
||||||
|
"edit" = "Chỉnh sửa quy tắc"
|
||||||
|
"useComma" = "Các mục được phân tách bằng dấu phẩy"
|
||||||
|
|
||||||
|
[pages.xray.outbound]
|
||||||
|
"addOutbound" = "Thêm Đầu vào"
|
||||||
|
"addReverse" = "Thêm đảo ngược"
|
||||||
|
"editOutbound" = "Chỉnh sửa đầu vào"
|
||||||
|
"editReverse" = "Chỉnh sửa đảo ngược"
|
||||||
|
"tag" = "Nhãn"
|
||||||
|
"tagDesc" = "thẻ duy nhất"
|
||||||
|
"address" = "Địa chỉ"
|
||||||
|
"reverse" = "Đảo ngược"
|
||||||
|
"domain" = "Tên Miền"
|
||||||
|
"type" = "Kiểu"
|
||||||
|
"bridge" = "Liên kết"
|
||||||
|
"portal" = "Cổng thông tin"
|
||||||
|
"intercon" = "Kết nối"
|
||||||
|
|
||||||
|
[pages.xray.wireguard]
|
||||||
|
"secretKey" = "Chìa khoá bí mật"
|
||||||
|
"publicKey" = "Khóa công khai"
|
||||||
|
"allowedIPs" = "IP được phép"
|
||||||
|
"endpoint" = "Điểm cuối"
|
||||||
|
"psk" = "Khóa chia sẻ"
|
||||||
|
"domainStrategy" = "Chiến lược tên miền"
|
||||||
|
|
||||||
|
[tgbot]
|
||||||
|
"noResult" = "❗ Không có kết quả!"
|
||||||
|
"wentWrong" = "❌ Đã xảy ra lỗi!"
|
||||||
|
"noInbounds" = "❗ Không tìm thấy inbound!"
|
||||||
|
"unlimited" = "♾ Không giới hạn"
|
||||||
|
"day" = "Ngày"
|
||||||
|
"days" = "hôm nay"
|
||||||
|
"unknown" = "không xác định"
|
||||||
|
"inbounds" = "Đầu vào"
|
||||||
|
"clients" = "Khách hàng"
|
||||||
|
|
||||||
|
[tgbot.commands]
|
||||||
|
"unknown" = "❗ Lệnh không rõ"
|
||||||
|
"pleaseChoose" = "👇 Vui lòng chọn:\r\n"
|
||||||
|
"help" = "🤖 Chào mừng bạn đến với bot này! Bot được thiết kế để cung cấp cho bạn dữ liệu cụ thể từ máy chủ và cho phép bạn thực hiện các thay đổi cần thiết.\r\n\r\n"
|
||||||
|
"start" = "👋 Xin chào <i>{{ .Firstname }}</i>.\r\n"
|
||||||
|
"welcome" = "🤖 Chào mừng đến với bot quản lý của <b>{{ .Hostname }}</b>.\r\n"
|
||||||
|
"status" = "✅ Bot hoạt động bình thường!"
|
||||||
|
"usage" = "❗ Vui lòng cung cấp văn bản để tìm kiếm!"
|
||||||
|
"getID" = "🆔 ID của bạn: <code>{{ .ID }}</code>"
|
||||||
|
"helpAdminCommands" = "Tìm kiếm email của khách hàng:\r\n<code>/usage [Email]</code>\r\n \r\nTìm kiếm inbounds (với thống kê của khách hàng):\r\n<code>/inbound [Ghi chú]</code>"
|
||||||
|
"helpClientCommands" = "Để tìm kiếm thống kê, hãy sử dụng lệnh sau:\r\n \r\n<code>/usage [UUID|Mật khẩu]</code>\r\n \r\nSử dụng UUID cho vmess/vless và Mật khẩu cho Trojan."
|
||||||
|
|
||||||
|
[tgbot.messages]
|
||||||
|
"cpuThreshold" = "🔴 Sử dụng CPU {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%"
|
||||||
|
"loginSuccess" = "✅ Đăng nhập thành công vào bảng điều khiển.\r\n"
|
||||||
|
"loginFailed" = "❗️ Đăng nhập vào bảng không thành công.\r\n"
|
||||||
|
"report" = "🕰 Báo cáo theo lịch trình: {{ .RunTime }}\r\n"
|
||||||
|
"datetime" = "⏰ Ngày-Giờ: {{ .DateTime }}\r\n"
|
||||||
|
"hostname" = "💻 Tên máy chủ: {{ .Hostname }}\r\n"
|
||||||
|
"version" = "🚀 Phiên bản X-UI: {{ .Version }}\r\n"
|
||||||
|
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
||||||
|
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
||||||
|
"ip" = "🌐 IP: {{ .IP }}\r\n"
|
||||||
|
"serverUpTime" = "⏳ Thời gian hoạt động của máy chủ: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||||
|
"serverLoad" = "📈 Tải máy chủ: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||||
|
"serverMemory" = "📋 Bộ nhớ máy chủ: {{ .Current }}/{{ .Total }}\r\n"
|
||||||
|
"tcpCount" = "🔹 TcpCount: {{ .Count }}\r\n"
|
||||||
|
"udpCount" = "🔸 UdpCount: {{ .Count }}\r\n"
|
||||||
|
"traffic" = "🚦 Giao thông: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
|
"xrayStatus" = "ℹ️ Tình trạng Xray: {{ .State }}\r\n"
|
||||||
|
"username" = "👤 Tên tài khoản: {{ .Username }}\r\n"
|
||||||
|
"time" = "⏰ Thời gian: {{ .Time }}\r\n"
|
||||||
|
"inbound" = "📍 Điểm vào: {{ .Remark }}\r\n"
|
||||||
|
"port" = "🔌 Cổng: {{ .Port }}\r\n"
|
||||||
|
"expire" = "📅 Hạn sử dụng: {{ .DateTime }}\r\n \r\n"
|
||||||
|
"expireIn" = "📅 Hết hạn vào: {{ .Time }}\r\n \r\n"
|
||||||
|
"active" = "💡 Có hiệu lực {{ .Enable }}\r\n"
|
||||||
|
"online" = "🌐 Tình trạng kết nối: {{ .Status }}\r\n"
|
||||||
|
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||||
|
"upload" = "🔼 Tải lên↑: {{ .Upload }}\r\n"
|
||||||
|
"download" = "🔽 Tải xuống↓: {{ .Download }}\r\n"
|
||||||
|
"total" = "🔄 Tổng cộng: {{ .UpDown }} / {{ .Total }}\r\n"
|
||||||
|
"exhaustedMsg" = "🚨 Đã cạn kiệt {{ .Type }}:\r\n"
|
||||||
|
"exhaustedCount" = "🚨 Đã cạn kiệt {{ .Type }} count:\r\n"
|
||||||
|
"onlinesCount" = "🌐 Số lượng khách hàng trực tuyến: {{ .Count }}\r\n"
|
||||||
|
"disabled" = "🛑 Cấm sử dụng {{ .Disabled }}\r\n"
|
||||||
|
"depleteSoon" = "🔜 Sớm cạn kiệt: {{ .Deplete }}\r\n \r\n"
|
||||||
|
"backupTime" = "🗄 Thời gian sao lưu: {{ .Time }}\r\n"
|
||||||
|
"yes" = "✅ Yes"
|
||||||
|
"no" = "❌ No"
|
||||||
|
|
||||||
|
[tgbot.buttons]
|
||||||
|
"dbBackup" = "Tải Backup DB"
|
||||||
|
"serverUsage" = "Sử Dụng Máy Chủ"
|
||||||
|
"getInbounds" = "Lấy Inbounds"
|
||||||
|
"depleteSoon" = "Sắp Cạn Kiệt"
|
||||||
|
"clientUsage" = "Lấy Sử Dụng"
|
||||||
|
"onlines" = "Khách hàng trực tuyến"
|
||||||
|
"commands" = "Lệnh"
|
||||||
|
|
||||||
|
[tgbot.answers]
|
||||||
|
"getInboundsFailed" = "❌ Không vào được"
|
||||||
|
"askToAddUser" = "Không tìm thấy cấu hình của bạn!\r\nBạn nên định cấu hình tên người dùng telegram của mình và yêu cầu Quản trị viên thêm nó vào cấu hình của bạn."
|
||||||
|
"askToAddUserName" = "Không tìm thấy cấu hình của bạn!\r\nVui lòng yêu cầu Quản trị viên của bạn sử dụng tên người dùng telegram trong cấu hình của bạn(s).\r\n\r\nTên người dùng của bạn: <b>@{{ .TgUserName }}</b>"
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
"transmission" = "传输"
|
"transmission" = "传输"
|
||||||
"host" = "主持人"
|
"host" = "主持人"
|
||||||
"path" = "小路"
|
"path" = "小路"
|
||||||
"camouflage" = "伪装"
|
"camouflage" = "混淆"
|
||||||
"status" = "状态"
|
"status" = "状态"
|
||||||
"enabled" = "开启"
|
"enabled" = "开启"
|
||||||
"disabled" = "关闭"
|
"disabled" = "关闭"
|
||||||
@@ -60,10 +60,10 @@
|
|||||||
"settings" = "面板设置"
|
"settings" = "面板设置"
|
||||||
"xray" = "Xray 设置"
|
"xray" = "Xray 设置"
|
||||||
"logout" = "退出登录"
|
"logout" = "退出登录"
|
||||||
"link" = "其他"
|
"link" = "管理"
|
||||||
|
|
||||||
[pages.login]
|
[pages.login]
|
||||||
"title" = "登录"
|
"title" = "欢迎"
|
||||||
"loginAgain" = "登录时效已过,请重新登录"
|
"loginAgain" = "登录时效已过,请重新登录"
|
||||||
|
|
||||||
[pages.login.toasts]
|
[pages.login.toasts]
|
||||||
@@ -86,8 +86,8 @@
|
|||||||
"operationHours" = "运行时间"
|
"operationHours" = "运行时间"
|
||||||
"operationHoursDesc" = "系统自启动以来的运行时间"
|
"operationHoursDesc" = "系统自启动以来的运行时间"
|
||||||
"systemLoad" = "系统负载"
|
"systemLoad" = "系统负载"
|
||||||
"connectionCount" = "连接数"
|
"connectionTcpCountDesc" = "所有网卡的总 TCP 连接数。"
|
||||||
"connectionCountDesc" = "所有网卡的总连接数"
|
"connectionUdpCountDesc" = "所有网卡的总 UDP 连接数。"
|
||||||
"upSpeed" = "所有网卡的总上传速度"
|
"upSpeed" = "所有网卡的总上传速度"
|
||||||
"downSpeed" = "所有网卡的总下载速度"
|
"downSpeed" = "所有网卡的总下载速度"
|
||||||
"totalSent" = "系统启动以来所有网卡的总上传流量"
|
"totalSent" = "系统启动以来所有网卡的总上传流量"
|
||||||
@@ -100,8 +100,8 @@
|
|||||||
"backup" = "备份"
|
"backup" = "备份"
|
||||||
"backupTitle" = "备份数据库"
|
"backupTitle" = "备份数据库"
|
||||||
"backupDescription" = "请记住在导入新数据库之前进行备份"
|
"backupDescription" = "请记住在导入新数据库之前进行备份"
|
||||||
"exportDatabase" = "下载数据库"
|
"exportDatabase" = "备份"
|
||||||
"importDatabase" = "上传数据库"
|
"importDatabase" = "恢复"
|
||||||
|
|
||||||
[pages.inbounds]
|
[pages.inbounds]
|
||||||
"title" = "入站列表"
|
"title" = "入站列表"
|
||||||
@@ -167,9 +167,13 @@
|
|||||||
"emailDesc" = "电子邮件必须完全唯"
|
"emailDesc" = "电子邮件必须完全唯"
|
||||||
"setDefaultCert" = "从面板设置证书"
|
"setDefaultCert" = "从面板设置证书"
|
||||||
"telegramDesc" = "使用 Telegram ID,不包含 @ 符号或聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
|
"telegramDesc" = "使用 Telegram ID,不包含 @ 符号或聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
|
||||||
"subscriptionDesc" = "您可以在详细信息上找到您的子链接,也可以对多个配置使用相同的名称"
|
"subscriptionDesc" = "您可以在详细信息上找到您的子链接,你可以为多个客户端使用相同的名称"
|
||||||
"info" = "信息"
|
"info" = "信息"
|
||||||
"same" = "相同"
|
"same" = "相同"
|
||||||
|
"inboundData" = "入站数据"
|
||||||
|
"copyToClipboard" = "复制到剪贴板"
|
||||||
|
"import"="导入"
|
||||||
|
"importInbound" = "导入入站"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "添加客户端"
|
"add" = "添加客户端"
|
||||||
@@ -184,10 +188,10 @@
|
|||||||
"prefix" = "前缀"
|
"prefix" = "前缀"
|
||||||
"postfix" = "后缀"
|
"postfix" = "后缀"
|
||||||
"delayedStart" = "首次使用后开始"
|
"delayedStart" = "首次使用后开始"
|
||||||
"expireDays" = "过期天数"
|
"expireDays" = "期间"
|
||||||
"days" = "天"
|
"days" = "天"
|
||||||
"renew" = "自动续订"
|
"renew" = "自动续订"
|
||||||
"renewDesc" = "过期后自动续订。0 = 禁用"
|
"renewDesc" = "到期后自动续订。(0 = 禁用)(单元: 天)"
|
||||||
|
|
||||||
[pages.inbounds.toasts]
|
[pages.inbounds.toasts]
|
||||||
"obtain" = "获取"
|
"obtain" = "获取"
|
||||||
@@ -233,6 +237,8 @@
|
|||||||
"panelUrlPathDesc" = "必须以 '/' 开头,以 '/' 结尾"
|
"panelUrlPathDesc" = "必须以 '/' 开头,以 '/' 结尾"
|
||||||
"pageSize" = "分页大小"
|
"pageSize" = "分页大小"
|
||||||
"pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用"
|
"pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用"
|
||||||
|
"remarkModel" = "备注模型和分隔符"
|
||||||
|
"sampleRemark" = "备注示例"
|
||||||
"oldUsername" = "原用户名"
|
"oldUsername" = "原用户名"
|
||||||
"currentPassword" = "原密码"
|
"currentPassword" = "原密码"
|
||||||
"newUsername" = "新用户名"
|
"newUsername" = "新用户名"
|
||||||
@@ -306,6 +312,8 @@
|
|||||||
"directCountryConfigsDesc" = "这些选项会将用户直接连接到特定国家/地区的域。"
|
"directCountryConfigsDesc" = "这些选项会将用户直接连接到特定国家/地区的域。"
|
||||||
"ipv4Configs" = "IPv4 配置"
|
"ipv4Configs" = "IPv4 配置"
|
||||||
"ipv4ConfigsDesc" = "此选项将仅通过 IPv4 路由到目标域"
|
"ipv4ConfigsDesc" = "此选项将仅通过 IPv4 路由到目标域"
|
||||||
|
"warpConfigs" = "WARP 配置"
|
||||||
|
"warpConfigsDesc" = "WARP 将通过 Cloudflare 服务器将流量路由到网站。"
|
||||||
"Template" = "Xray 配置模板"
|
"Template" = "Xray 配置模板"
|
||||||
"TemplateDesc" = "以该模型为基础生成最终的Xray配置文件,重新启动面板生成效率"
|
"TemplateDesc" = "以该模型为基础生成最终的Xray配置文件,重新启动面板生成效率"
|
||||||
"FreedomStrategy" = "配置自由协议的策略"
|
"FreedomStrategy" = "配置自由协议的策略"
|
||||||
@@ -348,6 +356,14 @@
|
|||||||
"GoogleIPv4Desc" = "添加谷歌连接IPv4的路由"
|
"GoogleIPv4Desc" = "添加谷歌连接IPv4的路由"
|
||||||
"NetflixIPv4" = "为 Netflix 使用 IPv4"
|
"NetflixIPv4" = "为 Netflix 使用 IPv4"
|
||||||
"NetflixIPv4Desc" = "添加Netflix连接IPv4的路由"
|
"NetflixIPv4Desc" = "添加Netflix连接IPv4的路由"
|
||||||
|
"GoogleWARP" = "将谷歌路由到 WARP"
|
||||||
|
"GoogleWARPDesc" = "为谷歌添加路由到WARP"
|
||||||
|
"OpenAIWARP" = "将 OpenAI (ChatGPT) 路由到 WARP"
|
||||||
|
"OpenAIWARPDesc" = "将OpenAI(ChatGPT)路由添加到WARP"
|
||||||
|
"NetflixWARP" = "将 Netflix 路由到 WARP"
|
||||||
|
"NetflixWARPDesc" = "为Netflix添加路由到WARP"
|
||||||
|
"SpotifyWARP" = "将 Spotify 路由到 WARP"
|
||||||
|
"SpotifyWARPDesc" = "为Spotify添加路由到WARP"
|
||||||
"completeTemplate" = "全部"
|
"completeTemplate" = "全部"
|
||||||
"Inbounds" = "界内"
|
"Inbounds" = "界内"
|
||||||
"Outbounds" = "出站"
|
"Outbounds" = "出站"
|
||||||
@@ -355,7 +371,7 @@
|
|||||||
"RoutingsDesc" = "每条规则的优先级都很重要"
|
"RoutingsDesc" = "每条规则的优先级都很重要"
|
||||||
|
|
||||||
[pages.xray.rules]
|
[pages.xray.rules]
|
||||||
"firsto" = "第一个"
|
"first" = "第一个"
|
||||||
"last" = "最后"
|
"last" = "最后"
|
||||||
"up" = "向上"
|
"up" = "向上"
|
||||||
"down" = "向下"
|
"down" = "向下"
|
||||||
@@ -374,14 +390,23 @@
|
|||||||
"editOutbound" = "编辑出站"
|
"editOutbound" = "编辑出站"
|
||||||
"editReverse" = "编辑反向"
|
"editReverse" = "编辑反向"
|
||||||
"tag" = "标签"
|
"tag" = "标签"
|
||||||
|
"tagDesc" = "独特的标签"
|
||||||
"address" = "地址"
|
"address" = "地址"
|
||||||
"rreverse" = "反转"
|
"reverse" = "反转"
|
||||||
"domain" = "域名"
|
"domain" = "域名"
|
||||||
"type" = "类型"
|
"type" = "类型"
|
||||||
"bridge" = "桥"
|
"bridge" = "桥"
|
||||||
"portal" = "门户"
|
"portal" = "门户"
|
||||||
"intercon" = "互连"
|
"intercon" = "互连"
|
||||||
|
|
||||||
|
[pages.xray.wireguard]
|
||||||
|
"secretKey" = "密钥"
|
||||||
|
"publicKey" = "公钥"
|
||||||
|
"allowedIPs" = "允许的 IP"
|
||||||
|
"endpoint" = "终点"
|
||||||
|
"psk" = "共享密钥"
|
||||||
|
"domainStrategy" = "域策略"
|
||||||
|
|
||||||
[tgbot]
|
[tgbot]
|
||||||
"noResult" = "❗ 没有结果!"
|
"noResult" = "❗ 没有结果!"
|
||||||
"wentWrong" = "❌ 出了点问题!"
|
"wentWrong" = "❌ 出了点问题!"
|
||||||
|
|||||||
172
x-ui.sh
@@ -115,6 +115,24 @@ update() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
custom_version() {
|
||||||
|
echo "Enter the panel version (like 1.6.0):"
|
||||||
|
read panel_version
|
||||||
|
|
||||||
|
if [ -z "$panel_version" ]; then
|
||||||
|
echo "Panel version cannot be empty. Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
download_link="https://raw.githubusercontent.com/alireza0/x-ui/master/install.sh"
|
||||||
|
|
||||||
|
# Use the entered panel version in the download link
|
||||||
|
install_command="bash <(curl -Ls $download_link) $panel_version"
|
||||||
|
|
||||||
|
echo "Downloading and installing panel version $panel_version..."
|
||||||
|
eval $install_command
|
||||||
|
}
|
||||||
|
|
||||||
uninstall() {
|
uninstall() {
|
||||||
confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n"
|
confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n"
|
||||||
if [[ $? != 0 ]]; then
|
if [[ $? != 0 ]]; then
|
||||||
@@ -283,12 +301,6 @@ show_log() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
migrate_v2_ui() {
|
|
||||||
/usr/local/x-ui/x-ui v2-ui
|
|
||||||
|
|
||||||
before_show_menu
|
|
||||||
}
|
|
||||||
|
|
||||||
install_bbr() {
|
install_bbr() {
|
||||||
# temporary workaround for installing bbr
|
# temporary workaround for installing bbr
|
||||||
bash <(curl -L -s https://raw.githubusercontent.com/teddysun/across/master/bbr.sh)
|
bash <(curl -L -s https://raw.githubusercontent.com/teddysun/across/master/bbr.sh)
|
||||||
@@ -362,7 +374,7 @@ show_status() {
|
|||||||
check_status
|
check_status
|
||||||
case $? in
|
case $? in
|
||||||
0)
|
0)
|
||||||
echo -e "Panel state: ${green}Runing${plain}"
|
echo -e "Panel state: ${green}Running${plain}"
|
||||||
show_enable_status
|
show_enable_status
|
||||||
;;
|
;;
|
||||||
1)
|
1)
|
||||||
@@ -397,7 +409,7 @@ check_xray_status() {
|
|||||||
show_xray_status() {
|
show_xray_status() {
|
||||||
check_xray_status
|
check_xray_status
|
||||||
if [[ $? == 0 ]]; then
|
if [[ $? == 0 ]]; then
|
||||||
echo -e "xray state: ${green}Runing${plain}"
|
echo -e "xray state: ${green}Running${plain}"
|
||||||
else
|
else
|
||||||
echo -e "xray state: ${red}Not Running${plain}"
|
echo -e "xray state: ${red}Not Running${plain}"
|
||||||
fi
|
fi
|
||||||
@@ -612,54 +624,95 @@ ssl_cert_issue_CF() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_geo() {
|
||||||
|
cd /usr/local/x-ui/bin
|
||||||
|
echo -e "${green}\t1.${plain} Update Geofiles [Recommended choice] "
|
||||||
|
echo -e "${green}\t2.${plain} Download from optional jsDelivr CDN "
|
||||||
|
echo -e "${green}\t0.${plain} Back To Main Menu "
|
||||||
|
read -p "Select: " select
|
||||||
|
|
||||||
|
case "$select" in
|
||||||
|
0)
|
||||||
|
show_menu
|
||||||
|
;;
|
||||||
|
|
||||||
|
1)
|
||||||
|
wget -N "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n"
|
||||||
|
wget -N "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n"
|
||||||
|
wget "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" -O /tmp/wget && mv /tmp/wget geoip_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n"
|
||||||
|
wget "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" -O /tmp/wget && mv /tmp/wget geosite_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n"
|
||||||
|
echo -e "${green}Files are updated.${plain}"
|
||||||
|
confirm_restart
|
||||||
|
;;
|
||||||
|
|
||||||
|
2)
|
||||||
|
wget -N "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geoip.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n"
|
||||||
|
wget -N "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat" && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n"
|
||||||
|
wget "https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/geoip.dat" -O /tmp/wget && mv /tmp/wget geoip_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n"
|
||||||
|
wget "https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/geosite.dat" -O /tmp/wget && mv /tmp/wget geosite_IR.dat && echo -e "${green}Success${plain}\n" || echo -e "${red}Failure${plain}\n"
|
||||||
|
echo -e "${green}Files are updated.${plain}"
|
||||||
|
confirm_restart
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
LOGE "Please enter a correct number [0-2]\n"
|
||||||
|
update_geo
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
show_usage() {
|
show_usage() {
|
||||||
echo "x-ui control menu usages: "
|
echo "X-UI Control Menu Usage"
|
||||||
echo "------------------------------------------"
|
echo "------------------------------------------"
|
||||||
echo "x-ui - Enter Admin menu"
|
echo "SUBCOMMANDS:"
|
||||||
echo "x-ui start - Start x-ui"
|
echo "x-ui - Admin Management Script"
|
||||||
echo "x-ui stop - Stop x-ui"
|
echo "x-ui start - Start"
|
||||||
echo "x-ui restart - Restart x-ui"
|
echo "x-ui stop - Stop"
|
||||||
echo "x-ui status - Show x-ui status"
|
echo "x-ui restart - Restart"
|
||||||
echo "x-ui enable - Enable x-ui on system startup"
|
echo "x-ui status - Current Status"
|
||||||
echo "x-ui disable - Disable x-ui on system startup"
|
echo "x-ui enable - Enable Autostart on OS Startup"
|
||||||
echo "x-ui log - Check x-ui logs"
|
echo "x-ui disable - Disable Autostart on OS Startup"
|
||||||
echo "x-ui update - Update x-ui"
|
echo "x-ui log - Check Logs"
|
||||||
echo "x-ui install - Install x-ui"
|
echo "x-ui update - Update"
|
||||||
echo "x-ui uninstall - Uninstall x-ui"
|
echo "x-ui install - Install"
|
||||||
|
echo "x-ui uninstall - Uninstall"
|
||||||
|
echo "x-ui help - Control Menu Usage"
|
||||||
echo "------------------------------------------"
|
echo "------------------------------------------"
|
||||||
}
|
}
|
||||||
|
|
||||||
show_menu() {
|
show_menu() {
|
||||||
echo -e "
|
echo -e "
|
||||||
${green}X-UI Panel Management Script${plain}
|
${green}X-UI Admin Management Script ${plain}
|
||||||
————————————————
|
————————————————
|
||||||
${green}0.${plain} Exit Script
|
${green}0.${plain} Exit
|
||||||
————————————————
|
————————————————
|
||||||
${green}1.${plain} Install X-UI
|
${green}1.${plain} Install
|
||||||
${green}2.${plain} Update X-UI
|
${green}2.${plain} Update
|
||||||
${green}3.${plain} Uninstall X-UI
|
${green}3.${plain} Custom Version
|
||||||
|
${green}4.${plain} Uninstall
|
||||||
————————————————
|
————————————————
|
||||||
${green}4.${plain} Reset Username and Password
|
${green}5.${plain} Reset Username and Password
|
||||||
${green}5.${plain} Reset Panel Settings
|
${green}6.${plain} Reset Panel Settings
|
||||||
${green}6.${plain} Set Panel Port
|
${green}7.${plain} Set Panel Port
|
||||||
${green}7.${plain} View Current Panel Settings
|
${green}8.${plain} View Panel Settings
|
||||||
————————————————
|
————————————————
|
||||||
${green}8.${plain} Start X-UI
|
${green}9.${plain} Start
|
||||||
${green}9.${plain} Stop X-UI
|
${green}10.${plain} Stop
|
||||||
${green}10.${plain} Reboot X-UI
|
${green}11.${plain} Restart
|
||||||
${green}11.${plain} Check X-UI State
|
${green}12.${plain} Check State
|
||||||
${green}12.${plain} Check X-UI Logs
|
${green}13.${plain} Check Logs
|
||||||
————————————————
|
————————————————
|
||||||
${green}13.${plain} Set X-UI Autostart
|
${green}14.${plain} Enable Autostart
|
||||||
${green}14.${plain} Cancel X-UI Autostart
|
${green}15.${plain} Disable Autostart
|
||||||
————————————————
|
————————————————
|
||||||
${green}15.${plain} 一A Key Installation BBR (latest kernel)
|
${green}16.${plain} A Key Installation BBR (latest kernel)
|
||||||
${green}16.${plain} 一SSL Certificate Management
|
${green}17.${plain} SSL Certificate Management
|
||||||
${green}17.${plain} 一Cloudflare SSL Certificate
|
${green}18.${plain} Cloudflare SSL Certificate
|
||||||
|
${green}19.${plain} Update Geo Files
|
||||||
————————————————
|
————————————————
|
||||||
"
|
"
|
||||||
show_status
|
show_status
|
||||||
echo && read -p "Please enter your selection [0-17]: " num
|
echo && read -p "Please enter your selection [0-19]: " num
|
||||||
|
|
||||||
case "${num}" in
|
case "${num}" in
|
||||||
0)
|
0)
|
||||||
@@ -672,52 +725,58 @@ show_menu() {
|
|||||||
check_install && update
|
check_install && update
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
check_install && uninstall
|
check_install && custom_version
|
||||||
;;
|
;;
|
||||||
4)
|
4)
|
||||||
check_install && reset_user
|
check_install && uninstall
|
||||||
;;
|
;;
|
||||||
5)
|
5)
|
||||||
check_install && reset_config
|
check_install && reset_user
|
||||||
;;
|
;;
|
||||||
6)
|
6)
|
||||||
check_install && set_port
|
check_install && reset_config
|
||||||
;;
|
;;
|
||||||
7)
|
7)
|
||||||
check_install && check_config
|
check_install && set_port
|
||||||
;;
|
;;
|
||||||
8)
|
8)
|
||||||
check_install && start
|
check_install && check_config
|
||||||
;;
|
;;
|
||||||
9)
|
9)
|
||||||
check_install && stop
|
check_install && start
|
||||||
;;
|
;;
|
||||||
10)
|
10)
|
||||||
check_install && restart
|
check_install && stop
|
||||||
;;
|
;;
|
||||||
11)
|
11)
|
||||||
check_install && status
|
check_install && restart
|
||||||
;;
|
;;
|
||||||
12)
|
12)
|
||||||
check_install && show_log
|
check_install && status
|
||||||
;;
|
;;
|
||||||
13)
|
13)
|
||||||
check_install && enable
|
check_install && show_log
|
||||||
;;
|
;;
|
||||||
14)
|
14)
|
||||||
check_install && disable
|
check_install && enable
|
||||||
;;
|
;;
|
||||||
15)
|
15)
|
||||||
install_bbr
|
check_install && disable
|
||||||
;;
|
;;
|
||||||
16)
|
16)
|
||||||
ssl_cert_issue_main
|
install_bbr
|
||||||
;;
|
;;
|
||||||
17)
|
17)
|
||||||
|
ssl_cert_issue_main
|
||||||
|
;;
|
||||||
|
18)
|
||||||
ssl_cert_issue_CF
|
ssl_cert_issue_CF
|
||||||
;;
|
;;
|
||||||
|
19)
|
||||||
|
update_geo
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
LOGE "Please enter the correct number [0-16]"
|
LOGE "Please enter the correct number [0-19]"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -745,9 +804,6 @@ if [[ $# > 0 ]]; then
|
|||||||
"log")
|
"log")
|
||||||
check_install 0 && show_log 0
|
check_install 0 && show_log 0
|
||||||
;;
|
;;
|
||||||
"v2-ui")
|
|
||||||
check_install 0 && migrate_v2_ui 0
|
|
||||||
;;
|
|
||||||
"update")
|
"update")
|
||||||
check_install 0 && update 0
|
check_install 0 && update 0
|
||||||
;;
|
;;
|
||||||
|
|||||||
58
xray/log_writer.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package xray
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"x-ui/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewLogWriter() *LogWriter {
|
||||||
|
return &LogWriter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogWriter struct {
|
||||||
|
lastLine string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lw *LogWriter) Write(m []byte) (n int, err error) {
|
||||||
|
// Convert the data to a string
|
||||||
|
message := strings.TrimSpace(string(m))
|
||||||
|
messages := strings.Split(message, "\n")
|
||||||
|
lw.lastLine = messages[len(messages)-1]
|
||||||
|
|
||||||
|
for _, msg := range messages {
|
||||||
|
messageBody := msg
|
||||||
|
|
||||||
|
// Remove timestamp
|
||||||
|
splittedMsg := strings.SplitN(msg, " ", 3)
|
||||||
|
if len(splittedMsg) > 2 {
|
||||||
|
messageBody = strings.TrimSpace(strings.SplitN(msg, " ", 3)[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find level in []
|
||||||
|
startIndex := strings.Index(messageBody, "[")
|
||||||
|
endIndex := strings.Index(messageBody, "]")
|
||||||
|
if startIndex != -1 && endIndex != -1 {
|
||||||
|
level := strings.TrimSpace(messageBody[startIndex+1 : endIndex])
|
||||||
|
msgBody := "XRAY: " + strings.TrimSpace(messageBody[endIndex+1:])
|
||||||
|
|
||||||
|
// Map the level to the appropriate logger function
|
||||||
|
switch level {
|
||||||
|
case "Debug":
|
||||||
|
logger.Debug(msgBody)
|
||||||
|
case "Info":
|
||||||
|
logger.Info(msgBody)
|
||||||
|
case "Warning":
|
||||||
|
logger.Warning(msgBody)
|
||||||
|
case "Error":
|
||||||
|
logger.Error(msgBody)
|
||||||
|
default:
|
||||||
|
logger.Debug("XRAY: " + msg)
|
||||||
|
}
|
||||||
|
} else if msg != "" {
|
||||||
|
logger.Debug("XRAY: " + msg)
|
||||||
|
return len(m), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(m), nil
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package xray
|
package xray
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -10,13 +9,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"x-ui/config"
|
"x-ui/config"
|
||||||
"x-ui/util/common"
|
"x-ui/util/common"
|
||||||
|
|
||||||
"github.com/Workiva/go-datastructures/queue"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetBinaryName() string {
|
func GetBinaryName() string {
|
||||||
@@ -62,7 +58,7 @@ type process struct {
|
|||||||
onlineClients []string
|
onlineClients []string
|
||||||
|
|
||||||
config *Config
|
config *Config
|
||||||
lines *queue.Queue
|
logWriter *LogWriter
|
||||||
exitErr error
|
exitErr error
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
}
|
}
|
||||||
@@ -71,7 +67,7 @@ func newProcess(config *Config) *process {
|
|||||||
return &process{
|
return &process{
|
||||||
version: "Unknown",
|
version: "Unknown",
|
||||||
config: config,
|
config: config,
|
||||||
lines: queue.New(100),
|
logWriter: NewLogWriter(),
|
||||||
startTime: time.Now(),
|
startTime: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,17 +87,10 @@ func (p *process) GetErr() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *process) GetResult() string {
|
func (p *process) GetResult() string {
|
||||||
if p.lines.Empty() && p.exitErr != nil {
|
if len(p.logWriter.lastLine) == 0 && p.exitErr != nil {
|
||||||
return p.exitErr.Error()
|
return p.exitErr.Error()
|
||||||
}
|
}
|
||||||
items, _ := p.lines.TakeUntil(func(item interface{}) bool {
|
return p.logWriter.lastLine
|
||||||
return true
|
|
||||||
})
|
|
||||||
lines := make([]string, 0, len(items))
|
|
||||||
for _, item := range items {
|
|
||||||
lines = append(lines, item.(string))
|
|
||||||
}
|
|
||||||
return strings.Join(lines, "\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *process) GetVersion() string {
|
func (p *process) GetVersion() string {
|
||||||
@@ -176,50 +165,8 @@ func (p *process) Start() (err error) {
|
|||||||
cmd := exec.Command(GetBinaryPath(), "-c", configPath)
|
cmd := exec.Command(GetBinaryPath(), "-c", configPath)
|
||||||
p.cmd = cmd
|
p.cmd = cmd
|
||||||
|
|
||||||
stdReader, err := cmd.StdoutPipe()
|
cmd.Stdout = p.logWriter
|
||||||
if err != nil {
|
cmd.Stderr = p.logWriter
|
||||||
return err
|
|
||||||
}
|
|
||||||
errReader, err := cmd.StderrPipe()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer func() {
|
|
||||||
common.Recover("")
|
|
||||||
stdReader.Close()
|
|
||||||
}()
|
|
||||||
reader := bufio.NewReaderSize(stdReader, 8192)
|
|
||||||
for {
|
|
||||||
line, _, err := reader.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if p.lines.Len() >= 100 {
|
|
||||||
p.lines.Get(1)
|
|
||||||
}
|
|
||||||
p.lines.Put(string(line))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
defer func() {
|
|
||||||
common.Recover("")
|
|
||||||
errReader.Close()
|
|
||||||
}()
|
|
||||||
reader := bufio.NewReaderSize(errReader, 8192)
|
|
||||||
for {
|
|
||||||
line, _, err := reader.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if p.lines.Len() >= 100 {
|
|
||||||
p.lines.Get(1)
|
|
||||||
}
|
|
||||||
p.lines.Put(string(line))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
|
|||||||