mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-03-19 17:15:49 +00:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0bbacf013 | ||
|
|
34af7f8bfa | ||
|
|
b569c21fec | ||
|
|
c4a5c059e3 | ||
|
|
c21ed90da0 | ||
|
|
9b58277945 | ||
|
|
5a4a42aeb8 | ||
|
|
9476472bf6 | ||
|
|
2ce9c3cc81 | ||
|
|
0a8bfc2725 | ||
|
|
c5bbb6b632 | ||
|
|
f497a8dbcc | ||
|
|
4290081486 | ||
|
|
ccda652e69 | ||
|
|
2982d809ab | ||
|
|
7ad4a3dffc | ||
|
|
c0ef53e542 | ||
|
|
b7d1c84cd0 | ||
|
|
111bfe5d2e | ||
|
|
6e59aa14b0 | ||
|
|
a4cf77422f | ||
|
|
35df2a0505 | ||
|
|
9f445686a4 | ||
|
|
0fc935e996 | ||
|
|
adb08a60cf | ||
|
|
e3576e8a85 | ||
|
|
9c065aed4e | ||
|
|
7abb092211 | ||
|
|
937bfb4c78 | ||
|
|
1bcdc54b68 | ||
|
|
eb58314c53 | ||
|
|
c158e6ec73 | ||
|
|
5ae587ee81 | ||
|
|
d40fa46851 | ||
|
|
19a31686da | ||
|
|
13f7e07128 | ||
|
|
0e3691fdbd | ||
|
|
e359b5c75e | ||
|
|
3b3bd3dea4 | ||
|
|
8f36b7ea84 | ||
|
|
569d99512c | ||
|
|
ac84553a68 | ||
|
|
610db7827d | ||
|
|
088b55c9ed | ||
|
|
c800e29900 | ||
|
|
14435db0d8 | ||
|
|
bd6402562e | ||
|
|
d16ad11136 | ||
|
|
6c27e4177d | ||
|
|
bebf83f06c | ||
|
|
07bf741b15 | ||
|
|
5e5851029d | ||
|
|
e6020850fc | ||
|
|
80183f61e8 | ||
|
|
1b9432ff37 | ||
|
|
f1f813269c | ||
|
|
a23f390402 | ||
|
|
2950ce0c17 | ||
|
|
514c4909a4 | ||
|
|
99cadf7652 | ||
|
|
4ca36d64a8 | ||
|
|
863009dcaa | ||
|
|
2ef5ccc2fd | ||
|
|
744583b4e7 | ||
|
|
b36032e22c | ||
|
|
ac7901abba | ||
|
|
dff2496d73 | ||
|
|
d97d36bb9e | ||
|
|
b0d2cb93e1 | ||
|
|
f98d78c356 | ||
|
|
d85226dc79 | ||
|
|
1c2b6095c9 | ||
|
|
5f8c8f4525 |
67
.github/workflows/docker.yml
vendored
67
.github/workflows/docker.yml
vendored
@@ -1,5 +1,4 @@
|
|||||||
name: Release 3X-UI for Docker
|
name: Release 3X-UI for Docker
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
@@ -7,36 +6,50 @@ on:
|
|||||||
- "v*.*.*"
|
- "v*.*.*"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_and_push:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the code
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v4
|
with:
|
||||||
- name: Set up QEMU
|
submodules: true
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Docker meta
|
||||||
uses: docker/setup-buildx-action@v3
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
hsanaeii/3x-ui
|
||||||
|
ghcr.io/mhsanaei/3x-ui
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=tag
|
||||||
|
type=pep440,pattern={{version}}
|
||||||
|
|
||||||
- name: Login to GHCR
|
- name: Set up QEMU
|
||||||
uses: docker/login-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Set up Docker Buildx
|
||||||
id: meta
|
uses: docker/setup-buildx-action@v3
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: ghcr.io/${{ github.repository }}
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Login to Docker Hub
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
context: .
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
||||||
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
- name: Login to GHCR
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.repository_owner }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
|||||||
cd x-ui/bin
|
cd x-ui/bin
|
||||||
|
|
||||||
# Download dependencies
|
# Download dependencies
|
||||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.9.30/"
|
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.11.21/"
|
||||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||||
wget ${Xray_URL}Xray-linux-64.zip
|
wget ${Xray_URL}Xray-linux-64.zip
|
||||||
unzip Xray-linux-64.zip
|
unzip Xray-linux-64.zip
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ case $1 in
|
|||||||
esac
|
esac
|
||||||
mkdir -p build/bin
|
mkdir -p build/bin
|
||||||
cd build/bin
|
cd build/bin
|
||||||
wget "https://github.com/XTLS/Xray-core/releases/download/v24.9.30/Xray-linux-${ARCH}.zip"
|
wget "https://github.com/XTLS/Xray-core/releases/download/v24.11.21/Xray-linux-${ARCH}.zip"
|
||||||
unzip "Xray-linux-${ARCH}.zip"
|
unzip "Xray-linux-${ARCH}.zip"
|
||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||||
mv xray "xray-linux-${FNAME}"
|
mv xray "xray-linux-${FNAME}"
|
||||||
|
|||||||
@@ -30,38 +30,62 @@
|
|||||||
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)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Instalar una Versión Personalizada
|
## Instalar versión antigua (no recomendamos)
|
||||||
|
|
||||||
Para instalar la versión deseada, agrega la versión al final del comando de instalación. Por ejemplo, ver `v2.4.3`:
|
Para instalar la versión deseada, utiliza el siguiente comando de instalación. Por ejemplo, ver `v1.7.9`:
|
||||||
|
|
||||||
```
|
```
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.3
|
VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||||
```
|
```
|
||||||
|
|
||||||
## Certificado SSL
|
## Certificado SSL
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Haz clic para el Certificado SSL</summary>
|
<summary>Haga clic para ver los detalles del certificado SSL</summary>
|
||||||
|
|
||||||
### Cloudflare
|
### ACME
|
||||||
|
|
||||||
El script de gestión tiene una aplicación de certificado SSL incorporada para Cloudflare. Para usar este script para colocar un certificado, necesitas lo siguiente:
|
Para gestionar certificados SSL utilizando ACME:
|
||||||
|
|
||||||
- Correo electrónico registrado en Cloudflare
|
1. Asegúrate de que tu dominio esté correctamente resuelto al servidor.
|
||||||
- Clave Global de API de Cloudflare
|
2. Ejecuta el comando `x-ui` en la terminal y elige `Gestión de Certificados SSL`.
|
||||||
- El nombre de dominio se ha resuelto en el servidor actual a través de Cloudflare
|
3. Se te presentarán las siguientes opciones:
|
||||||
|
|
||||||
**1:** Ejecuta el comando`x-ui`en la terminal, luego elige `Certificado SSL de Cloudflare`.
|
|
||||||
|
|
||||||
|
- **Get SSL:** Obtener certificados SSL.
|
||||||
|
- **Revoke:** Revocar certificados SSL existentes.
|
||||||
|
- **Force Renew:** Forzar la renovación de certificados SSL.
|
||||||
|
- **Show Existing Domains:** Mostrar todos los certificados de dominio disponibles en el servidor.
|
||||||
|
- **Set Certificate Paths for the Panel:** Especificar el certificado para tu dominio que será utilizado por el panel.
|
||||||
|
|
||||||
### Certbot
|
### Certbot
|
||||||
```
|
|
||||||
|
Para instalar y usar Certbot:
|
||||||
|
|
||||||
|
```sh
|
||||||
apt-get install certbot -y
|
apt-get install certbot -y
|
||||||
certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
|
certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
|
||||||
certbot renew --dry-run
|
certbot renew --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
***Consejo:*** *Certbot también está integrado en el script de gestión. Puedes ejecutar el comando `x-ui` , luego elegir `Gestión de Certificados SSL`.*
|
### Cloudflare
|
||||||
|
|
||||||
|
El script de gestión incluye una aplicación de certificado SSL integrada para Cloudflare. Para usar este script para solicitar un certificado, necesitas lo siguiente:
|
||||||
|
|
||||||
|
- Correo electrónico registrado en Cloudflare
|
||||||
|
- Clave API Global de Cloudflare
|
||||||
|
- El nombre de dominio debe estar resuelto al servidor actual a través de Cloudflare
|
||||||
|
|
||||||
|
**Cómo obtener la Clave API Global de Cloudflare:**
|
||||||
|
|
||||||
|
1. Ejecuta el comando `x-ui` en la terminal y elige `Certificado SSL de Cloudflare`.
|
||||||
|
2. Visita el enlace: [Tokens de API de Cloudflare](https://dash.cloudflare.com/profile/api-tokens).
|
||||||
|
3. Haz clic en "Ver Clave API Global" (consulta la captura de pantalla a continuación):
|
||||||
|

|
||||||
|
4. Es posible que necesites volver a autenticar tu cuenta. Después de eso, se mostrará la Clave API (consulta la captura de pantalla a continuación):
|
||||||
|

|
||||||
|
|
||||||
|
Al utilizarlo, simplemente ingresa tu `nombre de dominio`, `correo electrónico` y `CLAVE API`. El diagrama es el siguiente:
|
||||||
|

|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@@ -229,6 +253,7 @@ location /sub {
|
|||||||
- Oracle Linux 8+
|
- Oracle Linux 8+
|
||||||
- OpenSUSE Tubleweed
|
- OpenSUSE Tubleweed
|
||||||
- Amazon Linux 2023
|
- Amazon Linux 2023
|
||||||
|
- Windows x64
|
||||||
|
|
||||||
## Arquitecturas y Dispositivos Compatibles
|
## Arquitecturas y Dispositivos Compatibles
|
||||||
|
|
||||||
@@ -252,14 +277,18 @@ Nuestra plataforma ofrece compatibilidad con una amplia gama de arquitecturas y
|
|||||||
|
|
||||||
## Idiomas
|
## Idiomas
|
||||||
|
|
||||||
- Inglés
|
- English (inglés)
|
||||||
- Farsi
|
- Persian (persa)
|
||||||
- Chino
|
- Traditional Chinese (chino tradicional)
|
||||||
- Ruso
|
- Simplified Chinese (chino simplificado)
|
||||||
- Vietnamita
|
- Japanese (japonés)
|
||||||
- Español
|
- Russian (ruso)
|
||||||
- Indonesio
|
- Vietnamese (vietnamita)
|
||||||
- Ucraniano
|
- Spanish (español)
|
||||||
|
- Indonesian (indonesio)
|
||||||
|
- Ukrainian (ucraniano)
|
||||||
|
- Turkish (turco)
|
||||||
|
- Português (Brazil) (portugués (Brasil))
|
||||||
|
|
||||||
|
|
||||||
## Características
|
## Características
|
||||||
@@ -452,7 +481,7 @@ Ingresa el ID de chat de usuario en el campo de entrada número 4. Las cuentas d
|
|||||||
|
|
||||||
#### Uso
|
#### Uso
|
||||||
|
|
||||||
- [Documentación de API](https://documenter.getpostman.com/view/5146551/2sAXxP8Y12)
|
- [Documentación de API](https://www.postman.com/hsanaei/3x-ui/collection/q1l5l0u/3x-ui)
|
||||||
- `/login` con `POST` datos de usuario: `{username: '', password: ''}` para iniciar sesión
|
- `/login` con `POST` datos de usuario: `{username: '', password: ''}` para iniciar sesión
|
||||||
- `/panel/api/inbounds` base para las siguientes acciones:
|
- `/panel/api/inbounds` base para las siguientes acciones:
|
||||||
|
|
||||||
@@ -482,7 +511,7 @@ Ingresa el ID de chat de usuario en el campo de entrada número 4. Las cuentas d
|
|||||||
- `client.password` para TROJAN
|
- `client.password` para TROJAN
|
||||||
- `client.email` para Shadowsocks
|
- `client.email` para Shadowsocks
|
||||||
|
|
||||||
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://god.gw.postman.com/run-collection/5146551-e6aac565-e0e2-46df-acff-2607a51bbd04?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-e6aac565-e0e2-46df-acff-2607a51bbd04%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
|
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://app.getpostman.com/run-collection/5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Variables de Entorno
|
## Variables de Entorno
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -30,12 +30,12 @@
|
|||||||
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 legacy Version (we don't recommend)
|
||||||
|
|
||||||
To install your desired version, add the version to the end of the installation command. e.g., ver `v2.4.3`:
|
To install your desired version, use following installation command. e.g., ver `v1.7.9`:
|
||||||
|
|
||||||
```
|
```
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.3
|
VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||||
```
|
```
|
||||||
|
|
||||||
## SSL Certificate
|
## SSL Certificate
|
||||||
@@ -54,6 +54,8 @@ To manage SSL certificates using ACME:
|
|||||||
- **Get SSL:** Obtain SSL certificates.
|
- **Get SSL:** Obtain SSL certificates.
|
||||||
- **Revoke:** Revoke existing SSL certificates.
|
- **Revoke:** Revoke existing SSL certificates.
|
||||||
- **Force Renew:** Force renewal of SSL certificates.
|
- **Force Renew:** Force renewal of SSL certificates.
|
||||||
|
- **Show Existing Domains:** Display all domain certificates available on the server.
|
||||||
|
- **Set Certificate Paths for the Panel:** Specify the certificate for your domain to be used by the panel.
|
||||||
|
|
||||||
### Certbot
|
### Certbot
|
||||||
|
|
||||||
@@ -256,6 +258,7 @@ location /sub {
|
|||||||
- Oracle Linux 8+
|
- Oracle Linux 8+
|
||||||
- OpenSUSE Tubleweed
|
- OpenSUSE Tubleweed
|
||||||
- Amazon Linux 2023
|
- Amazon Linux 2023
|
||||||
|
- Windows x64
|
||||||
|
|
||||||
## Supported Architectures and Devices
|
## Supported Architectures and Devices
|
||||||
|
|
||||||
@@ -282,8 +285,10 @@ Our platform offers compatibility with a diverse range of architectures and devi
|
|||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
- English
|
- English
|
||||||
- Farsi
|
- Persian
|
||||||
- Chinese
|
- Traditional Chinese
|
||||||
|
- Simplified Chinese
|
||||||
|
- Japanese
|
||||||
- Russian
|
- Russian
|
||||||
- Vietnamese
|
- Vietnamese
|
||||||
- Spanish
|
- Spanish
|
||||||
@@ -484,7 +489,7 @@ Enter the user ID in input field number 4. The Telegram accounts with this id wi
|
|||||||
|
|
||||||
#### Usage
|
#### Usage
|
||||||
|
|
||||||
- [API Documentation](https://documenter.getpostman.com/view/5146551/2sAXxP8Y12)
|
- [API Documentation](https://www.postman.com/hsanaei/3x-ui/collection/q1l5l0u/3x-ui)
|
||||||
- `/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:
|
||||||
|
|
||||||
@@ -515,7 +520,7 @@ Enter the user ID in input field number 4. The Telegram accounts with this id wi
|
|||||||
- `client.password` for TROJAN
|
- `client.password` for TROJAN
|
||||||
- `client.email` for Shadowsocks
|
- `client.email` for Shadowsocks
|
||||||
|
|
||||||
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://god.gw.postman.com/run-collection/5146551-e6aac565-e0e2-46df-acff-2607a51bbd04?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-e6aac565-e0e2-46df-acff-2607a51bbd04%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
|
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://app.getpostman.com/run-collection/5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|||||||
@@ -30,12 +30,12 @@
|
|||||||
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)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Установка определённой версии
|
## Установить старую версию (мы не рекомендуем)
|
||||||
|
|
||||||
Чтобы установить нужную вам версию, добавьте номер версии в конец команды установки. Например, `v2.4.3`:
|
Чтобы установить желаемую версию, используйте следующую команду установки. Например, ver `v1.7.9`:
|
||||||
|
|
||||||
```
|
```
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.3
|
VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||||
```
|
```
|
||||||
|
|
||||||
## SSL Сертификат
|
## SSL Сертификат
|
||||||
@@ -54,6 +54,8 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
|||||||
- **Get SSL:** Получить SSL сертификаты.
|
- **Get SSL:** Получить SSL сертификаты.
|
||||||
- **Revoke:** Отозвать существующие SSL сертификаты.
|
- **Revoke:** Отозвать существующие SSL сертификаты.
|
||||||
- **Force Renew:** Принудительно перевыпустить SSL сертификаты.
|
- **Force Renew:** Принудительно перевыпустить SSL сертификаты.
|
||||||
|
- **Show Existing Domains:** Отобразить все сертификаты доменов, доступные на сервере.
|
||||||
|
- **Set Certificate Paths for the Panel:** Укажите сертификат для вашего домена, который будет использоваться панелью.
|
||||||
|
|
||||||
### Certbot
|
### Certbot
|
||||||
|
|
||||||
@@ -255,6 +257,7 @@ location /sub {
|
|||||||
- Oracle Linux 8+
|
- Oracle Linux 8+
|
||||||
- OpenSUSE Tubleweed
|
- OpenSUSE Tubleweed
|
||||||
- Amazon Linux 2023
|
- Amazon Linux 2023
|
||||||
|
- Windows x64
|
||||||
|
|
||||||
## Поддерживаемые архитектуры и устройства
|
## Поддерживаемые архитектуры и устройства
|
||||||
|
|
||||||
@@ -280,16 +283,18 @@ location /sub {
|
|||||||
|
|
||||||
## Языки
|
## Языки
|
||||||
|
|
||||||
- Английский
|
- English (английский)
|
||||||
- Фарси
|
- Persian (персидский)
|
||||||
- Китайский
|
- Traditional Chinese (традиционный китайский)
|
||||||
- Русский
|
- Simplified Chinese (упрощенный китайский)
|
||||||
- Вьетнамский
|
- Japanese (японский)
|
||||||
- Испанский
|
- Russian (русский)
|
||||||
- Индонезийский
|
- Vietnamese (вьетнамский)
|
||||||
- Украинский
|
- Spanish (испанский)
|
||||||
- Турецкий
|
- Indonesian (индонезийский)
|
||||||
- Португальский (Бразилия)
|
- Ukrainian (украинский)
|
||||||
|
- Turkish (турецкий)
|
||||||
|
- Português (Brazil) (португальский (Бразилия))
|
||||||
|
|
||||||
## Возможности
|
## Возможности
|
||||||
|
|
||||||
@@ -479,7 +484,7 @@ WARP встроен, и дополнительная установка не т
|
|||||||
|
|
||||||
#### Использование
|
#### Использование
|
||||||
|
|
||||||
- [API документация](https://documenter.getpostman.com/view/5146551/2sAXxP8Y12)
|
- [API документация](https://www.postman.com/hsanaei/3x-ui/collection/q1l5l0u/3x-ui)
|
||||||
- `/login` с `POST`-данными: `{username: '', password: ''}` для входа
|
- `/login` с `POST`-данными: `{username: '', password: ''}` для входа
|
||||||
- `/panel/api/inbounds` это базовый путь для следующих действий:
|
- `/panel/api/inbounds` это базовый путь для следующих действий:
|
||||||
|
|
||||||
@@ -513,7 +518,7 @@ WARP встроен, и дополнительная установка не т
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
||||||
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://god.gw.postman.com/run-collection/5146551-e6aac565-e0e2-46df-acff-2607a51bbd04?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-e6aac565-e0e2-46df-acff-2607a51bbd04%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
|
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://app.getpostman.com/run-collection/5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Переменные среды
|
## Переменные среды
|
||||||
|
|||||||
@@ -30,12 +30,12 @@
|
|||||||
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)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 安装指定版本
|
## 安装旧版本 (我们不建议)
|
||||||
|
|
||||||
要安装所需的版本,请将该版本添加到安装命令的末尾。 e.g., ver `v2.4.3`:
|
要安装您想要的版本,请使用以下安装命令。例如,ver `v1.7.9`:
|
||||||
|
|
||||||
```
|
```
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.3
|
VERSION=v1.7.9 && <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||||
```
|
```
|
||||||
|
|
||||||
### SSL证书
|
### SSL证书
|
||||||
@@ -51,9 +51,11 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
|||||||
2. 在终端中运行 `x-ui` 命令,然后选择 `SSL证书管理`。
|
2. 在终端中运行 `x-ui` 命令,然后选择 `SSL证书管理`。
|
||||||
3. 您将看到以下选项:
|
3. 您将看到以下选项:
|
||||||
|
|
||||||
- **获取SSL证书:** 获取SSL证书。
|
- **Get SSL:** 获取SSL证书。
|
||||||
- **吊销:** 吊销现有的SSL证书。
|
- **Revoke:** 吊销现有的SSL证书。
|
||||||
- **强制更新:** 强制更新SSL证书。
|
- **Force Renew:** 强制更新SSL证书。
|
||||||
|
- **Show Existing Domains:** 显示服务器上所有可用的域证书。
|
||||||
|
- **Set Certificate Paths for the Panel:** 指定用于面板的域证书。
|
||||||
|
|
||||||
### Certbot
|
### Certbot
|
||||||
|
|
||||||
@@ -252,6 +254,7 @@ location /sub {
|
|||||||
- Oracle Linux 8+
|
- Oracle Linux 8+
|
||||||
- OpenSUSE Tubleweed
|
- OpenSUSE Tubleweed
|
||||||
- Amazon Linux 2023
|
- Amazon Linux 2023
|
||||||
|
- Windows x64
|
||||||
|
|
||||||
## 支持的架构和设备
|
## 支持的架构和设备
|
||||||
<details>
|
<details>
|
||||||
@@ -275,13 +278,17 @@ location /sub {
|
|||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
- English(英语)
|
- English(英语)
|
||||||
- Farsi(伊朗语)
|
- Persian(波斯语)
|
||||||
- Chinese(中文)
|
- Traditional Chinese(繁体中文)
|
||||||
|
- Simplified Chinese(简体中文)
|
||||||
|
- Japanese(日语)
|
||||||
- Russian(俄语)
|
- Russian(俄语)
|
||||||
- Vietnamese(越南语)
|
- Vietnamese(越南语)
|
||||||
- Spanish(西班牙语)
|
- Spanish(西班牙语)
|
||||||
- Indonesian (印度尼西亚语)
|
- Indonesian(印尼语)
|
||||||
- Ukrainian(乌克兰语)
|
- Ukrainian(乌克兰语)
|
||||||
|
- Turkish(土耳其语)
|
||||||
|
- Português (Brazil)(葡萄牙语(巴西))
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
@@ -474,7 +481,7 @@ Web 面板通过 Telegram Bot 支持每日流量、面板登录、数据库备
|
|||||||
|
|
||||||
#### 使用
|
#### 使用
|
||||||
|
|
||||||
- [API 文档](https://documenter.getpostman.com/view/5146551/2sAXxP8Y12)
|
- [API 文档](https://www.postman.com/hsanaei/3x-ui/collection/q1l5l0u/3x-ui)
|
||||||
- `/login` 使用 `POST` 用户名称 & 密码: `{username: '', password: ''}` 登录
|
- `/login` 使用 `POST` 用户名称 & 密码: `{username: '', password: ''}` 登录
|
||||||
- `/panel/api/inbounds` 以下操作的基础:
|
- `/panel/api/inbounds` 以下操作的基础:
|
||||||
|
|
||||||
@@ -504,7 +511,7 @@ Web 面板通过 Telegram Bot 支持每日流量、面板登录、数据库备
|
|||||||
- `client.password` TROJAN
|
- `client.password` TROJAN
|
||||||
- `client.email` Shadowsocks
|
- `client.email` Shadowsocks
|
||||||
|
|
||||||
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://god.gw.postman.com/run-collection/5146551-e6aac565-e0e2-46df-acff-2607a51bbd04?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-e6aac565-e0e2-46df-acff-2607a51bbd04%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
|
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://app.getpostman.com/run-collection/5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## 环境变量
|
## 环境变量
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.4.3
|
2.4.8
|
||||||
64
go.mod
64
go.mod
@@ -1,46 +1,46 @@
|
|||||||
module x-ui
|
module x-ui
|
||||||
|
|
||||||
go 1.23.2
|
go 1.23.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/gzip v1.0.1
|
github.com/gin-contrib/gzip v1.0.1
|
||||||
github.com/gin-contrib/sessions v1.0.1
|
github.com/gin-contrib/sessions v1.0.1
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/goccy/go-json v0.10.3
|
github.com/goccy/go-json v0.10.3
|
||||||
github.com/mymmrac/telego v0.31.3
|
github.com/mymmrac/telego v0.31.4
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.4.0
|
github.com/nicksnyder/go-i18n/v2 v2.4.1
|
||||||
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.2.3
|
github.com/pelletier/go-toml/v2 v2.2.3
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v4 v4.24.9
|
github.com/shirou/gopsutil/v4 v4.24.10
|
||||||
github.com/valyala/fasthttp v1.56.0
|
github.com/valyala/fasthttp v1.57.0
|
||||||
github.com/xtls/xray-core v1.8.25-0.20241005021528-c30f5d47964b
|
github.com/xtls/xray-core v1.8.25-0.20241121054707-513f18bf531e
|
||||||
go.uber.org/atomic v1.11.0
|
go.uber.org/atomic v1.11.0
|
||||||
golang.org/x/text v0.19.0
|
golang.org/x/text v0.20.0
|
||||||
google.golang.org/grpc v1.67.1
|
google.golang.org/grpc v1.68.0
|
||||||
gorm.io/driver/sqlite v1.5.6
|
gorm.io/driver/sqlite v1.5.6
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
github.com/bytedance/sonic v1.12.3 // indirect
|
github.com/bytedance/sonic v1.12.4 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
github.com/bytedance/sonic/loader v0.2.1 // indirect
|
||||||
github.com/cloudflare/circl v1.5.0 // indirect
|
github.com/cloudflare/circl v1.5.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/iasm v0.2.0 // 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/ebitengine/purego v0.8.0 // indirect
|
github.com/ebitengine/purego v0.8.1 // indirect
|
||||||
github.com/fasthttp/router v1.5.2 // indirect
|
github.com/fasthttp/router v1.5.2 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.7 // 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.22.1 // indirect
|
github.com/go-playground/validator/v10 v10.23.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/google/btree v1.1.3 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect
|
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect
|
||||||
github.com/gorilla/context v1.1.2 // indirect
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/sessions v1.4.0 // indirect
|
github.com/gorilla/sessions v1.4.0 // indirect
|
||||||
@@ -49,8 +49,8 @@ require (
|
|||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.10 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
||||||
@@ -58,15 +58,15 @@ require (
|
|||||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
github.com/mattn/go-sqlite3 v1.14.24 // 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.20.2 // indirect
|
github.com/onsi/ginkgo/v2 v2.21.0 // indirect
|
||||||
github.com/pires/go-proxyproto v0.8.0 // indirect
|
github.com/pires/go-proxyproto v0.8.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/quic-go/quic-go v0.47.0 // indirect
|
github.com/quic-go/quic-go v0.48.1 // indirect
|
||||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
github.com/refraction-networking/utls v1.6.7 // 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.13.1 // indirect
|
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||||
github.com/sagernet/sing v0.4.3 // indirect
|
github.com/sagernet/sing v0.5.1 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
|
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
|
||||||
@@ -78,24 +78,24 @@ require (
|
|||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fastjson v1.6.4 // indirect
|
github.com/valyala/fastjson v1.6.4 // indirect
|
||||||
github.com/vishvananda/netlink v1.3.0 // indirect
|
github.com/vishvananda/netlink v1.3.0 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
|
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
golang.org/x/arch v0.11.0 // indirect
|
golang.org/x/arch v0.12.0 // indirect
|
||||||
golang.org/x/crypto v0.28.0 // indirect
|
golang.org/x/crypto v0.29.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||||
golang.org/x/mod v0.21.0 // indirect
|
golang.org/x/mod v0.22.0 // indirect
|
||||||
golang.org/x/net v0.30.0 // indirect
|
golang.org/x/net v0.31.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.9.0 // indirect
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
golang.org/x/sys v0.27.0 // indirect
|
||||||
golang.org/x/time v0.7.0 // indirect
|
golang.org/x/time v0.8.0 // indirect
|
||||||
golang.org/x/tools v0.26.0 // indirect
|
golang.org/x/tools v0.27.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect
|
||||||
google.golang.org/protobuf v1.35.1 // indirect
|
google.golang.org/protobuf v1.35.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
|
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
|
||||||
lukechampine.com/blake3 v1.3.0 // indirect
|
lukechampine.com/blake3 v1.3.0 // indirect
|
||||||
|
|||||||
134
go.sum
134
go.sum
@@ -1,14 +1,14 @@
|
|||||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I=
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I=
|
||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
||||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
|
github.com/bytedance/sonic v1.12.4 h1:9Csb3c9ZJhfUWeMtpCDCq6BUoH5ogfDFLUgQ/jG+R0k=
|
||||||
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
github.com/bytedance/sonic v1.12.4/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
|
||||||
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
||||||
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
@@ -22,12 +22,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
|
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
|
||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||||
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
|
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
|
||||||
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
github.com/fasthttp/router v1.5.2 h1:ckJCCdV7hWkkrMeId3WfEhz+4Gyyf6QPwxi/RHIMZ6I=
|
github.com/fasthttp/router v1.5.2 h1:ckJCCdV7hWkkrMeId3WfEhz+4Gyyf6QPwxi/RHIMZ6I=
|
||||||
github.com/fasthttp/router v1.5.2/go.mod h1:C8EY53ozOwpONyevc/V7Gr8pqnEjwnkFFqPo1alAGs0=
|
github.com/fasthttp/router v1.5.2/go.mod h1:C8EY53ozOwpONyevc/V7Gr8pqnEjwnkFFqPo1alAGs0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
|
||||||
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=
|
||||||
github.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE=
|
github.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE=
|
||||||
@@ -49,14 +49,16 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
|||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
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.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||||
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
@@ -64,8 +66,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
|||||||
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 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20241009165004-a3522334989c h1:NDovD0SMpBYXlE1zJmS1q55vWB/fUQBcPAqAboZSccA=
|
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs=
|
||||||
github.com/google/pprof v0.0.0-20241009165004-a3522334989c/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||||
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||||
@@ -82,11 +84,11 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
|||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
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/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
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.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
@@ -107,14 +109,14 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mymmrac/telego v0.31.3 h1:yZlD+dm+1W6p3OmCG8K+MbS02Y6paUgwPnqfZN3RWQQ=
|
github.com/mymmrac/telego v0.31.4 h1:NpiNl0P/8eydknka/k6XaaaWVj5BKMlM3Ibba63QTBU=
|
||||||
github.com/mymmrac/telego v0.31.3/go.mod h1:coOoqXVmjFnwBlzusjfEezbQ7RH9wQnDowJdMm+bnEo=
|
github.com/mymmrac/telego v0.31.4/go.mod h1:T12js1PgbYDYznvoN05MSMuPMfWTYo7D9LKl5cPFWiI=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
|
github.com/nicksnyder/go-i18n/v2 v2.4.1 h1:zwzjtX4uYyiaU02K5Ia3zSkpJZrByARkRB4V3YPrr0g=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
|
github.com/nicksnyder/go-i18n/v2 v2.4.1/go.mod h1:++Pl70FR6Cki7hdzZRnEEqdc2dJt+SAGotyFg/SvZMk=
|
||||||
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||||
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
@@ -129,8 +131,8 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
|||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
|
github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
|
||||||
github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E=
|
github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||||
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||||
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=
|
||||||
@@ -139,16 +141,16 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
|||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||||
github.com/sagernet/sing v0.4.3 h1:Ty/NAiNnVd6844k7ujlL5lkzydhcTH5Psc432jXA4Y8=
|
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
|
||||||
github.com/sagernet/sing v0.4.3/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
|
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||||
github.com/shirou/gopsutil/v4 v4.24.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
|
github.com/shirou/gopsutil/v4 v4.24.10 h1:7VOzPtfw/5YDU+jLEoBwXwxJbQetULywoSV4RYY7HkM=
|
||||||
github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q=
|
github.com/shirou/gopsutil/v4 v4.24.10/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
@@ -172,65 +174,65 @@ github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF
|
|||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW8U=
|
github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg=
|
||||||
github.com/valyala/fasthttp v1.56.0/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI=
|
github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE=
|
||||||
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||||
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
||||||
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
|
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
|
||||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
|
||||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
|
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
|
||||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
|
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
|
||||||
github.com/xtls/xray-core v1.8.25-0.20241005021528-c30f5d47964b h1:bWuePNnzV4ptnSYJkY96dAg3WgjYbfVgGnasLe3++9w=
|
github.com/xtls/xray-core v1.8.25-0.20241121054707-513f18bf531e h1:J5sTv0Sw+BonaI+rBh4Jkw9BfBqDjfAts81/HbIaqNg=
|
||||||
github.com/xtls/xray-core v1.8.25-0.20241005021528-c30f5d47964b/go.mod h1:YSvBScSqyzAocGDvzHBbEeoHNrFy8nV6gityRVDvHaM=
|
github.com/xtls/xray-core v1.8.25-0.20241121054707-513f18bf531e/go.mod h1:wByClH1yrH8I611sREjG62gxbP5hFtdAWYJfydQF/zI=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
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.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
|
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
|
||||||
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||||
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-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
||||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
|
||||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
|
||||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
|||||||
93
install.sh
93
install.sh
@@ -49,6 +49,8 @@ elif [[ "${release}" == "manjaro" ]]; then
|
|||||||
echo "Your OS is Manjaro"
|
echo "Your OS is Manjaro"
|
||||||
elif [[ "${release}" == "armbian" ]]; then
|
elif [[ "${release}" == "armbian" ]]; then
|
||||||
echo "Your OS is Armbian"
|
echo "Your OS is Armbian"
|
||||||
|
elif [[ "${release}" == "alpine" ]]; then
|
||||||
|
echo "Your OS is Alpine Linux"
|
||||||
elif [[ "${release}" == "opensuse-tumbleweed" ]]; then
|
elif [[ "${release}" == "opensuse-tumbleweed" ]]; then
|
||||||
echo "Your OS is OpenSUSE Tumbleweed"
|
echo "Your OS is OpenSUSE Tumbleweed"
|
||||||
elif [[ "${release}" == "openEuler" ]]; then
|
elif [[ "${release}" == "openEuler" ]]; then
|
||||||
@@ -137,62 +139,59 @@ gen_random_string() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config_after_install() {
|
config_after_install() {
|
||||||
echo -e "${yellow}Install/update finished! For security, it's recommended to modify panel settings ${plain}"
|
local existing_username=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'username: .+' | awk '{print $2}')
|
||||||
read -p "Would you like to customize the Panel Port settings? (If not, random settings will be applied) [y/n]: " config_confirm
|
local existing_password=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'password: .+' | awk '{print $2}')
|
||||||
|
local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
||||||
|
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
||||||
|
local server_ip=$(curl -s https://api.ipify.org)
|
||||||
|
|
||||||
local config_webBasePath=$(gen_random_string 15)
|
if [[ ${#existing_webBasePath} -lt 4 ]]; then
|
||||||
local config_account=$(gen_random_string 10)
|
if [[ "$existing_username" == "admin" && "$existing_password" == "admin" ]]; then
|
||||||
local config_password=$(gen_random_string 10)
|
local config_webBasePath=$(gen_random_string 15)
|
||||||
|
local config_username=$(gen_random_string 10)
|
||||||
|
local config_password=$(gen_random_string 10)
|
||||||
|
|
||||||
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
read -p "Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: " config_confirm
|
||||||
|
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
||||||
|
read -p "Please set up the panel port: " config_port
|
||||||
|
echo -e "${yellow}Your Panel Port is: ${config_port}${plain}"
|
||||||
|
else
|
||||||
|
local config_port=$(shuf -i 1024-62000 -n 1)
|
||||||
|
echo -e "${yellow}Generated random port: ${config_port}${plain}"
|
||||||
|
fi
|
||||||
|
|
||||||
read -p "Please set up the panel port: " config_port
|
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
|
||||||
echo -e "${yellow}Your Panel Port is: ${config_port}${plain}"
|
|
||||||
|
|
||||||
echo -e "${yellow}Your Username will be generated randomly: ${config_account}${plain}"
|
|
||||||
echo -e "${yellow}Your Password will be generated randomly: ${config_password}${plain}"
|
|
||||||
echo -e "${yellow}Your Web Base Path will be generated randomly: ${config_webBasePath}${plain}"
|
|
||||||
|
|
||||||
echo -e "${yellow}Initializing, please wait...${plain}"
|
|
||||||
|
|
||||||
/usr/local/x-ui/x-ui setting -username "${config_account}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}"
|
|
||||||
echo -e "${green}Settings applied successfully!${plain}"
|
|
||||||
|
|
||||||
echo -e "###############################################"
|
|
||||||
echo -e "${green}Username: ${config_account}${plain}"
|
|
||||||
echo -e "${green}Password: ${config_password}${plain}"
|
|
||||||
echo -e "${green}Port: ${config_port}${plain}"
|
|
||||||
echo -e "${green}WebBasePath: ${config_webBasePath}${plain}"
|
|
||||||
echo -e "###############################################"
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
echo -e "${red}Cancel...${plain}"
|
|
||||||
|
|
||||||
if [[ ! -f "/etc/x-ui/x-ui.db" ]]; then
|
|
||||||
|
|
||||||
local portTemp=$(shuf -i 1024-62000 -n 1)
|
|
||||||
|
|
||||||
/usr/local/x-ui/x-ui setting -username "${config_account}" -password "${config_password}" -port "${portTemp}" -webBasePath "${config_webBasePath}"
|
|
||||||
echo -e "This is a fresh installation, generating random login info for security concerns:"
|
echo -e "This is a fresh installation, generating random login info for security concerns:"
|
||||||
echo -e "###############################################"
|
echo -e "###############################################"
|
||||||
echo -e "${green}Username: ${config_account}${plain}"
|
echo -e "${green}Username: ${config_username}${plain}"
|
||||||
echo -e "${green}Password: ${config_password}${plain}"
|
echo -e "${green}Password: ${config_password}${plain}"
|
||||||
echo -e "${green}Port: ${portTemp}${plain}"
|
echo -e "${green}Port: ${config_port}${plain}"
|
||||||
echo -e "${green}WebBasePath: ${config_webBasePath}${plain}"
|
echo -e "${green}WebBasePath: ${config_webBasePath}${plain}"
|
||||||
|
echo -e "${green}Access URL: http://${server_ip}:${config_port}/${config_webBasePath}${plain}"
|
||||||
echo -e "###############################################"
|
echo -e "###############################################"
|
||||||
echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check after installation${plain}"
|
echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check${plain}"
|
||||||
else
|
else
|
||||||
echo -e "${yellow}This is your upgrade, keeping old settings. If you forgot your login info, you can type 'x-ui settings' to check${plain}"
|
local config_webBasePath=$(gen_random_string 15)
|
||||||
|
echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}"
|
||||||
|
/usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}"
|
||||||
|
echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
|
||||||
|
echo -e "${green}Access URL: http://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [[ "$existing_username" == "admin" && "$existing_password" == "admin" ]]; then
|
||||||
|
local config_username=$(gen_random_string 10)
|
||||||
|
local config_password=$(gen_random_string 10)
|
||||||
|
|
||||||
local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
echo -e "${yellow}Default credentials detected. Security update required...${plain}"
|
||||||
|
/usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}"
|
||||||
if [[ ${#existing_webBasePath} -lt 4 ]]; then
|
echo -e "Generated new random login credentials:"
|
||||||
echo -e "${yellow}WebBasePath is empty, generating a random one...${plain}"
|
echo -e "###############################################"
|
||||||
|
echo -e "${green}Username: ${config_username}${plain}"
|
||||||
/usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}"
|
echo -e "${green}Password: ${config_password}${plain}"
|
||||||
echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
|
echo -e "###############################################"
|
||||||
fi
|
echo -e "${yellow}If you forgot your login info, you can type 'x-ui settings' to check${plain}"
|
||||||
|
else
|
||||||
|
echo -e "${green}Username, Password, and WebBasePath are properly set. Exiting...${plain}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -275,7 +274,7 @@ install_x-ui() {
|
|||||||
echo -e "x-ui log - Check logs"
|
echo -e "x-ui log - Check logs"
|
||||||
echo -e "x-ui banlog - Check Fail2ban ban logs"
|
echo -e "x-ui banlog - Check Fail2ban ban logs"
|
||||||
echo -e "x-ui update - Update"
|
echo -e "x-ui update - Update"
|
||||||
echo -e "x-ui custom - custom version"
|
echo -e "x-ui legacy - legacy version"
|
||||||
echo -e "x-ui install - Install"
|
echo -e "x-ui install - Install"
|
||||||
echo -e "x-ui uninstall - Uninstall"
|
echo -e "x-ui uninstall - Uninstall"
|
||||||
echo -e "----------------------------------------------"
|
echo -e "----------------------------------------------"
|
||||||
|
|||||||
77
main.go
77
main.go
@@ -136,6 +136,15 @@ func showSetting(show bool) {
|
|||||||
fmt.Println("get webBasePath failed, error info:", err)
|
fmt.Println("get webBasePath failed, error info:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
certFile, err := settingService.GetCertFile()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("get cert file failed, error info:", err)
|
||||||
|
}
|
||||||
|
keyFile, err := settingService.GetKeyFile()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("get key file failed, error info:", err)
|
||||||
|
}
|
||||||
|
|
||||||
userService := service.UserService{}
|
userService := service.UserService{}
|
||||||
userModel, err := userService.GetFirstUser()
|
userModel, err := userService.GetFirstUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -149,14 +158,15 @@ func showSetting(show bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("current panel settings as follows:")
|
fmt.Println("current panel settings as follows:")
|
||||||
|
if certFile == "" || keyFile == "" {
|
||||||
|
fmt.Println("Warning: Panel is not secure with SSL")
|
||||||
|
} else {
|
||||||
|
fmt.Println("Panel is secure with SSL")
|
||||||
|
}
|
||||||
fmt.Println("username:", username)
|
fmt.Println("username:", username)
|
||||||
fmt.Println("password:", userpasswd)
|
fmt.Println("password:", userpasswd)
|
||||||
fmt.Println("port:", port)
|
fmt.Println("port:", port)
|
||||||
if webBasePath != "" {
|
fmt.Println("webBasePath:", webBasePath)
|
||||||
fmt.Println("webBasePath:", webBasePath)
|
|
||||||
} else {
|
|
||||||
fmt.Println("webBasePath is not set")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +226,7 @@ func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSetting(port int, username string, password string, webBasePath string) {
|
func updateSetting(port int, username string, password string, webBasePath string, listenIP string) {
|
||||||
err := database.InitDB(config.GetDBPath())
|
err := database.InitDB(config.GetDBPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Database initialization failed:", err)
|
fmt.Println("Database initialization failed:", err)
|
||||||
@@ -252,6 +262,15 @@ func updateSetting(port int, username string, password string, webBasePath strin
|
|||||||
fmt.Println("Base URI path set successfully")
|
fmt.Println("Base URI path set successfully")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if listenIP != "" {
|
||||||
|
err := settingService.SetListen(listenIP)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Failed to set listen IP:", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("listen %v set successfully", listenIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateCert(publicKey string, privateKey string) {
|
func updateCert(publicKey string, privateKey string) {
|
||||||
@@ -281,6 +300,37 @@ func updateCert(publicKey string, privateKey string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCertificate(getCert bool) {
|
||||||
|
if getCert {
|
||||||
|
settingService := service.SettingService{}
|
||||||
|
certFile, err := settingService.GetCertFile()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("get cert file failed, error info:", err)
|
||||||
|
}
|
||||||
|
keyFile, err := settingService.GetKeyFile()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("get key file failed, error info:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("cert:", certFile)
|
||||||
|
fmt.Println("key:", keyFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetListenIP(getListen bool) {
|
||||||
|
if getListen {
|
||||||
|
|
||||||
|
settingService := service.SettingService{}
|
||||||
|
ListenIP, err := settingService.GetListen()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to retrieve listen IP: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("listenIP:", ListenIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func migrateDb() {
|
func migrateDb() {
|
||||||
inboundService := service.InboundService{}
|
inboundService := service.InboundService{}
|
||||||
|
|
||||||
@@ -339,6 +389,8 @@ func main() {
|
|||||||
var username string
|
var username string
|
||||||
var password string
|
var password string
|
||||||
var webBasePath string
|
var webBasePath string
|
||||||
|
var listenIP string
|
||||||
|
var getListen bool
|
||||||
var webCertFile string
|
var webCertFile string
|
||||||
var webKeyFile string
|
var webKeyFile string
|
||||||
var tgbottoken string
|
var tgbottoken string
|
||||||
@@ -347,6 +399,7 @@ func main() {
|
|||||||
var tgbotRuntime string
|
var tgbotRuntime string
|
||||||
var reset bool
|
var reset bool
|
||||||
var show bool
|
var show bool
|
||||||
|
var getCert bool
|
||||||
var remove_secret bool
|
var remove_secret bool
|
||||||
settingCmd.BoolVar(&reset, "reset", false, "Reset all settings")
|
settingCmd.BoolVar(&reset, "reset", false, "Reset all settings")
|
||||||
settingCmd.BoolVar(&show, "show", false, "Display current settings")
|
settingCmd.BoolVar(&show, "show", false, "Display current settings")
|
||||||
@@ -355,6 +408,9 @@ func main() {
|
|||||||
settingCmd.StringVar(&username, "username", "", "Set login username")
|
settingCmd.StringVar(&username, "username", "", "Set login username")
|
||||||
settingCmd.StringVar(&password, "password", "", "Set login password")
|
settingCmd.StringVar(&password, "password", "", "Set login password")
|
||||||
settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel")
|
settingCmd.StringVar(&webBasePath, "webBasePath", "", "Set base path for Panel")
|
||||||
|
settingCmd.StringVar(&listenIP, "listenIP", "", "set panel listenIP IP")
|
||||||
|
settingCmd.BoolVar(&getListen, "getListen", false, "Display current panel listenIP IP")
|
||||||
|
settingCmd.BoolVar(&getCert, "getCert", false, "Display current certificate settings")
|
||||||
settingCmd.StringVar(&webCertFile, "webCert", "", "Set path to public key file for panel")
|
settingCmd.StringVar(&webCertFile, "webCert", "", "Set path to public key file for panel")
|
||||||
settingCmd.StringVar(&webKeyFile, "webCertKey", "", "Set path to private key file for panel")
|
settingCmd.StringVar(&webKeyFile, "webCertKey", "", "Set path to private key file for panel")
|
||||||
settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "Set token for Telegram bot")
|
settingCmd.StringVar(&tgbottoken, "tgbottoken", "", "Set token for Telegram bot")
|
||||||
@@ -397,11 +453,17 @@ func main() {
|
|||||||
if reset {
|
if reset {
|
||||||
resetSetting()
|
resetSetting()
|
||||||
} else {
|
} else {
|
||||||
updateSetting(port, username, password, webBasePath)
|
updateSetting(port, username, password, webBasePath, listenIP)
|
||||||
}
|
}
|
||||||
if show {
|
if show {
|
||||||
showSetting(show)
|
showSetting(show)
|
||||||
}
|
}
|
||||||
|
if getListen {
|
||||||
|
GetListenIP(getListen)
|
||||||
|
}
|
||||||
|
if getCert {
|
||||||
|
GetCertificate(getCert)
|
||||||
|
}
|
||||||
if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") {
|
if (tgbottoken != "") || (tgbotchatid != "") || (tgbotRuntime != "") {
|
||||||
updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
|
updateTgbotSetting(tgbottoken, tgbotchatid, tgbotRuntime)
|
||||||
}
|
}
|
||||||
@@ -422,7 +484,6 @@ func main() {
|
|||||||
} else {
|
} else {
|
||||||
updateCert(webCertFile, webKeyFile)
|
updateCert(webCertFile, webKeyFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fmt.Println("Invalid subcommands")
|
fmt.Println("Invalid subcommands")
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|||||||
@@ -47,7 +47,9 @@
|
|||||||
"tag": "direct",
|
"tag": "direct",
|
||||||
"protocol": "freedom",
|
"protocol": "freedom",
|
||||||
"settings": {
|
"settings": {
|
||||||
"domainStrategy": "UseIP"
|
"domainStrategy": "AsIs",
|
||||||
|
"redirect": "",
|
||||||
|
"noises": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
|||||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
headers, _ := splithttp["headers"].(map[string]interface{})
|
||||||
obj["host"] = searchHost(headers)
|
obj["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
|
obj["mode"] = splithttp["mode"].(string)
|
||||||
}
|
}
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
obj["tls"] = security
|
obj["tls"] = security
|
||||||
@@ -389,6 +390,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
|||||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
headers, _ := splithttp["headers"].(map[string]interface{})
|
||||||
params["host"] = searchHost(headers)
|
params["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
|
params["mode"] = splithttp["mode"].(string)
|
||||||
}
|
}
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
if security == "tls" {
|
if security == "tls" {
|
||||||
@@ -452,38 +454,7 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if security == "xtls" {
|
if security != "tls" && security != "reality" {
|
||||||
params["security"] = "xtls"
|
|
||||||
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
|
|
||||||
alpns, _ := xtlsSetting["alpn"].([]interface{})
|
|
||||||
var alpn []string
|
|
||||||
for _, a := range alpns {
|
|
||||||
alpn = append(alpn, a.(string))
|
|
||||||
}
|
|
||||||
if len(alpn) > 0 {
|
|
||||||
params["alpn"] = strings.Join(alpn, ",")
|
|
||||||
}
|
|
||||||
if sniValue, ok := searchKey(xtlsSetting, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
|
||||||
if xtlsSetting != nil {
|
|
||||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
|
||||||
params["fp"], _ = fpValue.(string)
|
|
||||||
}
|
|
||||||
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
|
|
||||||
if insecure.(bool) {
|
|
||||||
params["allowInsecure"] = "1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
|
||||||
params["flow"] = clients[clientIndex].Flow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if security != "tls" && security != "reality" && security != "xtls" {
|
|
||||||
params["security"] = "none"
|
params["security"] = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,6 +588,7 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
|||||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
headers, _ := splithttp["headers"].(map[string]interface{})
|
||||||
params["host"] = searchHost(headers)
|
params["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
|
params["mode"] = splithttp["mode"].(string)
|
||||||
}
|
}
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
if security == "tls" {
|
if security == "tls" {
|
||||||
@@ -676,39 +648,7 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if security == "xtls" {
|
if security != "tls" && security != "reality" {
|
||||||
params["security"] = "xtls"
|
|
||||||
xtlsSetting, _ := stream["xtlsSettings"].(map[string]interface{})
|
|
||||||
alpns, _ := xtlsSetting["alpn"].([]interface{})
|
|
||||||
var alpn []string
|
|
||||||
for _, a := range alpns {
|
|
||||||
alpn = append(alpn, a.(string))
|
|
||||||
}
|
|
||||||
if len(alpn) > 0 {
|
|
||||||
params["alpn"] = strings.Join(alpn, ",")
|
|
||||||
}
|
|
||||||
if sniValue, ok := searchKey(xtlsSetting, "serverName"); ok {
|
|
||||||
params["sni"], _ = sniValue.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
xtlsSettings, _ := searchKey(xtlsSetting, "settings")
|
|
||||||
if xtlsSetting != nil {
|
|
||||||
if fpValue, ok := searchKey(xtlsSettings, "fingerprint"); ok {
|
|
||||||
params["fp"], _ = fpValue.(string)
|
|
||||||
}
|
|
||||||
if insecure, ok := searchKey(xtlsSettings, "allowInsecure"); ok {
|
|
||||||
if insecure.(bool) {
|
|
||||||
params["allowInsecure"] = "1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if streamNetwork == "tcp" && len(clients[clientIndex].Flow) > 0 {
|
|
||||||
params["flow"] = clients[clientIndex].Flow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if security != "tls" && security != "reality" && security != "xtls" {
|
|
||||||
params["security"] = "none"
|
params["security"] = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -846,6 +786,7 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
|||||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
headers, _ := splithttp["headers"].(map[string]interface{})
|
||||||
params["host"] = searchHost(headers)
|
params["host"] = searchHost(headers)
|
||||||
}
|
}
|
||||||
|
params["mode"] = splithttp["mode"].(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
security, _ := stream["security"].(string)
|
security, _ := stream["security"].(string)
|
||||||
|
|||||||
@@ -4,18 +4,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FormatTraffic(trafficBytes int64) (size string) {
|
func FormatTraffic(trafficBytes int64) string {
|
||||||
if trafficBytes < 1024 {
|
units := []string{"B", "KB", "MB", "GB", "TB", "PB"}
|
||||||
return fmt.Sprintf("%.2fB", float64(trafficBytes)/float64(1))
|
unitIndex := 0
|
||||||
} else if trafficBytes < (1024 * 1024) {
|
size := float64(trafficBytes)
|
||||||
return fmt.Sprintf("%.2fKB", float64(trafficBytes)/float64(1024))
|
|
||||||
} else if trafficBytes < (1024 * 1024 * 1024) {
|
for size >= 1024 && unitIndex < len(units)-1 {
|
||||||
return fmt.Sprintf("%.2fMB", float64(trafficBytes)/float64(1024*1024))
|
size /= 1024
|
||||||
} else if trafficBytes < (1024 * 1024 * 1024 * 1024) {
|
unitIndex++
|
||||||
return fmt.Sprintf("%.2fGB", float64(trafficBytes)/float64(1024*1024*1024))
|
|
||||||
} else if trafficBytes < (1024 * 1024 * 1024 * 1024 * 1024) {
|
|
||||||
return fmt.Sprintf("%.2fTB", float64(trafficBytes)/float64(1024*1024*1024*1024))
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("%.2fEB", float64(trafficBytes)/float64(1024*1024*1024*1024*1024))
|
|
||||||
}
|
}
|
||||||
|
return fmt.Sprintf("%.2f%s", size, units[unitIndex])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@
|
|||||||
return scriptHint(editor, javascriptKeywords,
|
return scriptHint(editor, javascriptKeywords,
|
||||||
function (e, cur) {return e.getTokenAt(cur);},
|
function (e, cur) {return e.getTokenAt(cur);},
|
||||||
options);
|
options);
|
||||||
};
|
}
|
||||||
CodeMirror.registerHelper("hint", "javascript", javascriptHint);
|
CodeMirror.registerHelper("hint", "javascript", javascriptHint);
|
||||||
|
|
||||||
function getCoffeeScriptToken(editor, cur) {
|
function getCoffeeScriptToken(editor, cur) {
|
||||||
|
|||||||
@@ -362,7 +362,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
|||||||
if (type == wanted) return cont();
|
if (type == wanted) return cont();
|
||||||
else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
|
else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
|
||||||
else return cont(exp);
|
else return cont(exp);
|
||||||
};
|
}
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
.CodeMirror-lint-tooltip {
|
.CodeMirror-lint-tooltip {
|
||||||
background-color: #ffd;
|
background-color: #ffd;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
border-radius: 4px 4px 4px 4px;
|
border-radius: 4px;
|
||||||
color: black;
|
color: black;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const supportLangs = [
|
|||||||
icon: "🇮🇷",
|
icon: "🇮🇷",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "簡體中文",
|
name: "简体中文",
|
||||||
value: "zh-CN",
|
value: "zh-CN",
|
||||||
icon: "🇨🇳",
|
icon: "🇨🇳",
|
||||||
},
|
},
|
||||||
@@ -19,6 +19,11 @@ const supportLangs = [
|
|||||||
value: "zh-TW",
|
value: "zh-TW",
|
||||||
icon: "🇹🇼",
|
icon: "🇹🇼",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "日本語",
|
||||||
|
value: "ja-JP",
|
||||||
|
icon: "🇯🇵",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Русский",
|
name: "Русский",
|
||||||
value: "ru-RU",
|
value: "ru-RU",
|
||||||
|
|||||||
@@ -141,8 +141,8 @@ class DBInbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
genInboundLinks(remarkModel) {
|
genInboundLinks(remarkModel) {
|
||||||
const inbound = this.toInbound();
|
const inbound = this.toInbound();
|
||||||
return inbound.genInboundLinks(this.remark,remarkModel);
|
return inbound.genInboundLinks(this.remark, remarkModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,11 +21,6 @@ const SSMethods = {
|
|||||||
BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
|
BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
|
||||||
};
|
};
|
||||||
|
|
||||||
const XTLS_FLOW_CONTROL = {
|
|
||||||
ORIGIN: "xtls-rprx-origin",
|
|
||||||
DIRECT: "xtls-rprx-direct",
|
|
||||||
};
|
|
||||||
|
|
||||||
const TLS_FLOW_CONTROL = {
|
const TLS_FLOW_CONTROL = {
|
||||||
VISION: "xtls-rprx-vision",
|
VISION: "xtls-rprx-vision",
|
||||||
VISION_UDP443: "xtls-rprx-vision-udp443",
|
VISION_UDP443: "xtls-rprx-vision-udp443",
|
||||||
@@ -118,9 +113,14 @@ const USERS_SECURITY = {
|
|||||||
ZERO: "zero",
|
ZERO: "zero",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MODE_OPTION = {
|
||||||
|
AUTO: "auto",
|
||||||
|
PACKET_UP: "packet-up",
|
||||||
|
STREAM_UP: "stream-up",
|
||||||
|
};
|
||||||
|
|
||||||
Object.freeze(Protocols);
|
Object.freeze(Protocols);
|
||||||
Object.freeze(SSMethods);
|
Object.freeze(SSMethods);
|
||||||
Object.freeze(XTLS_FLOW_CONTROL);
|
|
||||||
Object.freeze(TLS_FLOW_CONTROL);
|
Object.freeze(TLS_FLOW_CONTROL);
|
||||||
Object.freeze(TLS_VERSION_OPTION);
|
Object.freeze(TLS_VERSION_OPTION);
|
||||||
Object.freeze(TLS_CIPHER_OPTION);
|
Object.freeze(TLS_CIPHER_OPTION);
|
||||||
@@ -131,6 +131,7 @@ Object.freeze(USAGE_OPTION);
|
|||||||
Object.freeze(DOMAIN_STRATEGY_OPTION);
|
Object.freeze(DOMAIN_STRATEGY_OPTION);
|
||||||
Object.freeze(TCP_CONGESTION_OPTION);
|
Object.freeze(TCP_CONGESTION_OPTION);
|
||||||
Object.freeze(USERS_SECURITY);
|
Object.freeze(USERS_SECURITY);
|
||||||
|
Object.freeze(MODE_OPTION);
|
||||||
|
|
||||||
class XrayCommonClass {
|
class XrayCommonClass {
|
||||||
|
|
||||||
@@ -530,11 +531,13 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
|||||||
noSSEHeader = false,
|
noSSEHeader = false,
|
||||||
xPaddingBytes = "100-1000",
|
xPaddingBytes = "100-1000",
|
||||||
xmux = {
|
xmux = {
|
||||||
maxConcurrency: 0,
|
maxConcurrency: "16-32",
|
||||||
maxConnections: 0,
|
maxConnections: 0,
|
||||||
cMaxReuseTimes: 0,
|
cMaxReuseTimes: "64-128",
|
||||||
cMaxLifetimeMs: 0
|
cMaxLifetimeMs: 0
|
||||||
}
|
},
|
||||||
|
mode = MODE_OPTION.AUTO,
|
||||||
|
noGRPCHeader = false,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.path = path;
|
this.path = path;
|
||||||
@@ -546,6 +549,8 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
|||||||
this.noSSEHeader = noSSEHeader;
|
this.noSSEHeader = noSSEHeader;
|
||||||
this.xPaddingBytes = xPaddingBytes;
|
this.xPaddingBytes = xPaddingBytes;
|
||||||
this.xmux = xmux;
|
this.xmux = xmux;
|
||||||
|
this.mode = mode;
|
||||||
|
this.noGRPCHeader = noGRPCHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
addHeader(name, value) {
|
addHeader(name, value) {
|
||||||
@@ -567,6 +572,8 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
|||||||
json.noSSEHeader,
|
json.noSSEHeader,
|
||||||
json.xPaddingBytes,
|
json.xPaddingBytes,
|
||||||
json.xmux,
|
json.xmux,
|
||||||
|
json.mode,
|
||||||
|
json.noGRPCHeader
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,7 +592,9 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
|||||||
maxConnections: this.xmux.maxConnections,
|
maxConnections: this.xmux.maxConnections,
|
||||||
cMaxReuseTimes: this.xmux.cMaxReuseTimes,
|
cMaxReuseTimes: this.xmux.cMaxReuseTimes,
|
||||||
cMaxLifetimeMs: this.xmux.cMaxLifetimeMs
|
cMaxLifetimeMs: this.xmux.cMaxLifetimeMs
|
||||||
}
|
},
|
||||||
|
mode: this.mode,
|
||||||
|
noGRPCHeader: this.noGRPCHeader
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -755,137 +764,6 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class XtlsStreamSettings extends XrayCommonClass {
|
|
||||||
constructor(
|
|
||||||
serverName = '',
|
|
||||||
certificates = [new XtlsStreamSettings.Cert()],
|
|
||||||
alpn = [ALPN_OPTION.H3, ALPN_OPTION.H2, ALPN_OPTION.HTTP1],
|
|
||||||
settings = new XtlsStreamSettings.Settings()
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
this.sni = serverName;
|
|
||||||
this.certs = certificates;
|
|
||||||
this.alpn = alpn;
|
|
||||||
this.settings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
addCert() {
|
|
||||||
this.certs.push(new XtlsStreamSettings.Cert());
|
|
||||||
}
|
|
||||||
|
|
||||||
removeCert(index) {
|
|
||||||
this.certs.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
|
||||||
let certs;
|
|
||||||
let settings;
|
|
||||||
if (!ObjectUtil.isEmpty(json.certificates)) {
|
|
||||||
certs = json.certificates.map(cert => XtlsStreamSettings.Cert.fromJson(cert));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ObjectUtil.isEmpty(json.settings)) {
|
|
||||||
settings = new XtlsStreamSettings.Settings(json.settings.allowInsecure, json.settings.serverName);
|
|
||||||
}
|
|
||||||
return new XtlsStreamSettings(
|
|
||||||
json.serverName,
|
|
||||||
certs,
|
|
||||||
json.alpn,
|
|
||||||
settings,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson() {
|
|
||||||
return {
|
|
||||||
serverName: this.sni,
|
|
||||||
certificates: XtlsStreamSettings.toJsonArray(this.certs),
|
|
||||||
alpn: this.alpn,
|
|
||||||
settings: this.settings,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XtlsStreamSettings.Cert = class extends XrayCommonClass {
|
|
||||||
constructor(
|
|
||||||
useFile = true,
|
|
||||||
certificateFile = '',
|
|
||||||
keyFile = '',
|
|
||||||
certificate = '',
|
|
||||||
key = '',
|
|
||||||
ocspStapling = 3600,
|
|
||||||
oneTimeLoading = false,
|
|
||||||
usage = USAGE_OPTION.ENCIPHERMENT
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
this.useFile = useFile;
|
|
||||||
this.certFile = certificateFile;
|
|
||||||
this.keyFile = keyFile;
|
|
||||||
this.cert = Array.isArray(certificate) ? certificate.join('\n') : certificate;
|
|
||||||
this.key = Array.isArray(key) ? key.join('\n') : key;
|
|
||||||
this.ocspStapling = ocspStapling;
|
|
||||||
this.oneTimeLoading = oneTimeLoading;
|
|
||||||
this.usage = usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
|
||||||
if ('certificateFile' in json && 'keyFile' in json) {
|
|
||||||
return new XtlsStreamSettings.Cert(
|
|
||||||
true,
|
|
||||||
json.certificateFile,
|
|
||||||
json.keyFile, '', '',
|
|
||||||
json.ocspStapling,
|
|
||||||
json.oneTimeLoading,
|
|
||||||
json.usage,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return new XtlsStreamSettings.Cert(
|
|
||||||
false, '', '',
|
|
||||||
json.certificate.join('\n'),
|
|
||||||
json.key.join('\n'),
|
|
||||||
json.ocspStapling,
|
|
||||||
json.oneTimeLoading,
|
|
||||||
json.usage,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJson() {
|
|
||||||
if (this.useFile) {
|
|
||||||
return {
|
|
||||||
certificateFile: this.certFile,
|
|
||||||
keyFile: this.keyFile,
|
|
||||||
ocspStapling: this.ocspStapling,
|
|
||||||
oneTimeLoading: this.oneTimeLoading,
|
|
||||||
usage: this.usage,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
certificate: this.cert.split('\n'),
|
|
||||||
key: this.key.split('\n'),
|
|
||||||
ocspStapling: this.ocspStapling,
|
|
||||||
oneTimeLoading: this.oneTimeLoading,
|
|
||||||
usage: this.usage,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
XtlsStreamSettings.Settings = class extends XrayCommonClass {
|
|
||||||
constructor(allowInsecure = false) {
|
|
||||||
super();
|
|
||||||
this.allowInsecure = allowInsecure;
|
|
||||||
}
|
|
||||||
static fromJson(json = {}) {
|
|
||||||
return new XtlsStreamSettings.Settings(
|
|
||||||
json.allowInsecure,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
toJson() {
|
|
||||||
return {
|
|
||||||
allowInsecure: this.allowInsecure,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class RealityStreamSettings extends XrayCommonClass {
|
class RealityStreamSettings extends XrayCommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -920,7 +798,9 @@ class RealityStreamSettings extends XrayCommonClass {
|
|||||||
json.settings.publicKey,
|
json.settings.publicKey,
|
||||||
json.settings.fingerprint,
|
json.settings.fingerprint,
|
||||||
json.settings.serverName,
|
json.settings.serverName,
|
||||||
json.settings.spiderX);}
|
json.settings.spiderX
|
||||||
|
);
|
||||||
|
}
|
||||||
return new RealityStreamSettings(
|
return new RealityStreamSettings(
|
||||||
json.show,
|
json.show,
|
||||||
json.xver,
|
json.xver,
|
||||||
@@ -1069,7 +949,6 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
security = 'none',
|
security = 'none',
|
||||||
externalProxy = [],
|
externalProxy = [],
|
||||||
tlsSettings = new TlsStreamSettings(),
|
tlsSettings = new TlsStreamSettings(),
|
||||||
xtlsSettings = new XtlsStreamSettings(),
|
|
||||||
realitySettings = new RealityStreamSettings(),
|
realitySettings = new RealityStreamSettings(),
|
||||||
tcpSettings = new TcpStreamSettings(),
|
tcpSettings = new TcpStreamSettings(),
|
||||||
kcpSettings = new KcpStreamSettings(),
|
kcpSettings = new KcpStreamSettings(),
|
||||||
@@ -1085,7 +964,6 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
this.security = security;
|
this.security = security;
|
||||||
this.externalProxy = externalProxy;
|
this.externalProxy = externalProxy;
|
||||||
this.tls = tlsSettings;
|
this.tls = tlsSettings;
|
||||||
this.xtls = xtlsSettings;
|
|
||||||
this.reality = realitySettings;
|
this.reality = realitySettings;
|
||||||
this.tcp = tcpSettings;
|
this.tcp = tcpSettings;
|
||||||
this.kcp = kcpSettings;
|
this.kcp = kcpSettings;
|
||||||
@@ -1109,18 +987,6 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isXtls() {
|
|
||||||
return this.security === "xtls";
|
|
||||||
}
|
|
||||||
|
|
||||||
set isXtls(isXtls) {
|
|
||||||
if (isXtls) {
|
|
||||||
this.security = 'xtls';
|
|
||||||
} else {
|
|
||||||
this.security = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//for Reality
|
//for Reality
|
||||||
get isReality() {
|
get isReality() {
|
||||||
return this.security === "reality";
|
return this.security === "reality";
|
||||||
@@ -1148,7 +1014,6 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
json.security,
|
json.security,
|
||||||
json.externalProxy,
|
json.externalProxy,
|
||||||
TlsStreamSettings.fromJson(json.tlsSettings),
|
TlsStreamSettings.fromJson(json.tlsSettings),
|
||||||
XtlsStreamSettings.fromJson(json.xtlsSettings),
|
|
||||||
RealityStreamSettings.fromJson(json.realitySettings),
|
RealityStreamSettings.fromJson(json.realitySettings),
|
||||||
TcpStreamSettings.fromJson(json.tcpSettings),
|
TcpStreamSettings.fromJson(json.tcpSettings),
|
||||||
KcpStreamSettings.fromJson(json.kcpSettings),
|
KcpStreamSettings.fromJson(json.kcpSettings),
|
||||||
@@ -1168,7 +1033,6 @@ class StreamSettings extends XrayCommonClass {
|
|||||||
security: this.security,
|
security: this.security,
|
||||||
externalProxy: this.externalProxy,
|
externalProxy: this.externalProxy,
|
||||||
tlsSettings: this.isTls ? this.tls.toJson() : undefined,
|
tlsSettings: this.isTls ? this.tls.toJson() : undefined,
|
||||||
xtlsSettings: this.isXtls ? this.xtls.toJson() : undefined,
|
|
||||||
realitySettings: this.isReality ? this.reality.toJson() : undefined,
|
realitySettings: this.isReality ? this.reality.toJson() : undefined,
|
||||||
tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
|
tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
|
||||||
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
|
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
|
||||||
@@ -1281,18 +1145,6 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get xtls() {
|
|
||||||
return this.stream.security === 'xtls';
|
|
||||||
}
|
|
||||||
|
|
||||||
set xtls(isXtls) {
|
|
||||||
if (isXtls) {
|
|
||||||
this.stream.security = 'xtls';
|
|
||||||
} else {
|
|
||||||
this.stream.security = 'none';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get network() {
|
get network() {
|
||||||
return this.stream.network;
|
return this.stream.network;
|
||||||
}
|
}
|
||||||
@@ -1347,7 +1199,6 @@ class Inbound extends XrayCommonClass {
|
|||||||
|
|
||||||
get serverName() {
|
get serverName() {
|
||||||
if (this.stream.isTls) return this.stream.tls.sni;
|
if (this.stream.isTls) return this.stream.tls.sni;
|
||||||
if (this.stream.isXtls) return this.stream.xtls.sni;
|
|
||||||
if (this.stream.isReality) return this.stream.reality.serverNames;
|
if (this.stream.isReality) return this.stream.reality.serverNames;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -1423,12 +1274,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
|
|
||||||
canEnableReality() {
|
canEnableReality() {
|
||||||
if (![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
if (![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
||||||
return ["tcp", "http", "grpc"].includes(this.network);
|
return ["tcp", "http", "grpc", "splithttp"].includes(this.network);
|
||||||
}
|
|
||||||
|
|
||||||
canEnableXtls() {
|
|
||||||
if (![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
|
||||||
return this.network === "tcp";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canEnableStream() {
|
canEnableStream() {
|
||||||
@@ -1498,6 +1344,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
const splithttp = this.stream.splithttp;
|
const splithttp = this.stream.splithttp;
|
||||||
obj.path = splithttp.path;
|
obj.path = splithttp.path;
|
||||||
obj.host = splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host');
|
obj.host = splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host');
|
||||||
|
obj.mode = splithttp.mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (security === 'tls') {
|
if (security === 'tls') {
|
||||||
@@ -1570,6 +1417,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
const splithttp = this.stream.splithttp;
|
const splithttp = this.stream.splithttp;
|
||||||
params.set("path", splithttp.path);
|
params.set("path", splithttp.path);
|
||||||
params.set("host", splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
params.set("host", splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
||||||
|
params.set("mode", splithttp.mode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1590,18 +1438,6 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (security === 'xtls') {
|
|
||||||
params.set("security", "xtls");
|
|
||||||
params.set("alpn", this.stream.xtls.alpn);
|
|
||||||
if (this.stream.xtls.settings.allowInsecure) {
|
|
||||||
params.set("allowInsecure", "1");
|
|
||||||
}
|
|
||||||
if (!ObjectUtil.isEmpty(this.stream.xtls.sni)) {
|
|
||||||
params.set("sni", this.stream.xtls.sni);
|
|
||||||
}
|
|
||||||
params.set("flow", flow);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (security === 'reality') {
|
else if (security === 'reality') {
|
||||||
params.set("security", "reality");
|
params.set("security", "reality");
|
||||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||||
@@ -1685,6 +1521,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
const splithttp = this.stream.splithttp;
|
const splithttp = this.stream.splithttp;
|
||||||
params.set("path", splithttp.path);
|
params.set("path", splithttp.path);
|
||||||
params.set("host", splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
params.set("host", splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
||||||
|
params.set("mode", splithttp.mode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1767,6 +1604,7 @@ class Inbound extends XrayCommonClass {
|
|||||||
const splithttp = this.stream.splithttp;
|
const splithttp = this.stream.splithttp;
|
||||||
params.set("path", splithttp.path);
|
params.set("path", splithttp.path);
|
||||||
params.set("host", splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
params.set("host", splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
||||||
|
params.set("mode", splithttp.mode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1799,18 +1637,6 @@ class Inbound extends XrayCommonClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (security === 'xtls') {
|
|
||||||
params.set("security", "xtls");
|
|
||||||
params.set("alpn", this.stream.xtls.alpn);
|
|
||||||
if (this.stream.xtls.settings.allowInsecure) {
|
|
||||||
params.set("allowInsecure", "1");
|
|
||||||
}
|
|
||||||
if (!ObjectUtil.isEmpty(this.stream.xtls.sni)) {
|
|
||||||
params.set("sni", this.stream.xtls.sni);
|
|
||||||
}
|
|
||||||
params.set("flow", flow);
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
else {
|
||||||
params.set("security", "none");
|
params.set("security", "none");
|
||||||
}
|
}
|
||||||
@@ -2271,7 +2097,6 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
|
|||||||
Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
password = RandomUtil.randomSeq(10),
|
password = RandomUtil.randomSeq(10),
|
||||||
flow = '',
|
|
||||||
email = RandomUtil.randomLowerAndNum(8),
|
email = RandomUtil.randomLowerAndNum(8),
|
||||||
limitIp = 0,
|
limitIp = 0,
|
||||||
totalGB = 0,
|
totalGB = 0,
|
||||||
@@ -2283,7 +2108,6 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
|||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.flow = flow;
|
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.limitIp = limitIp;
|
this.limitIp = limitIp;
|
||||||
this.totalGB = totalGB;
|
this.totalGB = totalGB;
|
||||||
@@ -2297,7 +2121,6 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
|||||||
toJson() {
|
toJson() {
|
||||||
return {
|
return {
|
||||||
password: this.password,
|
password: this.password,
|
||||||
flow: this.flow,
|
|
||||||
email: this.email,
|
email: this.email,
|
||||||
limitIp: this.limitIp,
|
limitIp: this.limitIp,
|
||||||
totalGB: this.totalGB,
|
totalGB: this.totalGB,
|
||||||
@@ -2312,7 +2135,6 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
|||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new Inbound.TrojanSettings.Trojan(
|
return new Inbound.TrojanSettings.Trojan(
|
||||||
json.password,
|
json.password,
|
||||||
json.flow,
|
|
||||||
json.email,
|
json.email,
|
||||||
json.limitIp,
|
json.limitIp,
|
||||||
json.totalGB,
|
json.totalGB,
|
||||||
@@ -2395,13 +2217,15 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
|
|||||||
method = SSMethods.BLAKE3_AES_256_GCM,
|
method = SSMethods.BLAKE3_AES_256_GCM,
|
||||||
password = RandomUtil.randomShadowsocksPassword(),
|
password = RandomUtil.randomShadowsocksPassword(),
|
||||||
network = 'tcp,udp',
|
network = 'tcp,udp',
|
||||||
shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()]
|
shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()],
|
||||||
|
ivCheck = false,
|
||||||
) {
|
) {
|
||||||
super(protocol);
|
super(protocol);
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.network = network;
|
this.network = network;
|
||||||
this.shadowsockses = shadowsockses;
|
this.shadowsockses = shadowsockses;
|
||||||
|
this.ivCheck = ivCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
@@ -2411,6 +2235,7 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
|
|||||||
json.password,
|
json.password,
|
||||||
json.network,
|
json.network,
|
||||||
json.clients.map(client => Inbound.ShadowsocksSettings.Shadowsocks.fromJson(client)),
|
json.clients.map(client => Inbound.ShadowsocksSettings.Shadowsocks.fromJson(client)),
|
||||||
|
json.ivCheck,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2419,7 +2244,8 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
|
|||||||
method: this.method,
|
method: this.method,
|
||||||
password: this.password,
|
password: this.password,
|
||||||
network: this.network,
|
network: this.network,
|
||||||
clients: Inbound.ShadowsocksSettings.toJsonArray(this.shadowsockses)
|
clients: Inbound.ShadowsocksSettings.toJsonArray(this.shadowsockses),
|
||||||
|
ivCheck: this.ivCheck,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2644,13 +2470,19 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Inbound.WireguardSettings = class extends XrayCommonClass {
|
Inbound.WireguardSettings = class extends XrayCommonClass {
|
||||||
constructor(protocol, mtu = 1420, secretKey = Wireguard.generateKeypair().privateKey, peers = [new Inbound.WireguardSettings.Peer()], kernelMode = false) {
|
constructor(
|
||||||
|
protocol,
|
||||||
|
mtu = 1420,
|
||||||
|
secretKey = Wireguard.generateKeypair().privateKey,
|
||||||
|
peers = [new Inbound.WireguardSettings.Peer()],
|
||||||
|
noKernelTun = false
|
||||||
|
) {
|
||||||
super(protocol);
|
super(protocol);
|
||||||
this.mtu = mtu;
|
this.mtu = mtu;
|
||||||
this.secretKey = secretKey;
|
this.secretKey = secretKey;
|
||||||
this.pubKey = secretKey.length > 0 ? Wireguard.generateKeypair(secretKey).publicKey : '';
|
this.pubKey = secretKey.length > 0 ? Wireguard.generateKeypair(secretKey).publicKey : '';
|
||||||
this.peers = peers;
|
this.peers = peers;
|
||||||
this.kernelMode = kernelMode;
|
this.noKernelTun = noKernelTun;
|
||||||
}
|
}
|
||||||
|
|
||||||
addPeer() {
|
addPeer() {
|
||||||
@@ -2667,7 +2499,7 @@ Inbound.WireguardSettings = class extends XrayCommonClass {
|
|||||||
json.mtu,
|
json.mtu,
|
||||||
json.secretKey,
|
json.secretKey,
|
||||||
json.peers.map(peer => Inbound.WireguardSettings.Peer.fromJson(peer)),
|
json.peers.map(peer => Inbound.WireguardSettings.Peer.fromJson(peer)),
|
||||||
json.kernelMode,
|
json.noKernelTun,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2676,7 +2508,7 @@ Inbound.WireguardSettings = class extends XrayCommonClass {
|
|||||||
mtu: this.mtu ?? undefined,
|
mtu: this.mtu ?? undefined,
|
||||||
secretKey: this.secretKey,
|
secretKey: this.secretKey,
|
||||||
peers: Inbound.WireguardSettings.Peer.toJsonArray(this.peers),
|
peers: Inbound.WireguardSettings.Peer.toJsonArray(this.peers),
|
||||||
kernelMode: this.kernelMode,
|
noKernelTun: this.noKernelTun,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -77,6 +77,12 @@ const USERS_SECURITY = {
|
|||||||
ZERO: "zero",
|
ZERO: "zero",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MODE_OPTION = {
|
||||||
|
AUTO: "auto",
|
||||||
|
PACKET_UP: "packet-up",
|
||||||
|
STREAM_UP: "stream-up",
|
||||||
|
};
|
||||||
|
|
||||||
Object.freeze(Protocols);
|
Object.freeze(Protocols);
|
||||||
Object.freeze(SSMethods);
|
Object.freeze(SSMethods);
|
||||||
Object.freeze(TLS_FLOW_CONTROL);
|
Object.freeze(TLS_FLOW_CONTROL);
|
||||||
@@ -85,6 +91,7 @@ Object.freeze(ALPN_OPTION);
|
|||||||
Object.freeze(OutboundDomainStrategies);
|
Object.freeze(OutboundDomainStrategies);
|
||||||
Object.freeze(WireguardDomainStrategy);
|
Object.freeze(WireguardDomainStrategy);
|
||||||
Object.freeze(USERS_SECURITY);
|
Object.freeze(USERS_SECURITY);
|
||||||
|
Object.freeze(MODE_OPTION);
|
||||||
|
|
||||||
|
|
||||||
class CommonClass {
|
class CommonClass {
|
||||||
@@ -320,16 +327,22 @@ class HttpUpgradeStreamSettings extends CommonClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SplitHTTPStreamSettings extends CommonClass {
|
class SplitHTTPStreamSettings extends CommonClass {
|
||||||
constructor(path = '/', host = '') {
|
constructor(
|
||||||
|
path = '/',
|
||||||
|
host = '',
|
||||||
|
mode = '',
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.host = host;
|
this.host = host;
|
||||||
|
this.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new SplitHTTPStreamSettings(
|
return new SplitHTTPStreamSettings(
|
||||||
json.path,
|
json.path,
|
||||||
json.host,
|
json.host,
|
||||||
|
json.mode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +350,7 @@ class SplitHTTPStreamSettings extends CommonClass {
|
|||||||
return {
|
return {
|
||||||
path: this.path,
|
path: this.path,
|
||||||
host: this.host,
|
host: this.host,
|
||||||
|
mode: this.mode,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -602,7 +616,7 @@ class Outbound extends CommonClass {
|
|||||||
|
|
||||||
canEnableReality() {
|
canEnableReality() {
|
||||||
if (![Protocols.VLESS, Protocols.Trojan].includes(this.protocol)) return false;
|
if (![Protocols.VLESS, Protocols.Trojan].includes(this.protocol)) return false;
|
||||||
return ["tcp", "http", "grpc"].includes(this.stream.network);
|
return ["tcp", "http", "grpc", "splithttp"].includes(this.stream.network);
|
||||||
}
|
}
|
||||||
|
|
||||||
canEnableStream() {
|
canEnableStream() {
|
||||||
@@ -710,7 +724,7 @@ class Outbound extends CommonClass {
|
|||||||
} else if (network === 'httpupgrade') {
|
} else if (network === 'httpupgrade') {
|
||||||
stream.httpupgrade = new HttpUpgradeStreamSettings(json.path, json.host);
|
stream.httpupgrade = new HttpUpgradeStreamSettings(json.path, json.host);
|
||||||
} else if (network === 'splithttp') {
|
} else if (network === 'splithttp') {
|
||||||
stream.splithttp = new SplitHTTPStreamSettings(json.path, json.host);
|
stream.splithttp = new SplitHTTPStreamSettings(json.path, json.host, json.mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.tls && json.tls == 'tls') {
|
if (json.tls && json.tls == 'tls') {
|
||||||
@@ -754,7 +768,7 @@ class Outbound extends CommonClass {
|
|||||||
} else if (type === 'httpupgrade') {
|
} else if (type === 'httpupgrade') {
|
||||||
stream.httpupgrade = new HttpUpgradeStreamSettings(path, host);
|
stream.httpupgrade = new HttpUpgradeStreamSettings(path, host);
|
||||||
} else if (type === 'splithttp') {
|
} else if (type === 'splithttp') {
|
||||||
stream.splithttp = new SplitHTTPStreamSettings(path, host);
|
stream.splithttp = new SplitHTTPStreamSettings(path, host, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (security == 'tls') {
|
if (security == 'tls') {
|
||||||
@@ -875,22 +889,26 @@ Outbound.FreedomSettings = class extends CommonClass {
|
|||||||
json.domainStrategy,
|
json.domainStrategy,
|
||||||
json.redirect,
|
json.redirect,
|
||||||
json.fragment ? Outbound.FreedomSettings.Fragment.fromJson(json.fragment) : undefined,
|
json.fragment ? Outbound.FreedomSettings.Fragment.fromJson(json.fragment) : undefined,
|
||||||
json.noises ? json.noises.map(noise => Outbound.FreedomSettings.Noise.fromJson(noise)) : [new Outbound.FreedomSettings.Noise()],
|
json.noises ? json.noises.map(noise => Outbound.FreedomSettings.Noise.fromJson(noise)) : undefined,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
return {
|
||||||
domainStrategy: ObjectUtil.isEmpty(this.domainStrategy) ? undefined : this.domainStrategy,
|
domainStrategy: ObjectUtil.isEmpty(this.domainStrategy) ? undefined : this.domainStrategy,
|
||||||
redirect: this.redirect,
|
redirect: ObjectUtil.isEmpty(this.redirect) ? undefined: this.redirect,
|
||||||
fragment: Object.keys(this.fragment).length === 0 ? undefined : this.fragment,
|
fragment: Object.keys(this.fragment).length === 0 ? undefined : this.fragment,
|
||||||
noises: Outbound.FreedomSettings.Noise.toJsonArray(this.noises),
|
noises: this.noises.length === 0 ? undefined : Outbound.FreedomSettings.Noise.toJsonArray(this.noises),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Outbound.FreedomSettings.Fragment = class extends CommonClass {
|
Outbound.FreedomSettings.Fragment = class extends CommonClass {
|
||||||
constructor(packets = '1-3', length = '', interval = '') {
|
constructor(
|
||||||
|
packets = '1-3',
|
||||||
|
length = '',
|
||||||
|
interval = ''
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
this.packets = packets;
|
this.packets = packets;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
@@ -933,10 +951,6 @@ Outbound.FreedomSettings.Noise = class extends CommonClass {
|
|||||||
delay: this.delay,
|
delay: this.delay,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static toJsonArray(noises) {
|
|
||||||
return noises.map(noise => noise.toJson());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Outbound.BlackholeSettings = class extends CommonClass {
|
Outbound.BlackholeSettings = class extends CommonClass {
|
||||||
@@ -960,7 +974,7 @@ Outbound.BlackholeSettings = class extends CommonClass {
|
|||||||
Outbound.DNSSettings = class extends CommonClass {
|
Outbound.DNSSettings = class extends CommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
network = 'udp',
|
network = 'udp',
|
||||||
address = '1.1.1.1',
|
address = '',
|
||||||
port = 53,
|
port = 53,
|
||||||
nonIPQuery = 'drop',
|
nonIPQuery = 'drop',
|
||||||
blockTypes = []
|
blockTypes = []
|
||||||
@@ -1178,7 +1192,7 @@ Outbound.WireguardSettings = class extends CommonClass {
|
|||||||
domainStrategy = '',
|
domainStrategy = '',
|
||||||
reserved = '',
|
reserved = '',
|
||||||
peers = [new Outbound.WireguardSettings.Peer()],
|
peers = [new Outbound.WireguardSettings.Peer()],
|
||||||
kernelMode = false
|
noKernelTun = false,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.mtu = mtu;
|
this.mtu = mtu;
|
||||||
@@ -1189,7 +1203,7 @@ Outbound.WireguardSettings = class extends CommonClass {
|
|||||||
this.domainStrategy = domainStrategy;
|
this.domainStrategy = domainStrategy;
|
||||||
this.reserved = Array.isArray(reserved) ? reserved.join(',') : reserved;
|
this.reserved = Array.isArray(reserved) ? reserved.join(',') : reserved;
|
||||||
this.peers = peers;
|
this.peers = peers;
|
||||||
this.kernelMode = kernelMode;
|
this.noKernelTun = noKernelTun;
|
||||||
}
|
}
|
||||||
|
|
||||||
addPeer() {
|
addPeer() {
|
||||||
@@ -1209,7 +1223,7 @@ Outbound.WireguardSettings = class extends CommonClass {
|
|||||||
json.domainStrategy,
|
json.domainStrategy,
|
||||||
json.reserved,
|
json.reserved,
|
||||||
json.peers.map(peer => Outbound.WireguardSettings.Peer.fromJson(peer)),
|
json.peers.map(peer => Outbound.WireguardSettings.Peer.fromJson(peer)),
|
||||||
json.kernelMode,
|
json.noKernelTun,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1222,7 +1236,7 @@ Outbound.WireguardSettings = class extends CommonClass {
|
|||||||
domainStrategy: WireguardDomainStrategy.includes(this.domainStrategy) ? this.domainStrategy : undefined,
|
domainStrategy: WireguardDomainStrategy.includes(this.domainStrategy) ? this.domainStrategy : undefined,
|
||||||
reserved: this.reserved ? this.reserved.split(",").map(Number) : undefined,
|
reserved: this.reserved ? this.reserved.split(",").map(Number) : undefined,
|
||||||
peers: Outbound.WireguardSettings.Peer.toJsonArray(this.peers),
|
peers: Outbound.WireguardSettings.Peer.toJsonArray(this.peers),
|
||||||
kernelMode: this.kernelMode,
|
noKernelTun: this.noKernelTun,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class AllSetting {
|
|||||||
this.tgBotEnable = false;
|
this.tgBotEnable = false;
|
||||||
this.tgBotToken = "";
|
this.tgBotToken = "";
|
||||||
this.tgBotProxy = "";
|
this.tgBotProxy = "";
|
||||||
|
this.tgBotAPIServer = "";
|
||||||
this.tgBotChatId = "";
|
this.tgBotChatId = "";
|
||||||
this.tgRunTime = "@daily";
|
this.tgRunTime = "@daily";
|
||||||
this.tgBotBackup = false;
|
this.tgBotBackup = false;
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ const ONE_TB = ONE_GB * 1024;
|
|||||||
const ONE_PB = ONE_TB * 1024;
|
const ONE_PB = ONE_TB * 1024;
|
||||||
|
|
||||||
function sizeFormat(size) {
|
function sizeFormat(size) {
|
||||||
if (size < 0) {
|
if (size <= 0) return "0 B";
|
||||||
return "0 B";
|
|
||||||
} else if (size < ONE_KB) {
|
if (size < ONE_KB) {
|
||||||
return size.toFixed(0) + " B";
|
return size.toFixed(0) + " B";
|
||||||
} else if (size < ONE_MB) {
|
} else if (size < ONE_MB) {
|
||||||
return (size / ONE_KB).toFixed(2) + " KB";
|
return (size / ONE_KB).toFixed(2) + " KB";
|
||||||
@@ -59,7 +59,7 @@ function formatSecond(second) {
|
|||||||
return (second / 3600).toFixed(0) + 'h';
|
return (second / 3600).toFixed(0) + 'h';
|
||||||
} else {
|
} else {
|
||||||
day = Math.floor(second / 3600 / 24);
|
day = Math.floor(second / 3600 / 24);
|
||||||
remain = ((second/3600) - (day*24)).toFixed(0);
|
remain = ((second / 3600) - (day * 24)).toFixed(0);
|
||||||
return day + 'd' + (remain > 0 ? ' ' + remain + 'h' : '');
|
return day + 'd' + (remain > 0 ? ' ' + remain + 'h' : '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ function userExpiryColor(threshold, client, isDark = false) {
|
|||||||
return isDark ? '#2c3950' : '#bcbcbc';
|
return isDark ? '#2c3950' : '#bcbcbc';
|
||||||
}
|
}
|
||||||
now = new Date().getTime(),
|
now = new Date().getTime(),
|
||||||
expiry = client.expiryTime;
|
expiry = client.expiryTime;
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case expiry === null:
|
case expiry === null:
|
||||||
return "#7a316f"; // purple
|
return "#7a316f"; // purple
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type AllSetting struct {
|
|||||||
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"`
|
TgBotProxy string `json:"tgBotProxy" form:"tgBotProxy"`
|
||||||
|
TgBotAPIServer string `json:"tgBotAPIServer" form:"tgBotAPIServer"`
|
||||||
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"`
|
||||||
|
|||||||
@@ -39,12 +39,6 @@
|
|||||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Flow' v-if="clientsBulkModal.inbound.xtls">
|
|
||||||
<a-select v-model="clientsBulkModal.flow" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
|
||||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item v-if="app.subSettings.enable">
|
<a-form-item v-if="app.subSettings.enable">
|
||||||
<template slot="label">
|
<template slot="label">
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
@@ -181,9 +175,6 @@
|
|||||||
if (clientsBulkModal.inbound.canEnableTlsFlow()) {
|
if (clientsBulkModal.inbound.canEnableTlsFlow()) {
|
||||||
newClient.flow = clientsBulkModal.flow;
|
newClient.flow = clientsBulkModal.flow;
|
||||||
}
|
}
|
||||||
if (clientsBulkModal.inbound.xtls) {
|
|
||||||
newClient.flow = clientsBulkModal.flow;
|
|
||||||
}
|
|
||||||
newClient.reset = clientsBulkModal.reset;
|
newClient.reset = clientsBulkModal.reset;
|
||||||
clients.push(newClient);
|
clients.push(newClient);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,12 +104,6 @@
|
|||||||
</a-textarea>
|
</a-textarea>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="inbound.stream.isXtls" label='Flow'>
|
|
||||||
<a-select v-model="client.flow" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
|
|
||||||
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item v-if="inbound.canEnableTlsFlow()" label='Flow'>
|
<a-form-item v-if="inbound.canEnableTlsFlow()" label='Flow'>
|
||||||
<a-select v-model="client.flow" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="client.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>
|
||||||
|
|||||||
@@ -147,8 +147,8 @@
|
|||||||
<a-form-item label='Workers'>
|
<a-form-item label='Workers'>
|
||||||
<a-input-number v-model.number="outbound.settings.workers" min="0"></a-input-number>
|
<a-input-number v-model.number="outbound.settings.workers" min="0"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Kernel Mode'>
|
<a-form-item label='No Kernel Tun'>
|
||||||
<a-switch v-model="outbound.settings.kernelMode"></a-switch>
|
<a-switch v-model="outbound.settings.noKernelTun"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<template slot="label">
|
<template slot="label">
|
||||||
@@ -267,13 +267,13 @@
|
|||||||
<template v-if="outbound.canEnableStream()">
|
<template v-if="outbound.canEnableStream()">
|
||||||
<a-form-item label='{{ i18n "transmission" }}'>
|
<a-form-item label='{{ i18n "transmission" }}'>
|
||||||
<a-select v-model="outbound.stream.network" @change="streamNetworkChange" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="outbound.stream.network" @change="streamNetworkChange" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="tcp">TCP</a-select-option>
|
<a-select-option value="tcp">TCP (RAW)</a-select-option>
|
||||||
<a-select-option value="kcp">mKCP</a-select-option>
|
<a-select-option value="kcp">mKCP</a-select-option>
|
||||||
<a-select-option value="ws">WebSocket</a-select-option>
|
<a-select-option value="ws">WebSocket</a-select-option>
|
||||||
<a-select-option value="http">H2</a-select-option>
|
<a-select-option value="http">HTTP</a-select-option>
|
||||||
<a-select-option value="grpc">gRPC</a-select-option>
|
<a-select-option value="grpc">gRPC</a-select-option>
|
||||||
<a-select-option value="httpupgrade">HTTPUpgrade</a-select-option>
|
<a-select-option value="httpupgrade">HTTPUpgrade</a-select-option>
|
||||||
<a-select-option value="splithttp">SplitHTTP</a-select-option>
|
<a-select-option value="splithttp">SplitHTTP (XHTTP)</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<template v-if="outbound.stream.network === 'tcp'">
|
<template v-if="outbound.stream.network === 'tcp'">
|
||||||
@@ -380,6 +380,11 @@
|
|||||||
<a-form-item label='{{ i18n "path" }}'>
|
<a-form-item label='{{ i18n "path" }}'>
|
||||||
<a-input v-model.trim="outbound.stream.splithttp.path"></a-input>
|
<a-input v-model.trim="outbound.stream.splithttp.path"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label='Mode'>
|
||||||
|
<a-select v-model="outbound.stream.splithttp.mode" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -43,5 +43,8 @@
|
|||||||
<a-select-option value="udp">UDP</a-select-option>
|
<a-select-option value="udp">UDP</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label='ivCheck'>
|
||||||
|
<a-switch v-model="inbound.settings.ivCheck"></a-switch>
|
||||||
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
<a-form-item label='MTU'>
|
<a-form-item label='MTU'>
|
||||||
<a-input-number v-model.number="inbound.settings.mtu"></a-input-number>
|
<a-input-number v-model.number="inbound.settings.mtu"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Kernel Mode'>
|
<a-form-item label='No Kernel Tun'>
|
||||||
<a-switch v-model="inbound.settings.kernelMode"></a-switch>
|
<a-switch v-model="inbound.settings.noKernelTun"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Peers">
|
<a-form-item label="Peers">
|
||||||
<a-button icon="plus" type="primary" size="small" @click="inbound.settings.addPeer()"></a-button>
|
<a-button icon="plus" type="primary" size="small" @click="inbound.settings.addPeer()"></a-button>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Dest'>
|
<a-form-item label='Dest (Target)'>
|
||||||
<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='SNI'>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<a-select-option value="http">HTTP</a-select-option>
|
<a-select-option value="http">HTTP</a-select-option>
|
||||||
<a-select-option value="grpc">gRPC</a-select-option>
|
<a-select-option value="grpc">gRPC</a-select-option>
|
||||||
<a-select-option value="httpupgrade">HTTPUpgrade</a-select-option>
|
<a-select-option value="httpupgrade">HTTPUpgrade</a-select-option>
|
||||||
<a-select-option value="splithttp">SplitHTTP</a-select-option>
|
<a-select-option value="splithttp">SplitHTTP (XHTTP)</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|||||||
@@ -4,21 +4,30 @@
|
|||||||
<a-input v-model.trim="inbound.stream.splithttp.host"></a-input>
|
<a-input v-model.trim="inbound.stream.splithttp.host"></a-input>
|
||||||
</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.splithttp.path"></a-input>
|
<a-input v-model.trim="inbound.stream.splithttp.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.tcp.requestHeader" }}'>
|
||||||
<a-button icon="plus" size="small" @click="inbound.stream.splithttp.addHeader('host', '')"></a-button>
|
<a-button icon="plus" size="small" @click="inbound.stream.splithttp.addHeader('host', '')"></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.splithttp.headers">
|
<a-input-group compact v-for="(header, index) in inbound.stream.splithttp.headers">
|
||||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
<a-input style="width: 50%" v-model.trim="header.name"
|
||||||
|
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
<a-input style="width: 50%" v-model.trim="header.value"
|
||||||
<a-button slot="addonAfter" size="small" @click="inbound.stream.splithttp.removeHeader(index)">-</a-button>
|
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
|
<a-button slot="addonAfter" size="small"
|
||||||
|
@click="inbound.stream.splithttp.removeHeader(index)">-</a-button>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item label='Mode'>
|
||||||
|
<a-select v-model="inbound.stream.splithttp.mode" style="width: 50%"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item label="Max Concurrent Upload">
|
<a-form-item label="Max Concurrent Upload">
|
||||||
<a-input v-model.trim="inbound.stream.splithttp.scMaxConcurrentPosts"></a-input>
|
<a-input v-model.trim="inbound.stream.splithttp.scMaxConcurrentPosts"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -35,16 +44,19 @@
|
|||||||
<a-switch v-model="inbound.stream.splithttp.noSSEHeader"></a-switch>
|
<a-switch v-model="inbound.stream.splithttp.noSSEHeader"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Max Concurrency" v-if="!inbound.stream.splithttp.xmux.maxConnections">
|
<a-form-item label="Max Concurrency" v-if="!inbound.stream.splithttp.xmux.maxConnections">
|
||||||
<a-input-number v-model="inbound.stream.splithttp.xmux.maxConcurrency"></a-input-number>
|
<a-input v-model="inbound.stream.splithttp.xmux.maxConcurrency"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Max Connections" v-if="!inbound.stream.splithttp.xmux.maxConcurrency">
|
<a-form-item label="Max Connections" v-if="!inbound.stream.splithttp.xmux.maxConcurrency">
|
||||||
<a-input-number v-model="inbound.stream.splithttp.xmux.maxConnections"></a-input-number>
|
<a-input v-model="inbound.stream.splithttp.xmux.maxConnections"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Max Reuse Times">
|
<a-form-item label="Max Reuse Times">
|
||||||
<a-input-number v-model="inbound.stream.splithttp.xmux.cMaxReuseTimes"></a-input-number>
|
<a-input v-model="inbound.stream.splithttp.xmux.cMaxReuseTimes"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Max Lifetime (ms)">
|
<a-form-item label="Max Lifetime (ms)">
|
||||||
<a-input-number v-model="inbound.stream.splithttp.xmux.cMaxLifetimeMs"></a-input-number>
|
<a-input v-model="inbound.stream.splithttp.xmux.cMaxLifetimeMs"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="No gRPC Header">
|
||||||
|
<a-switch v-model="inbound.stream.splithttp.noGRPCHeader"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -5,18 +5,7 @@
|
|||||||
<a-form-item label='{{ i18n "security" }}'>
|
<a-form-item label='{{ i18n "security" }}'>
|
||||||
<a-radio-group v-model="inbound.stream.security" button-style="solid">
|
<a-radio-group v-model="inbound.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-tooltip>
|
<a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
|
||||||
<template slot="title">
|
|
||||||
<span>{{ i18n "pages.inbounds.xtlsDesc" }}</span>
|
|
||||||
</template>
|
|
||||||
<a-radio-button v-if="inbound.canEnableXtls()" value="xtls">XTLS</a-radio-button>
|
|
||||||
</a-tooltip>
|
|
||||||
<a-tooltip>
|
|
||||||
<template slot="title">
|
|
||||||
<span>{{ i18n "pages.inbounds.realityDesc" }}</span>
|
|
||||||
</template>
|
|
||||||
<a-radio-button v-if="inbound.canEnableReality()" value="reality">REALITY</a-radio-button>
|
|
||||||
</a-tooltip>
|
|
||||||
<a-radio-button value="tls">TLS</a-radio-button>
|
<a-radio-button value="tls">TLS</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -116,11 +105,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- xtls settings -->
|
|
||||||
<template v-else-if="inbound.stream.isXtls">
|
|
||||||
{{template "form/xtlsSettings"}}
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- reality settings -->
|
<!-- reality settings -->
|
||||||
<template v-if="inbound.stream.isReality">
|
<template v-if="inbound.stream.isReality">
|
||||||
{{template "form/realitySettings"}}
|
{{template "form/realitySettings"}}
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
{{define "form/xtlsSettings"}}
|
|
||||||
<template>
|
|
||||||
<a-form-item label="SNI" placeholder="Server Name Indication">
|
|
||||||
<a-input v-model.trim="inbound.stream.xtls.sni"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="ALPN">
|
|
||||||
<a-select mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme" v-model="inbound.stream.xtls.alpn">
|
|
||||||
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="Allow Insecure">
|
|
||||||
<a-switch v-model="inbound.stream.xtls.settings.allowInsecure"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
<template v-for="cert,index in inbound.stream.xtls.certs">
|
|
||||||
<a-form-item label='{{ i18n "certificate" }}'>
|
|
||||||
<a-radio-group v-model="cert.useFile" button-style="solid">
|
|
||||||
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
|
||||||
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
|
||||||
</a-radio-group>
|
|
||||||
<a-button icon="plus" v-if="index === 0" type="primary" size="small" @click="inbound.stream.xtls.addCert()"
|
|
||||||
style="margin-left: 10px"></a-button>
|
|
||||||
<a-button icon="minus" v-if="inbound.stream.xtls.certs.length>1" type="primary" size="small"
|
|
||||||
@click="inbound.stream.xtls.removeCert(index)" style="margin-left: 10px"></a-button>
|
|
||||||
</a-form-item>
|
|
||||||
<template v-if="cert.useFile">
|
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
|
|
||||||
<a-input v-model.trim="cert.certFile"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
|
|
||||||
<a-input v-model.trim="cert.keyFile"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label=" ">
|
|
||||||
<a-button type="primary" icon="import" @click="setDefaultCertXtls(index)">
|
|
||||||
{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
|
|
||||||
</a-form-item>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
|
|
||||||
<a-input type="textarea" :rows="3" v-model="cert.cert"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
|
|
||||||
<a-input type="textarea" :rows="3" v-model="cert.key"></a-input>
|
|
||||||
</a-form-item>
|
|
||||||
</template>
|
|
||||||
<a-form-item label='OCSP stapling'>
|
|
||||||
<a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="One Time Loading">
|
|
||||||
<a-switch v-model="cert.oneTimeLoading"></a-switch>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label='Usage Option'>
|
|
||||||
<a-select v-model="cert.usage" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option v-for="key in USAGE_OPTION" :value="key">[[ key ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
{{end}}
|
|
||||||
@@ -58,6 +58,14 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="inbound.isSplithttp">
|
||||||
|
<tr>
|
||||||
|
<td>Mode</td>
|
||||||
|
<td>
|
||||||
|
<a-tag>[[ inbound.stream.splithttp.mode ]]</a-tag>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
<template v-if="inbound.isKcp">
|
<template v-if="inbound.isKcp">
|
||||||
<tr>
|
<tr>
|
||||||
<td>kcp {{ i18n "encryption" }}</td>
|
<td>kcp {{ i18n "encryption" }}</td>
|
||||||
@@ -154,15 +162,6 @@
|
|||||||
<a-tag color="orange">{{ i18n "none" }}</a-tag>
|
<a-tag color="orange">{{ i18n "none" }}</a-tag>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr v-if="infoModal.inbound.xtls">
|
|
||||||
<td>Flow</td>
|
|
||||||
<td v-if="infoModal.clientSettings.flow">
|
|
||||||
<a-tag>[[ infoModal.clientSettings.flow ]]</a-tag>
|
|
||||||
</td>
|
|
||||||
<td v-else>
|
|
||||||
<a-tag color="orange">{{ i18n "none" }}</a-tag>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="infoModal.clientSettings.password">
|
<tr v-if="infoModal.clientSettings.password">
|
||||||
<td>{{ i18n "password" }}</td>
|
<td>{{ i18n "password" }}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -370,8 +369,8 @@
|
|||||||
<td>[[ inbound.settings.mtu ]]</td>
|
<td>[[ inbound.settings.mtu ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Kernel Mode</td>
|
<td>No Kernel Tun</td>
|
||||||
<td>[[ inbound.settings.kernelMode ]]</td>
|
<td>[[ inbound.settings.noKernelTun ]]</td>
|
||||||
</tr>
|
</tr>
|
||||||
<template v-for="(peer, index) in inbound.settings.peers">
|
<template v-for="(peer, index) in inbound.settings.peers">
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -102,11 +102,6 @@
|
|||||||
client.flow = "";
|
client.flow = "";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ((this.inModal.inbound.protocol == Protocols.VLESS || this.inModal.inbound.protocol == Protocols.TROJAN) && !inModal.inbound.xtls) {
|
|
||||||
this.inModal.inbound.settings.vlesses.forEach(client => {
|
|
||||||
client.flow = "";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
SSMethodChange() {
|
SSMethodChange() {
|
||||||
if (this.inModal.inbound.isSSMultiUser) {
|
if (this.inModal.inbound.isSSMultiUser) {
|
||||||
@@ -132,10 +127,6 @@
|
|||||||
inModal.inbound.stream.tls.certs[index].certFile = app.defaultCert;
|
inModal.inbound.stream.tls.certs[index].certFile = app.defaultCert;
|
||||||
inModal.inbound.stream.tls.certs[index].keyFile = app.defaultKey;
|
inModal.inbound.stream.tls.certs[index].keyFile = app.defaultKey;
|
||||||
},
|
},
|
||||||
setDefaultCertXtls(index) {
|
|
||||||
inModal.inbound.stream.xtls.certs[index].certFile = app.defaultCert;
|
|
||||||
inModal.inbound.stream.xtls.certs[index].keyFile = app.defaultKey;
|
|
||||||
},
|
|
||||||
async getNewX25519Cert() {
|
async getNewX25519Cert() {
|
||||||
inModal.loading(true);
|
inModal.loading(true);
|
||||||
const msg = await HttpUtil.post('/server/getNewX25519Cert');
|
const msg = await HttpUtil.post('/server/getNewX25519Cert');
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
.ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper {
|
.ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper {
|
||||||
margin:-10px 22px -10px !important;
|
margin:-10px 22px !important;
|
||||||
}
|
}
|
||||||
.ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper .ant-table {
|
.ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper .ant-table {
|
||||||
border-bottom-left-radius: 1rem;
|
border-bottom-left-radius: 1rem;
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
}
|
}
|
||||||
.ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper {
|
.ant-table .ant-table-content .ant-table-tbody tr:last-child .ant-table-wrapper {
|
||||||
margin:-10px 2px -10px !important;
|
margin:-10px 2px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ant-col-sm-24 {
|
.ant-col-sm-24 {
|
||||||
@@ -338,7 +338,6 @@
|
|||||||
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||||
<a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
|
<a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
|
||||||
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag>
|
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag>
|
||||||
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isXtls" color="blue">XTLS</a-tag>
|
|
||||||
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag>
|
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
@@ -549,7 +548,7 @@
|
|||||||
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/clipboard/clipboard.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/clipboard/clipboard.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/model/inbound.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" .}}
|
{{template "component/persianDatepicker" .}}
|
||||||
|
|||||||
@@ -291,6 +291,7 @@
|
|||||||
<a-select-option value="20">20</a-select-option>
|
<a-select-option value="20">20</a-select-option>
|
||||||
<a-select-option value="50">50</a-select-option>
|
<a-select-option value="50">50</a-select-option>
|
||||||
<a-select-option value="100">100</a-select-option>
|
<a-select-option value="100">100</a-select-option>
|
||||||
|
<a-select-option value="500">500</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select size="small" v-model="logModal.level" style="width:95px;"
|
<a-select size="small" v-model="logModal.level" style="width:95px;"
|
||||||
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
padding: .5rem 1rem;
|
padding: .5rem 1rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: rgb(255 145 0 / 15%);
|
background: rgb(255 145 0 / 15%);
|
||||||
margin: 1.5rem 2.5rem 0rem 2.5rem;
|
margin: 1.5rem 2.5rem 0rem;
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
transition: all 0.5s;
|
transition: all 0.5s;
|
||||||
animation: signal 3s cubic-bezier(0.18, 0.89, 0.32, 1.28) infinite;
|
animation: signal 3s cubic-bezier(0.18, 0.89, 0.32, 1.28) infinite;
|
||||||
@@ -246,6 +246,7 @@
|
|||||||
<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>
|
<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>
|
||||||
|
<setting-list-item type="text" title='{{ i18n "pages.settings.telegramAPIServer"}}' desc='{{ i18n "pages.settings.telegramAPIServerDesc"}}' v-model="allSetting.tgBotAPIServer" placeholder="https://api.example.com"></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">
|
||||||
@@ -382,12 +383,12 @@
|
|||||||
<a-collapse v-if="enableDirect" style="margin-top: 14px;">
|
<a-collapse v-if="enableDirect" style="margin-top: 14px;">
|
||||||
<a-collapse-panel header='{{ i18n "pages.xray.directips"}}'>
|
<a-collapse-panel header='{{ i18n "pages.xray.directips"}}'>
|
||||||
<a-list-item style="padding: 10px 20px">
|
<a-list-item style="padding: 10px 20px">
|
||||||
<a-checkbox-group v-model="geoIP" :options="geoIPOptions"></a-checkbox-group>
|
<a-checkbox-group v-model="directIPs" :options="IPsOptions"></a-checkbox-group>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
<a-collapse-panel header='{{ i18n "pages.xray.directdomains"}}'>
|
<a-collapse-panel header='{{ i18n "pages.xray.directdomains"}}'>
|
||||||
<a-list-item style="padding: 10px 20px">
|
<a-list-item style="padding: 10px 20px">
|
||||||
<a-checkbox-group v-model="geoSite" :options="geoSiteOptions"></a-checkbox-group>
|
<a-checkbox-group v-model="directDomains" :options="DomainsOptions"></a-checkbox-group>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
@@ -475,7 +476,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
geoIPOptions: [
|
IPsOptions: [
|
||||||
{ label: 'Private IP', value: 'private' },
|
{ label: 'Private IP', value: 'private' },
|
||||||
{ label: '🇮🇷 Iran', value: 'ir' },
|
{ label: '🇮🇷 Iran', value: 'ir' },
|
||||||
{ label: '🇨🇳 China', value: 'cn' },
|
{ label: '🇨🇳 China', value: 'cn' },
|
||||||
@@ -487,7 +488,7 @@
|
|||||||
{ label: '🇹🇷 Türkiye', value: 'tr' },
|
{ label: '🇹🇷 Türkiye', value: 'tr' },
|
||||||
{ label: '🇧🇷 Brazil', value: 'br' },
|
{ label: '🇧🇷 Brazil', value: 'br' },
|
||||||
],
|
],
|
||||||
geoSiteOptions: [
|
DomainsOptions: [
|
||||||
{ label: '🇮🇷 Iran', value: 'ir' },
|
{ label: '🇮🇷 Iran', value: 'ir' },
|
||||||
{ label: '🇨🇳 China', value: 'cn' },
|
{ label: '🇨🇳 China', value: 'cn' },
|
||||||
{ label: '🇷🇺 Russia', value: 'ru' },
|
{ label: '🇷🇺 Russia', value: 'ru' },
|
||||||
@@ -745,7 +746,7 @@
|
|||||||
this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
|
this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
geoIP: {
|
directIPs: {
|
||||||
get: function () {
|
get: function () {
|
||||||
if (!this.enableDirect) return [];
|
if (!this.enableDirect) return [];
|
||||||
const rules = JSON.parse(this.allSetting.subJsonRules);
|
const rules = JSON.parse(this.allSetting.subJsonRules);
|
||||||
@@ -771,7 +772,7 @@
|
|||||||
this.allSetting.subJsonRules = JSON.stringify(rules);
|
this.allSetting.subJsonRules = JSON.stringify(rules);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
geoSite: {
|
directDomains: {
|
||||||
get: function () {
|
get: function () {
|
||||||
if (!this.enableDirect) return [];
|
if (!this.enableDirect) return [];
|
||||||
const rules = JSON.parse(this.allSetting.subJsonRules);
|
const rules = JSON.parse(this.allSetting.subJsonRules);
|
||||||
|
|||||||
@@ -147,7 +147,7 @@
|
|||||||
publicKey: peer.public_key,
|
publicKey: peer.public_key,
|
||||||
endpoint: peer.endpoint.host,
|
endpoint: peer.endpoint.host,
|
||||||
}],
|
}],
|
||||||
kernelMode: false
|
noKernelTun: false,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
.ant-collapse-content-box>li {
|
.ant-collapse-content-box>li {
|
||||||
padding: 12px 0 0 0 !important;
|
padding: 12px 0 0 !important;
|
||||||
}
|
}
|
||||||
.ant-list-item>li {
|
.ant-list-item>li {
|
||||||
padding: 10px 20px !important;
|
padding: 10px 20px !important;
|
||||||
@@ -270,7 +270,7 @@
|
|||||||
v-model="blockedDomains"
|
v-model="blockedDomains"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option :value="p.value" :label="p.label"
|
<a-select-option :value="p.value" :label="p.label"
|
||||||
v-for="p in settingsData.DomainsOptions"> [[ p.label ]]
|
v-for="p in settingsData.BlockDomainsOptions"> [[ p.label ]]
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</template>
|
</template>
|
||||||
@@ -316,7 +316,7 @@
|
|||||||
v-model="directDomains"
|
v-model="directDomains"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option :value="p.value" :label="p.label"
|
<a-select-option :value="p.value" :label="p.label"
|
||||||
v-for="p in settingsData.DirectDomainsOptions"> [[ p.label ]]
|
v-for="p in settingsData.DomainsOptions"> [[ p.label ]]
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</template>
|
</template>
|
||||||
@@ -898,7 +898,7 @@
|
|||||||
bittorrent: ["bittorrent"],
|
bittorrent: ["bittorrent"],
|
||||||
},
|
},
|
||||||
IPsOptions: [
|
IPsOptions: [
|
||||||
{ label: 'Private IP', value: 'geoip:private' },
|
{ label: 'Private IPs', value: 'geoip:private' },
|
||||||
{ label: '🇮🇷 Iran', value: 'ext:geoip_IR.dat:ir' },
|
{ label: '🇮🇷 Iran', value: 'ext:geoip_IR.dat:ir' },
|
||||||
{ label: '🇨🇳 China', value: 'geoip:cn' },
|
{ label: '🇨🇳 China', value: 'geoip:cn' },
|
||||||
{ label: '🇷🇺 Russia', value: 'geoip:ru' },
|
{ label: '🇷🇺 Russia', value: 'geoip:ru' },
|
||||||
@@ -910,12 +910,6 @@
|
|||||||
{ label: '🇧🇷 Brazil', value: 'geoip:br' },
|
{ label: '🇧🇷 Brazil', value: 'geoip:br' },
|
||||||
],
|
],
|
||||||
DomainsOptions: [
|
DomainsOptions: [
|
||||||
{ label: 'Ads All', value: 'geosite:category-ads-all' },
|
|
||||||
{ label: 'Ads IR 🇮🇷', value: 'ext:geosite_IR.dat:category-ads-all' },
|
|
||||||
{ label: 'Ads VN 🇻🇳', value: 'ext:geosite_VN.dat:ads' },
|
|
||||||
{ label: 'Sec-IR malware', value: 'geosite_IR.dat:malware' },
|
|
||||||
{ label: 'Sec-IR phishing', value: 'geosite_IR.dat:phishing' },
|
|
||||||
{ label: 'Sec-IR cryptominers', value: 'geosite_IR.dat:cryptominers' },
|
|
||||||
{ label: '🇮🇷 Iran', value: 'ext:geosite_IR.dat:ir' },
|
{ label: '🇮🇷 Iran', value: 'ext:geosite_IR.dat:ir' },
|
||||||
{ label: '🇮🇷 .ir', value: 'regexp:.*\\.ir$' },
|
{ label: '🇮🇷 .ir', value: 'regexp:.*\\.ir$' },
|
||||||
{ label: '🇮🇷 .ایران', value: 'regexp:.*\\.xn--mgba3a4f16a$' },
|
{ label: '🇮🇷 .ایران', value: 'regexp:.*\\.xn--mgba3a4f16a$' },
|
||||||
@@ -926,7 +920,13 @@
|
|||||||
{ label: '🇻🇳 Vietnam', value: 'ext:geosite_VN.dat:vn' },
|
{ label: '🇻🇳 Vietnam', value: 'ext:geosite_VN.dat:vn' },
|
||||||
{ label: '🇻🇳 .vn', value: 'regexp:.*\\.vn$' },
|
{ label: '🇻🇳 .vn', value: 'regexp:.*\\.vn$' },
|
||||||
],
|
],
|
||||||
DirectDomainsOptions: [
|
BlockDomainsOptions: [
|
||||||
|
{ label: 'Ads All', value: 'geosite:category-ads-all' },
|
||||||
|
{ label: 'Ads IR 🇮🇷', value: 'ext:geosite_IR.dat:category-ads-all' },
|
||||||
|
{ label: 'Ads VN 🇻🇳', value: 'ext:geosite_VN.dat:ads' },
|
||||||
|
{ label: 'Malware 🇮🇷', value: 'ext:geosite_IR.dat:malware' },
|
||||||
|
{ label: 'Phishing 🇮🇷', value: 'ext:geosite_IR.dat:phishing' },
|
||||||
|
{ label: 'Cryptominers 🇮🇷', value: 'ext:geosite_IR.dat:cryptominers' },
|
||||||
{ label: '🇮🇷 Iran', value: 'ext:geosite_IR.dat:ir' },
|
{ label: '🇮🇷 Iran', value: 'ext:geosite_IR.dat:ir' },
|
||||||
{ label: '🇮🇷 .ir', value: 'regexp:.*\\.ir$' },
|
{ label: '🇮🇷 .ir', value: 'regexp:.*\\.ir$' },
|
||||||
{ label: '🇮🇷 .ایران', value: 'regexp:.*\\.xn--mgba3a4f16a$' },
|
{ label: '🇮🇷 .ایران', value: 'regexp:.*\\.xn--mgba3a4f16a$' },
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"x-ui/database"
|
"x-ui/database"
|
||||||
@@ -37,11 +36,17 @@ func (j *CheckClientIpJob) Run() {
|
|||||||
|
|
||||||
shouldClearAccessLog := false
|
shouldClearAccessLog := false
|
||||||
iplimitActive := j.hasLimitIp()
|
iplimitActive := j.hasLimitIp()
|
||||||
f2bInstalled := j.checkFail2BanInstalled(iplimitActive)
|
f2bInstalled := j.checkFail2BanInstalled()
|
||||||
isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive)
|
isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive)
|
||||||
|
|
||||||
if iplimitActive && f2bInstalled && isAccessLogAvailable {
|
if iplimitActive {
|
||||||
shouldClearAccessLog = j.processLogFile()
|
if f2bInstalled && isAccessLogAvailable {
|
||||||
|
shouldClearAccessLog = j.processLogFile()
|
||||||
|
} else {
|
||||||
|
if !f2bInstalled {
|
||||||
|
logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldClearAccessLog || (isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600) {
|
if shouldClearAccessLog || (isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600) {
|
||||||
@@ -53,23 +58,18 @@ func (j *CheckClientIpJob) clearAccessLog() {
|
|||||||
logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
logAccessP, err := os.OpenFile(xray.GetAccessPersistentLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
// get access log path to open it
|
|
||||||
accessLogPath, err := xray.GetAccessLogPath()
|
accessLogPath, err := xray.GetAccessLogPath()
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
// reopen the access log file for reading
|
|
||||||
file, err := os.Open(accessLogPath)
|
file, err := os.Open(accessLogPath)
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
// copy access log content to persistent file
|
|
||||||
_, err = io.Copy(logAccessP, file)
|
_, err = io.Copy(logAccessP, file)
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
|
|
||||||
// close the file after copying content
|
|
||||||
logAccessP.Close()
|
logAccessP.Close()
|
||||||
file.Close()
|
file.Close()
|
||||||
|
|
||||||
// clean access log
|
|
||||||
err = os.Truncate(accessLogPath, 0)
|
err = os.Truncate(accessLogPath, 0)
|
||||||
j.checkError(err)
|
j.checkError(err)
|
||||||
j.lastClear = time.Now().Unix()
|
j.lastClear = time.Now().Unix()
|
||||||
@@ -105,74 +105,69 @@ func (j *CheckClientIpJob) hasLimitIp() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (j *CheckClientIpJob) processLogFile() bool {
|
func (j *CheckClientIpJob) processLogFile() bool {
|
||||||
accessLogPath, err := xray.GetAccessLogPath()
|
|
||||||
j.checkError(err)
|
|
||||||
|
|
||||||
file, err := os.Open(accessLogPath)
|
ipRegex := regexp.MustCompile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
|
||||||
j.checkError(err)
|
emailRegex := regexp.MustCompile(`email: (.+)$`)
|
||||||
|
|
||||||
InboundClientIps := make(map[string][]string)
|
accessLogPath, _ := xray.GetAccessLogPath()
|
||||||
|
file, _ := os.Open(accessLogPath)
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
inboundClientIps := make(map[string]map[string]struct{}, 100)
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
scanner := bufio.NewScanner(file)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
|
|
||||||
ipRegx, _ := regexp.Compile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
|
ipMatches := ipRegex.FindStringSubmatch(line)
|
||||||
emailRegx, _ := regexp.Compile(`email: (\S+)$`)
|
if len(ipMatches) < 2 {
|
||||||
|
continue
|
||||||
matches := ipRegx.FindStringSubmatch(line)
|
|
||||||
if len(matches) > 1 {
|
|
||||||
ip := matches[1]
|
|
||||||
if ip == "127.0.0.1" || ip == "::1" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
matchesEmail := emailRegx.FindString(line)
|
|
||||||
if matchesEmail == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
matchesEmail = strings.Split(matchesEmail, "email: ")[1]
|
|
||||||
|
|
||||||
if InboundClientIps[matchesEmail] != nil {
|
|
||||||
if j.contains(InboundClientIps[matchesEmail], ip) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
|
||||||
} else {
|
|
||||||
InboundClientIps[matchesEmail] = append(InboundClientIps[matchesEmail], ip)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ip := ipMatches[1]
|
||||||
|
|
||||||
|
if ip == "127.0.0.1" || ip == "::1" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
emailMatches := emailRegex.FindStringSubmatch(line)
|
||||||
|
if len(emailMatches) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
email := emailMatches[1]
|
||||||
|
|
||||||
|
if _, exists := inboundClientIps[email]; !exists {
|
||||||
|
inboundClientIps[email] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
inboundClientIps[email][ip] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
j.checkError(scanner.Err())
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
shouldCleanLog := false
|
shouldCleanLog := false
|
||||||
|
for email, uniqueIps := range inboundClientIps {
|
||||||
|
|
||||||
for clientEmail, ips := range InboundClientIps {
|
ips := make([]string, 0, len(uniqueIps))
|
||||||
inboundClientIps, err := j.getInboundClientIps(clientEmail)
|
for ip := range uniqueIps {
|
||||||
sort.Strings(ips)
|
ips = append(ips, ip)
|
||||||
if err != nil {
|
|
||||||
j.addInboundClientIps(clientEmail, ips)
|
|
||||||
} else {
|
|
||||||
shouldCleanLog = j.updateInboundClientIps(inboundClientIps, clientEmail, ips)
|
|
||||||
}
|
}
|
||||||
|
sort.Strings(ips)
|
||||||
|
|
||||||
|
inboundClientIps, err := j.getInboundClientIps(email)
|
||||||
|
if err != nil {
|
||||||
|
j.addInboundClientIps(email, ips)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldCleanLog = j.updateInboundClientIps(inboundClientIps, email, ips) || shouldCleanLog
|
||||||
}
|
}
|
||||||
|
|
||||||
return shouldCleanLog
|
return shouldCleanLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *CheckClientIpJob) checkFail2BanInstalled(iplimitActive bool) bool {
|
func (j *CheckClientIpJob) checkFail2BanInstalled() bool {
|
||||||
cmd := "fail2ban-client"
|
cmd := "fail2ban-client"
|
||||||
args := []string{"-h"}
|
args := []string{"-h"}
|
||||||
err := exec.Command(cmd, args...).Run()
|
err := exec.Command(cmd, args...).Run()
|
||||||
|
return err == nil
|
||||||
if iplimitActive && err != nil {
|
|
||||||
logger.Warning("[LimitIP] Fail2Ban is not installed, Please install Fail2Ban from the x-ui bash menu.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool {
|
func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool {
|
||||||
@@ -253,7 +248,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
|
|||||||
inboundClientIps.ClientEmail = clientEmail
|
inboundClientIps.ClientEmail = clientEmail
|
||||||
inboundClientIps.Ips = string(jsonIps)
|
inboundClientIps.Ips = string(jsonIps)
|
||||||
|
|
||||||
// Fetch inbound settings by client email
|
|
||||||
inbound, err := j.getInboundByEmail(clientEmail)
|
inbound, err := j.getInboundByEmail(clientEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("failed to fetch inbound settings for email %s: %s", clientEmail, err)
|
logger.Errorf("failed to fetch inbound settings for email %s: %s", clientEmail, err)
|
||||||
@@ -265,14 +259,12 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal settings to get client limits
|
|
||||||
settings := map[string][]model.Client{}
|
settings := map[string][]model.Client{}
|
||||||
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{}
|
j.disAllowedIps = []string{}
|
||||||
|
|
||||||
// Open log file for IP limits
|
|
||||||
logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
logIpFile, err := os.OpenFile(xray.GetIPLimitLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("failed to open IP limit log file: %s", err)
|
logger.Errorf("failed to open IP limit log file: %s", err)
|
||||||
@@ -282,7 +274,6 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
|
|||||||
log.SetOutput(logIpFile)
|
log.SetOutput(logIpFile)
|
||||||
log.SetFlags(log.LstdFlags)
|
log.SetFlags(log.LstdFlags)
|
||||||
|
|
||||||
// Check client IP limits
|
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.Email == clientEmail {
|
if client.Email == clientEmail {
|
||||||
limitIp := client.LimitIP
|
limitIp := client.LimitIP
|
||||||
|
|||||||
@@ -30,7 +30,9 @@
|
|||||||
"tag": "direct",
|
"tag": "direct",
|
||||||
"protocol": "freedom",
|
"protocol": "freedom",
|
||||||
"settings": {
|
"settings": {
|
||||||
"domainStrategy": "UseIP"
|
"domainStrategy": "AsIs",
|
||||||
|
"redirect": "",
|
||||||
|
"noises": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -588,8 +588,12 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
|
|||||||
logger.Debug("Client deleted by api:", email)
|
logger.Debug("Client deleted by api:", email)
|
||||||
needRestart = false
|
needRestart = false
|
||||||
} else {
|
} else {
|
||||||
logger.Debug("Unable to del client by api:", err1)
|
if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
|
||||||
needRestart = true
|
logger.Debug("User is already deleted. Nothing to do more...")
|
||||||
|
} else {
|
||||||
|
logger.Debug("Error in deleting client by api:", err1)
|
||||||
|
needRestart = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.xrayApi.Close()
|
s.xrayApi.Close()
|
||||||
}
|
}
|
||||||
@@ -713,10 +717,14 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
|
|||||||
if oldClients[clientIndex].Enable {
|
if oldClients[clientIndex].Enable {
|
||||||
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
|
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
|
||||||
if err1 == nil {
|
if err1 == nil {
|
||||||
logger.Debug("Old client deleted by api:", clients[0].Email)
|
logger.Debug("Old client deleted by api:", oldEmail)
|
||||||
} else {
|
} else {
|
||||||
logger.Debug("Error in deleting client by api:", err1)
|
if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", oldEmail)) {
|
||||||
needRestart = true
|
logger.Debug("User is already deleted. Nothing to do more...")
|
||||||
|
} else {
|
||||||
|
logger.Debug("Error in deleting client by api:", err1)
|
||||||
|
needRestart = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if clients[0].Enable {
|
if clients[0].Enable {
|
||||||
@@ -1037,12 +1045,8 @@ func (s *InboundService) disableInvalidInbounds(tx *gorm.DB) (bool, int64, error
|
|||||||
if err1 == nil {
|
if err1 == nil {
|
||||||
logger.Debug("Inbound disabled by api:", tag)
|
logger.Debug("Inbound disabled by api:", tag)
|
||||||
} else {
|
} else {
|
||||||
if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", tag)) {
|
logger.Debug("Error in disabling inbound by api:", err1)
|
||||||
logger.Debug("User is already disabled. Nothing to do more...")
|
needRestart = true
|
||||||
} else {
|
|
||||||
logger.Debug("Error in disabling client by api:", err1)
|
|
||||||
needRestart = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.xrayApi.Close()
|
s.xrayApi.Close()
|
||||||
@@ -1574,7 +1578,7 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, e
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
for _, client := range clients {
|
for _, client := range clients {
|
||||||
if client.Email == clientEmail {
|
if client.Email == clientEmail && client.Enable {
|
||||||
s.xrayApi.Init(p.GetAPIPort())
|
s.xrayApi.Init(p.GetAPIPort())
|
||||||
cipher := ""
|
cipher := ""
|
||||||
if string(inbound.Protocol) == "shadowsocks" {
|
if string(inbound.Protocol) == "shadowsocks" {
|
||||||
|
|||||||
@@ -248,28 +248,46 @@ func (s *ServerService) GetStatus(lastStatus *Status) *Status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServerService) GetXrayVersions() ([]string, error) {
|
func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||||
url := "https://api.github.com/repos/XTLS/Xray-core/releases"
|
const (
|
||||||
resp, err := http.Get(url)
|
XrayURL = "https://api.github.com/repos/XTLS/Xray-core/releases"
|
||||||
|
bufferSize = 8192
|
||||||
|
)
|
||||||
|
|
||||||
|
resp, err := http.Get(XrayURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
buffer := bytes.NewBuffer(make([]byte, 8192))
|
|
||||||
|
buffer := bytes.NewBuffer(make([]byte, bufferSize))
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
_, err = buffer.ReadFrom(resp.Body)
|
if _, err := buffer.ReadFrom(resp.Body); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
releases := make([]Release, 0)
|
var releases []Release
|
||||||
err = json.Unmarshal(buffer.Bytes(), &releases)
|
if err := json.Unmarshal(buffer.Bytes(), &releases); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var versions []string
|
var versions []string
|
||||||
for _, release := range releases {
|
for _, release := range releases {
|
||||||
if release.TagName >= "v1.7.5" {
|
tagVersion := strings.TrimPrefix(release.TagName, "v")
|
||||||
|
tagParts := strings.Split(tagVersion, ".")
|
||||||
|
if len(tagParts) != 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
major, err1 := strconv.Atoi(tagParts[0])
|
||||||
|
minor, err2 := strconv.Atoi(tagParts[1])
|
||||||
|
patch, err3 := strconv.Atoi(tagParts[2])
|
||||||
|
if err1 != nil || err2 != nil || err3 != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (major == 1 && minor == 8 && patch == 24) ||
|
||||||
|
(major == 24 && ((minor > 11) || (minor == 11 && patch >= 11))) ||
|
||||||
|
(major > 24) {
|
||||||
versions = append(versions, release.TagName)
|
versions = append(versions, release.TagName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ var defaultValueMap = map[string]string{
|
|||||||
"tgBotEnable": "false",
|
"tgBotEnable": "false",
|
||||||
"tgBotToken": "",
|
"tgBotToken": "",
|
||||||
"tgBotProxy": "",
|
"tgBotProxy": "",
|
||||||
|
"tgBotAPIServer": "",
|
||||||
"tgBotChatId": "",
|
"tgBotChatId": "",
|
||||||
"tgRunTime": "@daily",
|
"tgRunTime": "@daily",
|
||||||
"tgBotBackup": "false",
|
"tgBotBackup": "false",
|
||||||
@@ -242,6 +243,10 @@ func (s *SettingService) GetListen() (string, error) {
|
|||||||
return s.getString("webListen")
|
return s.getString("webListen")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) SetListen(ip string) error {
|
||||||
|
return s.setString("webListen", ip)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetWebDomain() (string, error) {
|
func (s *SettingService) GetWebDomain() (string, error) {
|
||||||
return s.getString("webDomain")
|
return s.getString("webDomain")
|
||||||
}
|
}
|
||||||
@@ -262,6 +267,14 @@ func (s *SettingService) SetTgBotProxy(token string) error {
|
|||||||
return s.setString("tgBotProxy", token)
|
return s.setString("tgBotProxy", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) GetTgBotAPIServer() (string, error) {
|
||||||
|
return s.getString("tgBotAPIServer")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SettingService) SetTgBotAPIServer(token string) error {
|
||||||
|
return s.setString("tgBotAPIServer", token)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SettingService) GetTgBotChatId() (string, error) {
|
func (s *SettingService) GetTgBotChatId() (string, error) {
|
||||||
return s.getString("tgBotChatId")
|
return s.getString("tgBotChatId")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,8 +108,14 @@ func (t *Tgbot) Start(i18nFS embed.FS) error {
|
|||||||
logger.Warning("Failed to get Telegram bot proxy URL:", err)
|
logger.Warning("Failed to get Telegram bot proxy URL:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get Telegram bot API server URL
|
||||||
|
tgBotAPIServer, err := t.settingService.GetTgBotAPIServer()
|
||||||
|
if err != nil {
|
||||||
|
logger.Warning("Failed to get Telegram bot API server URL:", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Create new Telegram bot instance
|
// Create new Telegram bot instance
|
||||||
bot, err = t.NewBot(tgBotToken, tgBotProxy)
|
bot, err = t.NewBot(tgBotToken, tgBotProxy, tgBotAPIServer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to initialize Telegram bot API:", err)
|
logger.Error("Failed to initialize Telegram bot API:", err)
|
||||||
return err
|
return err
|
||||||
@@ -125,26 +131,40 @@ func (t *Tgbot) Start(i18nFS embed.FS) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tgbot) NewBot(token string, proxyUrl string) (*telego.Bot, error) {
|
func (t *Tgbot) NewBot(token string, proxyUrl string, apiServerUrl string) (*telego.Bot, error) {
|
||||||
if proxyUrl == "" {
|
if proxyUrl == "" && apiServerUrl == "" {
|
||||||
// No proxy URL provided, use default instance
|
|
||||||
return telego.NewBot(token)
|
return telego.NewBot(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(proxyUrl, "socks5://") {
|
if proxyUrl != "" {
|
||||||
logger.Warning("Invalid socks5 URL, starting with default")
|
if !strings.HasPrefix(proxyUrl, "socks5://") {
|
||||||
|
logger.Warning("Invalid socks5 URL, using default")
|
||||||
|
return telego.NewBot(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := url.Parse(proxyUrl)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warningf("Can't parse proxy URL, using default instance for tgbot: %v", err)
|
||||||
|
return telego.NewBot(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
return telego.NewBot(token, telego.WithFastHTTPClient(&fasthttp.Client{
|
||||||
|
Dial: fasthttpproxy.FasthttpSocksDialer(proxyUrl),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(apiServerUrl, "http") {
|
||||||
|
logger.Warning("Invalid http(s) URL, using default")
|
||||||
return telego.NewBot(token)
|
return telego.NewBot(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := url.Parse(proxyUrl)
|
_, err := url.Parse(apiServerUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Warning("Can't parse proxy URL, using default instance for tgbot:", err)
|
logger.Warningf("Can't parse API server URL, using default instance for tgbot: %v", err)
|
||||||
return telego.NewBot(token)
|
return telego.NewBot(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
return telego.NewBot(token, telego.WithFastHTTPClient(&fasthttp.Client{
|
return telego.NewBot(token, telego.WithAPIServer(apiServerUrl))
|
||||||
Dial: fasthttpproxy.FasthttpSocksDialer(proxyUrl),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tgbot) IsRunning() bool {
|
func (t *Tgbot) IsRunning() bool {
|
||||||
@@ -243,7 +263,12 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo
|
|||||||
|
|
||||||
command, _, commandArgs := tu.ParseCommand(message.Text)
|
command, _, commandArgs := tu.ParseCommand(message.Text)
|
||||||
|
|
||||||
// Extract the command from the Message.
|
// Helper function to handle unknown commands.
|
||||||
|
handleUnknownCommand := func() {
|
||||||
|
msg += t.I18nBot("tgbot.commands.unknown")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the command.
|
||||||
switch command {
|
switch command {
|
||||||
case "help":
|
case "help":
|
||||||
msg += t.I18nBot("tgbot.commands.help")
|
msg += t.I18nBot("tgbot.commands.help")
|
||||||
@@ -266,9 +291,7 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo
|
|||||||
if isAdmin {
|
if isAdmin {
|
||||||
t.searchClient(chatId, commandArgs[0])
|
t.searchClient(chatId, commandArgs[0])
|
||||||
} else {
|
} else {
|
||||||
// Convert message.From.ID to int64
|
t.getClientUsage(chatId, int64(message.From.ID), commandArgs[0])
|
||||||
fromID := int64(message.From.ID)
|
|
||||||
t.getClientUsage(chatId, fromID, commandArgs[0])
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
msg += t.I18nBot("tgbot.commands.usage")
|
msg += t.I18nBot("tgbot.commands.usage")
|
||||||
@@ -278,19 +301,46 @@ func (t *Tgbot) answerCommand(message *telego.Message, chatId int64, isAdmin boo
|
|||||||
if isAdmin && len(commandArgs) > 0 {
|
if isAdmin && len(commandArgs) > 0 {
|
||||||
t.searchInbound(chatId, commandArgs[0])
|
t.searchInbound(chatId, commandArgs[0])
|
||||||
} else {
|
} else {
|
||||||
msg += t.I18nBot("tgbot.commands.unknown")
|
handleUnknownCommand()
|
||||||
|
}
|
||||||
|
case "restart":
|
||||||
|
onlyMessage = true
|
||||||
|
if isAdmin {
|
||||||
|
if len(commandArgs) == 0 {
|
||||||
|
msg += t.I18nBot("tgbot.commands.restartUsage")
|
||||||
|
} else if strings.ToLower(commandArgs[0]) == "force" {
|
||||||
|
if t.xrayService.IsXrayRunning() {
|
||||||
|
err := t.xrayService.RestartXray(true)
|
||||||
|
if err != nil {
|
||||||
|
msg += t.I18nBot("tgbot.commands.restartFailed", "Error=="+err.Error())
|
||||||
|
} else {
|
||||||
|
msg += t.I18nBot("tgbot.commands.restartSuccess")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
msg += t.I18nBot("tgbot.commands.xrayNotRunning")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleUnknownCommand()
|
||||||
|
msg += t.I18nBot("tgbot.commands.restartUsage")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleUnknownCommand()
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
msg += t.I18nBot("tgbot.commands.unknown")
|
handleUnknownCommand()
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
if onlyMessage {
|
t.sendResponse(chatId, msg, onlyMessage, isAdmin)
|
||||||
t.SendMsgToTgbot(chatId, msg)
|
}
|
||||||
return
|
}
|
||||||
} else {
|
|
||||||
t.SendAnswer(chatId, msg, isAdmin)
|
// Helper function to send the message based on onlyMessage flag.
|
||||||
}
|
func (t *Tgbot) sendResponse(chatId int64, msg string, onlyMessage, isAdmin bool) {
|
||||||
|
if onlyMessage {
|
||||||
|
t.SendMsgToTgbot(chatId, msg)
|
||||||
|
} else {
|
||||||
|
t.SendAnswer(chatId, msg, isAdmin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,6 +922,7 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
|
|||||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.onlines")).WithCallbackData(t.encodeQuery("onlines")),
|
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.onlines")).WithCallbackData(t.encodeQuery("onlines")),
|
||||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.allClients")).WithCallbackData(t.encodeQuery("get_inbounds")),
|
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.allClients")).WithCallbackData(t.encodeQuery("get_inbounds")),
|
||||||
),
|
),
|
||||||
|
// TODOOOOOOOOOOOOOO: Add restart button here.
|
||||||
)
|
)
|
||||||
numericKeyboardClient := tu.InlineKeyboard(
|
numericKeyboardClient := tu.InlineKeyboard(
|
||||||
tu.InlineKeyboardRow(
|
tu.InlineKeyboardRow(
|
||||||
|
|||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "The IPs history log. (to enable inbound after disabling, clear the log)"
|
"IPLimitlogDesc" = "The IPs history log. (to enable inbound after disabling, 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"
|
|
||||||
"realityDesc" = "Xray must be v1.8.0+"
|
|
||||||
"telegramDesc" = "Please provide Telegram Chat ID. (use '/id' command in the bot) or (@userinfobot)"
|
"telegramDesc" = "Please provide Telegram Chat ID. (use '/id' command in the bot) or (@userinfobot)"
|
||||||
"subscriptionDesc" = "To find your subscription URL, navigate to the 'Details'. Additionally, you can use the same name for several clients."
|
"subscriptionDesc" = "To find your subscription URL, navigate to the 'Details'. Additionally, you can use the same name for several clients."
|
||||||
"info" = "Info"
|
"info" = "Info"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"telegramTokenDesc" = "The Telegram bot token obtained from '@BotFather'."
|
"telegramTokenDesc" = "The Telegram bot token obtained from '@BotFather'."
|
||||||
"telegramProxy" = "SOCKS Proxy"
|
"telegramProxy" = "SOCKS Proxy"
|
||||||
"telegramProxyDesc" = "Enables SOCKS5 proxy for connecting to Telegram. (adjust settings as per guide)"
|
"telegramProxyDesc" = "Enables SOCKS5 proxy for connecting to Telegram. (adjust settings as per guide)"
|
||||||
|
"telegramAPIServer" = "Telegram API Server"
|
||||||
|
"telegramAPIServerDesc" = "The Telegram API server to use. Leave blank to use the default server."
|
||||||
"telegramChatId" = "Admin Chat ID"
|
"telegramChatId" = "Admin Chat ID"
|
||||||
"telegramChatIdDesc" = "The Telegram Admin Chat ID(s). (comma-separated)(get it here @userinfobot) or (use '/id' command in the bot)"
|
"telegramChatIdDesc" = "The Telegram Admin Chat ID(s). (comma-separated)(get it here @userinfobot) or (use '/id' command in the bot)"
|
||||||
"telegramNotifyTime" = "Notification Time"
|
"telegramNotifyTime" = "Notification Time"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"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>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>"
|
"helpAdminCommands" = "To restart Xray Core:\r\n<code>/restart force</code>\r\n\r\nTo 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>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "To search for statistics, use the following command:\r\n\r\n<code>/usage [Email]</code>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>"
|
"helpClientCommands" = "To search for statistics, use the following command:\r\n\r\n<code>/usage [Email]</code>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ Operation successful!"
|
||||||
|
"restartFailed" = "❗ Error in operation.\r\n\r\n<code>Error: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core is not running."
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 CPU Load {{ .Percent }}% exceeds the threshold of {{ .Threshold }}%"
|
"cpuThreshold" = "🔴 CPU Load {{ .Percent }}% exceeds the threshold of {{ .Threshold }}%"
|
||||||
|
|||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "Registro de historial de IPs (antes de habilitar la entrada después de que haya sido desactivada por el límite de IP, debes borrar el registro)."
|
"IPLimitlogDesc" = "Registro de historial de IPs (antes de habilitar la entrada después de que haya sido desactivada por el límite de IP, debes borrar el registro)."
|
||||||
"IPLimitlogclear" = "Limpiar el Registro"
|
"IPLimitlogclear" = "Limpiar el Registro"
|
||||||
"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"
|
|
||||||
"realityDesc" = "La versión del núcleo de Xray debe ser 1.8.0 o superior."
|
|
||||||
"telegramDesc" = "Por favor, proporciona el ID de Chat de Telegram. (usa el comando '/id' en el bot) o (@userinfobot)"
|
"telegramDesc" = "Por favor, proporciona el ID de Chat de Telegram. (usa el comando '/id' en el bot) o (@userinfobot)"
|
||||||
"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"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"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"
|
"telegramProxy" = "Socks5 Proxy"
|
||||||
"telegramProxyDesc" = "Si necesita el proxy Socks5 para conectarse a Telegram. Ajuste su configuración según la guía."
|
"telegramProxyDesc" = "Si necesita el proxy Socks5 para conectarse a Telegram. Ajuste su configuración según la guía."
|
||||||
|
"telegramAPIServer" = "API Server de Telegram"
|
||||||
|
"telegramAPIServerDesc" = "El servidor API de Telegram a utilizar. Déjelo en blanco para utilizar el servidor predeterminado."
|
||||||
"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"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"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" = "Para buscar un correo electrónico de cliente:\r\n<code>/usage [Correo electrónico]</code>\r\n\r\nPara buscar entradas (con estadísticas de cliente):\r\n<code>/inbound [Observación]</code>\r\n\r\nID de Chat de Telegram:\r\n<code>/id</code>"
|
"helpAdminCommands" = "Para reiniciar Xray Core:\r\n<code>/restart force</code>\r\n\r\nPara buscar un correo electrónico de cliente:\r\n<code>/usage [Correo electrónico]</code>\r\n\r\nPara buscar entradas (con estadísticas de cliente):\r\n<code>/inbound [Observación]</code>\r\n\r\nID de Chat de Telegram:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "Para buscar estadísticas, utiliza el siguiente comando:\r\n<code>/usage [Correo electrónico]</code>\r\n\r\nID de Chat de Telegram:\r\n<code>/id</code>"
|
"helpClientCommands" = "Para buscar estadísticas, utiliza el siguiente comando:\r\n<code>/usage [Correo electrónico]</code>\r\n\r\nID de Chat de Telegram:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ ¡Operación exitosa!"
|
||||||
|
"restartFailed" = "❗ Error en la operación.\r\n\r\n<code>Error: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core no está en ejecución."
|
||||||
|
|
||||||
[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 }}%"
|
||||||
|
|||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "گزارش تاریخچه آیپی. برای فعال کردن ورودی پس از غیرفعال شدن، گزارش را پاک کنید"
|
"IPLimitlogDesc" = "گزارش تاریخچه آیپی. برای فعال کردن ورودی پس از غیرفعال شدن، گزارش را پاک کنید"
|
||||||
"IPLimitlogclear" = "پاک کردن گزارشها"
|
"IPLimitlogclear" = "پاک کردن گزارشها"
|
||||||
"setDefaultCert" = "استفاده از گواهی پنل"
|
"setDefaultCert" = "استفاده از گواهی پنل"
|
||||||
"xtlsDesc" = "ایکسری باید 1.7.5 باشد"
|
|
||||||
"realityDesc" = "ایکسری باید +1.8.0 باشد"
|
|
||||||
"telegramDesc" = "لطفا شناسه گفتگوی تلگرام را وارد کنید. (از دستور '/id' در ربات استفاده کنید) یا (@userinfobot)"
|
"telegramDesc" = "لطفا شناسه گفتگوی تلگرام را وارد کنید. (از دستور '/id' در ربات استفاده کنید) یا (@userinfobot)"
|
||||||
"subscriptionDesc" = "شما میتوانید لینک سابسکربپشن خودرا در 'جزئیات' پیدا کنید، همچنین میتوانید از همین نام برای چندین کاربر استفادهکنید"
|
"subscriptionDesc" = "شما میتوانید لینک سابسکربپشن خودرا در 'جزئیات' پیدا کنید، همچنین میتوانید از همین نام برای چندین کاربر استفادهکنید"
|
||||||
"info" = "اطلاعات"
|
"info" = "اطلاعات"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"telegramTokenDesc" = "دریافت کنید @botfather توکن را میتوانید از"
|
"telegramTokenDesc" = "دریافت کنید @botfather توکن را میتوانید از"
|
||||||
"telegramProxy" = "SOCKS پراکسی"
|
"telegramProxy" = "SOCKS پراکسی"
|
||||||
"telegramProxyDesc" = "را برای اتصال به تلگرام فعال می کند SOCKS5 پراکسی"
|
"telegramProxyDesc" = "را برای اتصال به تلگرام فعال می کند SOCKS5 پراکسی"
|
||||||
|
"telegramAPIServer" = "سرور API تلگرام"
|
||||||
|
"telegramAPIServerDesc" = "API سرور تلگرام برای اتصال را تغییر میدهد. برای استفاده از سرور پیش فرض خالی بگذارید"
|
||||||
"telegramChatId" = "آیدی چت مدیر"
|
"telegramChatId" = "آیدی چت مدیر"
|
||||||
"telegramChatIdDesc" = "دریافت کنید ('/id'یا (دستور (@userinfobot) آیدی(های) چت تلگرام مدیر، از"
|
"telegramChatIdDesc" = "دریافت کنید ('/id'یا (دستور (@userinfobot) آیدی(های) چت تلگرام مدیر، از"
|
||||||
"telegramNotifyTime" = "زمان نوتیفیکیشن"
|
"telegramNotifyTime" = "زمان نوتیفیکیشن"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"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>\r\n\r\nشناسه گفتگوی تلگرام:\r\n<code>/id</code>"
|
"helpAdminCommands" = "برای راهاندازی مجدد Xray Core:\r\n<code>/restart force</code>\r\n\r\nبرای جستجوی ایمیل مشتری:\r\n<code>/usage [ایمیل]</code>\r\n\r\nبرای جستجوی ورودیها (با آمار مشتری):\r\n<code>/inbound [توضیحات]</code>\r\n\r\nشناسه گفتگوی تلگرام:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "برای جستجوی آمار، از دستور زیر استفاده کنید:\r\n<code>/usage [ایمیل]</code>\r\n\r\nشناسه گفتگوی تلگرام:\r\n<code>/id</code>"
|
"helpClientCommands" = "برای جستجوی آمار، از دستور زیر استفاده کنید:\r\n<code>/usage [ایمیل]</code>\r\n\r\nشناسه گفتگوی تلگرام:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ عملیات با موفقیت انجام شد!"
|
||||||
|
"restartFailed" = "❗ خطا در عملیات.\r\n\r\n<code>خطا: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core در حال اجرا نیست."
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 بار پردازنده {{ .Percent }}% بیشتر از آستانه است {{ .Threshold }}%"
|
"cpuThreshold" = "🔴 بار پردازنده {{ .Percent }}% بیشتر از آستانه است {{ .Threshold }}%"
|
||||||
|
|||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "Log histori IP. (untuk mengaktifkan masuk setelah menonaktifkan, hapus log)"
|
"IPLimitlogDesc" = "Log histori IP. (untuk mengaktifkan masuk setelah menonaktifkan, hapus log)"
|
||||||
"IPLimitlogclear" = "Hapus Log"
|
"IPLimitlogclear" = "Hapus Log"
|
||||||
"setDefaultCert" = "Atur Sertifikat dari Panel"
|
"setDefaultCert" = "Atur Sertifikat dari Panel"
|
||||||
"xtlsDesc" = "Xray harus versi 1.7.5"
|
|
||||||
"realityDesc" = "Xray harus versi 1.8.0+"
|
|
||||||
"telegramDesc" = "Harap berikan ID Obrolan Telegram. (gunakan perintah '/id' di bot) atau (@userinfobot)"
|
"telegramDesc" = "Harap berikan ID Obrolan Telegram. (gunakan perintah '/id' di bot) atau (@userinfobot)"
|
||||||
"subscriptionDesc" = "Untuk menemukan URL langganan Anda, buka 'Rincian'. Selain itu, Anda dapat menggunakan nama yang sama untuk beberapa klien."
|
"subscriptionDesc" = "Untuk menemukan URL langganan Anda, buka 'Rincian'. Selain itu, Anda dapat menggunakan nama yang sama untuk beberapa klien."
|
||||||
"info" = "Info"
|
"info" = "Info"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"telegramTokenDesc" = "Token bot Telegram yang diperoleh dari '@BotFather'."
|
"telegramTokenDesc" = "Token bot Telegram yang diperoleh dari '@BotFather'."
|
||||||
"telegramProxy" = "Proxy SOCKS"
|
"telegramProxy" = "Proxy SOCKS"
|
||||||
"telegramProxyDesc" = "Mengaktifkan proxy SOCKS5 untuk terhubung ke Telegram. (sesuaikan pengaturan sesuai panduan)"
|
"telegramProxyDesc" = "Mengaktifkan proxy SOCKS5 untuk terhubung ke Telegram. (sesuaikan pengaturan sesuai panduan)"
|
||||||
|
"telegramAPIServer" = "Telegram API Server"
|
||||||
|
"telegramAPIServerDesc" = "Server API Telegram yang akan digunakan. Biarkan kosong untuk menggunakan server default."
|
||||||
"telegramChatId" = "ID Obrolan Admin"
|
"telegramChatId" = "ID Obrolan Admin"
|
||||||
"telegramChatIdDesc" = "ID Obrolan Admin Telegram. (dipisahkan koma)(dapatkan di sini @userinfobot) atau (gunakan perintah '/id' di bot)"
|
"telegramChatIdDesc" = "ID Obrolan Admin Telegram. (dipisahkan koma)(dapatkan di sini @userinfobot) atau (gunakan perintah '/id' di bot)"
|
||||||
"telegramNotifyTime" = "Waktu Notifikasi"
|
"telegramNotifyTime" = "Waktu Notifikasi"
|
||||||
@@ -482,9 +482,13 @@
|
|||||||
"welcome" = "🤖 Selamat datang di <b>{{.Hostname }}</b> bot managemen.\r\n"
|
"welcome" = "🤖 Selamat datang di <b>{{.Hostname }}</b> bot managemen.\r\n"
|
||||||
"status" = "✅ Bot dalam keadaan baik!"
|
"status" = "✅ Bot dalam keadaan baik!"
|
||||||
"usage" = "❗ Harap berikan teks untuk mencari!"
|
"usage" = "❗ Harap berikan teks untuk mencari!"
|
||||||
"getID" = "🆔 ID Anda:<code>{{.ID }}</code>"
|
"getID" = "🆔 ID Anda: <code>{{ .ID }}</code>"
|
||||||
"helpAdminCommands" = "Untuk mencari email klien:\r\n<code>/usage [Email]</code>\r\n\r\nUntuk mencari inbound (dengan statistik klien):\r\n<code>/inbound [Catatan]</code>\r\n\r\nID Obrolan Telegram:\r\n<code>/id</code>"
|
"helpAdminCommands" = "Untuk memulai ulang Xray Core:\r\n<code>/restart force</code>\r\n\r\nUntuk mencari email klien:\r\n<code>/usage [Email]</code>\r\n\r\nUntuk mencari inbound (dengan statistik klien):\r\n<code>/inbound [Catatan]</code>\r\n\r\nID Obrolan Telegram:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "Untuk mencari statistik, gunakan perintah berikut:\r\n<code>/usage [Email]</code>\r\n\r\nID Obrolan Telegram:\r\n<code>/id</code>"
|
"helpClientCommands" = "Untuk mencari statistik, gunakan perintah berikut:\r\n<code>/usage [Email]</code>\r\n\r\nID Obrolan Telegram:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ Operasi berhasil!"
|
||||||
|
"restartFailed" = "❗ Kesalahan dalam operasi.\r\n\r\n<code>Error: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core tidak berjalan."
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 Beban CPU {{ .Percent }}% melebihi batas {{ .Threshold }}%"
|
"cpuThreshold" = "🔴 Beban CPU {{ .Percent }}% melebihi batas {{ .Threshold }}%"
|
||||||
|
|||||||
595
web/translation/translate.ja_JP.toml
Normal file
595
web/translation/translate.ja_JP.toml
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
"username" = "ユーザー名"
|
||||||
|
"password" = "パスワード"
|
||||||
|
"login" = "ログイン"
|
||||||
|
"confirm" = "確認"
|
||||||
|
"cancel" = "キャンセル"
|
||||||
|
"close" = "閉じる"
|
||||||
|
"copy" = "コピー"
|
||||||
|
"copied" = "コピー済み"
|
||||||
|
"download" = "ダウンロード"
|
||||||
|
"remark" = "備考"
|
||||||
|
"enable" = "有効化"
|
||||||
|
"protocol" = "プロトコル"
|
||||||
|
"search" = "検索"
|
||||||
|
"filter" = "フィルター"
|
||||||
|
"loading" = "読み込み中..."
|
||||||
|
"second" = "秒"
|
||||||
|
"minute" = "分"
|
||||||
|
"hour" = "時間"
|
||||||
|
"day" = "日"
|
||||||
|
"check" = "確認"
|
||||||
|
"indefinite" = "無期限"
|
||||||
|
"unlimited" = "無制限"
|
||||||
|
"none" = "なし"
|
||||||
|
"qrCode" = "QRコード"
|
||||||
|
"info" = "詳細情報"
|
||||||
|
"edit" = "編集"
|
||||||
|
"delete" = "削除"
|
||||||
|
"reset" = "リセット"
|
||||||
|
"copySuccess" = "コピー成功"
|
||||||
|
"sure" = "確定"
|
||||||
|
"encryption" = "暗号化"
|
||||||
|
"transmission" = "伝送"
|
||||||
|
"host" = "ホスト"
|
||||||
|
"path" = "パス"
|
||||||
|
"camouflage" = "偽装"
|
||||||
|
"status" = "ステータス"
|
||||||
|
"enabled" = "有効"
|
||||||
|
"disabled" = "無効"
|
||||||
|
"depleted" = "消耗済み"
|
||||||
|
"depletingSoon" = "間もなく消耗"
|
||||||
|
"offline" = "オフライン"
|
||||||
|
"online" = "オンライン"
|
||||||
|
"domainName" = "ドメイン名"
|
||||||
|
"monitor" = "監視"
|
||||||
|
"certificate" = "証明書"
|
||||||
|
"fail" = "失敗"
|
||||||
|
"success" = "成功"
|
||||||
|
"getVersion" = "バージョン取得"
|
||||||
|
"install" = "インストール"
|
||||||
|
"clients" = "クライアント"
|
||||||
|
"usage" = "利用状況"
|
||||||
|
"secretToken" = "シークレットトークン"
|
||||||
|
"remained" = "残り"
|
||||||
|
"security" = "セキュリティ"
|
||||||
|
"secAlertTitle" = "セキュリティアラート"
|
||||||
|
"secAlertSsl" = "この接続は安全ではありません。TLSを有効にしてデータ保護を行うまで、機密情報を入力しないでください。"
|
||||||
|
"secAlertConf" = "一部の設定は脆弱です。潜在的な脆弱性を防ぐために、セキュリティプロトコルを強化することをお勧めします。"
|
||||||
|
"secAlertSSL" = "セキュアな接続がありません。データ保護のためにTLS証明書をインストールしてください。"
|
||||||
|
"secAlertPanelPort" = "デフォルトのポートにはセキュリティリスクがあります。ランダムなポートまたは特定のポートを設定してください。"
|
||||||
|
"secAlertPanelURI" = "デフォルトのURIパスは安全ではありません。複雑なURIパスを設定してください。"
|
||||||
|
"secAlertSubURI" = "サブスクリプションのデフォルトURIパスは安全ではありません。複雑なURIパスを設定してください。"
|
||||||
|
"secAlertSubJsonURI" = "JSONサブスクリプションのデフォルトURIパスは安全ではありません。複雑なURIパスを設定してください。"
|
||||||
|
|
||||||
|
[menu]
|
||||||
|
"dashboard" = "ダッシュボード"
|
||||||
|
"inbounds" = "インバウンド一覧"
|
||||||
|
"settings" = "パネル設定"
|
||||||
|
"xray" = "Xray設定"
|
||||||
|
"logout" = "ログアウト"
|
||||||
|
"link" = "リンク管理"
|
||||||
|
|
||||||
|
[pages.login]
|
||||||
|
"hello" = "こんにちは"
|
||||||
|
"title" = "ようこそ"
|
||||||
|
"loginAgain" = "ログインセッションが切れました。再度ログインしてください。"
|
||||||
|
|
||||||
|
[pages.login.toasts]
|
||||||
|
"invalidFormData" = "データ形式エラー"
|
||||||
|
"emptyUsername" = "ユーザー名を入力してください"
|
||||||
|
"emptyPassword" = "パスワードを入力してください"
|
||||||
|
"wrongUsernameOrPassword" = "ユーザー名またはパスワードが間違っています"
|
||||||
|
"successLogin" = "ログイン成功"
|
||||||
|
|
||||||
|
[pages.index]
|
||||||
|
"title" = "システムステータス"
|
||||||
|
"memory" = "メモリ"
|
||||||
|
"hard" = "ハードディスク"
|
||||||
|
"xrayStatus" = "Xray"
|
||||||
|
"stopXray" = "停止"
|
||||||
|
"restartXray" = "再起動"
|
||||||
|
"xraySwitch" = "バージョン"
|
||||||
|
"xraySwitchClick" = "切り替えるバージョンを選択してください"
|
||||||
|
"xraySwitchClickDesk" = "慎重に選択してください。古いバージョンは現在の設定と互換性がない可能性があります。"
|
||||||
|
"operationHours" = "システム稼働時間"
|
||||||
|
"systemLoad" = "システム負荷"
|
||||||
|
"systemLoadDesc" = "過去1、5、15分間のシステム平均負荷"
|
||||||
|
"connectionTcpCountDesc" = "システム内のすべてのTCP接続数"
|
||||||
|
"connectionUdpCountDesc" = "システム内のすべてのUDP接続数"
|
||||||
|
"connectionCount" = "接続数"
|
||||||
|
"upSpeed" = "総アップロード速度"
|
||||||
|
"downSpeed" = "総ダウンロード速度"
|
||||||
|
"totalSent" = "システム起動以降の送信データ量"
|
||||||
|
"totalReceive" = "システム起動以降の受信データ量"
|
||||||
|
"xraySwitchVersionDialog" = "Xrayバージョン切り替え"
|
||||||
|
"xraySwitchVersionDialogDesc" = "Xrayのバージョンを切り替えますか?"
|
||||||
|
"dontRefresh" = "インストール中、このページをリロードしないでください"
|
||||||
|
"logs" = "ログ"
|
||||||
|
"config" = "設定"
|
||||||
|
"backup" = "バックアップと復元"
|
||||||
|
"backupTitle" = "データベースのバックアップと復元"
|
||||||
|
"backupDescription" = "データベースを復元する前にバックアップすることをお勧めします"
|
||||||
|
"exportDatabase" = "バックアップ"
|
||||||
|
"importDatabase" = "復元"
|
||||||
|
|
||||||
|
[pages.inbounds]
|
||||||
|
"title" = "インバウンド一覧"
|
||||||
|
"totalDownUp" = "総アップロード / ダウンロード"
|
||||||
|
"totalUsage" = "総使用量"
|
||||||
|
"inboundCount" = "インバウンド数"
|
||||||
|
"operate" = "メニュー"
|
||||||
|
"enable" = "有効化"
|
||||||
|
"remark" = "備考"
|
||||||
|
"protocol" = "プロトコル"
|
||||||
|
"port" = "ポート"
|
||||||
|
"traffic" = "トラフィック"
|
||||||
|
"details" = "詳細情報"
|
||||||
|
"transportConfig" = "トランスポート設定"
|
||||||
|
"expireDate" = "有効期限"
|
||||||
|
"resetTraffic" = "トラフィックリセット"
|
||||||
|
"addInbound" = "インバウンド追加"
|
||||||
|
"generalActions" = "一般操作"
|
||||||
|
"create" = "追加"
|
||||||
|
"update" = "更新"
|
||||||
|
"modifyInbound" = "インバウンド修正"
|
||||||
|
"deleteInbound" = "インバウンド削除"
|
||||||
|
"deleteInboundContent" = "インバウンドを削除してもよろしいですか?"
|
||||||
|
"deleteClient" = "クライアント削除"
|
||||||
|
"deleteClientContent" = "クライアントを削除してもよろしいですか?"
|
||||||
|
"resetTrafficContent" = "トラフィックをリセットしてもよろしいですか?"
|
||||||
|
"copyLink" = "リンクをコピー"
|
||||||
|
"address" = "アドレス"
|
||||||
|
"network" = "ネットワーク"
|
||||||
|
"destinationPort" = "宛先ポート"
|
||||||
|
"targetAddress" = "宛先アドレス"
|
||||||
|
"monitorDesc" = "空白にするとすべてのIPを監視"
|
||||||
|
"meansNoLimit" = "= 無制限(単位:GB)"
|
||||||
|
"totalFlow" = "総トラフィック"
|
||||||
|
"leaveBlankToNeverExpire" = "空白にすると期限なし"
|
||||||
|
"noRecommendKeepDefault" = "デフォルト値を保持することをお勧めします"
|
||||||
|
"certificatePath" = "ファイルパス"
|
||||||
|
"certificateContent" = "ファイル内容"
|
||||||
|
"publicKey" = "公開鍵"
|
||||||
|
"privatekey" = "秘密鍵"
|
||||||
|
"clickOnQRcode" = "QRコードをクリックしてコピー"
|
||||||
|
"client" = "クライアント"
|
||||||
|
"export" = "リンクエクスポート"
|
||||||
|
"clone" = "複製"
|
||||||
|
"cloneInbound" = "複製"
|
||||||
|
"cloneInboundContent" = "このインバウンドルールは、ポート(Port)、リスニングIP(Listening IP)、クライアント(Clients)を除くすべての設定がクローンされます"
|
||||||
|
"cloneInboundOk" = "クローン作成"
|
||||||
|
"resetAllTraffic" = "すべてのインバウンドトラフィックをリセット"
|
||||||
|
"resetAllTrafficTitle" = "すべてのインバウンドトラフィックをリセット"
|
||||||
|
"resetAllTrafficContent" = "すべてのインバウンドトラフィックをリセットしてもよろしいですか?"
|
||||||
|
"resetInboundClientTraffics" = "クライアントトラフィックをリセット"
|
||||||
|
"resetInboundClientTrafficTitle" = "すべてのクライアントトラフィックをリセット"
|
||||||
|
"resetInboundClientTrafficContent" = "このインバウンドクライアントのすべてのトラフィックをリセットしてもよろしいですか?"
|
||||||
|
"resetAllClientTraffics" = "すべてのクライアントトラフィックをリセット"
|
||||||
|
"resetAllClientTrafficTitle" = "すべてのクライアントトラフィックをリセット"
|
||||||
|
"resetAllClientTrafficContent" = "すべてのクライアントのトラフィックをリセットしてもよろしいですか?"
|
||||||
|
"delDepletedClients" = "トラフィックが尽きたクライアントを削除"
|
||||||
|
"delDepletedClientsTitle" = "トラフィックが尽きたクライアントを削除"
|
||||||
|
"delDepletedClientsContent" = "トラフィックが尽きたすべてのクライアントを削除してもよろしいですか?"
|
||||||
|
"email" = "メールアドレス"
|
||||||
|
"emailDesc" = "メールアドレスは一意でなければなりません"
|
||||||
|
"IPLimit" = "IP制限"
|
||||||
|
"IPLimitDesc" = "設定値を超えるとインバウンドトラフィックが無効になります。(0 = 無効)"
|
||||||
|
"IPLimitlog" = "IPログ"
|
||||||
|
"IPLimitlogDesc" = "IP履歴ログ(無効なインバウンドトラフィックを有効にするには、ログをクリアしてください)"
|
||||||
|
"IPLimitlogclear" = "ログをクリア"
|
||||||
|
"setDefaultCert" = "パネル設定から証明書を設定"
|
||||||
|
"telegramDesc" = "TelegramチャットIDを提供してください。(ボットで'/id'コマンドを使用)または(@userinfobot)"
|
||||||
|
"subscriptionDesc" = "サブスクリプションURLを見つけるには、“詳細情報”に移動してください。また、複数のクライアントに同じ名前を使用することができます。"
|
||||||
|
"info" = "情報"
|
||||||
|
"same" = "同じ"
|
||||||
|
"inboundData" = "インバウンドデータ"
|
||||||
|
"exportInbound" = "インバウンドルールをエクスポート"
|
||||||
|
"import" = "インポート"
|
||||||
|
"importInbound" = "インバウンドルールをインポート"
|
||||||
|
|
||||||
|
[pages.client]
|
||||||
|
"add" = "クライアント追加"
|
||||||
|
"edit" = "クライアント編集"
|
||||||
|
"submitAdd" = "クライアント追加"
|
||||||
|
"submitEdit" = "変更を保存"
|
||||||
|
"clientCount" = "クライアント数"
|
||||||
|
"bulk" = "一括作成"
|
||||||
|
"method" = "方法"
|
||||||
|
"first" = "最初"
|
||||||
|
"last" = "最後"
|
||||||
|
"prefix" = "プレフィックス"
|
||||||
|
"postfix" = "サフィックス"
|
||||||
|
"delayedStart" = "初回使用後に開始"
|
||||||
|
"expireDays" = "期間"
|
||||||
|
"days" = "日"
|
||||||
|
"renew" = "自動更新"
|
||||||
|
"renewDesc" = "期限が切れた後に自動更新。(0 = 無効)(単位:日)"
|
||||||
|
|
||||||
|
[pages.inbounds.toasts]
|
||||||
|
"obtain" = "取得"
|
||||||
|
|
||||||
|
[pages.inbounds.stream.general]
|
||||||
|
"request" = "リクエスト"
|
||||||
|
"response" = "レスポンス"
|
||||||
|
"name" = "名前"
|
||||||
|
"value" = "値"
|
||||||
|
|
||||||
|
[pages.inbounds.stream.tcp]
|
||||||
|
"version" = "バージョン"
|
||||||
|
"method" = "方法"
|
||||||
|
"path" = "パス"
|
||||||
|
"status" = "ステータス"
|
||||||
|
"statusDescription" = "ステータス説明"
|
||||||
|
"requestHeader" = "リクエストヘッダー"
|
||||||
|
"responseHeader" = "レスポンスヘッダー"
|
||||||
|
|
||||||
|
[pages.settings]
|
||||||
|
"title" = "パネル設定"
|
||||||
|
"save" = "保存"
|
||||||
|
"infoDesc" = "ここでのすべての変更は、保存してパネルを再起動する必要があります"
|
||||||
|
"restartPanel" = "パネル再起動"
|
||||||
|
"restartPanelDesc" = "パネルを再起動してもよろしいですか?再起動後にパネルにアクセスできない場合は、サーバーでパネルログを確認してください"
|
||||||
|
"actions" = "操作"
|
||||||
|
"resetDefaultConfig" = "デフォルト設定にリセット"
|
||||||
|
"panelSettings" = "一般"
|
||||||
|
"securitySettings" = "セキュリティ設定"
|
||||||
|
"TGBotSettings" = "Telegramボット設定"
|
||||||
|
"panelListeningIP" = "パネル監視IP"
|
||||||
|
"panelListeningIPDesc" = "デフォルトではすべてのIPを監視する"
|
||||||
|
"panelListeningDomain" = "パネル監視ドメイン"
|
||||||
|
"panelListeningDomainDesc" = "デフォルトで空白の場合、すべてのドメインとIPアドレスを監視する"
|
||||||
|
"panelPort" = "パネル監視ポート"
|
||||||
|
"panelPortDesc" = "再起動で有効"
|
||||||
|
"publicKeyPath" = "パネル証明書公開鍵ファイルパス"
|
||||||
|
"publicKeyPathDesc" = "'/'で始まる絶対パスを入力"
|
||||||
|
"privateKeyPath" = "パネル証明書秘密鍵ファイルパス"
|
||||||
|
"privateKeyPathDesc" = "'/'で始まる絶対パスを入力"
|
||||||
|
"panelUrlPath" = "パネルURLルートパス"
|
||||||
|
"panelUrlPathDesc" = "'/'で始まり、'/'で終わる必要があります"
|
||||||
|
"pageSize" = "ページサイズ"
|
||||||
|
"pageSizeDesc" = "インバウンドテーブルのページサイズを定義します。0を設定すると無効化されます"
|
||||||
|
"remarkModel" = "備考モデルと区切り記号"
|
||||||
|
"datepicker" = "日付ピッカー"
|
||||||
|
"datepickerPlaceholder" = "日付を選択"
|
||||||
|
"datepickerDescription" = "日付選択カレンダーで有効期限を指定する"
|
||||||
|
"sampleRemark" = "備考の例"
|
||||||
|
"oldUsername" = "旧ユーザー名"
|
||||||
|
"currentPassword" = "旧パスワード"
|
||||||
|
"newUsername" = "新しいユーザー名"
|
||||||
|
"newPassword" = "新しいパスワード"
|
||||||
|
"telegramBotEnable" = "Telegramボットを有効にする"
|
||||||
|
"telegramBotEnableDesc" = "Telegramボット機能を有効にする"
|
||||||
|
"telegramToken" = "Telegramボットトークン"
|
||||||
|
"telegramTokenDesc" = "'@BotFather'から取得したTelegramボットトークン"
|
||||||
|
"telegramProxy" = "SOCKS5プロキシ"
|
||||||
|
"telegramProxyDesc" = "SOCKS5プロキシを有効にしてTelegramに接続する(ガイドに従って設定を調整)"
|
||||||
|
"telegramAPIServer" = "Telegram APIサーバー"
|
||||||
|
"telegramAPIServerDesc" = "使用するTelegram APIサーバー。空白の場合はデフォルトサーバーを使用する"
|
||||||
|
"telegramChatId" = "管理者チャットID"
|
||||||
|
"telegramChatIdDesc" = "Telegram管理者チャットID(複数の場合はカンマで区切る)@userinfobotで取得するか、ボットで'/id'コマンドを使用して取得する"
|
||||||
|
"telegramNotifyTime" = "通知時間"
|
||||||
|
"telegramNotifyTimeDesc" = "定期的なTelegramボット通知時間を設定する(crontab時間形式を使用)"
|
||||||
|
"tgNotifyBackup" = "データベースバックアップ"
|
||||||
|
"tgNotifyBackupDesc" = "レポート付きのデータベースバックアップファイルを送信"
|
||||||
|
"tgNotifyLogin" = "ログイン通知"
|
||||||
|
"tgNotifyLoginDesc" = "誰かがパネルにログインしようとしたときに、ユーザー名、IPアドレス、時間を表示する"
|
||||||
|
"sessionMaxAge" = "セッション期間"
|
||||||
|
"sessionMaxAgeDesc" = "ログイン状態を保持する期間(単位:分)"
|
||||||
|
"expireTimeDiff" = "有効期限通知のしきい値"
|
||||||
|
"expireTimeDiffDesc" = "このしきい値に達した場合、有効期限に関する通知を受け取る(単位:日)"
|
||||||
|
"trafficDiff" = "トラフィック消耗しきい値"
|
||||||
|
"trafficDiffDesc" = "このしきい値に達した場合、トラフィック消耗に関する通知を受け取る(単位:GB)"
|
||||||
|
"tgNotifyCpu" = "CPU負荷通知しきい値"
|
||||||
|
"tgNotifyCpuDesc" = "CPU負荷がこのしきい値を超えた場合、通知を受け取る(単位:%)"
|
||||||
|
"timeZone" = "タイムゾーン"
|
||||||
|
"timeZoneDesc" = "定時タスクはこのタイムゾーンの時間に従って実行される"
|
||||||
|
"subSettings" = "サブスクリプション設定"
|
||||||
|
"subEnable" = "サブスクリプションサービスを有効にする"
|
||||||
|
"subEnableDesc" = "サブスクリプションサービス機能を有効にする"
|
||||||
|
"subListen" = "監視IP"
|
||||||
|
"subListenDesc" = "サブスクリプションサービスが監視するIPアドレス(空白にするとすべてのIPを監視)"
|
||||||
|
"subPort" = "監視ポート"
|
||||||
|
"subPortDesc" = "サブスクリプションサービスが監視するポート番号(使用されていないポートである必要があります)"
|
||||||
|
"subCertPath" = "公開鍵パス"
|
||||||
|
"subCertPathDesc" = "サブスクリプションサービスで使用する公開鍵ファイルのパス('/'で始まる)"
|
||||||
|
"subKeyPath" = "秘密鍵パス"
|
||||||
|
"subKeyPathDesc" = "サブスクリプションサービスで使用する秘密鍵ファイルのパス('/'で始まる)"
|
||||||
|
"subPath" = "URIパス"
|
||||||
|
"subPathDesc" = "サブスクリプションサービスで使用するURIパス('/'で始まり、'/'で終わる)"
|
||||||
|
"subDomain" = "監視ドメイン"
|
||||||
|
"subDomainDesc" = "サブスクリプションサービスが監視するドメイン(空白にするとすべてのドメインとIPを監視)"
|
||||||
|
"subUpdates" = "更新間隔"
|
||||||
|
"subUpdatesDesc" = "クライアントアプリケーションでサブスクリプションURLの更新間隔(単位:時間)"
|
||||||
|
"subEncrypt" = "エンコード"
|
||||||
|
"subEncryptDesc" = "サブスクリプションサービスが返す内容をBase64エンコードする"
|
||||||
|
"subShowInfo" = "利用情報を表示"
|
||||||
|
"subShowInfoDesc" = "クライアントアプリで残りのトラフィックと日付情報を表示する"
|
||||||
|
"subURI" = "リバースプロキシURI"
|
||||||
|
"subURIDesc" = "プロキシ後ろのサブスクリプションURLのURIパスに使用する"
|
||||||
|
"fragment" = "フラグメント"
|
||||||
|
"fragmentDesc" = "TLS helloパケットのフラグメントを有効にする"
|
||||||
|
"fragmentSett" = "設定"
|
||||||
|
"noisesDesc" = "Noisesを有効にする"
|
||||||
|
"noisesSett" = "Noises設定"
|
||||||
|
"mux" = "マルチプレクサ"
|
||||||
|
"muxDesc" = "確立されたストリーム内で複数の独立したストリームを伝送する"
|
||||||
|
"muxSett" = "マルチプレクサ設定"
|
||||||
|
"direct" = "直接接続"
|
||||||
|
"directDesc" = "特定の国のドメインまたはIP範囲に直接接続する"
|
||||||
|
|
||||||
|
|
||||||
|
[pages.xray]
|
||||||
|
"title" = "Xray 設定"
|
||||||
|
"save" = "保存"
|
||||||
|
"restart" = "Xray 再起動"
|
||||||
|
"basicTemplate" = "基本設定"
|
||||||
|
"advancedTemplate" = "高度な設定"
|
||||||
|
"generalConfigs" = "一般設定"
|
||||||
|
"generalConfigsDesc" = "これらのオプションは一般設定を決定します"
|
||||||
|
"logConfigs" = "ログ"
|
||||||
|
"logConfigsDesc" = "ログはサーバーのパフォーマンスに影響を与える可能性があるため、必要な場合にのみ有効にすることをお勧めします"
|
||||||
|
"blockConfigs" = "防御フィルター"
|
||||||
|
"blockConfigsDesc" = "これらのオプションは、特定のプロトコルやウェブサイトへのユーザー接続をブロックします"
|
||||||
|
"basicRouting" = "基本ルーティング"
|
||||||
|
"blockConnectionsConfigsDesc" = "これらのオプションにより、特定のリクエスト元の国に基づいてトラフィックをブロックします。"
|
||||||
|
"directConnectionsConfigsDesc" = "直接接続により、特定のトラフィックが他のサーバーを経由しないようにします。"
|
||||||
|
"blockips" = "IPをブロック"
|
||||||
|
"blockdomains" = "ドメインをブロック"
|
||||||
|
"directips" = "直接IP"
|
||||||
|
"directdomains" = "直接ドメイン"
|
||||||
|
"ipv4Routing" = "IPv4 ルーティング"
|
||||||
|
"ipv4RoutingDesc" = "このオプションはIPv4のみを介してターゲットドメインへルーティングします"
|
||||||
|
"warpRouting" = "WARP ルーティング"
|
||||||
|
"warpRoutingDesc" = "注意:これらのオプションを使用する前に、パネルのGitHubの手順に従って、サーバーにsocks5プロキシモードでWARPをインストールしてください。WARPはCloudflareサーバー経由でトラフィックをウェブサイトにルーティングします。"
|
||||||
|
"Template" = "高度なXray設定テンプレート"
|
||||||
|
"TemplateDesc" = "最終的なXray設定ファイルはこのテンプレートに基づいて生成されます"
|
||||||
|
"FreedomStrategy" = "Freedom プロトコル戦略"
|
||||||
|
"FreedomStrategyDesc" = "Freedomプロトコル内のネットワークの出力戦略を設定する"
|
||||||
|
"RoutingStrategy" = "ルーティングドメイン戦略設定"
|
||||||
|
"RoutingStrategyDesc" = "DNS解決の全体的なルーティング戦略を設定する"
|
||||||
|
"Torrent" = "BitTorrent プロトコルをブロック"
|
||||||
|
"TorrentDesc" = "BitTorrentの使用を禁止する"
|
||||||
|
"Family" = "ファミリー保護"
|
||||||
|
"FamilyDesc" = "アダルトコンテンツや悪意のあるサイトをブロックする"
|
||||||
|
"Inbounds" = "インバウンドルール"
|
||||||
|
"InboundsDesc" = "特定のクライアントからのトラフィックを受け入れる"
|
||||||
|
"Outbounds" = "アウトバウンドルール"
|
||||||
|
"Balancers" = "負荷分散"
|
||||||
|
"OutboundsDesc" = "アウトバウンドトラフィックの送信方法を設定する"
|
||||||
|
"Routings" = "ルーティングルール"
|
||||||
|
"RoutingsDesc" = "各ルールの優先順位が重要です"
|
||||||
|
"completeTemplate" = "すべて"
|
||||||
|
"logLevel" = "ログレベル"
|
||||||
|
"logLevelDesc" = "エラーログのレベルを指定し、記録する情報を示します"
|
||||||
|
"accessLog" = "アクセスログ"
|
||||||
|
"accessLogDesc" = "アクセスログのファイルパス。特殊値 'none' はアクセスログを無効にします"
|
||||||
|
"errorLog" = "エラーログ"
|
||||||
|
"errorLogDesc" = "エラーログのファイルパス。特殊値 'none' はエラーログを無効にします"
|
||||||
|
"dnsLog" = "DNS ログ"
|
||||||
|
"dnsLogDesc" = "DNSクエリのログを有効にするかどうか"
|
||||||
|
"maskAddress" = "アドレスをマスク"
|
||||||
|
"maskAddressDesc" = "IPアドレスをマスクし、有効にするとログに表示されるIPアドレスを自動的に置き換えます"
|
||||||
|
|
||||||
|
[pages.xray.rules]
|
||||||
|
"first" = "最初"
|
||||||
|
"last" = "最後"
|
||||||
|
"up" = "上へ"
|
||||||
|
"down" = "下へ"
|
||||||
|
"source" = "ソース"
|
||||||
|
"dest" = "宛先アドレス"
|
||||||
|
"inbound" = "インバウンド"
|
||||||
|
"outbound" = "アウトバウンド"
|
||||||
|
"balancer" = "負荷分散"
|
||||||
|
"info" = "情報"
|
||||||
|
"add" = "ルール追加"
|
||||||
|
"edit" = "ルール編集"
|
||||||
|
"useComma" = "カンマ区切りの項目"
|
||||||
|
|
||||||
|
[pages.xray.outbound]
|
||||||
|
"addOutbound" = "アウトバウンド追加"
|
||||||
|
"addReverse" = "リバース追加"
|
||||||
|
"editOutbound" = "アウトバウンド編集"
|
||||||
|
"editReverse" = "リバース編集"
|
||||||
|
"tag" = "タグ"
|
||||||
|
"tagDesc" = "一意のタグ"
|
||||||
|
"address" = "アドレス"
|
||||||
|
"reverse" = "リバース"
|
||||||
|
"domain" = "ドメイン"
|
||||||
|
"type" = "タイプ"
|
||||||
|
"bridge" = "ブリッジ"
|
||||||
|
"portal" = "ポータル"
|
||||||
|
"intercon" = "インターコネクション"
|
||||||
|
"settings" = "設定"
|
||||||
|
"accountInfo" = "アカウント情報"
|
||||||
|
"outboundStatus" = "アウトバウンドステータス"
|
||||||
|
"sendThrough" = "送信経路"
|
||||||
|
|
||||||
|
[pages.xray.balancer]
|
||||||
|
"addBalancer" = "負荷分散追加"
|
||||||
|
"editBalancer" = "負荷分散編集"
|
||||||
|
"balancerStrategy" = "戦略"
|
||||||
|
"balancerSelectors" = "セレクター"
|
||||||
|
"tag" = "タグ"
|
||||||
|
"tagDesc" = "一意のタグ"
|
||||||
|
"balancerDesc" = "balancerTagとoutboundTagは同時に使用できません。同時に使用された場合、outboundTagのみが有効になります。"
|
||||||
|
|
||||||
|
[pages.xray.wireguard]
|
||||||
|
"secretKey" = "シークレットキー"
|
||||||
|
"publicKey" = "公開鍵"
|
||||||
|
"allowedIPs" = "許可されたIP"
|
||||||
|
"endpoint" = "エンドポイント"
|
||||||
|
"psk" = "共有キー"
|
||||||
|
"domainStrategy" = "ドメイン戦略"
|
||||||
|
|
||||||
|
[pages.xray.dns]
|
||||||
|
"enable" = "DNSを有効にする"
|
||||||
|
"enableDesc" = "組み込みDNSサーバーを有効にする"
|
||||||
|
"tag" = "DNSインバウンドタグ"
|
||||||
|
"tagDesc" = "このタグはルーティングルールでインバウンドタグとして使用できます"
|
||||||
|
"strategy" = "クエリ戦略"
|
||||||
|
"strategyDesc" = "ドメイン名解決の全体的な戦略"
|
||||||
|
"add" = "サーバー追加"
|
||||||
|
"edit" = "サーバー編集"
|
||||||
|
"domains" = "ドメイン"
|
||||||
|
"expectIPs" = "期待されるIP"
|
||||||
|
|
||||||
|
[pages.xray.fakedns]
|
||||||
|
"add" = "フェイクDNS追加"
|
||||||
|
"edit" = "フェイクDNS編集"
|
||||||
|
"ipPool" = "IPプールサブネット"
|
||||||
|
"poolSize" = "プールサイズ"
|
||||||
|
|
||||||
|
[pages.settings.security]
|
||||||
|
"admin" = "管理者"
|
||||||
|
"secret" = "セキュリティトークン"
|
||||||
|
"loginSecurity" = "ログインセキュリティ"
|
||||||
|
"loginSecurityDesc" = "追加の認証を追加してセキュリティを向上させる"
|
||||||
|
"secretToken" = "セキュリティトークン"
|
||||||
|
"secretTokenDesc" = "このトークンを安全な場所に保管してください。このトークンはログインに使用され、紛失すると回復できません。"
|
||||||
|
|
||||||
|
[pages.settings.toasts]
|
||||||
|
"modifySettings" = "設定を変更"
|
||||||
|
"getSettings" = "設定を取得"
|
||||||
|
"modifyUser" = "管理者を変更"
|
||||||
|
"originalUserPassIncorrect" = "旧ユーザー名または旧パスワードが間違っています"
|
||||||
|
"userPassMustBeNotEmpty" = "新しいユーザー名と新しいパスワードは空にできません"
|
||||||
|
|
||||||
|
[tgbot]
|
||||||
|
"keyboardClosed" = "❌ カスタムキーボードが閉じられました!"
|
||||||
|
"noResult" = "❗ 結果がありません!"
|
||||||
|
"noQuery" = "❌ クエリが見つかりませんでした!もう一度コマンドを使用してください!"
|
||||||
|
"wentWrong" = "❌ 問題が発生しました!"
|
||||||
|
"noIpRecord" = "❗ IP記録がありません!"
|
||||||
|
"noInbounds" = "❗ インバウンド接続が見つかりません!"
|
||||||
|
"unlimited" = "♾ 無制限"
|
||||||
|
"add" = "追加"
|
||||||
|
"month" = "月"
|
||||||
|
"months" = "月"
|
||||||
|
"day" = "日"
|
||||||
|
"days" = "日"
|
||||||
|
"hours" = "時間"
|
||||||
|
"unknown" = "不明"
|
||||||
|
"inbounds" = "インバウンド接続"
|
||||||
|
"clients" = "クライアント"
|
||||||
|
"offline" = "🔴 オフライン"
|
||||||
|
"online" = "🟢 オンライン"
|
||||||
|
|
||||||
|
[tgbot.commands]
|
||||||
|
"unknown" = "❗ 不明なコマンド"
|
||||||
|
"pleaseChoose" = "👇 選択してください:\r\n"
|
||||||
|
"help" = "🤖 このボットをご利用いただきありがとうございます!サーバーから特定のデータを提供し、必要な変更を行うことができます。\r\n\r\n"
|
||||||
|
"start" = "👋 こんにちは、<i>{{ .Firstname }}</i>。\r\n"
|
||||||
|
"welcome" = "🤖 <b>{{ .Hostname }}</b> 管理ボットへようこそ。\r\n"
|
||||||
|
"status" = "✅ ボットは正常に動作しています!"
|
||||||
|
"usage" = "❗ 検索するテキストを入力してください!"
|
||||||
|
"getID" = "🆔 あなたのIDは:<code>{{ .ID }}</code>"
|
||||||
|
"helpAdminCommands" = "Xray Coreを再起動するには:\r\n<code>/restart force</code>\r\n\r\nクライアントの電子メールを検索するには:\r\n<code>/usage [電子メール]</code>\r\n\r\nインバウンド(クライアントの統計情報を含む)を検索するには:\r\n<code>/inbound [備考]</code>\r\n\r\nTelegramチャットID:\r\n<code>/id</code>"
|
||||||
|
"helpClientCommands" = "統計情報を検索するには、次のコマンドを使用してください:\r\n<code>/usage [電子メール]</code>\r\n\r\nTelegramチャットID:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ 操作成功!"
|
||||||
|
"restartFailed" = "❗ 操作エラー。\r\n\r\n<code>エラー: {{ .Error }}</code>"
|
||||||
|
"xrayNotRunning" = "❗ Xray Core は動作していません。"
|
||||||
|
|
||||||
|
[tgbot.messages]
|
||||||
|
"cpuThreshold" = "🔴 CPU使用率は{{ .Percent }}%、しきい値{{ .Threshold }}%を超えました"
|
||||||
|
"selectUserFailed" = "❌ ユーザーの選択に失敗しました!"
|
||||||
|
"userSaved" = "✅ Telegramユーザーが保存されました。"
|
||||||
|
"loginSuccess" = "✅ パネルに正常にログインしました。\r\n"
|
||||||
|
"loginFailed" = "❗️ パネルのログインに失敗しました。\r\n"
|
||||||
|
"report" = "🕰 定期報告:{{ .RunTime }}\r\n"
|
||||||
|
"datetime" = "⏰ 日時:{{ .DateTime }}\r\n"
|
||||||
|
"hostname" = "💻 ホスト名:{{ .Hostname }}\r\n"
|
||||||
|
"version" = "🚀 X-UI バージョン:{{ .Version }}\r\n"
|
||||||
|
"xrayVersion" = "📡 Xray バージョン: {{ .XrayVersion }}\r\n"
|
||||||
|
"ipv6" = "🌐 IPv6:{{ .IPv6 }}\r\n"
|
||||||
|
"ipv4" = "🌐 IPv4:{{ .IPv4 }}\r\n"
|
||||||
|
"ip" = "🌐 IP:{{ .IP }}\r\n"
|
||||||
|
"ips" = "🔢 IPアドレス:\r\n{{ .IPs }}\r\n"
|
||||||
|
"serverUpTime" = "⏳ サーバー稼働時間:{{ .UpTime }} {{ .Unit }}\r\n"
|
||||||
|
"serverLoad" = "📈 サーバー負荷:{{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||||
|
"serverMemory" = "📋 サーバーメモリ:{{ .Current }}/{{ .Total }}\r\n"
|
||||||
|
"tcpCount" = "🔹 TCP接続数:{{ .Count }}\r\n"
|
||||||
|
"udpCount" = "🔸 UDP接続数:{{ .Count }}\r\n"
|
||||||
|
"traffic" = "🚦 トラフィック:{{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||||
|
"xrayStatus" = "ℹ️ Xrayステータス:{{ .State }}\r\n"
|
||||||
|
"username" = "👤 ユーザー名:{{ .Username }}\r\n"
|
||||||
|
"password" = "👤 パスワード: {{ .Password }}\r\n"
|
||||||
|
"time" = "⏰ 時間:{{ .Time }}\r\n"
|
||||||
|
"inbound" = "📍 インバウンド:{{ .Remark }}\r\n"
|
||||||
|
"port" = "🔌 ポート:{{ .Port }}\r\n"
|
||||||
|
"expire" = "📅 有効期限:{{ .Time }}\r\n"
|
||||||
|
"expireIn" = "📅 残り時間:{{ .Time }}\r\n"
|
||||||
|
"active" = "💡 有効:{{ .Enable }}\r\n"
|
||||||
|
"enabled" = "🚨 有効化済み:{{ .Enable }}\r\n"
|
||||||
|
"online" = "🌐 接続ステータス:{{ .Status }}\r\n"
|
||||||
|
"email" = "📧 メール:{{ .Email }}\r\n"
|
||||||
|
"upload" = "🔼 アップロード↑:{{ .Upload }}\r\n"
|
||||||
|
"download" = "🔽 ダウンロード↓:{{ .Download }}\r\n"
|
||||||
|
"total" = "📊 合計:{{ .UpDown }} / {{ .Total }}\r\n"
|
||||||
|
"TGUser" = "👤 Telegramユーザー:{{ .TelegramID }}\r\n"
|
||||||
|
"exhaustedMsg" = "🚨 消耗済みの {{ .Type }}:\r\n"
|
||||||
|
"exhaustedCount" = "🚨 消耗済みの {{ .Type }} 数量:\r\n"
|
||||||
|
"onlinesCount" = "🌐 オンラインクライアント:{{ .Count }}\r\n"
|
||||||
|
"disabled" = "🛑 無効化:{{ .Disabled }}\r\n"
|
||||||
|
"depleteSoon" = "🔜 間もなく消耗:{{ .Deplete }}\r\n\r\n"
|
||||||
|
"backupTime" = "🗄 バックアップ時間:{{ .Time }}\r\n"
|
||||||
|
"refreshedOn" = "\r\n📋🔄 更新時間:{{ .Time }}\r\n\r\n"
|
||||||
|
"yes" = "✅ はい"
|
||||||
|
"no" = "❌ いいえ"
|
||||||
|
|
||||||
|
[tgbot.buttons]
|
||||||
|
"closeKeyboard" = "❌ キーボードを閉じる"
|
||||||
|
"cancel" = "❌ キャンセル"
|
||||||
|
"cancelReset" = "❌ リセットをキャンセル"
|
||||||
|
"cancelIpLimit" = "❌ IP制限をキャンセル"
|
||||||
|
"confirmResetTraffic" = "✅ トラフィックをリセットしますか?"
|
||||||
|
"confirmClearIps" = "✅ IPをクリアしますか?"
|
||||||
|
"confirmRemoveTGUser" = "✅ Telegramユーザーを削除しますか?"
|
||||||
|
"confirmToggle" = "✅ ユーザーを有効/無効にしますか?"
|
||||||
|
"dbBackup" = "データベースバックアップを取得"
|
||||||
|
"serverUsage" = "サーバーの使用状況"
|
||||||
|
"getInbounds" = "インバウンド情報を取得"
|
||||||
|
"depleteSoon" = "間もなく消耗"
|
||||||
|
"clientUsage" = "使用状況を取得"
|
||||||
|
"onlines" = "オンラインクライアント"
|
||||||
|
"commands" = "コマンド"
|
||||||
|
"refresh" = "🔄 更新"
|
||||||
|
"clearIPs" = "❌ IPをクリア"
|
||||||
|
"removeTGUser" = "❌ Telegramユーザーを削除"
|
||||||
|
"selectTGUser" = "👤 Telegramユーザーを選択"
|
||||||
|
"selectOneTGUser" = "👤 1人のTelegramユーザーを選択:"
|
||||||
|
"resetTraffic" = "📈 トラフィックをリセット"
|
||||||
|
"resetExpire" = "📅 有効期限を変更"
|
||||||
|
"ipLog" = "🔢 IPログ"
|
||||||
|
"ipLimit" = "🔢 IP制限"
|
||||||
|
"setTGUser" = "👤 Telegramユーザーを設定"
|
||||||
|
"toggle" = "🔘 有効/無効"
|
||||||
|
"custom" = "🔢 カスタム"
|
||||||
|
"confirmNumber" = "✅ 確認: {{ .Num }}"
|
||||||
|
"confirmNumberAdd" = "✅ 追加を確認:{{ .Num }}"
|
||||||
|
"limitTraffic" = "🚧 トラフィック制限"
|
||||||
|
"getBanLogs" = "禁止ログ"
|
||||||
|
"allClients" = "すべてのクライアント"
|
||||||
|
|
||||||
|
[tgbot.answers]
|
||||||
|
"successfulOperation" = "✅ 成功!"
|
||||||
|
"errorOperation" = "❗ 操作エラー。"
|
||||||
|
"getInboundsFailed" = "❌ インバウンド情報の取得に失敗しました。"
|
||||||
|
"getClientsFailed" = "❌ クライアントの取得に失敗しました。"
|
||||||
|
"canceled" = "❌ {{ .Email }}:操作がキャンセルされました。"
|
||||||
|
"clientRefreshSuccess" = "✅ {{ .Email }}:クライアントが正常に更新されました。"
|
||||||
|
"IpRefreshSuccess" = "✅ {{ .Email }}:IPが正常に更新されました。"
|
||||||
|
"TGIdRefreshSuccess" = "✅ {{ .Email }}:クライアントのTelegramユーザーが正常に更新されました。"
|
||||||
|
"resetTrafficSuccess" = "✅ {{ .Email }}:トラフィックが正常にリセットされました。"
|
||||||
|
"setTrafficLimitSuccess" = "✅ {{ .Email }}:トラフィック制限が正常に保存されました。"
|
||||||
|
"expireResetSuccess" = "✅ {{ .Email }}:有効期限の日数が正常にリセットされました。"
|
||||||
|
"resetIpSuccess" = "✅ {{ .Email }}:IP制限数が正常に保存されました:{{ .Count }}。"
|
||||||
|
"clearIpSuccess" = "✅ {{ .Email }}:IPが正常にクリアされました。"
|
||||||
|
"getIpLog" = "✅ {{ .Email }}:IPログの取得。"
|
||||||
|
"getUserInfo" = "✅ {{ .Email }}:Telegramユーザー情報の取得。"
|
||||||
|
"removedTGUserSuccess" = "✅ {{ .Email }}:Telegramユーザーが正常に削除されました。"
|
||||||
|
"enableSuccess" = "✅ {{ .Email }}:正常に有効化されました。"
|
||||||
|
"disableSuccess" = "✅ {{ .Email }}:正常に無効化されました。"
|
||||||
|
"askToAddUserId" = "設定が見つかりませんでした!\r\n管理者に問い合わせて、設定にTelegramユーザーのChatIDを使用してください。\r\n\r\nあなたのユーザーChatID:<code>{{ .TgUserID }}</code>"
|
||||||
|
"chooseClient" = "インバウンド {{ .Inbound }} のクライアントを選択"
|
||||||
|
"chooseInbound" = "インバウンドを選択"
|
||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "O histórico de IPs. (para ativar o inbound após a desativação, limpe o log)"
|
"IPLimitlogDesc" = "O histórico de IPs. (para ativar o inbound após a desativação, limpe o log)"
|
||||||
"IPLimitlogclear" = "Limpar o Log"
|
"IPLimitlogclear" = "Limpar o Log"
|
||||||
"setDefaultCert" = "Definir Certificado pelo Painel"
|
"setDefaultCert" = "Definir Certificado pelo Painel"
|
||||||
"xtlsDesc" = "O Xray deve ser v1.7.5"
|
|
||||||
"realityDesc" = "O Xray deve ser v1.8.0+"
|
|
||||||
"telegramDesc" = "Por favor, forneça o ID do Chat do Telegram. (use o comando '/id' no bot) ou (@userinfobot)"
|
"telegramDesc" = "Por favor, forneça o ID do Chat do Telegram. (use o comando '/id' no bot) ou (@userinfobot)"
|
||||||
"subscriptionDesc" = "Para encontrar seu URL de assinatura, navegue até 'Detalhes'. Além disso, você pode usar o mesmo nome para vários clientes."
|
"subscriptionDesc" = "Para encontrar seu URL de assinatura, navegue até 'Detalhes'. Além disso, você pode usar o mesmo nome para vários clientes."
|
||||||
"info" = "Informações"
|
"info" = "Informações"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"telegramTokenDesc" = "O token do bot do Telegram obtido de '@BotFather'."
|
"telegramTokenDesc" = "O token do bot do Telegram obtido de '@BotFather'."
|
||||||
"telegramProxy" = "Proxy SOCKS"
|
"telegramProxy" = "Proxy SOCKS"
|
||||||
"telegramProxyDesc" = "Ativa o proxy SOCKS5 para conectar ao Telegram. (ajuste as configurações conforme o guia)"
|
"telegramProxyDesc" = "Ativa o proxy SOCKS5 para conectar ao Telegram. (ajuste as configurações conforme o guia)"
|
||||||
|
"telegramAPIServer" = "API Server do Telegram"
|
||||||
|
"telegramAPIServerDesc" = "O servidor API do Telegram a ser usado. Deixe em branco para usar o servidor padrão."
|
||||||
"telegramChatId" = "ID de Chat do Administrador"
|
"telegramChatId" = "ID de Chat do Administrador"
|
||||||
"telegramChatIdDesc" = "O(s) ID(s) de Chat do Administrador no Telegram. (separado por vírgulas)(obtenha aqui @userinfobot) ou (use o comando '/id' no bot)"
|
"telegramChatIdDesc" = "O(s) ID(s) de Chat do Administrador no Telegram. (separado por vírgulas)(obtenha aqui @userinfobot) ou (use o comando '/id' no bot)"
|
||||||
"telegramNotifyTime" = "Hora da Notificação"
|
"telegramNotifyTime" = "Hora da Notificação"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"status" = "✅ Bot está OK!"
|
"status" = "✅ Bot está OK!"
|
||||||
"usage" = "❗ Por favor, forneça um texto para pesquisar!"
|
"usage" = "❗ Por favor, forneça um texto para pesquisar!"
|
||||||
"getID" = "🆔 Seu ID: <code>{{ .ID }}</code>"
|
"getID" = "🆔 Seu ID: <code>{{ .ID }}</code>"
|
||||||
"helpAdminCommands" = "Para pesquisar por um email de cliente:\r\n<code>/usage [Email]</code>\r\n\r\nPara pesquisar por inbounds (com estatísticas do cliente):\r\n<code>/inbound [Remark]</code>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>"
|
"helpAdminCommands" = "Para reiniciar o Xray Core:\r\n<code>/restart force</code>\r\n\r\nPara pesquisar por um email de cliente:\r\n<code>/usage [Email]</code>\r\n\r\nPara pesquisar por inbounds (com estatísticas do cliente):\r\n<code>/inbound [Remark]</code>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "Para pesquisar por estatísticas, use o seguinte comando:\r\n\r\n<code>/usage [Email]</code>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>"
|
"helpClientCommands" = "Para pesquisar por estatísticas, use o seguinte comando:\r\n\r\n<code>/usage [Email]</code>\r\n\r\nTelegram Chat ID:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ Operação bem-sucedida!"
|
||||||
|
"restartFailed" = "❗ Erro na operação.\r\n\r\n<code>Erro: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core não está em execução."
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 A carga da CPU {{ .Percent }}% excede o limite de {{ .Threshold }}%"
|
"cpuThreshold" = "🔴 A carga da CPU {{ .Percent }}% excede o limite de {{ .Threshold }}%"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"username" = "Имя пользователя"
|
"username" = "Имя пользователя"
|
||||||
"password" = "Пароль"
|
"password" = "Пароль"
|
||||||
"login" = "Логин"
|
"login" = "Войти"
|
||||||
"confirm" = "Подтвердить"
|
"confirm" = "Подтвердить"
|
||||||
"cancel" = "Отмена"
|
"cancel" = "Отмена"
|
||||||
"close" = "Закрыть"
|
"close" = "Закрыть"
|
||||||
@@ -67,12 +67,12 @@
|
|||||||
"settings" = "Настройки панели"
|
"settings" = "Настройки панели"
|
||||||
"xray" = "Настройки Xray"
|
"xray" = "Настройки Xray"
|
||||||
"logout" = "Выход"
|
"logout" = "Выход"
|
||||||
"link" = "Менеджмент"
|
"link" = "Управление"
|
||||||
|
|
||||||
[pages.login]
|
[pages.login]
|
||||||
"hello" = "Привет"
|
"hello" = "Привет"
|
||||||
"title" = "Добро пожаловать"
|
"title" = "Добро пожаловать"
|
||||||
"loginAgain" = "Время пребывания в сети вышло. Пожалуйста, войдите в систему снова"
|
"loginAgain" = "Ваша сессия истекла. Пожалуйста, войдите в систему снова"
|
||||||
|
|
||||||
[pages.login.toasts]
|
[pages.login.toasts]
|
||||||
"invalidFormData" = "Недопустимый формат данных"
|
"invalidFormData" = "Недопустимый формат данных"
|
||||||
@@ -93,8 +93,8 @@
|
|||||||
"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" = "Общая скорость upload для всех сетей"
|
"upSpeed" = "Общая скорость upload для всех сетей"
|
||||||
@@ -133,21 +133,21 @@
|
|||||||
"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" = "Оставьте пустым для прослушивания всех IP-адресов"
|
||||||
"meansNoLimit" = "= Без ограничений (значение: ГБ)"
|
"meansNoLimit" = "= Без ограничений (значение: ГБ)"
|
||||||
"totalFlow" = "Общий расход"
|
"totalFlow" = "Общий расход"
|
||||||
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
|
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
|
||||||
"noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию"
|
"noRecommendKeepDefault" = "Не рекомендуется оставлять настройки по умолчанию"
|
||||||
"certificatePath" = "Путь файла"
|
"certificatePath" = "Путь к файлу"
|
||||||
"certificateContent" = "Содержимое файла"
|
"certificateContent" = "Содержимое файла"
|
||||||
"publicKey" = "Публичный ключ"
|
"publicKey" = "Публичный ключ"
|
||||||
"privatekey" = "Приватный ключ"
|
"privatekey" = "Приватный ключ"
|
||||||
@@ -160,16 +160,16 @@
|
|||||||
"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"
|
"email" = "Email"
|
||||||
"emailDesc" = "Пожалуйста, укажите уникальный Email"
|
"emailDesc" = "Пожалуйста, укажите уникальный Email"
|
||||||
"IPLimit" = "Ограничение по IP"
|
"IPLimit" = "Ограничение по IP"
|
||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "Лог IP-адресов (перед включением лога IP-адресов, вы должны очистить список)"
|
"IPLimitlogDesc" = "Лог IP-адресов (перед включением лога IP-адресов, вы должны очистить список)"
|
||||||
"IPLimitlogclear" = "Очистить лог"
|
"IPLimitlogclear" = "Очистить лог"
|
||||||
"setDefaultCert" = "Установить сертификат с панели"
|
"setDefaultCert" = "Установить сертификат с панели"
|
||||||
"xtlsDesc" = "Версия Xray должна быть не ниже 1.7.5"
|
|
||||||
"realityDesc" = "Версия Xray должна быть не ниже 1.8.0"
|
|
||||||
"telegramDesc" = "Пожалуйста, укажите ID чата Telegram. (используйте команду '/id' в боте) или (@userinfobot)"
|
"telegramDesc" = "Пожалуйста, укажите ID чата Telegram. (используйте команду '/id' в боте) или (@userinfobot)"
|
||||||
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
|
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
|
||||||
"info" = "Информация"
|
"info" = "Информация"
|
||||||
@@ -187,7 +185,7 @@
|
|||||||
"inboundData" = "Входящие данные"
|
"inboundData" = "Входящие данные"
|
||||||
"exportInbound" = "Экспорт входящих"
|
"exportInbound" = "Экспорт входящих"
|
||||||
"import" = "Импортировать"
|
"import" = "Импортировать"
|
||||||
"importInbound" = "Импортировать входящее сообщение"
|
"importInbound" = "Импортировать подключение"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Добавить пользователя"
|
"add" = "Добавить пользователя"
|
||||||
@@ -214,13 +212,13 @@
|
|||||||
"request" = "Запрос"
|
"request" = "Запрос"
|
||||||
"response" = "Ответ"
|
"response" = "Ответ"
|
||||||
"name" = "Имя"
|
"name" = "Имя"
|
||||||
"value" = "Ценить"
|
"value" = "Значение"
|
||||||
|
|
||||||
[pages.inbounds.stream.tcp]
|
[pages.inbounds.stream.tcp]
|
||||||
"version" = "Версия"
|
"version" = "Версия"
|
||||||
"method" = "Метод"
|
"method" = "Метод"
|
||||||
"path" = "Путь"
|
"path" = "Путь"
|
||||||
"status" = "Положение дел"
|
"status" = "Статус"
|
||||||
"statusDescription" = "Описание статуса"
|
"statusDescription" = "Описание статуса"
|
||||||
"requestHeader" = "Заголовок запроса"
|
"requestHeader" = "Заголовок запроса"
|
||||||
"responseHeader" = "Заголовок ответа"
|
"responseHeader" = "Заголовок ответа"
|
||||||
@@ -228,9 +226,9 @@
|
|||||||
[pages.settings]
|
[pages.settings]
|
||||||
"title" = "Настройки"
|
"title" = "Настройки"
|
||||||
"save" = "Сохранить"
|
"save" = "Сохранить"
|
||||||
"infoDesc" = "Каждое сделанное здесь изменение необходимо сохранить. Пожалуйста, перезапустите панель, чтобы изменения вступили в силу"
|
"infoDesc" = "Каждое выполненное изменение необходимо сохранить. Пожалуйста, перезапустите панель, чтобы изменения вступили в силу"
|
||||||
"restartPanel" = "Перезапуск панели"
|
"restartPanel" = "Перезапуск панели"
|
||||||
"restartPanelDesc" = "Подтвердите перезапуск панели? ОК для перезапуска панели через 3 сек. Если вы не можете пользоваться панелью после перезапуска, пожалуйста, посмотрите лог панели на сервере"
|
"restartPanelDesc" = "Вы уверены, что хотите перезапустить панель? Нажмите ОК для перезапуска панели через 3 сек. Если вы не можете пользоваться панелью после перезапуска, пожалуйста, посмотрите лог панели на сервере"
|
||||||
"actions" = "Действия"
|
"actions" = "Действия"
|
||||||
"resetDefaultConfig" = "Сбросить на конфигурацию по умолчанию"
|
"resetDefaultConfig" = "Сбросить на конфигурацию по умолчанию"
|
||||||
"panelSettings" = "Настройки панели"
|
"panelSettings" = "Настройки панели"
|
||||||
@@ -243,21 +241,21 @@
|
|||||||
"panelPort" = "Порт панели"
|
"panelPort" = "Порт панели"
|
||||||
"panelPortDesc" = "Порт, используемый для отображения этой панели"
|
"panelPortDesc" = "Порт, используемый для отображения этой панели"
|
||||||
"publicKeyPath" = "Путь к файлу публичного ключа сертификата панели"
|
"publicKeyPath" = "Путь к файлу публичного ключа сертификата панели"
|
||||||
"publicKeyPathDesc" = "Введите полный путь, начинающийся с"
|
"publicKeyPathDesc" = "Введите полный путь, начинающийся с '/'"
|
||||||
"privateKeyPath" = "Путь к файлу приватного ключа сертификата панели"
|
"privateKeyPath" = "Путь к файлу приватного ключа сертификата панели"
|
||||||
"privateKeyPathDesc" = "Введите полный путь, начинающийся с"
|
"privateKeyPathDesc" = "Введите полный путь, начинающийся с '/'"
|
||||||
"panelUrlPath" = "Корневой путь URL адреса панели"
|
"panelUrlPath" = "Корневой путь URL адреса панели"
|
||||||
"panelUrlPathDesc" = "Должен начинаться с '/' и заканчиваться на"
|
"panelUrlPathDesc" = "Должен начинаться с '/' и заканчиваться на '/'"
|
||||||
"pageSize" = "Размер нумерации страниц"
|
"pageSize" = "Размер нумерации страниц"
|
||||||
"pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить"
|
"pageSizeDesc" = "Определить размер страницы для входящей таблицы. Установите 0, чтобы отключить"
|
||||||
"remarkModel" = "Модель примечания и символ разделения"
|
"remarkModel" = "Модель примечания и символ разделения"
|
||||||
"datepicker" = "Выбор даты"
|
"datepicker" = "Выбор даты"
|
||||||
"datepickerPlaceholder" = "Выберите дату"
|
"datepickerPlaceholder" = "Выберите дату"
|
||||||
"datepickerDescription" = "Тип календаря выбора указывает дату истечения срока действия."
|
"datepickerDescription" = "Запланированные задачи выполняются в соответствии с данным календарём"
|
||||||
"sampleRemark" = "Пример замечания"
|
"sampleRemark" = "Пример замечания"
|
||||||
"oldUsername" = "Текущее имя пользователя"
|
"oldUsername" = "Текущий логин"
|
||||||
"currentPassword" = "Текущий пароль"
|
"currentPassword" = "Текущий пароль"
|
||||||
"newUsername" = "Новое имя пользователя"
|
"newUsername" = "Новый логин"
|
||||||
"newPassword" = "Новый пароль"
|
"newPassword" = "Новый пароль"
|
||||||
"telegramBotEnable" = "Включить Telegram бота"
|
"telegramBotEnable" = "Включить Telegram бота"
|
||||||
"telegramBotEnableDesc" = "Подключайтесь к функциям этой панели через Telegram бота"
|
"telegramBotEnableDesc" = "Подключайтесь к функциям этой панели через Telegram бота"
|
||||||
@@ -265,8 +263,10 @@
|
|||||||
"telegramTokenDesc" = "Необходимо получить токен у менеджера ботов Telegram @botfather"
|
"telegramTokenDesc" = "Необходимо получить токен у менеджера ботов Telegram @botfather"
|
||||||
"telegramProxy" = "Прокси Socks5"
|
"telegramProxy" = "Прокси Socks5"
|
||||||
"telegramProxyDesc" = "Если для подключения к Telegram вам нужен прокси Socks5. Настройте его параметры согласно руководству."
|
"telegramProxyDesc" = "Если для подключения к Telegram вам нужен прокси Socks5. Настройте его параметры согласно руководству."
|
||||||
"telegramChatId" = "Telegram ChatID админа бота"
|
"telegramAPIServer" = "API-сервер Telegram"
|
||||||
"telegramChatIdDesc" = "Множественные идентификаторы чата, разделенные запятыми. Чтобы получить свои идентификаторы чатов, используйте @userinfobot или команду '/id' в боте."
|
"telegramAPIServerDesc" = "Используемый API-сервер Telegram. Оставьте пустым, чтобы использовать сервер по умолчанию."
|
||||||
|
"telegramChatId" = "Идентификатор Telegram администратора бота"
|
||||||
|
"telegramChatIdDesc" = "Один или несколько идентификаторов администратора бота. Чтобы получить идентификатор, используйте @userinfobot или команду '/id' в боте."
|
||||||
"telegramNotifyTime" = "Частота уведомлений бота Telegram"
|
"telegramNotifyTime" = "Частота уведомлений бота Telegram"
|
||||||
"telegramNotifyTimeDesc" = "Используйте формат времени Crontab"
|
"telegramNotifyTimeDesc" = "Используйте формат времени Crontab"
|
||||||
"tgNotifyBackup" = "Резервное копирование базы данных"
|
"tgNotifyBackup" = "Резервное копирование базы данных"
|
||||||
@@ -291,19 +291,19 @@
|
|||||||
"subPort" = "Порт подписки"
|
"subPort" = "Порт подписки"
|
||||||
"subPortDesc" = "Номер порта для обслуживания службы подписки не должен использоваться на сервере"
|
"subPortDesc" = "Номер порта для обслуживания службы подписки не должен использоваться на сервере"
|
||||||
"subCertPath" = "Путь к файлу открытого ключа сертификата подписки"
|
"subCertPath" = "Путь к файлу открытого ключа сертификата подписки"
|
||||||
"subCertPathDesc" = "Введите абсолютный путь, начинающийся с '/'"
|
"subCertPathDesc" = "Введите полный путь, начинающийся с '/'"
|
||||||
"subKeyPath" = "Путь к файлу закрытого ключа сертификата подписки"
|
"subKeyPath" = "Путь к файлу закрытого ключа сертификата подписки"
|
||||||
"subKeyPathDesc" = "Введите абсолютный путь, начинающийся с '/'"
|
"subKeyPathDesc" = "Введите полный путь, начинающийся с '/'"
|
||||||
"subPath" = "Корневой путь URL-адреса подписки"
|
"subPath" = "Корневой путь URL-адреса подписки"
|
||||||
"subPathDesc" = "Должен начинаться с '/' и заканчиваться на '/'"
|
"subPathDesc" = "Должен начинаться с '/' и заканчиваться на '/'"
|
||||||
"subDomain" = "Домен прослушивания"
|
"subDomain" = "Домен прослушивания"
|
||||||
"subDomainDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все домены и IP-адреса"
|
"subDomainDesc" = "Оставьте пустым по умолчанию, чтобы отслеживать все домены и IP-адреса"
|
||||||
"subUpdates" = "Интервалы обновления подписки"
|
"subUpdates" = "Интервалы обновления подписки"
|
||||||
"subUpdatesDesc" = "Часовой интервал между обновлениями в клиентском приложении"
|
"subUpdatesDesc" = "Интервал между обновлениями в клиентском приложении (в часах)"
|
||||||
"subEncrypt" = "Шифровать конфиги"
|
"subEncrypt" = "Шифровать конфиги"
|
||||||
"subEncryptDesc" = "Шифровать возвращенные конфиги в подписке"
|
"subEncryptDesc" = "Шифровать возвращенные конфиги в подписке"
|
||||||
"subShowInfo" = "Показать информацию об использовании"
|
"subShowInfo" = "Показать информацию об использовании"
|
||||||
"subShowInfoDesc" = "Показывать восстановленный трафик и дату после имени конфигурации"
|
"subShowInfoDesc" = "Показывать оставшиеся трафик и дату после имени конфигурации"
|
||||||
"subURI" = "URI обратного прокси"
|
"subURI" = "URI обратного прокси"
|
||||||
"subURIDesc" = "Изменить базовый URI URL-адреса подписки для использования за прокси-серверами"
|
"subURIDesc" = "Изменить базовый URI URL-адреса подписки для использования за прокси-серверами"
|
||||||
"fragment" = "Фрагментация"
|
"fragment" = "Фрагментация"
|
||||||
@@ -327,7 +327,7 @@
|
|||||||
"generalConfigs" = "Основные настройки"
|
"generalConfigs" = "Основные настройки"
|
||||||
"generalConfigsDesc" = "Эти параметры описывают общие настройки"
|
"generalConfigsDesc" = "Эти параметры описывают общие настройки"
|
||||||
"logConfigs" = "Журнал"
|
"logConfigs" = "Журнал"
|
||||||
"logConfigsDesc" = "Журналы могут повлиять на эффективность вашего сервера. Рекомендуется включать их с умом только в случае ваших нужд!"
|
"logConfigsDesc" = "Журналы могут повлиять на эффективность вашего сервера. Рекомендуется включать их только в случае необходимости!"
|
||||||
"blockConfigs" = "Блокировка конфигураций"
|
"blockConfigs" = "Блокировка конфигураций"
|
||||||
"blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам"
|
"blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам"
|
||||||
"basicRouting" = "Базовые соединения"
|
"basicRouting" = "Базовые соединения"
|
||||||
@@ -344,7 +344,7 @@
|
|||||||
"Template" = "Шаблон конфигурации Xray"
|
"Template" = "Шаблон конфигурации Xray"
|
||||||
"TemplateDesc" = "Создание файла конфигурации Xray на основе этого шаблона"
|
"TemplateDesc" = "Создание файла конфигурации Xray на основе этого шаблона"
|
||||||
"FreedomStrategy" = "Настройка стратегии протокола Freedom"
|
"FreedomStrategy" = "Настройка стратегии протокола Freedom"
|
||||||
"FreedomStrategyDesc" = "Установка стратегию вывода сети в протоколе Freedom"
|
"FreedomStrategyDesc" = "Установка стратегии вывода сети в протоколе Freedom"
|
||||||
"RoutingStrategy" = "Настройка стратегии маршрутизации доменов"
|
"RoutingStrategy" = "Настройка стратегии маршрутизации доменов"
|
||||||
"RoutingStrategyDesc" = "Установка общей стратегии маршрутизации разрешения DNS"
|
"RoutingStrategyDesc" = "Установка общей стратегии маршрутизации разрешения DNS"
|
||||||
"Torrent" = "Запрет использования BitTorrent"
|
"Torrent" = "Запрет использования BitTorrent"
|
||||||
@@ -362,7 +362,7 @@
|
|||||||
"logLevel" = "Уровень журнала"
|
"logLevel" = "Уровень журнала"
|
||||||
"logLevelDesc" = "Уровень журнала для журналов ошибок, указывающий информацию, которую необходимо записать."
|
"logLevelDesc" = "Уровень журнала для журналов ошибок, указывающий информацию, которую необходимо записать."
|
||||||
"accessLog" = "Журнал доступа"
|
"accessLog" = "Журнал доступа"
|
||||||
"accessLogDesc" = "Путь к файлу журнала доступа. Специальное значение «none» отключило журналы доступа."
|
"accessLogDesc" = "Путь к файлу журнала доступа. Специальное значение «none» отключает журналы доступа."
|
||||||
"errorLog" = "Журнал ошибок"
|
"errorLog" = "Журнал ошибок"
|
||||||
"errorLogDesc" = "Путь к файлу журнала ошибок. Специальное значение «none» отключает журналы ошибок."
|
"errorLogDesc" = "Путь к файлу журнала ошибок. Специальное значение «none» отключает журналы ошибок."
|
||||||
"dnsLog" = "DNS Журнал"
|
"dnsLog" = "DNS Журнал"
|
||||||
@@ -379,7 +379,7 @@
|
|||||||
"dest" = "Пункт назначения"
|
"dest" = "Пункт назначения"
|
||||||
"inbound" = "Входящий"
|
"inbound" = "Входящий"
|
||||||
"outbound" = "Исходящий"
|
"outbound" = "Исходящий"
|
||||||
"balancer" = "балансир"
|
"balancer" = "Балансировщик"
|
||||||
"info" = "Информация"
|
"info" = "Информация"
|
||||||
"add" = "Добавить правило"
|
"add" = "Добавить правило"
|
||||||
"edit" = "Редактировать правило"
|
"edit" = "Редактировать правило"
|
||||||
@@ -391,7 +391,7 @@
|
|||||||
"editOutbound" = "Изменить исходящий"
|
"editOutbound" = "Изменить исходящий"
|
||||||
"editReverse" = "Редактировать реверс"
|
"editReverse" = "Редактировать реверс"
|
||||||
"tag" = "Тег"
|
"tag" = "Тег"
|
||||||
"tagDesc" = "уникальный тег"
|
"tagDesc" = "Уникальный тег"
|
||||||
"address" = "Адрес"
|
"address" = "Адрес"
|
||||||
"reverse" = "Обратный"
|
"reverse" = "Обратный"
|
||||||
"domain" = "Домен"
|
"domain" = "Домен"
|
||||||
@@ -400,21 +400,21 @@
|
|||||||
"portal" = "Портал"
|
"portal" = "Портал"
|
||||||
"intercon" = "Соединение"
|
"intercon" = "Соединение"
|
||||||
"settings" = "Настройки"
|
"settings" = "Настройки"
|
||||||
"accountInfo" = "Информация Об Учетной Записи"
|
"accountInfo" = "Информация об учетной записи"
|
||||||
"outboundStatus" = "Исходящий статус"
|
"outboundStatus" = "Исходящий статус"
|
||||||
"sendThrough" = "Отправить через"
|
"sendThrough" = "Отправить через"
|
||||||
|
|
||||||
[pages.xray.balancer]
|
[pages.xray.balancer]
|
||||||
"addBalancer" = "Добавить балансир"
|
"addBalancer" = "Добавить балансировщик"
|
||||||
"editBalancer" = "Редактировать балансир"
|
"editBalancer" = "Редактировать балансировщик"
|
||||||
"balancerStrategy" = "Стратегия"
|
"balancerStrategy" = "Стратегия"
|
||||||
"balancerSelectors" = "Селекторы"
|
"balancerSelectors" = "Селекторы"
|
||||||
"tag" = "Тег"
|
"tag" = "Тег"
|
||||||
"tagDesc" = "уникальный тег"
|
"tagDesc" = "Уникальный тег"
|
||||||
"balancerDesc" = "Невозможно одновременно использовать balancerTag и outboundTag. При одновременном использовании будет работать только outboundTag."
|
"balancerDesc" = "Невозможно одновременно использовать balancerTag и outboundTag. При одновременном использовании будет работать только outboundTag."
|
||||||
|
|
||||||
[pages.xray.wireguard]
|
[pages.xray.wireguard]
|
||||||
"secretKey" = "Секретный ключ"
|
"secretKey" = "Приватный ключ"
|
||||||
"publicKey" = "Публичный ключ"
|
"publicKey" = "Публичный ключ"
|
||||||
"allowedIPs" = "Разрешенные IP-адреса"
|
"allowedIPs" = "Разрешенные IP-адреса"
|
||||||
"endpoint" = "Конечная точка"
|
"endpoint" = "Конечная точка"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"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 [Примечание]</code>\r\n\r\nID чата Telegram:\r\n<code>/id</code>"
|
"helpAdminCommands" = "Для перезапуска Xray Core:\r\n<code>/restart force</code>\r\n\r\nДля поиска электронной почты клиента:\r\n<code>/usage [Email]</code>\r\n\r\nДля поиска входящих (со статистикой клиента):\r\n<code>/inbound [Примечание]</code>\r\n\r\nID чата Telegram:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "Для поиска статистики используйте следующую команду:\r\n<code>/usage [Email]</code>\r\n\r\nID чата Telegram:\r\n<code>/id</code>"
|
"helpClientCommands" = "Для поиска статистики используйте следующую команду:\r\n<code>/usage [Email]</code>\r\n\r\nID чата Telegram:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ Операция успешно завершена!"
|
||||||
|
"restartFailed" = "❗ Ошибка в операции.\r\n\r\n<code>Ошибка: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core не запущен."
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 Загрузка процессора составляет {{ .Percent }}%, что превышает пороговое значение {{ .Threshold }}%"
|
"cpuThreshold" = "🔴 Загрузка процессора составляет {{ .Percent }}%, что превышает пороговое значение {{ .Threshold }}%"
|
||||||
|
|||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "IP geçmiş günlüğü. (devre dışı bırakıldıktan sonra gelini etkinleştirmek için günlüğü temizleyin)"
|
"IPLimitlogDesc" = "IP geçmiş günlüğü. (devre dışı bırakıldıktan sonra gelini etkinleştirmek için günlüğü temizleyin)"
|
||||||
"IPLimitlogclear" = "Günlüğü Temizle"
|
"IPLimitlogclear" = "Günlüğü Temizle"
|
||||||
"setDefaultCert" = "Panelden Sertifikayı Ayarla"
|
"setDefaultCert" = "Panelden Sertifikayı Ayarla"
|
||||||
"xtlsDesc" = "Xray v1.7.5 olmalıdır"
|
|
||||||
"realityDesc" = "Xray v1.8.0+ olmalıdır"
|
|
||||||
"telegramDesc" = "Lütfen Telegram Sohbet Kimliği sağlayın. (botta '/id' komutunu kullanın) veya (@userinfobot)"
|
"telegramDesc" = "Lütfen Telegram Sohbet Kimliği sağlayın. (botta '/id' komutunu kullanın) veya (@userinfobot)"
|
||||||
"subscriptionDesc" = "Abonelik URL'inizi bulmak için 'Detaylar'a gidin. Ayrıca, aynı adı birden fazla müşteri için kullanabilirsiniz."
|
"subscriptionDesc" = "Abonelik URL'inizi bulmak için 'Detaylar'a gidin. Ayrıca, aynı adı birden fazla müşteri için kullanabilirsiniz."
|
||||||
"info" = "Bilgi"
|
"info" = "Bilgi"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"telegramTokenDesc" = "'@BotFather'dan alınan Telegram bot token."
|
"telegramTokenDesc" = "'@BotFather'dan alınan Telegram bot token."
|
||||||
"telegramProxy" = "SOCKS Proxy"
|
"telegramProxy" = "SOCKS Proxy"
|
||||||
"telegramProxyDesc" = "Telegram'a bağlanmak için SOCKS5 proxy'sini etkinleştirir. (ayarları kılavuzda belirtilen şekilde ayarlayın)"
|
"telegramProxyDesc" = "Telegram'a bağlanmak için SOCKS5 proxy'sini etkinleştirir. (ayarları kılavuzda belirtilen şekilde ayarlayın)"
|
||||||
|
"telegramAPIServer" = "Telegram API Server"
|
||||||
|
"telegramAPIServerDesc" = "Kullanılacak Telegram API sunucusu. Varsayılan sunucuyu kullanmak için boş bırakın."
|
||||||
"telegramChatId" = "Yönetici Sohbet Kimliği"
|
"telegramChatId" = "Yönetici Sohbet Kimliği"
|
||||||
"telegramChatIdDesc" = "Telegram Yönetici Sohbet Kimliği(leri). (virgülle ayrılmış)(buradan alın @userinfobot) veya (botta '/id' komutunu kullanın)"
|
"telegramChatIdDesc" = "Telegram Yönetici Sohbet Kimliği(leri). (virgülle ayrılmış)(buradan alın @userinfobot) veya (botta '/id' komutunu kullanın)"
|
||||||
"telegramNotifyTime" = "Bildirim Zamanı"
|
"telegramNotifyTime" = "Bildirim Zamanı"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"status" = "✅ Bot çalışıyor!"
|
"status" = "✅ Bot çalışıyor!"
|
||||||
"usage" = "❗ Lütfen aramak için bir metin sağlayın!"
|
"usage" = "❗ Lütfen aramak için bir metin sağlayın!"
|
||||||
"getID" = "🆔 Kimliğiniz: <code>{{ .ID }}</code>"
|
"getID" = "🆔 Kimliğiniz: <code>{{ .ID }}</code>"
|
||||||
"helpAdminCommands" = "Bir müşteri e-postasını aramak için:\r\n<code>/usage [E-posta]</code>\r\n\r\nGelenleri aramak için (müşteri istatistikleri ile):\r\n<code>/inbound [Açıklama]</code>\r\n\r\nTelegram Sohbet Kimliği:\r\n<code>/id</code>"
|
"helpAdminCommands" = "Xray Core'u yeniden başlatmak için:\r\n<code>/restart force</code>\r\n\r\nBir müşteri e-postasını aramak için:\r\n<code>/usage [E-posta]</code>\r\n\r\nGelenleri aramak için (müşteri istatistikleri ile):\r\n<code>/inbound [Açıklama]</code>\r\n\r\nTelegram Sohbet Kimliği:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "İstatistikleri aramak için şu komutu kullanın:\r\n\r\n<code>/usage [E-posta]</code>\r\n\r\nTelegram Sohbet Kimliği:\r\n<code>/id</code>"
|
"helpClientCommands" = "İstatistikleri aramak için şu komutu kullanın:\r\n\r\n<code>/usage [E-posta]</code>\r\n\r\nTelegram Sohbet Kimliği:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ İşlem başarılı!"
|
||||||
|
"restartFailed" = "❗ İşlem hatası.\r\n\r\n<code>Hata: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core çalışmıyor."
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 CPU Yükü {{ .Percent }}% eşiği {{ .Threshold }}%'yi aşıyor"
|
"cpuThreshold" = "🔴 CPU Yükü {{ .Percent }}% eşiği {{ .Threshold }}%'yi aşıyor"
|
||||||
|
|||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "Журнал історії IP-адрес. (щоб увімкнути вхідну після вимкнення, очистіть журнал)"
|
"IPLimitlogDesc" = "Журнал історії IP-адрес. (щоб увімкнути вхідну після вимкнення, очистіть журнал)"
|
||||||
"IPLimitlogclear" = "Очистити журнал"
|
"IPLimitlogclear" = "Очистити журнал"
|
||||||
"setDefaultCert" = "Установити сертифікат з панелі"
|
"setDefaultCert" = "Установити сертифікат з панелі"
|
||||||
"xtlsDesc" = "Xray має бути v1.7.5"
|
|
||||||
"realityDesc" = "Xray має бути v1.8.0+"
|
|
||||||
"telegramDesc" = "Будь ласка, вкажіть ID чату Telegram. (використовуйте команду '/id' у боті) або (@userinfobot)"
|
"telegramDesc" = "Будь ласка, вкажіть ID чату Telegram. (використовуйте команду '/id' у боті) або (@userinfobot)"
|
||||||
"subscriptionDesc" = "Щоб знайти URL-адресу вашої підписки, перейдіть до «Деталі». Крім того, ви можете використовувати одне ім'я для кількох клієнтів."
|
"subscriptionDesc" = "Щоб знайти URL-адресу вашої підписки, перейдіть до «Деталі». Крім того, ви можете використовувати одне ім'я для кількох клієнтів."
|
||||||
"info" = "Інформація"
|
"info" = "Інформація"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"telegramTokenDesc" = "Токен бота Telegram, отриманий від '@BotFather'."
|
"telegramTokenDesc" = "Токен бота Telegram, отриманий від '@BotFather'."
|
||||||
"telegramProxy" = "SOCKS Проксі"
|
"telegramProxy" = "SOCKS Проксі"
|
||||||
"telegramProxyDesc" = "Вмикає проксі-сервер SOCKS5 для підключення до Telegram. (відкоригуйте параметри відповідно до посібника)"
|
"telegramProxyDesc" = "Вмикає проксі-сервер SOCKS5 для підключення до Telegram. (відкоригуйте параметри відповідно до посібника)"
|
||||||
|
"telegramAPIServer" = "Сервер Telegram API"
|
||||||
|
"telegramAPIServerDesc" = "Сервер Telegram API для використання. Залиште поле порожнім, щоб використовувати сервер за умовчанням."
|
||||||
"telegramChatId" = "Ідентифікатор чату адміністратора"
|
"telegramChatId" = "Ідентифікатор чату адміністратора"
|
||||||
"telegramChatIdDesc" = "Ідентифікатори чату адміністратора Telegram. (розділені комами) (отримайте тут @userinfobot) або (використовуйте команду '/id' у боті)"
|
"telegramChatIdDesc" = "Ідентифікатори чату адміністратора Telegram. (розділені комами) (отримайте тут @userinfobot) або (використовуйте команду '/id' у боті)"
|
||||||
"telegramNotifyTime" = "Час сповіщення"
|
"telegramNotifyTime" = "Час сповіщення"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"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>\r\n\r\nID чату Telegram:\r\n<code>/id</code>"
|
"helpAdminCommands" = "Для перезапуску Xray Core:\r\n<code>/restart force</code>\r\n\r\nДля пошуку електронної пошти клієнта:\r\n<code>/usage [Електронна пошта]</code>\r\n\r\nДля пошуку вхідних (зі статистикою клієнта):\r\n<code>/inbound [Примітка]</code>\r\n\r\nID чату Telegram:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "Для пошуку статистики використовуйте наступну команду:\r\n<code>/usage [Електронна пошта]</code>\r\n\r\nID чату Telegram:\r\n<code>/id</code>"
|
"helpClientCommands" = "Для пошуку статистики використовуйте наступну команду:\r\n<code>/usage [Електронна пошта]</code>\r\n\r\nID чату Telegram:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ Операція успішна!"
|
||||||
|
"restartFailed" = "❗ Помилка в операції.\r\n\r\n<code>Помилка: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core не запущений."
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 Навантаження ЦП {{ .Percent }}% перевищує порогове значення {{ .Threshold }}%"
|
"cpuThreshold" = "🔴 Навантаження ЦП {{ .Percent }}% перевищує порогове значення {{ .Threshold }}%"
|
||||||
|
|||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "Lịch sử đăng nhập IP (trước khi kích hoạt điểm vào sau khi bị vô hiệu hóa bởi giới hạn IP, bạn nên xóa lịch sử)."
|
"IPLimitlogDesc" = "Lịch sử đăng nhập IP (trước khi kích hoạt điểm vào sau khi bị vô hiệu hóa bởi giới hạn IP, bạn nên xóa lịch sử)."
|
||||||
"IPLimitlogclear" = "Xóa Lịch sử"
|
"IPLimitlogclear" = "Xóa Lịch sử"
|
||||||
"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"
|
|
||||||
"realityDesc" = "Xray core cần phiên bản 1.8.0 hoặc cao hơn."
|
|
||||||
"telegramDesc" = "Vui lòng cung cấp ID Trò chuyện Telegram. (sử dụng lệnh '/id' trong bot) hoặc (@userinfobot)"
|
"telegramDesc" = "Vui lòng cung cấp ID Trò chuyện Telegram. (sử dụng lệnh '/id' trong bot) hoặc (@userinfobot)"
|
||||||
"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 gói đăng ký của mình trong Chi tiết, cũng như bạn có thể sử dụng cùng tên cho nhiều cấu hình khác nhau"
|
||||||
"info" = "Thông tin"
|
"info" = "Thông tin"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"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"
|
"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."
|
"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."
|
||||||
|
"telegramAPIServer" = "Telegram API Server"
|
||||||
|
"telegramAPIServerDesc" = "Máy chủ API Telegram để sử dụng. Để trống để sử dụng máy chủ mặc định."
|
||||||
"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"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"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\nĐể tìm kiếm các nhập (với số liệu thống kê của khách hàng):\r\n<code>/inbound [Ghi chú]</code>\r\n\r\nID Trò chuyện Telegram:\r\n<code>/id</code>"
|
"helpAdminCommands" = "Để khởi động lại Xray Core:\r\n<code>/restart force</code>\r\n\r\nĐể tìm kiếm email của khách hàng:\r\n<code>/usage [Email]</code>\r\n\r\nĐể tìm kiếm các nhập (với số liệu thống kê của khách hàng):\r\n<code>/inbound [Ghi chú]</code>\r\n\r\nID Trò chuyện Telegram:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "Để tìm kiếm thống kê, sử dụng lệnh sau:\r\n<code>/usage [Email]</code>\r\n\r\nID Trò chuyện Telegram:\r\n<code>/id</code>"
|
"helpClientCommands" = "Để tìm kiếm thống kê, sử dụng lệnh sau:\r\n<code>/usage [Email]</code>\r\n\r\nID Trò chuyện Telegram:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ Hoạt động thành công!"
|
||||||
|
"restartFailed" = "❗ Lỗi trong quá trình hoạt động.\r\n\r\n<code>Lỗi: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core không chạy."
|
||||||
|
|
||||||
[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 }}%"
|
||||||
|
|||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "IP 历史日志(要启用被禁用的入站流量,请清除日志)"
|
"IPLimitlogDesc" = "IP 历史日志(要启用被禁用的入站流量,请清除日志)"
|
||||||
"IPLimitlogclear" = "清除日志"
|
"IPLimitlogclear" = "清除日志"
|
||||||
"setDefaultCert" = "从面板设置证书"
|
"setDefaultCert" = "从面板设置证书"
|
||||||
"xtlsDesc" = "Xray 核心需要 1.7.5"
|
|
||||||
"realityDesc" = "Xray 核心需要 1.8.0 及以上版本"
|
|
||||||
"telegramDesc" = "请提供Telegram聊天ID。(在机器人中使用'/id'命令)或(@userinfobot"
|
"telegramDesc" = "请提供Telegram聊天ID。(在机器人中使用'/id'命令)或(@userinfobot"
|
||||||
"subscriptionDesc" = "要找到你的订阅 URL,请导航到“详细信息”。此外,你可以为多个客户端使用相同的名称。"
|
"subscriptionDesc" = "要找到你的订阅 URL,请导航到“详细信息”。此外,你可以为多个客户端使用相同的名称。"
|
||||||
"info" = "信息"
|
"info" = "信息"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"telegramTokenDesc" = "从 '@BotFather' 获取的 Telegram 机器人令牌"
|
"telegramTokenDesc" = "从 '@BotFather' 获取的 Telegram 机器人令牌"
|
||||||
"telegramProxy" = "SOCKS5 Proxy"
|
"telegramProxy" = "SOCKS5 Proxy"
|
||||||
"telegramProxyDesc" = "启用 SOCKS5 代理连接到 Telegram(根据指南调整设置)"
|
"telegramProxyDesc" = "启用 SOCKS5 代理连接到 Telegram(根据指南调整设置)"
|
||||||
|
"telegramAPIServer" = "Telegram API Server"
|
||||||
|
"telegramAPIServerDesc" = "要使用的 Telegram API 服务器。留空以使用默认服务器。"
|
||||||
"telegramChatId" = "管理员聊天 ID"
|
"telegramChatId" = "管理员聊天 ID"
|
||||||
"telegramChatIdDesc" = "Telegram 管理员聊天 ID (多个以逗号分隔)(可通过 @userinfobot 获取,或在机器人中使用 '/id' 命令获取)"
|
"telegramChatIdDesc" = "Telegram 管理员聊天 ID (多个以逗号分隔)(可通过 @userinfobot 获取,或在机器人中使用 '/id' 命令获取)"
|
||||||
"telegramNotifyTime" = "通知时间"
|
"telegramNotifyTime" = "通知时间"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"status" = "✅ 机器人正常运行!"
|
"status" = "✅ 机器人正常运行!"
|
||||||
"usage" = "❗ 请输入要搜索的文本!"
|
"usage" = "❗ 请输入要搜索的文本!"
|
||||||
"getID" = "🆔 您的 ID 为:<code>{{ .ID }}</code>"
|
"getID" = "🆔 您的 ID 为:<code>{{ .ID }}</code>"
|
||||||
"helpAdminCommands" = "要搜索客户电子邮件:\r\n<code>/usage [电子邮件]</code>\r\n\r\n要搜索入站(带有客户统计数据):\r\n<code>/inbound [备注]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
"helpAdminCommands" = "要重新启动 Xray Core:\r\n<code>/restart force</code>\r\n\r\n要搜索客户电子邮件:\r\n<code>/usage [电子邮件]</code>\r\n\r\n要搜索入站(带有客户统计数据):\r\n<code>/inbound [备注]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "要搜索统计数据,请使用以下命令:\r\n<code>/usage [电子邮件]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
"helpClientCommands" = "要搜索统计数据,请使用以下命令:\r\n<code>/usage [电子邮件]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ 操作成功!"
|
||||||
|
"restartFailed" = "❗ 操作错误。\r\n\r\n<code>错误: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core 未运行。"
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 CPU 使用率为 {{ .Percent }}%,超过阈值 {{ .Threshold }}%"
|
"cpuThreshold" = "🔴 CPU 使用率为 {{ .Percent }}%,超过阈值 {{ .Threshold }}%"
|
||||||
|
|||||||
@@ -178,8 +178,6 @@
|
|||||||
"IPLimitlogDesc" = "IP 歷史日誌(要啟用被禁用的入站流量,請清除日誌)"
|
"IPLimitlogDesc" = "IP 歷史日誌(要啟用被禁用的入站流量,請清除日誌)"
|
||||||
"IPLimitlogclear" = "清除日誌"
|
"IPLimitlogclear" = "清除日誌"
|
||||||
"setDefaultCert" = "從面板設定證書"
|
"setDefaultCert" = "從面板設定證書"
|
||||||
"xtlsDesc" = "Xray 核心需要 1.7.5"
|
|
||||||
"realityDesc" = "Xray 核心需要 1.8.0 及以上版本"
|
|
||||||
"telegramDesc" = "請提供Telegram聊天ID。(在機器人中使用'/id'命令)或(@userinfobot"
|
"telegramDesc" = "請提供Telegram聊天ID。(在機器人中使用'/id'命令)或(@userinfobot"
|
||||||
"subscriptionDesc" = "要找到你的訂閱 URL,請導航到“詳細資訊”。此外,你可以為多個客戶端使用相同的名稱。"
|
"subscriptionDesc" = "要找到你的訂閱 URL,請導航到“詳細資訊”。此外,你可以為多個客戶端使用相同的名稱。"
|
||||||
"info" = "資訊"
|
"info" = "資訊"
|
||||||
@@ -265,6 +263,8 @@
|
|||||||
"telegramTokenDesc" = "從 '@BotFather' 獲取的 Telegram 機器人令牌"
|
"telegramTokenDesc" = "從 '@BotFather' 獲取的 Telegram 機器人令牌"
|
||||||
"telegramProxy" = "SOCKS5 Proxy"
|
"telegramProxy" = "SOCKS5 Proxy"
|
||||||
"telegramProxyDesc" = "啟用 SOCKS5 代理連線到 Telegram(根據指南調整設定)"
|
"telegramProxyDesc" = "啟用 SOCKS5 代理連線到 Telegram(根據指南調整設定)"
|
||||||
|
"telegramAPIServer" = "Telegram API Server"
|
||||||
|
"telegramAPIServerDesc" = "要使用的 Telegram API 伺服器。留空以使用預設伺服器。"
|
||||||
"telegramChatId" = "管理員聊天 ID"
|
"telegramChatId" = "管理員聊天 ID"
|
||||||
"telegramChatIdDesc" = "Telegram 管理員聊天 ID (多個以逗號分隔)(可通過 @userinfobot 獲取,或在機器人中使用 '/id' 命令獲取)"
|
"telegramChatIdDesc" = "Telegram 管理員聊天 ID (多個以逗號分隔)(可通過 @userinfobot 獲取,或在機器人中使用 '/id' 命令獲取)"
|
||||||
"telegramNotifyTime" = "通知時間"
|
"telegramNotifyTime" = "通知時間"
|
||||||
@@ -483,8 +483,12 @@
|
|||||||
"status" = "✅ 機器人正常執行!"
|
"status" = "✅ 機器人正常執行!"
|
||||||
"usage" = "❗ 請輸入要搜尋的文字!"
|
"usage" = "❗ 請輸入要搜尋的文字!"
|
||||||
"getID" = "🆔 您的 ID 為:<code>{{ .ID }}</code>"
|
"getID" = "🆔 您的 ID 為:<code>{{ .ID }}</code>"
|
||||||
"helpAdminCommands" = "要搜尋客戶電子郵件:\r\n<code>/usage [電子郵件]</code>\r\n\r\n要搜尋入站(帶有客戶統計資料):\r\n<code>/inbound [備註]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
"helpAdminCommands" = "要重新啟動 Xray Core:\r\n<code>/restart force</code>\r\n\r\n要搜尋客戶電子郵件:\r\n<code>/usage [電子郵件]</code>\r\n\r\n要搜尋入站(帶有客戶統計資料):\r\n<code>/inbound [備註]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
||||||
"helpClientCommands" = "要搜尋統計資料,請使用以下命令:\r\n<code>/usage [電子郵件]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
"helpClientCommands" = "要搜尋統計資料,請使用以下命令:\r\n<code>/usage [電子郵件]</code>\r\n\r\nTelegram聊天ID:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||||
|
"restartSuccess" = "✅ 操作成功!"
|
||||||
|
"restartFailed" = "❗ 操作錯誤。\r\n\r\n<code>錯誤: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core 未運行。"
|
||||||
|
|
||||||
[tgbot.messages]
|
[tgbot.messages]
|
||||||
"cpuThreshold" = "🔴 CPU 使用率為 {{ .Percent }}%,超過閾值 {{ .Threshold }}%"
|
"cpuThreshold" = "🔴 CPU 使用率為 {{ .Percent }}%,超過閾值 {{ .Threshold }}%"
|
||||||
|
|||||||
452
x-ui.sh
452
x-ui.sh
@@ -46,6 +46,8 @@ elif [[ "${release}" == "manjaro" ]]; then
|
|||||||
echo "Your OS is Manjaro"
|
echo "Your OS is Manjaro"
|
||||||
elif [[ "${release}" == "armbian" ]]; then
|
elif [[ "${release}" == "armbian" ]]; then
|
||||||
echo "Your OS is Armbian"
|
echo "Your OS is Armbian"
|
||||||
|
elif [[ "${release}" == "alpine" ]]; then
|
||||||
|
echo "Your OS is Alpine Linux"
|
||||||
elif [[ "${release}" == "opensuse-tumbleweed" ]]; then
|
elif [[ "${release}" == "opensuse-tumbleweed" ]]; then
|
||||||
echo "Your OS is OpenSUSE Tumbleweed"
|
echo "Your OS is OpenSUSE Tumbleweed"
|
||||||
elif [[ "${release}" == "openEuler" ]]; then
|
elif [[ "${release}" == "openEuler" ]]; then
|
||||||
@@ -162,7 +164,7 @@ update() {
|
|||||||
bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh)
|
||||||
if [[ $? == 0 ]]; then
|
if [[ $? == 0 ]]; then
|
||||||
LOGI "Update is complete, Panel has automatically restarted "
|
LOGI "Update is complete, Panel has automatically restarted "
|
||||||
exit 0
|
before_show_menu
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,14 +185,14 @@ update_menu() {
|
|||||||
|
|
||||||
if [[ $? == 0 ]]; then
|
if [[ $? == 0 ]]; then
|
||||||
echo -e "${green}Update successful. The panel has automatically restarted.${plain}"
|
echo -e "${green}Update successful. The panel has automatically restarted.${plain}"
|
||||||
exit 0
|
before_show_menu
|
||||||
else
|
else
|
||||||
echo -e "${red}Failed to update the menu.${plain}"
|
echo -e "${red}Failed to update the menu.${plain}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
custom_version() {
|
legacy_version() {
|
||||||
echo "Enter the panel version (like 2.4.0):"
|
echo "Enter the panel version (like 2.4.0):"
|
||||||
read tag_version
|
read tag_version
|
||||||
|
|
||||||
@@ -198,17 +200,8 @@ custom_version() {
|
|||||||
echo "Panel version cannot be empty. Exiting."
|
echo "Panel version cannot be empty. Exiting."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
min_version="2.3.5"
|
|
||||||
if [[ "$(printf '%s\n' "$tag_version" "$min_version" | sort -V | head -n1)" == "$tag_version" && "$tag_version" != "$min_version" ]]; then
|
|
||||||
echo "Please use a newer version (at least 2.3.5). 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
|
# Use the entered panel version in the download link
|
||||||
install_command="bash <(curl -Ls $download_link) v$tag_version"
|
install_command="bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/v$tag_version/install.sh") v$tag_version"
|
||||||
|
|
||||||
echo "Downloading and installing panel version $tag_version..."
|
echo "Downloading and installing panel version $tag_version..."
|
||||||
eval $install_command
|
eval $install_command
|
||||||
@@ -301,17 +294,35 @@ reset_config() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
/usr/local/x-ui/x-ui setting -reset
|
/usr/local/x-ui/x-ui setting -reset
|
||||||
echo -e "All panel settings have been reset to default, Please restart the panel now, and use the default ${green}2053${plain} Port to Access the web Panel"
|
echo -e "All panel settings have been reset to default."
|
||||||
confirm_restart
|
restart
|
||||||
}
|
}
|
||||||
|
|
||||||
check_config() {
|
check_config() {
|
||||||
info=$(/usr/local/x-ui/x-ui setting -show true)
|
local info=$(/usr/local/x-ui/x-ui setting -show true)
|
||||||
if [[ $? != 0 ]]; then
|
if [[ $? != 0 ]]; then
|
||||||
LOGE "get current settings error, please check logs"
|
LOGE "get current settings error, please check logs"
|
||||||
show_menu
|
show_menu
|
||||||
|
return
|
||||||
fi
|
fi
|
||||||
LOGI "${info}"
|
LOGI "${info}"
|
||||||
|
|
||||||
|
local existing_webBasePath=$(echo "$info" | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
||||||
|
local existing_port=$(echo "$info" | grep -Eo 'port: .+' | awk '{print $2}')
|
||||||
|
local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'cert: .+' | awk '{print $2}')
|
||||||
|
local server_ip=$(curl -s https://api.ipify.org)
|
||||||
|
|
||||||
|
if [[ -n "$existing_cert" ]]; then
|
||||||
|
local domain=$(basename "$(dirname "$existing_cert")")
|
||||||
|
|
||||||
|
if [[ "$domain" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
|
||||||
|
echo -e "${green}Access URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}"
|
||||||
|
else
|
||||||
|
echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${green}Access URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
set_port() {
|
set_port() {
|
||||||
@@ -416,22 +427,63 @@ disable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show_log() {
|
show_log() {
|
||||||
journalctl -u x-ui.service -e --no-pager -f
|
echo -e "${green}\t1.${plain} Debug Log"
|
||||||
if [[ $# == 0 ]]; then
|
echo -e "${green}\t2.${plain} Clear All logs"
|
||||||
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
|
read -p "Choose an option: " choice
|
||||||
|
|
||||||
|
case "$choice" in
|
||||||
|
0)
|
||||||
|
show_menu
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
journalctl -u x-ui -e --no-pager -f -p debug
|
||||||
|
if [[ $# == 0 ]]; then
|
||||||
before_show_menu
|
before_show_menu
|
||||||
fi
|
fi
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
sudo journalctl --rotate
|
||||||
|
sudo journalctl --vacuum-time=1s
|
||||||
|
echo "All Logs cleared."
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||||
|
show_log
|
||||||
|
;;
|
||||||
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
show_banlog() {
|
show_banlog() {
|
||||||
if test -f "${iplimit_banned_log_path}"; then
|
local system_log="/var/log/fail2ban.log"
|
||||||
|
|
||||||
|
echo -e "${green}Checking ban logs...${plain}\n"
|
||||||
|
|
||||||
|
if ! systemctl is-active --quiet fail2ban; then
|
||||||
|
echo -e "${red}Fail2ban service is not running!${plain}\n"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$system_log" ]]; then
|
||||||
|
echo -e "${green}Recent system ban activities from fail2ban.log:${plain}"
|
||||||
|
grep "3x-ipl" "$system_log" | grep -E "Ban|Unban" | tail -n 10 || echo -e "${yellow}No recent system ban activities found${plain}"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "${iplimit_banned_log_path}" ]]; then
|
||||||
|
echo -e "${green}3X-IPL ban log entries:${plain}"
|
||||||
if [[ -s "${iplimit_banned_log_path}" ]]; then
|
if [[ -s "${iplimit_banned_log_path}" ]]; then
|
||||||
cat ${iplimit_banned_log_path}
|
grep -v "INIT" "${iplimit_banned_log_path}" | tail -n 10 || echo -e "${yellow}No ban entries found${plain}"
|
||||||
else
|
else
|
||||||
echo -e "${red}Log file is empty.${plain}\n"
|
echo -e "${yellow}Ban log file is empty${plain}"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo -e "${red}Log file not found. Please Install Fail2ban and IP Limit first.${plain}\n"
|
echo -e "${red}Ban log file not found at: ${iplimit_banned_log_path}${plain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo -e "\n${green}Current jail status:${plain}"
|
||||||
|
fail2ban-client status 3x-ipl || echo -e "${yellow}Unable to get jail status${plain}"
|
||||||
}
|
}
|
||||||
|
|
||||||
bbr_menu() {
|
bbr_menu() {
|
||||||
@@ -445,11 +497,16 @@ bbr_menu() {
|
|||||||
;;
|
;;
|
||||||
1)
|
1)
|
||||||
enable_bbr
|
enable_bbr
|
||||||
|
bbr_menu
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
disable_bbr
|
disable_bbr
|
||||||
|
bbr_menu
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||||
|
bbr_menu
|
||||||
;;
|
;;
|
||||||
*) echo "Invalid choice" ;;
|
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +514,7 @@ disable_bbr() {
|
|||||||
|
|
||||||
if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf || ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
|
if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf || ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
|
||||||
echo -e "${yellow}BBR is not currently enabled.${plain}"
|
echo -e "${yellow}BBR is not currently enabled.${plain}"
|
||||||
exit 0
|
before_show_menu
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Replace BBR with CUBIC configurations
|
# Replace BBR with CUBIC configurations
|
||||||
@@ -478,7 +535,7 @@ disable_bbr() {
|
|||||||
enable_bbr() {
|
enable_bbr() {
|
||||||
if grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf && grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
|
if grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf && grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then
|
||||||
echo -e "${green}BBR is already enabled!${plain}"
|
echo -e "${green}BBR is already enabled!${plain}"
|
||||||
exit 0
|
before_show_menu
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check the OS and install necessary packages
|
# Check the OS and install necessary packages
|
||||||
@@ -524,7 +581,8 @@ update_shell() {
|
|||||||
before_show_menu
|
before_show_menu
|
||||||
else
|
else
|
||||||
chmod +x /usr/bin/x-ui
|
chmod +x /usr/bin/x-ui
|
||||||
LOGI "Upgrade script succeeded, Please rerun the script" && exit 0
|
LOGI "Upgrade script succeeded, Please rerun the script"
|
||||||
|
before_show_menu
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -636,17 +694,24 @@ firewall_menu() {
|
|||||||
;;
|
;;
|
||||||
1)
|
1)
|
||||||
open_ports
|
open_ports
|
||||||
|
firewall_menu
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
sudo ufw status
|
sudo ufw status
|
||||||
|
firewall_menu
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
delete_ports
|
delete_ports
|
||||||
|
firewall_menu
|
||||||
;;
|
;;
|
||||||
4)
|
4)
|
||||||
sudo ufw disable
|
sudo ufw disable
|
||||||
|
firewall_menu
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||||
|
firewall_menu
|
||||||
;;
|
;;
|
||||||
*) echo "Invalid choice" ;;
|
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -765,21 +830,25 @@ update_geo() {
|
|||||||
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||||
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||||
echo -e "${green}Loyalsoldier datasets have been updated successfully!${plain}"
|
echo -e "${green}Loyalsoldier datasets have been updated successfully!${plain}"
|
||||||
|
restart
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
rm -f geoip_IR.dat geosite_IR.dat
|
rm -f geoip_IR.dat geosite_IR.dat
|
||||||
wget -O geoip_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
wget -O geoip_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||||
wget -O geosite_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
wget -O geosite_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||||
echo -e "${green}chocolate4u datasets have been updated successfully!${plain}"
|
echo -e "${green}chocolate4u datasets have been updated successfully!${plain}"
|
||||||
|
restart
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
rm -f geoip_VN.dat geosite_VN.dat
|
rm -f geoip_VN.dat geosite_VN.dat
|
||||||
wget -O geoip_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat
|
wget -O geoip_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat
|
||||||
wget -O geosite_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
|
wget -O geosite_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
|
||||||
echo -e "${green}vuong2023 datasets have been updated successfully!${plain}"
|
echo -e "${green}vuong2023 datasets have been updated successfully!${plain}"
|
||||||
|
restart
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Invalid option selected! No updates made."
|
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||||
|
update_geo
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -823,6 +892,7 @@ ssl_cert_issue_main() {
|
|||||||
;;
|
;;
|
||||||
1)
|
1)
|
||||||
ssl_cert_issue
|
ssl_cert_issue
|
||||||
|
ssl_cert_issue_main
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||||
@@ -839,6 +909,7 @@ ssl_cert_issue_main() {
|
|||||||
echo "Invalid domain entered."
|
echo "Invalid domain entered."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
ssl_cert_issue_main
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||||
@@ -855,6 +926,7 @@ ssl_cert_issue_main() {
|
|||||||
echo "Invalid domain entered."
|
echo "Invalid domain entered."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
ssl_cert_issue_main
|
||||||
;;
|
;;
|
||||||
4)
|
4)
|
||||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||||
@@ -874,6 +946,7 @@ ssl_cert_issue_main() {
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
ssl_cert_issue_main
|
||||||
;;
|
;;
|
||||||
5)
|
5)
|
||||||
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||||
@@ -901,15 +974,19 @@ ssl_cert_issue_main() {
|
|||||||
echo "Invalid domain entered."
|
echo "Invalid domain entered."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
ssl_cert_issue_main
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
echo "Invalid choice"
|
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||||
|
ssl_cert_issue_main
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
ssl_cert_issue() {
|
ssl_cert_issue() {
|
||||||
|
local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
||||||
|
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
||||||
# check for acme.sh first
|
# check for acme.sh first
|
||||||
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
||||||
echo "acme.sh could not be found. we will install it"
|
echo "acme.sh could not be found. we will install it"
|
||||||
@@ -1028,6 +1105,7 @@ ssl_cert_issue() {
|
|||||||
LOGI "Panel paths set for domain: $domain"
|
LOGI "Panel paths set for domain: $domain"
|
||||||
LOGI " - Certificate File: $webCertFile"
|
LOGI " - Certificate File: $webCertFile"
|
||||||
LOGI " - Private Key File: $webKeyFile"
|
LOGI " - Private Key File: $webKeyFile"
|
||||||
|
echo -e "${green}Access URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}"
|
||||||
restart
|
restart
|
||||||
else
|
else
|
||||||
LOGE "Error: Certificate or private key file not found for domain: $domain."
|
LOGE "Error: Certificate or private key file not found for domain: $domain."
|
||||||
@@ -1038,76 +1116,119 @@ ssl_cert_issue() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ssl_cert_issue_CF() {
|
ssl_cert_issue_CF() {
|
||||||
echo -E ""
|
local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
||||||
LOGD "******Instructions for use******"
|
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
||||||
LOGI "This Acme script requires the following data:"
|
LOGI "****** Instructions for Use ******"
|
||||||
LOGI "1.Cloudflare Registered e-mail"
|
LOGI "Follow the steps below to complete the process:"
|
||||||
LOGI "2.Cloudflare Global API Key"
|
LOGI "1. Cloudflare Registered E-mail."
|
||||||
LOGI "3.The domain name that has been resolved dns to the current server by Cloudflare"
|
LOGI "2. Cloudflare Global API Key."
|
||||||
LOGI "4.The script applies for a certificate. The default installation path is /root/cert "
|
LOGI "3. The Domain Name."
|
||||||
confirm "Confirmed?[y/n]" "y"
|
LOGI "4. Once the certificate is issued, you will be prompted to set the certificate for the panel (optional)."
|
||||||
|
LOGI "5. The script also supports automatic renewal of the SSL certificate after installation."
|
||||||
|
|
||||||
|
confirm "Do you confirm the information and wish to proceed? [y/n]" "y"
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
# check for acme.sh first
|
# Check for acme.sh first
|
||||||
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then
|
||||||
echo "acme.sh could not be found. we will install it"
|
echo "acme.sh could not be found. We will install it."
|
||||||
install_acme
|
install_acme
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "install acme failed, please check logs"
|
LOGE "Install acme failed, please check logs."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CF_Domain=""
|
CF_Domain=""
|
||||||
CF_GlobalKey=""
|
certPath="/root/cert-CF"
|
||||||
CF_AccountEmail=""
|
|
||||||
certPath=/root/cert
|
|
||||||
if [ ! -d "$certPath" ]; then
|
if [ ! -d "$certPath" ]; then
|
||||||
mkdir $certPath
|
mkdir -p $certPath
|
||||||
else
|
else
|
||||||
rm -rf $certPath
|
rm -rf $certPath
|
||||||
mkdir $certPath
|
mkdir -p $certPath
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LOGD "Please set a domain name:"
|
LOGD "Please set a domain name:"
|
||||||
read -p "Input your domain here:" CF_Domain
|
read -p "Input your domain here: " CF_Domain
|
||||||
LOGD "Your domain name is set to:${CF_Domain}"
|
LOGD "Your domain name is set to: ${CF_Domain}"
|
||||||
|
|
||||||
|
# Set up Cloudflare API details
|
||||||
|
CF_GlobalKey=""
|
||||||
|
CF_AccountEmail=""
|
||||||
LOGD "Please set the API key:"
|
LOGD "Please set the API key:"
|
||||||
read -p "Input your key here:" CF_GlobalKey
|
read -p "Input your key here: " CF_GlobalKey
|
||||||
LOGD "Your API key is:${CF_GlobalKey}"
|
LOGD "Your API key is: ${CF_GlobalKey}"
|
||||||
|
|
||||||
LOGD "Please set up registered email:"
|
LOGD "Please set up registered email:"
|
||||||
read -p "Input your email here:" CF_AccountEmail
|
read -p "Input your email here: " CF_AccountEmail
|
||||||
LOGD "Your registered email address is:${CF_AccountEmail}"
|
LOGD "Your registered email address is: ${CF_AccountEmail}"
|
||||||
|
|
||||||
|
# Set the default CA to Let's Encrypt
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Default CA, Lets'Encrypt fail, script exiting..."
|
LOGE "Default CA, Let'sEncrypt fail, script exiting..."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export CF_Key="${CF_GlobalKey}"
|
export CF_Key="${CF_GlobalKey}"
|
||||||
export CF_Email=${CF_AccountEmail}
|
export CF_Email="${CF_AccountEmail}"
|
||||||
|
|
||||||
|
# Issue the certificate using Cloudflare DNS
|
||||||
~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log
|
~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Certificate issuance failed, script exiting..."
|
LOGE "Certificate issuance failed, script exiting..."
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
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 \
|
|
||||||
--cert-file /root/cert/${CF_Domain}.cer --key-file /root/cert/${CF_Domain}.key \
|
# Install the certificate
|
||||||
--fullchain-file /root/cert/fullchain.cer
|
mkdir -p ${certPath}/${CF_Domain}
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
LOGE "Failed to create directory: ${certPath}/${CF_Domain}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} \
|
||||||
|
--fullchain-file ${certPath}/${CF_Domain}/fullchain.pem \
|
||||||
|
--key-file ${certPath}/${CF_Domain}/privkey.pem
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Certificate installation failed, script exiting..."
|
LOGE "Certificate installation failed, script exiting..."
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
LOGI "Certificate installed Successfully,Turning on automatic updates..."
|
LOGI "Certificate installed successfully, Turning on automatic updates..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Enable auto-update
|
||||||
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Auto update setup Failed, script exiting..."
|
LOGE "Auto update setup failed, script exiting..."
|
||||||
ls -lah cert
|
|
||||||
chmod 755 $certPath
|
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
LOGI "The certificate is installed and auto-renewal is turned on, Specific information is as follows"
|
LOGI "The certificate is installed and auto-renewal is turned on. Specific information is as follows:"
|
||||||
ls -lah cert
|
ls -lah ${certPath}/${CF_Domain}
|
||||||
chmod 755 $certPath
|
chmod 755 ${certPath}/${CF_Domain}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prompt user to set panel paths after successful certificate installation
|
||||||
|
read -p "Would you like to set this certificate for the panel? (y/n): " setPanel
|
||||||
|
if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
|
||||||
|
local webCertFile="${certPath}/${CF_Domain}/fullchain.pem"
|
||||||
|
local webKeyFile="${certPath}/${CF_Domain}/privkey.pem"
|
||||||
|
|
||||||
|
if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then
|
||||||
|
/usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile"
|
||||||
|
LOGI "Panel paths set for domain: $CF_Domain"
|
||||||
|
LOGI " - Certificate File: $webCertFile"
|
||||||
|
LOGI " - Private Key File: $webKeyFile"
|
||||||
|
echo -e "${green}Access URL: https://${CF_Domain}:${existing_port}${existing_webBasePath}${plain}"
|
||||||
|
restart
|
||||||
|
else
|
||||||
|
LOGE "Error: Certificate or private key file not found for domain: $CF_Domain."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
LOGI "Skipping panel path setting."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
show_menu
|
show_menu
|
||||||
@@ -1117,36 +1238,44 @@ ssl_cert_issue_CF() {
|
|||||||
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, determine installation method
|
||||||
local pkg_manager=""
|
if command -v snap &>/dev/null; then
|
||||||
local speedtest_install_script=""
|
# Use snap to install Speedtest
|
||||||
|
echo "Installing Speedtest using snap..."
|
||||||
if command -v dnf &>/dev/null; then
|
snap install speedtest
|
||||||
pkg_manager="dnf"
|
|
||||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
|
|
||||||
elif command -v yum &>/dev/null; then
|
|
||||||
pkg_manager="yum"
|
|
||||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
|
|
||||||
elif command -v apt-get &>/dev/null; then
|
|
||||||
pkg_manager="apt-get"
|
|
||||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
|
|
||||||
elif command -v apt &>/dev/null; then
|
|
||||||
pkg_manager="apt"
|
|
||||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z $pkg_manager ]]; then
|
|
||||||
echo "Error: Package manager not found. You may need to install Speedtest manually."
|
|
||||||
return 1
|
|
||||||
else
|
else
|
||||||
curl -s $speedtest_install_script | bash
|
# Fallback to using package managers
|
||||||
$pkg_manager install -y speedtest
|
local pkg_manager=""
|
||||||
|
local speedtest_install_script=""
|
||||||
|
|
||||||
|
if command -v dnf &>/dev/null; then
|
||||||
|
pkg_manager="dnf"
|
||||||
|
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
|
||||||
|
elif command -v yum &>/dev/null; then
|
||||||
|
pkg_manager="yum"
|
||||||
|
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
|
||||||
|
elif command -v apt-get &>/dev/null; then
|
||||||
|
pkg_manager="apt-get"
|
||||||
|
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
|
||||||
|
elif command -v apt &>/dev/null; then
|
||||||
|
pkg_manager="apt"
|
||||||
|
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $pkg_manager ]]; then
|
||||||
|
echo "Error: Package manager not found. You may need to install Speedtest manually."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
echo "Installing Speedtest using $pkg_manager..."
|
||||||
|
curl -s $speedtest_install_script | bash
|
||||||
|
$pkg_manager install -y speedtest
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Run Speedtest
|
|
||||||
speedtest
|
speedtest
|
||||||
}
|
}
|
||||||
|
|
||||||
create_iplimit_jails() {
|
create_iplimit_jails() {
|
||||||
# Use default bantime if not passed => 15 minutes
|
# Use default bantime if not passed => 15 minutes
|
||||||
local bantime="${1:-15}"
|
local bantime="${1:-15}"
|
||||||
@@ -1154,7 +1283,7 @@ create_iplimit_jails() {
|
|||||||
# Uncomment 'allowipv6 = auto' in fail2ban.conf
|
# Uncomment 'allowipv6 = auto' in fail2ban.conf
|
||||||
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
|
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
|
||||||
|
|
||||||
#On Debian 12+ fail2ban's default backend should be changed to systemd
|
# On Debian 12+ fail2ban's default backend should be changed to systemd
|
||||||
if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then
|
if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then
|
||||||
sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf
|
sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf
|
||||||
fi
|
fi
|
||||||
@@ -1180,7 +1309,7 @@ EOF
|
|||||||
|
|
||||||
cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
|
cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
|
||||||
[INCLUDES]
|
[INCLUDES]
|
||||||
before = iptables-allports.conf
|
before = iptables-common.conf
|
||||||
|
|
||||||
[Definition]
|
[Definition]
|
||||||
actionstart = <iptables> -N f2b-<name>
|
actionstart = <iptables> -N f2b-<name>
|
||||||
@@ -1200,6 +1329,9 @@ actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
|
|||||||
echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = <F-USER> [IP] = <ip> unbanned." >> ${iplimit_banned_log_path}
|
echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = <F-USER> [IP] = <ip> unbanned." >> ${iplimit_banned_log_path}
|
||||||
|
|
||||||
[Init]
|
[Init]
|
||||||
|
name = default
|
||||||
|
protocol = tcp
|
||||||
|
chain = INPUT
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo -e "${green}Ip Limit jail files created with a bantime of ${bantime} minutes.${plain}"
|
echo -e "${green}Ip Limit jail files created with a bantime of ${bantime} minutes.${plain}"
|
||||||
@@ -1224,10 +1356,11 @@ iplimit_main() {
|
|||||||
echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
|
echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
|
||||||
echo -e "${green}\t2.${plain} Change Ban Duration"
|
echo -e "${green}\t2.${plain} Change Ban Duration"
|
||||||
echo -e "${green}\t3.${plain} Unban Everyone"
|
echo -e "${green}\t3.${plain} Unban Everyone"
|
||||||
echo -e "${green}\t4.${plain} Check Logs"
|
echo -e "${green}\t4.${plain} Ban Logs"
|
||||||
echo -e "${green}\t5.${plain} Fail2ban Status"
|
echo -e "${green}\t5.${plain} Real-Time Logs"
|
||||||
echo -e "${green}\t6.${plain} Restart Fail2ban"
|
echo -e "${green}\t6.${plain} Service Status"
|
||||||
echo -e "${green}\t7.${plain} Uninstall Fail2ban"
|
echo -e "${green}\t7.${plain} Service Restart"
|
||||||
|
echo -e "${green}\t8.${plain} Uninstall Fail2ban and IP Limit"
|
||||||
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
|
||||||
@@ -1266,17 +1399,28 @@ iplimit_main() {
|
|||||||
;;
|
;;
|
||||||
4)
|
4)
|
||||||
show_banlog
|
show_banlog
|
||||||
|
iplimit_main
|
||||||
;;
|
;;
|
||||||
5)
|
5)
|
||||||
service fail2ban status
|
tail -f /var/log/fail2ban.log
|
||||||
|
iplimit_main
|
||||||
;;
|
;;
|
||||||
6)
|
6)
|
||||||
systemctl restart fail2ban
|
service fail2ban status
|
||||||
|
iplimit_main
|
||||||
;;
|
;;
|
||||||
7)
|
7)
|
||||||
remove_iplimit
|
systemctl restart fail2ban
|
||||||
|
iplimit_main
|
||||||
|
;;
|
||||||
|
8)
|
||||||
|
remove_iplimit
|
||||||
|
iplimit_main
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||||
|
iplimit_main
|
||||||
;;
|
;;
|
||||||
*) echo "Invalid choice" ;;
|
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1357,7 +1501,7 @@ install_iplimit() {
|
|||||||
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} Back to Main Menu"
|
||||||
read -p "Choose an option: " num
|
read -p "Choose an option: " num
|
||||||
case "$num" in
|
case "$num" in
|
||||||
1)
|
1)
|
||||||
@@ -1397,8 +1541,7 @@ remove_iplimit() {
|
|||||||
before_show_menu
|
before_show_menu
|
||||||
;;
|
;;
|
||||||
0)
|
0)
|
||||||
echo -e "${yellow}Cancelled.${plain}\n"
|
show_menu
|
||||||
iplimit_main
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||||
@@ -1407,6 +1550,83 @@ remove_iplimit() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SSH_port_forwarding() {
|
||||||
|
local server_ip=$(curl -s https://api.ipify.org)
|
||||||
|
local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
||||||
|
local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
||||||
|
local existing_listenIP=$(/usr/local/x-ui/x-ui setting -getListen true | grep -Eo 'listenIP: .+' | awk '{print $2}')
|
||||||
|
local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'cert: .+' | awk '{print $2}')
|
||||||
|
local existing_key=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'key: .+' | awk '{print $2}')
|
||||||
|
|
||||||
|
local config_listenIP=""
|
||||||
|
local listen_choice=""
|
||||||
|
|
||||||
|
if [[ -n "$existing_cert" && -n "$existing_key" ]]; then
|
||||||
|
echo -e "${green}Panel is secure with SSL.${plain}"
|
||||||
|
before_show_menu
|
||||||
|
fi
|
||||||
|
if [[ -z "$existing_cert" && -z "$existing_key" && (-z "$existing_listenIP" || "$existing_listenIP" == "0.0.0.0") ]]; then
|
||||||
|
echo -e "\n${red}Warning: No Cert and Key found! The panel is not secure.${plain}"
|
||||||
|
echo "Please obtain a certificate or set up SSH port forwarding."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$existing_listenIP" && "$existing_listenIP" != "0.0.0.0" && (-z "$existing_cert" && -z "$existing_key") ]]; then
|
||||||
|
echo -e "\n${green}Current SSH Port Forwarding Configuration:${plain}"
|
||||||
|
echo -e "Standard SSH command:"
|
||||||
|
echo -e "${yellow}ssh -L 2222:${existing_listenIP}:${existing_port} root@${server_ip}${plain}"
|
||||||
|
echo -e "\nIf using SSH key:"
|
||||||
|
echo -e "${yellow}ssh -i <sshkeypath> -L 2222:${existing_listenIP}:${existing_port} root@${server_ip}${plain}"
|
||||||
|
echo -e "\nAfter connecting, access the panel at:"
|
||||||
|
echo -e "${yellow}http://localhost:2222${existing_webBasePath}${plain}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\nChoose an option:"
|
||||||
|
echo -e "${green}1.${plain} Set listen IP"
|
||||||
|
echo -e "${green}2.${plain} Clear listen IP"
|
||||||
|
echo -e "${green}0.${plain} Back to Main Menu"
|
||||||
|
read -p "Choose an option: " num
|
||||||
|
|
||||||
|
case "$num" in
|
||||||
|
1)
|
||||||
|
if [[ -z "$existing_listenIP" || "$existing_listenIP" == "0.0.0.0" ]]; then
|
||||||
|
echo -e "\nNo listenIP configured. Choose an option:"
|
||||||
|
echo -e "1. Use default IP (127.0.0.1)"
|
||||||
|
echo -e "2. Set a custom IP"
|
||||||
|
read -p "Select an option (1 or 2): " listen_choice
|
||||||
|
|
||||||
|
config_listenIP="127.0.0.1"
|
||||||
|
[[ "$listen_choice" == "2" ]] && read -p "Enter custom IP to listen on: " config_listenIP
|
||||||
|
|
||||||
|
/usr/local/x-ui/x-ui setting -listenIP "${config_listenIP}" >/dev/null 2>&1
|
||||||
|
echo -e "${green}listen IP has been set to ${config_listenIP}.${plain}"
|
||||||
|
echo -e "\n${green}SSH Port Forwarding Configuration:${plain}"
|
||||||
|
echo -e "Standard SSH command:"
|
||||||
|
echo -e "${yellow}ssh -L 2222:${config_listenIP}:${existing_port} root@${server_ip}${plain}"
|
||||||
|
echo -e "\nIf using SSH key:"
|
||||||
|
echo -e "${yellow}ssh -i <sshkeypath> -L 2222:${config_listenIP}:${existing_port} root@${server_ip}${plain}"
|
||||||
|
echo -e "\nAfter connecting, access the panel at:"
|
||||||
|
echo -e "${yellow}http://localhost:2222${existing_webBasePath}${plain}"
|
||||||
|
restart
|
||||||
|
else
|
||||||
|
config_listenIP="${existing_listenIP}"
|
||||||
|
echo -e "${green}Current listen IP is already set to ${config_listenIP}.${plain}"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
/usr/local/x-ui/x-ui setting -listenIP 0.0.0.0 >/dev/null 2>&1
|
||||||
|
echo -e "${green}Listen IP has been cleared.${plain}"
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
0)
|
||||||
|
show_menu
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||||
|
SSH_port_forwarding
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
show_usage() {
|
show_usage() {
|
||||||
echo "x-ui control menu usages: "
|
echo "x-ui control menu usages: "
|
||||||
echo "------------------------------------------"
|
echo "------------------------------------------"
|
||||||
@@ -1436,7 +1656,7 @@ show_menu() {
|
|||||||
${green}1.${plain} Install
|
${green}1.${plain} Install
|
||||||
${green}2.${plain} Update
|
${green}2.${plain} Update
|
||||||
${green}3.${plain} Update Menu
|
${green}3.${plain} Update Menu
|
||||||
${green}4.${plain} Custom Version
|
${green}4.${plain} Legacy Version
|
||||||
${green}5.${plain} Uninstall
|
${green}5.${plain} Uninstall
|
||||||
————————————————
|
————————————————
|
||||||
${green}6.${plain} Reset Username & Password & Secret Token
|
${green}6.${plain} Reset Username & Password & Secret Token
|
||||||
@@ -1449,7 +1669,7 @@ show_menu() {
|
|||||||
${green}12.${plain} Stop
|
${green}12.${plain} Stop
|
||||||
${green}13.${plain} Restart
|
${green}13.${plain} Restart
|
||||||
${green}14.${plain} Check Status
|
${green}14.${plain} Check Status
|
||||||
${green}15.${plain} Check Logs
|
${green}15.${plain} Logs Management
|
||||||
————————————————
|
————————————————
|
||||||
${green}16.${plain} Enable Autostart
|
${green}16.${plain} Enable Autostart
|
||||||
${green}17.${plain} Disable Autostart
|
${green}17.${plain} Disable Autostart
|
||||||
@@ -1458,13 +1678,14 @@ show_menu() {
|
|||||||
${green}19.${plain} Cloudflare SSL Certificate
|
${green}19.${plain} Cloudflare SSL Certificate
|
||||||
${green}20.${plain} IP Limit Management
|
${green}20.${plain} IP Limit Management
|
||||||
${green}21.${plain} Firewall Management
|
${green}21.${plain} Firewall Management
|
||||||
|
${green}22.${plain} SSH Port Forwarding Management
|
||||||
————————————————
|
————————————————
|
||||||
${green}22.${plain} Enable BBR
|
${green}23.${plain} Enable BBR
|
||||||
${green}23.${plain} Update Geo Files
|
${green}24.${plain} Update Geo Files
|
||||||
${green}24.${plain} Speedtest by Ookla
|
${green}25.${plain} Speedtest by Ookla
|
||||||
"
|
"
|
||||||
show_status
|
show_status
|
||||||
echo && read -p "Please enter your selection [0-24]: " num
|
echo && read -p "Please enter your selection [0-25]: " num
|
||||||
|
|
||||||
case "${num}" in
|
case "${num}" in
|
||||||
0)
|
0)
|
||||||
@@ -1480,7 +1701,7 @@ show_menu() {
|
|||||||
check_install && update_menu
|
check_install && update_menu
|
||||||
;;
|
;;
|
||||||
4)
|
4)
|
||||||
check_install && custom_version
|
check_install && legacy_version
|
||||||
;;
|
;;
|
||||||
5)
|
5)
|
||||||
check_install && uninstall
|
check_install && uninstall
|
||||||
@@ -1534,16 +1755,19 @@ show_menu() {
|
|||||||
firewall_menu
|
firewall_menu
|
||||||
;;
|
;;
|
||||||
22)
|
22)
|
||||||
bbr_menu
|
SSH_port_forwarding
|
||||||
;;
|
;;
|
||||||
23)
|
23)
|
||||||
update_geo
|
bbr_menu
|
||||||
;;
|
;;
|
||||||
24)
|
24)
|
||||||
|
update_geo
|
||||||
|
;;
|
||||||
|
25)
|
||||||
run_speedtest
|
run_speedtest
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
LOGE "Please enter the correct number [0-24]"
|
LOGE "Please enter the correct number [0-25]"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -1580,8 +1804,8 @@ if [[ $# > 0 ]]; then
|
|||||||
"update")
|
"update")
|
||||||
check_install 0 && update 0
|
check_install 0 && update 0
|
||||||
;;
|
;;
|
||||||
"custom")
|
"legacy")
|
||||||
check_install 0 && custom_version 0
|
check_install 0 && legacy_version 0
|
||||||
;;
|
;;
|
||||||
"install")
|
"install")
|
||||||
check_uninstall 0 && install 0
|
check_uninstall 0 && install 0
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ func (x *XrayAPI) AddUser(Protocol string, inboundTag string, user map[string]in
|
|||||||
CipherType: ssCipherType,
|
CipherType: ssCipherType,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
account = serial.ToTypedMessage(&shadowsocks_2022.User{
|
account = serial.ToTypedMessage(&shadowsocks_2022.ServerConfig{
|
||||||
Key: user["password"].(string),
|
Key: user["password"].(string),
|
||||||
Email: user["email"].(string),
|
Email: user["email"].(string),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ func (p *process) Start() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failure in running xray-core:", err)
|
logger.Error("Failure in running xray-core:", err)
|
||||||
p.exitErr = err
|
p.exitErr = err
|
||||||
|
p.witeCrachReport(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -241,3 +242,8 @@ func (p *process) Stop() error {
|
|||||||
}
|
}
|
||||||
return p.cmd.Process.Signal(syscall.SIGTERM)
|
return p.cmd.Process.Signal(syscall.SIGTERM)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *process) witeCrachReport(err error) error {
|
||||||
|
crashReportPath := config.GetBinFolderPath() + "/core_crash_" + time.Now().Format("20060102_150405") + ".log"
|
||||||
|
return os.WriteFile(crashReportPath, []byte(err.Error()), os.ModePerm)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user