Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82244ced73 | ||
|
|
6856807726 | ||
|
|
2488adc042 | ||
|
|
62f08e877d | ||
|
|
3ed6fc4036 | ||
|
|
6550aa6bad | ||
|
|
2a20243751 | ||
|
|
fc56a1acac | ||
|
|
679dc69f6e | ||
|
|
ca2b3dc4fc | ||
|
|
c3d90c3f94 | ||
|
|
98cf1f2db6 | ||
|
|
4b5dd99555 | ||
|
|
491f7aecef | ||
|
|
590a8f07b9 | ||
|
|
594f004d2a | ||
|
|
bee690429f | ||
|
|
2111632702 | ||
|
|
a9229ecafe | ||
|
|
dadfa107af | ||
|
|
6feff78968 | ||
|
|
f035837aed | ||
|
|
b056c2dda0 | ||
|
|
97e6420018 | ||
|
|
abfcbf4226 | ||
|
|
1f9b3730d4 | ||
|
|
0824512a46 | ||
|
|
ee703ad857 | ||
|
|
722f5e716f | ||
|
|
fdf31d80e7 | ||
|
|
f8fccc057b | ||
|
|
4bb31b0af4 | ||
|
|
8b25e21f4e | ||
|
|
984b469c6f | ||
|
|
96c4cfeb23 | ||
|
|
e50b8db6c5 | ||
|
|
0734f4cd54 | ||
|
|
fbab0df504 | ||
|
|
5e3478f1c1 | ||
|
|
c76199514a | ||
|
|
31e9734414 | ||
|
|
ceee1e4277 | ||
|
|
b725ea7de5 | ||
|
|
0a4c8ffcf5 | ||
|
|
d305dd4e9f | ||
|
|
bbcab768ca | ||
|
|
954bf6fb5d | ||
|
|
77b83d81e2 | ||
|
|
10cd5159d1 | ||
|
|
b8b2acf853 | ||
|
|
4f3b93171a | ||
|
|
9261f9c665 | ||
|
|
6a41e19f7a | ||
|
|
0d2bdde149 | ||
|
|
bcfa47e2ad | ||
|
|
77776e1a62 | ||
|
|
c0d8f931da | ||
|
|
eec4d71097 | ||
|
|
a2bdf23940 | ||
|
|
d08aaa0068 | ||
|
|
1e1f947060 | ||
|
|
62ae7aaffa | ||
|
|
c234bbe9e1 | ||
|
|
ed38102f17 | ||
|
|
69651ecfb5 | ||
|
|
78d3680ced | ||
|
|
495bfb9683 | ||
|
|
9b60b0fd45 | ||
|
|
c6881e5149 | ||
|
|
5971e3f856 | ||
|
|
647b72e4fa |
6
.github/workflows/docker.yml
vendored
6
.github/workflows/docker.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Release X-ui dockerhub
|
||||
name: Release 3X-UI dockerhub
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5.3.0
|
||||
uses: docker/metadata-action@v5.5.0
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
|
||||
@@ -36,6 +36,6 @@ jobs:
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
platforms: linux/amd64, linux/arm64/v8
|
||||
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
43
.github/workflows/release.yml
vendored
43
.github/workflows/release.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Release 3X-ui
|
||||
name: Release 3X-UI
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -10,7 +10,11 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [amd64, arm64]
|
||||
platform:
|
||||
- amd64
|
||||
- arm64
|
||||
- armv7
|
||||
- armv6
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -20,12 +24,15 @@ jobs:
|
||||
uses: actions/setup-go@v5.0.0
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Install dependencies for arm64
|
||||
if: matrix.platform == 'arm64'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt install gcc-aarch64-linux-gnu
|
||||
if [ "${{ matrix.platform }}" == "arm64" ]; then
|
||||
sudo apt install gcc-aarch64-linux-gnu
|
||||
elif [ "${{ matrix.platform }}" == "armv7" ] || [ "${{ matrix.platform }}" == "armv6" ]; then
|
||||
sudo apt install gcc-arm-linux-gnueabihf
|
||||
fi
|
||||
|
||||
- name: Build x-ui
|
||||
run: |
|
||||
@@ -33,7 +40,12 @@ jobs:
|
||||
export GOOS=linux
|
||||
export GOARCH=${{ matrix.platform }}
|
||||
if [ "${{ matrix.platform }}" == "arm64" ]; then
|
||||
export GOARCH=arm64
|
||||
export CC=aarch64-linux-gnu-gcc
|
||||
elif [ "${{ matrix.platform }}" == "armv7" ] || [ "${{ matrix.platform }}" == "armv6" ]; then
|
||||
export GOARCH=arm
|
||||
export GOARM=7
|
||||
export CC=arm-linux-gnueabihf-gcc
|
||||
fi
|
||||
go build -o xui-release -v main.go
|
||||
|
||||
@@ -46,20 +58,31 @@ jobs:
|
||||
cd x-ui/bin
|
||||
|
||||
# Download dependencies
|
||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.7/"
|
||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||
wget https://github.com/XTLS/Xray-core/releases/download/v1.8.6/Xray-linux-64.zip
|
||||
wget ${Xray_URL}Xray-linux-64.zip
|
||||
unzip Xray-linux-64.zip
|
||||
rm -f Xray-linux-64.zip
|
||||
else
|
||||
wget https://github.com/XTLS/Xray-core/releases/download/v1.8.6/Xray-linux-arm64-v8a.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
|
||||
elif [ "${{ matrix.platform }}" == "armv7" ]; then
|
||||
wget ${Xray_URL}Xray-linux-arm32-v7a.zip
|
||||
unzip Xray-linux-arm32-v7a.zip
|
||||
rm -f Xray-linux-arm32-v7a.zip
|
||||
elif [ "${{ matrix.platform }}" == "armv6" ]; then
|
||||
wget ${Xray_URL}Xray-linux-arm32-v6.zip
|
||||
unzip Xray-linux-arm32-v6.zip
|
||||
rm -f Xray-linux-arm32-v6.zip
|
||||
fi
|
||||
rm -f geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat
|
||||
rm -f geoip.dat geosite.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 -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
|
||||
wget -O geoip_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
|
||||
mv xray xray-linux-${{ matrix.platform }}
|
||||
cd ../..
|
||||
|
||||
|
||||
@@ -9,21 +9,32 @@ case $1 in
|
||||
ARCH="arm64-v8a"
|
||||
FNAME="arm64"
|
||||
;;
|
||||
armv7 | arm | arm32)
|
||||
ARCH="arm32-v7a"
|
||||
FNAME="arm32"
|
||||
;;
|
||||
armv6)
|
||||
ARCH="arm32-v6"
|
||||
FNAME="armv6"
|
||||
;;
|
||||
*)
|
||||
ARCH="64"
|
||||
FNAME="amd64"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
mkdir -p 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"
|
||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat
|
||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat geoip_VN.dat geosite_VN.dat
|
||||
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/geosite.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
|
||||
wget -O geoip_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
|
||||
|
||||
318
README.md
318
README.md
@@ -1,6 +1,6 @@
|
||||
# 3x-ui
|
||||
# 3X-UI
|
||||
|
||||
> **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**
|
||||
**An Advanced Web Panel • Built on Xray Core**
|
||||
|
||||
[](https://github.com/MHSanaei/3x-ui/releases)
|
||||
[](#)
|
||||
@@ -8,58 +8,34 @@
|
||||
[](#)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
|
||||
3x-ui panel supporting multi-protocol, **Multi-lang (English,Farsi,Chinese,Russian,Vietnamese,Spanish)**
|
||||
**If you think this project is helpful to you, you may wish to give a** :star2:
|
||||
> **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
|
||||
|
||||
**Buy Me a Coffee :**
|
||||
**If this project is helpful to you, you may wish to give it a**:star2:
|
||||
|
||||
- Tron USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC`
|
||||
<a href="#">
|
||||
<img width="125" alt="image" src="https://github.com/MHSanaei/3x-ui/assets/115543613/7aa895dd-048a-42e7-989b-afd41a74e2e1.jpg"></a>
|
||||
|
||||
# Install & Upgrade
|
||||
- USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC`
|
||||
|
||||
## Install & Upgrade
|
||||
|
||||
```
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-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 `v2.0.1`:
|
||||
To install your desired version, add the version to the end of the installation command. e.g., ver `v2.1.1`:
|
||||
|
||||
```
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.0.1
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.1.1
|
||||
```
|
||||
|
||||
# SSL
|
||||
|
||||
```
|
||||
apt-get install certbot -y
|
||||
certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
|
||||
certbot renew --dry-run
|
||||
```
|
||||
|
||||
You also can use `x-ui` menu then select `SSL Certificate Management`
|
||||
|
||||
# Features
|
||||
|
||||
- System Status Monitoring
|
||||
- Search within all inbounds and clients
|
||||
- Support Dark/Light theme UI
|
||||
- Support multi-user multi-protocol, web page visualization operation
|
||||
- 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 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
|
||||
- Fix api routes (user setting will create with api)
|
||||
- Support to change configs by different items provided in panel
|
||||
- Support export/import database from panel
|
||||
|
||||
# Manual Install & Upgrade
|
||||
## Manual Install & Upgrade
|
||||
|
||||
<details>
|
||||
<summary>Click for Manual Install details</summary>
|
||||
<summary>Click for manual install details</summary>
|
||||
|
||||
#### Usage
|
||||
|
||||
1. To download the latest version of the compressed package directly to your server, run the following command:
|
||||
|
||||
@@ -88,11 +64,13 @@ systemctl restart x-ui
|
||||
|
||||
</details>
|
||||
|
||||
# Install with Docker
|
||||
## Install with Docker
|
||||
|
||||
<details>
|
||||
<summary>Click for Docker details</summary>
|
||||
|
||||
#### Usage
|
||||
|
||||
1. Install Docker:
|
||||
|
||||
```sh
|
||||
@@ -125,63 +103,130 @@ systemctl restart x-ui
|
||||
ghcr.io/mhsanaei/3x-ui:latest
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
# Default settings
|
||||
|
||||
<details>
|
||||
<summary>Click for Default settings details</summary>
|
||||
|
||||
- Port: 2053
|
||||
- username and password will be generated randomly if you skip to modify your own security(x-ui "7")
|
||||
- database path: /etc/x-ui/x-ui.db
|
||||
- xray config path: /usr/local/x-ui/bin/config.json
|
||||
|
||||
Before you set ssl on settings
|
||||
|
||||
- http://ip:2053/panel
|
||||
- http://domain:2053/panel
|
||||
|
||||
After you set ssl on settings
|
||||
|
||||
- https://yourdomain:2053/panel
|
||||
</details>
|
||||
|
||||
# Xray Configurations:
|
||||
|
||||
<details>
|
||||
<summary>Click for Xray Configurations details</summary>
|
||||
|
||||
**copy and paste to xray Configuration :** (you don't need to do this if you have a fresh install)
|
||||
|
||||
- [traffic](./media/configs/traffic.json)
|
||||
- [traffic + Block all Iran IP address](./media/configs/traffic+block-iran-ip.json)
|
||||
- [traffic + Block all Iran Domains](./media/configs/traffic+block-iran-domains.json)
|
||||
- [traffic + Block Ads + Use IPv4 for Google](./media/configs/traffic+block-ads+ipv4-google.json)
|
||||
- [traffic + Block Ads + Route Google + Netflix + Spotify + OpenAI (ChatGPT) to WARP](./media/configs/traffic+block-ads+warp.json)
|
||||
|
||||
</details>
|
||||
|
||||
# [WARP Configuration](https://gitlab.com/fscarmen/warp) (Optional)
|
||||
|
||||
<details>
|
||||
<summary>Click for WARP Configuration details</summary>
|
||||
|
||||
If you want to use routing to WARP follow steps as below:
|
||||
|
||||
1. If you already installed warp, you can uninstall using below command:
|
||||
update to latest version
|
||||
|
||||
```sh
|
||||
warp u
|
||||
cd 3x-ui
|
||||
docker compose down
|
||||
docker compose pull 3x-ui
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
2. Install WARP on **socks proxy mode**:
|
||||
</details>
|
||||
|
||||
|
||||
## Recommended OS
|
||||
|
||||
- Ubuntu 20.04+
|
||||
- Debian 11+
|
||||
- CentOS 8+
|
||||
- Fedora 36+
|
||||
- Arch Linux
|
||||
- Manjaro
|
||||
- Armbian
|
||||
- AlmaLinux 9+
|
||||
- Rockylinux 9+
|
||||
|
||||
|
||||
## Languages
|
||||
|
||||
- English
|
||||
- Farsi
|
||||
- Chinese
|
||||
- Russian
|
||||
- Vietnamese
|
||||
- Spanish
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- System Status Monitoring
|
||||
- Search within all inbounds and clients
|
||||
- Dark/Light theme
|
||||
- Supports multi-user and multi-protocol
|
||||
- Supports protocols, including VMess, VLESS, Trojan, Shadowsocks, Dokodemo-door, Socks, HTTP, wireguard
|
||||
- Supports XTLS native Protocols, including RPRX-Direct, Vision, REALITY
|
||||
- Traffic statistics, traffic limit, expiration time limit
|
||||
- Customizable Xray configuration templates
|
||||
- Supports HTTPS access panel (self-provided domain name + SSL certificate)
|
||||
- Supports One-Click SSL certificate application and automatic renewal
|
||||
- For more advanced configuration items, please refer to the panel
|
||||
- Fixes API routes (user setting will be created with API)
|
||||
- Supports changing configs by different items provided in the panel.
|
||||
- Supports export/import database from the panel
|
||||
|
||||
|
||||
## Default Settings
|
||||
|
||||
<details>
|
||||
<summary>Click for default settings details</summary>
|
||||
|
||||
### Information
|
||||
|
||||
- **Port:** 2053
|
||||
- **Username & Password:** It will be generated randomly if you skip modifying.
|
||||
- **Database Path:**
|
||||
- /etc/x-ui/x-ui.db
|
||||
- **Xray Config Path:**
|
||||
- /usr/local/x-ui/bin/config.json
|
||||
- **Web Panel Path w/o Deploying SSL:**
|
||||
- http://ip:2053/panel
|
||||
- http://domain:2053/panel
|
||||
- **Web Panel Path w/ Deploying SSL:**
|
||||
- https://domain:2053/panel
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## SSL Certificate
|
||||
|
||||
<details>
|
||||
<summary>Click for SSL Certificate</summary>
|
||||
|
||||
### Cloudflare
|
||||
|
||||
The 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
|
||||
|
||||
**1:** Run the`x-ui`command on the terminal, then choose `Cloudflare SSL Certificate`.
|
||||
|
||||
|
||||
### Certbot
|
||||
```
|
||||
apt-get install certbot -y
|
||||
certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
|
||||
certbot renew --dry-run
|
||||
```
|
||||
|
||||
***Tip:*** *Certbot is also built into the Management script. You can run the `x-ui` command, then choose `SSL Certificate Management`.*
|
||||
|
||||
</details>
|
||||
|
||||
## [WARP Configuration](https://gitlab.com/fscarmen/warp)
|
||||
|
||||
<details>
|
||||
<summary>Click for WARP configuration details</summary>
|
||||
|
||||
#### Usage
|
||||
|
||||
If you want to use routing to WARP before v2.1.0 follow steps as below:
|
||||
|
||||
**1.** Install WARP on **SOCKS Proxy Mode**:
|
||||
|
||||
```sh
|
||||
bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
|
||||
```
|
||||
|
||||
3. Turn on the config you need in panel or [Copy and paste this file to Xray Configuration](./media/configs/traffic+block-ads+warp.json)
|
||||
**2.** If you already installed warp, you can uninstall using below command:
|
||||
|
||||
```sh
|
||||
warp u
|
||||
```
|
||||
|
||||
**3.** Turn on the config you need in panel
|
||||
|
||||
Config Features:
|
||||
|
||||
@@ -191,12 +236,14 @@ If you want to use routing to WARP follow steps as below:
|
||||
|
||||
</details>
|
||||
|
||||
# IP Limit
|
||||
## IP Limit
|
||||
|
||||
<details>
|
||||
<summary>Click for IP Limit details</summary>
|
||||
<summary>Click for IP limit details</summary>
|
||||
|
||||
**Note: IP Limit won't work correctly when using IP Tunnel**
|
||||
#### Usage
|
||||
|
||||
**Note:** IP Limit won't work correctly when using IP Tunnel
|
||||
|
||||
- For versions up to `v1.6.1`:
|
||||
|
||||
@@ -222,32 +269,34 @@ If you want to use routing to WARP follow steps as below:
|
||||
|
||||
</details>
|
||||
|
||||
# Telegram Bot
|
||||
## Telegram Bot
|
||||
|
||||
<details>
|
||||
<summary>Click for Telegram Bot details</summary>
|
||||
<summary>Click for Telegram bot 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)
|
||||
Set the robot-related parameters in the panel background, including:
|
||||
#### Usage
|
||||
|
||||
- Tg robot Token
|
||||
- Tg robot ChatId
|
||||
- Tg robot cycle runtime, in crontab syntax
|
||||
- Tg robot Expiration threshold
|
||||
- Tg robot Traffic threshold
|
||||
- Tg robot Enable send backup in cycle runtime
|
||||
- Tg robot Enable CPU usage alarm threshold
|
||||
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:
|
||||
|
||||
Reference syntax:
|
||||
- Telegram Token
|
||||
- Admin Chat ID(s)
|
||||
- Notification Time (in cron syntax)
|
||||
- Expiration Date Notification
|
||||
- Traffic Cap Notification
|
||||
- Database Backup
|
||||
- CPU Load Notification
|
||||
|
||||
- 30 \* \* \* \* \* //Notify at the 30s of each point
|
||||
- 0 \*/10 \* \* \* \* //Notify at the first second of each 10 minutes
|
||||
- @hourly // hourly notification
|
||||
- @daily // Daily notification (00:00 in the morning)
|
||||
- @weekly // weekly notification
|
||||
- @every 8h // notify every 8 hours
|
||||
|
||||
# Telegram Bot Features
|
||||
**Reference syntax:**
|
||||
|
||||
- `30 \* \* \* \* \*` - Notify at the 30s of each point
|
||||
- `0 \*/10 \* \* \* \*` - Notify at the first second of each 10 minutes
|
||||
- `@hourly` - Hourly notification
|
||||
- `@daily` - Daily notification (00:00 in the morning)
|
||||
- `@weekly` - weekly notification
|
||||
- `@every 8h` - Notify every 8 hours
|
||||
|
||||
### Telegram Bot Features
|
||||
|
||||
- Report periodic
|
||||
- Login notification
|
||||
@@ -262,9 +311,8 @@ Reference syntax:
|
||||
- Check depleted users
|
||||
- Receive backup by request and in periodic reports
|
||||
- Multi language bot
|
||||
</details>
|
||||
|
||||
# Setting up Telegram bot
|
||||
### Setting up Telegram bot
|
||||
|
||||
- Start [Botfather](https://t.me/BotFather) in your Telegram account:
|
||||

|
||||
@@ -284,12 +332,15 @@ Enter the user ID in input field number 4. The Telegram accounts with this id wi
|
||||
- How to get Telegram user ID? Use this [bot](https://t.me/useridinfobot), Start the bot and it will give you the Telegram user ID.
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
# API routes
|
||||
## API Routes
|
||||
|
||||
<details>
|
||||
<summary>Click for API routes details</summary>
|
||||
|
||||
#### Usage
|
||||
|
||||
- `/login` with `POST` user data: `{username: '', password: ''}` for login
|
||||
- `/panel/api/inbounds` base for following actions:
|
||||
|
||||
@@ -324,10 +375,12 @@ Enter the user ID in input field number 4. The Telegram accounts with this id wi
|
||||
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://app.getpostman.com/run-collection/16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415%26entityType%3Dcollection%26workspaceId%3D2cd38c01-c851-4a15-a972-f181c23359d9)
|
||||
</details>
|
||||
|
||||
# Environment Variables
|
||||
## Environment Variables
|
||||
|
||||
<details>
|
||||
<summary>Click for Environment Variables details</summary>
|
||||
<summary>Click for environment variables details</summary>
|
||||
|
||||
#### Usage
|
||||
|
||||
| Variable | Type | Default |
|
||||
| -------------- | :--------------------------------------------: | :------------ |
|
||||
@@ -345,25 +398,21 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
|
||||
</details>
|
||||
|
||||
# A Special Thanks To
|
||||
|
||||
- [alireza0](https://github.com/alireza0/)
|
||||
## Supported Architectures and Devices
|
||||
|
||||
# Acknowledgment
|
||||
Supports a variety of different architectures and devices. Here are some of the main architectures that we support:
|
||||
|
||||
- [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._
|
||||
- [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._
|
||||
- [PersianBlocker](https://github.com/MasterKia/PersianBlocker) (License: **AGPLv3**): _An optimal and extensive list to block ads and trackers on Persian websites._
|
||||
- **amd64**: This is the most common architecture for personal computers and servers. It supports most modern operating systems.
|
||||
|
||||
# Suggestion System
|
||||
- **armv8 / arm64 / aarch64**: This is the architecture for modern mobile and embedded devices, including smartphones and tablets. (Ex: Raspberry Pi 4, Raspberry Pi 3, Raspberry Pi Zero 2/Zero 2 W, Orange Pi 3 LTS,...)
|
||||
|
||||
- Ubuntu 20.04+
|
||||
- Debian 10+
|
||||
- CentOS 8+
|
||||
- Fedora 36+
|
||||
- Arch Linux
|
||||
- **armv7 / arm / arm32**: This is the architecture for older mobile and embedded devices. It is still widely used in many devices. (Ex: Orange Pi Zero LTS, Orange Pi PC Plus, Raspberry Pi 2,...)
|
||||
|
||||
# Pictures
|
||||
- **armv6 / arm / arm32**: This is the architecture for very old embedded devices. While not as common as before, there are still some devices using this architecture. (Ex: Raspberry Pi 1, Raspberry Pi Zero/Zero W,...)
|
||||
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||

|
||||
@@ -373,6 +422,15 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||

|
||||

|
||||
|
||||
## Stargazers over time
|
||||
## A Special Thanks to
|
||||
|
||||
- [alireza0](https://github.com/alireza0/)
|
||||
|
||||
## Acknowledgment
|
||||
|
||||
- [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._
|
||||
- [Vietnam Adblock rules](https://github.com/vuong2023/vn-v2ray-rules) (License: **GPL-3.0**): _A hosted domain hosted in Vietnam and blocklist with the most efficiency for Vietnamese._
|
||||
|
||||
## Stargazers over Time
|
||||
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.0.1
|
||||
2.1.1
|
||||
@@ -37,7 +37,7 @@ type Inbound struct {
|
||||
|
||||
// config part
|
||||
Listen string `json:"listen" form:"listen"`
|
||||
Port int `json:"port" form:"port" gorm:"unique"`
|
||||
Port int `json:"port" form:"port"`
|
||||
Protocol Protocol `json:"protocol" form:"protocol"`
|
||||
Settings string `json:"settings" form:"settings"`
|
||||
StreamSettings string `json:"streamSettings" form:"streamSettings"`
|
||||
|
||||
59
go.mod
59
go.mod
@@ -12,11 +12,12 @@ require (
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/pelletier/go-toml/v2 v2.1.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/shirou/gopsutil/v3 v3.23.11
|
||||
github.com/xtls/xray-core v1.8.6
|
||||
github.com/shirou/gopsutil/v3 v3.23.12
|
||||
github.com/valyala/fasthttp v1.51.0
|
||||
github.com/xtls/xray-core v1.8.7
|
||||
go.uber.org/atomic v1.11.0
|
||||
golang.org/x/text v0.14.0
|
||||
google.golang.org/grpc v1.60.0
|
||||
google.golang.org/grpc v1.60.1
|
||||
gorm.io/driver/sqlite v1.5.4
|
||||
gorm.io/gorm v1.25.5
|
||||
)
|
||||
@@ -26,46 +27,45 @@ require (
|
||||
github.com/bytedance/sonic v1.10.2 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.1 // 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/fasthttp/router v1.4.22 // indirect
|
||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gaukas/godicttls v0.0.4 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.15.5 // indirect
|
||||
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
|
||||
github.com/gorilla/context v1.1.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/gorilla/sessions v1.2.1 // indirect
|
||||
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.2.2 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // 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/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.19 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/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/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||
github.com/quic-go/quic-go v0.40.0 // indirect
|
||||
github.com/refraction-networking/utls v1.5.4 // indirect
|
||||
github.com/quic-go/quic-go v0.40.1 // indirect
|
||||
github.com/refraction-networking/utls v1.6.0 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||
github.com/sagernet/sing v0.2.17 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5 // indirect
|
||||
github.com/sagernet/sing v0.3.0 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
@@ -75,25 +75,24 @@ require (
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.uber.org/mock v0.3.0 // indirect
|
||||
go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/arch v0.6.0 // indirect
|
||||
golang.org/x/crypto v0.15.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/net v0.18.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/time v0.4.0 // indirect
|
||||
golang.org/x/tools v0.15.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.16.1 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect
|
||||
lukechampine.com/blake3 v1.2.1 // indirect
|
||||
|
||||
119
go.sum
119
go.sum
@@ -30,8 +30,8 @@ github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLI
|
||||
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
|
||||
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||
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.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
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/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=
|
||||
@@ -49,8 +49,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/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
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.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=
|
||||
@@ -78,8 +76,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24=
|
||||
github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
|
||||
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
@@ -109,19 +107,21 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/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-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8=
|
||||
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 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
||||
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
@@ -136,8 +136,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
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.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
@@ -156,15 +156,15 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik=
|
||||
github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
|
||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0=
|
||||
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
|
||||
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
@@ -181,8 +181,8 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.3.0 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ=
|
||||
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.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
|
||||
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs=
|
||||
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/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||
@@ -208,10 +208,10 @@ github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7q
|
||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
|
||||
github.com/quic-go/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.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
|
||||
github.com/refraction-networking/utls v1.5.4 h1:9k6EO2b8TaOGsQ7Pl7p9w6PUhx18/ZCeT0WNTZ7Uw4o=
|
||||
github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw=
|
||||
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q=
|
||||
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
|
||||
github.com/refraction-networking/utls v1.6.0 h1:X5vQMqVx7dY7ehxxqkFER/W6DSjy8TMqSItXm8hRDYQ=
|
||||
github.com/refraction-networking/utls v1.6.0/go.mod h1:kHJ6R9DFFA0WsRgBM35iiDku4O7AqPR6y79iuzW7b10=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
@@ -221,17 +221,17 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
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.2.17/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A=
|
||||
github.com/sagernet/sing v0.3.0 h1:PIDVFZHnQAAYRL1UYqNM+0k5s8f/tb1lUW6UDcQiOc8=
|
||||
github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
|
||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
|
||||
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
|
||||
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/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.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||
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/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
|
||||
@@ -299,18 +299,18 @@ github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1Y
|
||||
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/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
|
||||
github.com/xtls/xray-core v1.8.6 h1:tr3nk/fZnFfCsmgZv7B3RC72N5qUC88oMGVLlybDey8=
|
||||
github.com/xtls/xray-core v1.8.6/go.mod h1:hj2EB8rtcLdlTC8zxiWm5xL+C0k2Aie9Pk0mXtDEP6U=
|
||||
github.com/xtls/xray-core v1.8.7 h1:lb8O1l3/eAg3YAXA6tLm5M6N7BsX8wxW9sJLjU3dHkA=
|
||||
github.com/xtls/xray-core v1.8.7/go.mod h1:9rFpflfQbgFeH1VKJw7yUmEy7myOyDCgNXXl0bmmyOo=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
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/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
|
||||
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
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-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
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.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
|
||||
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
@@ -319,11 +319,11 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
|
||||
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-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
|
||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
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-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM=
|
||||
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-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -338,8 +338,8 @@ 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-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
|
||||
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
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-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -349,8 +349,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-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.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -369,8 +369,9 @@ 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.8.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.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/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=
|
||||
@@ -380,20 +381,20 @@ 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/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.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
|
||||
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
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-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-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.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
|
||||
golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
|
||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/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-20231022001213-2e0774f246fb/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||
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-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||
@@ -406,19 +407,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-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/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
|
||||
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.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.60.0 h1:6FQAR0kM31P6MRdeluor2w2gPaS4SVNrD/DNTxrQ15k=
|
||||
google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
|
||||
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
|
||||
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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
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.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
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 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
||||
58
install.sh
58
install.sh
@@ -26,7 +26,9 @@ echo "The OS release is: $release"
|
||||
arch3xui() {
|
||||
case "$(uname -m)" in
|
||||
x86_64 | x64 | amd64) echo 'amd64' ;;
|
||||
armv8 | arm64 | aarch64) echo 'arm64' ;;
|
||||
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
|
||||
armv7* | armv7 | arm) echo 'armv7' ;;
|
||||
armv6* | armv6 | arm) echo 'armv6' ;;
|
||||
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
|
||||
esac
|
||||
}
|
||||
@@ -41,20 +43,34 @@ if [[ "${release}" == "centos" ]]; then
|
||||
fi
|
||||
elif [[ "${release}" == "ubuntu" ]]; then
|
||||
if [[ ${os_version} -lt 20 ]]; then
|
||||
echo -e "${red}please use Ubuntu 20 or higher version!${plain}\n" && exit 1
|
||||
echo -e "${red} Please use Ubuntu 20 or higher version!${plain}\n" && exit 1
|
||||
fi
|
||||
|
||||
elif [[ "${release}" == "fedora" ]]; then
|
||||
if [[ ${os_version} -lt 36 ]]; then
|
||||
echo -e "${red}please use Fedora 36 or higher version!${plain}\n" && exit 1
|
||||
echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1
|
||||
fi
|
||||
|
||||
elif [[ "${release}" == "debian" ]]; then
|
||||
if [[ ${os_version} -lt 10 ]]; then
|
||||
echo -e "${red} Please use Debian 10 or higher ${plain}\n" && exit 1
|
||||
if [[ ${os_version} -lt 11 ]]; then
|
||||
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
|
||||
elif [[ "${release}" == "almalinux" ]]; then
|
||||
if [[ ${os_version} -lt 9 ]]; then
|
||||
echo -e "${red} Please use AlmaLinux 9 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
|
||||
elif [[ "${release}" == "rocky" ]]; then
|
||||
if [[ ${os_version} -lt 9 ]]; then
|
||||
echo -e "${red} Please use RockyLinux 9 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
elif [[ "${release}" == "arch" ]]; then
|
||||
echo "OS is ArchLinux"
|
||||
echo "Your OS is ArchLinux"
|
||||
elif [[ "${release}" == "manjaro" ]]; then
|
||||
echo "Your OS is Manjaro"
|
||||
elif [[ "${release}" == "armbian" ]]; then
|
||||
echo "Your OS is Armbian"
|
||||
|
||||
else
|
||||
echo -e "${red}Failed to check the OS version, please contact the author!${plain}" && exit 1
|
||||
@@ -62,14 +78,14 @@ fi
|
||||
|
||||
install_base() {
|
||||
case "${release}" in
|
||||
centos|fedora)
|
||||
yum install -y -q wget curl tar
|
||||
centos|fedora|almalinux|rocky)
|
||||
yum -y update && yum install -y -q wget curl tar
|
||||
;;
|
||||
arch)
|
||||
pacman -Syu --noconfirm wget curl tar
|
||||
arch|manjaro)
|
||||
pacman -Syu && pacman -Syu --noconfirm wget curl tar
|
||||
;;
|
||||
*)
|
||||
apt install -y -q wget curl tar
|
||||
apt-get update && apt install -y -q wget curl tar
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -78,7 +94,7 @@ install_base() {
|
||||
# This function will be called when user installed x-ui out of security
|
||||
config_after_install() {
|
||||
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
|
||||
read -p "Do you want to continue with the modification [y/n]? ": config_confirm
|
||||
read -p "Do you want to continue with the modification [y/n]?": config_confirm
|
||||
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
||||
read -p "Please set up your username:" config_account
|
||||
echo -e "${yellow}Your username will be:${config_account}${plain}"
|
||||
@@ -128,7 +144,7 @@ install_x-ui() {
|
||||
else
|
||||
last_version=$1
|
||||
url="https://github.com/MHSanaei/3x-ui/releases/download/${last_version}/x-ui-linux-$(arch3xui).tar.gz"
|
||||
echo -e "Begining to install x-ui $1"
|
||||
echo -e "Beginning to install x-ui $1"
|
||||
wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch3xui).tar.gz ${url}
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo -e "${red}Download x-ui $1 failed,please check the version exists ${plain}"
|
||||
@@ -144,18 +160,21 @@ install_x-ui() {
|
||||
tar zxvf x-ui-linux-$(arch3xui).tar.gz
|
||||
rm x-ui-linux-$(arch3xui).tar.gz -f
|
||||
cd x-ui
|
||||
chmod +x x-ui
|
||||
|
||||
# Check the system's architecture and rename the file accordingly
|
||||
if [[ $(arch3xui) == "armv6" || $(arch3xui) == "armv7" ]]; then
|
||||
mv bin/xray-linux-$(arch3xui) bin/xray-linux-arm
|
||||
chmod +x bin/xray-linux-arm
|
||||
fi
|
||||
|
||||
chmod +x x-ui bin/xray-linux-$(arch3xui)
|
||||
cp -f x-ui.service /etc/systemd/system/
|
||||
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
|
||||
chmod +x /usr/local/x-ui/x-ui.sh
|
||||
chmod +x /usr/bin/x-ui
|
||||
config_after_install
|
||||
#echo -e "If it is a new installation, the default web port is ${green}2053${plain}, The username and password are ${green}admin${plain} by default"
|
||||
#echo -e "Please make sure that this port is not occupied by other procedures,${yellow} And make sure that port 2053 has been released${plain}"
|
||||
# echo -e "If you want to modify the 2053 to other ports and enter the x-ui command to modify it, you must also ensure that the port you modify is also released"
|
||||
#echo -e ""
|
||||
#echo -e "If it is updated panel, access the panel in your previous way"
|
||||
#echo -e ""
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable x-ui
|
||||
systemctl start x-ui
|
||||
@@ -178,6 +197,7 @@ install_x-ui() {
|
||||
echo -e "----------------------------------------------"
|
||||
}
|
||||
|
||||
|
||||
echo -e "${green}Running...${plain}"
|
||||
install_base
|
||||
install_x-ui $1
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
{
|
||||
"log": {
|
||||
"loglevel": "warning",
|
||||
"error": "./error.log"
|
||||
},
|
||||
"api": {
|
||||
"tag": "api",
|
||||
"services": [
|
||||
"HandlerService",
|
||||
"LoggerService",
|
||||
"StatsService"
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"tag": "api",
|
||||
"listen": "127.0.0.1",
|
||||
"port": 62789,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "127.0.0.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "blocked",
|
||||
"protocol": "blackhole",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "IPv4",
|
||||
"protocol": "freedom",
|
||||
"settings": {
|
||||
"domainStrategy": "UseIPv4"
|
||||
}
|
||||
}
|
||||
],
|
||||
"policy": {
|
||||
"levels": {
|
||||
"0": {
|
||||
"statsUserDownlink": true,
|
||||
"statsUserUplink": true
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"statsInboundDownlink": true,
|
||||
"statsInboundUplink": true
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": [
|
||||
"api"
|
||||
],
|
||||
"outboundTag": "api"
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"ip": [
|
||||
"geoip:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"protocol": [
|
||||
"bittorrent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"domain": [
|
||||
"geosite:category-ads-all",
|
||||
"ext:geosite_IR.dat:category-ads-all"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "IPv4",
|
||||
"domain": [
|
||||
"geosite:google"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
{
|
||||
"log": {
|
||||
"loglevel": "warning",
|
||||
"error": "./error.log"
|
||||
},
|
||||
"api": {
|
||||
"tag": "api",
|
||||
"services": [
|
||||
"HandlerService",
|
||||
"LoggerService",
|
||||
"StatsService"
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"tag": "api",
|
||||
"listen": "127.0.0.1",
|
||||
"port": 62789,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "127.0.0.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "blocked",
|
||||
"protocol": "blackhole",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "WARP",
|
||||
"protocol": "socks",
|
||||
"settings": {
|
||||
"servers": [
|
||||
{
|
||||
"address": "127.0.0.1",
|
||||
"port": 40000
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"policy": {
|
||||
"levels": {
|
||||
"0": {
|
||||
"statsUserDownlink": true,
|
||||
"statsUserUplink": true
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"statsInboundDownlink": true,
|
||||
"statsInboundUplink": true
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": [
|
||||
"api"
|
||||
],
|
||||
"outboundTag": "api"
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"ip": [
|
||||
"geoip:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"protocol": [
|
||||
"bittorrent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"domain": [
|
||||
"geosite:category-ads-all",
|
||||
"ext:geosite_IR.dat:category-ads-all"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "WARP",
|
||||
"domain": [
|
||||
"geosite:spotify",
|
||||
"geosite:netflix",
|
||||
"geosite:openai",
|
||||
"geosite:google"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
{
|
||||
"log": {
|
||||
"loglevel": "warning",
|
||||
"error": "./error.log"
|
||||
},
|
||||
"api": {
|
||||
"tag": "api",
|
||||
"services": [
|
||||
"HandlerService",
|
||||
"LoggerService",
|
||||
"StatsService"
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"tag": "api",
|
||||
"listen": "127.0.0.1",
|
||||
"port": 62789,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "127.0.0.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "blocked",
|
||||
"protocol": "blackhole",
|
||||
"settings": {}
|
||||
}
|
||||
],
|
||||
"policy": {
|
||||
"levels": {
|
||||
"0": {
|
||||
"statsUserDownlink": true,
|
||||
"statsUserUplink": true
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"statsInboundDownlink": true,
|
||||
"statsInboundUplink": true
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": [
|
||||
"api"
|
||||
],
|
||||
"outboundTag": "api"
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"ip": [
|
||||
"geoip:private"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"protocol": [
|
||||
"bittorrent"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"domain": [
|
||||
"regexp:.*\\.ir$",
|
||||
"regexp:.*\\.xn--mgba3a4f16a$",
|
||||
"ext:geosite_IR.dat:ir"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
{
|
||||
"log": {
|
||||
"loglevel": "warning",
|
||||
"error": "./error.log"
|
||||
},
|
||||
"api": {
|
||||
"tag": "api",
|
||||
"services": [
|
||||
"HandlerService",
|
||||
"LoggerService",
|
||||
"StatsService"
|
||||
]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"tag": "api",
|
||||
"listen": "127.0.0.1",
|
||||
"port": 62789,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "127.0.0.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "blocked",
|
||||
"protocol": "blackhole",
|
||||
"settings": {}
|
||||
}
|
||||
],
|
||||
"policy": {
|
||||
"levels": {
|
||||
"0": {
|
||||
"statsUserDownlink": true,
|
||||
"statsUserUplink": true
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"statsInboundDownlink": true,
|
||||
"statsInboundUplink": true
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": [
|
||||
"api"
|
||||
],
|
||||
"outboundTag": "api"
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"ip": [
|
||||
"geoip:private",
|
||||
"ext:geoip_IR.dat:ir"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"protocol": [
|
||||
"bittorrent"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"log": {
|
||||
"loglevel": "warning",
|
||||
"error": "./error.log"
|
||||
},
|
||||
"api": {
|
||||
"tag": "api",
|
||||
"services": ["HandlerService", "LoggerService", "StatsService"]
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"tag": "api",
|
||||
"listen": "127.0.0.1",
|
||||
"port": 62789,
|
||||
"protocol": "dokodemo-door",
|
||||
"settings": {
|
||||
"address": "127.0.0.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{
|
||||
"protocol": "freedom",
|
||||
"settings": {}
|
||||
},
|
||||
{
|
||||
"tag": "blocked",
|
||||
"protocol": "blackhole",
|
||||
"settings": {}
|
||||
}
|
||||
],
|
||||
"policy": {
|
||||
"levels": {
|
||||
"0": {
|
||||
"statsUserDownlink": true,
|
||||
"statsUserUplink": true
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"statsInboundDownlink": true,
|
||||
"statsInboundUplink": true
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"domainStrategy": "IPIfNonMatch",
|
||||
"rules": [
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": ["api"],
|
||||
"outboundTag": "api"
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"ip": ["geoip:private"]
|
||||
},
|
||||
{
|
||||
"type": "field",
|
||||
"outboundTag": "blocked",
|
||||
"protocol": ["bittorrent"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"stats": {}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ type SubService struct {
|
||||
address string
|
||||
showInfo bool
|
||||
remarkModel string
|
||||
datepicker string
|
||||
inboundService service.InboundService
|
||||
settingService service.SettingService
|
||||
}
|
||||
@@ -39,6 +40,10 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string
|
||||
if err != nil {
|
||||
s.remarkModel = "-ieo"
|
||||
}
|
||||
s.datepicker, err = s.settingService.GetDatepicker()
|
||||
if err != nil {
|
||||
s.datepicker = "gregorian"
|
||||
}
|
||||
for _, inbound := range inbounds {
|
||||
clients, err := s.inboundService.GetClients(inbound)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ body {
|
||||
html {
|
||||
--antd-wave-shadow-color: #008771;
|
||||
line-height: 1.15;
|
||||
text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-ms-overflow-style: scrollbar;
|
||||
@@ -680,7 +681,7 @@ style attribute {
|
||||
.dark .ant-table-row-expand-icon {
|
||||
color: #fff;
|
||||
background-color: #fff0;
|
||||
border-color: #9ea2a8;
|
||||
border-color: rgb(255 255 255 / 20%);
|
||||
}
|
||||
|
||||
.dark .ant-table-row-expand-icon:hover {
|
||||
@@ -850,13 +851,17 @@ style attribute {
|
||||
.ant-menu,
|
||||
.ant-radio-button-wrapper {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.ant-calendar-date,
|
||||
.ant-calendar-year-panel-year,
|
||||
.ant-calendar-decade-panel-decade,
|
||||
.ant-calendar-month-panel-month {
|
||||
border-radius: 5px;
|
||||
.ant-calendar-month-panel-month,
|
||||
.ant-checkbox-inner,
|
||||
.ant-checkbox-checked:after,
|
||||
.ant-table-row-expand-icon {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.ant-calendar-date:hover {
|
||||
@@ -1095,7 +1100,24 @@ li.ant-select-dropdown-menu-item:empty:after {
|
||||
background-color: #313f5a;
|
||||
}
|
||||
|
||||
.ant-select-dropdown,
|
||||
.ant-popover-inner {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.ant-popover-inner-content {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.qr-bg {
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
padding: 0.5rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.ant-input-group-addon:not(:first-child):not(:last-child), .ant-input-group-wrap:not(:first-child):not(:last-child), .ant-input-group>.ant-input:not(:first-child):not(:last-child) {
|
||||
border-radius: 0rem 1rem 1rem 0rem;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,10 @@ class DBInbound {
|
||||
return this.protocol === Protocols.HTTP;
|
||||
}
|
||||
|
||||
get isWireguard() {
|
||||
return this.protocol === Protocols.WIREGUARD;
|
||||
}
|
||||
|
||||
get address() {
|
||||
let address = location.hostname;
|
||||
if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") {
|
||||
|
||||
@@ -8,6 +8,7 @@ const Protocols = {
|
||||
Shadowsocks: "shadowsocks",
|
||||
Socks: "socks",
|
||||
HTTP: "http",
|
||||
Wireguard: "wireguard"
|
||||
};
|
||||
|
||||
const SSMethods = {
|
||||
@@ -46,18 +47,27 @@ const ALPN_OPTION = {
|
||||
HTTP1: "http/1.1",
|
||||
};
|
||||
|
||||
const outboundDomainStrategies = [
|
||||
const OutboundDomainStrategies = [
|
||||
"AsIs",
|
||||
"UseIP",
|
||||
"UseIPv4",
|
||||
"UseIPv6"
|
||||
]
|
||||
];
|
||||
|
||||
const WireguardDomainStrategy = [
|
||||
"ForceIP",
|
||||
"ForceIPv4",
|
||||
"ForceIPv4v6",
|
||||
"ForceIPv6",
|
||||
"ForceIPv6v4"
|
||||
];
|
||||
|
||||
Object.freeze(Protocols);
|
||||
Object.freeze(SSMethods);
|
||||
Object.freeze(TLS_FLOW_CONTROL);
|
||||
Object.freeze(ALPN_OPTION);
|
||||
Object.freeze(outboundDomainStrategies);
|
||||
Object.freeze(OutboundDomainStrategies);
|
||||
Object.freeze(WireguardDomainStrategy);
|
||||
|
||||
class CommonClass {
|
||||
|
||||
@@ -625,6 +635,7 @@ Outbound.Settings = class extends CommonClass {
|
||||
case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings();
|
||||
case Protocols.Socks: return new Outbound.SocksSettings();
|
||||
case Protocols.HTTP: return new Outbound.HttpSettings();
|
||||
case Protocols.Wireguard: return new Outbound.WireguardSettings();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
@@ -640,6 +651,7 @@ Outbound.Settings = class extends CommonClass {
|
||||
case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json);
|
||||
case Protocols.Socks: return Outbound.SocksSettings.fromJson(json);
|
||||
case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json);
|
||||
case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json);
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
@@ -838,13 +850,14 @@ Outbound.ShadowsocksSettings = class extends CommonClass {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Outbound.SocksSettings = class extends CommonClass {
|
||||
constructor(address, port, user, password) {
|
||||
constructor(address, port, user, pass) {
|
||||
super();
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
this.pass = pass;
|
||||
}
|
||||
|
||||
static fromJson(json={}) {
|
||||
@@ -854,7 +867,7 @@ Outbound.SocksSettings = class extends CommonClass {
|
||||
servers[0].address,
|
||||
servers[0].port,
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -863,18 +876,18 @@ Outbound.SocksSettings = class extends CommonClass {
|
||||
servers: [{
|
||||
address: this.address,
|
||||
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 {
|
||||
constructor(address, port, user, password) {
|
||||
constructor(address, port, user, pass) {
|
||||
super();
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.user = user;
|
||||
this.password = password;
|
||||
this.pass = pass;
|
||||
}
|
||||
|
||||
static fromJson(json={}) {
|
||||
@@ -884,7 +897,7 @@ Outbound.HttpSettings = class extends CommonClass {
|
||||
servers[0].address,
|
||||
servers[0].port,
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -893,8 +906,91 @@ Outbound.HttpSettings = class extends CommonClass {
|
||||
servers: [{
|
||||
address: this.address,
|
||||
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,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -12,8 +12,10 @@ class AllSetting {
|
||||
this.expireDiff = "";
|
||||
this.trafficDiff = "";
|
||||
this.remarkModel = "-ieo";
|
||||
this.datepicker = "gregorian";
|
||||
this.tgBotEnable = false;
|
||||
this.tgBotToken = "";
|
||||
this.tgBotProxy = "";
|
||||
this.tgBotChatId = "";
|
||||
this.tgRunTime = "@daily";
|
||||
this.tgBotBackup = false;
|
||||
|
||||
@@ -6,6 +6,7 @@ const Protocols = {
|
||||
DOKODEMO: 'dokodemo-door',
|
||||
SOCKS: 'socks',
|
||||
HTTP: 'http',
|
||||
WIREGUARD: 'wireguard',
|
||||
};
|
||||
|
||||
const SSMethods = {
|
||||
@@ -235,6 +236,7 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
version: this.version,
|
||||
method: this.method,
|
||||
path: ObjectUtil.clone(this.path),
|
||||
headers: XrayCommonClass.toV2Headers(this.headers),
|
||||
@@ -474,7 +476,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
cipherSuites = '',
|
||||
rejectUnknownSni = false,
|
||||
certificates=[new TlsStreamSettings.Cert()],
|
||||
alpn=[ALPN_OPTION.HTTP1,ALPN_OPTION.H2],
|
||||
alpn=[ALPN_OPTION.H2,ALPN_OPTION.HTTP1],
|
||||
settings=new TlsStreamSettings.Settings()) {
|
||||
super();
|
||||
this.sni = serverName;
|
||||
@@ -764,16 +766,18 @@ class RealityStreamSettings extends XrayCommonClass {
|
||||
}
|
||||
|
||||
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
||||
constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, spiderX= '/') {
|
||||
constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, serverName = '', spiderX= '/') {
|
||||
super();
|
||||
this.publicKey = publicKey;
|
||||
this.fingerprint = fingerprint;
|
||||
this.serverName = serverName;
|
||||
this.spiderX = spiderX;
|
||||
}
|
||||
static fromJson(json = {}) {
|
||||
return new RealityStreamSettings.Settings(
|
||||
json.publicKey,
|
||||
json.fingerprint,
|
||||
json.serverName,
|
||||
json.spiderX,
|
||||
);
|
||||
}
|
||||
@@ -781,6 +785,7 @@ RealityStreamSettings.Settings = class extends XrayCommonClass {
|
||||
return {
|
||||
publicKey: this.publicKey,
|
||||
fingerprint: this.fingerprint,
|
||||
serverName: this.serverName,
|
||||
spiderX: this.spiderX,
|
||||
};
|
||||
}
|
||||
@@ -794,6 +799,7 @@ class SockoptStreamSettings extends XrayCommonClass {
|
||||
this.mark = mark;
|
||||
this.tproxy = tproxy;
|
||||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
if (Object.keys(json).length === 0) return undefined;
|
||||
return new SockoptStreamSettings(
|
||||
@@ -995,18 +1001,6 @@ class Inbound extends XrayCommonClass {
|
||||
}
|
||||
}
|
||||
|
||||
get tls() {
|
||||
return this.stream.security === 'tls';
|
||||
}
|
||||
|
||||
set tls(isTls) {
|
||||
if (isTls) {
|
||||
this.stream.security = 'tls';
|
||||
} else {
|
||||
this.stream.security = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
get xtls() {
|
||||
return this.stream.security === 'xtls';
|
||||
}
|
||||
@@ -1018,19 +1012,7 @@ class Inbound extends XrayCommonClass {
|
||||
this.stream.security = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
get reality() {
|
||||
return this.stream.security === 'reality';
|
||||
}
|
||||
|
||||
set reality(isReality) {
|
||||
if (isReality) {
|
||||
this.stream.security = 'reality';
|
||||
} else {
|
||||
this.stream.security = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get network() {
|
||||
return this.stream.network;
|
||||
}
|
||||
@@ -1142,11 +1124,6 @@ class Inbound extends XrayCommonClass {
|
||||
return ["tcp", "ws", "http", "quic", "grpc"].includes(this.network);
|
||||
}
|
||||
|
||||
canEnableReality() {
|
||||
if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
||||
return ["tcp", "http", "grpc"].includes(this.network);
|
||||
}
|
||||
|
||||
//this is used for xtls-rprx-vision
|
||||
canEnableTlsFlow() {
|
||||
if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) {
|
||||
@@ -1155,6 +1132,11 @@ class Inbound extends XrayCommonClass {
|
||||
return false;
|
||||
}
|
||||
|
||||
canEnableReality() {
|
||||
if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
||||
return ["tcp", "http", "grpc"].includes(this.network);
|
||||
}
|
||||
|
||||
canEnableXtls() {
|
||||
if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
||||
return this.network === "tcp";
|
||||
@@ -1607,7 +1589,7 @@ class Inbound extends XrayCommonClass {
|
||||
});
|
||||
return links.join('\r\n');
|
||||
} else {
|
||||
if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, remark);
|
||||
if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, 'same', remark);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -1657,7 +1639,8 @@ Inbound.Settings = class extends XrayCommonClass {
|
||||
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol);
|
||||
case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(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;
|
||||
}
|
||||
}
|
||||
@@ -1671,6 +1654,7 @@ Inbound.Settings = class extends XrayCommonClass {
|
||||
case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json);
|
||||
case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json);
|
||||
case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json);
|
||||
case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json);
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
@@ -2273,3 +2257,69 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass {
|
||||
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,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -295,3 +295,190 @@ class ObjectUtil {
|
||||
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)
|
||||
};
|
||||
}
|
||||
}
|
||||
1252
web/assets/moment/moment-jalali.min.js
vendored
Normal file
1252
web/assets/moment/moment-jalali.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
web/assets/persian-datepicker/persian-datepicker.min.css
vendored
Normal file
1
web/assets/persian-datepicker/persian-datepicker.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
web/assets/persian-datepicker/persian-datepicker.min.js
vendored
Normal file
1
web/assets/persian-datepicker/persian-datepicker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -85,7 +85,11 @@ func (a *InboundController) addInbound(c *gin.Context) {
|
||||
}
|
||||
user := session.GetLoginUser(c)
|
||||
inbound.UserId = user.Id
|
||||
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
|
||||
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
|
||||
inbound.Tag = fmt.Sprintf("inbound-0.0.0.0:%v", inbound.Port)
|
||||
} else {
|
||||
inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
|
||||
}
|
||||
|
||||
needRestart := false
|
||||
inbound, needRestart, err = a.inboundService.AddInbound(inbound)
|
||||
@@ -278,7 +282,11 @@ func (a *InboundController) importInbound(c *gin.Context) {
|
||||
user := session.GetLoginUser(c)
|
||||
inbound.Id = 0
|
||||
inbound.UserId = user.Id
|
||||
inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
|
||||
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
|
||||
inbound.Tag = fmt.Sprintf("inbound-0.0.0.0:%v", inbound.Port)
|
||||
} else {
|
||||
inbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
|
||||
}
|
||||
|
||||
for index := range inbound.ClientStats {
|
||||
inbound.ClientStats[index].Id = 0
|
||||
|
||||
@@ -26,6 +26,7 @@ func (a *XraySettingController) initRouter(g *gin.RouterGroup) {
|
||||
g.POST("/update", a.updateSetting)
|
||||
g.GET("/getXrayResult", a.getXrayResult)
|
||||
g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig)
|
||||
g.POST("/warp/:action", a.warp)
|
||||
}
|
||||
|
||||
func (a *XraySettingController) getXraySetting(c *gin.Context) {
|
||||
@@ -61,3 +62,25 @@ func (a *XraySettingController) getDefaultXrayConfig(c *gin.Context) {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ type AllSetting struct {
|
||||
RemarkModel string `json:"remarkModel" form:"remarkModel"`
|
||||
TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"`
|
||||
TgBotToken string `json:"tgBotToken" form:"tgBotToken"`
|
||||
TgBotProxy string `json:"tgBotProxy" form:"tgBotProxy"`
|
||||
TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"`
|
||||
TgRunTime string `json:"tgRunTime" form:"tgRunTime"`
|
||||
TgBotBackup bool `json:"tgBotBackup" form:"tgBotBackup"`
|
||||
@@ -47,6 +48,7 @@ type AllSetting struct {
|
||||
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"`
|
||||
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
|
||||
SubURI string `json:"subURI" form:"subURI"`
|
||||
Datepicker string `json:"datepicker" form:"datepicker"`
|
||||
}
|
||||
|
||||
func (s *AllSetting) CheckValid() error {
|
||||
@@ -72,8 +74,8 @@ func (s *AllSetting) CheckValid() error {
|
||||
return common.NewError("Sub port is not a valid port:", s.SubPort)
|
||||
}
|
||||
|
||||
if s.SubPort == s.WebPort {
|
||||
return common.NewError("Sub and Web could not use same port:", s.SubPort)
|
||||
if (s.SubPort == s.WebPort) && (s.WebListen == s.SubListen) {
|
||||
return common.NewError("Sub and Web could not use same ip:port, ", s.SubListen, ":", s.SubPort, " & ", s.WebListen, ":", s.WebPort)
|
||||
}
|
||||
|
||||
if s.WebCertFile != "" || s.WebKeyFile != "" {
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
</a-tag>
|
||||
<template v-if="app.subSettings.enable && qrModal.subId">
|
||||
<a-divider>Subscription</a-divider>
|
||||
<canvas @click="copyToClipboard('qrCode-sub',genSubLink(qrModal.client.subId))" id="qrCode-sub" style="width: 100%; height: 100%;"></canvas>
|
||||
<div class="qr-bg"><canvas @click="copyToClipboard('qrCode-sub',genSubLink(qrModal.client.subId))" id="qrCode-sub" style="width: 100%; height: 100%;"></canvas></div>
|
||||
</template>
|
||||
<a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
|
||||
<template v-for="(row, index) in qrModal.qrcodes">
|
||||
<a-tag color="green" style="margin: 10px 0; display: block; text-align: center;">[[ row.remark ]]</a-tag>
|
||||
<canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" style="width: 100%; height: 100%;"></canvas>
|
||||
<div class="qr-bg"><canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" style="width: 100%; height: 100%;"></canvas></div>
|
||||
</template>
|
||||
</a-modal>
|
||||
|
||||
@@ -87,4 +87,4 @@
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -56,35 +56,12 @@
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.wave {
|
||||
opacity: 0.6;
|
||||
position: absolute;
|
||||
top: 80%;
|
||||
left: 60%;
|
||||
width: 6000px;
|
||||
height: 6000px;
|
||||
background-color: rgba(0, 135, 113, 0.08);
|
||||
margin-left: -3000px;
|
||||
transform-origin: 50% 50%;
|
||||
pointer-events: none;
|
||||
rotate: 115deg;
|
||||
}
|
||||
.wave2 {
|
||||
opacity: 0.4;
|
||||
rotate: 105deg;
|
||||
}
|
||||
.wave3 {
|
||||
opacity: 0.2;
|
||||
rotate: 62deg;
|
||||
}
|
||||
.under {
|
||||
background-color: #dbf5ed;
|
||||
}
|
||||
.dark .wave {
|
||||
background: rgb(10 117 87 / 20%);
|
||||
background-color: #c7ebe2;
|
||||
z-index: 0;
|
||||
}
|
||||
.dark .under {
|
||||
background-color: #101828;
|
||||
background-color: #0f2d32;
|
||||
}
|
||||
.dark #login {
|
||||
background-color: #151f31;
|
||||
@@ -95,7 +72,6 @@
|
||||
.ant-form-item {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.ant-btn-primary-login {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -138,6 +114,7 @@
|
||||
position: relative;
|
||||
border-radius: 25px;
|
||||
width: 100%;
|
||||
transition: all 0.3s cubic-bezier(.645,.045,.355,1);
|
||||
}
|
||||
.dark .wave-btn-bg {
|
||||
color: #fff;
|
||||
@@ -147,8 +124,8 @@
|
||||
background-origin: border-box;
|
||||
background-clip: padding-box, border-box;
|
||||
background-size: 300%;
|
||||
transition: all 0.5s ease;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
.dark .wave-btn-bg:hover {animation: wave-btn-tara 4s ease infinite;}
|
||||
.dark .wave-btn-bg-cl {
|
||||
@@ -206,14 +183,84 @@
|
||||
background-position-x: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.waves-header {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
background-color: #dbf5ed;
|
||||
color: white;
|
||||
z-index: -1;
|
||||
}
|
||||
.dark .waves-header {
|
||||
background-color: #101828;
|
||||
}
|
||||
.waves-inner-header {
|
||||
height: 50vh;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.waves {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 15vh;
|
||||
margin-bottom: -5px; /*Fix for safari gap*/
|
||||
min-height: 100px;
|
||||
max-height: 150px;
|
||||
}
|
||||
.parallax > use {
|
||||
animation: move-forever 25s cubic-bezier(0.55, 0.5, 0.45, 0.5) infinite;
|
||||
}
|
||||
.dark .parallax > use {
|
||||
fill: rgb(10 117 87 / 20%);
|
||||
}
|
||||
.parallax > use:nth-child(1) {
|
||||
animation-delay: -2s;
|
||||
animation-duration: 7s;
|
||||
opacity: 0.2;
|
||||
}
|
||||
.parallax > use:nth-child(2) {
|
||||
animation-delay: -3s;
|
||||
animation-duration: 10s;
|
||||
opacity: 0.4;
|
||||
}
|
||||
.parallax > use:nth-child(3) {
|
||||
animation-delay: -4s;
|
||||
animation-duration: 13s;
|
||||
opacity: 0.6;
|
||||
}
|
||||
@keyframes move-forever {
|
||||
0% {
|
||||
transform: translate3d(-90px, 0, 0);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(85px, 0, 0);
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.waves {
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||
<transition name="list" appear>
|
||||
<a-layout-content class="under" style="min-height: 0;">
|
||||
<div class='wave'></div>
|
||||
<div class='wave wave2'></div>
|
||||
<div class='wave wave3'></div>
|
||||
<div class="waves-header">
|
||||
<div class="waves-inner-header"></div>
|
||||
<svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
|
||||
<defs>
|
||||
<path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />
|
||||
</defs>
|
||||
<g class="parallax">
|
||||
<use xlink:href="#gentle-wave" x="48" y="3" fill="rgba(0, 135, 113, 0.08)" />
|
||||
<use xlink:href="#gentle-wave" x="48" y="5" fill="rgba(0, 135, 113, 0.08)" />
|
||||
<use xlink:href="#gentle-wave" x="48" y="7" fill="rgba(0, 135, 113, 0.08)" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto;">
|
||||
<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">
|
||||
@@ -243,9 +290,8 @@
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<a-row justify="center" class="centered">
|
||||
<div class="wave-btn-bg wave-btn-bg-cl">
|
||||
<a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined"
|
||||
:style="loading ? { width: '50px' } : { display: 'inline-block' }">
|
||||
<div class="wave-btn-bg wave-btn-bg-cl" :style="loading ? { width: '54px' } : { display: 'inline-block' }">
|
||||
<a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined">
|
||||
[[ loading ? '' : '{{ i18n "login" }}' ]]
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.totalFlow" }} (GB)
|
||||
{{ i18n "pages.inbounds.totalFlow" }}
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
@@ -104,8 +104,10 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
||||
<a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme" v-model="clientsBulkModal.expiryTime"></a-date-picker>
|
||||
<persian-datepicker v-else :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
value="clientsBulkModal.expiryTime" v-model="clientsBulkModal.expiryTime"></persian-datepicker>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="clientsBulkModal.expiryTime != 0">
|
||||
<template slot="label">
|
||||
@@ -234,6 +236,9 @@
|
||||
get delayedExpireDays() {
|
||||
return this.clientsBulkModal.expiryTime < 0 ? this.clientsBulkModal.expiryTime / -86400000 : 0;
|
||||
},
|
||||
get datepicker() {
|
||||
return app.datepicker;
|
||||
},
|
||||
set delayedExpireDays(days) {
|
||||
this.clientsBulkModal.expiryTime = -86400000 * days;
|
||||
},
|
||||
|
||||
@@ -94,6 +94,9 @@
|
||||
get isEdit() {
|
||||
return this.clientModal.isEdit;
|
||||
},
|
||||
get datepicker() {
|
||||
return app.datepicker;
|
||||
},
|
||||
get isTrafficExhausted() {
|
||||
if (!clientStats) return false
|
||||
if (clientStats.total <= 0) return false
|
||||
|
||||
60
web/html/xui/component/persianDatepicker.html
Normal file
60
web/html/xui/component/persianDatepicker.html
Normal file
@@ -0,0 +1,60 @@
|
||||
{{define "component/persianDatepickerTemplate"}}
|
||||
<template>
|
||||
<div>
|
||||
<a-input :value="value" type="text" v-model="date" data-jdp class="persian-datepicker"
|
||||
@input="$emit('input', convertToGregorian($event.target.value)); jalaliDatepicker.hide();"
|
||||
placeholder="انتخاب تاریخ">
|
||||
<template #addonAfter>
|
||||
<a-icon type="calendar" style="font-size: 16px;"/>
|
||||
</template>
|
||||
</a-input>
|
||||
</div>
|
||||
</template>
|
||||
{{end}}
|
||||
|
||||
{{define "component/persianDatepicker"}}
|
||||
<link rel="stylesheet" href="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.css"/>
|
||||
<script src="{{ .base_path }}assets/moment/moment-jalali.min.js"></script>
|
||||
<script src="{{ .base_path }}assets/persian-datepicker/persian-datepicker.min.js"></script>
|
||||
<script>
|
||||
|
||||
const persianDatepicker = {};
|
||||
|
||||
Vue.component('persian-datepicker', {
|
||||
props: ['dropdown-class-name', 'format', 'value'],
|
||||
template: `{{template "component/persianDatepickerTemplate"}}`,
|
||||
data() {
|
||||
return {
|
||||
date: '',
|
||||
persianDatepicker,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value: function (date) {
|
||||
this.date = this.convertToJalalian(date)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.date = this.convertToJalalian(this.value)
|
||||
this.listenToDatepicker()
|
||||
},
|
||||
methods: {
|
||||
convertToGregorian(date) {
|
||||
return date ? moment(moment(date, 'jYYYY/jMM/jDD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss')) : null
|
||||
},
|
||||
convertToJalalian(date) {
|
||||
return date && moment.isMoment(date) ? date.format('jYYYY/jMM/jDD HH:mm:ss') : null
|
||||
},
|
||||
listenToDatepicker() {
|
||||
jalaliDatepicker.startWatch({
|
||||
time: true,
|
||||
container: '.ant-modal-wrap',
|
||||
hideAfterChange: true,
|
||||
useDropDownYears: false,
|
||||
changeMonthRotateYear: true,
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -19,7 +19,7 @@
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.client.renew" }}</span>
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
{{ i18n "password" }}
|
||||
<a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS"@click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
|
||||
@@ -32,7 +32,7 @@
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.client.renew" }}</span>
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
ID <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"></a-icon>
|
||||
</a-tooltip>
|
||||
@@ -117,7 +117,7 @@
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.totalFlow" }}(GB)
|
||||
{{ i18n "pages.inbounds.totalFlow" }}
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
@@ -150,8 +150,10 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
||||
<a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme" v-model="client._expiryTime"></a-date-picker>
|
||||
<persian-datepicker v-else :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
value="client._expiryTime" v-model="client._expiryTime"></persian-datepicker>
|
||||
<a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="client.expiryTime != 0">
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<template slot="title">
|
||||
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.totalFlow" }}(GB)
|
||||
{{ i18n "pages.inbounds.totalFlow" }}
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
@@ -54,9 +54,11 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
||||
<a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
v-model="dbInbound._expiryTime"></a-date-picker>
|
||||
<persian-datepicker v-else :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
value="dbInbound._expiryTime" v-model="dbInbound._expiryTime"></persian-datepicker>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
@@ -95,6 +97,11 @@
|
||||
{{template "form/http"}}
|
||||
</template>
|
||||
|
||||
<!-- wireguard -->
|
||||
<template v-if="inbound.protocol === Protocols.WIREGUARD">
|
||||
{{template "form/wireguard"}}
|
||||
</template>
|
||||
|
||||
<!-- stream settings -->
|
||||
<template v-if="inbound.canEnableStream()">
|
||||
{{template "form/streamSettings"}}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<a-select
|
||||
v-model="outbound.settings.domainStrategy"
|
||||
: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-form-item>
|
||||
<a-form-item label='Fragment'>
|
||||
@@ -46,24 +46,114 @@
|
||||
|
||||
<!-- blackhole settings -->
|
||||
<template v-if="outbound.protocol === Protocols.Blackhole">
|
||||
<a-form-item label='Response Type'>
|
||||
<a-select
|
||||
v-model="outbound.settings.type"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="s in ['', 'none','http']" :value="s">[[ s ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Response Type'>
|
||||
<a-select
|
||||
v-model="outbound.settings.type"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="s in ['', 'none','http']" :value="s">[[ s ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<!-- dns settings -->
|
||||
<template v-if="outbound.protocol === Protocols.DNS">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
||||
<a-select
|
||||
v-model="outbound.settings.network"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="s in ['udp','tcp']" :value="s">[[ s ]]</a-select-option>
|
||||
</a-select>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
||||
<a-select
|
||||
v-model="outbound.settings.network"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="s in ['udp','tcp']" :value="s">[[ s ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<!-- wireguard settings -->
|
||||
<template v-if="outbound.protocol === Protocols.Wireguard">
|
||||
<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>
|
||||
|
||||
<!-- Address + Port -->
|
||||
@@ -81,8 +171,9 @@
|
||||
<a-form-item label='ID'>
|
||||
<a-input v-model.trim="outbound.settings.id"></a-input>
|
||||
</a-form-item>
|
||||
<!-- vless settings -->
|
||||
<template v-if="outbound.canEnableTlsFlow()">
|
||||
|
||||
<!-- vless settings -->
|
||||
<template v-if="outbound.canEnableTlsFlow()">
|
||||
<a-form-item label='Flow'>
|
||||
<a-select v-model="outbound.settings.flow" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
||||
@@ -94,19 +185,21 @@
|
||||
|
||||
<!-- Servers (trojan/shadowsocks/socks/http) settings -->
|
||||
<template v-if="outbound.hasServers()">
|
||||
<!-- http / socks -->
|
||||
<template v-if="outbound.hasUsername()">
|
||||
<a-form-item label='{{ i18n "username" }}'>
|
||||
<a-input v-model.trim="outbound.settings.user"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-input v-model.trim="outbound.settings.password"></a-input>
|
||||
<a-input v-model.trim="outbound.settings.pass"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- shadowsocks -->
|
||||
<template v-if="outbound.protocol === Protocols.Shadowsocks">
|
||||
|
||||
<!-- shadowsocks -->
|
||||
<template v-if="outbound.protocol === Protocols.Shadowsocks">
|
||||
<a-form-item label='{{ i18n "encryption" }}'>
|
||||
<a-select v-model="outbound.settings.method" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
|
||||
<a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='UDP over TCP'>
|
||||
@@ -121,9 +214,9 @@
|
||||
<a-select v-model="outbound.stream.network" @change="streamNetworkChange"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<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">WS</a-select-option>
|
||||
<a-select-option value="http">HTTP2</a-select-option>
|
||||
<a-select-option value="http">H2</a-select-option>
|
||||
<a-select-option value="quic">QUIC</a-select-option>
|
||||
<a-select-option value="grpc">gRPC</a-select-option>
|
||||
</a-select>
|
||||
@@ -149,36 +242,36 @@
|
||||
<template v-if="outbound.stream.network === 'kcp'">
|
||||
<a-form-item label='{{ i18n "camouflage" }}'>
|
||||
<a-select v-model="outbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="none">none (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-option value="none">None</a-select-option>
|
||||
<a-select-option value="srtp">SRTP</a-select-option>
|
||||
<a-select-option value="utp">uTP</a-select-option>
|
||||
<a-select-option value="wechat-video">WeChat</a-select-option>
|
||||
<a-select-option value="dtls">DTLS 1.2</a-select-option>
|
||||
<a-select-option value="wireguard">WireGuard</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-input v-model="outbound.stream.kcp.seed"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='mtu'>
|
||||
<a-form-item label='MTU'>
|
||||
<a-input-number v-model.number="outbound.stream.kcp.mtu"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='tti (ms)'>
|
||||
<a-form-item label='TTI (ms)'>
|
||||
<a-input-number v-model.number="outbound.stream.kcp.tti"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='uplink capacity (MB/S)'>
|
||||
<a-form-item label='Uplink (MB/s)'>
|
||||
<a-input-number v-model.number="outbound.stream.kcp.upCap"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='downlink capacity (MB/S)'>
|
||||
<a-form-item label='Downlink (MB/s)'>
|
||||
<a-input-number v-model.number="outbound.stream.kcp.downCap"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='congestion'>
|
||||
<a-form-item label='Congestion'>
|
||||
<a-switch v-model="outbound.stream.kcp.congestion"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label='read buffer size (MB)'>
|
||||
<a-form-item label='Read Buffer (MB)'>
|
||||
<a-input-number v-model.number="outbound.stream.kcp.readBuffer"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='write buffer size (MB)'>
|
||||
<a-form-item label='Write Buffer (MB)'>
|
||||
<a-input-number v-model.number="outbound.stream.kcp.writeBuffer"></a-input-number>
|
||||
</a-form-item>
|
||||
</template>
|
||||
@@ -207,9 +300,9 @@
|
||||
<template v-if="outbound.stream.network === 'quic'">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
|
||||
<a-select v-model="outbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="none">none</a-select-option>
|
||||
<a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
|
||||
<a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
|
||||
<a-select-option value="none">None</a-select-option>
|
||||
<a-select-option value="aes-128-gcm">AES-128-GCM</a-select-option>
|
||||
<a-select-option value="chacha20-poly1305">CHACHA20-POLY1305</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
@@ -217,22 +310,22 @@
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "camouflage" }}'>
|
||||
<a-select v-model="outbound.stream.quic.type" :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-option value="none">None</a-select-option>
|
||||
<a-select-option value="srtp">SRTP</a-select-option>
|
||||
<a-select-option value="utp">uTP</a-select-option>
|
||||
<a-select-option value="wechat-video">WeChat</a-select-option>
|
||||
<a-select-option value="dtls">DTLS 1.2</a-select-option>
|
||||
<a-select-option value="wireguard">WireGuard</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<!-- grpc -->
|
||||
<template v-if="outbound.stream.network === 'grpc'">
|
||||
<a-form-item label='serviceName'>
|
||||
<a-form-item label='Service Name'>
|
||||
<a-input v-model.trim="outbound.stream.grpc.serviceName"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='MultiMode'>
|
||||
<a-form-item label='Multi Mode'>
|
||||
<a-switch v-model="outbound.stream.grpc.multiMode"></a-switch>
|
||||
</a-form-item>
|
||||
</template>
|
||||
@@ -240,59 +333,54 @@
|
||||
|
||||
<!-- tls settings -->
|
||||
<template v-if="outbound.canEnableTls()">
|
||||
<a-form-item label='{{ i18n "security" }}'>
|
||||
<a-radio-group v-model="outbound.stream.security" button-style="solid">
|
||||
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
||||
<a-radio-button value="tls">TLS</a-radio-button>
|
||||
<a-radio-button v-if="outbound.canEnableReality()" value="reality">Reality</a-radio-button>
|
||||
</a-radio-group>
|
||||
<a-form-item label='{{ i18n "security" }}'>
|
||||
<a-radio-group v-model="outbound.stream.security" button-style="solid">
|
||||
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
||||
<a-radio-button value="tls">TLS</a-radio-button>
|
||||
<a-radio-button v-if="outbound.canEnableReality()" value="reality">REALITY</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<template v-if="outbound.stream.isTls">
|
||||
<a-form-item label="SNI" placeholder="Server Name Indication">
|
||||
<a-input v-model.trim="outbound.stream.tls.serverName"></a-input>
|
||||
</a-form-item>
|
||||
<template v-if="outbound.stream.isTls">
|
||||
<a-form-item label="SNI" placeholder="Server Name Indication">
|
||||
<a-input v-model.trim="outbound.stream.tls.serverName"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="uTLS">
|
||||
<a-select v-model="outbound.stream.tls.fingerprint"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<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>
|
||||
<a-form-item label="ALPN">
|
||||
<a-select
|
||||
mode="multiple"
|
||||
|
||||
: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>
|
||||
<a-form-item label="Allow insecure">
|
||||
<a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<!-- reality settings -->
|
||||
<template v-if="outbound.stream.isReality">
|
||||
<a-form-item label='{{ i18n "domainName" }}'>
|
||||
<a-input v-model.trim="outbound.stream.reality.serverName"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="uTLS">
|
||||
<a-select v-model="outbound.stream.reality.fingerprint"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Short Id">
|
||||
<a-input v-model.trim="outbound.stream.reality.shortId" style="width:250px"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="SpiderX">
|
||||
<a-input v-model.trim="outbound.stream.reality.spiderX" style="width:250px"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Public Key">
|
||||
<a-input v-model.trim="outbound.stream.reality.publicKey"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label="uTLS">
|
||||
<a-select v-model="outbound.stream.tls.fingerprint" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<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>
|
||||
<a-form-item label="ALPN">
|
||||
<a-select mode="multiple" :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>
|
||||
<a-form-item label="Allow Insecure">
|
||||
<a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<!-- reality settings -->
|
||||
<template v-if="outbound.stream.isReality">
|
||||
<a-form-item label='{{ i18n "domainName" }}'>
|
||||
<a-input v-model.trim="outbound.stream.reality.serverName"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="uTLS">
|
||||
<a-select v-model="outbound.stream.reality.fingerprint" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Short ID">
|
||||
<a-input v-model.trim="outbound.stream.reality.shortId" style="width:250px"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="SpiderX">
|
||||
<a-input v-model.trim="outbound.stream.reality.spiderX" style="width:250px"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Public Key">
|
||||
<a-input v-model.trim="outbound.stream.reality.publicKey"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</template>
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
@@ -304,4 +392,4 @@
|
||||
<textarea style="position:absolute; left: -800px;" id="outboundJson"></textarea>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.network"}}'>
|
||||
<a-select v-model="inbound.settings.network" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="tcp,udp">TCP+UDP</a-select-option>
|
||||
<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>
|
||||
<a-form-item label='FollowRedirect'>
|
||||
<a-form-item label='Follow Redirect'>
|
||||
<a-switch v-model="inbound.settings.followRedirect"></a-switch>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -20,38 +20,28 @@
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</template>
|
||||
<table width="100%" class="ant-table-tbody">
|
||||
<tr>
|
||||
<td>{{ i18n "encryption" }}</td>
|
||||
<td>
|
||||
<a-form-item>
|
||||
<a-select v-model="inbound.settings.method" style="width: 250px;" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="inbound.isSS2022">
|
||||
<td>{{ i18n "password" }}
|
||||
<a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
|
||||
</td>
|
||||
<td>
|
||||
<a-form-item>
|
||||
<a-input v-model.trim="inbound.settings.password" style="width: 250px"></a-input>
|
||||
</a-form-item>
|
||||
</td>
|
||||
</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>
|
||||
</table>
|
||||
{{end}}
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "encryption" }}'>
|
||||
<a-select v-model="inbound.settings.method" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.isSS2022">
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template> Password <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="inbound.settings.password"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
||||
<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>
|
||||
</a-form>
|
||||
{{end}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{{define "form/socks"}}
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.enable" }} udp'>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.enable" }} UDP'>
|
||||
<a-switch v-model="inbound.settings.udp"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="IP" v-if="inbound.settings.udp">
|
||||
@@ -30,4 +30,4 @@
|
||||
</a-input-group>
|
||||
</template>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -33,14 +33,14 @@
|
||||
<!-- trojan fallbacks -->
|
||||
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-divider style="margin:0;">
|
||||
Fallback[[ index + 1 ]]
|
||||
Fallback [[ index + 1 ]]
|
||||
<a-icon type="delete" @click="() => inbound.settings.delFallback(index)"
|
||||
style="color: rgb(255, 77, 79);cursor: pointer;"/>
|
||||
</a-divider>
|
||||
<a-form-item label='Name'>
|
||||
<a-form-item label='SNI'>
|
||||
<a-input v-model="fallback.name"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Alpn'>
|
||||
<a-form-item label='ALPN'>
|
||||
<a-input v-model="fallback.alpn"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Path'>
|
||||
@@ -55,4 +55,4 @@
|
||||
</a-form>
|
||||
<a-divider style="margin:0;"></a-divider>
|
||||
</template>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -35,14 +35,14 @@
|
||||
<!-- vless fallbacks -->
|
||||
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-divider style="margin:0;">
|
||||
Fallback[[ index + 1 ]]
|
||||
Fallback [[ index + 1 ]]
|
||||
<a-icon type="delete" @click="() => inbound.settings.delFallback(index)"
|
||||
style="color: rgb(255, 77, 79);cursor: pointer;"/>
|
||||
</a-divider>
|
||||
<a-form-item label='Name'>
|
||||
<a-form-item label='SNI'>
|
||||
<a-input v-model="fallback.name"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Alpn'>
|
||||
<a-form-item label='ALPN'>
|
||||
<a-input v-model="fallback.alpn"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Path'>
|
||||
@@ -55,6 +55,6 @@
|
||||
<a-input-number v-model="fallback.xver" :min="0" :max="2"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-divider style="margin:0;"></a-divider>
|
||||
<a-divider style="margin:5px 0;"></a-divider>
|
||||
</template>
|
||||
{{end}}
|
||||
|
||||
56
web/html/xui/form/protocol/wireguard.html
Normal file
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,5 +1,5 @@
|
||||
{{define "form/sniffing"}}
|
||||
<a-divider style="margin:0;"></a-divider>
|
||||
<a-divider style="margin:5px 0 0;"></a-divider>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
@@ -19,4 +19,4 @@
|
||||
</a-checkbox-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{{define "form/externalProxy"}}
|
||||
<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:5px 0 0;"></a-divider>
|
||||
<a-form-item label="External Proxy">
|
||||
<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-form-item>
|
||||
<a-input-group style="margin: 5px 0;" compact v-for="(row, index) in inbound.stream.externalProxy">
|
||||
<a-input-group style="margin: 8px 0;" compact v-for="(row, index) in inbound.stream.externalProxy">
|
||||
<template>
|
||||
<a-tooltip title="Force TLS">
|
||||
<a-select v-model="row.forceTls" style="width:20%; margin: 0px" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{{define "form/streamGRPC"}}
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="Service Name">
|
||||
<a-input v-model.trim="inbound.stream.grpc.serviceName" style="width: 250px;"></a-input>
|
||||
<a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="MultiMode">
|
||||
<a-form-item label="Multi Mode">
|
||||
<a-switch v-model="inbound.stream.grpc.multiMode"></a-switch>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -2,37 +2,46 @@
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "camouflage" }}'>
|
||||
<a-select v-model="inbound.stream.kcp.type" :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-option value="none">None</a-select-option>
|
||||
<a-select-option value="srtp">SRTP</a-select-option>
|
||||
<a-select-option value="utp">uTP</a-select-option>
|
||||
<a-select-option value="wechat-video">WeChat</a-select-option>
|
||||
<a-select-option value="dtls">DTLS 1.2</a-select-option>
|
||||
<a-select-option value="wireguard">WireGuard</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-input v-model="inbound.stream.kcp.seed"></a-input>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
{{ i18n "password" }}
|
||||
<a-icon @click="inbound.stream.kcp.seed = RandomUtil.randomSeq(10)"type="sync"> </a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="inbound.stream.kcp.seed"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='MTU'>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.mtu"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='TTI(ms)'>
|
||||
<a-form-item label='TTI (ms)'>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.tti"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='Uplink(Mb/s)'>
|
||||
<a-form-item label='Uplink (MB/s)'>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.upCap"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='Downlink(Mb/s)'>
|
||||
<a-form-item label='Downlink (MB/s)'>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.downCap"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='Congestion'>
|
||||
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label='Read buffer(MB)'>
|
||||
<a-form-item label='Read Buffer (MB)'>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.readBuffer"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='Write buffer(MB)'>
|
||||
<a-form-item label='Write Buffer (MB)'>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.writeBuffer"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -2,23 +2,32 @@
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
|
||||
<a-select v-model="inbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="none">none</a-select-option>
|
||||
<a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
|
||||
<a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
|
||||
<a-select-option value="none">None</a-select-option>
|
||||
<a-select-option value="aes-128-gcm">AES-128-GCM</a-select-option>
|
||||
<a-select-option value="chacha20-poly1305">CHACHA20-POLY1305</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
{{ i18n "password" }}
|
||||
<a-icon @click="inbound.stream.quic.key = RandomUtil.randomSeq(10)"type="sync"> </a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="inbound.stream.quic.key"></a-input>
|
||||
</a-form-item>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "camouflage" }}'>
|
||||
<a-select v-model="inbound.stream.quic.type" :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-option value="none">None</a-select-option>
|
||||
<a-select-option value="srtp">SRTP</a-select-option>
|
||||
<a-select-option value="utp">uTP</a-select-option>
|
||||
<a-select-option value="wechat-video">WeChat</a-select-option>
|
||||
<a-select-option value="dtls">DTLS 1.2</a-select-option>
|
||||
<a-select-option value="wireguard">WireGuard</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{{define "form/streamSettings"}}
|
||||
<!-- select stream network -->
|
||||
<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"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<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">WS</a-select-option>
|
||||
<a-select-option value="http">H2</a-select-option>
|
||||
<a-select-option value="quic">QUIC</a-select-option>
|
||||
@@ -47,4 +47,4 @@
|
||||
<template>
|
||||
{{template "form/streamSockopt"}}
|
||||
</template>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
{{define "form/streamSockopt"}}
|
||||
<a-divider style="margin:0;"></a-divider>
|
||||
<a-divider style="margin:5px 0 0;"></a-divider>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="Transparent Proxy">
|
||||
<a-form-item label="TPROXY">
|
||||
<a-switch v-model="inbound.stream.sockoptSwitch"></a-switch>
|
||||
</a-form-item>
|
||||
<template v-if="inbound.stream.sockoptSwitch">
|
||||
<a-form-item label="Accept Proxy Protocol">
|
||||
<a-form-item label="PROXY Protocol">
|
||||
<a-switch v-model="inbound.stream.sockopt.acceptProxyProtocol"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP FastOpen">
|
||||
<a-form-item label="TCP Fast Open">
|
||||
<a-switch v-model.trim="inbound.stream.sockopt.tcpFastOpen"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="Route Mark">
|
||||
<a-input-number v-model="inbound.stream.sockopt.mark" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="T-Proxy">
|
||||
<a-form-item label="TPROXY">
|
||||
<a-select v-model="inbound.stream.sockopt.tproxy" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="off">OFF</a-select-option>
|
||||
<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-option value="tproxy">TPROXY</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -1,58 +1,63 @@
|
||||
{{define "form/streamTCP"}}
|
||||
<!-- tcp type -->
|
||||
<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-form-item>
|
||||
<a-form-item label='HTTP {{ i18n "camouflage" }}'>
|
||||
<a-switch
|
||||
:checked="inbound.stream.tcp.type === 'http'"
|
||||
<a-switch :checked="inbound.stream.tcp.type === 'http'"
|
||||
@change="checked => inbound.stream.tcp.type = checked ? 'http' : 'none'">
|
||||
</a-switch>
|
||||
</a-form-item>
|
||||
</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-item label='{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}'>
|
||||
<a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }">
|
||||
<!-- tcp request -->
|
||||
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.request" }}</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.method"></a-input>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.method" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.method"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">{{ i18n "pages.inbounds.stream.tcp.requestPath" }}
|
||||
<template slot="label">{{ i18n "pages.inbounds.stream.tcp.path" }}
|
||||
<a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button>
|
||||
</template>
|
||||
<template v-for="(path, index) in inbound.stream.tcp.request.path">
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.path[index]">
|
||||
<a-button size="small" slot="addonAfter"
|
||||
@click="inbound.stream.tcp.request.removePath(index)"
|
||||
v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
|
||||
<a-button size="small" slot="addonAfter" @click="inbound.stream.tcp.request.removePath(index)"
|
||||
v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
|
||||
</a-input>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
||||
<a-button size="small" @click="inbound.stream.tcp.request.addHeader('', '')">+</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button size="small" @click="inbound.stream.tcp.request.addHeader('host', '')">+</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.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>
|
||||
</a-input>
|
||||
<a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
|
||||
<a-input style="width: 50%" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button slot="addonAfter" size="small"
|
||||
@click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<!-- tcp response -->
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}'>
|
||||
|
||||
<!-- tcp response -->
|
||||
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.response" }}</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}'>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.status" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.response.status"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}'>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.statusDescription" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.response.reason"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}'>
|
||||
@@ -61,11 +66,12 @@
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<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" }}'>
|
||||
<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" }}'>
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<template slot="addonAfter">
|
||||
<a-button size="small" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button>
|
||||
</template>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{{define "form/streamWS"}}
|
||||
<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-form-item>
|
||||
<a-form-item label='{{ i18n "path" }}'>
|
||||
<a-input v-model.trim="inbound.stream.ws.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button size="small" @click="inbound.stream.ws.addHeader()">+</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
@@ -20,4 +20,4 @@
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{{define "form/tlsSettings"}}
|
||||
<!-- tls enable -->
|
||||
<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:3px 0;"></a-divider>
|
||||
<a-form-item label='{{ i18n "security" }}'>
|
||||
<a-radio-group v-model="inbound.stream.security" button-style="solid">
|
||||
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
||||
@@ -15,7 +15,7 @@
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.inbounds.realityDesc" }}</span>
|
||||
</template>
|
||||
<a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
|
||||
<a-radio-button v-if="inbound.canEnableReality()" value="reality">REALITY</a-radio-button>
|
||||
</a-tooltip>
|
||||
<a-radio-button value="tls">TLS</a-radio-button>
|
||||
</a-radio-group>
|
||||
@@ -26,9 +26,9 @@
|
||||
<a-form-item label="SNI" placeholder="Server Name Indication">
|
||||
<a-input v-model.trim="inbound.stream.tls.sni"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="CipherSuites">
|
||||
<a-form-item label="Cipher Suites">
|
||||
<a-select v-model="inbound.stream.tls.cipherSuites" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="">auto</a-select-option>
|
||||
<a-select-option value="">Auto</a-select-option>
|
||||
<a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[ value ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
@@ -55,7 +55,7 @@
|
||||
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Allow insecure">
|
||||
<a-form-item label="Allow Insecure">
|
||||
<a-switch v-model="inbound.stream.tls.settings.allowInsecure"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="Reject Unknown SNI">
|
||||
@@ -74,10 +74,10 @@
|
||||
</a-form-item>
|
||||
<template v-if="cert.useFile">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'>
|
||||
<a-input v-model.trim="cert.certFile" style="width:250px;"></a-input>
|
||||
<a-input v-model.trim="cert.certFile"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'>
|
||||
<a-input v-model.trim="cert.keyFile" style="width:250px;"></a-input>
|
||||
<a-input v-model.trim="cert.keyFile"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">{{ i18n
|
||||
@@ -86,13 +86,13 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
|
||||
<a-input type="textarea" :rows="3" style="width:250px;" v-model="cert.cert"></a-input>
|
||||
<a-input type="textarea" :rows="3" v-model="cert.cert"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'>
|
||||
<a-input type="textarea" :rows="3" style="width:250px;" v-model="cert.key"></a-input>
|
||||
<a-input type="textarea" :rows="3" v-model="cert.key"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label='ocspStapling'>
|
||||
<a-form-item label='OCSP stapling'>
|
||||
<a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
</template>
|
||||
@@ -109,7 +109,7 @@
|
||||
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Allow insecure">
|
||||
<a-form-item label="Allow Insecure">
|
||||
<a-switch v-model="inbound.stream.xtls.settings.allowInsecure"></a-switch>
|
||||
</a-form-item>
|
||||
<template v-for="cert,index in inbound.stream.xtls.certs">
|
||||
@@ -125,10 +125,10 @@
|
||||
</a-form-item>
|
||||
<template v-if="cert.useFile">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'>
|
||||
<a-input v-model.trim="cert.certFile" style="width:250px;"></a-input>
|
||||
<a-input v-model.trim="cert.certFile"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'>
|
||||
<a-input v-model.trim="cert.keyFile" style="width:250px;"></a-input>
|
||||
<a-input v-model.trim="cert.keyFile"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" icon="import" @click="setDefaultCertXtls(index)">{{ i18n
|
||||
@@ -137,10 +137,10 @@
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'>
|
||||
<a-input type="textarea" :rows="3" style="width:250px;" v-model="cert.cert"></a-input>
|
||||
<a-input type="textarea" :rows="3" v-model="cert.cert"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'>
|
||||
<a-input type="textarea" :rows="3" style="width:250px;" v-model="cert.key"></a-input>
|
||||
<a-input type="textarea" :rows="3" v-model="cert.key"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</template>
|
||||
@@ -162,23 +162,23 @@
|
||||
<a-form-item label='Dest'>
|
||||
<a-input v-model.trim="inbound.stream.reality.dest"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Server Names'>
|
||||
<a-form-item label='SNI'>
|
||||
<a-input v-model.trim="inbound.stream.reality.serverNames"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.client.renew" }}</span>
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
Short Ids
|
||||
Short ID
|
||||
<a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId()" type="sync"> </a-icon>
|
||||
</a-icon>
|
||||
</template>
|
||||
<a-input v-model.trim="inbound.stream.reality.shortIds" style="width:250px"></a-input>
|
||||
<a-input v-model.trim="inbound.stream.reality.shortIds"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='SpiderX'>
|
||||
<a-input v-model.trim="inbound.stream.reality.settings.spiderX" style="width:250px"></a-input>
|
||||
<a-input v-model.trim="inbound.stream.reality.settings.spiderX"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Private Key'>
|
||||
<a-input v-model.trim="inbound.stream.reality.privateKey"></a-input>
|
||||
@@ -187,8 +187,8 @@
|
||||
<a-input v-model.trim="inbound.stream.reality.settings.publicKey"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get new cert</a-button>
|
||||
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</a-form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
<template v-if="app.subSettings.enable && infoModal.clientSettings.subId">
|
||||
<a-divider>Subscription link</a-divider>
|
||||
<a-divider>Subscription URL</a-divider>
|
||||
<a-row>
|
||||
<a-col :sx="24" :md="22"><a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a></a-col>
|
||||
<a-col :sx="24" :md="2" style="text-align: right;">
|
||||
@@ -264,6 +264,45 @@
|
||||
<td><a-tag color="green">[[ account.pass ]]</a-tag></td>
|
||||
</tr>
|
||||
</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>
|
||||
</a-modal>
|
||||
<script>
|
||||
@@ -349,4 +388,4 @@
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
@@ -63,6 +63,9 @@
|
||||
get client() {
|
||||
return inModal.inbound.clients[0];
|
||||
},
|
||||
get datepicker() {
|
||||
return app.datepicker;
|
||||
},
|
||||
get delayedExpireDays() {
|
||||
return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;
|
||||
},
|
||||
|
||||
@@ -161,9 +161,9 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
</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"
|
||||
style="margin-right: .5rem;"
|
||||
:style="isMobile ? 'margin-bottom: .5rem; display: flex;' : 'margin-right: .5rem;'"
|
||||
@change="toggleFilter">
|
||||
<a-icon slot="checkedChildren" type="search"></a-icon>
|
||||
<a-icon slot="unCheckedChildren" type="filter"></a-icon>
|
||||
@@ -244,7 +244,7 @@
|
||||
</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="isMobile">
|
||||
<a-switch size="small" v-model="dbInbound.enable" @change="switchEnable(dbInbound.id)"></a-switch>
|
||||
<a-switch size="small" v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch>
|
||||
{{ i18n "pages.inbounds.enable" }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
@@ -314,7 +314,7 @@
|
||||
</a-popover>
|
||||
</template>
|
||||
<template slot="enable" slot-scope="text, dbInbound">
|
||||
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound.id)"></a-switch>
|
||||
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch>
|
||||
</template>
|
||||
<template slot="expiryTime" slot-scope="text, dbInbound">
|
||||
<a-popover v-if="dbInbound.expiryTime > 0" :overlay-class-name="themeSwitcher.currentTheme">
|
||||
@@ -427,7 +427,7 @@
|
||||
:columns="isMobile ? innerMobileColumns : innerColumns"
|
||||
:data-source="getInboundClients(record)"
|
||||
:pagination=pagination(getInboundClients(record))
|
||||
style="margin: -12px 2px -13px;">
|
||||
:style="isMobile ? 'margin: -12px 2px -13px;' : 'margin: -12px 22px -13px;'">
|
||||
{{template "client_table"}}
|
||||
</a-table>
|
||||
</template>
|
||||
@@ -446,27 +446,28 @@
|
||||
<script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script>
|
||||
<script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
|
||||
{{template "component/themeSwitcher" .}}
|
||||
{{template "component/persianDatepicker" .}}
|
||||
<script>
|
||||
const columns = [{
|
||||
title: "ID",
|
||||
align: 'right',
|
||||
dataIndex: "id",
|
||||
width: 40,
|
||||
width: 30,
|
||||
responsive: ["xs"],
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.operate" }}',
|
||||
align: 'center',
|
||||
width: 40,
|
||||
width: 30,
|
||||
scopedSlots: { customRender: 'action' },
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.enable" }}',
|
||||
align: 'center',
|
||||
width: 40,
|
||||
width: 30,
|
||||
scopedSlots: { customRender: 'enable' },
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.remark" }}',
|
||||
align: 'center',
|
||||
width: 80,
|
||||
width: 60,
|
||||
dataIndex: "remark",
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.port" }}',
|
||||
@@ -476,7 +477,7 @@
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.protocol" }}',
|
||||
align: 'left',
|
||||
width: 90,
|
||||
width: 70,
|
||||
scopedSlots: { customRender: 'protocol' },
|
||||
}, {
|
||||
title: '{{ i18n "clients" }}',
|
||||
@@ -491,7 +492,7 @@
|
||||
}, {
|
||||
title: '{{ i18n "pages.inbounds.expireDate" }}',
|
||||
align: 'center',
|
||||
width: 60,
|
||||
width: 40,
|
||||
scopedSlots: { customRender: 'expiryTime' },
|
||||
}];
|
||||
|
||||
@@ -539,6 +540,7 @@
|
||||
data: {
|
||||
siderDrawer,
|
||||
themeSwitcher,
|
||||
persianDatepicker,
|
||||
spinning: false,
|
||||
inbounds: [],
|
||||
dbInbounds: [],
|
||||
@@ -560,6 +562,7 @@
|
||||
subURI : ''
|
||||
},
|
||||
remarkModel: '-ieo',
|
||||
datepicker: 'gregorian',
|
||||
tgBotEnable: false,
|
||||
pageSize: 0,
|
||||
isMobile: window.innerWidth <= 768,
|
||||
@@ -605,6 +608,7 @@
|
||||
};
|
||||
this.pageSize = pageSize;
|
||||
this.remarkModel = remarkModel;
|
||||
this.datepicker = datepicker;
|
||||
}
|
||||
},
|
||||
setInbounds(dbInbounds) {
|
||||
@@ -1030,8 +1034,9 @@
|
||||
newDbInbound = this.checkFallback(dbInbound);
|
||||
infoModal.show(newDbInbound, index);
|
||||
},
|
||||
switchEnable(dbInboundId) {
|
||||
switchEnable(dbInboundId,state) {
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
dbInbound.enable = state;
|
||||
this.submit(`/panel/inbound/update/${dbInboundId}`, dbInbound);
|
||||
},
|
||||
async switchEnableClient(dbInboundId, client) {
|
||||
|
||||
@@ -77,8 +77,8 @@
|
||||
<a-row>
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
3X: <a href="https://github.com/MHSanaei/3x-ui/releases" target="_blank"><a-tag color="green">v{{ .cur_ver }}</a-tag></a>
|
||||
Xray: <a-tag color="green" style="cursor: pointer;" @click="openSelectV2rayVersion">v[[ status.xray.version ]]</a-tag>
|
||||
3X-UI <a href="https://github.com/MHSanaei/3x-ui/releases" target="_blank"><a-tag color="green">v{{ .cur_ver }}</a-tag></a>
|
||||
Xray <a-tag color="green" style="cursor: pointer;" @click="openSelectV2rayVersion">v[[ status.xray.version ]]</a-tag>
|
||||
<a href="https://t.me/panel3xui" target="_blank"><a-tag color="green">@panel3xui</a-tag></a>
|
||||
</a-card>
|
||||
</a-col>
|
||||
@@ -93,10 +93,10 @@
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
{{ i18n "pages.index.xrayStatus" }}:
|
||||
<a-tag :color="status.xray.color">[[ status.xray.state ]]</a-tag>
|
||||
<a-tag :color="status.xray.color">[[ status.xray.state.toUpperCase() ]]</a-tag>
|
||||
<a-popover v-if="status.xray.state === State.Error"
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<span slot="title" style="font-size: 12pt">Error in running xray-core
|
||||
<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">
|
||||
@@ -112,9 +112,9 @@
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
{{ i18n "pages.index.operationHours" }}:
|
||||
Xray:
|
||||
Xray
|
||||
<a-tag color="green">[[ formatSecond(status.appStats.uptime) ]]</a-tag>
|
||||
OS:
|
||||
OS
|
||||
<a-tag color="green">[[ formatSecond(status.uptime) ]]</a-tag>
|
||||
</a-card>
|
||||
</a-col>
|
||||
@@ -132,7 +132,7 @@
|
||||
<a-col :sm="24" :md="12">
|
||||
<a-card hoverable>
|
||||
{{ i18n "usage"}}:
|
||||
Memory [[ sizeFormat(status.appStats.mem) ]] -
|
||||
RAM [[ sizeFormat(status.appStats.mem) ]] -
|
||||
Threads [[ status.appStats.threads ]]
|
||||
</a-tooltip>
|
||||
</a-card>
|
||||
@@ -141,6 +141,7 @@
|
||||
<a-card hoverable>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-icon type="global"></a-icon>
|
||||
IPv4:
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
@@ -150,6 +151,7 @@
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-icon type="global"></a-icon>
|
||||
IPv6:
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
@@ -165,6 +167,7 @@
|
||||
<a-card hoverable>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-icon type="swap"></a-icon>
|
||||
TCP: [[ status.tcpCount ]]
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
@@ -174,6 +177,7 @@
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-icon type="swap"></a-icon>
|
||||
UDP: [[ status.udpCount ]]
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
@@ -190,7 +194,7 @@
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<a-icon type="arrow-up"></a-icon>
|
||||
[[ sizeFormat(status.netIO.up) ]]/S
|
||||
[[ sizeFormat(status.netIO.up) ]]/s
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.upSpeed" }}
|
||||
@@ -200,7 +204,7 @@
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-icon type="arrow-down"></a-icon>
|
||||
[[ sizeFormat(status.netIO.down) ]]/S
|
||||
[[ sizeFormat(status.netIO.down) ]]/s
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
{{ i18n "pages.index.downSpeed" }}
|
||||
@@ -246,8 +250,10 @@
|
||||
:closable="true" @ok="() => versionModal.visible = false"
|
||||
:class="themeSwitcher.currentTheme"
|
||||
footer="">
|
||||
<h2>{{ i18n "pages.index.xraySwitchClick"}}</h2>
|
||||
<h2>{{ i18n "pages.index.xraySwitchClickDesk"}}</h2>
|
||||
<a-alert type="warning" style="margin-bottom: 12px; width: fit-content"
|
||||
message='{{ i18n "pages.index.xraySwitchClickDesk" }}'
|
||||
show-icon
|
||||
></a-alert>
|
||||
<template v-for="version, index in versionModal.versions">
|
||||
<a-tag :color="index % 2 == 0 ? 'purple' : 'green'"
|
||||
style="margin: 10px" @click="switchV2rayVersion(version)">
|
||||
@@ -650,4 +656,4 @@
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -138,6 +138,27 @@
|
||||
<setting-list-item type="number" title='{{ i18n "pages.settings.expireTimeDiff" }}' desc='{{ i18n "pages.settings.expireTimeDiffDesc" }}' v-model="allSetting.expireDiff" :min="0"></setting-list-item>
|
||||
<setting-list-item type="number" title='{{ i18n "pages.settings.trafficDiff" }}' desc='{{ i18n "pages.settings.trafficDiffDesc" }}' v-model="allSetting.trafficDiff" :min="0"></setting-list-item>
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.timeZone"}}' desc='{{ i18n "pages.settings.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.settings.datepicker"}}'>
|
||||
<template slot="description">{{ i18n "pages.settings.datepickerDescription"}}</template>
|
||||
</a-list-item-meta>
|
||||
</a-col>
|
||||
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<a-select style="width: 100%"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
v-model="datepicker">
|
||||
<a-select-option v-for="item in datepickerList" :value="item.value">
|
||||
<span v-text="item.name"></span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
@@ -166,42 +187,22 @@
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings"}}' style="padding: 20px;">
|
||||
<a-divider>{{ i18n "pages.settings.security.admin"}}</a-divider>
|
||||
<a-form style="padding: 20px;" layout="inline">
|
||||
<table cellpadding="2">
|
||||
<tr>
|
||||
<td>{{ i18n "pages.settings.oldUsername"}}:</td>
|
||||
<td>
|
||||
<a-form-item>
|
||||
<a-input v-model="user.oldUsername" style="width: 200px"></a-input>
|
||||
</a-form-item>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ i18n "pages.settings.currentPassword"}}:</td>
|
||||
<td>
|
||||
<a-form-item>
|
||||
<password-input v-model="user.oldPassword" style="width: 200px"></password-input>
|
||||
</a-form-item>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ i18n "pages.settings.newUsername"}}:</td>
|
||||
<td>
|
||||
<a-form-item>
|
||||
<a-input v-model="user.newUsername" style="width: 200px"></a-input>
|
||||
</a-form-item>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ i18n "pages.settings.newPassword"}}:</td>
|
||||
<td>
|
||||
<a-form-item>
|
||||
<password-input v-model="user.newPassword" style="width: 200px"></password-input>
|
||||
</a-form-item>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
||||
<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-input v-model="user.oldUsername"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.settings.currentPassword"}}'>
|
||||
<password-input v-model="user.oldPassword"></password-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.settings.newUsername"}}'>
|
||||
<a-input v-model="user.newUsername"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.settings.newPassword"}}'>
|
||||
<password-input v-model="user.newPassword"></password-input>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-divider>{{ i18n "pages.settings.security.secret"}}</a-divider>
|
||||
<a-form style="padding: 20px;">
|
||||
@@ -243,6 +244,7 @@
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.tgNotifyBackup" }}' desc='{{ i18n "pages.settings.tgNotifyBackupDesc" }}' v-model="allSetting.tgBotBackup"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.tgNotifyLogin" }}' desc='{{ i18n "pages.settings.tgNotifyLoginDesc" }}' v-model="allSetting.tgBotLoginNotify"></setting-list-item>
|
||||
<setting-list-item type="number" title='{{ i18n "pages.settings.tgNotifyCpu" }}' desc='{{ i18n "pages.settings.tgNotifyCpuDesc" }}' v-model="allSetting.tgCpu" :min="0" :max="100"></setting-list-item>
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.telegramProxy"}}' desc='{{ i18n "pages.settings.telegramProxyDesc"}}' v-model="allSetting.tgBotProxy" placeholder="socks5://user:pass@host:port"></setting-list-item>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
@@ -311,6 +313,7 @@
|
||||
showAlert: false,
|
||||
remarkModels: {i:'Inbound',e:'Email',o:'Other'},
|
||||
remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'],
|
||||
datepickerList: [{name:'Gregorian (Standard)', value: 'gregorian'}, {name:'Jalalian (شمسی)', value: 'jalalian'}],
|
||||
remarkSample: '',
|
||||
get remarkModel() {
|
||||
rm = this.allSetting.remarkModel;
|
||||
@@ -328,6 +331,12 @@
|
||||
this.allSetting.remarkModel = value + this.allSetting.remarkModel.substring(1);
|
||||
this.changeRemarkSample();
|
||||
},
|
||||
get datepicker() {
|
||||
return this.allSetting.datepicker ? this.allSetting.datepicker : 'gregorian';
|
||||
},
|
||||
set datepicker(value) {
|
||||
this.allSetting.datepicker = value;
|
||||
},
|
||||
changeRemarkSample(){
|
||||
sample = []
|
||||
this.remarkModel.forEach(r => sample.push(this.remarkModels[r]));
|
||||
@@ -384,9 +393,7 @@
|
||||
if (msg.success) {
|
||||
this.loading(true);
|
||||
await PromiseUtil.sleep(5000);
|
||||
var { webCertFile, webKeyFile, webDomain: host, webPort: port, webBasePath: base } = this.allSetting;
|
||||
if (host == this.oldAllSetting.webDomain) host = null;
|
||||
if (port == this.oldAllSetting.webPort) port = null;
|
||||
let { webCertFile, webKeyFile, webDomain: host, webPort: port, webBasePath: base } = this.allSetting;
|
||||
const isTLS = webCertFile !== "" || webKeyFile !== "";
|
||||
const url = buildURL({ host, port, isTLS, base, path: "panel/settings" });
|
||||
window.location.replace(url);
|
||||
|
||||
204
web/html/xui/warp_modal.html
Normal file
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('/panel/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('/panel/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('/panel/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('/panel/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}}
|
||||
@@ -6,7 +6,8 @@
|
||||
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.css?{{ .cur_ver }}">
|
||||
<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/javascript.js"></script>
|
||||
<script src="{{ .base_path }}assets/codemirror/jshint.js"></script>
|
||||
@@ -80,17 +81,21 @@
|
||||
</a-space>
|
||||
</a-col>
|
||||
<a-col :xs="24" :sm="16">
|
||||
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200">
|
||||
</a-back-top>
|
||||
<a-alert type="warning" style="float: right; width: fit-content"
|
||||
message='{{ i18n "pages.settings.infoDesc" }}'
|
||||
show-icon
|
||||
>
|
||||
<template>
|
||||
<div>
|
||||
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200">
|
||||
</a-back-top>
|
||||
<a-alert type="warning" style="float: right; width: fit-content"
|
||||
message='{{ i18n "pages.settings.infoDesc" }}'
|
||||
show-icon
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
<a-tabs class="ant-card-dark-box-nohover" default-active-key="tpl-1"
|
||||
@change="(activeKey) => { if(activeKey == 'tpl-4') this.changeCode(); }"
|
||||
@change="(activeKey) => { if(activeKey == 'tpl-advanced') this.changeCode(); }"
|
||||
:class="themeSwitcher.currentTheme">
|
||||
<a-tab-pane key="tpl-1" tab='{{ i18n "pages.xray.basicTemplate"}}'>
|
||||
<a-space direction="horizontal" style="padding: 20px 20px">
|
||||
@@ -119,7 +124,7 @@
|
||||
v-model="freedomStrategy"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
style="width: 100%">
|
||||
<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>
|
||||
</template>
|
||||
</a-col>
|
||||
@@ -157,6 +162,7 @@
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.PrivateIp"}}' desc='{{ i18n "pages.xray.PrivateIpDesc"}}' v-model="privateIpSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.Ads"}}' desc='{{ i18n "pages.xray.AdsDesc"}}' v-model="AdsSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.Family"}}' desc='{{ i18n "pages.xray.FamilyDesc"}}' v-model="familyProtectSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.Security"}}' desc='{{ i18n "pages.xray.SecurityDesc"}}' v-model="SecuritySettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.Speedtest"}}' desc='{{ i18n "pages.xray.SpeedtestDesc"}}' v-model="SpeedTestSettings"></setting-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.xray.blockCountryConfigs"}}'>
|
||||
@@ -174,6 +180,8 @@
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.ChinaDomain"}}' desc='{{ i18n "pages.xray.ChinaDomainDesc"}}' v-model="ChinaDomainSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaIp"}}' desc='{{ i18n "pages.xray.RussiaIpDesc"}}' v-model="RussiaIpSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaDomain"}}' desc='{{ i18n "pages.xray.RussiaDomainDesc"}}' v-model="RussiaDomainSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.VNIp"}}' desc='{{ i18n "pages.xray.VNIpDesc"}}' v-model="VNIpSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.VNDomain"}}' desc='{{ i18n "pages.xray.VNDomainDesc"}}' v-model="VNDomainSettings"></setting-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.xray.directCountryConfigs"}}'>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
@@ -190,6 +198,8 @@
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectChinaDomain"}}' desc='{{ i18n "pages.xray.DirectChinaDomainDesc"}}' v-model="ChinaDomainDirectSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaIp"}}' desc='{{ i18n "pages.xray.DirectRussiaIpDesc"}}' v-model="RussiaIpDirectSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaDomain"}}' desc='{{ i18n "pages.xray.DirectRussiaDomainDesc"}}' v-model="RussiaDomainDirectSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNIp"}}' desc='{{ i18n "pages.xray.DirectVNIpDesc"}}' v-model="VNIpDirectSettings"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNDomain"}}' desc='{{ i18n "pages.xray.DirectVNDomainDesc"}}' v-model="VNDomainDirectSettings"></setting-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.xray.ipv4Configs"}}'>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
@@ -212,10 +222,13 @@
|
||||
</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>
|
||||
</a-tab-pane>
|
||||
@@ -328,79 +341,74 @@
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
<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="addReverse()">{{ i18n "pages.xray.outbound.addReverse" }}</a-button>
|
||||
<a-row>
|
||||
<a-col :sm="24" :md="12">
|
||||
<p style="margin: 10px;">{{ i18n "pages.xray.Outbounds"}}</p>
|
||||
<a-table :columns="outboundColumns" bordered
|
||||
:row-key="r => r.key"
|
||||
:data-source="outboundData"
|
||||
:scroll="isMobile ? {} : { x: 200 }"
|
||||
:pagination="false"
|
||||
:indent-size="0"
|
||||
:style="isMobile ? 'padding: 5px 5px' : 'margin-right: 1px;'">
|
||||
<template slot="action" slot-scope="text, outbound, 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="editOutbound(index)">
|
||||
<a-icon type="edit"></a-icon>
|
||||
{{ i18n "edit" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="deleteOutbound(index)">
|
||||
<span style="color: #FF4D4F">
|
||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||
</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template slot="address" slot-scope="text, outbound, index">
|
||||
<p style="margin: 0 5px;" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
|
||||
</template>
|
||||
<template slot="protocol" slot-scope="text, outbound, index">
|
||||
<a-tag style="margin:0;" color="purple">[[ outbound.protocol ]]</a-tag>
|
||||
<template v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
|
||||
<a-tag style="margin:0;" color="blue">[[ outbound.streamSettings.network ]]</a-tag>
|
||||
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
|
||||
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='reality'" color="green">reality</a-tag>
|
||||
</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-button type="primary" icon="plus" @click="addOutbound()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||
<a-button type="primary" @click="showWarp()" style="margin-bottom: 10px;">WARP</a-button>
|
||||
<a-table :columns="outboundColumns" bordered
|
||||
:row-key="r => r.key"
|
||||
:data-source="outboundData"
|
||||
:scroll="isMobile ? {} : { x: 200 }"
|
||||
:pagination="false"
|
||||
:indent-size="0"
|
||||
:style="isMobile ? 'padding: 5px 5px' : 'margin-right: 1px;'">
|
||||
<template slot="action" slot-scope="text, outbound, 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="editOutbound(index)">
|
||||
<a-icon type="edit"></a-icon>
|
||||
{{ i18n "edit" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="deleteOutbound(index)">
|
||||
<span style="color: #FF4D4F">
|
||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||
</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template slot="address" slot-scope="text, outbound, index">
|
||||
<p style="margin: 0 5px;" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
|
||||
</template>
|
||||
<template slot="protocol" slot-scope="text, outbound, index">
|
||||
<a-tag style="margin:0;" color="purple">[[ outbound.protocol ]]</a-tag>
|
||||
<template v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
|
||||
<a-tag style="margin:0;" color="blue">[[ outbound.streamSettings.network ]]</a-tag>
|
||||
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
|
||||
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='reality'" color="green">reality</a-tag>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</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-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>
|
||||
@@ -422,12 +430,13 @@
|
||||
{{template "ruleModal"}}
|
||||
{{template "outModal"}}
|
||||
{{template "reverseModal"}}
|
||||
{{template "warpModal"}}
|
||||
<script>
|
||||
const rulesColumns = [
|
||||
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
|
||||
{ title: '{{ i18n "pages.xray.rules.source"}}', children: [
|
||||
{ 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: 'L4', dataIndex: 'network', align: 'center', width: 10 },
|
||||
{ title: 'Protocol', dataIndex: 'protocol', align: 'center', width: 10, ellipsis: true },
|
||||
@@ -438,7 +447,7 @@
|
||||
{ title: 'Port', dataIndex: 'port', align: 'center', width: 10, ellipsis: true }]},
|
||||
{ title: '{{ i18n "pages.xray.rules.inbound"}}', children: [
|
||||
{ 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 },
|
||||
];
|
||||
|
||||
@@ -462,6 +471,7 @@
|
||||
{ title: '{{ i18n "pages.xray.outbound.tag"}}', dataIndex: 'tag', align: 'center', width: 50 },
|
||||
{ title: '{{ i18n "pages.xray.outbound.domain"}}', dataIndex: 'domain', align: 'center', width: 50 },
|
||||
];
|
||||
|
||||
const app = new Vue({
|
||||
delimiters: ['[[', ']]'],
|
||||
el: '#app',
|
||||
@@ -506,23 +516,10 @@
|
||||
domainStrategy: "UseIPv4"
|
||||
}
|
||||
},
|
||||
warpSettings: {
|
||||
tag: "WARP",
|
||||
protocol: "socks",
|
||||
settings: {
|
||||
servers: [
|
||||
{
|
||||
address: "127.0.0.1",
|
||||
port: 40000
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
directSettings: {
|
||||
tag: "direct",
|
||||
protocol: "freedom"
|
||||
},
|
||||
outboundDomainStrategies: ["AsIs", "UseIP", "UseIPv4", "UseIPv6"],
|
||||
routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"],
|
||||
settingsData: {
|
||||
protocols: {
|
||||
@@ -533,12 +530,18 @@
|
||||
cn: ["geoip:cn"],
|
||||
ir: ["ext:geoip_IR.dat:ir"],
|
||||
ru: ["geoip:ru"],
|
||||
vn: ["ext:geoip_VN.dat:vn"],
|
||||
},
|
||||
domains: {
|
||||
ads: [
|
||||
"geosite:category-ads-all",
|
||||
"ext:geosite_IR.dat:category-ads-all"
|
||||
],
|
||||
security: [
|
||||
"ext:geosite_IR.dat:malware",
|
||||
"ext:geosite_IR.dat:phishing",
|
||||
"ext:geosite_IR.dat:cryptominers"
|
||||
],
|
||||
speedtest: ["geosite:speedtest"],
|
||||
openai: ["geosite:openai"],
|
||||
google: ["geosite:google"],
|
||||
@@ -555,15 +558,18 @@
|
||||
ir: [
|
||||
"regexp:.*\\.ir$",
|
||||
"regexp:.*\\.xn--mgba3a4f16a$", // .ایران
|
||||
"ext:geosite_IR.dat:ir" // have rules to bypass all .ir domains.
|
||||
"ext:geosite_IR.dat:ir"
|
||||
],
|
||||
vn: [
|
||||
"regexp:.*\\.vn$",
|
||||
"ext:geosite_VN.dat:vn",
|
||||
"ext:geosite_VN.dat:ads"
|
||||
]
|
||||
},
|
||||
familyProtectDNS: {
|
||||
"servers": [
|
||||
"1.1.1.3", // https://developers.cloudflare.com/1.1.1.1/setup/
|
||||
"1.0.0.3",
|
||||
"94.140.14.15", // https://adguard-dns.io/kb/general/dns-providers/
|
||||
"94.140.15.16"
|
||||
"1.0.0.3"
|
||||
],
|
||||
"queryStrategy": "UseIPv4"
|
||||
},
|
||||
@@ -662,13 +668,13 @@
|
||||
}
|
||||
},
|
||||
syncRulesWithOutbound(tag, setting) {
|
||||
const newTemplateSettings = {...this.templateSettings};
|
||||
const newTemplateSettings = this.templateSettings;
|
||||
const haveRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === tag);
|
||||
const outboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.tag === tag);
|
||||
if (!haveRules && outboundIndex >= 0) {
|
||||
newTemplateSettings.outbounds.splice(outboundIndex, 1);
|
||||
if (!haveRules && outboundIndex > 0) {
|
||||
newTemplateSettings.outbounds.splice(outboundIndex);
|
||||
}
|
||||
if (haveRules && outboundIndex === -1) {
|
||||
if (haveRules && outboundIndex < 0) {
|
||||
newTemplateSettings.outbounds.push(setting);
|
||||
}
|
||||
this.templateSettings = newTemplateSettings;
|
||||
@@ -766,6 +772,8 @@
|
||||
break;
|
||||
case Protocols.DNS:
|
||||
return [o.settings.address + ':' + o.settings.port];
|
||||
case Protocols.Wireguard:
|
||||
return o.settings.peers.map(peer => peer.endpoint);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -841,17 +849,27 @@
|
||||
confirm: (reverse, rules) => {
|
||||
reverseModal.loading();
|
||||
if(reverse.tag.length > 0){
|
||||
oldtag = this.reverseData[index].tag;
|
||||
this.deleteReverse(index);
|
||||
oldData = this.reverseData[index];
|
||||
newTemplateSettings = this.templateSettings;
|
||||
if(newTemplateSettings.reverse == undefined) newTemplateSettings.reverse = {};
|
||||
if(newTemplateSettings.reverse[reverse.type+'s'] == undefined) newTemplateSettings.reverse[reverse.type+'s'] = [];
|
||||
newTemplateSettings.reverse[reverse.type+'s'].push({ tag: reverse.tag, domain: reverse.domain });
|
||||
oldReverseIndex = newTemplateSettings.reverse[oldData.type+'s'].findIndex(rs => rs.tag == oldData.tag);
|
||||
oldRuleIndex0 = oldRules.length>0 ? newTemplateSettings.routing.rules.findIndex(r => JSON.stringify(r) == JSON.stringify(oldRules[0])) : -1;
|
||||
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;
|
||||
|
||||
// Adjust Rules
|
||||
newRules = this.templateSettings.routing.rules.filter(r => r.outboundTag != oldtag && (r.inboundTag && !r.inboundTag.includes(oldtag)));
|
||||
newRules.push(...rules)
|
||||
newRules = this.templateSettings.routing.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);
|
||||
}
|
||||
reverseModal.close();
|
||||
@@ -866,10 +884,17 @@
|
||||
realIndex = reverseTypeObj.findIndex(r => r.tag==oldData.tag && r.domain==oldData.domain);
|
||||
newTemplateSettings.reverse[oldData.type+'s'].splice(realIndex,1);
|
||||
|
||||
// delete empty objects
|
||||
if(reverseTypeObj.length == 0) Reflect.deleteProperty(newTemplateSettings.reverse, oldData.type+'s');
|
||||
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;
|
||||
|
||||
this.templateSettings = newTemplateSettings;
|
||||
@@ -914,13 +939,16 @@
|
||||
rules = this.templateSettings.routing.rules;
|
||||
rules.splice(index,1);
|
||||
this.routingRuleSettings = JSON.stringify(rules);
|
||||
},
|
||||
showWarp(){
|
||||
warpModal.show();
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.getXraySetting();
|
||||
await this.getXrayResult();
|
||||
while (true) {
|
||||
await PromiseUtil.sleep(600);
|
||||
await PromiseUtil.sleep(800);
|
||||
this.saveBtnDisable = this.oldXraySetting === this.xraySetting;
|
||||
}
|
||||
},
|
||||
@@ -1088,37 +1116,12 @@
|
||||
},
|
||||
warpDomains: {
|
||||
get: function () {
|
||||
return this.templateRuleGetter({ outboundTag: "WARP", property: "domain" });
|
||||
return this.templateRuleGetter({ outboundTag: "warp", property: "domain" });
|
||||
},
|
||||
set: function (newValue) {
|
||||
this.templateRuleSetter({ outboundTag: "WARP", property: "domain", data: newValue });
|
||||
this.syncRulesWithOutbound("WARP", this.warpSettings);
|
||||
this.templateRuleSetter({ outboundTag: "warp", property: "domain", data: newValue });
|
||||
}
|
||||
},
|
||||
manualBlockedIPs: {
|
||||
get: function () { return JSON.stringify(this.blockedIPs, null, 2); },
|
||||
set: debounce(function (value) { this.blockedIPs = JSON.parse(value); }, 1000)
|
||||
},
|
||||
manualBlockedDomains: {
|
||||
get: function () { return JSON.stringify(this.blockedDomains, null, 2); },
|
||||
set: debounce(function (value) { this.blockedDomains = JSON.parse(value); }, 1000)
|
||||
},
|
||||
manualDirectIPs: {
|
||||
get: function () { return JSON.stringify(this.directIPs, null, 2); },
|
||||
set: debounce(function (value) { this.directIPs = JSON.parse(value); }, 1000)
|
||||
},
|
||||
manualDirectDomains: {
|
||||
get: function () { return JSON.stringify(this.directDomains, null, 2); },
|
||||
set: debounce(function (value) { this.directDomains = JSON.parse(value); }, 1000)
|
||||
},
|
||||
manualIPv4Domains: {
|
||||
get: function () { return JSON.stringify(this.ipv4Domains, null, 2); },
|
||||
set: debounce(function (value) { this.ipv4Domains = JSON.parse(value); }, 1000)
|
||||
},
|
||||
manualWARPDomains: {
|
||||
get: function () { return JSON.stringify(this.warpDomains, null, 2); },
|
||||
set: debounce(function (value) { this.warpDomains = JSON.parse(value); }, 1000)
|
||||
},
|
||||
torrentSettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols);
|
||||
@@ -1155,6 +1158,18 @@
|
||||
}
|
||||
},
|
||||
},
|
||||
SecuritySettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.domains.security, this.blockedDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.security];
|
||||
} else {
|
||||
this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.security.includes(data));
|
||||
}
|
||||
},
|
||||
},
|
||||
SpeedTestSettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.domains.speedtest, this.blockedDomains);
|
||||
@@ -1278,6 +1293,30 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
VNIpSettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.ips.vn, this.blockedIPs);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.vn];
|
||||
} else {
|
||||
this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.vn.includes(data));
|
||||
}
|
||||
}
|
||||
},
|
||||
VNDomainSettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.domains.vn, this.blockedDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.vn];
|
||||
} else {
|
||||
this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.vn.includes(data));
|
||||
}
|
||||
}
|
||||
},
|
||||
IRIpDirectSettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.ips.ir, this.directIPs);
|
||||
@@ -1350,6 +1389,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
VNIpDirectSettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.ips.vn, this.directIPs);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
this.directIPs = [...this.directIPs, ...this.settingsData.ips.vn];
|
||||
} else {
|
||||
this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.vn.includes(data));
|
||||
}
|
||||
}
|
||||
},
|
||||
VNDomainDirectSettings: {
|
||||
get: function () {
|
||||
return doAllItemsExist(this.settingsData.domains.vn, this.directDomains);
|
||||
},
|
||||
set: function (newValue) {
|
||||
if (newValue) {
|
||||
this.directDomains = [...this.directDomains, ...this.settingsData.domains.vn];
|
||||
} else {
|
||||
this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.vn.includes(data));
|
||||
}
|
||||
}
|
||||
},
|
||||
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);
|
||||
|
||||
@@ -32,12 +32,12 @@
|
||||
</a-form-item>
|
||||
<a-form-item label='Network'>
|
||||
<a-select v-model="ruleModal.rule.network" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in ['','tcp','tdp','tcp,udp']" :value="x">[[ x ]]</a-select-option>
|
||||
<a-select-option v-for="x in ['','TCP','UDP','TCP,UDP']" :value="x">[[ x ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Protocol'>
|
||||
<a-select v-model="ruleModal.rule.protocol" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in ['','http','tls','bittorrent']" :value="x">[[ x ]]</a-select-option>
|
||||
<a-select v-model="ruleModal.rule.protocol" mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in ['http','tls','bittorrent']" :value="x">[[ x ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Attributes'>
|
||||
@@ -75,6 +75,17 @@
|
||||
</template>
|
||||
<a-input v-model.trim="ruleModal.rule.domain"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.xray.rules.useComma" }}</span>
|
||||
</template>
|
||||
User <a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="ruleModal.rule.user"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
|
||||
@@ -22,8 +22,11 @@ var job *CheckClientIpJob
|
||||
var disAllowedIps []string
|
||||
var ipFiles = []string{
|
||||
xray.GetIPLimitLogPath(),
|
||||
xray.GetIPLimitPrevLogPath(),
|
||||
xray.GetIPLimitBannedLogPath(),
|
||||
xray.GetIPLimitBannedPrevLogPath(),
|
||||
xray.GetAccessPersistentLogPath(),
|
||||
xray.GetAccessPersistentPrevLogPath(),
|
||||
}
|
||||
|
||||
func NewCheckClientIpJob() *CheckClientIpJob {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package job
|
||||
|
||||
import "x-ui/web/service"
|
||||
import (
|
||||
"x-ui/logger"
|
||||
"x-ui/web/service"
|
||||
)
|
||||
|
||||
type CheckXrayRunningJob struct {
|
||||
xrayService service.XrayService
|
||||
@@ -15,11 +18,15 @@ func NewCheckXrayRunningJob() *CheckXrayRunningJob {
|
||||
func (j *CheckXrayRunningJob) Run() {
|
||||
if j.xrayService.IsXrayRunning() {
|
||||
j.checkTime = 0
|
||||
return
|
||||
} else {
|
||||
j.checkTime++
|
||||
//only restart if it's down 2 times in a row
|
||||
if j.checkTime > 1 {
|
||||
err := j.xrayService.RestartXray(false)
|
||||
j.checkTime = 0
|
||||
if err != nil {
|
||||
logger.Error("Restart xray failed:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
j.checkTime++
|
||||
if j.checkTime < 2 {
|
||||
return
|
||||
}
|
||||
j.xrayService.SetToNeedRestart()
|
||||
}
|
||||
|
||||
@@ -15,10 +15,37 @@ func NewClearLogsJob() *ClearLogsJob {
|
||||
// Here Run is an interface method of the Job interface
|
||||
func (j *ClearLogsJob) Run() {
|
||||
logFiles := []string{xray.GetIPLimitLogPath(), xray.GetIPLimitBannedLogPath(), xray.GetAccessPersistentLogPath()}
|
||||
logFilesPrev := []string{xray.GetIPLimitPrevLogPath(), xray.GetIPLimitBannedPrevLogPath(), xray.GetAccessPersistentPrevLogPath()}
|
||||
|
||||
// clear log files
|
||||
// clear old previous logs
|
||||
for i := 0; i < len(logFilesPrev); i++ {
|
||||
if err := os.Truncate(logFilesPrev[i], 0); err != nil {
|
||||
logger.Warning("clear logs job err:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// clear log files and copy to previous logs
|
||||
for i := 0; i < len(logFiles); i++ {
|
||||
if err := os.Truncate(logFiles[i], 0); err != nil {
|
||||
|
||||
// copy to previous logs
|
||||
logFilePrev, err := os.OpenFile(logFilesPrev[i], os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
|
||||
if err != nil {
|
||||
logger.Warning("clear logs job err:", err)
|
||||
}
|
||||
|
||||
logFile, err := os.ReadFile(logFiles[i])
|
||||
if err != nil {
|
||||
logger.Warning("clear logs job err:", err)
|
||||
}
|
||||
|
||||
_, err = logFilePrev.Write(logFile)
|
||||
if err != nil {
|
||||
logger.Warning("clear logs job err:", err)
|
||||
}
|
||||
defer logFilePrev.Close()
|
||||
|
||||
err = os.Truncate(logFiles[i], 0)
|
||||
if err != nil {
|
||||
logger.Warning("clear logs job err:", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,9 +38,25 @@ func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
|
||||
return inbounds, nil
|
||||
}
|
||||
|
||||
func (s *InboundService) checkPortExist(port int, ignoreId int) (bool, error) {
|
||||
func (s *InboundService) checkPortExist(listen string, port int, ignoreId int) (bool, error) {
|
||||
db := database.GetDB()
|
||||
db = db.Model(model.Inbound{}).Where("port = ?", port)
|
||||
if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" {
|
||||
db = db.Model(model.Inbound{}).Where("port = ?", port)
|
||||
} else {
|
||||
db = db.Model(model.Inbound{}).
|
||||
Where("port = ?", port).
|
||||
Where(
|
||||
db.Model(model.Inbound{}).Where(
|
||||
"listen = ?", listen,
|
||||
).Or(
|
||||
"listen = \"\"",
|
||||
).Or(
|
||||
"listen = \"0.0.0.0\"",
|
||||
).Or(
|
||||
"listen = \"::\"",
|
||||
).Or(
|
||||
"listen = \"::0\""))
|
||||
}
|
||||
if ignoreId > 0 {
|
||||
db = db.Where("id != ?", ignoreId)
|
||||
}
|
||||
@@ -135,7 +151,7 @@ func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbound) (stri
|
||||
}
|
||||
|
||||
func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
|
||||
exist, err := s.checkPortExist(inbound.Port, 0)
|
||||
exist, err := s.checkPortExist(inbound.Listen, inbound.Port, 0)
|
||||
if err != nil {
|
||||
return inbound, false, err
|
||||
}
|
||||
@@ -252,7 +268,7 @@ func (s *InboundService) GetInbound(id int) (*model.Inbound, error) {
|
||||
}
|
||||
|
||||
func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
|
||||
exist, err := s.checkPortExist(inbound.Port, inbound.Id)
|
||||
exist, err := s.checkPortExist(inbound.Listen, inbound.Port, inbound.Id)
|
||||
if err != nil {
|
||||
return inbound, false, err
|
||||
}
|
||||
@@ -295,7 +311,12 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
|
||||
oldInbound.Settings = inbound.Settings
|
||||
oldInbound.StreamSettings = inbound.StreamSettings
|
||||
oldInbound.Sniffing = inbound.Sniffing
|
||||
oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
|
||||
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
|
||||
oldInbound.Tag = fmt.Sprintf("inbound-0.0.0.0:%v", inbound.Port)
|
||||
} else {
|
||||
oldInbound.Tag = fmt.Sprintf("inbound-%v:%v", inbound.Listen, inbound.Port)
|
||||
}
|
||||
|
||||
|
||||
needRestart := false
|
||||
s.xrayApi.Init(p.GetAPIPort())
|
||||
@@ -1146,6 +1167,8 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId string) err
|
||||
if oldClient.Email == clientEmail {
|
||||
if inbound.Protocol == "trojan" {
|
||||
clientId = oldClient.Password
|
||||
} else if inbound.Protocol == "shadowsocks" {
|
||||
clientId = oldClient.Email
|
||||
} else {
|
||||
clientId = oldClient.ID
|
||||
}
|
||||
@@ -1184,6 +1207,32 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InboundService) checkIsEnabledByEmail(clientEmail string) (bool, error) {
|
||||
_, inbound, err := s.GetClientInboundByEmail(clientEmail)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if inbound == nil {
|
||||
return false, common.NewError("Inbound Not Found For Email:", clientEmail)
|
||||
}
|
||||
|
||||
clients, err := s.GetClients(inbound)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
isEnable := false
|
||||
|
||||
for _, client := range clients {
|
||||
if client.Email == clientEmail {
|
||||
isEnable = client.Enable
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return isEnable, err
|
||||
}
|
||||
|
||||
func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, error) {
|
||||
_, inbound, err := s.GetClientInboundByEmail(clientEmail)
|
||||
if err != nil {
|
||||
@@ -1205,6 +1254,8 @@ func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, er
|
||||
if oldClient.Email == clientEmail {
|
||||
if inbound.Protocol == "trojan" {
|
||||
clientId = oldClient.Password
|
||||
} else if inbound.Protocol == "shadowsocks" {
|
||||
clientId = oldClient.Email
|
||||
} else {
|
||||
clientId = oldClient.ID
|
||||
}
|
||||
@@ -1266,6 +1317,8 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int
|
||||
if oldClient.Email == clientEmail {
|
||||
if inbound.Protocol == "trojan" {
|
||||
clientId = oldClient.Password
|
||||
} else if inbound.Protocol == "shadowsocks" {
|
||||
clientId = oldClient.Email
|
||||
} else {
|
||||
clientId = oldClient.ID
|
||||
}
|
||||
@@ -1324,6 +1377,8 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry
|
||||
if oldClient.Email == clientEmail {
|
||||
if inbound.Protocol == "trojan" {
|
||||
clientId = oldClient.Password
|
||||
} else if inbound.Protocol == "shadowsocks" {
|
||||
clientId = oldClient.Email
|
||||
} else {
|
||||
clientId = oldClient.ID
|
||||
}
|
||||
@@ -1385,6 +1440,8 @@ func (s *InboundService) ResetClientTrafficLimitByEmail(clientEmail string, tota
|
||||
if oldClient.Email == clientEmail {
|
||||
if inbound.Protocol == "trojan" {
|
||||
clientId = oldClient.Password
|
||||
} else if inbound.Protocol == "shadowsocks" {
|
||||
clientId = oldClient.Email
|
||||
} else {
|
||||
clientId = oldClient.ID
|
||||
}
|
||||
@@ -1613,10 +1670,10 @@ func (s *InboundService) DelDepletedClients(id int) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTraffic, error) {
|
||||
func (s *InboundService) GetClientTrafficTgBot(tgId string) ([]*xray.ClientTraffic, error) {
|
||||
db := database.GetDB()
|
||||
var inbounds []*model.Inbound
|
||||
err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": "%s"%%`, tguname)).Find(&inbounds).Error
|
||||
err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": "%s"%%`, tgId)).Find(&inbounds).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1627,7 +1684,7 @@ func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTr
|
||||
logger.Error("Unable to get clients from inbound")
|
||||
}
|
||||
for _, client := range clients {
|
||||
if client.TgID == tguname {
|
||||
if client.TgID == tgId {
|
||||
emails = append(emails, client.Email)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ var defaultValueMap = map[string]string{
|
||||
"timeLocation": "Asia/Tehran",
|
||||
"tgBotEnable": "false",
|
||||
"tgBotToken": "",
|
||||
"tgBotProxy": "",
|
||||
"tgBotChatId": "",
|
||||
"tgRunTime": "@daily",
|
||||
"tgBotBackup": "false",
|
||||
@@ -56,6 +57,8 @@ var defaultValueMap = map[string]string{
|
||||
"subEncrypt": "true",
|
||||
"subShowInfo": "true",
|
||||
"subURI": "",
|
||||
"datepicker": "gregorian",
|
||||
"warp": "",
|
||||
}
|
||||
|
||||
type SettingService struct {
|
||||
@@ -244,6 +247,14 @@ func (s *SettingService) SetTgBotToken(token string) error {
|
||||
return s.setString("tgBotToken", token)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetTgBotProxy() (string, error) {
|
||||
return s.getString("tgBotProxy")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetTgBotProxy(token string) error {
|
||||
return s.setString("tgBotProxy", token)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetTgBotChatId() (string, error) {
|
||||
return s.getString("tgBotChatId")
|
||||
}
|
||||
@@ -413,12 +424,23 @@ func (s *SettingService) GetSubShowInfo() (bool, error) {
|
||||
return s.getBool("subShowInfo")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetPageSize() (int, error) {
|
||||
return s.getInt("pageSize")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubURI() (string, error) {
|
||||
return s.getString("subURI")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetPageSize() (int, error) {
|
||||
return s.getInt("pageSize")
|
||||
func (s *SettingService) GetDatepicker() (string, error) {
|
||||
return s.getString("datepicker")
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -463,6 +485,7 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) {
|
||||
"subEnable": func() (interface{}, error) { return s.GetSubEnable() },
|
||||
"subURI": func() (interface{}, error) { return s.GetSubURI() },
|
||||
"remarkModel": func() (interface{}, error) { return s.GetRemarkModel() },
|
||||
"datepicker": func() (interface{}, error) { return s.GetDatepicker() },
|
||||
}
|
||||
|
||||
result := make(map[string]interface{})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -185,7 +185,7 @@ func (s *XrayService) RestartXray(isForce bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if p != nil && p.IsRunning() {
|
||||
if s.IsXrayRunning() {
|
||||
if !isForce && p.GetConfig().Equals(xrayConfig) {
|
||||
logger.Debug("It does not need to restart xray")
|
||||
return nil
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
"x-ui/util/common"
|
||||
"x-ui/xray"
|
||||
)
|
||||
@@ -26,3 +31,142 @@ func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error {
|
||||
}
|
||||
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"
|
||||
"password" = "Password"
|
||||
"login" = "Login"
|
||||
"login" = "Log In"
|
||||
"confirm" = "Confirm"
|
||||
"cancel" = "Cancel"
|
||||
"close" = "Close"
|
||||
@@ -8,7 +8,7 @@
|
||||
"copied" = "Copied"
|
||||
"download" = "Download"
|
||||
"remark" = "Remark"
|
||||
"enable" = "Enable"
|
||||
"enable" = "Enabled"
|
||||
"protocol" = "Protocol"
|
||||
"search" = "Search"
|
||||
"filter" = "Filter"
|
||||
@@ -26,13 +26,13 @@
|
||||
"edit" = "Edit"
|
||||
"delete" = "Delete"
|
||||
"reset" = "Reset"
|
||||
"copySuccess" = "Copied successfully"
|
||||
"copySuccess" = "Copied Successful"
|
||||
"sure" = "Sure"
|
||||
"encryption" = "Encryption"
|
||||
"transmission" = "Transmission"
|
||||
"host" = "Host"
|
||||
"path" = "Path"
|
||||
"camouflage" = "Camouflage"
|
||||
"camouflage" = "Obfuscation"
|
||||
"status" = "Status"
|
||||
"enabled" = "Enabled"
|
||||
"disabled" = "Disabled"
|
||||
@@ -40,12 +40,12 @@
|
||||
"depletingSoon" = "Depleting"
|
||||
"offline" = "Offline"
|
||||
"online" = "Online"
|
||||
"domainName" = "Domain name"
|
||||
"monitor" = "Listening IP"
|
||||
"domainName" = "Domain Name"
|
||||
"monitor" = "Listen IP"
|
||||
"certificate" = "Certificate"
|
||||
"fail" = "Fail"
|
||||
"success" = "Success"
|
||||
"getVersion" = "Get version"
|
||||
"fail" = " Failed"
|
||||
"success" = " Successful"
|
||||
"getVersion" = "Get Version"
|
||||
"install" = "Install"
|
||||
"clients" = "Clients"
|
||||
"usage" = "Usage"
|
||||
@@ -54,69 +54,69 @@
|
||||
"security" = "Security"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "System Status"
|
||||
"inbounds" = "Inbounds"
|
||||
"settings" = "Panel Settings"
|
||||
"xray" = "Xray Settings"
|
||||
"logout" = "Logout"
|
||||
"link" = "Other"
|
||||
"dashboard" = "OVERVIEW"
|
||||
"inbounds" = "INBOUNDS"
|
||||
"settings" = "PANEL SETTINGS"
|
||||
"xray" = "XRAY CONFIGS"
|
||||
"logout" = "LOG OUT"
|
||||
"link" = "Manage"
|
||||
|
||||
[pages.login]
|
||||
"title" = "Login"
|
||||
"loginAgain" = "The login time limit has expired. Please log in again."
|
||||
"title" = "Welcome"
|
||||
"loginAgain" = "Your session has expired, please log in again"
|
||||
|
||||
[pages.login.toasts]
|
||||
"invalidFormData" = "Input data format is invalid."
|
||||
"emptyUsername" = "Please enter username."
|
||||
"emptyPassword" = "Please enter password."
|
||||
"invalidFormData" = "The Input data format is invalid."
|
||||
"emptyUsername" = "Username is required"
|
||||
"emptyPassword" = "Password is required"
|
||||
"wrongUsernameOrPassword" = "Invalid username or password."
|
||||
"successLogin" = "Login"
|
||||
|
||||
[pages.index]
|
||||
"title" = "System Status"
|
||||
"memory" = "Memory"
|
||||
"hard" = "Hard Disk"
|
||||
"title" = "Overview"
|
||||
"memory" = "RAM"
|
||||
"hard" = "Disk"
|
||||
"xrayStatus" = "Status"
|
||||
"stopXray" = "Stop"
|
||||
"restartXray" = "Restart"
|
||||
"xraySwitch" = "SwitchV"
|
||||
"xraySwitch" = "Version"
|
||||
"xraySwitchClick" = "Choose the version you want to switch to."
|
||||
"xraySwitchClickDesk" = "Choose wisely, as older versions may not be compatible with current configurations."
|
||||
"xraySwitchClickDesk" = "Choose carefully, as older versions may not be compatible with current configurations."
|
||||
"operationHours" = "Uptime"
|
||||
"systemLoad" = "System Load"
|
||||
"systemLoadDesc" = "system load average for the past 1, 5, and 15 minutes"
|
||||
"connectionTcpCountDesc" = "Total TCP connections across all network cards."
|
||||
"connectionUdpCountDesc" = "Total UDP connections across all network cards."
|
||||
"connectionCount" = "Number of Connections"
|
||||
"upSpeed" = "Total upload speed for all network cards."
|
||||
"downSpeed" = "Total download speed for all network cards."
|
||||
"totalSent" = "Total upload traffic of all network cards since system startup."
|
||||
"totalReceive" = "Total download data across all network cards since system startup."
|
||||
"xraySwitchVersionDialog" = "Switch Xray Version"
|
||||
"xraySwitchVersionDialogDesc" = "Are you sure you want to switch the Xray version to"
|
||||
"dontRefresh" = "Installation is in progress, please do not refresh this page."
|
||||
"systemLoadDesc" = "System load average for the past 1, 5, and 15 minutes"
|
||||
"connectionTcpCountDesc" = "Total TCP connections across the system"
|
||||
"connectionUdpCountDesc" = "Total UDP connections across the system"
|
||||
"connectionCount" = "Connection Stats"
|
||||
"upSpeed" = "Overall upload speed across the system"
|
||||
"downSpeed" = "Overall download speed across the system"
|
||||
"totalSent" = "Total data sent across the system since OS startup"
|
||||
"totalReceive" = "Total data received across the system since OS startup"
|
||||
"xraySwitchVersionDialog" = "Change Xray Version"
|
||||
"xraySwitchVersionDialogDesc" = "Are you sure you want to change the Xray version to"
|
||||
"dontRefresh" = "Installation is in progress, please do not refresh this page"
|
||||
"logs" = "Logs"
|
||||
"config" = "Config"
|
||||
"backup" = "Backup & Restore"
|
||||
"backupTitle" = "Backup & Restore Database"
|
||||
"backupDescription" = "Remember to backup before importing a new database."
|
||||
"exportDatabase" = "Download Database"
|
||||
"importDatabase" = "Upload Database"
|
||||
"backupTitle" = "Database Backup & Restore"
|
||||
"backupDescription" = "It is recommended to make a backup before restoring a database."
|
||||
"exportDatabase" = "Back Up"
|
||||
"importDatabase" = "Restore"
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "Inbounds"
|
||||
"totalDownUp" = "Total Uploads/Downloads"
|
||||
"totalDownUp" = "Total Sent/Received"
|
||||
"totalUsage" = "Total Usage"
|
||||
"inboundCount" = "Number of Inbounds"
|
||||
"inboundCount" = "Total Inbounds"
|
||||
"operate" = "Menu"
|
||||
"enable" = "Enable"
|
||||
"enable" = "Enabled"
|
||||
"remark" = "Remark"
|
||||
"protocol" = "Protocol"
|
||||
"port" = "Port"
|
||||
"traffic" = "Traffic"
|
||||
"details" = "Details"
|
||||
"transportConfig" = "Transport"
|
||||
"expireDate" = "Expire Date"
|
||||
"expireDate" = "Duration"
|
||||
"resetTraffic" = "Reset Traffic"
|
||||
"addInbound" = "Add Inbound"
|
||||
"generalActions" = "General Actions"
|
||||
@@ -124,20 +124,20 @@
|
||||
"update" = "Update"
|
||||
"modifyInbound" = "Modify Inbound"
|
||||
"deleteInbound" = "Delete Inbound"
|
||||
"deleteInboundContent" = "Confirm deletion of inbound?"
|
||||
"deleteInboundContent" = "Are you sure you want to delete inbound?"
|
||||
"deleteClient" = "Delete Client"
|
||||
"deleteClientContent" = "Are you sure you want to delete client?"
|
||||
"resetTrafficContent" = "Confirm traffic reset?"
|
||||
"copyLink" = "Copy Link"
|
||||
"resetTrafficContent" = "Are you sure you want to reset traffic?"
|
||||
"copyLink" = "Copy URL"
|
||||
"address" = "Address"
|
||||
"network" = "Network"
|
||||
"destinationPort" = "Destination Port"
|
||||
"targetAddress" = "Target Address"
|
||||
"monitorDesc" = "Leave blank by default"
|
||||
"meansNoLimit" = "Means No Limit"
|
||||
"monitorDesc" = "Leave blank to listen on all IPs"
|
||||
"meansNoLimit" = " = Unlimited. (unit: GB)"
|
||||
"totalFlow" = "Total Flow"
|
||||
"leaveBlankToNeverExpire" = "Leave Blank to Never Expire"
|
||||
"noRecommendKeepDefault" = "No special requirements to maintain default settings"
|
||||
"leaveBlankToNeverExpire" = "Leave blank to never expire"
|
||||
"noRecommendKeepDefault" = "It is recommended to keep the default"
|
||||
"certificatePath" = "File Path"
|
||||
"certificateContent" = "File Content"
|
||||
"publicKeyPath" = "Public Key Path"
|
||||
@@ -146,47 +146,47 @@
|
||||
"keyContent" = "Private Key Content"
|
||||
"clickOnQRcode" = "Click on QR Code to Copy"
|
||||
"client" = "Client"
|
||||
"export" = "Export Links"
|
||||
"export" = "Export All URLs"
|
||||
"clone" = "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 of this inbound, except Port, Listening IP, and Clients, will be applied to the clone."
|
||||
"cloneInboundOk" = "Clone"
|
||||
"resetAllTraffic" = "Reset All Inbounds Traffic"
|
||||
"resetAllTrafficTitle" = "Reset all inbounds traffic"
|
||||
"resetAllTrafficContent" = "Are you sure you want to reset all inbounds traffic?"
|
||||
"resetAllTrafficTitle" = "Reset All Inbounds Traffic"
|
||||
"resetAllTrafficContent" = "Are you sure you want to reset the traffic of all inbounds?"
|
||||
"resetInboundClientTraffics" = "Reset Clients Traffic"
|
||||
"resetInboundClientTrafficTitle" = "Reset all client traffic"
|
||||
"resetInboundClientTrafficContent" = "Are you sure you want to reset all traffic for this inbound's clients?"
|
||||
"resetInboundClientTrafficTitle" = "Reset Clients Traffic"
|
||||
"resetInboundClientTrafficContent" = "Are you sure you want to reset the traffic of this inbound's clients?"
|
||||
"resetAllClientTraffics" = "Reset All Clients Traffic"
|
||||
"resetAllClientTrafficTitle" = "Reset all clients traffic"
|
||||
"resetAllClientTrafficContent" = "Are you sure you want to reset all traffics for all clients?"
|
||||
"resetAllClientTrafficTitle" = "Reset All Clients Traffic"
|
||||
"resetAllClientTrafficContent" = "Are you sure you want to reset the traffic of all clients?"
|
||||
"delDepletedClients" = "Delete Depleted Clients"
|
||||
"delDepletedClientsTitle" = "Delete depleted clients"
|
||||
"delDepletedClientsContent" = "Are you sure you want to delete all depleted clients?"
|
||||
"delDepletedClientsTitle" = "Delete Depleted Clients"
|
||||
"delDepletedClientsContent" = "Are you sure you want to delete all the depleted clients?"
|
||||
"email" = "Email"
|
||||
"emailDesc" = "Please provide a unique email address."
|
||||
"IPLimit" = "IP Limit"
|
||||
"IPLimitDesc" = "Disable inbound if the count exceeds the entered value (enter 0 to disable IP limit)."
|
||||
"IPLimitDesc" = "Disables inbound if the count exceeds the set value. (0 = disable)"
|
||||
"IPLimitlog" = "IP Log"
|
||||
"IPLimitlogDesc" = "IPs history log (before enabling inbound after it has been disabled by IP limit, you should clear the log)."
|
||||
"IPLimitlogDesc" = "The IPs history log. (to enable inbound after disabling, clear the log)"
|
||||
"IPLimitlogclear" = "Clear The Log"
|
||||
"setDefaultCert" = "Set cert from panel"
|
||||
"xtlsDesc" = "Xray core needs to be 1.7.5"
|
||||
"realityDesc" = "Xray core needs to be 1.8.0 or higher."
|
||||
"telegramDesc" = "use Telegram ID without @ or chat IDs ( you can get it here @userinfobot or use '/id' command in bot )"
|
||||
"subscriptionDesc" = "you can find your sub link on Details, also you can use the same name for several configurations"
|
||||
"setDefaultCert" = "Set Cert from Panel"
|
||||
"xtlsDesc" = "Xray must be v1.7.5"
|
||||
"realityDesc" = "Xray must be v1.8.0+"
|
||||
"telegramDesc" = "Please provide Telegram or chat ID(s) without using the '@'. (get it here @userinfobot) or (use '/id' command in the bot)"
|
||||
"subscriptionDesc" = "To find your subscription URL, navigate to the 'Details'. Additionally, you can use the same name for several clients."
|
||||
"info" = "Info"
|
||||
"same" = "Same"
|
||||
"inboundData" = "Inbound's data"
|
||||
"copyToClipboard" = "Copy to clipboard"
|
||||
"inboundData" = "Inbound's Data"
|
||||
"copyToClipboard" = "Copy to Clipboard"
|
||||
"import" = "Import"
|
||||
"importInbound" = "Import an inbound"
|
||||
"importInbound" = "Import an Inbound"
|
||||
|
||||
[pages.client]
|
||||
"add" = "Add Client"
|
||||
"edit" = "Edit Client"
|
||||
"submitAdd" = "Add Client"
|
||||
"submitEdit" = "Save changes"
|
||||
"submitEdit" = "Save Changes"
|
||||
"clientCount" = "Number of Clients"
|
||||
"bulk" = "Add Bulk"
|
||||
"method" = "Method"
|
||||
@@ -194,186 +194,201 @@
|
||||
"last" = "Last"
|
||||
"prefix" = "Prefix"
|
||||
"postfix" = "Postfix"
|
||||
"delayedStart" = "Start after first use"
|
||||
"expireDays" = "Expire days"
|
||||
"days" = "day(s)"
|
||||
"renew" = "Auto renew"
|
||||
"renewDesc" = "Auto renew days after expiration. 0 = disable"
|
||||
"delayedStart" = "Start on Initial Use"
|
||||
"expireDays" = "Duration"
|
||||
"days" = "Day(s)"
|
||||
"renew" = "Auto Renew"
|
||||
"renewDesc" = "Auto-renewal after expiration. (0 = disable)(unit: day)"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "Obtain"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "Request header"
|
||||
"request" = "Request"
|
||||
"response" = "Response"
|
||||
"name" = "Name"
|
||||
"value" = "Value"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "Request version"
|
||||
"requestMethod" = "Request method"
|
||||
"requestPath" = "Request path"
|
||||
"responseVersion" = "Response version"
|
||||
"responseStatus" = "Response status"
|
||||
"responseStatusDescription" = "Response status description"
|
||||
"responseHeader" = "Response header"
|
||||
"version" = "Version"
|
||||
"method" = "Method"
|
||||
"path" = "Path"
|
||||
"status" = "Status"
|
||||
"statusDescription" = "Status Desc"
|
||||
"requestHeader" = "Request Header"
|
||||
"responseHeader" = "Response Header"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
"encryption" = "Encryption"
|
||||
|
||||
[pages.settings]
|
||||
"title" = "Settings"
|
||||
"title" = "Panel Settings"
|
||||
"save" = "Save"
|
||||
"infoDesc" = "Every change made here needs to be saved. Please restart the panel to apply changes."
|
||||
"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."
|
||||
"restartPanel" = "Restart Panel"
|
||||
"restartPanelDesc" = "Are you sure you want to restart the panel? If you cannot access the panel after restarting, please view the panel log info on the server."
|
||||
"actions" = "Actions"
|
||||
"resetDefaultConfig" = "Reset to Default Configuration"
|
||||
"panelSettings" = "Panel Settings"
|
||||
"securitySettings" = "Security Settings"
|
||||
"TGBotSettings" = "Telegram Bot Settings"
|
||||
"panelListeningIP" = "Panel Listening IP"
|
||||
"panelListeningIPDesc" = "Leave blank by default to monitor all IPs."
|
||||
"panelListeningDomain" = "Panel Listening Domain"
|
||||
"panelListeningDomainDesc" = "Leave blank by default to monitor all domains and IPs"
|
||||
"panelPort" = "Panel Port"
|
||||
"panelPortDesc" = "The port used to display this panel"
|
||||
"publicKeyPath" = "Panel Certificate Public Key File Path"
|
||||
"publicKeyPathDesc" = "Fill in an absolute path starting with."
|
||||
"privateKeyPath" = "Panel Certificate Private Key File Path"
|
||||
"privateKeyPathDesc" = "Fill in an absolute path starting with."
|
||||
"panelUrlPath" = "Panel URL Root Path"
|
||||
"panelUrlPathDesc" = "Must start with '/' and end with."
|
||||
"pageSize" = "Pagination size"
|
||||
"pageSizeDesc" = "Define page size for inbounds table. Set 0 to disable"
|
||||
"remarkModel" = "Remark Model and Seperation charachter"
|
||||
"sampleRemark" = "Sample remark"
|
||||
"resetDefaultConfig" = "Reset to Default"
|
||||
"panelSettings" = "General"
|
||||
"securitySettings" = "Authentication"
|
||||
"TGBotSettings" = "Telegram Bot"
|
||||
"panelListeningIP" = "Listen IP"
|
||||
"panelListeningIPDesc" = "The IP address for the web panel. (leave blank to listen on all IPs)"
|
||||
"panelListeningDomain" = "Listen Domain"
|
||||
"panelListeningDomainDesc" = "The domain name for the web panel. (leave blank to listen on all domains and IPs)"
|
||||
"panelPort" = "Listen Port"
|
||||
"panelPortDesc" = "The port number for the web panel. (must be an unused port)"
|
||||
"publicKeyPath" = "Public Key Path"
|
||||
"publicKeyPathDesc" = "The public key file path for the web panel. (begins with ‘/‘)"
|
||||
"privateKeyPath" = "Private Key Path"
|
||||
"privateKeyPathDesc" = "The private key file path for the web panel. (begins with ‘/‘)"
|
||||
"panelUrlPath" = "URI Path"
|
||||
"panelUrlPathDesc" = "The URI path for the web panel. (begins with ‘/‘ and concludes with ‘/‘)"
|
||||
"pageSize" = "Pagination Size"
|
||||
"pageSizeDesc" = "Define page size for inbounds table. (0 = disable)"
|
||||
"remarkModel" = "Remark Model & Separation Character"
|
||||
"datepicker" = "Calendar Type"
|
||||
"datepickerDescription" = "Scheduled tasks will run based on this calendar."
|
||||
"sampleRemark" = "Sample Remark"
|
||||
"oldUsername" = "Current Username"
|
||||
"currentPassword" = "Current Password"
|
||||
"newUsername" = "New Username"
|
||||
"newPassword" = "New Password"
|
||||
"telegramBotEnable" = "Enable Telegram bot"
|
||||
"telegramBotEnableDesc" = "Connect to the features of this panel through the Telegram bot"
|
||||
"telegramBotEnable" = "Enable Telegram Bot"
|
||||
"telegramBotEnableDesc" = "Enables the Telegram bot."
|
||||
"telegramToken" = "Telegram Token"
|
||||
"telegramTokenDesc" = "You must get the token from the manager of Telegram bots @botfather"
|
||||
"telegramChatId" = "Telegram Admin Chat IDs"
|
||||
"telegramChatIdDesc" = "Multiple Chat IDs separated by comma. use @userinfobot or use '/id' command in bot to get your Chat IDs."
|
||||
"telegramNotifyTime" = "Telegram bot notification time"
|
||||
"telegramNotifyTimeDesc" = "Use Crontab timing format."
|
||||
"telegramTokenDesc" = "The Telegram bot token obtained from '@BotFather'."
|
||||
"telegramProxy" = "SOCKS Proxy"
|
||||
"telegramProxyDesc" = "Enables SOCKS5 proxy for connecting to Telegram. (adjust settings as per guide)"
|
||||
"telegramChatId" = "Admin Chat ID"
|
||||
"telegramChatIdDesc" = "The Telegram Admin Chat ID(s). (comma-separated)(get it here @userinfobot) or (use '/id' command in the bot)"
|
||||
"telegramNotifyTime" = "Notification Time"
|
||||
"telegramNotifyTimeDesc" = "The Telegram bot notification time set for periodic reports. (use the crontab time format)"
|
||||
"tgNotifyBackup" = "Database Backup"
|
||||
"tgNotifyBackupDesc" = "Include database backup file with report notification."
|
||||
"tgNotifyBackupDesc" = "Send a database backup file with a report."
|
||||
"tgNotifyLogin" = "Login Notification"
|
||||
"tgNotifyLoginDesc" = "Displays the username, IP address, and time when someone tries to log into your panel."
|
||||
"sessionMaxAge" = "Session maximum age"
|
||||
"sessionMaxAgeDesc" = "The duration of a login session (unit: minute)"
|
||||
"expireTimeDiff" = "Expiration threshold for notification"
|
||||
"expireTimeDiffDesc" = "Get notified about account expiration before the threshold (unit: day)"
|
||||
"trafficDiff" = "Traffic threshold for notification"
|
||||
"trafficDiffDesc" = "Get notified about traffic exhaustion before reaching the threshold (unit: GB)"
|
||||
"tgNotifyCpu" = "CPU percentage alert threshold"
|
||||
"tgNotifyCpuDesc" = "Receive notification if CPU usage exceeds this threshold (unit: %)"
|
||||
"timeZone" = "Time zone"
|
||||
"timeZoneDesc" = "Scheduled tasks run according to the time in this time zone."
|
||||
"tgNotifyLoginDesc" = "Get notified about the username, IP address, and time whenever someone attempts to log into your web panel."
|
||||
"sessionMaxAge" = "Session Duration"
|
||||
"sessionMaxAgeDesc" = "The duration for which you can stay logged in. (unit: minute)"
|
||||
"expireTimeDiff" = "Expiration Date Notification"
|
||||
"expireTimeDiffDesc" = "Get notified about expiration date when reaching this threshold. (unit: day)"
|
||||
"trafficDiff" = "Traffic Cap Notification"
|
||||
"trafficDiffDesc" = "Get notified about traffic cap when reaching this threshold. (unit: GB)"
|
||||
"tgNotifyCpu" = "CPU Load Notification"
|
||||
"tgNotifyCpuDesc" = "Get notified if CPU load exceeds this threshold. (unit: %)"
|
||||
"timeZone" = "Time Zone"
|
||||
"timeZoneDesc" = "Scheduled tasks will run based on this time zone."
|
||||
"subSettings" = "Subscription"
|
||||
"subEnable" = "Enable service"
|
||||
"subEnableDesc" = "Subscription feature with separate configuration"
|
||||
"subListen" = "Listening IP"
|
||||
"subListenDesc" = "Leave blank by default to monitor all IPs"
|
||||
"subPort" = "Subscription Port"
|
||||
"subPortDesc" = "Port number for serving the subscription service must be unused in server"
|
||||
"subCertPath" = "Subscription Certificate Public Key File Path"
|
||||
"subCertPathDesc" = "Fill in an absolute path starting with '/'"
|
||||
"subKeyPath" = "Subscription Certificate Private Key File Path"
|
||||
"subKeyPathDesc" = "Fill in an absolute path starting with '/'"
|
||||
"subPath" = "Subscription URL Root Path"
|
||||
"subPathDesc" = "Must start with '/' and end with '/'"
|
||||
"subDomain" = "Listening Domain"
|
||||
"subDomainDesc" = "Leave blank by default to monitor all domains and IPs"
|
||||
"subUpdates" = "Subscription update intervals"
|
||||
"subUpdatesDesc" = "Interval hours between updates in client application"
|
||||
"subEncrypt" = "Encrypt configs"
|
||||
"subEncryptDesc" = "Encrypt the returned configs in subscription"
|
||||
"subShowInfo" = "Show usage info"
|
||||
"subShowInfoDesc" = "Show remained traffic and date after config name"
|
||||
"subEnable" = "Enable Subscription Service"
|
||||
"subEnableDesc" = "Enables the subscription service."
|
||||
"subListen" = "Listen IP"
|
||||
"subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)"
|
||||
"subPort" = "Listen Port"
|
||||
"subPortDesc" = "The port number for the subscription service. (must be an unused port)"
|
||||
"subCertPath" = "Public Key Path"
|
||||
"subCertPathDesc" = "The public key file path for the subscription service. (begins with ‘/‘)"
|
||||
"subKeyPath" = "Private Key Path"
|
||||
"subKeyPathDesc" = "The private key file path for the subscription service. (begins with ‘/‘)"
|
||||
"subPath" = "URI Path"
|
||||
"subPathDesc" = "The URI path for the subscription service. (begins with ‘/‘ and concludes with ‘/‘)"
|
||||
"subDomain" = "Listen Domain"
|
||||
"subDomainDesc" = "The domain name for the subscription service. (leave blank to listen on all domains and IPs)"
|
||||
"subUpdates" = "Update Intervals"
|
||||
"subUpdatesDesc" = "The update intervals of the subscription URL in the client apps. (unit: hour)"
|
||||
"subEncrypt" = "Encode"
|
||||
"subEncryptDesc" = "The returned content of subscription service will be Base64 encoded."
|
||||
"subShowInfo" = "Show Usage Info"
|
||||
"subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps."
|
||||
"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.xray]
|
||||
"title" = "Xray Settings"
|
||||
"save" = "Save Settings"
|
||||
"title" = "Xray Configs"
|
||||
"save" = "Save"
|
||||
"restart" = "Restart Xray"
|
||||
"basicTemplate" = "Basic Template"
|
||||
"advancedTemplate" = "Advanced Template"
|
||||
"generalConfigs" = "General Configs"
|
||||
"generalConfigsDesc" = "These options will provide general adjustments."
|
||||
"blockConfigs" = "Blocking Configs"
|
||||
"blockConfigsDesc" = "These options will prevent users from connecting to specific protocols and websites."
|
||||
"blockCountryConfigs" = "Block Country Configs"
|
||||
"blockCountryConfigsDesc" = "These options will prevent users from connecting to specific country domains."
|
||||
"directCountryConfigs" = "Direct Country Configs"
|
||||
"directCountryConfigsDesc" = "These options will connect users directly to specific country domains."
|
||||
"ipv4Configs" = "IPv4 Configs"
|
||||
"ipv4ConfigsDesc" = "These options will route to target domains only via IPv4."
|
||||
"warpConfigs" = "WARP Configs"
|
||||
"warpConfigsDesc" = "Caution: Before using these options, install WARP in socks5 proxy mode on your server by following the steps on the panel's GitHub. WARP will route traffic to websites through Cloudflare servers."
|
||||
"Template" = "Xray Configuration Template"
|
||||
"TemplateDesc" = "Generate the final Xray configuration file based on this template."
|
||||
"FreedomStrategy" = "Configure Strategy for Freedom Protocol"
|
||||
"FreedomStrategyDesc" = "Set the output strategy of the network in the Freedom Protocol."
|
||||
"RoutingStrategy" = "Configure Domains Routing Strategy"
|
||||
"RoutingStrategyDesc" = "Set the overall routing strategy for DNS resolving."
|
||||
"Torrent" = "Ban BitTorrent Usage"
|
||||
"TorrentDesc" = "Change the configuration template to avoid using BitTorrent by users."
|
||||
"PrivateIp" = "Ban Private IP Ranges to Connect"
|
||||
"PrivateIpDesc" = "Change the configuration template to avoid connecting to private IP ranges."
|
||||
"basicTemplate" = "Basics"
|
||||
"advancedTemplate" = "Advanced"
|
||||
"generalConfigs" = "General Strategy"
|
||||
"generalConfigsDesc" = "These options will determine general strategy adjustments."
|
||||
"blockConfigs" = "Protection Shield"
|
||||
"blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites."
|
||||
"blockCountryConfigs" = "Block Country"
|
||||
"blockCountryConfigsDesc" = "These options will block traffic based on the specific requested country."
|
||||
"directCountryConfigs" = "Direct Country"
|
||||
"directCountryConfigsDesc" = "These options will directly forward traffic based on the specific requested country."
|
||||
"ipv4Configs" = "IPv4 Routing"
|
||||
"ipv4ConfigsDesc" = "These options will route traffic based on a specific destination via IPv4."
|
||||
"warpConfigs" = "WARP Routing"
|
||||
"warpConfigsDesc" = "These options will route traffic based on a specific destination via WARP. (follow the guide on the Panel’s GitHub)"
|
||||
"Template" = "Advanced Xray Configuration Template"
|
||||
"TemplateDesc" = "The final Xray config file will be generated based on this template."
|
||||
"FreedomStrategy" = "Freedom Protocol Strategy"
|
||||
"FreedomStrategyDesc" = "Set the output strategy for the network in the Freedom Protocol."
|
||||
"RoutingStrategy" = "Overall Routing Strategy"
|
||||
"RoutingStrategyDesc" = "Set the overall traffic routing strategy for resolving all requests."
|
||||
"Torrent" = "Block BitTorrent Protocol"
|
||||
"TorrentDesc" = "Blocks BitTorrent protocol."
|
||||
"PrivateIp" = "Block Connection to Private IPs"
|
||||
"PrivateIpDesc" = "Blocks establishing connections to private IP ranges."
|
||||
"Ads" = "Block Ads"
|
||||
"AdsDesc" = "Change the configuration template to block ads."
|
||||
"Family" = "Block Malware and Adult Content"
|
||||
"FamilyDesc" = "DNS resolvers to block malware and adult content for family protection."
|
||||
"Speedtest" = "Block Speedtest Websites"
|
||||
"SpeedtestDesc" = "Change the configuration template to avoid connecting to speedtest websites."
|
||||
"IRIp" = "Disable connection to Iran IP ranges"
|
||||
"IRIpDesc" = "Change the configuration template to avoid connecting to Iran IP ranges."
|
||||
"IRDomain" = "Disable connection to Iran domains"
|
||||
"IRDomainDesc" = "Change the configuration template to avoid connecting to Iran domains."
|
||||
"ChinaIp" = "Disable connection to China IP ranges"
|
||||
"ChinaIpDesc" = "Change the configuration template to avoid connecting to China IP ranges."
|
||||
"ChinaDomain" = "Disable connection to China domains"
|
||||
"ChinaDomainDesc" = "Change the configuration template to avoid connecting to China domains."
|
||||
"RussiaIp" = "Disable connection to Russia IP ranges"
|
||||
"RussiaIpDesc" = "Change the configuration template to avoid connecting to Russia IP ranges."
|
||||
"RussiaDomain" = "Disable connection to Russia domains"
|
||||
"RussiaDomainDesc" = "Change the configuration template to avoid connecting to Russia domains."
|
||||
"DirectIRIp" = "Direct connection to Iran IP ranges"
|
||||
"DirectIRIpDesc" = "Change the configuration template for direct connecting to Iran IP ranges."
|
||||
"DirectIRDomain" = "Direct connection to Iran domains"
|
||||
"DirectIRDomainDesc" = "Change the configuration template for direct connecting to Iran domains."
|
||||
"DirectChinaIp" = "Direct connection to China IP ranges"
|
||||
"DirectChinaIpDesc" = "Change the configuration template for direct connecting to China IP ranges."
|
||||
"DirectChinaDomain" = "Direct connection to China domains"
|
||||
"DirectChinaDomainDesc" = "Change the configuration template for direct connecting to China domains."
|
||||
"DirectRussiaIp" = "Direct connection to Russia IP ranges"
|
||||
"DirectRussiaIpDesc" = "Change the configuration template for direct connecting to Russia IP ranges."
|
||||
"DirectRussiaDomain" = "Direct connection to Russia domains"
|
||||
"DirectRussiaDomainDesc" = "Change the configuration template for direct connecting to Russia domains."
|
||||
"GoogleIPv4" = "Use IPv4 for Google"
|
||||
"GoogleIPv4Desc" = "Add routing for Google to connect with IPv4."
|
||||
"NetflixIPv4" = "Use IPv4 for Netflix"
|
||||
"NetflixIPv4Desc" = "Add routing for Netflix to connect with IPv4."
|
||||
"GoogleWARP" = "Route Google through WARP."
|
||||
"AdsDesc" = "Blocks advertising websites."
|
||||
"Family" = "Family Protection"
|
||||
"FamilyDesc" = "Blocks adult content, and malware websites."
|
||||
"Security" = "Security Shield"
|
||||
"SecurityDesc" = "Blocks malware, phishing, and cryptominers websites."
|
||||
"Speedtest" = "Block Speedtest"
|
||||
"SpeedtestDesc" = "Blocks establishing connectins to speedtest websites."
|
||||
"IRIp" = "Block Connection to Iran IPs"
|
||||
"IRIpDesc" = "Blocks establishing connections to Iran IP ranges."
|
||||
"IRDomain" = "Block Connection to Iran Domains"
|
||||
"IRDomainDesc" = "Blocks establishing connections to Iran domains."
|
||||
"ChinaIp" = "Block Connection to China IPs"
|
||||
"ChinaIpDesc" = "Blocks establishing connections to China IP ranges."
|
||||
"ChinaDomain" = "Block Connection to China Domains"
|
||||
"ChinaDomainDesc" = "Blocks establishing connections to China domains."
|
||||
"RussiaIp" = "Block Connection to Russia IPs"
|
||||
"RussiaIpDesc" = "Blocks establishing connections to Russia IP ranges."
|
||||
"RussiaDomain" = "Block Connection to Russia Domains"
|
||||
"RussiaDomainDesc" = "Blocks establishing connections to Russia domains."
|
||||
"VNIp" = "Block Connection to Vietnam IPs"
|
||||
"VNIpDesc" = "Blocks establishing connections to Vietnam IP ranges."
|
||||
"VNDomain" = "Block Connection to Vietnam Domains"
|
||||
"VNDomainDesc" = "Blocks establishing connections to Vietnam domains."
|
||||
"DirectIRIp" = "Direct Connection to Iran IPs"
|
||||
"DirectIRIpDesc" = "Directly establishes connections to Iran IP ranges."
|
||||
"DirectIRDomain" = "Direct Connection to Iran Domains"
|
||||
"DirectIRDomainDesc" = "Directly establishes connections to Iran domains."
|
||||
"DirectChinaIp" = "Direct Connection to China IPs"
|
||||
"DirectChinaIpDesc" = "Directly establishes connections to China IP ranges."
|
||||
"DirectChinaDomain" = "Direct Connection to China Domains"
|
||||
"DirectChinaDomainDesc" = "Directly establishes connections to China domains."
|
||||
"DirectRussiaIp" = "Direct Connection to Russia IPs"
|
||||
"DirectRussiaIpDesc" = "Directly establishes connections to Russia IP ranges."
|
||||
"DirectRussiaDomain" = "Direct Connection to Russia Domains"
|
||||
"DirectRussiaDomainDesc" = "Directly establishes connections to Russia domains."
|
||||
"DirectVNIp" = "Direct Connection to Vietnam IPs"
|
||||
"DirectVNIpDesc" = "Directly establishes connections to Vietnam IP ranges."
|
||||
"DirectVNDomain" = "Direct Connection to Vietnam Domains"
|
||||
"DirectVNDomainDesc" = "Directly establishes connections to Vietnam domains."
|
||||
"GoogleIPv4" = "Google"
|
||||
"GoogleIPv4Desc" = "Routes traffic to Google via IPv4."
|
||||
"NetflixIPv4" = "Netflix"
|
||||
"NetflixIPv4Desc" = "Routes traffic to Netflix via IPv4."
|
||||
"GoogleWARP" = "Google"
|
||||
"GoogleWARPDesc" = "Add routing for Google via WARP."
|
||||
"OpenAIWARP" = "Route OpenAI (ChatGPT) through WARP."
|
||||
"OpenAIWARPDesc" = "Add routing for OpenAI (ChatGPT) via WARP."
|
||||
"NetflixWARP" = "Route Netflix through WARP."
|
||||
"NetflixWARPDesc" = "Add routing for Netflix via WARP."
|
||||
"SpotifyWARP" = "Route Spotify through WARP."
|
||||
"SpotifyWARPDesc" = "Add routing for Spotify via WARP."
|
||||
"IRWARP" = "Route Iran domains through WARP."
|
||||
"IRWARPDesc" = "Add routing for Iran domains via WARP."
|
||||
"OpenAIWARP" = "ChatGPT"
|
||||
"OpenAIWARPDesc" = "Routes traffic to ChatGPT via WARP."
|
||||
"NetflixWARP" = "Netflix"
|
||||
"NetflixWARPDesc" = "Routes traffic to Netflix via WARP."
|
||||
"SpotifyWARP" = "Spotify"
|
||||
"SpotifyWARPDesc" = "Routes traffic to Spotify via WARP."
|
||||
"IRWARP" = "Iran domains"
|
||||
"IRWARPDesc" = "Routes traffic to Iran domains via WARP."
|
||||
"Inbounds" = "Inbounds"
|
||||
"InboundsDesc" = "Change the configuration template to accept specific clients."
|
||||
"InboundsDesc" = "Accepting the specific clients."
|
||||
"Outbounds" = "Outbounds"
|
||||
"OutboundsDesc" = "Change the configuration template to define outgoing ways for this server."
|
||||
"Routings" = "routing rules."
|
||||
"OutboundsDesc" = "Set the outgoing traffic pathway."
|
||||
"Routings" = "Routing Rules"
|
||||
"RoutingsDesc" = "The priority of each rule is important!"
|
||||
"completeTemplate" = "All"
|
||||
|
||||
@@ -389,15 +404,15 @@
|
||||
"info" = "Info"
|
||||
"add" = "Add Rule"
|
||||
"edit" = "Edit Rule"
|
||||
"useComma" = "Comma separated items"
|
||||
"useComma" = "Comma-separated items"
|
||||
|
||||
[pages.xray.outbound]
|
||||
"addOutbound" = "Add outbound"
|
||||
"addReverse" = "Add reverse"
|
||||
"editOutbound" = "Edit outbound"
|
||||
"editReverse" = "Edit reverse"
|
||||
"addOutbound" = "Add Outbound"
|
||||
"addReverse" = "Add Reverse"
|
||||
"editOutbound" = "Edit Outbound"
|
||||
"editReverse" = "Edit Reverse"
|
||||
"tag" = "Tag"
|
||||
"tagDesc" = "Unique tag"
|
||||
"tagDesc" = "Unique Tag"
|
||||
"address" = "Address"
|
||||
"reverse" = "Reverse"
|
||||
"domain" = "Domain"
|
||||
@@ -406,20 +421,28 @@
|
||||
"portal" = "Portal"
|
||||
"intercon" = "Interconnection"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Secret Key"
|
||||
"publicKey" = "Public Key"
|
||||
"allowedIPs" = "Allowed IPs"
|
||||
"endpoint" = "Endpoint"
|
||||
"psk" = "PreShared Key"
|
||||
"domainStrategy" = "Domain Strategy"
|
||||
|
||||
[pages.settings.security]
|
||||
"admin" = "Admin"
|
||||
"secret" = "Secret Token"
|
||||
"loginSecurity" = "Login security"
|
||||
"loginSecurityDesc" = "Enable additional user login security step"
|
||||
"loginSecurity" = "Secure Login"
|
||||
"loginSecurityDesc" = "Adds an additional layer of authentication to provide more security."
|
||||
"secretToken" = "Secret Token"
|
||||
"secretTokenDesc" = "Please copy and securely store this token in a safe place. This token is required for login and cannot be recovered from the x-ui command tool."
|
||||
"secretTokenDesc" = "Please securely store this token in a safe place. This token is required for login and cannot be recovered."
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "Modify Settings "
|
||||
"getSettings" = "Get Settings "
|
||||
"modifyUser" = "Modify User "
|
||||
"originalUserPassIncorrect" = "Incorrect original username or password"
|
||||
"userPassMustBeNotEmpty" = "New username and new password cannot be empty"
|
||||
"modifySettings" = "Modify Settings"
|
||||
"getSettings" = "Get Settings"
|
||||
"modifyUser" = "Modify Admin"
|
||||
"originalUserPassIncorrect" = "The Current username or password is invalid"
|
||||
"userPassMustBeNotEmpty" = "The new username and password is empty"
|
||||
|
||||
[tgbot]
|
||||
"keyboardClosed" = "❌ Custom keyboard closed!"
|
||||
@@ -429,6 +452,7 @@
|
||||
"noIpRecord" = "❗ No IP Record!"
|
||||
"noInbounds" = "❗ No inbound found!"
|
||||
"unlimited" = "♾ Unlimited"
|
||||
"add" = "Add"
|
||||
"month" = "Month"
|
||||
"months" = "Months"
|
||||
"day" = "Day"
|
||||
@@ -437,48 +461,51 @@
|
||||
"unknown" = "Unknown"
|
||||
"inbounds" = "Inbounds"
|
||||
"clients" = "Clients"
|
||||
"offline" = "🔴 Offline"
|
||||
"online" = "🟢 Online"
|
||||
|
||||
[tgbot.commands]
|
||||
"unknown" = "❗ Unknown command"
|
||||
"unknown" = "❗ Unknown command."
|
||||
"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 offer 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"
|
||||
"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!"
|
||||
"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>"
|
||||
"helpClientCommands" = "To search for statistics, just 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."
|
||||
"helpAdminCommands" = "To search for a client email:\r\n<code>/usage [Email]</code>\r\n\r\nTo search for inbounds (with client stats):\r\n<code>/inbound [Remark]</code>"
|
||||
"helpClientCommands" = "To search for statistics, use the following command:\r\n\r\n<code>/usage [Email]</code>"
|
||||
|
||||
[tgbot.messages]
|
||||
"cpuThreshold" = "🔴 The CPU usage {{ .Percent }}% is more than threshold {{ .Threshold }}%"
|
||||
"cpuThreshold" = "🔴 CPU Load {{ .Percent }}% exceeds the threshold of {{ .Threshold }}%"
|
||||
"selectUserFailed" = "❌ Error in user selection!"
|
||||
"userSaved" = "✅ Telegram User saved."
|
||||
"loginSuccess" = "✅ Successfully logged-in to the panel.\r\n"
|
||||
"loginFailed" = "❗️ Login to the panel failed.\r\n"
|
||||
"loginSuccess" = "✅ Logged in to the panel successfully.\r\n"
|
||||
"loginFailed" = "❗️ Log in to the panel failed.\r\n"
|
||||
"report" = "🕰 Scheduled Reports: {{ .RunTime }}\r\n"
|
||||
"datetime" = "⏰ Date-Time: {{ .DateTime }}\r\n"
|
||||
"hostname" = "💻 Hostname: {{ .Hostname }}\r\n"
|
||||
"version" = "🚀 X-UI Version: {{ .Version }}\r\n"
|
||||
"datetime" = "⏰ Date&Time: {{ .DateTime }}\r\n"
|
||||
"hostname" = "💻 Host: {{ .Hostname }}\r\n"
|
||||
"version" = "🚀 3X-UI Version: {{ .Version }}\r\n"
|
||||
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
||||
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
||||
"ip" = "🌐 IP: {{ .IP }}\r\n"
|
||||
"ips" = "🔢 IPs: \r\n{{ .IPs }}\r\n"
|
||||
"serverUpTime" = "⏳ Server Uptime: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"serverLoad" = "📈 Server Load: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||
"serverMemory" = "📋 Server Memory: {{ .Current }}/{{ .Total }}\r\n"
|
||||
"tcpCount" = "🔹 TcpCount: {{ .Count }}\r\n"
|
||||
"udpCount" = "🔸 UdpCount: {{ .Count }}\r\n"
|
||||
"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n"
|
||||
"serverUpTime" = "⏳ Uptime: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"serverLoad" = "📈 System Load: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||
"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n"
|
||||
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
|
||||
"udpCount" = "🔸 UDP: {{ .Count }}\r\n"
|
||||
"traffic" = "🚦 Traffic: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||
"xrayStatus" = "ℹ️ Xray Status: {{ .State }}\r\n"
|
||||
"xrayStatus" = "ℹ️ Status: {{ .State }}\r\n"
|
||||
"username" = "👤 Username: {{ .Username }}\r\n"
|
||||
"time" = "⏰ Time: {{ .Time }}\r\n"
|
||||
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
|
||||
"port" = "🔌 Port: {{ .Port }}\r\n"
|
||||
"expire" = "📅 Expire Date: {{ .Time }}\r\n"
|
||||
"expireIn" = "📅 Expire In: {{ .Time }}\r\n"
|
||||
"active" = "💡 Active: ✅ Yes\r\n"
|
||||
"inactive" = "💡 Active: ❌ No\r\n"
|
||||
"active" = "💡 Active: {{ .Enable }}\r\n"
|
||||
"enabled" = "🚨 Enabled: {{ .Enable }}\r\n"
|
||||
"online" = "🌐 Connection status: {{ .Status }}\r\n"
|
||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
|
||||
"download" = "🔽 Download: ↓{{ .Download }}\r\n"
|
||||
@@ -486,10 +513,13 @@
|
||||
"TGUser" = "👤 Telegram User: {{ .TelegramID }}\r\n"
|
||||
"exhaustedMsg" = "🚨 Exhausted {{ .Type }}:\r\n"
|
||||
"exhaustedCount" = "🚨 Exhausted {{ .Type }} count:\r\n"
|
||||
"onlinesCount" = "🌐 Online Clients: {{ .Count }}\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"
|
||||
"refreshedOn" = "\r\n📋🔄 Refreshed On: {{ .Time }}\r\n \r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 Refreshed On: {{ .Time }}\r\n\r\n"
|
||||
"yes" = "✅ Yes"
|
||||
"no" = "❌ No"
|
||||
|
||||
[tgbot.buttons]
|
||||
"closeKeyboard" = "❌ Close Keyboard"
|
||||
@@ -499,44 +529,47 @@
|
||||
"confirmResetTraffic" = "✅ Confirm Reset Traffic?"
|
||||
"confirmClearIps" = "✅ Confirm Clear IPs?"
|
||||
"confirmRemoveTGUser" = "✅ Confirm Remove Telegram User?"
|
||||
"confirmToggle" = "✅ Confirm Enable/Disable User?"
|
||||
"dbBackup" = "Get DB Backup"
|
||||
"serverUsage" = "Server Usage"
|
||||
"getInbounds" = "Get Inbounds"
|
||||
"depleteSoon" = "Deplete soon"
|
||||
"depleteSoon" = "Deplete Soon"
|
||||
"clientUsage" = "Get Usage"
|
||||
"onlines" = "Online Clients"
|
||||
"commands" = "Commands"
|
||||
"refresh" = "🔄 Refresh"
|
||||
"clearIPs" = "❌ Clear IPs"
|
||||
"removeTGUser" = "❌ Remove Telegram User"
|
||||
"selectTGUser" = "👤 Select Telegram User"
|
||||
"selectOneTGUser" = "👤 Select a telegram user:"
|
||||
"selectOneTGUser" = "👤 Select a Telegram User:"
|
||||
"resetTraffic" = "📈 Reset Traffic"
|
||||
"resetExpire" = "📅 Reset Expire Days"
|
||||
"resetExpire" = "📅 Change Expiry Date"
|
||||
"ipLog" = "🔢 IP Log"
|
||||
"ipLimit" = "🔢 IP Limit"
|
||||
"setTGUser" = "👤 Set Telegram User"
|
||||
"toggle" = "🔘 Enable / Disable"
|
||||
"custom" = "🔢 Custom"
|
||||
"confirmNumber" = "✅ Confirm : {{ .Num }}"
|
||||
"confirmNumber" = "✅ Confirm: {{ .Num }}"
|
||||
"confirmNumberAdd" = "✅ Confirm adding: {{ .Num }}"
|
||||
"limitTraffic" = "🚧 Traffic Limit"
|
||||
"getBanLogs" = "Get Ban Logs"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ Successful!"
|
||||
"errorOperation" = "❗ Error in Operation."
|
||||
"getInboundsFailed" = "❌ Failed to get inbounds"
|
||||
"canceled" = "❌ {{ .Email }} : Operation canceled."
|
||||
"clientRefreshSuccess" = "✅ {{ .Email }} : Client refreshed successfully."
|
||||
"IpRefreshSuccess" = "✅ {{ .Email }} : IPs refreshed successfully."
|
||||
"TGIdRefreshSuccess" = "✅ {{ .Email }} : Client's Telegram User refreshed successfully."
|
||||
"resetTrafficSuccess" = "✅ {{ .Email }} : Traffic reset successfully."
|
||||
"setTrafficLimitSuccess" = "✅ {{ .Email }} : Traffic limit saved successfully."
|
||||
"expireResetSuccess" = "✅ {{ .Email }} : Expire days reset successfully."
|
||||
"resetIpSuccess" = "✅ {{ .Email }} : IP limit {{ .Count }} saved successfully."
|
||||
"clearIpSuccess" = "✅ {{ .Email }} : IPs cleared successfully."
|
||||
"getIpLog" = "✅ {{ .Email }} : Get IP Log."
|
||||
"getUserInfo" = "✅ {{ .Email }} : Get Telegram User Info."
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }} : Telegram User removed successfully."
|
||||
"enableSuccess" = "✅ {{ .Email }} : Enabled successfully."
|
||||
"disableSuccess" = "✅ {{ .Email }} : Disabled successfully."
|
||||
"askToAddUserId" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram user id in your configuration(s).\r\n\r\nYour user id: <b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserName" = "Your configuration is not found!\r\nPlease ask your Admin to use your telegram username or user id in your configuration(s).\r\n\r\nYour username: <b>@{{ .TgUserName }}</b>\r\n\r\nYour user id: <b>{{ .TgUserID }}</b>"
|
||||
"successfulOperation" = "✅ Operation successful!"
|
||||
"errorOperation" = "❗ Error in operation."
|
||||
"getInboundsFailed" = "❌ Failed to get inbounds."
|
||||
"canceled" = "❌ {{ .Email }}: Operation canceled."
|
||||
"clientRefreshSuccess" = "✅ {{ .Email }}: Client refreshed successfully."
|
||||
"IpRefreshSuccess" = "✅ {{ .Email }}: IPs refreshed successfully."
|
||||
"TGIdRefreshSuccess" = "✅ {{ .Email }}: Client's Telegram User refreshed successfully."
|
||||
"resetTrafficSuccess" = "✅ {{ .Email }}: Traffic reset successfully."
|
||||
"setTrafficLimitSuccess" = "✅ {{ .Email }}: Traffic limit saved successfully."
|
||||
"expireResetSuccess" = "✅ {{ .Email }}: Expire days reset successfully."
|
||||
"resetIpSuccess" = "✅ {{ .Email }}: IP limit {{ .Count }} saved successfully."
|
||||
"clearIpSuccess" = "✅ {{ .Email }}: IPs cleared successfully."
|
||||
"getIpLog" = "✅ {{ .Email }}: Get IP Log."
|
||||
"getUserInfo" = "✅ {{ .Email }}: Get Telegram User Info."
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }}: Telegram User removed successfully."
|
||||
"enableSuccess" = "✅ {{ .Email }}: Enabled successfully."
|
||||
"disableSuccess" = "✅ {{ .Email }}: Disabled successfully."
|
||||
"askToAddUserId" = "Your configuration is not found!\r\nPlease ask your admin to use your Telegram ID in your configuration(s).\r\n\r\nYour User ID: <code>{{ .TgUserID }}</code>"
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"link" = "Otro"
|
||||
|
||||
[pages.login]
|
||||
"title" = "Iniciar Sesión"
|
||||
"title" = "Grata"
|
||||
"loginAgain" = "El límite de tiempo de inicio de sesión ha expirado. Por favor, inicia sesión nuevamente."
|
||||
|
||||
[pages.login.toasts]
|
||||
@@ -76,10 +76,10 @@
|
||||
"title" = "Estado del Sistema"
|
||||
"memory" = "Memoria"
|
||||
"hard" = "Disco Duro"
|
||||
"xrayStatus" = "Estado de Xray"
|
||||
"stopXray" = "Detener Xray"
|
||||
"xrayStatus" = "Estado de"
|
||||
"stopXray" = "Detener"
|
||||
"restartXray" = "Reiniciar"
|
||||
"xraySwitch" = "Cambiar Versión"
|
||||
"xraySwitch" = "Versión"
|
||||
"xraySwitchClick" = "Elige la versión a la que deseas cambiar."
|
||||
"xraySwitchClickDesk" = "Elige sabiamente, ya que las versiones anteriores pueden no ser compatibles con las configuraciones actuales."
|
||||
"operationHours" = "Tiempo de Funcionamiento"
|
||||
@@ -134,7 +134,7 @@
|
||||
"destinationPort" = "Puerto de Destino"
|
||||
"targetAddress" = "Dirección de Destino"
|
||||
"monitorDesc" = "Dejar en blanco por defecto"
|
||||
"meansNoLimit" = "Significa Sin Límite"
|
||||
"meansNoLimit" = " = illimitata. (unidad: GB)"
|
||||
"totalFlow" = "Flujo Total"
|
||||
"leaveBlankToNeverExpire" = "Dejar en Blanco para Nunca Expirar"
|
||||
"noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada"
|
||||
@@ -173,7 +173,7 @@
|
||||
"setDefaultCert" = "Establecer certificado desde el panel"
|
||||
"xtlsDesc" = "La versión del núcleo de Xray debe ser 1.7.5"
|
||||
"realityDesc" = "La versión del núcleo de Xray debe ser 1.8.0 o superior."
|
||||
"telegramDesc" = "Utiliza el ID de Telegram sin @ o los IDs de chat (puedes obtenerlo aquí @userinfobot o usando el comando '/id' en el bot)."
|
||||
"telegramDesc" = "Utiliza únicamente IDs de chat (puedes obtenerlo aquí @userinfobot o usando el comando '/id' en el bot)."
|
||||
"subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones."
|
||||
"info" = "Info"
|
||||
"same" = "misma"
|
||||
@@ -195,27 +195,28 @@
|
||||
"prefix" = "Prefijo"
|
||||
"postfix" = "Sufijo"
|
||||
"delayedStart" = "Iniciar después del primer uso"
|
||||
"expireDays" = "Días de Expiración"
|
||||
"expireDays" = "Duratio"
|
||||
"days" = "día(s)"
|
||||
"renew" = "Renovación automática"
|
||||
"renewDesc" = "Renovación automática días después del vencimiento. 0 = deshabilitar"
|
||||
"renewDesc" = "Auto-renovatio post tutelam receptam. (0 = disable) (unitas: dies)"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "Recibir"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "Encabezado de la Petición"
|
||||
"request" = "Pedido"
|
||||
"response" = "Respuesta"
|
||||
"name" = "Nombre"
|
||||
"value" = "Valor"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "Versión de la Petición"
|
||||
"requestMethod" = "Método de la Petición"
|
||||
"requestPath" = "Ruta de la Petición"
|
||||
"responseVersion" = "Versión de la Respuesta"
|
||||
"responseStatus" = "Estado de la Respuesta"
|
||||
"responseStatusDescription" = "Descripción del Estado de la Respuesta"
|
||||
"responseHeader" = "Encabezado de la Respuesta"
|
||||
"version" = "Versión"
|
||||
"method" = "Método"
|
||||
"path" = "Camino"
|
||||
"status" = "Estado"
|
||||
"statusDescription" = "Descripción de la Situación"
|
||||
"requestHeader" = "Encabezado de solicitud"
|
||||
"responseHeader" = "Encabezado de respuesta"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
"encryption" = "Cifrado"
|
||||
@@ -246,6 +247,8 @@
|
||||
"pageSize" = "Tamaño de paginación"
|
||||
"pageSizeDesc" = "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar"
|
||||
"remarkModel" = "Modelo de observación y carácter de separación"
|
||||
"datepicker" = "selector de fechas"
|
||||
"datepickerDescription" = "El tipo de calendario selector especifica la fecha de vencimiento"
|
||||
"sampleRemark" = "Observación de muestra"
|
||||
"oldUsername" = "Nombre de Usuario Actual"
|
||||
"currentPassword" = "Contraseña Actual"
|
||||
@@ -255,6 +258,8 @@
|
||||
"telegramBotEnableDesc" = "Conéctese a las funciones de este panel a través del bot de Telegram."
|
||||
"telegramToken" = "Token de Telegram"
|
||||
"telegramTokenDesc" = "Debe obtener el token del administrador de bots de Telegram @botfather."
|
||||
"telegramProxy" = "Socks5 Proxy"
|
||||
"telegramProxyDesc" = "Si necesita el proxy Socks5 para conectarse a Telegram. Ajuste su configuración según la guía."
|
||||
"telegramChatId" = "IDs de Chat de Telegram para Administradores"
|
||||
"telegramChatIdDesc" = "IDs de Chat múltiples separados por comas. Use @userinfobot o use el comando '/id' en el bot para obtener sus IDs de Chat."
|
||||
"telegramNotifyTime" = "Hora de Notificación del Bot de Telegram"
|
||||
@@ -327,8 +332,10 @@
|
||||
"PrivateIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP privadas."
|
||||
"Ads" = "Bloquear Anuncios"
|
||||
"AdsDesc" = "Cambia la plantilla de configuración para bloquear anuncios."
|
||||
"Family" = "Bloquear Malware y Contenido para Adultos"
|
||||
"FamilyDesc" = "Resolvedores de DNS para bloquear malware y contenido para adultos para protección familiar."
|
||||
"Family" = "Bloquee malware y contenido para adultos"
|
||||
"FamilyDesc" = "Resolutores de DNS de Cloudflare para bloquear malware y contenido para adultos para protección familiar."
|
||||
"Security" = "Bloquee sitios web de malware, phishing y criptomineros"
|
||||
"SecurityDesc" = "Cambiar la plantilla de configuración para la protección de seguridad."
|
||||
"Speedtest" = "Bloquear Sitios Web de Pruebas de Velocidad"
|
||||
"SpeedtestDesc" = "Cambia la plantilla de configuración para evitar la conexión a sitios web de pruebas de velocidad."
|
||||
"IRIp" = "Desactivar Conexión a Rangos de IP de Irán"
|
||||
@@ -343,6 +350,10 @@
|
||||
"RussiaIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de Rusia."
|
||||
"RussiaDomain" = "Desactivar Conexión a Dominios de Rusia"
|
||||
"RussiaDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de Rusia."
|
||||
"VNIp" = "Deshabilitar la conexión a las IP de Vietnam"
|
||||
"VNIpDesc" = "Cambie la plantilla de configuración para evitar conectarse a rangos de IP de Vietnam."
|
||||
"VNDomain" = "Deshabilitar la conexión a dominios de Vietnam"
|
||||
"VNDomainDesc" = "Cambie la plantilla de configuración para evitar conectarse a dominios de Vietnam."
|
||||
"DirectIRIp" = "Conexión Directa a Rangos de IP de Irán"
|
||||
"DirectIRIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de Irán."
|
||||
"DirectIRDomain" = "Conexión Directa a Dominios de Irán"
|
||||
@@ -355,6 +366,10 @@
|
||||
"DirectRussiaIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de Rusia."
|
||||
"DirectRussiaDomain" = "Conexión Directa a Dominios de Rusia"
|
||||
"DirectRussiaDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de Rusia."
|
||||
"DirectVNIp" = "Conexión directa a IP de Vietnam"
|
||||
"DirectVNIpDesc" = "Cambie la plantilla de configuración para la conexión directa a rangos de IP de Vietnam."
|
||||
"DirectVNDomain" = "Conexión directa a dominios de Vietnam"
|
||||
"DirectVNDomainDesc" = "Cambie la plantilla de configuración para la conexión directa a dominios de Vietnam."
|
||||
"GoogleIPv4" = "Usar IPv4 para Google"
|
||||
"GoogleIPv4Desc" = "Agregar enrutamiento para que Google se conecte con IPv4."
|
||||
"NetflixIPv4" = "Usar IPv4 para Netflix"
|
||||
@@ -406,6 +421,14 @@
|
||||
"portal" = "portal"
|
||||
"intercon" = "Interconexión"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Llave secreta"
|
||||
"publicKey" = "Llave pública"
|
||||
"allowedIPs" = "IP permitidas"
|
||||
"endpoint" = "Punto final"
|
||||
"psk" = "Clave precompartida"
|
||||
"domainStrategy" = "Estrategia de dominio"
|
||||
|
||||
[pages.settings.security]
|
||||
"admin" = "Administrador"
|
||||
"secret" = "Token Secreto"
|
||||
@@ -429,6 +452,7 @@
|
||||
"noIpRecord" = "❗ ¡Sin Registro de IP!"
|
||||
"noInbounds" = "❗ ¡No se encontraron entradas!"
|
||||
"unlimited" = "♾ Ilimitado"
|
||||
"add" = "Agregar"
|
||||
"month" = "Mes"
|
||||
"months" = "Meses"
|
||||
"day" = "Día"
|
||||
@@ -437,6 +461,8 @@
|
||||
"unknown" = "Desconocido"
|
||||
"inbounds" = "Entradas"
|
||||
"clients" = "Clientes"
|
||||
"offline" = "🔴 Sin conexión"
|
||||
"online" = "🟢 En línea"
|
||||
|
||||
[tgbot.commands]
|
||||
"unknown" = "❗ Comando desconocido"
|
||||
@@ -447,8 +473,8 @@
|
||||
"status" = "✅ ¡El bot está bien!"
|
||||
"usage" = "❗ ¡Por favor proporciona un texto para buscar!"
|
||||
"getID" = "🆔 Tu ID: <code>{{ .ID }}</code>"
|
||||
"helpAdminCommands" = "Buscar un correo electrónico de cliente:\r\n<code>/usage [Email]</code>\r\n \r\nBuscar entradas (con estadísticas de cliente):\r\n<code>/inbound [Nota]</code>"
|
||||
"helpClientCommands" = "Para buscar estadísticas, simplemente usa el siguiente comando:\r\n \r\n<code>/usage [UUID|Contraseña]</code>\r\n \r\nUsa UUID para vmess/vless y Contraseña para Trojan."
|
||||
"helpAdminCommands" = "Buscar un correo electrónico de cliente:\r\n<code>/usage [Email]</code>\r\n\r\nBuscar entradas (con estadísticas de cliente):\r\n<code>/inbound [Nota]</code>"
|
||||
"helpClientCommands" = "Para buscar estadísticas, simplemente usa el siguiente comando:\r\n\r\n<code>/usage [UUID|Contraseña]</code>"
|
||||
|
||||
[tgbot.messages]
|
||||
"cpuThreshold" = "🔴 El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%"
|
||||
@@ -463,7 +489,7 @@
|
||||
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
||||
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
||||
"ip" = "🌐 IP: {{ .IP }}\r\n"
|
||||
"ips" = "🔢 IPs: \r\n{{ .IPs }}\r\n"
|
||||
"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n"
|
||||
"serverUpTime" = "⏳ Tiempo de actividad del servidor: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"serverLoad" = "📈 Carga del servidor: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||
"serverMemory" = "📋 Memoria del servidor: {{ .Current }}/{{ .Total }}\r\n"
|
||||
@@ -477,19 +503,23 @@
|
||||
"port" = "🔌 Puerto: {{ .Port }}\r\n"
|
||||
"expire" = "📅 Fecha de Vencimiento: {{ .Time }}\r\n"
|
||||
"expireIn" = "📅 Vence en: {{ .Time }}\r\n"
|
||||
"active" = "💡 Activo: ✅ Sí\r\n"
|
||||
"inactive" = "💡 Activo: ❌ No\r\n"
|
||||
"active" = "💡 Activo: {{ .Enable }}\r\n"
|
||||
"enabled" = "🚨 Habilitado: {{ .Enable }}\r\n"
|
||||
"online" = "🌐 Estado de conexión: {{ .Status }}\r\n"
|
||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||
"upload" = "🔼 Subida: ↑{{ .Upload }}\r\n"
|
||||
"download" = "🔽 Bajada: ↓{{ .Download }}\r\n"
|
||||
"total" = "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n"
|
||||
"TGUser" = "👤 Usuario de Telegram: {{ .TelegramID }}\r\n"
|
||||
"exhaustedMsg" = "🚨 Agotado {{ .Type }}: \r\n"
|
||||
"exhaustedCount" = "🚨 Cantidad de Agotados {{ .Type }}: \r\n"
|
||||
"exhaustedMsg" = "🚨 Agotado {{ .Type }}:\r\n"
|
||||
"exhaustedCount" = "🚨 Cantidad de Agotados {{ .Type }}:\r\n"
|
||||
"onlinesCount" = "🌐 Clientes en línea: {{ .Count }}\r\n"
|
||||
"disabled" = "🛑 Desactivado: {{ .Disabled }}\r\n"
|
||||
"depleteSoon" = "🔜 Se agotará pronto: {{ .Deplete }}\r\n \r\n"
|
||||
"depleteSoon" = "🔜 Se agotará pronto: {{ .Deplete }}\r\n\r\n"
|
||||
"backupTime" = "🗄 Hora de la Copia de Seguridad: {{ .Time }}\r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 Actualizado en: {{ .Time }}\r\n \r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 Actualizado en: {{ .Time }}\r\n\r\n"
|
||||
"yes" = "✅ Sí"
|
||||
"no" = "❌ No"
|
||||
|
||||
[tgbot.buttons]
|
||||
"closeKeyboard" = "❌ Cerrar Teclado"
|
||||
@@ -499,11 +529,13 @@
|
||||
"confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?"
|
||||
"confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?"
|
||||
"confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?"
|
||||
"confirmToggle" = " ✅ ¿Confirmar habilitar/deshabilitar usuario?"
|
||||
"dbBackup" = "Obtener Copia de Seguridad de BD"
|
||||
"serverUsage" = "Uso del Servidor"
|
||||
"getInbounds" = "Obtener Entradas"
|
||||
"depleteSoon" = "Pronto se Agotará"
|
||||
"clientUsage" = "Obtener Uso"
|
||||
"onlines" = "Clientes en línea"
|
||||
"commands" = "Comandos"
|
||||
"refresh" = "🔄 Actualizar"
|
||||
"clearIPs" = "❌ Limpiar IPs"
|
||||
@@ -511,14 +543,16 @@
|
||||
"selectTGUser" = "👤 Seleccionar Usuario de Telegram"
|
||||
"selectOneTGUser" = "👤 Selecciona un usuario de telegram:"
|
||||
"resetTraffic" = "📈 Reiniciar Tráfico"
|
||||
"resetExpire" = "📅 Reiniciar Días de Vencimiento"
|
||||
"resetExpire" = "📅 Cambiar fecha de Vencimiento"
|
||||
"ipLog" = "🔢 Registro de IP"
|
||||
"ipLimit" = "🔢 Límite de IP"
|
||||
"setTGUser" = "👤 Establecer Usuario de Telegram"
|
||||
"toggle" = "🔘 Habilitar / Deshabilitar"
|
||||
"custom" = "🔢 Costumbre"
|
||||
"confirmNumber" = "✅ Confirmar : {{ .Num }}"
|
||||
"confirmNumber" = "✅ Confirmar: {{ .Num }}"
|
||||
"confirmNumberAdd" = "✅ Confirmar agregando: {{ .Num }}"
|
||||
"limitTraffic" = "🚧 Límite de tráfico"
|
||||
"getBanLogs" = "Registros de prohibición"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ ¡Exitosa!"
|
||||
@@ -538,5 +572,4 @@
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente."
|
||||
"enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente."
|
||||
"disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente."
|
||||
"askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: <b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserName" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su nombre de usuario o ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu nombre de usuario: <b>@{{ .TgUserName }}</b>\r\n\r\nSu ID de usuario: <b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: <code>{{ .TgUserID }}</code>"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"username" = "نام کاربری"
|
||||
"password" = "رمز عبور"
|
||||
"username" = "نامکاربری"
|
||||
"password" = "رمزعبور"
|
||||
"login" = "ورود"
|
||||
"confirm" = "تایید"
|
||||
"cancel" = "انصراف"
|
||||
@@ -12,7 +12,7 @@
|
||||
"protocol" = "پروتکل"
|
||||
"search" = "جستجو"
|
||||
"filter" = "فیلتر"
|
||||
"loading" = "در حال بروزرسانی..."
|
||||
"loading" = "...در حال بارگذاری"
|
||||
"second" = "ثانیه"
|
||||
"minute" = "دقیقه"
|
||||
"hour" = "ساعت"
|
||||
@@ -21,94 +21,94 @@
|
||||
"indefinite" = "نامحدود"
|
||||
"unlimited" = "نامحدود"
|
||||
"none" = "هیچ"
|
||||
"qrCode" = "QR کد"
|
||||
"qrCode" = "QRکد"
|
||||
"info" = "اطلاعات بیشتر"
|
||||
"edit" = "ویرایش"
|
||||
"delete" = "حذف"
|
||||
"reset" = "ریست"
|
||||
"copySuccess" = "با موفقیت کپی شد"
|
||||
"copySuccess" = "باموفقیت کپیشد"
|
||||
"sure" = "مطمئن"
|
||||
"encryption" = "رمزگذاری"
|
||||
"transmission" = "راه اتصال"
|
||||
"transmission" = "راهاتصال"
|
||||
"host" = "آدرس"
|
||||
"path" = "مسیر"
|
||||
"camouflage" = "استتار"
|
||||
"camouflage" = "مبهمسازی"
|
||||
"status" = "وضعیت"
|
||||
"enabled" = "فعال"
|
||||
"disabled" = "غیرفعال"
|
||||
"depleted" = "منقضی"
|
||||
"depletingSoon" = "در حال انقضا"
|
||||
"depletingSoon" = "درحالانقضا"
|
||||
"offline" = "آفلاین"
|
||||
"online" = "آنلاین"
|
||||
"domainName" = "آدرس دامنه"
|
||||
"monitor" = "آی پی اتصال"
|
||||
"certificate" = "گواهی دیجیتال"
|
||||
"fail" = "خطا"
|
||||
"monitor" = "آیپی اتصال"
|
||||
"certificate" = "گواهی"
|
||||
"fail" = "ناموفق"
|
||||
"success" = " موفق"
|
||||
"getVersion" = "دریافت ورژن"
|
||||
"getVersion" = "دریافت نسخه"
|
||||
"install" = "نصب"
|
||||
"clients" = "کاربران"
|
||||
"usage" = "استفاده"
|
||||
"secretToken" = "توکن امنیتی"
|
||||
"remained" = "باقیمانده"
|
||||
"remained" = "باقیمانده"
|
||||
"security" = "امنیت"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "وضعیت سیستم"
|
||||
"inbounds" = "سرویس ها"
|
||||
"dashboard" = "نمای کلی"
|
||||
"inbounds" = "ورودیها"
|
||||
"settings" = "تنظیمات پنل"
|
||||
"xray" = "الگوی ایکسری"
|
||||
"xray" = "پیکربندی ایکسری"
|
||||
"logout" = "خروج"
|
||||
"link" = "دیگر"
|
||||
"link" = "مدیریت"
|
||||
|
||||
[pages.login]
|
||||
"title" = "ورود به سیستم"
|
||||
"loginAgain" = "مدت زمان استفاده به اتمام رسیده ، لطفا دوباره وارد شوید"
|
||||
"title" = "خوشآمدید"
|
||||
"loginAgain" = "مدت زمان استفاده بهاتمامرسیده، لطفا دوباره وارد شوید"
|
||||
|
||||
[pages.login.toasts]
|
||||
"invalidFormData" = "اطلاعات وارد شده به صورت درست وارد نشده است"
|
||||
"emptyUsername" = "نام کاربری خالی میباشد"
|
||||
"emptyPassword" = "رمز عبور خالی میباشد"
|
||||
"wrongUsernameOrPassword" = "نام کاربری و رمز عبور اشتباه میباشد"
|
||||
"successLogin" = "خوش آمدید"
|
||||
"invalidFormData" = "اطلاعات بهدرستی وارد نشدهاست"
|
||||
"emptyUsername" = "لطفا یک نامکاربری وارد کنید"
|
||||
"emptyPassword" = "لطفا یک رمزعبور وارد کنید"
|
||||
"wrongUsernameOrPassword" = "نامکاربری یا رمزعبوراشتباهاست"
|
||||
"successLogin" = "ورود"
|
||||
|
||||
[pages.index]
|
||||
"title" = "وضعیت سیستم"
|
||||
"memory" = "حافظه رم"
|
||||
"hard" = "حافظه دیسک"
|
||||
"xrayStatus" = "وضعیت"
|
||||
"title" = "نمای کلی"
|
||||
"memory" = "RAM"
|
||||
"hard" = "Disk"
|
||||
"xrayStatus" = "وضعیتایکسری"
|
||||
"stopXray" = "توقف"
|
||||
"restartXray" = "شروع مجدد"
|
||||
"xraySwitch" = "تغییر ورژن"
|
||||
"xraySwitchClick" = "ورژن مورد نظر را انتخاب کنید"
|
||||
"xraySwitchClickDesk" = "لطفا با دقت انتخاب کنید ، در صورت انتخاب اشتباه امکان قطعی سیستم وجود دارد "
|
||||
"operationHours" = "آپ تایم سیستم"
|
||||
"systemLoad" = "بار سیستم"
|
||||
"restartXray" = "شروعمجدد"
|
||||
"xraySwitch" = "نسخه"
|
||||
"xraySwitchClick" = "نسخه مورد نظر را انتخاب کنید"
|
||||
"xraySwitchClickDesk" = "لطفا بادقت انتخاب کنید. درصورت انتخاب نسخه قدیمیتر، امکان ناهماهنگی با پیکربندی فعلی وجود دارد"
|
||||
"operationHours" = "مدتکارکرد"
|
||||
"systemLoad" = "بارسیستم"
|
||||
"systemLoadDesc" = "میانگین بار سیستم برای 1، 5 و 15 دقیقه گذشته"
|
||||
"connectionTcpCountDesc" = "مجموع اتصالات TCP در تمام کارت های شبکه"
|
||||
"connectionUdpCountDesc" = "مجموع اتصالات UDP در تمام کارت های شبکه"
|
||||
"connectionTcpCountDesc" = "در تمامشبکهها TCP مجموعاتصالات"
|
||||
"connectionUdpCountDesc" = "در تمامشبکهها UDP مجموعاتصالات"
|
||||
"connectionCount" = "تعداد کانکشن ها"
|
||||
"upSpeed" = "سرعت آپلود در حال حاضر سیستم"
|
||||
"downSpeed" = "سرعت دانلود در حال حاضر سیستم"
|
||||
"totalSent" = "جمع کل ترافیک آپلود مصرفی"
|
||||
"totalReceive" = "جمع کل ترافیک دانلود مصرفی"
|
||||
"xraySwitchVersionDialog" = "تغییر ورژن"
|
||||
"xraySwitchVersionDialogDesc" = "آیا از تغییر ورژن مطمئن هستین"
|
||||
"dontRefresh" = "در حال نصب ، لطفا رفرش نکنید "
|
||||
"logs" = "گزارش ها"
|
||||
"config" = "تنظیمات"
|
||||
"backup" = "پشتیبان گیری و بازیابی"
|
||||
"backupTitle" = "پشتیبان گیری و بازیابی دیتابیس"
|
||||
"backupDescription" = "به یاد داشته باشید که قبل از وارد کردن یک دیتابیس جدید، نسخه پشتیبان تهیه کنید"
|
||||
"exportDatabase" = "دانلود دیتابیس"
|
||||
"importDatabase" = "آپلود دیتابیس"
|
||||
"upSpeed" = "سرعت کلی آپلود در تمامشبکهها"
|
||||
"downSpeed" = "سرعت کلی دانلود در تمامشبکهها"
|
||||
"totalSent" = "مجموع ترافیک ارسالشده پساز شروعبهکار سیستمعامل"
|
||||
"totalReceive" = "مجموع ترافیک دریافتشده پساز شروعبهکار سیستمعامل"
|
||||
"xraySwitchVersionDialog" = "تغییر نسخه ایکسری"
|
||||
"xraySwitchVersionDialogDesc" = "آیا از تغییر نسخه مطمئن هستید؟"
|
||||
"dontRefresh" = "در حال نصب، لطفا صفحه را رفرش نکنید"
|
||||
"logs" = "گزارشها"
|
||||
"config" = "پیکربندی"
|
||||
"backup" = "پشتیبانگیری"
|
||||
"backupTitle" = "پشتیبانگیری دیتابیس"
|
||||
"backupDescription" = "توصیهمیشود قبلاز واردکردن یک دیتابیس جدید، نسخه پشتیبان تهیه کنید"
|
||||
"exportDatabase" = " پشتیبانگیری"
|
||||
"importDatabase" = "بازگرداندن"
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "کاربران"
|
||||
"totalDownUp" = "جمع آپلود/دانلود"
|
||||
"totalUsage" = "جمع کل"
|
||||
"inboundCount" = "تعداد سرویس ها"
|
||||
"operate" = "فهرست"
|
||||
"totalDownUp" = "دریافت/ارسال کل"
|
||||
"totalUsage" = "مصرف کل"
|
||||
"inboundCount" = "کل ورودیها"
|
||||
"operate" = "عملیات"
|
||||
"enable" = "فعال"
|
||||
"remark" = "نام"
|
||||
"protocol" = "پروتکل"
|
||||
@@ -116,71 +116,71 @@
|
||||
"traffic" = "ترافیک"
|
||||
"details" = "توضیحات"
|
||||
"transportConfig" = "نحوه اتصال"
|
||||
"expireDate" = "تاریخ انقضا"
|
||||
"expireDate" = "مدت زمان"
|
||||
"resetTraffic" = "ریست ترافیک"
|
||||
"addInbound" = "اضافه کردن سرویس"
|
||||
"addInbound" = "افزودن ورودی"
|
||||
"generalActions" = "عملیات کلی"
|
||||
"create" = "اضافه کردن"
|
||||
"create" = "افزودن"
|
||||
"update" = "ویرایش"
|
||||
"modifyInbound" = "ویرایش سرویس"
|
||||
"deleteInbound" = "حذف سرویس"
|
||||
"deleteInboundContent" = "آیا مطمئن به حذف سرویس هستید ؟"
|
||||
"modifyInbound" = "ویرایش ورودی"
|
||||
"deleteInbound" = "حذف ورودی"
|
||||
"deleteInboundContent" = "آیا مطمئن به حذف ورودی هستید؟"
|
||||
"deleteClient" = "حذف کاربر"
|
||||
"deleteClientContent" = "آیا مطمئن به حذف کاربر هستید ؟"
|
||||
"resetTrafficContent" = "آیا مطمئن به ریست ترافیک هستید ؟"
|
||||
"deleteClientContent" = "آیا مطمئن به حذف کاربر هستید؟"
|
||||
"resetTrafficContent" = "آیا مطمئن به ریست ترافیک هستید؟"
|
||||
"copyLink" = "کپی لینک"
|
||||
"address" = "آدرس"
|
||||
"network" = "شبکه"
|
||||
"destinationPort" = "پورت مقصد"
|
||||
"targetAddress" = "آدرس مقصد"
|
||||
"monitorDesc" = "به طور پیش فرض خالی بگذارید"
|
||||
"meansNoLimit" = "یعنی بدون محدودیت"
|
||||
"totalFlow" = "کل ترافیک"
|
||||
"leaveBlankToNeverExpire" = "خالی بگذارید تا هرگز منقضی نشود"
|
||||
"noRecommendKeepDefault" = "توصیه می شود به عنوان پیش فرض حفظ شود"
|
||||
"certificatePath" = "مسیر فایل گواهی"
|
||||
"certificateContent" = "محتوای فایل گواهی"
|
||||
"monitorDesc" = "بهطور پیشفرض خالیبگذارید"
|
||||
"meansNoLimit" = " = واحد: گیگابایت) نامحدود)"
|
||||
"totalFlow" = "ترافیک کل"
|
||||
"leaveBlankToNeverExpire" = "برای منقضینشدن خالیبگذارید"
|
||||
"noRecommendKeepDefault" = "توصیهمیشود بهطور پیشفرض حفظشود"
|
||||
"certificatePath" = "مسیر فایل"
|
||||
"certificateContent" = "محتوای فایل"
|
||||
"publicKeyPath" = "مسیر کلید عمومی"
|
||||
"publicKeyContent" = "محتوای کلید عمومی"
|
||||
"keyPath" = "مسیر کلید خصوصی"
|
||||
"keyContent" = "محتوای کلید خصوصی"
|
||||
"clickOnQRcode" = "برای کپی بر روی کد تصویری کلیک کنید"
|
||||
"clickOnQRcode" = "برای کپی بر روی کدتصویری کلیک کنید"
|
||||
"client" = "کاربر"
|
||||
"export" = "استخراج لینکها"
|
||||
"clone" = "شبیه سازی"
|
||||
"cloneInbound" = "ایجاد"
|
||||
"cloneInboundContent" = "همه موارد این ورودی بجز پورت ، ای پی و کلاینت ها شبیه سازی خواهند شد"
|
||||
"export" = "استخراج لینکها"
|
||||
"clone" = "شبیهسازی"
|
||||
"cloneInbound" = "شبیهسازی ورودی"
|
||||
"cloneInboundContent" = "همه موارد این ورودی بجز پورت، آیپی و کاربرها شبیهسازی خواهند شد"
|
||||
"cloneInboundOk" = "ساختن شبیه ساز"
|
||||
"resetAllTraffic" = "ریست ترافیک کل سرویس ها"
|
||||
"resetAllTrafficTitle" = "ریست ترافیک کل سرویس ها"
|
||||
"resetAllTrafficContent" = "آیا مطمئن هستید که میخواهید تمام ترافیک سرویس ها را ریست کنید؟"
|
||||
"resetAllTraffic" = "ریست ترافیک کل ورودیها"
|
||||
"resetAllTrafficTitle" = "ریست ترافیک کل ورودیها"
|
||||
"resetAllTrafficContent" = "آیا مطمئن به ریست ترافیک تمام ورودیها هستید؟"
|
||||
"resetInboundClientTraffics" = "ریست ترافیک کاربران"
|
||||
"resetInboundClientTrafficTitle" = "ریست ترافیک کل کاربران"
|
||||
"resetInboundClientTrafficContent" = "آیا مطمئن هستید که میخواهید تمام ترافیک کاربران این سرویس را ریست کنید؟"
|
||||
"resetAllClientTraffics" = "ریست ترافیک کاربران"
|
||||
"resetInboundClientTrafficTitle" = "ریست ترافیک کاربران"
|
||||
"resetInboundClientTrafficContent" = "آیا مطمئن به ریست ترافیک تمام کاربران این ورودی هستید؟"
|
||||
"resetAllClientTraffics" = "ریست ترافیک کل کاربران"
|
||||
"resetAllClientTrafficTitle" = "ریست ترافیک کل کاربران"
|
||||
"resetAllClientTrafficContent" = "آیا مطمئن هستید که میخواهید تمام ترافیک کاربران را ریست کنید؟"
|
||||
"resetAllClientTrafficContent" = "آیا مطمئن به ریست ترافیک تمام کاربران هستید؟"
|
||||
"delDepletedClients" = "حذف کاربران منقضی"
|
||||
"delDepletedClientsTitle" = "حذف کاربران منقضی"
|
||||
"delDepletedClientsContent" = "آیا مطمئن هستید مه میخواهید تمامی کاربران منقضی شده را حذف کنید؟"
|
||||
"delDepletedClientsContent" = "آیا مطمئن به حذف تمام کاربران منقضیشده هستید؟"
|
||||
"email" = "ایمیل"
|
||||
"emailDesc" = "ایمیل باید کاملا منحصر به فرد باشد"
|
||||
"IPLimit" = "محدودیت ای پی"
|
||||
"IPLimitDesc" = "غیرفعال کردن ورودی در صورت بیش از تعداد وارد شده (0 برای غیرفعال کردن محدودیت ای پی )"
|
||||
"IPLimitlog" = "گزارش ها"
|
||||
"IPLimitlogDesc" = "گزارش سابقه ای پی (قبل از فعال کردن ورودی پس از غیرفعال شدن توسط محدودیت ای پی، باید گزارش را پاک کنید)"
|
||||
"IPLimitlogclear" = "پاک کردن گزارش ها"
|
||||
"emailDesc" = "باید یک ایمیل یکتا باشد"
|
||||
"IPLimit" = "محدودیت آیپی"
|
||||
"IPLimitDesc" = "(اگر تعداد از مقدار تنظیم شده بیشتر شود، ورودی را غیرفعال می کند. (0 = غیرفعال"
|
||||
"IPLimitlog" = "گزارشها"
|
||||
"IPLimitlogDesc" = "گزارش تاریخچه آیپی. برای فعال کردن ورودی پس از غیرفعال شدن، گزارش را پاک کنید"
|
||||
"IPLimitlogclear" = "پاک کردن گزارشها"
|
||||
"setDefaultCert" = "استفاده از گواهی پنل"
|
||||
"xtlsDesc" = "هسته Xray باید 1.7.5 باشد"
|
||||
"realityDesc" = "هسته Xray باید 1.8.0 و بالاتر باشد"
|
||||
"telegramDesc" = "از آیدی تلگرام بدون @ یا آیدی چت استفاده کنید (می توانید آن را از اینجا دریافت کنید @userinfobot یا در ربات دستور '/id' را وارد کنید)"
|
||||
"subscriptionDesc" = "می توانید ساب لینک خود را در جزئیات پیدا کنید، همچنین می توانید از همین نام برای چندین کانفیگ استفاده کنید"
|
||||
"xtlsDesc" = "ایکسری باید 1.7.5 باشد"
|
||||
"realityDesc" = "ایکسری باید +1.8.0 باشد"
|
||||
"telegramDesc" = "دریافت کنید ('/id'یا (دستور (@userinfobot) آیدی(های) چت تلگرام مدیر، از"
|
||||
"subscriptionDesc" = "شما میتوانید لینک سابسکربپشن خودرا در 'جزئیات' پیدا کنید، همچنین میتوانید از همین نام برای چندین کاربر استفادهکنید"
|
||||
"info" = "اطلاعات"
|
||||
"same" = "همسان"
|
||||
"inboundData" = "دادههای سرویس"
|
||||
"inboundData" = "دادههای ورودی"
|
||||
"copyToClipboard" = "کپی در حافظه"
|
||||
"import" = "وارد کردن"
|
||||
"importInbound" = "وارد کردن یک سرویس"
|
||||
"import" = "افزودن"
|
||||
"importInbound" = "افزودن یک ورودی"
|
||||
|
||||
[pages.client]
|
||||
"add" = "کاربر جدید"
|
||||
@@ -188,193 +188,208 @@
|
||||
"submitAdd" = "اضافه کردن"
|
||||
"submitEdit" = "ذخیره تغییرات"
|
||||
"clientCount" = "تعداد کاربران"
|
||||
"bulk" = "انبوه سازی"
|
||||
"bulk" = "انبوهسازی"
|
||||
"method" = "روش"
|
||||
"first" = "از"
|
||||
"last" = "تا"
|
||||
"prefix" = "پیشوند"
|
||||
"postfix" = "پسوند"
|
||||
"delayedStart" = "شروع بعد از اولین استفاده"
|
||||
"expireDays" = "روزهای اعتبار"
|
||||
"delayedStart" = "شروعپسازاولیناستفاده"
|
||||
"expireDays" = "مدت زمان"
|
||||
"days" = "(روز)"
|
||||
"renew" = "تمدید خودکار"
|
||||
"renewDesc" = "روزهای تمدید خودکار پس از انقضا. 0 = غیرفعال"
|
||||
"renewDesc" = "(تمدید خودکار پساز انقضا. (0 = غیرفعال)(واحد: روز"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "Obtain"
|
||||
"obtain" = "فراهمسازی"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "درخواست سربرگ"
|
||||
"request" = "درخواست"
|
||||
"response" = "پاسخ"
|
||||
"name" = "نام"
|
||||
"value" = "مقدار"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "ورژن درخواست"
|
||||
"requestMethod" = "متد درخواست"
|
||||
"requestPath" = "مسیر درخواست"
|
||||
"responseVersion" = "ورژن پاسخ"
|
||||
"responseStatus" = "وضعیت پاسخ"
|
||||
"responseStatusDescription" = "توضیحات وضعیت پاسخ"
|
||||
"version" = "نسخه"
|
||||
"method" = "متد"
|
||||
"path" = "مسیر"
|
||||
"status" = "وضعیت"
|
||||
"statusDescription" = "توضیحات وضعیت"
|
||||
"requestHeader" = "سربرگ درخواست"
|
||||
"responseHeader" = "سربرگ پاسخ"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
"encryption" = "رمزنگاری"
|
||||
|
||||
[pages.settings]
|
||||
"title" = "تنظیمات"
|
||||
"title" = "تنظیمات پنل"
|
||||
"save" = "ذخیره"
|
||||
"infoDesc" = "برای اعمال تغییرات در این بخش باید پس از ذخیره کردن، پنل را ریستارت کنید"
|
||||
"restartPanel" = "ریستارت پنل"
|
||||
"restartPanelDesc" = "آیا مطمئن هستید که می خواهید پنل را دوباره راه اندازی کنید؟ برای راه اندازی مجدد روی OK کلیک کنید. اگر بعد از 3 ثانیه نمی توانید به پنل دسترسی پیدا کنید، لطفاً برای مشاهده اطلاعات گزارش پانل به سرور برگردید"
|
||||
"restartPanelDesc" = "آیا مطمئن به ریستارت پنل هستید؟ اگر پساز ریستارت نمیتوانید به پنل دسترسی پیدا کنید، لطفاً گزارشهای موجود در اسکریپت پنل را بررسی کنید"
|
||||
"actions" = "عملیات ها"
|
||||
"resetDefaultConfig" = "برگشت به تنظیمات پیشفرض"
|
||||
"panelSettings" = "تنظیمات پنل"
|
||||
"securitySettings" = "تنظیمات امنیتی"
|
||||
"TGBotSettings" = "تنظیمات ربات تلگرام"
|
||||
"panelListeningIP" = "محدودیت آی پی پنل"
|
||||
"panelListeningIPDesc" = "برای استفاده از تمام آیپیها به طور پیش فرض خالی بگذارید"
|
||||
"panelListeningDomain" = "محدودیت دامین پنل"
|
||||
"panelListeningDomainDesc" = "برای استفاده از تمام دامنهها و آیپیها به طور پیش فرض خالی بگذارید"
|
||||
"panelPort" = "پورت پنل"
|
||||
"panelPortDesc" = "پورت مورد استفاده برای نمایش این پنل"
|
||||
"publicKeyPath" = "مسیر فایل گواهی کلید عمومی پنل"
|
||||
"publicKeyPathDesc" = "باید یک مسیر مطلق باشد که با / شروع می شود "
|
||||
"privateKeyPath" = "مسیر فایل گواهی کلید خصوصی پنل"
|
||||
"privateKeyPathDesc" = "باید یک مسیر مطلق باشد که با / شروع می شود "
|
||||
"panelUrlPath" = "آدرس روت پنل"
|
||||
"panelUrlPathDesc" = "باید با '/' شروع شود و با '/' تمام شود"
|
||||
"resetDefaultConfig" = "برگشت به پیشفرض"
|
||||
"panelSettings" = "پیکربندی"
|
||||
"securitySettings" = "احرازهویت"
|
||||
"TGBotSettings" = "ربات تلگرام"
|
||||
"panelListeningIP" = "آدرس آیپی"
|
||||
"panelListeningIPDesc" = "آدرس آیپی برای وب پنل. برای گوشدادن بهتمام آیپیها خالیبگذارید"
|
||||
"panelListeningDomain" = "نام دامنه"
|
||||
"panelListeningDomainDesc" = "آدرس دامنه برای وب پنل. برای گوش دادن بهتمام دامنهها و آیپیها خالیبگذارید"
|
||||
"panelPort" = "پورت"
|
||||
"panelPortDesc" = "شماره پورت برای وب پنل. باید پورت استفاده نشدهباشد"
|
||||
"publicKeyPath" = "مسیر کلید عمومی"
|
||||
"publicKeyPathDesc" = "مسیر فایل کلیدعمومی برای وب پنل. با '/' شروعمیشود"
|
||||
"privateKeyPath" = "مسیر کلید خصوصی"
|
||||
"privateKeyPathDesc" = "مسیر فایل کلیدخصوصی برای وب پنل. با '/' شروعمیشود"
|
||||
"panelUrlPath" = "URI مسیر"
|
||||
"panelUrlPathDesc" = "برای وب پنل. با '/' شروع و با '/' خاتمه مییابد URI مسیر"
|
||||
"pageSize" = "اندازه صفحه بندی جدول"
|
||||
"pageSizeDesc" = "اندازه صفحه را برای جدول سرویس ها تعریف کنید. 0: غیرفعال"
|
||||
"remarkModel" = "نام کانفیگ و جداکننده"
|
||||
"sampleRemark" = "نمونه نام"
|
||||
"oldUsername" = "نام کاربری فعلی"
|
||||
"currentPassword" = "رمز عبور فعلی"
|
||||
"newUsername" = "نام کاربری جدید"
|
||||
"newPassword" = "رمز عبور جدید"
|
||||
"telegramBotEnable" = "فعالسازی ربات تلگرام"
|
||||
"telegramBotEnableDesc" = "از طریق بات تلگرام به امکانات این پنل متصل شوید"
|
||||
"pageSizeDesc" = "(اندازه صفحه برای جدول ورودیها.(0 = غیرفعال"
|
||||
"remarkModel" = "نامکانفیگ و جداکننده"
|
||||
"datepicker" = "نوع تقویم"
|
||||
"datepickerDescription" = "وظایف برنامه ریزی شده بر اساس این تقویم اجرا میشود"
|
||||
"sampleRemark" = "نمونهنام"
|
||||
"oldUsername" = "نامکاربری فعلی"
|
||||
"currentPassword" = "رمزعبور فعلی"
|
||||
"newUsername" = "نامکاربری جدید"
|
||||
"newPassword" = "رمزعبور جدید"
|
||||
"telegramBotEnable" = "فعالسازی ربات تلگرام"
|
||||
"telegramBotEnableDesc" = "ربات تلگرام را فعال میکند"
|
||||
"telegramToken" = "توکن تلگرام"
|
||||
"telegramTokenDesc" = "توکن را باید از مدیر بات های تلگرام دریافت کنید @botfather"
|
||||
"telegramChatId" = "آی دی تلگرام مدیریت"
|
||||
"telegramChatIdDesc" = "از @userinfobot یا دستور '/id' در ربات برای دریافت شناسه های چت خود استفاده کنید. با استفاده از کاما میتونید چند آی دی را از هم جدا کنید. "
|
||||
"telegramNotifyTime" = "مدت زمان نوتیفیکیشن ربات تلگرام"
|
||||
"telegramNotifyTimeDesc" = "از فرمت زمان بندی لینوکس استفاده کنید "
|
||||
"tgNotifyBackup" = "پشتیبان گیری از پایگاه داده"
|
||||
"tgNotifyBackupDesc" = "ارسال کپی فایل پایگاه داده به همراه گزارش دوره ای"
|
||||
"telegramTokenDesc" = "دریافت کنید @botfather توکن را میتوانید از"
|
||||
"telegramProxy" = "SOCKS پراکسی"
|
||||
"telegramProxyDesc" = "را برای اتصال به تلگرام فعال می کند SOCKS5 پراکسی"
|
||||
"telegramChatId" = "آیدی چت مدیر"
|
||||
"telegramChatIdDesc" = "دریافت کنید ('/id'یا (دستور (@userinfobot) آیدی(های) چت تلگرام مدیر، از"
|
||||
"telegramNotifyTime" = "زمان نوتیفیکیشن"
|
||||
"telegramNotifyTimeDesc" = "زماناطلاعرسانی ربات تلگرام برای گزارش های دورهای. از فرمت زمانبندی لینوکس استفادهکنید"
|
||||
"tgNotifyBackup" = "پشتیبانگیری از دیتابیس"
|
||||
"tgNotifyBackupDesc" = "فایل پشتیباندیتابیس را بههمراه گزارش ارسال میکند"
|
||||
"tgNotifyLogin" = "اعلان ورود"
|
||||
"tgNotifyLoginDesc" = "نام کاربری، آدرس ای پی، و زمان وقتی که فردی سعی میکند به پنل شما وارد شود نمایش میدهد"
|
||||
"tgNotifyLoginDesc" = "نامکاربری، آدرس آیپی، و زمان ورود، فردی که سعی میکند وارد پنل شود را نمایش میدهد"
|
||||
"sessionMaxAge" = "بیشینه زمان جلسه وب"
|
||||
"sessionMaxAgeDesc" = "بیشینه زمانی که میتوانید لاگین بمانید (واحد: دقیقه)"
|
||||
"sessionMaxAgeDesc" = "(بیشینه زمانی که میتوانید لاگین بمانید. (واحد: دقیقه"
|
||||
"expireTimeDiff" = "آستانه زمان باقی مانده"
|
||||
"expireTimeDiffDesc" = "فاصله زمانی هشدار تا رسیدن به زمان انقضا (واحد: روز)"
|
||||
"expireTimeDiffDesc" = "(فاصله زمانی هشدار تا رسیدن به زمان انقضا. (واحد: روز"
|
||||
"trafficDiff" = "آستانه ترافیک باقی مانده"
|
||||
"trafficDiffDesc" = "فاصله زمانی هشدار تا رسیدن به اتمام ترافیک (واحد: گیگابایت)"
|
||||
"tgNotifyCpu" = "آستانه هشدار درصد پردازنده"
|
||||
"tgNotifyCpuDesc" = "این ربات تلگرام در صورت استفاده پردازنده بیشتر از این درصد برای شما پیام ارسال می کند.(واحد: درصد)"
|
||||
"timeZone" = "منظقه زمانی"
|
||||
"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه زمانی اجرا می شوند. پنل را مجدداً راه اندازی می کند تا اعمال شود"
|
||||
"trafficDiffDesc" = "(فاصله زمانی هشدار تا رسیدن به اتمام ترافیک. (واحد: گیگابایت"
|
||||
"tgNotifyCpu" = "آستانه هشدار بار پردازنده"
|
||||
"tgNotifyCpuDesc" = "(اگر بار روی پردازنده ازاین آستانه فراتر رفت، برای شما پیام ارسال میشود. (واحد: درصد"
|
||||
"timeZone" = "منطقه زمانی"
|
||||
"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقهزمانی اجرا میشود"
|
||||
"subSettings" = "سابسکریپشن"
|
||||
"subEnable" = "فعال کردن سرویس"
|
||||
"subEnableDesc" = "ویژگی سابسکریپشن با پیکربندی جداگانه"
|
||||
"subListen" = "محدودیت آیپی"
|
||||
"subListenDesc" = "برای استفاده از همه آیپی ها به طور پیش فرض خالی بگذارید"
|
||||
"subPort" = "پورت سرویس"
|
||||
"subPortDesc" = "شماره پورت برای ارائه خدمات سابسکریپشن باید خالی باشد"
|
||||
"subCertPath" = "مسیر فایل کلید عمومی گواهی سابسکریپشن"
|
||||
"subCertPathDesc" = "یک مسیر مطلق که با '/' شروع می شود را پر کنید."
|
||||
"subKeyPath" = "مسیر فایل کلید خصوصی گواهی سابسکریپشن"
|
||||
"subKeyPathDesc" = "یک مسیر مطلق که با '/' شروع می شود را پر کنید."
|
||||
"subPath" = "مسیر ریشه سابسکریپشن"
|
||||
"subPathDesc" = "باید با '/' شروع شود و با '/' ختم شود."
|
||||
"subDomain" = "دامنه مخصوص سابسکریپشن"
|
||||
"subDomainDesc" = "برای نظارت بر همه دامنه ها و آیپی ها به طور پیش فرض خالی بگذارید"
|
||||
"subUpdates" = "فاصله به روز رسانی های سابسکریپشن"
|
||||
"subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر"
|
||||
"subEncrypt" = "رمزگذاری کانفیگ ها"
|
||||
"subEncryptDesc" = "رمزگذاری کانفیگ های بازگشتی سابسکریپشن"
|
||||
"subEnable" = "فعالسازی سرویس سابسکریپشن"
|
||||
"subEnableDesc" = " سرویس سابسکریپشن را فعالمیکند"
|
||||
"subListen" = "آدرس آیپی"
|
||||
"subListenDesc" = "آدرس آیپی برای سرویس سابسکریپشن. برای گوش دادن بهتمام آیپیها خالیبگذارید"
|
||||
"subPort" = "پورت"
|
||||
"subPortDesc" = "شماره پورت برای سرویس سابسکریپشن. باید پورت استفاده نشدهباشد"
|
||||
"subCertPath" = "مسیر کلید عمومی"
|
||||
"subCertPathDesc" = "مسیر فایل کلیدعمومی برای سرویس سابیکریپشن. با '/' شروعمیشود"
|
||||
"subKeyPath" = "مسیر کلید خصوصی"
|
||||
"subKeyPathDesc" = "مسیر فایل کلیدخصوصی برای سرویس سابسکریپشن. با '/' شروعمیشود"
|
||||
"subPath" = "URI مسیر"
|
||||
"subPathDesc" = "برای سرویس سابسکریپشن. با '/' شروع و با '/' خاتمه مییابد URI مسیر"
|
||||
"subDomain" = "نام دامنه"
|
||||
"subDomainDesc" = "آدرس دامنه برای سرویس سابسکریپشن. برای گوش دادن به تمام دامنهها و آیپیها خالیبگذارید"
|
||||
"subUpdates" = "فاصله بروزرسانی سابسکریپشن"
|
||||
"subUpdatesDesc" = "(فاصله مابین بروزرسانی در برنامههای کاربری. (واحد: ساعت"
|
||||
"subEncrypt" = "کدگذاری"
|
||||
"subEncryptDesc" = "کدگذاری خواهدشد Base64 محتوای برگشتی سرویس سابسکریپشن برپایه"
|
||||
"subShowInfo" = "نمایش اطلاعات مصرف"
|
||||
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد"
|
||||
"subURI" = "آدرس پایه پروکسی معکوس"
|
||||
"subURIDesc" = "آدرس پایه سابسکریپشن را برای استفاده در پشت پراکسی ها تغییر میدهد"
|
||||
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در برنامههای کاربری نمایش میدهد"
|
||||
"subURI" = "پروکسی معکوس URI مسیر"
|
||||
"subURIDesc" = "سابسکریپشن را برای استفاده در پشت پراکسیها تغییر میدهد URI مسیر"
|
||||
|
||||
[pages.xray]
|
||||
"title" = "الگوها"
|
||||
"save" = "ذخیره تنظیمات"
|
||||
"title" = "پیکربندی ایکسری"
|
||||
"save" = "ذخیره"
|
||||
"restart" = "ریستارت ایکسری"
|
||||
"basicTemplate" = "بخش الگو پایه"
|
||||
"advancedTemplate" = "بخش الگو پیشرفته"
|
||||
"generalConfigs" = "تنظیمات عمومی"
|
||||
"generalConfigsDesc" = "این تنظیمات میتواند ترافیک کلی سرویس را متاثر کند"
|
||||
"blockConfigs" = "مسدود سازی"
|
||||
"blockConfigsDesc" = "این گزینه ها از اتصال کاربران به پروتکل ها و وب سایت های خاص جلوگیری می کند"
|
||||
"blockCountryConfigs" = "تنظیمات برای مسدودسازی کشورها"
|
||||
"blockCountryConfigsDesc" = "این گزینه اتصال کاربران به دامنه های کشوری خاص را مسدود می کند"
|
||||
"directCountryConfigs" = "تنظیمات برای اتصال مستقیم کشورها"
|
||||
"directCountryConfigsDesc" = "این گزینه کاربران را به دامنه های کشوری خاص را به طور مستقیم، متصل می کند"
|
||||
"ipv4Configs" = "تنظیمات برای IPv4"
|
||||
"ipv4ConfigsDesc" = "این گزینه فقط از طریق آیپی ورژن ۴ به دامنه های هدف هدایت می شود"
|
||||
"warpConfigs" = "تنظیمات برای WARP"
|
||||
"warpConfigsDesc" = "هشدار: قبل از استفاده از این گزینه، WARP را در حالت پراکسی socks5 با دنبال کردن مراحل در GitHub پنل روی سرور خود نصب کنید. WARP ترافیک را از طریق سرورهای Cloudflare به وب سایت ها هدایت می کند"
|
||||
"Template" = "تنظیمات الگو ایکس ری"
|
||||
"TemplateDesc" = "فایل پیکربندی ایکس ری نهایی بر اساس این الگو ایجاد میشود. لطفاً این را تغییر ندهید مگر اینکه دقیقاً بدانید که چه کاری انجام می دهید!"
|
||||
"FreedomStrategy" = "روش استفاده از شبکه خروجی مستقیم"
|
||||
"FreedomStrategyDesc" = "تعیین روش استفاده از خروجی برای پرتکل مستقیم"
|
||||
"RoutingStrategy" = "پیکربندی استراتژی حل دامنه در مسیریابی"
|
||||
"RoutingStrategyDesc" = "تعیین استراتژی مسیریابی کلی برای پیدا کردن دامنه"
|
||||
"Torrent" = "فیلتر کردن بیت تورنت"
|
||||
"TorrentDesc" = "الگوی تنظیمات را برای فیلتر کردن پروتکل بیت تورنت برای کاربران تغییر میدهد"
|
||||
"PrivateIp" = "جلوگیری از اتصال آیپی های خصوصی یا محلی"
|
||||
"PrivateIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های خصوصی یا محلی و بسته های سرگردان تغییر میدهد"
|
||||
"Ads" = "مسدود کردن تبلیغات"
|
||||
"AdsDesc" = "الگوی تنظیمات را برای مسدود کردن تبلیغات تغییر میدهد"
|
||||
"Family" = "فعال کردن حالت خانواده"
|
||||
"FamilyDesc" = "برای جلوگیری از ارتباط با وبسایت های ناامن"
|
||||
"Speedtest" = "جلوگیری از اتصال به سایت های تست سرعت"
|
||||
"SpeedtestDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال به سایت های تست سرعت تغییر میدهد"
|
||||
"IRIp" = "جلوگیری از اتصال آیپی های ایران"
|
||||
"IRIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های ایران تغییر میدهد"
|
||||
"IRDomain" = "جلوگیری از اتصال دامنه های ایران"
|
||||
"IRDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های ایران تغییر میدهد"
|
||||
"ChinaIp" = "جلوگیری از اتصال آیپی های چین"
|
||||
"ChinaIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های چین تغییر میدهد"
|
||||
"ChinaDomain" = "جلوگیری از اتصال دامنه های چین"
|
||||
"ChinaDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های چین تغییر میدهد"
|
||||
"RussiaIp" = "جلوگیری از اتصال آیپی های روسیه"
|
||||
"RussiaIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های روسیه تغییر میدهد"
|
||||
"RussiaDomain" = "جلوگیری از اتصال دامنه های روسیه"
|
||||
"RussiaDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های روسیه تغییر میدهد"
|
||||
"DirectIRIp" = "ارتباط مستقیم به آیپی های ایران"
|
||||
"DirectIRIpDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به آیپی های ایران تغییر میدهد"
|
||||
"DirectIRDomain" = "ارتباط مستقیم به دامنه های ایران"
|
||||
"DirectIRDomainDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به دامنه های ایران تغییر میدهد"
|
||||
"DirectChinaIp" = "ارتباط مستقیم به آیپی های چین"
|
||||
"DirectChinaIpDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به آیپی های چین تغییر میدهد"
|
||||
"DirectChinaDomain" = "ارتباط مستقیم به دامنه های چین"
|
||||
"DirectChinaDomainDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به دامنه های چین تغییر میدهد"
|
||||
"DirectRussiaIp" = "ارتباط مستقیم به آیپی های روسیه"
|
||||
"DirectRussiaIpDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به آیپی های روسیه تغییر میدهد"
|
||||
"DirectRussiaDomain" = "ارتباط مستقیم به دامنه های روسیه"
|
||||
"DirectRussiaDomainDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به دامنه های روسیه تغییر میدهد"
|
||||
"GoogleIPv4" = "استفاده از آیپی ورژن 4 برای اتصال به گوگل"
|
||||
"GoogleIPv4Desc" = "مسیردهی جدید برای اتصال به گوگل با آیپی ورژن 4 اضافه میکند"
|
||||
"NetflixIPv4" = "استفاده از آیپی ورژن 4 برای اتصال به نتفلیکس"
|
||||
"NetflixIPv4Desc" = "مسیردهی جدید برای اتصال به نتفلیکس با آیپی ورژن 4 اضافه میکند"
|
||||
"GoogleWARP" = "مسیردهی گوگل به WARP"
|
||||
"GoogleWARPDesc" = "مسیردهی جدید برای اتصال به گوگل به WARP اضافه میکند"
|
||||
"OpenAIWARP" = "مسیردهی OpenAI (ChatGPT) به WARP"
|
||||
"OpenAIWARPDesc" = "مسیردهی جدید برای اتصال به OpenAI (ChatGPT) به WARP اضافه میکند"
|
||||
"NetflixWARP" = "مسیردهی نتفلیکس به WARP"
|
||||
"NetflixWARPDesc" = "مسیردهی جدید برای اتصال به نتفلیکس به WARP اضافه میکند"
|
||||
"SpotifyWARP" = "مسیردهی اسپاتیفای به WARP"
|
||||
"SpotifyWARPDesc" = "مسیردهی جدید برای اتصال به اسپاتیفای به WARP اضافه میکند"
|
||||
"IRWARP" = "مسیردهی دامنه های ایران به WARP"
|
||||
"IRWARPDesc" = "مسیردهی جدید برای اتصال به دامنه های ایران به WARP اضافه میکند"
|
||||
"basicTemplate" = "پایه"
|
||||
"advancedTemplate" = "پیشرفته"
|
||||
"generalConfigs" = "استراتژی کلی"
|
||||
"generalConfigsDesc" = "این گزینهها استراتژی کلی ترافیک را تعیین میکنند"
|
||||
"blockConfigs" = "سپر محافظ"
|
||||
"blockConfigsDesc" = "این گزینهها ترافیک را بر اساس پروتکلهای درخواستی خاص، و وب سایتها مسدود میکند"
|
||||
"blockCountryConfigs" = "مسدودسازی کشور"
|
||||
"blockCountryConfigsDesc" = "این گزینهها ترافیک را بر اساس کشور درخواستی خاص مسدود میکند"
|
||||
"directCountryConfigs" = "اتصال مستقیم کشور"
|
||||
"directCountryConfigsDesc" = "این گزینهها ترافیک را بر اساس کشور درخواستی خاص بصورت مستقیم ارسال میکند"
|
||||
"ipv4Configs" = "IPv4 مسیریابی"
|
||||
"ipv4ConfigsDesc" = "این گزینهها ترافیک را از طریق آیپینسخه4 به مقصد هدایت میکند"
|
||||
"warpConfigs" = "WARP مسیریابی"
|
||||
"warpConfigsDesc" = "طبق راهنما نصب کنید SOCKS5 این گزینهها ترافیک را از طریق وارپ کلادفلر به مقصد هدایت میکند. ابتدا، وارپ را در حالت پراکسی"
|
||||
"Template" = "پیکربندی پیشرفته الگو ایکسری"
|
||||
"TemplateDesc" = "فایل پیکربندی نهایی ایکسری بر اساس این الگو ایجاد میشود"
|
||||
"FreedomStrategy" = "Freedom استراتژی پروتکل"
|
||||
"FreedomStrategyDesc" = "تعیین میکند Freedom استراتژی خروجی شبکه را برای پروتکل"
|
||||
"RoutingStrategy" = "استراتژی کلی مسیریابی"
|
||||
"RoutingStrategyDesc" = "استراتژی کلی مسیریابی برای حل تمام درخواستها را تعیین میکند"
|
||||
"Torrent" = "مسدودسازی پروتکل بیتتورنت"
|
||||
"TorrentDesc" = "پروتکل بیت تورنت را مسدود میکند"
|
||||
"PrivateIp" = "مسدودسازی اتصال آیپیهای خصوصی"
|
||||
"PrivateIpDesc" = "اتصال به آیپیهای رنج خصوصی را مسدود میکند"
|
||||
"Ads" = "مسدودسازی تبلیغات"
|
||||
"AdsDesc" = "وبسایتهای تبلیغاتی را مسدود میکند"
|
||||
"Family" = "محافظت خانواده"
|
||||
"FamilyDesc" = "محتوای مخصوص بزرگسالان، و وبسایتهای ناامن را مسدود میکند"
|
||||
"Security" = "محافظت امنیتی"
|
||||
"SecurityDesc" = "وبسایتهای ناامن، بدافزار، فیشینگ، و کریپتوماینرها را مسدود میکند"
|
||||
"Speedtest" = "مسدودسازی اسپیدتست"
|
||||
"SpeedtestDesc" = "اتصال به وبسایتهای تست سرعت را مسدود میکند"
|
||||
"IRIp" = "مسدودسازی اتصال به آیپیهای ایران"
|
||||
"IRIpDesc" = "اتصال به آیپیهای کشور ایران را مسدود میکند"
|
||||
"IRDomain" = "مسدودسازی اتصال به دامنههای ایران"
|
||||
"IRDomainDesc" = "اتصال به دامنههای کشور ایران را مسدود میکند"
|
||||
"ChinaIp" = "مسدودسازی اتصال به آیپیهای چین"
|
||||
"ChinaIpDesc" = "اتصال به آیپیهای کشور چین را مسدود میکند"
|
||||
"ChinaDomain" = "مسدودسازی اتصال به دامنههای چین"
|
||||
"ChinaDomainDesc" = "اتصال به دامنههای کشور چین را مسدود میکند"
|
||||
"RussiaIp" = "مسدودسازی اتصال به آیپیهای روسیه"
|
||||
"RussiaIpDesc" = "اتصال به آیپیهای کشور روسیه را مسدود میکند"
|
||||
"RussiaDomain" = "مسدودسازی اتصال به دامنههای روسیه"
|
||||
"RussiaDomainDesc" = "اتصال به دامنههای کشور روسیه را مسدود میکند"
|
||||
"VNIp" = "مسدودسازی اتصال به آیپیهای ویتنام"
|
||||
"VNIpDesc" = "اتصال به آیپیهای کشور ویتنام را مسدود میکند"
|
||||
"VNDomain" = "مسدودسازی اتصال به دامنه های ویتنام"
|
||||
"VNDomainDesc" = "اتصال به دامنههای کشور ویتنام را مسدود میکند"
|
||||
"DirectIRIp" = "اتصال مستقیم آیپیهای ایران"
|
||||
"DirectIRIpDesc" = "اتصال مستقیم به آیپیهای کشور ایران"
|
||||
"DirectIRDomain" = "اتصال مستقیم دامنههای ایران"
|
||||
"DirectIRDomainDesc" = "اتصال مستقیم به دامنههای کشور ایران"
|
||||
"DirectChinaIp" = "اتصال مستقیم آیپیهای چین"
|
||||
"DirectChinaIpDesc" = "اتصال مستقیم به آیپیهای کشور چین"
|
||||
"DirectChinaDomain" = "اتصال مستقیم دامنههای چین"
|
||||
"DirectChinaDomainDesc" = "اتصال مستقیم به دامنههای کشور چین"
|
||||
"DirectRussiaIp" = "اتصال مستقیم آیپیهای روسیه"
|
||||
"DirectRussiaIpDesc" = "اتصال مستقیم به آیپیهای کشور روسیه"
|
||||
"DirectRussiaDomain" = "اتصال مستقیم دامنههای روسیه"
|
||||
"DirectRussiaDomainDesc" = "اتصال مستقیم به دامنههای کشور روسیه"
|
||||
"DirectVNIp" = "اتصال مستقیم آیپیهای ویتنام"
|
||||
"DirectVNIpDesc" = "اتصال مستقیم به آیپیهای کشور ویتنام"
|
||||
"DirectVNDomain" = "اتصال مستقیم دامنههای ویتنام"
|
||||
"DirectVNDomainDesc" = "اتصال مستقیم به دامنههای کشور ویتنام"
|
||||
"GoogleIPv4" = "گوگل"
|
||||
"GoogleIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به گوگل هدایت میکند"
|
||||
"NetflixIPv4" = "نتفلیکس"
|
||||
"NetflixIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به نتفلیکس هدایت میکند"
|
||||
"GoogleWARP" = "گوگل"
|
||||
"GoogleWARPDesc" = "ترافیک را از طریق وارپ به گوگل هدایت میکند"
|
||||
"OpenAIWARP" = "چت جیپیتی"
|
||||
"OpenAIWARPDesc" = "ترافیک را از طریق وارپ به چت جیپیتی هدایت میکند"
|
||||
"NetflixWARP" = "نتفلیکس"
|
||||
"NetflixWARPDesc" = "ترافیک را از طریق وارپ به نتفلیکس هدایت میکند"
|
||||
"SpotifyWARP" = "اسپاتیفای"
|
||||
"SpotifyWARPDesc" = " ترافیک را از طریق وارپ به اسپاتیفای هدایت میکند"
|
||||
"IRWARP" = "دامنههای ایران"
|
||||
"IRWARPDesc" = "ترافیک را از طریق وارپ به دامنههای کشور ایران هدایت میکند"
|
||||
"Inbounds" = "ورودیها"
|
||||
"InboundsDesc" = "میتوانید الگوی تنظیمات را برای ورودی های خاص تنظیم نمایید"
|
||||
"InboundsDesc" = "پذیرش کلاینت خاص"
|
||||
"Outbounds" = "خروجیها"
|
||||
"OutboundsDesc" = "میتوانید الگوی تنظیمات را برای خروجی اینترنت تنظیم نمایید"
|
||||
"OutboundsDesc" = "مسیر ترافیک خروجی را تنظیم کنید"
|
||||
"Routings" = "قوانین مسیریابی"
|
||||
"RoutingsDesc" = "اولویت هر قانون مهم است!"
|
||||
"RoutingsDesc" = "اولویت هر قانون مهم است"
|
||||
"completeTemplate" = "کامل"
|
||||
|
||||
[pages.xray.rules]
|
||||
@@ -403,23 +418,31 @@
|
||||
"domain" = "دامنه"
|
||||
"type" = "نوع"
|
||||
"bridge" = "پل"
|
||||
"portal" = "پرتال"
|
||||
"portal" = "پورتال"
|
||||
"intercon" = "اتصال میانی"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "کلید شخصی"
|
||||
"publicKey" = "کلید عمومی"
|
||||
"allowedIPs" = "آیپیهای مجاز"
|
||||
"endpoint" = "نقطه پایانی"
|
||||
"psk" = "کلید مشترک"
|
||||
"domainStrategy" = "استراتژی حل دامنه"
|
||||
|
||||
[pages.settings.security]
|
||||
"admin" = "مدیر"
|
||||
"secret" = "توکن امنیتی"
|
||||
"loginSecurity" = "لاگین ایمن"
|
||||
"loginSecurityDesc" = "افزودن یک مرحله دیگر به فرآیند لاگین"
|
||||
"secretToken" = "توکن امنیتی"
|
||||
"secretTokenDesc" = "این کد امنیتی را نزد خود در این جای امن نگه دارید، بدون این کد امکان ورود به پنل را نخواهید داشت. امکان بازیابی آن وجود ندارد!"
|
||||
"secret" = "توکن مخفی"
|
||||
"loginSecurity" = "ورود ایمن"
|
||||
"loginSecurityDesc" = "یک لایه اضافی از احراز هویت برای ایجاد امنیت بیشتر اضافه می کند"
|
||||
"secretToken" = "توکن مخفی"
|
||||
"secretTokenDesc" = "لطفاً این توکن را در مکانی امن ذخیره کنید. این توکن برای ورود به سیستم مورد نیاز است و قابل بازیابی نیست"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "ویرایش تنظیمات"
|
||||
"getSettings" = "دریافت تنظیمات"
|
||||
"modifyUser" = "ویرایش کاربر"
|
||||
"originalUserPassIncorrect" = "نام کاربری و رمز عبور فعلی اشتباه می باشد "
|
||||
"userPassMustBeNotEmpty" = "نام کاربری و رمز عبور جدید نمیتواند خالی باشد "
|
||||
"modifyUser" = "ویرایش مدیر"
|
||||
"originalUserPassIncorrect" = "نامکاربری یا رمزعبور فعلی اشتباهاست"
|
||||
"userPassMustBeNotEmpty" = "نامکاربری یا رمزعبور جدید خالیاست"
|
||||
|
||||
[tgbot]
|
||||
"keyboardClosed" = "❌ کیبورد سفارشی بسته شد!"
|
||||
@@ -429,14 +452,17 @@
|
||||
"noIpRecord" = "❗ رکورد IP یافت نشد!"
|
||||
"noInbounds" = "❗ هیچ ورودی یافت نشد!"
|
||||
"unlimited" = "♾ نامحدود"
|
||||
"add" = "اضافه کردن"
|
||||
"month" = "ماه"
|
||||
"months" = "ماهها"
|
||||
"day" = "روز"
|
||||
"days" = "روزها"
|
||||
"hours" = "ساعت ها"
|
||||
"hours" = "ساعتها"
|
||||
"unknown" = "نامشخص"
|
||||
"inbounds" = "ورودیها"
|
||||
"clients" = "کلاینتها"
|
||||
"offline" = "🔴 آفلاین"
|
||||
"online" = "🟢 آنلاین"
|
||||
|
||||
[tgbot.commands]
|
||||
"unknown" = "❗ دستور ناشناخته"
|
||||
@@ -447,63 +473,69 @@
|
||||
"status" = "✅ ربات در حالت عادی است!"
|
||||
"usage" = "❗ لطفاً یک متن برای جستجو وارد کنید!"
|
||||
"getID" = "🆔 شناسه شما: <code>{{ .ID }}</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 استفاده کنید."
|
||||
"helpAdminCommands" = "برای جستجوی ایمیل مشتری:\r\n<code>/usage [ایمیل]</code>\r\n\r\nبرای جستجوی ورودیها (با آمار مشتری):\r\n<code>/inbound [توضیح]</code>"
|
||||
"helpClientCommands" = "برای جستجوی آمار، فقط از دستور زیر استفاده کنید:\r\n\r\n<code>/usage [Email]</code>"
|
||||
|
||||
[tgbot.messages]
|
||||
"cpuThreshold" = "🔴 میزان استفاده از CPU {{ .Percent }}% بیشتر از آستانه {{ .Threshold }}% است."
|
||||
"cpuThreshold" = "🔴 بار پردازنده {{ .Percent }}% بیشتر از آستانه است {{ .Threshold }}%"
|
||||
"selectUserFailed" = "❌ خطا در انتخاب کاربر!"
|
||||
"userSaved" = "✅ کاربر تلگرام ذخیره شد."
|
||||
"loginSuccess" = "✅ با موفقیت به پنل وارد شدید.\r\n"
|
||||
"loginFailed" = "❗️ ورود به پنل ناموفق بود.\r\n"
|
||||
"report" = "🕰 گزارشات زمانبندی شده: {{ .RunTime }}\r\n"
|
||||
"datetime" = "⏰ تاریخ-زمان: {{ .DateTime }}\r\n"
|
||||
"hostname" = "💻 نام میزبان: {{ .Hostname }}\r\n"
|
||||
"version" = "🚀 نسخه X-UI: {{ .Version }}\r\n"
|
||||
"loginFailed" = "❗️ ورود به پنل ناموفقبود \r\n"
|
||||
"report" = "🕰 گزارشاتزمانبندیشده: {{ .RunTime }}\r\n"
|
||||
"datetime" = "⏰ تاریخوزمان: {{ .DateTime }}\r\n"
|
||||
"hostname" = "💻 ناممیزبان: {{ .Hostname }}\r\n"
|
||||
"version" = "🚀 نسخهپنل: {{ .Version }}\r\n"
|
||||
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
||||
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
||||
"ip" = "🌐 آدرس IP: {{ .IP }}\r\n"
|
||||
"ips" = "🔢 آدرسهای IP: \r\n{{ .IPs }}\r\n"
|
||||
"serverUpTime" = "⏳ زمان کارکرد سرور: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"serverLoad" = "📈 بار سرور: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||
"serverMemory" = "📋 حافظه سرور: {{ .Current }}/{{ .Total }}\r\n"
|
||||
"tcpCount" = "🔹 تعداد ترافیک TCP: {{ .Count }}\r\n"
|
||||
"udpCount" = "🔸 تعداد ترافیک UDP: {{ .Count }}\r\n"
|
||||
"ip" = "🌐 آدرسآیپی: {{ .IP }}\r\n"
|
||||
"ips" = "🔢 آدرسهای آیپی:\r\n{{ .IPs }}\r\n"
|
||||
"serverUpTime" = "⏳ مدتکارکردسیستم: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"serverLoad" = "📈 بارسیستم: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||
"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n"
|
||||
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
|
||||
"udpCount" = "🔸 UDP: {{ .Count }}\r\n"
|
||||
"traffic" = "🚦 ترافیک: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||
"xrayStatus" = "ℹ️ وضعیت Xray: {{ .State }}\r\n"
|
||||
"username" = "👤 نام کاربری: {{ .Username }}\r\n"
|
||||
"xrayStatus" = "ℹ️ وضعیتایکسری: {{ .State }}\r\n"
|
||||
"username" = "👤 نامکاربری: {{ .Username }}\r\n"
|
||||
"time" = "⏰ زمان: {{ .Time }}\r\n"
|
||||
"inbound" = "📍 ورودی: {{ .Remark }}\r\n"
|
||||
"inbound" = "📍 نامورودی: {{ .Remark }}\r\n"
|
||||
"port" = "🔌 پورت: {{ .Port }}\r\n"
|
||||
"expire" = "📅 تاریخ انقضا: {{ .Time }}\r\n"
|
||||
"expireIn" = "📅 باقیمانده از انقضا: {{ .Time }}\r\n"
|
||||
"active" = "💡 فعال: ✅\r\n"
|
||||
"inactive" = "💡 فعال: ❌\r\n"
|
||||
"expire" = "📅 تاریخانقضا: {{ .Time }}\r\n\r\n"
|
||||
"expireIn" = "📅 باقیماندهتاانقضا: {{ .Time }}\r\n\r\n"
|
||||
"active" = "💡 فعال: {{ .Enable }}\r\n"
|
||||
"enabled" = "🚨 وضعیت: {{ .Enable }}\r\n"
|
||||
"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
|
||||
"email" = "📧 ایمیل: {{ .Email }}\r\n"
|
||||
"upload" = "🔼 آپلود↑: {{ .Upload }}\r\n"
|
||||
"download" = "🔽 دانلود↓: {{ .Download }}\r\n"
|
||||
"total" = "📊 کل: {{ .UpDown }} / {{ .Total }}\r\n"
|
||||
"total" = "🔄 کل: {{ .UpDown }} / {{ .Total }}\r\n"
|
||||
"TGUser" = "👤 کاربر تلگرام: {{ .TelegramID }}\r\n"
|
||||
"exhaustedMsg" = "🚨 {{ .Type }} به اتمام رسیده است:\r\n"
|
||||
"exhaustedCount" = "🚨 تعداد {{ .Type }} به اتمام رسیده:\r\n"
|
||||
"exhaustedMsg" = "🚨 {{ .Type }} بهاتمامرسیدهاست:\r\n"
|
||||
"exhaustedCount" = "🚨 تعداد {{ .Type }} بهاتمامرسیدهاست:\r\n"
|
||||
"onlinesCount" = "🌐 کاربرانآنلاین: {{ .Count }}\r\n"
|
||||
"disabled" = "🛑 غیرفعال: {{ .Disabled }}\r\n"
|
||||
"depleteSoon" = "🔜 به زودی به پایان خواهد رسید: {{ .Deplete }}\r\n \r\n"
|
||||
"backupTime" = "🗄 زمان پشتیبانگیری: {{ .Time }}\r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 تازهسازی شده در: {{ .Time }}\r\n \r\n"
|
||||
"depleteSoon" = "🔜 بهزودیبهپایانخواهدرسید: {{ .Deplete }}\r\n\r\n"
|
||||
"backupTime" = "🗄 زمانپشتیبانگیری: {{ .Time }}\r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 تازهسازی شده در: {{ .Time }}\r\n\r\n"
|
||||
"yes" = "✅ بله"
|
||||
"no" = "❌ خیر"
|
||||
|
||||
[tgbot.buttons]
|
||||
"closeKeyboard" = "❌ بستن کیبورد"
|
||||
"cancel" = "❌ لغو"
|
||||
"cancelReset" = "❌ لغو تنظیم مجدد"
|
||||
"cancelIpLimit" = "❌ لغو محدودیت IP"
|
||||
"cancelIpLimit" = "❌ لغو محدودیت آیپی"
|
||||
"confirmResetTraffic" = "✅ تأیید تنظیم مجدد ترافیک؟"
|
||||
"confirmClearIps" = "✅ تأیید پاکسازی آدرسهای IP؟"
|
||||
"confirmClearIps" = "✅ تأیید پاکسازی آدرسهای آیپی؟"
|
||||
"confirmRemoveTGUser" = "✅ تأیید حذف کاربر تلگرام؟"
|
||||
"dbBackup" = "دریافت پشتیبان پایگاه داده"
|
||||
"serverUsage" = "استفاده از سرور"
|
||||
"confirmToggle" = "✅ تایید فعال/غیرفعال کردن کاربر؟"
|
||||
"dbBackup" = "دریافت پشتیبان"
|
||||
"serverUsage" = "استفاده از سیستم"
|
||||
"getInbounds" = "دریافت ورودیها"
|
||||
"depleteSoon" = "به زودی به پایان خواهد رسید"
|
||||
"depleteSoon" = "بهزودی به پایان خواهد رسید"
|
||||
"clientUsage" = "دریافت آمار کاربر"
|
||||
"onlines" = "کاربران آنلاین"
|
||||
"commands" = "دستورات"
|
||||
"refresh" = "🔄 تازهسازی"
|
||||
"clearIPs" = "❌ پاکسازی آدرسها"
|
||||
@@ -517,8 +549,10 @@
|
||||
"setTGUser" = "👤 تنظیم کاربر تلگرام"
|
||||
"toggle" = "🔘 فعال / غیرفعال"
|
||||
"custom" = "🔢 سفارشی"
|
||||
"confirmNumber" = "✅ تایید : {{ .Num }}"
|
||||
"confirmNumber" = "✅ تایید: {{ .Num }}"
|
||||
"confirmNumberAdd" = "✅ تایید اضافه کردن: {{ .Num }}"
|
||||
"limitTraffic" = "🚧 محدودیت ترافیک"
|
||||
"getBanLogs" = "گزارش های بلوک را دریافت کنید"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ انجام شد!"
|
||||
@@ -538,5 +572,4 @@
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }} : کاربر تلگرام با موفقیت حذف شد."
|
||||
"enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد."
|
||||
"disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد."
|
||||
"askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserName" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که نام کاربری یا شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nنام کاربری شما: <b>@{{ .TgUserName }}</b>\r\n\r\nشناسه کاربری شما: <b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <code>{{ .TgUserID }}</code>"
|
||||
|
||||
@@ -59,10 +59,10 @@
|
||||
"settings" = "Настройки панели"
|
||||
"xray" = "Настройки Xray"
|
||||
"logout" = "Выход"
|
||||
"link" = "Прочее"
|
||||
"link" = "менеджмент"
|
||||
|
||||
[pages.login]
|
||||
"title" = "Логин"
|
||||
"title" = "Добро пожаловать"
|
||||
"loginAgain" = "Время пребывания в сети вышло. Пожалуйста, войдите в систему снова"
|
||||
|
||||
[pages.login.toasts]
|
||||
@@ -77,9 +77,9 @@
|
||||
"memory" = "Память"
|
||||
"hard" = "Жесткий диск"
|
||||
"xrayStatus" = "Статус"
|
||||
"stopXray" = "Остановить Xray"
|
||||
"restartXray" = "Рестарт Xray"
|
||||
"xraySwitch" = "Переключить версию"
|
||||
"stopXray" = "Остановить"
|
||||
"restartXray" = "Перезапустить"
|
||||
"xraySwitch" = "Версия"
|
||||
"xraySwitchClick" = "Выберите желаемую версию"
|
||||
"xraySwitchClickDesk" = "Выбирайте внимательно, так как старые версии могут быть несовместимы с текущими конфигурациями"
|
||||
"operationHours" = "Время работы системы"
|
||||
@@ -134,7 +134,7 @@
|
||||
"destinationPort" = "Порт назначения"
|
||||
"targetAddress" = "Целевой адрес"
|
||||
"monitorDesc" = "Оставьте пустым по умолчанию"
|
||||
"meansNoLimit" = "Значит без ограничений"
|
||||
"meansNoLimit" = " = Без ограничений (значение: ГБ)"
|
||||
"totalFlow" = "Общий расход"
|
||||
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
|
||||
"noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию"
|
||||
@@ -173,7 +173,7 @@
|
||||
"setDefaultCert" = "Установить сертификат с панели"
|
||||
"xtlsDesc" = "Версия Xray должна быть не ниже 1.7.5"
|
||||
"realityDesc" = "Версия Xray должна быть не ниже 1.8.0"
|
||||
"telegramDesc" = "Используйте идентификатор Telegram без символа @ или идентификатора чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
|
||||
"telegramDesc" = "Используйте только ID чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
|
||||
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
|
||||
"info" = "Информация"
|
||||
"same" = "Тот же"
|
||||
@@ -195,26 +195,27 @@
|
||||
"prefix" = "Префикс"
|
||||
"postfix" = "Постфикс"
|
||||
"delayedStart" = "Начать с момента первого подключения"
|
||||
"expireDays" = "Срок действия"
|
||||
"expireDays" = "Длительность"
|
||||
"days" = "дней"
|
||||
"renew" = "Автопродление"
|
||||
"renewDesc" = "Автоматическое продление через несколько дней после истечения срока действия. 0 = отключить"
|
||||
"renewDesc" = "Автопродление после истечения срока действия. (0 = отключить)(единица: день)"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "Получить"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "Заголовок запроса"
|
||||
"request" = "Запрос"
|
||||
"response" = "Ответ"
|
||||
"name" = "Имя"
|
||||
"value" = "Значение"
|
||||
"value" = "Ценить"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "Версия запроса"
|
||||
"requestMethod" = "Метод запроса"
|
||||
"requestPath" = "Путь запроса"
|
||||
"responseVersion" = "Версия ответа"
|
||||
"responseStatus" = "Статус ответа"
|
||||
"responseStatusDescription" = "Описание статуса ответа"
|
||||
"version" = "Версия"
|
||||
"method" = "Метод"
|
||||
"path" = "Путь"
|
||||
"status" = "Положение дел"
|
||||
"statusDescription" = "Описание статуса"
|
||||
"requestHeader" = "Заголовок запроса"
|
||||
"responseHeader" = "Заголовок ответа"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
@@ -246,6 +247,8 @@
|
||||
"pageSize" = "Размер нумерации страниц"
|
||||
"pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить"
|
||||
"remarkModel" = "Модель примечания и символ разделения"
|
||||
"datepicker" = "выбор даты"
|
||||
"datepickerDescription" = "Тип календаря выбора указывает дату истечения срока действия."
|
||||
"sampleRemark" = "Пример замечания"
|
||||
"oldUsername" = "Текущее имя пользователя"
|
||||
"currentPassword" = "Текущий пароль"
|
||||
@@ -255,6 +258,8 @@
|
||||
"telegramBotEnableDesc" = "Подключайтесь к функциям этой панели через Telegram бота"
|
||||
"telegramToken" = "Токен Telegram бота"
|
||||
"telegramTokenDesc" = "Необходимо получить токен у менеджера ботов Telegram @botfather"
|
||||
"telegramProxy" = "Прокси Socks5"
|
||||
"telegramProxyDesc" = "Если для подключения к Telegram вам нужен прокси Socks5. Настройте его параметры согласно руководству."
|
||||
"telegramChatId" = "Telegram ID админа бота"
|
||||
"telegramChatIdDesc" = "Множественные идентификаторы чата, разделенные запятыми. Чтобы получить свои идентификаторы чатов, используйте @userinfobot или команду '/id' в боте."
|
||||
"telegramNotifyTime" = "Частота уведомлений бота Telegram"
|
||||
@@ -300,7 +305,7 @@
|
||||
[pages.xray]
|
||||
"title" = "Настройки Xray"
|
||||
"save" = "Сохранить настройки"
|
||||
"restart" = "Перезапустить рентген"
|
||||
"restart" = "Перезапустить Xray"
|
||||
"basicTemplate" = "Базовый шаблон"
|
||||
"advancedTemplate" = "Расширенный шаблон"
|
||||
"generalConfigs" = "Основные настройки"
|
||||
@@ -327,8 +332,10 @@
|
||||
"PrivateIpDesc" = "Изменение шаблона конфигурации для предупреждения подключения к диапазонам частных IP-адресов"
|
||||
"Ads" = "Блокировка рекламы"
|
||||
"AdsDesc" = "Изменение конфигурации для блокировки рекламы"
|
||||
"Family" = "Блокировать вредоносное ПО и контент для взрослых"
|
||||
"FamilyDesc" = "Резольверы DNS для блокировки вредоносных программ и контента для взрослых для защиты семьи"
|
||||
"Family" = "Блокируйте вредоносное ПО и контент для взрослых"
|
||||
"FamilyDesc" = "DNS-преобразователи Cloudflare для блокировки вредоносного ПО и контента для взрослых в целях защиты семьи."
|
||||
"Security" = "Блокируйте вредоносное ПО, фишинговые сайты и сайты криптомайнеров"
|
||||
"SecurityDesc" = "Изменение шаблона конфигурации для защиты безопасности."
|
||||
"Speedtest" = "Блокировать сайты для проверки скорости"
|
||||
"SpeedtestDesc" = "Изменение шаблона конфигурации для предупреждения подключения к веб-сайтам для тестирования скорости"
|
||||
"IRIp" = "Заблокировать подключения к диапазонам IP-адресов Ирана"
|
||||
@@ -343,6 +350,10 @@
|
||||
"RussiaIpDesc" = "Изменение конфигурации, чтобы заблокировать подключения к диапазонами IP-адресов России"
|
||||
"RussiaDomain" = "Заблокировать подключения к доменам России"
|
||||
"RussiaDomainDesc" = "Изменение конфигурации, чтобы заблокировать подключения к доменам России"
|
||||
"VNIp" = "Отключить подключение к IP-адресам Вьетнама"
|
||||
"VNIpDesc" = "Измените шаблон конфигурации, чтобы избежать подключения к диапазонам IP-адресов Вьетнама"
|
||||
"VNDomain" = "Отключить подключение к доменам Вьетнама"
|
||||
"VNDomainDesc" = "Измените шаблон конфигурации, чтобы избежать подключения к доменам Вьетнама."
|
||||
"DirectIRIp" = "Прямое подключения к диапазонам IP-адресов Ирана"
|
||||
"DirectIRIpDesc" = "Изменение шаблона конфигурации для прямого подключения к диапазонам IP-адресов Ирана"
|
||||
"DirectIRDomain" = "Прямое подключение к доменам Ирана"
|
||||
@@ -355,6 +366,10 @@
|
||||
"DirectRussiaIpDesc" = "Изменение шаблона конфигурации для прямого подключения к диапазонам IP-адресов России"
|
||||
"DirectRussiaDomain" = "Прямое подключение к доменам России"
|
||||
"DirectRussiaDomainDesc" = "Изменение шаблона конфигурации для прямого подключения к доменам России"
|
||||
"DirectVNIp" = "Прямое подключение к IP-адресам Вьетнама"
|
||||
"DirectVNIpDesc" = "Измените шаблон конфигурации для прямого подключения к диапазонам IP-адресов Вьетнама"
|
||||
"DirectVNDomain" = "Прямое подключение к доменам Вьетнама"
|
||||
"DirectVNDomainDesc" = "Измените шаблон конфигурации для прямого подключения к доменам Вьетнама"
|
||||
"GoogleIPv4" = "Использовать IPv4 для Google"
|
||||
"GoogleIPv4Desc" = "Добавить маршрутизацию для Google для подключения к IPv4"
|
||||
"NetflixIPv4" = "Использовать IPv4 для Netflix"
|
||||
@@ -406,6 +421,14 @@
|
||||
"portal" = "Портал"
|
||||
"intercon" = "Соединение"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Секретный ключ"
|
||||
"publicKey" = "Открытый ключ"
|
||||
"allowedIPs" = "Разрешенные IP-адреса"
|
||||
"endpoint" = "Конечная точка"
|
||||
"psk" = "Общий ключ"
|
||||
"domainStrategy" = "Стратегия домена"
|
||||
|
||||
[pages.settings.security]
|
||||
"admin" = "Админ"
|
||||
"secret" = "Секретный токен"
|
||||
@@ -429,6 +452,7 @@
|
||||
"noIpRecord" = "❗ Нет записей об IP-адресе!"
|
||||
"noInbounds" = "❗ Входящих соединений не найдено!"
|
||||
"unlimited" = "♾ Неограниченно"
|
||||
"add" = "Добавить"
|
||||
"month" = "Месяц"
|
||||
"months" = "Месяцев"
|
||||
"day" = "День"
|
||||
@@ -437,6 +461,8 @@
|
||||
"unknown" = "Неизвестно"
|
||||
"inbounds" = "Входящие"
|
||||
"clients" = "Клиенты"
|
||||
"offline" = "🔴 Офлайн"
|
||||
"online" = "🟢 Онлайн"
|
||||
|
||||
[tgbot.commands]
|
||||
"unknown" = "❗ Неизвестная команда"
|
||||
@@ -447,8 +473,8 @@
|
||||
"status" = "✅ Бот работает нормально!"
|
||||
"usage" = "❗ Пожалуйста, укажите текст для поиска!"
|
||||
"getID" = "🆔 Ваш ID: <code>{{ .ID }}</code>"
|
||||
"helpAdminCommands" = "Поиск по электронной почте клиента:\r\n<code>/usage [Email]</code>\r\n \r\nПоиск входящих соединений (со статистикой клиента):\r\n<code>/inbound [Remark]</code>"
|
||||
"helpClientCommands" = "Для получения статистики используйте следующую команду:\r\n \r\n<code>/usage [UUID|Password]</code>\r\n \r\nИспользуйте UUID для vmess/vless и пароль для Trojan."
|
||||
"helpAdminCommands" = "Поиск по электронной почте клиента:\r\n<code>/usage [Email]</code>\r\n\r\nПоиск входящих соединений (со статистикой клиента):\r\n<code>/inbound [Remark]</code>"
|
||||
"helpClientCommands" = "Для получения статистики используйте следующую команду:\r\n\r\n<code>/usage [Email]</code>"
|
||||
|
||||
[tgbot.messages]
|
||||
"cpuThreshold" = "🔴 Загрузка процессора составляет {{ .Percent }}%, что превышает пороговое значение {{ .Threshold }}%"
|
||||
@@ -463,7 +489,7 @@
|
||||
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
||||
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
||||
"ip" = "🌐 IP: {{ .IP }}\r\n"
|
||||
"ips" = "🔢 IP-адреса: \r\n{{ .IPs }}\r\n"
|
||||
"ips" = "🔢 IP-адреса:\r\n{{ .IPs }}\r\n"
|
||||
"serverUpTime" = "⏳ Время работы сервера: {{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"serverLoad" = "📈 Загрузка сервера: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||
"serverMemory" = "📋 Память сервера: {{ .Current }}/{{ .Total }}\r\n"
|
||||
@@ -477,8 +503,9 @@
|
||||
"port" = "🔌 Порт: {{ .Port }}\r\n"
|
||||
"expire" = "📅 Дата окончания: {{ .Time }}\r\n"
|
||||
"expireIn" = "📅 Окончание через: {{ .Time }}\r\n"
|
||||
"active" = "💡 Активен: ✅ Да\r\n"
|
||||
"inactive" = "💡 Активен: ❌ Нет\r\n"
|
||||
"active" = "💡 Активен: {{ .Enable }}\r\n"
|
||||
"enabled" = "🚨 Включен: {{ .Enable }}\r\n"
|
||||
"online" = "🌐 Статус соединения: {{ .Status }}\r\n"
|
||||
"email" = "📧 Email: {{ .Email }}\r\n"
|
||||
"upload" = "🔼 Исходящий трафик: ↑{{ .Upload }}\r\n"
|
||||
"download" = "🔽 Входящий трафик: ↓{{ .Download }}\r\n"
|
||||
@@ -486,10 +513,13 @@
|
||||
"TGUser" = "👤 Пользователь Telegram: {{ .TelegramID }}\r\n"
|
||||
"exhaustedMsg" = "🚨 Исчерпаны {{ .Type }}:\r\n"
|
||||
"exhaustedCount" = "🚨 Количество исчерпанных {{ .Type }}:\r\n"
|
||||
"onlinesCount" = "🌐 Клиентов онлайн: {{ .Count }}\r\n"
|
||||
"disabled" = "🛑 Отключено: {{ .Disabled }}\r\n"
|
||||
"depleteSoon" = "🔜 Скоро исчерпание: {{ .Deplete }}\r\n \r\n"
|
||||
"depleteSoon" = "🔜 Скоро исчерпание: {{ .Deplete }}\r\n\r\n"
|
||||
"backupTime" = "🗄 Время резервного копирования: {{ .Time }}\r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 Обновлено: {{ .Time }}\r\n \r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 Обновлено: {{ .Time }}\r\n\r\n"
|
||||
"yes" = "✅ Да"
|
||||
"no" = "❌ Нет"
|
||||
|
||||
[tgbot.buttons]
|
||||
"closeKeyboard" = "❌ Закрыть клавиатуру"
|
||||
@@ -499,11 +529,13 @@
|
||||
"confirmResetTraffic" = "✅ Подтвердить сброс трафика?"
|
||||
"confirmClearIps" = "✅ Подтвердить очистку IP?"
|
||||
"confirmRemoveTGUser" = "✅ Подтвердить удаление пользователя Telegram?"
|
||||
"confirmToggle" = "✅ Подтвердить вкл/выкл пользователя?"
|
||||
"dbBackup" = "Получить резервную копию DB"
|
||||
"serverUsage" = "Использование сервера"
|
||||
"getInbounds" = "Получить входящие потоки"
|
||||
"depleteSoon" = "Скоро исчерпание"
|
||||
"clientUsage" = "Получить использование"
|
||||
"onlines" = "Онлайн-клиенты"
|
||||
"commands" = "Команды"
|
||||
"refresh" = "🔄 Обновить"
|
||||
"clearIPs" = "❌ Очистить IP"
|
||||
@@ -511,14 +543,16 @@
|
||||
"selectTGUser" = "👤 Выбрать пользователя Telegram"
|
||||
"selectOneTGUser" = "👤 Выберите пользователя Telegram:"
|
||||
"resetTraffic" = "📈 Сбросить трафик"
|
||||
"resetExpire" = "📅 Сбросить дату окончания"
|
||||
"resetExpire" = "📅 Изменить дату окончания"
|
||||
"ipLog" = "🔢 Лог IP"
|
||||
"ipLimit" = "🔢 Лимит IP"
|
||||
"setTGUser" = "👤 Установить пользователя Telegram"
|
||||
"toggle" = "🔘 Вкл./Выкл."
|
||||
"custom" = "🔢 Обычай"
|
||||
"confirmNumber" = "✅ Подтвердить : {{ .Num }}"
|
||||
"custom" = "🔢 Свой"
|
||||
"confirmNumber" = "✅ Подтвердить: {{ .Num }}"
|
||||
"confirmNumberAdd" = "✅ Подтвердить добавление: {{ .Num }}"
|
||||
"limitTraffic" = "🚧 Лимит трафика"
|
||||
"getBanLogs" = "Логи блокировок"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ Успешный!"
|
||||
@@ -538,5 +572,4 @@
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }}: Пользователь Telegram успешно удален."
|
||||
"enableSuccess" = "✅ {{ .Email }}: Включено успешно."
|
||||
"disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
|
||||
"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserName" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваше имя пользователя или идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаше имя пользователя: <b>@{{ .TgUserName }}</b>\r\n\r\nВаш идентификатор пользователя: <b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <code>{{ .TgUserID }}</code>"
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"transmission" = "Truyền tải"
|
||||
"host" = "Máy chủ"
|
||||
"path" = "Đường dẫn"
|
||||
"camouflage" = "camouflage"
|
||||
"camouflage" = "Ngụy trang"
|
||||
"status" = "Trạng thái"
|
||||
"enabled" = "Đã kích hoạt"
|
||||
"disabled" = "Đã tắt"
|
||||
@@ -55,14 +55,14 @@
|
||||
|
||||
[menu]
|
||||
"dashboard" = "Trạng thái hệ thống"
|
||||
"inbounds" = "Inbounds"
|
||||
"inbounds" = "Đầu vào khách hàng"
|
||||
"settings" = "Cài đặt bảng điều khiển"
|
||||
"logout" = "Đăng xuất"
|
||||
"xray" = "Xray Cài đặt"
|
||||
"link" = "Khác"
|
||||
"xray" = "Cài đặt Xray"
|
||||
"link" = "Quản lý"
|
||||
|
||||
[pages.login]
|
||||
"title" = "Đăng nhập"
|
||||
"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]
|
||||
@@ -75,21 +75,21 @@
|
||||
[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"
|
||||
"hard" = "Dung lượng"
|
||||
"xrayStatus" = "Trạng thái"
|
||||
"stopXray" = "Dừng lại"
|
||||
"restartXray" = "Khởi động lại"
|
||||
"xraySwitch" = "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."
|
||||
"operationHours" = "Thời gian hoạt động"
|
||||
"systemLoad" = "Tải hệ thống"
|
||||
"systemLoadDesc" = "trung bình tải hệ thống trong 1, 5 và 15 phút qua"
|
||||
"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."
|
||||
"connectionTcpCountDesc" = "Tổng số kết nối TCP trên tất cả các thẻ mạng."
|
||||
"connectionUdpCountDesc" = "Tổng số kết nối UDP trên tất cả các thẻ mạng."
|
||||
"connectionCount" = "Số lượng kết nối"
|
||||
"upSpeed" = "Tổng tốc độ tải lên cho tất cả các card mạng."
|
||||
"downSpeed" = "Tổng tốc độ tải xuống cho 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 hệ thống khởi động."
|
||||
"totalReceive" = "Tổng lưu lượng tải xuống trên tất cả các thẻ mạng kể từ khi hệ thống khởi động."
|
||||
"xraySwitchVersionDialog" = "Chuyển đổi Phiên bản Xray"
|
||||
@@ -125,8 +125,8 @@
|
||||
"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 ứng dụng khách không?"
|
||||
"deleteClient" = "Xóa người dùng"
|
||||
"deleteClientContent" = "Bạn có chắc chắn muốn xóa người dùng không?"
|
||||
"resetTrafficContent" = "Xác nhận đặt lại lưu lượng?"
|
||||
"copyLink" = "Sao chép liên kết"
|
||||
"address" = "Địa chỉ"
|
||||
@@ -134,7 +134,7 @@
|
||||
"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"
|
||||
"meansNoLimit" = " = Không giới hạn (đơn vị: GB)"
|
||||
"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"
|
||||
@@ -145,24 +145,24 @@
|
||||
"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" = "Client"
|
||||
"client" = "Người dùng"
|
||||
"export" = "Xuất liên kết"
|
||||
"clone" = "Sao chép"
|
||||
"cloneInbound" = "Sao chép điểm vào (Inbound)"
|
||||
"cloneInboundContent" = "Tất cả cài đặt của điểm vào này, trừ Cổng, IP nghe và Clients, sẽ được áp dụng cho bản sao."
|
||||
"cloneInboundContent" = "Tất cả cài đặt của điểm vào này, trừ Cổng, IP nghe và máy khách, sẽ được áp dụng cho bản sao."
|
||||
"cloneInboundOk" = "Sao chép"
|
||||
"resetAllTraffic" = "Đặt lại lưu lượng cho tất cả điểm vào"
|
||||
"resetAllTrafficTitle" = "Đặt lại lưu lượng cho tất cả điểm vào"
|
||||
"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 client của điểm vào"
|
||||
"resetInboundClientTrafficTitle" = "Đặt lại lưu lượng cho tất cả lưu lượng của client"
|
||||
"resetInboundClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho các client của điểm vào này không?"
|
||||
"resetAllClientTraffics" = "Đặt lại lưu lượng cho tất cả client"
|
||||
"resetAllClientTrafficTitle" = "Đặt lại lưu lượng cho tất cả client"
|
||||
"resetAllClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho tất cả client không?"
|
||||
"delDepletedClients" = "Xóa các client đã cạn kiệt"
|
||||
"delDepletedClientsTitle" = "Xóa các client đã cạn kiệt"
|
||||
"delDepletedClientsContent" = "Bạn có chắc chắn muốn xóa tất cả các client đã cạn kiệt không?"
|
||||
"resetInboundClientTraffics" = "Đặt lại lưu lượng toàn bộ người dùng của điểm vào"
|
||||
"resetInboundClientTrafficTitle" = "Đặt lại lưu lượng cho toàn bộ người dùng của điểm vào"
|
||||
"resetInboundClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho các người dùng của điểm vào này không?"
|
||||
"resetAllClientTraffics" = "Đặt lại lưu lượng cho toàn bộ người dùng"
|
||||
"resetAllClientTrafficTitle" = "Đặt lại lưu lượng cho toàn bộ người dùng"
|
||||
"resetAllClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho toàn bộ người dùng không?"
|
||||
"delDepletedClients" = "Xóa các người dùng đã cạn kiệt"
|
||||
"delDepletedClientsTitle" = "Xóa các người dùng đã cạn kiệt"
|
||||
"delDepletedClientsContent" = "Bạn có chắc chắn muốn xóa toàn bộ người dùng đã cạn kiệt không?"
|
||||
"email" = "Email"
|
||||
"emailDesc" = "Vui lòng cung cấp một địa chỉ email duy nhất."
|
||||
"IPLimit" = "Giới hạn IP"
|
||||
@@ -173,21 +173,21 @@
|
||||
"setDefaultCert" = "Đặt chứng chỉ từ bảng điều khiển"
|
||||
"xtlsDesc" = "Xray core cần phiên bản 1.7.5"
|
||||
"realityDesc" = "Xray core cần phiên bản 1.8.0 hoặc cao hơn."
|
||||
"telegramDesc" = "Sử dụng Telegram ID mà không cần ký hiệu @ hoặc chat IDs (bạn có thể nhận được nó ở đây @userinfobot hoặc sử dụng lệnh '/id' trong bot)"
|
||||
"subscriptionDesc" = "Bạn có thể tìm liên kết đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau"
|
||||
"telegramDesc" = "Chỉ sử dụng ID trò chuyện (bạn có thể nhận được nó ở đây @userinfobot hoặc sử dụng lệnh '/id' trong bot)"
|
||||
"subscriptionDesc" = "Bạn có thể tìm liên kết gói đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau"
|
||||
"info" = "Thông tin"
|
||||
"same" = "Giống 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ề"
|
||||
"importInbound" = "Nhập inbound"
|
||||
|
||||
[pages.client]
|
||||
"add" = "Thêm Client"
|
||||
"edit" = "Chỉnh sửa Client"
|
||||
"submitAdd" = "Thêm Client"
|
||||
"add" = "Thêm người dùng"
|
||||
"edit" = "Chỉnh sửa người dùng"
|
||||
"submitAdd" = "Thêm"
|
||||
"submitEdit" = "Lưu thay đổi"
|
||||
"clientCount" = "Số lượng Client"
|
||||
"clientCount" = "Số lượng người dùng"
|
||||
"bulk" = "Thêm hàng loạt"
|
||||
"method" = "Phương pháp"
|
||||
"first" = "Đầu tiên"
|
||||
@@ -195,27 +195,28 @@
|
||||
"prefix" = "Tiền tố"
|
||||
"postfix" = "Hậu tố"
|
||||
"delayedStart" = "Bắt đầu sau khi sử dụng lần đầu"
|
||||
"expireDays" = "Số ngày hết hạn"
|
||||
"expireDays" = "Khoảng thời gian"
|
||||
"days" = "ngày"
|
||||
"renew" = "Tự động gia hạn"
|
||||
"renewDesc" = "Tự động gia hạn những ngày sau khi hết hạn. 0 = tắt"
|
||||
"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"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "Tiêu đề yêu cầu"
|
||||
"request" = "Lời yêu cầu"
|
||||
"response" = "Phản ứng"
|
||||
"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"
|
||||
"version" = "Phiên bản"
|
||||
"method" = "Phương pháp"
|
||||
"path" = "Đường dẫn"
|
||||
"status" = "Trạng thái"
|
||||
"statusDescription" = "Tình trạng Mô tả"
|
||||
"requestHeader" = "Header yêu cầu"
|
||||
"responseHeader" = "Header phản hồi"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
"encryption" = "Mã hóa"
|
||||
@@ -224,28 +225,30 @@
|
||||
"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."
|
||||
"restartPanel" = "Khởi động lại Bảng điều khiển"
|
||||
"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ủ."
|
||||
"actions" = "Hành động"
|
||||
"resetDefaultConfig" = "Đặt lại Cấu hình Mặc định"
|
||||
"panelSettings" = "Cài đặt Bảng điều khiển"
|
||||
"securitySettings" = "Cài đặt Bảo mật"
|
||||
"TGBotSettings" = "Cài đặt Bot Telegram"
|
||||
"panelListeningIP" = "IP Nghe của Bảng điều khiển"
|
||||
"resetDefaultConfig" = "Đặt lại cấu hình mặc định"
|
||||
"panelSettings" = "Bảng điều khiển"
|
||||
"securitySettings" = "Bảo mật"
|
||||
"TGBotSettings" = "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"
|
||||
"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."
|
||||
"panelPort" = "Cổng bảng điều khiển"
|
||||
"panelPortDesc" = "Cổng được sử dụng để kết nối với bảng điều khiển này"
|
||||
"publicKeyPath" = "Đường dẫn file chứng chỉ bảng điều khiển"
|
||||
"publicKeyPathDesc" = "Điền vào đường dẫn đầy đủ (bắt đầu từ '/')"
|
||||
"privateKeyPath" = "Đường dẫn file khóa của chứng chỉ bảng điều khiển"
|
||||
"privateKeyPathDesc" = "Điền vào đường dẫn đầy đủ (bắt đầu từ '/')"
|
||||
"panelUrlPath" = "Đường dẫn gốc URL bảng điều khiển"
|
||||
"panelUrlPathDesc" = "Phải bắt đầu 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"
|
||||
"datepicker" = "Kiểu lịch"
|
||||
"datepickerDescription" = "Tác vụ chạy theo lịch trình sẽ chạy theo kiểu lịch này."
|
||||
"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"
|
||||
@@ -255,6 +258,8 @@
|
||||
"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"
|
||||
"telegramProxy" = "Socks5 Proxy"
|
||||
"telegramProxyDesc" = "Nếu bạn cần socks5 proxy để kết nối với Telegram. Điều chỉnh cài đặt của nó theo hướng dẫn."
|
||||
"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"
|
||||
@@ -263,7 +268,7 @@
|
||||
"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"
|
||||
"sessionMaxAge" = "Thời gian 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)"
|
||||
@@ -273,32 +278,32 @@
|
||||
"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ý"
|
||||
"subSettings" = "Gói đăng ký"
|
||||
"subEnable" = "Bật dịch vụ"
|
||||
"subEnableDesc" = "Tính năng đăng ký với cấu hình riêng"
|
||||
"subEnableDesc" = "Tính năng gói đă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ý"
|
||||
"subPort" = "Cổng gói đă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 '/'"
|
||||
"subCertPath" = "Đường dẫn file chứng chỉ gói đăng ký"
|
||||
"subCertPathDesc" = "Điền vào đường dẫn đầy đủ (bắt đầu với '/')"
|
||||
"subKeyPath" = "Đường dẫn file khóa của chứng chỉ gói đăng ký"
|
||||
"subKeyPathDesc" = "Điền vào đường dẫn đầy đủ (bắt đầu với '/')"
|
||||
"subPath" = "Đường dẫn gốc URL gói đăng ký"
|
||||
"subPathDesc" = "Phải bắt đầu 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ý"
|
||||
"subUpdates" = "Khoảng thời gian cập nhật gói đă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ý"
|
||||
"subEncryptDesc" = "Mã hóa các cấu hình được trả về trong gói đă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"
|
||||
"subURI" = "URI proxy trung gian"
|
||||
"subURIDesc" = "Thay đổi URI cơ sở của URL gói đăng ký để sử dụng cho proxy trung gian"
|
||||
|
||||
[pages.xray]
|
||||
"title" = "Xray Cài đặt"
|
||||
"title" = "Cài đặt Xray"
|
||||
"save" = "Lưu cài đặt"
|
||||
"restart" = "Khởi động lại Xray"
|
||||
"basicTemplate" = "Mẫu Cơ bản"
|
||||
@@ -327,8 +332,10 @@
|
||||
"PrivateIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP riêng tư."
|
||||
"Ads" = "Chặn Quảng cáo"
|
||||
"AdsDesc" = "Thay đổi mẫu cấu hình để chặn quảng cáo."
|
||||
"Family" = "Chặn Phần mềm độc hại và Nội dung cho Người lớn"
|
||||
"FamilyDesc" = "Các trình giải quyết DNS để chặn phần mềm độc hại và nội dung cho bảo vệ gia đình."
|
||||
"Family" = "Chặn phần mềm độc hại và nội dung người lớn"
|
||||
"FamilyDesc" = "Trình phân giải DNS của Cloudflare để chặn phần mềm độc hại và nội dung người lớn để bảo vệ gia đình."
|
||||
"Security" = "Chặn các trang web chứa phần mềm độc hại, lừa đảo và khai thác tiền điện tử"
|
||||
"SecurityDesc" = "Thay đổi mẫu cấu hình để bảo vệ Bảo mật."
|
||||
"Speedtest" = "Chặn Trang web Speedtest"
|
||||
"SpeedtestDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các trang web Speedtest."
|
||||
"IRIp" = "Vô hiệu hóa kết nối đến dải IP của Iran"
|
||||
@@ -343,6 +350,10 @@
|
||||
"RussiaIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP của Nga."
|
||||
"RussiaDomain" = "Vô hiệu hóa kết nối đến tên miền của Nga"
|
||||
"RussiaDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các tên miền của Nga."
|
||||
"VNIp" = "Vô hiệu hóa kết nối đến dải IP của Việt Nam"
|
||||
"VNIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP của Việt Nam."
|
||||
"VNDomain" = "Vô hiệu hóa kết nối đến tên miền của Việt Nam"
|
||||
"VNDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các tên miền của Việt Nam."
|
||||
"DirectIRIp" = "Kết nối trực tiếp đến dải IP của Iran"
|
||||
"DirectIRIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Iran."
|
||||
"DirectIRDomain" = "Kết nối trực tiếp đến tên miền của Iran"
|
||||
@@ -355,6 +366,10 @@
|
||||
"DirectRussiaIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Nga."
|
||||
"DirectRussiaDomain" = "Kết nối trực tiếp đến tên miền của Nga"
|
||||
"DirectRussiaDomainDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến các tên miền của Nga."
|
||||
"DirectVNIp" = "Kết nối trực tiếp đến dải IP của Việt Nam"
|
||||
"DirectVNIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Việt Nam"
|
||||
"DirectVNDomain" = "Kết nối trực tiếp đến tên miền của Việt Nam"
|
||||
"DirectVNDomainDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến các tên miền của Việt Nam."
|
||||
"GoogleIPv4" = "Sử dụng IPv4 cho Google"
|
||||
"GoogleIPv4Desc" = "Thêm định tuyến cho Google để kết nối qua IPv4."
|
||||
"NetflixIPv4" = "Sử dụng IPv4 cho Netflix"
|
||||
@@ -406,13 +421,21 @@
|
||||
"portal" = "Cổng thông tin"
|
||||
"intercon" = "Kết nối"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "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"
|
||||
|
||||
[pages.settings.security]
|
||||
"admin" = "Quản trị viên"
|
||||
"secret" = "Mã thông báo bí mật"
|
||||
"loginSecurity" = "Bảo mật đăng nhập"
|
||||
"loginSecurityDesc" = "Bật bước bảo mật đăng nhập bổ sung cho người dùng"
|
||||
"secretToken" = "Mã thông báo bí mật"
|
||||
"secretTokenDesc" = "Vui lòng sao chép và lưu trữ mã thông báo này một cách an toàn ở nơi an toàn. Mã thông báo này cần thiết để đăng nhập và không thể phục hồi từ công cụ lệnh x-ui."
|
||||
"secretToken" = "Mã bí mật"
|
||||
"secretTokenDesc" = "Vui lòng sao chép và lưu trữ mã này một cách an toàn ở nơi an toàn. Mã này cần thiết để đăng nhập và không thể phục hồi từ công cụ lệnh x-ui."
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "Chỉnh sửa cài đặt "
|
||||
@@ -429,6 +452,7 @@
|
||||
"noIpRecord" = "❗ Không có bản ghi IP!"
|
||||
"noInbounds" = "❗ Không tìm thấy inbound!"
|
||||
"unlimited" = "♾ Không giới hạn"
|
||||
"add" = "Thêm"
|
||||
"month" = "Tháng"
|
||||
"months" = "Tháng"
|
||||
"day" = "Ngày"
|
||||
@@ -436,7 +460,9 @@
|
||||
"hours" = "Giờ"
|
||||
"unknown" = "Không rõ"
|
||||
"inbounds" = "Vào"
|
||||
"clients" = "Các khách hàng"
|
||||
"clients" = "Các người dùng"
|
||||
"offline" = "🔴 Ngoại tuyến"
|
||||
"online" = "🟢 Trực tuyến"
|
||||
|
||||
[tgbot.commands]
|
||||
"unknown" = "❗ Lệnh không rõ"
|
||||
@@ -447,8 +473,8 @@
|
||||
"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."
|
||||
"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 [Email]</code>"
|
||||
|
||||
[tgbot.messages]
|
||||
"cpuThreshold" = "🔴 Sử dụng CPU {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%"
|
||||
@@ -463,7 +489,7 @@
|
||||
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
|
||||
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
|
||||
"ip" = "🌐 IP: {{ .IP }}\r\n"
|
||||
"ips" = "🔢 Các IP: \r\n{{ .IPs }}\r\n"
|
||||
"ips" = "🔢 Các IP:\r\n{{ .IPs }}\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"
|
||||
@@ -477,8 +503,9 @@
|
||||
"port" = "🔌 Cổng: {{ .Port }}\r\n"
|
||||
"expire" = "📅 Ngày hết hạn: {{ .Time }}\r\n"
|
||||
"expireIn" = "📅 Hết hạn sau: {{ .Time }}\r\n"
|
||||
"active" = "💡 Hoạt động: ✅ Có\r\n"
|
||||
"inactive" = "💡 Hoạt động: ❌ Không\r\n"
|
||||
"active" = "💡 Đang hoạt động: {{ .Enable }}\r\n"
|
||||
"enabled" = "🚨 Đã bật: {{ .Enable }}\r\n"
|
||||
"online" = "🌐 Trạng thái 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"
|
||||
@@ -486,10 +513,13 @@
|
||||
"TGUser" = "👤 Người dùng Telegram: {{ .TelegramID }}\r\n"
|
||||
"exhaustedMsg" = "🚨 Sự cạn kiệt {{ .Type }}:\r\n"
|
||||
"exhaustedCount" = "🚨 Số lần cạn kiệt {{ .Type }}:\r\n"
|
||||
"onlinesCount" = "🌐 Khách hàng trực tuyến: {{ .Count }}\r\n"
|
||||
"disabled" = "🛑 Vô hiệu hóa: {{ .Disabled }}\r\n"
|
||||
"depleteSoon" = "🔜 Sắp cạn kiệt: {{ .Deplete }}\r\n \r\n"
|
||||
"depleteSoon" = "🔜 Sắp cạn kiệt: {{ .Deplete }}\r\n\r\n"
|
||||
"backupTime" = "🗄 Thời gian sao lưu: {{ .Time }}\r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 Đã cập nhật lần cuối vào: {{ .Time }}\r\n \r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 Đã cập nhật lần cuối vào: {{ .Time }}\r\n\r\n"
|
||||
"yes" = "✅ Có"
|
||||
"no" = "❌ Không"
|
||||
|
||||
[tgbot.buttons]
|
||||
"closeKeyboard" = "❌ Đóng Bàn Phím"
|
||||
@@ -499,11 +529,13 @@
|
||||
"confirmResetTraffic" = "✅ Xác Nhận Đặt Lại Lưu Lượng?"
|
||||
"confirmClearIps" = "✅ Xác Nhận Xóa Các IP?"
|
||||
"confirmRemoveTGUser" = "✅ Xác Nhận Xóa Người Dùng Telegram?"
|
||||
"confirmToggle" = "✅ Xác nhận Bật/Tắt người dùng?"
|
||||
"dbBackup" = "Tải bản sao lưu cơ sở dữ liệu"
|
||||
"serverUsage" = "Sử Dụng Máy Chủ"
|
||||
"getInbounds" = "Lấy cổng vào"
|
||||
"depleteSoon" = "Depleted Soon"
|
||||
"clientUsage" = "Lấy Sử Dụng"
|
||||
"onlines" = "Khách hàng trực tuyến"
|
||||
"commands" = "Lệnh"
|
||||
"refresh" = "🔄 Cập Nhật"
|
||||
"clearIPs" = "❌ Xóa IP"
|
||||
@@ -511,14 +543,16 @@
|
||||
"selectTGUser" = "👤 Chọn Người Dùng Telegram"
|
||||
"selectOneTGUser" = "👤 Chọn một người dùng telegram:"
|
||||
"resetTraffic" = "📈 Đặt Lại Lưu Lượng"
|
||||
"resetExpire" = "📅 Đặt Lại Ngày Hết Hạn"
|
||||
"resetExpire" = "📅 Thay đổi ngày hết hạn"
|
||||
"ipLog" = "🔢 Nhật ký địa chỉ IP"
|
||||
"ipLimit" = "🔢 Giới Hạn địa chỉ IP"
|
||||
"setTGUser" = "👤 Đặt Người Dùng Telegram"
|
||||
"toggle" = "🔘 Bật / Tắt"
|
||||
"custom" = "🔢 Tùy chỉnh"
|
||||
"confirmNumber" = "✅ Xác nhận : {{ .Num }}"
|
||||
"confirmNumber" = "✅ Xác nhận: {{ .Num }}"
|
||||
"confirmNumberAdd" = "✅ Xác nhận thêm: {{ .Num }}"
|
||||
"limitTraffic" = "🚧 Giới hạn lưu lượng"
|
||||
"getBanLogs" = "Cấm nhật ký"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ Thành công!"
|
||||
@@ -538,5 +572,4 @@
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }} : Người Dùng Telegram Đã Được Xóa Thành Công."
|
||||
"enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công."
|
||||
"disableSuccess" = "✅ {{ .Email }} : Đã Tắt Thành Công."
|
||||
"askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserName" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng tên người dùng hoặc ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nTên người dùng của bạn: <b>@{{ .TgUserName }}</b>\r\n\r\nID người dùng của bạn: <b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <code>{{ .TgUserID }}</code>"
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"remark" = "备注"
|
||||
"enable" = "启用"
|
||||
"protocol" = "协议"
|
||||
"search" = "搜尋"
|
||||
"filter" = "过滤器"
|
||||
"search" = "搜索"
|
||||
"filter" = "筛选"
|
||||
"loading" = "加载中..."
|
||||
"second" = "秒"
|
||||
"minute" = "分钟"
|
||||
@@ -30,8 +30,8 @@
|
||||
"sure" = "确定"
|
||||
"encryption" = "加密"
|
||||
"transmission" = "传输"
|
||||
"host" = "主持人"
|
||||
"path" = "小路"
|
||||
"host" = "Host"
|
||||
"path" = "Path"
|
||||
"camouflage" = "伪装"
|
||||
"status" = "状态"
|
||||
"enabled" = "开启"
|
||||
@@ -49,8 +49,8 @@
|
||||
"install" = "安装"
|
||||
"clients" = "客户端"
|
||||
"usage" = "用法"
|
||||
"secretToken" = "秘密令牌"
|
||||
"remained" = "仍然存在"
|
||||
"secretToken" = "安全密钥"
|
||||
"remained" = "剩余"
|
||||
"security" = "安全"
|
||||
|
||||
[menu]
|
||||
@@ -59,10 +59,10 @@
|
||||
"settings" = "面板设置"
|
||||
"xray" = "Xray 设置"
|
||||
"logout" = "退出登录"
|
||||
"link" = "其他"
|
||||
"link" = "管理"
|
||||
|
||||
[pages.login]
|
||||
"title" = "登录"
|
||||
"title" = "欢迎"
|
||||
"loginAgain" = "登录时效已过,请重新登录"
|
||||
|
||||
[pages.login.toasts]
|
||||
@@ -79,7 +79,7 @@
|
||||
"xrayStatus" = "状态"
|
||||
"stopXray" = "停止"
|
||||
"restartXray" = "重启"
|
||||
"xraySwitch" = "切换版本"
|
||||
"xraySwitch" = "版本"
|
||||
"xraySwitchClick" = "点击你想切换的版本"
|
||||
"xraySwitchClickDesk" = "请谨慎选择,旧版本可能配置不兼容"
|
||||
"operationHours" = "系统正常运行时间"
|
||||
@@ -134,7 +134,7 @@
|
||||
"destinationPort" = "目标端口"
|
||||
"targetAddress" = "目标地址"
|
||||
"monitorDesc" = "默认留空即可"
|
||||
"meansNoLimit" = "表示不限制"
|
||||
"meansNoLimit" = " = 无限制(单位:GB)"
|
||||
"totalFlow" = "总流量"
|
||||
"leaveBlankToNeverExpire" = "留空则永不到期"
|
||||
"noRecommendKeepDefault" = "没有特殊需求保持默认即可"
|
||||
@@ -148,7 +148,7 @@
|
||||
"client" = "客户"
|
||||
"export" = "导出链接"
|
||||
"clone" = "克隆"
|
||||
"cloneInbound" = "创造"
|
||||
"cloneInbound" = "克隆"
|
||||
"cloneInboundContent" = "此入站的所有项目除 Port、Listening IP、Clients 将应用于克隆"
|
||||
"cloneInboundOk" = "从创建克隆"
|
||||
"resetAllTraffic" = "重置所有入站流量"
|
||||
@@ -173,7 +173,7 @@
|
||||
"setDefaultCert" = "从面板设置证书"
|
||||
"xtlsDesc" = "Xray核心需要1.7.5"
|
||||
"realityDesc" = "Xray核心需要1.8.0及以上版本"
|
||||
"telegramDesc" = "使用 Telegram ID,不包含 @ 符号或聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
|
||||
"telegramDesc" = "仅使用聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
|
||||
"subscriptionDesc" = "您可以在详细信息上找到您的子链接,也可以对多个配置使用相同的名称"
|
||||
"info" = "信息"
|
||||
"same" = "相同"
|
||||
@@ -195,26 +195,27 @@
|
||||
"prefix" = "前缀"
|
||||
"postfix" = "后缀"
|
||||
"delayedStart" = "首次使用后开始"
|
||||
"expireDays" = "过期天数"
|
||||
"expireDays" = "期间"
|
||||
"days" = "天"
|
||||
"renew" = "自动续订"
|
||||
"renewDesc" = "过期后自动续订。0 = 禁用"
|
||||
"renewDesc" = "到期后自动续订。(0 = 禁用)(单元: 天)"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "获取"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"requestHeader" = "请求头"
|
||||
"name" = "名称"
|
||||
"value" = "值"
|
||||
"request" = "要求"
|
||||
"response" = "回复"
|
||||
"name" = "姓名"
|
||||
"value" = "价值"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"requestVersion" = "请求版本"
|
||||
"requestMethod" = "请求方法"
|
||||
"requestPath" = "请求路径"
|
||||
"responseVersion" = "响应版本"
|
||||
"responseStatus" = "响应状态"
|
||||
"responseStatusDescription" = "响应状态说明"
|
||||
"version" = "版本"
|
||||
"method" = "方法"
|
||||
"path" = "小路"
|
||||
"status" = "地位"
|
||||
"statusDescription" = "状态说明"
|
||||
"requestHeader" = "请求头"
|
||||
"responseHeader" = "响应头"
|
||||
|
||||
[pages.inbounds.stream.quic]
|
||||
@@ -246,6 +247,8 @@
|
||||
"pageSize" = "分页大小"
|
||||
"pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用"
|
||||
"remarkModel" = "备注模型和分隔符"
|
||||
"datepicker" = "日期选择器"
|
||||
"datepickerDescription" = "选择器日历类型指定到期日期"
|
||||
"sampleRemark" = "备注示例"
|
||||
"oldUsername" = "原用户名"
|
||||
"currentPassword" = "原密码"
|
||||
@@ -255,6 +258,8 @@
|
||||
"telegramBotEnableDesc" = "重启面板生效"
|
||||
"telegramToken" = "电报机器人TOKEN"
|
||||
"telegramTokenDesc" = "重启面板生效"
|
||||
"telegramProxy" = "Socks5 代理"
|
||||
"telegramProxyDesc" = "如果您需要 Socks5 代理来连接 Telegram。 根据指南调整其设置。"
|
||||
"telegramChatId" = "以逗号分隔的多个 chatID 重启面板生效"
|
||||
"telegramChatIdDesc" = "多个聊天 ID 用逗号分隔。使用 @userinfobot 或在机器人中使用'/id'命令获取您的聊天 ID。"
|
||||
"telegramNotifyTime" = "电报机器人通知时间"
|
||||
@@ -327,8 +332,10 @@
|
||||
"PrivateIpDesc" = "更改配置模板以避免连接私有 IP 范围"
|
||||
"Ads" = "屏蔽广告"
|
||||
"AdsDesc" = "修改配置模板屏蔽广告"
|
||||
"Family" = "启用家庭友好配置"
|
||||
"FamilyDesc" = "避免为家人连接到不安全的网站"
|
||||
"Family" = "阻止恶意软件和成人内容"
|
||||
"FamilyDesc" = "Cloudflare DNS 解析器可阻止恶意软件和成人内容以保护家庭."
|
||||
"Security" = "阻止恶意软件、网络钓鱼和加密货币挖矿网站"
|
||||
"SecurityDesc" = "更改安全防护配置模板."
|
||||
"Speedtest" = "阻止测速网站"
|
||||
"SpeedtestDesc" = "更改配置模板以避免连接到速度测试网站。 重新启动面板以应用更改。"
|
||||
"IRIp" = "禁止伊朗 IP 范围连接"
|
||||
@@ -343,6 +350,10 @@
|
||||
"RussiaIpDesc" = "修改配置模板避免连接俄罗斯IP范围"
|
||||
"RussiaDomain" = "禁止俄罗斯域连接"
|
||||
"RussiaDomainDesc" = "更改配置模板避免连接俄罗斯域"
|
||||
"VNIp" = "禁用与越南 IP 的连接"
|
||||
"VNIpDesc" = "更改配置模板以避免连接到越南 IP 范围"
|
||||
"VNDomain" = "禁用与越南域的连接"
|
||||
"VNDomainDesc" = "更改配置模板以避免连接到越南域"
|
||||
"DirectIRIp" = "直接连接到伊朗 IP 范围"
|
||||
"DirectIRIpDesc" = "更改直接连接到伊朗 IP 范围的配置模板"
|
||||
"DirectIRDomain" = "直接连接到伊朗域"
|
||||
@@ -355,6 +366,10 @@
|
||||
"DirectRussiaIpDesc" = "更改直接连接到俄罗斯 IP 范围的配置模板"
|
||||
"DirectRussiaDomain" = "直接连接到俄罗斯域"
|
||||
"DirectRussiaDomainDesc" = "更改直接连接到俄罗斯域的配置模板"
|
||||
"DirectVNIp" = "直接连接越南IP"
|
||||
"DirectVNIpDesc" = "更改直接连接到越南 IP 范围的配置模板"
|
||||
"DirectVNDomain" = "直接连接至越南域名"
|
||||
"DirectVNDomainDesc" = "更改直连越南域的配置模板。"
|
||||
"GoogleIPv4" = "为谷歌使用 IPv4"
|
||||
"GoogleIPv4Desc" = "添加谷歌连接IPv4的路由"
|
||||
"NetflixIPv4" = "为 Netflix 使用 IPv4"
|
||||
@@ -369,7 +384,7 @@
|
||||
"SpotifyWARPDesc" = "为Spotify添加路由到WARP"
|
||||
"IRWARP" = "将伊朗域名路由到 WARP"
|
||||
"IRWARPDesc" = "将伊朗域的路由添加到 WARP。 重启面板生效"
|
||||
"Inbounds" = "界内"
|
||||
"Inbounds" = "入站"
|
||||
"InboundsDesc" = "更改配置模板接受特殊客户端"
|
||||
"Outbounds" = "出站"
|
||||
"OutboundsDesc" = "更改配置模板定义此服务器的传出方式"
|
||||
@@ -397,7 +412,7 @@
|
||||
"editOutbound" = "编辑出站"
|
||||
"editReverse" = "编辑反向"
|
||||
"tag" = "标签"
|
||||
"tagDesc" = "独特的标签"
|
||||
"tagDesc" = "唯一标记"
|
||||
"address" = "地址"
|
||||
"reverse" = "反转"
|
||||
"domain" = "域名"
|
||||
@@ -406,6 +421,14 @@
|
||||
"portal" = "门户"
|
||||
"intercon" = "互连"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "密钥"
|
||||
"publicKey" = "公钥"
|
||||
"allowedIPs" = "允许的 IP"
|
||||
"endpoint" = "终点"
|
||||
"psk" = "共享密钥"
|
||||
"domainStrategy" = "域策略"
|
||||
|
||||
[pages.settings.security]
|
||||
"admin" = "管理员"
|
||||
"secret" = "密钥"
|
||||
@@ -429,6 +452,7 @@
|
||||
"noIpRecord" = "❗ 没有IP记录!"
|
||||
"noInbounds" = "❗ 没有找到入站连接!"
|
||||
"unlimited" = "♾ 无限制"
|
||||
"add" = "添加"
|
||||
"month" = "月"
|
||||
"months" = "月"
|
||||
"day" = "天"
|
||||
@@ -437,6 +461,8 @@
|
||||
"unknown" = "未知"
|
||||
"inbounds" = "入站连接"
|
||||
"clients" = "客户端"
|
||||
"offline" = "🔴 离线"
|
||||
"online" = "🟢 在线的"
|
||||
|
||||
[tgbot.commands]
|
||||
"unknown" = "❗ 未知命令"
|
||||
@@ -447,8 +473,8 @@
|
||||
"status" = "✅ 机器人正常运行!"
|
||||
"usage" = "❗ 请输入要搜索的文本!"
|
||||
"getID" = "🆔 您的ID为:<code>{{ .ID }}</code>"
|
||||
"helpAdminCommands" = "搜索客户端邮箱:\r\n<code>/usage [Email]</code>\r\n \r\n搜索入站连接(包含客户端统计信息):\r\n<code>/inbound [Remark]</code>"
|
||||
"helpClientCommands" = "要搜索统计信息,请使用以下命令:\r\n \r\n<code>/usage [UUID|Password]</code>\r\n \r\n对于vmess/vless,请使用UUID;对于Trojan,请使用密码。"
|
||||
"helpAdminCommands" = "搜索客户端邮箱:\r\n<code>/usage [Email]</code>\r\n\r\n搜索入站连接(包含客户端统计信息):\r\n<code>/inbound [Remark]</code>"
|
||||
"helpClientCommands" = "要搜索统计信息,请使用以下命令:\r\n\r\n<code>/usage [Email]</code>"
|
||||
|
||||
[tgbot.messages]
|
||||
"cpuThreshold" = "🔴 CPU 使用率为 {{ .Percent }}%,超过阈值 {{ .Threshold }}%"
|
||||
@@ -477,8 +503,9 @@
|
||||
"port" = "🔌 端口:{{ .Port }}\r\n"
|
||||
"expire" = "📅 过期日期:{{ .Time }}\r\n"
|
||||
"expireIn" = "📅 剩余时间:{{ .Time }}\r\n"
|
||||
"active" = "💡 激活:✅\r\n"
|
||||
"inactive" = "💡 激活: ❌\r\n"
|
||||
"active" = "💡 激活:{{ .Enable }}\r\n"
|
||||
"enabled" = "🚨 已启用:{{ .Enable }}\r\n"
|
||||
"online" = "🌐 连接状态:{{ .Status }}\r\n"
|
||||
"email" = "📧 邮箱:{{ .Email }}\r\n"
|
||||
"upload" = "🔼 上传↑:{{ .Upload }}\r\n"
|
||||
"download" = "🔽 下载↓:{{ .Download }}\r\n"
|
||||
@@ -486,10 +513,13 @@
|
||||
"TGUser" = "👤 电报用户:{{ .TelegramID }}\r\n"
|
||||
"exhaustedMsg" = "🚨 耗尽的{{ .Type }}:\r\n"
|
||||
"exhaustedCount" = "🚨 耗尽的{{ .Type }}数量:\r\n"
|
||||
"onlinesCount" = "🌐 在线客户:{{ .Count }}\r\n"
|
||||
"disabled" = "🛑 禁用:{{ .Disabled }}\r\n"
|
||||
"depleteSoon" = "🔜 即将耗尽:{{ .Deplete }}\r\n \r\n"
|
||||
"depleteSoon" = "🔜 即将耗尽:{{ .Deplete }}\r\n\r\n"
|
||||
"backupTime" = "🗄 备份时间:{{ .Time }}\r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 刷新时间:{{ .Time }}\r\n \r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 刷新时间:{{ .Time }}\r\n\r\n"
|
||||
"yes" = "✅ 是的"
|
||||
"no" = "❌ 没有"
|
||||
|
||||
[tgbot.buttons]
|
||||
"closeKeyboard" = "❌ 关闭键盘"
|
||||
@@ -499,11 +529,13 @@
|
||||
"confirmResetTraffic" = "✅ 确认重置流量?"
|
||||
"confirmClearIps" = "✅ 确认清除 IP?"
|
||||
"confirmRemoveTGUser" = "✅ 确认移除 Telegram 用户?"
|
||||
"confirmToggle" = "✅ 确认启用/禁用用户?"
|
||||
"dbBackup" = "获取数据库备份"
|
||||
"serverUsage" = "服务器使用情况"
|
||||
"getInbounds" = "获取入站信息"
|
||||
"depleteSoon" = "即将耗尽"
|
||||
"clientUsage" = "获取使用情况"
|
||||
"onlines" = "在线客户"
|
||||
"commands" = "命令"
|
||||
"refresh" = "🔄 刷新"
|
||||
"clearIPs" = "❌ 清除 IP"
|
||||
@@ -511,14 +543,16 @@
|
||||
"selectTGUser" = "👤 选择 Telegram 用户"
|
||||
"selectOneTGUser" = "👤 选择一个 Telegram 用户:"
|
||||
"resetTraffic" = "📈 重置流量"
|
||||
"resetExpire" = "📅 重置过期天数"
|
||||
"resetExpire" = "📅 更改到期日期"
|
||||
"ipLog" = "🔢 IP 日志"
|
||||
"ipLimit" = "🔢 IP 限制"
|
||||
"setTGUser" = "👤 设置 Telegram 用户"
|
||||
"toggle" = "🔘 启用/禁用"
|
||||
"custom" = "🔢 风俗"
|
||||
"confirmNumber" = "✅ 确认 : {{ .Num }}"
|
||||
"confirmNumber" = "✅ 确认: {{ .Num }}"
|
||||
"confirmNumberAdd" = "✅ 确认添加:{{ .Num }}"
|
||||
"limitTraffic" = "🚧 交通限制"
|
||||
"getBanLogs" = "禁止日志"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ 成功的!"
|
||||
@@ -538,5 +572,4 @@
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }}:Telegram 用户已成功移除。"
|
||||
"enableSuccess" = "✅ {{ .Email }}:已成功启用。"
|
||||
"disableSuccess" = "✅ {{ .Email }}:已成功禁用。"
|
||||
"askToAddUserId" = "未找到您的配置!\r\n请向管理员询问,在您的配置中使用您的 Telegram 用户ID。\r\n\r\n您的用户ID:<b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserName" = "未找到您的配置!\r\n请向管理员询问,在您的配置中使用您的 Telegram 用户名或用户ID。\r\n\r\n您的用户名:<b>@{{ .TgUserName }}</b>\r\n\r\n您的用户ID:<b>{{ .TgUserID }}</b>"
|
||||
"askToAddUserId" = "未找到您的配置!\r\n请向管理员询问,在您的配置中使用您的 Telegram 用户ID。\r\n\r\n您的用户ID:<code>{{ .TgUserID }}</code>"
|
||||
|
||||
@@ -240,11 +240,11 @@ func (s *Server) startTask() {
|
||||
if err != nil {
|
||||
logger.Warning("start xray failed:", err)
|
||||
}
|
||||
// Check whether xray is running every 30 seconds
|
||||
s.cron.AddJob("@every 30s", job.NewCheckXrayRunningJob())
|
||||
// Check whether xray is running every second
|
||||
s.cron.AddJob("@every 1s", job.NewCheckXrayRunningJob())
|
||||
|
||||
// Check if xray needs to be restarted
|
||||
s.cron.AddFunc("@every 10s", func() {
|
||||
// Check if xray needs to be restarted every 30 seconds
|
||||
s.cron.AddFunc("@every 30s", func() {
|
||||
if s.xrayService.IsNeedRestartAndSetFalse() {
|
||||
err := s.xrayService.RestartXray(false)
|
||||
if err != nil {
|
||||
|
||||
173
x-ui.sh
173
x-ui.sh
@@ -51,11 +51,23 @@ elif [[ "${release}" == "fedora" ]]; then
|
||||
echo -e "${red}please use Fedora 36 or higher version! ${plain}\n" && exit 1
|
||||
fi
|
||||
elif [[ "${release}" == "debian" ]]; then
|
||||
if [[ ${os_version} -lt 10 ]]; then
|
||||
echo -e "${red} Please use Debian 10 or higher ${plain}\n" && exit 1
|
||||
if [[ ${os_version} -lt 11 ]]; then
|
||||
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
elif [[ "${release}" == "almalinux" ]]; then
|
||||
if [[ ${os_version} -lt 9 ]]; then
|
||||
echo -e "${red} Please use Almalinux 9 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
elif [[ "${release}" == "rocky" ]]; then
|
||||
if [[ ${os_version} -lt 9 ]]; then
|
||||
echo -e "${red} Please use Rockylinux 9 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
elif [[ "${release}" == "arch" ]]; then
|
||||
echo "OS is ArchLinux"
|
||||
echo "Your OS is ArchLinux"
|
||||
elif [[ "${release}" == "manjaro" ]]; then
|
||||
echo "Your OS is Manjaro"
|
||||
elif [[ "${release}" == "armbian" ]]; then
|
||||
echo "Your OS is Armbian"
|
||||
fi
|
||||
|
||||
|
||||
@@ -107,7 +119,7 @@ install() {
|
||||
}
|
||||
|
||||
update() {
|
||||
confirm "This function will forcefully reinstall the latest version, and the data will not be lost. Do you want to continue?" "n"
|
||||
confirm "This function will forcefully reinstall the latest version, and the data will not be lost. Do you want to continue?" "y"
|
||||
if [[ $? != 0 ]]; then
|
||||
LOGE "Cancelled"
|
||||
if [[ $# == 0 ]]; then
|
||||
@@ -122,6 +134,24 @@ update() {
|
||||
fi
|
||||
}
|
||||
|
||||
custom_version() {
|
||||
echo "Enter the panel version (like 2.0.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/mhsanaei/3x-ui/master/install.sh"
|
||||
|
||||
# Use the entered panel version in the download link
|
||||
install_command="bash <(curl -Ls $download_link) v$panel_version"
|
||||
|
||||
echo "Downloading and installing panel version $panel_version..."
|
||||
eval $install_command
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n"
|
||||
if [[ $? != 0 ]]; then
|
||||
@@ -321,7 +351,7 @@ enable_bbr() {
|
||||
ubuntu|debian)
|
||||
apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
|
||||
;;
|
||||
centos)
|
||||
centos|almalinux|rocky)
|
||||
yum -y update && yum -y install ca-certificates
|
||||
;;
|
||||
fedora)
|
||||
@@ -519,11 +549,13 @@ update_geo() {
|
||||
|
||||
systemctl stop x-ui
|
||||
cd ${binFolder}
|
||||
rm -f geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat
|
||||
rm -f geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat geoip_VN.dat geosite_VN.dat
|
||||
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
wget -O geoip_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||
wget -O geoip_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
|
||||
systemctl start x-ui
|
||||
echo -e "${green}Geosite.dat + Geoip.dat + geoip_IR.dat + geosite_IR.dat have been updated successfully in bin folder '${binfolder}'!${plain}"
|
||||
before_show_menu
|
||||
@@ -579,9 +611,9 @@ ssl_cert_issue() {
|
||||
fi
|
||||
# install socat second
|
||||
case "${release}" in
|
||||
ubuntu|debian)
|
||||
ubuntu|debian|armbian)
|
||||
apt update && apt install socat -y ;;
|
||||
centos)
|
||||
centos|almalinux|rocky)
|
||||
yum -y update && yum -y install socat ;;
|
||||
fedora)
|
||||
dnf -y update && dnf -y install socat ;;
|
||||
@@ -768,6 +800,33 @@ warp_cloudflare() {
|
||||
esac
|
||||
}
|
||||
|
||||
multi_protocol() {
|
||||
echo "This script only supports Vless and Vmess. if you use another protocols, DON'T INSTALL or get backup first! "
|
||||
echo -e "${green}\t1.${plain} Install Multi Protocol Script"
|
||||
echo -e "${green}\t2.${plain} Uninstall"
|
||||
echo -e "${green}\t3.${plain} Start Service"
|
||||
echo -e "${green}\t4.${plain} Stop Service"
|
||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||
read -p "Choose an option: " choice
|
||||
case "$choice" in
|
||||
0)
|
||||
show_menu ;;
|
||||
1)
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/M4mmad/3xui-multi-protocol/master/install.sh --ipv4)
|
||||
;;
|
||||
2)
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/M4mmad/3xui-multi-protocol/master/unistall.sh --ipv4)
|
||||
;;
|
||||
3)
|
||||
systemctl start 3xui-multi-protocol
|
||||
;;
|
||||
4)
|
||||
systemctl stop 3xui-multi-protocol
|
||||
;;
|
||||
*) echo "Invalid choice" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_speedtest() {
|
||||
# Check if Speedtest is already installed
|
||||
if ! command -v speedtest &> /dev/null; then
|
||||
@@ -924,7 +983,7 @@ install_iplimit() {
|
||||
case "${release}" in
|
||||
ubuntu|debian)
|
||||
apt update && apt install fail2ban -y ;;
|
||||
centos)
|
||||
centos|almalinux|rocky)
|
||||
yum -y update && yum -y install fail2ban ;;
|
||||
fedora)
|
||||
dnf -y update && dnf -y install fail2ban ;;
|
||||
@@ -987,7 +1046,7 @@ remove_iplimit(){
|
||||
case "${release}" in
|
||||
ubuntu|debian)
|
||||
apt-get purge fail2ban -y;;
|
||||
centos)
|
||||
centos|almalinux|rocky)
|
||||
yum remove fail2ban -y;;
|
||||
fedora)
|
||||
dnf remove fail2ban -y;;
|
||||
@@ -1029,36 +1088,38 @@ show_menu() {
|
||||
${green}3X-ui Panel Management Script${plain}
|
||||
${green}0.${plain} Exit Script
|
||||
————————————————
|
||||
${green}1.${plain} Install x-ui
|
||||
${green}2.${plain} Update x-ui
|
||||
${green}3.${plain} Uninstall x-ui
|
||||
${green}1.${plain} Install
|
||||
${green}2.${plain} Update
|
||||
${green}3.${plain} Custom Version
|
||||
${green}4.${plain} Uninstall
|
||||
————————————————
|
||||
${green}4.${plain} Reset Username & Password & Secret Token
|
||||
${green}5.${plain} Reset Panel Settings
|
||||
${green}6.${plain} Change Panel Port
|
||||
${green}7.${plain} View Current Panel Settings
|
||||
${green}5.${plain} Reset Username & Password & Secret Token
|
||||
${green}6.${plain} Reset Settings
|
||||
${green}7.${plain} Change Port
|
||||
${green}8.${plain} View Current Settings
|
||||
————————————————
|
||||
${green}8.${plain} Start x-ui
|
||||
${green}9.${plain} Stop x-ui
|
||||
${green}10.${plain} Restart x-ui
|
||||
${green}11.${plain} Check x-ui Status
|
||||
${green}12.${plain} Check x-ui Logs
|
||||
${green}9.${plain} Start
|
||||
${green}10.${plain} Stop
|
||||
${green}11.${plain} Restart
|
||||
${green}12.${plain} Check Status
|
||||
${green}13.${plain} Check Logs
|
||||
————————————————
|
||||
${green}13.${plain} Enable x-ui On System Startup
|
||||
${green}14.${plain} Disable x-ui On System Startup
|
||||
${green}14.${plain} Enable x-ui On System Startup
|
||||
${green}15.${plain} Disable x-ui On System Startup
|
||||
————————————————
|
||||
${green}15.${plain} SSL Certificate Management
|
||||
${green}16.${plain} Cloudflare SSL Certificate
|
||||
${green}17.${plain} IP Limit Management
|
||||
${green}18.${plain} WARP Management
|
||||
${green}16.${plain} SSL Certificate Management
|
||||
${green}17.${plain} Cloudflare SSL Certificate
|
||||
${green}18.${plain} IP Limit Management
|
||||
${green}19.${plain} WARP Management
|
||||
${green}20.${plain} Multi Protocol Management
|
||||
————————————————
|
||||
${green}19.${plain} Enable BBR
|
||||
${green}20.${plain} Update Geo Files
|
||||
${green}21.${plain} Active Firewall and open ports
|
||||
${green}22.${plain} Speedtest by Ookla
|
||||
${green}21.${plain} Enable BBR
|
||||
${green}22.${plain} Update Geo Files
|
||||
${green}23.${plain} Active Firewall and open ports
|
||||
${green}24.${plain} Speedtest by Ookla
|
||||
"
|
||||
show_status
|
||||
echo && read -p "Please enter your selection [0-22]: " num
|
||||
echo && read -p "Please enter your selection [0-24]: " num
|
||||
|
||||
case "${num}" in
|
||||
0)
|
||||
@@ -1071,67 +1132,73 @@ show_menu() {
|
||||
check_install && update
|
||||
;;
|
||||
3)
|
||||
check_install && uninstall
|
||||
check_install && custom_version
|
||||
;;
|
||||
4)
|
||||
check_install && reset_user
|
||||
check_install && uninstall
|
||||
;;
|
||||
5)
|
||||
check_install && reset_config
|
||||
check_install && reset_user
|
||||
;;
|
||||
6)
|
||||
check_install && set_port
|
||||
check_install && reset_config
|
||||
;;
|
||||
7)
|
||||
check_install && check_config
|
||||
check_install && set_port
|
||||
;;
|
||||
8)
|
||||
check_install && start
|
||||
check_install && check_config
|
||||
;;
|
||||
9)
|
||||
check_install && stop
|
||||
check_install && start
|
||||
;;
|
||||
10)
|
||||
check_install && restart
|
||||
check_install && stop
|
||||
;;
|
||||
11)
|
||||
check_install && status
|
||||
check_install && restart
|
||||
;;
|
||||
12)
|
||||
check_install && show_log
|
||||
check_install && status
|
||||
;;
|
||||
13)
|
||||
check_install && enable
|
||||
check_install && show_log
|
||||
;;
|
||||
14)
|
||||
check_install && disable
|
||||
check_install && enable
|
||||
;;
|
||||
15)
|
||||
ssl_cert_issue_main
|
||||
check_install && disable
|
||||
;;
|
||||
16)
|
||||
ssl_cert_issue_CF
|
||||
ssl_cert_issue_main
|
||||
;;
|
||||
17)
|
||||
iplimit_main
|
||||
ssl_cert_issue_CF
|
||||
;;
|
||||
18)
|
||||
warp_cloudflare
|
||||
iplimit_main
|
||||
;;
|
||||
19)
|
||||
enable_bbr
|
||||
warp_cloudflare
|
||||
;;
|
||||
20)
|
||||
update_geo
|
||||
multi_protocol
|
||||
;;
|
||||
21)
|
||||
open_ports
|
||||
enable_bbr
|
||||
;;
|
||||
22)
|
||||
update_geo
|
||||
;;
|
||||
23)
|
||||
open_ports
|
||||
;;
|
||||
24)
|
||||
run_speedtest
|
||||
;;
|
||||
*)
|
||||
LOGE "Please enter the correct number [0-22]"
|
||||
LOGE "Please enter the correct number [0-24]"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -50,7 +50,9 @@ func (x *XrayAPI) Init(apiPort int) (err error) {
|
||||
}
|
||||
|
||||
func (x *XrayAPI) Close() {
|
||||
x.grpcClient.Close()
|
||||
if x.grpcClient != nil {
|
||||
x.grpcClient.Close()
|
||||
}
|
||||
x.HandlerServiceClient = nil
|
||||
x.StatsServiceClient = nil
|
||||
x.isConnected = false
|
||||
|
||||
@@ -20,8 +20,13 @@ func (lw *LogWriter) Write(m []byte) (n int, err error) {
|
||||
lw.lastLine = messages[len(messages)-1]
|
||||
|
||||
for _, msg := range messages {
|
||||
messageBody := msg
|
||||
|
||||
// Remove timestamp
|
||||
messageBody := strings.TrimSpace(strings.SplitN(msg, " ", 3)[2])
|
||||
splittedMsg := strings.SplitN(msg, " ", 3)
|
||||
if len(splittedMsg) > 2 {
|
||||
messageBody = strings.TrimSpace(strings.SplitN(msg, " ", 3)[2])
|
||||
}
|
||||
|
||||
// Find level in []
|
||||
startIndex := strings.Index(messageBody, "[")
|
||||
|
||||
@@ -41,12 +41,24 @@ func GetIPLimitLogPath() string {
|
||||
return config.GetLogFolder() + "/3xipl.log"
|
||||
}
|
||||
|
||||
func GetIPLimitPrevLogPath() string {
|
||||
return config.GetLogFolder() + "/3xipl.prev.log"
|
||||
}
|
||||
|
||||
func GetIPLimitBannedLogPath() string {
|
||||
return config.GetLogFolder() + "/3xipl-banned.log"
|
||||
}
|
||||
|
||||
func GetIPLimitBannedPrevLogPath() string {
|
||||
return config.GetLogFolder() + "/3xipl-banned.prev.log"
|
||||
}
|
||||
|
||||
func GetAccessPersistentLogPath() string {
|
||||
return config.GetLogFolder() + "/3xipl-access-persistent.log"
|
||||
return config.GetLogFolder() + "/3xipl-ap.log"
|
||||
}
|
||||
|
||||
func GetAccessPersistentPrevLogPath() string {
|
||||
return config.GetLogFolder() + "/3xipl-ap.prev.log"
|
||||
}
|
||||
|
||||
func GetAccessLogPath() string {
|
||||
|
||||
Reference in New Issue
Block a user