Compare commits

...

91 Commits

Author SHA1 Message Date
mhsanaei
80cfbefd75 v2.4.3 2024-10-01 09:41:12 +02:00
mhsanaei
f2ee18235f update dependencies 2024-10-01 09:40:03 +02:00
mhsanaei
f48df1e5c0 Xray Core v24.9.30 2024-10-01 09:35:06 +02:00
mhsanaei
b24855082e RAW as an alias of TCP 2024-10-01 09:34:13 +02:00
Vyacheslav Scherbinin
27434f3235 Fix toasts (#2571)
* Add space to toast status messages

* Removed opening space from translations
2024-09-30 17:06:38 +02:00
mhsanaei
cdb6eac0e6 Reality - min,max client (not active) 2024-09-27 14:57:04 +02:00
mhsanaei
9e13513205 Temporarily disabled Allocate 2024-09-27 14:37:57 +02:00
mhsanaei
b09f52357c update block and direct connection 2024-09-27 13:34:12 +02:00
mhsanaei
ac08e86747 inbound - better view 2024-09-26 16:20:35 +02:00
mhsanaei
c9c8abe97b refactor: split XTLS and Reality form tls into separate files 2024-09-26 16:01:58 +02:00
mhsanaei
0b8beafc89 DNS - Expect IPs 2024-09-26 13:08:54 +02:00
mhsanaei
8b6e3491c4 base install for amzn 2024-09-26 12:19:18 +02:00
dependabot[bot]
6cf2b56f9b Bump github.com/valyala/fasthttp from 1.55.0 to 1.56.0 (#2566)
Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.55.0 to 1.56.0.
- [Release notes](https://github.com/valyala/fasthttp/releases)
- [Commits](https://github.com/valyala/fasthttp/compare/v1.55.0...v1.56.0)

---
updated-dependencies:
- dependency-name: github.com/valyala/fasthttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-25 13:07:10 +02:00
mhsanaei
19b95829e0 OS Support - AlmaLinux 8.0 + 2024-09-25 11:31:07 +02:00
mhsanaei
4bea427c79 minor change 2024-09-25 10:40:21 +02:00
Sanaei
da7e4d51d6 OS Support - Amazon Linux + AlmaLinux 8 2024-09-25 10:13:54 +02:00
mhsanaei
43713fbdf8 v2.4.2 2024-09-24 15:15:29 +02:00
mhsanaei
dc29e858c9 Xray Core v24.9.19 2024-09-24 15:15:19 +02:00
mhsanaei
c30c6f08f3 Direct Sub Json - geoIP, geoSite 2024-09-24 15:11:16 +02:00
mhsanaei
7c892ac051 Iplimit - warning improved 2024-09-24 13:24:10 +02:00
mhsanaei
75dd7b93f5 update dependencies 2024-09-24 12:19:08 +02:00
mhsanaei
d5de8e1bf3 removeNoise - hide when only one noise 2024-09-24 12:18:42 +02:00
mhsanaei
16b4795956 removed - timeout 2024-09-24 12:00:37 +02:00
Pavel Kogen
e5835c299c OS Support - Amazon Linux (#2564) 2024-09-24 11:53:12 +02:00
mhsanaei
4fdef3cfde add or remove noise 2024-09-24 11:38:10 +02:00
dependabot[bot]
cf6a8bd463 Bump google.golang.org/grpc from 1.66.2 to 1.67.0 (#2563)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.66.2 to 1.67.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.66.2...v1.67.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 11:45:21 +02:00
mhsanaei
59a84e844c HTTP - Allow Transparent 2024-09-17 11:37:24 +02:00
mhsanaei
6b0c9a5fad update noise to noises
+ type
2024-09-17 09:51:57 +02:00
mhsanaei
77edea5419 v2.4.1 2024-09-16 17:30:53 +02:00
mhsanaei
521870df0a minor changes 2024-09-16 16:49:00 +02:00
mhsanaei
dc1c1eb998 Xray Core v24.9.16 2024-09-16 16:48:44 +02:00
mhsanaei
e78427245a New - splithttp (xmux) 2024-09-16 16:47:59 +02:00
mhsanaei
fbcab5bc52 login page - more width for lang 2024-09-16 13:52:21 +02:00
mhsanaei
89c79c3ec3 more details - vmess security 2024-09-16 13:51:36 +02:00
mhsanaei
566cd9e9c4 New - Allocate 2024-09-16 11:41:21 +02:00
mhsanaei
ad78cec7c7 fix mistake - security only for vmess 2024-09-16 10:37:02 +02:00
mhsanaei
176ab5f48e New - DNS Outbound (nonIPQuery, blockTypes) 2024-09-16 10:30:51 +02:00
laperuz92
9c4fa23931 Some README updates (#2562)
* Update README.ru_RU.md

Fixed some typos here and there

* Add info about docker image autoupdate

* Update Russian translation

* Add info on Portuguese (Brazip) translation
2024-09-13 11:25:57 +02:00
mhsanaei
2938694c45 geosite:category-ru and ir 2024-09-13 11:15:55 +02:00
mhsanaei
88d0fb9753 Reality - maxTimediff 2024-09-13 09:39:57 +02:00
mhsanaei
d23a7f81ef freedom - default value for timeout
I've seen many users make the mistake of not setting the timeout value in Xray, which causes errors. Then, they mistakenly assume that 3x-ui has a bug.
2024-09-13 09:21:37 +02:00
mhsanaei
2e363445fc update dependencies 2024-09-12 11:58:21 +02:00
mhsanaei
33d983bc20 New - maskAddress , dnslog 2024-09-12 11:44:13 +02:00
mhsanaei
3e7c7831bc Email Validation - new pattern
@ included
2024-09-12 10:07:53 +02:00
mhsanaei
374d49eb92 Iplimit - improved
Ensure accurate extraction of email.
Access logs are needed when the IP limit feature is active.
2024-09-12 09:44:17 +02:00
mhsanaei
663cf5649f Session - default 60 minute (minimum) 2024-09-12 09:41:24 +02:00
dependabot[bot]
095ebccbb0 Bump google.golang.org/grpc from 1.66.0 to 1.66.1 (#2558)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.66.0 to 1.66.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.66.0...v1.66.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-10 11:19:10 +02:00
dependabot[bot]
f4bb6b0517 Bump gorm.io/gorm from 1.25.11 to 1.25.12 (#2543)
Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.25.11 to 1.25.12.
- [Release notes](https://github.com/go-gorm/gorm/releases)
- [Commits](https://github.com/go-gorm/gorm/compare/v1.25.11...v1.25.12)

---
updated-dependencies:
- dependency-name: gorm.io/gorm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-09 17:58:47 +02:00
mhsanaei
575234ac37 v2.4.0 2024-09-09 10:42:20 +02:00
mhsanaei
16cbd86371 update dependencies 2024-09-09 10:35:43 +02:00
mhsanaei
2d3666e339 Xray core v24.9.7 2024-09-09 10:35:39 +02:00
mhsanaei
47987b7785 update readme 2024-09-09 10:31:55 +02:00
mhsanaei
2e1461e6dc remove warning for access log
because you can't see the iplimit when there is no path for access log :D
2024-09-09 09:57:30 +02:00
mhsanaei
272457740f warp script removed
we don't need it
2024-09-09 09:48:48 +02:00
mhsanaei
58c721e7d2 quic removed 2024-09-09 09:46:39 +02:00
mhsanaei
b4baf35ed8 Update Email Validation 2024-09-05 15:16:15 +02:00
mhsanaei
2001d96148 iplimit - ipv6 support 2024-09-05 13:59:30 +02:00
mhsanaei
5c390341fb v2.3.15 2024-09-05 12:06:16 +02:00
mhsanaei
c8b1b162ff update dependencies 2024-09-05 10:21:37 +02:00
mhsanaei
a55645584b new - Português (Brazil) langs 2024-09-05 10:02:32 +02:00
dependabot[bot]
f536307914 Bump github.com/shirou/gopsutil/v4 from 4.24.7 to 4.24.8 (#2508)
Bumps [github.com/shirou/gopsutil/v4](https://github.com/shirou/gopsutil) from 4.24.7 to 4.24.8.
- [Release notes](https://github.com/shirou/gopsutil/releases)
- [Commits](https://github.com/shirou/gopsutil/compare/v4.24.7...v4.24.8)

---
updated-dependencies:
- dependency-name: github.com/shirou/gopsutil/v4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-02 12:02:32 +02:00
Dmitry Zhavoronkov
4494ffabb6 fix TypeError: this.shortIds.split is not a function (#2507)
* fix TypeError: this.shortIds.split is not a function
2024-09-02 12:02:15 +02:00
mhsanaei
2dc59a601c fix restart after enabling user
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-09-02 10:26:19 +02:00
mhsanaei
4ad04e2032 [fragment] manual config for packets
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-09-02 10:24:02 +02:00
mhsanaei
f2ebe128cc freedom redirect
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
2024-09-02 10:23:10 +02:00
mhsanaei
f0165c1ad8 v2.3.14 2024-08-30 10:28:01 +02:00
mhsanaei
898f80cb30 Xray Core v1.8.24 2024-08-30 09:25:49 +02:00
Rizvan Nukhtarov
5d7de0858c better autocomplete for password and token inputs (#2505) 2024-08-30 09:16:34 +02:00
bigbug
c0b88fe736 Nginx reverse proxy setting (#2504)
* Nginx reverse proxy web

Reverse proxy root path or subpath

* Update README.md

* Fix ru_RU doc translation

* Fix translation issues

Fix similar problems with other translations
2024-08-30 09:16:13 +02:00
mhsanaei
fa43248e30 New - Noise
freedom
2024-08-29 11:27:43 +02:00
mhsanaei
cb3da25bc8 bug fix - TLS
disableSystemRoot & enableSessionResumption
2024-08-29 11:06:48 +02:00
dependabot[bot]
a40058bb0b Bump google.golang.org/grpc from 1.65.0 to 1.66.0 (#2502)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.65.0 to 1.66.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.65.0...v1.66.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-29 09:08:14 +02:00
Ilya
6ab3bbe7bd Correct Russian README (#2503)
* Remove trailing whitespace in READMEs

* Correct Russian README and make it more natual

Co-authored-by: Viacheslav64 <74322784+Viacheslav64@users.noreply.github.com>

---------

Co-Authored-By: Viacheslav64 <74322784+Viacheslav64@users.noreply.github.com>
2024-08-29 09:07:42 +02:00
bigbug
9e73c82eb3 Fix Language Code for CN (#2501) 2024-08-28 11:30:49 +02:00
bigbug
6b3b1b6cbc GO v1.23.0 , docker (#2500)
docker build error
2024-08-28 09:15:48 +02:00
mhsanaei
b3b433f84b Русский README 2024-08-27 10:32:06 +02:00
mhsanaei
7f16a53309 GO v1.23.0 + update dependencies 2024-08-27 09:37:07 +02:00
mhsanaei
2471bda211 New - Splithttp (xPaddingBytes) 2024-08-19 00:13:07 +02:00
mhsanaei
cd49971535 update translate for #2493 2024-08-18 23:52:29 +02:00
dependabot[bot]
0013f8989b Bump github.com/mymmrac/telego from 0.31.0 to 0.31.1 (#2492)
Bumps [github.com/mymmrac/telego](https://github.com/mymmrac/telego) from 0.31.0 to 0.31.1.
- [Release notes](https://github.com/mymmrac/telego/releases)
- [Commits](https://github.com/mymmrac/telego/compare/v0.31.0...v0.31.1)

---
updated-dependencies:
- dependency-name: github.com/mymmrac/telego
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-18 23:31:23 +02:00
Rizvan Nukhtarov
de8c80597f New - TGbot, "All clients" button (#2493) 2024-08-18 23:30:56 +02:00
Konstantin Larin
9dcc55ea1b Update Russian translate (#2494) 2024-08-18 23:29:39 +02:00
Matin
8255390131 Update Persian translate (#2495)
* Adjust translates for persian

* Also add spacing for expreIn key

* Adjust spacing agian for expireIn (thanks to vscode for breaking it)
2024-08-18 23:29:10 +02:00
Ilya
438a9684ee Fix #2470 (The subscription service gives two ports for VLESS) (#2479)
* Fix #2470
2024-08-11 18:26:43 +02:00
mhsanaei
a6000f22a2 v2.3.13 2024-08-11 12:43:47 +02:00
mhsanaei
2ec5eeb442 Dokodemo - default timeout to 30s 2024-08-11 12:41:21 +02:00
mhsanaei
d319476eb6 splithttp - change default to accept range (better upload now) 2024-08-11 11:38:59 +02:00
mhsanaei
93d52bc86c new - vmess security (inbound client side - outbound) 2024-08-11 00:47:44 +02:00
mhsanaei
bda5c2c915 small changes 2024-08-08 17:40:26 +02:00
mhsanaei
b838fe2e74 update dependencies 2024-08-08 17:39:50 +02:00
mhsanaei
604b9be4a0 Fix domain validation for Nginx/CDN compatibility #2450 2024-08-08 17:39:12 +02:00
68 changed files with 3162 additions and 1492 deletions

View File

@@ -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/v1.8.23/" Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.9.30/"
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

View File

@@ -27,7 +27,7 @@ case $1 in
esac esac
mkdir -p build/bin mkdir -p build/bin
cd build/bin cd build/bin
wget "https://github.com/XTLS/Xray-core/releases/download/v1.8.23/Xray-linux-${ARCH}.zip" wget "https://github.com/XTLS/Xray-core/releases/download/v24.9.30/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}"

View File

@@ -1,7 +1,7 @@
# ======================================================== # ========================================================
# Stage: Builder # Stage: Builder
# ======================================================== # ========================================================
FROM golang:1.22-alpine AS builder FROM golang:1.23-alpine AS builder
WORKDIR /app WORKDIR /app
ARG TARGETARCH ARG TARGETARCH

View File

@@ -1,4 +1,4 @@
[English](/README.md) | [Chinese](/README.zh.md) | [Español](/README.es_ES.md) [English](/README.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p> <p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p>
@@ -32,10 +32,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## Instalar una Versión Personalizada ## Instalar una Versión Personalizada
Para instalar la versión deseada, agrega la versión al final del comando de instalación. Por ejemplo, ver `v2.3.12`: Para instalar la versión deseada, agrega la versión al final del comando de instalación. Por ejemplo, ver `v2.4.3`:
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.3.12 bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.3
``` ```
## Certificado SSL ## Certificado SSL
@@ -177,6 +177,41 @@ eliminar 3x-ui de docker
</details> </details>
## Configuración de Nginx
<details>
<summary>Haga clic aquí para configurar el proxy inverso</summary>
#### Proxy inverso Nginx
```nginx
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_redirect off;
proxy_pass http://127.0.0.1:2053;
}
```
#### Nginx sub-path
- EAsegúrese de que la "Ruta Raíz de la URL del Panel" en la configuración del panel `/sub` es la misma.
- El `url` en la configuración del panel debe terminar con `/`.
```nginx
location /sub {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_redirect off;
proxy_pass http://127.0.0.1:2053;
}
```
</details>
## SO Recomendados ## SO Recomendados
@@ -185,11 +220,14 @@ eliminar 3x-ui de docker
- CentOS 8+ - CentOS 8+
- Fedora 36+ - Fedora 36+
- Arch Linux - Arch Linux
- Parch Linux
- Manjaro - Manjaro
- Armbian - Armbian
- AlmaLinux 9+ - AlmaLinux 8.0+
- Rockylinux 9+ - Rocky Linux 8+
- Oracle Linux 8+
- OpenSUSE Tubleweed - OpenSUSE Tubleweed
- Amazon Linux 2023
## Arquitecturas y Dispositivos Compatibles ## Arquitecturas y Dispositivos Compatibles
@@ -241,88 +279,100 @@ Nuestra plataforma ofrece compatibilidad con una amplia gama de arquitecturas y
- Soporta exportar/importar base de datos desde el panel - Soporta exportar/importar base de datos desde el panel
## Configuraciones por Defecto ## Configuración Predeterminada del Panel
<details> <details>
<summary>Haz clic para detalles de las configuraciones por defecto</summary> <summary>Haz clic para ver los detalles de la configuración predeterminada</summary>
### Información ### Nombre de Usuario & Contraseña & Ruta Base Web:
Estos se generarán aleatoriamente si no los modificas.
- **Puerto:** el puerto predeterminado para el panel es `2053`
### Gestión de la Base de Datos:
Puedes realizar copias de seguridad y restauraciones de la base de datos directamente desde el panel.
- **Puerto:** 2053
- **Usuario y Contraseña:** Se generarán aleatoriamente si omites la modificación.
- **Ruta de la Base de Datos:** - **Ruta de la Base de Datos:**
- /etc/x-ui/x-ui.db - `/etc/x-ui/x-ui.db`
- **Ruta de Configuración de Xray:**
- /usr/local/x-ui/bin/config.json ### Ruta Base Web
- **Ruta del Panel Web sin Implementar SSL:**
- http://ip:2053/panel 1. **Restablecer la Ruta Base Web:**
- http://domain:2053/panel - Abre tu terminal.
- **Ruta del Panel Web con Implementación de SSL:** - Ejecuta el comando `x-ui`.
- https://domain:2053/panel - Selecciona la opción `Restablecer la Ruta Base Web`.
2. **Generar o Personalizar la Ruta:**
- La ruta se generará aleatoriamente, o puedes ingresar una ruta personalizada.
3. **Ver Configuración Actual:**
- Para ver tu configuración actual, utiliza el comando `x-ui settings` en el terminal o selecciona `Ver Configuración Actual` en `x-ui`.
### Recomendación de Seguridad:
- Para mayor seguridad, utiliza una palabra larga y aleatoria en la estructura de tu URL.
**Ejemplos:**
- `http://ip:port/*webbasepath*/panel`
- `http://domain:port/*webbasepath*/panel`
</details> </details>
## Configuración WARP ## Configuración de WARP
<details> <details>
<summary>Haz clic para detalles de la configuración WARP</summary> <summary>Haz clic para ver los detalles de la configuración de WARP</summary>
#### Uso #### Uso
Si deseas usar enrutamiento a WARP antes de la versión v2.1.0, sigue los pasos a continuación: **Para versiones `v2.1.0` y posteriores:**
**1.** Instala WARP en **Modo de Proxy SOCKS**: WARP está integrado, no se requiere instalación adicional. Simplemente habilita la configuración necesaria en el panel.
```sh
bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
```
**2.** Si ya instalaste warp, puedes desinstalarlo usando el siguiente comando:
```sh
warp u
```
**3.** Activa la configuración que necesites en el panel
Características de Configuración:
- Bloquear Anuncios
- Enrutar Google + Netflix + Spotify + OpenAI (ChatGPT) a WARP
- Corregir error 403 de Google
</details> </details>
## Límite de IP ## Límite de IP
<details> <details>
<summary>Haz clic para s detalles del límite de IP</summary> <summary>Haz clic para ver los detalles del límite de IP</summary>
#### Uso #### Uso
**Nota:** El Límite de IP no funcionará correctamente cuando se use IP Tunnel **Nota:** El Límite de IP no funcionará correctamente cuando uses Túnel IP.
- Para versiones hasta `v1.6.1`:
- **Para versiones hasta `v1.6.1`:**
- El límite de IP está integrado en el panel. - El límite de IP está integrado en el panel.
- Para versiones `v1.7.0` y posteriores: **Para versiones `v1.7.0` y posteriores:**
- Para que el Límite de IP funcione correctamente, necesitas instalar fail2ban y sus archivos requeridos siguiendo estos pasos: Para habilitar la funcionalidad de límite de IP, necesitas instalar `fail2ban` y los archivos requeridos siguiendo estos pasos:
1. Usa el comando `x-ui` dentro de la terminal. 1. Ejecuta el comando `x-ui` en el terminal, luego elige `Gestión de Límite de IP`.
2. Selecciona `Gestión de Límite de IP`. 2. Verás las siguientes opciones:
3. Elige las opciones apropiadas según tus necesidades.
- **Cambiar la Duración del Bloqueo:** Ajustar la duración de los bloqueos.
- asegúrate de tener ./access.log en tu Configuración de Xray después de la v2.1.3 tenemos una opción para ello - **Desbloquear a Todos:** Levantar todos los bloqueos actuales.
- **Revisar los Registros:** Revisar los registros.
```sh - **Estado de Fail2ban:** Verificar el estado de `fail2ban`.
- **Reiniciar Fail2ban:** Reiniciar el servicio `fail2ban`.
- **Desinstalar Fail2ban:** Desinstalar Fail2ban con la configuración.
3. Agrega una ruta para el registro de acceso en el panel configurando `Xray Configs/log/Access log` a `./access.log`, luego guarda y reinicia Xray.
- **Para versiones anteriores a `v2.1.3`:**
- Necesitas configurar manualmente la ruta del registro de acceso en tu configuración de Xray:
```sh
"log": { "log": {
"access": "./access.log", "access": "./access.log",
"dnsLog": false, "dnsLog": false,
"loglevel": "warning" "loglevel": "warning"
}, },
``` ```
- **Para versiones `v2.1.3` y posteriores:**
- Hay una opción para configurar `access.log` directamente desde el panel.
</details> </details>
@@ -373,7 +423,7 @@ El panel web admite tráfico diario, inicio de sesión en el panel, copia de seg
- Inicia [Botfather](https://t.me/BotFather) en tu cuenta de Telegram: - Inicia [Botfather](https://t.me/BotFather) en tu cuenta de Telegram:
![Botfather](./media/botfather.png) ![Botfather](./media/botfather.png)
- Crea un nuevo bot usando el comando /newbot: Te hará 2 preguntas, Un nombre y un nombre de usuario para tu bot. Ten en cuenta que el nombre de usuario debe terminar con la palabra "bot". - Crea un nuevo bot usando el comando /newbot: Te hará 2 preguntas, Un nombre y un nombre de usuario para tu bot. Ten en cuenta que el nombre de usuario debe terminar con la palabra "bot".
![Create new bot](./media/newbot.png) ![Create new bot](./media/newbot.png)

View File

@@ -1,4 +1,4 @@
[English](/README.md) | [Chinese](/README.zh.md) | [Español](/README.es_ES.md) [English](/README.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p> <p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p>
@@ -32,10 +32,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## Install Custom Version ## Install Custom Version
To install your desired version, add the version to the end of the installation command. e.g., ver `v2.3.12`: To install your desired version, add the version to the end of the installation command. e.g., ver `v2.4.3`:
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.3.12 bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.3
``` ```
## SSL Certificate ## SSL Certificate
@@ -169,6 +169,8 @@ systemctl restart x-ui
docker compose up -d docker compose up -d
``` ```
Add ```--pull always``` flag to make docker automatically recreate container if a newer image is pulled. See https://docs.docker.com/reference/cli/docker/container/run/#pull for more info.
**OR** **OR**
```sh ```sh
@@ -202,6 +204,41 @@ systemctl restart x-ui
</details> </details>
## Nginx Settings
<details>
<summary>Click for Reverse Proxy Configuration</summary>
#### Nginx Reverse Proxy
```nginx
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_redirect off;
proxy_pass http://127.0.0.1:2053;
}
```
#### Nginx sub-path
- Ensure that the "URI Path" in the `/sub` panel settings is the same.
- The `url` in the panel settings needs to end with `/`.
```nginx
location /sub {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_redirect off;
proxy_pass http://127.0.0.1:2053;
}
```
</details>
## Recommended OS ## Recommended OS
@@ -213,10 +250,11 @@ systemctl restart x-ui
- Parch Linux - Parch Linux
- Manjaro - Manjaro
- Armbian - Armbian
- AlmaLinux 9+ - AlmaLinux 8.0+
- Rocky Linux 9+ - Rocky Linux 8+
- Oracle Linux 8+ - Oracle Linux 8+
- OpenSUSE Tubleweed - OpenSUSE Tubleweed
- Amazon Linux 2023
## Supported Architectures and Devices ## Supported Architectures and Devices
@@ -248,8 +286,10 @@ Our platform offers compatibility with a diverse range of architectures and devi
- Russian - Russian
- Vietnamese - Vietnamese
- Spanish - Spanish
- Indonesian - Indonesian
- Ukrainian - Ukrainian
- Turkish
- Português (Brazil)
## Features ## Features
@@ -258,7 +298,7 @@ Our platform offers compatibility with a diverse range of architectures and devi
- Search within all inbounds and clients - Search within all inbounds and clients
- Dark/Light theme - Dark/Light theme
- Supports multi-user and multi-protocol - Supports multi-user and multi-protocol
- Supports protocols, including VMess, VLESS, Trojan, Shadowsocks, Dokodemo-door, Socks, HTTP, wireguard - Supports protocols, including VMESS, VLESS, Trojan, Shadowsocks, Dokodemo-door, Socks, HTTP, wireguard
- Supports XTLS native Protocols, including RPRX-Direct, Vision, REALITY - Supports XTLS native Protocols, including RPRX-Direct, Vision, REALITY
- Traffic statistics, traffic limit, expiration time limit - Traffic statistics, traffic limit, expiration time limit
- Customizable Xray configuration templates - Customizable Xray configuration templates
@@ -322,17 +362,6 @@ Our platform offers compatibility with a diverse range of architectures and devi
WARP is built-in, and no additional installation is required. Simply turn on the necessary configuration in the panel. WARP is built-in, and no additional installation is required. Simply turn on the necessary configuration in the panel.
**For versions before `v2.1.0`:**
1. Run the `x-ui` command in the terminal, then choose `WARP Management`.
2. You will see the following options:
- **Account Type (free, plus, team):** Choose the appropriate account type.
- **Enable/Disable WireProxy:** Toggle WireProxy on or off.
- **Uninstall WARP:** Remove the WARP application.
3. Configure the settings as needed in the panel.
</details> </details>
## IP Limit ## IP Limit
@@ -362,7 +391,7 @@ To enable the IP Limit functionality, you need to install `fail2ban` and its req
- **Uninstall Fail2ban:** Uninstall Fail2ban with configuration. - **Uninstall Fail2ban:** Uninstall Fail2ban with configuration.
3. Add a path for the access log on the panel by setting `Xray Configs/log/Access log` to `./access.log` then save and restart xray. 3. Add a path for the access log on the panel by setting `Xray Configs/log/Access log` to `./access.log` then save and restart xray.
- **For versions before `v2.1.3`:** - **For versions before `v2.1.3`:**
- You need to set the access log path manually in your Xray configuration: - You need to set the access log path manually in your Xray configuration:
@@ -414,19 +443,19 @@ The web panel supports daily traffic, panel login, database backup, system statu
- Threshold for Expiration time and Traffic to report in advance - Threshold for Expiration time and Traffic to report in advance
- Support client report menu if client's telegram username added to the user's configurations - Support client report menu if client's telegram username added to the user's configurations
- Support telegram traffic report searched with UUID (VMESS/VLESS) or Password (TROJAN) - anonymously - Support telegram traffic report searched with UUID (VMESS/VLESS) or Password (TROJAN) - anonymously
- Menu based bot - Menu-based bot
- Search client by email ( only admin ) - Search client by email (only admin)
- Check all inbounds - Check all inbounds
- Check server status - Check server status
- Check depleted users - Check depleted users
- Receive backup by request and in periodic reports - Receive backup by request and in periodic reports
- Multi language bot - Multi-language bot
### Setting up Telegram bot ### Setting up Telegram bot
- Start [Botfather](https://t.me/BotFather) in your Telegram account: - Start [Botfather](https://t.me/BotFather) in your Telegram account:
![Botfather](./media/botfather.png) ![Botfather](./media/botfather.png)
- Create a new Bot using /newbot command: It will ask you 2 questions, A name and a username for your bot. Note that the username has to end with the word "bot". - Create a new Bot using /newbot command: It will ask you 2 questions, A name and a username for your bot. Note that the username has to end with the word "bot".
![Create new bot](./media/newbot.png) ![Create new bot](./media/newbot.png)

559
README.ru_RU.md Normal file
View File

@@ -0,0 +1,559 @@
[English](/README.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p>
**Продвинутая веб-панель • Построена на основе Xray Core**
[![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases)
[![](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](#)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](#)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
> **Отказ от ответственности:** Этот проект предназначен только для личного обучения и общения. Пожалуйста, не используйте его в незаконных целях и не применяйте в производственной среде.
**Если этот проект оказался полезным для вас, вы можете оценить его, поставив звёздочку** :star2:
<p align="left">
<a href="https://buymeacoffee.com/mhsanaei" target="_blank">
<img src="./media/buymeacoffe.png" alt="Image">
</a>
</p>
- USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC`
- MATIC (polygon): `0x41C9548675D044c6Bfb425786C765bc37427256A`
- LTC (Litecoin): `ltc1q2ach7x6d2zq0n4l0t4zl7d7xe2s6fs7a3vspwv`
## Установка и обновление
```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
```
## Установка определённой версии
Чтобы установить нужную вам версию, добавьте номер версии в конец команды установки. Например, `v2.4.3`:
```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.3
```
## SSL Сертификат
<details>
<summary>Нажмите для получения информации об SSL сертификате</summary>
### ACME
Для управления SSL сертификатами с помощью ACME:
1. Убедитесь, что ваш домен правильно настроен и указывает на сервер.
2. Выполните команду `x-ui` в терминале, затем выберите `SSL Certificate Management`.
3. Вам будут предложены следующие опции:
- **Get SSL:** Получить SSL сертификаты.
- **Revoke:** Отозвать существующие SSL сертификаты.
- **Force Renew:** Принудительно перевыпустить SSL сертификаты.
### Certbot
Для установки и использования Certbot:
```sh
apt-get install certbot -y
certbot certonly --standalone --agree-tos --register-unsafely-without-email -d вашдомен.com
certbot renew --dry-run
```
### Cloudflare
Скрипт управления включает встроенное приложение для получения SSL сертификата через Cloudflare. Чтобы использовать этот скрипт для запроса сертификата, вам потребуется следующее:
- Email, зарегистрированный в Cloudflare
- Глобальный API-ключ Cloudflare
- Доменное имя должно указывать на текущий сервер через Cloudflare
**Как получить глобальный API-ключ Cloudflare:**
1. Выполните команду `x-ui` в терминале, затем выберите `Cloudflare SSL Certificate`.
2. Перейдите по ссылке: [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens).
3. Нажмите на "View Global API Key" (см. скриншот ниже):
![](media/APIKey1.PNG)
4. Возможно, вам потребуется повторно пройти аутентификацию. После этого ключ API будет отображён (см. скриншот ниже):
![](media/APIKey2.png)
При использовании просто введите ваше `доменное имя`, `email` и `API-ключ`. Схема приведена ниже:
![](media/DetailEnter.png)
</details>
## Ручная установка и обновление
<details>
<summary>Нажмите для получения информации о ручной установке</summary>
#### Использование
1. Чтобы скачать последнюю версию архива напрямую на ваш сервер, выполните следующую команду:
```sh
ARCH=$(uname -m)
case "${ARCH}" in
x86_64 | x64 | amd64) XUI_ARCH="amd64" ;;
i*86 | x86) XUI_ARCH="386" ;;
armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;;
armv7* | armv7) XUI_ARCH="armv7" ;;
armv6* | armv6) XUI_ARCH="armv6" ;;
armv5* | armv5) XUI_ARCH="armv5" ;;
s390x) echo 's390x' ;;
*) XUI_ARCH="amd64" ;;
esac
wget https://github.com/MHSanaei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz
```
2. После загрузки архива выполните следующие команды для установки или обновления x-ui:
```sh
ARCH=$(uname -m)
case "${ARCH}" in
x86_64 | x64 | amd64) XUI_ARCH="amd64" ;;
i*86 | x86) XUI_ARCH="386" ;;
armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;;
armv7* | armv7) XUI_ARCH="armv7" ;;
armv6* | armv6) XUI_ARCH="armv6" ;;
armv5* | armv5) XUI_ARCH="armv5" ;;
s390x) echo 's390x' ;;
*) XUI_ARCH="amd64" ;;
esac
cd /root/
rm -rf x-ui/ /usr/local/x-ui/ /usr/bin/x-ui
tar zxvf x-ui-linux-${XUI_ARCH}.tar.gz
chmod +x x-ui/x-ui x-ui/bin/xray-linux-* x-ui/x-ui.sh
cp x-ui/x-ui.sh /usr/bin/x-ui
cp -f x-ui/x-ui.service /etc/systemd/system/
mv x-ui/ /usr/local/
systemctl daemon-reload
systemctl enable x-ui
systemctl restart x-ui
```
</details>
## Установка с помощью Docker
<details>
<summary>Нажмите для получения информации о Docker</summary>
#### Использование
1. **Установите Docker:**
```sh
bash <(curl -sSL https://get.docker.com)
```
2. **Склонируйте репозиторий проекта:**
```sh
git clone https://github.com/MHSanaei/3x-ui.git
cd 3x-ui
```
3. **Запустите сервис:**
```sh
docker compose up -d
```
Добавьте параметр ```--pull always``` для автоматического обновления контейнера, когда публикуется новый образ. Подробности: https://docs.docker.com/reference/cli/docker/container/run/#pull
**ИЛИ**
```sh
docker run -itd \
-e XRAY_VMESS_AEAD_FORCED=false \
-v $PWD/db/:/etc/x-ui/ \
-v $PWD/cert/:/root/cert/ \
--network=host \
--restart=unless-stopped \
--name 3x-ui \
ghcr.io/mhsanaei/3x-ui:latest
```
4. **Обновление до последней версии:**
```sh
cd 3x-ui
docker compose down
docker compose pull 3x-ui
docker compose up -d
```
5. **Удаление 3x-ui из Docker:**
```sh
docker stop 3x-ui
docker rm 3x-ui
cd --
rm -r 3x-ui
```
</details>
## Настройки Nginx
<details>
<summary>Нажмите чтобы просмотреть конфигурацию обратного прокси-сервера</summary>
#### Обратный прокси-сервер Nginx
```nginx
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_redirect off;
proxy_pass http://127.0.0.1:2053;
}
```
#### Nginx sub-path
- Убедитесь, что "корневой путь URL адреса панели" в настройках панели и `/sub` совпадают.
- В настройках панели `url` должен заканчиваться на `/`.
```nginx
location /sub {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_redirect off;
proxy_pass http://127.0.0.1:2053;
}
```
</details>
## Рекомендуемые ОС
- Ubuntu 20.04+
- Debian 11+
- CentOS 8+
- Fedora 36+
- Arch Linux
- Parch Linux
- Manjaro
- Armbian
- AlmaLinux 8.0+
- Rocky Linux 8+
- Oracle Linux 8+
- OpenSUSE Tubleweed
- Amazon Linux 2023
## Поддерживаемые архитектуры и устройства
<details>
<summary>Нажмите для получения информации о поддерживаемых архитектурах и устройствах</summary>
Наша платформа поддерживает разнообразные архитектуры и устройства, обеспечивая гибкость в различных вычислительных средах. Вот основные архитектуры, которые мы поддерживаем:
- **amd64**: Эта распространенная архитектура является стандартом для персональных компьютеров и серверов, обеспечивая беспроблемную работу большинства современных операционных систем.
- **x86 / i386**: Широко используется в настольных и портативных компьютерах. Эта архитектура имеет широкую поддержку со стороны множества операционных систем и приложений, включая, но не ограничиваясь, Windows, macOS и Linux.
- **armv8 / arm64 / aarch64**: Предназначена для современных мобильных и встроенных устройств, таких как смартфоны и планшеты. Эта архитектура представлена устройствами, такими как Raspberry Pi 4, Raspberry Pi 3, Raspberry Pi Zero 2/Zero 2 W, Orange Pi 3 LTS и другими.
- **armv7 / arm / arm32**: Служит архитектурой для старых мобильных и встроенных устройств, оставаясь широко используемой в таких устройствах, как Orange Pi Zero LTS, Orange Pi PC Plus, Raspberry Pi 2 и других.
- **armv6 / arm / arm32**: Ориентирована на очень старые встроенные устройства, эта архитектура, хотя и менее распространенная, всё ещё используется. Например, такие устройства, как Raspberry Pi 1, Raspberry Pi Zero/Zero W, полагаются на эту архитектуру.
- **armv5 / arm / arm32**: Более старая архитектура, ассоциируемая с ранними встроенными системами, сегодня менее распространена, но всё ещё может быть найдена в устаревших устройствах, таких как ранние версии Raspberry Pi и некоторые старые смартфоны.
- **s390x**: Эта архитектура обычно используется в мейнфреймах IBM и обеспечивает высокую производительность и надежность для корпоративных рабочих нагрузок.
</details>
## Языки
- Английский
- Фарси
- Китайский
- Русский
- Вьетнамский
- Испанский
- Индонезийский
- Украинский
- Турецкий
- Португальский (Бразилия)
## Возможности
- Мониторинг состояния системы
- Поиск по всем входящим подключениям и клиентам
- Тёмная/светлая тема
- Поддержка нескольких пользователей и протоколов
- Поддержка протоколов, включая VMESS, VLESS, Trojan, Shadowsocks, Dokodemo-door, Socks, HTTP, WireGuard
- Поддержка протоколов XTLS, включая RPRX-Direct, Vision, REALITY
- Статистика трафика, ограничение трафика, ограничение по времени истечения
- Настраиваемые шаблоны конфигурации Xray
- Поддержка HTTPS доступа к панели (ваше доменное имя + SSL сертификат)
- Поддержка установки SSL-сертификата в один клик и автоматического перевыпуска
- Для получения более продвинутых настроек обращайтесь к панели
- Исправляет маршруты API (настройка пользователя будет создана через API)
- Поддержка изменения конфигураций по различным элементам, предоставленным в панели
- Поддержка экспорта/импорта базы данных из панели
## Настройки панели по умолчанию
<details>
<summary>Нажмите для получения информации о настройках по умолчанию</summary>
### Имя пользователя и пароль & webbasepath:
Эти параметры будут сгенерированы случайным образом, если вы пропустите их изменение.
- **Порт:** порт панели по умолчанию — `2053`
### Управление базой данных:
Вы можете удобно выполнять резервное копирование и восстановление базы данных прямо из панели.
- **Путь к базе данных:**
- `/etc/x-ui/x-ui.db`
### Webbasepath
1. **Сбросить webbasepath:**
- Откройте терминал.
- Выполните команду `x-ui`.
- Выберите опцию `Reset Web Base Path`.
2. **Генерация или настройка пути:**
- Путь будет сгенерирован случайным образом, или вы можете ввести собственный путь.
3. **Просмотр текущих настроек:**
- Чтобы просмотреть текущие настройки, используйте команду `x-ui settings` в терминале или опцию `View Current Settings` в `x-ui`.
### Рекомендации по безопасности:
- Для повышения безопасности используйте длинное случайное слово в структуре вашего URL.
**Примеры:**
- `http://ip_адрес:порт/*webbasepath*/panel`
- `http://домен:порт/*webbasepath*/panel`
</details>
## Настройка WARP
<details>
<summary>Нажмите для получения информации о настройке WARP</summary>
#### Использование
**Для версий `v2.1.0` и новее:**
WARP встроен, и дополнительная установка не требуется. Просто включите необходимую конфигурацию в панели.
</details>
## Ограничение IP
<details>
<summary>Нажмите для получения информации об ограничении IP</summary>
#### Использование
**Примечание:** Ограничение IP не будет работать корректно при использовании IP Tunnel.
- **Для версий до `v1.6.1`:**
- Ограничение IP встроено в панель.
**Для версий `v1.7.0` и новее:**
Чтобы включить функциональность ограничения IP, вам нужно установить `fail2ban` и его необходимые файлы, выполнив следующие шаги:
1. Выполните команду `x-ui` в терминале, затем выберите `IP Limit Management`.
2. Вам будут предложены следующие опции:
- **Change Ban Duration:** Отрегулировать длительность блокировок.
- **Unban Everyone:** Снять все текущие блокировки.
- **Check Logs:** Просмотреть логи.
- **Fail2ban Status:** Проверить статус `fail2ban`.
- **Restart Fail2ban:** Перезапустить службу `fail2ban`.
- **Uninstall Fail2ban:** Удалить Fail2ban с его конфигурацией.
3. Добавьте путь к логам доступа в панели, установив `Xray Configs/log/Access log` в `./access.log`, затем сохраните и перезапустите xray.
- **Для версий до `v2.1.3`:**
- Вам нужно вручную установить путь к логам доступа в вашей конфигурации Xray:
```sh
"log": {
"access": "./access.log",
"dnsLog": false,
"loglevel": "warning"
},
```
- **Для версий `v2.1.3` и новее:**
- Есть возможность настройки `access.log` непосредственно из панели.
</details>
## Телеграм-бот
<details>
<summary>Нажмите для получения информации о телеграм-боте</summary>
#### Использование
Веб-панель поддерживает уведомления и функции, такие как ежедневный трафик, вход в панель, резервное копирование базы данных, состояние системы, информация о клиентах и другие, через телеграм-бота. Чтобы использовать бота, вам нужно настроить параметры, связанные с ботом, в панели, включая:
- Токен Telegram
- ID чата админа(-ов)
- Время уведомлений (в синтаксисе cron)
- Уведомления о дате истечения
- Уведомления о лимите трафика
- Резервное копирование базы данных
- Уведомления о загрузке CPU
**Примеры синтаксиса:**
- `30 * * * * *` - Уведомлять на 30-й секунде каждого часа
- `0 */10 * * * *` - Уведомлять на первой секунде каждых 10 минут
- `@hourly` - Ежечасное уведомление
- `@daily` - Ежедневное уведомление (в 00:00)
- `@weekly` - Еженедельное уведомление
- `@every 8h` - Уведомлять каждые 8 часов
### Возможности телеграм-бота
- Периодические отчеты
- Уведомления о входе
- Уведомления о пороге загруженности процессора
- Уведомления о времени истечения и трафике заранее
- Поддерживает меню отчетов клиента, если имя пользователя телеграм клиента добавлено в конфигурации пользователя
- Поддержка отчета о трафике через Telegram, поиск по UUID (VMESS/VLESS) или паролю (TROJAN) - анонимно
- Бот, основанный на меню
- Поиск клиента по email (только администратор)
- Проверка всех входящих соединений
- Проверка состояния сервера
- Проверка истекших пользователей
- Получение резервных копий по запросу и в периодических отчётах
- Многоязычный бот
### Настройка телеграм-бота
- Запустите [Botfather](https://t.me/BotFather) в вашем аккаунте Telegram:
![Botfather](./media/botfather.png)
- Создайте нового бота с помощью команды /newbot: у вас спросят 2 вопроса: отображаемое имя и имя пользователя для вашего бота. Обратите внимание, что имя пользователя должно заканчиваться на слово "bot".
![Создать нового бота](./media/newbot.png)
- Запустите созданного бота. Ссылку на вашего бота можно найти здесь.
![токен](./media/token.png)
- Перейдите в панель и настройте параметры телеграм-бота следующим образом:
![Настройки панели](./media/panel-bot-config.png)
Введите токен вашего бота в поле ввода номер 3.
Введите ID пользователя в поле ввода номер 4. Telegram-аккаунты с этим ID будут администраторами бота. (Вы можете ввести несколько ID, разделяя их запятой)
- Как получить ID пользователя Telegram? Используйте этот [бот](https://t.me/useridinfobot). Запустите бота, и он отобразит ваш ID пользователя Telegram.
![ID пользователя](./media/user-id.png)
</details>
## Маршруты API
<details>
<summary>Нажмите для получения информации о маршрутах API</summary>
#### Использование
- `/login` с `POST`-данными: `{username: '', password: ''}` для входа
- `/panel/api/inbounds` это базовый путь для следующих действий:
| Метод | Путь | Действие
| :----: | -----------------------------------| -------------------------------------------
| `GET` | `"/list"` | Получить все входящие соединения
| `GET` | `"/get/:id"` | Получить входящее соединение с inbound.id
| `GET` | `"/getClientTraffics/:email"` | Получить трафик клиента по email
| `GET` | `"/getClientTrafficsById/:id"` | Получить трафик клиента по ID
| `GET` | `"/createbackup"` | Telegram-бот отправит резервную копию администраторам
| `POST` | `"/add"` | Добавить входящее соединение
| `POST` | `"/del/:id"` | Удалить входящее соединение
| `POST` | `"/update/:id"` | Обновить входящее соединение
| `POST` | `"/clientIps/:email"` | IP-адрес клиента
| `POST` | `"/clearClientIps/:email"` | Очистить IP-адреса клиента
| `POST` | `"/addClient"` | Добавить клиента к входящему соединению
| `POST` | `"/:id/delClient/:clientId"` | Удалить клиента по clientId\*
| `POST` | `"/updateClient/:clientId"` | Обновить клиента по clientId\*
| `POST` | `"/:id/resetClientTraffic/:email"` | Сбросить трафик клиента
| `POST` | `"/resetAllTraffics"` | Сбросить трафик всех входящих соединений
| `POST` | `"/resetAllClientTraffics/:id"` | Сбросить трафик всех клиентов в входящем соединении
| `POST` | `"/delDepletedClients/:id"` | Удалить истекших клиентов в входящем соединении (-1: всех)
| `POST` | `"/onlines"` | Получить пользователей, которые находятся онлайн (список email'ов)
\*- Поле `clientId` должно быть заполнено следующим образом:
- `client.id` для VMESS и VLESS
- `client.password` для TROJAN
- `client.email` для Shadowsocks
</details>
- [API-документация](https://documenter.getpostman.com/view/16802678/2s9YkgD5jm)
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://app.getpostman.com/run-collection/16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415%26entityType%3Dcollection%26workspaceId%3D2cd38c01-c851-4a15-a972-f181c23359d9)
</details>
## Переменные среды
<details>
<summary>Нажмите для получения информации о переменных среды</summary>
#### Использование
| Переменная | Тип | Значение по умолчанию |
| ---------------- | :------------------------------------------: | :-------------------- |
| XUI_LOG_LEVEL | `"debug"` \| `"info"` \| `"warn"` \| `"error"` | `"info"` |
| XUI_DEBUG | `boolean` | `false` |
| XUI_BIN_FOLDER | `string` | `"bin"` |
| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` |
| XUI_LOG_FOLDER | `string` | `"/var/log"` |
Пример:
```sh
XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
```
</details>
## Предварительный Просмотр
![1](./media/1.png)
![2](./media/2.png)
![3](./media/3.png)
![4](./media/4.png)
![5](./media/5.png)
![6](./media/6.png)
![7](./media/7.png)
## Особая благодарность
- [alireza0](https://github.com/alireza0/)
## Благодарности
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._
- [Vietnam Adblock rules](https://github.com/vuong2023/vn-v2ray-rules) (License: **GPL-3.0**): _A hosted domain hosted in Vietnam and blocklist with the most efficiency for Vietnamese._
## Число звёзд со временем
[![Stargazers over time](https://starchart.cc/MHSanaei/3x-ui.svg)](https://starchart.cc/MHSanaei/3x-ui)

View File

@@ -1,4 +1,4 @@
[English](/README.md) | [Chinese](/README.zh.md) | [Español](/README.es_ES.md) [English](/README.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
<p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p> <p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p>
@@ -32,36 +32,58 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## 安装指定版本 ## 安装指定版本
要安装所需的版本,请将该版本添加到安装命令的末尾。 e.g., ver `v2.3.12`: 要安装所需的版本,请将该版本添加到安装命令的末尾。 e.g., ver `v2.4.3`:
``` ```
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.3.12 bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.3
``` ```
## SSL ### SSL证
<details> <details>
<summary>点击查看 SSL证</summary> <summary>点击查看SSL证书详情</summary>
### Cloudflare ### ACME
管理脚本具有用于 Cloudflare 的内置 SSL 证书应用程序。若要使用此脚本申请证书,需要满足以下条件 使用ACME管理SSL证书
- Cloudflare 邮箱地址 1. 确保您的域名正确解析到服务器。
- Cloudflare Global API Key 2. 在终端中运行 `x-ui` 命令,然后选择 `SSL证书管理`
- 域名已通过 cloudflare 解析到当前服务器 3. 您将看到以下选项:
**1:** 在终端中运行`x-ui` 选择 `Cloudflare SSL Certificate`.
- **获取SSL证书:** 获取SSL证书。
- **吊销:** 吊销现有的SSL证书。
- **强制更新:** 强制更新SSL证书。
### Certbot ### Certbot
```
安装并使用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
``` ```
***Tip:*** *管理脚本具有 Certbot 。使用 `x-ui` 命令, 选择 `SSL Certificate Management`.* ### Cloudflare
管理脚本内置了Cloudflare的SSL证书申请。要使用此脚本申请证书您需要以下信息
- Cloudflare注册的电子邮件
- Cloudflare全局API密钥
- 域名必须通过Cloudflare解析到当前服务器
**如何获取Cloudflare全局API密钥**
1. 在终端中运行 `x-ui` 命令,然后选择 `Cloudflare SSL证书`
2. 访问链接:[Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens)。
3. 点击“查看全局API密钥”参见下图
![](media/APIKey1.PNG)
4. 您可能需要重新验证您的账户。之后将显示API密钥参见下图
![](media/APIKey2.png)
使用时,只需输入您的 `域名``电子邮件``API密钥`。如下图所示:
![](media/DetailEnter.png)
</details> </details>
@@ -166,7 +188,7 @@ systemctl restart x-ui
docker compose up -d docker compose up -d
``` ```
从Docker中删除3x-ui 从Docker中删除3x-ui
```sh ```sh
docker stop 3x-ui docker stop 3x-ui
@@ -178,6 +200,42 @@ systemctl restart x-ui
</details> </details>
## Nginx 设置
<details>
<summary>点击查看 反向代理配置</summary>
#### Nginx反向代理
```nginx
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_redirect off;
proxy_pass http://127.0.0.1:2053;
}
```
#### Nginx子路径
- 确保 `/sub` 面板设置中的"面板url根路径"一致
- 面板设置中的 `url` 需要以 `/` 结尾
```nginx
location /sub {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_redirect off;
proxy_pass http://127.0.0.1:2053;
}
```
</details>
## 建议使用的操作系统 ## 建议使用的操作系统
- Ubuntu 20.04+ - Ubuntu 20.04+
@@ -185,11 +243,14 @@ systemctl restart x-ui
- CentOS 8+ - CentOS 8+
- Fedora 36+ - Fedora 36+
- Arch Linux - Arch Linux
- Parch Linux
- Manjaro - Manjaro
- Armbian - Armbian
- AlmaLinux 9+ - AlmaLinux 8.0+
- Rockylinux 9+ - Rocky Linux 8+
- Oracle Linux 8+
- OpenSUSE Tubleweed - OpenSUSE Tubleweed
- Amazon Linux 2023
## 支持的架构和设备 ## 支持的架构和设备
<details> <details>
@@ -240,88 +301,100 @@ systemctl restart x-ui
- 支持从面板导出/导入数据库 - 支持从面板导出/导入数据库
## 默认设置 ## 默认面板设置
<details> <details>
<summary>点击查看 默认设置</summary> <summary>点击查看默认设置详情</summary>
### 信息 ### 用户名 & 密码 & Web基础路径
如果不修改这些,它们将会随机生成。
- **端口号:** 面板的默认端口号是 `2053`
### 数据库管理:
您可以直接在面板中方便地进行数据库备份和还原。
- **数据库路径:**
- `/etc/x-ui/x-ui.db`
### Web 基础路径
1. **重置 Web 基础路径:**
- 打开终端。
- 运行 `x-ui` 命令。
- 选择 `重置 Web 基础路径` 选项。
2. **生成或自定义路径:**
- 路径将会随机生成,或者您可以输入自定义路径。
3. **查看当前设置:**
- 要查看当前设置,请在终端中使用 `x-ui settings` 命令,或在 `x-ui` 面板中点击 `查看当前设置`
### 安全建议:
- 为了提高安全性建议在URL结构中使用一个长的随机词。
**示例:**
- `http://ip:port/*webbasepath*/panel`
- `http://domain:port/*webbasepath*/panel`
- **端口:** 2053
- **用户名 & 密码:** 当您跳过设置时,此项会随机生成。
- **数据库路径:**
- /etc/x-ui/x-ui.db
- **Xray 配置路径:**
- /usr/local/x-ui/bin/config.json
- **面板链接无SSL**
- http://ip:2053/panel
- http://domain:2053/panel
- **面板链接有SSL**
- https://domain:2053/panel
</details> </details>
## WARP 配置 ## WARP 配置
<details> <details>
<summary>点击查看 WARP 配置</summary> <summary>点击查看 WARP 配置详情</summary>
#### 使用 #### 使用方法
如果要在 v2.1.0 之前使用 WARP 路由,请按照以下步骤操作: **对于 `v2.1.0` 及之后的版本:**
**1.** 在 **SOCKS Proxy Mode** 模式中安装Wrap WARP 已内置,无需额外安装。只需在面板中开启相关配置即可。
```sh
bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
```
**2.** 如果您已经安装了 warp您可以使用以下命令卸载
```sh
warp u
```
**3.** 在面板中打开您需要的配置
配置:
- Block Ads
- Route Google + Netflix + Spotify + OpenAI (ChatGPT) to WARP
- Fix Google 403 error
</details> </details>
## IP 限制 ## IP 限制
<details> <details>
<summary>点击查看 IP 限制</summary> <summary>点击查看 IP 限制详情</summary>
#### 使用 #### 使用方法
**注意** 使用 IP 隧道时IP 限制无法正常工作。 **注意:** 使用 IP 隧道时IP 限制无法正常工作。
- 适用于最高 `v1.6.1` - **对于 `v1.6.1` 及之前的版本:**
- IP 限制功能已内置于面板中。
- IP 限制 已被集成在面板中。 **对于 `v1.7.0` 及更新的版本:**
- 适用于 `v1.7.0` 以及更新的版本 要启用 IP 限制功能,您需要安装 `fail2ban` 及其所需的文件,步骤如下
- 要使 IP 限制正常工作,您需要按照以下步骤安装 fail2ban 及其所需的文件: 1. 在终端中运行 `x-ui` 命令,然后选择 `IP 限制管理`
2. 您将看到以下选项:
1. 使用面板内置的 `x-ui` 指令 - **更改封禁时长:** 调整封禁时长。
2. 选择 `IP Limit Management`. - **解除所有封禁:** 解除当前的所有封禁。
3. 根据您的需要选择合适的选项 - **查看日志:** 查看日志
- **Fail2ban 状态:** 检查 `fail2ban` 的状态。
- 确保您的 Xray 配置上有 ./access.log 。在 v2.1.3 之后,我们有一个选项 - **重启 Fail2ban:** 重启 `fail2ban` 服务
- **卸载 Fail2ban:** 卸载带有配置的 Fail2ban。
```sh
3. 在面板中通过设置 `Xray 配置/log/访问日志``./access.log` 添加访问日志路径,然后保存并重启 Xray。
- **对于 `v2.1.3` 之前的版本:**
- 您需要在 Xray 配置中手动设置访问日志路径:
```sh
"log": { "log": {
"access": "./access.log", "access": "./access.log",
"dnsLog": false, "dnsLog": false,
"loglevel": "warning" "loglevel": "warning"
}, },
``` ```
- **对于 `v2.1.3` 及之后的版本:**
- 面板中直接提供了配置 `access.log` 的选项。
</details> </details>
@@ -372,7 +445,7 @@ Web 面板通过 Telegram Bot 支持每日流量、面板登录、数据库备
- 与 [Botfather](https://t.me/BotFather) 对话: - 与 [Botfather](https://t.me/BotFather) 对话:
![Botfather](./media/botfather.png) ![Botfather](./media/botfather.png)
- 使用 /newbot 创建新机器人你需要提供机器人名称以及用户名注意名称中末尾要包含“bot” - 使用 /newbot 创建新机器人你需要提供机器人名称以及用户名注意名称中末尾要包含“bot”
![创建机器人](./media/newbot.png) ![创建机器人](./media/newbot.png)

View File

@@ -1 +1 @@
2.3.12 2.4.3

View File

@@ -10,7 +10,7 @@ import (
type Protocol string type Protocol string
const ( const (
VMess Protocol = "vmess" VMESS Protocol = "vmess"
VLESS Protocol = "vless" VLESS Protocol = "vless"
DOKODEMO Protocol = "dokodemo-door" DOKODEMO Protocol = "dokodemo-door"
HTTP Protocol = "http" HTTP Protocol = "http"
@@ -46,6 +46,7 @@ type Inbound struct {
StreamSettings string `json:"streamSettings" form:"streamSettings"` StreamSettings string `json:"streamSettings" form:"streamSettings"`
Tag string `json:"tag" form:"tag" gorm:"unique"` Tag string `json:"tag" form:"tag" gorm:"unique"`
Sniffing string `json:"sniffing" form:"sniffing"` Sniffing string `json:"sniffing" form:"sniffing"`
Allocate string `json:"allocate" form:"allocate"`
} }
type OutboundTraffics struct { type OutboundTraffics struct {
@@ -75,6 +76,7 @@ func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
StreamSettings: json_util.RawMessage(i.StreamSettings), StreamSettings: json_util.RawMessage(i.StreamSettings),
Tag: i.Tag, Tag: i.Tag,
Sniffing: json_util.RawMessage(i.Sniffing), Sniffing: json_util.RawMessage(i.Sniffing),
Allocate: json_util.RawMessage(i.Allocate),
} }
} }
@@ -86,6 +88,7 @@ type Setting struct {
type Client struct { type Client struct {
ID string `json:"id"` ID string `json:"id"`
Security string `json:"security"`
Password string `json:"password"` Password string `json:"password"`
Flow string `json:"flow"` Flow string `json:"flow"`
Email string `json:"email"` Email string `json:"email"`

68
go.mod
View File

@@ -1,35 +1,36 @@
module x-ui module x-ui
go 1.22.5 go 1.23.1
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.0 github.com/mymmrac/telego v0.31.3
github.com/nicksnyder/go-i18n/v2 v2.4.0 github.com/nicksnyder/go-i18n/v2 v2.4.0
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
github.com/pelletier/go-toml/v2 v2.2.2 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.7 github.com/shirou/gopsutil/v4 v4.24.9
github.com/valyala/fasthttp v1.55.0 github.com/valyala/fasthttp v1.56.0
github.com/xtls/xray-core v1.8.23 github.com/xtls/xray-core v1.8.24
go.uber.org/atomic v1.11.0 go.uber.org/atomic v1.11.0
golang.org/x/text v0.16.0 golang.org/x/text v0.18.0
google.golang.org/grpc v1.65.0 google.golang.org/grpc v1.67.1
gorm.io/driver/sqlite v1.5.6 gorm.io/driver/sqlite v1.5.6
gorm.io/gorm v1.25.11 gorm.io/gorm v1.25.12
) )
require ( require (
github.com/andybalholm/brotli v1.1.0 // indirect github.com/andybalholm/brotli v1.1.0 // indirect
github.com/bytedance/sonic v1.12.0 // indirect github.com/bytedance/sonic v1.12.3 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cloudflare/circl v1.3.9 // indirect github.com/cloudflare/circl v1.4.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/fasthttp/router v1.5.2 // indirect github.com/fasthttp/router v1.5.2 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect github.com/francoispqt/gojay v1.2.13 // indirect
github.com/gabriel-vasile/mimetype v1.4.5 // indirect github.com/gabriel-vasile/mimetype v1.4.5 // indirect
@@ -37,39 +38,38 @@ require (
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.0 // indirect github.com/go-playground/validator/v10 v10.22.1 // 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.2 // indirect github.com/google/btree v1.1.3 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d // 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.3.0 // indirect github.com/gorilla/sessions v1.4.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect github.com/gorilla/websocket v1.5.3 // indirect
github.com/grbit/go-json v0.11.0 // indirect github.com/grbit/go-json v0.11.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/compress v1.17.10 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // 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-20240513124658-fba389f38bae // indirect github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/mattn/go-sqlite3 v1.14.23 // 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.19.1 // indirect github.com/onsi/ginkgo/v2 v2.20.2 // indirect
github.com/pires/go-proxyproto v0.7.0 // indirect github.com/pires/go-proxyproto v0.7.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.45.2 // indirect github.com/quic-go/quic-go v0.47.0 // 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.12.0 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/sagernet/sing v0.4.2 // indirect github.com/sagernet/sing v0.4.3 // 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
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect github.com/tklauser/numcpus v0.8.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
@@ -77,24 +77,24 @@ require (
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fastjson v1.6.4 // indirect github.com/valyala/fastjson v1.6.4 // indirect
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 // indirect github.com/vishvananda/netlink v1.3.0 // indirect
github.com/vishvananda/netns v0.0.4 // indirect github.com/vishvananda/netns v0.0.4 // indirect
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d // 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.4.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.9.0 // indirect golang.org/x/arch v0.10.0 // indirect
golang.org/x/crypto v0.25.0 // indirect golang.org/x/crypto v0.27.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/mod v0.20.0 // indirect golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.27.0 // indirect golang.org/x/net v0.29.0 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.23.0 // indirect golang.org/x/sys v0.25.0 // indirect
golang.org/x/time v0.6.0 // indirect golang.org/x/time v0.6.0 // indirect
golang.org/x/tools v0.23.0 // indirect golang.org/x/tools v0.25.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-20240805194559-2c9e96a0b5d4 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 // indirect
google.golang.org/protobuf v1.34.2 // indirect google.golang.org/protobuf v1.34.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

149
go.sum
View File

@@ -18,14 +18,14 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYU
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3ls= github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= github.com/bytedance/sonic v1.12.3/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.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
@@ -38,6 +38,8 @@ github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fp
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/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
github.com/ebitengine/purego v0.8.0/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/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
@@ -70,8 +72,8 @@ 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.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-playground/validator/v10 v10.22.1/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=
@@ -86,8 +88,8 @@ github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.2/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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -98,8 +100,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d h1:Jaz2JzpQaQXyET0AjLBXShrthbpqMkhGiEfkcQAiAUs=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -107,8 +109,8 @@ 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=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grbit/go-json v0.11.0 h1:bAbyMdYrYl/OjYsSqLH99N2DyQ291mHy726Mx+sYrnc= github.com/grbit/go-json v0.11.0 h1:bAbyMdYrYl/OjYsSqLH99N2DyQ291mHy726Mx+sYrnc=
@@ -125,8 +127,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/compress v1.17.10/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.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
@@ -140,41 +142,41 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI= github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
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.0 h1:vsN+JCNkh7Z9vfL/2/AHZ2xBsRk2GCMj3zydjCxkgIc= github.com/mymmrac/telego v0.31.3 h1:yZlD+dm+1W6p3OmCG8K+MbS02Y6paUgwPnqfZN3RWQQ=
github.com/mymmrac/telego v0.31.0/go.mod h1:MuqgVf2xXnIOWZs0prvsp3f4Yss80kCSjVEj4CRl7Ig= github.com/mymmrac/telego v0.31.3/go.mod h1:coOoqXVmjFnwBlzusjfEezbQ7RH9wQnDowJdMm+bnEo=
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0= github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA= github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs= github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4= github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -186,21 +188,21 @@ github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.45.2 h1:DfqBmqjb4ExSdxRIb/+qXhPC+7k6+DUNZha4oeiC9fY= github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
github.com/quic-go/quic-go v0.45.2/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI= github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E=
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=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
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.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sagernet/sing v0.4.2 h1:jzGNJdZVRI0xlAfFugsIQUPvyB9SuWvbJK7zQCXc4QM= github.com/sagernet/sing v0.4.3 h1:Ty/NAiNnVd6844k7ujlL5lkzydhcTH5Psc432jXA4Y8=
github.com/sagernet/sing v0.4.2/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls= github.com/sagernet/sing v0.4.3/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
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=
@@ -208,12 +210,8 @@ github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEo
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/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shirou/gopsutil/v4 v4.24.7 h1:V9UGTK4gQ8HvcnPKf6Zt3XHyQq/peaekfxpJ2HSocJk= github.com/shirou/gopsutil/v4 v4.24.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
github.com/shirou/gopsutil/v4 v4.24.7/go.mod h1:0uW/073rP7FYLOkvxolUQM5rMOLTNmRXnFKafpb71rw= github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
@@ -241,7 +239,6 @@ github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod
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=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -249,7 +246,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
@@ -265,21 +261,20 @@ 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.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW8U=
github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= github.com/valyala/fasthttp v1.56.0/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI=
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/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 h1:tkMT5pTye+1NlKIXETU78NXw0fyjnaNHmJyyLyzw8+U= github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3/go.mod h1:cAAsePK2e15YDAMJNyOpGYEWNe4sIghTY7gpz4cX/Ik= github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= 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/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d h1:+B97uD9uHLgAAulhigmys4BVwZZypzK7gPN3WtpgRJg= github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE= github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
github.com/xtls/xray-core v1.8.23 h1:A8Wr50ildMYLpaNu3EiK+Stg/tps6i0h7z5Hr4f9H2k= github.com/xtls/xray-core v1.8.24 h1:Y2NumdlnJ9C9gvh1Ivs2+73ui5XQgB70wZXYCiI9DyY=
github.com/xtls/xray-core v1.8.23/go.mod h1:0CwyMPNA5Cs+ukPXHbYQGgne/ug0PuXOSVqBu7zyXOc= github.com/xtls/xray-core v1.8.24/go.mod h1:cWIOI6iBBOsB0HHU9PGhaiBhaMPfiktUjwA0IWolWJc=
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.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
@@ -290,22 +285,22 @@ go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
go4.org/netipx v0.0.0-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.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k= golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -314,8 +309,8 @@ golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -333,18 +328,18 @@ golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
@@ -353,8 +348,8 @@ golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
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=
@@ -371,14 +366,14 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240805194559-2c9e96a0b5d4 h1:OsSGQeIIsyOEOimVxLEIL4rwGcnrjOydQaiA2bOnZUM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240805194559-2c9e96a0b5d4/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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=
@@ -395,8 +390,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE=
gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 h1:ze1vwAdliUAr68RQ5NtufWaXaOg8WUO2OACzEV+TNdE= gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 h1:ze1vwAdliUAr68RQ5NtufWaXaOg8WUO2OACzEV+TNdE=
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk= gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=

View File

@@ -39,12 +39,12 @@ arch() {
echo "arch: $(arch)" echo "arch: $(arch)"
os_version="" os_version=""
os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1) os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.')
if [[ "${release}" == "arch" ]]; then if [[ "${release}" == "arch" ]]; then
echo "Your OS is Arch Linux" echo "Your OS is Arch Linux"
elif [[ "${release}" == "parch" ]]; then elif [[ "${release}" == "parch" ]]; then
echo "Your OS is Parch linux" echo "Your OS is Parch Linux"
elif [[ "${release}" == "manjaro" ]]; then elif [[ "${release}" == "manjaro" ]]; then
echo "Your OS is Manjaro" echo "Your OS is Manjaro"
elif [[ "${release}" == "armbian" ]]; then elif [[ "${release}" == "armbian" ]]; then
@@ -56,24 +56,28 @@ elif [[ "${release}" == "centos" ]]; then
echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1 echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "ubuntu" ]]; then elif [[ "${release}" == "ubuntu" ]]; then
if [[ ${os_version} -lt 20 ]]; then if [[ ${os_version} -lt 2004 ]]; then
echo -e "${red} Please use Ubuntu 20 or higher version!${plain}\n" && exit 1 echo -e "${red} Please use Ubuntu 20 or higher version!${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "fedora" ]]; then elif [[ "${release}" == "fedora" ]]; then
if [[ ${os_version} -lt 36 ]]; then if [[ ${os_version} -lt 36 ]]; then
echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1 echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "amzn" ]]; then
if [[ ${os_version} != "2023" ]]; then
echo -e "${red} Please use Amazon Linux 2023!${plain}\n" && exit 1
fi
elif [[ "${release}" == "debian" ]]; then elif [[ "${release}" == "debian" ]]; then
if [[ ${os_version} -lt 11 ]]; then if [[ ${os_version} -lt 11 ]]; then
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1 echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "almalinux" ]]; then elif [[ "${release}" == "almalinux" ]]; then
if [[ ${os_version} -lt 9 ]]; then if [[ ${os_version} -lt 80 ]]; then
echo -e "${red} Please use AlmaLinux 9 or higher ${plain}\n" && exit 1 echo -e "${red} Please use AlmaLinux 8.0 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "rocky" ]]; then elif [[ "${release}" == "rocky" ]]; then
if [[ ${os_version} -lt 9 ]]; then if [[ ${os_version} -lt 8 ]]; then
echo -e "${red} Please use Rocky Linux 9 or higher ${plain}\n" && exit 1 echo -e "${red} Please use Rocky Linux 8 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "oracle" ]]; then elif [[ "${release}" == "oracle" ]]; then
if [[ ${os_version} -lt 8 ]]; then if [[ ${os_version} -lt 8 ]]; then
@@ -90,12 +94,12 @@ else
echo "- Parch Linux" echo "- Parch Linux"
echo "- Manjaro" echo "- Manjaro"
echo "- Armbian" echo "- Armbian"
echo "- AlmaLinux 9+" echo "- AlmaLinux 8.0+"
echo "- Rocky Linux 9+" echo "- Rocky Linux 8+"
echo "- Oracle Linux 8+" echo "- Oracle Linux 8+"
echo "- OpenSUSE Tumbleweed" echo "- OpenSUSE Tumbleweed"
echo "- Amazon Linux 2023"
exit 1 exit 1
fi fi
install_base() { install_base() {
@@ -106,7 +110,7 @@ install_base() {
centos | almalinux | rocky | oracle) centos | almalinux | rocky | oracle)
yum -y update && yum install -y -q wget curl tar tzdata yum -y update && yum install -y -q wget curl tar tzdata
;; ;;
fedora) fedora | amzn)
dnf -y update && dnf install -y -q wget curl tar tzdata dnf -y update && dnf install -y -q wget curl tar tzdata
;; ;;
arch | manjaro | parch) arch | manjaro | parch)

View File

@@ -73,11 +73,11 @@ func runWebServer() {
err := server.Stop() err := server.Stop()
if err != nil { if err != nil {
logger.Warning("Error stopping web server:", err) logger.Debug("Error stopping web server:", err)
} }
err = subServer.Stop() err = subServer.Stop()
if err != nil { if err != nil {
logger.Warning("Error stopping sub server:", err) logger.Debug("Error stopping sub server:", err)
} }
server = web.NewServer() server = web.NewServer()

View File

@@ -23,6 +23,7 @@
"destOverride": [ "destOverride": [
"http", "http",
"tls", "tls",
"quic",
"fakedns" "fakedns"
], ],
"enabled": true "enabled": true

View File

@@ -92,6 +92,11 @@ func (s *Server) initRouter() (*gin.Engine, error) {
SubJsonFragment = "" SubJsonFragment = ""
} }
SubJsonNoises, err := s.settingService.GetSubJsonNoises()
if err != nil {
SubJsonNoises = ""
}
SubJsonMux, err := s.settingService.GetSubJsonMux() SubJsonMux, err := s.settingService.GetSubJsonMux()
if err != nil { if err != nil {
SubJsonMux = "" SubJsonMux = ""
@@ -106,7 +111,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
s.sub = NewSUBController( s.sub = NewSUBController(
g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates, g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates,
SubJsonFragment, SubJsonMux, SubJsonRules) SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules)
return engine, nil return engine, nil
} }

View File

@@ -3,6 +3,7 @@ package sub
import ( import (
"encoding/base64" "encoding/base64"
"net" "net"
"strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -26,6 +27,7 @@ func NewSUBController(
rModel string, rModel string,
update string, update string,
jsonFragment string, jsonFragment string,
jsonNoise string,
jsonMux string, jsonMux string,
jsonRules string, jsonRules string,
) *SUBController { ) *SUBController {
@@ -37,7 +39,7 @@ func NewSUBController(
updateInterval: update, updateInterval: update,
subService: sub, subService: sub,
subJsonService: NewSubJsonService(jsonFragment, jsonMux, jsonRules, sub), subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub),
} }
a.initRouter(g) a.initRouter(g)
return a return a
@@ -54,7 +56,10 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) {
func (a *SUBController) subs(c *gin.Context) { func (a *SUBController) subs(c *gin.Context) {
subId := c.Param("subid") subId := c.Param("subid")
host := c.GetHeader("X-Forwarded-Host") var host string
if h, err := getHostFromXFH(c.GetHeader("X-Forwarded-Host")); err == nil {
host = h
}
if host == "" { if host == "" {
host = c.GetHeader("X-Real-IP") host = c.GetHeader("X-Real-IP")
} }
@@ -89,7 +94,10 @@ func (a *SUBController) subs(c *gin.Context) {
func (a *SUBController) subJsons(c *gin.Context) { func (a *SUBController) subJsons(c *gin.Context) {
subId := c.Param("subid") subId := c.Param("subid")
host := c.GetHeader("X-Forwarded-Host") var host string
if h, err := getHostFromXFH(c.GetHeader("X-Forwarded-Host")); err == nil {
host = h
}
if host == "" { if host == "" {
host = c.GetHeader("X-Real-IP") host = c.GetHeader("X-Real-IP")
} }
@@ -113,3 +121,14 @@ func (a *SUBController) subJsons(c *gin.Context) {
c.String(200, jsonSub) c.String(200, jsonSub)
} }
} }
func getHostFromXFH(s string) (string, error) {
if strings.Contains(s, ":") {
realHost, _, err := net.SplitHostPort(s)
if err != nil {
return "", err
}
return realHost, nil
}
return s, nil
}

View File

@@ -21,13 +21,14 @@ type SubJsonService struct {
configJson map[string]interface{} configJson map[string]interface{}
defaultOutbounds []json_util.RawMessage defaultOutbounds []json_util.RawMessage
fragment string fragment string
noises string
mux string mux string
inboundService service.InboundService inboundService service.InboundService
SubService *SubService SubService *SubService
} }
func NewSubJsonService(fragment string, mux string, rules string, subService *SubService) *SubJsonService { func NewSubJsonService(fragment string, noises string, mux string, rules string, subService *SubService) *SubJsonService {
var configJson map[string]interface{} var configJson map[string]interface{}
var defaultOutbounds []json_util.RawMessage var defaultOutbounds []json_util.RawMessage
json.Unmarshal([]byte(defaultJson), &configJson) json.Unmarshal([]byte(defaultJson), &configJson)
@@ -52,10 +53,15 @@ func NewSubJsonService(fragment string, mux string, rules string, subService *Su
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment)) defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment))
} }
if noises != "" {
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(noises))
}
return &SubJsonService{ return &SubJsonService{
configJson: configJson, configJson: configJson,
defaultOutbounds: defaultOutbounds, defaultOutbounds: defaultOutbounds,
fragment: fragment, fragment: fragment,
noises: noises,
mux: mux, mux: mux,
SubService: subService, SubService: subService,
} }
@@ -282,6 +288,9 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_ut
usersData[0].ID = client.ID usersData[0].ID = client.ID
usersData[0].Level = 8 usersData[0].Level = 8
if inbound.Protocol == model.VMESS {
usersData[0].Security = client.Security
}
if inbound.Protocol == model.VLESS { if inbound.Protocol == model.VLESS {
usersData[0].Flow = client.Flow usersData[0].Flow = client.Flow
usersData[0].Encryption = "none" usersData[0].Encryption = "none"
@@ -371,6 +380,7 @@ type UserVnext struct {
Encryption string `json:"encryption,omitempty"` Encryption string `json:"encryption,omitempty"`
Flow string `json:"flow,omitempty"` Flow string `json:"flow,omitempty"`
ID string `json:"id"` ID string `json:"id"`
Security string `json:"security,omitempty"`
Level int `json:"level"` Level int `json:"level"`
} }

View File

@@ -168,7 +168,7 @@ func (s *SubService) getLink(inbound *model.Inbound, email string) string {
} }
func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string { func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
if inbound.Protocol != model.VMess { if inbound.Protocol != model.VMESS {
return "" return ""
} }
obj := map[string]interface{}{ obj := map[string]interface{}{
@@ -213,12 +213,6 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
http, _ := stream["httpSettings"].(map[string]interface{}) http, _ := stream["httpSettings"].(map[string]interface{})
obj["path"], _ = http["path"].(string) obj["path"], _ = http["path"].(string)
obj["host"] = searchHost(http) obj["host"] = searchHost(http)
case "quic":
quic, _ := stream["quicSettings"].(map[string]interface{})
header := quic["header"].(map[string]interface{})
obj["type"], _ = header["type"].(string)
obj["host"], _ = quic["security"].(string)
obj["path"], _ = quic["key"].(string)
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
obj["path"] = grpc["serviceName"].(string) obj["path"] = grpc["serviceName"].(string)
@@ -281,6 +275,7 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
} }
} }
obj["id"] = clients[clientIndex].ID obj["id"] = clients[clientIndex].ID
obj["scy"] = clients[clientIndex].Security
externalProxies, _ := stream["externalProxy"].([]interface{}) externalProxies, _ := stream["externalProxy"].([]interface{})
@@ -369,12 +364,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
http, _ := stream["httpSettings"].(map[string]interface{}) http, _ := stream["httpSettings"].(map[string]interface{})
params["path"] = http["path"].(string) params["path"] = http["path"].(string)
params["host"] = searchHost(http) params["host"] = searchHost(http)
case "quic":
quic, _ := stream["quicSettings"].(map[string]interface{})
params["quicSecurity"] = quic["security"].(string)
params["key"] = quic["key"].(string)
header := quic["header"].(map[string]interface{})
params["headerType"] = header["type"].(string)
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
params["serviceName"] = grpc["serviceName"].(string) params["serviceName"] = grpc["serviceName"].(string)
@@ -603,12 +592,6 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
http, _ := stream["httpSettings"].(map[string]interface{}) http, _ := stream["httpSettings"].(map[string]interface{})
params["path"] = http["path"].(string) params["path"] = http["path"].(string)
params["host"] = searchHost(http) params["host"] = searchHost(http)
case "quic":
quic, _ := stream["quicSettings"].(map[string]interface{})
params["quicSecurity"] = quic["security"].(string)
params["key"] = quic["key"].(string)
header := quic["header"].(map[string]interface{})
params["headerType"] = header["type"].(string)
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
params["serviceName"] = grpc["serviceName"].(string) params["serviceName"] = grpc["serviceName"].(string)
@@ -838,12 +821,6 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
http, _ := stream["httpSettings"].(map[string]interface{}) http, _ := stream["httpSettings"].(map[string]interface{})
params["path"] = http["path"].(string) params["path"] = http["path"].(string)
params["host"] = searchHost(http) params["host"] = searchHost(http)
case "quic":
quic, _ := stream["quicSettings"].(map[string]interface{})
params["quicSecurity"] = quic["security"].(string)
params["key"] = quic["key"].(string)
header := quic["header"].(map[string]interface{})
params["headerType"] = header["type"].(string)
case "grpc": case "grpc":
grpc, _ := stream["grpcSettings"].(map[string]interface{}) grpc, _ := stream["grpcSettings"].(map[string]interface{})
params["serviceName"] = grpc["serviceName"].(string) params["serviceName"] = grpc["serviceName"].(string)

View File

@@ -10,8 +10,8 @@ const supportLangs = [
icon: '🇮🇷', icon: '🇮🇷',
}, },
{ {
name: '汉语', name: '中文',
value: 'zh-Hans', value: 'zh-CN',
icon: '🇨🇳', icon: '🇨🇳',
}, },
{ {
@@ -44,6 +44,11 @@ const supportLangs = [
value: 'tr-TR', value: 'tr-TR',
icon: '🇹🇷', icon: '🇹🇷',
}, },
{
name: "Português",
value: "pt-BR",
icon: "🇧🇷",
},
]; ];
function getLang() { function getLang() {

View File

@@ -69,6 +69,14 @@ const WireguardDomainStrategy = [
"ForceIPv6v4" "ForceIPv6v4"
]; ];
const USERS_SECURITY = {
AES_128_GCM: "aes-128-gcm",
CHACHA20_POLY1305: "chacha20-poly1305",
AUTO: "auto",
NONE: "none",
ZERO: "zero",
};
Object.freeze(Protocols); Object.freeze(Protocols);
Object.freeze(SSMethods); Object.freeze(SSMethods);
Object.freeze(TLS_FLOW_CONTROL); Object.freeze(TLS_FLOW_CONTROL);
@@ -76,6 +84,7 @@ Object.freeze(UTLS_FINGERPRINT);
Object.freeze(ALPN_OPTION); Object.freeze(ALPN_OPTION);
Object.freeze(OutboundDomainStrategies); Object.freeze(OutboundDomainStrategies);
Object.freeze(WireguardDomainStrategy); Object.freeze(WireguardDomainStrategy);
Object.freeze(USERS_SECURITY);
class CommonClass { class CommonClass {
@@ -462,7 +471,6 @@ class StreamSettings extends CommonClass {
this.kcp = kcpSettings; this.kcp = kcpSettings;
this.ws = wsSettings; this.ws = wsSettings;
this.http = httpSettings; this.http = httpSettings;
this.quic = quicSettings;
this.grpc = grpcSettings; this.grpc = grpcSettings;
this.httpupgrade = httpupgradeSettings; this.httpupgrade = httpupgradeSettings;
this.splithttp = splithttpSettings; this.splithttp = splithttpSettings;
@@ -514,7 +522,6 @@ class StreamSettings extends CommonClass {
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined, kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
wsSettings: network === 'ws' ? this.ws.toJson() : undefined, wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
httpSettings: network === 'http' ? this.http.toJson() : undefined, httpSettings: network === 'http' ? this.http.toJson() : undefined,
quicSettings: network === 'quic' ? this.quic.toJson() : undefined,
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
splithttpSettings: network === 'splithttp' ? this.splithttp.toJson() : undefined, splithttpSettings: network === 'splithttp' ? this.splithttp.toJson() : undefined,
@@ -582,7 +589,7 @@ class Outbound extends CommonClass {
canEnableTls() { canEnableTls() {
if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false; if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false;
return ["tcp", "ws", "http", "quic", "grpc", "httpupgrade", "splithttp"].includes(this.stream.network); return ["tcp", "ws", "http", "grpc", "httpupgrade", "splithttp"].includes(this.stream.network);
} }
//this is used for xtls-rprx-vision //this is used for xtls-rprx-vision
@@ -698,11 +705,6 @@ class Outbound extends CommonClass {
stream.http = new HttpStreamSettings( stream.http = new HttpStreamSettings(
json.path, json.path,
json.host); json.host);
} else if (network === 'quic') {
stream.quic = new QuicStreamSettings(
json.host ? json.host : 'none',
json.path,
json.type ? json.type : 'none');
} else if (network === 'grpc') { } else if (network === 'grpc') {
stream.grpc = new GrpcStreamSettings(json.path, json.authority, json.type == 'multi'); stream.grpc = new GrpcStreamSettings(json.path, json.authority, json.type == 'multi');
} else if (network === 'httpupgrade') { } else if (network === 'httpupgrade') {
@@ -721,7 +723,7 @@ class Outbound extends CommonClass {
const port = json.port * 1; const port = json.port * 1;
return new Outbound(json.ps, Protocols.VMess, new Outbound.VmessSettings(json.add, port, json.id), stream); return new Outbound(json.ps, Protocols.VMess, new Outbound.VmessSettings(json.add, port, json.id, json.scy), stream);
} }
static fromParamLink(link) { static fromParamLink(link) {
@@ -744,11 +746,6 @@ class Outbound extends CommonClass {
stream.ws = new WsStreamSettings(path, host); stream.ws = new WsStreamSettings(path, host);
} else if (type === 'http' || type == 'h2') { } else if (type === 'http' || type == 'h2') {
stream.http = new HttpStreamSettings(path, host); stream.http = new HttpStreamSettings(path, host);
} else if (type === 'quic') {
stream.quic = new QuicStreamSettings(
url.searchParams.get('quicSecurity') ?? 'none',
url.searchParams.get('key') ?? '',
headerType ?? 'none');
} else if (type === 'grpc') { } else if (type === 'grpc') {
stream.grpc = new GrpcStreamSettings( stream.grpc = new GrpcStreamSettings(
url.searchParams.get('serviceName') ?? '', url.searchParams.get('serviceName') ?? '',
@@ -852,26 +849,46 @@ Outbound.Settings = class extends CommonClass {
} }
}; };
Outbound.FreedomSettings = class extends CommonClass { Outbound.FreedomSettings = class extends CommonClass {
constructor(domainStrategy = '', fragment = {}) { constructor(
domainStrategy = '',
redirect = '',
fragment = {},
noises = []
) {
super(); super();
this.domainStrategy = domainStrategy; this.domainStrategy = domainStrategy;
this.redirect = redirect;
this.fragment = fragment; this.fragment = fragment;
this.noises = noises;
}
addNoise() {
this.noises.push(new Outbound.FreedomSettings.Noise());
}
delNoise(index) {
this.noises.splice(index, 1);
} }
static fromJson(json = {}) { static fromJson(json = {}) {
return new Outbound.FreedomSettings( return new Outbound.FreedomSettings(
json.domainStrategy, json.domainStrategy,
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()],
); );
} }
toJson() { toJson() {
return { return {
domainStrategy: ObjectUtil.isEmpty(this.domainStrategy) ? undefined : this.domainStrategy, domainStrategy: ObjectUtil.isEmpty(this.domainStrategy) ? undefined : this.domainStrategy,
redirect: 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),
}; };
} }
}; };
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();
@@ -888,6 +905,40 @@ Outbound.FreedomSettings.Fragment = class extends CommonClass {
); );
} }
}; };
Outbound.FreedomSettings.Noise = class extends CommonClass {
constructor(
type = 'rand',
packet = '10-20',
delay = '10-16'
) {
super();
this.type = type;
this.packet = packet;
this.delay = delay;
}
static fromJson(json = {}) {
return new Outbound.FreedomSettings.Noise(
json.type,
json.packet,
json.delay,
);
}
toJson() {
return {
type: this.type,
packet: this.packet,
delay: this.delay,
};
}
static toJsonArray(noises) {
return noises.map(noise => noise.toJson());
}
};
Outbound.BlackholeSettings = class extends CommonClass { Outbound.BlackholeSettings = class extends CommonClass {
constructor(type) { constructor(type) {
super(); super();
@@ -907,11 +958,19 @@ Outbound.BlackholeSettings = class extends CommonClass {
} }
}; };
Outbound.DNSSettings = class extends CommonClass { Outbound.DNSSettings = class extends CommonClass {
constructor(network = 'udp', address = '1.1.1.1', port = 53) { constructor(
network = 'udp',
address = '1.1.1.1',
port = 53,
nonIPQuery = 'drop',
blockTypes = []
) {
super(); super();
this.network = network; this.network = network;
this.address = address; this.address = address;
this.port = port; this.port = port;
this.nonIPQuery = nonIPQuery;
this.blockTypes = blockTypes;
} }
static fromJson(json = {}) { static fromJson(json = {}) {
@@ -919,15 +978,18 @@ Outbound.DNSSettings = class extends CommonClass {
json.network, json.network,
json.address, json.address,
json.port, json.port,
json.nonIPQuery,
json.blockTypes,
); );
} }
}; };
Outbound.VmessSettings = class extends CommonClass { Outbound.VmessSettings = class extends CommonClass {
constructor(address, port, id) { constructor(address, port, id, security) {
super(); super();
this.address = address; this.address = address;
this.port = port; this.port = port;
this.id = id; this.id = id;
this.security = security;
} }
static fromJson(json = {}) { static fromJson(json = {}) {
@@ -936,6 +998,7 @@ Outbound.VmessSettings = class extends CommonClass {
json.vnext[0].address, json.vnext[0].address,
json.vnext[0].port, json.vnext[0].port,
json.vnext[0].users[0].id, json.vnext[0].users[0].id,
json.vnext[0].users[0].security,
); );
} }
@@ -944,7 +1007,7 @@ Outbound.VmessSettings = class extends CommonClass {
vnext: [{ vnext: [{
address: this.address, address: this.address,
port: this.port, port: this.port,
users: [{ id: this.id }], users: [{ id: this.id, security: this.security }],
}], }],
}; };
} }

View File

@@ -7,7 +7,7 @@ class AllSetting {
this.webCertFile = ""; this.webCertFile = "";
this.webKeyFile = ""; this.webKeyFile = "";
this.webBasePath = "/"; this.webBasePath = "/";
this.sessionMaxAge = 0; this.sessionMaxAge = 60;
this.pageSize = 50; this.pageSize = 50;
this.expireDiff = 0; this.expireDiff = 0;
this.trafficDiff = 0; this.trafficDiff = 0;
@@ -38,6 +38,7 @@ class AllSetting {
this.subURI = ""; this.subURI = "";
this.subJsonURI = ""; this.subJsonURI = "";
this.subJsonFragment = ""; this.subJsonFragment = "";
this.subJsonNoises = "";
this.subJsonMux = ""; this.subJsonMux = "";
this.subJsonRules = ""; this.subJsonRules = "";

View File

@@ -110,6 +110,14 @@ const TCP_CONGESTION_OPTION = {
RENO: "reno", RENO: "reno",
}; };
const USERS_SECURITY = {
AES_128_GCM: "aes-128-gcm",
CHACHA20_POLY1305: "chacha20-poly1305",
AUTO: "auto",
NONE: "none",
ZERO: "zero",
};
Object.freeze(Protocols); Object.freeze(Protocols);
Object.freeze(SSMethods); Object.freeze(SSMethods);
Object.freeze(XTLS_FLOW_CONTROL); Object.freeze(XTLS_FLOW_CONTROL);
@@ -122,6 +130,7 @@ Object.freeze(SNIFFING_OPTION);
Object.freeze(USAGE_OPTION); 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);
class XrayCommonClass { class XrayCommonClass {
@@ -440,37 +449,6 @@ class HttpStreamSettings extends XrayCommonClass {
} }
} }
class QuicStreamSettings extends XrayCommonClass {
constructor(
security = 'none',
key = RandomUtil.randomSeq(10),
type = 'none'
) {
super();
this.security = security;
this.key = key;
this.type = type;
}
static fromJson(json = {}) {
return new QuicStreamSettings(
json.security,
json.key,
json.header ? json.header.type : 'none',
);
}
toJson() {
return {
security: this.security,
key: this.key,
header: {
type: this.type,
}
}
}
}
class GrpcStreamSettings extends XrayCommonClass { class GrpcStreamSettings extends XrayCommonClass {
constructor( constructor(
serviceName = "", serviceName = "",
@@ -546,10 +524,17 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
path = '/', path = '/',
host = '', host = '',
headers = [], headers = [],
scMaxConcurrentPosts = 100, scMaxConcurrentPosts = "100-200",
scMaxEachPostBytes = 1000000, scMaxEachPostBytes = "1000000-2000000",
scMinPostsIntervalMs = 30, scMinPostsIntervalMs = "10-50",
noSSEHeader = false, noSSEHeader = false,
xPaddingBytes = "100-1000",
xmux = {
maxConcurrency: 0,
maxConnections: 0,
cMaxReuseTimes: 0,
cMaxLifetimeMs: 0
}
) { ) {
super(); super();
this.path = path; this.path = path;
@@ -559,6 +544,8 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
this.scMaxEachPostBytes = scMaxEachPostBytes; this.scMaxEachPostBytes = scMaxEachPostBytes;
this.scMinPostsIntervalMs = scMinPostsIntervalMs; this.scMinPostsIntervalMs = scMinPostsIntervalMs;
this.noSSEHeader = noSSEHeader; this.noSSEHeader = noSSEHeader;
this.xPaddingBytes = xPaddingBytes;
this.xmux = xmux;
} }
addHeader(name, value) { addHeader(name, value) {
@@ -578,6 +565,8 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
json.scMaxEachPostBytes, json.scMaxEachPostBytes,
json.scMinPostsIntervalMs, json.scMinPostsIntervalMs,
json.noSSEHeader, json.noSSEHeader,
json.xPaddingBytes,
json.xmux,
); );
} }
@@ -590,6 +579,13 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
scMaxEachPostBytes: this.scMaxEachPostBytes, scMaxEachPostBytes: this.scMaxEachPostBytes,
scMinPostsIntervalMs: this.scMinPostsIntervalMs, scMinPostsIntervalMs: this.scMinPostsIntervalMs,
noSSEHeader: this.noSSEHeader, noSSEHeader: this.noSSEHeader,
xPaddingBytes: this.xPaddingBytes,
xmux: {
maxConcurrency: this.xmux.maxConcurrency,
maxConnections: this.xmux.maxConnections,
cMaxReuseTimes: this.xmux.cMaxReuseTimes,
cMaxLifetimeMs: this.xmux.cMaxLifetimeMs
}
}; };
} }
} }
@@ -901,7 +897,7 @@ class RealityStreamSettings extends XrayCommonClass {
minClient = '', minClient = '',
maxClient = '', maxClient = '',
maxTimediff = 0, maxTimediff = 0,
shortIds = RandomUtil.randomShortId(), shortIds = RandomUtil.randomShortIds(),
settings = new RealityStreamSettings.Settings() settings = new RealityStreamSettings.Settings()
) { ) {
super(); super();
@@ -1079,7 +1075,6 @@ class StreamSettings extends XrayCommonClass {
kcpSettings = new KcpStreamSettings(), kcpSettings = new KcpStreamSettings(),
wsSettings = new WsStreamSettings(), wsSettings = new WsStreamSettings(),
httpSettings = new HttpStreamSettings(), httpSettings = new HttpStreamSettings(),
quicSettings = new QuicStreamSettings(),
grpcSettings = new GrpcStreamSettings(), grpcSettings = new GrpcStreamSettings(),
httpupgradeSettings = new HTTPUpgradeStreamSettings(), httpupgradeSettings = new HTTPUpgradeStreamSettings(),
splithttpSettings = new SplitHTTPStreamSettings(), splithttpSettings = new SplitHTTPStreamSettings(),
@@ -1096,7 +1091,6 @@ class StreamSettings extends XrayCommonClass {
this.kcp = kcpSettings; this.kcp = kcpSettings;
this.ws = wsSettings; this.ws = wsSettings;
this.http = httpSettings; this.http = httpSettings;
this.quic = quicSettings;
this.grpc = grpcSettings; this.grpc = grpcSettings;
this.httpupgrade = httpupgradeSettings; this.httpupgrade = httpupgradeSettings;
this.splithttp = splithttpSettings; this.splithttp = splithttpSettings;
@@ -1160,7 +1154,6 @@ class StreamSettings extends XrayCommonClass {
KcpStreamSettings.fromJson(json.kcpSettings), KcpStreamSettings.fromJson(json.kcpSettings),
WsStreamSettings.fromJson(json.wsSettings), WsStreamSettings.fromJson(json.wsSettings),
HttpStreamSettings.fromJson(json.httpSettings), HttpStreamSettings.fromJson(json.httpSettings),
QuicStreamSettings.fromJson(json.quicSettings),
GrpcStreamSettings.fromJson(json.grpcSettings), GrpcStreamSettings.fromJson(json.grpcSettings),
HTTPUpgradeStreamSettings.fromJson(json.httpupgradeSettings), HTTPUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
SplitHTTPStreamSettings.fromJson(json.splithttpSettings), SplitHTTPStreamSettings.fromJson(json.splithttpSettings),
@@ -1181,7 +1174,6 @@ class StreamSettings extends XrayCommonClass {
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined, kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
wsSettings: network === 'ws' ? this.ws.toJson() : undefined, wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
httpSettings: network === 'http' ? this.http.toJson() : undefined, httpSettings: network === 'http' ? this.http.toJson() : undefined,
quicSettings: network === 'quic' ? this.quic.toJson() : undefined,
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
splithttpSettings: network === 'splithttp' ? this.splithttp.toJson() : undefined, splithttpSettings: network === 'splithttp' ? this.splithttp.toJson() : undefined,
@@ -1219,6 +1211,27 @@ class Sniffing extends XrayCommonClass {
} }
} }
class Allocate extends XrayCommonClass {
constructor(
strategy = "always",
refresh = 5,
concurrency = 3,
) {
super();
this.strategy = strategy;
this.refresh = refresh;
this.concurrency = concurrency;
}
static fromJson(json = {}) {
return new Allocate(
json.strategy,
json.refresh,
json.concurrency,
);
}
}
class Inbound extends XrayCommonClass { class Inbound extends XrayCommonClass {
constructor( constructor(
port = RandomUtil.randomIntRange(10000, 60000), port = RandomUtil.randomIntRange(10000, 60000),
@@ -1228,6 +1241,7 @@ class Inbound extends XrayCommonClass {
streamSettings = new StreamSettings(), streamSettings = new StreamSettings(),
tag = '', tag = '',
sniffing = new Sniffing(), sniffing = new Sniffing(),
allocate = new Allocate(),
clientStats = '', clientStats = '',
) { ) {
super(); super();
@@ -1238,6 +1252,7 @@ class Inbound extends XrayCommonClass {
this.stream = streamSettings; this.stream = streamSettings;
this.tag = tag; this.tag = tag;
this.sniffing = sniffing; this.sniffing = sniffing;
this.allocate = allocate;
this.clientStats = clientStats; this.clientStats = clientStats;
} }
getClientStats() { getClientStats() {
@@ -1298,10 +1313,6 @@ class Inbound extends XrayCommonClass {
return this.network === "kcp"; return this.network === "kcp";
} }
get isQuic() {
return this.network === "quic"
}
get isGrpc() { get isGrpc() {
return this.network === "grpc"; return this.network === "grpc";
} }
@@ -1380,18 +1391,6 @@ class Inbound extends XrayCommonClass {
return null; return null;
} }
get quicSecurity() {
return this.stream.quic.security;
}
get quicKey() {
return this.stream.quic.key;
}
get quicType() {
return this.stream.quic.type;
}
get kcpType() { get kcpType() {
return this.stream.kcp.type; return this.stream.kcp.type;
} }
@@ -1411,7 +1410,7 @@ class Inbound extends XrayCommonClass {
canEnableTls() { canEnableTls() {
if (![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false; if (![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false;
return ["tcp", "ws", "http", "quic", "grpc", "httpupgrade", "splithttp"].includes(this.network); return ["tcp", "ws", "http", "grpc", "httpupgrade", "splithttp"].includes(this.network);
} }
//this is used for xtls-rprx-vision //this is used for xtls-rprx-vision
@@ -1444,22 +1443,24 @@ class Inbound extends XrayCommonClass {
this.stream = new StreamSettings(); this.stream = new StreamSettings();
this.tag = ''; this.tag = '';
this.sniffing = new Sniffing(); this.sniffing = new Sniffing();
this.allocate = new Allocate();
} }
genVmessLink(address = '', port = this.port, forceTls, remark = '', clientId) { genVmessLink(address = '', port = this.port, forceTls, remark = '', clientId, security) {
if (this.protocol !== Protocols.VMESS) { if (this.protocol !== Protocols.VMESS) {
return ''; return '';
} }
const security = forceTls == 'same' ? this.stream.security : forceTls; const tls = forceTls == 'same' ? this.stream.security : forceTls;
let obj = { let obj = {
v: '2', v: '2',
ps: remark, ps: remark,
add: address, add: address,
port: port, port: port,
id: clientId, id: clientId,
scy: security,
net: this.stream.network, net: this.stream.network,
type: 'none', type: 'none',
tls: security, tls: tls,
}; };
const network = this.stream.network; const network = this.stream.network;
if (network === 'tcp') { if (network === 'tcp') {
@@ -1483,10 +1484,6 @@ class Inbound extends XrayCommonClass {
obj.net = 'h2'; obj.net = 'h2';
obj.path = this.stream.http.path; obj.path = this.stream.http.path;
obj.host = this.stream.http.host.join(','); obj.host = this.stream.http.host.join(',');
} else if (network === 'quic') {
obj.type = this.stream.quic.type;
obj.host = this.stream.quic.security;
obj.path = this.stream.quic.key;
} else if (network === 'grpc') { } else if (network === 'grpc') {
obj.path = this.stream.grpc.serviceName; obj.path = this.stream.grpc.serviceName;
obj.authority = this.stream.grpc.authority; obj.authority = this.stream.grpc.authority;
@@ -1556,12 +1553,6 @@ class Inbound extends XrayCommonClass {
params.set("path", http.path); params.set("path", http.path);
params.set("host", http.host); params.set("host", http.host);
break; break;
case "quic":
const quic = this.stream.quic;
params.set("quicSecurity", quic.security);
params.set("key", quic.key);
params.set("headerType", quic.type);
break;
case "grpc": case "grpc":
const grpc = this.stream.grpc; const grpc = this.stream.grpc;
params.set("serviceName", grpc.serviceName); params.set("serviceName", grpc.serviceName);
@@ -1677,12 +1668,6 @@ class Inbound extends XrayCommonClass {
params.set("path", http.path); params.set("path", http.path);
params.set("host", http.host); params.set("host", http.host);
break; break;
case "quic":
const quic = this.stream.quic;
params.set("quicSecurity", quic.security);
params.set("key", quic.key);
params.set("headerType", quic.type);
break;
case "grpc": case "grpc":
const grpc = this.stream.grpc; const grpc = this.stream.grpc;
params.set("serviceName", grpc.serviceName); params.set("serviceName", grpc.serviceName);
@@ -1765,12 +1750,6 @@ class Inbound extends XrayCommonClass {
params.set("path", http.path); params.set("path", http.path);
params.set("host", http.host); params.set("host", http.host);
break; break;
case "quic":
const quic = this.stream.quic;
params.set("quicSecurity", quic.security);
params.set("key", quic.key);
params.set("headerType", quic.type);
break;
case "grpc": case "grpc":
const grpc = this.stream.grpc; const grpc = this.stream.grpc;
params.set("serviceName", grpc.serviceName); params.set("serviceName", grpc.serviceName);
@@ -1870,7 +1849,7 @@ class Inbound extends XrayCommonClass {
genLink(address = '', port = this.port, forceTls = 'same', remark = '', client) { genLink(address = '', port = this.port, forceTls = 'same', remark = '', client) {
switch (this.protocol) { switch (this.protocol) {
case Protocols.VMESS: case Protocols.VMESS:
return this.genVmessLink(address, port, forceTls, remark, client.id); return this.genVmessLink(address, port, forceTls, remark, client.id, client.security);
case Protocols.VLESS: case Protocols.VLESS:
return this.genVLESSLink(address, port, forceTls, remark, client.id, client.flow); return this.genVLESSLink(address, port, forceTls, remark, client.id, client.flow);
case Protocols.SHADOWSOCKS: case Protocols.SHADOWSOCKS:
@@ -1944,6 +1923,7 @@ class Inbound extends XrayCommonClass {
StreamSettings.fromJson(json.streamSettings), StreamSettings.fromJson(json.streamSettings),
json.tag, json.tag,
Sniffing.fromJson(json.sniffing), Sniffing.fromJson(json.sniffing),
Allocate.fromJson(json.allocate),
json.clientStats json.clientStats
) )
} }
@@ -1961,6 +1941,7 @@ class Inbound extends XrayCommonClass {
streamSettings: streamSettings, streamSettings: streamSettings,
tag: this.tag, tag: this.tag,
sniffing: this.sniffing.toJson(), sniffing: this.sniffing.toJson(),
allocate: this.allocate.toJson(),
clientStats: this.clientStats clientStats: this.clientStats
}; };
} }
@@ -2007,24 +1988,24 @@ Inbound.Settings = class extends XrayCommonClass {
Inbound.VmessSettings = class extends Inbound.Settings { Inbound.VmessSettings = class extends Inbound.Settings {
constructor(protocol, constructor(protocol,
vmesses = [new Inbound.VmessSettings.Vmess()]) { vmesses = [new Inbound.VmessSettings.VMESS()]) {
super(protocol); super(protocol);
this.vmesses = vmesses; this.vmesses = vmesses;
} }
indexOfVmessById(id) { indexOfVmessById(id) {
return this.vmesses.findIndex(vmess => vmess.id === id); return this.vmesses.findIndex(VMESS => VMESS.id === id);
} }
addVmess(vmess) { addVmess(VMESS) {
if (this.indexOfVmessById(vmess.id) >= 0) { if (this.indexOfVmessById(VMESS.id) >= 0) {
return false; return false;
} }
this.vmesses.push(vmess); this.vmesses.push(VMESS);
} }
delVmess(vmess) { delVmess(VMESS) {
const i = this.indexOfVmessById(vmess.id); const i = this.indexOfVmessById(VMESS.id);
if (i >= 0) { if (i >= 0) {
this.vmesses.splice(i, 1); this.vmesses.splice(i, 1);
} }
@@ -2033,7 +2014,7 @@ Inbound.VmessSettings = class extends Inbound.Settings {
static fromJson(json = {}) { static fromJson(json = {}) {
return new Inbound.VmessSettings( return new Inbound.VmessSettings(
Protocols.VMESS, Protocols.VMESS,
json.clients.map(client => Inbound.VmessSettings.Vmess.fromJson(client)), json.clients.map(client => Inbound.VmessSettings.VMESS.fromJson(client)),
); );
} }
@@ -2043,10 +2024,23 @@ Inbound.VmessSettings = class extends Inbound.Settings {
}; };
} }
}; };
Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
constructor(id = RandomUtil.randomUUID(), email = RandomUtil.randomLowerAndNum(8), limitIp = 0, totalGB = 0, expiryTime = 0, enable = true, tgId = '', subId = RandomUtil.randomLowerAndNum(16), reset = 0) { Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
constructor(
id = RandomUtil.randomUUID(),
security = USERS_SECURITY.AUTO,
email = RandomUtil.randomLowerAndNum(8),
limitIp = 0,
totalGB = 0,
expiryTime = 0,
enable = true,
tgId = '',
subId = RandomUtil.randomLowerAndNum(16),
reset = 0
) {
super(); super();
this.id = id; this.id = id;
this.security = security;
this.email = email; this.email = email;
this.limitIp = limitIp; this.limitIp = limitIp;
this.totalGB = totalGB; this.totalGB = totalGB;
@@ -2058,8 +2052,9 @@ Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
} }
static fromJson(json = {}) { static fromJson(json = {}) {
return new Inbound.VmessSettings.Vmess( return new Inbound.VmessSettings.VMESS(
json.id, json.id,
json.security,
json.email, json.email,
json.limitIp, json.limitIp,
json.totalGB, json.totalGB,
@@ -2098,10 +2093,12 @@ Inbound.VmessSettings.Vmess = class extends XrayCommonClass {
}; };
Inbound.VLESSSettings = class extends Inbound.Settings { Inbound.VLESSSettings = class extends Inbound.Settings {
constructor(protocol, constructor(
protocol,
vlesses = [new Inbound.VLESSSettings.VLESS()], vlesses = [new Inbound.VLESSSettings.VLESS()],
decryption = 'none', decryption = 'none',
fallbacks = []) { fallbacks = []
) {
super(protocol); super(protocol);
this.vlesses = vlesses; this.vlesses = vlesses;
this.decryption = decryption; this.decryption = decryption;
@@ -2135,7 +2132,18 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
}; };
Inbound.VLESSSettings.VLESS = class extends XrayCommonClass { Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
constructor(id = RandomUtil.randomUUID(), flow = '', email = RandomUtil.randomLowerAndNum(8), limitIp = 0, totalGB = 0, expiryTime = 0, enable = true, tgId = '', subId = RandomUtil.randomLowerAndNum(16), reset = 0) { constructor(
id = RandomUtil.randomUUID(),
flow = '',
email = RandomUtil.randomLowerAndNum(8),
limitIp = 0,
totalGB = 0,
expiryTime = 0,
enable = true,
tgId = '',
subId = RandomUtil.randomLowerAndNum(16),
reset = 0
) {
super(); super();
this.id = id; this.id = id;
this.flow = flow; this.flow = flow;
@@ -2259,8 +2267,20 @@ Inbound.TrojanSettings = class extends Inbound.Settings {
}; };
} }
}; };
Inbound.TrojanSettings.Trojan = class extends XrayCommonClass { Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
constructor(password = RandomUtil.randomSeq(10), flow = '', email = RandomUtil.randomLowerAndNum(8), limitIp = 0, totalGB = 0, expiryTime = 0, enable = true, tgId = '', subId = RandomUtil.randomLowerAndNum(16), reset = 0) { constructor(
password = RandomUtil.randomSeq(10),
flow = '',
email = RandomUtil.randomLowerAndNum(8),
limitIp = 0,
totalGB = 0,
expiryTime = 0,
enable = true,
tgId = '',
subId = RandomUtil.randomLowerAndNum(16),
reset = 0
) {
super(); super();
this.password = password; this.password = password;
this.flow = flow; this.flow = flow;
@@ -2405,7 +2425,18 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings {
}; };
Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass { Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
constructor(method = '', password = RandomUtil.randomShadowsocksPassword(), email = RandomUtil.randomLowerAndNum(8), limitIp = 0, totalGB = 0, expiryTime = 0, enable = true, tgId = '', subId = RandomUtil.randomLowerAndNum(16), reset = 0) { constructor(
method = '',
password = RandomUtil.randomShadowsocksPassword(),
email = RandomUtil.randomLowerAndNum(8),
limitIp = 0,
totalGB = 0,
expiryTime = 0,
enable = true,
tgId = '',
subId = RandomUtil.randomLowerAndNum(16),
reset = 0
) {
super(); super();
this.method = method; this.method = method;
this.password = password; this.password = password;
@@ -2477,13 +2508,18 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
}; };
Inbound.DokodemoSettings = class extends Inbound.Settings { Inbound.DokodemoSettings = class extends Inbound.Settings {
constructor(protocol, address, port, network = 'tcp,udp', followRedirect = false, timeout = 0) { constructor(
protocol,
address,
port,
network = 'tcp,udp',
followRedirect = false
) {
super(protocol); super(protocol);
this.address = address; this.address = address;
this.port = port; this.port = port;
this.network = network; this.network = network;
this.followRedirect = followRedirect; this.followRedirect = followRedirect;
this.timeout = timeout;
} }
static fromJson(json = {}) { static fromJson(json = {}) {
@@ -2493,7 +2529,6 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
json.port, json.port,
json.network, json.network,
json.followRedirect, json.followRedirect,
json.timeout,
); );
} }
@@ -2503,7 +2538,6 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
port: this.port, port: this.port,
network: this.network, network: this.network,
followRedirect: this.followRedirect, followRedirect: this.followRedirect,
timeout: this.timeout,
}; };
} }
}; };
@@ -2563,9 +2597,14 @@ Inbound.SocksSettings.SocksAccount = class extends XrayCommonClass {
}; };
Inbound.HttpSettings = class extends Inbound.Settings { Inbound.HttpSettings = class extends Inbound.Settings {
constructor(protocol, accounts = [new Inbound.HttpSettings.HttpAccount()]) { constructor(
protocol,
accounts = [new Inbound.HttpSettings.HttpAccount()],
allowTransparent = false,
) {
super(protocol); super(protocol);
this.accounts = accounts; this.accounts = accounts;
this.allowTransparent = allowTransparent;
} }
addAccount(account) { addAccount(account) {
@@ -2580,12 +2619,14 @@ Inbound.HttpSettings = class extends Inbound.Settings {
return new Inbound.HttpSettings( return new Inbound.HttpSettings(
Protocols.HTTP, Protocols.HTTP,
json.accounts.map(account => Inbound.HttpSettings.HttpAccount.fromJson(account)), json.accounts.map(account => Inbound.HttpSettings.HttpAccount.fromJson(account)),
json.allowTransparent,
); );
} }
toJson() { toJson() {
return { return {
accounts: Inbound.HttpSettings.toJsonArray(this.accounts), accounts: Inbound.HttpSettings.toJsonArray(this.accounts),
allowTransparent: this.allowTransparent,
}; };
} }
}; };

View File

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

View File

@@ -83,10 +83,6 @@ func (a *IndexController) login(c *gin.Context) {
logger.Warning("Unable to get session's max age from DB") logger.Warning("Unable to get session's max age from DB")
} }
if sessionMaxAge <= 0 {
sessionMaxAge = 60
}
err = session.SetMaxAge(c, sessionMaxAge*60) err = session.SetMaxAge(c, sessionMaxAge*60)
if err != nil { if err != nil {
logger.Warning("Unable to set session's max age") logger.Warning("Unable to set session's max age")

View File

@@ -42,12 +42,12 @@ func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) {
if err == nil { if err == nil {
m.Success = true m.Success = true
if msg != "" { if msg != "" {
m.Msg = msg + I18nWeb(c, "success") m.Msg = msg + " " + I18nWeb(c, "success")
} }
} else { } else {
m.Success = false m.Success = false
m.Msg = msg + I18nWeb(c, "fail") + ": " + err.Error() m.Msg = msg + " " + I18nWeb(c, "fail") + ": " + err.Error()
logger.Warning(msg+I18nWeb(c, "fail")+": ", err) logger.Warning(msg+" "+I18nWeb(c, "fail")+": ", err)
} }
c.JSON(http.StatusOK, m) c.JSON(http.StatusOK, m)
} }

View File

@@ -52,6 +52,7 @@ type AllSetting struct {
SubJsonPath string `json:"subJsonPath" form:"subJsonPath"` SubJsonPath string `json:"subJsonPath" form:"subJsonPath"`
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"` SubJsonURI string `json:"subJsonURI" form:"subJsonURI"`
SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"` SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"`
SubJsonNoises string `json:"subJsonNoises" form:"subJsonNoises"`
SubJsonMux string `json:"subJsonMux" form:"subJsonMux"` SubJsonMux string `json:"subJsonMux" form:"subJsonMux"`
SubJsonRules string `json:"subJsonRules" form:"subJsonRules"` SubJsonRules string `json:"subJsonRules" form:"subJsonRules"`
Datepicker string `json:"datepicker" form:"datepicker"` Datepicker string `json:"datepicker" form:"datepicker"`

View File

@@ -416,19 +416,19 @@
<a-col span="24"> <a-col span="24">
<a-form> <a-form>
<a-form-item> <a-form-item>
<a-input autocomplete="username" v-model.trim="user.username" placeholder='{{ i18n "username" }}' <a-input autocomplete="username" name="username" v-model.trim="user.username" placeholder='{{ i18n "username" }}'
@keydown.enter.native="login" autofocus> @keydown.enter.native="login" autofocus>
<a-icon slot="prefix" type="user" style="font-size: 16px;"></a-icon> <a-icon slot="prefix" type="user" style="font-size: 16px;"></a-icon>
</a-input> </a-input>
</a-form-item> </a-form-item>
<a-form-item> <a-form-item>
<password-input autocomplete="current-password" icon="lock" v-model.trim="user.password" <password-input autocomplete="password" name="password" icon="lock" v-model.trim="user.password"
placeholder='{{ i18n "password" }}' placeholder='{{ i18n "password" }}'
@keydown.enter.native="login"> @keydown.enter.native="login">
</password-input> </password-input>
</a-form-item> </a-form-item>
<a-form-item v-if="secretEnable"> <a-form-item v-if="secretEnable">
<password-input autocomplete="secret" icon="key" v-model.trim="user.loginSecret" <password-input autocomplete="secret" name="secret" icon="key" v-model.trim="user.loginSecret"
placeholder='{{ i18n "secretToken" }}' placeholder='{{ i18n "secretToken" }}'
@keydown.enter.native="login"> @keydown.enter.native="login">
</password-input> </password-input>
@@ -449,7 +449,7 @@
<a-row justify="center" class="centered"> <a-row justify="center" class="centered">
<a-col :span="24"> <a-col :span="24">
<a-select ref="selectLang" v-model="lang" <a-select ref="selectLang" v-model="lang"
@change="setLang(lang)" style="width: 150px;" @change="setLang(lang)" style="width: 200px;"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option :value="l.value" label="English" v-for="l in supportLangs"> <a-select-option :value="l.value" label="English" v-for="l in supportLangs">
<span role="img" aria-label="l.name" v-text="l.icon"></span> <span role="img" aria-label="l.name" v-text="l.icon"></span>

View File

@@ -28,6 +28,11 @@
<a-form-item label='{{ i18n "pages.client.clientCount" }}' v-if="clientsBulkModal.emailMethod < 2"> <a-form-item label='{{ i18n "pages.client.clientCount" }}' v-if="clientsBulkModal.emailMethod < 2">
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number> <a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "security" }}' v-if="inbound.protocol === Protocols.VMESS">
<a-select v-model="clientsBulkModal.security" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in USERS_SECURITY" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='Flow' v-if="clientsBulkModal.inbound.canEnableTlsFlow()"> <a-form-item label='Flow' v-if="clientsBulkModal.inbound.canEnableTlsFlow()">
<a-select v-model="clientsBulkModal.flow" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="clientsBulkModal.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>
@@ -146,6 +151,7 @@
emailPostfix: "", emailPostfix: "",
subId: "", subId: "",
tgId: '', tgId: '',
security: "auto",
flow: "", flow: "",
delayedStart: false, delayedStart: false,
reset: 0, reset: 0,
@@ -168,6 +174,7 @@
newClient.email += useNum ? prefix + i.toString() + postfix : prefix + postfix; newClient.email += useNum ? prefix + i.toString() + postfix : prefix + postfix;
if (clientsBulkModal.subId.length > 0) newClient.subId = clientsBulkModal.subId; if (clientsBulkModal.subId.length > 0) newClient.subId = clientsBulkModal.subId;
newClient.tgId = clientsBulkModal.tgId; newClient.tgId = clientsBulkModal.tgId;
newClient.security = clientsBulkModal.security;
newClient.limitIp = clientsBulkModal.limitIp; newClient.limitIp = clientsBulkModal.limitIp;
newClient._totalGB = clientsBulkModal.totalGB; newClient._totalGB = clientsBulkModal.totalGB;
newClient._expiryTime = clientsBulkModal.expiryTime; newClient._expiryTime = clientsBulkModal.expiryTime;
@@ -203,6 +210,7 @@
this.emailPostfix = ""; this.emailPostfix = "";
this.subId = ""; this.subId = "";
this.tgId = ''; this.tgId = '';
this.security = "auto";
this.flow = ""; this.flow = "";
this.dbInbound = new DBInbound(dbInbound); this.dbInbound = new DBInbound(dbInbound);
this.inbound = dbInbound.toInbound(); this.inbound = dbInbound.toInbound();
@@ -211,7 +219,7 @@
}, },
newClient(protocol) { newClient(protocol) {
switch (protocol) { switch (protocol) {
case Protocols.VMESS: return new Inbound.VmessSettings.Vmess(); case Protocols.VMESS: return new Inbound.VmessSettings.VMESS();
case Protocols.VLESS: return new Inbound.VLESSSettings.VLESS(); case Protocols.VLESS: return new Inbound.VLESSSettings.VLESS();
case Protocols.TROJAN: return new Inbound.TrojanSettings.Trojan(); case Protocols.TROJAN: return new Inbound.TrojanSettings.Trojan();
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings.Shadowsocks(clientsBulkModal.inbound.settings.shadowsockses[0].method); case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings.Shadowsocks(clientsBulkModal.inbound.settings.shadowsockses[0].method);

View File

@@ -61,7 +61,7 @@
}, },
addClient(protocol, clients) { addClient(protocol, clients) {
switch (protocol) { switch (protocol) {
case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.Vmess()); case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.VMESS());
case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS()); case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS());
case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan()); case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan());
case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method)); case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method));

View File

@@ -1,8 +1,10 @@
{{define "component/passwordInput"}} {{define "component/passwordInput"}}
<template> <template>
<a-input :value="value" :type="showPassword ? 'text' : 'password'" <a-input :value="value" :type="showPassword ? 'text' : 'password'"
:placeholder="placeholder" :placeholder="placeholder"
@input="$emit('input', $event.target.value)"> :autocomplete="autocomplete"
:name="name"
@input="$emit('input', $event.target.value)">
<template v-if="icon" #prefix> <template v-if="icon" #prefix>
<a-icon :type="icon" style="font-size: 16px;" /> <a-icon :type="icon" style="font-size: 16px;" />
</template> </template>
@@ -18,7 +20,7 @@
{{define "component/password"}} {{define "component/password"}}
<script> <script>
Vue.component('password-input', { Vue.component('password-input', {
props: ["title", "value", "placeholder", "icon"], props: ["title", "value", "placeholder", "icon", "autocomplete", "name"],
template: `{{template "component/passwordInput"}}`, template: `{{template "component/passwordInput"}}`,
data() { data() {
return { return {

View File

@@ -1,5 +1,7 @@
{{define "dnsModal"}} {{define "dnsModal"}}
<a-modal id="dns-modal" v-model="dnsModal.visible" :title="dnsModal.title" @ok="dnsModal.ok" :closable="true" :mask-closable="false" :ok-text="dnsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme"> <a-modal id="dns-modal" v-model="dnsModal.visible" :title="dnsModal.title" @ok="dnsModal.ok" :closable="true"
:mask-closable="false" :ok-text="dnsModal.okText" cancel-text='{{ i18n "close" }}'
:class="themeSwitcher.currentTheme">
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.xray.outbound.address" }}'> <a-form-item label='{{ i18n "pages.xray.outbound.address" }}'>
<a-input v-model.trim="dnsModal.dnsServer.address"></a-input> <a-input v-model.trim="dnsModal.dnsServer.address"></a-input>
@@ -8,18 +10,29 @@
<a-button icon="plus" size="small" type="primary" @click="dnsModal.dnsServer.domains.push('')"></a-button> <a-button icon="plus" size="small" type="primary" @click="dnsModal.dnsServer.domains.push('')"></a-button>
<template v-for="(domain, index) in dnsModal.dnsServer.domains"> <template v-for="(domain, index) in dnsModal.dnsServer.domains">
<a-input v-model.trim="dnsModal.dnsServer.domains[index]"> <a-input v-model.trim="dnsModal.dnsServer.domains[index]">
<a-button icon="minus" size="small" slot="addonAfter" @click="dnsModal.dnsServer.domains.splice(index,1)"></a-button> <a-button icon="minus" size="small" slot="addonAfter"
@click="dnsModal.dnsServer.domains.splice(index,1)"></a-button>
</a-input> </a-input>
</template> </template>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.xray.dns.strategy" }}' v-if="isAdvanced"> <a-form-item label='{{ i18n "pages.xray.dns.strategy" }}' v-if="isAdvanced">
<a-select v-model="dnsModal.dnsServer.queryStrategy" style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="dnsModal.dnsServer.queryStrategy" style="width: 100%"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']"> [[ l ]] </a-select-option> <a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']"> [[ l ]] </a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='Skip Fallback' v-if="isAdvanced"> <a-form-item label='Skip Fallback' v-if="isAdvanced">
<a-switch v-model="dnsModal.dnsServer.skipFallback"></a-switch> <a-switch v-model="dnsModal.dnsServer.skipFallback"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "pages.xray.dns.expectIPs"}}'>
<a-button icon="plus" size="small" type="primary" @click="dnsModal.dnsServer.expectIPs.push('')"></a-button>
<template v-for="(domain, index) in dnsModal.dnsServer.expectIPs">
<a-input v-model.trim="dnsModal.dnsServer.expectIPs[index]">
<a-button icon="minus" size="small" slot="addonAfter"
@click="dnsModal.dnsServer.expectIPs.splice(index,1)"></a-button>
</a-input>
</template>
</a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
<script> <script>
@@ -32,20 +45,24 @@
dnsServer: { dnsServer: {
address: "localhost", address: "localhost",
domains: [], domains: [],
expectIPs: [],
queryStrategy: 'UseIP', queryStrategy: 'UseIP',
skipFallback: true, skipFallback: true,
}, },
ok() { ok() {
domains = dnsModal.dnsServer.domains.filter(d => d.length > 0); domains = dnsModal.dnsServer.domains.filter(d => d.length > 0);
expectIPs = dnsModal.dnsServer.expectIPs.filter(ip => ip.length > 0);
dnsModal.dnsServer.domains = domains; dnsModal.dnsServer.domains = domains;
newDnsServer = domains.length > 0 ? dnsModal.dnsServer : dnsModal.dnsServer.address; dnsModal.dnsServer.expectIPs = expectIPs;
newDnsServer = (domains.length > 0 || expectIPs.length > 0) ? dnsModal.dnsServer : dnsModal.dnsServer.address;
ObjectUtil.execute(dnsModal.confirm, newDnsServer); ObjectUtil.execute(dnsModal.confirm, newDnsServer);
}, },
show({ show({
title = '', title = '',
okText = '{{ i18n "confirm" }}', okText = '{{ i18n "confirm" }}',
dnsServer, dnsServer,
confirm = (dnsServer) => {}, confirm = (dnsServer) => { },
isEdit = false isEdit = false
}) { }) {
this.title = title; this.title = title;
@@ -59,6 +76,7 @@
this.dnsServer = { this.dnsServer = {
address: dnsServer ?? "", address: dnsServer ?? "",
domains: [], domains: [],
expectIPs: [],
queryStrategy: 'UseIP', queryStrategy: 'UseIP',
skipFallback: true, skipFallback: true,
} }
@@ -67,6 +85,7 @@
this.dnsServer = { this.dnsServer = {
address: "localhost", address: "localhost",
domains: [], domains: [],
expectIPs: [],
queryStrategy: 'UseIP', queryStrategy: 'UseIP',
skipFallback: true, skipFallback: true,
} }
@@ -85,8 +104,8 @@
}, },
computed: { computed: {
isAdvanced: { isAdvanced: {
get: function() { get: function () {
return dnsModal.dnsServer.domains.length > 0 return dnsModal.dnsServer.domains.length > 0;
} }
} }
} }

View File

@@ -0,0 +1,15 @@
{{define "form/allocate"}}
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='Strategy'>
<a-select v-model="inbound.allocate.strategy" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="s in ['always','random']" :value="s">[[ s ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='Refresh'>
<a-input-number v-model.number="inbound.allocate.refresh" min="0"></a-input-number>
</a-form-item>
<a-form-item label='Concurrency'>
<a-input-number v-model.number="inbound.allocate.concurrency" min="0"></a-input-number>
</a-form-item>
</a-form>
{{end}}

View File

@@ -39,6 +39,11 @@
</template> </template>
<a-input v-model.trim="client.id"></a-input> <a-input v-model.trim="client.id"></a-input>
</a-form-item> </a-form-item>
<a-form-item v-if="inbound.protocol === Protocols.VMESS" label='{{ i18n "security" }}'>
<a-select v-model="client.security" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in USERS_SECURITY" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item v-if="client.email && app.subSettings.enable"> <a-form-item v-if="client.email && app.subSettings.enable">
<template slot="label"> <template slot="label">
<a-tooltip> <a-tooltip>
@@ -57,7 +62,7 @@
<template slot="title"> <template slot="title">
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span> <span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
</template> </template>
Telegram ID Telegram ChatID
<a-icon type="question-circle"></a-icon> <a-icon type="question-circle"></a-icon>
</a-tooltip> </a-tooltip>
</template> </template>
@@ -99,7 +104,7 @@
</a-textarea> </a-textarea>
</a-form> </a-form>
</a-form-item> </a-form-item>
<a-form-item v-if="inbound.xtls" label='Flow'> <a-form-item v-if="inbound.stream.isXtls" 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>
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option> <a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>

View File

@@ -115,7 +115,19 @@
</template> </template>
<!-- sniffing --> <!-- sniffing -->
<template> <a-collapse>
{{template "form/sniffing"}} <a-collapse-panel header='Sniffing'>
</template> {{template "form/sniffing"}}
</a-collapse-panel>
</a-collapse>
<!-- allocate -->
<!-- Temporarily disabled until we accepts range for port allocation
<a-collapse>
<a-collapse-panel header='Allocate'>
{{template "form/allocate"}}
</a-collapse-panel>
</a-collapse>
-->
{{end}} {{end}}

View File

@@ -22,8 +22,13 @@
<a-select-option v-for="s in OutboundDomainStrategies" :value="s">[[ s ]]</a-select-option> <a-select-option v-for="s in OutboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='Redirect'>
<a-input v-model="outbound.settings.redirect"></a-input>
</a-form-item>
<a-form-item label='Fragment'> <a-form-item label='Fragment'>
<a-switch :checked="Object.keys(outbound.settings.fragment).length >0" @change="checked => outbound.settings.fragment = checked ? new Outbound.FreedomSettings.Fragment() : {}"></a-switch> <a-switch :checked="Object.keys(outbound.settings.fragment).length >0"
@change="checked => outbound.settings.fragment = checked ? new Outbound.FreedomSettings.Fragment() : {}">
</a-switch>
</a-form-item> </a-form-item>
<template v-if="Object.keys(outbound.settings.fragment).length >0"> <template v-if="Object.keys(outbound.settings.fragment).length >0">
<a-form-item label='Packets'> <a-form-item label='Packets'>
@@ -38,6 +43,40 @@
<a-input v-model.trim="outbound.settings.fragment.interval"></a-input> <a-input v-model.trim="outbound.settings.fragment.interval"></a-input>
</a-form-item> </a-form-item>
</template> </template>
<!-- Switch for Noises -->
<a-form-item label='Noises'>
<a-switch :checked="outbound.settings.noises.length > 0"
@change="checked => outbound.settings.noises = checked ? [new Outbound.FreedomSettings.Noise()] : []">
</a-switch>
</a-form-item>
<!-- Add Noise Button -->
<template v-if="outbound.settings.noises.length > 0">
<a-form-item label="Noises">
<a-button icon="plus" type="primary" size="small" @click="outbound.settings.addNoise()"></a-button>
</a-form-item>
<!-- Noise Configurations -->
<a-form v-for="(noise, index) in outbound.settings.noises" :key="index" :colon="false" :label-col="{ md: {span:8} }"
:wrapper-col="{ md: {span:14} }">
<a-divider style="margin:0;"> Noise [[ index + 1 ]]
<a-icon v-if="outbound.settings.noises.length > 1" type="delete" @click="() => outbound.settings.delNoise(index)"
style="color: rgb(255, 77, 79); cursor: pointer;"></a-icon>
</a-divider>
<a-form-item label='Type'>
<a-select v-model="noise.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="s in ['rand','base64','str']" :value="s">[[ s ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='Packet'>
<a-input v-model.trim="noise.packet"></a-input>
</a-form-item>
<a-form-item label='Delay'>
<a-input v-model.trim="noise.delay"></a-input>
</a-form-item>
</a-form>
</template>
</template> </template>
<!-- blackhole settings --> <!-- blackhole settings -->
@@ -56,6 +95,14 @@
<a-select-option v-for="s in ['udp','tcp']" :value="s">[[ s ]]</a-select-option> <a-select-option v-for="s in ['udp','tcp']" :value="s">[[ s ]]</a-select-option>
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='non-IP queries'>
<a-select v-model="outbound.settings.nonIPQuery" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="s in ['drop','skip']" :value="s">[[ s ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item v-if="outbound.settings.nonIPQuery === 'skip'" label='Block Types' >
<a-input v-model.number="outbound.settings.blockTypes"></a-input>
</a-form-item>
</template> </template>
<!-- wireguard settings --> <!-- wireguard settings -->
@@ -95,10 +142,10 @@
</a-select> </a-select>
</a-form-item> </a-form-item>
<a-form-item label='MTU'> <a-form-item label='MTU'>
<a-input-number v-model.number="outbound.settings.mtu"></a-input-number> <a-input-number v-model.number="outbound.settings.mtu" min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Workers'> <a-form-item label='Workers'>
<a-input-number min="0" v-model.number="outbound.settings.workers"></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='Kernel Mode'>
<a-switch v-model="outbound.settings.kernelMode"></a-switch> <a-switch v-model="outbound.settings.kernelMode"></a-switch>
@@ -161,6 +208,15 @@
<a-input v-model.trim="outbound.settings.id"></a-input> <a-input v-model.trim="outbound.settings.id"></a-input>
</a-form-item> </a-form-item>
<!-- vmess settings -->
<template v-if="outbound.protocol === Protocols.VMess">
<a-form-item label='Security'>
<a-select v-model="outbound.settings.security" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in USERS_SECURITY" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
</template>
<!-- vless settings --> <!-- vless settings -->
<template v-if="outbound.canEnableTlsFlow()"> <template v-if="outbound.canEnableTlsFlow()">
<a-form-item label='Flow'> <a-form-item label='Flow'>
@@ -215,7 +271,6 @@
<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">H2</a-select-option>
<a-select-option value="quic">QUIC</a-select-option>
<a-select-option value="grpc">gRPC</a-select-option> <a-select-option value="grpc">gRPC</a-select-option>
<a-select-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</a-select-option>
@@ -252,25 +307,25 @@
<a-input v-model="outbound.stream.kcp.seed"></a-input> <a-input v-model="outbound.stream.kcp.seed"></a-input>
</a-form-item> </a-form-item>
<a-form-item label='MTU'> <a-form-item label='MTU'>
<a-input-number v-model.number="outbound.stream.kcp.mtu"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.mtu" min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='TTI (ms)'> <a-form-item label='TTI (ms)'>
<a-input-number v-model.number="outbound.stream.kcp.tti"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.tti" min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Uplink (MB/s)'> <a-form-item label='Uplink (MB/s)'>
<a-input-number v-model.number="outbound.stream.kcp.upCap"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.upCap" min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Downlink (MB/s)'> <a-form-item label='Downlink (MB/s)'>
<a-input-number v-model.number="outbound.stream.kcp.downCap"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.downCap" min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Congestion'> <a-form-item label='Congestion'>
<a-switch v-model="outbound.stream.kcp.congestion"></a-switch> <a-switch v-model="outbound.stream.kcp.congestion"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label='Read Buffer (MB)'> <a-form-item label='Read Buffer (MB)'>
<a-input-number v-model.number="outbound.stream.kcp.readBuffer"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.readBuffer" min="0"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label='Write Buffer (MB)'> <a-form-item label='Write Buffer (MB)'>
<a-input-number v-model.number="outbound.stream.kcp.writeBuffer"></a-input-number> <a-input-number v-model.number="outbound.stream.kcp.writeBuffer" min="0"></a-input-number>
</a-form-item> </a-form-item>
</template> </template>
@@ -293,31 +348,7 @@
<a-input v-model.trim="outbound.stream.http.path"></a-input> <a-input v-model.trim="outbound.stream.http.path"></a-input>
</a-form-item> </a-form-item>
</template> </template>
<!-- quic -->
<template v-if="outbound.stream.network === 'quic'">
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
<a-select v-model="outbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option>
<a-select-option value="aes-128-gcm">AES-128-GCM</a-select-option>
<a-select-option value="chacha20-poly1305">CHACHA20-POLY1305</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='{{ i18n "password" }}'>
<a-input v-model.trim="outbound.stream.quic.key"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="outbound.stream.quic.type" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option>
<a-select-option value="utp">uTP</a-select-option>
<a-select-option value="wechat-video">WeChat</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option>
</a-select>
</a-form-item>
</template>
<!-- grpc --> <!-- grpc -->
<template v-if="outbound.stream.network === 'grpc'"> <template v-if="outbound.stream.network === 'grpc'">
<a-form-item label='Service Name'> <a-form-item label='Service Name'>

View File

@@ -16,8 +16,5 @@
<a-form-item label='Follow Redirect'> <a-form-item label='Follow Redirect'>
<a-switch v-model="inbound.settings.followRedirect"></a-switch> <a-switch v-model="inbound.settings.followRedirect"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label='Timeout'>
<a-input-number v-model.number="inbound.settings.timeout" :min="0"></a-input-number>
</a-form-item>
</a-form> </a-form>
{{end}} {{end}}

View File

@@ -19,5 +19,8 @@
</template> </template>
</a-input> </a-input>
</a-input-group> </a-input-group>
<a-form-item label="Allow Transparent">
<a-switch v-model="inbound.settings.allowTransparent" />
</a-form-item>
</a-form> </a-form>
{{end}} {{end}}

View File

@@ -9,12 +9,10 @@
<table width="100%"> <table width="100%">
<tr class="client-table-header"> <tr class="client-table-header">
<th>{{ i18n "pages.inbounds.email" }}</th> <th>{{ i18n "pages.inbounds.email" }}</th>
<th>Flow</th>
<th>ID</th> <th>ID</th>
</tr> </tr>
<tr v-for="(client, index) in inbound.settings.vlesses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''"> <tr v-for="(client, index) in inbound.settings.vlesses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
<td>[[ client.email ]]</td> <td>[[ client.email ]]</td>
<td>[[ client.flow ]]</td>
<td>[[ client.id ]]</td> <td>[[ client.id ]]</td>
</tr> </tr>
</table> </table>

View File

@@ -10,10 +10,12 @@
<tr class="client-table-header"> <tr class="client-table-header">
<th>{{ i18n "pages.inbounds.email" }}</th> <th>{{ i18n "pages.inbounds.email" }}</th>
<th>ID</th> <th>ID</th>
<th>{{ i18n "security" }}</th>
</tr> </tr>
<tr v-for="(client, index) in inbound.settings.vmesses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''"> <tr v-for="(client, index) in inbound.settings.vmesses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
<td>[[ client.email ]]</td> <td>[[ client.email ]]</td>
<td>[[ client.id ]]</td> <td>[[ client.id ]]</td>
<td>[[ client.security ]]</td>
</tr> </tr>
</table> </table>
</a-collapse-panel> </a-collapse-panel>

View File

@@ -0,0 +1,56 @@
{{define "form/realitySettings"}}
<template>
<a-form-item label='Show'>
<a-switch v-model="inbound.stream.reality.show"></a-switch>
</a-form-item>
<a-form-item label='Xver'>
<a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number>
</a-form-item>
<a-form-item label='uTLS'>
<a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 50%"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='Dest'>
<a-input v-model.trim="inbound.stream.reality.dest"></a-input>
</a-form-item>
<a-form-item label='SNI'>
<a-input v-model.trim="inbound.stream.reality.serverNames"></a-input>
</a-form-item>
<a-form-item label='Max Time Diff (ms)'>
<a-input-number v-model.number="inbound.stream.reality.maxTimediff" :min="0"></a-input-number>
</a-form-item>
<!-- we also have this but i think it's not necessary
<a-form-item label='Min Client'>
<a-input v-model.trim="inbound.stream.reality.minClient"></a-input>
</a-form-item>
<a-form-item label='Max Client'>
<a-input v-model.trim="inbound.stream.reality.maxClient"></a-input>
</a-form-item>
-->
<a-form-item>
<template slot="label">
<a-tooltip>
<template slot="title">
<span>{{ i18n "reset" }}</span>
</template> Short IDs <a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortIds()"
type="sync"></a-icon>
</a-tooltip>
</template>
<a-input v-model.trim="inbound.stream.reality.shortIds"></a-input>
</a-form-item>
<a-form-item label='SpiderX'>
<a-input v-model.trim="inbound.stream.reality.settings.spiderX"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
<a-input v-model.trim="inbound.stream.reality.privateKey"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
<a-input v-model.trim="inbound.stream.reality.settings.publicKey"></a-input>
</a-form-item>
<a-form-item label=" ">
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button>
</a-form-item>
</template>
{{end}}

View File

@@ -1,9 +1,8 @@
{{define "form/sniffing"}} {{define "form/sniffing"}}
<a-divider style="margin:5px 0 0;"></a-divider>
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item> <a-form-item>
<span slot="label"> <span slot="label">
Sniffing {{ i18n "enabled" }}
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
<span>{{ i18n "pages.inbounds.noRecommendKeepDefault" }}</span> <span>{{ i18n "pages.inbounds.noRecommendKeepDefault" }}</span>

View File

@@ -1,33 +0,0 @@
{{define "form/streamQUIC"}}
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'>
<a-select v-model="inbound.stream.quic.security" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option>
<a-select-option value="aes-128-gcm">AES-128-GCM</a-select-option>
<a-select-option value="chacha20-poly1305">CHACHA20-POLY1305</a-select-option>
</a-select>
</a-form-item>
<a-form-item>
<template slot="label">
<a-tooltip>
<template slot="title">
<span>{{ i18n "reset" }}</span>
</template>
{{ i18n "password" }}
<a-icon @click="inbound.stream.quic.key = RandomUtil.randomSeq(10)"type="sync"> </a-icon>
</a-tooltip>
</template>
<a-input v-model.trim="inbound.stream.quic.key"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "camouflage" }}'>
<a-select v-model="inbound.stream.quic.type" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">None</a-select-option>
<a-select-option value="srtp">SRTP</a-select-option>
<a-select-option value="utp">uTP</a-select-option>
<a-select-option value="wechat-video">WeChat</a-select-option>
<a-select-option value="dtls">DTLS 1.2</a-select-option>
<a-select-option value="wireguard">WireGuard</a-select-option>
</a-select>
</a-form-item>
</a-form>
{{end}}

View File

@@ -4,11 +4,10 @@
<a-form-item label='{{ i18n "transmission" }}'> <a-form-item label='{{ i18n "transmission" }}'>
<a-select v-model="inbound.stream.network" style="width: 75%" @change="streamNetworkChange" <a-select v-model="inbound.stream.network" style="width: 75%" @change="streamNetworkChange"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="tcp">TCP</a-select-option> <a-select-option value="tcp">TCP (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">H2</a-select-option>
<a-select-option value="quic">QUIC</a-select-option>
<a-select-option value="grpc">gRPC</a-select-option> <a-select-option value="grpc">gRPC</a-select-option>
<a-select-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</a-select-option>
@@ -36,11 +35,6 @@
{{template "form/streamHTTP"}} {{template "form/streamHTTP"}}
</template> </template>
<!-- quic -->
<template v-if="inbound.stream.network === 'quic'">
{{template "form/streamQUIC"}}
</template>
<!-- grpc --> <!-- grpc -->
<template v-if="inbound.stream.network === 'grpc'"> <template v-if="inbound.stream.network === 'grpc'">
{{template "form/streamGRPC"}} {{template "form/streamGRPC"}}

View File

@@ -20,16 +20,31 @@
</a-input-group> </a-input-group>
</a-form-item> </a-form-item>
<a-form-item label="Max Concurrent Upload"> <a-form-item label="Max Concurrent Upload">
<a-input-number v-model="inbound.stream.splithttp.scMaxConcurrentPosts" :min="0"></a-input-number> <a-input v-model.trim="inbound.stream.splithttp.scMaxConcurrentPosts"></a-input>
</a-form-item> </a-form-item>
<a-form-item label="Max Upload Size (Byte)"> <a-form-item label="Max Upload Size (Byte)">
<a-input-number v-model="inbound.stream.splithttp.scMaxEachPostBytes" :min="0"></a-input-number> <a-input v-model.trim="inbound.stream.splithttp.scMaxEachPostBytes"></a-input>
</a-form-item> </a-form-item>
<a-form-item label="Min Upload Interval (Ms)"> <a-form-item label="Min Upload Interval (Ms)">
<a-input-number v-model="inbound.stream.splithttp.scMinPostsIntervalMs" :min="0"></a-input-number> <a-input v-model.trim="inbound.stream.splithttp.scMinPostsIntervalMs"></a-input>
</a-form-item>
<a-form-item label="Padding Bytes">
<a-input v-model.trim="inbound.stream.splithttp.xPaddingBytes"></a-input>
</a-form-item> </a-form-item>
<a-form-item label="No SSE Header"> <a-form-item label="No SSE Header">
<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">
<a-input-number v-model="inbound.stream.splithttp.xmux.maxConcurrency"></a-input-number>
</a-form-item>
<a-form-item label="Max Connections">
<a-input-number v-model="inbound.stream.splithttp.xmux.maxConnections"></a-input-number>
</a-form-item>
<a-form-item label="Max Reuse Times">
<a-input-number v-model="inbound.stream.splithttp.xmux.cMaxReuseTimes"></a-input-number>
</a-form-item>
<a-form-item label="Max Lifetime (ms)">
<a-input-number v-model="inbound.stream.splithttp.xmux.cMaxLifetimeMs"></a-input-number>
</a-form-item>
</a-form> </a-form>
{{end}} {{end}}

View File

@@ -34,16 +34,19 @@
</a-form-item> </a-form-item>
<a-form-item label="Min/Max Version"> <a-form-item label="Min/Max Version">
<a-input-group compact> <a-input-group compact>
<a-select v-model="inbound.stream.tls.minVersion" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.tls.minVersion" style="width: 50%"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option> <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
</a-select> </a-select>
<a-select v-model="inbound.stream.tls.maxVersion" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.tls.maxVersion" style="width: 50%"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option> <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
</a-select> </a-select>
</a-input-group> </a-input-group>
</a-form-item> </a-form-item>
<a-form-item label="uTLS"> <a-form-item label="uTLS">
<a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme"> <a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 50%"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value=''>None</a-select-option> <a-select-option value=''>None</a-select-option>
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option> <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-select> </a-select>
@@ -60,10 +63,10 @@
<a-switch v-model="inbound.stream.tls.rejectUnknownSni"></a-switch> <a-switch v-model="inbound.stream.tls.rejectUnknownSni"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label="Disable System Root"> <a-form-item label="Disable System Root">
<a-switch v-model="inbound.stream.tls.settings.disableSystemRoot"></a-switch> <a-switch v-model="inbound.stream.tls.disableSystemRoot"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label="Session Resumption"> <a-form-item label="Session Resumption">
<a-switch v-model="inbound.stream.tls.settings.enableSessionResumption"></a-switch> <a-switch v-model="inbound.stream.tls.enableSessionResumption"></a-switch>
</a-form-item> </a-form-item>
<template v-for="cert,index in inbound.stream.tls.certs"> <template v-for="cert,index in inbound.stream.tls.certs">
<a-form-item label='{{ i18n "certificate" }}'> <a-form-item label='{{ i18n "certificate" }}'>
@@ -71,8 +74,10 @@
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button> <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-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
</a-radio-group> </a-radio-group>
<a-button icon="plus" v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()" style="margin-left: 10px"></a-button> <a-button icon="plus" v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()"
<a-button icon="minus" v-if="inbound.stream.tls.certs.length>1" type="primary" size="small" @click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px"></a-button> style="margin-left: 10px"></a-button>
<a-button icon="minus" v-if="inbound.stream.tls.certs.length>1" type="primary" size="small"
@click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px"></a-button>
</a-form-item> </a-form-item>
<template v-if="cert.useFile"> <template v-if="cert.useFile">
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'> <a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
@@ -82,7 +87,8 @@
<a-input v-model.trim="cert.keyFile"></a-input> <a-input v-model.trim="cert.keyFile"></a-input>
</a-form-item> </a-form-item>
<a-form-item label=" "> <a-form-item label=" ">
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button> <a-button type="primary" icon="import" @click="setDefaultCertData(index)">
{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
</a-form-item> </a-form-item>
</template> </template>
<template v-else> <template v-else>
@@ -111,101 +117,13 @@
</template> </template>
<!-- xtls settings --> <!-- xtls settings -->
<template v-else-if="inbound.xtls"> <template v-else-if="inbound.stream.isXtls">
<a-form-item label="SNI" placeholder="Server Name Indication"> {{template "form/xtlsSettings"}}
<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> </template>
<!-- reality settings --> <!-- reality settings -->
<template v-if="inbound.stream.isReality"> <template v-if="inbound.stream.isReality">
<a-form-item label='Show'> {{template "form/realitySettings"}}
<a-switch v-model="inbound.stream.reality.show"></a-switch>
</a-form-item>
<a-form-item label='Xver'>
<a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number>
</a-form-item>
<a-form-item label='uTLS'>
<a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item label='Dest'>
<a-input v-model.trim="inbound.stream.reality.dest"></a-input>
</a-form-item>
<a-form-item label='SNI'>
<a-input v-model.trim="inbound.stream.reality.serverNames"></a-input>
</a-form-item>
<a-form-item>
<template slot="label">
<a-tooltip>
<template slot="title">
<span>{{ i18n "reset" }}</span>
</template> Short ID <a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId()" type="sync"></a-icon>
</a-tooltip>
</template>
<a-input v-model.trim="inbound.stream.reality.shortIds"></a-input>
</a-form-item>
<a-form-item label='SpiderX'>
<a-input v-model.trim="inbound.stream.reality.settings.spiderX"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
<a-input v-model.trim="inbound.stream.reality.privateKey"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
<a-input v-model.trim="inbound.stream.reality.settings.publicKey"></a-input>
</a-form-item>
<a-form-item label=" ">
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button>
</a-form-item>
</template> </template>
</a-form> </a-form>
{{end}} {{end}}

View File

@@ -0,0 +1,58 @@
{{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}}

View File

@@ -58,26 +58,6 @@
</td> </td>
</tr> </tr>
</template> </template>
<template v-if="inbound.isQuic">
<tr>
<td>quic {{ i18n "encryption" }}</td>
<td>
<a-tag>[[ inbound.quicSecurity ]]</a-tag>
</td>
</tr>
<tr>
<td>quic {{ i18n "password" }}</td>
<td>
<a-tag>[[ inbound.quicKey ]]</a-tag>
</td>
</tr>
<tr>
<td>quic {{ i18n "camouflage" }}</td>
<td>
<a-tag>[[ inbound.quicType ]]</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>
@@ -159,6 +139,12 @@
<a-tag>[[ infoModal.clientSettings.id ]]</a-tag> <a-tag>[[ infoModal.clientSettings.id ]]</a-tag>
</td> </td>
</tr> </tr>
<tr v-if="dbInbound.isVMess">
<td>{{ i18n "security" }}</td>
<td>
<a-tag>[[ infoModal.clientSettings.security ]]</a-tag>
</td>
</tr>
<tr v-if="infoModal.inbound.canEnableTlsFlow()"> <tr v-if="infoModal.inbound.canEnableTlsFlow()">
<td>Flow</td> <td>Flow</td>
<td v-if="infoModal.clientSettings.flow"> <td v-if="infoModal.clientSettings.flow">

View File

@@ -733,7 +733,7 @@
this.inbounds.push(to_inbound); this.inbounds.push(to_inbound);
this.dbInbounds.push(dbInbound); this.dbInbounds.push(dbInbound);
if ([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(inbound.protocol)) { if ([Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(inbound.protocol)) {
if (inbound.protocol === Protocols.SHADOWSOCKS && (!to_inbound.isSSMultiUser)) { if (dbInbound.isSS && (!to_inbound.isSSMultiUser)) {
continue; continue;
} }
this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound); this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
@@ -935,6 +935,7 @@
settings: Inbound.Settings.getSettings(baseInbound.protocol).toString(), settings: Inbound.Settings.getSettings(baseInbound.protocol).toString(),
streamSettings: baseInbound.stream.toString(), streamSettings: baseInbound.stream.toString(),
sniffing: baseInbound.sniffing.toString(), sniffing: baseInbound.sniffing.toString(),
allocate: baseInbound.allocate.toString(),
}; };
await this.submit('/panel/inbound/add', data, inModal); await this.submit('/panel/inbound/add', data, inModal);
}, },
@@ -980,6 +981,7 @@
}; };
if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString(); if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString();
data.sniffing = inbound.sniffing.toString(); data.sniffing = inbound.sniffing.toString();
data.allocate = inbound.allocate.toString();
await this.submit('/panel/inbound/add', data, inModal); await this.submit('/panel/inbound/add', data, inModal);
}, },
@@ -999,6 +1001,7 @@
}; };
if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString(); if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString();
data.sniffing = inbound.sniffing.toString(); data.sniffing = inbound.sniffing.toString();
data.allocate = inbound.allocate.toString();
await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal); await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal);
}, },

View File

@@ -138,7 +138,7 @@
<setting-list-item type="text" title='{{ i18n "pages.settings.publicKeyPath"}}' desc='{{ i18n "pages.settings.publicKeyPathDesc"}}' v-model="allSetting.webCertFile"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.publicKeyPath"}}' desc='{{ i18n "pages.settings.publicKeyPathDesc"}}' v-model="allSetting.webCertFile"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.privateKeyPath"}}' desc='{{ i18n "pages.settings.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.privateKeyPath"}}' desc='{{ i18n "pages.settings.privateKeyPathDesc"}}' v-model="allSetting.webKeyFile"></setting-list-item>
<setting-list-item type="text" title='{{ i18n "pages.settings.panelUrlPath"}}' desc='{{ i18n "pages.settings.panelUrlPathDesc"}}' v-model="allSetting.webBasePath"></setting-list-item> <setting-list-item type="text" title='{{ i18n "pages.settings.panelUrlPath"}}' desc='{{ i18n "pages.settings.panelUrlPathDesc"}}' v-model="allSetting.webBasePath"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.sessionMaxAge" }}' desc='{{ i18n "pages.settings.sessionMaxAgeDesc" }}' v-model="allSetting.sessionMaxAge" :min="0"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.sessionMaxAge" }}' desc='{{ i18n "pages.settings.sessionMaxAgeDesc" }}' v-model="allSetting.sessionMaxAge" :min="60"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.pageSize" }}' desc='{{ i18n "pages.settings.pageSizeDesc" }}' v-model="allSetting.pageSize" :min="0" :step="5"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.pageSize" }}' desc='{{ i18n "pages.settings.pageSizeDesc" }}' v-model="allSetting.pageSize" :min="0" :step="5"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.expireTimeDiff" }}' desc='{{ i18n "pages.settings.expireTimeDiffDesc" }}' v-model="allSetting.expireDiff" :min="0"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.expireTimeDiff" }}' desc='{{ i18n "pages.settings.expireTimeDiffDesc" }}' v-model="allSetting.expireDiff" :min="0"></setting-list-item>
<setting-list-item type="number" title='{{ i18n "pages.settings.trafficDiff" }}' desc='{{ i18n "pages.settings.trafficDiffDesc" }}' v-model="allSetting.trafficDiff" :min="0"></setting-list-item> <setting-list-item type="number" title='{{ i18n "pages.settings.trafficDiff" }}' desc='{{ i18n "pages.settings.trafficDiffDesc" }}' v-model="allSetting.trafficDiff" :min="0"></setting-list-item>
@@ -296,23 +296,48 @@
</a-row> </a-row>
<a-collapse v-if="fragment" style="margin-top: 14px;"> <a-collapse v-if="fragment" style="margin-top: 14px;">
<a-collapse-panel header='{{ i18n "pages.settings.fragmentSett"}}' v-if="fragment"> <a-collapse-panel header='{{ i18n "pages.settings.fragmentSett"}}' v-if="fragment">
<a-list-item style="padding: 10px 20px"> <setting-list-item style="padding: 10px 20px" type="text" title='Packets' v-model="fragmentPackets" placeholder="1-1 | 1-3 | tlshello | ..."></setting-list-item>
<a-row>
<a-col :lg="24" :xl="12">
<a-list-item-meta title='Packets'></a-list-item-meta>
</a-col>
<a-col :lg="24" :xl="12">
<a-select v-model="fragmentPackets" style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option :value="p" :label="p" v-for="p in ['1-1', '1-3', 'tlshello']"> [[ p ]] </a-select-option>
</a-select>
</a-col>
</a-row>
</a-list-item>
<setting-list-item style="padding: 10px 20px" type="text" title='Length' v-model="fragmentLength" placeholder="100-200"></setting-list-item> <setting-list-item style="padding: 10px 20px" type="text" title='Length' v-model="fragmentLength" placeholder="100-200"></setting-list-item>
<setting-list-item style="padding: 10px 20px" type="text" title='Interval' v-model="fragmentInterval" placeholder="10-20"></setting-list-item> <setting-list-item style="padding: 10px 20px" type="text" title='Interval' v-model="fragmentInterval" placeholder="10-20"></setting-list-item>
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
</a-list-item> </a-list-item>
<a-list-item style="padding: 20px">
<a-row>
<a-col :lg="24" :xl="12">
<a-list-item-meta title='Noises'>
<template slot="description">{{ i18n "pages.settings.noisesDesc"}}</template>
</a-list-item-meta>
</a-col>
<a-col :lg="24" :xl="12">
<a-switch v-model="noises"></a-switch>
</a-col>
</a-row>
<a-collapse v-if="noises" style="margin-top: 14px;">
<a-collapse-panel v-for="(noise, index) in noisesArray" :key="index" :header="`Noise ${index + 1}`">
<a-list-item style="padding: 10px 20px">
<a-row>
<a-col :lg="24" :xl="12">
<a-list-item-meta title='Type'></a-list-item-meta>
</a-col>
<a-col :lg="24" :xl="12">
<a-select :value="noise.type" style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme"
@change="(value) => updateNoiseType(index, value)">
<a-select-option :value="p" :label="p" v-for="p in ['rand', 'base64', 'str']" :key="p">
[[ p ]] </a-select-option>
</a-select>
</a-col>
</a-row>
</a-list-item>
<setting-list-item style="padding: 10px 20px" type="text" title='Packet' :value="noise.packet"
@input="(value) => updateNoisePacket(index, value)" placeholder="5-10"></setting-list-item>
<setting-list-item style="padding: 10px 20px" type="text" title='Delay (ms)' :value="noise.delay"
@input="(value) => updateNoiseDelay(index, value)" placeholder="10-20"></setting-list-item>
<a-button v-if="noisesArray.length > 1" type="danger" @click="removeNoise(index)">Remove</a-button>
</a-collapse-panel>
</a-collapse>
<a-button v-if="noises" type="primary" @click="addNoise" style="margin-top: 10px">Add Noise</a-button>
</a-list-item>
<a-list-item style="padding: 20px"> <a-list-item style="padding: 20px">
<a-row> <a-row>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
@@ -355,9 +380,14 @@
</a-col> </a-col>
</a-row> </a-row>
<a-collapse v-if="enableDirect" style="margin-top: 14px;"> <a-collapse v-if="enableDirect" style="margin-top: 14px;">
<a-collapse-panel header='{{ i18n "pages.settings.directSett"}}'> <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="directCountries" name="Countries" :options="countryOptions"></a-checkbox-group> <a-checkbox-group v-model="geoIP" :options="geoIPOptions"></a-checkbox-group>
</a-list-item>
</a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.directdomains"}}'>
<a-list-item style="padding: 10px 20px">
<a-checkbox-group v-model="geoSite" :options="geoSiteOptions"></a-checkbox-group>
</a-list-item> </a-list-item>
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
@@ -412,6 +442,16 @@
} }
} }
}, },
defaultNoises: {
tag: "noises",
protocol: "freedom",
settings: {
domainStrategy: "AsIs",
noises: [
{ type: "rand", packet: "10-20", delay: "10-16" },
],
},
},
defaultMux: { defaultMux: {
enabled: true, enabled: true,
concurrency: 8, concurrency: 8,
@@ -423,8 +463,7 @@
type: "field", type: "field",
outboundTag: "direct", outboundTag: "direct",
domain: [ domain: [
"geosite:category-ir", "geosite:category-ir"
"geosite:cn"
], ],
"enabled": true "enabled": true
}, },
@@ -433,17 +472,30 @@
outboundTag: "direct", outboundTag: "direct",
ip: [ ip: [
"geoip:private", "geoip:private",
"geoip:ir", "geoip:ir"
"geoip:cn"
], ],
enabled: true enabled: true
}, },
], ],
countryOptions: [ geoIPOptions: [
{ label: 'Private IP/Domain', value: 'private' }, { label: 'Private IP', value: 'private' },
{ label: '🇮🇷 Iran', value: 'ir' }, { label: '🇮🇷 Iran', value: 'ir' },
{ label: '🇨🇳 China', value: 'cn' }, { label: '🇨🇳 China', value: 'cn' },
{ label: '🇷🇺 Russia', value: 'ru' }, { label: '🇷🇺 Russia', value: 'ru' },
{ label: '🇻🇳 Vietnam', value: 'vn' },
{ label: '🇪🇸 Spain', value: 'es' },
{ label: '🇮🇩 Indonesia', value: 'id' },
{ label: '🇺🇦 Ukraine', value: 'ua' },
{ label: '🇹🇷 Türkiye', value: 'tr' },
{ label: '🇧🇷 Brazil', value: 'br' },
],
geoSiteOptions: [
{ label: '🇮🇷 Iran', value: 'ir' },
{ label: '🇨🇳 China', value: 'cn' },
{ label: '🇷🇺 Russia', value: 'ru' },
{ label: 'Apple', value: 'apple' },
{ label: 'Meta', value: 'meta' },
{ label: 'Google', value: 'google' },
], ],
get remarkModel() { get remarkModel() {
rm = this.allSetting.remarkModel; rm = this.allSetting.remarkModel;
@@ -523,7 +575,9 @@
if (msg.success) { if (msg.success) {
this.loading(true); this.loading(true);
await PromiseUtil.sleep(5000); await PromiseUtil.sleep(5000);
let { webCertFile, webKeyFile, webDomain: host, webPort: port, webBasePath: base } = this.allSetting; var { webCertFile, webKeyFile, webDomain: host, webPort: port, webBasePath: base } = this.allSetting;
if (host == this.oldAllSetting.webDomain) host = null;
if (port == this.oldAllSetting.webPort) port = null;
const isTLS = webCertFile !== "" || webKeyFile !== ""; const isTLS = webCertFile !== "" || webKeyFile !== "";
const url = buildURL({ host, port, isTLS, base, path: "panel/settings" }); const url = buildURL({ host, port, isTLS, base, path: "panel/settings" });
window.location.replace(url); window.location.replace(url);
@@ -571,6 +625,30 @@
this.user.loginSecret = ""; this.user.loginSecret = "";
} }
}, },
addNoise() {
const newNoise = { type: "rand", packet: "10-20", delay: "10-16" };
this.noisesArray = [...this.noisesArray, newNoise];
},
removeNoise(index) {
const newNoises = [...this.noisesArray];
newNoises.splice(index, 1);
this.noisesArray = newNoises;
},
updateNoiseType(index, value) {
const updatedNoises = [...this.noisesArray];
updatedNoises[index] = { ...updatedNoises[index], type: value };
this.noisesArray = updatedNoises;
},
updateNoisePacket(index, value) {
const updatedNoises = [...this.noisesArray];
updatedNoises[index] = { ...updatedNoises[index], packet: value };
this.noisesArray = updatedNoises;
},
updateNoiseDelay(index, value) {
const updatedNoises = [...this.noisesArray];
updatedNoises[index] = { ...updatedNoises[index], delay: value };
this.noisesArray = updatedNoises;
},
}, },
computed: { computed: {
fragment: { fragment: {
@@ -609,6 +687,30 @@
} }
} }
}, },
noises: {
get() {
return this.allSetting?.subJsonNoises != "";
},
set(v) {
if (v) {
this.allSetting.subJsonNoises = JSON.stringify(this.defaultNoises);
} else {
this.allSetting.subJsonNoises = "";
}
}
},
noisesArray: {
get() {
return this.noises ? JSON.parse(this.allSetting.subJsonNoises).settings.noises : [];
},
set(value) {
if (this.noises) {
const newNoises = JSON.parse(this.allSetting.subJsonNoises);
newNoises.settings.noises = value;
this.allSetting.subJsonNoises = JSON.stringify(newNoises);
}
}
},
enableMux: { enableMux: {
get: function () { return this.allSetting?.subJsonMux != ""; }, get: function () { return this.allSetting?.subJsonMux != ""; },
set: function (v) { set: function (v) {
@@ -645,28 +747,49 @@
this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : ""; this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
} }
}, },
directCountries: { geoIP: {
get: function () { get: function () {
if (!this.enableDirect) return []; if (!this.enableDirect) return [];
rules = JSON.parse(this.allSetting.subJsonRules); const rules = JSON.parse(this.allSetting.subJsonRules);
return Array.isArray(rules) ? rules[1].ip.map(d => d.replace("geoip:", "")) : []; return Array.isArray(rules) ? rules[1].ip.map(d => d.replace("geoip:", "")) : [];
}, },
set: function (v) { set: function (v) {
rules = JSON.parse(this.allSetting.subJsonRules); const rules = JSON.parse(this.allSetting.subJsonRules);
if (!Array.isArray(rules)) return; if (!Array.isArray(rules)) return;
rules[0].domain = [];
rules[1].ip = []; rules[1].ip = [];
v.forEach(d => {
rules[1].ip.push("geoip:" + d);
});
this.allSetting.subJsonRules = JSON.stringify(rules);
}
},
geoSite: {
get: function () {
if (!this.enableDirect) return [];
const rules = JSON.parse(this.allSetting.subJsonRules);
return Array.isArray(rules) ?
rules[0].domain.map(d => {
if (d.startsWith("geosite:category-")) {
return d.replace("geosite:category-", "");
}
return d.replace("geosite:", "");
})
: [];
},
set: function (v) {
const rules = JSON.parse(this.allSetting.subJsonRules);
if (!Array.isArray(rules)) return;
rules[0].domain = [];
v.forEach(d => { v.forEach(d => {
let category = ''; let category = '';
if (["cn", "private"].includes(d)) { if (["cn", "apple", "meta", "google"].includes(d)) {
category = ""; category = "";
} else if (d === 'ru') { } else if (["ru", "ir"].includes(d)) {
category = "category-gov-";
} else {
category = "category-"; category = "category-";
} }
rules[0].domain.push("geosite:" + category + d); rules[0].domain.push("geosite:" + category + d);
rules[1].ip.push("geoip:" + d);
}); });
this.allSetting.subJsonRules = JSON.stringify(rules); this.allSetting.subJsonRules = JSON.stringify(rules);
} }

View File

@@ -163,8 +163,8 @@
</a-col> </a-col>
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<template> <template>
<a-select v-model="setLogLevel" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%"> <a-select v-model="logLevel" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
<a-select-option v-for="s in logLevel" :value="s">[[ s ]]</a-select-option> <a-select-option v-for="s in log.loglevel" :value="s">[[ s ]]</a-select-option>
</a-select> </a-select>
</template> </template>
</a-col> </a-col>
@@ -178,7 +178,8 @@
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<template> <template>
<a-select v-model="accessLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%"> <a-select v-model="accessLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
<a-select-option v-for="s in access" :key="s" :value="s">[[ s ]]</a-select-option> <a-select-option value=''>Empty</a-select-option>
<a-select-option v-for="s in log.access" :value="s">[[ s ]]</a-select-option>
</a-select> </a-select>
</template> </template>
</a-col> </a-col>
@@ -192,11 +193,28 @@
<a-col :lg="24" :xl="12"> <a-col :lg="24" :xl="12">
<template> <template>
<a-select v-model="errorLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%"> <a-select v-model="errorLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
<a-select-option v-for="s in error" :key="s" :value="s">[[ s ]]</a-select-option> <a-select-option value=''>Empty</a-select-option>
<a-select-option v-for="s in log.error" :value="s">[[ s ]]</a-select-option>
</a-select> </a-select>
</template> </template>
</a-col> </a-col>
</a-row> </a-row>
<a-row style="padding: 10px 20px">
<a-col :lg="24" :xl="12">
<a-list-item-meta title='{{ i18n "pages.xray.maskAddress" }}'
description='{{ i18n "pages.xray.maskAddressDesc" }}'>
</a-list-item-meta>
</a-col>
<a-col :lg="24" :xl="12">
<template>
<a-select v-model="maskAddressLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
<a-select-option value=''>Empty</a-select-option>
<a-select-option v-for="s in log.maskAddress" :value="s">[[ s ]]</a-select-option>
</a-select>
</template>
</a-col>
</a-row>
<setting-list-item type="switch" title='{{ i18n "pages.xray.dnsLog"}}' desc='{{ i18n "pages.xray.dnsLogDesc"}}' v-model="dnslog"></setting-list-item>
</a-list-item> </a-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.blockConfigs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.blockConfigs"}}'>
@@ -217,44 +235,78 @@
<setting-list-item type="switch" title='{{ i18n "pages.xray.Speedtest"}}' desc='{{ i18n "pages.xray.SpeedtestDesc"}}' v-model="SpeedTestSettings"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.Speedtest"}}' desc='{{ i18n "pages.xray.SpeedtestDesc"}}' v-model="SpeedTestSettings"></setting-list-item>
</a-list-item> </a-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.blockCountryConfigs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.blockConnectionsConfigs"}}'>
<a-row :xs="24" :sm="24" :lg="12"> <a-row :xs="24" :sm="24" :lg="12">
<a-alert type="warning" style="text-align: center;"> <a-alert type="warning" style="text-align: center;">
<template slot="message"> <template slot="message">
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon> <a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
{{ i18n "pages.xray.blockCountryConfigsDesc" }} {{ i18n "pages.xray.blockConnectionsConfigsDesc" }}
</template> </template>
</a-alert> </a-alert>
</a-row> </a-row>
<a-list-item> <a-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.IRIp"}}' desc='{{ i18n "pages.xray.IRIpDesc"}}' v-model="IRIpSettings"></setting-list-item> <a-collapse>
<setting-list-item type="switch" title='{{ i18n "pages.xray.IRDomain"}}' desc='{{ i18n "pages.xray.IRDomainDesc"}}' v-model="IRDomainSettings"></setting-list-item> <a-collapse-panel header='{{ i18n "pages.xray.blockips"}}'>
<setting-list-item type="switch" title='{{ i18n "pages.xray.ChinaIp"}}' desc='{{ i18n "pages.xray.ChinaIpDesc"}}' v-model="ChinaIpSettings"></setting-list-item> <setting-list-item type="switch" title='🇮🇷 Iran'
<setting-list-item type="switch" title='{{ i18n "pages.xray.ChinaDomain"}}' desc='{{ i18n "pages.xray.ChinaDomainDesc"}}' v-model="ChinaDomainSettings"></setting-list-item> v-model="IRIpSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaIp"}}' desc='{{ i18n "pages.xray.RussiaIpDesc"}}' v-model="RussiaIpSettings"></setting-list-item> <setting-list-item type="switch" title='🇨🇳 China'
<setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaDomain"}}' desc='{{ i18n "pages.xray.RussiaDomainDesc"}}' v-model="RussiaDomainSettings"></setting-list-item> v-model="ChinaIpSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.VNIp"}}' desc='{{ i18n "pages.xray.VNIpDesc"}}' v-model="VNIpSettings"></setting-list-item> <setting-list-item type="switch" title='🇷🇺 Russia'
<setting-list-item type="switch" title='{{ i18n "pages.xray.VNDomain"}}' desc='{{ i18n "pages.xray.VNDomainDesc"}}' v-model="VNDomainSettings"></setting-list-item> v-model="RussiaIpSettings"></setting-list-item>
<setting-list-item type="switch" title='🇻🇳 Vietnam'
v-model="VNIpSettings"></setting-list-item>
</a-collapse-panel>
</a-collapse>
<br>
<a-collapse>
<a-collapse-panel header='{{ i18n "pages.xray.blockdomains"}}'>
<setting-list-item type="switch" title='🇮🇷 Iran'
v-model="IRDomainSettings"></setting-list-item>
<setting-list-item type="switch" title='🇨🇳 China'
v-model="ChinaDomainSettings"></setting-list-item>
<setting-list-item type="switch" title='🇷🇺 Russia'
v-model="RussiaDomainSettings"></setting-list-item>
<setting-list-item type="switch" title='🇻🇳 Vietnam'
v-model="VNDomainSettings"></setting-list-item>
</a-collapse-panel>
</a-collapse>
</a-list-item> </a-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.directCountryConfigs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.directConnectionsConfigs"}}'>
<a-row :xs="24" :sm="24" :lg="12"> <a-row :xs="24" :sm="24" :lg="12">
<a-alert type="warning" style="text-align: center;"> <a-alert type="warning" style="text-align: center;">
<template slot="message"> <template slot="message">
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon> <a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
{{ i18n "pages.xray.directCountryConfigsDesc" }} {{ i18n "pages.xray.directConnectionsConfigsDesc" }}
</template> </template>
</a-alert> </a-alert>
</a-row> </a-row>
<a-list-item> <a-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectIRIp"}}' desc='{{ i18n "pages.xray.DirectIRIpDesc"}}' v-model="IRIpDirectSettings"></setting-list-item> <a-collapse>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectIRDomain"}}' desc='{{ i18n "pages.xray.DirectIRDomainDesc"}}' v-model="IRDomainDirectSettings"></setting-list-item> <a-collapse-panel header='{{ i18n "pages.xray.directips"}}'>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectChinaIp"}}' desc='{{ i18n "pages.xray.DirectChinaIpDesc"}}' v-model="ChinaIpDirectSettings"></setting-list-item> <setting-list-item type="switch" title='🇮🇷 Iran'
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectChinaDomain"}}' desc='{{ i18n "pages.xray.DirectChinaDomainDesc"}}' v-model="ChinaDomainDirectSettings"></setting-list-item> v-model="IRIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaIp"}}' desc='{{ i18n "pages.xray.DirectRussiaIpDesc"}}' v-model="RussiaIpDirectSettings"></setting-list-item> <setting-list-item type="switch" title='🇨🇳 China'
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaDomain"}}' desc='{{ i18n "pages.xray.DirectRussiaDomainDesc"}}' v-model="RussiaDomainDirectSettings"></setting-list-item> v-model="ChinaIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNIp"}}' desc='{{ i18n "pages.xray.DirectVNIpDesc"}}' v-model="VNIpDirectSettings"></setting-list-item> <setting-list-item type="switch" title='🇷🇺 Russia'
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNDomain"}}' desc='{{ i18n "pages.xray.DirectVNDomainDesc"}}' v-model="VNDomainDirectSettings"></setting-list-item> v-model="RussiaIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='🇻🇳 Vietnam'
v-model="VNIpDirectSettings"></setting-list-item>
</a-collapse-panel>
</a-collapse>
<br>
<a-collapse>
<a-collapse-panel header='{{ i18n "pages.xray.directdomains"}}'>
<setting-list-item type="switch" title='🇮🇷 Iran'
v-model="IRDomainDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='🇨🇳 China'
v-model="ChinaDomainDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='🇷🇺 Russia'
v-model="RussiaDomainDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='🇻🇳 Vietnam'
v-model="VNDomainDirectSettings"></setting-list-item>
</a-collapse-panel>
</a-collapse>
</a-list-item> </a-list-item>
</a-collapse-panel> </a-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.ipv4Configs"}}'> <a-collapse-panel header='{{ i18n "pages.xray.ipv4Configs"}}'>
@@ -569,86 +621,90 @@
</a-radio-group> </a-radio-group>
<textarea style="position:absolute; left: -800px;" id="obsSetting"></textarea> <textarea style="position:absolute; left: -800px;" id="obsSetting"></textarea>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-dns" tab='DNS' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-dns" tab='DNS' style="padding-top: 20px;" force-render="true">
<setting-list-item type="switch" title='{{ i18n "pages.xray.dns.enable" }}' desc='{{ i18n "pages.xray.dns.enableDesc" }}' v-model="enableDNS"></setting-list-item> <setting-list-item type="switch" title='{{ i18n "pages.xray.dns.enable" }}'
desc='{{ i18n "pages.xray.dns.enableDesc" }}' v-model="enableDNS"></setting-list-item>
<template v-if="enableDNS"> <template v-if="enableDNS">
<setting-list-item style="padding: 10px 20px" type="text" title='{{ i18n "pages.xray.dns.tag" }}' desc='{{ i18n "pages.xray.dns.tagDesc" }}' v-model="dnsTag"></setting-list-item> <setting-list-item style="padding: 10px 20px" type="text" title='{{ i18n "pages.xray.dns.tag" }}'
<a-list-item style="padding: 10px 20px"> desc='{{ i18n "pages.xray.dns.tagDesc" }}' v-model="dnsTag"></setting-list-item>
<a-row> <a-list-item style="padding: 10px 20px">
<a-col :lg="24" :xl="12"> <a-row>
<a-list-item-meta title='{{ i18n "pages.xray.dns.strategy" }}' description='{{ i18n "pages.xray.dns.strategyDesc" }}' /> <a-col :lg="24" :xl="12">
</a-col> <a-list-item-meta title='{{ i18n "pages.xray.dns.strategy" }}'
<a-col :lg="24" :xl="12"> description='{{ i18n "pages.xray.dns.strategyDesc" }}' />
<a-select </a-col>
v-model="dnsStrategy" <a-col :lg="24" :xl="12">
style="width: 100%" <a-select v-model="dnsStrategy" style="width: 100%"
:dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']"> <a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']">
[[ l ]] [[ l ]]
</a-select-option> </a-select-option>
</a-select> </a-select>
</a-col> </a-col>
</a-row> </a-row>
</a-list-item> </a-list-item>
<a-divider>DNS</a-divider> <a-divider>DNS</a-divider>
<a-button type="primary" icon="plus" @click="addDNSServer()" style="margin-bottom: 10px;">{{ i18n "pages.xray.dns.add" }}</a-button> <a-button type="primary" icon="plus" @click="addDNSServer()" style="margin-bottom: 10px;">{{ i18n
<a-table :columns="dnsColumns" bordered v-if="dnsServers.length>0" "pages.xray.dns.add" }}</a-button>
:row-key="r => r.key" <a-table :columns="dnsColumns" bordered v-if="dnsServers.length>0" :row-key="r => r.key"
:data-source="dnsServers" :data-source="dnsServers" :scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0"
:scroll="isMobile ? {} : { x: 200 }" :style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
:pagination="false" <template slot="action" slot-scope="text,dns,index">
:indent-size="0" [[ index+1 ]]
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'"> <a-dropdown :trigger="['click']">
<template slot="action" slot-scope="text,dns,index"> <a-icon @click="e => e.preventDefault()" type="more"
[[ index+1 ]] style="font-size: 16px; text-decoration: bold;"></a-icon>
<a-dropdown :trigger="['click']"> <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon> <a-menu-item @click="editDNSServer(index)">
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme"> <a-icon type="edit"></a-icon>
<a-menu-item @click="editDNSServer(index)"> {{ i18n "edit" }}
<a-icon type="edit"></a-icon> </a-menu-item>
{{ i18n "edit" }} <a-menu-item @click="deleteDNSServer(index)">
</a-menu-item> <span style="color: #FF4D4F">
<a-menu-item @click="deleteDNSServer(index)"> <a-icon type="delete"></a-icon> {{ i18n "delete"}}
<span style="color: #FF4D4F"> </span>
<a-icon type="delete"></a-icon> {{ i18n "delete"}} </a-menu-item>
</span> </a-menu>
</a-menu-item> </a-dropdown>
</a-menu> </template>
</a-dropdown> <template slot="address" slot-scope="dns,index">
</template> <span v-if="typeof dns == 'object'">[[ dns.address ]]</span>
<template slot="address" slot-scope="dns,index"> <span v-else>[[ dns ]]</span>
<span v-if="typeof dns == 'object'">[[ dns.address ]]</span> </template>
<span v-else>[[ dns ]]</span> <template slot="domain" slot-scope="dns,index">
</template> <span v-if="typeof dns == 'object'">[[ dns.domains.join(",") ]]</span>
<template slot="domain" slot-scope="dns,index"> </template>
<span v-if="typeof dns == 'object'">[[ dns.domains.join(",") ]]</span> <template slot="expectIPs" slot-scope="dns,index">
</template> <span v-if="typeof dns == 'object'">[[ dns.expectIPs.join(",") ]]</span>
</a-table> </template>
<a-divider>Fake DNS</a-divider> </a-table>
<a-button type="primary" icon="plus" @click="addFakedns()" style="margin-bottom: 10px;">{{ i18n "pages.xray.fakedns.add" }}</a-button> <a-divider>Fake DNS</a-divider>
<a-table :columns="fakednsColumns" bordered v-if="fakeDns && fakeDns.length>0" :row-key="r => r.key" <a-button type="primary" icon="plus" @click="addFakedns()" style="margin-bottom: 10px;">{{ i18n
:data-source="fakeDns" :scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0" "pages.xray.fakedns.add" }}</a-button>
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'"> <a-table :columns="fakednsColumns" bordered v-if="fakeDns && fakeDns.length>0" :row-key="r => r.key"
<template slot="action" slot-scope="text,fakedns,index"> :data-source="fakeDns" :scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0"
[[ index+1 ]] :style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
<a-dropdown :trigger="['click']"> <template slot="action" slot-scope="text,fakedns,index">
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon> [[ index+1 ]]
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme"> <a-dropdown :trigger="['click']">
<a-menu-item @click="editFakedns(index)"> <a-icon @click="e => e.preventDefault()" type="more"
<a-icon type="edit"></a-icon> style="font-size: 16px; text-decoration: bold;"></a-icon>
{{ i18n "edit" }} <a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
</a-menu-item> <a-menu-item @click="editFakedns(index)">
<a-menu-item @click="deleteFakedns(index)"> <a-icon type="edit"></a-icon>
<span style="color: #FF4D4F"> {{ i18n "edit" }}
<a-icon type="delete"></a-icon> {{ i18n "delete"}} </a-menu-item>
</span> <a-menu-item @click="deleteFakedns(index)">
</a-menu-item> <span style="color: #FF4D4F">
</a-menu> <a-icon type="delete"></a-icon> {{ i18n "delete"}}
</a-dropdown> </span>
</template> </a-menu-item>
</a-table> </a-menu>
</a-dropdown>
</template>
</a-table>
</template> </template>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;" force-render="true"> <a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;" force-render="true">
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}' description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta> <a-list-item-meta title='{{ i18n "pages.xray.Template"}}' description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;" :size="isMobile ? 'small' : ''"> <a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;" :size="isMobile ? 'small' : ''">
@@ -730,6 +786,7 @@
{ title: "#", align: 'center', width: 20, scopedSlots: { customRender: 'action' } }, { title: "#", align: 'center', width: 20, scopedSlots: { customRender: 'action' } },
{ title: '{{ i18n "pages.xray.outbound.address"}}', align: 'center', width: 50, scopedSlots: { customRender: 'address' } }, { title: '{{ i18n "pages.xray.outbound.address"}}', align: 'center', width: 50, scopedSlots: { customRender: 'address' } },
{ title: '{{ i18n "pages.xray.dns.domains"}}', align: 'center', width: 50, scopedSlots: { customRender: 'domain' } }, { title: '{{ i18n "pages.xray.dns.domains"}}', align: 'center', width: 50, scopedSlots: { customRender: 'domain' } },
{ title: '{{ i18n "pages.xray.dns.expectIPs"}}', align: 'center', width: 50, scopedSlots: { customRender: 'expectIPs' } },
]; ];
const fakednsColumns = [ const fakednsColumns = [
@@ -791,9 +848,13 @@
protocol: "freedom" protocol: "freedom"
}, },
routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"], routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"],
logLevel: ["none" , "debug" , "info" , "warning", "error"], log: {
access: [], loglevel: ["none", "debug", "info", "warning", "error"],
error: [], access: ["none", "./access.log"],
error: ["none", "./error.log"],
dnsLog: false,
maskAddress: ["quarter", "half", "full"],
},
settingsData: { settingsData: {
protocols: { protocols: {
bittorrent: ["bittorrent"], bittorrent: ["bittorrent"],
@@ -824,17 +885,19 @@
apple: ["geosite:apple"], apple: ["geosite:apple"],
reddit: ["geosite:reddit"], reddit: ["geosite:reddit"],
cn: [ cn: [
"geosite:cn", "regexp:.*\\.cn$",
"regexp:.*\\.cn$" "geosite:cn"
], ],
ru: [ ru: [
"geosite:category-gov-ru", "regexp:.*\\.ru$",
"regexp:.*\\.ru$" "geosite:category-ru" //https://github.com/v2fly/domain-list-community/blob/master/data/category-ru
], ],
ir: [ ir: [
"regexp:.*\\.ir$", "regexp:.*\\.ir$",
"regexp:.*\\.xn--mgba3a4f16a$", // .ایران "ext:geosite_IR.dat:ir",
"ext:geosite_IR.dat:ir" "geosite:category-ir", // https://github.com/v2fly/domain-list-community/blob/master/data/category-ir
"regexp:.*\\.xn--mgba3a4f16a$" // .ایران
], ],
vn: [ vn: [
"regexp:.*\\.vn$", "regexp:.*\\.vn$",
@@ -1519,27 +1582,11 @@
templateSettings: { templateSettings: {
get: function () { get: function () {
const parsedSettings = this.xraySetting ? JSON.parse(this.xraySetting) : null; const parsedSettings = this.xraySetting ? JSON.parse(this.xraySetting) : null;
let accessLogPath = "./access.log";
let errorLogPath = "./error.log";
if (parsedSettings && parsedSettings.log) {
if (parsedSettings.log.access && parsedSettings.log.access !== "none") {
accessLogPath = parsedSettings.log.access;
}
if (parsedSettings.log.error && parsedSettings.log.error !== "none") {
errorLogPath = parsedSettings.log.error;
}
}
this.access = ["none", accessLogPath];
this.error = ["none", errorLogPath];
return parsedSettings; return parsedSettings;
}, },
set: function (newValue) { set: function (newValue) {
if (newValue && newValue.log) { if (newValue) {
this.xraySetting = JSON.stringify(newValue, null, 2); this.xraySetting = JSON.stringify(newValue, null, 2);
this.access = ["none", newValue.log.access || "./access.log"];
this.error = ["none", newValue.log.error || "./error.log"];
} }
}, },
}, },
@@ -1688,7 +1735,7 @@
this.templateSettings = newTemplateSettings; this.templateSettings = newTemplateSettings;
} }
}, },
setLogLevel: { logLevel: {
get: function () { get: function () {
if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.loglevel) return "warning"; if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.loglevel) return "warning";
return this.templateSettings.log.loglevel; return this.templateSettings.log.loglevel;
@@ -1721,6 +1768,28 @@
this.templateSettings = newTemplateSettings; this.templateSettings = newTemplateSettings;
} }
}, },
dnslog: {
get: function () {
if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.dnsLog) return false;
return this.templateSettings.log.dnsLog;
},
set: function (newValue) {
newTemplateSettings = this.templateSettings;
newTemplateSettings.log.dnsLog = newValue;
this.templateSettings = newTemplateSettings;
}
},
maskAddressLog: {
get: function () {
if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.maskAddress) return "";
return this.templateSettings.log.maskAddress;
},
set: function (newValue) {
newTemplateSettings = this.templateSettings;
newTemplateSettings.log.maskAddress = newValue;
this.templateSettings = newTemplateSettings;
}
},
blockedIPs: { blockedIPs: {
get: function () { get: function () {
return this.templateRuleGetter({ outboundTag: "blocked", property: "ip" }); return this.templateRuleGetter({ outboundTag: "blocked", property: "ip" });

View File

@@ -36,20 +36,15 @@ func (j *CheckClientIpJob) Run() {
} }
shouldClearAccessLog := false shouldClearAccessLog := false
f2bInstalled := j.checkFail2BanInstalled() iplimitActive := j.hasLimitIp()
isAccessLogAvailable := j.checkAccessLogAvailable(f2bInstalled) f2bInstalled := j.checkFail2BanInstalled(iplimitActive)
isAccessLogAvailable := j.checkAccessLogAvailable(iplimitActive)
if j.hasLimitIp() { if iplimitActive && f2bInstalled && isAccessLogAvailable {
if f2bInstalled && isAccessLogAvailable { shouldClearAccessLog = j.processLogFile()
shouldClearAccessLog = j.processLogFile()
} else {
if !f2bInstalled {
logger.Warning("[iplimit] fail2ban is not installed. IP limiting may not work properly.")
}
}
} }
if shouldClearAccessLog || isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600 { if shouldClearAccessLog || (isAccessLogAvailable && time.Now().Unix()-j.lastClear > 3600) {
j.clearAccessLog() j.clearAccessLog()
} }
} }
@@ -122,13 +117,13 @@ func (j *CheckClientIpJob) processLogFile() bool {
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
ipRegx, _ := regexp.Compile(`(\d+\.\d+\.\d+\.\d+).* accepted`) ipRegx, _ := regexp.Compile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
emailRegx, _ := regexp.Compile(`email:.+`) emailRegx, _ := regexp.Compile(`email: (\S+)$`)
matches := ipRegx.FindStringSubmatch(line) matches := ipRegx.FindStringSubmatch(line)
if len(matches) > 1 { if len(matches) > 1 {
ip := matches[1] ip := matches[1]
if ip == "127.0.0.1" { if ip == "127.0.0.1" || ip == "::1" {
continue continue
} }
@@ -136,7 +131,7 @@ func (j *CheckClientIpJob) processLogFile() bool {
if matchesEmail == "" { if matchesEmail == "" {
continue continue
} }
matchesEmail = strings.TrimSpace(strings.Split(matchesEmail, "email: ")[1]) matchesEmail = strings.Split(matchesEmail, "email: ")[1]
if InboundClientIps[matchesEmail] != nil { if InboundClientIps[matchesEmail] != nil {
if j.contains(InboundClientIps[matchesEmail], ip) { if j.contains(InboundClientIps[matchesEmail], ip) {
@@ -167,35 +162,33 @@ func (j *CheckClientIpJob) processLogFile() bool {
return shouldCleanLog return shouldCleanLog
} }
func (j *CheckClientIpJob) checkFail2BanInstalled() bool { func (j *CheckClientIpJob) checkFail2BanInstalled(iplimitActive bool) 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(handleWarning bool) bool { func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool {
isAvailable := true
warningMsg := ""
accessLogPath, err := xray.GetAccessLogPath() accessLogPath, err := xray.GetAccessLogPath()
if err != nil { if err != nil {
return false return false
} }
// access log is not available if it is set to 'none' or an empty string if accessLogPath == "none" || accessLogPath == "" {
switch accessLogPath { if iplimitActive {
case "none": logger.Warning("[LimitIP] Access log path is not set, Please configure the access log path in Xray configs.")
warningMsg = "Access log is set to 'none', check your Xray Configs" }
isAvailable = false return false
case "":
warningMsg = "Access log doesn't exist in your Xray Configs"
isAvailable = false
} }
if handleWarning && warningMsg != "" { return true
logger.Warning(warningMsg)
}
return isAvailable
} }
func (j *CheckClientIpJob) checkError(e error) { func (j *CheckClientIpJob) checkError(e error) {

View File

@@ -10,15 +10,9 @@ import (
func DomainValidatorMiddleware(domain string) gin.HandlerFunc { func DomainValidatorMiddleware(domain string) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
host := c.GetHeader("X-Forwarded-Host") host := c.Request.Host
if host == "" { if colonIndex := strings.LastIndex(host, ":"); colonIndex != -1 {
host = c.GetHeader("X-Real-IP") host, _, _ = net.SplitHostPort(c.Request.Host)
}
if host == "" {
host = c.Request.Host
if colonIndex := strings.LastIndex(host, ":"); colonIndex != -1 {
host, _, _ = net.SplitHostPort(host)
}
} }
if host != domain { if host != domain {

View File

@@ -2,8 +2,9 @@
"log": { "log": {
"access": "none", "access": "none",
"dnsLog": false, "dnsLog": false,
"error": "./error.log", "error": "",
"loglevel": "warning" "loglevel": "warning",
"maskAddress": ""
}, },
"api": { "api": {
"tag": "api", "tag": "api",

View File

@@ -2,7 +2,9 @@ package service
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -329,6 +331,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
oldInbound.Settings = inbound.Settings oldInbound.Settings = inbound.Settings
oldInbound.StreamSettings = inbound.StreamSettings oldInbound.StreamSettings = inbound.StreamSettings
oldInbound.Sniffing = inbound.Sniffing oldInbound.Sniffing = inbound.Sniffing
oldInbound.Allocate = inbound.Allocate
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" { if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
} else { } else {
@@ -411,6 +414,12 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
return false, err return false, err
} }
email := clients[0].Email
valid, err := validateEmail(email)
if !valid {
return false, err
}
var settings map[string]interface{} var settings map[string]interface{}
err = json.Unmarshal([]byte(data.Settings), &settings) err = json.Unmarshal([]byte(data.Settings), &settings)
if err != nil { if err != nil {
@@ -490,6 +499,7 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{ err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
"email": client.Email, "email": client.Email,
"id": client.ID, "id": client.ID,
"security": client.Security,
"flow": client.Flow, "flow": client.Flow,
"password": client.Password, "password": client.Password,
"cipher": cipher, "cipher": cipher,
@@ -533,11 +543,13 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
interfaceClients := settings["clients"].([]interface{}) interfaceClients := settings["clients"].([]interface{})
var newClients []interface{} var newClients []interface{}
needApiDel := false
for _, client := range interfaceClients { for _, client := range interfaceClients {
c := client.(map[string]interface{}) c := client.(map[string]interface{})
c_id := c[client_key].(string) c_id := c[client_key].(string)
if c_id == clientId { if c_id == clientId {
email = c["email"].(string) email, _ = c["email"].(string)
needApiDel, _ = c["enable"].(bool)
} else { } else {
newClients = append(newClients, client) newClients = append(newClients, client)
} }
@@ -556,11 +568,6 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
oldInbound.Settings = string(newSettings) oldInbound.Settings = string(newSettings)
db := database.GetDB() db := database.GetDB()
err = s.DelClientStat(db, email)
if err != nil {
logger.Error("Delete stats Data Error")
return false, err
}
err = s.DelClientIPs(db, email) err = s.DelClientIPs(db, email)
if err != nil { if err != nil {
@@ -568,17 +575,31 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
return false, err return false, err
} }
needRestart := false needRestart := false
if len(email) > 0 { if len(email) > 0 {
s.xrayApi.Init(p.GetAPIPort()) notDepleted := true
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, email) err = db.Model(xray.ClientTraffic{}).Select("enable").Where("email = ?", email).First(&notDepleted).Error
if err1 == nil { if err != nil {
logger.Debug("Client deleted by api:", email) logger.Error("Get stats error")
needRestart = false return false, err
} else { }
logger.Debug("Unable to del client by api:", err1) err = s.DelClientStat(db, email)
needRestart = true if err != nil {
logger.Error("Delete stats Data Error")
return false, err
}
if needApiDel && notDepleted {
s.xrayApi.Init(p.GetAPIPort())
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, email)
if err1 == nil {
logger.Debug("Client deleted by api:", email)
needRestart = false
} else {
logger.Debug("Unable to del client by api:", err1)
needRestart = true
}
s.xrayApi.Close()
} }
s.xrayApi.Close()
} }
return needRestart, db.Save(oldInbound).Error return needRestart, db.Save(oldInbound).Error
} }
@@ -589,6 +610,12 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
return false, err return false, err
} }
email := clients[0].Email
valid, err := validateEmail(email)
if !valid {
return false, err
}
var settings map[string]interface{} var settings map[string]interface{}
err = json.Unmarshal([]byte(data.Settings), &settings) err = json.Unmarshal([]byte(data.Settings), &settings)
if err != nil { if err != nil {
@@ -696,12 +723,14 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
needRestart := false needRestart := false
if len(oldEmail) > 0 { if len(oldEmail) > 0 {
s.xrayApi.Init(p.GetAPIPort()) s.xrayApi.Init(p.GetAPIPort())
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail) if oldClients[clientIndex].Enable {
if err1 == nil { err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
logger.Debug("Old client deleted by api:", clients[0].Email) if err1 == nil {
} else { logger.Debug("Old client deleted by api:", clients[0].Email)
logger.Debug("Error in deleting client by api:", err1) } else {
needRestart = true logger.Debug("Error in deleting client by api:", err1)
needRestart = true
}
} }
if clients[0].Enable { if clients[0].Enable {
cipher := "" cipher := ""
@@ -711,6 +740,7 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{ err1 := s.xrayApi.AddUser(string(oldInbound.Protocol), oldInbound.Tag, map[string]interface{}{
"email": clients[0].Email, "email": clients[0].Email,
"id": clients[0].ID, "id": clients[0].ID,
"security": clients[0].Security,
"flow": clients[0].Flow, "flow": clients[0].Flow,
"password": clients[0].Password, "password": clients[0].Password,
"cipher": cipher, "cipher": cipher,
@@ -1559,6 +1589,7 @@ func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (bool, e
err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{ err1 := s.xrayApi.AddUser(string(inbound.Protocol), inbound.Tag, map[string]interface{}{
"email": client.Email, "email": client.Email,
"id": client.ID, "id": client.ID,
"security": client.Security,
"flow": client.Flow, "flow": client.Flow,
"password": client.Password, "password": client.Password,
"cipher": cipher, "cipher": cipher,
@@ -1991,3 +2022,20 @@ func (s *InboundService) MigrateDB() {
func (s *InboundService) GetOnlineClients() []string { func (s *InboundService) GetOnlineClients() []string {
return p.GetOnlineClients() return p.GetOnlineClients()
} }
func validateEmail(email string) (bool, error) {
if strings.Contains(email, " ") {
return false, errors.New("email contains spaces, please remove them")
}
if email != strings.ToLower(email) {
return false, errors.New("email contains uppercase letters, please convert to lowercase")
}
emailPattern := `^[a-z0-9@._-]+$`
if !regexp.MustCompile(emailPattern).MatchString(email) {
return false, errors.New("email contains invalid characters, please use only lowercase letters, digits, and @._-")
}
return true, nil
}

View File

@@ -32,7 +32,7 @@ var defaultValueMap = map[string]string{
"webKeyFile": "", "webKeyFile": "",
"secret": random.Seq(32), "secret": random.Seq(32),
"webBasePath": "/", "webBasePath": "/",
"sessionMaxAge": "0", "sessionMaxAge": "60",
"pageSize": "50", "pageSize": "50",
"expireDiff": "0", "expireDiff": "0",
"trafficDiff": "0", "trafficDiff": "0",
@@ -62,6 +62,7 @@ var defaultValueMap = map[string]string{
"subJsonPath": "/json/", "subJsonPath": "/json/",
"subJsonURI": "", "subJsonURI": "",
"subJsonFragment": "", "subJsonFragment": "",
"subJsonNoises": "",
"subJsonMux": "", "subJsonMux": "",
"subJsonRules": "", "subJsonRules": "",
"datepicker": "gregorian", "datepicker": "gregorian",
@@ -458,6 +459,10 @@ func (s *SettingService) GetSubJsonFragment() (string, error) {
return s.getString("subJsonFragment") return s.getString("subJsonFragment")
} }
func (s *SettingService) GetSubJsonNoises() (string, error) {
return s.getString("subJsonNoises")
}
func (s *SettingService) GetSubJsonMux() (string, error) { func (s *SettingService) GetSubJsonMux() (string, error) {
return s.getString("subJsonMux") return s.getString("subJsonMux")
} }

View File

@@ -2,6 +2,7 @@ package service
import ( import (
"embed" "embed"
"errors"
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
@@ -769,8 +770,40 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
} else { } else {
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation")) t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
} }
case "get_clients":
inboundId := dataArray[1]
inboundIdInt, err := strconv.Atoi(inboundId)
if err != nil {
t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
return
}
inbound, err := t.inboundService.GetInbound(inboundIdInt)
if err != nil {
t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
return
}
clients, err := t.getInboundClients(inboundIdInt)
if err != nil {
t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
return
}
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseClient", "Inbound=="+inbound.Remark), clients)
} }
return return
} else {
switch callbackQuery.Data {
case "get_inbounds":
inbounds, err := t.getInbounds()
if err != nil {
t.sendCallbackAnswerTgBot(callbackQuery.ID, err.Error())
return
}
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.allClients"))
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.chooseInbound"), inbounds)
}
} }
} }
@@ -837,6 +870,7 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
tu.InlineKeyboardRow( tu.InlineKeyboardRow(
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.commands")).WithCallbackData(t.encodeQuery("commands")), tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.commands")).WithCallbackData(t.encodeQuery("commands")),
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")),
), ),
) )
numericKeyboardClient := tu.InlineKeyboard( numericKeyboardClient := tu.InlineKeyboard(
@@ -1082,6 +1116,72 @@ func (t *Tgbot) getInboundUsages() string {
return info return info
} }
func (t *Tgbot) getInbounds() (*telego.InlineKeyboardMarkup, error) {
inbounds, err := t.inboundService.GetAllInbounds()
var buttons []telego.InlineKeyboardButton
if err != nil {
logger.Warning("GetAllInbounds run failed:", err)
return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
} else {
if len(inbounds) > 0 {
for _, inbound := range inbounds {
status := "❌"
if inbound.Enable {
status = "✅"
}
buttons = append(buttons, tu.InlineKeyboardButton(fmt.Sprintf("%v - %v", inbound.Remark, status)).WithCallbackData(t.encodeQuery("get_clients "+strconv.Itoa(inbound.Id))))
}
} else {
logger.Warning("GetAllInbounds run failed:", err)
return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
}
}
cols := 0
if len(buttons) < 6 {
cols = 3
} else {
cols = 2
}
keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
return keyboard, nil
}
func (t *Tgbot) getInboundClients(id int) (*telego.InlineKeyboardMarkup, error) {
inbound, err := t.inboundService.GetInbound(id)
if err != nil {
logger.Warning("getIboundClients run failed:", err)
return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
}
clients, err := t.inboundService.GetClients(inbound)
var buttons []telego.InlineKeyboardButton
if err != nil {
logger.Warning("GetInboundClients run failed:", err)
return nil, errors.New(t.I18nBot("tgbot.answers.getInboundsFailed"))
} else {
if len(clients) > 0 {
for _, client := range clients {
buttons = append(buttons, tu.InlineKeyboardButton(client.Email).WithCallbackData(t.encodeQuery("client_get_usage "+client.Email)))
}
} else {
return nil, errors.New(t.I18nBot("tgbot.answers.getClientsFailed"))
}
}
cols := 0
if len(buttons) < 6 {
cols = 3
} else {
cols = 2
}
keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
return keyboard, nil
}
func (t *Tgbot) clientInfoMsg( func (t *Tgbot) clientInfoMsg(
traffic *xray.ClientTraffic, traffic *xray.ClientTraffic,
printEnabled bool, printEnabled bool,

View File

@@ -43,8 +43,8 @@
"domainName" = "Domain Name" "domainName" = "Domain Name"
"monitor" = "Listen IP" "monitor" = "Listen IP"
"certificate" = "Digital Certificate" "certificate" = "Digital Certificate"
"fail" = " Failed" "fail" = "Failed"
"success" = " Successfully" "success" = "Successfully"
"getVersion" = "Get Version" "getVersion" = "Get Version"
"install" = "Install" "install" = "Install"
"clients" = "Clients" "clients" = "Clients"
@@ -143,7 +143,7 @@
"destinationPort" = "Destination Port" "destinationPort" = "Destination Port"
"targetAddress" = "Target Address" "targetAddress" = "Target Address"
"monitorDesc" = "Leave blank to listen on all IPs" "monitorDesc" = "Leave blank to listen on all IPs"
"meansNoLimit" = " = Unlimited. (unit: GB)" "meansNoLimit" = "= Unlimited. (unit: GB)"
"totalFlow" = "Total Flow" "totalFlow" = "Total Flow"
"leaveBlankToNeverExpire" = "Leave blank to never expire" "leaveBlankToNeverExpire" = "Leave blank to never expire"
"noRecommendKeepDefault" = "It is recommended to keep the default" "noRecommendKeepDefault" = "It is recommended to keep the default"
@@ -225,9 +225,6 @@
"requestHeader" = "Request Header" "requestHeader" = "Request Header"
"responseHeader" = "Response Header" "responseHeader" = "Response Header"
[pages.inbounds.stream.quic]
"encryption" = "Encryption"
[pages.settings] [pages.settings]
"title" = "Panel Settings" "title" = "Panel Settings"
"save" = "Save" "save" = "Save"
@@ -312,12 +309,14 @@
"fragment" = "Fragmentation" "fragment" = "Fragmentation"
"fragmentDesc" = "Enable fragmentation for TLS hello packet." "fragmentDesc" = "Enable fragmentation for TLS hello packet."
"fragmentSett" = "Fragmentation Settings" "fragmentSett" = "Fragmentation Settings"
"noisesDesc" = "Enable Noises."
"noisesSett" = "Noises Settings"
"mux" = "Mux" "mux" = "Mux"
"muxDesc" = "Transmit multiple independent data streams within an established data stream." "muxDesc" = "Transmit multiple independent data streams within an established data stream."
"muxSett" = "Mux Settings" "muxSett" = "Mux Settings"
"direct" = "Direct Connection" "direct" = "Direct Connection"
"directDesc" = "Directly establishes connections with domains or IP ranges of a specific country." "directDesc" = "Directly establishes connections with domains or IP ranges of a specific country."
"directSett" = "Direct Connection Options"
[pages.xray] [pages.xray]
"title" = "Xray Configs" "title" = "Xray Configs"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Logs may affect your server's efficiency. It is recommended to enable it wisely only in case of your needs" "logConfigsDesc" = "Logs may affect your server's efficiency. It is recommended to enable it wisely only in case of your needs"
"blockConfigs" = "Protection Shield" "blockConfigs" = "Protection Shield"
"blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites." "blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites."
"blockCountryConfigs" = "Block Country" "blockConnectionsConfigs" = "Block Connections"
"blockCountryConfigsDesc" = "These options will block traffic based on the specific requested country." "blockConnectionsConfigsDesc" = "These options will block traffic based on the specific requested country."
"directCountryConfigs" = "Direct Country" "directConnectionsConfigs" = "Direct Connections"
"directCountryConfigsDesc" = "A direct connection ensures that specific traffic is not routed through another server." "directConnectionsConfigsDesc" = "A direct connection ensures that specific traffic is not routed through another server."
"blockips" = "Block IPs"
"blockdomains" = "Block Domains"
"directips" = "Direct IPs"
"directdomains" = "Direct Domains"
"ipv4Configs" = "IPv4 Routing" "ipv4Configs" = "IPv4 Routing"
"ipv4ConfigsDesc" = "These options will route traffic based on a specific destination via IPv4." "ipv4ConfigsDesc" = "These options will route traffic based on a specific destination via IPv4."
"warpConfigs" = "WARP Routing" "warpConfigs" = "WARP Routing"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Blocks malware, phishing, and cryptominers websites." "SecurityDesc" = "Blocks malware, phishing, and cryptominers websites."
"Speedtest" = "Block Speedtest" "Speedtest" = "Block Speedtest"
"SpeedtestDesc" = "Blocks establishing connectins to speedtest websites." "SpeedtestDesc" = "Blocks establishing connectins to speedtest websites."
"IRIp" = "Block Connection to Iran IPs"
"IRIpDesc" = "Blocks establishing connections to Iran IP ranges."
"IRDomain" = "Block Connection to Iran Domains"
"IRDomainDesc" = "Blocks establishing connections to Iran domains."
"ChinaIp" = "Block Connection to China IPs"
"ChinaIpDesc" = "Blocks establishing connections to China IP ranges."
"ChinaDomain" = "Block Connection to China Domains"
"ChinaDomainDesc" = "Blocks establishing connections to China domains."
"RussiaIp" = "Block Connection to Russia IPs"
"RussiaIpDesc" = "Blocks establishing connections to Russia IP ranges."
"RussiaDomain" = "Block Connection to Russia Domains"
"RussiaDomainDesc" = "Blocks establishing connections to Russia domains."
"VNIp" = "Block Connection to Vietnam IPs"
"VNIpDesc" = "Blocks establishing connections to Vietnam IP ranges."
"VNDomain" = "Block Connection to Vietnam Domains"
"VNDomainDesc" = "Blocks establishing connections to Vietnam domains."
"DirectIRIp" = "Direct Connection to Iran IPs"
"DirectIRIpDesc" = "Directly establishes connections to Iran IP ranges."
"DirectIRDomain" = "Direct Connection to Iran Domains"
"DirectIRDomainDesc" = "Directly establishes connections to Iran domains."
"DirectChinaIp" = "Direct Connection to China IPs"
"DirectChinaIpDesc" = "Directly establishes connections to China IP ranges."
"DirectChinaDomain" = "Direct Connection to China Domains"
"DirectChinaDomainDesc" = "Directly establishes connections to China domains."
"DirectRussiaIp" = "Direct Connection to Russia IPs"
"DirectRussiaIpDesc" = "Directly establishes connections to Russia IP ranges."
"DirectRussiaDomain" = "Direct Connection to Russia Domains"
"DirectRussiaDomainDesc" = "Directly establishes connections to Russia domains."
"DirectVNIp" = "Direct Connection to Vietnam IPs"
"DirectVNIpDesc" = "Directly establishes connections to Vietnam IP ranges."
"DirectVNDomain" = "Direct Connection to Vietnam Domains"
"DirectVNDomainDesc" = "Directly establishes connections to Vietnam domains."
"GoogleIPv4" = "Google" "GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "Routes traffic to Google via IPv4." "GoogleIPv4Desc" = "Routes traffic to Google via IPv4."
"NetflixIPv4" = "Netflix" "NetflixIPv4" = "Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "The file path for the access log. The special value 'none' disabled access logs" "accessLogDesc" = "The file path for the access log. The special value 'none' disabled access logs"
"errorLog" = "Error Log" "errorLog" = "Error Log"
"errorLogDesc" = "The file path for the error log. The special value 'none' disabled error logs" "errorLogDesc" = "The file path for the error log. The special value 'none' disabled error logs"
"dnsLog" = "DNS Log"
"dnsLogDesc" = "Whether to enable DNS query logs"
"maskAddress" = "Mask Address"
"maskAddressDesc" = "IP address mask, when enabled, will automatically replace the IP address that appears in the log."
[pages.xray.rules] [pages.xray.rules]
"first" = "First" "first" = "First"
@@ -485,6 +460,7 @@
"add" = "Add Server" "add" = "Add Server"
"edit" = "Edit Server" "edit" = "Edit Server"
"domains" = "Domains" "domains" = "Domains"
"expectIPs" = "Expect IPs"
[pages.xray.fakedns] [pages.xray.fakedns]
"add" = "Add Fake DNS" "add" = "Add Fake DNS"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Confirm adding: {{ .Num }}" "confirmNumberAdd" = "✅ Confirm adding: {{ .Num }}"
"limitTraffic" = "🚧 Traffic Limit" "limitTraffic" = "🚧 Traffic Limit"
"getBanLogs" = "Get Ban Logs" "getBanLogs" = "Get Ban Logs"
"allClients" = "All Clients"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ Operation successful!" "successfulOperation" = "✅ Operation successful!"
"errorOperation" = "❗ Error in operation." "errorOperation" = "❗ Error in operation."
"getInboundsFailed" = "❌ Failed to get inbounds." "getInboundsFailed" = "❌ Failed to get inbounds."
"getClientsFailed" = "❌ Failed to get clients."
"canceled" = "❌ {{ .Email }}: Operation canceled." "canceled" = "❌ {{ .Email }}: Operation canceled."
"clientRefreshSuccess" = "✅ {{ .Email }}: Client refreshed successfully." "clientRefreshSuccess" = "✅ {{ .Email }}: Client refreshed successfully."
"IpRefreshSuccess" = "✅ {{ .Email }}: IPs refreshed successfully." "IpRefreshSuccess" = "✅ {{ .Email }}: IPs refreshed successfully."
@@ -637,4 +615,6 @@
"removedTGUserSuccess" = "✅ {{ .Email }}: Telegram User removed successfully." "removedTGUserSuccess" = "✅ {{ .Email }}: Telegram User removed successfully."
"enableSuccess" = "✅ {{ .Email }}: Enabled successfully." "enableSuccess" = "✅ {{ .Email }}: Enabled successfully."
"disableSuccess" = "✅ {{ .Email }}: Disabled successfully." "disableSuccess" = "✅ {{ .Email }}: Disabled successfully."
"askToAddUserId" = "Your configuration is not found!\r\nPlease ask your admin to use your Telegram ID in your configuration(s).\r\n\r\nYour User ID: <code>{{ .TgUserID }}</code>" "askToAddUserId" = "Your configuration is not found!\r\nPlease ask your admin to use your Telegram ChatID in your configuration(s).\r\n\r\nYour ChatID: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Choose a Client for Inbound {{ .Inbound }}"
"chooseInbound" = "Choose an Inbound"

View File

@@ -143,7 +143,7 @@
"destinationPort" = "Puerto de Destino" "destinationPort" = "Puerto de Destino"
"targetAddress" = "Dirección de Destino" "targetAddress" = "Dirección de Destino"
"monitorDesc" = "Dejar en blanco por defecto" "monitorDesc" = "Dejar en blanco por defecto"
"meansNoLimit" = " = illimitata. (unidad: GB)" "meansNoLimit" = "= illimitata. (unidad: GB)"
"totalFlow" = "Flujo Total" "totalFlow" = "Flujo Total"
"leaveBlankToNeverExpire" = "Dejar en Blanco para Nunca Expirar" "leaveBlankToNeverExpire" = "Dejar en Blanco para Nunca Expirar"
"noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada" "noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada"
@@ -225,9 +225,6 @@
"requestHeader" = "Encabezado de solicitud" "requestHeader" = "Encabezado de solicitud"
"responseHeader" = "Encabezado de respuesta" "responseHeader" = "Encabezado de respuesta"
[pages.inbounds.stream.quic]
"encryption" = "Cifrado"
[pages.settings] [pages.settings]
"title" = "Configuraciones" "title" = "Configuraciones"
"save" = "Guardar" "save" = "Guardar"
@@ -312,12 +309,14 @@
"fragment" = "Fragmentación" "fragment" = "Fragmentación"
"fragmentDesc" = "Habilitar la fragmentación para el paquete de saludo de TLS" "fragmentDesc" = "Habilitar la fragmentación para el paquete de saludo de TLS"
"fragmentSett" = "Configuración de Fragmentación" "fragmentSett" = "Configuración de Fragmentación"
"noisesDesc" = "Activar Noises."
"noisesSett" = "Configuración de Noises"
"mux" = "Mux" "mux" = "Mux"
"muxDesc" = "Transmite múltiples flujos de datos independientes dentro de un flujo de datos establecido." "muxDesc" = "Transmite múltiples flujos de datos independientes dentro de un flujo de datos establecido."
"muxSett" = "Configuración Mux" "muxSett" = "Configuración Mux"
"direct" = "Conexión Directa" "direct" = "Conexión Directa"
"directDesc" = "Establece conexiones directas con dominios o rangos de IP de un país específico." "directDesc" = "Establece conexiones directas con dominios o rangos de IP de un país específico."
"directSett" = "Opciones de Conexión Directa"
[pages.xray] [pages.xray]
"title" = "Xray Configuración" "title" = "Xray Configuración"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Los registros pueden afectar la eficiencia de su servidor. Se recomienda habilitarlos sabiamente solo en caso de sus necesidades." "logConfigsDesc" = "Los registros pueden afectar la eficiencia de su servidor. Se recomienda habilitarlos sabiamente solo en caso de sus necesidades."
"blockConfigs" = "Configuraciones de Bloqueo" "blockConfigs" = "Configuraciones de Bloqueo"
"blockConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a protocolos y sitios web específicos." "blockConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a protocolos y sitios web específicos."
"blockCountryConfigs" = "Configuraciones de Bloqueo por País" "blockConnectionsConfigs" = "Bloquear Conexiones"
"blockCountryConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a dominios de países específicos." "blockConnectionsConfigsDesc" = "Estas opciones bloquearán el tráfico según el país solicitado específico."
"directCountryConfigs" = "Configuraciones de Conexión Directa por País" "directConnectionsConfigs" = "Conexiones Directas"
"directCountryConfigsDesc" = "Una conexión directa asegura que el tráfico específico no se enruta a través de otro servidor." "directConnectionsConfigsDesc" = "Una conexión directa asegura que el tráfico específico no sea enrutado a través de otro servidor."
"blockips" = "Bloquear IPs"
"blockdomains" = "Bloquear Dominios"
"directips" = "IPs Directas"
"directdomains" = "Dominios Directos"
"ipv4Configs" = "Configuraciones IPv4" "ipv4Configs" = "Configuraciones IPv4"
"ipv4ConfigsDesc" = "Estas opciones solo enrutarán a los dominios objetivo a través de IPv4." "ipv4ConfigsDesc" = "Estas opciones solo enrutarán a los dominios objetivo a través de IPv4."
"warpConfigs" = "Configuraciones de WARP" "warpConfigs" = "Configuraciones de WARP"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Cambiar la plantilla de configuración para la protección de seguridad." "SecurityDesc" = "Cambiar la plantilla de configuración para la protección de seguridad."
"Speedtest" = "Bloquear Sitios Web de Pruebas de Velocidad" "Speedtest" = "Bloquear Sitios Web de Pruebas de Velocidad"
"SpeedtestDesc" = "Cambia la plantilla de configuración para evitar la conexión a sitios web de pruebas de velocidad." "SpeedtestDesc" = "Cambia la plantilla de configuración para evitar la conexión a sitios web de pruebas de velocidad."
"IRIp" = "Desactivar Conexión a Rangos de IP de Irán"
"IRIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de Irán."
"IRDomain" = "Desactivar Conexión a Dominios de Irán"
"IRDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de Irán."
"ChinaIp" = "Desactivar Conexión a Rangos de IP de China"
"ChinaIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de China."
"ChinaDomain" = "Desactivar Conexión a Dominios de China"
"ChinaDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de China."
"RussiaIp" = "Desactivar Conexión a Rangos de IP de Rusia"
"RussiaIpDesc" = "Cambia la plantilla de configuración para evitar la conexión a rangos de IP de Rusia."
"RussiaDomain" = "Desactivar Conexión a Dominios de Rusia"
"RussiaDomainDesc" = "Cambia la plantilla de configuración para evitar la conexión a dominios de Rusia."
"VNIp" = "Deshabilitar la conexión a las IP de Vietnam"
"VNIpDesc" = "Cambie la plantilla de configuración para evitar conectarse a rangos de IP de Vietnam."
"VNDomain" = "Deshabilitar la conexión a dominios de Vietnam"
"VNDomainDesc" = "Cambie la plantilla de configuración para evitar conectarse a dominios de Vietnam."
"DirectIRIp" = "Conexión Directa a Rangos de IP de Irán"
"DirectIRIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de Irán."
"DirectIRDomain" = "Conexión Directa a Dominios de Irán"
"DirectIRDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de Irán."
"DirectChinaIp" = "Conexión Directa a Rangos de IP de China"
"DirectChinaIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de China."
"DirectChinaDomain" = "Conexión Directa a Dominios de China"
"DirectChinaDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de China."
"DirectRussiaIp" = "Conexión Directa a Rangos de IP de Rusia"
"DirectRussiaIpDesc" = "Cambia la plantilla de configuración para conectarse directamente a rangos de IP de Rusia."
"DirectRussiaDomain" = "Conexión Directa a Dominios de Rusia"
"DirectRussiaDomainDesc" = "Cambia la plantilla de configuración para conectarse directamente a dominios de Rusia."
"DirectVNIp" = "Conexión directa a IP de Vietnam"
"DirectVNIpDesc" = "Cambie la plantilla de configuración para la conexión directa a rangos de IP de Vietnam."
"DirectVNDomain" = "Conexión directa a dominios de Vietnam"
"DirectVNDomainDesc" = "Cambie la plantilla de configuración para la conexión directa a dominios de Vietnam."
"GoogleIPv4" = "Usar IPv4 para Google" "GoogleIPv4" = "Usar IPv4 para Google"
"GoogleIPv4Desc" = "Agregar enrutamiento para que Google se conecte con IPv4." "GoogleIPv4Desc" = "Agregar enrutamiento para que Google se conecte con IPv4."
"NetflixIPv4" = "Usar IPv4 para Netflix" "NetflixIPv4" = "Usar IPv4 para Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "La ruta del archivo para el registro de acceso. El valor especial 'ninguno' deshabilita los registros de acceso" "accessLogDesc" = "La ruta del archivo para el registro de acceso. El valor especial 'ninguno' deshabilita los registros de acceso"
"errorLog" = "Registro de Errores" "errorLog" = "Registro de Errores"
"errorLogDesc" = "La ruta del archivo para el registro de errores. El valor especial 'none' desactiva los registros de errores." "errorLogDesc" = "La ruta del archivo para el registro de errores. El valor especial 'none' desactiva los registros de errores."
"dnsLog" = "Registro DNS"
"dnsLogDesc" = "Si habilitar los registros de consulta DNS"
"maskAddress" = "Enmascarar Dirección"
"maskAddressDesc" = "Máscara de dirección IP, cuando se habilita, reemplazará automáticamente la dirección IP que aparece en el registro."
[pages.xray.rules] [pages.xray.rules]
"first" = "Primero" "first" = "Primero"
@@ -483,6 +458,7 @@
"add" = "Agregar Servidor" "add" = "Agregar Servidor"
"edit" = "Editar Servidor" "edit" = "Editar Servidor"
"domains" = "Dominios" "domains" = "Dominios"
"expectIPs" = "IPs esperadas"
[pages.xray.fakedns] [pages.xray.fakedns]
"add" = "Agregar DNS Falso" "add" = "Agregar DNS Falso"
@@ -592,7 +568,7 @@
"confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?" "confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?"
"confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?" "confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?"
"confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?" "confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?"
"confirmToggle" = " ✅ ¿Confirmar habilitar/deshabilitar usuario?" "confirmToggle" = "✅ ¿Confirmar habilitar/deshabilitar usuario?"
"dbBackup" = "Obtener Copia de Seguridad de BD" "dbBackup" = "Obtener Copia de Seguridad de BD"
"serverUsage" = "Uso del Servidor" "serverUsage" = "Uso del Servidor"
"getInbounds" = "Obtener Entradas" "getInbounds" = "Obtener Entradas"
@@ -616,11 +592,13 @@
"confirmNumberAdd" = "✅ Confirmar agregando: {{ .Num }}" "confirmNumberAdd" = "✅ Confirmar agregando: {{ .Num }}"
"limitTraffic" = "🚧 Límite de tráfico" "limitTraffic" = "🚧 Límite de tráfico"
"getBanLogs" = "Registros de prohibición" "getBanLogs" = "Registros de prohibición"
"allClients" = "Todos los Clientes"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ ¡Exitosa!" "successfulOperation" = "✅ ¡Exitosa!"
"errorOperation" = "❗ Error en la Operación." "errorOperation" = "❗ Error en la Operación."
"getInboundsFailed" = "❌ Error al obtener las entradas" "getInboundsFailed" = "❌ Error al obtener las entradas"
"getClientsFailed" = "❌ No se pudo obtener los clientes."
"canceled" = "❌ {{ .Email }} : Operación cancelada." "canceled" = "❌ {{ .Email }} : Operación cancelada."
"clientRefreshSuccess" = "✅ {{ .Email }} : Cliente actualizado exitosamente." "clientRefreshSuccess" = "✅ {{ .Email }} : Cliente actualizado exitosamente."
"IpRefreshSuccess" = "✅ {{ .Email }} : IPs actualizadas exitosamente." "IpRefreshSuccess" = "✅ {{ .Email }} : IPs actualizadas exitosamente."
@@ -635,4 +613,6 @@
"removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente." "removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente."
"enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente." "enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente."
"disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente." "disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente."
"askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: <code>{{ .TgUserID }}</code>" "askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ChatID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ChatID de usuario: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Elige un Cliente para Inbound {{ .Inbound }}"
"chooseInbound" = "Elige un Inbound"

View File

@@ -44,7 +44,7 @@
"monitor" = "آی‌پی اتصال" "monitor" = "آی‌پی اتصال"
"certificate" = "گواهی دیجیتال" "certificate" = "گواهی دیجیتال"
"fail" = "ناموفق" "fail" = "ناموفق"
"success" = " موفق" "success" = "موفق"
"getVersion" = "دریافت نسخه" "getVersion" = "دریافت نسخه"
"install" = "نصب" "install" = "نصب"
"clients" = "کاربران" "clients" = "کاربران"
@@ -109,7 +109,7 @@
"backup" = "پشتیبان‌گیری" "backup" = "پشتیبان‌گیری"
"backupTitle" = "پشتیبان‌گیری دیتابیس" "backupTitle" = "پشتیبان‌گیری دیتابیس"
"backupDescription" = "توصیه‌می‌شود قبل‌از واردکردن یک دیتابیس جدید، نسخه پشتیبان تهیه ‌کنید" "backupDescription" = "توصیه‌می‌شود قبل‌از واردکردن یک دیتابیس جدید، نسخه پشتیبان تهیه ‌کنید"
"exportDatabase" = " پشتیبان‌گیری" "exportDatabase" = "پشتیبان‌گیری"
"importDatabase" = "بازگرداندن" "importDatabase" = "بازگرداندن"
[pages.inbounds] [pages.inbounds]
@@ -225,9 +225,6 @@
"requestHeader" = "سربرگ درخواست" "requestHeader" = "سربرگ درخواست"
"responseHeader" = "سربرگ پاسخ" "responseHeader" = "سربرگ پاسخ"
[pages.inbounds.stream.quic]
"encryption" = "رمزنگاری"
[pages.settings] [pages.settings]
"title" = "تنظیمات پنل" "title" = "تنظیمات پنل"
"save" = "ذخیره" "save" = "ذخیره"
@@ -288,7 +285,7 @@
"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه‌زمانی اجرا می‌شود" "timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه‌زمانی اجرا می‌شود"
"subSettings" = "سابسکریپشن" "subSettings" = "سابسکریپشن"
"subEnable" = "فعال‌سازی سرویس سابسکریپشن" "subEnable" = "فعال‌سازی سرویس سابسکریپشن"
"subEnableDesc" = " سرویس سابسکریپشن‌ را فعال‌می‌کند" "subEnableDesc" = "سرویس سابسکریپشن‌ را فعال‌می‌کند"
"subListen" = "آدرس آی‌پی" "subListen" = "آدرس آی‌پی"
"subListenDesc" = "آدرس آی‌پی برای سرویس سابسکریپشن. برای گوش دادن به‌تمام آی‌پی‌ها خالی‌بگذارید" "subListenDesc" = "آدرس آی‌پی برای سرویس سابسکریپشن. برای گوش دادن به‌تمام آی‌پی‌ها خالی‌بگذارید"
"subPort" = "پورت" "subPort" = "پورت"
@@ -312,12 +309,14 @@
"fragment" = "فرگمنت" "fragment" = "فرگمنت"
"fragmentDesc" = "فعال کردن فرگمنت برای بسته‌ی نخست تی‌ال‌اس" "fragmentDesc" = "فعال کردن فرگمنت برای بسته‌ی نخست تی‌ال‌اس"
"fragmentSett" = "تنظیمات فرگمنت" "fragmentSett" = "تنظیمات فرگمنت"
"noisesDesc" = "فعال کردن Noises."
"noisesSett" = "تنظیمات Noises"
"mux" = "ماکس" "mux" = "ماکس"
"muxDesc" = "چندین جریان داده مستقل را در یک جریان داده ثابت منتقل می کند" "muxDesc" = "چندین جریان داده مستقل را در یک جریان داده ثابت منتقل می کند"
"muxSett" = "تنظیمات ماکس" "muxSett" = "تنظیمات ماکس"
"direct" = "اتصال مستقیم" "direct" = "اتصال مستقیم"
"directDesc" = "به طور مستقیم با دامنه ها یا محدوده آی‌پی یک کشور خاص ارتباط برقرار می کند" "directDesc" = "به طور مستقیم با دامنه ها یا محدوده آی‌پی یک کشور خاص ارتباط برقرار می کند"
"directSett" = "گزینه های اتصال مستقیم"
[pages.xray] [pages.xray]
"title" = "پیکربندی ایکس‌ری" "title" = "پیکربندی ایکس‌ری"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "گزارش‌ها ممکن است بر کارایی سرور شما تأثیر بگذارد. توصیه می شود فقط در صورت نیاز آن را عاقلانه فعال کنید" "logConfigsDesc" = "گزارش‌ها ممکن است بر کارایی سرور شما تأثیر بگذارد. توصیه می شود فقط در صورت نیاز آن را عاقلانه فعال کنید"
"blockConfigs" = "سپر محافظ" "blockConfigs" = "سپر محافظ"
"blockConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس پروتکل‌های درخواستی خاص، و وب سایت‌ها مسدود می‌کند" "blockConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس پروتکل‌های درخواستی خاص، و وب سایت‌ها مسدود می‌کند"
"blockCountryConfigs" = "مسدودسازی کشور" "blockConnectionsConfigs" = "مسدود کردن اتصالات"
"blockCountryConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس کشور درخواستی خاص مسدود می‌کند" "blockConnectionsConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس کشور درخواست‌شده خاص مسدود می‌کنند."
"directCountryConfigs" = "اتصال مستقیم کشور" "directConnectionsConfigs" = "اتصالات مستقیم"
"directCountryConfigsDesc" = "اتصال مستقیم اطمینان حاصل می‌کند که ترافیک خاص از طریق یک سرور دیگر هدایت نمی‌شود." "directConnectionsConfigsDesc" = "یک اتصال مستقیم تضمین می‌کند که ترافیک خاص از طریق سرور دیگری مسیریابی نشود."
"blockips" = "مسدود کردن آی‌پی‌ها"
"blockdomains" = "مسدود کردن دامنه‌ها"
"directips" = "آی‌پی‌های مستقیم"
"directdomains" = "دامنه‌های مستقیم"
"ipv4Configs" = "IPv4 مسیریابی" "ipv4Configs" = "IPv4 مسیریابی"
"ipv4ConfigsDesc" = "این گزینه‌ها ترافیک‌ را از طریق آیپینسخه4 به مقصد هدایت می‌کند" "ipv4ConfigsDesc" = "این گزینه‌ها ترافیک‌ را از طریق آیپینسخه4 به مقصد هدایت می‌کند"
"warpConfigs" = "WARP مسیریابی" "warpConfigs" = "WARP مسیریابی"
@@ -357,38 +360,6 @@
"SecurityDesc" = "وب‌سایت‌های ناامن، بدافزار، فیشینگ، و کریپتوماینرها را مسدود می‌کند" "SecurityDesc" = "وب‌سایت‌های ناامن، بدافزار، فیشینگ، و کریپتوماینرها را مسدود می‌کند"
"Speedtest" = "مسدودسازی اسپیدتست" "Speedtest" = "مسدودسازی اسپیدتست"
"SpeedtestDesc" = "اتصال به وب‌سایت‌های تست سرعت را مسدود می‌کند" "SpeedtestDesc" = "اتصال به وب‌سایت‌های تست سرعت را مسدود می‌کند"
"IRIp" = "مسدودسازی اتصال به آی‌پی‌های ایران"
"IRIpDesc" = "اتصال به آی‌پی‌های کشور ایران را مسدود می‌کند"
"IRDomain" = "مسدودسازی اتصال به دامنه‌های‌ ایران"
"IRDomainDesc" = "اتصال به دامنه‌های کشور ایران را مسدود می‌کند"
"ChinaIp" = "مسدودسازی اتصال به آی‌‌پی‌های چین"
"ChinaIpDesc" = "اتصال به آی‌پی‌های کشور چین را مسدود می‌کند"
"ChinaDomain" = "مسدودسازی اتصال به دامنه‌های چین"
"ChinaDomainDesc" = "اتصال به دامنه‌های کشور چین را مسدود می‌کند"
"RussiaIp" = "مسدودسازی اتصال به آی‌پی‌های روسیه"
"RussiaIpDesc" = "اتصال به آی‌پی‌های کشور روسیه را مسدود می‌کند"
"RussiaDomain" = "مسدودسازی اتصال به دامنه‌های روسیه"
"RussiaDomainDesc" = "اتصال به دامنه‌های کشور روسیه را مسدود می‌کند"
"VNIp" = "مسدودسازی اتصال به آی‌پی‌های ویتنام"
"VNIpDesc" = "اتصال به آی‌پی‌های کشور ویتنام را مسدود می‌کند"
"VNDomain" = "مسدودسازی اتصال به دامنه های ویتنام"
"VNDomainDesc" = "اتصال به دامنه‌های کشور ویتنام را مسدود می‌کند"
"DirectIRIp" = "اتصال مستقیم آی‌پی‌های ایران"
"DirectIRIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور ایران"
"DirectIRDomain" = "اتصال مستقیم دامنه‌های ایران"
"DirectIRDomainDesc" = "اتصال مستقیم به دامنه‌های کشور ایران"
"DirectChinaIp" = "اتصال مستقیم آی‌پی‌های چین"
"DirectChinaIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور چین"
"DirectChinaDomain" = "اتصال مستقیم دامنه‌های چین"
"DirectChinaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور چین"
"DirectRussiaIp" = "اتصال مستقیم آی‌پی‌های روسیه"
"DirectRussiaIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور روسیه"
"DirectRussiaDomain" = "اتصال مستقیم دامنه‌های روسیه"
"DirectRussiaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور روسیه"
"DirectVNIp" = "اتصال مستقیم آی‌پی‌های ویتنام"
"DirectVNIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور ویتنام"
"DirectVNDomain" = "اتصال مستقیم دامنه‌های ویتنام"
"DirectVNDomainDesc" = "اتصال مستقیم به دامنه‌های کشور ویتنام"
"GoogleIPv4" = "گوگل" "GoogleIPv4" = "گوگل"
"GoogleIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به گوگل هدایت می‌کند" "GoogleIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به گوگل هدایت می‌کند"
"NetflixIPv4" = "نتفلیکس" "NetflixIPv4" = "نتفلیکس"
@@ -402,11 +373,11 @@
"MetaWARP" = "متا" "MetaWARP" = "متا"
"MetaWARPDesc" = "ترافیک را از طریق وارپ به متا (اینستاگرام، فیس بوک، واتساپ، تردز و...) هدایت می کند." "MetaWARPDesc" = "ترافیک را از طریق وارپ به متا (اینستاگرام، فیس بوک، واتساپ، تردز و...) هدایت می کند."
"AppleWARP" = "اپل" "AppleWARP" = "اپل"
"AppleWARPDesc" = " ترافیک را از طریق وارپ به اپل هدایت می‌کند" "AppleWARPDesc" = "ترافیک را از طریق وارپ به اپل هدایت می‌کند"
"RedditWARP" = "ردیت" "RedditWARP" = "ردیت"
"RedditWARPDesc" = " ترافیک را از طریق وارپ به ردیت هدایت می‌کند" "RedditWARPDesc" = "ترافیک را از طریق وارپ به ردیت هدایت می‌کند"
"SpotifyWARP" = "اسپاتیفای" "SpotifyWARP" = "اسپاتیفای"
"SpotifyWARPDesc" = " ترافیک را از طریق وارپ به اسپاتیفای هدایت می‌کند" "SpotifyWARPDesc" = "ترافیک را از طریق وارپ به اسپاتیفای هدایت می‌کند"
"IRWARP" = "دامنه‌های ایران" "IRWARP" = "دامنه‌های ایران"
"IRWARPDesc" = "ترافیک را از طریق وارپ به دامنه‌های کشور ایران هدایت می‌کند" "IRWARPDesc" = "ترافیک را از طریق وارپ به دامنه‌های کشور ایران هدایت می‌کند"
"Inbounds" = "ورودی‌ها" "Inbounds" = "ورودی‌ها"
@@ -423,6 +394,10 @@
"accessLogDesc" = "مسیر فایل برای گزارش دسترسی. مقدار ویژه «هیچ» گزارش‌های دسترسی را غیرفعال میکند." "accessLogDesc" = "مسیر فایل برای گزارش دسترسی. مقدار ویژه «هیچ» گزارش‌های دسترسی را غیرفعال میکند."
"errorLog" = "گزارش خطا" "errorLog" = "گزارش خطا"
"errorLogDesc" = "مسیر فایل برای ورود به سیستم خطا. مقدار ویژه «هیچ» گزارش های خطا را غیرفعال میکند" "errorLogDesc" = "مسیر فایل برای ورود به سیستم خطا. مقدار ویژه «هیچ» گزارش های خطا را غیرفعال میکند"
"dnsLog" = "گزارش DNS"
"dnsLogDesc" = "آیا ثبت‌های درخواست DNS را فعال کنید"
"maskAddress" = "پنهان کردن آدرس"
"maskAddressDesc" = "پوشش آدرس IP، هنگامی که فعال می‌شود، به طور خودکار آدرس IP که در لاگ ظاهر می‌شود را جایگزین می‌کند."
[pages.xray.rules] [pages.xray.rules]
"first" = "اولین" "first" = "اولین"
@@ -485,6 +460,7 @@
"add" = "افزودن سرور" "add" = "افزودن سرور"
"edit" = "ویرایش سرور" "edit" = "ویرایش سرور"
"domains" = "دامنه‌ها" "domains" = "دامنه‌ها"
"expectIPs" = "آی‌پی‌های مورد انتظار"
[pages.xray.fakedns] [pages.xray.fakedns]
"add" = "افزودن دی‌ان‌اس جعلی" "add" = "افزودن دی‌ان‌اس جعلی"
@@ -517,10 +493,10 @@
"unlimited" = "♾ - نامحدود(ریست)" "unlimited" = "♾ - نامحدود(ریست)"
"add" = "اضافه کردن" "add" = "اضافه کردن"
"month" = "ماه" "month" = "ماه"
"months" = "ماه‌ها" "months" = "ماه‌"
"day" = "روز" "day" = "روز"
"days" = "روزها" "days" = "روز"
"hours" = "ساعت‌ها" "hours" = "ساعت‌"
"unknown" = "نامشخص" "unknown" = "نامشخص"
"inbounds" = "ورودی‌ها" "inbounds" = "ورودی‌ها"
"clients" = "کلاینت‌ها" "clients" = "کلاینت‌ها"
@@ -567,7 +543,7 @@
"inbound" = "📍 نام‌ورودی: {{ .Remark }}\r\n" "inbound" = "📍 نام‌ورودی: {{ .Remark }}\r\n"
"port" = "🔌 پورت: {{ .Port }}\r\n" "port" = "🔌 پورت: {{ .Port }}\r\n"
"expire" = "📅 تاریخ‌انقضا: {{ .Time }}\r\n\r\n" "expire" = "📅 تاریخ‌انقضا: {{ .Time }}\r\n\r\n"
"expireIn" = "📅 باقی‌مانده‌تاانقضا: {{ .Time }}\r\n\r\n" "expireIn" = "📅 باقی‌ مانده‌ تا انقضا: {{ .Time }}\r\n\r\n"
"active" = "💡 فعال: {{ .Enable }}\r\n" "active" = "💡 فعال: {{ .Enable }}\r\n"
"enabled" = "🚨 وضعیت: {{ .Enable }}\r\n" "enabled" = "🚨 وضعیت: {{ .Enable }}\r\n"
"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n" "online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ تایید اضافه کردن: {{ .Num }}" "confirmNumberAdd" = "✅ تایید اضافه کردن: {{ .Num }}"
"limitTraffic" = "🚧 محدودیت ترافیک" "limitTraffic" = "🚧 محدودیت ترافیک"
"getBanLogs" = "گزارش های بلوک را دریافت کنید" "getBanLogs" = "گزارش های بلوک را دریافت کنید"
"allClients" = "همه مشتریان"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ انجام شد!" "successfulOperation" = "✅ انجام شد!"
"errorOperation" = "❗ خطا در عملیات." "errorOperation" = "❗ خطا در عملیات."
"getInboundsFailed" = "❌ دریافت ورودی‌ها با خطا مواجه شد." "getInboundsFailed" = "❌ دریافت ورودی‌ها با خطا مواجه شد."
"getClientsFailed" = "❌ دریافت مشتریان با شکست مواجه شد."
"canceled" = "❌ {{ .Email }} : عملیات لغو شد." "canceled" = "❌ {{ .Email }} : عملیات لغو شد."
"clientRefreshSuccess" = "✅ {{ .Email }} : کلاینت با موفقیت تازه‌سازی شد." "clientRefreshSuccess" = "✅ {{ .Email }} : کلاینت با موفقیت تازه‌سازی شد."
"IpRefreshSuccess" = "✅ {{ .Email }} : آدرس‌ها با موفقیت تازه‌سازی شدند." "IpRefreshSuccess" = "✅ {{ .Email }} : آدرس‌ها با موفقیت تازه‌سازی شدند."
@@ -638,3 +616,5 @@
"enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد." "enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد."
"disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد." "disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد."
"askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <code>{{ .TgUserID }}</code>" "askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <code>{{ .TgUserID }}</code>"
"chooseClient" = "یک مشتری برای ورودی {{ .Inbound }} انتخاب کنید"
"chooseInbound" = "یک ورودی انتخاب کنید"

View File

@@ -143,7 +143,7 @@
"destinationPort" = "Port Tujuan" "destinationPort" = "Port Tujuan"
"targetAddress" = "Alamat Target" "targetAddress" = "Alamat Target"
"monitorDesc" = "Biarkan kosong untuk mendengarkan semua IP" "monitorDesc" = "Biarkan kosong untuk mendengarkan semua IP"
"meansNoLimit" = " = Unlimited. (unit: GB)" "meansNoLimit" = "= Unlimited. (unit: GB)"
"totalFlow" = "Total Aliran" "totalFlow" = "Total Aliran"
"leaveBlankToNeverExpire" = "Biarkan kosong untuk tidak pernah kedaluwarsa" "leaveBlankToNeverExpire" = "Biarkan kosong untuk tidak pernah kedaluwarsa"
"noRecommendKeepDefault" = "Disarankan untuk tetap menggunakan pengaturan default" "noRecommendKeepDefault" = "Disarankan untuk tetap menggunakan pengaturan default"
@@ -225,9 +225,6 @@
"requestHeader" = "Header Permintaan" "requestHeader" = "Header Permintaan"
"responseHeader" = "Header Respons" "responseHeader" = "Header Respons"
[pages.inbounds.stream.quic]
"encryption" = "Enkripsi"
[pages.settings] [pages.settings]
"title" = "Pengaturan Panel" "title" = "Pengaturan Panel"
"save" = "Simpan" "save" = "Simpan"
@@ -312,12 +309,14 @@
"fragment" = "Fragmentasi" "fragment" = "Fragmentasi"
"fragmentDesc" = "Aktifkan fragmentasi untuk paket hello TLS" "fragmentDesc" = "Aktifkan fragmentasi untuk paket hello TLS"
"fragmentSett" = "Pengaturan Fragmentasi" "fragmentSett" = "Pengaturan Fragmentasi"
"noisesDesc" = "Aktifkan Noises."
"noisesSett" = "Pengaturan Noises"
"mux" = "Mux" "mux" = "Mux"
"muxDesc" = "Mengirimkan beberapa aliran data independen dalam aliran data yang sudah ada." "muxDesc" = "Mengirimkan beberapa aliran data independen dalam aliran data yang sudah ada."
"muxSett" = "Pengaturan Mux" "muxSett" = "Pengaturan Mux"
"direct" = "Koneksi langsung" "direct" = "Koneksi langsung"
"directDesc" = "Secara langsung membuat koneksi dengan domain atau rentang IP negara tertentu." "directDesc" = "Secara langsung membuat koneksi dengan domain atau rentang IP negara tertentu."
"directSett" = "Opsi Koneksi Langsung"
[pages.xray] [pages.xray]
"title" = "Konfigurasi Xray" "title" = "Konfigurasi Xray"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Log dapat mempengaruhi efisiensi server Anda. Disarankan untuk mengaktifkannya dengan bijak hanya jika diperlukan" "logConfigsDesc" = "Log dapat mempengaruhi efisiensi server Anda. Disarankan untuk mengaktifkannya dengan bijak hanya jika diperlukan"
"blockConfigs" = "Pelindung" "blockConfigs" = "Pelindung"
"blockConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan protokol dan situs web yang diminta." "blockConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan protokol dan situs web yang diminta."
"blockCountryConfigs" = "Blokir Negara" "blockConnectionsConfigs" = "Blokir Koneksi"
"blockCountryConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan negara yang diminta." "blockConnectionsConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan negara yang diminta."
"directCountryConfigs" = "Langsung ke Negara" "directConnectionsConfigs" = "Koneksi Langsung"
"directCountryConfigsDesc" = "Koneksi langsung memastikan bahwa lalu lintas tertentu tidak diarahkan melalui server lain." "directConnectionsConfigsDesc" = "Koneksi langsung memastikan bahwa lalu lintas tertentu tidak dialihkan melalui server lain."
"blockips" = "Blokir IP"
"blockdomains" = "Blokir Domain"
"directips" = "IP Langsung"
"directdomains" = "Domain Langsung"
"ipv4Configs" = "Pengalihan IPv4" "ipv4Configs" = "Pengalihan IPv4"
"ipv4ConfigsDesc" = "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui IPv4." "ipv4ConfigsDesc" = "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui IPv4."
"warpConfigs" = "Pengalihan WARP" "warpConfigs" = "Pengalihan WARP"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Memblokir situs web malware, phishing, dan penambang kripto." "SecurityDesc" = "Memblokir situs web malware, phishing, dan penambang kripto."
"Speedtest" = "Blokir Speedtest" "Speedtest" = "Blokir Speedtest"
"SpeedtestDesc" = "Memblokir pembentukan koneksi ke situs web speedtest." "SpeedtestDesc" = "Memblokir pembentukan koneksi ke situs web speedtest."
"IRIp" = "Blokir Koneksi ke IP Iran"
"IRIpDesc" = "Memblokir pembentukan koneksi ke rentang IP Iran."
"IRDomain" = "Blokir Koneksi ke Domain Iran"
"IRDomainDesc" = "Memblokir pembentukan koneksi ke domain Iran."
"ChinaIp" = "Blokir Koneksi ke IP China"
"ChinaIpDesc" = "Memblokir pembentukan koneksi ke rentang IP China."
"ChinaDomain" = "Blokir Koneksi ke Domain China"
"ChinaDomainDesc" = "Memblokir pembentukan koneksi ke domain China."
"RussiaIp" = "Blokir Koneksi ke IP Rusia"
"RussiaIpDesc" = "Memblokir pembentukan koneksi ke rentang IP Rusia."
"RussiaDomain" = "Blokir Koneksi ke Domain Rusia"
"RussiaDomainDesc" = "Memblokir pembentukan koneksi ke domain Rusia."
"VNIp" = "Blokir Koneksi ke IP Vietnam"
"VNIpDesc" = "Memblokir pembentukan koneksi ke rentang IP Vietnam."
"VNDomain" = "Blokir Koneksi ke Domain Vietnam"
"VNDomainDesc" = "Memblokir pembentukan koneksi ke domain Vietnam."
"DirectIRIp" = "Koneksi Langsung ke IP Iran"
"DirectIRIpDesc" = "Membentuk koneksi langsung ke rentang IP Iran."
"DirectIRDomain" = "Koneksi Langsung ke Domain Iran"
"DirectIRDomainDesc" = "Membentuk koneksi langsung ke domain Iran."
"DirectChinaIp" = "Koneksi Langsung ke IP China"
"DirectChinaIpDesc" = "Membentuk koneksi langsung ke rentang IP China."
"DirectChinaDomain" = "Koneksi Langsung ke Domain China"
"DirectChinaDomainDesc" = "Membentuk koneksi langsung ke domain China."
"DirectRussiaIp" = "Koneksi Langsung ke IP Rusia"
"DirectRussiaIpDesc" = "Membentuk koneksi langsung ke rentang IP Rusia."
"DirectRussiaDomain" = "Koneksi Langsung ke Domain Rusia"
"DirectRussiaDomainDesc" = "Membentuk koneksi langsung ke domain Rusia."
"DirectVNIp" = "Koneksi Langsung ke IP Vietnam"
"DirectVNIpDesc" = "Membentuk koneksi langsung ke rentang IP Vietnam."
"DirectVNDomain" = "Koneksi Langsung ke Domain Vietnam"
"DirectVNDomainDesc" = "Membentuk koneksi langsung ke domain Vietnam."
"GoogleIPv4" = "Google" "GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "Rute lalu lintas ke Google melalui IPv4." "GoogleIPv4Desc" = "Rute lalu lintas ke Google melalui IPv4."
"NetflixIPv4" = "Netflix" "NetflixIPv4" = "Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "Jalur file untuk log akses. Nilai khusus 'tidak ada' menonaktifkan log akses" "accessLogDesc" = "Jalur file untuk log akses. Nilai khusus 'tidak ada' menonaktifkan log akses"
"errorLog" = "Catatan eror" "errorLog" = "Catatan eror"
"errorLogDesc" = "Jalur file untuk log kesalahan. Nilai khusus 'tidak ada' menonaktifkan log kesalahan" "errorLogDesc" = "Jalur file untuk log kesalahan. Nilai khusus 'tidak ada' menonaktifkan log kesalahan"
"dnsLog" = "Log DNS"
"dnsLogDesc" = "Apakah akan mengaktifkan log kueri DNS"
"maskAddress" = "Alamat Masker"
"maskAddressDesc" = "Masker alamat IP, ketika diaktifkan, akan secara otomatis mengganti alamat IP yang muncul di log."
[pages.xray.rules] [pages.xray.rules]
"first" = "Pertama" "first" = "Pertama"
@@ -485,6 +460,7 @@
"add" = "Tambahkan Server" "add" = "Tambahkan Server"
"edit" = "Sunting Server" "edit" = "Sunting Server"
"domains" = "Domains" "domains" = "Domains"
"expectIPs" = "IP yang Diharapkan"
[pages.xray.fakedns] [pages.xray.fakedns]
"add" = "Tambahkan DNS Palsu" "add" = "Tambahkan DNS Palsu"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Konfirmasi menambahkan: {{ .Num }}" "confirmNumberAdd" = "✅ Konfirmasi menambahkan: {{ .Num }}"
"limitTraffic" = "🚧 Batas Lalu Lintas" "limitTraffic" = "🚧 Batas Lalu Lintas"
"getBanLogs" = "Dapatkan Log Pemblokiran" "getBanLogs" = "Dapatkan Log Pemblokiran"
"allClients" = "Semua Klien"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ Operasi berhasil!" "successfulOperation" = "✅ Operasi berhasil!"
"errorOperation" = "❗ Kesalahan dalam operasi." "errorOperation" = "❗ Kesalahan dalam operasi."
"getInboundsFailed" = "❌ Gagal mendapatkan inbounds." "getInboundsFailed" = "❌ Gagal mendapatkan inbounds."
"getClientsFailed" = "❌ Gagal mendapatkan klien."
"canceled" = "❌ {{ .Email }}: Operasi dibatalkan." "canceled" = "❌ {{ .Email }}: Operasi dibatalkan."
"clientRefreshSuccess" = "✅ {{ .Email }}: Klien diperbarui dengan berhasil." "clientRefreshSuccess" = "✅ {{ .Email }}: Klien diperbarui dengan berhasil."
"IpRefreshSuccess" = "✅ {{ .Email }}: IP diperbarui dengan berhasil." "IpRefreshSuccess" = "✅ {{ .Email }}: IP diperbarui dengan berhasil."
@@ -637,4 +615,6 @@
"removedTGUserSuccess" = "✅ {{ .Email }}: Pengguna Telegram dihapus dengan berhasil." "removedTGUserSuccess" = "✅ {{ .Email }}: Pengguna Telegram dihapus dengan berhasil."
"enableSuccess" = "✅ {{ .Email }}: Diaktifkan dengan berhasil." "enableSuccess" = "✅ {{ .Email }}: Diaktifkan dengan berhasil."
"disableSuccess" = "✅ {{ .Email }}: Dinonaktifkan dengan berhasil." "disableSuccess" = "✅ {{ .Email }}: Dinonaktifkan dengan berhasil."
"askToAddUserId" = "Konfigurasi Anda tidak ditemukan!\r\nSilakan minta admin Anda untuk menggunakan ID Telegram Anda dalam konfigurasi Anda.\r\n\r\nID Pengguna Anda: <code>{{ .TgUserID }}</code>" "askToAddUserId" = "Konfigurasi Anda tidak ditemukan!\r\nSilakan minta admin Anda untuk menggunakan ChatID Telegram Anda dalam konfigurasi Anda.\r\n\r\nChatID Pengguna Anda: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Pilih Klien untuk Inbound {{ .Inbound }}"
"chooseInbound" = "Pilih Inbound"

View File

@@ -0,0 +1,620 @@
"username" = "Nome de Usuário"
"password" = "Senha"
"login" = "Entrar"
"confirm" = "Confirmar"
"cancel" = "Cancelar"
"close" = "Fechar"
"copy" = "Copiar"
"copied" = "Copiado"
"download" = "Baixar"
"remark" = "Observação"
"enable" = "Ativado"
"protocol" = "Protocolo"
"search" = "Pesquisar"
"filter" = "Filtrar"
"loading" = "Carregando..."
"second" = "Segundo"
"minute" = "Minuto"
"hour" = "Hora"
"day" = "Dia"
"check" = "Verificar"
"indefinite" = "Indeterminado"
"unlimited" = "Ilimitado"
"none" = "Nada"
"qrCode" = "Código QR"
"info" = "Mais Informações"
"edit" = "Editar"
"delete" = "Excluir"
"reset" = "Redefinir"
"copySuccess" = "Copiado com Sucesso"
"sure" = "Certo"
"encryption" = "Criptografia"
"transmission" = "Transmissão"
"host" = "Servidor"
"path" = "Caminho"
"camouflage" = "Ofuscação"
"status" = "Status"
"enabled" = "Ativado"
"disabled" = "Desativado"
"depleted" = "Encerrado"
"depletingSoon" = "Esgotando"
"offline" = "Offline"
"online" = "Online"
"domainName" = "Nome de Domínio"
"monitor" = "IP de Escuta"
"certificate" = "Certificado Digital"
"fail" = "Falhou"
"success" = "Com Sucesso"
"getVersion" = "Obter Versão"
"install" = "Instalar"
"clients" = "Clientes"
"usage" = "Uso"
"secretToken" = "Token Secreto"
"remained" = "Restante"
"security" = "Segurança"
"secAlertTitle" = "Alerta de Segurança"
"secAlertSsl" = "Esta conexão não é segura. Evite inserir informações confidenciais até que o TLS seja ativado para proteção de dados."
"secAlertConf" = "Algumas configurações estão vulneráveis a ataques. Recomenda-se reforçar os protocolos de segurança para evitar possíveis violações."
"secAlertSSL" = "O painel não possui uma conexão segura. Instale o certificado TLS para proteção de dados."
"secAlertPanelPort" = "A porta padrão do painel é vulnerável. Configure uma porta aleatória ou específica."
"secAlertPanelURI" = "O caminho URI padrão do painel não é seguro. Configure um caminho URI complexo."
"secAlertSubURI" = "O caminho URI padrão de inscrição não é seguro. Configure um caminho URI complexo."
"secAlertSubJsonURI" = "O caminho URI JSON de inscrição padrão não é seguro. Configure um caminho URI complexo."
[menu]
"dashboard" = "Visão Geral"
"inbounds" = "Inbounds"
"settings" = "Panel Settings"
"xray" = "Xray Configs"
"logout" = "Sair"
"link" = "Gerenciar"
[pages.login]
"hello" = "Olá"
"title" = "Bem-vindo"
"loginAgain" = "Sua sessão expirou, faça login novamente"
[pages.login.toasts]
"invalidFormData" = "O formato dos dados de entrada é inválido."
"emptyUsername" = "Nome de usuário é obrigatório"
"emptyPassword" = "Senha é obrigatória"
"wrongUsernameOrPassword" = "Nome de usuário, senha ou segredo inválidos."
"successLogin" = "Login realizado com sucesso"
[pages.index]
"title" = "Visão Geral"
"memory" = "Memória RAM"
"hard" = "Disco"
"xrayStatus" = "Xray"
"stopXray" = "Parar"
"restartXray" = "Reiniciar"
"xraySwitch" = "Versão"
"xraySwitchClick" = "Escolha a versão para a qual deseja alternar."
"xraySwitchClickDesk" = "Escolha com cuidado, pois versões mais antigas podem não ser compatíveis com as configurações atuais."
"operationHours" = "Tempo de Atividade"
"systemLoad" = "Carga do Sistema"
"systemLoadDesc" = "Média de carga do sistema nos últimos 1, 5 e 15 minutos"
"connectionTcpCountDesc" = "Total de conexões TCP no sistema"
"connectionUdpCountDesc" = "Total de conexões UDP no sistema"
"connectionCount" = "Estatísticas de Conexão"
"upSpeed" = "Velocidade total de upload no sistema"
"downSpeed" = "Velocidade total de download no sistema"
"totalSent" = "Dados totais enviados desde a inicialização do sistema"
"totalReceive" = "Dados totais recebidos desde a inicialização do sistema"
"xraySwitchVersionDialog" = "Alterar Versão do Xray"
"xraySwitchVersionDialogDesc" = "Tem certeza de que deseja alterar a versão do Xray para"
"dontRefresh" = "Instalação em andamento, por favor não atualize a página"
"logs" = "Logs"
"config" = "Configuração"
"backup" = "Backup e Restauração"
"backupTitle" = "Backup e Restauração do Banco de Dados"
"backupDescription" = "É recomendado fazer um backup antes de restaurar o banco de dados."
"exportDatabase" = "Fazer Backup"
"importDatabase" = "Restaurar"
[pages.inbounds]
"title" = "Inbounds"
"totalDownUp" = "Total Enviado/Recebido"
"totalUsage" = "Uso Total"
"inboundCount" = "Total de Inbounds"
"operate" = "Menu"
"enable" = "Ativado"
"remark" = "Observação"
"protocol" = "Protocolo"
"port" = "Porta"
"traffic" = "Tráfego"
"details" = "Detalhes"
"transportConfig" = "Transporte"
"expireDate" = "Duração"
"resetTraffic" = "Redefinir Tráfego"
"addInbound" = "Adicionar Inbound"
"generalActions" = "Ações Gerais"
"create" = "Criar"
"update" = "Atualizar"
"modifyInbound" = "Modificar Inbound"
"deleteInbound" = "Excluir Inbound"
"deleteInboundContent" = "Tem certeza de que deseja excluir o inbound?"
"deleteClient" = "Excluir Cliente"
"deleteClientContent" = "Tem certeza de que deseja excluir o cliente?"
"resetTrafficContent" = "Tem certeza de que deseja redefinir o tráfego?"
"copyLink" = "Copiar URL"
"address" = "Endereço"
"network" = "Rede"
"destinationPort" = "Porta de Destino"
"targetAddress" = "Endereço de Destino"
"monitorDesc" = "Deixe em branco para ouvir todos os IPs"
"meansNoLimit" = "= Ilimitado. (unidade: GB)"
"totalFlow" = "Fluxo Total"
"leaveBlankToNeverExpire" = "Deixe em branco para nunca expirar"
"noRecommendKeepDefault" = "Recomenda-se manter o padrão"
"certificatePath" = "Caminho"
"certificateContent" = "Conteúdo"
"publicKey" = "Chave Pública"
"privatekey" = "Chave Privada"
"clickOnQRcode" = "Clique no Código QR para Copiar"
"client" = "Cliente"
"export" = "Exportar Todos os URLs"
"clone" = "Clonar"
"cloneInbound" = "Clonar"
"cloneInboundContent" = "Todas as configurações deste inbound, exceto Porta, IP de Escuta e Clientes, serão aplicadas ao clone."
"cloneInboundOk" = "Clonar"
"resetAllTraffic" = "Redefinir Tráfego de Todos os Inbounds"
"resetAllTrafficTitle" = "Redefinir Tráfego de Todos os Inbounds"
"resetAllTrafficContent" = "Tem certeza de que deseja redefinir o tráfego de todos os inbounds?"
"resetInboundClientTraffics" = "Redefinir Tráfego dos Clientes"
"resetInboundClientTrafficTitle" = "Redefinir Tráfego dos Clientes"
"resetInboundClientTrafficContent" = "Tem certeza de que deseja redefinir o tráfego dos clientes deste inbound?"
"resetAllClientTraffics" = "Redefinir Tráfego de Todos os Clientes"
"resetAllClientTrafficTitle" = "Redefinir Tráfego de Todos os Clientes"
"resetAllClientTrafficContent" = "Tem certeza de que deseja redefinir o tráfego de todos os clientes?"
"delDepletedClients" = "Excluir Clientes Esgotados"
"delDepletedClientsTitle" = "Excluir Clientes Esgotados"
"delDepletedClientsContent" = "Tem certeza de que deseja excluir todos os clientes esgotados?"
"email" = "Email"
"emailDesc" = "Por favor, forneça um endereço de e-mail único."
"IPLimit" = "Limite de IP"
"IPLimitDesc" = "Desativa o inbound se o número ultrapassar o valor definido. (0 = desativar)"
"IPLimitlog" = "Log de IP"
"IPLimitlogDesc" = "O histórico de IPs. (para ativar o inbound após a desativação, limpe o log)"
"IPLimitlogclear" = "Limpar o Log"
"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)"
"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"
"same" = "Igual"
"inboundData" = "Dados do Inbound"
"exportInbound" = "Exportar Inbound"
"import" = "Importar"
"importInbound" = "Importar um Inbound"
[pages.client]
"add" = "Adicionar Cliente"
"edit" = "Editar Cliente"
"submitAdd" = "Adicionar Cliente"
"submitEdit" = "Salvar Alterações"
"clientCount" = "Número de Clientes"
"bulk" = "Adicionar Vários"
"method" = "Método"
"first" = "Primeiro"
"last" = "Último"
"prefix" = "Prefixo"
"postfix" = "Sufixo"
"delayedStart" = "Iniciar Após Primeiro Uso"
"expireDays" = "Duração"
"days" = "Dia(s)"
"renew" = "Renovação Automática"
"renewDesc" = "Renovação automática após expiração. (0 = desativado)(unidade: dia)"
[pages.inbounds.toasts]
"obtain" = "Obter"
[pages.inbounds.stream.general]
"request" = "Requisição"
"response" = "Resposta"
"name" = "Nome"
"value" = "Valor"
[pages.inbounds.stream.tcp]
"version" = "Versão"
"method" = "Método"
"path" = "Caminho"
"status" = "Status"
"statusDescription" = "Descrição do Status"
"requestHeader" = "Cabeçalho da Requisição"
"responseHeader" = "Cabeçalho da Resposta"
[pages.settings]
"title" = "Configurações do Painel"
"save" = "Salvar"
"infoDesc" = "Toda alteração feita aqui precisa ser salva. Reinicie o painel para aplicar as alterações."
"restartPanel" = "Reiniciar Painel"
"restartPanelDesc" = "Tem certeza de que deseja reiniciar o painel? Se não conseguir acessar o painel após reiniciar, consulte os logs do painel no servidor."
"actions" = "Ações"
"resetDefaultConfig" = "Redefinir para Padrão"
"panelSettings" = "Geral"
"securitySettings" = "Autenticação"
"TGBotSettings" = "Bot do Telegram"
"panelListeningIP" = "IP de Escuta"
"panelListeningIPDesc" = "O endereço IP para o painel web. (deixe em branco para escutar em todos os IPs)"
"panelListeningDomain" = "Domínio de Escuta"
"panelListeningDomainDesc" = "O nome de domínio para o painel web. (deixe em branco para escutar em todos os domínios e IPs)"
"panelPort" = "Porta de Escuta"
"panelPortDesc" = "O número da porta para o painel web. (deve ser uma porta não usada)"
"publicKeyPath" = "Caminho da Chave Pública"
"publicKeyPathDesc" = "O caminho do arquivo de chave pública para o painel web. (começa com /)"
"privateKeyPath" = "Caminho da Chave Privada"
"privateKeyPathDesc" = "O caminho do arquivo de chave privada para o painel web. (começa com /)"
"panelUrlPath" = "Caminho URI"
"panelUrlPathDesc" = "O caminho URI para o painel web. (começa com / e termina com /)"
"pageSize" = "Tamanho da Paginação"
"pageSizeDesc" = "Definir o tamanho da página para a tabela de entradas. (0 = desativado)"
"remarkModel" = "Modelo de Observação & Caractere de Separação"
"datepicker" = "Tipo de Calendário"
"datepickerPlaceholder" = "Selecionar data"
"datepickerDescription" = "Tarefas agendadas serão executadas com base neste calendário."
"sampleRemark" = "Exemplo de Observação"
"oldUsername" = "Nome de Usuário Atual"
"currentPassword" = "Senha Atual"
"newUsername" = "Novo Nome de Usuário"
"newPassword" = "Nova Senha"
"telegramBotEnable" = "Ativar Bot do Telegram"
"telegramBotEnableDesc" = "Ativa o bot do Telegram."
"telegramToken" = "Token do Telegram"
"telegramTokenDesc" = "O token do bot do Telegram obtido de '@BotFather'."
"telegramProxy" = "Proxy SOCKS"
"telegramProxyDesc" = "Ativa o proxy SOCKS5 para conectar ao Telegram. (ajuste as configurações conforme o guia)"
"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)"
"telegramNotifyTime" = "Hora da Notificação"
"telegramNotifyTimeDesc" = "O horário de notificação do bot do Telegram configurado para relatórios periódicos. (use o formato de tempo do crontab)"
"tgNotifyBackup" = "Backup do Banco de Dados"
"tgNotifyBackupDesc" = "Enviar arquivo de backup do banco de dados junto com o relatório."
"tgNotifyLogin" = "Notificação de Login"
"tgNotifyLoginDesc" = "Receba notificações sobre o nome de usuário, endereço IP e horário sempre que alguém tentar fazer login no seu painel web."
"sessionMaxAge" = "Duração da Sessão"
"sessionMaxAgeDesc" = "A duração pela qual você pode permanecer logado. (unidade: minuto)"
"expireTimeDiff" = "Notificação de Expiração"
"expireTimeDiffDesc" = "Receba notificações sobre a data de expiração ao atingir esse limite. (unidade: dia)"
"trafficDiff" = "Notificação de Limite de Tráfego"
"trafficDiffDesc" = "Receba notificações sobre o limite de tráfego ao atingir esse limite. (unidade: GB)"
"tgNotifyCpu" = "Notificação de Carga da CPU"
"tgNotifyCpuDesc" = "Receba notificações se a carga da CPU ultrapassar esse limite. (unidade: %)"
"timeZone" = "Fuso Horário"
"timeZoneDesc" = "As tarefas agendadas serão executadas com base nesse fuso horário."
"subSettings" = "Assinatura"
"subEnable" = "Ativar Serviço de Assinatura"
"subEnableDesc" = "Ativa o serviço de assinatura."
"subListen" = "IP de Escuta"
"subListenDesc" = "O endereço IP para o serviço de assinatura. (deixe em branco para escutar em todos os IPs)"
"subPort" = "Porta de Escuta"
"subPortDesc" = "O número da porta para o serviço de assinatura. (deve ser uma porta não usada)"
"subCertPath" = "Caminho da Chave Pública"
"subCertPathDesc" = "O caminho do arquivo de chave pública para o serviço de assinatura. (começa com /)"
"subKeyPath" = "Caminho da Chave Privada"
"subKeyPathDesc" = "O caminho do arquivo de chave privada para o serviço de assinatura. (começa com /)"
"subPath" = "Caminho URI"
"subPathDesc" = "O caminho URI para o serviço de assinatura. (começa com / e termina com /)"
"subDomain" = "Domínio de Escuta"
"subDomainDesc" = "O nome de domínio para o serviço de assinatura. (deixe em branco para escutar em todos os domínios e IPs)"
"subUpdates" = "Intervalos de Atualização"
"subUpdatesDesc" = "Os intervalos de atualização da URL de assinatura nos aplicativos de cliente. (unidade: hora)"
"subEncrypt" = "Codificar"
"subEncryptDesc" = "O conteúdo retornado pelo serviço de assinatura será codificado em Base64."
"subShowInfo" = "Mostrar Informações de Uso"
"subShowInfoDesc" = "O tráfego restante e a data serão exibidos nos aplicativos de cliente."
"subURI" = "URI de Proxy Reverso"
"subURIDesc" = "O caminho URI da URL de assinatura para uso por trás de proxies."
"fragment" = "Fragmentação"
"fragmentDesc" = "Ativa a fragmentação para o pacote TLS hello."
"fragmentSett" = "Configurações de Fragmentação"
"noisesDesc" = "Ativar Noises."
"noisesSett" = "Configurações de Noises"
"mux" = "Mux"
"muxDesc" = "Transmitir múltiplos fluxos de dados independentes dentro de um fluxo de dados estabelecido."
"muxSett" = "Configurações de Mux"
"direct" = "Conexão Direta"
"directDesc" = "Estabelece conexões diretamente com domínios ou intervalos de IP de um país específico."
[pages.xray]
"title" = "Configurações Xray"
"save" = "Salvar"
"restart" = "Reiniciar Xray"
"basicTemplate" = "Básico"
"advancedTemplate" = "Avançado"
"generalConfigs" = "Geral"
"generalConfigsDesc" = "Essas opções determinam ajustes gerais."
"logConfigs" = "Log"
"logConfigsDesc" = "Os logs podem afetar a eficiência do servidor. É recomendável habilitá-los com sabedoria apenas se necessário."
"blockConfigs" = "Escudo de Proteção"
"blockConfigsDesc" = "Essas opções bloqueiam tráfego com base em protocolos e sites específicos solicitados."
"blockConnectionsConfigs" = "Bloquear Conexões"
"blockConnectionsConfigsDesc" = "Essas opções bloquearão o tráfego com base no país solicitado."
"directConnectionsConfigs" = "Conexões Diretas"
"directConnectionsConfigsDesc" = "Uma conexão direta garante que o tráfego específico não seja roteado por outro servidor."
"blockips" = "Bloquear IPs"
"blockdomains" = "Bloquear Domínios"
"directips" = "IPs Diretos"
"directdomains" = "Domínios Diretos"
"ipv4Configs" = "Roteamento IPv4"
"ipv4ConfigsDesc" = "Essas opções roteam o tráfego para um destino específico via IPv4."
"warpConfigs" = "Roteamento WARP"
"warpConfigsDesc" = "Essas opções roteam o tráfego para um destino específico via WARP."
"Template" = "Modelo de Configuração Avançada do Xray"
"TemplateDesc" = "O arquivo final de configuração do Xray será gerado com base neste modelo."
"FreedomStrategy" = "Estratégia do Protocolo Freedom"
"FreedomStrategyDesc" = "Definir a estratégia de saída para a rede no Protocolo Freedom."
"RoutingStrategy" = "Estratégia Geral de Roteamento"
"RoutingStrategyDesc" = "Definir a estratégia geral de roteamento de tráfego para resolver todas as solicitações."
"Torrent" = "Bloquear Protocolo BitTorrent"
"TorrentDesc" = "Bloqueia o protocolo BitTorrent."
"PrivateIp" = "Bloquear Conexão para IPs Privados"
"PrivateIpDesc" = "Bloqueia a conexão com faixas de IP privadas."
"Ads" = "Bloquear Anúncios"
"AdsDesc" = "Bloqueia sites de publicidade."
"Family" = "Proteção Familiar"
"FamilyDesc" = "Bloqueia conteúdo adulto e sites maliciosos."
"Security" = "Escudo de Segurança"
"SecurityDesc" = "Bloqueia sites de malware, phishing e mineradores de criptomoedas."
"Speedtest" = "Bloquear Speedtest"
"SpeedtestDesc" = "Bloqueia a conexão com sites de teste de velocidade."
"GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "Roteia tráfego para o Google via IPv4."
"NetflixIPv4" = "Netflix"
"NetflixIPv4Desc" = "Roteia tráfego para a Netflix via IPv4."
"GoogleWARP" = "Google"
"GoogleWARPDesc" = "Adiciona roteamento para o Google via WARP."
"OpenAIWARP" = "ChatGPT"
"OpenAIWARPDesc" = "Roteia tráfego para o ChatGPT via WARP."
"NetflixWARP" = "Netflix"
"NetflixWARPDesc" = "Roteia tráfego para a Netflix via WARP."
"MetaWARP" = "Meta"
"MetaWARPDesc" = "Roteia tráfego para Meta (Instagram, Facebook, WhatsApp, Threads,...) via WARP."
"AppleWARP" = "Apple"
"AppleWARPDesc" = "Roteia tráfego para a Apple via WARP."
"RedditWARP" = "Reddit"
"RedditWARPDesc" = "Roteia tráfego para o Reddit via WARP."
"SpotifyWARP" = "Spotify"
"SpotifyWARPDesc" = "Roteia tráfego para o Spotify via WARP."
"IRWARP" = "Domínios do Irã"
"IRWARPDesc" = "Roteia tráfego para domínios do Irã via WARP."
"Inbounds" = "Inbounds"
"InboundsDesc" = "Aceitar clientes específicos."
"Outbounds" = "Outbounds"
"Balancers" = "Balanceadores"
"OutboundsDesc" = "Definir o caminho de saída do tráfego."
"Routings" = "Regras de Roteamento"
"RoutingsDesc" = "A prioridade de cada regra é importante!"
"completeTemplate" = "Todos"
"logLevel" = "Nível de Log"
"logLevelDesc" = "O nível de log para erros, indicando a informação que precisa ser registrada."
"accessLog" = "Log de Acesso"
"accessLogDesc" = "O caminho do arquivo para o log de acesso. O valor especial 'none' desativa os logs de acesso."
"errorLog" = "Log de Erros"
"errorLogDesc" = "O caminho do arquivo para o log de erros. O valor especial 'none' desativa os logs de erro."
"dnsLog" = "Log DNS"
"dnsLogDesc" = "Se ativar logs de consulta DNS"
"maskAddress" = "Mascarar Endereço"
"maskAddressDesc" = "Máscara de endereço IP, quando ativado, substitui automaticamente o endereço IP que aparece no log."
[pages.xray.rules]
"first" = "Primeiro"
"last" = "Último"
"up" = "Cima"
"down" = "Baixo"
"source" = "Fonte"
"dest" = "Destino"
"inbound" = "Entrada"
"outbound" = "Saída"
"balancer" = "Balanceador"
"info" = "Info"
"add" = "Adicionar Regra"
"edit" = "Editar Regra"
"useComma" = "Itens separados por vírgula"
[pages.xray.outbound]
"addOutbound" = "Adicionar Saída"
"addReverse" = "Adicionar Reverso"
"editOutbound" = "Editar Saída"
"editReverse" = "Editar Reverso"
"tag" = "Tag"
"tagDesc" = "Tag Única"
"address" = "Endereço"
"reverse" = "Reverso"
"domain" = "Domínio"
"type" = "Tipo"
"bridge" = "Ponte"
"portal" = "Portal"
"intercon" = "Interconexão"
"settings" = "Configurações"
"accountInfo" = "Informações da Conta"
"outboundStatus" = "Status de Saída"
"sendThrough" = "Enviar Através de"
[pages.xray.balancer]
"addBalancer" = "Adicionar Balanceador"
"editBalancer" = "Editar Balanceador"
"balancerStrategy" = "Estratégia"
"balancerSelectors" = "Seletores"
"tag" = "Tag"
"tagDesc" = "Tag Única"
"balancerDesc" = "Não é possível usar balancerTag e outboundTag ao mesmo tempo. Se usados simultaneamente, apenas outboundTag funcionará."
[pages.xray.wireguard]
"secretKey" = "Chave Secreta"
"publicKey" = "Chave Pública"
"allowedIPs" = "IPs Permitidos"
"endpoint" = "Ponto Final"
"psk" = "Chave Pré-Compartilhada"
"domainStrategy" = "Estratégia de Domínio"
[pages.xray.dns]
"enable" = "Ativar DNS"
"enableDesc" = "Ativar o servidor DNS integrado"
"tag" = "Tag de Entrada DNS"
"tagDesc" = "Esta tag estará disponível como uma tag de Entrada nas regras de roteamento."
"strategy" = "Estratégia de Consulta"
"strategyDesc" = "Estratégia geral para resolver nomes de domínio"
"add" = "Adicionar Servidor"
"edit" = "Editar Servidor"
"domains" = "Domínios"
"expectIPs" = "IPs Esperadas"
[pages.xray.fakedns]
"add" = "Adicionar Fake DNS"
"edit" = "Editar Fake DNS"
"ipPool" = "Sub-rede do Pool de IP"
"poolSize" = "Tamanho do Pool"
[pages.settings.security]
"admin" = "Admin"
"secret" = "Token Secreto"
"loginSecurity" = "Login Seguro"
"loginSecurityDesc" = "Adiciona uma camada extra de autenticação para fornecer mais segurança."
"secretToken" = "Token Secreto"
"secretTokenDesc" = "Por favor, armazene este token em um local seguro. Este token é necessário para o login e não pode ser recuperado."
[pages.settings.toasts]
"modifySettings" = "Modificar Configurações"
"getSettings" = "Obter Configurações"
"modifyUser" = "Modificar Admin"
"originalUserPassIncorrect" = "O nome de usuário ou senha atual é inválido"
"userPassMustBeNotEmpty" = "O novo nome de usuário e senha não podem estar vazios"
[tgbot]
"keyboardClosed" = "❌ Teclado personalizado fechado!"
"noResult" = "❗ Nenhum resultado!"
"noQuery" = "❌ Consulta não encontrada! Por favor, use o comando novamente!"
"wentWrong" = "❌ Algo deu errado!"
"noIpRecord" = "❗ Nenhum registro de IP!"
"noInbounds" = "❗ Nenhuma entrada encontrada!"
"unlimited" = "♾ Ilimitado (Reiniciar)"
"add" = "Adicionar"
"month" = "Mês"
"months" = "Meses"
"day" = "Dia"
"days" = "Dias"
"hours" = "Horas"
"unknown" = "Desconhecido"
"inbounds" = "Entradas"
"clients" = "Clientes"
"offline" = "🔴 Offline"
"online" = "🟢 Online"
[tgbot.commands]
"unknown" = "❗ Comando desconhecido."
"pleaseChoose" = "👇 Escolha:\r\n"
"help" = "🤖 Bem-vindo a este bot! Ele foi projetado para oferecer dados específicos do painel da web e permite que você faça as modificações necessárias.\r\n\r\n"
"start" = "👋 Olá <i>{{ .Firstname }}</i>.\r\n"
"welcome" = "🤖 Bem-vindo ao bot de gerenciamento do <b>{{ .Hostname }}</b>.\r\n"
"status" = "✅ Bot está OK!"
"usage" = "❗ Por favor, forneça um texto para pesquisar!"
"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>"
"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>"
[tgbot.messages]
"cpuThreshold" = "🔴 A carga da CPU {{ .Percent }}% excede o limite de {{ .Threshold }}%"
"selectUserFailed" = "❌ Erro na seleção do usuário!"
"userSaved" = "✅ Usuário do Telegram salvo."
"loginSuccess" = "✅ Conectado ao painel com sucesso.\r\n"
"loginFailed" = "❗Tentativa de login no painel falhou.\r\n"
"report" = "🕰 Relatórios agendados: {{ .RunTime }}\r\n"
"datetime" = "⏰ Data&Hora: {{ .DateTime }}\r\n"
"hostname" = "💻 Host: {{ .Hostname }}\r\n"
"version" = "🚀 Versão 3X-UI: {{ .Version }}\r\n"
"xrayVersion" = "📡 Versão Xray: {{ .XrayVersion }}\r\n"
"ipv6" = "🌐 IPv6: {{ .IPv6 }}\r\n"
"ipv4" = "🌐 IPv4: {{ .IPv4 }}\r\n"
"ip" = "🌐 IP: {{ .IP }}\r\n"
"ips" = "🔢 IPs:\r\n{{ .IPs }}\r\n"
"serverUpTime" = "⏳ Tempo de atividade: {{ .UpTime }} {{ .Unit }}\r\n"
"serverLoad" = "📈 Carga do sistema: {{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
"serverMemory" = "📋 RAM: {{ .Current }}/{{ .Total }}\r\n"
"tcpCount" = "🔹 TCP: {{ .Count }}\r\n"
"udpCount" = "🔸 UDP: {{ .Count }}\r\n"
"traffic" = "🚦 Tráfego: {{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
"xrayStatus" = " Status: {{ .State }}\r\n"
"username" = "👤 Nome de usuário: {{ .Username }}\r\n"
"password" = "👤 Senha: {{ .Password }}\r\n"
"time" = "⏰ Hora: {{ .Time }}\r\n"
"inbound" = "📍 Inbound: {{ .Remark }}\r\n"
"port" = "🔌 Porta: {{ .Port }}\r\n"
"expire" = "📅 Data de expiração: {{ .Time }}\r\n"
"expireIn" = "📅 Expira em: {{ .Time }}\r\n"
"active" = "💡 Ativo: {{ .Enable }}\r\n"
"enabled" = "🚨 Ativado: {{ .Enable }}\r\n"
"online" = "🌐 Status da conexão: {{ .Status }}\r\n"
"email" = "📧 Email: {{ .Email }}\r\n"
"upload" = "🔼 Upload: ↑{{ .Upload }}\r\n"
"download" = "🔽 Download: ↓{{ .Download }}\r\n"
"total" = "📊 Total: ↑↓{{ .UpDown }} / {{ .Total }}\r\n"
"TGUser" = "👤 Usuário do Telegram: {{ .TelegramID }}\r\n"
"exhaustedMsg" = "🚨 {{ .Type }} esgotado:\r\n"
"exhaustedCount" = "🚨 Contagem de {{ .Type }} esgotado:\r\n"
"onlinesCount" = "🌐 Clientes online: {{ .Count }}\r\n"
"disabled" = "🛑 Desativado: {{ .Disabled }}\r\n"
"depleteSoon" = "🔜 Esgotar em breve: {{ .Deplete }}\r\n\r\n"
"backupTime" = "🗄 Hora do backup: {{ .Time }}\r\n"
"refreshedOn" = "\r\n📋🔄 Atualizado em: {{ .Time }}\r\n\r\n"
"yes" = "✅ Sim"
"no" = "❌ Não"
[tgbot.buttons]
"closeKeyboard" = "❌ Fechar teclado"
"cancel" = "❌ Cancelar"
"cancelReset" = "❌ Cancelar redefinição"
"cancelIpLimit" = "❌ Cancelar limite de IP"
"confirmResetTraffic" = "✅ Confirmar redefinição de tráfego?"
"confirmClearIps" = "✅ Confirmar limpar IPs?"
"confirmRemoveTGUser" = "✅ Confirmar remover usuário do Telegram?"
"confirmToggle" = "✅ Confirmar ativar/desativar usuário?"
"dbBackup" = "Obter backup do DB"
"serverUsage" = "Uso do servidor"
"getInbounds" = "Obter Inbounds"
"depleteSoon" = "Esgotar em breve"
"clientUsage" = "Obter uso"
"onlines" = "Clientes online"
"commands" = "Comandos"
"refresh" = "🔄 Atualizar"
"clearIPs" = "❌ Limpar IPs"
"removeTGUser" = "❌ Remover usuário do Telegram"
"selectTGUser" = "👤 Selecionar usuário do Telegram"
"selectOneTGUser" = "👤 Selecione um usuário do Telegram:"
"resetTraffic" = "📈 Redefinir tráfego"
"resetExpire" = "📅 Alterar data de expiração"
"ipLog" = "🔢 Log de IP"
"ipLimit" = "🔢 Limite de IP"
"setTGUser" = "👤 Definir usuário do Telegram"
"toggle" = "🔘 Ativar / Desativar"
"custom" = "🔢 Personalizado"
"confirmNumber" = "✅ Confirmar: {{ .Num }}"
"confirmNumberAdd" = "✅ Confirmar adicionar: {{ .Num }}"
"limitTraffic" = "🚧 Limite de tráfego"
"getBanLogs" = "Obter logs de banimento"
"allClients" = "Todos os clientes"
[tgbot.answers]
"successfulOperation" = "✅ Operação bem-sucedida!"
"errorOperation" = "❗ Erro na operação."
"getInboundsFailed" = "❌ Falha ao obter inbounds."
"getClientsFailed" = "❌ Falha ao obter clientes."
"canceled" = "❌ {{ .Email }}: Operação cancelada."
"clientRefreshSuccess" = "✅ {{ .Email }}: Cliente atualizado com sucesso."
"IpRefreshSuccess" = "✅ {{ .Email }}: IPs atualizados com sucesso."
"TGIdRefreshSuccess" = "✅ {{ .Email }}: Usuário do Telegram do cliente atualizado com sucesso."
"resetTrafficSuccess" = "✅ {{ .Email }}: Tráfego redefinido com sucesso."
"setTrafficLimitSuccess" = "✅ {{ .Email }}: Limite de tráfego salvo com sucesso."
"expireResetSuccess" = "✅ {{ .Email }}: Dias de expiração redefinidos com sucesso."
"resetIpSuccess" = "✅ {{ .Email }}: Limite de IP {{ .Count }} salvo com sucesso."
"clearIpSuccess" = "✅ {{ .Email }}: IPs limpos com sucesso."
"getIpLog" = "✅ {{ .Email }}: Obter log de IP."
"getUserInfo" = "✅ {{ .Email }}: Obter informações do usuário do Telegram."
"removedTGUserSuccess" = "✅ {{ .Email }}: Usuário do Telegram removido com sucesso."
"enableSuccess" = "✅ {{ .Email }}: Ativado com sucesso."
"disableSuccess" = "✅ {{ .Email }}: Desativado com sucesso."
"askToAddUserId" = "Sua configuração não foi encontrada!\r\nPeça ao seu administrador para usar seu Telegram ChatID em suas configurações.\r\n\r\nSeu ChatID: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Escolha um cliente para Inbound {{ .Inbound }}"
"chooseInbound" = "Escolha um Inbound"

View File

@@ -143,7 +143,7 @@
"destinationPort" = "Порт назначения" "destinationPort" = "Порт назначения"
"targetAddress" = "Целевой адрес" "targetAddress" = "Целевой адрес"
"monitorDesc" = "Оставьте пустым по умолчанию" "monitorDesc" = "Оставьте пустым по умолчанию"
"meansNoLimit" = " = Без ограничений (значение: ГБ)" "meansNoLimit" = "= Без ограничений (значение: ГБ)"
"totalFlow" = "Общий расход" "totalFlow" = "Общий расход"
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало" "leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
"noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию" "noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию"
@@ -225,9 +225,6 @@
"requestHeader" = "Заголовок запроса" "requestHeader" = "Заголовок запроса"
"responseHeader" = "Заголовок ответа" "responseHeader" = "Заголовок ответа"
[pages.inbounds.stream.quic]
"encryption" = "Шифрование"
[pages.settings] [pages.settings]
"title" = "Настройки" "title" = "Настройки"
"save" = "Сохранить" "save" = "Сохранить"
@@ -268,7 +265,7 @@
"telegramTokenDesc" = "Необходимо получить токен у менеджера ботов Telegram @botfather" "telegramTokenDesc" = "Необходимо получить токен у менеджера ботов Telegram @botfather"
"telegramProxy" = "Прокси Socks5" "telegramProxy" = "Прокси Socks5"
"telegramProxyDesc" = "Если для подключения к Telegram вам нужен прокси Socks5. Настройте его параметры согласно руководству." "telegramProxyDesc" = "Если для подключения к Telegram вам нужен прокси Socks5. Настройте его параметры согласно руководству."
"telegramChatId" = "Telegram ID админа бота" "telegramChatId" = "Telegram ChatID админа бота"
"telegramChatIdDesc" = "Множественные идентификаторы чата, разделенные запятыми. Чтобы получить свои идентификаторы чатов, используйте @userinfobot или команду '/id' в боте." "telegramChatIdDesc" = "Множественные идентификаторы чата, разделенные запятыми. Чтобы получить свои идентификаторы чатов, используйте @userinfobot или команду '/id' в боте."
"telegramNotifyTime" = "Частота уведомлений бота Telegram" "telegramNotifyTime" = "Частота уведомлений бота Telegram"
"telegramNotifyTimeDesc" = "Используйте формат времени Crontab" "telegramNotifyTimeDesc" = "Используйте формат времени Crontab"
@@ -312,12 +309,14 @@
"fragment" = "Фрагментация" "fragment" = "Фрагментация"
"fragmentDesc" = "Включить фрагментацию для пакета приветствия TLS" "fragmentDesc" = "Включить фрагментацию для пакета приветствия TLS"
"fragmentSett" = "Настройки фрагментации" "fragmentSett" = "Настройки фрагментации"
"noisesDesc" = "Включить Noises."
"noisesSett" = "Настройки Noises"
"mux" = "Mux" "mux" = "Mux"
"muxDesc" = "Передача нескольких независимых потоков данных в рамках установленного потока данных." "muxDesc" = "Передача нескольких независимых потоков данных в рамках установленного потока данных."
"muxSett" = "Mux Настройки" "muxSett" = "Mux Настройки"
"direct" = "Прямая связь" "direct" = "Прямая связь"
"directDesc" = "Напрямую устанавливает соединения с доменами или диапазонами IP конкретной страны." "directDesc" = "Напрямую устанавливает соединения с доменами или диапазонами IP конкретной страны."
"directSett" = "Варианты прямого подключения"
[pages.xray] [pages.xray]
"title" = "Настройки Xray" "title" = "Настройки Xray"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Журналы могут повлиять на эффективность вашего сервера. Рекомендуется включать их с умом только в случае ваших нужд!" "logConfigsDesc" = "Журналы могут повлиять на эффективность вашего сервера. Рекомендуется включать их с умом только в случае ваших нужд!"
"blockConfigs" = "Блокировка конфигураций" "blockConfigs" = "Блокировка конфигураций"
"blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам" "blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам"
"blockCountryConfigs" = "Конфигурации блокировки страны" "blockConnectionsConfigs" = "Блокировать соединения"
"blockCountryConfigsDesc" = "Эти параметры не позволят пользователям подключаться к доменам определенной страны" "blockConnectionsConfigsDesc" = "Эти параметры будут блокировать трафик в зависимости от запрашиваемой страны."
"directCountryConfigs" = "Настройки прямого подключения для страны" "directConnectionsConfigs" = "Прямые соединения"
"directCountryConfigsDesc" = "Прямое подключение обеспечивает, что конкретный трафик не направляется через другой сервер." "directConnectionsConfigsDesc" = "Прямое соединение гарантирует, что определенный трафик не будет перенаправлен через другой сервер."
"blockips" = "Блокировать IP"
"blockdomains" = "Блокировать домены"
"directips" = "Прямые IP"
"directdomains" = "Прямые домены"
"ipv4Configs" = "Настройки IPv4" "ipv4Configs" = "Настройки IPv4"
"ipv4ConfigsDesc" = "Эти параметры позволят пользователям маршрутизироваться к целевым доменам только через IPv4" "ipv4ConfigsDesc" = "Эти параметры позволят пользователям маршрутизироваться к целевым доменам только через IPv4"
"warpConfigs" = "Настройки WARP" "warpConfigs" = "Настройки WARP"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Изменение шаблона конфигурации для защиты безопасности." "SecurityDesc" = "Изменение шаблона конфигурации для защиты безопасности."
"Speedtest" = "Блокировать сайты для проверки скорости" "Speedtest" = "Блокировать сайты для проверки скорости"
"SpeedtestDesc" = "Изменение шаблона конфигурации для предупреждения подключения к веб-сайтам для тестирования скорости" "SpeedtestDesc" = "Изменение шаблона конфигурации для предупреждения подключения к веб-сайтам для тестирования скорости"
"IRIp" = "Заблокировать подключения к диапазонам IP-адресов Ирана"
"IRIpDesc" = "Изменение конфигурации, чтобы заблокировать подключения к диапазонам IP-адресов Ирана"
"IRDomain" = "Заблокировать подключения к доменам Ирана"
"IRDomainDesc" = "Изменение конфигурации, чтобы заблокировать подключения к доменам Ирана"
"ChinaIp" = "Заблокировать подключения к диапазонам IP-адресов Китая"
"ChinaIpDesc" = "Изменение конфигурации, чтобы заблокировать подключения к диапазонам IP-адресов Китая"
"ChinaDomain" = "Заблокировать подключения к доменам Китая"
"ChinaDomainDesc" = "Изменение конфигурации, чтобы заблокировать подключения к доменам Китая"
"RussiaIp" = "Заблокировать подключения к диапазонам IP-адресов России"
"RussiaIpDesc" = "Изменение конфигурации, чтобы заблокировать подключения к диапазонами IP-адресов России"
"RussiaDomain" = "Заблокировать подключения к доменам России"
"RussiaDomainDesc" = "Изменение конфигурации, чтобы заблокировать подключения к доменам России"
"VNIp" = "Отключить подключение к IP-адресам Вьетнама"
"VNIpDesc" = "Измените шаблон конфигурации, чтобы избежать подключения к диапазонам IP-адресов Вьетнама"
"VNDomain" = "Отключить подключение к доменам Вьетнама"
"VNDomainDesc" = "Измените шаблон конфигурации, чтобы избежать подключения к доменам Вьетнама."
"DirectIRIp" = "Прямое подключения к диапазонам IP-адресов Ирана"
"DirectIRIpDesc" = "Изменение шаблона конфигурации для прямого подключения к диапазонам IP-адресов Ирана"
"DirectIRDomain" = "Прямое подключение к доменам Ирана"
"DirectIRDomainDesc" = "Изменение шаблон конфигурации для прямого подключения к доменам Ирана"
"DirectChinaIp" = "Прямое подключение к диапазонам IP-адресов Китая"
"DirectChinaIpDesc" = "Изменение шаблона конфигурации для прямого подключения к диапазонам IP-адресов Китая"
"DirectChinaDomain" = "Прямое подключение к доменам Китая"
"DirectChinaDomainDesc" = "Изменение шаблона конфигурации для прямого подключения к доменам Китая"
"DirectRussiaIp" = "Прямое подключение к диапазонам IP-адресов России"
"DirectRussiaIpDesc" = "Изменение шаблона конфигурации для прямого подключения к диапазонам IP-адресов России"
"DirectRussiaDomain" = "Прямое подключение к доменам России"
"DirectRussiaDomainDesc" = "Изменение шаблона конфигурации для прямого подключения к доменам России"
"DirectVNIp" = "Прямое подключение к IP-адресам Вьетнама"
"DirectVNIpDesc" = "Измените шаблон конфигурации для прямого подключения к диапазонам IP-адресов Вьетнама"
"DirectVNDomain" = "Прямое подключение к доменам Вьетнама"
"DirectVNDomainDesc" = "Измените шаблон конфигурации для прямого подключения к доменам Вьетнама"
"GoogleIPv4" = "Использовать IPv4 для Google" "GoogleIPv4" = "Использовать IPv4 для Google"
"GoogleIPv4Desc" = "Добавить маршрутизацию для Google для подключения к IPv4" "GoogleIPv4Desc" = "Добавить маршрутизацию для Google для подключения к IPv4"
"NetflixIPv4" = "Использовать IPv4 для Netflix" "NetflixIPv4" = "Использовать IPv4 для Netflix"
@@ -398,7 +369,7 @@
"OpenAIWARP" = "OpenAI (ChatGPT)" "OpenAIWARP" = "OpenAI (ChatGPT)"
"OpenAIWARPDesc" = "Направляет трафик в OpenAI (ChatGPT) через WARP." "OpenAIWARPDesc" = "Направляет трафик в OpenAI (ChatGPT) через WARP."
"NetflixWARP" = "Netflix" "NetflixWARP" = "Netflix"
"NetflixWARPDesc" = "Направляет трафик в Apple через WARP." "NetflixWARPDesc" = "Направляет трафик в Netflix через WARP."
"MetaWARP" = "Мета" "MetaWARP" = "Мета"
"MetaWARPDesc" = "Направляет трафик в Meta (Instagram, Facebook, WhatsApp, Threads...) через WARP." "MetaWARPDesc" = "Направляет трафик в Meta (Instagram, Facebook, WhatsApp, Threads...) через WARP."
"AppleWARP" = "Apple" "AppleWARP" = "Apple"
@@ -423,6 +394,10 @@
"accessLogDesc" = "Путь к файлу журнала доступа. Специальное значение «none» отключило журналы доступа." "accessLogDesc" = "Путь к файлу журнала доступа. Специальное значение «none» отключило журналы доступа."
"errorLog" = "Журнал ошибок" "errorLog" = "Журнал ошибок"
"errorLogDesc" = "Путь к файлу журнала ошибок. Специальное значение «none» отключает журналы ошибок." "errorLogDesc" = "Путь к файлу журнала ошибок. Специальное значение «none» отключает журналы ошибок."
"dnsLog" = "DNS Журнал"
"dnsLogDesc" = "Включить логи запросов DNS"
"maskAddress" = "Маскировать Адрес"
"maskAddressDesc" = "Маска IP-адреса, при активации автоматически заменяет IP-адрес, который появляется в логе."
[pages.xray.rules] [pages.xray.rules]
"first" = "Первый" "first" = "Первый"
@@ -485,6 +460,7 @@
"add" = "Добавить сервер" "add" = "Добавить сервер"
"edit" = "Редактировать сервер" "edit" = "Редактировать сервер"
"domains" = "Домены" "domains" = "Домены"
"expectIPs" = "Ожидаемые IP"
[pages.xray.fakedns] [pages.xray.fakedns]
"add" = "Добавить поддельный DNS" "add" = "Добавить поддельный DNS"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Подтвердить добавление: {{ .Num }}" "confirmNumberAdd" = "✅ Подтвердить добавление: {{ .Num }}"
"limitTraffic" = "🚧 Лимит трафика" "limitTraffic" = "🚧 Лимит трафика"
"getBanLogs" = "Логи блокировок" "getBanLogs" = "Логи блокировок"
"allClients" = "Все клиенты"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ Успешный!" "successfulOperation" = "✅ Успешный!"
"errorOperation" = "❗ Ошибка в операции." "errorOperation" = "❗ Ошибка в операции."
"getInboundsFailed" = "❌ Не удалось получить входящие потоки." "getInboundsFailed" = "❌ Не удалось получить входящие потоки."
"getClientsFailed" = "❌ Не удалось получить клиентов."
"canceled" = "❌ {{ .Email }}: Операция отменена." "canceled" = "❌ {{ .Email }}: Операция отменена."
"clientRefreshSuccess" = "✅ {{ .Email }}: Клиент успешно обновлен." "clientRefreshSuccess" = "✅ {{ .Email }}: Клиент успешно обновлен."
"IpRefreshSuccess" = "✅ {{ .Email }}: IP-адреса успешно обновлены." "IpRefreshSuccess" = "✅ {{ .Email }}: IP-адреса успешно обновлены."
@@ -638,3 +616,5 @@
"enableSuccess" = "✅ {{ .Email }}: Включено успешно." "enableSuccess" = "✅ {{ .Email }}: Включено успешно."
"disableSuccess" = "✅ {{ .Email }}: Отключено успешно." "disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <code>{{ .TgUserID }}</code>" "askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Выберите пользователя для подключения {{ .Inbound }}"
"chooseInbound" = "Выберите подключение"

View File

@@ -143,7 +143,7 @@
"destinationPort" = "Hedef Port" "destinationPort" = "Hedef Port"
"targetAddress" = "Hedef Adres" "targetAddress" = "Hedef Adres"
"monitorDesc" = "Tüm IP'leri dinlemek için boş bırakın" "monitorDesc" = "Tüm IP'leri dinlemek için boş bırakın"
"meansNoLimit" = " = Sınırsız. (birim: GB)" "meansNoLimit" = "= Sınırsız. (birim: GB)"
"totalFlow" = "Toplam Akış" "totalFlow" = "Toplam Akış"
"leaveBlankToNeverExpire" = "Hiçbir zaman sona ermemesi için boş bırakın" "leaveBlankToNeverExpire" = "Hiçbir zaman sona ermemesi için boş bırakın"
"noRecommendKeepDefault" = "Varsayılanı korumanız önerilir" "noRecommendKeepDefault" = "Varsayılanı korumanız önerilir"
@@ -225,9 +225,6 @@
"requestHeader" = "İstek Başlığı" "requestHeader" = "İstek Başlığı"
"responseHeader" = "Yanıt Başlığı" "responseHeader" = "Yanıt Başlığı"
[pages.inbounds.stream.quic]
"encryption" = "Şifreleme"
[pages.settings] [pages.settings]
"title" = "Panel Ayarları" "title" = "Panel Ayarları"
"save" = "Kaydet" "save" = "Kaydet"
@@ -312,12 +309,14 @@
"fragment" = "Parçalama" "fragment" = "Parçalama"
"fragmentDesc" = "TLS merhaba paketinin parçalanmasını etkinleştir." "fragmentDesc" = "TLS merhaba paketinin parçalanmasını etkinleştir."
"fragmentSett" = "Parçalama Ayarları" "fragmentSett" = "Parçalama Ayarları"
"noisesDesc" = "Noises'i Etkinleştir."
"noisesSett" = "Noises Ayarları"
"mux" = "Mux" "mux" = "Mux"
"muxDesc" = "Kurulmuş bir veri akışında birden çok bağımsız veri akışını iletir." "muxDesc" = "Kurulmuş bir veri akışında birden çok bağımsız veri akışını iletir."
"muxSett" = "Mux Ayarları" "muxSett" = "Mux Ayarları"
"direct" = "Doğrudan Bağlantı" "direct" = "Doğrudan Bağlantı"
"directDesc" = "Belirli bir ülkenin alan adları veya IP aralıkları ile doğrudan bağlantı kurar." "directDesc" = "Belirli bir ülkenin alan adları veya IP aralıkları ile doğrudan bağlantı kurar."
"directSett" = "Doğrudan Bağlantı Seçenekleri"
[pages.xray] [pages.xray]
"title" = "Xray Yapılandırmaları" "title" = "Xray Yapılandırmaları"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Günlükler sunucunuzun verimliliğini etkileyebilir. Yalnızca ihtiyaç durumunda akıllıca etkinleştirmeniz önerilir" "logConfigsDesc" = "Günlükler sunucunuzun verimliliğini etkileyebilir. Yalnızca ihtiyaç durumunda akıllıca etkinleştirmeniz önerilir"
"blockConfigs" = "Koruma Kalkanı" "blockConfigs" = "Koruma Kalkanı"
"blockConfigsDesc" = "Bu seçenekler belirli istek protokolleri ve web siteleri temelinde trafiği engeller." "blockConfigsDesc" = "Bu seçenekler belirli istek protokolleri ve web siteleri temelinde trafiği engeller."
"blockCountryConfigs" = "Ülke Engelleme" "blockConnectionsConfigs" = "Bağlantıları Engelle"
"blockCountryConfigsDesc" = "Bu seçenekler belirli istek ülkesi temelinde trafiği engeller." "blockConnectionsConfigsDesc" = "Bu seçenekler belirli bir istenen ülkeye göre trafiği engelleyecektir."
"directCountryConfigs" = "Doğrudan Ülke" "directConnectionsConfigs" = "Doğrudan Bağlantılar"
"directCountryConfigsDesc" = "Doğrudan bağlantı, belirli trafiğin başka bir sunucu üzerinden yönlendirilmemesini sağlar." "directConnectionsConfigsDesc" = "Doğrudan bağlantı, belirli bir trafiğin başka bir sunucu üzerinden yönlendirilmediğini sağlar."
"blockips" = "IP'leri Engelle"
"blockdomains" = "Alan Adlarını Engelle"
"directips" = "Doğrudan IP'ler"
"directdomains" = "Doğrudan Alan Adları"
"ipv4Configs" = "IPv4 Yönlendirme" "ipv4Configs" = "IPv4 Yönlendirme"
"ipv4ConfigsDesc" = "Bu seçenekler belirli bir varış yerine IPv4 üzerinden trafiği yönlendirir." "ipv4ConfigsDesc" = "Bu seçenekler belirli bir varış yerine IPv4 üzerinden trafiği yönlendirir."
"warpConfigs" = "WARP Yönlendirme" "warpConfigs" = "WARP Yönlendirme"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Kötü amaçlı yazılım, kimlik avı ve kripto madencilik web sitelerini engeller." "SecurityDesc" = "Kötü amaçlı yazılım, kimlik avı ve kripto madencilik web sitelerini engeller."
"Speedtest" = "Speedtest Bağlantısını Engelle" "Speedtest" = "Speedtest Bağlantısını Engelle"
"SpeedtestDesc" = "Speedtest web sitelerine bağlantı kurmayı engeller." "SpeedtestDesc" = "Speedtest web sitelerine bağlantı kurmayı engeller."
"IRIp" = "İran IP'lerine Bağlantıyı Engelle"
"IRIpDesc" = "İran IP aralıklarına bağlantı kurmayı engeller."
"IRDomain" = "İran Alan Adlarına Bağlantıyı Engelle"
"IRDomainDesc" = "İran alan adlarına bağlantı kurmayı engeller."
"ChinaIp" = "Çin IP'lerine Bağlantıyı Engelle"
"ChinaIpDesc" = "Çin IP aralıklarına bağlantı kurmayı engeller."
"ChinaDomain" = "Çin Alan Adlarına Bağlantıyı Engelle"
"ChinaDomainDesc" = "Çin alan adlarına bağlantı kurmayı engeller."
"RussiaIp" = "Rusya IP'lerine Bağlantıyı Engelle"
"RussiaIpDesc" = "Rusya IP aralıklarına bağlantı kurmayı engeller."
"RussiaDomain" = "Rusya Alan Adlarına Bağlantıyı Engelle"
"RussiaDomainDesc" = "Rusya alan adlarına bağlantı kurmayı engeller."
"VNIp" = "Vietnam IP'lerine Bağlantıyı Engelle"
"VNIpDesc" = "Vietnam IP aralıklarına bağlantı kurmayı engeller."
"VNDomain" = "Vietnam Alan Adlarına Bağlantıyı Engelle"
"VNDomainDesc" = "Vietnam alan adlarına bağlantı kurmayı engeller."
"DirectIRIp" = "İran IP'lerine Doğrudan Bağlantı"
"DirectIRIpDesc" = "İran IP aralıklarına doğrudan bağlantı kurar."
"DirectIRDomain" = "İran Alan Adlarına Doğrudan Bağlantı"
"DirectIRDomainDesc" = "İran alan adlarına doğrudan bağlantı kurar."
"DirectChinaIp" = "Çin IP'lerine Doğrudan Bağlantı"
"DirectChinaIpDesc" = "Çin IP aralıklarına doğrudan bağlantı kurar."
"DirectChinaDomain" = "Çin Alan Adlarına Doğrudan Bağlantı"
"DirectChinaDomainDesc" = "Çin alan adlarına doğrudan bağlantı kurar."
"DirectRussiaIp" = "Rusya IP'lerine Doğrudan Bağlantı"
"DirectRussiaIpDesc" = "Rusya IP aralıklarına doğrudan bağlantı kurar."
"DirectRussiaDomain" = "Rusya Alan Adlarına Doğrudan Bağlantı"
"DirectRussiaDomainDesc" = "Rusya alan adlarına doğrudan bağlantı kurar."
"DirectVNIp" = "Vietnam IP'lerine Doğrudan Bağlantı"
"DirectVNIpDesc" = "Vietnam IP aralıklarına doğrudan bağlantı kurar."
"DirectVNDomain" = "Vietnam Alan Adlarına Doğrudan Bağlantı"
"DirectVNDomainDesc" = "Vietnam alan adlarına doğrudan bağlantı kurar."
"GoogleIPv4" = "Google" "GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "Google'a trafiği IPv4 üzerinden yönlendirir." "GoogleIPv4Desc" = "Google'a trafiği IPv4 üzerinden yönlendirir."
"NetflixIPv4" = "Netflix" "NetflixIPv4" = "Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "Erişim günlüğü için dosya yolu. 'none' özel değeri erişim günlüklerini devre dışı bırakır" "accessLogDesc" = "Erişim günlüğü için dosya yolu. 'none' özel değeri erişim günlüklerini devre dışı bırakır"
"errorLog" = "Hata Günlüğü" "errorLog" = "Hata Günlüğü"
"errorLogDesc" = "Hata günlüğü için dosya yolu. 'none' özel değeri hata günlüklerini devre dışı bırakır" "errorLogDesc" = "Hata günlüğü için dosya yolu. 'none' özel değeri hata günlüklerini devre dışı bırakır"
"dnsLog" = "DNS Günlüğü"
"dnsLogDesc" = "DNS sorgu günlüklerini etkinleştirin"
"maskAddress" = "Adres Maskesi"
"maskAddressDesc" = "IP adresi maskesi, etkinleştirildiğinde, günlükte görünen IP adresini otomatik olarak değiştirecektir."
[pages.xray.rules] [pages.xray.rules]
"first" = "İlk" "first" = "İlk"
@@ -485,6 +460,7 @@
"add" = "Sunucu Ekle" "add" = "Sunucu Ekle"
"edit" = "Sunucuyu Düzenle" "edit" = "Sunucuyu Düzenle"
"domains" = "Alan Adları" "domains" = "Alan Adları"
"expectIPs" = "Beklenen IP'ler"
[pages.xray.fakedns] [pages.xray.fakedns]
"add" = "Sahte DNS Ekle" "add" = "Sahte DNS Ekle"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Ekleme onayı: {{ .Num }}" "confirmNumberAdd" = "✅ Ekleme onayı: {{ .Num }}"
"limitTraffic" = "🚧 Trafik Sınırı" "limitTraffic" = "🚧 Trafik Sınırı"
"getBanLogs" = "Yasak Günlüklerini Al" "getBanLogs" = "Yasak Günlüklerini Al"
"allClients" = "Tüm Müşteriler"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ İşlem başarılı!" "successfulOperation" = "✅ İşlem başarılı!"
"errorOperation" = "❗ İşlemde hata." "errorOperation" = "❗ İşlemde hata."
"getInboundsFailed" = "❌ Gelenler alınamadı." "getInboundsFailed" = "❌ Gelenler alınamadı."
"getClientsFailed" = "❌ Müşteriler alınamadı."
"canceled" = "❌ {{ .Email }}: İşlem iptal edildi." "canceled" = "❌ {{ .Email }}: İşlem iptal edildi."
"clientRefreshSuccess" = "✅ {{ .Email }}: Müşteri başarıyla yenilendi." "clientRefreshSuccess" = "✅ {{ .Email }}: Müşteri başarıyla yenilendi."
"IpRefreshSuccess" = "✅ {{ .Email }}: IP'ler başarıyla yenilendi." "IpRefreshSuccess" = "✅ {{ .Email }}: IP'ler başarıyla yenilendi."
@@ -637,4 +615,6 @@
"removedTGUserSuccess" = "✅ {{ .Email }}: Telegram Kullanıcısı başarıyla kaldırıldı." "removedTGUserSuccess" = "✅ {{ .Email }}: Telegram Kullanıcısı başarıyla kaldırıldı."
"enableSuccess" = "✅ {{ .Email }}: Başarıyla etkinleştirildi." "enableSuccess" = "✅ {{ .Email }}: Başarıyla etkinleştirildi."
"disableSuccess" = "✅ {{ .Email }}: Başarıyla devre dışı bırakıldı." "disableSuccess" = "✅ {{ .Email }}: Başarıyla devre dışı bırakıldı."
"askToAddUserId" = "Yapılandırmanız bulunamadı!\r\nLütfen yöneticinizden yapılandırmalarınıza Telegram ID'nizi eklemesini isteyin.\r\n\r\nKullanıcı ID'niz: <code>{{ .TgUserID }}</code>" "askToAddUserId" = "Yapılandırmanız bulunamadı!\r\nLütfen yöneticinizden yapılandırmalarınıza Telegram ChatID'nizi eklemesini isteyin.\r\n\r\nKullanıcı ChatID'niz: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Gelen {{ .Inbound }} için bir Müşteri Seçin"
"chooseInbound" = "Bir Gelen Seçin"

View File

@@ -43,8 +43,8 @@
"domainName" = "Доменне ім`я" "domainName" = "Доменне ім`я"
"monitor" = "Слухати IP" "monitor" = "Слухати IP"
"certificate" = "Цифровий сертифікат" "certificate" = "Цифровий сертифікат"
"fail" = " Помилка" "fail" = "Помилка"
"success" = " Успішно" "success" = "Успішно"
"getVersion" = "Отримати версію" "getVersion" = "Отримати версію"
"install" = "Встановити" "install" = "Встановити"
"clients" = "Клієнти" "clients" = "Клієнти"
@@ -143,7 +143,7 @@
"destinationPort" = "Порт призначення" "destinationPort" = "Порт призначення"
"targetAddress" = "Цільова адреса" "targetAddress" = "Цільова адреса"
"monitorDesc" = "Залиште порожнім, щоб слухати всі IP-адреси" "monitorDesc" = "Залиште порожнім, щоб слухати всі IP-адреси"
"meansNoLimit" = " = Необмежено. (одиниця: ГБ)" "meansNoLimit" = "= Необмежено. (одиниця: ГБ)"
"totalFlow" = "Загальна витрата" "totalFlow" = "Загальна витрата"
"leaveBlankToNeverExpire" = "Залиште порожнім, щоб ніколи не закінчувався" "leaveBlankToNeverExpire" = "Залиште порожнім, щоб ніколи не закінчувався"
"noRecommendKeepDefault" = "Рекомендується зберегти значення за замовчуванням" "noRecommendKeepDefault" = "Рекомендується зберегти значення за замовчуванням"
@@ -225,9 +225,6 @@
"requestHeader" = "Заголовок запиту" "requestHeader" = "Заголовок запиту"
"responseHeader" = "Заголовок відповіді" "responseHeader" = "Заголовок відповіді"
[pages.inbounds.stream.quic]
"encryption" = "Шифрування"
[pages.settings] [pages.settings]
"title" = "Параметри панелі" "title" = "Параметри панелі"
"save" = "Зберегти" "save" = "Зберегти"
@@ -312,12 +309,14 @@
"fragment" = "Фрагментація" "fragment" = "Фрагментація"
"fragmentDesc" = "Увімкнути фрагментацію для пакету привітання TLS" "fragmentDesc" = "Увімкнути фрагментацію для пакету привітання TLS"
"fragmentSett" = "Параметри фрагментації" "fragmentSett" = "Параметри фрагментації"
"noisesDesc" = "Увімкнути Noises."
"noisesSett" = "Налаштування Noises"
"mux" = "Mux" "mux" = "Mux"
"muxDesc" = "Передавати кілька незалежних потоків даних у межах встановленого потоку даних." "muxDesc" = "Передавати кілька незалежних потоків даних у межах встановленого потоку даних."
"muxSett" = "Налаштування Mux" "muxSett" = "Налаштування Mux"
"direct" = "Пряме підключення" "direct" = "Пряме підключення"
"directDesc" = "Безпосередньо встановлює з’єднання з доменами або діапазонами IP певної країни." "directDesc" = "Безпосередньо встановлює з’єднання з доменами або діапазонами IP певної країни."
"directSett" = "Параметри прямого підключення"
[pages.xray] [pages.xray]
"title" = "Xray конфігурації" "title" = "Xray конфігурації"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Журнали можуть вплинути на ефективність вашого сервера. Рекомендується вмикати його з розумом лише у випадку ваших потреб" "logConfigsDesc" = "Журнали можуть вплинути на ефективність вашого сервера. Рекомендується вмикати його з розумом лише у випадку ваших потреб"
"blockConfigs" = "Захисний екран" "blockConfigs" = "Захисний екран"
"blockConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретних запитуваних протоколів і веб-сайтів." "blockConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретних запитуваних протоколів і веб-сайтів."
"blockCountryConfigs" = "Заблокувати країну" "blockConnectionsConfigs" = "Блокувати з'єднання"
"blockCountryConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретної запитуваної країни." "blockConnectionsConfigsDesc" = "Ці параметри блокуватимуть трафік на основі запитаних країн."
"directCountryConfigs" = "Пряма країна" "directConnectionsConfigs" = "Прямі з'єднання"
"directCountryConfigsDesc" = "Пряме підключення забезпечує, що конкретний трафік не маршрутизується через інший сервер." "directConnectionsConfigsDesc" = "Пряме з'єднання гарантує, що певний трафік не буде маршрутизовано через інший сервер."
"blockips" = "Блокувати IP"
"blockdomains" = "Блокувати домени"
"directips" = "Прямі IP"
"directdomains" = "Прямі домени"
"ipv4Configs" = "Маршрутизація IPv4" "ipv4Configs" = "Маршрутизація IPv4"
"ipv4ConfigsDesc" = "Ці параметри спрямовуватимуть трафік на основі певного призначення через IPv4." "ipv4ConfigsDesc" = "Ці параметри спрямовуватимуть трафік на основі певного призначення через IPv4."
"warpConfigs" = "WARP маршрутизація" "warpConfigs" = "WARP маршрутизація"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Блокує веб-сайти шкідливого програмного забезпечення, фішингу та майнерів." "SecurityDesc" = "Блокує веб-сайти шкідливого програмного забезпечення, фішингу та майнерів."
"Speedtest" = "Заблокувати Speedtest" "Speedtest" = "Заблокувати Speedtest"
"SpeedtestDesc" = "Блокує підключення до веб-сайтів для тестування швидкості." "SpeedtestDesc" = "Блокує підключення до веб-сайтів для тестування швидкості."
"IRIp" = "Блокувати підключення до IP-адрес Ірану"
"IRIpDesc" = "Блокує встановлення з'єднань з діапазонами IP Ірану."
"IRDomain" = "Блокувати підключення до доменів Ірану"
"IRDomainDesc" = "Блокує встановлення з'єднань з доменами Ірану."
"ChinaIp" = "Блокувати підключення до IP-адрес Китаю"
"ChinaIpDesc" = "Блокує встановлення з'єднань із діапазонами IP-адрес Китаю."
"ChinaDomain" = "Блокувати підключення до китайських доменів"
"ChinaDomainDesc" = "Блокує встановлення підключень до доменів Китаю."
"RussiaIp" = "Блокувати підключення до російських IP-адрес"
"RussiaIpDesc" = "Блокує встановлення з'єднань з діапазонами IP-адрес Росії."
"RussiaDomain" = "Блокувати підключення до російських доменів"
"RussiaDomainDesc" = "Блокує встановлення з'єднань з російськими доменами."
"VNIp" = "Блокувати підключення до IP-адрес В'єтнаму"
"VNIpDesc" = "Блокує встановлення з'єднань із діапазонами IP-адрес В'єтнаму."
"VNDomain" = "Блокувати підключення до доменів В'єтнаму"
"VNDomainDesc" = "Блокує встановлення з'єднань із доменами В'єтнаму."
"DirectIRIp" = "Пряме підключення до IP-адрес Ірану"
"DirectIRIpDesc" = "Безпосередньо встановлює з'єднання з діапазонами IP Ірану."
"DirectIRDomain" = "Пряме підключення до доменів Ірану"
"DirectIRDomainDesc" = "Безпосередньо встановлює підключення до доменів Ірану."
"DirectChinaIp" = "Пряме підключення до китайських IP-адрес"
"DirectChinaIpDesc" = "Безпосередньо встановлює підключення до IP-діапазонів Китаю."
"DirectChinaDomain" = "Пряме підключення до китайських доменів"
"DirectChinaDomainDesc" = "Безпосередньо встановлює підключення до доменів Китаю."
"DirectRussiaIp" = "Пряме підключення до IP-адрес Росії"
"DirectRussiaIpDesc" = "Безпосередньо встановлює з'єднання з діапазонами IP-адрес Росії."
"DirectRussiaDomain" = "Пряме підключення до російських доменів"
"DirectRussiaDomainDesc" = "Безпосередньо встановлює підключення до російських доменів."
"DirectVNIp" = "Пряме підключення до IP-адрес В'єтнаму"
"DirectVNIpDesc" = "Безпосередньо встановлює з'єднання з діапазонами IP-адрес В'єтнаму."
"DirectVNDomain" = "Пряме підключення до доменів В'єтнаму"
"DirectVNDomainDesc" = "Безпосередньо встановлює з'єднання з доменами В'єтнаму."
"GoogleIPv4" = "Google" "GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "Направляє трафік до Google через IPv4." "GoogleIPv4Desc" = "Направляє трафік до Google через IPv4."
"NetflixIPv4" = "Netflix" "NetflixIPv4" = "Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "Шлях до файлу журналу доступу. Спеціальне значення 'none' вимикає журнали доступу" "accessLogDesc" = "Шлях до файлу журналу доступу. Спеціальне значення 'none' вимикає журнали доступу"
"errorLog" = "Журнал помилок" "errorLog" = "Журнал помилок"
"errorLogDesc" = "Шлях до файлу журналу помилок. Спеціальне значення 'none' вимикає журнали помилок" "errorLogDesc" = "Шлях до файлу журналу помилок. Спеціальне значення 'none' вимикає журнали помилок"
"dnsLog" = "Журнал DNS"
"dnsLogDesc" = "Чи включити журнали запитів DNS"
"maskAddress" = "Маскувати Адресу"
"maskAddressDesc" = "Маска IP-адреси, при активації автоматично замінює IP-адресу, яка з'являється у журналі."
[pages.xray.rules] [pages.xray.rules]
"first" = "Перший" "first" = "Перший"
@@ -485,6 +460,7 @@
"add" = "Додати сервер" "add" = "Додати сервер"
"edit" = "Редагувати сервер" "edit" = "Редагувати сервер"
"domains" = "Домени" "domains" = "Домени"
"expectIPs" = "Очікувані IP"
[pages.xray.fakedns] [pages.xray.fakedns]
"add" = "Додати підроблений DNS" "add" = "Додати підроблений DNS"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Підтвердити додавання: {{ .Num }}" "confirmNumberAdd" = "✅ Підтвердити додавання: {{ .Num }}"
"limitTraffic" = "🚧 Ліміт трафіку" "limitTraffic" = "🚧 Ліміт трафіку"
"getBanLogs" = "Отримати журнали заборон" "getBanLogs" = "Отримати журнали заборон"
"allClients" = "Всі Клієнти"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ Операція успішна!" "successfulOperation" = "✅ Операція успішна!"
"errorOperation" = "❗ Помилка в роботі." "errorOperation" = "❗ Помилка в роботі."
"getInboundsFailed" = "❌ Не вдалося отримати вхідні повідомлення." "getInboundsFailed" = "❌ Не вдалося отримати вхідні повідомлення."
"getClientsFailed" = "❌ Не вдалося отримати клієнтів."
"canceled" = "❌ {{ .Email }}: Операцію скасовано." "canceled" = "❌ {{ .Email }}: Операцію скасовано."
"clientRefreshSuccess" = "✅ {{ .Email }}: Клієнт успішно оновлено." "clientRefreshSuccess" = "✅ {{ .Email }}: Клієнт успішно оновлено."
"IpRefreshSuccess" = "✅ {{ .Email }}: IP-адреси успішно оновлено." "IpRefreshSuccess" = "✅ {{ .Email }}: IP-адреси успішно оновлено."
@@ -638,3 +616,5 @@
"enableSuccess" = "✅ {{ .Email }}: Увімкнути успішно." "enableSuccess" = "✅ {{ .Email }}: Увімкнути успішно."
"disableSuccess" = "✅ {{ .Email }}: Успішно вимкнено." "disableSuccess" = "✅ {{ .Email }}: Успішно вимкнено."
"askToAddUserId" = "Вашу конфігурацію не знайдено!\r\nБудь ласка, попросіть свого адміністратора використовувати ваш ідентифікатор Telegram у вашій конфігурації.\r\n\r\nВаш ідентифікатор користувача: <code>{{ .TgUserID }}</code>" "askToAddUserId" = "Вашу конфігурацію не знайдено!\r\nБудь ласка, попросіть свого адміністратора використовувати ваш ідентифікатор Telegram у вашій конфігурації.\r\n\r\nВаш ідентифікатор користувача: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Виберіть клієнта для Вхідного {{ .Inbound }}"
"chooseInbound" = "Виберіть Вхідний"

View File

@@ -143,7 +143,7 @@
"destinationPort" = "Cổng đích" "destinationPort" = "Cổng đích"
"targetAddress" = "Địa chỉ mục tiêu" "targetAddress" = "Địa chỉ mục tiêu"
"monitorDesc" = "Mặc định để trống" "monitorDesc" = "Mặc định để trống"
"meansNoLimit" = " = Không giới hạn (đơn vị: GB)" "meansNoLimit" = "= Không giới hạn (đơn vị: GB)"
"totalFlow" = "Tổng lưu lượng" "totalFlow" = "Tổng lưu lượng"
"leaveBlankToNeverExpire" = "Để trống để không bao giờ hết hạn" "leaveBlankToNeverExpire" = "Để trống để không bao giờ hết hạn"
"noRecommendKeepDefault" = "Không yêu cầu đặc biệt để giữ nguyên cài đặt mặc định" "noRecommendKeepDefault" = "Không yêu cầu đặc biệt để giữ nguyên cài đặt mặc định"
@@ -225,9 +225,6 @@
"requestHeader" = "Header yêu cầu" "requestHeader" = "Header yêu cầu"
"responseHeader" = "Header phản hồi" "responseHeader" = "Header phản hồi"
[pages.inbounds.stream.quic]
"encryption" = "Mã hóa"
[pages.settings] [pages.settings]
"title" = "Cài đặt" "title" = "Cài đặt"
"save" = "Lưu" "save" = "Lưu"
@@ -312,12 +309,14 @@
"fragment" = "Sự phân mảnh" "fragment" = "Sự phân mảnh"
"fragmentDesc" = "Kích hoạt phân mảnh cho gói TLS hello" "fragmentDesc" = "Kích hoạt phân mảnh cho gói TLS hello"
"fragmentSett" = "Cài đặt phân mảnh" "fragmentSett" = "Cài đặt phân mảnh"
"noisesDesc" = "Bật Noises."
"noisesSett" = "Cài đặt Noises"
"mux" = "Mux" "mux" = "Mux"
"muxDesc" = "Truyền nhiều luồng dữ liệu độc lập trong luồng dữ liệu đã thiết lập." "muxDesc" = "Truyền nhiều luồng dữ liệu độc lập trong luồng dữ liệu đã thiết lập."
"muxSett" = "Mux Cài đặt" "muxSett" = "Mux Cài đặt"
"direct" = "Kết nối trực tiếp" "direct" = "Kết nối trực tiếp"
"directDesc" = "Trực tiếp thiết lập kết nối với tên miền hoặc dải IP của một quốc gia cụ thể." "directDesc" = "Trực tiếp thiết lập kết nối với tên miền hoặc dải IP của một quốc gia cụ thể."
"directSett" = "Tùy chọn kết nối trực tiếp"
[pages.xray] [pages.xray]
"title" = "Cài đặt Xray" "title" = "Cài đặt Xray"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Nhật ký có thể ảnh hưởng đến hiệu suất máy chủ của bạn. Bạn chỉ nên kích hoạt nó một cách khôn ngoan trong trường hợp bạn cần" "logConfigsDesc" = "Nhật ký có thể ảnh hưởng đến hiệu suất máy chủ của bạn. Bạn chỉ nên kích hoạt nó một cách khôn ngoan trong trường hợp bạn cần"
"blockConfigs" = "Cấu hình Chặn" "blockConfigs" = "Cấu hình Chặn"
"blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể." "blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể."
"blockCountryConfigs" = "Cấu hình Chặn Quốc gia" "blockConnectionsConfigs" = "Chặn Kết Nối"
"blockCountryConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các tên miền quốc gia cụ thể." "blockConnectionsConfigsDesc" = "Các tùy chọn này sẽ chặn lưu lượng truy cập dựa trên quốc gia được yêu cầu cụ thể."
"directCountryConfigs" = "Cấu hình Kết nối Trực tiếp Quốc gia" "directConnectionsConfigs" = "Kết Nối Trực Tiếp"
"directCountryConfigsDesc" = "Một kết nối trực tiếp đảm bảo rằng lưu lượng cụ thể không được định tuyến qua một máy chủ khác." "directConnectionsConfigsDesc" = "Kết nối trực tiếp đảm bảo rằng lưu lượng truy cập cụ thể không được định tuyến qua máy chủ khác."
"blockips" = "Chặn IP"
"blockdomains" = "Chặn Tên Miền"
"directips" = "IP Trực Tiếp"
"directdomains" = "Tên Miền Trực Tiếp"
"ipv4Configs" = "Cấu hình IPv4" "ipv4Configs" = "Cấu hình IPv4"
"ipv4ConfigsDesc" = "Những tùy chọn này sẽ chỉ định kết nối đến các tên miền mục tiêu qua IPv4." "ipv4ConfigsDesc" = "Những tùy chọn này sẽ chỉ định kết nối đến các tên miền mục tiêu qua IPv4."
"warpConfigs" = "Cấu hình WARP" "warpConfigs" = "Cấu hình WARP"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Thay đổi mẫu cấu hình để bảo vệ Bảo mật." "SecurityDesc" = "Thay đổi mẫu cấu hình để bảo vệ Bảo mật."
"Speedtest" = "Chặn Trang web Speedtest" "Speedtest" = "Chặn Trang web Speedtest"
"SpeedtestDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các trang web Speedtest." "SpeedtestDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các trang web Speedtest."
"IRIp" = "Vô hiệu hóa kết nối đến dải IP của Iran"
"IRIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP của Iran."
"IRDomain" = "Vô hiệu hóa kết nối đến tên miền của Iran"
"IRDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các tên miền của Iran."
"ChinaIp" = "Vô hiệu hóa kết nối đến dải IP của Trung Quốc"
"ChinaIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP của Trung Quốc."
"ChinaDomain" = "Vô hiệu hóa kết nối đến tên miền của Trung Quốc"
"ChinaDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các tên miền của Trung Quốc."
"RussiaIp" = "Vô hiệu hóa kết nối đến dải IP của Nga"
"RussiaIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP của Nga."
"RussiaDomain" = "Vô hiệu hóa kết nối đến tên miền của Nga"
"RussiaDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các tên miền của Nga."
"VNIp" = "Vô hiệu hóa kết nối đến dải IP của Việt Nam"
"VNIpDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến dải IP của Việt Nam."
"VNDomain" = "Vô hiệu hóa kết nối đến tên miền của Việt Nam"
"VNDomainDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các tên miền của Việt Nam."
"DirectIRIp" = "Kết nối trực tiếp đến dải IP của Iran"
"DirectIRIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Iran."
"DirectIRDomain" = "Kết nối trực tiếp đến tên miền của Iran"
"DirectIRDomainDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến các tên miền của Iran."
"DirectChinaIp" = "Kết nối trực tiếp đến dải IP của Trung Quốc"
"DirectChinaIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Trung Quốc."
"DirectChinaDomain" = "Kết nối trực tiếp đến tên miền của Trung Quốc"
"DirectChinaDomainDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến các tên miền của Trung Quốc."
"DirectRussiaIp" = "Kết nối trực tiếp đến dải IP của Nga"
"DirectRussiaIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Nga."
"DirectRussiaDomain" = "Kết nối trực tiếp đến tên miền của Nga"
"DirectRussiaDomainDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến các tên miền của Nga."
"DirectVNIp" = "Kết nối trực tiếp đến dải IP của Việt Nam"
"DirectVNIpDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến dải IP của Việt Nam"
"DirectVNDomain" = "Kết nối trực tiếp đến tên miền của Việt Nam"
"DirectVNDomainDesc" = "Thay đổi mẫu cấu hình cho kết nối trực tiếp đến các tên miền của Việt Nam."
"GoogleIPv4" = "Sử dụng IPv4 cho Google" "GoogleIPv4" = "Sử dụng IPv4 cho Google"
"GoogleIPv4Desc" = "Thêm định tuyến cho Google để kết nối qua IPv4." "GoogleIPv4Desc" = "Thêm định tuyến cho Google để kết nối qua IPv4."
"NetflixIPv4" = "Sử dụng IPv4 cho Netflix" "NetflixIPv4" = "Sử dụng IPv4 cho Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "Đường dẫn tệp cho nhật ký truy cập. Nhật ký truy cập bị vô hiệu hóa có giá trị đặc biệt 'không'" "accessLogDesc" = "Đường dẫn tệp cho nhật ký truy cập. Nhật ký truy cập bị vô hiệu hóa có giá trị đặc biệt 'không'"
"errorLog" = "Nhật ký lỗi" "errorLog" = "Nhật ký lỗi"
"errorLogDesc" = "Đường dẫn tệp cho nhật ký lỗi. Nhật ký lỗi bị vô hiệu hóa có giá trị đặc biệt 'không'" "errorLogDesc" = "Đường dẫn tệp cho nhật ký lỗi. Nhật ký lỗi bị vô hiệu hóa có giá trị đặc biệt 'không'"
"dnsLog" = "Nhật ký DNS"
"dnsLogDesc" = "Có bật nhật ký truy vấn DNS không"
"maskAddress" = "Ẩn Địa Chỉ"
"maskAddressDesc" = "Mặt nạ địa chỉ IP, khi được bật, sẽ tự động thay thế địa chỉ IP xuất hiện trong nhật ký."
[pages.xray.rules] [pages.xray.rules]
"first" = "Đầu tiên" "first" = "Đầu tiên"
@@ -485,6 +460,7 @@
"add" = "Thêm máy chủ" "add" = "Thêm máy chủ"
"edit" = "Chỉnh sửa máy chủ" "edit" = "Chỉnh sửa máy chủ"
"domains" = "Tên miền" "domains" = "Tên miền"
"expectIPs" = "Các IP Dự Kiến"
[pages.xray.fakedns] [pages.xray.fakedns]
"add" = "Thêm DNS giả" "add" = "Thêm DNS giả"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Xác nhận thêm: {{ .Num }}" "confirmNumberAdd" = "✅ Xác nhận thêm: {{ .Num }}"
"limitTraffic" = "🚧 Giới hạn lưu lượng" "limitTraffic" = "🚧 Giới hạn lưu lượng"
"getBanLogs" = "Cấm nhật ký" "getBanLogs" = "Cấm nhật ký"
"allClients" = "Tất cả Khách hàng"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ Thành công!" "successfulOperation" = "✅ Thành công!"
"errorOperation" = "❗ Lỗi Trong Quá Trình Thực Hiện." "errorOperation" = "❗ Lỗi Trong Quá Trình Thực Hiện."
"getInboundsFailed" = "❌ Không Thể Lấy Được Inbounds" "getInboundsFailed" = "❌ Không Thể Lấy Được Inbounds"
"getClientsFailed" = "❌ Không thể lấy khách hàng."
"canceled" = "❌ {{ .Email }} : Thao Tác Đã Bị Hủy." "canceled" = "❌ {{ .Email }} : Thao Tác Đã Bị Hủy."
"clientRefreshSuccess" = "✅ {{ .Email }} : Cập Nhật Thành Công Cho Khách Hàng." "clientRefreshSuccess" = "✅ {{ .Email }} : Cập Nhật Thành Công Cho Khách Hàng."
"IpRefreshSuccess" = "✅ {{ .Email }} : Cập Nhật Thành Công Cho IPs." "IpRefreshSuccess" = "✅ {{ .Email }} : Cập Nhật Thành Công Cho IPs."
@@ -638,3 +616,5 @@
"enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công." "enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công."
"disableSuccess" = "✅ {{ .Email }} : Đã Tắt Thành Công." "disableSuccess" = "✅ {{ .Email }} : Đã Tắt Thành Công."
"askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <code>{{ .TgUserID }}</code>" "askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Chọn một Khách hàng cho Inbound {{ .Inbound }}"
"chooseInbound" = "Chọn một Inbound"

View File

@@ -143,7 +143,7 @@
"destinationPort" = "目标端口" "destinationPort" = "目标端口"
"targetAddress" = "目标地址" "targetAddress" = "目标地址"
"monitorDesc" = "留空表示监听所有 IP" "monitorDesc" = "留空表示监听所有 IP"
"meansNoLimit" = " = 无限制单位GB)" "meansNoLimit" = "= 无限制单位GB)"
"totalFlow" = "总流量" "totalFlow" = "总流量"
"leaveBlankToNeverExpire" = "留空表示永不过期" "leaveBlankToNeverExpire" = "留空表示永不过期"
"noRecommendKeepDefault" = "建议保留默认值" "noRecommendKeepDefault" = "建议保留默认值"
@@ -225,9 +225,6 @@
"requestHeader" = "请求头" "requestHeader" = "请求头"
"responseHeader" = "响应头" "responseHeader" = "响应头"
[pages.inbounds.stream.quic]
"encryption" = "加密"
[pages.settings] [pages.settings]
"title" = "面板设置" "title" = "面板设置"
"save" = "保存" "save" = "保存"
@@ -312,12 +309,14 @@
"fragment" = "分片" "fragment" = "分片"
"fragmentDesc" = "启用 TLS hello 数据包分片" "fragmentDesc" = "启用 TLS hello 数据包分片"
"fragmentSett" = "设置" "fragmentSett" = "设置"
"noisesDesc" = "启用 Noises."
"noisesSett" = "Noises 设置"
"mux" = "多路复用器" "mux" = "多路复用器"
"muxDesc" = "在已建立的数据流内传输多个独立的数据流" "muxDesc" = "在已建立的数据流内传输多个独立的数据流"
"muxSett" = "复用器设置" "muxSett" = "复用器设置"
"direct" = "直接连接" "direct" = "直接连接"
"directDesc" = "直接与特定国家的域或IP范围建立连接" "directDesc" = "直接与特定国家的域或IP范围建立连接"
"directSett" = "直接连接选项"
[pages.xray] [pages.xray]
"title" = "Xray 配置" "title" = "Xray 配置"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "日志可能会影响服务器的性能,建议仅在需要时启用" "logConfigsDesc" = "日志可能会影响服务器的性能,建议仅在需要时启用"
"blockConfigs" = "防护屏蔽" "blockConfigs" = "防护屏蔽"
"blockConfigsDesc" = "这些选项将阻止用户连接到特定协议和网站" "blockConfigsDesc" = "这些选项将阻止用户连接到特定协议和网站"
"blockCountryConfigs" = "屏蔽国家/地区" "blockConnectionsConfigs" = "阻止连接"
"blockCountryConfigsDesc" = "这些选项将阻止用户连接到特定国家/地区" "blockConnectionsConfigsDesc" = "这些选项将根据特定的请求国家阻止流量。"
"directCountryConfigs" = "直连国家/地区" "directConnectionsConfigs" = "直接连接"
"directCountryConfigsDesc" = "直接连接确保特定流量不会通过其他服务器路由" "directConnectionsConfigsDesc" = "直接连接确保特定流量不会通过其他服务器路由"
"blockips" = "阻止IP"
"blockdomains" = "阻止域名"
"directips" = "直接IP"
"directdomains" = "直接域名"
"ipv4Configs" = "IPv4 路由" "ipv4Configs" = "IPv4 路由"
"ipv4ConfigsDesc" = "此选项将仅通过 IPv4 路由到目标域" "ipv4ConfigsDesc" = "此选项将仅通过 IPv4 路由到目标域"
"warpConfigs" = "WARP 路由" "warpConfigs" = "WARP 路由"
@@ -357,38 +360,6 @@
"SecurityDesc" = "屏蔽恶意软件、网络钓鱼和挖矿网站" "SecurityDesc" = "屏蔽恶意软件、网络钓鱼和挖矿网站"
"Speedtest" = "屏蔽测速网站" "Speedtest" = "屏蔽测速网站"
"SpeedtestDesc" = "阻止连接到测速网站" "SpeedtestDesc" = "阻止连接到测速网站"
"IRIp" = "屏蔽连接到伊朗 IP"
"IRIpDesc" = "阻止建立到伊朗 IP 范围的连接"
"IRDomain" = "屏蔽连接到伊朗域名"
"IRDomainDesc" = "阻止建立到伊朗域名的连接"
"ChinaIp" = "屏蔽连接到中国 IP"
"ChinaIpDesc" = "阻止建立到中国 IP 范围的连接"
"ChinaDomain" = "屏蔽连接到中国域名"
"ChinaDomainDesc" = "阻止建立到中国域名的连接"
"RussiaIp" = "屏蔽连接到俄罗斯 IP"
"RussiaIpDesc" = "阻止建立到俄罗斯 IP 范围的连接"
"RussiaDomain" = "屏蔽连接到俄罗斯域名"
"RussiaDomainDesc" = "阻止建立到俄罗斯域名的连接"
"VNIp" = "屏蔽连接到越南 IP"
"VNIpDesc" = "阻止建立到越南 IP 范围的连接"
"VNDomain" = "屏蔽连接到越南域名"
"VNDomainDesc" = "阻止建立到越南域名的连接"
"DirectIRIp" = "直连伊朗 IP"
"DirectIRIpDesc" = "直接建立到伊朗 IP 范围的连接"
"DirectIRDomain" = "直连伊朗域名"
"DirectIRDomainDesc" = "直接建立到伊朗域名的连接"
"DirectChinaIp" = "直连中国 IP"
"DirectChinaIpDesc" = "直接建立到中国 IP 范围的连接"
"DirectChinaDomain" = "直连中国域名"
"DirectChinaDomainDesc" = "直接建立到中国域名的连接"
"DirectRussiaIp" = "直连俄罗斯 IP"
"DirectRussiaIpDesc" = "直接建立到俄罗斯 IP 范围的连接"
"DirectRussiaDomain" = "直连俄罗斯域名"
"DirectRussiaDomainDesc" = "直接建立到俄罗斯域名的连接"
"DirectVNIp" = "直连越南 IP"
"DirectVNIpDesc" = "直接建立到越南 IP 范围的连接"
"DirectVNDomain" = "直连越南域名"
"DirectVNDomainDesc" = "直接建立到越南域名的连接"
"GoogleIPv4" = "Google" "GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "通过 IPv4 将流量路由到谷歌" "GoogleIPv4Desc" = "通过 IPv4 将流量路由到谷歌"
"NetflixIPv4" = "Netflix" "NetflixIPv4" = "Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "访问日志的文件路径。特殊值 'none' 禁用访问日志" "accessLogDesc" = "访问日志的文件路径。特殊值 'none' 禁用访问日志"
"errorLog" = "错误日志" "errorLog" = "错误日志"
"errorLogDesc" = "错误日志的文件路径。特殊值 'none' 禁用错误日志" "errorLogDesc" = "错误日志的文件路径。特殊值 'none' 禁用错误日志"
"dnsLog" = "DNS 日志"
"dnsLogDesc" = "是否启用 DNS 查询日志"
"maskAddress" = "隐藏地址"
"maskAddressDesc" = "IP 地址掩码,启用时会自动替换日志中出现的 IP 地址。"
[pages.xray.rules] [pages.xray.rules]
"first" = "置顶" "first" = "置顶"
@@ -485,6 +460,7 @@
"add" = "添加服务器" "add" = "添加服务器"
"edit" = "编辑服务器" "edit" = "编辑服务器"
"domains" = "域" "domains" = "域"
"expectIPs" = "预期 IP"
[pages.xray.fakedns] [pages.xray.fakedns]
"add" = "添加假 DNS" "add" = "添加假 DNS"
@@ -498,7 +474,7 @@
"loginSecurity" = "登录安全" "loginSecurity" = "登录安全"
"loginSecurityDesc" = "添加额外的身份验证以提高安全性" "loginSecurityDesc" = "添加额外的身份验证以提高安全性"
"secretToken" = "安全令牌" "secretToken" = "安全令牌"
"secretTokenDesc" = "请将此令牌存储在安全的地方。此令牌用于登录,丢失无法恢复。" "secretTokenDesc" = "请将此令牌存储在安全的地方。此令牌用于登录,丢失无法恢复。"
[pages.settings.toasts] [pages.settings.toasts]
"modifySettings" = "修改设置" "modifySettings" = "修改设置"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ 确认添加:{{ .Num }}" "confirmNumberAdd" = "✅ 确认添加:{{ .Num }}"
"limitTraffic" = "🚧 流量限制" "limitTraffic" = "🚧 流量限制"
"getBanLogs" = "禁止日志" "getBanLogs" = "禁止日志"
"allClients" = "所有客户"
[tgbot.answers] [tgbot.answers]
"successfulOperation" = "✅ 成功!" "successfulOperation" = "✅ 成功!"
"errorOperation" = "❗ 操作错误。" "errorOperation" = "❗ 操作错误。"
"getInboundsFailed" = "❌ 获取入站信息失败。" "getInboundsFailed" = "❌ 获取入站信息失败。"
"getClientsFailed" = "❌ 获取客户失败。"
"canceled" = "❌ {{ .Email }}:操作已取消。" "canceled" = "❌ {{ .Email }}:操作已取消。"
"clientRefreshSuccess" = "✅ {{ .Email }}:客户端刷新成功。" "clientRefreshSuccess" = "✅ {{ .Email }}:客户端刷新成功。"
"IpRefreshSuccess" = "✅ {{ .Email }}IP 刷新成功。" "IpRefreshSuccess" = "✅ {{ .Email }}IP 刷新成功。"
@@ -637,4 +615,6 @@
"removedTGUserSuccess" = "✅ {{ .Email }}Telegram 用户已成功移除。" "removedTGUserSuccess" = "✅ {{ .Email }}Telegram 用户已成功移除。"
"enableSuccess" = "✅ {{ .Email }}:已成功启用。" "enableSuccess" = "✅ {{ .Email }}:已成功启用。"
"disableSuccess" = "✅ {{ .Email }}:已成功禁用。" "disableSuccess" = "✅ {{ .Email }}:已成功禁用。"
"askToAddUserId" = "未找到您的配置!\r\n请向管理员询问在您的配置中使用您的 Telegram 用户 ID。\r\n\r\n您的用户 ID<code>{{ .TgUserID }}</code>" "askToAddUserId" = "未找到您的配置!\r\n请向管理员询问在您的配置中使用您的 Telegram 用户 ChatID。\r\n\r\n您的用户 ChatID<code>{{ .TgUserID }}</code>"
"chooseClient" = "为入站 {{ .Inbound }} 选择一个客户"
"chooseInbound" = "选择一个入站"

81
x-ui.sh
View File

@@ -36,12 +36,12 @@ fi
echo "The OS release is: $release" echo "The OS release is: $release"
os_version="" os_version=""
os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1) os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.')
if [[ "${release}" == "arch" ]]; then if [[ "${release}" == "arch" ]]; then
echo "Your OS is Arch Linux" echo "Your OS is Arch Linux"
elif [[ "${release}" == "parch" ]]; then elif [[ "${release}" == "parch" ]]; then
echo "Your OS is Parch linux" echo "Your OS is Parch Linux"
elif [[ "${release}" == "manjaro" ]]; then elif [[ "${release}" == "manjaro" ]]; then
echo "Your OS is Manjaro" echo "Your OS is Manjaro"
elif [[ "${release}" == "armbian" ]]; then elif [[ "${release}" == "armbian" ]]; then
@@ -53,24 +53,28 @@ elif [[ "${release}" == "centos" ]]; then
echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1 echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "ubuntu" ]]; then elif [[ "${release}" == "ubuntu" ]]; then
if [[ ${os_version} -lt 20 ]]; then if [[ ${os_version} -lt 2004 ]]; then
echo -e "${red} Please use Ubuntu 20 or higher version!${plain}\n" && exit 1 echo -e "${red} Please use Ubuntu 20 or higher version!${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "fedora" ]]; then elif [[ "${release}" == "fedora" ]]; then
if [[ ${os_version} -lt 36 ]]; then if [[ ${os_version} -lt 36 ]]; then
echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1 echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "amzn" ]]; then
if [[ ${os_version} != "2023" ]]; then
echo -e "${red} Please use Amazon Linux 2023!${plain}\n" && exit 1
fi
elif [[ "${release}" == "debian" ]]; then elif [[ "${release}" == "debian" ]]; then
if [[ ${os_version} -lt 11 ]]; then if [[ ${os_version} -lt 11 ]]; then
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1 echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "almalinux" ]]; then elif [[ "${release}" == "almalinux" ]]; then
if [[ ${os_version} -lt 9 ]]; then if [[ ${os_version} -lt 80 ]]; then
echo -e "${red} Please use AlmaLinux 9 or higher ${plain}\n" && exit 1 echo -e "${red} Please use AlmaLinux 8.0 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "rocky" ]]; then elif [[ "${release}" == "rocky" ]]; then
if [[ ${os_version} -lt 9 ]]; then if [[ ${os_version} -lt 8 ]]; then
echo -e "${red} Please use Rocky Linux 9 or higher ${plain}\n" && exit 1 echo -e "${red} Please use Rocky Linux 8 or higher ${plain}\n" && exit 1
fi fi
elif [[ "${release}" == "oracle" ]]; then elif [[ "${release}" == "oracle" ]]; then
if [[ ${os_version} -lt 8 ]]; then if [[ ${os_version} -lt 8 ]]; then
@@ -87,12 +91,12 @@ else
echo "- Parch Linux" echo "- Parch Linux"
echo "- Manjaro" echo "- Manjaro"
echo "- Armbian" echo "- Armbian"
echo "- AlmaLinux 9+" echo "- AlmaLinux 8.0+"
echo "- Rocky Linux 9+" echo "- Rocky Linux 8+"
echo "- Oracle Linux 8+" echo "- Oracle Linux 8+"
echo "- OpenSUSE Tumbleweed" echo "- OpenSUSE Tumbleweed"
echo "- Amazon Linux 2023"
exit 1 exit 1
fi fi
# Declare Variables # Declare Variables
@@ -474,7 +478,7 @@ enable_bbr() {
centos | almalinux | rocky | oracle) centos | almalinux | rocky | oracle)
yum -y update && yum -y install ca-certificates yum -y update && yum -y install ca-certificates
;; ;;
fedora) fedora | amzn)
dnf -y update && dnf -y install ca-certificates dnf -y update && dnf -y install ca-certificates
;; ;;
arch | manjaro | parch) arch | manjaro | parch)
@@ -814,7 +818,7 @@ ssl_cert_issue() {
centos | almalinux | rocky | oracle) centos | almalinux | rocky | oracle)
yum -y update && yum -y install socat yum -y update && yum -y install socat
;; ;;
fedora) fedora | amzn)
dnf -y update && dnf -y install socat dnf -y update && dnf -y install socat
;; ;;
arch | manjaro | parch) arch | manjaro | parch)
@@ -978,33 +982,6 @@ ssl_cert_issue_CF() {
fi fi
} }
warp_cloudflare() {
echo -e "${green}\t1.${plain} Install WARP socks5 proxy"
echo -e "${green}\t2.${plain} Account Type (free, plus, team)"
echo -e "${green}\t3.${plain} Turn on/off WireProxy"
echo -e "${green}\t4.${plain} Uninstall WARP"
echo -e "${green}\t0.${plain} Back to Main Menu"
read -p "Choose an option: " choice
case "$choice" in
0)
show_menu
;;
1)
bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh)
;;
2)
warp a
;;
3)
warp y
;;
4)
warp u
;;
*) echo "Invalid choice" ;;
esac
}
run_speedtest() { run_speedtest() {
# Check if Speedtest is already installed # Check if Speedtest is already installed
if ! command -v speedtest &>/dev/null; then if ! command -v speedtest &>/dev/null; then
@@ -1192,7 +1169,7 @@ install_iplimit() {
yum update -y && yum install epel-release -y yum update -y && yum install epel-release -y
yum -y install fail2ban yum -y install fail2ban
;; ;;
fedora) fedora | amzn)
dnf -y update && dnf -y install fail2ban dnf -y update && dnf -y install fail2ban
;; ;;
arch | manjaro | parch) arch | manjaro | parch)
@@ -1273,7 +1250,7 @@ remove_iplimit() {
yum remove fail2ban -y yum remove fail2ban -y
yum autoremove -y yum autoremove -y
;; ;;
fedora) fedora | amzn)
dnf remove fail2ban -y dnf remove fail2ban -y
dnf autoremove -y dnf autoremove -y
;; ;;
@@ -1349,15 +1326,14 @@ show_menu() {
${green}18.${plain} SSL Certificate Management ${green}18.${plain} SSL Certificate Management
${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} WARP Management ${green}21.${plain} Firewall Management
${green}22.${plain} Firewall Management
———————————————— ————————————————
${green}23.${plain} Enable BBR ${green}22.${plain} Enable BBR
${green}24.${plain} Update Geo Files ${green}23.${plain} Update Geo Files
${green}25.${plain} Speedtest by Ookla ${green}24.${plain} Speedtest by Ookla
" "
show_status show_status
echo && read -p "Please enter your selection [0-25]: " num echo && read -p "Please enter your selection [0-24]: " num
case "${num}" in case "${num}" in
0) 0)
@@ -1424,22 +1400,19 @@ show_menu() {
iplimit_main iplimit_main
;; ;;
21) 21)
warp_cloudflare
;;
22)
firewall_menu firewall_menu
;; ;;
23) 22)
bbr_menu bbr_menu
;; ;;
24) 23)
update_geo update_geo
;; ;;
25) 24)
run_speedtest run_speedtest
;; ;;
*) *)
LOGE "Please enter the correct number [0-25]" LOGE "Please enter the correct number [0-24]"
;; ;;
esac esac
} }

View File

@@ -14,6 +14,7 @@ type InboundConfig struct {
StreamSettings json_util.RawMessage `json:"streamSettings"` StreamSettings json_util.RawMessage `json:"streamSettings"`
Tag string `json:"tag"` Tag string `json:"tag"`
Sniffing json_util.RawMessage `json:"sniffing"` Sniffing json_util.RawMessage `json:"sniffing"`
Allocate json_util.RawMessage `json:"allocate"`
} }
func (c *InboundConfig) Equals(other *InboundConfig) bool { func (c *InboundConfig) Equals(other *InboundConfig) bool {
@@ -38,5 +39,8 @@ func (c *InboundConfig) Equals(other *InboundConfig) bool {
if !bytes.Equal(c.Sniffing, other.Sniffing) { if !bytes.Equal(c.Sniffing, other.Sniffing) {
return false return false
} }
if !bytes.Equal(c.Allocate, other.Allocate) {
return false
}
return true return true
} }