Compare commits

..

1 Commits

Author SHA1 Message Date
MHSanaei
5cd37abbaa v2.0.2 2023-12-19 14:26:00 +03:30
75 changed files with 2402 additions and 4948 deletions

View File

@@ -1,5 +1,4 @@
name: Release 3X-UI for Docker name: Release X-ui dockerhub
on: on:
push: push:
tags: tags:
@@ -7,44 +6,36 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
build: build_and_push:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Check out the code
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.1
with:
submodules: true
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/mhsanaei/3x-ui
tags: |
type=ref,event=branch
type=ref,event=tag
type=pep440,pattern={{version}}
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3.0.0 uses: docker/setup-qemu-action@v3.0.0
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.0.0 uses: docker/setup-buildx-action@v3.0.0
- name: Login to GHCR - name: Log in to GitHub Container Registry
uses: docker/login-action@v3.0.0 uses: docker/login-action@v3.0.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push - name: Docker meta
id: meta
uses: docker/metadata-action@v5.4.0
with:
images: ghcr.io/${{ github.repository }}
- name: Build and push Docker image
uses: docker/build-push-action@v5.1.0 uses: docker/build-push-action@v5.1.0
with: with:
context: . context: .
push: true push: ${{ github.event_name != 'pull_request' }}
platforms: linux/amd64,linux/arm64/v8,linux/arm/v7,linux/arm/v6,linux/386 platforms: linux/amd64, linux/arm64/v8, linux/arm/v7
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}

View File

@@ -1,4 +1,4 @@
name: Release 3X-UI name: Release 3X-ui
on: on:
push: push:
@@ -10,13 +10,7 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
platform: platform: [amd64, arm64, arm]
- amd64
- arm64
- armv7
- armv6
- 386
- armv5
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -26,20 +20,14 @@ jobs:
uses: actions/setup-go@v5.0.0 uses: actions/setup-go@v5.0.0
with: with:
go-version: '1.21' go-version: '1.21'
- name: Install dependencies - name: Install dependencies for arm64 and arm
if: matrix.platform == 'arm64' || matrix.platform == 'arm'
run: | run: |
sudo apt-get update sudo apt-get update
if [ "${{ matrix.platform }}" == "arm64" ]; then sudo apt install gcc-aarch64-linux-gnu
sudo apt install gcc-aarch64-linux-gnu if [ "${{ matrix.platform }}" == "arm" ]; then
elif [ "${{ matrix.platform }}" == "armv7" ]; then
sudo apt install gcc-arm-linux-gnueabihf sudo apt install gcc-arm-linux-gnueabihf
elif [ "${{ matrix.platform }}" == "armv6" ]; then
sudo apt install gcc-arm-linux-gnueabihf
elif [ "${{ matrix.platform }}" == "386" ]; then
sudo apt install gcc-i686-linux-gnu
elif [ "${{ matrix.platform }}" == "armv5" ]; then
sudo apt install gcc-arm-linux-gnueabi
fi fi
- name: Build x-ui - name: Build x-ui
@@ -48,23 +36,9 @@ jobs:
export GOOS=linux export GOOS=linux
export GOARCH=${{ matrix.platform }} export GOARCH=${{ matrix.platform }}
if [ "${{ matrix.platform }}" == "arm64" ]; then if [ "${{ matrix.platform }}" == "arm64" ]; then
export GOARCH=arm64
export CC=aarch64-linux-gnu-gcc export CC=aarch64-linux-gnu-gcc
elif [ "${{ matrix.platform }}" == "armv7" ]; then elif [ "${{ matrix.platform }}" == "arm" ]; then
export GOARCH=arm
export GOARM=7
export CC=arm-linux-gnueabihf-gcc export CC=arm-linux-gnueabihf-gcc
elif [ "${{ matrix.platform }}" == "armv6" ]; then
export GOARCH=arm
export GOARM=6
export CC=arm-linux-gnueabihf-gcc
elif [ "${{ matrix.platform }}" == "386" ]; then
export GOARCH=386
export CC=i686-linux-gnu-gcc
elif [ "${{ matrix.platform }}" == "armv5" ]; then
export GOARCH=arm
export GOARM=5
export CC=arm-linux-gnueabi-gcc
fi fi
go build -o xui-release -v main.go go build -o xui-release -v main.go
@@ -77,33 +51,20 @@ jobs:
cd x-ui/bin cd x-ui/bin
# Download dependencies # Download dependencies
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v1.8.7/"
if [ "${{ matrix.platform }}" == "amd64" ]; then if [ "${{ matrix.platform }}" == "amd64" ]; then
wget ${Xray_URL}Xray-linux-64.zip wget https://github.com/XTLS/Xray-core/releases/download/v1.8.6/Xray-linux-64.zip
unzip Xray-linux-64.zip unzip Xray-linux-64.zip
rm -f Xray-linux-64.zip rm -f Xray-linux-64.zip
elif [ "${{ matrix.platform }}" == "arm64" ]; then elif [ "${{ matrix.platform }}" == "arm64" ]; then
wget ${Xray_URL}Xray-linux-arm64-v8a.zip wget https://github.com/XTLS/Xray-core/releases/download/v1.8.6/Xray-linux-arm64-v8a.zip
unzip Xray-linux-arm64-v8a.zip unzip Xray-linux-arm64-v8a.zip
rm -f Xray-linux-arm64-v8a.zip rm -f Xray-linux-arm64-v8a.zip
elif [ "${{ matrix.platform }}" == "armv7" ]; then else
wget ${Xray_URL}Xray-linux-arm32-v7a.zip wget https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-arm32-v7a.zip
unzip Xray-linux-arm32-v7a.zip unzip Xray-linux-arm32-v7a.zip
rm -f 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
elif [ "${{ matrix.platform }}" == "386" ]; then
wget ${Xray_URL}Xray-linux-32.zip
unzip Xray-linux-32.zip
rm -f Xray-linux-32.zip
elif [ "${{ matrix.platform }}" == "armv5" ]; then
wget ${Xray_URL}Xray-linux-arm32-v5.zip
unzip Xray-linux-arm32-v5.zip
rm -f Xray-linux-arm32-v5.zip
fi fi
rm -f geoip.dat geosite.dat rm -f geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat geoip_VN.dat geosite_VN.dat
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
wget -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat wget -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat

View File

@@ -1,13 +1,10 @@
#!/bin/sh #!/bin/sh
case $1 in case $1 in
amd64) amd64)
ARCH="64" ARCH="64"
FNAME="amd64" FNAME="amd64"
;; ;;
i386)
ARCH="32"
FNAME="i386"
;;
armv8 | arm64 | aarch64) armv8 | arm64 | aarch64)
ARCH="arm64-v8a" ARCH="arm64-v8a"
FNAME="arm64" FNAME="arm64"
@@ -16,25 +13,23 @@ case $1 in
ARCH="arm32-v7a" ARCH="arm32-v7a"
FNAME="arm32" FNAME="arm32"
;; ;;
armv6)
ARCH="arm32-v6"
FNAME="armv6"
;;
*) *)
ARCH="64" ARCH="64"
FNAME="amd64" FNAME="amd64"
;; ;;
esac esac
mkdir -p build/bin mkdir -p build/bin
cd build/bin cd build/bin
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.7/Xray-linux-${ARCH}.zip"
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.6/Xray-linux-${ARCH}.zip"
unzip "Xray-linux-${ARCH}.zip" unzip "Xray-linux-${ARCH}.zip"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat geoip_VN.dat geosite_VN.dat
mv xray "xray-linux-${FNAME}" mv xray "xray-linux-${FNAME}"
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
wget -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.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 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 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 wget -O geosite_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
cd ../../

View File

@@ -1,9 +1,11 @@
# ======================================================== # ========================================================
# Stage: Builder # Stage: Builder
# ======================================================== # ========================================================
FROM golang:1.21-alpine AS builder FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
WORKDIR /app WORKDIR /app
ARG TARGETARCH ARG TARGETARCH
ENV CGO_ENABLED=1
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
RUN apk --no-cache --update add \ RUN apk --no-cache --update add \
build-base \ build-base \
@@ -13,8 +15,6 @@ RUN apk --no-cache --update add \
COPY . . COPY . .
ENV CGO_ENABLED=1
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
RUN go build -o build/x-ui main.go RUN go build -o build/x-ui main.go
RUN ./DockerInit.sh "$TARGETARCH" RUN ./DockerInit.sh "$TARGETARCH"
@@ -30,9 +30,9 @@ RUN apk add --no-cache --update \
tzdata \ tzdata \
fail2ban fail2ban
COPY --from=builder /app/build/ /app/ COPY --from=builder /app/build/ /app/
COPY --from=builder /app/DockerEntrypoint.sh /app/ COPY --from=builder /app/DockerEntrypoint.sh /app/
COPY --from=builder /app/x-ui.sh /usr/bin/x-ui COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
# Configure fail2ban # Configure fail2ban
RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \ RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \

330
README.md
View File

@@ -1,6 +1,6 @@
# 3X-UI # 3x-ui
**An Advanced Web Panel • Built on Xray Core** > **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**
[![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases) [![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases)
[![](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](#) [![](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](#)
@@ -8,34 +8,58 @@
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](#) [![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](#)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html) [![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
> **Disclaimer:** This project is only for personal learning and communication, please do not use it for illegal purposes, please do not use it in a production environment 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:
**If this project is helpful to you, you may wish to give it a**:star2: **Buy Me a Coffee :**
<a href="#"> - Tron USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC`
<img width="125" alt="image" src="https://github.com/MHSanaei/3x-ui/assets/115543613/7aa895dd-048a-42e7-989b-afd41a74e2e1.jpg"></a>
- USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC` # Install & Upgrade
## Install & Upgrade
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
``` ```
## Install Custom Version # Install custom version
To install your desired version, add the version to the end of the installation command. e.g., ver `v2.1.2`: To install your desired version you can add the version to the end of install command. Example for ver `v2.0.2`:
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.1.2 bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.0.2
``` ```
## Manual Install & Upgrade
# 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
<details> <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: 1. To download the latest version of the compressed package directly to your server, run the following command:
@@ -64,13 +88,11 @@ systemctl restart x-ui
</details> </details>
## Install with Docker # Install with Docker
<details> <details>
<summary>Click for Docker details</summary> <summary>Click for Docker details</summary>
#### Usage
1. Install Docker: 1. Install Docker:
```sh ```sh
@@ -103,154 +125,63 @@ systemctl restart x-ui
ghcr.io/mhsanaei/3x-ui:latest ghcr.io/mhsanaei/3x-ui:latest
``` ```
update to latest version </details>
```sh # Default settings
cd 3x-ui
docker compose down
docker compose pull 3x-ui
docker compose up -d
```
remove 3x-ui from docker <details>
<summary>Click for Default settings details</summary>
```sh - Port: 2053
docker stop 3x-ui - username and password will be generated randomly if you skip to modify your own security(x-ui "7")
docker rm 3x-ui - database path: /etc/x-ui/x-ui.db
cd -- - xray config path: /usr/local/x-ui/bin/config.json
rm -r 3x-ui
``` 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> </details>
# [WARP Configuration](https://gitlab.com/fscarmen/warp) (Optional)
## Recommended OS
- Ubuntu 20.04+
- Debian 11+
- CentOS 8+
- Fedora 36+
- Arch Linux
- Manjaro
- Armbian
- AlmaLinux 9+
- Rockylinux 9+
## Compatible Architectures & Devices
Supports a variety of different architectures and devices. Here are some of the main architectures that we support:
- **amd64**: This is the most common architecture for personal computers and servers. It supports most modern operating systems.
- **x86 / i386**: This architecture is prevalent in desktop and laptop computers. It's widely supported by various operating systems and applications. (Ex: Most Windows, macOS, and Linux systems)
- **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,...)
- **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,...)
- **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,...)
- **armv5 / arm / arm32**: This is an older architecture primarily used in early embedded systems. While it's less common today, some legacy devices may still rely on this architecture. (Ex: Early versions of Raspberry Pi, some older smartphones)
## 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> <details>
<summary>Click for default settings details</summary> <summary>Click for WARP Configuration details</summary>
### Information If you want to use routing to WARP follow steps as below:
- **Port:** 2053 1. If you already installed warp, you can uninstall using below command:
- **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)
```
**2.** If you already installed warp, you can uninstall using below command:
```sh ```sh
warp u warp u
``` ```
**3.** Turn on the config you need in panel 2. 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)
Config Features: Config Features:
@@ -260,14 +191,12 @@ If you want to use routing to WARP before v2.1.0 follow steps as below:
</details> </details>
## IP Limit # IP Limit
<details> <details>
<summary>Click for IP limit details</summary> <summary>Click for IP Limit details</summary>
#### Usage **Note: IP Limit won't work correctly when using IP Tunnel**
**Note:** IP Limit won't work correctly when using IP Tunnel
- For versions up to `v1.6.1`: - For versions up to `v1.6.1`:
@@ -293,34 +222,32 @@ If you want to use routing to WARP before v2.1.0 follow steps as below:
</details> </details>
## Telegram Bot # Telegram Bot
<details> <details>
<summary>Click for Telegram bot details</summary> <summary>Click for Telegram Bot details</summary>
#### Usage 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:
The web panel supports daily traffic, panel login, database backup, system status, client info, and other notification and functions through the Telegram Bot. To use the bot, you need to set the bot-related parameters in the panel, including: - Tg robot 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
- Telegram Token Reference syntax:
- 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
**Reference syntax:** # Telegram Bot Features
- `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 - Report periodic
- Login notification - Login notification
@@ -335,8 +262,9 @@ The web panel supports daily traffic, panel login, database backup, system statu
- Check depleted users - Check depleted users
- Receive backup by request and in periodic reports - Receive backup by request and in periodic reports
- Multi language bot - Multi language bot
</details>
### Setting up Telegram bot # Setting up Telegram bot
- Start [Botfather](https://t.me/BotFather) in your Telegram account: - Start [Botfather](https://t.me/BotFather) in your Telegram account:
![Botfather](./media/botfather.png) ![Botfather](./media/botfather.png)
@@ -356,15 +284,12 @@ 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. - 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.
![User ID](./media/user-id.png) ![User ID](./media/user-id.png)
</details>
## API Routes # API routes
<details> <details>
<summary>Click for API routes details</summary> <summary>Click for API routes details</summary>
#### Usage
- `/login` with `POST` user data: `{username: '', password: ''}` for login - `/login` with `POST` user data: `{username: '', password: ''}` for login
- `/panel/api/inbounds` base for following actions: - `/panel/api/inbounds` base for following actions:
@@ -399,12 +324,10 @@ 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) - [<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> </details>
## Environment Variables # Environment Variables
<details> <details>
<summary>Click for environment variables details</summary> <summary>Click for Environment Variables details</summary>
#### Usage
| Variable | Type | Default | | Variable | Type | Default |
| -------------- | :--------------------------------------------: | :------------ | | -------------- | :--------------------------------------------: | :------------ |
@@ -422,7 +345,27 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
</details> </details>
## Preview # 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._
- [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._
# Suggestion System
- Ubuntu 20.04+
- Debian 10+
- CentOS 8+
- Fedora 36+
- Arch Linux
- Manjaro
- Armbian (for ARM devices)
# Pictures
![1](./media/1.png) ![1](./media/1.png)
![2](./media/2.png) ![2](./media/2.png)
@@ -432,15 +375,6 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
![6](./media/6.png) ![6](./media/6.png)
![7](./media/7.png) ![7](./media/7.png)
## A Special Thanks to ## Stargazers over time
- [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
[![Stargazers over time](https://starchart.cc/MHSanaei/3x-ui.svg)](https://starchart.cc/MHSanaei/3x-ui) [![Stargazers over time](https://starchart.cc/MHSanaei/3x-ui.svg)](https://starchart.cc/MHSanaei/3x-ui)

View File

@@ -1 +1 @@
2.1.2 2.0.2

View File

@@ -37,7 +37,7 @@ type Inbound struct {
// config part // config part
Listen string `json:"listen" form:"listen"` Listen string `json:"listen" form:"listen"`
Port int `json:"port" form:"port"` Port int `json:"port" form:"port" gorm:"unique"`
Protocol Protocol `json:"protocol" form:"protocol"` Protocol Protocol `json:"protocol" form:"protocol"`
Settings string `json:"settings" form:"settings"` Settings string `json:"settings" form:"settings"`
StreamSettings string `json:"streamSettings" form:"streamSettings"` StreamSettings string `json:"streamSettings" form:"streamSettings"`

61
go.mod
View File

@@ -12,14 +12,13 @@ require (
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pelletier/go-toml/v2 v2.1.1 github.com/pelletier/go-toml/v2 v2.1.1
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil/v3 v3.23.12 github.com/shirou/gopsutil/v3 v3.23.11
github.com/valyala/fasthttp v1.51.0 github.com/xtls/xray-core v1.8.6
github.com/xtls/xray-core v1.8.7
go.uber.org/atomic v1.11.0 go.uber.org/atomic v1.11.0
golang.org/x/text v0.14.0 golang.org/x/text v0.14.0
google.golang.org/grpc v1.61.0 google.golang.org/grpc v1.60.1
gorm.io/driver/sqlite v1.5.4 gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.6 gorm.io/gorm v1.25.5
) )
require ( require (
@@ -27,45 +26,46 @@ require (
github.com/bytedance/sonic v1.10.2 // indirect github.com/bytedance/sonic v1.10.2 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect github.com/cloudflare/circl v1.3.6 // indirect
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
github.com/fasthttp/router v1.4.22 // indirect github.com/fasthttp/router v1.4.22 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect github.com/francoispqt/gojay v1.2.13 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // 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/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.3.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/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.17.0 // indirect github.com/go-playground/validator/v10 v10.15.5 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/btree v1.1.2 // indirect github.com/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 // indirect github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.2.2 // indirect github.com/gorilla/sessions v1.2.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/compress v1.17.2 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.19 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.13.2 // indirect github.com/onsi/ginkgo/v2 v2.13.1 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/quic-go/quic-go v0.40.1 // indirect github.com/quic-go/quic-go v0.40.0 // indirect
github.com/refraction-networking/utls v1.6.0 // indirect github.com/refraction-networking/utls v1.5.4 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/sagernet/sing v0.3.0 // indirect github.com/sagernet/sing v0.2.17 // indirect
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect github.com/sagernet/sing-shadowsocks v0.2.5 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect
@@ -75,24 +75,25 @@ require (
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.11 // indirect
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
github.com/valyala/bytebufferpool v1.0.0 // 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/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect
github.com/vishvananda/netns v0.0.4 // indirect github.com/vishvananda/netns v0.0.4 // indirect
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 // indirect github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.uber.org/mock v0.4.0 // indirect go.uber.org/mock v0.3.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect
golang.org/x/arch v0.6.0 // indirect golang.org/x/arch v0.6.0 // indirect
golang.org/x/crypto v0.18.0 // indirect golang.org/x/crypto v0.17.0 // indirect
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
golang.org/x/mod v0.14.0 // indirect golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.20.0 // indirect golang.org/x/net v0.18.0 // indirect
golang.org/x/sys v0.16.0 // indirect golang.org/x/sys v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.4.0 // indirect
golang.org/x/tools v0.16.1 // indirect golang.org/x/tools v0.15.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/protobuf v1.32.0 // indirect google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b // indirect
lukechampine.com/blake3 v1.2.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect

123
go.sum
View File

@@ -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 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -49,6 +49,8 @@ github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiD
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= 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/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.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
@@ -76,8 +78,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 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 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.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ4E5T9gDA0AIH74= github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24=
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-playground/validator/v10 v10.15.5/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 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 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= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
@@ -107,21 +109,19 @@ 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-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/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.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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42 h1:dHLYa5D8/Ta0aLR2XcPsrkpAgGeFs6thhMcQK0oQ0n8= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
github.com/google/pprof v0.0.0-20231229205709-960ae82b1e42/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= 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= 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/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
@@ -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 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 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-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0= github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik=
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= 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/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.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 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.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/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 h1:2NPsCsNFCVd7i+Su0xYsBrIhS3bE2XMv5gNTft2O+PQ=
github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/nicksnyder/go-i18n/v2 v2.3.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/onsi/ginkgo/v2 v2.13.2 h1:Bi2gGVkfn6gQcjNjZJVO8Gf0FHzMPf2phUei9tejVMs= github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU=
github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM=
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
@@ -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/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 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q= github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw=
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= github.com/quic-go/quic-go v0.40.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c=
github.com/refraction-networking/utls v1.6.0 h1:X5vQMqVx7dY7ehxxqkFER/W6DSjy8TMqSItXm8hRDYQ= github.com/refraction-networking/utls v1.5.4 h1:9k6EO2b8TaOGsQ7Pl7p9w6PUhx18/ZCeT0WNTZ7Uw4o=
github.com/refraction-networking/utls v1.6.0/go.mod h1:kHJ6R9DFFA0WsRgBM35iiDku4O7AqPR6y79iuzW7b10= github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
@@ -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 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sagernet/sing v0.3.0 h1:PIDVFZHnQAAYRL1UYqNM+0k5s8f/tb1lUW6UDcQiOc8= github.com/sagernet/sing v0.2.17 h1:vMPKb3MV0Aa5ws4dCJkRI8XEjrsUcDn810czd0FwmzI=
github.com/sagernet/sing v0.3.0/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g= github.com/sagernet/sing v0.2.17/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s= github.com/sagernet/sing-shadowsocks v0.2.5 h1:qxIttos4xu6ii7MTVJYA8EFQR7Q3KG6xMqmLJIFtBaY=
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM= github.com/sagernet/sing-shadowsocks v0.2.5/go.mod h1:MGWGkcU2xW2G2mfArT9/QqpVLOGU+dBaahZCtPHdt7A=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= 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/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 h1:XfLJSPIOUX+osiMraVgIrMR27uMXnRJWGm1+GL8/63U=
github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg= github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ=
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -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/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19 h1:capMfFYRgH9BCLd6A3Er/cH3A9Nz3CU2KwxwOQZIePI=
github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE= github.com/xtls/reality v0.0.0-20231112171332-de1173cf2b19/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
github.com/xtls/xray-core v1.8.7 h1:lb8O1l3/eAg3YAXA6tLm5M6N7BsX8wxW9sJLjU3dHkA= github.com/xtls/xray-core v1.8.6 h1:tr3nk/fZnFfCsmgZv7B3RC72N5qUC88oMGVLlybDey8=
github.com/xtls/xray-core v1.8.7/go.mod h1:9rFpflfQbgFeH1VKJw7yUmEy7myOyDCgNXXl0bmmyOo= github.com/xtls/xray-core v1.8.6/go.mod h1:hj2EB8rtcLdlTC8zxiWm5xL+C0k2Aie9Pk0mXtDEP6U=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -369,9 +369,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -381,20 +380,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/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb h1:c5tyN8sSp8jSDxdCCDXVOpJwYXXhmTkNMt+g0zTSOic=
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
@@ -407,19 +406,19 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4= google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
@@ -437,8 +436,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0= gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4= gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A= gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h1:yqkg3pTifuKukuWanp8spDsL4irJkHF5WI0J47hU87o= gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b h1:yqkg3pTifuKukuWanp8spDsL4irJkHF5WI0J47hU87o=
gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk= gvisor.dev/gvisor v0.0.0-20231104011432-48a6d7d5bd0b/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=

View File

@@ -26,15 +26,11 @@ echo "The OS release is: $release"
arch3xui() { arch3xui() {
case "$(uname -m)" in case "$(uname -m)" in
x86_64 | x64 | amd64) echo 'amd64' ;; x86_64 | x64 | amd64) echo 'amd64' ;;
i*86 | x86) echo '386' ;;
armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;; armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;;
armv7* | armv7 | arm) echo 'armv7' ;; armv7* | armv7 | arm | arm32 ) echo 'arm32' ;;
armv6* | armv6) echo 'armv6' ;;
armv5* | armv5) echo 'armv5' ;;
*) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;; *) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;;
esac esac
} }
echo "arch: $(arch3xui)" echo "arch: $(arch3xui)"
os_version="" os_version=""
@@ -46,27 +42,17 @@ if [[ "${release}" == "centos" ]]; then
fi fi
elif [[ "${release}" == "ubuntu" ]]; then elif [[ "${release}" == "ubuntu" ]]; then
if [[ ${os_version} -lt 20 ]]; 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 fi
elif [[ "${release}" == "fedora" ]]; then elif [[ "${release}" == "fedora" ]]; then
if [[ ${os_version} -lt 36 ]]; 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 fi
elif [[ "${release}" == "debian" ]]; then elif [[ "${release}" == "debian" ]]; then
if [[ ${os_version} -lt 11 ]]; then if [[ ${os_version} -lt 10 ]]; then
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1 echo -e "${red} Please use Debian 10 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 fi
elif [[ "${release}" == "arch" ]]; then elif [[ "${release}" == "arch" ]]; then
echo "Your OS is ArchLinux" echo "Your OS is ArchLinux"
@@ -81,21 +67,19 @@ fi
install_base() { install_base() {
case "${release}" in case "${release}" in
centos | almalinux | rocky) centos|fedora)
yum -y update && yum install -y -q wget curl tar yum -y update && yum install -y -q wget curl tar
;; ;;
fedora) arch|manjaro)
dnf -y update && dnf install -y -q wget curl tar pacman -Syu && pacman -Syu --noconfirm wget curl tar
;; ;;
arch | manjaro) *)
pacman -Syu && pacman -Syu --noconfirm wget curl tar apt-get update && apt-get upgrade -y && apt install -y -q wget curl tar
;; ;;
*)
apt-get update && apt install -y -q wget curl tar
;;
esac esac
} }
# This function will be called when user installed x-ui out of security # This function will be called when user installed x-ui out of security
config_after_install() { config_after_install() {
echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}" echo -e "${yellow}Install/update finished! For security it's recommended to modify panel settings ${plain}"
@@ -123,9 +107,9 @@ config_after_install() {
echo -e "${green}username:${usernameTemp}${plain}" echo -e "${green}username:${usernameTemp}${plain}"
echo -e "${green}password:${passwordTemp}${plain}" echo -e "${green}password:${passwordTemp}${plain}"
echo -e "###############################################" echo -e "###############################################"
echo -e "${red}if you forgot your login info,you can type x-ui and then type 8 to check after installation${plain}" echo -e "${red}if you forgot your login info,you can type x-ui and then type 7 to check after installation${plain}"
else else
echo -e "${red} this is your upgrade,will keep old settings,if you forgot your login info,you can type x-ui and then type 8 to check${plain}" echo -e "${red} this is your upgrade,will keep old settings,if you forgot your login info,you can type x-ui and then type 7 to check${plain}"
fi fi
fi fi
/usr/local/x-ui/x-ui migrate /usr/local/x-ui/x-ui migrate
@@ -149,7 +133,7 @@ install_x-ui() {
else else
last_version=$1 last_version=$1
url="https://github.com/MHSanaei/3x-ui/releases/download/${last_version}/x-ui-linux-$(arch3xui).tar.gz" url="https://github.com/MHSanaei/3x-ui/releases/download/${last_version}/x-ui-linux-$(arch3xui).tar.gz"
echo -e "Beginning to install x-ui $1" echo -e "Begining to install x-ui $1"
wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch3xui).tar.gz ${url} wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch3xui).tar.gz ${url}
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
echo -e "${red}Download x-ui $1 failed,please check the version exists ${plain}" echo -e "${red}Download x-ui $1 failed,please check the version exists ${plain}"
@@ -165,21 +149,18 @@ install_x-ui() {
tar zxvf x-ui-linux-$(arch3xui).tar.gz tar zxvf x-ui-linux-$(arch3xui).tar.gz
rm x-ui-linux-$(arch3xui).tar.gz -f rm x-ui-linux-$(arch3xui).tar.gz -f
cd x-ui cd x-ui
chmod +x x-ui
# Check the system's architecture and rename the file accordingly
if [[ $(arch3xui) == "armv5" || $(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) chmod +x x-ui bin/xray-linux-$(arch3xui)
cp -f x-ui.service /etc/systemd/system/ cp -f x-ui.service /etc/systemd/system/
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh 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/local/x-ui/x-ui.sh
chmod +x /usr/bin/x-ui chmod +x /usr/bin/x-ui
config_after_install 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 daemon-reload
systemctl enable x-ui systemctl enable x-ui
systemctl start x-ui systemctl start x-ui

View File

@@ -0,0 +1,97 @@
{
"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": {}
}

View File

@@ -0,0 +1,105 @@
{
"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": {}
}

View File

@@ -0,0 +1,84 @@
{
"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": {}
}

View File

@@ -0,0 +1,76 @@
{
"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": {}
}

View File

@@ -0,0 +1,65 @@
{
"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": {}
}

View File

@@ -20,7 +20,6 @@ type SubService struct {
address string address string
showInfo bool showInfo bool
remarkModel string remarkModel string
datepicker string
inboundService service.InboundService inboundService service.InboundService
settingService service.SettingService settingService service.SettingService
} }
@@ -40,10 +39,6 @@ func (s *SubService) GetSubs(subId string, host string, showInfo bool) ([]string
if err != nil { if err != nil {
s.remarkModel = "-ieo" s.remarkModel = "-ieo"
} }
s.datepicker, err = s.settingService.GetDatepicker()
if err != nil {
s.datepicker = "gregorian"
}
for _, inbound := range inbounds { for _, inbound := range inbounds {
clients, err := s.inboundService.GetClients(inbound) clients, err := s.inboundService.GetClients(inbound)
if err != nil { if err != nil {

View File

@@ -1117,7 +1117,3 @@ li.ant-select-dropdown-menu-item:empty:after {
padding: 0.5rem; padding: 0.5rem;
border-radius: 1rem; 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;
}

View File

@@ -56,10 +56,6 @@ class DBInbound {
return this.protocol === Protocols.HTTP; return this.protocol === Protocols.HTTP;
} }
get isWireguard() {
return this.protocol === Protocols.WIREGUARD;
}
get address() { get address() {
let address = location.hostname; let address = location.hostname;
if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") { if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") {

View File

@@ -8,7 +8,6 @@ const Protocols = {
Shadowsocks: "shadowsocks", Shadowsocks: "shadowsocks",
Socks: "socks", Socks: "socks",
HTTP: "http", HTTP: "http",
Wireguard: "wireguard"
}; };
const SSMethods = { const SSMethods = {
@@ -47,27 +46,18 @@ const ALPN_OPTION = {
HTTP1: "http/1.1", HTTP1: "http/1.1",
}; };
const OutboundDomainStrategies = [ const outboundDomainStrategies = [
"AsIs", "AsIs",
"UseIP", "UseIP",
"UseIPv4", "UseIPv4",
"UseIPv6" "UseIPv6"
]; ]
const WireguardDomainStrategy = [
"ForceIP",
"ForceIPv4",
"ForceIPv4v6",
"ForceIPv6",
"ForceIPv6v4"
];
Object.freeze(Protocols); Object.freeze(Protocols);
Object.freeze(SSMethods); Object.freeze(SSMethods);
Object.freeze(TLS_FLOW_CONTROL); Object.freeze(TLS_FLOW_CONTROL);
Object.freeze(ALPN_OPTION); Object.freeze(ALPN_OPTION);
Object.freeze(OutboundDomainStrategies); Object.freeze(outboundDomainStrategies);
Object.freeze(WireguardDomainStrategy);
class CommonClass { class CommonClass {
@@ -635,7 +625,6 @@ Outbound.Settings = class extends CommonClass {
case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings(); case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings();
case Protocols.Socks: return new Outbound.SocksSettings(); case Protocols.Socks: return new Outbound.SocksSettings();
case Protocols.HTTP: return new Outbound.HttpSettings(); case Protocols.HTTP: return new Outbound.HttpSettings();
case Protocols.Wireguard: return new Outbound.WireguardSettings();
default: return null; default: return null;
} }
} }
@@ -651,7 +640,6 @@ Outbound.Settings = class extends CommonClass {
case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json); case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json);
case Protocols.Socks: return Outbound.SocksSettings.fromJson(json); case Protocols.Socks: return Outbound.SocksSettings.fromJson(json);
case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json); case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json);
case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json);
default: return null; default: return null;
} }
} }
@@ -850,14 +838,13 @@ Outbound.ShadowsocksSettings = class extends CommonClass {
}; };
} }
}; };
Outbound.SocksSettings = class extends CommonClass { Outbound.SocksSettings = class extends CommonClass {
constructor(address, port, user, pass) { constructor(address, port, user, password) {
super(); super();
this.address = address; this.address = address;
this.port = port; this.port = port;
this.user = user; this.user = user;
this.pass = pass; this.password = password;
} }
static fromJson(json={}) { static fromJson(json={}) {
@@ -867,7 +854,7 @@ Outbound.SocksSettings = class extends CommonClass {
servers[0].address, servers[0].address,
servers[0].port, servers[0].port,
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user, ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
ObjectUtil.isArrEmpty(servers[0].pass) ? '' : servers[0].users[0].pass, ObjectUtil.isArrEmpty(servers[0].password) ? '' : servers[0].users[0].password,
); );
} }
@@ -876,18 +863,18 @@ Outbound.SocksSettings = class extends CommonClass {
servers: [{ servers: [{
address: this.address, address: this.address,
port: this.port, port: this.port,
users: ObjectUtil.isEmpty(this.user) ? [] : [{user: this.user, pass: this.pass}], users: ObjectUtil.isEmpty(this.user) ? [] : [{user: this.user, password: this.password}],
}], }],
}; };
} }
}; };
Outbound.HttpSettings = class extends CommonClass { Outbound.HttpSettings = class extends CommonClass {
constructor(address, port, user, pass) { constructor(address, port, user, password) {
super(); super();
this.address = address; this.address = address;
this.port = port; this.port = port;
this.user = user; this.user = user;
this.pass = pass; this.password = password;
} }
static fromJson(json={}) { static fromJson(json={}) {
@@ -897,7 +884,7 @@ Outbound.HttpSettings = class extends CommonClass {
servers[0].address, servers[0].address,
servers[0].port, servers[0].port,
ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user, ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
ObjectUtil.isArrEmpty(servers[0].pass) ? '' : servers[0].users[0].pass, ObjectUtil.isArrEmpty(servers[0].password) ? '' : servers[0].users[0].password,
); );
} }
@@ -906,91 +893,8 @@ Outbound.HttpSettings = class extends CommonClass {
servers: [{ servers: [{
address: this.address, address: this.address,
port: this.port, port: this.port,
users: ObjectUtil.isEmpty(this.user) ? [] : [{user: this.user, pass: this.pass}], users: ObjectUtil.isEmpty(this.user) ? [] : [{user: this.user, password: this.password}],
}], }],
}; };
} }
};
Outbound.WireguardSettings = class extends CommonClass {
constructor(
mtu=1420, secretKey=Wireguard.generateKeypair().privateKey,
address=[''], workers=2, domainStrategy='ForceIPv6v4', 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=Wireguard.generateKeypair().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,
};
}
}; };

View File

@@ -12,10 +12,8 @@ class AllSetting {
this.expireDiff = ""; this.expireDiff = "";
this.trafficDiff = ""; this.trafficDiff = "";
this.remarkModel = "-ieo"; this.remarkModel = "-ieo";
this.datepicker = "gregorian";
this.tgBotEnable = false; this.tgBotEnable = false;
this.tgBotToken = ""; this.tgBotToken = "";
this.tgBotProxy = "";
this.tgBotChatId = ""; this.tgBotChatId = "";
this.tgRunTime = "@daily"; this.tgRunTime = "@daily";
this.tgBotBackup = false; this.tgBotBackup = false;

View File

@@ -6,7 +6,6 @@ const Protocols = {
DOKODEMO: 'dokodemo-door', DOKODEMO: 'dokodemo-door',
SOCKS: 'socks', SOCKS: 'socks',
HTTP: 'http', HTTP: 'http',
WIREGUARD: 'wireguard',
}; };
const SSMethods = { const SSMethods = {
@@ -236,7 +235,6 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
toJson() { toJson() {
return { return {
version: this.version,
method: this.method, method: this.method,
path: ObjectUtil.clone(this.path), path: ObjectUtil.clone(this.path),
headers: XrayCommonClass.toV2Headers(this.headers), headers: XrayCommonClass.toV2Headers(this.headers),
@@ -766,18 +764,16 @@ class RealityStreamSettings extends XrayCommonClass {
} }
RealityStreamSettings.Settings = class extends XrayCommonClass { RealityStreamSettings.Settings = class extends XrayCommonClass {
constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, serverName = '', spiderX= '/') { constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, spiderX= '/') {
super(); super();
this.publicKey = publicKey; this.publicKey = publicKey;
this.fingerprint = fingerprint; this.fingerprint = fingerprint;
this.serverName = serverName;
this.spiderX = spiderX; this.spiderX = spiderX;
} }
static fromJson(json = {}) { static fromJson(json = {}) {
return new RealityStreamSettings.Settings( return new RealityStreamSettings.Settings(
json.publicKey, json.publicKey,
json.fingerprint, json.fingerprint,
json.serverName,
json.spiderX, json.spiderX,
); );
} }
@@ -785,7 +781,6 @@ RealityStreamSettings.Settings = class extends XrayCommonClass {
return { return {
publicKey: this.publicKey, publicKey: this.publicKey,
fingerprint: this.fingerprint, fingerprint: this.fingerprint,
serverName: this.serverName,
spiderX: this.spiderX, spiderX: this.spiderX,
}; };
} }
@@ -799,7 +794,6 @@ class SockoptStreamSettings extends XrayCommonClass {
this.mark = mark; this.mark = mark;
this.tproxy = tproxy; this.tproxy = tproxy;
} }
static fromJson(json = {}) { static fromJson(json = {}) {
if (Object.keys(json).length === 0) return undefined; if (Object.keys(json).length === 0) return undefined;
return new SockoptStreamSettings( return new SockoptStreamSettings(
@@ -1001,6 +995,18 @@ 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() { get xtls() {
return this.stream.security === 'xtls'; return this.stream.security === 'xtls';
} }
@@ -1012,7 +1018,19 @@ class Inbound extends XrayCommonClass {
this.stream.security = 'none'; 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() { get network() {
return this.stream.network; return this.stream.network;
} }
@@ -1124,6 +1142,11 @@ class Inbound extends XrayCommonClass {
return ["tcp", "ws", "http", "quic", "grpc"].includes(this.network); 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 //this is used for xtls-rprx-vision
canEnableTlsFlow() { canEnableTlsFlow() {
if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) { if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) {
@@ -1132,11 +1155,6 @@ class Inbound extends XrayCommonClass {
return false; return false;
} }
canEnableReality() {
if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
return ["tcp", "http", "grpc"].includes(this.network);
}
canEnableXtls() { canEnableXtls() {
if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false; if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
return this.network === "tcp"; return this.network === "tcp";
@@ -1589,7 +1607,7 @@ class Inbound extends XrayCommonClass {
}); });
return links.join('\r\n'); return links.join('\r\n');
} else { } else {
if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, 'same', remark); if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, remark);
return ''; return '';
} }
} }
@@ -1639,8 +1657,7 @@ Inbound.Settings = class extends XrayCommonClass {
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol); case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol);
case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol); case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol);
case Protocols.SOCKS: return new Inbound.SocksSettings(protocol); case Protocols.SOCKS: return new Inbound.SocksSettings(protocol);
case Protocols.HTTP: return new Inbound.HttpSettings(protocol); case Protocols.HTTP: return new Inbound.HttpSettings(protocol);
case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol);
default: return null; default: return null;
} }
} }
@@ -1654,7 +1671,6 @@ Inbound.Settings = class extends XrayCommonClass {
case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json); case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json);
case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json); case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json);
case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json); case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json);
case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json);
default: return null; default: return null;
} }
} }
@@ -2257,69 +2273,3 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass {
return new Inbound.HttpSettings.HttpAccount(json.user, json.pass); return new Inbound.HttpSettings.HttpAccount(json.user, json.pass);
} }
}; };
Inbound.WireguardSettings = class extends XrayCommonClass {
constructor(protocol, mtu=1420, secretKey=Wireguard.generateKeypair().privateKey, peers=[new Inbound.WireguardSettings.Peer()], kernelMode=false) {
super(protocol);
this.mtu = mtu;
this.secretKey = secretKey;
this.pubKey = secretKey.length>0 ? Wireguard.generateKeypair(secretKey).publicKey : '';
this.peers = peers;
this.kernelMode = kernelMode;
}
addPeer() {
this.peers.push(new Inbound.WireguardSettings.Peer());
}
delPeer(index) {
this.peers.splice(index, 1);
}
static fromJson(json={}){
return new Inbound.WireguardSettings(
Protocols.WIREGUARD,
json.mtu,
json.secretKey,
json.peers.map(peer => Inbound.WireguardSettings.Peer.fromJson(peer)),
json.kernelMode,
);
}
toJson() {
return {
mtu: this.mtu?? undefined,
secretKey: this.secretKey,
peers: Inbound.WireguardSettings.Peer.toJsonArray(this.peers),
kernelMode: this.kernelMode,
};
}
};
Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
constructor(publicKey=Wireguard.generateKeypair().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,
};
}
};

View File

@@ -295,190 +295,3 @@ class ObjectUtil {
return true; return true;
} }
} }
class Wireguard {
static gf(init) {
var r = new Float64Array(16);
if (init) {
for (var i = 0; i < init.length; ++i)
r[i] = init[i];
}
return r;
}
static pack(o, n) {
var b, m = this.gf(), t = this.gf();
for (var i = 0; i < 16; ++i)
t[i] = n[i];
this.carry(t);
this.carry(t);
this.carry(t);
for (var j = 0; j < 2; ++j) {
m[0] = t[0] - 0xffed;
for (var i = 1; i < 15; ++i) {
m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
m[i - 1] &= 0xffff;
}
m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
b = (m[15] >> 16) & 1;
m[14] &= 0xffff;
this.cswap(t, m, 1 - b);
}
for (var i = 0; i < 16; ++i) {
o[2 * i] = t[i] & 0xff;
o[2 * i + 1] = t[i] >> 8;
}
}
static carry(o) {
var c;
for (var i = 0; i < 16; ++i) {
o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536);
o[i] &= 0xffff;
}
}
static cswap(p, q, b) {
var t, c = ~(b - 1);
for (var i = 0; i < 16; ++i) {
t = c & (p[i] ^ q[i]);
p[i] ^= t;
q[i] ^= t;
}
}
static add(o, a, b) {
for (var i = 0; i < 16; ++i)
o[i] = (a[i] + b[i]) | 0;
}
static subtract(o, a, b) {
for (var i = 0; i < 16; ++i)
o[i] = (a[i] - b[i]) | 0;
}
static multmod(o, a, b) {
var t = new Float64Array(31);
for (var i = 0; i < 16; ++i) {
for (var j = 0; j < 16; ++j)
t[i + j] += a[i] * b[j];
}
for (var i = 0; i < 15; ++i)
t[i] += 38 * t[i + 16];
for (var i = 0; i < 16; ++i)
o[i] = t[i];
this.carry(o);
this.carry(o);
}
static invert(o, i) {
var c = this.gf();
for (var a = 0; a < 16; ++a)
c[a] = i[a];
for (var a = 253; a >= 0; --a) {
this.multmod(c, c, c);
if (a !== 2 && a !== 4)
this.multmod(c, c, i);
}
for (var a = 0; a < 16; ++a)
o[a] = c[a];
}
static clamp(z) {
z[31] = (z[31] & 127) | 64;
z[0] &= 248;
}
static generatePublicKey(privateKey) {
var r, z = new Uint8Array(32);
var a = this.gf([1]),
b = this.gf([9]),
c = this.gf(),
d = this.gf([1]),
e = this.gf(),
f = this.gf(),
_121665 = this.gf([0xdb41, 1]),
_9 = this.gf([9]);
for (var i = 0; i < 32; ++i)
z[i] = privateKey[i];
this.clamp(z);
for (var i = 254; i >= 0; --i) {
r = (z[i >>> 3] >>> (i & 7)) & 1;
this.cswap(a, b, r);
this.cswap(c, d, r);
this.add(e, a, c);
this.subtract(a, a, c);
this.add(c, b, d);
this.subtract(b, b, d);
this.multmod(d, e, e);
this.multmod(f, a, a);
this.multmod(a, c, a);
this.multmod(c, b, e);
this.add(e, a, c);
this.subtract(a, a, c);
this.multmod(b, a, a);
this.subtract(c, d, f);
this.multmod(a, c, _121665);
this.add(a, a, d);
this.multmod(c, c, a);
this.multmod(a, d, f);
this.multmod(d, b, _9);
this.multmod(b, e, e);
this.cswap(a, b, r);
this.cswap(c, d, r);
}
this.invert(c, c);
this.multmod(a, a, c);
this.pack(z, a);
return z;
}
static generatePresharedKey() {
var privateKey = new Uint8Array(32);
window.crypto.getRandomValues(privateKey);
return privateKey;
}
static generatePrivateKey() {
var privateKey = this.generatePresharedKey();
this.clamp(privateKey);
return privateKey;
}
static encodeBase64(dest, src) {
var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]);
for (var i = 0; i < 4; ++i)
dest[i] = input[i] + 65 +
(((25 - input[i]) >> 8) & 6) -
(((51 - input[i]) >> 8) & 75) -
(((61 - input[i]) >> 8) & 15) +
(((62 - input[i]) >> 8) & 3);
}
static keyToBase64(key) {
var i, base64 = new Uint8Array(44);
for (i = 0; i < 32 / 3; ++i)
this.encodeBase64(base64.subarray(i * 4), key.subarray(i * 3));
this.encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0]));
base64[43] = 61;
return String.fromCharCode.apply(null, base64);
}
static keyFromBase64(encoded) {
const binaryStr = atob(encoded);
const bytes = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
bytes[i] = binaryStr.charCodeAt(i);
}
return bytes;
}
static generateKeypair(secretKey='') {
var privateKey = secretKey.length>0 ? this.keyFromBase64(secretKey) : this.generatePrivateKey();
var publicKey = this.generatePublicKey(privateKey);
return {
publicKey: this.keyToBase64(publicKey),
privateKey: secretKey.length>0 ? secretKey : this.keyToBase64(privateKey)
};
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -85,11 +85,7 @@ func (a *InboundController) addInbound(c *gin.Context) {
} }
user := session.GetLoginUser(c) user := session.GetLoginUser(c)
inbound.UserId = user.Id inbound.UserId = user.Id
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" { inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
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 needRestart := false
inbound, needRestart, err = a.inboundService.AddInbound(inbound) inbound, needRestart, err = a.inboundService.AddInbound(inbound)
@@ -282,11 +278,7 @@ func (a *InboundController) importInbound(c *gin.Context) {
user := session.GetLoginUser(c) user := session.GetLoginUser(c)
inbound.Id = 0 inbound.Id = 0
inbound.UserId = user.Id inbound.UserId = user.Id
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" { inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
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 { for index := range inbound.ClientStats {
inbound.ClientStats[index].Id = 0 inbound.ClientStats[index].Id = 0

View File

@@ -26,7 +26,6 @@ func (a *XraySettingController) initRouter(g *gin.RouterGroup) {
g.POST("/update", a.updateSetting) g.POST("/update", a.updateSetting)
g.GET("/getXrayResult", a.getXrayResult) g.GET("/getXrayResult", a.getXrayResult)
g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig) g.GET("/getDefaultJsonConfig", a.getDefaultXrayConfig)
g.POST("/warp/:action", a.warp)
} }
func (a *XraySettingController) getXraySetting(c *gin.Context) { func (a *XraySettingController) getXraySetting(c *gin.Context) {
@@ -62,25 +61,3 @@ func (a *XraySettingController) getDefaultXrayConfig(c *gin.Context) {
func (a *XraySettingController) getXrayResult(c *gin.Context) { func (a *XraySettingController) getXrayResult(c *gin.Context) {
jsonObj(c, a.XrayService.GetXrayResult(), nil) 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)
}

View File

@@ -28,7 +28,6 @@ type AllSetting struct {
RemarkModel string `json:"remarkModel" form:"remarkModel"` RemarkModel string `json:"remarkModel" form:"remarkModel"`
TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"` TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"`
TgBotToken string `json:"tgBotToken" form:"tgBotToken"` TgBotToken string `json:"tgBotToken" form:"tgBotToken"`
TgBotProxy string `json:"tgBotProxy" form:"tgBotProxy"`
TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"` TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"`
TgRunTime string `json:"tgRunTime" form:"tgRunTime"` TgRunTime string `json:"tgRunTime" form:"tgRunTime"`
TgBotBackup bool `json:"tgBotBackup" form:"tgBotBackup"` TgBotBackup bool `json:"tgBotBackup" form:"tgBotBackup"`
@@ -48,7 +47,6 @@ type AllSetting struct {
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"` SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"`
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"` SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
SubURI string `json:"subURI" form:"subURI"` SubURI string `json:"subURI" form:"subURI"`
Datepicker string `json:"datepicker" form:"datepicker"`
} }
func (s *AllSetting) CheckValid() error { func (s *AllSetting) CheckValid() error {

View File

@@ -114,7 +114,6 @@
position: relative; position: relative;
border-radius: 25px; border-radius: 25px;
width: 100%; width: 100%;
transition: all 0.3s cubic-bezier(.645,.045,.355,1);
} }
.dark .wave-btn-bg { .dark .wave-btn-bg {
color: #fff; color: #fff;
@@ -124,6 +123,7 @@
background-origin: border-box; background-origin: border-box;
background-clip: padding-box, border-box; background-clip: padding-box, border-box;
background-size: 300%; background-size: 300%;
transition: all 0.3s cubic-bezier(.645,.045,.355,1);
width: 100%; width: 100%;
z-index: 1; z-index: 1;
} }
@@ -291,7 +291,8 @@
<a-form-item> <a-form-item>
<a-row justify="center" class="centered"> <a-row justify="center" class="centered">
<div class="wave-btn-bg wave-btn-bg-cl" :style="loading ? { width: '54px' } : { 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"> <a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login" :icon="loading ? 'poweroff' : undefined"
:style="loading ? { width: '50px' } : { display: 'inline-block' }">
[[ loading ? '' : '{{ i18n "login" }}' ]] [[ loading ? '' : '{{ i18n "login" }}' ]]
</a-button> </a-button>
</div> </div>

View File

@@ -82,7 +82,7 @@
<template slot="title"> <template slot="title">
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span> 0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
</template> </template>
{{ i18n "pages.inbounds.totalFlow" }} {{ i18n "pages.inbounds.totalFlow" }} (GB)
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
@@ -104,10 +104,8 @@
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
<a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="themeSwitcher.currentTheme" v-model="clientsBulkModal.expiryTime"></a-date-picker> :dropdown-class-name="themeSwitcher.currentTheme" v-model="clientsBulkModal.expiryTime"></a-date-picker>
<persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
value="clientsBulkModal.expiryTime" v-model="clientsBulkModal.expiryTime"></persian-datepicker>
</a-form-item> </a-form-item>
<a-form-item v-if="clientsBulkModal.expiryTime != 0"> <a-form-item v-if="clientsBulkModal.expiryTime != 0">
<template slot="label"> <template slot="label">
@@ -236,9 +234,6 @@
get delayedExpireDays() { get delayedExpireDays() {
return this.clientsBulkModal.expiryTime < 0 ? this.clientsBulkModal.expiryTime / -86400000 : 0; return this.clientsBulkModal.expiryTime < 0 ? this.clientsBulkModal.expiryTime / -86400000 : 0;
}, },
get datepicker() {
return app.datepicker;
},
set delayedExpireDays(days) { set delayedExpireDays(days) {
this.clientsBulkModal.expiryTime = -86400000 * days; this.clientsBulkModal.expiryTime = -86400000 * days;
}, },

View File

@@ -94,9 +94,6 @@
get isEdit() { get isEdit() {
return this.clientModal.isEdit; return this.clientModal.isEdit;
}, },
get datepicker() {
return app.datepicker;
},
get isTrafficExhausted() { get isTrafficExhausted() {
if (!clientStats) return false if (!clientStats) return false
if (clientStats.total <= 0) return false if (clientStats.total <= 0) return false

View File

@@ -1,60 +0,0 @@
{{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="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: ['placeholder', '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,
zIndex: '9999',
hideAfterChange: true,
useDropDownYears: false,
changeMonthRotateYear: true,
});
},
}
});
</script>
{{end}}

View File

@@ -11,7 +11,6 @@
function createThemeSwitcher() { function createThemeSwitcher() {
const isDarkTheme = localStorage.getItem('dark-mode') === 'true'; const isDarkTheme = localStorage.getItem('dark-mode') === 'true';
const theme = isDarkTheme ? 'dark' : 'light'; const theme = isDarkTheme ? 'dark' : 'light';
document.querySelector('body').setAttribute('class', theme)
return { return {
isDarkTheme, isDarkTheme,
get currentTheme() { get currentTheme() {
@@ -20,7 +19,6 @@
toggleTheme() { toggleTheme() {
this.isDarkTheme = !this.isDarkTheme; this.isDarkTheme = !this.isDarkTheme;
localStorage.setItem('dark-mode', this.isDarkTheme); localStorage.setItem('dark-mode', this.isDarkTheme);
document.querySelector('body').setAttribute('class', this.isDarkTheme ? 'dark' : 'light')
}, },
}; };
} }

View File

@@ -19,7 +19,7 @@
<template slot="label"> <template slot="label">
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
<span>{{ i18n "reset" }}</span> <span>{{ i18n "pages.client.renew" }}</span>
</template> </template>
{{ i18n "password" }} {{ i18n "password" }}
<a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS"@click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon> <a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS"@click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
@@ -32,7 +32,7 @@
<template slot="label"> <template slot="label">
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
<span>{{ i18n "reset" }}</span> <span>{{ i18n "pages.client.renew" }}</span>
</template> </template>
ID <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"></a-icon> ID <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"></a-icon>
</a-tooltip> </a-tooltip>
@@ -117,7 +117,7 @@
<template slot="title"> <template slot="title">
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span> 0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
</template> </template>
{{ i18n "pages.inbounds.totalFlow" }} {{ i18n "pages.inbounds.totalFlow" }}(GB)
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
@@ -150,10 +150,8 @@
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
<a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="themeSwitcher.currentTheme" v-model="client._expiryTime"></a-date-picker> :dropdown-class-name="themeSwitcher.currentTheme" v-model="client._expiryTime"></a-date-picker>
<persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
value="client._expiryTime" v-model="client._expiryTime"></persian-datepicker>
<a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag> <a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
</a-form-item> </a-form-item>
<a-form-item v-if="client.expiryTime != 0"> <a-form-item v-if="client.expiryTime != 0">

View File

@@ -37,7 +37,7 @@
<template slot="title"> <template slot="title">
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span> 0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
</template> </template>
{{ i18n "pages.inbounds.totalFlow" }} {{ i18n "pages.inbounds.totalFlow" }}(GB)
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
@@ -54,11 +54,9 @@
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
<a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="themeSwitcher.currentTheme" :dropdown-class-name="themeSwitcher.currentTheme"
v-model="dbInbound._expiryTime"></a-date-picker> v-model="dbInbound._expiryTime"></a-date-picker>
<persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
value="dbInbound._expiryTime" v-model="dbInbound._expiryTime"></persian-datepicker>
</a-form-item> </a-form-item>
</a-form> </a-form>
@@ -97,11 +95,6 @@
{{template "form/http"}} {{template "form/http"}}
</template> </template>
<!-- wireguard -->
<template v-if="inbound.protocol === Protocols.WIREGUARD">
{{template "form/wireguard"}}
</template>
<!-- stream settings --> <!-- stream settings -->
<template v-if="inbound.canEnableStream()"> <template v-if="inbound.canEnableStream()">
{{template "form/streamSettings"}} {{template "form/streamSettings"}}

View File

@@ -18,7 +18,7 @@
<a-select <a-select
v-model="outbound.settings.domainStrategy" v-model="outbound.settings.domainStrategy"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="s in OutboundDomainStrategies" :value="s">[[ s ]]</a-select-option> <a-select-option v-for="s in outboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='Fragment'> <a-form-item label='Fragment'>
@@ -46,132 +46,24 @@
<!-- blackhole settings --> <!-- blackhole settings -->
<template v-if="outbound.protocol === Protocols.Blackhole"> <template v-if="outbound.protocol === Protocols.Blackhole">
<a-form-item label='Response Type'> <a-form-item label='Response Type'>
<a-select <a-select
v-model="outbound.settings.type" v-model="outbound.settings.type"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="s in ['', 'none','http']" :value="s">[[ s ]]</a-select-option> <a-select-option v-for="s in ['', 'none','http']" :value="s">[[ s ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</template> </template>
<!-- dns settings --> <!-- dns settings -->
<template v-if="outbound.protocol === Protocols.DNS"> <template v-if="outbound.protocol === Protocols.DNS">
<a-form-item label='{{ i18n "pages.inbounds.network" }}'> <a-form-item label='{{ i18n "pages.inbounds.network" }}'>
<a-select <a-select
v-model="outbound.settings.network" v-model="outbound.settings.network"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="s in ['udp','tcp']" :value="s">[[ s ]]</a-select-option> <a-select-option v-for="s in ['udp','tcp']" :value="s">[[ s ]]</a-select-option>
</a-select> </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>
<a-form-item>
<template slot="label">
<a-tooltip>
<template slot="title">
<span>{{ i18n "reset" }}</span>
</template>
{{ i18n "pages.xray.wireguard.publicKey" }}
<a-icon @click="peer.publicKey = publicKey=Wireguard.generateKeypair().publicKey"type="sync"> </a-icon>
</a-tooltip>
</template>
<a-input v-model.trim="peer.publicKey"></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.psk" }}
<a-icon @click="peer.psk = publicKey=Wireguard.generateKeypair().publicKey"type="sync"> </a-icon>
</a-tooltip>
</template>
<a-input v-model.trim="peer.psk"></a-input>
</a-form-item>
<a-form-item>
<template slot="label">
{{ i18n "pages.xray.wireguard.allowedIPs" }} <a-button type="primary" size="small" @click="peer.allowedIPs.push('')">+</a-button>
</template>
<template v-for="(aip, index) in peer.allowedIPs" style="margin-bottom: 10px;">
<a-input v-model.trim="peer.allowedIPs[index]">
<a-button v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)">-</a-button>
</a-input>
</template>
</a-form-item>
<a-form-item label='Keep Alive'>
<a-input-number v-model.number="peer.keepAlive" :min="0"></a-input>
</a-form-item>
</a-form>
</template> </template>
<!-- Address + Port --> <!-- Address + Port -->
@@ -189,9 +81,8 @@
<a-form-item label='ID'> <a-form-item label='ID'>
<a-input v-model.trim="outbound.settings.id"></a-input> <a-input v-model.trim="outbound.settings.id"></a-input>
</a-form-item> </a-form-item>
<!-- vless settings -->
<!-- vless settings --> <template v-if="outbound.canEnableTlsFlow()">
<template v-if="outbound.canEnableTlsFlow()">
<a-form-item label='Flow'> <a-form-item label='Flow'>
<a-select v-model="outbound.settings.flow" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="outbound.settings.flow" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option> <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
@@ -203,21 +94,19 @@
<!-- Servers (trojan/shadowsocks/socks/http) settings --> <!-- Servers (trojan/shadowsocks/socks/http) settings -->
<template v-if="outbound.hasServers()"> <template v-if="outbound.hasServers()">
<!-- http / socks -->
<template v-if="outbound.hasUsername()"> <template v-if="outbound.hasUsername()">
<a-form-item label='{{ i18n "username" }}'> <a-form-item label='{{ i18n "username" }}'>
<a-input v-model.trim="outbound.settings.user"></a-input> <a-input v-model.trim="outbound.settings.user"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "password" }}'> <a-form-item label='{{ i18n "password" }}'>
<a-input v-model.trim="outbound.settings.pass"></a-input> <a-input v-model.trim="outbound.settings.password"></a-input>
</a-form-item> </a-form-item>
</template> </template>
<!-- shadowsocks -->
<!-- shadowsocks --> <template v-if="outbound.protocol === Protocols.Shadowsocks">
<template v-if="outbound.protocol === Protocols.Shadowsocks">
<a-form-item label='{{ i18n "encryption" }}'> <a-form-item label='{{ i18n "encryption" }}'>
<a-select v-model="outbound.settings.method" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="outbound.settings.method" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option> <a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='UDP over TCP'> <a-form-item label='UDP over TCP'>
@@ -232,9 +121,9 @@
<a-select v-model="outbound.stream.network" @change="streamNetworkChange" <a-select v-model="outbound.stream.network" @change="streamNetworkChange"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="tcp">TCP</a-select-option> <a-select-option value="tcp">TCP</a-select-option>
<a-select-option value="kcp">mKCP</a-select-option> <a-select-option value="kcp">KCP</a-select-option>
<a-select-option value="ws">WS</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="http">HTTP2</a-select-option>
<a-select-option value="quic">QUIC</a-select-option> <a-select-option value="quic">QUIC</a-select-option>
<a-select-option value="grpc">gRPC</a-select-option> <a-select-option value="grpc">gRPC</a-select-option>
</a-select> </a-select>
@@ -260,12 +149,12 @@
<template v-if="outbound.stream.network === 'kcp'"> <template v-if="outbound.stream.network === 'kcp'">
<a-form-item label='{{ i18n "camouflage" }}'> <a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="outbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="outbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option> <a-select-option value="none">none (not camouflage)</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option> <a-select-option value="srtp">srtp (video call)</a-select-option>
<a-select-option value="utp">uTP</a-select-option> <a-select-option value="utp">utp (BT download)</a-select-option>
<a-select-option value="wechat-video">WeChat</a-select-option> <a-select-option value="wechat-video">wechat-video (WeChat video)</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option> <a-select-option value="dtls">dtls (DTLS 1.2 packages)</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option> <a-select-option value="wireguard">wireguard (wireguard packages)</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "password" }}'> <a-form-item label='{{ i18n "password" }}'>
@@ -277,19 +166,19 @@
<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-input-number v-model.number="outbound.stream.kcp.tti"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Uplink (MB/s)'> <a-form-item label='Uplink Capacity (MB/s)'>
<a-input-number v-model.number="outbound.stream.kcp.upCap"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.upCap"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Downlink (MB/s)'> <a-form-item label='Downlink Capacity (MB/s)'>
<a-input-number v-model.number="outbound.stream.kcp.downCap"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.downCap"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Congestion'> <a-form-item label='Congestion'>
<a-switch v-model="outbound.stream.kcp.congestion"></a-switch> <a-switch v-model="outbound.stream.kcp.congestion"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label='Read Buffer (MB)'> <a-form-item label='Read Buffer Size (MB)'>
<a-input-number v-model.number="outbound.stream.kcp.readBuffer"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.readBuffer"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Write Buffer (MB)'> <a-form-item label='Write Buffer Size (MB)'>
<a-input-number v-model.number="outbound.stream.kcp.writeBuffer"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.writeBuffer"></a-input-number>
</a-form-item> </a-form-item>
</template> </template>
@@ -318,9 +207,9 @@
<template v-if="outbound.stream.network === 'quic'"> <template v-if="outbound.stream.network === 'quic'">
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'> <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 v-model="outbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option> <a-select-option value="none">none</a-select-option>
<a-select-option value="aes-128-gcm">AES-128-GCM</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="chacha20-poly1305">chacha20-poly1305</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "password" }}'> <a-form-item label='{{ i18n "password" }}'>
@@ -328,12 +217,12 @@
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "camouflage" }}'> <a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="outbound.stream.quic.type" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="outbound.stream.quic.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option> <a-select-option value="none">none (No Obfuscation)</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option> <a-select-option value="srtp">SRTP (Video Call)</a-select-option>
<a-select-option value="utp">uTP</a-select-option> <a-select-option value="utp">uTP (Bittorrent)</a-select-option>
<a-select-option value="wechat-video">WeChat</a-select-option> <a-select-option value="wechat-video">WeChat Video</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option> <a-select-option value="dtls">DTLS (DTLS 1.2 packages)</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option> <a-select-option value="wireguard">WireGuard (WireGuard Packages)</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</template> </template>
@@ -351,54 +240,59 @@
<!-- tls settings --> <!-- tls settings -->
<template v-if="outbound.canEnableTls()"> <template v-if="outbound.canEnableTls()">
<a-form-item label='{{ i18n "security" }}'> <a-form-item label='{{ i18n "security" }}'>
<a-radio-group v-model="outbound.stream.security" button-style="solid"> <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="none">{{ i18n "none" }}</a-radio-button>
<a-radio-button value="tls">TLS</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-button v-if="outbound.canEnableReality()" value="reality">Reality</a-radio-button>
</a-radio-group> </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> </a-form-item>
<a-form-item label="uTLS"> <template v-if="outbound.stream.isTls">
<a-select v-model="outbound.stream.tls.fingerprint" :dropdown-class-name="themeSwitcher.currentTheme"> <a-form-item label="SNI" placeholder="Server Name Indication">
<a-select-option value=''>None</a-select-option> <a-input v-model.trim="outbound.stream.tls.serverName"></a-input>
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option> </a-form-item>
</a-select> <a-form-item label="uTLS">
</a-form-item> <a-select v-model="outbound.stream.tls.fingerprint"
<a-form-item label="ALPN"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme" <a-select-option value=''>None</a-select-option>
v-model="outbound.stream.tls.alpn"> <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option> </a-select>
</a-select> </a-form-item>
</a-form-item> <a-form-item label="ALPN">
<a-form-item label="Allow Insecure"> <a-select
<a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch> mode="multiple"
</a-form-item>
</template> :dropdown-class-name="themeSwitcher.currentTheme"
v-model="outbound.stream.tls.alpn">
<!-- reality settings --> <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
<template v-if="outbound.stream.isReality"> </a-select>
<a-form-item label='{{ i18n "domainName" }}'> </a-form-item>
<a-input v-model.trim="outbound.stream.reality.serverName"></a-input> <a-form-item label="Allow Insecure">
</a-form-item> <a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch>
<a-form-item label="uTLS"> </a-form-item>
<a-select v-model="outbound.stream.reality.fingerprint" :dropdown-class-name="themeSwitcher.currentTheme"> </template>
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-select> <!-- reality settings -->
</a-form-item> <template v-if="outbound.stream.isReality">
<a-form-item label="Short ID"> <a-form-item label='{{ i18n "domainName" }}'>
<a-input v-model.trim="outbound.stream.reality.shortId" style="width:250px"></a-input> <a-input v-model.trim="outbound.stream.reality.serverName"></a-input>
</a-form-item> </a-form-item>
<a-form-item label="SpiderX"> <a-form-item label="uTLS">
<a-input v-model.trim="outbound.stream.reality.spiderX" style="width:250px"></a-input> <a-select v-model="outbound.stream.reality.fingerprint"
</a-form-item> :dropdown-class-name="themeSwitcher.currentTheme">
<a-form-item label="Public Key"> <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
<a-input v-model.trim="outbound.stream.reality.publicKey"></a-input> </a-select>
</a-form-item> </a-form-item>
</template> <a-form-item label="Short IDs">
<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> </template>
</a-form> </a-form>
</a-tab-pane> </a-tab-pane>

View File

@@ -23,14 +23,14 @@
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "encryption" }}'> <a-form-item label='{{ i18n "encryption" }}'>
<a-select v-model="inbound.settings.method" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.settings.method" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option> <a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item v-if="inbound.isSS2022"> <a-form-item v-if="inbound.isSS2022">
<template slot="label"> <template slot="label">
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
<span>{{ i18n "reset" }}</span> <span>{{ i18n "password" }}</span>
</template> Password <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon> </template> Password <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>

View File

@@ -37,7 +37,7 @@
<a-icon type="delete" @click="() => inbound.settings.delFallback(index)" <a-icon type="delete" @click="() => inbound.settings.delFallback(index)"
style="color: rgb(255, 77, 79);cursor: pointer;"/> style="color: rgb(255, 77, 79);cursor: pointer;"/>
</a-divider> </a-divider>
<a-form-item label='SNI'> <a-form-item label='Name'>
<a-input v-model="fallback.name"></a-input> <a-input v-model="fallback.name"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='ALPN'> <a-form-item label='ALPN'>

View File

@@ -39,7 +39,7 @@
<a-icon type="delete" @click="() => inbound.settings.delFallback(index)" <a-icon type="delete" @click="() => inbound.settings.delFallback(index)"
style="color: rgb(255, 77, 79);cursor: pointer;"/> style="color: rgb(255, 77, 79);cursor: pointer;"/>
</a-divider> </a-divider>
<a-form-item label='SNI'> <a-form-item label='Name'>
<a-input v-model="fallback.name"></a-input> <a-input v-model="fallback.name"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='ALPN'> <a-form-item label='ALPN'>

View File

@@ -1,74 +0,0 @@
{{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>
<template slot="label">
<a-tooltip>
<template slot="title">
<span>{{ i18n "reset" }}</span>
</template>
{{ i18n "pages.xray.wireguard.publicKey" }}
<a-icon @click="peer.publicKey = publicKey=Wireguard.generateKeypair().publicKey"type="sync"> </a-icon>
</a-tooltip>
</template>
<a-input v-model.trim="peer.publicKey"></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.psk" }}
<a-icon @click="peer.psk = publicKey=Wireguard.generateKeypair().publicKey"type="sync"> </a-icon>
</a-tooltip>
</template>
<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}}

View File

@@ -2,25 +2,16 @@
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "camouflage" }}'> <a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="inbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.kcp.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option> <a-select-option value="none">none (No Obfuscation)</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option> <a-select-option value="srtp">SRTP (Video Call)</a-select-option>
<a-select-option value="utp">uTP</a-select-option> <a-select-option value="utp">uTP (Bittorrent)</a-select-option>
<a-select-option value="wechat-video">WeChat</a-select-option> <a-select-option value="wechat-video">WeChat Video</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option> <a-select-option value="dtls">DTLS (DTLS 1.2 packages)</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option> <a-select-option value="wireguard">WireGuard (WireGuard packages)</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item label='{{ i18n "password" }}'>
<template slot="label"> <a-input v-model="inbound.stream.kcp.seed"></a-input>
<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>
<a-form-item label='MTU'> <a-form-item label='MTU'>
<a-input-number v-model.number="inbound.stream.kcp.mtu"></a-input-number> <a-input-number v-model.number="inbound.stream.kcp.mtu"></a-input-number>

View File

@@ -2,31 +2,22 @@
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <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-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
<a-select v-model="inbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option> <a-select-option value="none">none</a-select-option>
<a-select-option value="aes-128-gcm">AES-128-GCM</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="chacha20-poly1305">chacha20-poly1305</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item label='{{ i18n "password" }}'>
<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-input v-model.trim="inbound.stream.quic.key"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "camouflage" }}'> <a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="inbound.stream.quic.type" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.quic.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option> <a-select-option value="none">none (No Obfuscation)</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option> <a-select-option value="srtp">SRTP (Video Call)</a-select-option>
<a-select-option value="utp">uTP</a-select-option> <a-select-option value="utp">uTP (Bittorrent)</a-select-option>
<a-select-option value="wechat-video">WeChat</a-select-option> <a-select-option value="wechat-video">WeChat Video</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option> <a-select-option value="dtls">DTLS (DTLS 1.2 packages)</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option> <a-select-option value="wireguard">WireGuard (WireGuard Packages)</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</a-form> </a-form>

View File

@@ -1,11 +1,11 @@
{{define "form/streamSettings"}} {{define "form/streamSettings"}}
<!-- select stream network --> <!-- select stream network -->
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "transmission" }}'> <a-form-item label="{{ i18n "transmission" }}">
<a-select v-model="inbound.stream.network" @change="streamNetworkChange" <a-select v-model="inbound.stream.network" @change="streamNetworkChange"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="tcp">TCP</a-select-option> <a-select-option value="tcp">TCP</a-select-option>
<a-select-option value="kcp">mKCP</a-select-option> <a-select-option value="kcp">KCP</a-select-option>
<a-select-option value="ws">WS</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="http">H2</a-select-option>
<a-select-option value="quic">QUIC</a-select-option> <a-select-option value="quic">QUIC</a-select-option>
@@ -47,4 +47,4 @@
<template> <template>
{{template "form/streamSockopt"}} {{template "form/streamSockopt"}}
</template> </template>
{{end}} {{end}}

View File

@@ -1,24 +1,24 @@
{{define "form/streamSockopt"}} {{define "form/streamSockopt"}}
<a-divider style="margin:5px 0 0;"></a-divider> <a-divider style="margin:5px 0 0;"></a-divider>
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label="TPROXY"> <a-form-item label="Transparent Proxy">
<a-switch v-model="inbound.stream.sockoptSwitch"></a-switch> <a-switch v-model="inbound.stream.sockoptSwitch"></a-switch>
</a-form-item> </a-form-item>
<template v-if="inbound.stream.sockoptSwitch"> <template v-if="inbound.stream.sockoptSwitch">
<a-form-item label="PROXY Protocol"> <a-form-item label="Accept Proxy Protocol">
<a-switch v-model="inbound.stream.sockopt.acceptProxyProtocol"></a-switch> <a-switch v-model="inbound.stream.sockopt.acceptProxyProtocol"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label="TCP Fast Open"> <a-form-item label="TCP FastOpen">
<a-switch v-model.trim="inbound.stream.sockopt.tcpFastOpen"></a-switch> <a-switch v-model.trim="inbound.stream.sockopt.tcpFastOpen"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label="Route Mark"> <a-form-item label="Route Mark">
<a-input-number v-model="inbound.stream.sockopt.mark" :min="0"></a-input-number> <a-input-number v-model="inbound.stream.sockopt.mark" :min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label="TPROXY"> <a-form-item label="T-Proxy">
<a-select v-model="inbound.stream.sockopt.tproxy" :dropdown-class-name="themeSwitcher.currentTheme"> <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="redirect">Redirect</a-select-option>
<a-select-option value="tproxy">TPROXY</a-select-option> <a-select-option value="tproxy">T-Proxy</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
</template> </template>

View File

@@ -1,63 +1,58 @@
{{define "form/streamTCP"}} {{define "form/streamTCP"}}
<!-- tcp type --> <!-- tcp type -->
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label="PROXY Protocol" v-if="inbound.canEnableTls()"> <a-form-item label="Accept Proxy Protocol" v-if="inbound.canEnableTls()">
<a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch> <a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label='HTTP {{ i18n "camouflage" }}'> <a-form-item label='HTTP {{ i18n "camouflage" }}'>
<a-switch :checked="inbound.stream.tcp.type === 'http'" <a-switch
:checked="inbound.stream.tcp.type === 'http'"
@change="checked => inbound.stream.tcp.type = checked ? 'http' : 'none'"> @change="checked => inbound.stream.tcp.type = checked ? 'http' : 'none'">
</a-switch> </a-switch>
</a-form-item> </a-form-item>
</a-form> </a-form>
<a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }" <!-- tcp request -->
:wrapper-col="{ md: {span:14} }"> <a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<!-- tcp request --> <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}'>
<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-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.method" }}'> <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}'>
<a-input v-model.trim="inbound.stream.tcp.request.method"></a-input> <a-input v-model.trim="inbound.stream.tcp.request.method"></a-input>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<template slot="label">{{ i18n "pages.inbounds.stream.tcp.path" }} <template slot="label">{{ i18n "pages.inbounds.stream.tcp.requestPath" }}
<a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button> <a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button>
</template> </template>
<template v-for="(path, index) in inbound.stream.tcp.request.path"> <template v-for="(path, index) in inbound.stream.tcp.request.path">
<a-input v-model.trim="inbound.stream.tcp.request.path[index]"> <a-input v-model.trim="inbound.stream.tcp.request.path[index]">
<a-button size="small" slot="addonAfter" @click="inbound.stream.tcp.request.removePath(index)" <a-button size="small" slot="addonAfter"
v-if="inbound.stream.tcp.request.path.length>1">-</a-button> @click="inbound.stream.tcp.request.removePath(index)"
v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
</a-input> </a-input>
</template> </template>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'> <a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
<a-button size="small" @click="inbound.stream.tcp.request.addHeader('host', '')">+</a-button> <a-button size="small" @click="inbound.stream.tcp.request.addHeader('', '')">+</a-button>
</a-form-item> </a-form-item>
<a-form-item :wrapper-col="{span:24}"> <a-form-item :wrapper-col="{span:24}">
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers"> <a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
<a-input style="width: 50%" v-model.trim="header.name" <a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template> <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
</a-input> </a-input>
<a-input style="width: 50%" v-model.trim="header.value" <a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'> <a-button slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
<a-button slot="addonAfter" size="small"
@click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
</a-input> </a-input>
</a-input-group> </a-input-group>
</a-form-item> </a-form-item>
<!-- tcp response -->
<!-- tcp response --> <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}'>
<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-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.status" }}'> <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}'>
<a-input v-model.trim="inbound.stream.tcp.response.status"></a-input> <a-input v-model.trim="inbound.stream.tcp.response.status"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.statusDescription" }}'> <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}'>
<a-input v-model.trim="inbound.stream.tcp.response.reason"></a-input> <a-input v-model.trim="inbound.stream.tcp.response.reason"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}'> <a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}'>
@@ -66,12 +61,11 @@
</a-form-item> </a-form-item>
<a-form-item :wrapper-col="{span:24}"> <a-form-item :wrapper-col="{span:24}">
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers"> <a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers">
<a-input style="width: 50%" v-model.trim="header.name" <a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template> <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
</a-input> </a-input>
<a-input style="width: 50%" v-model.trim="header.value" <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"> <template slot="addonAfter">
<a-button size="small" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button> <a-button size="small" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button>
</template> </template>

View File

@@ -1,12 +1,12 @@
{{define "form/streamWS"}} {{define "form/streamWS"}}
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label="PROXY Protocol"> <a-form-item label="Accept Proxy Protocol">
<a-switch v-model="inbound.stream.ws.acceptProxyProtocol"></a-switch> <a-switch v-model="inbound.stream.ws.acceptProxyProtocol"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "path" }}'> <a-form-item label='{{ i18n "path" }}'>
<a-input v-model.trim="inbound.stream.ws.path"></a-input> <a-input v-model.trim="inbound.stream.ws.path"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'> <a-form-item label='{{ i18n "pages.inbounds.stream.general.requestHeader" }}'>
<a-button size="small" @click="inbound.stream.ws.addHeader()">+</a-button> <a-button size="small" @click="inbound.stream.ws.addHeader()">+</a-button>
</a-form-item> </a-form-item>
<a-form-item :wrapper-col="{span:24}"> <a-form-item :wrapper-col="{span:24}">

View File

@@ -15,7 +15,7 @@
<template slot="title"> <template slot="title">
<span>{{ i18n "pages.inbounds.realityDesc" }}</span> <span>{{ i18n "pages.inbounds.realityDesc" }}</span>
</template> </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-tooltip>
<a-radio-button value="tls">TLS</a-radio-button> <a-radio-button value="tls">TLS</a-radio-button>
</a-radio-group> </a-radio-group>
@@ -26,9 +26,9 @@
<a-form-item label="SNI" placeholder="Server Name Indication"> <a-form-item label="SNI" placeholder="Server Name Indication">
<a-input v-model.trim="inbound.stream.tls.sni"></a-input> <a-input v-model.trim="inbound.stream.tls.sni"></a-input>
</a-form-item> </a-form-item>
<a-form-item label="Cipher Suites"> <a-form-item label="CipherSuites">
<a-select v-model="inbound.stream.tls.cipherSuites" :dropdown-class-name="themeSwitcher.currentTheme"> <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-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[ value ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
@@ -92,7 +92,7 @@
<a-input type="textarea" :rows="3" v-model="cert.key"></a-input> <a-input type="textarea" :rows="3" v-model="cert.key"></a-input>
</a-form-item> </a-form-item>
</template> </template>
<a-form-item label='OCSP stapling'> <a-form-item label='ocspStapling'>
<a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number> <a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
</a-form-item> </a-form-item>
</template> </template>
@@ -162,16 +162,16 @@
<a-form-item label='Dest'> <a-form-item label='Dest'>
<a-input v-model.trim="inbound.stream.reality.dest"></a-input> <a-input v-model.trim="inbound.stream.reality.dest"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='SNI'> <a-form-item label='Server Names'>
<a-input v-model.trim="inbound.stream.reality.serverNames"></a-input> <a-input v-model.trim="inbound.stream.reality.serverNames"></a-input>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<template slot="label"> <template slot="label">
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
<span>{{ i18n "reset" }}</span> <span>{{ i18n "pages.client.renew" }}</span>
</template> </template>
Short ID Short IDs
<a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId()" type="sync"> </a-icon> <a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId()" type="sync"> </a-icon>
</a-icon> </a-icon>
</template> </template>
@@ -187,7 +187,7 @@
<a-input v-model.trim="inbound.stream.reality.settings.publicKey"></a-input> <a-input v-model.trim="inbound.stream.reality.settings.publicKey"></a-input>
</a-form-item> </a-form-item>
<a-form-item label=" "> <a-form-item label=" ">
<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> </a-form-item>
</template> </template>
</a-form> </a-form>

View File

@@ -164,7 +164,7 @@
</tr> </tr>
</table> </table>
<template v-if="app.subSettings.enable && infoModal.clientSettings.subId"> <template v-if="app.subSettings.enable && infoModal.clientSettings.subId">
<a-divider>Subscription URL</a-divider> <a-divider>Subscription link</a-divider>
<a-row> <a-row>
<a-col :sx="24" :md="22"><a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a></a-col> <a-col :sx="24" :md="22"><a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a></a-col>
<a-col :sx="24" :md="2" style="text-align: right;"> <a-col :sx="24" :md="2" style="text-align: right;">
@@ -264,45 +264,6 @@
<td><a-tag color="green">[[ account.pass ]]</a-tag></td> <td><a-tag color="green">[[ account.pass ]]</a-tag></td>
</tr> </tr>
</table> </table>
<table v-if="dbInbound.isWireguard" style="margin-bottom: 10px; width: 100%;">
<tr class="client-table-odd-row">
<td>{{ i18n "pages.xray.wireguard.secretKey" }}</td>
<td>[[ inbound.settings.secretKey ]]</td>
</tr>
<tr>
<td>{{ i18n "pages.xray.wireguard.publicKey" }}</td>
<td>[[ inbound.settings.pubKey ]]</td>
</tr>
<tr class="client-table-odd-row">
<td>MTU</td>
<td>[[ inbound.settings.mtu ]]</td>
</tr>
<tr>
<td>Kernel Mode</td>
<td>[[ inbound.settings.kernelMode ]]</td>
</tr>
<template v-for="(peer, index) in inbound.settings.peers">
<tr>
<td colspan="2"><a-tag>Peer [[ index + 1 ]]</a-tag></td>
</tr>
<tr class="client-table-odd-row">
<td>{{ i18n "pages.xray.wireguard.publicKey" }}</td>
<td>[[ peer.publicKey ]]</td>
</tr>
<tr>
<td>{{ i18n "pages.xray.wireguard.psk" }}</td>
<td>[[ peer.psk ]]</td>
</tr>
<tr class="client-table-odd-row">
<td>{{ i18n "pages.xray.wireguard.allowedIPs" }}</td>
<td>[[ peer.allowedIPs.join(",") ]]</td>
</tr>
<tr>
<td>Keep Alive</td>
<td>[[ peer.keepAlive ]]</td>
</tr>
</table>
</template>
</template> </template>
</a-modal> </a-modal>
<script> <script>
@@ -388,4 +349,4 @@
}); });
</script> </script>
{{end}} {{end}}

View File

@@ -63,9 +63,6 @@
get client() { get client() {
return inModal.inbound.clients[0]; return inModal.inbound.clients[0];
}, },
get datepicker() {
return app.datepicker;
},
get delayedExpireDays() { get delayedExpireDays() {
return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0; return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;
}, },

View File

@@ -230,7 +230,7 @@
</template> </template>
<a-menu-item key="clipboard"> <a-menu-item key="clipboard">
<a-icon type="copy"></a-icon> <a-icon type="copy"></a-icon>
{{ i18n "pages.inbounds.exportInbound" }} {{ i18n "pages.inbounds.copyToClipboard" }}
</a-menu-item> </a-menu-item>
<a-menu-item key="resetTraffic"> <a-menu-item key="resetTraffic">
<a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }} <a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }}
@@ -244,7 +244,7 @@
</span> </span>
</a-menu-item> </a-menu-item>
<a-menu-item v-if="isMobile"> <a-menu-item v-if="isMobile">
<a-switch size="small" v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch> <a-switch size="small" v-model="dbInbound.enable" @change="switchEnable(dbInbound.id)"></a-switch>
{{ i18n "pages.inbounds.enable" }} {{ i18n "pages.inbounds.enable" }}
</a-menu-item> </a-menu-item>
</a-menu> </a-menu>
@@ -314,7 +314,7 @@
</a-popover> </a-popover>
</template> </template>
<template slot="enable" slot-scope="text, dbInbound"> <template slot="enable" slot-scope="text, dbInbound">
<a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch> <a-switch v-model="dbInbound.enable" @change="switchEnable(dbInbound.id)"></a-switch>
</template> </template>
<template slot="expiryTime" slot-scope="text, dbInbound"> <template slot="expiryTime" slot-scope="text, dbInbound">
<a-popover v-if="dbInbound.expiryTime > 0" :overlay-class-name="themeSwitcher.currentTheme"> <a-popover v-if="dbInbound.expiryTime > 0" :overlay-class-name="themeSwitcher.currentTheme">
@@ -446,28 +446,27 @@
<script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
{{template "component/themeSwitcher" .}} {{template "component/themeSwitcher" .}}
{{template "component/persianDatepicker" .}}
<script> <script>
const columns = [{ const columns = [{
title: "ID", title: "ID",
align: 'right', align: 'right',
dataIndex: "id", dataIndex: "id",
width: 30, width: 40,
responsive: ["xs"], responsive: ["xs"],
}, { }, {
title: '{{ i18n "pages.inbounds.operate" }}', title: '{{ i18n "pages.inbounds.operate" }}',
align: 'center', align: 'center',
width: 30, width: 40,
scopedSlots: { customRender: 'action' }, scopedSlots: { customRender: 'action' },
}, { }, {
title: '{{ i18n "pages.inbounds.enable" }}', title: '{{ i18n "pages.inbounds.enable" }}',
align: 'center', align: 'center',
width: 30, width: 40,
scopedSlots: { customRender: 'enable' }, scopedSlots: { customRender: 'enable' },
}, { }, {
title: '{{ i18n "pages.inbounds.remark" }}', title: '{{ i18n "pages.inbounds.remark" }}',
align: 'center', align: 'center',
width: 60, width: 80,
dataIndex: "remark", dataIndex: "remark",
}, { }, {
title: '{{ i18n "pages.inbounds.port" }}', title: '{{ i18n "pages.inbounds.port" }}',
@@ -477,7 +476,7 @@
}, { }, {
title: '{{ i18n "pages.inbounds.protocol" }}', title: '{{ i18n "pages.inbounds.protocol" }}',
align: 'left', align: 'left',
width: 70, width: 90,
scopedSlots: { customRender: 'protocol' }, scopedSlots: { customRender: 'protocol' },
}, { }, {
title: '{{ i18n "clients" }}', title: '{{ i18n "clients" }}',
@@ -492,7 +491,7 @@
}, { }, {
title: '{{ i18n "pages.inbounds.expireDate" }}', title: '{{ i18n "pages.inbounds.expireDate" }}',
align: 'center', align: 'center',
width: 40, width: 60,
scopedSlots: { customRender: 'expiryTime' }, scopedSlots: { customRender: 'expiryTime' },
}]; }];
@@ -540,7 +539,6 @@
data: { data: {
siderDrawer, siderDrawer,
themeSwitcher, themeSwitcher,
persianDatepicker,
spinning: false, spinning: false,
inbounds: [], inbounds: [],
dbInbounds: [], dbInbounds: [],
@@ -562,7 +560,6 @@
subURI : '' subURI : ''
}, },
remarkModel: '-ieo', remarkModel: '-ieo',
datepicker: 'gregorian',
tgBotEnable: false, tgBotEnable: false,
pageSize: 0, pageSize: 0,
isMobile: window.innerWidth <= 768, isMobile: window.innerWidth <= 768,
@@ -608,7 +605,6 @@
}; };
this.pageSize = pageSize; this.pageSize = pageSize;
this.remarkModel = remarkModel; this.remarkModel = remarkModel;
this.datepicker = datepicker;
} }
}, },
setInbounds(dbInbounds) { setInbounds(dbInbounds) {
@@ -1034,9 +1030,8 @@
newDbInbound = this.checkFallback(dbInbound); newDbInbound = this.checkFallback(dbInbound);
infoModal.show(newDbInbound, index); infoModal.show(newDbInbound, index);
}, },
switchEnable(dbInboundId,state) { switchEnable(dbInboundId) {
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId); dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
dbInbound.enable = state;
this.submit(`/panel/inbound/update/${dbInboundId}`, dbInbound); this.submit(`/panel/inbound/update/${dbInboundId}`, dbInbound);
}, },
async switchEnableClient(dbInboundId, client) { async switchEnableClient(dbInboundId, client) {

View File

@@ -77,8 +77,8 @@
<a-row> <a-row>
<a-col :sm="24" :md="12"> <a-col :sm="24" :md="12">
<a-card hoverable> <a-card hoverable>
3X-UI <a href="https://github.com/MHSanaei/3x-ui/releases" target="_blank"><a-tag color="green">v{{ .cur_ver }}</a-tag></a> 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> 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 href="https://t.me/panel3xui" target="_blank"><a-tag color="green">@panel3xui</a-tag></a>
</a-card> </a-card>
</a-col> </a-col>
@@ -96,7 +96,7 @@
<a-tag :color="status.xray.color">[[ status.xray.state ]]</a-tag> <a-tag :color="status.xray.color">[[ status.xray.state ]]</a-tag>
<a-popover v-if="status.xray.state === State.Error" <a-popover v-if="status.xray.state === State.Error"
:overlay-class-name="themeSwitcher.currentTheme"> :overlay-class-name="themeSwitcher.currentTheme">
<span slot="title" style="font-size: 12pt">An error occurred while running Xray <span slot="title" style="font-size: 12pt">Error in running xray-core
<a-tag color="purple" style="cursor: pointer; float: right;" @click="openLogs()">{{ i18n "pages.index.logs" }}</a-tag> <a-tag color="purple" style="cursor: pointer; float: right;" @click="openLogs()">{{ i18n "pages.index.logs" }}</a-tag>
</span> </span>
<template slot="content"> <template slot="content">
@@ -112,9 +112,9 @@
<a-col :sm="24" :md="12"> <a-col :sm="24" :md="12">
<a-card hoverable> <a-card hoverable>
{{ i18n "pages.index.operationHours" }}: {{ i18n "pages.index.operationHours" }}:
Xray Xray:
<a-tag color="green">[[ formatSecond(status.appStats.uptime) ]]</a-tag> <a-tag color="green">[[ formatSecond(status.appStats.uptime) ]]</a-tag>
OS OS:
<a-tag color="green">[[ formatSecond(status.uptime) ]]</a-tag> <a-tag color="green">[[ formatSecond(status.uptime) ]]</a-tag>
</a-card> </a-card>
</a-col> </a-col>
@@ -141,7 +141,6 @@
<a-card hoverable> <a-card hoverable>
<a-row> <a-row>
<a-col :span="12"> <a-col :span="12">
<a-icon type="global"></a-icon>
IPv4: IPv4:
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
@@ -151,7 +150,6 @@
</a-tooltip> </a-tooltip>
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-icon type="global"></a-icon>
IPv6: IPv6:
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
@@ -167,7 +165,6 @@
<a-card hoverable> <a-card hoverable>
<a-row> <a-row>
<a-col :span="12"> <a-col :span="12">
<a-icon type="swap"></a-icon>
TCP: [[ status.tcpCount ]] TCP: [[ status.tcpCount ]]
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
@@ -177,7 +174,6 @@
</a-tooltip> </a-tooltip>
</a-col> </a-col>
<a-col :span="12"> <a-col :span="12">
<a-icon type="swap"></a-icon>
UDP: [[ status.udpCount ]] UDP: [[ status.udpCount ]]
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
@@ -250,10 +246,8 @@
:closable="true" @ok="() => versionModal.visible = false" :closable="true" @ok="() => versionModal.visible = false"
:class="themeSwitcher.currentTheme" :class="themeSwitcher.currentTheme"
footer=""> footer="">
<a-alert type="warning" style="margin-bottom: 12px; width: fit-content" <h2>{{ i18n "pages.index.xraySwitchClick"}}</h2>
message='{{ i18n "pages.index.xraySwitchClickDesk" }}' <h2>{{ i18n "pages.index.xraySwitchClickDesk"}}</h2>
show-icon
></a-alert>
<template v-for="version, index in versionModal.versions"> <template v-for="version, index in versionModal.versions">
<a-tag :color="index % 2 == 0 ? 'purple' : 'green'" <a-tag :color="index % 2 == 0 ? 'purple' : 'green'"
style="margin: 10px" @click="switchV2rayVersion(version)"> style="margin: 10px" @click="switchV2rayVersion(version)">
@@ -299,12 +293,12 @@
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<a-button type="primary" style="margin-bottom: 10px;" <a-button type="primary" style="margin-bottom: 10px;"
:href="'data:application/text;charset=utf-8,' + encodeURIComponent(logModal.logs.join('\n'))" download="x-ui.log"> :href="'data:application/text;charset=utf-8,' + encodeURIComponent(logModal.logs)" download="x-ui.log">
{{ i18n "download" }} x-ui.log {{ i18n "download" }} x-ui.log
</a-button> </a-button>
</a-form-item> </a-form-item>
</a-form> </a-form>
<div class="ant-input" style="height: auto; max-height: 500px; overflow: auto;" v-html="logModal.formattedLogs"></div> <div class="ant-input" style="height: auto; max-height: 500px; overflow: auto;" v-html="logModal.logs"></div>
</a-modal> </a-modal>
<a-modal id="backup-modal" v-model="backupModal.visible" :title="backupModal.title" <a-modal id="backup-modal" v-model="backupModal.visible" :title="backupModal.title"
@@ -432,16 +426,14 @@
const logModal = { const logModal = {
visible: false, visible: false,
logs: [], logs: '',
formattedLogs: '',
rows: 20, rows: 20,
level: 'info', level: 'info',
syslog: false, syslog: false,
loading: false, loading: false,
show(logs) { show(logs) {
this.visible = true; this.visible = true;
this.logs = logs; this.logs = logs? this.formatLogs(logs) : "No Record...";
this.formattedLogs = logs.length > 0 ? this.formatLogs(logs) : "No Record...";
}, },
formatLogs(logs) { formatLogs(logs) {
let formattedLogs = ''; let formattedLogs = '';

View File

@@ -138,27 +138,6 @@
<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.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="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> <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-list-item>
<a-row style="padding: 20px"> <a-row style="padding: 20px">
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
@@ -187,22 +166,42 @@
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings"}}' style="padding: 20px;"> <a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings"}}' style="padding: 20px;">
<a-divider>{{ i18n "pages.settings.security.admin"}}</a-divider> <a-divider>{{ i18n "pages.settings.security.admin"}}</a-divider>
<a-form layout="horizontal" :colon="false" style="float: left; margin: 10px 0;" :label-col="{ md: {span:10} }" :wrapper-col="{ md: {span:14} }"> <a-form style="padding: 20px;" layout="inline">
<a-form-item label='{{ i18n "pages.settings.oldUsername"}}'> <table cellpadding="2">
<a-input v-model="user.oldUsername"></a-input> <tr>
</a-form-item> <td>{{ i18n "pages.settings.oldUsername"}}:</td>
<a-form-item label='{{ i18n "pages.settings.currentPassword"}}'> <td>
<password-input v-model="user.oldPassword"></password-input> <a-form-item>
</a-form-item> <a-input v-model="user.oldUsername" style="width: 200px"></a-input>
<a-form-item label='{{ i18n "pages.settings.newUsername"}}'> </a-form-item>
<a-input v-model="user.newUsername"></a-input> </td>
</a-form-item> </tr>
<a-form-item label='{{ i18n "pages.settings.newPassword"}}'> <tr>
<password-input v-model="user.newPassword"></password-input> <td>{{ i18n "pages.settings.currentPassword"}}:</td>
</a-form-item> <td>
<a-form-item label=" "> <a-form-item>
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button> <password-input v-model="user.oldPassword" style="width: 200px"></password-input>
</a-form-item> </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> </a-form>
<a-divider>{{ i18n "pages.settings.security.secret"}}</a-divider> <a-divider>{{ i18n "pages.settings.security.secret"}}</a-divider>
<a-form style="padding: 20px;"> <a-form style="padding: 20px;">
@@ -244,7 +243,6 @@
<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.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="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="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-list-item>
<a-row style="padding: 20px"> <a-row style="padding: 20px">
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
@@ -313,7 +311,6 @@
showAlert: false, showAlert: false,
remarkModels: {i:'Inbound',e:'Email',o:'Other'}, remarkModels: {i:'Inbound',e:'Email',o:'Other'},
remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'], remarkSeparators: [' ','-','_','@',':','~','|',',','.','/'],
datepickerList: [{name:'Gregorian (Standard)', value: 'gregorian'}, {name:'Jalalian (شمسی)', value: 'jalalian'}],
remarkSample: '', remarkSample: '',
get remarkModel() { get remarkModel() {
rm = this.allSetting.remarkModel; rm = this.allSetting.remarkModel;
@@ -331,12 +328,6 @@
this.allSetting.remarkModel = value + this.allSetting.remarkModel.substring(1); this.allSetting.remarkModel = value + this.allSetting.remarkModel.substring(1);
this.changeRemarkSample(); this.changeRemarkSample();
}, },
get datepicker() {
return this.allSetting.datepicker ? this.allSetting.datepicker : 'gregorian';
},
set datepicker(value) {
this.allSetting.datepicker = value;
},
changeRemarkSample(){ changeRemarkSample(){
sample = [] sample = []
this.remarkModel.forEach(r => sample.push(this.remarkModels[r])); this.remarkModel.forEach(r => sample.push(this.remarkModels[r]));
@@ -393,7 +384,9 @@
if (msg.success) { if (msg.success) {
this.loading(true); this.loading(true);
await PromiseUtil.sleep(5000); await PromiseUtil.sleep(5000);
let { webCertFile, webKeyFile, webDomain: host, webPort: port, webBasePath: base } = this.allSetting; 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;
const isTLS = webCertFile !== "" || webKeyFile !== ""; const isTLS = webCertFile !== "" || webKeyFile !== "";
const url = buildURL({ host, port, isTLS, base, path: "panel/settings" }); const url = buildURL({ host, port, isTLS, base, path: "panel/settings" });
window.location.replace(url); window.location.replace(url);

View File

@@ -1,205 +0,0 @@
{{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),
domainStrategy: 'ForceIPv6v4',
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}}

View File

@@ -6,8 +6,7 @@
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.css?{{ .cur_ver }}"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.css?{{ .cur_ver }}">
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css"> <link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css">
<script src="{{ .base_path }}assets/base64/base64.min.js"></script> <script src="{{ .base_path }}assets/js/model/outbound.js"></script>
<script src="{{ .base_path }}assets/js/model/outbound.js?{{ .cur_ver }}"></script>
<script src="{{ .base_path }}assets/codemirror/codemirror.js"></script> <script src="{{ .base_path }}assets/codemirror/codemirror.js"></script>
<script src="{{ .base_path }}assets/codemirror/javascript.js"></script> <script src="{{ .base_path }}assets/codemirror/javascript.js"></script>
<script src="{{ .base_path }}assets/codemirror/jshint.js"></script> <script src="{{ .base_path }}assets/codemirror/jshint.js"></script>
@@ -81,21 +80,17 @@
</a-space> </a-space>
</a-col> </a-col>
<a-col :xs="24" :sm="16"> <a-col :xs="24" :sm="16">
<template> <a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200">
<div> </a-back-top>
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"> <a-alert type="warning" style="float: right; width: fit-content"
</a-back-top> message='{{ i18n "pages.settings.infoDesc" }}'
<a-alert type="warning" style="float: right; width: fit-content" show-icon
message='{{ i18n "pages.settings.infoDesc" }}' >
show-icon
>
</div>
</template>
</a-col> </a-col>
</a-row> </a-row>
</a-card> </a-card>
<a-tabs class="ant-card-dark-box-nohover" default-active-key="tpl-1" <a-tabs class="ant-card-dark-box-nohover" default-active-key="tpl-1"
@change="(activeKey) => { if(activeKey == 'tpl-advanced') this.changeCode(); }" @change="(activeKey) => { if(activeKey == 'tpl-4') this.changeCode(); }"
:class="themeSwitcher.currentTheme"> :class="themeSwitcher.currentTheme">
<a-tab-pane key="tpl-1" tab='{{ i18n "pages.xray.basicTemplate"}}'> <a-tab-pane key="tpl-1" tab='{{ i18n "pages.xray.basicTemplate"}}'>
<a-space direction="horizontal" style="padding: 20px 20px"> <a-space direction="horizontal" style="padding: 20px 20px">
@@ -124,7 +119,7 @@
v-model="freedomStrategy" v-model="freedomStrategy"
:dropdown-class-name="themeSwitcher.currentTheme" :dropdown-class-name="themeSwitcher.currentTheme"
style="width: 100%"> 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> </a-select>
</template> </template>
</a-col> </a-col>
@@ -162,7 +157,6 @@
<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.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.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.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> <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>
<a-collapse-panel header='{{ i18n "pages.xray.blockCountryConfigs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.blockCountryConfigs"}}'>
@@ -180,7 +174,7 @@
<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.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.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.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.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> <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>
<a-collapse-panel header='{{ i18n "pages.xray.directCountryConfigs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.directCountryConfigs"}}'>
@@ -198,7 +192,7 @@
<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.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.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.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.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> <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>
<a-collapse-panel header='{{ i18n "pages.xray.ipv4Configs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.ipv4Configs"}}'>
@@ -222,13 +216,10 @@
</template> </template>
</a-alert> </a-alert>
</a-row> </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.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.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.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> <setting-list-item type="switch" title='{{ i18n "pages.xray.SpotifyWARP"}}' desc='{{ i18n "pages.xray.SpotifyWARPDesc"}}' v-model="SpotifyWARPSettings"></setting-list-item>
</template>
<a-button v-else style="margin: 10px 0;" @click="showWarp">WARP {{ i18n "pages.xray.rules.outbound" }}</a-button>
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
</a-tab-pane> </a-tab-pane>
@@ -341,74 +332,79 @@
</a-table> </a-table>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-3" tab='{{ i18n "pages.xray.Outbounds"}}' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-3" tab='{{ i18n "pages.xray.Outbounds"}}' style="padding-top: 20px;" force-render="true">
<a-button type="primary" icon="plus" @click="addOutbound()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button> <a-button type="primary" icon="plus" @click="addOutbound()">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
<a-button type="primary" @click="showWarp()" style="margin-bottom: 10px;">WARP</a-button> <a-button type="primary" icon="plus" @click="addReverse()">{{ i18n "pages.xray.outbound.addReverse" }}</a-button>
<a-table :columns="outboundColumns" bordered <a-row>
:row-key="r => r.key" <a-col :sm="24" :md="12">
:data-source="outboundData" <p style="margin: 10px;">{{ i18n "pages.xray.Outbounds"}}</p>
:scroll="isMobile ? {} : { x: 200 }" <a-table :columns="outboundColumns" bordered
:pagination="false" :row-key="r => r.key"
:indent-size="0" :data-source="outboundData"
:style="isMobile ? 'padding: 5px 5px' : 'margin-right: 1px;'"> :scroll="isMobile ? {} : { x: 200 }"
<template slot="action" slot-scope="text, outbound, index"> :pagination="false"
[[ index+1 ]] :indent-size="0"
<a-dropdown :trigger="['click']"> :style="isMobile ? 'padding: 5px 5px' : 'margin-right: 1px;'">
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon> <template slot="action" slot-scope="text, outbound, index">
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme"> [[ index+1 ]]
<a-menu-item @click="editOutbound(index)"> <a-dropdown :trigger="['click']">
<a-icon type="edit"></a-icon> <a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
{{ i18n "edit" }} <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
</a-menu-item> <a-menu-item @click="editOutbound(index)">
<a-menu-item @click="deleteOutbound(index)"> <a-icon type="edit"></a-icon>
<span style="color: #FF4D4F"> {{ i18n "edit" }}
<a-icon type="delete"></a-icon> {{ i18n "delete"}} </a-menu-item>
</span> <a-menu-item @click="deleteOutbound(index)">
</a-menu-item> <span style="color: #FF4D4F">
</a-menu> <a-icon type="delete"></a-icon> {{ i18n "delete"}}
</a-dropdown> </span>
</template> </a-menu-item>
<template slot="address" slot-scope="text, outbound, index"> </a-menu>
<p style="margin: 0 5px;" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p> </a-dropdown>
</template> </template>
<template slot="protocol" slot-scope="text, outbound, index"> <template slot="address" slot-scope="text, outbound, index">
<a-tag style="margin:0;" color="purple">[[ outbound.protocol ]]</a-tag> <p style="margin: 0 5px;" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
<template v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)"> </template>
<a-tag style="margin:0;" color="blue">[[ outbound.streamSettings.network ]]</a-tag> <template slot="protocol" slot-scope="text, outbound, index">
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag> <a-tag style="margin:0;" color="purple">[[ outbound.protocol ]]</a-tag>
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='reality'" color="green">reality</a-tag> <template v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
</template> <a-tag style="margin:0;" color="blue">[[ outbound.streamSettings.network ]]</a-tag>
</template> <a-tag style="margin:0;" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
</a-table> <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-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-4" tab='{{ i18n "pages.xray.outbound.reverse"}}' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-4" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;" force-render="true">
<a-button type="primary" icon="plus" @click="addReverse()" style="margin-bottom: 10px;">{{ i18n "pages.xray.outbound.addReverse" }}</a-button>
<a-table :columns="reverseColumns" bordered
:row-key="r => r.key"
:data-source="reverseData"
:scroll="isMobile ? {} : { x: 200 }"
:pagination="false"
:indent-size="0"
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
<template slot="action" slot-scope="text, reverse, index">
[[ index+1 ]]
<a-dropdown :trigger="['click']">
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
<a-menu-item @click="editReverse(index)">
<a-icon type="edit"></a-icon>
{{ i18n "edit" }}
</a-menu-item>
<a-menu-item @click="deleteReverse(index)">
<span style="color: #FF4D4F">
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
</span>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;" force-render="true">
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}' description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta> <a-list-item-meta title='{{ i18n "pages.xray.Template"}}' description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;" :size="isMobile ? 'small' : ''"> <a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;" :size="isMobile ? 'small' : ''">
<a-radio-button value="xraySetting">{{ i18n "pages.xray.completeTemplate"}}</a-radio-button> <a-radio-button value="xraySetting">{{ i18n "pages.xray.completeTemplate"}}</a-radio-button>
@@ -430,13 +426,12 @@
{{template "ruleModal"}} {{template "ruleModal"}}
{{template "outModal"}} {{template "outModal"}}
{{template "reverseModal"}} {{template "reverseModal"}}
{{template "warpModal"}}
<script> <script>
const rulesColumns = [ const rulesColumns = [
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } }, { title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
{ title: '{{ i18n "pages.xray.rules.source"}}', children: [ { title: '{{ i18n "pages.xray.rules.source"}}', children: [
{ title: 'IP', dataIndex: "source", align: 'center', width: 20, ellipsis: true }, { title: 'IP', dataIndex: "source", align: 'center', width: 20, ellipsis: true },
{ title: 'Port', dataIndex: 'sourcePort', align: 'center', width: 10, ellipsis: true } ]}, { title: 'port', dataIndex: 'sourcePort', align: 'center', width: 10, ellipsis: true } ]},
{ title: '{{ i18n "pages.inbounds.network"}}', children: [ { title: '{{ i18n "pages.inbounds.network"}}', children: [
{ title: 'L4', dataIndex: 'network', align: 'center', width: 10 }, { title: 'L4', dataIndex: 'network', align: 'center', width: 10 },
{ title: 'Protocol', dataIndex: 'protocol', align: 'center', width: 10, ellipsis: true }, { title: 'Protocol', dataIndex: 'protocol', align: 'center', width: 10, ellipsis: true },
@@ -471,7 +466,6 @@
{ title: '{{ i18n "pages.xray.outbound.tag"}}', dataIndex: 'tag', align: 'center', width: 50 }, { title: '{{ i18n "pages.xray.outbound.tag"}}', dataIndex: 'tag', align: 'center', width: 50 },
{ title: '{{ i18n "pages.xray.outbound.domain"}}', dataIndex: 'domain', align: 'center', width: 50 }, { title: '{{ i18n "pages.xray.outbound.domain"}}', dataIndex: 'domain', align: 'center', width: 50 },
]; ];
const app = new Vue({ const app = new Vue({
delimiters: ['[[', ']]'], delimiters: ['[[', ']]'],
el: '#app', el: '#app',
@@ -516,10 +510,23 @@
domainStrategy: "UseIPv4" domainStrategy: "UseIPv4"
} }
}, },
warpSettings: {
tag: "WARP",
protocol: "socks",
settings: {
servers: [
{
address: "127.0.0.1",
port: 40000
}
]
}
},
directSettings: { directSettings: {
tag: "direct", tag: "direct",
protocol: "freedom" protocol: "freedom"
}, },
outboundDomainStrategies: ["AsIs", "UseIP", "UseIPv4", "UseIPv6"],
routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"], routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"],
settingsData: { settingsData: {
protocols: { protocols: {
@@ -530,18 +537,13 @@
cn: ["geoip:cn"], cn: ["geoip:cn"],
ir: ["ext:geoip_IR.dat:ir"], ir: ["ext:geoip_IR.dat:ir"],
ru: ["geoip:ru"], ru: ["geoip:ru"],
vn: ["ext:geoip_VN.dat:vn"], vn: ["ext:geoip_VN.dat:vn"],
}, },
domains: { domains: {
ads: [ ads: [
"geosite:category-ads-all", "geosite:category-ads-all",
"ext:geosite_IR.dat: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"], speedtest: ["geosite:speedtest"],
openai: ["geosite:openai"], openai: ["geosite:openai"],
google: ["geosite:google"], google: ["geosite:google"],
@@ -558,22 +560,22 @@
ir: [ ir: [
"regexp:.*\\.ir$", "regexp:.*\\.ir$",
"regexp:.*\\.xn--mgba3a4f16a$", // .ایران "regexp:.*\\.xn--mgba3a4f16a$", // .ایران
"ext:geosite_IR.dat:ir" "ext:geosite_IR.dat:ir" // have rules to bypass all .ir domains.
], ],
vn: [ vn: [
"regexp:.*\\.vn$", "regexp:.*\\.vn$",
"ext:geosite_VN.dat:vn", "ext:geosite_VN.dat:vn",
"ext:geosite_VN.dat:ads" "ext:geosite_VN.dat:ads"
] ]
}, },
familyProtectDNS: { familyProtectDNS: {
"servers": [ "servers": [
"1.1.1.3", // https://developers.cloudflare.com/1.1.1.1/setup/ "1.1.1.3", // https://developers.cloudflare.com/1.1.1.1/setup/
"1.0.0.3", "1.0.0.3",
"2606:4700:4700::1113", "94.140.14.15", // https://adguard-dns.io/kb/general/dns-providers/
"2606:4700:4700::1003" "94.140.15.16"
], ],
"queryStrategy": "UseIP" "queryStrategy": "UseIPv4"
}, },
} }
}, },
@@ -670,13 +672,13 @@
} }
}, },
syncRulesWithOutbound(tag, setting) { syncRulesWithOutbound(tag, setting) {
const newTemplateSettings = this.templateSettings; const newTemplateSettings = {...this.templateSettings};
const haveRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === tag); const haveRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === tag);
const outboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.tag === tag); const outboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.tag === tag);
if (!haveRules && outboundIndex > 0) { if (!haveRules && outboundIndex >= 0) {
newTemplateSettings.outbounds.splice(outboundIndex); newTemplateSettings.outbounds.splice(outboundIndex, 1);
} }
if (haveRules && outboundIndex < 0) { if (haveRules && outboundIndex === -1) {
newTemplateSettings.outbounds.push(setting); newTemplateSettings.outbounds.push(setting);
} }
this.templateSettings = newTemplateSettings; this.templateSettings = newTemplateSettings;
@@ -774,8 +776,6 @@
break; break;
case Protocols.DNS: case Protocols.DNS:
return [o.settings.address + ':' + o.settings.port]; return [o.settings.address + ':' + o.settings.port];
case Protocols.Wireguard:
return o.settings.peers.map(peer => peer.endpoint);
default: default:
return null; return null;
} }
@@ -941,16 +941,13 @@
rules = this.templateSettings.routing.rules; rules = this.templateSettings.routing.rules;
rules.splice(index,1); rules.splice(index,1);
this.routingRuleSettings = JSON.stringify(rules); this.routingRuleSettings = JSON.stringify(rules);
},
showWarp(){
warpModal.show();
} }
}, },
async mounted() { async mounted() {
await this.getXraySetting(); await this.getXraySetting();
await this.getXrayResult(); await this.getXrayResult();
while (true) { while (true) {
await PromiseUtil.sleep(800); await PromiseUtil.sleep(600);
this.saveBtnDisable = this.oldXraySetting === this.xraySetting; this.saveBtnDisable = this.oldXraySetting === this.xraySetting;
} }
}, },
@@ -1118,12 +1115,37 @@
}, },
warpDomains: { warpDomains: {
get: function () { get: function () {
return this.templateRuleGetter({ outboundTag: "warp", property: "domain" }); return this.templateRuleGetter({ outboundTag: "WARP", property: "domain" });
}, },
set: function (newValue) { set: function (newValue) {
this.templateRuleSetter({ outboundTag: "warp", property: "domain", data: newValue }); this.templateRuleSetter({ outboundTag: "WARP", property: "domain", data: newValue });
this.syncRulesWithOutbound("WARP", this.warpSettings);
} }
}, },
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: { torrentSettings: {
get: function () { get: function () {
return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols); return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols);
@@ -1160,18 +1182,6 @@
} }
}, },
}, },
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: { SpeedTestSettings: {
get: function () { get: function () {
return doAllItemsExist(this.settingsData.domains.speedtest, this.blockedDomains); return doAllItemsExist(this.settingsData.domains.speedtest, this.blockedDomains);
@@ -1295,7 +1305,7 @@
} }
} }
}, },
VNIpSettings: { VNIpSettings: {
get: function () { get: function () {
return doAllItemsExist(this.settingsData.ips.vn, this.blockedIPs); return doAllItemsExist(this.settingsData.ips.vn, this.blockedIPs);
}, },
@@ -1391,7 +1401,7 @@
} }
} }
}, },
VNIpDirectSettings: { VNIpDirectSettings: {
get: function () { get: function () {
return doAllItemsExist(this.settingsData.ips.vn, this.directIPs); return doAllItemsExist(this.settingsData.ips.vn, this.directIPs);
}, },
@@ -1414,12 +1424,7 @@
this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.vn.includes(data)); 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: { GoogleWARPSettings: {
get: function () { get: function () {
return doAllItemsExist(this.settingsData.domains.google, this.warpDomains); return doAllItemsExist(this.settingsData.domains.google, this.warpDomains);

View File

@@ -36,8 +36,8 @@
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='Protocol'> <a-form-item label='Protocol'>
<a-select v-model="ruleModal.rule.protocol" mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme"> <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-option v-for="x in ['','http','tls','bittorrent']" :value="x">[[ x ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='Attributes'> <a-form-item label='Attributes'>

View File

@@ -16,18 +16,14 @@ import (
"x-ui/xray" "x-ui/xray"
) )
type CheckClientIpJob struct { type CheckClientIpJob struct{}
disAllowedIps []string
}
var job *CheckClientIpJob var job *CheckClientIpJob
var disAllowedIps []string
var ipFiles = []string{ var ipFiles = []string{
xray.GetIPLimitLogPath(), xray.GetIPLimitLogPath(),
xray.GetIPLimitPrevLogPath(),
xray.GetIPLimitBannedLogPath(), xray.GetIPLimitBannedLogPath(),
xray.GetIPLimitBannedPrevLogPath(),
xray.GetAccessPersistentLogPath(), xray.GetAccessPersistentLogPath(),
xray.GetAccessPersistentPrevLogPath(),
} }
func NewCheckClientIpJob() *CheckClientIpJob { func NewCheckClientIpJob() *CheckClientIpJob {
@@ -131,6 +127,7 @@ func (j *CheckClientIpJob) processLogFile() {
} }
} }
disAllowedIps = []string{}
shouldCleanLog := false shouldCleanLog := false
for clientEmail, ips := range InboundClientIps { for clientEmail, ips := range InboundClientIps {
@@ -237,7 +234,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
json.Unmarshal([]byte(inbound.Settings), &settings) json.Unmarshal([]byte(inbound.Settings), &settings)
clients := settings["clients"] clients := settings["clients"]
shouldCleanLog := false shouldCleanLog := false
j.disAllowedIps = []string{}
// create iplimit log file channel // create iplimit log file channel
logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644) logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
@@ -256,7 +252,7 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
shouldCleanLog = true shouldCleanLog = true
if limitIp < len(ips) && inbound.Enable { if limitIp < len(ips) && inbound.Enable {
j.disAllowedIps = append(j.disAllowedIps, ips[limitIp:]...) disAllowedIps = append(disAllowedIps, ips[limitIp:]...)
for i := limitIp; i < len(ips); i++ { for i := limitIp; i < len(ips); i++ {
log.Printf("[LIMIT_IP] Email = %s || SRC = %s", clientEmail, ips[i]) log.Printf("[LIMIT_IP] Email = %s || SRC = %s", clientEmail, ips[i])
} }
@@ -264,12 +260,8 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
} }
} }
} }
logger.Debug("disAllowedIps ", disAllowedIps)
sort.Strings(j.disAllowedIps) sort.Strings(disAllowedIps)
if len(j.disAllowedIps) > 0 {
logger.Debug("disAllowedIps ", j.disAllowedIps)
}
db := database.GetDB() db := database.GetDB()
err = db.Save(inboundClientIps).Error err = db.Save(inboundClientIps).Error

View File

@@ -1,9 +1,6 @@
package job package job
import ( import "x-ui/web/service"
"x-ui/logger"
"x-ui/web/service"
)
type CheckXrayRunningJob struct { type CheckXrayRunningJob struct {
xrayService service.XrayService xrayService service.XrayService
@@ -18,15 +15,11 @@ func NewCheckXrayRunningJob() *CheckXrayRunningJob {
func (j *CheckXrayRunningJob) Run() { func (j *CheckXrayRunningJob) Run() {
if j.xrayService.IsXrayRunning() { if j.xrayService.IsXrayRunning() {
j.checkTime = 0 j.checkTime = 0
} else { return
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()
} }

View File

@@ -15,37 +15,10 @@ func NewClearLogsJob() *ClearLogsJob {
// Here Run is an interface method of the Job interface // Here Run is an interface method of the Job interface
func (j *ClearLogsJob) Run() { func (j *ClearLogsJob) Run() {
logFiles := []string{xray.GetIPLimitLogPath(), xray.GetIPLimitBannedLogPath(), xray.GetAccessPersistentLogPath()} logFiles := []string{xray.GetIPLimitLogPath(), xray.GetIPLimitBannedLogPath(), xray.GetAccessPersistentLogPath()}
logFilesPrev := []string{xray.GetIPLimitPrevLogPath(), xray.GetIPLimitBannedPrevLogPath(), xray.GetAccessPersistentPrevLogPath()}
// clear old previous logs // clear log files
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++ { 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) logger.Warning("clear logs job err:", err)
} }
} }

View File

@@ -24,7 +24,6 @@
], ],
"outbounds": [ "outbounds": [
{ {
"tag": "direct",
"protocol": "freedom", "protocol": "freedom",
"settings": {} "settings": {}
}, },

View File

@@ -38,25 +38,9 @@ func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error) {
return inbounds, nil return inbounds, nil
} }
func (s *InboundService) checkPortExist(listen string, port int, ignoreId int) (bool, error) { func (s *InboundService) checkPortExist(port int, ignoreId int) (bool, error) {
db := database.GetDB() db := database.GetDB()
if listen == "" || listen == "0.0.0.0" || listen == "::" || listen == "::0" { db = db.Model(model.Inbound{}).Where("port = ?", port)
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 { if ignoreId > 0 {
db = db.Where("id != ?", ignoreId) db = db.Where("id != ?", ignoreId)
} }
@@ -151,7 +135,7 @@ func (s *InboundService) checkEmailExistForInbound(inbound *model.Inbound) (stri
} }
func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error) { func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
exist, err := s.checkPortExist(inbound.Listen, inbound.Port, 0) exist, err := s.checkPortExist(inbound.Port, 0)
if err != nil { if err != nil {
return inbound, false, err return inbound, false, err
} }
@@ -268,7 +252,7 @@ func (s *InboundService) GetInbound(id int) (*model.Inbound, error) {
} }
func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, bool, error) { func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, bool, error) {
exist, err := s.checkPortExist(inbound.Listen, inbound.Port, inbound.Id) exist, err := s.checkPortExist(inbound.Port, inbound.Id)
if err != nil { if err != nil {
return inbound, false, err return inbound, false, err
} }
@@ -311,11 +295,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
oldInbound.Settings = inbound.Settings oldInbound.Settings = inbound.Settings
oldInbound.StreamSettings = inbound.StreamSettings oldInbound.StreamSettings = inbound.StreamSettings
oldInbound.Sniffing = inbound.Sniffing oldInbound.Sniffing = inbound.Sniffing
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" { oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
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 needRestart := false
s.xrayApi.Init(p.GetAPIPort()) s.xrayApi.Init(p.GetAPIPort())
@@ -508,10 +488,6 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
} }
} }
if len(newClients) == 0 {
return false, common.NewError("no client remained in Inbound")
}
settings["clients"] = newClients settings["clients"] = newClients
newSettings, err := json.MarshalIndent(settings, "", " ") newSettings, err := json.MarshalIndent(settings, "", " ")
if err != nil { if err != nil {
@@ -1170,8 +1146,6 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId string) err
if oldClient.Email == clientEmail { if oldClient.Email == clientEmail {
if inbound.Protocol == "trojan" { if inbound.Protocol == "trojan" {
clientId = oldClient.Password clientId = oldClient.Password
} else if inbound.Protocol == "shadowsocks" {
clientId = oldClient.Email
} else { } else {
clientId = oldClient.ID clientId = oldClient.ID
} }
@@ -1210,32 +1184,6 @@ func (s *InboundService) SetClientTelegramUserID(trafficId int, tgId string) err
return nil 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) { func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, error) {
_, inbound, err := s.GetClientInboundByEmail(clientEmail) _, inbound, err := s.GetClientInboundByEmail(clientEmail)
if err != nil { if err != nil {
@@ -1257,8 +1205,6 @@ func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, er
if oldClient.Email == clientEmail { if oldClient.Email == clientEmail {
if inbound.Protocol == "trojan" { if inbound.Protocol == "trojan" {
clientId = oldClient.Password clientId = oldClient.Password
} else if inbound.Protocol == "shadowsocks" {
clientId = oldClient.Email
} else { } else {
clientId = oldClient.ID clientId = oldClient.ID
} }
@@ -1320,8 +1266,6 @@ func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int
if oldClient.Email == clientEmail { if oldClient.Email == clientEmail {
if inbound.Protocol == "trojan" { if inbound.Protocol == "trojan" {
clientId = oldClient.Password clientId = oldClient.Password
} else if inbound.Protocol == "shadowsocks" {
clientId = oldClient.Email
} else { } else {
clientId = oldClient.ID clientId = oldClient.ID
} }
@@ -1380,8 +1324,6 @@ func (s *InboundService) ResetClientExpiryTimeByEmail(clientEmail string, expiry
if oldClient.Email == clientEmail { if oldClient.Email == clientEmail {
if inbound.Protocol == "trojan" { if inbound.Protocol == "trojan" {
clientId = oldClient.Password clientId = oldClient.Password
} else if inbound.Protocol == "shadowsocks" {
clientId = oldClient.Email
} else { } else {
clientId = oldClient.ID clientId = oldClient.ID
} }
@@ -1443,8 +1385,6 @@ func (s *InboundService) ResetClientTrafficLimitByEmail(clientEmail string, tota
if oldClient.Email == clientEmail { if oldClient.Email == clientEmail {
if inbound.Protocol == "trojan" { if inbound.Protocol == "trojan" {
clientId = oldClient.Password clientId = oldClient.Password
} else if inbound.Protocol == "shadowsocks" {
clientId = oldClient.Email
} else { } else {
clientId = oldClient.ID clientId = oldClient.ID
} }
@@ -1673,10 +1613,10 @@ func (s *InboundService) DelDepletedClients(id int) (err error) {
return nil return nil
} }
func (s *InboundService) GetClientTrafficTgBot(tgId string) ([]*xray.ClientTraffic, error) { func (s *InboundService) GetClientTrafficTgBot(tguname string) ([]*xray.ClientTraffic, error) {
db := database.GetDB() db := database.GetDB()
var inbounds []*model.Inbound var inbounds []*model.Inbound
err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": "%s"%%`, tgId)).Find(&inbounds).Error err := db.Model(model.Inbound{}).Where("settings like ?", fmt.Sprintf(`%%"tgId": "%s"%%`, tguname)).Find(&inbounds).Error
if err != nil && err != gorm.ErrRecordNotFound { if err != nil && err != gorm.ErrRecordNotFound {
return nil, err return nil, err
} }
@@ -1687,7 +1627,7 @@ func (s *InboundService) GetClientTrafficTgBot(tgId string) ([]*xray.ClientTraff
logger.Error("Unable to get clients from inbound") logger.Error("Unable to get clients from inbound")
} }
for _, client := range clients { for _, client := range clients {
if client.TgID == tgId { if client.TgID == tguname {
emails = append(emails, client.Email) emails = append(emails, client.Email)
} }
} }

View File

@@ -38,7 +38,6 @@ var defaultValueMap = map[string]string{
"timeLocation": "Asia/Tehran", "timeLocation": "Asia/Tehran",
"tgBotEnable": "false", "tgBotEnable": "false",
"tgBotToken": "", "tgBotToken": "",
"tgBotProxy": "",
"tgBotChatId": "", "tgBotChatId": "",
"tgRunTime": "@daily", "tgRunTime": "@daily",
"tgBotBackup": "false", "tgBotBackup": "false",
@@ -57,8 +56,6 @@ var defaultValueMap = map[string]string{
"subEncrypt": "true", "subEncrypt": "true",
"subShowInfo": "true", "subShowInfo": "true",
"subURI": "", "subURI": "",
"datepicker": "gregorian",
"warp": "",
} }
type SettingService struct { type SettingService struct {
@@ -247,14 +244,6 @@ func (s *SettingService) SetTgBotToken(token string) error {
return s.setString("tgBotToken", token) 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) { func (s *SettingService) GetTgBotChatId() (string, error) {
return s.getString("tgBotChatId") return s.getString("tgBotChatId")
} }
@@ -424,23 +413,12 @@ func (s *SettingService) GetSubShowInfo() (bool, error) {
return s.getBool("subShowInfo") return s.getBool("subShowInfo")
} }
func (s *SettingService) GetPageSize() (int, error) {
return s.getInt("pageSize")
}
func (s *SettingService) GetSubURI() (string, error) { func (s *SettingService) GetSubURI() (string, error) {
return s.getString("subURI") return s.getString("subURI")
} }
func (s *SettingService) GetDatepicker() (string, error) { func (s *SettingService) GetPageSize() (int, error) {
return s.getString("datepicker") return s.getInt("pageSize")
}
func (s *SettingService) GetWarp() (string, error) {
return s.getString("warp")
}
func (s *SettingService) SetWarp(data string) error {
return s.setString("warp", data)
} }
func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error { func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error {
@@ -485,7 +463,6 @@ func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) {
"subEnable": func() (interface{}, error) { return s.GetSubEnable() }, "subEnable": func() (interface{}, error) { return s.GetSubEnable() },
"subURI": func() (interface{}, error) { return s.GetSubURI() }, "subURI": func() (interface{}, error) { return s.GetSubURI() },
"remarkModel": func() (interface{}, error) { return s.GetRemarkModel() }, "remarkModel": func() (interface{}, error) { return s.GetRemarkModel() },
"datepicker": func() (interface{}, error) { return s.GetDatepicker() },
} }
result := make(map[string]interface{}) result := make(map[string]interface{})

File diff suppressed because it is too large Load Diff

View File

@@ -185,7 +185,7 @@ func (s *XrayService) RestartXray(isForce bool) error {
return err return err
} }
if s.IsXrayRunning() { if p != nil && p.IsRunning() {
if !isForce && p.GetConfig().Equals(xrayConfig) { if !isForce && p.GetConfig().Equals(xrayConfig) {
logger.Debug("It does not need to restart xray") logger.Debug("It does not need to restart xray")
return nil return nil

View File

@@ -1,13 +1,8 @@
package service package service
import ( import (
"bytes"
_ "embed" _ "embed"
"encoding/json" "encoding/json"
"fmt"
"net/http"
"os"
"time"
"x-ui/util/common" "x-ui/util/common"
"x-ui/xray" "x-ui/xray"
) )
@@ -31,142 +26,3 @@ func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error {
} }
return nil 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 := "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
}

View File

@@ -1,6 +1,6 @@
"username" = "Username" "username" = "Username"
"password" = "Password" "password" = "Password"
"login" = "Log In" "login" = "Login"
"confirm" = "Confirm" "confirm" = "Confirm"
"cancel" = "Cancel" "cancel" = "Cancel"
"close" = "Close" "close" = "Close"
@@ -8,7 +8,7 @@
"copied" = "Copied" "copied" = "Copied"
"download" = "Download" "download" = "Download"
"remark" = "Remark" "remark" = "Remark"
"enable" = "Enabled" "enable" = "Enable"
"protocol" = "Protocol" "protocol" = "Protocol"
"search" = "Search" "search" = "Search"
"filter" = "Filter" "filter" = "Filter"
@@ -26,13 +26,13 @@
"edit" = "Edit" "edit" = "Edit"
"delete" = "Delete" "delete" = "Delete"
"reset" = "Reset" "reset" = "Reset"
"copySuccess" = "Copied Successful" "copySuccess" = "Copied Successfully"
"sure" = "Sure" "sure" = "Sure"
"encryption" = "Encryption" "encryption" = "Encryption"
"transmission" = "Transmission" "transmission" = "Transmission"
"host" = "Host" "host" = "Host"
"path" = "Path" "path" = "Path"
"camouflage" = "Obfuscation" "camouflage" = "Camouflage"
"status" = "Status" "status" = "Status"
"enabled" = "Enabled" "enabled" = "Enabled"
"disabled" = "Disabled" "disabled" = "Disabled"
@@ -41,10 +41,10 @@
"offline" = "Offline" "offline" = "Offline"
"online" = "Online" "online" = "Online"
"domainName" = "Domain Name" "domainName" = "Domain Name"
"monitor" = "Listen IP" "monitor" = "Listening IP"
"certificate" = "Certificate" "certificate" = "Certificate"
"fail" = " Failed" "fail" = "Fail"
"success" = " Successful" "success" = "Success"
"getVersion" = "Get Version" "getVersion" = "Get Version"
"install" = "Install" "install" = "Install"
"clients" = "Clients" "clients" = "Clients"
@@ -54,69 +54,69 @@
"security" = "Security" "security" = "Security"
[menu] [menu]
"dashboard" = "Overview" "dashboard" = "System Status"
"inbounds" = "Inbounds" "inbounds" = "Inbounds"
"settings" = "Panel Settings" "settings" = "Panel Settings"
"xray" = "Xray Configs" "xray" = "Xray Settings"
"logout" = "Log Out" "logout" = "Logout"
"link" = "Manage" "link" = "Management"
[pages.login] [pages.login]
"title" = "Welcome" "title" = "Login"
"loginAgain" = "Your session has expired, please log in again" "loginAgain" = "Your session has expired, please log in again"
[pages.login.toasts] [pages.login.toasts]
"invalidFormData" = "The Input data format is invalid." "invalidFormData" = "Input data format is invalid."
"emptyUsername" = "Username is required" "emptyUsername" = "Please enter username."
"emptyPassword" = "Password is required" "emptyPassword" = "Please enter password."
"wrongUsernameOrPassword" = "Invalid username or password." "wrongUsernameOrPassword" = "Invalid username or password."
"successLogin" = "Login" "successLogin" = "Login"
[pages.index] [pages.index]
"title" = "Overview" "title" = "System Status"
"memory" = "RAM" "memory" = "RAM"
"hard" = "Disk" "hard" = "Disk"
"xrayStatus" = "Status" "xrayStatus" = "Status"
"stopXray" = "Stop" "stopXray" = "Stop"
"restartXray" = "Restart" "restartXray" = "Restart"
"xraySwitch" = "Version" "xraySwitch" = "SwitchV"
"xraySwitchClick" = "Choose the version you want to switch to." "xraySwitchClick" = "Choose the version you want to switch to."
"xraySwitchClickDesk" = "Choose carefully, as older versions may not be compatible with current configurations." "xraySwitchClickDesk" = "Choose wisely, as older versions may not be compatible with current configurations."
"operationHours" = "Uptime" "operationHours" = "Uptime"
"systemLoad" = "System Load" "systemLoad" = "System Load"
"systemLoadDesc" = "System load average for the past 1, 5, and 15 minutes" "systemLoadDesc" = "system load average for the past 1, 5, and 15 minutes"
"connectionTcpCountDesc" = "Total TCP connections across the system" "connectionTcpCountDesc" = "Total TCP connections across all network cards"
"connectionUdpCountDesc" = "Total UDP connections across the system" "connectionUdpCountDesc" = "Total UDP connections across all network cards"
"connectionCount" = "Connection Stats" "connectionCount" = "Number of Connections"
"upSpeed" = "Overall upload speed across the system" "upSpeed" = "Total upload speed for all network cards"
"downSpeed" = "Overall download speed across the system" "downSpeed" = "Total download speed for all network cards"
"totalSent" = "Total data sent across the system since OS startup" "totalSent" = "Total upload data across all network cards since OS startup"
"totalReceive" = "Total data received across the system since OS startup" "totalReceive" = "Total download data across all network cards since OS startup"
"xraySwitchVersionDialog" = "Change Xray Version" "xraySwitchVersionDialog" = "Switch Xray Version"
"xraySwitchVersionDialogDesc" = "Are you sure you want to change the Xray version to" "xraySwitchVersionDialogDesc" = "Are you sure you want to switch the Xray version to"
"dontRefresh" = "Installation is in progress, please do not refresh this page" "dontRefresh" = "Installation is in progress, please do not refresh this page"
"logs" = "Logs" "logs" = "Logs"
"config" = "Config" "config" = "Config"
"backup" = "Backup & Restore" "backup" = "Backup & Restore"
"backupTitle" = "Database Backup & Restore" "backupTitle" = "Backup & Restore Database"
"backupDescription" = "It is recommended to make a backup before restoring a database." "backupDescription" = "It is recommended to backup before importing a new database."
"exportDatabase" = "Back Up" "exportDatabase" = "Download Database"
"importDatabase" = "Restore" "importDatabase" = "Upload Database"
[pages.inbounds] [pages.inbounds]
"title" = "Inbounds" "title" = "Inbounds"
"totalDownUp" = "Total Sent/Received" "totalDownUp" = "Total Uploads/Downloads"
"totalUsage" = "Total Usage" "totalUsage" = "Total Usage"
"inboundCount" = "Total Inbounds" "inboundCount" = "Number of Inbounds"
"operate" = "Menu" "operate" = "Menu"
"enable" = "Enabled" "enable" = "Enable"
"remark" = "Remark" "remark" = "Remark"
"protocol" = "Protocol" "protocol" = "Protocol"
"port" = "Port" "port" = "Port"
"traffic" = "Traffic" "traffic" = "Traffic"
"details" = "Details" "details" = "Details"
"transportConfig" = "Transport" "transportConfig" = "Transport"
"expireDate" = "Duration" "expireDate" = "Expire Date"
"resetTraffic" = "Reset Traffic" "resetTraffic" = "Reset Traffic"
"addInbound" = "Add Inbound" "addInbound" = "Add Inbound"
"generalActions" = "General Actions" "generalActions" = "General Actions"
@@ -124,20 +124,20 @@
"update" = "Update" "update" = "Update"
"modifyInbound" = "Modify Inbound" "modifyInbound" = "Modify Inbound"
"deleteInbound" = "Delete Inbound" "deleteInbound" = "Delete Inbound"
"deleteInboundContent" = "Are you sure you want to delete inbound?" "deleteInboundContent" = "Confirm deletion of inbound?"
"deleteClient" = "Delete Client" "deleteClient" = "Delete Client"
"deleteClientContent" = "Are you sure you want to delete client?" "deleteClientContent" = "Are you sure you want to delete client?"
"resetTrafficContent" = "Are you sure you want to reset traffic?" "resetTrafficContent" = "Confirm traffic reset?"
"copyLink" = "Copy URL" "copyLink" = "Copy Link"
"address" = "Address" "address" = "Address"
"network" = "Network" "network" = "Network"
"destinationPort" = "Destination Port" "destinationPort" = "Destination Port"
"targetAddress" = "Target Address" "targetAddress" = "Target Address"
"monitorDesc" = "Leave blank to listen on all IPs" "monitorDesc" = "Leave blank by default"
"meansNoLimit" = " = Unlimited. (unit: GB)" "meansNoLimit" = "Means no limit"
"totalFlow" = "Total Flow" "totalFlow" = "Total Flow"
"leaveBlankToNeverExpire" = "Leave blank to never expire" "leaveBlankToNeverExpire" = "Leave Blank to Never Expire"
"noRecommendKeepDefault" = "It is recommended to keep the default" "noRecommendKeepDefault" = "No special requirements to maintain default settings"
"certificatePath" = "File Path" "certificatePath" = "File Path"
"certificateContent" = "File Content" "certificateContent" = "File Content"
"publicKeyPath" = "Public Key Path" "publicKeyPath" = "Public Key Path"
@@ -146,39 +146,39 @@
"keyContent" = "Private Key Content" "keyContent" = "Private Key Content"
"clickOnQRcode" = "Click on QR Code to Copy" "clickOnQRcode" = "Click on QR Code to Copy"
"client" = "Client" "client" = "Client"
"export" = "Export All URLs" "export" = "Export Links"
"clone" = "Clone" "clone" = "Clone"
"cloneInbound" = "Clone" "cloneInbound" = "Clone"
"cloneInboundContent" = "All settings of this inbound, except Port, Listening IP, and Clients, will be applied to the clone." "cloneInboundContent" = "All settings of this inbound, except for Port, Listening IP, and Clients, will be applied to the clone."
"cloneInboundOk" = "Clone" "cloneInboundOk" = "Clone"
"resetAllTraffic" = "Reset All Inbounds Traffic" "resetAllTraffic" = "Reset All Inbounds Traffic"
"resetAllTrafficTitle" = "Reset All Inbounds Traffic" "resetAllTrafficTitle" = "Reset All Inbounds Traffic"
"resetAllTrafficContent" = "Are you sure you want to reset the traffic of all inbounds?" "resetAllTrafficContent" = "Are you sure you want to reset all inbounds traffic?"
"resetInboundClientTraffics" = "Reset Clients Traffic" "resetInboundClientTraffics" = "Reset Clients Traffic"
"resetInboundClientTrafficTitle" = "Reset Clients Traffic" "resetInboundClientTrafficTitle" = "Reset Clients Traffic"
"resetInboundClientTrafficContent" = "Are you sure you want to reset the traffic of this inbound's clients?" "resetInboundClientTrafficContent" = "Are you sure you want to reset all traffic for this inbound's clients?"
"resetAllClientTraffics" = "Reset All Clients Traffic" "resetAllClientTraffics" = "Reset All Clients Traffic"
"resetAllClientTrafficTitle" = "Reset All Clients Traffic" "resetAllClientTrafficTitle" = "Reset all clients traffic"
"resetAllClientTrafficContent" = "Are you sure you want to reset the traffic of all clients?" "resetAllClientTrafficContent" = "Are you sure you want to reset all traffics for all clients?"
"delDepletedClients" = "Delete Depleted Clients" "delDepletedClients" = "Delete Depleted Clients"
"delDepletedClientsTitle" = "Delete Depleted Clients" "delDepletedClientsTitle" = "Delete Depleted Clients"
"delDepletedClientsContent" = "Are you sure you want to delete all the depleted clients?" "delDepletedClientsContent" = "Are you sure you want to delete all depleted clients?"
"email" = "Email" "email" = "Email"
"emailDesc" = "Please provide a unique email address." "emailDesc" = "Please provide a unique email address."
"IPLimit" = "IP Limit" "IPLimit" = "IP Limit"
"IPLimitDesc" = "Disables inbound if the count exceeds the set value. (0 = disable)" "IPLimitDesc" = "Disable inbound if the count exceeds the entered value (enter 0 to disable IP limit)."
"IPLimitlog" = "IP Log" "IPLimitlog" = "IP Log"
"IPLimitlogDesc" = "The IPs history log. (to enable inbound after disabling, clear the log)" "IPLimitlogDesc" = "IPs history log (before enabling inbound after it has been disabled by IP limit, you should clear the log)."
"IPLimitlogclear" = "Clear The Log" "IPLimitlogclear" = "Clear The Log"
"setDefaultCert" = "Set Cert from Panel" "setDefaultCert" = "Set Cert from Panel"
"xtlsDesc" = "Xray must be v1.7.5" "xtlsDesc" = "Xray core needs to be 1.7.5"
"realityDesc" = "Xray must be v1.8.0+" "realityDesc" = "Xray core needs to be 1.8.0 or higher."
"telegramDesc" = "Please provide Telegram or chat ID(s) without using the '@'. (get it here @userinfobot) or (use '/id' command in the bot)" "telegramDesc" = "use Telegram ID without @ or chat IDs ( you can get it here @userinfobot or use '/id' command in bot )"
"subscriptionDesc" = "To find your subscription URL, navigate to the 'Details'. Additionally, you can use the same name for several clients." "subscriptionDesc" = "you can find your sub link on Details, also you can use the same name for several configurations"
"info" = "Info" "info" = "Info"
"same" = "Same" "same" = "Same"
"inboundData" = "Inbound's Data" "inboundData" = "Inbound's Data"
"exportInbound" = "Export Inbound" "copyToClipboard" = "Copy to Clipboard"
"import" = "Import" "import" = "Import"
"importInbound" = "Import an Inbound" "importInbound" = "Import an Inbound"
@@ -194,201 +194,193 @@
"last" = "Last" "last" = "Last"
"prefix" = "Prefix" "prefix" = "Prefix"
"postfix" = "Postfix" "postfix" = "Postfix"
"delayedStart" = "Start on Initial Use" "delayedStart" = "Start After First Use"
"expireDays" = "Duration" "expireDays" = "Expire Days"
"days" = "Day(s)" "days" = "Day(s)"
"renew" = "Auto Renew" "renew" = "Auto Renew"
"renewDesc" = "Auto-renewal after expiration. (0 = disable)(unit: day)" "renewDesc" = "Auto renew days after expiration. 0 = disable"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Obtain" "obtain" = "Obtain"
[pages.inbounds.stream.general] [pages.inbounds.stream.general]
"request" = "Request" "requestHeader" = "Request Header"
"response" = "Response"
"name" = "Name" "name" = "Name"
"value" = "Value" "value" = "Value"
[pages.inbounds.stream.tcp] [pages.inbounds.stream.tcp]
"version" = "Version" "requestVersion" = "Request Version"
"method" = "Method" "requestMethod" = "Request Method"
"path" = "Path" "requestPath" = "Request Path"
"status" = "Status" "responseVersion" = "Response Version"
"statusDescription" = "Status Desc" "responseStatus" = "Response Status"
"requestHeader" = "Request Header" "responseStatusDescription" = "Response Status Description"
"responseHeader" = "Response Header" "responseHeader" = "Response Header"
[pages.inbounds.stream.quic] [pages.inbounds.stream.quic]
"encryption" = "Encryption" "encryption" = "Encryption"
[pages.settings] [pages.settings]
"title" = "Panel Settings" "title" = "Settings"
"save" = "Save" "save" = "Save"
"infoDesc" = "Every change made here needs to be saved. Please restart the panel to apply changes." "infoDesc" = "Every change made here needs to be saved. Please restart the panel to apply changes."
"restartPanel" = "Restart Panel" "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." "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 info on the server."
"actions" = "Actions" "actions" = "Actions"
"resetDefaultConfig" = "Reset to Default" "resetDefaultConfig" = "Reset to Default Configuration"
"panelSettings" = "General" "panelSettings" = "Panel Settings"
"securitySettings" = "Authentication" "securitySettings" = "Security Settings"
"TGBotSettings" = "Telegram Bot" "TGBotSettings" = "Telegram Bot Settings"
"panelListeningIP" = "Listen IP" "panelListeningIP" = "Panel Listening IP"
"panelListeningIPDesc" = "The IP address for the web panel. (leave blank to listen on all IPs)" "panelListeningIPDesc" = "Leave blank by default to monitor all IPs."
"panelListeningDomain" = "Listen Domain" "panelListeningDomain" = "Panel Listening Domain"
"panelListeningDomainDesc" = "The domain name for the web panel. (leave blank to listen on all domains and IPs)" "panelListeningDomainDesc" = "Leave blank by default to monitor all domains and IPs."
"panelPort" = "Listen Port" "panelPort" = "Panel Port"
"panelPortDesc" = "The port number for the web panel. (must be an unused port)" "panelPortDesc" = "Port number for serving the panel."
"publicKeyPath" = "Public Key Path" "publicKeyPath" = "Panel Certificate Public Key Path"
"publicKeyPathDesc" = "The public key file path for the web panel. (begins with /)" "publicKeyPathDesc" = "Fill in an absolute path starting with."
"privateKeyPath" = "Private Key Path" "privateKeyPath" = "Panel Certificate Private Key Path"
"privateKeyPathDesc" = "The private key file path for the web panel. (begins with /)" "privateKeyPathDesc" = "Fill in an absolute path starting with."
"panelUrlPath" = "URI Path" "panelUrlPath" = "Panel URL Root Path"
"panelUrlPathDesc" = "The URI path for the web panel. (begins with / and concludes with /)" "panelUrlPathDesc" = "Must start with '/' and end with."
"pageSize" = "Pagination Size" "pageSize" = "Pagination Size"
"pageSizeDesc" = "Define page size for inbounds table. (0 = disable)" "pageSizeDesc" = "Define page size for inbounds table. Set 0 to disable"
"remarkModel" = "Remark Model & Separation Character" "remarkModel" = "Remark Model and Seperation Charachter"
"datepicker" = "Calendar Type"
"datepickerPlaceholder" = "Select date"
"datepickerDescription" = "Scheduled tasks will run based on this calendar."
"sampleRemark" = "Sample Remark" "sampleRemark" = "Sample Remark"
"oldUsername" = "Current Username" "oldUsername" = "Current Username"
"currentPassword" = "Current Password" "currentPassword" = "Current Password"
"newUsername" = "New Username" "newUsername" = "New Username"
"newPassword" = "New Password" "newPassword" = "New Password"
"telegramBotEnable" = "Enable Telegram Bot" "telegramBotEnable" = "Enable Telegram Bot"
"telegramBotEnableDesc" = "Enables the Telegram bot." "telegramBotEnableDesc" = "Connect to the features of this panel through the Telegram bot."
"telegramToken" = "Telegram Token" "telegramToken" = "Telegram Token"
"telegramTokenDesc" = "The Telegram bot token obtained from '@BotFather'." "telegramTokenDesc" = "The token you have got from @BotFather."
"telegramProxy" = "SOCKS Proxy" "telegramChatId" = "Telegram Admin Chat IDs"
"telegramProxyDesc" = "Enables SOCKS5 proxy for connecting to Telegram. (adjust settings as per guide)" "telegramChatIdDesc" = "Multiple chat IDs separated by comma. use @userinfobot or use '/id' command in bot to get your Chat IDs."
"telegramChatId" = "Admin Chat ID" "telegramNotifyTime" = "Telegram bot notification time"
"telegramChatIdDesc" = "The Telegram Admin Chat ID(s). (comma-separated)(get it here @userinfobot) or (use '/id' command in the bot)" "telegramNotifyTimeDesc" = "Use crontab timing format."
"telegramNotifyTime" = "Notification Time"
"telegramNotifyTimeDesc" = "The Telegram bot notification time set for periodic reports. (use the crontab time format)"
"tgNotifyBackup" = "Database Backup" "tgNotifyBackup" = "Database Backup"
"tgNotifyBackupDesc" = "Send a database backup file with a report." "tgNotifyBackupDesc" = "Include database backup file with report notification."
"tgNotifyLogin" = "Login Notification" "tgNotifyLogin" = "Login Notification"
"tgNotifyLoginDesc" = "Get notified about the username, IP address, and time whenever someone attempts to log into your web panel." "tgNotifyLoginDesc" = "Displays the username, IP address, and time when someone tries to log into your panel."
"sessionMaxAge" = "Session Duration" "sessionMaxAge" = "Session Duration"
"sessionMaxAgeDesc" = "The duration for which you can stay logged in. (unit: minute)" "sessionMaxAgeDesc" = "The duration of a login session. (unit: minute)"
"expireTimeDiff" = "Expiration Date Notification" "expireTimeDiff" = "Client Expiration Threshold Notification"
"expireTimeDiffDesc" = "Get notified about expiration date when reaching this threshold. (unit: day)" "expireTimeDiffDesc" = "Get notified about client expiration before the threshold. (unit: day)"
"trafficDiff" = "Traffic Cap Notification" "trafficDiff" = "Traffic Limit Threshold Notification"
"trafficDiffDesc" = "Get notified about traffic cap when reaching this threshold. (unit: GB)" "trafficDiffDesc" = "Get notified about traffic exhaustion before reaching the threshold. (unit: GB)"
"tgNotifyCpu" = "CPU Load Notification" "tgNotifyCpu" = "CPU Load Threshold Notification"
"tgNotifyCpuDesc" = "Get notified if CPU load exceeds this threshold. (unit: %)" "tgNotifyCpuDesc" = "Get notified if CPU usage exceeds this threshold. (unit: %)"
"timeZone" = "Time Zone" "timeZone" = "Time zone"
"timeZoneDesc" = "Scheduled tasks will run based on this time zone." "timeZoneDesc" = "Scheduled tasks run according to the time in this time zone."
"subSettings" = "Subscription" "subSettings" = "Subscription"
"subEnable" = "Enable Subscription Service" "subEnable" = "Enable Service"
"subEnableDesc" = "Enables the subscription service." "subEnableDesc" = "Subscription feature with separate configuration."
"subListen" = "Listen IP" "subListen" = "Listening IP"
"subListenDesc" = "The IP address for the subscription service. (leave blank to listen on all IPs)" "subListenDesc" = "Leave blank by default to monitor all IPs."
"subPort" = "Listen Port" "subPort" = "Subscription Port"
"subPortDesc" = "The port number for the subscription service. (must be an unused port)" "subPortDesc" = "Port number for serving the subscription service. Must be unused in server."
"subCertPath" = "Public Key Path" "subCertPath" = "Subscription Certificate Public Key Path"
"subCertPathDesc" = "The public key file path for the subscription service. (begins with /)" "subCertPathDesc" = "Fill in an absolute path starting with '/'"
"subKeyPath" = "Private Key Path" "subKeyPath" = "Subscription Certificate Private Key Path"
"subKeyPathDesc" = "The private key file path for the subscription service. (begins with /)" "subKeyPathDesc" = "Fill in an absolute path starting with '/'"
"subPath" = "URI Path" "subPath" = "Subscription URL Root Path"
"subPathDesc" = "The URI path for the subscription service. (begins with / and concludes with /)" "subPathDesc" = "Must start with '/' and end with '/'"
"subDomain" = "Listen Domain" "subDomain" = "Listening Domain"
"subDomainDesc" = "The domain name for the subscription service. (leave blank to listen on all domains and IPs)" "subDomainDesc" = "Leave blank by default to monitor all domains and IPs."
"subUpdates" = "Update Intervals" "subUpdates" = "Subscription update intervals"
"subUpdatesDesc" = "The update intervals of the subscription URL in the client apps. (unit: hour)" "subUpdatesDesc" = "Interval hours between updates in client application."
"subEncrypt" = "Encode" "subEncrypt" = "Encode Configs"
"subEncryptDesc" = "The returned content of subscription service will be Base64 encoded." "subEncryptDesc" = "Encode the returned configs in subscription."
"subShowInfo" = "Show Usage Info" "subShowInfo" = "Show Usage Info"
"subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps." "subShowInfoDesc" = "Show remained traffic and date after config name."
"subURI" = "Reverse Proxy URI" "subURI" = "Reverse Proxy URI"
"subURIDesc" = "The URI path of the subscription URL for use behind proxies." "subURIDesc" = "Change base URI of subscription URL for using on behind of proxies."
[pages.xray] [pages.xray]
"title" = "Xray Configs" "title" = "Xray Settings"
"save" = "Save" "save" = "Save Settings"
"restart" = "Restart Xray" "restart" = "Restart Xray"
"basicTemplate" = "Basics" "basicTemplate" = "Basic Template"
"advancedTemplate" = "Advanced" "advancedTemplate" = "Advanced Template"
"generalConfigs" = "General Strategy" "generalConfigs" = "General Configs"
"generalConfigsDesc" = "These options will determine general strategy adjustments." "generalConfigsDesc" = "These options will provide general adjustments."
"blockConfigs" = "Protection Shield" "blockConfigs" = "Blocking Configs"
"blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites." "blockConfigsDesc" = "These options will prevent users from connecting to specific protocols and websites."
"blockCountryConfigs" = "Block Country" "blockCountryConfigs" = "Block Country Configs"
"blockCountryConfigsDesc" = "These options will block traffic based on the specific requested country." "blockCountryConfigsDesc" = "These options will prevent users from connecting to specific country domains."
"directCountryConfigs" = "Direct Country" "directCountryConfigs" = "Direct Country Configs"
"directCountryConfigsDesc" = "These options will directly forward traffic based on the specific requested country." "directCountryConfigsDesc" = "These options will connect users directly to specific country domains."
"ipv4Configs" = "IPv4 Routing" "ipv4Configs" = "IPv4 Configs"
"ipv4ConfigsDesc" = "These options will route traffic based on a specific destination via IPv4." "ipv4ConfigsDesc" = "These options will route to target domains only via IPv4."
"warpConfigs" = "WARP Routing" "warpConfigs" = "WARP Configs"
"warpConfigsDesc" = "These options will route traffic based on a specific destination via WARP." "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" = "Advanced Xray Configuration Template" "Template" = "Xray Configuration Template"
"TemplateDesc" = "The final Xray config file will be generated based on this template." "TemplateDesc" = "Generate the final Xray configuration file based on this template."
"FreedomStrategy" = "Freedom Protocol Strategy" "FreedomStrategy" = "Configure Strategy for Freedom Protocol"
"FreedomStrategyDesc" = "Set the output strategy for the network in the Freedom Protocol." "FreedomStrategyDesc" = "Set the output strategy of the network in the Freedom Protocol."
"RoutingStrategy" = "Overall Routing Strategy" "RoutingStrategy" = "Configure Domains Routing Strategy"
"RoutingStrategyDesc" = "Set the overall traffic routing strategy for resolving all requests." "RoutingStrategyDesc" = "Set the overall routing strategy for DNS resolving."
"Torrent" = "Block BitTorrent Protocol" "Torrent" = "Ban BitTorrent Protocol"
"TorrentDesc" = "Blocks BitTorrent protocol." "TorrentDesc" = "Change the configuration template to avoid using BitTorrent protocol."
"PrivateIp" = "Block Connection to Private IPs" "PrivateIp" = "Ban Private IPs to Connect"
"PrivateIpDesc" = "Blocks establishing connections to private IP ranges." "PrivateIpDesc" = "Change the configuration template to avoid connecting to private IP ranges."
"Ads" = "Block Ads" "Ads" = "Block Ads"
"AdsDesc" = "Blocks advertising websites." "AdsDesc" = "Change the configuration template to block ads."
"Family" = "Family Protection" "Family" = "Block Malware and Adult Content"
"FamilyDesc" = "Blocks adult content, and malware websites." "FamilyDesc" = "DNS resolvers to block malware and adult content for family protection."
"Security" = "Security Shield" "Speedtest" = "Block Speedtest Websites"
"SecurityDesc" = "Blocks malware, phishing, and cryptominers websites." "SpeedtestDesc" = "Change the configuration template to avoid connecting to speedtest websites."
"Speedtest" = "Block Speedtest" "IRIp" = "Disable Connection to Iran IPs"
"SpeedtestDesc" = "Blocks establishing connectins to speedtest websites." "IRIpDesc" = "Change the configuration template to avoid connecting to Iran IP ranges."
"IRIp" = "Block Connection to Iran IPs" "IRDomain" = "Disable Connection to Iran Domains"
"IRIpDesc" = "Blocks establishing connections to Iran IP ranges." "IRDomainDesc" = "Change the configuration template to avoid connecting to Iran domains."
"IRDomain" = "Block Connection to Iran Domains" "ChinaIp" = "Disable Connection to China IPs"
"IRDomainDesc" = "Blocks establishing connections to Iran domains." "ChinaIpDesc" = "Change the configuration template to avoid connecting to China IP ranges."
"ChinaIp" = "Block Connection to China IPs" "ChinaDomain" = "Disable Connection to China Domains"
"ChinaIpDesc" = "Blocks establishing connections to China IP ranges." "ChinaDomainDesc" = "Change the configuration template to avoid connecting to China domains."
"ChinaDomain" = "Block Connection to China Domains" "RussiaIp" = "Disable Connection to Russia IPs"
"ChinaDomainDesc" = "Blocks establishing connections to China domains." "RussiaIpDesc" = "Change the configuration template to avoid connecting to Russia IP ranges."
"RussiaIp" = "Block Connection to Russia IPs" "RussiaDomain" = "Disable Connection to Russia Domains"
"RussiaIpDesc" = "Blocks establishing connections to Russia IP ranges." "RussiaDomainDesc" = "Change the configuration template to avoid connecting to Russia domains."
"RussiaDomain" = "Block Connection to Russia Domains" "VNIp" = "Disable Connection to Vietnam IPs"
"RussiaDomainDesc" = "Blocks establishing connections to Russia domains." "VNIpDesc" = "Change the configuration template to avoid connecting to Vietnam IP ranges."
"VNIp" = "Block Connection to Vietnam IPs" "VNDomain" = "Disable Connection to Vietnam Domains"
"VNIpDesc" = "Blocks establishing connections to Vietnam IP ranges." "VNDomainDesc" = "Change the configuration template to avoid connecting to Vietnam domains."
"VNDomain" = "Block Connection to Vietnam Domains"
"VNDomainDesc" = "Blocks establishing connections to Vietnam domains."
"DirectIRIp" = "Direct Connection to Iran IPs" "DirectIRIp" = "Direct Connection to Iran IPs"
"DirectIRIpDesc" = "Directly establishes connections to Iran IP ranges." "DirectIRIpDesc" = "Change the configuration template for direct connecting to Iran IP ranges."
"DirectIRDomain" = "Direct Connection to Iran Domains" "DirectIRDomain" = "Direct Connection to Iran Domains"
"DirectIRDomainDesc" = "Directly establishes connections to Iran domains." "DirectIRDomainDesc" = "Change the configuration template for direct connecting to Iran domains."
"DirectChinaIp" = "Direct Connection to China IPs" "DirectChinaIp" = "Direct Connection to China IPs"
"DirectChinaIpDesc" = "Directly establishes connections to China IP ranges." "DirectChinaIpDesc" = "Change the configuration template for direct connecting to China IP ranges."
"DirectChinaDomain" = "Direct Connection to China Domains" "DirectChinaDomain" = "Direct Connection to China Domains"
"DirectChinaDomainDesc" = "Directly establishes connections to China domains." "DirectChinaDomainDesc" = "Change the configuration template for direct connecting to China domains."
"DirectRussiaIp" = "Direct Connection to Russia IPs" "DirectRussiaIp" = "Direct Connection to Russia IPs"
"DirectRussiaIpDesc" = "Directly establishes connections to Russia IP ranges." "DirectRussiaIpDesc" = "Change the configuration template for direct connecting to Russia IP ranges."
"DirectRussiaDomain" = "Direct Connection to Russia Domains" "DirectRussiaDomain" = "Direct Connection to Russia Domains"
"DirectRussiaDomainDesc" = "Directly establishes connections to Russia domains." "DirectRussiaDomainDesc" = "Change the configuration template for direct connecting to Russia domains."
"DirectVNIp" = "Direct Connection to Vietnam IPs" "DirectVNIp" = "Direct Connection to Vietnam IPs"
"DirectVNIpDesc" = "Directly establishes connections to Vietnam IP ranges." "DirectVNIpDesc" = "Change the configuration template for direct connecting to Vietnam IP ranges."
"DirectVNDomain" = "Direct Connection to Vietnam Domains" "DirectVNDomain" = "Direct Connection to Vietnam Domains"
"DirectVNDomainDesc" = "Directly establishes connections to Vietnam domains." "DirectVNDomainDesc" = "Change the configuration template for direct connecting to Vietnam domains."
"GoogleIPv4" = "Google" "GoogleIPv4" = "Use IPv4 for Google"
"GoogleIPv4Desc" = "Routes traffic to Google via IPv4." "GoogleIPv4Desc" = "Add routing for Google to connect with IPv4."
"NetflixIPv4" = "Netflix" "NetflixIPv4" = "Use IPv4 for Netflix"
"NetflixIPv4Desc" = "Routes traffic to Netflix via IPv4." "NetflixIPv4Desc" = "Add routing for Netflix to connect with IPv4."
"GoogleWARP" = "Google" "GoogleWARP" = "Route Google through WARP."
"GoogleWARPDesc" = "Add routing for Google via WARP." "GoogleWARPDesc" = "Add routing for Google via WARP."
"OpenAIWARP" = "ChatGPT" "OpenAIWARP" = "Route OpenAI (ChatGPT) through WARP."
"OpenAIWARPDesc" = "Routes traffic to ChatGPT via WARP." "OpenAIWARPDesc" = "Add routing for OpenAI (ChatGPT) via WARP."
"NetflixWARP" = "Netflix" "NetflixWARP" = "Route Netflix through WARP."
"NetflixWARPDesc" = "Routes traffic to Netflix via WARP." "NetflixWARPDesc" = "Add routing for Netflix via WARP."
"SpotifyWARP" = "Spotify" "SpotifyWARP" = "Route Spotify through WARP."
"SpotifyWARPDesc" = "Routes traffic to Spotify via WARP." "SpotifyWARPDesc" = "Add routing for Spotify via WARP."
"IRWARP" = "Iran domains" "IRWARP" = "Route Iran domains through WARP."
"IRWARPDesc" = "Routes traffic to Iran domains via WARP." "IRWARPDesc" = "Add routing for Iran domains via WARP."
"Inbounds" = "Inbounds" "Inbounds" = "Inbounds"
"InboundsDesc" = "Accepting the specific clients." "InboundsDesc" = "Change the configuration template to accept specific clients."
"Outbounds" = "Outbounds" "Outbounds" = "Outbounds"
"OutboundsDesc" = "Set the outgoing traffic pathway." "OutboundsDesc" = "Change the configuration template to define outgoing ways for this server."
"Routings" = "Routing Rules" "Routings" = "Routing Rules"
"RoutingsDesc" = "The priority of each rule is important!" "RoutingsDesc" = "The priority of each rule is important!"
"completeTemplate" = "All" "completeTemplate" = "All"
@@ -405,7 +397,7 @@
"info" = "Info" "info" = "Info"
"add" = "Add Rule" "add" = "Add Rule"
"edit" = "Edit Rule" "edit" = "Edit Rule"
"useComma" = "Comma-separated items" "useComma" = "Comma separated items"
[pages.xray.outbound] [pages.xray.outbound]
"addOutbound" = "Add Outbound" "addOutbound" = "Add Outbound"
@@ -422,28 +414,20 @@
"portal" = "Portal" "portal" = "Portal"
"intercon" = "Interconnection" "intercon" = "Interconnection"
[pages.xray.wireguard]
"secretKey" = "Secret Key"
"publicKey" = "Public Key"
"allowedIPs" = "Allowed IPs"
"endpoint" = "Endpoint"
"psk" = "PreShared Key"
"domainStrategy" = "Domain Strategy"
[pages.settings.security] [pages.settings.security]
"admin" = "Admin" "admin" = "Admin"
"secret" = "Secret Token" "secret" = "Secret Token"
"loginSecurity" = "Secure Login" "loginSecurity" = "Login security"
"loginSecurityDesc" = "Adds an additional layer of authentication to provide more security." "loginSecurityDesc" = "Enable additional user login security step"
"secretToken" = "Secret Token" "secretToken" = "Secret Token"
"secretTokenDesc" = "Please securely store this token in a safe place. This token is required for login and cannot be recovered." "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."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Modify Settings" "modifySettings" = "Modify Settings"
"getSettings" = "Get Settings" "getSettings" = "Get Settings"
"modifyUser" = "Modify Admin" "modifyUser" = "Modify User"
"originalUserPassIncorrect" = "The Current username or password is invalid" "originalUserPassIncorrect" = "Incorrect original username or password"
"userPassMustBeNotEmpty" = "The new username and password is empty" "userPassMustBeNotEmpty" = "New username and password cannot be empty"
[tgbot] [tgbot]
"keyboardClosed" = "❌ Custom keyboard closed!" "keyboardClosed" = "❌ Custom keyboard closed!"
@@ -453,7 +437,6 @@
"noIpRecord" = "❗ No IP Record!" "noIpRecord" = "❗ No IP Record!"
"noInbounds" = "❗ No inbound found!" "noInbounds" = "❗ No inbound found!"
"unlimited" = "♾ Unlimited" "unlimited" = "♾ Unlimited"
"add" = "Add"
"month" = "Month" "month" = "Month"
"months" = "Months" "months" = "Months"
"day" = "Day" "day" = "Day"
@@ -462,51 +445,48 @@
"unknown" = "Unknown" "unknown" = "Unknown"
"inbounds" = "Inbounds" "inbounds" = "Inbounds"
"clients" = "Clients" "clients" = "Clients"
"offline" = "🔴 Offline"
"online" = "🟢 Online"
[tgbot.commands] [tgbot.commands]
"unknown" = "❗ Unknown command." "unknown" = "❗ Unknown command"
"pleaseChoose" = "👇 Please choose:\r\n" "pleaseChoose" = "👇 Please choose:\r\n"
"help" = "🤖 Welcome to this bot! It's designed to offer specific data from the web panel and allows you to make modifications as needed.\r\n\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"
"start" = "👋 Hello <i>{{ .Firstname }}</i>.\r\n" "start" = "👋 Hello <i>{{ .Firstname }}</i>.\r\n"
"welcome" = "🤖 Welcome to <b>{{ .Hostname }}</b> management bot.\r\n" "welcome" = "🤖 Welcome to <b>{{ .Hostname }}</b> management bot.\r\n"
"status" = "✅ Bot is OK!" "status" = "✅ Bot is OK!"
"usage" = "❗ Please provide a text to search!" "usage" = "❗ Please provide a text to search!"
"getID" = "🆔 Your ID: <code>{{ .ID }}</code>" "getID" = "🆔 Your ID: <code>{{ .ID }}</code>"
"helpAdminCommands" = "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>" "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, use the following command:\r\n\r\n<code>/usage [Email]</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."
[tgbot.messages] [tgbot.messages]
"cpuThreshold" = "🔴 CPU Load {{ .Percent }}% exceeds the threshold of {{ .Threshold }}%" "cpuThreshold" = "🔴 CPU Load {{ .Percent }}% is more than threshold {{ .Threshold }}%"
"selectUserFailed" = "❌ Error in user selection!" "selectUserFailed" = "❌ Error in user selection!"
"userSaved" = "✅ Telegram User saved." "userSaved" = "✅ Telegram User saved."
"loginSuccess" = "✅ Logged in to the panel successfully.\r\n" "loginSuccess" = "✅ Successfully logged-in to the panel.\r\n"
"loginFailed" = "❗️ Log in to the panel failed.\r\n" "loginFailed" = "❗️ Login to the panel failed.\r\n"
"report" = "🕰 Scheduled Reports: {{ .RunTime }}\r\n" "report" = "🕰 Scheduled Reports: {{ .RunTime }}\r\n"
"datetime" = "⏰ Date&Time: {{ .DateTime }}\r\n" "datetime" = "⏰ Date-Time: {{ .DateTime }}\r\n"
"hostname" = "💻 Host: {{ .Hostname }}\r\n" "hostname" = "💻 Hostname: {{ .Hostname }}\r\n"
"version" = "🚀 3X-UI Version: {{ .Version }}\r\n" "version" = "🚀 X-UI Version: {{ .Version }}\r\n"
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n" "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n" "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
"ip" = "🌐 IP: {{ .IP }}\r\n" "ip" = "🌐 IP: {{ .IP }}\r\n"
"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n" "ips" = "🔢 IPs: \r\n{{ .IPs }}\r\n"
"serverUpTime" = "⏳ Uptime: {{ .UpTime }} {{ .Unit }}\r\n" "serverUpTime" = "⏳ Server Uptime: {{ .UpTime }} {{ .Unit }}\r\n"
"serverLoad" = "📈 System Load: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n" "serverLoad" = "📈 Server Load: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n" "serverMemory" = "📋 Server RAM: {{ .Current }}/{{ .Total }}\r\n"
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n" "tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
"udpCount" = "🔸 UDP: {{ .Count }}\r\n" "udpCount" = "🔸 UDP: {{ .Count }}\r\n"
"traffic" = "🚦 Traffic: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n" "traffic" = "🚦 Traffic: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
"xrayStatus" = " Status: {{ .State }}\r\n" "xrayStatus" = " Xray Status: {{ .State }}\r\n"
"username" = "👤 Username: {{ .Username }}\r\n" "username" = "👤 Username: {{ .Username }}\r\n"
"time" = "⏰ Time: {{ .Time }}\r\n" "time" = "⏰ Time: {{ .Time }}\r\n"
"inbound" = "📍 Inbound: {{ .Remark }}\r\n" "inbound" = "📍 Inbound: {{ .Remark }}\r\n"
"port" = "🔌 Port: {{ .Port }}\r\n" "port" = "🔌 Port: {{ .Port }}\r\n"
"expire" = "📅 Expire Date: {{ .Time }}\r\n" "expire" = "📅 Expire Date: {{ .Time }}\r\n"
"expireIn" = "📅 Expire In: {{ .Time }}\r\n" "expireIn" = "📅 Expire In: {{ .Time }}\r\n"
"active" = "💡 Active: {{ .Enable }}\r\n" "active" = "💡 Active: ✅ Yes\r\n"
"enabled" = "🚨 Enabled: {{ .Enable }}\r\n" "inactive" = "💡 Active: ❌ No\r\n"
"online" = "🌐 Connection status: {{ .Status }}\r\n"
"email" = "📧 Email: {{ .Email }}\r\n" "email" = "📧 Email: {{ .Email }}\r\n"
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n" "upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
"download" = "🔽 Download: ↓{{ .Download }}\r\n" "download" = "🔽 Download: ↓{{ .Download }}\r\n"
@@ -514,13 +494,10 @@
"TGUser" = "👤 Telegram User: {{ .TelegramID }}\r\n" "TGUser" = "👤 Telegram User: {{ .TelegramID }}\r\n"
"exhaustedMsg" = "🚨 Exhausted {{ .Type }}:\r\n" "exhaustedMsg" = "🚨 Exhausted {{ .Type }}:\r\n"
"exhaustedCount" = "🚨 Exhausted {{ .Type }} count:\r\n" "exhaustedCount" = "🚨 Exhausted {{ .Type }} count:\r\n"
"onlinesCount" = "🌐 Online Clients: {{ .Count }}\r\n"
"disabled" = "🛑 Disabled: {{ .Disabled }}\r\n" "disabled" = "🛑 Disabled: {{ .Disabled }}\r\n"
"depleteSoon" = "🔜 Deplete Soon: {{ .Deplete }}\r\n\r\n" "depleteSoon" = "🔜 Deplete Soon: {{ .Deplete }}\r\n \r\n"
"backupTime" = "🗄 Backup Time: {{ .Time }}\r\n" "backupTime" = "🗄 Backup Time: {{ .Time }}\r\n"
"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] [tgbot.buttons]
"closeKeyboard" = "❌ Close Keyboard" "closeKeyboard" = "❌ Close Keyboard"
@@ -530,47 +507,44 @@
"confirmResetTraffic" = "✅ Confirm Reset Traffic?" "confirmResetTraffic" = "✅ Confirm Reset Traffic?"
"confirmClearIps" = "✅ Confirm Clear IPs?" "confirmClearIps" = "✅ Confirm Clear IPs?"
"confirmRemoveTGUser" = "✅ Confirm Remove Telegram User?" "confirmRemoveTGUser" = "✅ Confirm Remove Telegram User?"
"confirmToggle" = "✅ Confirm Enable/Disable User?"
"dbBackup" = "Get DB Backup" "dbBackup" = "Get DB Backup"
"serverUsage" = "Server Usage" "serverUsage" = "Server Usage"
"getInbounds" = "Get Inbounds" "getInbounds" = "Get Inbounds"
"depleteSoon" = "Deplete Soon" "depleteSoon" = "Deplete soon"
"clientUsage" = "Get Usage" "clientUsage" = "Get Usage"
"onlines" = "Online Clients"
"commands" = "Commands" "commands" = "Commands"
"refresh" = "🔄 Refresh" "refresh" = "🔄 Refresh"
"clearIPs" = "❌ Clear IPs" "clearIPs" = "❌ Clear IPs"
"removeTGUser" = "❌ Remove Telegram User" "removeTGUser" = "❌ Remove Telegram User"
"selectTGUser" = "👤 Select Telegram User" "selectTGUser" = "👤 Select Telegram User"
"selectOneTGUser" = "👤 Select a Telegram User:" "selectOneTGUser" = "👤 Select a telegram user:"
"resetTraffic" = "📈 Reset Traffic" "resetTraffic" = "📈 Reset Traffic"
"resetExpire" = "📅 Change Expiry Date" "resetExpire" = "📅 Reset Expire Days"
"ipLog" = "🔢 IP Log" "ipLog" = "🔢 IP Log"
"ipLimit" = "🔢 IP Limit" "ipLimit" = "🔢 IP Limit"
"setTGUser" = "👤 Set Telegram User" "setTGUser" = "👤 Set Telegram User"
"toggle" = "🔘 Enable / Disable" "toggle" = "🔘 Enable / Disable"
"custom" = "🔢 Custom" "custom" = "🔢 Custom"
"confirmNumber" = "✅ Confirm: {{ .Num }}" "confirmNumber" = "✅ Confirm : {{ .Num }}"
"confirmNumberAdd" = "✅ Confirm adding: {{ .Num }}"
"limitTraffic" = "🚧 Traffic Limit" "limitTraffic" = "🚧 Traffic Limit"
"getBanLogs" = "Get Ban Logs"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ Operation successful!" "successfulOperation" = "✅ Successful!"
"errorOperation" = "❗ Error in operation." "errorOperation" = "❗ Error in Operation."
"getInboundsFailed" = "❌ Failed to get inbounds." "getInboundsFailed" = "❌ Failed to get inbounds"
"canceled" = "❌ {{ .Email }}: Operation canceled." "canceled" = "❌ {{ .Email }} : Operation canceled."
"clientRefreshSuccess" = "✅ {{ .Email }}: Client refreshed successfully." "clientRefreshSuccess" = "✅ {{ .Email }} : Client refreshed successfully."
"IpRefreshSuccess" = "✅ {{ .Email }}: IPs refreshed successfully." "IpRefreshSuccess" = "✅ {{ .Email }} : IPs refreshed successfully."
"TGIdRefreshSuccess" = "✅ {{ .Email }}: Client's Telegram User refreshed successfully." "TGIdRefreshSuccess" = "✅ {{ .Email }} : Client's Telegram User refreshed successfully."
"resetTrafficSuccess" = "✅ {{ .Email }}: Traffic reset successfully." "resetTrafficSuccess" = "✅ {{ .Email }} : Traffic reset successfully."
"setTrafficLimitSuccess" = "✅ {{ .Email }}: Traffic limit saved successfully." "setTrafficLimitSuccess" = "✅ {{ .Email }} : Traffic limit saved successfully."
"expireResetSuccess" = "✅ {{ .Email }}: Expire days reset successfully." "expireResetSuccess" = "✅ {{ .Email }} : Expire days reset successfully."
"resetIpSuccess" = "✅ {{ .Email }}: IP limit {{ .Count }} saved successfully." "resetIpSuccess" = "✅ {{ .Email }} : IP limit {{ .Count }} saved successfully."
"clearIpSuccess" = "✅ {{ .Email }}: IPs cleared successfully." "clearIpSuccess" = "✅ {{ .Email }} : IPs cleared successfully."
"getIpLog" = "✅ {{ .Email }}: Get IP Log." "getIpLog" = "✅ {{ .Email }} : Get IP Log."
"getUserInfo" = "✅ {{ .Email }}: Get Telegram User Info." "getUserInfo" = "✅ {{ .Email }} : Get Telegram User Info."
"removedTGUserSuccess" = "✅ {{ .Email }}: Telegram User removed successfully." "removedTGUserSuccess" = "✅ {{ .Email }} : Telegram User removed successfully."
"enableSuccess" = "✅ {{ .Email }}: Enabled successfully." "enableSuccess" = "✅ {{ .Email }} : Enabled successfully."
"disableSuccess" = "✅ {{ .Email }}: Disabled 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>" "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>"

View File

@@ -1,6 +1,6 @@
"username" = "Nombre de Usuario" "username" = "Nombre de Usuario"
"password" = "Contraseña" "password" = "Contraseña"
"login" = "Acceder" "login" = "Acceso"
"confirm" = "Confirmar" "confirm" = "Confirmar"
"cancel" = "Cancelar" "cancel" = "Cancelar"
"close" = "Cerrar" "close" = "Cerrar"
@@ -59,10 +59,10 @@
"settings" = "Configuraciones" "settings" = "Configuraciones"
"xray" = "Configuración Xray" "xray" = "Configuración Xray"
"logout" = "Cerrar Sesión" "logout" = "Cerrar Sesión"
"link" = "Gestionar" "link" = "Otro"
[pages.login] [pages.login]
"title" = "Bienvenido" "title" = "Iniciar Sesión"
"loginAgain" = "El límite de tiempo de inicio de sesión ha expirado. Por favor, inicia sesión nuevamente." "loginAgain" = "El límite de tiempo de inicio de sesión ha expirado. Por favor, inicia sesión nuevamente."
[pages.login.toasts] [pages.login.toasts]
@@ -76,10 +76,10 @@
"title" = "Estado del Sistema" "title" = "Estado del Sistema"
"memory" = "Memoria" "memory" = "Memoria"
"hard" = "Disco Duro" "hard" = "Disco Duro"
"xrayStatus" = "Estado de" "xrayStatus" = "Estado de Xray"
"stopXray" = "Detener" "stopXray" = "Detener Xray"
"restartXray" = "Reiniciar" "restartXray" = "Reiniciar"
"xraySwitch" = "Versión" "xraySwitch" = "Cambiar Versión"
"xraySwitchClick" = "Elige la versión a la que deseas cambiar." "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." "xraySwitchClickDesk" = "Elige sabiamente, ya que las versiones anteriores pueden no ser compatibles con las configuraciones actuales."
"operationHours" = "Tiempo de Funcionamiento" "operationHours" = "Tiempo de Funcionamiento"
@@ -134,7 +134,7 @@
"destinationPort" = "Puerto de Destino" "destinationPort" = "Puerto de Destino"
"targetAddress" = "Dirección de Destino" "targetAddress" = "Dirección de Destino"
"monitorDesc" = "Dejar en blanco por defecto" "monitorDesc" = "Dejar en blanco por defecto"
"meansNoLimit" = " = illimitata. (unidad: GB)" "meansNoLimit" = "Significa Sin Límite"
"totalFlow" = "Flujo Total" "totalFlow" = "Flujo Total"
"leaveBlankToNeverExpire" = "Dejar en Blanco para Nunca Expirar" "leaveBlankToNeverExpire" = "Dejar en Blanco para Nunca Expirar"
"noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada" "noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada"
@@ -173,12 +173,12 @@
"setDefaultCert" = "Establecer certificado desde el panel" "setDefaultCert" = "Establecer certificado desde el panel"
"xtlsDesc" = "La versión del núcleo de Xray debe ser 1.7.5" "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." "realityDesc" = "La versión del núcleo de Xray debe ser 1.8.0 o superior."
"telegramDesc" = "Utiliza únicamente IDs de chat (puedes obtenerlo aquí @userinfobot o usando el comando '/id' en el bot)." "telegramDesc" = "Utiliza el ID de Telegram sin @ o los 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." "subscriptionDesc" = "Puedes encontrar tu enlace de suscripción en Detalles, también puedes usar el mismo nombre para varias configuraciones."
"info" = "Info" "info" = "Info"
"same" = "misma" "same" = "misma"
"inboundData" = "Datos de entrada" "inboundData" = "Datos de entrada"
"exportInbound" = "Exportación entrante" "copyToClipboard" = "Copiar al portapapeles"
"import" = "Importar" "import" = "Importar"
"importInbound" = "Importar un entrante" "importInbound" = "Importar un entrante"
@@ -195,28 +195,27 @@
"prefix" = "Prefijo" "prefix" = "Prefijo"
"postfix" = "Sufijo" "postfix" = "Sufijo"
"delayedStart" = "Iniciar después del primer uso" "delayedStart" = "Iniciar después del primer uso"
"expireDays" = "Duratio" "expireDays" = "Días de Expiración"
"days" = "día(s)" "days" = "día(s)"
"renew" = "Renovación automática" "renew" = "Renovación automática"
"renewDesc" = "Auto-renovatio post tutelam receptam. (0 = disable) (unitas: dies)" "renewDesc" = "Renovación automática días después del vencimiento. 0 = deshabilitar"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Recibir" "obtain" = "Recibir"
[pages.inbounds.stream.general] [pages.inbounds.stream.general]
"request" = "Pedido" "requestHeader" = "Encabezado de la Petición"
"response" = "Respuesta"
"name" = "Nombre" "name" = "Nombre"
"value" = "Valor" "value" = "Valor"
[pages.inbounds.stream.tcp] [pages.inbounds.stream.tcp]
"version" = "Versión" "requestVersion" = "Versión de la Petición"
"method" = "Método" "requestMethod" = "Método de la Petición"
"path" = "Camino" "requestPath" = "Ruta de la Petición"
"status" = "Estado" "responseVersion" = "Versión de la Respuesta"
"statusDescription" = "Descripción de la Situación" "responseStatus" = "Estado de la Respuesta"
"requestHeader" = "Encabezado de solicitud" "responseStatusDescription" = "Descripción del Estado de la Respuesta"
"responseHeader" = "Encabezado de respuesta" "responseHeader" = "Encabezado de la Respuesta"
[pages.inbounds.stream.quic] [pages.inbounds.stream.quic]
"encryption" = "Cifrado" "encryption" = "Cifrado"
@@ -247,9 +246,6 @@
"pageSize" = "Tamaño de paginación" "pageSize" = "Tamaño de paginación"
"pageSizeDesc" = "Defina el tamaño de página para la tabla de entradas. Establezca 0 para desactivar" "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" "remarkModel" = "Modelo de observación y carácter de separación"
"datepicker" = "selector de fechas"
"datepickerPlaceholder" = "Seleccionar fecha"
"datepickerDescription" = "El tipo de calendario selector especifica la fecha de vencimiento"
"sampleRemark" = "Observación de muestra" "sampleRemark" = "Observación de muestra"
"oldUsername" = "Nombre de Usuario Actual" "oldUsername" = "Nombre de Usuario Actual"
"currentPassword" = "Contraseña Actual" "currentPassword" = "Contraseña Actual"
@@ -259,8 +255,6 @@
"telegramBotEnableDesc" = "Conéctese a las funciones de este panel a través del bot de Telegram." "telegramBotEnableDesc" = "Conéctese a las funciones de este panel a través del bot de Telegram."
"telegramToken" = "Token de Telegram" "telegramToken" = "Token de Telegram"
"telegramTokenDesc" = "Debe obtener el token del administrador de bots de Telegram @botfather." "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" "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." "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" "telegramNotifyTime" = "Hora de Notificación del Bot de Telegram"
@@ -333,10 +327,8 @@
"PrivateIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP privadas." "PrivateIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP privadas."
"Ads" = "Bloquear Anuncios" "Ads" = "Bloquear Anuncios"
"AdsDesc" = "Cambia la plantilla de configuración para bloquear anuncios." "AdsDesc" = "Cambia la plantilla de configuración para bloquear anuncios."
"Family" = "Bloquee malware y contenido para adultos" "Family" = "Bloquear Malware y Contenido para Adultos"
"FamilyDesc" = "Resolutores de DNS de Cloudflare para bloquear malware y contenido para adultos para protección familiar." "FamilyDesc" = "Resolvedores de DNS 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" "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." "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" "IRIp" = "Desactivar Conexión a Rangos de IP de Irán"
@@ -422,14 +414,6 @@
"portal" = "portal" "portal" = "portal"
"intercon" = "Interconexión" "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] [pages.settings.security]
"admin" = "Administrador" "admin" = "Administrador"
"secret" = "Token Secreto" "secret" = "Token Secreto"
@@ -453,7 +437,6 @@
"noIpRecord" = "❗ ¡Sin Registro de IP!" "noIpRecord" = "❗ ¡Sin Registro de IP!"
"noInbounds" = "❗ ¡No se encontraron entradas!" "noInbounds" = "❗ ¡No se encontraron entradas!"
"unlimited" = "♾ Ilimitado" "unlimited" = "♾ Ilimitado"
"add" = "Agregar"
"month" = "Mes" "month" = "Mes"
"months" = "Meses" "months" = "Meses"
"day" = "Día" "day" = "Día"
@@ -462,8 +445,6 @@
"unknown" = "Desconocido" "unknown" = "Desconocido"
"inbounds" = "Entradas" "inbounds" = "Entradas"
"clients" = "Clientes" "clients" = "Clientes"
"offline" = "🔴 Sin conexión"
"online" = "🟢 En línea"
[tgbot.commands] [tgbot.commands]
"unknown" = "❗ Comando desconocido" "unknown" = "❗ Comando desconocido"
@@ -474,8 +455,8 @@
"status" = "✅ ¡El bot está bien!" "status" = "✅ ¡El bot está bien!"
"usage" = "❗ ¡Por favor proporciona un texto para buscar!" "usage" = "❗ ¡Por favor proporciona un texto para buscar!"
"getID" = "🆔 Tu ID: <code>{{ .ID }}</code>" "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>" "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>" "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."
[tgbot.messages] [tgbot.messages]
"cpuThreshold" = "🔴 El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%" "cpuThreshold" = "🔴 El uso de CPU {{ .Percent }}% es mayor que el umbral {{ .Threshold }}%"
@@ -490,7 +471,7 @@
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n" "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n" "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
"ip" = "🌐 IP: {{ .IP }}\r\n" "ip" = "🌐 IP: {{ .IP }}\r\n"
"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n" "ips" = "🔢 IPs: \r\n{{ .IPs }}\r\n"
"serverUpTime" = "⏳ Tiempo de actividad del servidor: {{ .UpTime }} {{ .Unit }}\r\n" "serverUpTime" = "⏳ Tiempo de actividad del servidor: {{ .UpTime }} {{ .Unit }}\r\n"
"serverLoad" = "📈 Carga del servidor: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n" "serverLoad" = "📈 Carga del servidor: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
"serverMemory" = "📋 Memoria del servidor: {{ .Current }}/{{ .Total }}\r\n" "serverMemory" = "📋 Memoria del servidor: {{ .Current }}/{{ .Total }}\r\n"
@@ -504,23 +485,19 @@
"port" = "🔌 Puerto: {{ .Port }}\r\n" "port" = "🔌 Puerto: {{ .Port }}\r\n"
"expire" = "📅 Fecha de Vencimiento: {{ .Time }}\r\n" "expire" = "📅 Fecha de Vencimiento: {{ .Time }}\r\n"
"expireIn" = "📅 Vence en: {{ .Time }}\r\n" "expireIn" = "📅 Vence en: {{ .Time }}\r\n"
"active" = "💡 Activo: {{ .Enable }}\r\n" "active" = "💡 Activo: ✅ Sí\r\n"
"enabled" = "🚨 Habilitado: {{ .Enable }}\r\n" "inactive" = "💡 Activo: ❌ No\r\n"
"online" = "🌐 Estado de conexión: {{ .Status }}\r\n"
"email" = "📧 Email: {{ .Email }}\r\n" "email" = "📧 Email: {{ .Email }}\r\n"
"upload" = "🔼 Subida: ↑{{ .Upload }}\r\n" "upload" = "🔼 Subida: ↑{{ .Upload }}\r\n"
"download" = "🔽 Bajada: ↓{{ .Download }}\r\n" "download" = "🔽 Bajada: ↓{{ .Download }}\r\n"
"total" = "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n" "total" = "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n"
"TGUser" = "👤 Usuario de Telegram: {{ .TelegramID }}\r\n" "TGUser" = "👤 Usuario de Telegram: {{ .TelegramID }}\r\n"
"exhaustedMsg" = "🚨 Agotado {{ .Type }}:\r\n" "exhaustedMsg" = "🚨 Agotado {{ .Type }}: \r\n"
"exhaustedCount" = "🚨 Cantidad de Agotados {{ .Type }}:\r\n" "exhaustedCount" = "🚨 Cantidad de Agotados {{ .Type }}: \r\n"
"onlinesCount" = "🌐 Clientes en línea: {{ .Count }}\r\n"
"disabled" = "🛑 Desactivado: {{ .Disabled }}\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" "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] [tgbot.buttons]
"closeKeyboard" = "❌ Cerrar Teclado" "closeKeyboard" = "❌ Cerrar Teclado"
@@ -530,13 +507,11 @@
"confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?" "confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?"
"confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?" "confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?"
"confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?" "confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?"
"confirmToggle" = " ✅ ¿Confirmar habilitar/deshabilitar usuario?"
"dbBackup" = "Obtener Copia de Seguridad de BD" "dbBackup" = "Obtener Copia de Seguridad de BD"
"serverUsage" = "Uso del Servidor" "serverUsage" = "Uso del Servidor"
"getInbounds" = "Obtener Entradas" "getInbounds" = "Obtener Entradas"
"depleteSoon" = "Pronto se Agotará" "depleteSoon" = "Pronto se Agotará"
"clientUsage" = "Obtener Uso" "clientUsage" = "Obtener Uso"
"onlines" = "Clientes en línea"
"commands" = "Comandos" "commands" = "Comandos"
"refresh" = "🔄 Actualizar" "refresh" = "🔄 Actualizar"
"clearIPs" = "❌ Limpiar IPs" "clearIPs" = "❌ Limpiar IPs"
@@ -544,16 +519,14 @@
"selectTGUser" = "👤 Seleccionar Usuario de Telegram" "selectTGUser" = "👤 Seleccionar Usuario de Telegram"
"selectOneTGUser" = "👤 Selecciona un usuario de telegram:" "selectOneTGUser" = "👤 Selecciona un usuario de telegram:"
"resetTraffic" = "📈 Reiniciar Tráfico" "resetTraffic" = "📈 Reiniciar Tráfico"
"resetExpire" = "📅 Cambiar fecha de Vencimiento" "resetExpire" = "📅 Reiniciar Días de Vencimiento"
"ipLog" = "🔢 Registro de IP" "ipLog" = "🔢 Registro de IP"
"ipLimit" = "🔢 Límite de IP" "ipLimit" = "🔢 Límite de IP"
"setTGUser" = "👤 Establecer Usuario de Telegram" "setTGUser" = "👤 Establecer Usuario de Telegram"
"toggle" = "🔘 Habilitar / Deshabilitar" "toggle" = "🔘 Habilitar / Deshabilitar"
"custom" = "🔢 Costumbre" "custom" = "🔢 Costumbre"
"confirmNumber" = "✅ Confirmar: {{ .Num }}" "confirmNumber" = "✅ Confirmar : {{ .Num }}"
"confirmNumberAdd" = "✅ Confirmar agregando: {{ .Num }}"
"limitTraffic" = "🚧 Límite de tráfico" "limitTraffic" = "🚧 Límite de tráfico"
"getBanLogs" = "Registros de prohibición"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ ¡Exitosa!" "successfulOperation" = "✅ ¡Exitosa!"
@@ -573,4 +546,5 @@
"removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente." "removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente."
"enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente." "enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente."
"disableSuccess" = "✅ {{ .Email }} : Deshabilitado 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: <code>{{ .TgUserID }}</code>" "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>"

View File

@@ -1,5 +1,5 @@
"username" = "نامکاربری" "username" = "نام کاربری"
"password" = "رمزعبور" "password" = "رمز عبور"
"login" = "ورود" "login" = "ورود"
"confirm" = "تایید" "confirm" = "تایید"
"cancel" = "انصراف" "cancel" = "انصراف"
@@ -12,7 +12,7 @@
"protocol" = "پروتکل" "protocol" = "پروتکل"
"search" = "جستجو" "search" = "جستجو"
"filter" = "فیلتر" "filter" = "فیلتر"
"loading" = "...در حال بارگذاری" "loading" = "در حال بروزرسانی..."
"second" = "ثانیه" "second" = "ثانیه"
"minute" = "دقیقه" "minute" = "دقیقه"
"hour" = "ساعت" "hour" = "ساعت"
@@ -21,94 +21,94 @@
"indefinite" = "نامحدود" "indefinite" = "نامحدود"
"unlimited" = "نامحدود" "unlimited" = "نامحدود"
"none" = "هیچ" "none" = "هیچ"
"qrCode" = "QRکد" "qrCode" = "QR کد"
"info" = "اطلاعات بیشتر" "info" = "اطلاعات بیشتر"
"edit" = "ویرایش" "edit" = "ویرایش"
"delete" = "حذف" "delete" = "حذف"
"reset" = "ریست" "reset" = "ریست"
"copySuccess" = "باموفقیت کپیشد" "copySuccess" = "با موفقیت کپی شد"
"sure" = "مطمئن" "sure" = "مطمئن"
"encryption" = "رمزگذاری" "encryption" = "رمزگذاری"
"transmission" = "راهاتصال" "transmission" = "راه اتصال"
"host" = "آدرس" "host" = "آدرس"
"path" = "مسیر" "path" = "مسیر"
"camouflage" = "مبهم‌سازی" "camouflage" = "استتار"
"status" = "وضعیت" "status" = "وضعیت"
"enabled" = "فعال" "enabled" = "فعال"
"disabled" = "غیرفعال" "disabled" = "غیرفعال"
"depleted" = "منقضی" "depleted" = "منقضی"
"depletingSoon" = "درحالانقضا" "depletingSoon" = "در حال انقضا"
"offline" = "آفلاین" "offline" = "آفلاین"
"online" = "آنلاین" "online" = "آنلاین"
"domainName" = "آدرس دامنه" "domainName" = "آدرس دامنه"
"monitor" = "آیپی اتصال" "monitor" = "آی پی اتصال"
"certificate" = "گواهی" "certificate" = "گواهی دیجیتال"
"fail" = "ناموفق" "fail" = "خطا"
"success" = " موفق" "success" = " موفق"
"getVersion" = "دریافت نسخه" "getVersion" = "دریافت ورژن"
"install" = "نصب" "install" = "نصب"
"clients" = "کاربران" "clients" = "کاربران"
"usage" = "استفاده" "usage" = "استفاده"
"secretToken" = "توکن امنیتی" "secretToken" = "توکن امنیتی"
"remained" = "باقیمانده" "remained" = "باقیمانده"
"security" = "امنیت" "security" = "امنیت"
[menu] [menu]
"dashboard" = "نمای کلی" "dashboard" = "وضعیت سیستم"
"inbounds" = "ورودی‌ها" "inbounds" = "سرویس ها"
"settings" = "تنظیمات پنل" "settings" = "تنظیمات پنل"
"xray" = "پیکربندی ایکس‌ری" "xray" = "الگوی ایکس‌ری"
"logout" = "خروج" "logout" = "خروج"
"link" = "مدیریت" "link" = "مدیریت"
[pages.login] [pages.login]
"title" = "خوش‌آمدید" "title" = "ورود به سیستم"
"loginAgain" = "مدت زمان استفاده بهاتمامرسیده، لطفا دوباره وارد شوید" "loginAgain" = "مدت زمان استفاده به اتمام رسیده ، لطفا دوباره وارد شوید"
[pages.login.toasts] [pages.login.toasts]
"invalidFormData" = "اطلاعات به‌درستی وارد نشدهاست" "invalidFormData" = "اطلاعات وارد شده به صورت درست وارد نشده است"
"emptyUsername" = "لطفا یک نامکاربری وارد کنید‌" "emptyUsername" = "نام کاربری خالی میباشد"
"emptyPassword" = "لطفا یک رمزعبور وارد کنید" "emptyPassword" = "رمز عبور خالی میباشد"
"wrongUsernameOrPassword" = "نامکاربری یا رمزعبوراشتباه‌است" "wrongUsernameOrPassword" = "نام کاربری و رمز عبور اشتباه میباشد"
"successLogin" = "ورود" "successLogin" = "خوش آمدید"
[pages.index] [pages.index]
"title" = "نمای کلی" "title" = "وضعیت سیستم"
"memory" = "RAM" "memory" = "حافظه رم"
"hard" = "Disk" "hard" = "حافظه دیسک"
"xrayStatus" = "وضعیت‌ایکس‌ری" "xrayStatus" = "وضعیت"
"stopXray" = "توقف" "stopXray" = "توقف"
"restartXray" = "شروعمجدد" "restartXray" = "شروع مجدد"
"xraySwitch" = "‌نسخه" "xraySwitch" = "تغییر ورژن"
"xraySwitchClick" = سخه مورد نظر را انتخاب کنید" "xraySwitchClick" = "ورژن مورد نظر را انتخاب کنید"
"xraySwitchClickDesk" = "لطفا بادقت انتخاب کنید. درصورت انتخاب نسخه قدیمی‌تر، امکان ناهماهنگی با پیکربندی فعلی وجود دارد" "xraySwitchClickDesk" = "لطفا با دقت انتخاب کنید ، در صورت انتخاب اشتباه امکان قطعی سیستم وجود دارد "
"operationHours" = "مدت‌کارکرد" "operationHours" = "آپ تایم سیستم"
"systemLoad" = "بارسیستم" "systemLoad" = "بار سیستم"
"systemLoadDesc" = "میانگین بار سیستم برای 1، 5 و 15 دقیقه گذشته" "systemLoadDesc" = "میانگین بار سیستم برای 1، 5 و 15 دقیقه گذشته"
"connectionTcpCountDesc" = "در تمام‌شبکه‌ها TCP مجموع‌اتصالات" "connectionTcpCountDesc" = "مجموع اتصالات TCP در تمام کارت های شبکه"
"connectionUdpCountDesc" = "در تمام‌شبکه‌ها UDP مجموع‌اتصالات" "connectionUdpCountDesc" = "مجموع اتصالات UDP در تمام کارت های شبکه"
"connectionCount" = "تعداد کانکشن ها" "connectionCount" = "تعداد کانکشن ها"
"upSpeed" = "سرعت کلی آپلود در تمام‌شبکه‌ها" "upSpeed" = "سرعت آپلود در حال حاضر سیستم"
"downSpeed" = "سرعت کلی دانلود در تمام‌شبکه‌ها" "downSpeed" = "سرعت دانلود در حال حاضر سیستم"
"totalSent" = "مجموع ترافیک ارسال‌‌شده پس‌از شروع‌به‌کار سیستم‌عامل" "totalSent" = "جمع کل ترافیک آپلود مصرفی"
"totalReceive" = "مجموع ترافیک دریافت‌شده پس‌از شروع‌به‌کار سیستم‌عامل" "totalReceive" = "جمع کل ترافیک دانلود مصرفی"
"xraySwitchVersionDialog" = "تغییر نسخه ایکس‌ری" "xraySwitchVersionDialog" = "تغییر ورژن"
"xraySwitchVersionDialogDesc" = "آیا از تغییر نسخه‌ مطمئن هستید؟" "xraySwitchVersionDialogDesc" = "آیا از تغییر ورژن مطمئن هستین"
"dontRefresh" = "در حال نصب، لطفا صفحه را رفرش نکنید" "dontRefresh" = "در حال نصب ، لطفا رفرش نکنید "
"logs" = "گزارشها" "logs" = "گزارش ها"
"config" = "پیکربندی" "config" = "تنظیمات"
"backup" = "پشتیبانگیری" "backup" = "پشتیبان گیری و بازیابی"
"backupTitle" = "پشتیبانگیری دیتابیس" "backupTitle" = "پشتیبان گیری و بازیابی دیتابیس"
"backupDescription" = "توصیه‌می‌شود قبلاز واردکردن یک دیتابیس جدید، نسخه پشتیبان تهیه کنید" "backupDescription" = "به یاد داشته باشید که قبل از وارد کردن یک دیتابیس جدید، نسخه پشتیبان تهیه کنید"
"exportDatabase" = " پشتیبان‌گیری" "exportDatabase" = "دانلود دیتابیس"
"importDatabase" = "بازگرداندن" "importDatabase" = "آپلود دیتابیس"
[pages.inbounds] [pages.inbounds]
"title" = "کاربران" "title" = "کاربران"
"totalDownUp" = "دریافت/ارسال کل" "totalDownUp" = "جمع آپلود/دانلود"
"totalUsage" = "‌‌‌مصرف کل" "totalUsage" = "جمع کل"
"inboundCount" = "کل ورودی‌ها" "inboundCount" = "تعداد سرویس ها"
"operate" = "عملیات" "operate" = "فهرست"
"enable" = "فعال" "enable" = "فعال"
"remark" = "نام" "remark" = "نام"
"protocol" = "پروتکل" "protocol" = "پروتکل"
@@ -116,71 +116,71 @@
"traffic" = "ترافیک" "traffic" = "ترافیک"
"details" = "توضیحات" "details" = "توضیحات"
"transportConfig" = "نحوه اتصال" "transportConfig" = "نحوه اتصال"
"expireDate" = "مدت زمان" "expireDate" = "تاریخ انقضا"
"resetTraffic" = "ریست ترافیک" "resetTraffic" = "ریست ترافیک"
"addInbound" = "افزودن ورودی" "addInbound" = "اضافه کردن سرویس"
"generalActions" = "عملیات کلی" "generalActions" = "عملیات کلی"
"create" = "افزودن" "create" = "اضافه کردن"
"update" = "ویرایش" "update" = "ویرایش"
"modifyInbound" = "ویرایش ورودی" "modifyInbound" = "ویرایش سرویس"
"deleteInbound" = "حذف ورودی" "deleteInbound" = "حذف سرویس"
"deleteInboundContent" = "آیا مطمئن به حذف ورودی هستید؟" "deleteInboundContent" = "آیا مطمئن به حذف سرویس هستید ؟"
"deleteClient" = "حذف کاربر" "deleteClient" = "حذف کاربر"
"deleteClientContent" = "آیا مطمئن به حذف کاربر هستید؟" "deleteClientContent" = "آیا مطمئن به حذف کاربر هستید ؟"
"resetTrafficContent" = "آیا مطمئن به ریست ترافیک هستید؟" "resetTrafficContent" = "آیا مطمئن به ریست ترافیک هستید ؟"
"copyLink" = "کپی لینک" "copyLink" = "کپی لینک"
"address" = "آدرس" "address" = "آدرس"
"network" = "شبکه" "network" = "شبکه"
"destinationPort" = "پورت مقصد" "destinationPort" = "پورت مقصد"
"targetAddress" = "آدرس مقصد" "targetAddress" = "آدرس مقصد"
"monitorDesc" = "بهطور پیشفرض خالیبگذارید" "monitorDesc" = "به طور پیش فرض خالی بگذارید"
"meansNoLimit" = " = واحد: گیگابایت) نامحدود)" "meansNoLimit" = "یعنی بدون محدودیت"
"totalFlow" = "ترافیک کل" "totalFlow" = "کل ترافیک"
"leaveBlankToNeverExpire" = "برای منقضینشدن خالی‌بگذارید" "leaveBlankToNeverExpire" = "خالی بگذارید تا هرگز منقضی نشود"
"noRecommendKeepDefault" = "توصیهمیشود به‌طور پیشفرض حفظشود" "noRecommendKeepDefault" = "توصیه می شود به عنوان پیش فرض حفظ شود"
"certificatePath" = "مسیر فایل" "certificatePath" = "مسیر فایل گواهی"
"certificateContent" = "محتوای فایل" "certificateContent" = "محتوای فایل گواهی"
"publicKeyPath" = "مسیر کلید عمومی" "publicKeyPath" = "مسیر کلید عمومی"
"publicKeyContent" = "محتوای کلید عمومی" "publicKeyContent" = "محتوای کلید عمومی"
"keyPath" = "مسیر کلید خصوصی" "keyPath" = "مسیر کلید خصوصی"
"keyContent" = "محتوای کلید خصوصی" "keyContent" = "محتوای کلید خصوصی"
"clickOnQRcode" = "برای کپی بر روی کدتصویری کلیک کنید" "clickOnQRcode" = "برای کپی بر روی کد تصویری کلیک کنید"
"client" = "کاربر" "client" = "کاربر"
"export" = "استخراج لینکها" "export" = "استخراج لینکها"
"clone" = "شبیهسازی" "clone" = "شبیه سازی"
"cloneInbound" = "شبیه‌سازی ورودی" "cloneInbound" = "ایجاد"
"cloneInboundContent" = "همه موارد این ورودی بجز پورت، آی‌پی و کاربر‌ها شبیهسازی خواهند شد" "cloneInboundContent" = "همه موارد این ورودی بجز پورت ، ای پی و کلاینت ها شبیه سازی خواهند شد"
"cloneInboundOk" = "ساختن شبیه ساز" "cloneInboundOk" = "ساختن شبیه ساز"
"resetAllTraffic" = "ریست ترافیک کل ورودی‌ها" "resetAllTraffic" = "ریست ترافیک کل سرویس ها"
"resetAllTrafficTitle" = "ریست ترافیک کل ورودی‌ها" "resetAllTrafficTitle" = "ریست ترافیک کل سرویس ها"
"resetAllTrafficContent" = "آیا مطمئن به ریست ترافیک تمام ورودی‌ها هستید؟" "resetAllTrafficContent" = "آیا مطمئن هستید که میخواهید تمام ترافیک سرویس ها را ریست کنید؟"
"resetInboundClientTraffics" = "ریست ترافیک کاربران" "resetInboundClientTraffics" = "ریست ترافیک کاربران"
"resetInboundClientTrafficTitle" = "ریست ترافیک کاربران" "resetInboundClientTrafficTitle" = "ریست ترافیک کل کاربران"
"resetInboundClientTrafficContent" = "آیا مطمئن به ریست ترافیک تمام کاربران این‌ ورودی هستید؟" "resetInboundClientTrafficContent" = "آیا مطمئن هستید که میخواهید تمام ترافیک کاربران این سرویس را ریست کنید؟"
"resetAllClientTraffics" = "ریست ترافیک کل کاربران" "resetAllClientTraffics" = "ریست ترافیک کاربران"
"resetAllClientTrafficTitle" = "ریست ترافیک کل کاربران" "resetAllClientTrafficTitle" = "ریست ترافیک کل کاربران"
"resetAllClientTrafficContent" = "آیا مطمئن به ریست ترافیک تمام کاربران هستید؟" "resetAllClientTrafficContent" = "آیا مطمئن هستید که میخواهید تمام ترافیک کاربران را ریست کنید؟"
"delDepletedClients" = "حذف کاربران منقضی" "delDepletedClients" = "حذف کاربران منقضی"
"delDepletedClientsTitle" = "حذف کاربران منقضی" "delDepletedClientsTitle" = "حذف کاربران منقضی"
"delDepletedClientsContent" = "آیا مطمئن به حذف تمام کاربران منقضیشده ‌هستید؟" "delDepletedClientsContent" = "آیا مطمئن هستید مه میخواهید تمامی کاربران منقضی شده را حذف کنید؟"
"email" = "ایمیل" "email" = "ایمیل"
"emailDesc" = "باید یک ایمیل یکتا باشد" "emailDesc" = "ایمیل باید کاملا منحصر به فرد باشد"
"IPLimit" = "محدودیت آی‌پی" "IPLimit" = "محدودیت ای پی"
"IPLimitDesc" = "(اگر تعداد از مقدار تنظیم شده بیشتر شود، ورودی را غیرفعال می کند. (0 = غیرفعال" "IPLimitDesc" = "غیرفعال کردن ورودی در صورت بیش از تعداد وارد شده (0 برای غیرفعال کردن محدودیت ای پی )"
"IPLimitlog" = "گزارشها" "IPLimitlog" = "گزارش ها"
"IPLimitlogDesc" = "گزارش تاریخچه آی‌پی. برای فعال کردن ورودی پس از غیرفعال شدن، گزارش را پاک کنید" "IPLimitlogDesc" = "گزارش سابقه ای پی (قبل از فعال کردن ورودی پس از غیرفعال شدن توسط محدودیت ای پی، باید گزارش را پاک کنید)"
"IPLimitlogclear" = "پاک کردن گزارشها" "IPLimitlogclear" = "پاک کردن گزارش ها"
"setDefaultCert" = "استفاده از گواهی پنل" "setDefaultCert" = "استفاده از گواهی پنل"
"xtlsDesc" = "ایکس‌ری باید 1.7.5 باشد" "xtlsDesc" = "هسته Xray باید 1.7.5 باشد"
"realityDesc" = "ایکس‌ری باید +1.8.0 باشد" "realityDesc" = "هسته Xray باید 1.8.0 و بالاتر باشد"
"telegramDesc" = "دریافت کنید ('/id'یا (دستور (@userinfobot) آی‌دی(های) چت تلگرام مدیر، از" "telegramDesc" = "از آیدی تلگرام بدون @ یا آیدی چت استفاده کنید (می توانید آن را از اینجا دریافت کنید @userinfobot یا در ربات دستور '/id' را وارد کنید)"
"subscriptionDesc" = "شما میتوانید لینک سابسکربپشن خودرا در 'جزئیات' پیدا کنید، همچنین میتوانید از همین نام برای چندین کاربر استفادهکنید" "subscriptionDesc" = "می توانید ساب لینک خود را در جزئیات پیدا کنید، همچنین می توانید از همین نام برای چندین کانفیگ استفاده کنید"
"info" = "اطلاعات" "info" = "اطلاعات"
"same" = "همسان" "same" = "همسان"
"inboundData" = "داده‌های ورودی" "inboundData" = "داده‌های سرویس"
"exportInbound" = "استخراج ورودی" "copyToClipboard" = "کپی در حافظه"
"import" = "افزودن" "import" = ارد کردن"
"importInbound" = "افزودن یک ورودی" "importInbound" = ارد کردن یک سرویس"
[pages.client] [pages.client]
"add" = "کاربر جدید" "add" = "کاربر جدید"
@@ -188,209 +188,201 @@
"submitAdd" = "اضافه کردن" "submitAdd" = "اضافه کردن"
"submitEdit" = "ذخیره تغییرات" "submitEdit" = "ذخیره تغییرات"
"clientCount" = "تعداد کاربران" "clientCount" = "تعداد کاربران"
"bulk" = "انبوهسازی" "bulk" = "انبوه سازی"
"method" = "روش" "method" = "روش"
"first" = "از" "first" = "از"
"last" = "تا" "last" = "تا"
"prefix" = "پیشوند" "prefix" = "پیشوند"
"postfix" = "پسوند" "postfix" = "پسوند"
"delayedStart" = "شروع‌پس‌ازاولیناستفاده" "delayedStart" = "شروع بعد از اولین استفاده"
"expireDays" = "مدت زمان" "expireDays" = "روزهای اعتبار"
"days" = "(روز)" "days" = "(روز)"
"renew" = "تمدید خودکار" "renew" = "تمدید خودکار"
"renewDesc" = "(تمدید خودکار پساز انقضا. (0 = غیرفعال)(واحد: روز" "renewDesc" = "روزهای تمدید خودکار پس از انقضا. 0 = غیرفعال"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "فراهم‌سازی" "obtain" = "Obtain"
[pages.inbounds.stream.general] [pages.inbounds.stream.general]
"request" = "درخواست" "requestHeader" = "درخواست سربرگ"
"response" = "پاسخ"
"name" = "نام" "name" = "نام"
"value" = "مقدار" "value" = "مقدار"
[pages.inbounds.stream.tcp] [pages.inbounds.stream.tcp]
"version" = "نسخه" "requestVersion" = "ورژن درخواست"
"method" = "متد" "requestMethod" = "متد درخواست"
"path" = "مسیر" "requestPath" = "مسیر درخواست"
"status" = ضعیت" "responseVersion" = رژن پاسخ"
"statusDescription" = "توضیحات وضعیت" "responseStatus" = "وضعیت پاسخ"
"requestHeader" = "سربرگ درخواست" "responseStatusDescription" = "توضیحات وضعیت پاسخ"
"responseHeader" = "سربرگ پاسخ" "responseHeader" = "سربرگ پاسخ"
[pages.inbounds.stream.quic] [pages.inbounds.stream.quic]
"encryption" = "رمزنگاری" "encryption" = "رمزنگاری"
[pages.settings] [pages.settings]
"title" = "تنظیمات پنل" "title" = "تنظیمات"
"save" = "ذخیره" "save" = "ذخیره"
"infoDesc" = "برای اعمال تغییرات در این بخش باید پس از ذخیره کردن، پنل را ریستارت کنید" "infoDesc" = "برای اعمال تغییرات در این بخش باید پس از ذخیره کردن، پنل را ریستارت کنید"
"restartPanel" = "ریستارت پنل" "restartPanel" = "ریستارت پنل"
"restartPanelDesc" = "آیا مطمئن به ریستارت پنل هستید؟ اگر پس‌از ریستارت نمیتوانید به پنل دسترسی پیدا کنید، لطفاً گزارش‌های موجود در اسکریپت پنل را بررسی کنید" "restartPanelDesc" = "آیا مطمئن هستید که می خواهید پنل را دوباره راه اندازی کنید؟ برای راه اندازی مجدد روی OK کلیک کنید. اگر بعد از 3 ثانیه نمی توانید به پنل دسترسی پیدا کنید، لطفاً برای مشاهده اطلاعات گزارش پانل به سرور برگردید"
"actions" = "عملیات ها" "actions" = "عملیات ها"
"resetDefaultConfig" = "برگشت به پیشفرض" "resetDefaultConfig" = "برگشت به تنظیمات پیشفرض"
"panelSettings" = "پیکربندی" "panelSettings" = "تنظیمات پنل"
"securitySettings" = "احرازهویت" "securitySettings" = "تنظیمات امنیتی"
"TGBotSettings" = "ربات تلگرام" "TGBotSettings" = "تنظیمات ربات تلگرام"
"panelListeningIP" = "آدرس آیپی" "panelListeningIP" = "محدودیت آی پی پنل"
"panelListeningIPDesc" = "آدرس آی‌پی برای وب پنل. برای گوش‌دادن به‌تمام آی‌پیها خالیبگذارید" "panelListeningIPDesc" = "برای استفاده از تمام آی‌پیها به طور پیش فرض خالی بگذارید"
"panelListeningDomain" = "نام دامنه" "panelListeningDomain" = "محدودیت دامین پنل"
"panelListeningDomainDesc" = "آدرس دامنه برای وب پنل. برای گوش دادن به‌تمام دامنه‌ها و آی‌پی‌ها خالیبگذارید" "panelListeningDomainDesc" = "برای استفاده از تمام دامنه‌ها و آی‌پی‌ها به طور پیش فرض خالی بگذارید"
"panelPort" = "پورت" "panelPort" = "پورت پنل"
"panelPortDesc" = "شماره پورت برای وب پنل. باید پورت استفاده نشده‌باشد" "panelPortDesc" = "پورت مورد استفاده برای نمایش این پنل"
"publicKeyPath" = "مسیر کلید عمومی" "publicKeyPath" = "مسیر فایل گواهی کلید عمومی پنل"
"publicKeyPathDesc" = "مسیر فایل کلیدعمومی برای وب پنل. با '/' شروعمیشود" "publicKeyPathDesc" = "باید یک مسیر مطلق باشد که با / شروع می شود "
"privateKeyPath" = "مسیر کلید خصوصی" "privateKeyPath" = "مسیر فایل گواهی کلید خصوصی پنل"
"privateKeyPathDesc" = "مسیر فایل کلیدخصوصی برای وب پنل. با '/' شروعمیشود" "privateKeyPathDesc" = "باید یک مسیر مطلق باشد که با / شروع می شود "
"panelUrlPath" = "URI مسیر" "panelUrlPath" = "آدرس روت پنل"
"panelUrlPathDesc" = رای وب پنل. با '/' شروع و با '/' خاتمه‌ می‌یابد URI مسیر" "panelUrlPathDesc" = "باید با '/' شروع شود و با '/' تمام شود"
"pageSize" = "اندازه صفحه بندی جدول" "pageSize" = "اندازه صفحه بندی جدول"
"pageSizeDesc" = "(اندازه صفحه برای جدول ورودی‌ها.(0 = غیرفعال" "pageSizeDesc" = "اندازه صفحه را برای جدول سرویس ها تعریف کنید. 0: غیرفعال"
"remarkModel" = "نامکانفیگ و جداکننده" "remarkModel" = "نام کانفیگ و جداکننده"
"datepicker" = وع تقویم" "sampleRemark" = مونه نام"
"datepickerPlaceholder" = "انتخاب تاریخ" "oldUsername" = "نام کاربری فعلی"
"datepickerDescription" = "وظایف برنامه ریزی شده بر اساس این تقویم اجرا می‌شود" "currentPassword" = "رمز عبور فعلی"
"sampleRemark" = "نمونه‌نام" "newUsername" = "نام کاربری جدید"
"oldUsername" = "نام‌کاربری فعلی" "newPassword" = "رمز عبور جدید"
"currentPassword" = "رمز‌عبور فعلی" "telegramBotEnable" = "فعالسازی ربات تلگرام"
"newUsername" = "نام‌کاربری جدید" "telegramBotEnableDesc" = "از طریق بات تلگرام به امکانات این پنل متصل شوید"
"newPassword" = "رمزعبور جدید"
"telegramBotEnable" = "فعال‌سازی ربات تلگرام"
"telegramBotEnableDesc" = "ربات تلگرام را فعال می‌کند"
"telegramToken" = "توکن تلگرام" "telegramToken" = "توکن تلگرام"
"telegramTokenDesc" = "دریافت کنید @botfather توکن را می‌توانید از" "telegramTokenDesc" = "توکن را باید از مدیر بات های تلگرام دریافت کنید @botfather"
"telegramProxy" = "SOCKS پراکسی" "telegramChatId" = "آی دی تلگرام مدیریت"
"telegramProxyDesc" = "را برای اتصال به تلگرام فعال می کند SOCKS5 پراکسی" "telegramChatIdDesc" = "از @userinfobot یا دستور '/id' در ربات برای دریافت شناسه های چت خود استفاده کنید. با استفاده از کاما میتونید چند آی دی را از هم جدا کنید. "
"telegramChatId" = "آی‌دی چت مدیر" "telegramNotifyTime" = "مدت زمان نوتیفیکیشن ربات تلگرام"
"telegramChatIdDesc" = "دریافت ‌کنید ('/id'یا (دستور (@userinfobot) آی‌دی(های) چت تلگرام مدیر، از" "telegramNotifyTimeDesc" = "از فرمت زمان بندی لینوکس استفاده کنید "
"telegramNotifyTime" = "زمان نوتیفیکیشن" "tgNotifyBackup" = "پشتیبان گیری از پایگاه داده"
"telegramNotifyTimeDesc" = "زمان‌اطلاع‌رسانی ربات تلگرام برای گزارش های دوره‌ای. از فرمت زمانبندی لینوکس استفاده‌کنید‌" "tgNotifyBackupDesc" = "ارسال کپی فایل پایگاه داده به همراه گزارش دوره ای"
"tgNotifyBackup" = "پشتیبان‌گیری از دیتابیس"
"tgNotifyBackupDesc" = "فایل پشتیبان‌دیتابیس را به‌همراه گزارش ارسال می‌کند"
"tgNotifyLogin" = "اعلان ورود" "tgNotifyLogin" = "اعلان ورود"
"tgNotifyLoginDesc" = "نامکاربری، آدرس آی‌پی، و زمان ورود، فردی که سعی می‌کند وارد پنل شود را نمایش میدهد" "tgNotifyLoginDesc" = "نام کاربری، آدرس ای پی، و زمان وقتی که فردی سعی می‌کند به پنل شما وارد شود نمایش میدهد"
"sessionMaxAge" = "بیشینه زمان جلسه وب" "sessionMaxAge" = "بیشینه زمان جلسه وب"
"sessionMaxAgeDesc" = "(بیشینه زمانی که میتوانید لاگین بمانید. (واحد: دقیقه" "sessionMaxAgeDesc" = "بیشینه زمانی که میتوانید لاگین بمانید (واحد: دقیقه)"
"expireTimeDiff" = "آستانه زمان باقی مانده" "expireTimeDiff" = "آستانه زمان باقی مانده"
"expireTimeDiffDesc" = "(فاصله زمانی هشدار تا رسیدن به زمان انقضا. (واحد: روز" "expireTimeDiffDesc" = "فاصله زمانی هشدار تا رسیدن به زمان انقضا (واحد: روز)"
"trafficDiff" = "آستانه ترافیک باقی مانده" "trafficDiff" = "آستانه ترافیک باقی مانده"
"trafficDiffDesc" = "(فاصله زمانی هشدار تا رسیدن به اتمام ترافیک. (واحد: گیگابایت" "trafficDiffDesc" = "فاصله زمانی هشدار تا رسیدن به اتمام ترافیک (واحد: گیگابایت)"
"tgNotifyCpu" = "آستانه هشدار بار پردازنده" "tgNotifyCpu" = "آستانه هشدار درصد پردازنده"
"tgNotifyCpuDesc" = "(اگر بار روی پردازنده ازاین آستانه فراتر رفت، برای شما پیام ارسال می‌شود. (واحد: درصد" "tgNotifyCpuDesc" = "این ربات تلگرام در صورت استفاده پردازنده بیشتر از این درصد برای شما پیام ارسال می کند.(واحد: درصد)"
"timeZone" = "منطقه زمانی" "timeZone" = "منظقه زمانی"
"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقهزمانی اجرا میشود" "timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه زمانی اجرا می شوند. پنل را مجدداً راه اندازی می کند تا اعمال شود"
"subSettings" = "سابسکریپشن" "subSettings" = "سابسکریپشن"
"subEnable" = "فعال‌سازی سرویس سابسکریپشن" "subEnable" = "فعال کردن سرویس"
"subEnableDesc" = " سرویس سابسکریپشن را فعال‌می‌کند" "subEnableDesc" = "ویژگی سابسکریپشن با پیکربندی جداگانه"
"subListen" = "آدرس آی‌پی" "subListen" = "محدودیت آی‌پی"
"subListenDesc" = "آدرس آی‌پی برای سرویس سابسکریپشن. برای گوش دادن به‌تمام آی‌پیها خالیبگذارید" "subListenDesc" = "برای استفاده از همه آی‌پی ها به طور پیش فرض خالی بگذارید"
"subPort" = "پورت" "subPort" = "پورت سرویس"
"subPortDesc" = "شماره پورت برای سرویس سابسکریپشن. باید پورت استفاده نشده‌باشد" "subPortDesc" = "شماره پورت برای ارائه خدمات سابسکریپشن باید خالی باشد"
"subCertPath" = "مسیر کلید عمومی" "subCertPath" = "مسیر فایل کلید عمومی گواهی سابسکریپشن"
"subCertPathDesc" = "مسیر فایل کلیدعمومی برای سرویس سابیکریپشن. با '/' شروعمیشود" "subCertPathDesc" = "یک مسیر مطلق که با '/' شروع می شود را پر کنید."
"subKeyPath" = "مسیر کلید خصوصی" "subKeyPath" = "مسیر فایل کلید خصوصی گواهی سابسکریپشن"
"subKeyPathDesc" = "مسیر فایل کلیدخصوصی برای سرویس سابسکریپشن. با '/' شروعمیشود" "subKeyPathDesc" = "یک مسیر مطلق که با '/' شروع می شود را پر کنید."
"subPath" = "URI مسیر" "subPath" = "مسیر ریشه سابسکریپشن"
"subPathDesc" = رای سرویس سابسکریپشن. با '/' شروع و با '/' خاتمه می‌یابد URI مسیر" "subPathDesc" = "باید با '/' شروع شود و با '/' ختم شود."
"subDomain" = "نام دامنه" "subDomain" = "دامنه مخصوص سابسکریپشن"
"subDomainDesc" = "آدرس دامنه برای سرویس سابسکریپشن. برای گوش دادن به تمام دامنهها و آی‌پیها خالیبگذارید" "subDomainDesc" = "برای نظارت بر همه دامنه ها و آی‌پی ها به طور پیش فرض خالی بگذارید"
"subUpdates" = "فاصله بروزرسانی سابسکریپشن" "subUpdates" = "فاصله به روز رسانی های سابسکریپشن"
"subUpdatesDesc" = "(فاصله مابین بروزرسانی در برنامه‌های کاربری. (واحد: ساعت" "subUpdatesDesc" = "ساعت های فاصله بین به روز رسانی در برنامه کاربر"
"subEncrypt" = "کدگذاری" "subEncrypt" = "رمزگذاری کانفیگ ها"
"subEncryptDesc" = "کدگذاری خواهدشد Base64 محتوای برگشتی سرویس سابسکریپشن برپایه" "subEncryptDesc" = "رمزگذاری کانفیگ های بازگشتی سابسکریپشن"
"subShowInfo" = "نمایش اطلاعات مصرف" "subShowInfo" = "نمایش اطلاعات مصرف"
"subShowInfoDesc" = "ترافیک و زمان باقیمانده را در برنامه‌های کاربری نمایش میدهد" "subShowInfoDesc" = "ترافیک و زمان باقیمانده را در هر کانفیگ نمایش میدهد"
"subURI" = "پروکسی معکوس URI مسیر" "subURI" = "آدرس پایه پروکسی معکوس"
"subURIDesc" = "سابسکریپشن را برای استفاده در پشت پراکسیها تغییر میدهد URI مسیر" "subURIDesc" = "آدرس پایه سابسکریپشن را برای استفاده در پشت پراکسی ها تغییر میدهد"
[pages.xray] [pages.xray]
"title" = "پیکربندی ایکس‌ری" "title" = "الگوها"
"save" = "ذخیره" "save" = "ذخیره تنظیمات"
"restart" = "ریستارت ایکس‌ری" "restart" = "ریستارت ایکس‌ری"
"basicTemplate" = "پایه" "basicTemplate" = "بخش الگو پایه"
"advancedTemplate" = "پیشرفته" "advancedTemplate" = "بخش الگو پیشرفته"
"generalConfigs" = "استراتژی‌ کلی" "generalConfigs" = "تنظیمات عمومی"
"generalConfigsDesc" = "این گزینه‌ها استراتژی کلی ترافیک را تعیین می‌کنند" "generalConfigsDesc" = "این تنظیمات میتواند ترافیک کلی سرویس را متاثر کند"
"blockConfigs" = "سپر محافظ" "blockConfigs" = "مسدود سازی"
"blockConfigsDesc" = "این گزینهها ترافیک را بر اساس پروتکل‌های درخواستی خاص، و وب سایتها مسدود میکند" "blockConfigsDesc" = "این گزینه ها از اتصال کاربران به پروتکل ها و وب سایت های خاص جلوگیری می کند"
"blockCountryConfigs" = "مسدودسازی کشور" "blockCountryConfigs" = "تنظیمات برای مسدودسازی کشورها"
"blockCountryConfigsDesc" = "این گزینهها ترافیک را بر اساس کشور درخواستی خاص مسدود میکند" "blockCountryConfigsDesc" = "این گزینه اتصال کاربران به دامنه های کشوری خاص را مسدود می کند"
"directCountryConfigs" = "اتصال مستقیم کشور" "directCountryConfigs" = "تنظیمات برای اتصال مستقیم کشورها"
"directCountryConfigsDesc" = "این گزینهها ترافیک را بر اساس کشور درخواستی خاص بصورت مستقیم ارسال میکند" "directCountryConfigsDesc" = "این گزینه کاربران را به دامنه های کشوری خاص را به طور مستقیم، متصل می کند"
"ipv4Configs" = "IPv4 مسیریابی" "ipv4Configs" = "تنظیمات برای IPv4"
"ipv4ConfigsDesc" = "این گزینهها ترافیک‌ را از طریق آیپینسخه4 به مقصد هدایت می‌کند" "ipv4ConfigsDesc" = "این گزینه فقط از طریق آیپی ورژن ۴ به دامنه های هدف هدایت می شود"
"warpConfigs" = "WARP مسیریابی" "warpConfigs" = "تنظیمات برای WARP"
"warpConfigsDesc" = "طبق راهنما نصب کنید SOCKS5 این گزینه‌ها ترافیک‌ را از طریق وارپ کلادفلر به مقصد هدایت می‌کند. ابتدا، وارپ را در حالت پراکسی" "warpConfigsDesc" = "هشدار: قبل از استفاده از این گزینه، WARP را در حالت پراکسی socks5 با دنبال کردن مراحل در GitHub پنل روی سرور خود نصب کنید. WARP ترافیک را از طریق سرورهای Cloudflare به وب سایت ها هدایت می کند"
"Template" = "‌پیکربندی پیشرفته الگو ایکسری" "Template" = "تنظیمات الگو ایکس ری"
"TemplateDesc" = "فایل پیکربندی نهایی ایکس‌ری بر اساس این الگو ایجاد میشود" "TemplateDesc" = "فایل پیکربندی ایکس ری نهایی بر اساس این الگو ایجاد میشود. لطفاً این را تغییر ندهید مگر اینکه دقیقاً بدانید که چه کاری انجام می دهید!"
"FreedomStrategy" = "Freedom استراتژی پروتکل" "FreedomStrategy" = "روش استفاده از شبکه خروجی مستقیم"
"FreedomStrategyDesc" = "تعیین می‌کند Freedom استراتژی خروجی شبکه را برای پروتکل" "FreedomStrategyDesc" = "تعیین روش استفاده از خروجی برای پرتکل مستقیم"
"RoutingStrategy" = "استراتژی کلی مسیریابی" "RoutingStrategy" = "پیکربندی استراتژی حل دامنه در مسیریابی"
"RoutingStrategyDesc" = "استراتژی کلی مسیریابی برای حل تمام درخواست‌ها را تعیین می‌کند" "RoutingStrategyDesc" = "تعیین استراتژی مسیریابی کلی برای پیدا کردن دامنه"
"Torrent" = "مسدودسازی پروتکل بیتتورنت" "Torrent" = "فیلتر کردن بیت تورنت"
"TorrentDesc" = "پروتکل بیت تورنت را مسدود می‌کند" "TorrentDesc" = "الگوی تنظیمات را برای فیلتر کردن پروتکل بیت تورنت برای کاربران تغییر میدهد"
"PrivateIp" = "مسدودسازی اتصال آیپیهای خصوصی" "PrivateIp" = "جلوگیری از اتصال آیپی های خصوصی یا محلی"
"PrivateIpDesc" = "اتصال به آیپیهای رنج خصوصی را مسدود می‌کند" "PrivateIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های خصوصی یا محلی و بسته های سرگردان تغییر میدهد"
"Ads" = "مسدودسازی تبلیغات" "Ads" = "مسدود کردن تبلیغات"
"AdsDesc" = "وب‌سایت‌های تبلیغاتی را مسدود می‌کند" "AdsDesc" = "الگوی تنظیمات را برای مسدود کردن تبلیغات تغییر میدهد"
"Family" = "محافظت خانواده" "Family" = "فعال کردن حالت خانواده"
"FamilyDesc" = "محتوای مخصوص بزرگسالان، و وبسایتهای ناامن را مسدود می‌کند" "FamilyDesc" = "برای جلوگیری از ارتباط با وبسایت های ناامن"
"Security" = "محافظت امنیتی" "Speedtest" = "جلوگیری از اتصال به سایت های تست سرعت"
"SecurityDesc" = "وب‌سایت‌های ناامن، بدافزار، فیشینگ، و کریپتوماینرها را مسدود می‌کند" "SpeedtestDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال به سایت های تست سرعت تغییر میدهد"
"Speedtest" = "مسدودسازی اسپیدتست" "IRIp" = "جلوگیری از اتصال آیپی های ایران"
"SpeedtestDesc" = "اتصال به وب‌سایت‌های تست سرعت را مسدود می‌کند" "IRIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های ایران تغییر میدهد"
"IRIp" = "مسدودسازی اتصال به آی‌پی‌های ایران" "IRDomain" = "جلوگیری از اتصال دامنه های ایران"
"IRIpDesc" = "اتصال به آی‌پی‌های کشور ایران را مسدود می‌کند" "IRDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های ایران تغییر میدهد"
"IRDomain" = "مسدودسازی اتصال به دامنه‌های‌ ایران" "ChinaIp" = "جلوگیری از اتصال آیپی های چین"
"IRDomainDesc" = "اتصال به دامنه‌های کشور ایران را مسدود می‌کند" "ChinaIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های چین تغییر میدهد"
"ChinaIp" = "مسدودسازی اتصال به آی‌‌پی‌های چین" "ChinaDomain" = "جلوگیری از اتصال دامنه های چین"
"ChinaIpDesc" = "اتصال به آی‌پی‌های کشور چین را مسدود می‌کند" "ChinaDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های چین تغییر میدهد"
"ChinaDomain" = "مسدودسازی اتصال به دامنه‌های چین" "RussiaIp" = "جلوگیری از اتصال آیپی های روسیه"
"ChinaDomainDesc" = "اتصال به دامنه‌های کشور چین را مسدود می‌کند" "RussiaIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های روسیه تغییر میدهد"
"RussiaIp" = "مسدودسازی اتصال به آی‌پی‌های روسیه" "RussiaDomain" = "جلوگیری از اتصال دامنه های روسیه"
"RussiaIpDesc" = "اتصال به آی‌پی‌های کشور روسیه را مسدود می‌کند" "RussiaDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های روسیه تغییر میدهد"
"RussiaDomain" = "مسدودسازی اتصال به دامنه‌های روسیه" "VNIp" = "جلوگیری از اتصال آیپی های ویتنام"
"RussiaDomainDesc" = "اتصال به دامنه‌های کشور روسیه را مسدود می‌کند" "VNIpDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال آیپی های ویتنام تغییر میدهد"
"VNIp" = "مسدودسازی اتصال به آی‌پی‌های ویتنام" "VNDomain" = "جلوگیری از اتصال دامنه های ویتنام"
"VNIpDesc" = "اتصال به آی‌پی‌های کشور ویتنام را مسدود می‌کند" "VNDomainDesc" = "الگوی تنظیمات را برای فیلتر کردن اتصال دامنه های ویتنام تغییر میدهد"
"VNDomain" = "مسدودسازی اتصال به دامنه های ویتنام" "DirectIRIp" = "ارتباط مستقیم به آیپی های ایران"
"VNDomainDesc" = "اتصال به دامنه‌های کشور ویتنام را مسدود می‌کند" "DirectIRIpDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به آیپی های ایران تغییر میدهد"
"DirectIRIp" = "اتصال مستقیم آی‌پی‌های ایران" "DirectIRDomain" = "ارتباط مستقیم به دامنه های ایران"
"DirectIRIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور ایران" "DirectIRDomainDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به دامنه های ایران تغییر میدهد"
"DirectIRDomain" = "اتصال مستقیم دامنه‌های ایران" "DirectChinaIp" = "ارتباط مستقیم به آیپی های چین"
"DirectIRDomainDesc" = "اتصال مستقیم به دامنه‌های کشور ایران" "DirectChinaIpDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به آیپی های چین تغییر میدهد"
"DirectChinaIp" = "اتصال مستقیم آی‌پی‌های چین" "DirectChinaDomain" = "ارتباط مستقیم به دامنه های چین"
"DirectChinaIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور چین" "DirectChinaDomainDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به دامنه های چین تغییر میدهد"
"DirectChinaDomain" = "اتصال مستقیم دامنه‌های چین" "DirectRussiaIp" = "ارتباط مستقیم به آیپی های روسیه"
"DirectChinaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور چین" "DirectRussiaIpDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به آیپی های روسیه تغییر میدهد"
"DirectRussiaIp" = "اتصال مستقیم آی‌پی‌های روسیه" "DirectRussiaDomain" = "ارتباط مستقیم به دامنه های روسیه"
"DirectRussiaIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور روسیه" "DirectRussiaDomainDesc" = "الگوی تنظیمات را برای ارتباط مستقیم به دامنه های روسیه تغییر میدهد"
"DirectRussiaDomain" = "اتصال مستقیم دامنه‌های روسیه" "DirectVNIp" = "اتصال مستقیم به ای پی های ویتنام"
"DirectRussiaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور روسیه" "DirectVNIpDesc" = "الگوی پیکربندی را برای اتصال مستقیم به محدوده آی پی های ویتنام تغییر میدهد"
"DirectVNIp" = "اتصال مستقیم آی‌پی‌های ویتنام" "DirectVNDomain" = "اتصال مستقیم به دامنه های ویتنام"
"DirectVNIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور ویتنام" "DirectVNDomainDesc" = "الگوی پیکربندی را برای اتصال مستقیم به دامنه های ویتنام تغییر میدهد"
"DirectVNDomain" = "اتصال مستقیم دامنه‌های ویتنام" "GoogleIPv4" = "استفاده از آیپی ورژن 4 برای اتصال به گوگل"
"DirectVNDomainDesc" = "اتصال مستقیم به دامنه‌های کشور ویتنام" "GoogleIPv4Desc" = "مسیردهی جدید برای اتصال به گوگل با آیپی ورژن 4 اضافه میکند"
"GoogleIPv4" = "گوگل" "NetflixIPv4" = "استفاده از آیپی ورژن 4 برای اتصال به نتفلیکس"
"GoogleIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به گوگل هدایت میکند" "NetflixIPv4Desc" = "مسیردهی جدید برای اتصال به نتفلیکس با آیپی ورژن 4 اضافه میکند"
"NetflixIPv4" = "نتفلیکس" "GoogleWARP" = "مسیردهی گوگل به WARP"
"NetflixIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به نتفلیکس هدایت میکند" "GoogleWARPDesc" = "مسیردهی جدید برای اتصال به گوگل به WARP اضافه میکند"
"GoogleWARP" = "گوگل" "OpenAIWARP" = "مسیردهی OpenAI (ChatGPT) به WARP"
"GoogleWARPDesc" = "ترافیک را از طریق وارپ به گوگل هدایت میکند" "OpenAIWARPDesc" = "مسیردهی جدید برای اتصال به OpenAI (ChatGPT) به WARP اضافه میکند"
"OpenAIWARP" = "چت جی‌پی‌تی" "NetflixWARP" = "مسیردهی نتفلیکس به WARP"
"OpenAIWARPDesc" = "ترافیک را از طریق وارپ به چت جی‌پی‌تی هدایت میکند" "NetflixWARPDesc" = "مسیردهی جدید برای اتصال به نتفلیکس به WARP اضافه میکند"
"NetflixWARP" = "نتفلیکس" "SpotifyWARP" = "مسیردهی اسپاتیفای به WARP"
"NetflixWARPDesc" = "ترافیک را از طریق وارپ به نتفلیکس هدایت میکند" "SpotifyWARPDesc" = "مسیردهی جدید برای اتصال به اسپاتیفای به WARP اضافه میکند"
"SpotifyWARP" = "اسپاتیفای" "IRWARP" = "مسیردهی دامنه های ایران به WARP"
"SpotifyWARPDesc" = " ترافیک را از طریق وارپ به اسپاتیفای هدایت میکند" "IRWARPDesc" = "مسیردهی جدید برای اتصال به دامنه های ایران به WARP اضافه میکند"
"IRWARP" = "دامنه‌های ایران"
"IRWARPDesc" = "ترافیک را از طریق وارپ به دامنه‌های کشور ایران هدایت می‌کند"
"Inbounds" = "ورودی‌ها" "Inbounds" = "ورودی‌ها"
"InboundsDesc" = "پذیرش کلاینت خاص" "InboundsDesc" = "میتوانید الگوی تنظیمات را برای ورودی های خاص تنظیم نمایید"
"Outbounds" = "خروجی‌ها" "Outbounds" = "خروجی‌ها"
"OutboundsDesc" = سیر ترافیک خروجی را تنظیم کنید" "OutboundsDesc" = یتوانید الگوی تنظیمات را برای خروجی اینترنت تنظیم نمایید"
"Routings" = "قوانین مسیریابی" "Routings" = "قوانین مسیریابی"
"RoutingsDesc" = "اولویت هر قانون مهم است" "RoutingsDesc" = "اولویت هر قانون مهم است!"
"completeTemplate" = "کامل" "completeTemplate" = "کامل"
[pages.xray.rules] [pages.xray.rules]
@@ -419,31 +411,23 @@
"domain" = "دامنه" "domain" = "دامنه"
"type" = "نوع" "type" = "نوع"
"bridge" = "پل" "bridge" = "پل"
"portal" = ورتال" "portal" = "پرتال"
"intercon" = "اتصال میانی" "intercon" = "اتصال میانی"
[pages.xray.wireguard]
"secretKey" = "کلید شخصی"
"publicKey" = "کلید عمومی"
"allowedIPs" = "آی‌پی‌های مجاز"
"endpoint" = "نقطه پایانی"
"psk" = "کلید مشترک"
"domainStrategy" = "استراتژی حل دامنه"
[pages.settings.security] [pages.settings.security]
"admin" = "مدیر" "admin" = "مدیر"
"secret" = "توکن مخفی" "secret" = "توکن امنیتی"
"loginSecurity" = "ورود ایمن" "loginSecurity" = "لاگین ایمن"
"loginSecurityDesc" = "یک لایه اضافی از احراز هویت برای ایجاد امنیت بیشتر اضافه می کند" "loginSecurityDesc" = "افزودن یک مرحله دیگر به فرآیند لاگین"
"secretToken" = "توکن مخفی" "secretToken" = "توکن امنیتی"
"secretTokenDesc" = "لطفاً این توکن را در مکانی امن ذخیره کنید. این توکن برای ورود به سیستم مورد نیاز است و قابل بازیابی نیست" "secretTokenDesc" = "این کد امنیتی را نزد خود در این جای امن نگه دارید، بدون این کد امکان ورود به پنل را نخواهید داشت. امکان بازیابی آن وجود ندارد!"
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "ویرایش تنظیمات" "modifySettings" = "ویرایش تنظیمات"
"getSettings" = "دریافت تنظیمات" "getSettings" = "دریافت تنظیمات"
"modifyUser" = "ویرایش مدیر" "modifyUser" = "ویرایش کاربر"
"originalUserPassIncorrect" = "نامکاربری یا رمزعبور فعلی اشتباه‌است" "originalUserPassIncorrect" = "نام کاربری و رمز عبور فعلی اشتباه می باشد "
"userPassMustBeNotEmpty" = "نامکاربری یا رمزعبور جدید خالی‌است" "userPassMustBeNotEmpty" = "نام کاربری و رمز عبور جدید نمیتواند خالی باشد "
[tgbot] [tgbot]
"keyboardClosed" = "❌ کیبورد سفارشی بسته شد!" "keyboardClosed" = "❌ کیبورد سفارشی بسته شد!"
@@ -453,17 +437,14 @@
"noIpRecord" = "❗ رکورد IP یافت نشد!" "noIpRecord" = "❗ رکورد IP یافت نشد!"
"noInbounds" = "❗ هیچ ورودی یافت نشد!" "noInbounds" = "❗ هیچ ورودی یافت نشد!"
"unlimited" = "♾ نامحدود" "unlimited" = "♾ نامحدود"
"add" = "اضافه کردن"
"month" = "ماه" "month" = "ماه"
"months" = "ماه‌ها" "months" = "ماه‌ها"
"day" = "روز" "day" = "روز"
"days" = "روزها" "days" = "روزها"
"hours" = "ساعتها" "hours" = "ساعت ها"
"unknown" = "نامشخص" "unknown" = "نامشخص"
"inbounds" = "ورودی‌ها" "inbounds" = "ورودی‌ها"
"clients" = "کلاینت‌ها" "clients" = "کلاینت‌ها"
"offline" = "🔴 آفلاین"
"online" = "🟢 آنلاین"
[tgbot.commands] [tgbot.commands]
"unknown" = "❗ دستور ناشناخته" "unknown" = "❗ دستور ناشناخته"
@@ -474,69 +455,63 @@
"status" = "✅ ربات در حالت عادی است!" "status" = "✅ ربات در حالت عادی است!"
"usage" = "❗ لطفاً یک متن برای جستجو وارد کنید!" "usage" = "❗ لطفاً یک متن برای جستجو وارد کنید!"
"getID" = "🆔 شناسه شما: <code>{{ .ID }}</code>" "getID" = "🆔 شناسه شما: <code>{{ .ID }}</code>"
"helpAdminCommands" = "برای جستجوی ایمیل مشتری:\r\n<code>/usage [ایمیل]</code>\r\n\r\nبرای جستجوی ورودی‌ها (با آمار مشتری):\r\n<code>/inbound [توضیح]</code>" "helpAdminCommands" = "برای جستجوی ایمیل مشتری:\r\n<code>/usage [ایمیل]</code>\r\n \r\nبرای جستجوی ورودی‌ها (با آمار مشتری):\r\n<code>/inbound [توضیح]</code>"
"helpClientCommands" = "برای جستجوی آمار، فقط از دستور زیر استفاده کنید:\r\n\r\n<code>/usage [Email]</code>" "helpClientCommands" = "برای جستجوی آمار، فقط از دستور زیر استفاده کنید:\r\n \r\n<code>/usage [UUID|رمز عبور]</code>\r\n \r\nاز UUID برای vmess/vless و از رمز عبور برای Trojan استفاده کنید."
[tgbot.messages] [tgbot.messages]
"cpuThreshold" = "🔴 بار ‌پردازنده {{ .Percent }}% بیشتر از آستانه است {{ .Threshold }}%" "cpuThreshold" = "🔴 میزان استفاده از CPU {{ .Percent }}% بیشتر از آستانه {{ .Threshold }}% است."
"selectUserFailed" = "❌ خطا در انتخاب کاربر!" "selectUserFailed" = "❌ خطا در انتخاب کاربر!"
"userSaved" = "✅ کاربر تلگرام ذخیره شد." "userSaved" = "✅ کاربر تلگرام ذخیره شد."
"loginSuccess" = "✅ با موفقیت به پنل وارد شدید.\r\n" "loginSuccess" = "✅ با موفقیت به پنل وارد شدید.\r\n"
"loginFailed" = "❗️ ورود به پنل ناموفقبود \r\n" "loginFailed" = "❗️ ورود به پنل ناموفق بود.\r\n"
"report" = "🕰 گزارشاتزمان‌بندیشده: {{ .RunTime }}\r\n" "report" = "🕰 گزارشات زمان‌بندی شده: {{ .RunTime }}\r\n"
"datetime" = "⏰ تاریخ‌وزمان: {{ .DateTime }}\r\n" "datetime" = "⏰ تاریخ-زمان: {{ .DateTime }}\r\n"
"hostname" = "💻 ناممیزبان: {{ .Hostname }}\r\n" "hostname" = "💻 نام میزبان: {{ .Hostname }}\r\n"
"version" = "🚀 نسخه‌پنل: {{ .Version }}\r\n" "version" = "🚀 نسخه X-UI: {{ .Version }}\r\n"
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n" "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n" "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
"ip" = "🌐 آدرس‌آی‌پی: {{ .IP }}\r\n" "ip" = "🌐 آدرس IP: {{ .IP }}\r\n"
"ips" = "🔢 آدرس‌های آی‌پی:\r\n{{ .IPs }}\r\n" "ips" = "🔢 آدرس‌های IP: \r\n{{ .IPs }}\r\n"
"serverUpTime" = "⏳ مدت‌کارکردسیستم: {{ .UpTime }} {{ .Unit }}\r\n" "serverUpTime" = "⏳ زمان کارکرد سرور: {{ .UpTime }} {{ .Unit }}\r\n"
"serverLoad" = "📈 بارسیستم: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n" "serverLoad" = "📈 بار سرور: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n" "serverMemory" = "📋 حافظه سرور: {{ .Current }}/{{ .Total }}\r\n"
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n" "tcpCount" = "🔹 تعداد ترافیک TCP: {{ .Count }}\r\n"
"udpCount" = "🔸 UDP: {{ .Count }}\r\n" "udpCount" = "🔸 تعداد ترافیک UDP: {{ .Count }}\r\n"
"traffic" = "🚦 ترافیک: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n" "traffic" = "🚦 ترافیک: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
"xrayStatus" = " وضعیت‌ایکس‌ری: {{ .State }}\r\n" "xrayStatus" = " وضعیت Xray: {{ .State }}\r\n"
"username" = "👤 نامکاربری: {{ .Username }}\r\n" "username" = "👤 نام کاربری: {{ .Username }}\r\n"
"time" = "⏰ زمان: {{ .Time }}\r\n" "time" = "⏰ زمان: {{ .Time }}\r\n"
"inbound" = "📍 نام‌ورودی: {{ .Remark }}\r\n" "inbound" = "📍 ورودی: {{ .Remark }}\r\n"
"port" = "🔌 پورت: {{ .Port }}\r\n" "port" = "🔌 پورت: {{ .Port }}\r\n"
"expire" = "📅 تاریخانقضا: {{ .Time }}\r\n\r\n" "expire" = "📅 تاریخ انقضا: {{ .Time }}\r\n"
"expireIn" = "📅 باقیمانده‌تاانقضا: {{ .Time }}\r\n\r\n" "expireIn" = "📅 باقیمانده از انقضا: {{ .Time }}\r\n"
"active" = "💡 فعال: {{ .Enable }}\r\n" "active" = "💡 فعال: \r\n"
"enabled" = "🚨 وضعیت: {{ .Enable }}\r\n" "inactive" = "💡 فعال: ❌\r\n"
"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
"email" = "📧 ایمیل: {{ .Email }}\r\n" "email" = "📧 ایمیل: {{ .Email }}\r\n"
"upload" = "🔼 آپلود↑: {{ .Upload }}\r\n" "upload" = "🔼 آپلود↑: {{ .Upload }}\r\n"
"download" = "🔽 دانلود↓: {{ .Download }}\r\n" "download" = "🔽 دانلود↓: {{ .Download }}\r\n"
"total" = "🔄 کل: {{ .UpDown }} / {{ .Total }}\r\n" "total" = "📊 کل: {{ .UpDown }} / {{ .Total }}\r\n"
"TGUser" = "👤 کاربر تلگرام: {{ .TelegramID }}\r\n" "TGUser" = "👤 کاربر تلگرام: {{ .TelegramID }}\r\n"
"exhaustedMsg" = "🚨 {{ .Type }} بهاتمامرسیدهاست:\r\n" "exhaustedMsg" = "🚨 {{ .Type }} به اتمام رسیده است:\r\n"
"exhaustedCount" = "🚨 تعداد {{ .Type }} بهاتمامرسیده‌است:\r\n" "exhaustedCount" = "🚨 تعداد {{ .Type }} به اتمام رسیده:\r\n"
"onlinesCount" = "🌐 کاربران‌آنلاین: {{ .Count }}\r\n"
"disabled" = "🛑 غیرفعال: {{ .Disabled }}\r\n" "disabled" = "🛑 غیرفعال: {{ .Disabled }}\r\n"
"depleteSoon" = "🔜 بهزودیبهپایانخواهدرسید: {{ .Deplete }}\r\n\r\n" "depleteSoon" = "🔜 به زودی به پایان خواهد رسید: {{ .Deplete }}\r\n \r\n"
"backupTime" = "🗄 زمانپشتیبان‌گیری: {{ .Time }}\r\n" "backupTime" = "🗄 زمان پشتیبان‌گیری: {{ .Time }}\r\n"
"refreshedOn" = "\r\n📋🔄 تازه‌سازی شده در: {{ .Time }}\r\n\r\n" "refreshedOn" = "\r\n📋🔄 تازه‌سازی شده در: {{ .Time }}\r\n \r\n"
"yes" = "✅ بله"
"no" = "❌ خیر"
[tgbot.buttons] [tgbot.buttons]
"closeKeyboard" = "❌ بستن کیبورد" "closeKeyboard" = "❌ بستن کیبورد"
"cancel" = "❌ لغو" "cancel" = "❌ لغو"
"cancelReset" = "❌ لغو تنظیم مجدد" "cancelReset" = "❌ لغو تنظیم مجدد"
"cancelIpLimit" = "❌ لغو محدودیت آی‌پی" "cancelIpLimit" = "❌ لغو محدودیت IP"
"confirmResetTraffic" = "✅ تأیید تنظیم مجدد ترافیک؟" "confirmResetTraffic" = "✅ تأیید تنظیم مجدد ترافیک؟"
"confirmClearIps" = "✅ تأیید پاک‌سازی آدرس‌های آی‌پی؟" "confirmClearIps" = "✅ تأیید پاک‌سازی آدرس‌های IP؟"
"confirmRemoveTGUser" = "✅ تأیید حذف کاربر تلگرام؟" "confirmRemoveTGUser" = "✅ تأیید حذف کاربر تلگرام؟"
"confirmToggle" = "✅ تایید فعال/غیرفعال کردن کاربر؟" "dbBackup" = "دریافت پشتیبان پایگاه داده"
"dbBackup" = "دریافت پشتیبان" "serverUsage" = "استفاده از سرور"
"serverUsage" = "استفاده از سیستم"
"getInbounds" = "دریافت ورودی‌ها" "getInbounds" = "دریافت ورودی‌ها"
"depleteSoon" = "بهزودی به پایان خواهد رسید" "depleteSoon" = "به زودی به پایان خواهد رسید"
"clientUsage" = "دریافت آمار کاربر" "clientUsage" = "دریافت آمار کاربر"
"onlines" = "کاربران آنلاین"
"commands" = "دستورات" "commands" = "دستورات"
"refresh" = "🔄 تازه‌سازی" "refresh" = "🔄 تازه‌سازی"
"clearIPs" = "❌ پاک‌سازی آدرس‌ها" "clearIPs" = "❌ پاک‌سازی آدرس‌ها"
@@ -550,10 +525,8 @@
"setTGUser" = "👤 تنظیم کاربر تلگرام" "setTGUser" = "👤 تنظیم کاربر تلگرام"
"toggle" = "🔘 فعال / غیرفعال" "toggle" = "🔘 فعال / غیرفعال"
"custom" = "🔢 سفارشی" "custom" = "🔢 سفارشی"
"confirmNumber" = "✅ تایید: {{ .Num }}" "confirmNumber" = "✅ تایید : {{ .Num }}"
"confirmNumberAdd" = "✅ تایید اضافه کردن: {{ .Num }}"
"limitTraffic" = "🚧 محدودیت ترافیک" "limitTraffic" = "🚧 محدودیت ترافیک"
"getBanLogs" = "گزارش های بلوک را دریافت کنید"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ انجام شد!" "successfulOperation" = "✅ انجام شد!"
@@ -573,4 +546,5 @@
"removedTGUserSuccess" = "✅ {{ .Email }} : کاربر تلگرام با موفقیت حذف شد." "removedTGUserSuccess" = "✅ {{ .Email }} : کاربر تلگرام با موفقیت حذف شد."
"enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد." "enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد."
"disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد." "disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد."
"askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <code>{{ .TgUserID }}</code>" "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>"

View File

@@ -62,7 +62,7 @@
"link" = "менеджмент" "link" = "менеджмент"
[pages.login] [pages.login]
"title" = "Добро пожаловать" "title" = "Логин"
"loginAgain" = "Время пребывания в сети вышло. Пожалуйста, войдите в систему снова" "loginAgain" = "Время пребывания в сети вышло. Пожалуйста, войдите в систему снова"
[pages.login.toasts] [pages.login.toasts]
@@ -77,9 +77,9 @@
"memory" = "Память" "memory" = "Память"
"hard" = "Жесткий диск" "hard" = "Жесткий диск"
"xrayStatus" = "Статус" "xrayStatus" = "Статус"
"stopXray" = "Остановить" "stopXray" = "Остановить Xray"
"restartXray" = "Перезапустить" "restartXray" = "Перезапустить Xray"
"xraySwitch" = "Версия" "xraySwitch" = "Переключить версию"
"xraySwitchClick" = "Выберите желаемую версию" "xraySwitchClick" = "Выберите желаемую версию"
"xraySwitchClickDesk" = "Выбирайте внимательно, так как старые версии могут быть несовместимы с текущими конфигурациями" "xraySwitchClickDesk" = "Выбирайте внимательно, так как старые версии могут быть несовместимы с текущими конфигурациями"
"operationHours" = "Время работы системы" "operationHours" = "Время работы системы"
@@ -134,7 +134,7 @@
"destinationPort" = "Порт назначения" "destinationPort" = "Порт назначения"
"targetAddress" = "Целевой адрес" "targetAddress" = "Целевой адрес"
"monitorDesc" = "Оставьте пустым по умолчанию" "monitorDesc" = "Оставьте пустым по умолчанию"
"meansNoLimit" = " = Без ограничений (значение: ГБ)" "meansNoLimit" = "Значит без ограничений"
"totalFlow" = "Общий расход" "totalFlow" = "Общий расход"
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало" "leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
"noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию" "noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию"
@@ -173,12 +173,12 @@
"setDefaultCert" = "Установить сертификат с панели" "setDefaultCert" = "Установить сертификат с панели"
"xtlsDesc" = "Версия Xray должна быть не ниже 1.7.5" "xtlsDesc" = "Версия Xray должна быть не ниже 1.7.5"
"realityDesc" = "Версия Xray должна быть не ниже 1.8.0" "realityDesc" = "Версия Xray должна быть не ниже 1.8.0"
"telegramDesc" = "Используйте только ID чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)" "telegramDesc" = "Используйте идентификатор Telegram без символа @ или идентификатора чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций" "subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
"info" = "Информация" "info" = "Информация"
"same" = "Тот же" "same" = "Тот же"
"inboundData" = "Входящие данные" "inboundData" = "Входящие данные"
"exportInbound" = "Экспорт входящих" "copyToClipboard" = "Копировать в буфер обмена"
"import" = "Импортировать" "import" = "Импортировать"
"importInbound" = "Импортировать входящее сообщение" "importInbound" = "Импортировать входящее сообщение"
@@ -195,27 +195,26 @@
"prefix" = "Префикс" "prefix" = "Префикс"
"postfix" = "Постфикс" "postfix" = "Постфикс"
"delayedStart" = "Начать с момента первого подключения" "delayedStart" = "Начать с момента первого подключения"
"expireDays" = "Длительность" "expireDays" = "Срок действия"
"days" = "дней" "days" = "дней"
"renew" = "Автопродление" "renew" = "Автопродление"
"renewDesc" = "Автопродление после истечения срока действия. (0 = отключить)(единица: день)" "renewDesc" = "Автоматическое продление через несколько дней после истечения срока действия. 0 = отключить"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Получить" "obtain" = "Получить"
[pages.inbounds.stream.general] [pages.inbounds.stream.general]
"request" = "Запрос" "requestHeader" = "Заголовок запроса"
"response" = "Ответ"
"name" = "Имя" "name" = "Имя"
"value" = "Ценить" "value" = "Значение"
[pages.inbounds.stream.tcp] [pages.inbounds.stream.tcp]
"version" = "Версия" "requestVersion" = "Версия запроса"
"method" = "Метод" "requestMethod" = "Метод запроса"
"path" = "Путь" "requestPath" = "Путь запроса"
"status" = "Положение дел" "responseVersion" = "Версия ответа"
"statusDescription" = "Описание статуса" "responseStatus" = "Статус ответа"
"requestHeader" = "Заголовок запроса" "responseStatusDescription" = "Описание статуса ответа"
"responseHeader" = "Заголовок ответа" "responseHeader" = "Заголовок ответа"
[pages.inbounds.stream.quic] [pages.inbounds.stream.quic]
@@ -247,9 +246,6 @@
"pageSize" = "Размер нумерации страниц" "pageSize" = "Размер нумерации страниц"
"pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить" "pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить"
"remarkModel" = "Модель примечания и символ разделения" "remarkModel" = "Модель примечания и символ разделения"
"datepicker" = "выбор даты"
"datepickerPlaceholder" = "Выберите дату"
"datepickerDescription" = "Тип календаря выбора указывает дату истечения срока действия."
"sampleRemark" = "Пример замечания" "sampleRemark" = "Пример замечания"
"oldUsername" = "Текущее имя пользователя" "oldUsername" = "Текущее имя пользователя"
"currentPassword" = "Текущий пароль" "currentPassword" = "Текущий пароль"
@@ -259,8 +255,6 @@
"telegramBotEnableDesc" = "Подключайтесь к функциям этой панели через Telegram бота" "telegramBotEnableDesc" = "Подключайтесь к функциям этой панели через Telegram бота"
"telegramToken" = "Токен Telegram бота" "telegramToken" = "Токен Telegram бота"
"telegramTokenDesc" = "Необходимо получить токен у менеджера ботов Telegram @botfather" "telegramTokenDesc" = "Необходимо получить токен у менеджера ботов Telegram @botfather"
"telegramProxy" = "Прокси Socks5"
"telegramProxyDesc" = "Если для подключения к Telegram вам нужен прокси Socks5. Настройте его параметры согласно руководству."
"telegramChatId" = "Telegram ID админа бота" "telegramChatId" = "Telegram ID админа бота"
"telegramChatIdDesc" = "Множественные идентификаторы чата, разделенные запятыми. Чтобы получить свои идентификаторы чатов, используйте @userinfobot или команду '/id' в боте." "telegramChatIdDesc" = "Множественные идентификаторы чата, разделенные запятыми. Чтобы получить свои идентификаторы чатов, используйте @userinfobot или команду '/id' в боте."
"telegramNotifyTime" = "Частота уведомлений бота Telegram" "telegramNotifyTime" = "Частота уведомлений бота Telegram"
@@ -333,10 +327,8 @@
"PrivateIpDesc" = "Изменение шаблона конфигурации для предупреждения подключения к диапазонам частных IP-адресов" "PrivateIpDesc" = "Изменение шаблона конфигурации для предупреждения подключения к диапазонам частных IP-адресов"
"Ads" = "Блокировка рекламы" "Ads" = "Блокировка рекламы"
"AdsDesc" = "Изменение конфигурации для блокировки рекламы" "AdsDesc" = "Изменение конфигурации для блокировки рекламы"
"Family" = "Блокируйте вредоносное ПО и контент для взрослых" "Family" = "Блокировать вредоносное ПО и контент для взрослых"
"FamilyDesc" = "DNS-преобразователи Cloudflare для блокировки вредоносного ПО и контента для взрослых в целях защиты семьи." "FamilyDesc" = "Резольверы DNS для блокировки вредоносных программ и контента для взрослых для защиты семьи"
"Security" = "Блокируйте вредоносное ПО, фишинговые сайты и сайты криптомайнеров"
"SecurityDesc" = "Изменение шаблона конфигурации для защиты безопасности."
"Speedtest" = "Блокировать сайты для проверки скорости" "Speedtest" = "Блокировать сайты для проверки скорости"
"SpeedtestDesc" = "Изменение шаблона конфигурации для предупреждения подключения к веб-сайтам для тестирования скорости" "SpeedtestDesc" = "Изменение шаблона конфигурации для предупреждения подключения к веб-сайтам для тестирования скорости"
"IRIp" = "Заблокировать подключения к диапазонам IP-адресов Ирана" "IRIp" = "Заблокировать подключения к диапазонам IP-адресов Ирана"
@@ -422,14 +414,6 @@
"portal" = "Портал" "portal" = "Портал"
"intercon" = "Соединение" "intercon" = "Соединение"
[pages.xray.wireguard]
"secretKey" = "Секретный ключ"
"publicKey" = "Открытый ключ"
"allowedIPs" = "Разрешенные IP-адреса"
"endpoint" = "Конечная точка"
"psk" = "Общий ключ"
"domainStrategy" = "Стратегия домена"
[pages.settings.security] [pages.settings.security]
"admin" = "Админ" "admin" = "Админ"
"secret" = "Секретный токен" "secret" = "Секретный токен"
@@ -453,7 +437,6 @@
"noIpRecord" = "❗ Нет записей об IP-адресе!" "noIpRecord" = "❗ Нет записей об IP-адресе!"
"noInbounds" = "❗ Входящих соединений не найдено!" "noInbounds" = "❗ Входящих соединений не найдено!"
"unlimited" = "♾ Неограниченно" "unlimited" = "♾ Неограниченно"
"add" = "Добавить"
"month" = "Месяц" "month" = "Месяц"
"months" = "Месяцев" "months" = "Месяцев"
"day" = "День" "day" = "День"
@@ -462,8 +445,6 @@
"unknown" = "Неизвестно" "unknown" = "Неизвестно"
"inbounds" = "Входящие" "inbounds" = "Входящие"
"clients" = "Клиенты" "clients" = "Клиенты"
"offline" = "🔴 Офлайн"
"online" = "🟢 Онлайн"
[tgbot.commands] [tgbot.commands]
"unknown" = "❗ Неизвестная команда" "unknown" = "❗ Неизвестная команда"
@@ -474,8 +455,8 @@
"status" = "✅ Бот работает нормально!" "status" = "✅ Бот работает нормально!"
"usage" = "❗ Пожалуйста, укажите текст для поиска!" "usage" = "❗ Пожалуйста, укажите текст для поиска!"
"getID" = "🆔 Ваш ID: <code>{{ .ID }}</code>" "getID" = "🆔 Ваш ID: <code>{{ .ID }}</code>"
"helpAdminCommands" = "Поиск по электронной почте клиента:\r\n<code>/usage [Email]</code>\r\n\r\nПоиск входящих соединений (со статистикой клиента):\r\n<code>/inbound [Remark]</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 [Email]</code>" "helpClientCommands" = "Для получения статистики используйте следующую команду:\r\n \r\n<code>/usage [UUID|Password]</code>\r\n \r\nИспользуйте UUID для vmess/vless и пароль для Trojan."
[tgbot.messages] [tgbot.messages]
"cpuThreshold" = "🔴 Загрузка процессора составляет {{ .Percent }}%, что превышает пороговое значение {{ .Threshold }}%" "cpuThreshold" = "🔴 Загрузка процессора составляет {{ .Percent }}%, что превышает пороговое значение {{ .Threshold }}%"
@@ -490,7 +471,7 @@
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n" "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n" "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
"ip" = "🌐 IP: {{ .IP }}\r\n" "ip" = "🌐 IP: {{ .IP }}\r\n"
"ips" = "🔢 IP-адреса:\r\n{{ .IPs }}\r\n" "ips" = "🔢 IP-адреса: \r\n{{ .IPs }}\r\n"
"serverUpTime" = "⏳ Время работы сервера: {{ .UpTime }} {{ .Unit }}\r\n" "serverUpTime" = "⏳ Время работы сервера: {{ .UpTime }} {{ .Unit }}\r\n"
"serverLoad" = "📈 Загрузка сервера: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n" "serverLoad" = "📈 Загрузка сервера: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
"serverMemory" = "📋 Память сервера: {{ .Current }}/{{ .Total }}\r\n" "serverMemory" = "📋 Память сервера: {{ .Current }}/{{ .Total }}\r\n"
@@ -504,9 +485,8 @@
"port" = "🔌 Порт: {{ .Port }}\r\n" "port" = "🔌 Порт: {{ .Port }}\r\n"
"expire" = "📅 Дата окончания: {{ .Time }}\r\n" "expire" = "📅 Дата окончания: {{ .Time }}\r\n"
"expireIn" = "📅 Окончание через: {{ .Time }}\r\n" "expireIn" = "📅 Окончание через: {{ .Time }}\r\n"
"active" = "💡 Активен: {{ .Enable }}\r\n" "active" = "💡 Активен: ✅ Да\r\n"
"enabled" = "🚨 Включен: {{ .Enable }}\r\n" "inactive" = "💡 Активен: ❌ Нет\r\n"
"online" = "🌐 Статус соединения: {{ .Status }}\r\n"
"email" = "📧 Email: {{ .Email }}\r\n" "email" = "📧 Email: {{ .Email }}\r\n"
"upload" = "🔼 Исходящий трафик: ↑{{ .Upload }}\r\n" "upload" = "🔼 Исходящий трафик: ↑{{ .Upload }}\r\n"
"download" = "🔽 Входящий трафик: ↓{{ .Download }}\r\n" "download" = "🔽 Входящий трафик: ↓{{ .Download }}\r\n"
@@ -514,13 +494,10 @@
"TGUser" = "👤 Пользователь Telegram: {{ .TelegramID }}\r\n" "TGUser" = "👤 Пользователь Telegram: {{ .TelegramID }}\r\n"
"exhaustedMsg" = "🚨 Исчерпаны {{ .Type }}:\r\n" "exhaustedMsg" = "🚨 Исчерпаны {{ .Type }}:\r\n"
"exhaustedCount" = "🚨 Количество исчерпанных {{ .Type }}:\r\n" "exhaustedCount" = "🚨 Количество исчерпанных {{ .Type }}:\r\n"
"onlinesCount" = "🌐 Клиентов онлайн: {{ .Count }}\r\n"
"disabled" = "🛑 Отключено: {{ .Disabled }}\r\n" "disabled" = "🛑 Отключено: {{ .Disabled }}\r\n"
"depleteSoon" = "🔜 Скоро исчерпание: {{ .Deplete }}\r\n\r\n" "depleteSoon" = "🔜 Скоро исчерпание: {{ .Deplete }}\r\n \r\n"
"backupTime" = "🗄 Время резервного копирования: {{ .Time }}\r\n" "backupTime" = "🗄 Время резервного копирования: {{ .Time }}\r\n"
"refreshedOn" = "\r\n📋🔄 Обновлено: {{ .Time }}\r\n\r\n" "refreshedOn" = "\r\n📋🔄 Обновлено: {{ .Time }}\r\n \r\n"
"yes" = "✅ Да"
"no" = "❌ Нет"
[tgbot.buttons] [tgbot.buttons]
"closeKeyboard" = "❌ Закрыть клавиатуру" "closeKeyboard" = "❌ Закрыть клавиатуру"
@@ -530,13 +507,11 @@
"confirmResetTraffic" = "✅ Подтвердить сброс трафика?" "confirmResetTraffic" = "✅ Подтвердить сброс трафика?"
"confirmClearIps" = "✅ Подтвердить очистку IP?" "confirmClearIps" = "✅ Подтвердить очистку IP?"
"confirmRemoveTGUser" = "✅ Подтвердить удаление пользователя Telegram?" "confirmRemoveTGUser" = "✅ Подтвердить удаление пользователя Telegram?"
"confirmToggle" = "✅ Подтвердить вкл/выкл пользователя?"
"dbBackup" = "Получить резервную копию DB" "dbBackup" = "Получить резервную копию DB"
"serverUsage" = "Использование сервера" "serverUsage" = "Использование сервера"
"getInbounds" = "Получить входящие потоки" "getInbounds" = "Получить входящие потоки"
"depleteSoon" = "Скоро исчерпание" "depleteSoon" = "Скоро исчерпание"
"clientUsage" = "Получить использование" "clientUsage" = "Получить использование"
"onlines" = "Онлайн-клиенты"
"commands" = "Команды" "commands" = "Команды"
"refresh" = "🔄 Обновить" "refresh" = "🔄 Обновить"
"clearIPs" = "❌ Очистить IP" "clearIPs" = "❌ Очистить IP"
@@ -544,16 +519,14 @@
"selectTGUser" = "👤 Выбрать пользователя Telegram" "selectTGUser" = "👤 Выбрать пользователя Telegram"
"selectOneTGUser" = "👤 Выберите пользователя Telegram:" "selectOneTGUser" = "👤 Выберите пользователя Telegram:"
"resetTraffic" = "📈 Сбросить трафик" "resetTraffic" = "📈 Сбросить трафик"
"resetExpire" = "📅 Изменить дату окончания" "resetExpire" = "📅 Сбросить дату окончания"
"ipLog" = "🔢 Лог IP" "ipLog" = "🔢 Лог IP"
"ipLimit" = "🔢 Лимит IP" "ipLimit" = "🔢 Лимит IP"
"setTGUser" = "👤 Установить пользователя Telegram" "setTGUser" = "👤 Установить пользователя Telegram"
"toggle" = "🔘 Вкл./Выкл." "toggle" = "🔘 Вкл./Выкл."
"custom" = "🔢 Свой" "custom" = "🔢 Обычай"
"confirmNumber" = "✅ Подтвердить: {{ .Num }}" "confirmNumber" = "✅ Подтвердить : {{ .Num }}"
"confirmNumberAdd" = "✅ Подтвердить добавление: {{ .Num }}"
"limitTraffic" = "🚧 Лимит трафика" "limitTraffic" = "🚧 Лимит трафика"
"getBanLogs" = "Логи блокировок"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ Успешный!" "successfulOperation" = "✅ Успешный!"
@@ -573,4 +546,5 @@
"removedTGUserSuccess" = "✅ {{ .Email }}: Пользователь Telegram успешно удален." "removedTGUserSuccess" = "✅ {{ .Email }}: Пользователь Telegram успешно удален."
"enableSuccess" = "✅ {{ .Email }}: Включено успешно." "enableSuccess" = "✅ {{ .Email }}: Включено успешно."
"disableSuccess" = "✅ {{ .Email }}: Отключено успешно." "disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <code>{{ .TgUserID }}</code>" "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>"

View File

@@ -32,7 +32,7 @@
"transmission" = "Truyền tải" "transmission" = "Truyền tải"
"host" = "Máy chủ" "host" = "Máy chủ"
"path" = "Đường dẫn" "path" = "Đường dẫn"
"camouflage" = "Ngụy trang" "camouflage" = "camouflage"
"status" = "Trạng thái" "status" = "Trạng thái"
"enabled" = "Đã kích hoạt" "enabled" = "Đã kích hoạt"
"disabled" = "Đã tắt" "disabled" = "Đã tắt"
@@ -55,14 +55,14 @@
[menu] [menu]
"dashboard" = "Trạng thái hệ thống" "dashboard" = "Trạng thái hệ thống"
"inbounds" = "Đầu vào khách hàng" "inbounds" = "Inbounds"
"settings" = "Cài đặt bảng điều khiển" "settings" = "Cài đặt bảng điều khiển"
"logout" = "Đăng xuất" "logout" = "Đăng xuất"
"xray" = "Cài đặt Xray" "xray" = "Xray Cài đặt"
"link" = "Quản lý" "link" = "sự quản lý"
[pages.login] [pages.login]
"title" = "Chào mừng" "title" = "Đăng nhập"
"loginAgain" = "Thời hạn đăng nhập đã hết. Vui lòng đăng nhập lại." "loginAgain" = "Thời hạn đăng nhập đã hết. Vui lòng đăng nhập lại."
[pages.login.toasts] [pages.login.toasts]
@@ -74,22 +74,22 @@
[pages.index] [pages.index]
"title" = "Trạng thái hệ thống" "title" = "Trạng thái hệ thống"
"memory" = "Ram" "memory" = "Bộ nhớ"
"hard" = "Dung lượng" "hard" = "Ổ cứng"
"xrayStatus" = "Trạng thái Xray" "xrayStatus" = "Trạng thái của Xray"
"stopXray" = "Dừng lại" "stopXray" = "Dừng Xray"
"restartXray" = "Khởi động lại" "restartXray" = "Khởi động lại Xray"
"xraySwitch" = "Phiên bản" "xraySwitch" = "Chuyển đổi phiên bản"
"xraySwitchClick" = "Chọn phiên bản mà bạn muốn chuyển đổi sang." "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." "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" "operationHours" = "Thời gian hoạt động"
"systemLoad" = "Tải hệ thống" "systemLoad" = "Tải hệ thống"
"systemLoadDesc" = "trung bình tải hệ thống trong 1, 5 và 15 phút qua" "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 thẻ mạng." "connectionTcpCountDesc" = "Tổng số kết nối TCP trên tất cả các card mạng."
"connectionUdpCountDesc" = "Tổng số kết nối UDP trên tất cả các thẻ mạng." "connectionUdpCountDesc" = "Tổng số kết nối UDP trên tất cả các card mạng."
"connectionCount" = "Số lượng kết nối" "connectionCount" = "Số lượng kết nối"
"upSpeed" = "Tổng tốc độ tải lên cho tất cả các thẻ mạng." "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 thẻ mạng." "downSpeed" = "Tổng tốc độ tải xuống cho tất cả các card 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." "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." "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" "xraySwitchVersionDialog" = "Chuyển đổi Phiên bản Xray"
@@ -125,8 +125,8 @@
"modifyInbound" = "Chỉnh sửa điểm vào (Inbound)" "modifyInbound" = "Chỉnh sửa điểm vào (Inbound)"
"deleteInbound" = "Xó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)" "deleteInboundContent" = "Xác nhận xóa điểm vào? (Inbound)"
"deleteClient" = "Xóa người dùng" "deleteClient" = "Xóa khách hàng"
"deleteClientContent" = "Bạn có chắc chắn muốn xóa người dùng không?" "deleteClientContent" = "Bạn có chắc chắn muốn xóa ng dng khách không?"
"resetTrafficContent" = "Xác nhận đặt lại lưu lượng?" "resetTrafficContent" = "Xác nhận đặt lại lưu lượng?"
"copyLink" = "Sao chép liên kết" "copyLink" = "Sao chép liên kết"
"address" = "Địa chỉ" "address" = "Địa chỉ"
@@ -134,7 +134,7 @@
"destinationPort" = "Cổng đích" "destinationPort" = "Cổng đích"
"targetAddress" = "Địa chỉ mục tiêu" "targetAddress" = "Địa chỉ mục tiêu"
"monitorDesc" = "Mặc định để trống" "monitorDesc" = "Mặc định để trống"
"meansNoLimit" = " = Không giới hạn (đơn vị: GB)" "meansNoLimit" = "Nghĩa là không giới hạn"
"totalFlow" = "Tổng lưu lượng" "totalFlow" = "Tổng lưu lượng"
"leaveBlankToNeverExpire" = "Để trống để không bao giờ hết hạn" "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" "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ư" "keyPath" = "Đường dẫn khóa riêng tư"
"keyContent" = "Nội dung khóa riêng tư" "keyContent" = "Nội dung khóa riêng tư"
"clickOnQRcode" = "Nhấn vào Mã QR để sao chép" "clickOnQRcode" = "Nhấn vào Mã QR để sao chép"
"client" = "Người dùng" "client" = "Client"
"export" = "Xuất liên kết" "export" = "Xuất liên kết"
"clone" = "Sao chép" "clone" = "Sao chép"
"cloneInbound" = "Sao chép điểm vào (Inbound)" "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à máy khách, 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à Clients, sẽ được áp dụng cho bản sao."
"cloneInboundOk" = "Sao chép" "cloneInboundOk" = "Sao chép"
"resetAllTraffic" = "Đặt lại lưu lượng cho tất cả điểm vào" "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" "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?" "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 toàn bộ người dùng của điểm vào" "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 toàn bộ người dùng 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 người dùng của điểm vào này không?" "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 toàn bộ người dùng" "resetAllClientTraffics" = "Đặt lại lưu lượng cho tất cả client"
"resetAllClientTrafficTitle" = "Đặt lại lưu lượng cho toàn bộ người dùng" "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 toàn bộ người dùng không?" "resetAllClientTrafficContent" = "Bạn có chắc chắn muốn đặt lại tất cả lưu lượng cho tất cả client không?"
"delDepletedClients" = "Xóa các người dùng đã cạn kiệt" "delDepletedClients" = "Xóa các client đã cạn kiệt"
"delDepletedClientsTitle" = "Xóa các người dùng đã 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 toàn bộ người dùng đã cạn kiệt không?" "delDepletedClientsContent" = "Bạn có chắc chắn muốn xóa tất cả các client đã cạn kiệt không?"
"email" = "Email" "email" = "Email"
"emailDesc" = "Vui lòng cung cấp một địa chỉ email duy nhất." "emailDesc" = "Vui lòng cung cấp một địa chỉ email duy nhất."
"IPLimit" = "Giới hạn IP" "IPLimit" = "Giới hạn IP"
@@ -173,21 +173,21 @@
"setDefaultCert" = "Đặt chứng chỉ từ bảng điều khiển" "setDefaultCert" = "Đặt chứng chỉ từ bảng điều khiển"
"xtlsDesc" = "Xray core cần phiên bản 1.7.5" "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." "realityDesc" = "Xray core cần phiên bản 1.8.0 hoặc cao hơn."
"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)" "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 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" "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"
"info" = "Thông tin" "info" = "Thông tin"
"same" = "Giống nhau" "same" = "Giống nhau"
"inboundData" = "Dữ liệu gửi đến" "inboundData" = "Dữ liệu gửi đến"
"exportInbound" = "Xuất nhập khẩu" "copyToClipboard" = "Sao chép vào bảng nhớ tạm"
"import" = "Nhập" "import" = "Nhập"
"importInbound" = "Nhập inbound" "importInbound" = "Nhập hàng gửi về"
[pages.client] [pages.client]
"add" = "Thêm người dùng" "add" = "Thêm Client"
"edit" = "Chỉnh sửa người dùng" "edit" = "Chỉnh sửa Client"
"submitAdd" = "Thêm" "submitAdd" = "Thêm Client"
"submitEdit" = "Lưu thay đổi" "submitEdit" = "Lưu thay đổi"
"clientCount" = "Số lượng người dùng" "clientCount" = "Số lượng Client"
"bulk" = "Thêm hàng loạt" "bulk" = "Thêm hàng loạt"
"method" = "Phương pháp" "method" = "Phương pháp"
"first" = "Đầu tiên" "first" = "Đầu tiên"
@@ -195,28 +195,27 @@
"prefix" = "Tiền tố" "prefix" = "Tiền tố"
"postfix" = "Hậu tố" "postfix" = "Hậu tố"
"delayedStart" = "Bắt đầu sau khi sử dụng lần đầu" "delayedStart" = "Bắt đầu sau khi sử dụng lần đầu"
"expireDays" = "Khoảng thời gian" "expireDays" = "Số ngày hết hạn"
"days" = "ngày" "days" = "ngày"
"renew" = "Tự động gia hạn" "renew" = "Tự động gia hạn"
"renewDesc" = "Tự động gia hạn sau khi hết hạn. (0 = tắt)(đơn vị: ngày)" "renewDesc" = "Tự động gia hạn những ngày sau khi hết hạn. 0 = tắt"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "Nhận" "obtain" = "Nhận"
[pages.inbounds.stream.general] [pages.inbounds.stream.general]
"request" = "Lời yêu cầu" "requestHeader" = "Tiêu đề yêu cầu"
"response" = "Phản ứng"
"name" = "Tên" "name" = "Tên"
"value" = "Giá trị" "value" = "Giá trị"
[pages.inbounds.stream.tcp] [pages.inbounds.stream.tcp]
"version" = "Phiên bản" "requestVersion" = "Phiên bản yêu cầu"
"method" = "Phương pháp" "requestMethod" = "Phương thức yêu cầu"
"path" = "Đường dẫn" "requestPath" = "Đường dẫn yêu cầu"
"status" = "Trạng thái" "responseVersion" = "Phiên bản phản hồi"
"statusDescription" = "Tình trạng Mô tả" "responseStatus" = "Trạng thái phản hồi"
"requestHeader" = "Header yêu cầu" "responseStatusDescription" = "Mô tả trạng thái phản hồi"
"responseHeader" = "Header phản hồi" "responseHeader" = "Tiêu đề phản hồi"
[pages.inbounds.stream.quic] [pages.inbounds.stream.quic]
"encryption" = "Mã hóa" "encryption" = "Mã hóa"
@@ -225,31 +224,28 @@
"title" = "Cài đặt" "title" = "Cài đặt"
"save" = "Lưu" "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." "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ủ." "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" "actions" = "Hành động"
"resetDefaultConfig" = "Đặt lại cấu hình mặc định" "resetDefaultConfig" = "Đặt lại Cấu hình Mặc định"
"panelSettings" = "Bảng điều khiển" "panelSettings" = "Cài đặt Bảng điều khiển"
"securitySettings" = "Bảo mật" "securitySettings" = "Cài đặt Bảo mật"
"TGBotSettings" = "Bot Telegram" "TGBotSettings" = "Cài đặt Bot Telegram"
"panelListeningIP" = "IP Nghe của bảng điều khiển" "panelListeningIP" = "IP Nghe của Bảng điều khiển"
"panelListeningIPDesc" = "Mặc định để trống để nghe tất cả các IP." "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" "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" "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" "panelPortDesc" = "Cổng được sử dụng để hiển thị bảng điều khiển này"
"publicKeyPath" = "Đường dẫn file chứng chỉ bảng điều khiển" "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 đầy đủ (bắt đầu từ '/')" "publicKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với."
"privateKeyPath" = "Đường dẫn file khóa của chứng chỉ bảng điều khiển" "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 đầy đủ (bắt đầu từ '/')" "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" "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 '/'" "panelUrlPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng."
"pageSize" = "Kích thước phân trang" "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" "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" "remarkModel" = "Ghi chú mô hình và ký tự phân tách"
"datepicker" = "Kiểu lịch"
"datepickerPlaceholder" = "Chọn ngày"
"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" "sampleRemark" = "Nhận xét mẫu"
"oldUsername" = "Tên người dùng hiện tại" "oldUsername" = "Tên người dùng hiện tại"
"currentPassword" = "Mật khẩu hiện tại" "currentPassword" = "Mật khẩu hiện tại"
@@ -259,8 +255,6 @@
"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" "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" "telegramToken" = "Token Telegram"
"telegramTokenDesc" = "Bạn phải nhận token từ quản lý bot Telegram @botfather" "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" "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." "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" "telegramNotifyTime" = "Thời gian thông báo của bot Telegram"
@@ -269,7 +263,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." "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" "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." "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" = "Thời gian tối đa của phiên" "sessionMaxAge" = "Tuổi tối đa của phiên"
"sessionMaxAgeDesc" = "Thời gian của phiên đăng nhập (đơn vị: phút)" "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" "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)" "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)"
@@ -279,32 +273,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ị: %)" "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ờ" "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." "timeZoneDesc" = "Các tác vụ được lên lịch chạy theo thời gian trong múi giờ này."
"subSettings" = "Gói đăng ký" "subSettings" = "Đăng ký"
"subEnable" = "Bật dịch vụ" "subEnable" = "Bật dịch vụ"
"subEnableDesc" = "Tính năng gói đăng ký với cấu hình riêng" "subEnableDesc" = "Tính năng đăng ký với cấu hình riêng"
"subListen" = "Listening IP" "subListen" = "Listening IP"
"subListenDesc" = "Mặc định để trống để nghe tất cả các IP" "subListenDesc" = "Mặc định để trống để nghe tất cả các IP"
"subPort" = "Cổng gói đăng ký" "subPort" = "Cổng Đăng ký"
"subPortDesc" = "Số cổng dịch vụ đăng ký phải chưa được sử dụng trên máy chủ" "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 file chứng chỉ gói đăng ký" "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 đầy đủ (bắt đầu với '/')" "subCertPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với '/'"
"subKeyPath" = "Đường dẫn file khóa của chứng chỉ gói đăng ký" "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 đầy đủ (bắt đầu với '/')" "subKeyPathDesc" = "Điền vào đường dẫn tuyệt đối bắt đầu với '/'"
"subPath" = "Đường dẫn gốc URL gói đăng ký" "subPath" = "Đường dẫn gốc URL Đăng ký"
"subPathDesc" = "Phải bắt đầu và kết thúc bằng '/'" "subPathDesc" = "Phải bắt đầu bằng '/' và kết thúc bằng '/'"
"subDomain" = "Tên miền con" "subDomain" = "Tên miền con"
"subDomainDesc" = "Mặc định để trống để nghe tất cả các tên miền và IP" "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 gói đăng ký" "subUpdates" = "Khoảng thời gian cập nhật đăng ký"
"subUpdatesDesc" = "Số giờ giữa các cập nhật trong ứng dụng khách" "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" "subEncrypt" = "Mã hóa cấu hình"
"subEncryptDesc" = "Mã hóa các cấu hình được trả về trong gói đăng ký" "subEncryptDesc" = "Mã hóa các cấu hình được trả về trong đăng ký"
"subShowInfo" = "Hiển thị thông tin sử dụng" "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" "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 trung gian" "subURI" = "URI proxy ngược"
"subURIDesc" = "Thay đổi URI cơ sở của URL gói đăng ký để sử dụng cho proxy trung gian" "subURIDesc" = "Thay đổi URI cơ sở của URL đăng ký để sử dụng ở phía sau proxy"
[pages.xray] [pages.xray]
"title" = "Cài đặt Xray" "title" = "Xray Cài đặt"
"save" = "Lưu cài đặt" "save" = "Lưu cài đặt"
"restart" = "Khởi động lại Xray" "restart" = "Khởi động lại Xray"
"basicTemplate" = "Mẫu Cơ bản" "basicTemplate" = "Mẫu Cơ bản"
@@ -333,10 +327,8 @@
"PrivateIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP riêng tư." "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" "Ads" = "Chặn Quảng cáo"
"AdsDesc" = "Thay đổi mẫu cấu hình để 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 người lớn" "Family" = "Chặn Phần mềm độc hại và Nội dung cho 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." "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."
"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" "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." "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" "IRIp" = "Vô hiệu hóa kết nối đến dải IP của Iran"
@@ -422,21 +414,13 @@
"portal" = "Cổng thông tin" "portal" = "Cổng thông tin"
"intercon" = "Kết nối" "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] [pages.settings.security]
"admin" = "Quản trị viên" "admin" = "Quản trị viên"
"secret" = "Mã thông báo bí mật" "secret" = "Mã thông báo bí mật"
"loginSecurity" = "Bảo mật đăng nhập" "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" "loginSecurityDesc" = "Bật bước bảo mật đăng nhập bổ sung cho người dùng"
"secretToken" = "Mã bí mật" "secretToken" = "Mã thông báo 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." "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."
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "Chỉnh sửa cài đặt " "modifySettings" = "Chỉnh sửa cài đặt "
@@ -453,7 +437,6 @@
"noIpRecord" = "❗ Không có bản ghi IP!" "noIpRecord" = "❗ Không có bản ghi IP!"
"noInbounds" = "❗ Không tìm thấy inbound!" "noInbounds" = "❗ Không tìm thấy inbound!"
"unlimited" = "♾ Không giới hạn" "unlimited" = "♾ Không giới hạn"
"add" = "Thêm"
"month" = "Tháng" "month" = "Tháng"
"months" = "Tháng" "months" = "Tháng"
"day" = "Ngày" "day" = "Ngày"
@@ -461,9 +444,7 @@
"hours" = "Giờ" "hours" = "Giờ"
"unknown" = "Không rõ" "unknown" = "Không rõ"
"inbounds" = "Vào" "inbounds" = "Vào"
"clients" = "Các người dùng" "clients" = "Các khách hàng"
"offline" = "🔴 Ngoại tuyến"
"online" = "🟢 Trực tuyến"
[tgbot.commands] [tgbot.commands]
"unknown" = "❗ Lệnh không rõ" "unknown" = "❗ Lệnh không rõ"
@@ -474,8 +455,8 @@
"status" = "✅ Bot hoạt động bình thường!" "status" = "✅ Bot hoạt động bình thường!"
"usage" = "❗ Vui lòng cung cấp văn bản để tìm kiếm!" "usage" = "❗ Vui lòng cung cấp văn bản để tìm kiếm!"
"getID" = "🆔 ID của bạn: <code>{{ .ID }}</code>" "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>" "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>" "helpClientCommands" = "Để tìm kiếm thống kê, hãy sử dụng lệnh sau:\r\n \r\n<code>/usage [UUID|Mật khẩu]</code>\r\n \r\nSử dụng UUID cho vmess/vless và Mật khẩu cho Trojan."
[tgbot.messages] [tgbot.messages]
"cpuThreshold" = "🔴 Sử dụng CPU {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%" "cpuThreshold" = "🔴 Sử dụng CPU {{ .Percent }}% vượt quá ngưỡng {{ .Threshold }}%"
@@ -490,7 +471,7 @@
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n" "ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n" "ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
"ip" = "🌐 IP: {{ .IP }}\r\n" "ip" = "🌐 IP: {{ .IP }}\r\n"
"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" "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" "serverLoad" = "📈 Tải máy chủ: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
"serverMemory" = "📋 Bộ nhớ máy chủ: {{ .Current }}/{{ .Total }}\r\n" "serverMemory" = "📋 Bộ nhớ máy chủ: {{ .Current }}/{{ .Total }}\r\n"
@@ -504,9 +485,8 @@
"port" = "🔌 Cổng: {{ .Port }}\r\n" "port" = "🔌 Cổng: {{ .Port }}\r\n"
"expire" = "📅 Ngày hết hạn: {{ .Time }}\r\n" "expire" = "📅 Ngày hết hạn: {{ .Time }}\r\n"
"expireIn" = "📅 Hết hạn sau: {{ .Time }}\r\n" "expireIn" = "📅 Hết hạn sau: {{ .Time }}\r\n"
"active" = "💡 Đang hoạt động: {{ .Enable }}\r\n" "active" = "💡 Hoạt động: ✅ Có\r\n"
"enabled" = "🚨 Đã bật: {{ .Enable }}\r\n" "inactive" = "💡 Hoạt động: ❌ Không\r\n"
"online" = "🌐 Trạng thái kết nối: {{ .Status }}\r\n"
"email" = "📧 Email: {{ .Email }}\r\n" "email" = "📧 Email: {{ .Email }}\r\n"
"upload" = "🔼 Tải lên: ↑{{ .Upload }}\r\n" "upload" = "🔼 Tải lên: ↑{{ .Upload }}\r\n"
"download" = "🔽 Tải xuống: ↓{{ .Download }}\r\n" "download" = "🔽 Tải xuống: ↓{{ .Download }}\r\n"
@@ -514,13 +494,10 @@
"TGUser" = "👤 Người dùng Telegram: {{ .TelegramID }}\r\n" "TGUser" = "👤 Người dùng Telegram: {{ .TelegramID }}\r\n"
"exhaustedMsg" = "🚨 Sự cạn kiệt {{ .Type }}:\r\n" "exhaustedMsg" = "🚨 Sự cạn kiệt {{ .Type }}:\r\n"
"exhaustedCount" = "🚨 Số lần 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" "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" "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] [tgbot.buttons]
"closeKeyboard" = "❌ Đóng Bàn Phím" "closeKeyboard" = "❌ Đóng Bàn Phím"
@@ -530,13 +507,11 @@
"confirmResetTraffic" = "✅ Xác Nhận Đặt Lại Lưu Lượng?" "confirmResetTraffic" = "✅ Xác Nhận Đặt Lại Lưu Lượng?"
"confirmClearIps" = "✅ Xác Nhận Xóa Các IP?" "confirmClearIps" = "✅ Xác Nhận Xóa Các IP?"
"confirmRemoveTGUser" = "✅ Xác Nhận Xóa Người Dùng Telegram?" "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" "dbBackup" = "Tải bản sao lưu cơ sở dữ liệu"
"serverUsage" = "Sử Dụng Máy Chủ" "serverUsage" = "Sử Dụng Máy Chủ"
"getInbounds" = "Lấy cổng vào" "getInbounds" = "Lấy cổng vào"
"depleteSoon" = "Depleted Soon" "depleteSoon" = "Depleted Soon"
"clientUsage" = "Lấy Sử Dụng" "clientUsage" = "Lấy Sử Dụng"
"onlines" = "Khách hàng trực tuyến"
"commands" = "Lệnh" "commands" = "Lệnh"
"refresh" = "🔄 Cập Nhật" "refresh" = "🔄 Cập Nhật"
"clearIPs" = "❌ Xóa IP" "clearIPs" = "❌ Xóa IP"
@@ -544,16 +519,14 @@
"selectTGUser" = "👤 Chọn Người Dùng Telegram" "selectTGUser" = "👤 Chọn Người Dùng Telegram"
"selectOneTGUser" = "👤 Chọn một người dùng telegram:" "selectOneTGUser" = "👤 Chọn một người dùng telegram:"
"resetTraffic" = "📈 Đặt Lại Lưu Lượng" "resetTraffic" = "📈 Đặt Lại Lưu Lượng"
"resetExpire" = "📅 Thay đổi ngày hết hạn" "resetExpire" = "📅 Đặt Lại Ngày Hết Hạn"
"ipLog" = "🔢 Nhật ký địa chỉ IP" "ipLog" = "🔢 Nhật ký địa chỉ IP"
"ipLimit" = "🔢 Giới Hạn địa chỉ IP" "ipLimit" = "🔢 Giới Hạn địa chỉ IP"
"setTGUser" = "👤 Đặt Người Dùng Telegram" "setTGUser" = "👤 Đặt Người Dùng Telegram"
"toggle" = "🔘 Bật / Tắt" "toggle" = "🔘 Bật / Tắt"
"custom" = "🔢 Tùy chỉnh" "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" "limitTraffic" = "🚧 Giới hạn lưu lượng"
"getBanLogs" = "Cấm nhật ký"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ Thành công!" "successfulOperation" = "✅ Thành công!"
@@ -573,4 +546,5 @@
"removedTGUserSuccess" = "✅ {{ .Email }} : Người Dùng Telegram Đã Được Xóa Thành Công." "removedTGUserSuccess" = "✅ {{ .Email }} : Người Dùng Telegram Đã Được Xóa Thành Công."
"enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công." "enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công."
"disableSuccess" = "✅ {{ .Email }} : Đã Tắ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: <code>{{ .TgUserID }}</code>" "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>"

View File

@@ -10,8 +10,8 @@
"remark" = "备注" "remark" = "备注"
"enable" = "启用" "enable" = "启用"
"protocol" = "协议" "protocol" = "协议"
"search" = "搜" "search" = "搜"
"filter" = "筛选" "filter" = "过滤器"
"loading" = "加载中..." "loading" = "加载中..."
"second" = "秒" "second" = "秒"
"minute" = "分钟" "minute" = "分钟"
@@ -30,8 +30,8 @@
"sure" = "确定" "sure" = "确定"
"encryption" = "加密" "encryption" = "加密"
"transmission" = "传输" "transmission" = "传输"
"host" = "Host" "host" = "主持人"
"path" = "Path" "path" = "小路"
"camouflage" = "伪装" "camouflage" = "伪装"
"status" = "状态" "status" = "状态"
"enabled" = "开启" "enabled" = "开启"
@@ -49,8 +49,8 @@
"install" = "安装" "install" = "安装"
"clients" = "客户端" "clients" = "客户端"
"usage" = "用法" "usage" = "用法"
"secretToken" = "安全密钥" "secretToken" = "秘密令牌"
"remained" = "剩余" "remained" = "仍然存在"
"security" = "安全" "security" = "安全"
[menu] [menu]
@@ -62,7 +62,7 @@
"link" = "管理" "link" = "管理"
[pages.login] [pages.login]
"title" = "欢迎" "title" = "登录"
"loginAgain" = "登录时效已过,请重新登录" "loginAgain" = "登录时效已过,请重新登录"
[pages.login.toasts] [pages.login.toasts]
@@ -79,7 +79,7 @@
"xrayStatus" = "状态" "xrayStatus" = "状态"
"stopXray" = "停止" "stopXray" = "停止"
"restartXray" = "重启" "restartXray" = "重启"
"xraySwitch" = "版本" "xraySwitch" = "切换版本"
"xraySwitchClick" = "点击你想切换的版本" "xraySwitchClick" = "点击你想切换的版本"
"xraySwitchClickDesk" = "请谨慎选择,旧版本可能配置不兼容" "xraySwitchClickDesk" = "请谨慎选择,旧版本可能配置不兼容"
"operationHours" = "系统正常运行时间" "operationHours" = "系统正常运行时间"
@@ -134,7 +134,7 @@
"destinationPort" = "目标端口" "destinationPort" = "目标端口"
"targetAddress" = "目标地址" "targetAddress" = "目标地址"
"monitorDesc" = "默认留空即可" "monitorDesc" = "默认留空即可"
"meansNoLimit" = " = 无限制单位GB)" "meansNoLimit" = "表示不限制"
"totalFlow" = "总流量" "totalFlow" = "总流量"
"leaveBlankToNeverExpire" = "留空则永不到期" "leaveBlankToNeverExpire" = "留空则永不到期"
"noRecommendKeepDefault" = "没有特殊需求保持默认即可" "noRecommendKeepDefault" = "没有特殊需求保持默认即可"
@@ -148,7 +148,7 @@
"client" = "客户" "client" = "客户"
"export" = "导出链接" "export" = "导出链接"
"clone" = "克隆" "clone" = "克隆"
"cloneInbound" = "克隆" "cloneInbound" = "创造"
"cloneInboundContent" = "此入站的所有项目除 Port、Listening IP、Clients 将应用于克隆" "cloneInboundContent" = "此入站的所有项目除 Port、Listening IP、Clients 将应用于克隆"
"cloneInboundOk" = "从创建克隆" "cloneInboundOk" = "从创建克隆"
"resetAllTraffic" = "重置所有入站流量" "resetAllTraffic" = "重置所有入站流量"
@@ -173,12 +173,12 @@
"setDefaultCert" = "从面板设置证书" "setDefaultCert" = "从面板设置证书"
"xtlsDesc" = "Xray核心需要1.7.5" "xtlsDesc" = "Xray核心需要1.7.5"
"realityDesc" = "Xray核心需要1.8.0及以上版本" "realityDesc" = "Xray核心需要1.8.0及以上版本"
"telegramDesc" = "使用聊天 ID可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)" "telegramDesc" = "使用 Telegram ID不包含 @ 符号或聊天 ID可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
"subscriptionDesc" = "您可以在详细信息上找到您的子链接,也可以对多个配置使用相同的名称" "subscriptionDesc" = "您可以在详细信息上找到您的子链接,也可以对多个配置使用相同的名称"
"info" = "信息" "info" = "信息"
"same" = "相同" "same" = "相同"
"inboundData" = "入站数据" "inboundData" = "入站数据"
"exportInbound" = "出口 入境" "copyToClipboard" = "复制到剪贴板"
"import"="导入" "import"="导入"
"importInbound" = "导入入站" "importInbound" = "导入入站"
@@ -195,27 +195,26 @@
"prefix" = "前缀" "prefix" = "前缀"
"postfix" = "后缀" "postfix" = "后缀"
"delayedStart" = "首次使用后开始" "delayedStart" = "首次使用后开始"
"expireDays" = "期间" "expireDays" = "过期天数"
"days" = "天" "days" = "天"
"renew" = "自动续订" "renew" = "自动续订"
"renewDesc" = "期后自动续订。(0 = 禁用)(单元: 天)" "renewDesc" = "期后自动续订。0 = 禁用"
[pages.inbounds.toasts] [pages.inbounds.toasts]
"obtain" = "获取" "obtain" = "获取"
[pages.inbounds.stream.general] [pages.inbounds.stream.general]
"request" = "要求" "requestHeader" = "请求头"
"response" = "回复" "name" = "名称"
"name" = "姓名" "value" = ""
"value" = "价值"
[pages.inbounds.stream.tcp] [pages.inbounds.stream.tcp]
"version" = "版本" "requestVersion" = "请求版本"
"method" = "方法" "requestMethod" = "请求方法"
"path" = "小路" "requestPath" = "请求路径"
"status" = "地位" "responseVersion" = "响应版本"
"statusDescription" = "状态说明" "responseStatus" = "响应状态"
"requestHeader" = "请求头" "responseStatusDescription" = "响应状态说明"
"responseHeader" = "响应头" "responseHeader" = "响应头"
[pages.inbounds.stream.quic] [pages.inbounds.stream.quic]
@@ -247,9 +246,6 @@
"pageSize" = "分页大小" "pageSize" = "分页大小"
"pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用" "pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用"
"remarkModel" = "备注模型和分隔符" "remarkModel" = "备注模型和分隔符"
"datepicker" = "日期选择器"
"datepickerPlaceholder" = "选择日期"
"datepickerDescription" = "选择器日历类型指定到期日期"
"sampleRemark" = "备注示例" "sampleRemark" = "备注示例"
"oldUsername" = "原用户名" "oldUsername" = "原用户名"
"currentPassword" = "原密码" "currentPassword" = "原密码"
@@ -259,8 +255,6 @@
"telegramBotEnableDesc" = "重启面板生效" "telegramBotEnableDesc" = "重启面板生效"
"telegramToken" = "电报机器人TOKEN" "telegramToken" = "电报机器人TOKEN"
"telegramTokenDesc" = "重启面板生效" "telegramTokenDesc" = "重启面板生效"
"telegramProxy" = "Socks5 代理"
"telegramProxyDesc" = "如果您需要 Socks5 代理来连接 Telegram。 根据指南调整其设置。"
"telegramChatId" = "以逗号分隔的多个 chatID 重启面板生效" "telegramChatId" = "以逗号分隔的多个 chatID 重启面板生效"
"telegramChatIdDesc" = "多个聊天 ID 用逗号分隔。使用 @userinfobot 或在机器人中使用'/id'命令获取您的聊天 ID。" "telegramChatIdDesc" = "多个聊天 ID 用逗号分隔。使用 @userinfobot 或在机器人中使用'/id'命令获取您的聊天 ID。"
"telegramNotifyTime" = "电报机器人通知时间" "telegramNotifyTime" = "电报机器人通知时间"
@@ -333,10 +327,8 @@
"PrivateIpDesc" = "更改配置模板以避免连接私有 IP 范围" "PrivateIpDesc" = "更改配置模板以避免连接私有 IP 范围"
"Ads" = "屏蔽广告" "Ads" = "屏蔽广告"
"AdsDesc" = "修改配置模板屏蔽广告" "AdsDesc" = "修改配置模板屏蔽广告"
"Family" = "阻止恶意软件和成人内容" "Family" = "启用家庭友好配置"
"FamilyDesc" = "Cloudflare DNS 解析器可阻止恶意软件和成人内容以保护家庭." "FamilyDesc" = "避免为家人连接到不安全的网站"
"Security" = "阻止恶意软件、网络钓鱼和加密货币挖矿网站"
"SecurityDesc" = "更改安全防护配置模板."
"Speedtest" = "阻止测速网站" "Speedtest" = "阻止测速网站"
"SpeedtestDesc" = "更改配置模板以避免连接到速度测试网站。 重新启动面板以应用更改。" "SpeedtestDesc" = "更改配置模板以避免连接到速度测试网站。 重新启动面板以应用更改。"
"IRIp" = "禁止伊朗 IP 范围连接" "IRIp" = "禁止伊朗 IP 范围连接"
@@ -385,7 +377,7 @@
"SpotifyWARPDesc" = "为Spotify添加路由到WARP" "SpotifyWARPDesc" = "为Spotify添加路由到WARP"
"IRWARP" = "将伊朗域名路由到 WARP" "IRWARP" = "将伊朗域名路由到 WARP"
"IRWARPDesc" = "将伊朗域的路由添加到 WARP。 重启面板生效" "IRWARPDesc" = "将伊朗域的路由添加到 WARP。 重启面板生效"
"Inbounds" = "入站" "Inbounds" = "界内"
"InboundsDesc" = "更改配置模板接受特殊客户端" "InboundsDesc" = "更改配置模板接受特殊客户端"
"Outbounds" = "出站" "Outbounds" = "出站"
"OutboundsDesc" = "更改配置模板定义此服务器的传出方式" "OutboundsDesc" = "更改配置模板定义此服务器的传出方式"
@@ -413,7 +405,7 @@
"editOutbound" = "编辑出站" "editOutbound" = "编辑出站"
"editReverse" = "编辑反向" "editReverse" = "编辑反向"
"tag" = "标签" "tag" = "标签"
"tagDesc" = "唯一标记" "tagDesc" = "独特的标签"
"address" = "地址" "address" = "地址"
"reverse" = "反转" "reverse" = "反转"
"domain" = "域名" "domain" = "域名"
@@ -422,14 +414,6 @@
"portal" = "门户" "portal" = "门户"
"intercon" = "互连" "intercon" = "互连"
[pages.xray.wireguard]
"secretKey" = "密钥"
"publicKey" = "公钥"
"allowedIPs" = "允许的 IP"
"endpoint" = "终点"
"psk" = "共享密钥"
"domainStrategy" = "域策略"
[pages.settings.security] [pages.settings.security]
"admin" = "管理员" "admin" = "管理员"
"secret" = "密钥" "secret" = "密钥"
@@ -453,7 +437,6 @@
"noIpRecord" = "❗ 没有IP记录" "noIpRecord" = "❗ 没有IP记录"
"noInbounds" = "❗ 没有找到入站连接!" "noInbounds" = "❗ 没有找到入站连接!"
"unlimited" = "♾ 无限制" "unlimited" = "♾ 无限制"
"add" = "添加"
"month" = "月" "month" = "月"
"months" = "月" "months" = "月"
"day" = "天" "day" = "天"
@@ -462,8 +445,6 @@
"unknown" = "未知" "unknown" = "未知"
"inbounds" = "入站连接" "inbounds" = "入站连接"
"clients" = "客户端" "clients" = "客户端"
"offline" = "🔴 离线"
"online" = "🟢 在线的"
[tgbot.commands] [tgbot.commands]
"unknown" = "❗ 未知命令" "unknown" = "❗ 未知命令"
@@ -474,8 +455,8 @@
"status" = "✅ 机器人正常运行!" "status" = "✅ 机器人正常运行!"
"usage" = "❗ 请输入要搜索的文本!" "usage" = "❗ 请输入要搜索的文本!"
"getID" = "🆔 您的ID为<code>{{ .ID }}</code>" "getID" = "🆔 您的ID为<code>{{ .ID }}</code>"
"helpAdminCommands" = "搜索客户端邮箱:\r\n<code>/usage [Email]</code>\r\n\r\n搜索入站连接包含客户端统计信息\r\n<code>/inbound [Remark]</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 [Email]</code>" "helpClientCommands" = "要搜索统计信息,请使用以下命令:\r\n \r\n<code>/usage [UUID|Password]</code>\r\n \r\n对于vmess/vless请使用UUID对于Trojan请使用密码。"
[tgbot.messages] [tgbot.messages]
"cpuThreshold" = "🔴 CPU 使用率为 {{ .Percent }}%,超过阈值 {{ .Threshold }}%" "cpuThreshold" = "🔴 CPU 使用率为 {{ .Percent }}%,超过阈值 {{ .Threshold }}%"
@@ -504,9 +485,8 @@
"port" = "🔌 端口:{{ .Port }}\r\n" "port" = "🔌 端口:{{ .Port }}\r\n"
"expire" = "📅 过期日期:{{ .Time }}\r\n" "expire" = "📅 过期日期:{{ .Time }}\r\n"
"expireIn" = "📅 剩余时间:{{ .Time }}\r\n" "expireIn" = "📅 剩余时间:{{ .Time }}\r\n"
"active" = "💡 激活:{{ .Enable }}\r\n" "active" = "💡 激活:\r\n"
"enabled" = "🚨 已启用:{{ .Enable }}\r\n" "inactive" = "💡 激活: ❌\r\n"
"online" = "🌐 连接状态:{{ .Status }}\r\n"
"email" = "📧 邮箱:{{ .Email }}\r\n" "email" = "📧 邮箱:{{ .Email }}\r\n"
"upload" = "🔼 上传↑:{{ .Upload }}\r\n" "upload" = "🔼 上传↑:{{ .Upload }}\r\n"
"download" = "🔽 下载↓:{{ .Download }}\r\n" "download" = "🔽 下载↓:{{ .Download }}\r\n"
@@ -514,13 +494,10 @@
"TGUser" = "👤 电报用户:{{ .TelegramID }}\r\n" "TGUser" = "👤 电报用户:{{ .TelegramID }}\r\n"
"exhaustedMsg" = "🚨 耗尽的{{ .Type }}\r\n" "exhaustedMsg" = "🚨 耗尽的{{ .Type }}\r\n"
"exhaustedCount" = "🚨 耗尽的{{ .Type }}数量:\r\n" "exhaustedCount" = "🚨 耗尽的{{ .Type }}数量:\r\n"
"onlinesCount" = "🌐 在线客户:{{ .Count }}\r\n"
"disabled" = "🛑 禁用:{{ .Disabled }}\r\n" "disabled" = "🛑 禁用:{{ .Disabled }}\r\n"
"depleteSoon" = "🔜 即将耗尽:{{ .Deplete }}\r\n\r\n" "depleteSoon" = "🔜 即将耗尽:{{ .Deplete }}\r\n \r\n"
"backupTime" = "🗄 备份时间:{{ .Time }}\r\n" "backupTime" = "🗄 备份时间:{{ .Time }}\r\n"
"refreshedOn" = "\r\n📋🔄 刷新时间:{{ .Time }}\r\n\r\n" "refreshedOn" = "\r\n📋🔄 刷新时间:{{ .Time }}\r\n \r\n"
"yes" = "✅ 是的"
"no" = "❌ 没有"
[tgbot.buttons] [tgbot.buttons]
"closeKeyboard" = "❌ 关闭键盘" "closeKeyboard" = "❌ 关闭键盘"
@@ -530,13 +507,11 @@
"confirmResetTraffic" = "✅ 确认重置流量?" "confirmResetTraffic" = "✅ 确认重置流量?"
"confirmClearIps" = "✅ 确认清除 IP" "confirmClearIps" = "✅ 确认清除 IP"
"confirmRemoveTGUser" = "✅ 确认移除 Telegram 用户?" "confirmRemoveTGUser" = "✅ 确认移除 Telegram 用户?"
"confirmToggle" = "✅ 确认启用/禁用用户?"
"dbBackup" = "获取数据库备份" "dbBackup" = "获取数据库备份"
"serverUsage" = "服务器使用情况" "serverUsage" = "服务器使用情况"
"getInbounds" = "获取入站信息" "getInbounds" = "获取入站信息"
"depleteSoon" = "即将耗尽" "depleteSoon" = "即将耗尽"
"clientUsage" = "获取使用情况" "clientUsage" = "获取使用情况"
"onlines" = "在线客户"
"commands" = "命令" "commands" = "命令"
"refresh" = "🔄 刷新" "refresh" = "🔄 刷新"
"clearIPs" = "❌ 清除 IP" "clearIPs" = "❌ 清除 IP"
@@ -544,16 +519,14 @@
"selectTGUser" = "👤 选择 Telegram 用户" "selectTGUser" = "👤 选择 Telegram 用户"
"selectOneTGUser" = "👤 选择一个 Telegram 用户:" "selectOneTGUser" = "👤 选择一个 Telegram 用户:"
"resetTraffic" = "📈 重置流量" "resetTraffic" = "📈 重置流量"
"resetExpire" = "📅 更改到期日期" "resetExpire" = "📅 重置过期天数"
"ipLog" = "🔢 IP 日志" "ipLog" = "🔢 IP 日志"
"ipLimit" = "🔢 IP 限制" "ipLimit" = "🔢 IP 限制"
"setTGUser" = "👤 设置 Telegram 用户" "setTGUser" = "👤 设置 Telegram 用户"
"toggle" = "🔘 启用/禁用" "toggle" = "🔘 启用/禁用"
"custom" = "🔢 风俗" "custom" = "🔢 风俗"
"confirmNumber" = "✅ 确认: {{ .Num }}" "confirmNumber" = "✅ 确认 : {{ .Num }}"
"confirmNumberAdd" = "✅ 确认添加:{{ .Num }}"
"limitTraffic" = "🚧 交通限制" "limitTraffic" = "🚧 交通限制"
"getBanLogs" = "禁止日志"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ 成功的!" "successfulOperation" = "✅ 成功的!"
@@ -573,4 +546,5 @@
"removedTGUserSuccess" = "✅ {{ .Email }}Telegram 用户已成功移除。" "removedTGUserSuccess" = "✅ {{ .Email }}Telegram 用户已成功移除。"
"enableSuccess" = "✅ {{ .Email }}:已成功启用。" "enableSuccess" = "✅ {{ .Email }}:已成功启用。"
"disableSuccess" = "✅ {{ .Email }}:已成功禁用。" "disableSuccess" = "✅ {{ .Email }}:已成功禁用。"
"askToAddUserId" = "未找到您的配置!\r\n请向管理员询问在您的配置中使用您的 Telegram 用户ID。\r\n\r\n您的用户ID<code>{{ .TgUserID }}</code>" "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>"

View File

@@ -240,11 +240,11 @@ func (s *Server) startTask() {
if err != nil { if err != nil {
logger.Warning("start xray failed:", err) logger.Warning("start xray failed:", err)
} }
// Check whether xray is running every second // Check whether xray is running every 30 seconds
s.cron.AddJob("@every 1s", job.NewCheckXrayRunningJob()) s.cron.AddJob("@every 30s", job.NewCheckXrayRunningJob())
// Check if xray needs to be restarted every 30 seconds // Check if xray needs to be restarted
s.cron.AddFunc("@every 30s", func() { s.cron.AddFunc("@every 10s", func() {
if s.xrayService.IsNeedRestartAndSetFalse() { if s.xrayService.IsNeedRestartAndSetFalse() {
err := s.xrayService.RestartXray(false) err := s.xrayService.RestartXray(false)
if err != nil { if err != nil {

462
x-ui.sh
View File

@@ -51,16 +51,8 @@ elif [[ "${release}" == "fedora" ]]; 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 fi
elif [[ "${release}" == "debian" ]]; then elif [[ "${release}" == "debian" ]]; then
if [[ ${os_version} -lt 11 ]]; then if [[ ${os_version} -lt 10 ]]; then
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1 echo -e "${red} Please use Debian 10 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 fi
elif [[ "${release}" == "arch" ]]; then elif [[ "${release}" == "arch" ]]; then
echo "Your OS is ArchLinux" echo "Your OS is ArchLinux"
@@ -70,11 +62,13 @@ elif [[ "${release}" == "armbian" ]]; then
echo "Your OS is Armbian" echo "Your OS is Armbian"
fi fi
# Declare Variables # Declare Variables
log_folder="${XUI_LOG_FOLDER:=/var/log}" log_folder="${XUI_LOG_FOLDER:=/var/log}"
iplimit_log_path="${log_folder}/3xipl.log" iplimit_log_path="${log_folder}/3xipl.log"
iplimit_banned_log_path="${log_folder}/3xipl-banned.log" iplimit_banned_log_path="${log_folder}/3xipl-banned.log"
confirm() { confirm() {
if [[ $# > 1 ]]; then if [[ $# > 1 ]]; then
echo && read -p "$1 [Default $2]: " temp echo && read -p "$1 [Default $2]: " temp
@@ -117,7 +111,7 @@ install() {
} }
update() { update() {
confirm "This function will forcefully reinstall the latest version, and the data will not be lost. Do you want to continue?" "y" confirm "This function will forcefully reinstall the latest version, and the data will not be lost. Do you want to continue?" "n"
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
LOGE "Cancelled" LOGE "Cancelled"
if [[ $# == 0 ]]; then if [[ $# == 0 ]]; then
@@ -132,24 +126,6 @@ update() {
fi 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() { uninstall() {
confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n" confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n"
if [[ $? != 0 ]]; then if [[ $? != 0 ]]; then
@@ -327,15 +303,15 @@ show_log() {
} }
show_banlog() { show_banlog() {
if test -f "${iplimit_banned_log_path}"; then if test -f "${iplimit_banned_log_path}"; then
if [[ -s "${iplimit_banned_log_path}" ]]; then if [[ -s "${iplimit_banned_log_path}" ]]; then
cat ${iplimit_banned_log_path} cat ${iplimit_banned_log_path}
else
echo -e "${red}Log file is empty.${plain}\n"
fi
else else
echo -e "${red}Log file not found. Please Install Fail2ban and IP Limit first.${plain}\n" echo -e "${red}Log file is empty.${plain}\n"
fi fi
else
echo -e "${red}Log file not found. Please Install Fail2ban and IP Limit first.${plain}\n"
fi
} }
enable_bbr() { enable_bbr() {
@@ -346,19 +322,19 @@ enable_bbr() {
# Check the OS and install necessary packages # Check the OS and install necessary packages
case "${release}" in case "${release}" in
ubuntu | debian) ubuntu|debian)
apt-get update && apt-get install -yqq --no-install-recommends ca-certificates apt-get update && apt-get install -yqq --no-install-recommends ca-certificates
;; ;;
centos | almalinux | rocky) centos)
yum -y update && yum -y install ca-certificates yum -y update && yum -y install ca-certificates
;; ;;
fedora) fedora)
dnf -y update && dnf -y install ca-certificates dnf -y update && dnf -y install ca-certificates
;; ;;
*) *)
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n" echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
exit 1 exit 1
;; ;;
esac esac
# Enable BBR # Enable BBR
@@ -579,24 +555,21 @@ ssl_cert_issue_main() {
echo -e "${green}\t0.${plain} Back to Main Menu" echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice read -p "Choose an option: " choice
case "$choice" in case "$choice" in
0) 0)
show_menu show_menu ;;
;; 1)
1) ssl_cert_issue ;;
ssl_cert_issue 2)
;; local domain=""
2) read -p "Please enter your domain name to revoke the certificate: " domain
local domain="" ~/.acme.sh/acme.sh --revoke -d ${domain}
read -p "Please enter your domain name to revoke the certificate: " domain LOGI "Certificate revoked"
~/.acme.sh/acme.sh --revoke -d ${domain} ;;
LOGI "Certificate revoked" 3)
;; local domain=""
3) read -p "Please enter your domain name to forcefully renew an SSL certificate: " domain
local domain="" ~/.acme.sh/acme.sh --renew -d ${domain} --force ;;
read -p "Please enter your domain name to forcefully renew an SSL certificate: " domain *) echo "Invalid choice" ;;
~/.acme.sh/acme.sh --renew -d ${domain} --force
;;
*) echo "Invalid choice" ;;
esac esac
} }
@@ -612,19 +585,15 @@ ssl_cert_issue() {
fi fi
# install socat second # install socat second
case "${release}" in case "${release}" in
ubuntu | debian | armbian) ubuntu|debian|armbian)
apt update && apt install socat -y apt update && apt install socat -y ;;
;; centos)
centos | almalinux | rocky) yum -y update && yum -y install socat ;;
yum -y update && yum -y install socat fedora)
;; dnf -y update && dnf -y install socat ;;
fedora) *)
dnf -y update && dnf -y install socat echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
;; exit 1 ;;
*)
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
exit 1
;;
esac esac
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
LOGE "install socat failed, please check logs" LOGE "install socat failed, please check logs"
@@ -755,8 +724,8 @@ ssl_cert_issue_CF() {
LOGI "Certificate issued Successfully, Installing..." LOGI "Certificate issued Successfully, Installing..."
fi fi
~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} --ca-file /root/cert/ca.cer \ ~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} --ca-file /root/cert/ca.cer \
--cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \ --cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \
--fullchain-file /root/cert/fullchain.cer --fullchain-file /root/cert/fullchain.cer
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
LOGE "Certificate installation failed, script exiting..." LOGE "Certificate installation failed, script exiting..."
exit 1 exit 1
@@ -787,46 +756,45 @@ warp_cloudflare() {
echo -e "${green}\t0.${plain} Back to Main Menu" echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice read -p "Choose an option: " choice
case "$choice" in case "$choice" in
0) 0)
show_menu show_menu ;;
;; 1)
1) bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh) ;;
;; 2)
2) warp a
warp a ;;
;; 3)
3) warp y
warp y ;;
;; 4)
4) warp u
warp u ;;
;; *) echo "Invalid choice" ;;
*) echo "Invalid choice" ;;
esac esac
} }
run_speedtest() { run_speedtest() {
# Check if Speedtest is already installed # Check if Speedtest is already installed
if ! command -v speedtest &>/dev/null; then if ! command -v speedtest &> /dev/null; then
# If not installed, install it # If not installed, install it
local pkg_manager="" local pkg_manager=""
local speedtest_install_script="" local speedtest_install_script=""
if command -v dnf &>/dev/null; then if command -v dnf &> /dev/null; then
pkg_manager="dnf" pkg_manager="dnf"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh" speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
elif command -v yum &>/dev/null; then elif command -v yum &> /dev/null; then
pkg_manager="yum" pkg_manager="yum"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh" speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
elif command -v apt-get &>/dev/null; then elif command -v apt-get &> /dev/null; then
pkg_manager="apt-get" pkg_manager="apt-get"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh" speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
elif command -v apt &>/dev/null; then elif command -v apt &> /dev/null; then
pkg_manager="apt" pkg_manager="apt"
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh" speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
fi fi
if [[ -z $pkg_manager ]]; then if [[ -z $pkg_manager ]]; then
echo "Error: Package manager not found. You may need to install Speedtest manually." echo "Error: Package manager not found. You may need to install Speedtest manually."
return 1 return 1
@@ -841,11 +809,8 @@ run_speedtest() {
} }
create_iplimit_jails() { create_iplimit_jails() {
# Use default bantime if not passed => 30 minutes # Use default bantime if not passed => 5 minutes
local bantime="${1:-30}" local bantime="${1:-5}"
# Uncomment 'allowipv6 = auto' in fail2ban.conf
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
cat << EOF > /etc/fail2ban/jail.d/3x-ipl.conf cat << EOF > /etc/fail2ban/jail.d/3x-ipl.conf
[3x-ipl] [3x-ipl]
@@ -889,7 +854,7 @@ actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
[Init] [Init]
EOF EOF
echo -e "${green}Ip Limit jail files created with a bantime of ${bantime} minutes.${plain}" echo -e "${green}Created Ip Limit jail files with a bantime of ${bantime} minutes.${plain}"
} }
iplimit_remove_conflicts() { iplimit_remove_conflicts() {
@@ -917,80 +882,62 @@ iplimit_main() {
echo -e "${green}\t0.${plain} Back to Main Menu" echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice read -p "Choose an option: " choice
case "$choice" in case "$choice" in
0) 0)
show_menu show_menu ;;
;; 1)
1) confirm "Proceed with installation of Fail2ban & IP Limit?" "y"
confirm "Proceed with installation of Fail2ban & IP Limit?" "y" if [[ $? == 0 ]]; then
if [[ $? == 0 ]]; then install_iplimit
install_iplimit else
else iplimit_main
iplimit_main fi ;;
fi 2)
;; read -rp "Please enter new Ban Duration in Minutes [default 5]: " NUM
2) if [[ $NUM =~ ^[0-9]+$ ]]; then
read -rp "Please enter new Ban Duration in Minutes [default 30]: " NUM create_iplimit_jails ${NUM}
if [[ $NUM =~ ^[0-9]+$ ]]; then systemctl restart fail2ban
create_iplimit_jails ${NUM} else
systemctl restart fail2ban echo -e "${red}${NUM} is not a number! Please, try again.${plain}"
else fi
echo -e "${red}${NUM} is not a number! Please, try again.${plain}" iplimit_main ;;
fi 3)
iplimit_main confirm "Proceed with Unbanning everyone from IP Limit jail?" "y"
;; if [[ $? == 0 ]]; then
3) fail2ban-client reload --restart --unban 3x-ipl
confirm "Proceed with Unbanning everyone from IP Limit jail?" "y" echo -e "${green}All users Unbanned successfully.${plain}"
if [[ $? == 0 ]]; then iplimit_main
fail2ban-client reload --restart --unban 3x-ipl else
truncate -s 0 "${iplimit_banned_log_path}" echo -e "${yellow}Cancelled.${plain}"
echo -e "${green}All users Unbanned successfully.${plain}" fi
iplimit_main iplimit_main ;;
else 4)
echo -e "${yellow}Cancelled.${plain}" show_banlog
fi ;;
iplimit_main 5)
;; service fail2ban status
4) ;;
show_banlog
;;
5)
service fail2ban status
;;
6) 6)
remove_iplimit remove_iplimit ;;
;; *) echo "Invalid choice" ;;
*) echo "Invalid choice" ;;
esac esac
} }
install_iplimit() { install_iplimit() {
if ! command -v fail2ban-client &>/dev/null; then if ! command -v fail2ban-client &>/dev/null; then
echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n" echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n"
# Check the OS and install necessary packages # Check the OS and install necessary packages
case "${release}" in case "${release}" in
ubuntu | debian) ubuntu|debian)
apt update && apt install fail2ban -y apt update && apt install fail2ban -y ;;
;; centos)
centos | almalinux | rocky) yum -y update && yum -y install fail2ban ;;
yum update -y && yum install epel-release -y fedora)
yum -y install fail2ban dnf -y update && dnf -y install fail2ban ;;
;; *)
fedora) echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
dnf -y update && dnf -y install fail2ban exit 1 ;;
;;
*)
echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n"
exit 1
;;
esac esac
if ! command -v fail2ban-client &>/dev/null; then
echo -e "${red}Fail2ban installation failed.${plain}\n"
exit 1
fi
echo -e "${green}Fail2ban installed successfully!${plain}\n" echo -e "${green}Fail2ban installed successfully!${plain}\n"
else else
echo -e "${yellow}Fail2ban is already installed.${plain}\n" echo -e "${yellow}Fail2ban is already installed.${plain}\n"
@@ -1018,7 +965,6 @@ install_iplimit() {
# Launching fail2ban # Launching fail2ban
if ! systemctl is-active --quiet fail2ban; then if ! systemctl is-active --quiet fail2ban; then
systemctl start fail2ban systemctl start fail2ban
systemctl enable fail2ban
else else
systemctl restart fail2ban systemctl restart fail2ban
fi fi
@@ -1028,53 +974,41 @@ install_iplimit() {
before_show_menu before_show_menu
} }
remove_iplimit() { remove_iplimit(){
echo -e "${green}\t1.${plain} Only remove IP Limit configurations" echo -e "${green}\t1.${plain} Only remove IP Limit configurations"
echo -e "${green}\t2.${plain} Uninstall Fail2ban and IP Limit" echo -e "${green}\t2.${plain} Uninstall Fail2ban and IP Limit"
echo -e "${green}\t0.${plain} Abort" echo -e "${green}\t0.${plain} Abort"
read -p "Choose an option: " num read -p "Choose an option: " num
case "$num" in case "$num" in
1) 1)
rm -f /etc/fail2ban/filter.d/3x-ipl.conf rm -f /etc/fail2ban/filter.d/3x-ipl.conf
rm -f /etc/fail2ban/action.d/3x-ipl.conf rm -f /etc/fail2ban/action.d/3x-ipl.conf
rm -f /etc/fail2ban/jail.d/3x-ipl.conf rm -f /etc/fail2ban/jail.d/3x-ipl.conf
systemctl restart fail2ban systemctl restart fail2ban
echo -e "${green}IP Limit removed successfully!${plain}\n" echo -e "${green}IP Limit removed successfully!${plain}\n"
before_show_menu before_show_menu ;;
;; 2)
2) rm -rf /etc/fail2ban
rm -rf /etc/fail2ban systemctl stop fail2ban
systemctl stop fail2ban case "${release}" in
case "${release}" in ubuntu|debian)
ubuntu | debian) apt-get purge fail2ban -y;;
apt-get remove -y fail2ban centos)
apt-get purge -y fail2ban -y yum remove fail2ban -y;;
apt-get autoremove -y fedora)
;; dnf remove fail2ban -y;;
centos | almalinux | rocky) *)
yum remove fail2ban -y echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n"
yum autoremove -y exit 1 ;;
;; esac
fedora) echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
dnf remove fail2ban -y before_show_menu ;;
dnf autoremove -y 0)
;; echo -e "${yellow}Cancelled.${plain}\n"
*) iplimit_main ;;
echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n" *)
exit 1 echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
;; remove_iplimit ;;
esac
echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n"
before_show_menu
;;
0)
echo -e "${yellow}Cancelled.${plain}\n"
iplimit_main
;;
*)
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
remove_iplimit
;;
esac esac
} }
@@ -1101,37 +1035,36 @@ show_menu() {
${green}3X-ui Panel Management Script${plain} ${green}3X-ui Panel Management Script${plain}
${green}0.${plain} Exit Script ${green}0.${plain} Exit Script
———————————————— ————————————————
${green}1.${plain} Install ${green}1.${plain} Install x-ui
${green}2.${plain} Update ${green}2.${plain} Update x-ui
${green}3.${plain} Custom Version ${green}3.${plain} Uninstall x-ui
${green}4.${plain} Uninstall
———————————————— ————————————————
${green}5.${plain} Reset Username & Password & Secret Token ${green}4.${plain} Reset Username & Password & Secret Token
${green}6.${plain} Reset Settings ${green}5.${plain} Reset Panel Settings
${green}7.${plain} Change Port ${green}6.${plain} Change Panel Port
${green}8.${plain} View Current Settings ${green}7.${plain} View Current Panel Settings
———————————————— ————————————————
${green}9.${plain} Start ${green}8.${plain} Start x-ui
${green}10.${plain} Stop ${green}9.${plain} Stop x-ui
${green}11.${plain} Restart ${green}10.${plain} Restart x-ui
${green}12.${plain} Check Status ${green}11.${plain} Check x-ui Status
${green}13.${plain} Check Logs ${green}12.${plain} Check x-ui Logs
———————————————— ————————————————
${green}14.${plain} Enable Autostart ${green}13.${plain} Enable x-ui On System Startup
${green}15.${plain} Disable Autostart ${green}14.${plain} Disable x-ui On System Startup
———————————————— ————————————————
${green}16.${plain} SSL Certificate Management ${green}15.${plain} SSL Certificate Management
${green}17.${plain} Cloudflare SSL Certificate ${green}16.${plain} Cloudflare SSL Certificate
${green}18.${plain} IP Limit Management ${green}17.${plain} IP Limit Management
${green}19.${plain} WARP Management ${green}18.${plain} WARP Management
———————————————— ————————————————
${green}20.${plain} Enable BBR ${green}19.${plain} Enable BBR
${green}21.${plain} Update Geo Files ${green}20.${plain} Update Geo Files
${green}22.${plain} Active Firewall and open ports ${green}21.${plain} Active Firewall and open ports
${green}23.${plain} Speedtest by Ookla ${green}22.${plain} Speedtest by Ookla
" "
show_status show_status
echo && read -p "Please enter your selection [0-23]: " num echo && read -p "Please enter your selection [0-22]: " num
case "${num}" in case "${num}" in
0) 0)
@@ -1144,70 +1077,67 @@ show_menu() {
check_install && update check_install && update
;; ;;
3) 3)
check_install && custom_version
;;
4)
check_install && uninstall check_install && uninstall
;; ;;
5) 4)
check_install && reset_user check_install && reset_user
;; ;;
6) 5)
check_install && reset_config check_install && reset_config
;; ;;
7) 6)
check_install && set_port check_install && set_port
;; ;;
8) 7)
check_install && check_config check_install && check_config
;; ;;
9) 8)
check_install && start check_install && start
;; ;;
10) 9)
check_install && stop check_install && stop
;; ;;
11) 10)
check_install && restart check_install && restart
;; ;;
12) 11)
check_install && status check_install && status
;; ;;
13) 12)
check_install && show_log check_install && show_log
;; ;;
14) 13)
check_install && enable check_install && enable
;; ;;
15) 14)
check_install && disable check_install && disable
;; ;;
16) 15)
ssl_cert_issue_main ssl_cert_issue_main
;; ;;
17) 16)
ssl_cert_issue_CF ssl_cert_issue_CF
;; ;;
18) 17)
iplimit_main iplimit_main
;; ;;
19) 18)
warp_cloudflare warp_cloudflare
;; ;;
20) 19)
enable_bbr enable_bbr
;; ;;
21) 20)
update_geo update_geo
;; ;;
22) 21)
open_ports open_ports
;; ;;
23) 22)
run_speedtest run_speedtest
;; ;;
*) *)
LOGE "Please enter the correct number [0-23]" LOGE "Please enter the correct number [0-22]"
;; ;;
esac esac
} }

View File

@@ -50,9 +50,7 @@ func (x *XrayAPI) Init(apiPort int) (err error) {
} }
func (x *XrayAPI) Close() { func (x *XrayAPI) Close() {
if x.grpcClient != nil { x.grpcClient.Close()
x.grpcClient.Close()
}
x.HandlerServiceClient = nil x.HandlerServiceClient = nil
x.StatsServiceClient = nil x.StatsServiceClient = nil
x.isConnected = false x.isConnected = false

View File

@@ -20,13 +20,8 @@ func (lw *LogWriter) Write(m []byte) (n int, err error) {
lw.lastLine = messages[len(messages)-1] lw.lastLine = messages[len(messages)-1]
for _, msg := range messages { for _, msg := range messages {
messageBody := msg
// Remove timestamp // Remove timestamp
splittedMsg := strings.SplitN(msg, " ", 3) messageBody := strings.TrimSpace(strings.SplitN(msg, " ", 3)[2])
if len(splittedMsg) > 2 {
messageBody = strings.TrimSpace(strings.SplitN(msg, " ", 3)[2])
}
// Find level in [] // Find level in []
startIndex := strings.Index(messageBody, "[") startIndex := strings.Index(messageBody, "[")

View File

@@ -41,24 +41,12 @@ func GetIPLimitLogPath() string {
return config.GetLogFolder() + "/3xipl.log" return config.GetLogFolder() + "/3xipl.log"
} }
func GetIPLimitPrevLogPath() string {
return config.GetLogFolder() + "/3xipl.prev.log"
}
func GetIPLimitBannedLogPath() string { func GetIPLimitBannedLogPath() string {
return config.GetLogFolder() + "/3xipl-banned.log" return config.GetLogFolder() + "/3xipl-banned.log"
} }
func GetIPLimitBannedPrevLogPath() string {
return config.GetLogFolder() + "/3xipl-banned.prev.log"
}
func GetAccessPersistentLogPath() string { func GetAccessPersistentLogPath() string {
return config.GetLogFolder() + "/3xipl-ap.log" return config.GetLogFolder() + "/3xipl-access-persistent.log"
}
func GetAccessPersistentPrevLogPath() string {
return config.GetLogFolder() + "/3xipl-ap.prev.log"
} }
func GetAccessLogPath() string { func GetAccessLogPath() string {