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
# 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
wget ${Xray_URL}Xray-linux-64.zip
unzip Xray-linux-64.zip

View File

@@ -27,7 +27,7 @@ case $1 in
esac
mkdir -p 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"
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
mv xray "xray-linux-${FNAME}"

View File

@@ -1,7 +1,7 @@
# ========================================================
# Stage: Builder
# ========================================================
FROM golang:1.22-alpine AS builder
FROM golang:1.23-alpine AS builder
WORKDIR /app
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>
@@ -32,10 +32,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## 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
@@ -177,6 +177,41 @@ eliminar 3x-ui de docker
</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
@@ -185,11 +220,14 @@ eliminar 3x-ui de docker
- CentOS 8+
- Fedora 36+
- Arch Linux
- Parch Linux
- Manjaro
- Armbian
- AlmaLinux 9+
- Rockylinux 9+
- AlmaLinux 8.0+
- Rocky Linux 8+
- Oracle Linux 8+
- OpenSUSE Tubleweed
- Amazon Linux 2023
## 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
## Configuraciones por Defecto
## Configuración Predeterminada del Panel
<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:**
- /etc/x-ui/x-ui.db
- **Ruta de Configuración de Xray:**
- /usr/local/x-ui/bin/config.json
- **Ruta del Panel Web sin Implementar SSL:**
- http://ip:2053/panel
- http://domain:2053/panel
- **Ruta del Panel Web con Implementación de SSL:**
- https://domain:2053/panel
- `/etc/x-ui/x-ui.db`
### Ruta Base Web
1. **Restablecer la Ruta Base Web:**
- Abre tu terminal.
- Ejecuta el comando `x-ui`.
- 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>
## Configuración WARP
## Configuración de WARP
<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
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**:
```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
WARP está integrado, no se requiere instalación adicional. Simplemente habilita la configuración necesaria en el panel.
</details>
## Límite de IP
<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
**Nota:** El Límite de IP no funcionará correctamente cuando se use IP Tunnel
- Para versiones hasta `v1.6.1`:
**Nota:** El Límite de IP no funcionará correctamente cuando uses Túnel IP.
- **Para versiones hasta `v1.6.1`:**
- 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.
2. Selecciona `Gestión de Límite de IP`.
3. Elige las opciones apropiadas según tus necesidades.
- 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
```sh
1. Ejecuta el comando `x-ui` en el terminal, luego elige `Gestión de Límite de IP`.
2. Verás las siguientes opciones:
- **Cambiar la Duración del Bloqueo:** Ajustar la duración de los bloqueos.
- **Desbloquear a Todos:** Levantar todos los bloqueos actuales.
- **Revisar los Registros:** Revisar los registros.
- **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": {
"access": "./access.log",
"dnsLog": false,
"loglevel": "warning"
},
```
```
- **Para versiones `v2.1.3` y posteriores:**
- Hay una opción para configurar `access.log` directamente desde el panel.
</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:
![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".
![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>
@@ -32,10 +32,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## 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
@@ -169,6 +169,8 @@ systemctl restart x-ui
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**
```sh
@@ -202,6 +204,41 @@ systemctl restart x-ui
</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
@@ -213,10 +250,11 @@ systemctl restart x-ui
- Parch Linux
- Manjaro
- Armbian
- AlmaLinux 9+
- Rocky Linux 9+
- AlmaLinux 8.0+
- Rocky Linux 8+
- Oracle Linux 8+
- OpenSUSE Tubleweed
- Amazon Linux 2023
## Supported Architectures and Devices
@@ -248,8 +286,10 @@ Our platform offers compatibility with a diverse range of architectures and devi
- Russian
- Vietnamese
- Spanish
- Indonesian
- Indonesian
- Ukrainian
- Turkish
- Português (Brazil)
## Features
@@ -258,7 +298,7 @@ Our platform offers compatibility with a diverse range of architectures and devi
- Search within all inbounds and clients
- Dark/Light theme
- Supports multi-user and multi-protocol
- Supports protocols, including VMess, VLESS, Trojan, Shadowsocks, Dokodemo-door, Socks, HTTP, wireguard
- Supports protocols, including VMESS, VLESS, Trojan, Shadowsocks, Dokodemo-door, Socks, HTTP, wireguard
- Supports XTLS native Protocols, including RPRX-Direct, Vision, REALITY
- Traffic statistics, traffic limit, expiration time limit
- Customizable Xray configuration templates
@@ -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.
**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>
## 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.
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`:**
- 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
- 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
- Menu based bot
- Search client by email ( only admin )
- Menu-based bot
- Search client by email (only admin)
- Check all inbounds
- Check server status
- Check depleted users
- Receive backup by request and in periodic reports
- Multi language bot
- Multi-language bot
### Setting up Telegram bot
- Start [Botfather](https://t.me/BotFather) in your Telegram account:
![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 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>
@@ -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>
<summary>点击查看 SSL证</summary>
<summary>点击查看SSL证书详情</summary>
### Cloudflare
### ACME
管理脚本具有用于 Cloudflare 的内置 SSL 证书应用程序。若要使用此脚本申请证书,需要满足以下条件
使用ACME管理SSL证书
- Cloudflare 邮箱地址
- Cloudflare Global API Key
- 域名已通过 cloudflare 解析到当前服务器
**1:** 在终端中运行`x-ui` 选择 `Cloudflare SSL Certificate`.
1. 确保您的域名正确解析到服务器。
2. 在终端中运行 `x-ui` 命令,然后选择 `SSL证书管理`
3. 您将看到以下选项:
- **获取SSL证书:** 获取SSL证书。
- **吊销:** 吊销现有的SSL证书。
- **强制更新:** 强制更新SSL证书。
### Certbot
```
安装并使用Certbot
```sh
apt-get install certbot -y
certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com
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>
@@ -166,7 +188,7 @@ systemctl restart x-ui
docker compose up -d
```
从Docker中删除3x-ui
从Docker中删除3x-ui
```sh
docker stop 3x-ui
@@ -178,6 +200,42 @@ systemctl restart x-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` 面板设置中的"面板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+
@@ -185,11 +243,14 @@ systemctl restart x-ui
- CentOS 8+
- Fedora 36+
- Arch Linux
- Parch Linux
- Manjaro
- Armbian
- AlmaLinux 9+
- Rockylinux 9+
- AlmaLinux 8.0+
- Rocky Linux 8+
- Oracle Linux 8+
- OpenSUSE Tubleweed
- Amazon Linux 2023
## 支持的架构和设备
<details>
@@ -240,88 +301,100 @@ systemctl restart x-ui
- 支持从面板导出/导入数据库
## 默认设置
## 默认面板设置
<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>
## WARP 配置
<details>
<summary>点击查看 WARP 配置</summary>
<summary>点击查看 WARP 配置详情</summary>
#### 使用
#### 使用方法
如果要在 v2.1.0 之前使用 WARP 路由,请按照以下步骤操作:
**对于 `v2.1.0` 及之后的版本:**
**1.** 在 **SOCKS Proxy Mode** 模式中安装Wrap
```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
WARP 已内置,无需额外安装。只需在面板中开启相关配置即可。
</details>
## IP 限制
<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. 根据您的需要选择合适的选项
- 确保您的 Xray 配置上有 ./access.log 。在 v2.1.3 之后,我们有一个选项
```sh
- **更改封禁时长:** 调整封禁时长。
- **解除所有封禁:** 解除当前的所有封禁。
- **查看日志:** 查看日志
- **Fail2ban 状态:** 检查 `fail2ban` 的状态。
- **重启 Fail2ban:** 重启 `fail2ban` 服务
- **卸载 Fail2ban:** 卸载带有配置的 Fail2ban。
3. 在面板中通过设置 `Xray 配置/log/访问日志``./access.log` 添加访问日志路径,然后保存并重启 Xray。
- **对于 `v2.1.3` 之前的版本:**
- 您需要在 Xray 配置中手动设置访问日志路径:
```sh
"log": {
"access": "./access.log",
"dnsLog": false,
"loglevel": "warning"
},
```
```
- **对于 `v2.1.3` 及之后的版本:**
- 面板中直接提供了配置 `access.log` 的选项。
</details>
@@ -372,7 +445,7 @@ Web 面板通过 Telegram Bot 支持每日流量、面板登录、数据库备
- 与 [Botfather](https://t.me/BotFather) 对话:
![Botfather](./media/botfather.png)
- 使用 /newbot 创建新机器人你需要提供机器人名称以及用户名注意名称中末尾要包含“bot”
![创建机器人](./media/newbot.png)

View File

@@ -1 +1 @@
2.3.12
2.4.3

View File

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

68
go.mod
View File

@@ -1,35 +1,36 @@
module x-ui
go 1.22.5
go 1.23.1
require (
github.com/gin-contrib/gzip v1.0.1
github.com/gin-contrib/sessions v1.0.1
github.com/gin-gonic/gin v1.10.0
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/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/shirou/gopsutil/v4 v4.24.7
github.com/valyala/fasthttp v1.55.0
github.com/xtls/xray-core v1.8.23
github.com/shirou/gopsutil/v4 v4.24.9
github.com/valyala/fasthttp v1.56.0
github.com/xtls/xray-core v1.8.24
go.uber.org/atomic v1.11.0
golang.org/x/text v0.16.0
google.golang.org/grpc v1.65.0
golang.org/x/text v0.18.0
google.golang.org/grpc v1.67.1
gorm.io/driver/sqlite v1.5.6
gorm.io/gorm v1.25.11
gorm.io/gorm v1.25.12
)
require (
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/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/iasm v0.2.0 // 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/francoispqt/gojay v1.2.13 // 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-playground/locales v0.14.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/google/btree v1.1.2 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d // indirect
github.com/gorilla/context 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/grbit/go-json v0.11.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/compress v1.17.10 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // 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-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/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/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/quic-go v0.45.2 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.47.0 // indirect
github.com/refraction-networking/utls v1.6.7 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/sagernet/sing v0.4.2 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/sagernet/sing v0.4.3 // indirect
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // 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/numcpus v0.8.0 // 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/valyala/bytebufferpool v1.0.0 // 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/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
go.uber.org/mock v0.4.0 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/arch v0.9.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/arch v0.10.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.29.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/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/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
gopkg.in/yaml.v3 v3.0.1 // 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/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/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3ls=
github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
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.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
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/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=
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/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
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/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
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/go.mod h1:C8EY53ozOwpONyevc/V7Gr8pqnEjwnkFFqPo1alAGs0=
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/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
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/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
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.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 v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/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/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/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-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d h1:Jaz2JzpQaQXyET0AjLBXShrthbpqMkhGiEfkcQAiAUs=
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 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
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/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
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.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
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/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
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.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
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/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/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tAFlj1FYZl8ztUZ13bdq+PLY+NOfbyI=
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
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/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/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.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
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/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.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
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.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
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.0/go.mod h1:MuqgVf2xXnIOWZs0prvsp3f4Yss80kCSjVEj4CRl7Ig=
github.com/mymmrac/telego v0.31.3 h1:yZlD+dm+1W6p3OmCG8K+MbS02Y6paUgwPnqfZN3RWQQ=
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/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/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
github.com/onsi/ginkgo/v2 v2.19.1 h1:QXgq3Z8Crl5EL1WBAC98A5sEBHARrAJNzAmMxzLcRF0=
github.com/onsi/ginkgo/v2 v2.19.1/go.mod h1:O3DtEWQkPa/F7fBMgmZQKKsluAy8pd3rEQdrjkPb9zA=
github.com/onsi/gomega v1.34.0 h1:eSSPsPNp6ZpsG8X1OVmOTxig+CblTc4AxpPBykhe2Os=
github.com/onsi/gomega v1.34.0/go.mod h1:MIKI8c+f+QLWk+hxbePD4i0LMJSExPaZOVfkoex4cAo=
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
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/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
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/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.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
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/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
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/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/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.45.2 h1:DfqBmqjb4ExSdxRIb/+qXhPC+7k6+DUNZha4oeiC9fY=
github.com/quic-go/quic-go v0.45.2/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
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/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/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/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.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/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.2/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
github.com/sagernet/sing v0.4.3 h1:Ty/NAiNnVd6844k7ujlL5lkzydhcTH5Psc432jXA4Y8=
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/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
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/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
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.7/go.mod h1:0uW/073rP7FYLOkvxolUQM5rMOLTNmRXnFKafpb71rw=
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/shirou/gopsutil/v4 v4.24.9 h1:KIV+/HaHD5ka5f570RZq+2SaeFsb/pq+fp2DGNWYoOI=
github.com/shirou/gopsutil/v4 v4.24.9/go.mod h1:3fkaHNeYsUFCGZ8+9vZVWtbyM1k2eRnlL+bWO8Bxa/Q=
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/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.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.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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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.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.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/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
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.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW8U=
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/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
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/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3 h1:tkMT5pTye+1NlKIXETU78NXw0fyjnaNHmJyyLyzw8+U=
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3/go.mod h1:cAAsePK2e15YDAMJNyOpGYEWNe4sIghTY7gpz4cX/Ik=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
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-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE=
github.com/xtls/xray-core v1.8.23 h1:A8Wr50ildMYLpaNu3EiK+Stg/tps6i0h7z5Hr4f9H2k=
github.com/xtls/xray-core v1.8.23/go.mod h1:0CwyMPNA5Cs+ukPXHbYQGgne/ug0PuXOSVqBu7zyXOc=
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
github.com/xtls/xray-core v1.8.24 h1:Y2NumdlnJ9C9gvh1Ivs2+73ui5XQgB70wZXYCiI9DyY=
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/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
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/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
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/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-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
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-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
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-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
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.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
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-20180826012351-8a410e7b638d/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-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.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
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-20181017192945-9dcd33a902f4/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-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-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-20220804214406-8e32c043e418/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.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.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
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-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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-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.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
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/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
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-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240805194559-2c9e96a0b5d4 h1:OsSGQeIIsyOEOimVxLEIL4rwGcnrjOydQaiA2bOnZUM=
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 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs=
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.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
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/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
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=
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/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg=
gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
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/go.mod h1:10sU+Uh5KKNv1+2x2A0Gvzt8FjD3ASIhorV3YsauXhk=

View File

@@ -39,12 +39,12 @@ arch() {
echo "arch: $(arch)"
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
echo "Your OS is Arch Linux"
elif [[ "${release}" == "parch" ]]; then
echo "Your OS is Parch linux"
echo "Your OS is Parch Linux"
elif [[ "${release}" == "manjaro" ]]; then
echo "Your OS is Manjaro"
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
fi
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
fi
elif [[ "${release}" == "fedora" ]]; then
if [[ ${os_version} -lt 36 ]]; then
echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1
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
if [[ ${os_version} -lt 11 ]]; then
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "almalinux" ]]; then
if [[ ${os_version} -lt 9 ]]; then
echo -e "${red} Please use AlmaLinux 9 or higher ${plain}\n" && exit 1
if [[ ${os_version} -lt 80 ]]; then
echo -e "${red} Please use AlmaLinux 8.0 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "rocky" ]]; then
if [[ ${os_version} -lt 9 ]]; then
echo -e "${red} Please use Rocky Linux 9 or higher ${plain}\n" && exit 1
if [[ ${os_version} -lt 8 ]]; then
echo -e "${red} Please use Rocky Linux 8 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "oracle" ]]; then
if [[ ${os_version} -lt 8 ]]; then
@@ -90,12 +94,12 @@ else
echo "- Parch Linux"
echo "- Manjaro"
echo "- Armbian"
echo "- AlmaLinux 9+"
echo "- Rocky Linux 9+"
echo "- AlmaLinux 8.0+"
echo "- Rocky Linux 8+"
echo "- Oracle Linux 8+"
echo "- OpenSUSE Tumbleweed"
echo "- Amazon Linux 2023"
exit 1
fi
install_base() {
@@ -106,7 +110,7 @@ install_base() {
centos | almalinux | rocky | oracle)
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
;;
arch | manjaro | parch)

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ package sub
import (
"encoding/base64"
"net"
"strings"
"github.com/gin-gonic/gin"
)
@@ -26,6 +27,7 @@ func NewSUBController(
rModel string,
update string,
jsonFragment string,
jsonNoise string,
jsonMux string,
jsonRules string,
) *SUBController {
@@ -37,7 +39,7 @@ func NewSUBController(
updateInterval: update,
subService: sub,
subJsonService: NewSubJsonService(jsonFragment, jsonMux, jsonRules, sub),
subJsonService: NewSubJsonService(jsonFragment, jsonNoise, jsonMux, jsonRules, sub),
}
a.initRouter(g)
return a
@@ -54,7 +56,10 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) {
func (a *SUBController) subs(c *gin.Context) {
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 == "" {
host = c.GetHeader("X-Real-IP")
}
@@ -89,7 +94,10 @@ func (a *SUBController) subs(c *gin.Context) {
func (a *SUBController) subJsons(c *gin.Context) {
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 == "" {
host = c.GetHeader("X-Real-IP")
}
@@ -113,3 +121,14 @@ func (a *SUBController) subJsons(c *gin.Context) {
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{}
defaultOutbounds []json_util.RawMessage
fragment string
noises string
mux string
inboundService service.InboundService
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 defaultOutbounds []json_util.RawMessage
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))
}
if noises != "" {
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(noises))
}
return &SubJsonService{
configJson: configJson,
defaultOutbounds: defaultOutbounds,
fragment: fragment,
noises: noises,
mux: mux,
SubService: subService,
}
@@ -282,6 +288,9 @@ func (s *SubJsonService) genVnext(inbound *model.Inbound, streamSettings json_ut
usersData[0].ID = client.ID
usersData[0].Level = 8
if inbound.Protocol == model.VMESS {
usersData[0].Security = client.Security
}
if inbound.Protocol == model.VLESS {
usersData[0].Flow = client.Flow
usersData[0].Encryption = "none"
@@ -371,6 +380,7 @@ type UserVnext struct {
Encryption string `json:"encryption,omitempty"`
Flow string `json:"flow,omitempty"`
ID string `json:"id"`
Security string `json:"security,omitempty"`
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 {
if inbound.Protocol != model.VMess {
if inbound.Protocol != model.VMESS {
return ""
}
obj := map[string]interface{}{
@@ -213,12 +213,6 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
http, _ := stream["httpSettings"].(map[string]interface{})
obj["path"], _ = http["path"].(string)
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":
grpc, _ := stream["grpcSettings"].(map[string]interface{})
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["scy"] = clients[clientIndex].Security
externalProxies, _ := stream["externalProxy"].([]interface{})
@@ -369,12 +364,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
http, _ := stream["httpSettings"].(map[string]interface{})
params["path"] = http["path"].(string)
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":
grpc, _ := stream["grpcSettings"].(map[string]interface{})
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{})
params["path"] = http["path"].(string)
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":
grpc, _ := stream["grpcSettings"].(map[string]interface{})
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{})
params["path"] = http["path"].(string)
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":
grpc, _ := stream["grpcSettings"].(map[string]interface{})
params["serviceName"] = grpc["serviceName"].(string)

View File

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

View File

@@ -69,6 +69,14 @@ const WireguardDomainStrategy = [
"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(SSMethods);
Object.freeze(TLS_FLOW_CONTROL);
@@ -76,6 +84,7 @@ Object.freeze(UTLS_FINGERPRINT);
Object.freeze(ALPN_OPTION);
Object.freeze(OutboundDomainStrategies);
Object.freeze(WireguardDomainStrategy);
Object.freeze(USERS_SECURITY);
class CommonClass {
@@ -462,7 +471,6 @@ class StreamSettings extends CommonClass {
this.kcp = kcpSettings;
this.ws = wsSettings;
this.http = httpSettings;
this.quic = quicSettings;
this.grpc = grpcSettings;
this.httpupgrade = httpupgradeSettings;
this.splithttp = splithttpSettings;
@@ -514,7 +522,6 @@ class StreamSettings extends CommonClass {
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
httpSettings: network === 'http' ? this.http.toJson() : undefined,
quicSettings: network === 'quic' ? this.quic.toJson() : undefined,
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
splithttpSettings: network === 'splithttp' ? this.splithttp.toJson() : undefined,
@@ -582,7 +589,7 @@ class Outbound extends CommonClass {
canEnableTls() {
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
@@ -698,11 +705,6 @@ class Outbound extends CommonClass {
stream.http = new HttpStreamSettings(
json.path,
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') {
stream.grpc = new GrpcStreamSettings(json.path, json.authority, json.type == 'multi');
} else if (network === 'httpupgrade') {
@@ -721,7 +723,7 @@ class Outbound extends CommonClass {
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) {
@@ -744,11 +746,6 @@ class Outbound extends CommonClass {
stream.ws = new WsStreamSettings(path, host);
} else if (type === 'http' || type == 'h2') {
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') {
stream.grpc = new GrpcStreamSettings(
url.searchParams.get('serviceName') ?? '',
@@ -852,26 +849,46 @@ Outbound.Settings = class extends CommonClass {
}
};
Outbound.FreedomSettings = class extends CommonClass {
constructor(domainStrategy = '', fragment = {}) {
constructor(
domainStrategy = '',
redirect = '',
fragment = {},
noises = []
) {
super();
this.domainStrategy = domainStrategy;
this.redirect = redirect;
this.fragment = fragment;
this.noises = noises;
}
addNoise() {
this.noises.push(new Outbound.FreedomSettings.Noise());
}
delNoise(index) {
this.noises.splice(index, 1);
}
static fromJson(json = {}) {
return new Outbound.FreedomSettings(
json.domainStrategy,
json.redirect,
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() {
return {
domainStrategy: ObjectUtil.isEmpty(this.domainStrategy) ? undefined : this.domainStrategy,
redirect: this.redirect,
fragment: Object.keys(this.fragment).length === 0 ? undefined : this.fragment,
noises: Outbound.FreedomSettings.Noise.toJsonArray(this.noises),
};
}
};
Outbound.FreedomSettings.Fragment = class extends CommonClass {
constructor(packets = '1-3', length = '', interval = '') {
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 {
constructor(type) {
super();
@@ -907,11 +958,19 @@ Outbound.BlackholeSettings = 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();
this.network = network;
this.address = address;
this.port = port;
this.nonIPQuery = nonIPQuery;
this.blockTypes = blockTypes;
}
static fromJson(json = {}) {
@@ -919,15 +978,18 @@ Outbound.DNSSettings = class extends CommonClass {
json.network,
json.address,
json.port,
json.nonIPQuery,
json.blockTypes,
);
}
};
Outbound.VmessSettings = class extends CommonClass {
constructor(address, port, id) {
constructor(address, port, id, security) {
super();
this.address = address;
this.port = port;
this.id = id;
this.security = security;
}
static fromJson(json = {}) {
@@ -936,6 +998,7 @@ Outbound.VmessSettings = class extends CommonClass {
json.vnext[0].address,
json.vnext[0].port,
json.vnext[0].users[0].id,
json.vnext[0].users[0].security,
);
}
@@ -944,7 +1007,7 @@ Outbound.VmessSettings = class extends CommonClass {
vnext: [{
address: this.address,
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.webKeyFile = "";
this.webBasePath = "/";
this.sessionMaxAge = 0;
this.sessionMaxAge = 60;
this.pageSize = 50;
this.expireDiff = 0;
this.trafficDiff = 0;
@@ -38,6 +38,7 @@ class AllSetting {
this.subURI = "";
this.subJsonURI = "";
this.subJsonFragment = "";
this.subJsonNoises = "";
this.subJsonMux = "";
this.subJsonRules = "";

View File

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

View File

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

View File

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

View File

@@ -416,19 +416,19 @@
<a-col span="24">
<a-form>
<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>
<a-icon slot="prefix" type="user" style="font-size: 16px;"></a-icon>
</a-input>
</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" }}'
@keydown.enter.native="login">
</password-input>
</a-form-item>
<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" }}'
@keydown.enter.native="login">
</password-input>
@@ -449,7 +449,7 @@
<a-row justify="center" class="centered">
<a-col :span="24">
<a-select ref="selectLang" v-model="lang"
@change="setLang(lang)" style="width: 150px;"
@change="setLang(lang)" style="width: 200px;"
:dropdown-class-name="themeSwitcher.currentTheme">
<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>

View File

@@ -28,6 +28,11 @@
<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-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-select v-model="clientsBulkModal.flow" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
@@ -146,6 +151,7 @@
emailPostfix: "",
subId: "",
tgId: '',
security: "auto",
flow: "",
delayedStart: false,
reset: 0,
@@ -168,6 +174,7 @@
newClient.email += useNum ? prefix + i.toString() + postfix : prefix + postfix;
if (clientsBulkModal.subId.length > 0) newClient.subId = clientsBulkModal.subId;
newClient.tgId = clientsBulkModal.tgId;
newClient.security = clientsBulkModal.security;
newClient.limitIp = clientsBulkModal.limitIp;
newClient._totalGB = clientsBulkModal.totalGB;
newClient._expiryTime = clientsBulkModal.expiryTime;
@@ -203,6 +210,7 @@
this.emailPostfix = "";
this.subId = "";
this.tgId = '';
this.security = "auto";
this.flow = "";
this.dbInbound = new DBInbound(dbInbound);
this.inbound = dbInbound.toInbound();
@@ -211,7 +219,7 @@
},
newClient(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.TROJAN: return new Inbound.TrojanSettings.Trojan();
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings.Shadowsocks(clientsBulkModal.inbound.settings.shadowsockses[0].method);

View File

@@ -61,7 +61,7 @@
},
addClient(protocol, clients) {
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.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan());
case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method));

View File

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

View File

@@ -1,5 +1,7 @@
{{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-item label='{{ i18n "pages.xray.outbound.address" }}'>
<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>
<template v-for="(domain, index) in dnsModal.dnsServer.domains">
<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>
</template>
</a-form-item>
<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>
</a-form-item>
<a-form-item label='Skip Fallback' v-if="isAdvanced">
<a-switch v-model="dnsModal.dnsServer.skipFallback"></a-switch>
</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-modal>
<script>
@@ -32,20 +45,24 @@
dnsServer: {
address: "localhost",
domains: [],
expectIPs: [],
queryStrategy: 'UseIP',
skipFallback: true,
},
ok() {
domains = dnsModal.dnsServer.domains.filter(d => d.length > 0);
expectIPs = dnsModal.dnsServer.expectIPs.filter(ip => ip.length > 0);
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);
},
show({
title = '',
okText = '{{ i18n "confirm" }}',
dnsServer,
confirm = (dnsServer) => {},
confirm = (dnsServer) => { },
isEdit = false
}) {
this.title = title;
@@ -59,6 +76,7 @@
this.dnsServer = {
address: dnsServer ?? "",
domains: [],
expectIPs: [],
queryStrategy: 'UseIP',
skipFallback: true,
}
@@ -67,6 +85,7 @@
this.dnsServer = {
address: "localhost",
domains: [],
expectIPs: [],
queryStrategy: 'UseIP',
skipFallback: true,
}
@@ -85,8 +104,8 @@
},
computed: {
isAdvanced: {
get: function() {
return dnsModal.dnsServer.domains.length > 0
get: function () {
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>
<a-input v-model.trim="client.id"></a-input>
</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">
<template slot="label">
<a-tooltip>
@@ -57,7 +62,7 @@
<template slot="title">
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
</template>
Telegram ID
Telegram ChatID
<a-icon type="question-circle"></a-icon>
</a-tooltip>
</template>
@@ -99,7 +104,7 @@
</a-textarea>
</a-form>
</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-option value="" selected>{{ i18n "none" }}</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>
<!-- sniffing -->
<template>
{{template "form/sniffing"}}
</template>
<a-collapse>
<a-collapse-panel header='Sniffing'>
{{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}}

View File

@@ -22,8 +22,13 @@
<a-select-option v-for="s in OutboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
</a-select>
</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-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>
<template v-if="Object.keys(outbound.settings.fragment).length >0">
<a-form-item label='Packets'>
@@ -38,6 +43,40 @@
<a-input v-model.trim="outbound.settings.fragment.interval"></a-input>
</a-form-item>
</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>
<!-- blackhole settings -->
@@ -56,6 +95,14 @@
<a-select-option v-for="s in ['udp','tcp']" :value="s">[[ s ]]</a-select-option>
</a-select>
</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>
<!-- wireguard settings -->
@@ -95,10 +142,10 @@
</a-select>
</a-form-item>
<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 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 label='Kernel Mode'>
<a-switch v-model="outbound.settings.kernelMode"></a-switch>
@@ -161,6 +208,15 @@
<a-input v-model.trim="outbound.settings.id"></a-input>
</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 -->
<template v-if="outbound.canEnableTlsFlow()">
<a-form-item label='Flow'>
@@ -215,7 +271,6 @@
<a-select-option value="kcp">mKCP</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="quic">QUIC</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="splithttp">SplitHTTP</a-select-option>
@@ -252,25 +307,25 @@
<a-input v-model="outbound.stream.kcp.seed"></a-input>
</a-form-item>
<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 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 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 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 label='Congestion'>
<a-switch v-model="outbound.stream.kcp.congestion"></a-switch>
</a-form-item>
<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 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>
</template>
@@ -293,31 +348,7 @@
<a-input v-model.trim="outbound.stream.http.path"></a-input>
</a-form-item>
</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 -->
<template v-if="outbound.stream.network === 'grpc'">
<a-form-item label='Service Name'>

View File

@@ -16,8 +16,5 @@
<a-form-item label='Follow Redirect'>
<a-switch v-model="inbound.settings.followRedirect"></a-switch>
</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>
{{end}}

View File

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

View File

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

View File

@@ -10,10 +10,12 @@
<tr class="client-table-header">
<th>{{ i18n "pages.inbounds.email" }}</th>
<th>ID</th>
<th>{{ i18n "security" }}</th>
</tr>
<tr v-for="(client, index) in inbound.settings.vmesses" :class="index % 2 == 1 ? 'client-table-odd-row' : ''">
<td>[[ client.email ]]</td>
<td>[[ client.id ]]</td>
<td>[[ client.security ]]</td>
</tr>
</table>
</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"}}
<a-divider style="margin:5px 0 0;"></a-divider>
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
<a-form-item>
<span slot="label">
Sniffing
{{ i18n "enabled" }}
<a-tooltip>
<template slot="title">
<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-select v-model="inbound.stream.network" style="width: 75%" @change="streamNetworkChange"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="tcp">TCP</a-select-option>
<a-select-option value="tcp">TCP (RAW)</a-select-option>
<a-select-option value="kcp">mKCP</a-select-option>
<a-select-option value="ws">WebSocket</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="httpupgrade">HTTPUpgrade</a-select-option>
<a-select-option value="splithttp">SplitHTTP</a-select-option>
@@ -36,11 +35,6 @@
{{template "form/streamHTTP"}}
</template>
<!-- quic -->
<template v-if="inbound.stream.network === 'quic'">
{{template "form/streamQUIC"}}
</template>
<!-- grpc -->
<template v-if="inbound.stream.network === 'grpc'">
{{template "form/streamGRPC"}}

View File

@@ -20,16 +20,31 @@
</a-input-group>
</a-form-item>
<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 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 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 label="No SSE Header">
<a-switch v-model="inbound.stream.splithttp.noSSEHeader"></a-switch>
</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>
{{end}}

View File

@@ -34,16 +34,19 @@
</a-form-item>
<a-form-item label="Min/Max Version">
<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>
<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>
</a-input-group>
</a-form-item>
<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 v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-select>
@@ -60,10 +63,10 @@
<a-switch v-model="inbound.stream.tls.rejectUnknownSni"></a-switch>
</a-form-item>
<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 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>
<template v-for="cert,index in inbound.stream.tls.certs">
<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="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.tls.addCert()" 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-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="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>
<template v-if="cert.useFile">
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
@@ -82,7 +87,8 @@
<a-input v-model.trim="cert.keyFile"></a-input>
</a-form-item>
<a-form-item label=" ">
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">
{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
</a-form-item>
</template>
<template v-else>
@@ -111,101 +117,13 @@
</template>
<!-- xtls settings -->
<template v-else-if="inbound.xtls">
<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 v-else-if="inbound.stream.isXtls">
{{template "form/xtlsSettings"}}
</template>
<!-- reality settings -->
<template v-if="inbound.stream.isReality">
<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>
<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 "form/realitySettings"}}
</template>
</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>
</tr>
</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">
<tr>
<td>kcp {{ i18n "encryption" }}</td>
@@ -159,6 +139,12 @@
<a-tag>[[ infoModal.clientSettings.id ]]</a-tag>
</td>
</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()">
<td>Flow</td>
<td v-if="infoModal.clientSettings.flow">

View File

@@ -733,7 +733,7 @@
this.inbounds.push(to_inbound);
this.dbInbounds.push(dbInbound);
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;
}
this.clientCount[inbound.id] = this.getClientCounts(inbound, to_inbound);
@@ -935,6 +935,7 @@
settings: Inbound.Settings.getSettings(baseInbound.protocol).toString(),
streamSettings: baseInbound.stream.toString(),
sniffing: baseInbound.sniffing.toString(),
allocate: baseInbound.allocate.toString(),
};
await this.submit('/panel/inbound/add', data, inModal);
},
@@ -980,6 +981,7 @@
};
if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString();
data.sniffing = inbound.sniffing.toString();
data.allocate = inbound.allocate.toString();
await this.submit('/panel/inbound/add', data, inModal);
},
@@ -999,6 +1001,7 @@
};
if (inbound.canEnableStream()) data.streamSettings = inbound.stream.toString();
data.sniffing = inbound.sniffing.toString();
data.allocate = inbound.allocate.toString();
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.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="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.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>
@@ -296,23 +296,48 @@
</a-row>
<a-collapse v-if="fragment" style="margin-top: 14px;">
<a-collapse-panel header='{{ i18n "pages.settings.fragmentSett"}}' v-if="fragment">
<a-list-item style="padding: 10px 20px">
<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='Packets' v-model="fragmentPackets" placeholder="1-1 | 1-3 | tlshello | ..."></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>
</a-collapse-panel>
</a-collapse>
</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-row>
<a-col :lg="24" :xl="12">
@@ -355,9 +380,14 @@
</a-col>
</a-row>
<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-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-collapse-panel>
</a-collapse>
@@ -412,6 +442,16 @@
}
}
},
defaultNoises: {
tag: "noises",
protocol: "freedom",
settings: {
domainStrategy: "AsIs",
noises: [
{ type: "rand", packet: "10-20", delay: "10-16" },
],
},
},
defaultMux: {
enabled: true,
concurrency: 8,
@@ -423,8 +463,7 @@
type: "field",
outboundTag: "direct",
domain: [
"geosite:category-ir",
"geosite:cn"
"geosite:category-ir"
],
"enabled": true
},
@@ -433,17 +472,30 @@
outboundTag: "direct",
ip: [
"geoip:private",
"geoip:ir",
"geoip:cn"
"geoip:ir"
],
enabled: true
},
],
countryOptions: [
{ label: 'Private IP/Domain', value: 'private' },
geoIPOptions: [
{ label: 'Private IP', value: 'private' },
{ label: '🇮🇷 Iran', value: 'ir' },
{ label: '🇨🇳 China', value: 'cn' },
{ 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() {
rm = this.allSetting.remarkModel;
@@ -523,7 +575,9 @@
if (msg.success) {
this.loading(true);
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 url = buildURL({ host, port, isTLS, base, path: "panel/settings" });
window.location.replace(url);
@@ -571,6 +625,30 @@
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: {
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: {
get: function () { return this.allSetting?.subJsonMux != ""; },
set: function (v) {
@@ -645,28 +747,49 @@
this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
}
},
directCountries: {
geoIP: {
get: function () {
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:", "")) : [];
},
set: function (v) {
rules = JSON.parse(this.allSetting.subJsonRules);
const rules = JSON.parse(this.allSetting.subJsonRules);
if (!Array.isArray(rules)) return;
rules[0].domain = [];
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 => {
let category = '';
if (["cn", "private"].includes(d)) {
if (["cn", "apple", "meta", "google"].includes(d)) {
category = "";
} else if (d === 'ru') {
category = "category-gov-";
} else {
} else if (["ru", "ir"].includes(d)) {
category = "category-";
}
rules[0].domain.push("geosite:" + category + d);
rules[1].ip.push("geoip:" + d);
});
this.allSetting.subJsonRules = JSON.stringify(rules);
}

View File

@@ -163,8 +163,8 @@
</a-col>
<a-col :lg="24" :xl="12">
<template>
<a-select v-model="setLogLevel" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
<a-select-option v-for="s in logLevel" :value="s">[[ s ]]</a-select-option>
<a-select v-model="logLevel" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
<a-select-option v-for="s in log.loglevel" :value="s">[[ s ]]</a-select-option>
</a-select>
</template>
</a-col>
@@ -178,7 +178,8 @@
<a-col :lg="24" :xl="12">
<template>
<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>
</template>
</a-col>
@@ -192,11 +193,28 @@
<a-col :lg="24" :xl="12">
<template>
<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>
</template>
</a-col>
</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-collapse-panel>
<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>
</a-list-item>
</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-alert type="warning" style="text-align: center;">
<template slot="message">
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
{{ i18n "pages.xray.blockCountryConfigsDesc" }}
{{ i18n "pages.xray.blockConnectionsConfigsDesc" }}
</template>
</a-alert>
</a-row>
<a-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.IRIp"}}' desc='{{ i18n "pages.xray.IRIpDesc"}}' v-model="IRIpSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.IRDomain"}}' desc='{{ i18n "pages.xray.IRDomainDesc"}}' v-model="IRDomainSettings"></setting-list-item>
<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='{{ i18n "pages.xray.ChinaDomain"}}' desc='{{ i18n "pages.xray.ChinaDomainDesc"}}' v-model="ChinaDomainSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaIp"}}' desc='{{ i18n "pages.xray.RussiaIpDesc"}}' v-model="RussiaIpSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.RussiaDomain"}}' desc='{{ i18n "pages.xray.RussiaDomainDesc"}}' v-model="RussiaDomainSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.VNIp"}}' desc='{{ i18n "pages.xray.VNIpDesc"}}' v-model="VNIpSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.VNDomain"}}' desc='{{ i18n "pages.xray.VNDomainDesc"}}' v-model="VNDomainSettings"></setting-list-item>
<a-collapse>
<a-collapse-panel header='{{ i18n "pages.xray.blockips"}}'>
<setting-list-item type="switch" title='🇮🇷 Iran'
v-model="IRIpSettings"></setting-list-item>
<setting-list-item type="switch" title='🇨🇳 China'
v-model="ChinaIpSettings"></setting-list-item>
<setting-list-item type="switch" title='🇷🇺 Russia'
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-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-alert type="warning" style="text-align: center;">
<template slot="message">
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
{{ i18n "pages.xray.directCountryConfigsDesc" }}
{{ i18n "pages.xray.directConnectionsConfigsDesc" }}
</template>
</a-alert>
</a-row>
<a-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectIRIp"}}' desc='{{ i18n "pages.xray.DirectIRIpDesc"}}' v-model="IRIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectIRDomain"}}' desc='{{ i18n "pages.xray.DirectIRDomainDesc"}}' v-model="IRDomainDirectSettings"></setting-list-item>
<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='{{ i18n "pages.xray.DirectChinaDomain"}}' desc='{{ i18n "pages.xray.DirectChinaDomainDesc"}}' v-model="ChinaDomainDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaIp"}}' desc='{{ i18n "pages.xray.DirectRussiaIpDesc"}}' v-model="RussiaIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectRussiaDomain"}}' desc='{{ i18n "pages.xray.DirectRussiaDomainDesc"}}' v-model="RussiaDomainDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNIp"}}' desc='{{ i18n "pages.xray.DirectVNIpDesc"}}' v-model="VNIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='{{ i18n "pages.xray.DirectVNDomain"}}' desc='{{ i18n "pages.xray.DirectVNDomainDesc"}}' v-model="VNDomainDirectSettings"></setting-list-item>
<a-collapse>
<a-collapse-panel header='{{ i18n "pages.xray.directips"}}'>
<setting-list-item type="switch" title='🇮🇷 Iran'
v-model="IRIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='🇨🇳 China'
v-model="ChinaIpDirectSettings"></setting-list-item>
<setting-list-item type="switch" title='🇷🇺 Russia'
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-collapse-panel>
<a-collapse-panel header='{{ i18n "pages.xray.ipv4Configs"}}'>
@@ -569,86 +621,90 @@
</a-radio-group>
<textarea style="position:absolute; left: -800px;" id="obsSetting"></textarea>
</a-tab-pane>
<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>
<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>
<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>
<a-list-item style="padding: 10px 20px">
<a-row>
<a-col :lg="24" :xl="12">
<a-list-item-meta title='{{ i18n "pages.xray.dns.strategy" }}' description='{{ i18n "pages.xray.dns.strategyDesc" }}' />
</a-col>
<a-col :lg="24" :xl="12">
<a-select
v-model="dnsStrategy"
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>
</a-col>
</a-row>
</a-list-item>
<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-table :columns="dnsColumns" bordered v-if="dnsServers.length>0"
:row-key="r => r.key"
:data-source="dnsServers"
:scroll="isMobile ? {} : { x: 200 }"
:pagination="false"
:indent-size="0"
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
<template slot="action" slot-scope="text,dns,index">
[[ index+1 ]]
<a-dropdown :trigger="['click']">
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
<a-menu-item @click="editDNSServer(index)">
<a-icon type="edit"></a-icon>
{{ i18n "edit" }}
</a-menu-item>
<a-menu-item @click="deleteDNSServer(index)">
<span style="color: #FF4D4F">
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
</span>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
<template slot="address" slot-scope="dns,index">
<span v-if="typeof dns == 'object'">[[ dns.address ]]</span>
<span v-else>[[ dns ]]</span>
</template>
<template slot="domain" slot-scope="dns,index">
<span v-if="typeof dns == 'object'">[[ dns.domains.join(",") ]]</span>
</template>
</a-table>
<a-divider>Fake DNS</a-divider>
<a-button type="primary" icon="plus" @click="addFakedns()" style="margin-bottom: 10px;">{{ i18n "pages.xray.fakedns.add" }}</a-button>
<a-table :columns="fakednsColumns" bordered v-if="fakeDns && fakeDns.length>0" :row-key="r => r.key"
:data-source="fakeDns" :scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0"
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
<template slot="action" slot-scope="text,fakedns,index">
[[ index+1 ]]
<a-dropdown :trigger="['click']">
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon>
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
<a-menu-item @click="editFakedns(index)">
<a-icon type="edit"></a-icon>
{{ i18n "edit" }}
</a-menu-item>
<a-menu-item @click="deleteFakedns(index)">
<span style="color: #FF4D4F">
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
</span>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
</a-table>
<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>
<a-list-item style="padding: 10px 20px">
<a-row>
<a-col :lg="24" :xl="12">
<a-list-item-meta title='{{ i18n "pages.xray.dns.strategy" }}'
description='{{ i18n "pages.xray.dns.strategyDesc" }}' />
</a-col>
<a-col :lg="24" :xl="12">
<a-select v-model="dnsStrategy" 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>
</a-col>
</a-row>
</a-list-item>
<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-table :columns="dnsColumns" bordered v-if="dnsServers.length>0" :row-key="r => r.key"
:data-source="dnsServers" :scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0"
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
<template slot="action" slot-scope="text,dns,index">
[[ index+1 ]]
<a-dropdown :trigger="['click']">
<a-icon @click="e => e.preventDefault()" type="more"
style="font-size: 16px; text-decoration: bold;"></a-icon>
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
<a-menu-item @click="editDNSServer(index)">
<a-icon type="edit"></a-icon>
{{ i18n "edit" }}
</a-menu-item>
<a-menu-item @click="deleteDNSServer(index)">
<span style="color: #FF4D4F">
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
</span>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
<template slot="address" slot-scope="dns,index">
<span v-if="typeof dns == 'object'">[[ dns.address ]]</span>
<span v-else>[[ dns ]]</span>
</template>
<template slot="domain" slot-scope="dns,index">
<span v-if="typeof dns == 'object'">[[ dns.domains.join(",") ]]</span>
</template>
<template slot="expectIPs" slot-scope="dns,index">
<span v-if="typeof dns == 'object'">[[ dns.expectIPs.join(",") ]]</span>
</template>
</a-table>
<a-divider>Fake DNS</a-divider>
<a-button type="primary" icon="plus" @click="addFakedns()" style="margin-bottom: 10px;">{{ i18n
"pages.xray.fakedns.add" }}</a-button>
<a-table :columns="fakednsColumns" bordered v-if="fakeDns && fakeDns.length>0" :row-key="r => r.key"
:data-source="fakeDns" :scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0"
:style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'">
<template slot="action" slot-scope="text,fakedns,index">
[[ index+1 ]]
<a-dropdown :trigger="['click']">
<a-icon @click="e => e.preventDefault()" type="more"
style="font-size: 16px; text-decoration: bold;"></a-icon>
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
<a-menu-item @click="editFakedns(index)">
<a-icon type="edit"></a-icon>
{{ i18n "edit" }}
</a-menu-item>
<a-menu-item @click="deleteFakedns(index)">
<span style="color: #FF4D4F">
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
</span>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
</a-table>
</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-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' : ''">
@@ -730,6 +786,7 @@
{ 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.dns.domains"}}', align: 'center', width: 50, scopedSlots: { customRender: 'domain' } },
{ title: '{{ i18n "pages.xray.dns.expectIPs"}}', align: 'center', width: 50, scopedSlots: { customRender: 'expectIPs' } },
];
const fakednsColumns = [
@@ -791,9 +848,13 @@
protocol: "freedom"
},
routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"],
logLevel: ["none" , "debug" , "info" , "warning", "error"],
access: [],
error: [],
log: {
loglevel: ["none", "debug", "info", "warning", "error"],
access: ["none", "./access.log"],
error: ["none", "./error.log"],
dnsLog: false,
maskAddress: ["quarter", "half", "full"],
},
settingsData: {
protocols: {
bittorrent: ["bittorrent"],
@@ -824,17 +885,19 @@
apple: ["geosite:apple"],
reddit: ["geosite:reddit"],
cn: [
"geosite:cn",
"regexp:.*\\.cn$"
"regexp:.*\\.cn$",
"geosite:cn"
],
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: [
"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: [
"regexp:.*\\.vn$",
@@ -1519,27 +1582,11 @@
templateSettings: {
get: function () {
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;
},
set: function (newValue) {
if (newValue && newValue.log) {
if (newValue) {
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;
}
},
setLogLevel: {
logLevel: {
get: function () {
if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.loglevel) return "warning";
return this.templateSettings.log.loglevel;
@@ -1721,6 +1768,28 @@
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: {
get: function () {
return this.templateRuleGetter({ outboundTag: "blocked", property: "ip" });

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,9 @@ package service
import (
"encoding/json"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"time"
@@ -329,6 +331,7 @@ func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound,
oldInbound.Settings = inbound.Settings
oldInbound.StreamSettings = inbound.StreamSettings
oldInbound.Sniffing = inbound.Sniffing
oldInbound.Allocate = inbound.Allocate
if inbound.Listen == "" || inbound.Listen == "0.0.0.0" || inbound.Listen == "::" || inbound.Listen == "::0" {
oldInbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port)
} else {
@@ -411,6 +414,12 @@ func (s *InboundService) AddInboundClient(data *model.Inbound) (bool, error) {
return false, err
}
email := clients[0].Email
valid, err := validateEmail(email)
if !valid {
return false, err
}
var settings map[string]interface{}
err = json.Unmarshal([]byte(data.Settings), &settings)
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{}{
"email": client.Email,
"id": client.ID,
"security": client.Security,
"flow": client.Flow,
"password": client.Password,
"cipher": cipher,
@@ -533,11 +543,13 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
interfaceClients := settings["clients"].([]interface{})
var newClients []interface{}
needApiDel := false
for _, client := range interfaceClients {
c := client.(map[string]interface{})
c_id := c[client_key].(string)
if c_id == clientId {
email = c["email"].(string)
email, _ = c["email"].(string)
needApiDel, _ = c["enable"].(bool)
} else {
newClients = append(newClients, client)
}
@@ -556,11 +568,6 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
oldInbound.Settings = string(newSettings)
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)
if err != nil {
@@ -568,17 +575,31 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
return false, err
}
needRestart := false
if len(email) > 0 {
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
notDepleted := true
err = db.Model(xray.ClientTraffic{}).Select("enable").Where("email = ?", email).First(&notDepleted).Error
if err != nil {
logger.Error("Get stats error")
return false, err
}
err = s.DelClientStat(db, email)
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
}
@@ -589,6 +610,12 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
return false, err
}
email := clients[0].Email
valid, err := validateEmail(email)
if !valid {
return false, err
}
var settings map[string]interface{}
err = json.Unmarshal([]byte(data.Settings), &settings)
if err != nil {
@@ -696,12 +723,14 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
needRestart := false
if len(oldEmail) > 0 {
s.xrayApi.Init(p.GetAPIPort())
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
if err1 == nil {
logger.Debug("Old client deleted by api:", clients[0].Email)
} else {
logger.Debug("Error in deleting client by api:", err1)
needRestart = true
if oldClients[clientIndex].Enable {
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
if err1 == nil {
logger.Debug("Old client deleted by api:", clients[0].Email)
} else {
logger.Debug("Error in deleting client by api:", err1)
needRestart = true
}
}
if clients[0].Enable {
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{}{
"email": clients[0].Email,
"id": clients[0].ID,
"security": clients[0].Security,
"flow": clients[0].Flow,
"password": clients[0].Password,
"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{}{
"email": client.Email,
"id": client.ID,
"security": client.Security,
"flow": client.Flow,
"password": client.Password,
"cipher": cipher,
@@ -1991,3 +2022,20 @@ func (s *InboundService) MigrateDB() {
func (s *InboundService) GetOnlineClients() []string {
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": "",
"secret": random.Seq(32),
"webBasePath": "/",
"sessionMaxAge": "0",
"sessionMaxAge": "60",
"pageSize": "50",
"expireDiff": "0",
"trafficDiff": "0",
@@ -62,6 +62,7 @@ var defaultValueMap = map[string]string{
"subJsonPath": "/json/",
"subJsonURI": "",
"subJsonFragment": "",
"subJsonNoises": "",
"subJsonMux": "",
"subJsonRules": "",
"datepicker": "gregorian",
@@ -458,6 +459,10 @@ func (s *SettingService) GetSubJsonFragment() (string, error) {
return s.getString("subJsonFragment")
}
func (s *SettingService) GetSubJsonNoises() (string, error) {
return s.getString("subJsonNoises")
}
func (s *SettingService) GetSubJsonMux() (string, error) {
return s.getString("subJsonMux")
}

View File

@@ -2,6 +2,7 @@ package service
import (
"embed"
"errors"
"fmt"
"net"
"net/url"
@@ -769,8 +770,40 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
} else {
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
} 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.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.allClients")).WithCallbackData(t.encodeQuery("get_inbounds")),
),
)
numericKeyboardClient := tu.InlineKeyboard(
@@ -1082,6 +1116,72 @@ func (t *Tgbot) getInboundUsages() string {
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(
traffic *xray.ClientTraffic,
printEnabled bool,

View File

@@ -43,8 +43,8 @@
"domainName" = "Domain Name"
"monitor" = "Listen IP"
"certificate" = "Digital Certificate"
"fail" = " Failed"
"success" = " Successfully"
"fail" = "Failed"
"success" = "Successfully"
"getVersion" = "Get Version"
"install" = "Install"
"clients" = "Clients"
@@ -143,7 +143,7 @@
"destinationPort" = "Destination Port"
"targetAddress" = "Target Address"
"monitorDesc" = "Leave blank to listen on all IPs"
"meansNoLimit" = " = Unlimited. (unit: GB)"
"meansNoLimit" = "= Unlimited. (unit: GB)"
"totalFlow" = "Total Flow"
"leaveBlankToNeverExpire" = "Leave blank to never expire"
"noRecommendKeepDefault" = "It is recommended to keep the default"
@@ -225,9 +225,6 @@
"requestHeader" = "Request Header"
"responseHeader" = "Response Header"
[pages.inbounds.stream.quic]
"encryption" = "Encryption"
[pages.settings]
"title" = "Panel Settings"
"save" = "Save"
@@ -312,12 +309,14 @@
"fragment" = "Fragmentation"
"fragmentDesc" = "Enable fragmentation for TLS hello packet."
"fragmentSett" = "Fragmentation Settings"
"noisesDesc" = "Enable Noises."
"noisesSett" = "Noises Settings"
"mux" = "Mux"
"muxDesc" = "Transmit multiple independent data streams within an established data stream."
"muxSett" = "Mux Settings"
"direct" = "Direct Connection"
"directDesc" = "Directly establishes connections with domains or IP ranges of a specific country."
"directSett" = "Direct Connection Options"
[pages.xray]
"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"
"blockConfigs" = "Protection Shield"
"blockConfigsDesc" = "These options will block traffic based on specific requested protocols and websites."
"blockCountryConfigs" = "Block Country"
"blockCountryConfigsDesc" = "These options will block traffic based on the specific requested country."
"directCountryConfigs" = "Direct Country"
"directCountryConfigsDesc" = "A direct connection ensures that specific traffic is not routed through another server."
"blockConnectionsConfigs" = "Block Connections"
"blockConnectionsConfigsDesc" = "These options will block traffic based on the specific requested country."
"directConnectionsConfigs" = "Direct Connections"
"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"
"ipv4ConfigsDesc" = "These options will route traffic based on a specific destination via IPv4."
"warpConfigs" = "WARP Routing"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Blocks malware, phishing, and cryptominers websites."
"Speedtest" = "Block Speedtest"
"SpeedtestDesc" = "Blocks establishing connectins to speedtest websites."
"IRIp" = "Block Connection to Iran IPs"
"IRIpDesc" = "Blocks establishing connections to Iran IP ranges."
"IRDomain" = "Block Connection to Iran Domains"
"IRDomainDesc" = "Blocks establishing connections to Iran domains."
"ChinaIp" = "Block Connection to China IPs"
"ChinaIpDesc" = "Blocks establishing connections to China IP ranges."
"ChinaDomain" = "Block Connection to China Domains"
"ChinaDomainDesc" = "Blocks establishing connections to China domains."
"RussiaIp" = "Block Connection to Russia IPs"
"RussiaIpDesc" = "Blocks establishing connections to Russia IP ranges."
"RussiaDomain" = "Block Connection to Russia Domains"
"RussiaDomainDesc" = "Blocks establishing connections to Russia domains."
"VNIp" = "Block Connection to Vietnam IPs"
"VNIpDesc" = "Blocks establishing connections to Vietnam IP ranges."
"VNDomain" = "Block Connection to Vietnam Domains"
"VNDomainDesc" = "Blocks establishing connections to Vietnam domains."
"DirectIRIp" = "Direct Connection to Iran IPs"
"DirectIRIpDesc" = "Directly establishes connections to Iran IP ranges."
"DirectIRDomain" = "Direct Connection to Iran Domains"
"DirectIRDomainDesc" = "Directly establishes connections to Iran domains."
"DirectChinaIp" = "Direct Connection to China IPs"
"DirectChinaIpDesc" = "Directly establishes connections to China IP ranges."
"DirectChinaDomain" = "Direct Connection to China Domains"
"DirectChinaDomainDesc" = "Directly establishes connections to China domains."
"DirectRussiaIp" = "Direct Connection to Russia IPs"
"DirectRussiaIpDesc" = "Directly establishes connections to Russia IP ranges."
"DirectRussiaDomain" = "Direct Connection to Russia Domains"
"DirectRussiaDomainDesc" = "Directly establishes connections to Russia domains."
"DirectVNIp" = "Direct Connection to Vietnam IPs"
"DirectVNIpDesc" = "Directly establishes connections to Vietnam IP ranges."
"DirectVNDomain" = "Direct Connection to Vietnam Domains"
"DirectVNDomainDesc" = "Directly establishes connections to Vietnam domains."
"GoogleIPv4" = "Google"
"GoogleIPv4Desc" = "Routes traffic to Google via IPv4."
"NetflixIPv4" = "Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "The file path for the access log. The special value 'none' disabled access logs"
"errorLog" = "Error Log"
"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]
"first" = "First"
@@ -485,6 +460,7 @@
"add" = "Add Server"
"edit" = "Edit Server"
"domains" = "Domains"
"expectIPs" = "Expect IPs"
[pages.xray.fakedns]
"add" = "Add Fake DNS"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Confirm adding: {{ .Num }}"
"limitTraffic" = "🚧 Traffic Limit"
"getBanLogs" = "Get Ban Logs"
"allClients" = "All Clients"
[tgbot.answers]
"successfulOperation" = "✅ Operation successful!"
"errorOperation" = "❗ Error in operation."
"getInboundsFailed" = "❌ Failed to get inbounds."
"getClientsFailed" = "❌ Failed to get clients."
"canceled" = "❌ {{ .Email }}: Operation canceled."
"clientRefreshSuccess" = "✅ {{ .Email }}: Client refreshed successfully."
"IpRefreshSuccess" = "✅ {{ .Email }}: IPs refreshed successfully."
@@ -637,4 +615,6 @@
"removedTGUserSuccess" = "✅ {{ .Email }}: Telegram User removed successfully."
"enableSuccess" = "✅ {{ .Email }}: Enabled successfully."
"disableSuccess" = "✅ {{ .Email }}: Disabled successfully."
"askToAddUserId" = "Your configuration is not found!\r\nPlease ask your admin to use your Telegram ID in your configuration(s).\r\n\r\nYour User ID: <code>{{ .TgUserID }}</code>"
"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"
"targetAddress" = "Dirección de Destino"
"monitorDesc" = "Dejar en blanco por defecto"
"meansNoLimit" = " = illimitata. (unidad: GB)"
"meansNoLimit" = "= illimitata. (unidad: GB)"
"totalFlow" = "Flujo Total"
"leaveBlankToNeverExpire" = "Dejar en Blanco para Nunca Expirar"
"noRecommendKeepDefault" = "No hay requisitos especiales para mantener la configuración predeterminada"
@@ -225,9 +225,6 @@
"requestHeader" = "Encabezado de solicitud"
"responseHeader" = "Encabezado de respuesta"
[pages.inbounds.stream.quic]
"encryption" = "Cifrado"
[pages.settings]
"title" = "Configuraciones"
"save" = "Guardar"
@@ -312,12 +309,14 @@
"fragment" = "Fragmentación"
"fragmentDesc" = "Habilitar la fragmentación para el paquete de saludo de TLS"
"fragmentSett" = "Configuración de Fragmentación"
"noisesDesc" = "Activar Noises."
"noisesSett" = "Configuración de Noises"
"mux" = "Mux"
"muxDesc" = "Transmite múltiples flujos de datos independientes dentro de un flujo de datos establecido."
"muxSett" = "Configuración Mux"
"direct" = "Conexión Directa"
"directDesc" = "Establece conexiones directas con dominios o rangos de IP de un país específico."
"directSett" = "Opciones de Conexión Directa"
[pages.xray]
"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."
"blockConfigs" = "Configuraciones de Bloqueo"
"blockConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a protocolos y sitios web específicos."
"blockCountryConfigs" = "Configuraciones de Bloqueo por País"
"blockCountryConfigsDesc" = "Estas opciones evitarán que los usuarios se conecten a dominios de países específicos."
"directCountryConfigs" = "Configuraciones de Conexión Directa por País"
"directCountryConfigsDesc" = "Una conexión directa asegura que el tráfico específico no se enruta a través de otro servidor."
"blockConnectionsConfigs" = "Bloquear Conexiones"
"blockConnectionsConfigsDesc" = "Estas opciones bloquearán el tráfico según el país solicitado específico."
"directConnectionsConfigs" = "Conexiones Directas"
"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"
"ipv4ConfigsDesc" = "Estas opciones solo enrutarán a los dominios objetivo a través de IPv4."
"warpConfigs" = "Configuraciones de WARP"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Cambiar la plantilla de configuración para la protección de seguridad."
"Speedtest" = "Bloquear Sitios Web de Pruebas de Velocidad"
"SpeedtestDesc" = "Cambia la plantilla de configuración para evitar la conexión a sitios web de pruebas de velocidad."
"IRIp" = "Desactivar Conexión a Rangos de IP de Irán"
"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"
"GoogleIPv4Desc" = "Agregar enrutamiento para que Google se conecte con IPv4."
"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"
"errorLog" = "Registro 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]
"first" = "Primero"
@@ -483,6 +458,7 @@
"add" = "Agregar Servidor"
"edit" = "Editar Servidor"
"domains" = "Dominios"
"expectIPs" = "IPs esperadas"
[pages.xray.fakedns]
"add" = "Agregar DNS Falso"
@@ -592,7 +568,7 @@
"confirmResetTraffic" = "✅ ¿Confirmar Reinicio de Tráfico?"
"confirmClearIps" = "✅ ¿Confirmar Limpiar IPs?"
"confirmRemoveTGUser" = "✅ ¿Confirmar Eliminar Usuario de Telegram?"
"confirmToggle" = " ✅ ¿Confirmar habilitar/deshabilitar usuario?"
"confirmToggle" = "✅ ¿Confirmar habilitar/deshabilitar usuario?"
"dbBackup" = "Obtener Copia de Seguridad de BD"
"serverUsage" = "Uso del Servidor"
"getInbounds" = "Obtener Entradas"
@@ -616,11 +592,13 @@
"confirmNumberAdd" = "✅ Confirmar agregando: {{ .Num }}"
"limitTraffic" = "🚧 Límite de tráfico"
"getBanLogs" = "Registros de prohibición"
"allClients" = "Todos los Clientes"
[tgbot.answers]
"successfulOperation" = "✅ ¡Exitosa!"
"errorOperation" = "❗ Error en la Operación."
"getInboundsFailed" = "❌ Error al obtener las entradas"
"getClientsFailed" = "❌ No se pudo obtener los clientes."
"canceled" = "❌ {{ .Email }} : Operación cancelada."
"clientRefreshSuccess" = "✅ {{ .Email }} : Cliente actualizado exitosamente."
"IpRefreshSuccess" = "✅ {{ .Email }} : IPs actualizadas exitosamente."
@@ -635,4 +613,6 @@
"removedTGUserSuccess" = "✅ {{ .Email }} : Usuario de Telegram eliminado exitosamente."
"enableSuccess" = "✅ {{ .Email }} : Habilitado exitosamente."
"disableSuccess" = "✅ {{ .Email }} : Deshabilitado exitosamente."
"askToAddUserId" = "¡No se encuentra su configuración!\r\nPor favor, pídale a su administrador que use su ID de usuario de Telegram en su(s) configuración(es).\r\n\r\nSu ID de usuario: <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" = "آی‌پی اتصال"
"certificate" = "گواهی دیجیتال"
"fail" = "ناموفق"
"success" = " موفق"
"success" = "موفق"
"getVersion" = "دریافت نسخه"
"install" = "نصب"
"clients" = "کاربران"
@@ -109,7 +109,7 @@
"backup" = "پشتیبان‌گیری"
"backupTitle" = "پشتیبان‌گیری دیتابیس"
"backupDescription" = "توصیه‌می‌شود قبل‌از واردکردن یک دیتابیس جدید، نسخه پشتیبان تهیه ‌کنید"
"exportDatabase" = " پشتیبان‌گیری"
"exportDatabase" = "پشتیبان‌گیری"
"importDatabase" = "بازگرداندن"
[pages.inbounds]
@@ -225,9 +225,6 @@
"requestHeader" = "سربرگ درخواست"
"responseHeader" = "سربرگ پاسخ"
[pages.inbounds.stream.quic]
"encryption" = "رمزنگاری"
[pages.settings]
"title" = "تنظیمات پنل"
"save" = "ذخیره"
@@ -288,7 +285,7 @@
"timeZoneDesc" = "وظایف برنامه ریزی شده بر اساس این منطقه‌زمانی اجرا می‌شود"
"subSettings" = "سابسکریپشن"
"subEnable" = "فعال‌سازی سرویس سابسکریپشن"
"subEnableDesc" = " سرویس سابسکریپشن‌ را فعال‌می‌کند"
"subEnableDesc" = "سرویس سابسکریپشن‌ را فعال‌می‌کند"
"subListen" = "آدرس آی‌پی"
"subListenDesc" = "آدرس آی‌پی برای سرویس سابسکریپشن. برای گوش دادن به‌تمام آی‌پی‌ها خالی‌بگذارید"
"subPort" = "پورت"
@@ -312,12 +309,14 @@
"fragment" = "فرگمنت"
"fragmentDesc" = "فعال کردن فرگمنت برای بسته‌ی نخست تی‌ال‌اس"
"fragmentSett" = "تنظیمات فرگمنت"
"noisesDesc" = "فعال کردن Noises."
"noisesSett" = "تنظیمات Noises"
"mux" = "ماکس"
"muxDesc" = "چندین جریان داده مستقل را در یک جریان داده ثابت منتقل می کند"
"muxSett" = "تنظیمات ماکس"
"direct" = "اتصال مستقیم"
"directDesc" = "به طور مستقیم با دامنه ها یا محدوده آی‌پی یک کشور خاص ارتباط برقرار می کند"
"directSett" = "گزینه های اتصال مستقیم"
[pages.xray]
"title" = "پیکربندی ایکس‌ری"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "گزارش‌ها ممکن است بر کارایی سرور شما تأثیر بگذارد. توصیه می شود فقط در صورت نیاز آن را عاقلانه فعال کنید"
"blockConfigs" = "سپر محافظ"
"blockConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس پروتکل‌های درخواستی خاص، و وب سایت‌ها مسدود می‌کند"
"blockCountryConfigs" = "مسدودسازی کشور"
"blockCountryConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس کشور درخواستی خاص مسدود می‌کند"
"directCountryConfigs" = "اتصال مستقیم کشور"
"directCountryConfigsDesc" = "اتصال مستقیم اطمینان حاصل می‌کند که ترافیک خاص از طریق یک سرور دیگر هدایت نمی‌شود."
"blockConnectionsConfigs" = "مسدود کردن اتصالات"
"blockConnectionsConfigsDesc" = "این گزینه‌ها ترافیک را بر اساس کشور درخواست‌شده خاص مسدود می‌کنند."
"directConnectionsConfigs" = "اتصالات مستقیم"
"directConnectionsConfigsDesc" = "یک اتصال مستقیم تضمین می‌کند که ترافیک خاص از طریق سرور دیگری مسیریابی نشود."
"blockips" = "مسدود کردن آی‌پی‌ها"
"blockdomains" = "مسدود کردن دامنه‌ها"
"directips" = "آی‌پی‌های مستقیم"
"directdomains" = "دامنه‌های مستقیم"
"ipv4Configs" = "IPv4 مسیریابی"
"ipv4ConfigsDesc" = "این گزینه‌ها ترافیک‌ را از طریق آیپینسخه4 به مقصد هدایت می‌کند"
"warpConfigs" = "WARP مسیریابی"
@@ -357,38 +360,6 @@
"SecurityDesc" = "وب‌سایت‌های ناامن، بدافزار، فیشینگ، و کریپتوماینرها را مسدود می‌کند"
"Speedtest" = "مسدودسازی اسپیدتست"
"SpeedtestDesc" = "اتصال به وب‌سایت‌های تست سرعت را مسدود می‌کند"
"IRIp" = "مسدودسازی اتصال به آی‌پی‌های ایران"
"IRIpDesc" = "اتصال به آی‌پی‌های کشور ایران را مسدود می‌کند"
"IRDomain" = "مسدودسازی اتصال به دامنه‌های‌ ایران"
"IRDomainDesc" = "اتصال به دامنه‌های کشور ایران را مسدود می‌کند"
"ChinaIp" = "مسدودسازی اتصال به آی‌‌پی‌های چین"
"ChinaIpDesc" = "اتصال به آی‌پی‌های کشور چین را مسدود می‌کند"
"ChinaDomain" = "مسدودسازی اتصال به دامنه‌های چین"
"ChinaDomainDesc" = "اتصال به دامنه‌های کشور چین را مسدود می‌کند"
"RussiaIp" = "مسدودسازی اتصال به آی‌پی‌های روسیه"
"RussiaIpDesc" = "اتصال به آی‌پی‌های کشور روسیه را مسدود می‌کند"
"RussiaDomain" = "مسدودسازی اتصال به دامنه‌های روسیه"
"RussiaDomainDesc" = "اتصال به دامنه‌های کشور روسیه را مسدود می‌کند"
"VNIp" = "مسدودسازی اتصال به آی‌پی‌های ویتنام"
"VNIpDesc" = "اتصال به آی‌پی‌های کشور ویتنام را مسدود می‌کند"
"VNDomain" = "مسدودسازی اتصال به دامنه های ویتنام"
"VNDomainDesc" = "اتصال به دامنه‌های کشور ویتنام را مسدود می‌کند"
"DirectIRIp" = "اتصال مستقیم آی‌پی‌های ایران"
"DirectIRIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور ایران"
"DirectIRDomain" = "اتصال مستقیم دامنه‌های ایران"
"DirectIRDomainDesc" = "اتصال مستقیم به دامنه‌های کشور ایران"
"DirectChinaIp" = "اتصال مستقیم آی‌پی‌های چین"
"DirectChinaIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور چین"
"DirectChinaDomain" = "اتصال مستقیم دامنه‌های چین"
"DirectChinaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور چین"
"DirectRussiaIp" = "اتصال مستقیم آی‌پی‌های روسیه"
"DirectRussiaIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور روسیه"
"DirectRussiaDomain" = "اتصال مستقیم دامنه‌های روسیه"
"DirectRussiaDomainDesc" = "اتصال مستقیم به دامنه‌های کشور روسیه"
"DirectVNIp" = "اتصال مستقیم آی‌پی‌های ویتنام"
"DirectVNIpDesc" = "اتصال مستقیم به آی‌پی‌های کشور ویتنام"
"DirectVNDomain" = "اتصال مستقیم دامنه‌های ویتنام"
"DirectVNDomainDesc" = "اتصال مستقیم به دامنه‌های کشور ویتنام"
"GoogleIPv4" = "گوگل"
"GoogleIPv4Desc" = "ترافیک را از طریق آیپینسخه4 به گوگل هدایت می‌کند"
"NetflixIPv4" = "نتفلیکس"
@@ -402,11 +373,11 @@
"MetaWARP" = "متا"
"MetaWARPDesc" = "ترافیک را از طریق وارپ به متا (اینستاگرام، فیس بوک، واتساپ، تردز و...) هدایت می کند."
"AppleWARP" = "اپل"
"AppleWARPDesc" = " ترافیک را از طریق وارپ به اپل هدایت می‌کند"
"AppleWARPDesc" = "ترافیک را از طریق وارپ به اپل هدایت می‌کند"
"RedditWARP" = "ردیت"
"RedditWARPDesc" = " ترافیک را از طریق وارپ به ردیت هدایت می‌کند"
"RedditWARPDesc" = "ترافیک را از طریق وارپ به ردیت هدایت می‌کند"
"SpotifyWARP" = "اسپاتیفای"
"SpotifyWARPDesc" = " ترافیک را از طریق وارپ به اسپاتیفای هدایت می‌کند"
"SpotifyWARPDesc" = "ترافیک را از طریق وارپ به اسپاتیفای هدایت می‌کند"
"IRWARP" = "دامنه‌های ایران"
"IRWARPDesc" = "ترافیک را از طریق وارپ به دامنه‌های کشور ایران هدایت می‌کند"
"Inbounds" = "ورودی‌ها"
@@ -423,6 +394,10 @@
"accessLogDesc" = "مسیر فایل برای گزارش دسترسی. مقدار ویژه «هیچ» گزارش‌های دسترسی را غیرفعال میکند."
"errorLog" = "گزارش خطا"
"errorLogDesc" = "مسیر فایل برای ورود به سیستم خطا. مقدار ویژه «هیچ» گزارش های خطا را غیرفعال میکند"
"dnsLog" = "گزارش DNS"
"dnsLogDesc" = "آیا ثبت‌های درخواست DNS را فعال کنید"
"maskAddress" = "پنهان کردن آدرس"
"maskAddressDesc" = "پوشش آدرس IP، هنگامی که فعال می‌شود، به طور خودکار آدرس IP که در لاگ ظاهر می‌شود را جایگزین می‌کند."
[pages.xray.rules]
"first" = "اولین"
@@ -485,6 +460,7 @@
"add" = "افزودن سرور"
"edit" = "ویرایش سرور"
"domains" = "دامنه‌ها"
"expectIPs" = "آی‌پی‌های مورد انتظار"
[pages.xray.fakedns]
"add" = "افزودن دی‌ان‌اس جعلی"
@@ -517,10 +493,10 @@
"unlimited" = "♾ - نامحدود(ریست)"
"add" = "اضافه کردن"
"month" = "ماه"
"months" = "ماه‌ها"
"months" = "ماه‌"
"day" = "روز"
"days" = "روزها"
"hours" = "ساعت‌ها"
"days" = "روز"
"hours" = "ساعت‌"
"unknown" = "نامشخص"
"inbounds" = "ورودی‌ها"
"clients" = "کلاینت‌ها"
@@ -567,7 +543,7 @@
"inbound" = "📍 نام‌ورودی: {{ .Remark }}\r\n"
"port" = "🔌 پورت: {{ .Port }}\r\n"
"expire" = "📅 تاریخ‌انقضا: {{ .Time }}\r\n\r\n"
"expireIn" = "📅 باقی‌مانده‌تاانقضا: {{ .Time }}\r\n\r\n"
"expireIn" = "📅 باقی‌ مانده‌ تا انقضا: {{ .Time }}\r\n\r\n"
"active" = "💡 فعال: {{ .Enable }}\r\n"
"enabled" = "🚨 وضعیت: {{ .Enable }}\r\n"
"online" = "🌐 وضعیت اتصال: {{ .Status }}\r\n"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ تایید اضافه کردن: {{ .Num }}"
"limitTraffic" = "🚧 محدودیت ترافیک"
"getBanLogs" = "گزارش های بلوک را دریافت کنید"
"allClients" = "همه مشتریان"
[tgbot.answers]
"successfulOperation" = "✅ انجام شد!"
"errorOperation" = "❗ خطا در عملیات."
"getInboundsFailed" = "❌ دریافت ورودی‌ها با خطا مواجه شد."
"getClientsFailed" = "❌ دریافت مشتریان با شکست مواجه شد."
"canceled" = "❌ {{ .Email }} : عملیات لغو شد."
"clientRefreshSuccess" = "✅ {{ .Email }} : کلاینت با موفقیت تازه‌سازی شد."
"IpRefreshSuccess" = "✅ {{ .Email }} : آدرس‌ها با موفقیت تازه‌سازی شدند."
@@ -638,3 +616,5 @@
"enableSuccess" = "✅ {{ .Email }} : با موفقیت فعال شد."
"disableSuccess" = "✅ {{ .Email }} : با موفقیت غیرفعال شد."
"askToAddUserId" = "پیکربندی شما یافت نشد!\r\nلطفاً از مدیر خود بخواهید که شناسه کاربر تلگرام خود را در پیکربندی (های) خود استفاده کند.\r\n\r\nشناسه کاربری شما: <code>{{ .TgUserID }}</code>"
"chooseClient" = "یک مشتری برای ورودی {{ .Inbound }} انتخاب کنید"
"chooseInbound" = "یک ورودی انتخاب کنید"

View File

@@ -143,7 +143,7 @@
"destinationPort" = "Port Tujuan"
"targetAddress" = "Alamat Target"
"monitorDesc" = "Biarkan kosong untuk mendengarkan semua IP"
"meansNoLimit" = " = Unlimited. (unit: GB)"
"meansNoLimit" = "= Unlimited. (unit: GB)"
"totalFlow" = "Total Aliran"
"leaveBlankToNeverExpire" = "Biarkan kosong untuk tidak pernah kedaluwarsa"
"noRecommendKeepDefault" = "Disarankan untuk tetap menggunakan pengaturan default"
@@ -225,9 +225,6 @@
"requestHeader" = "Header Permintaan"
"responseHeader" = "Header Respons"
[pages.inbounds.stream.quic]
"encryption" = "Enkripsi"
[pages.settings]
"title" = "Pengaturan Panel"
"save" = "Simpan"
@@ -312,12 +309,14 @@
"fragment" = "Fragmentasi"
"fragmentDesc" = "Aktifkan fragmentasi untuk paket hello TLS"
"fragmentSett" = "Pengaturan Fragmentasi"
"noisesDesc" = "Aktifkan Noises."
"noisesSett" = "Pengaturan Noises"
"mux" = "Mux"
"muxDesc" = "Mengirimkan beberapa aliran data independen dalam aliran data yang sudah ada."
"muxSett" = "Pengaturan Mux"
"direct" = "Koneksi langsung"
"directDesc" = "Secara langsung membuat koneksi dengan domain atau rentang IP negara tertentu."
"directSett" = "Opsi Koneksi Langsung"
[pages.xray]
"title" = "Konfigurasi Xray"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Log dapat mempengaruhi efisiensi server Anda. Disarankan untuk mengaktifkannya dengan bijak hanya jika diperlukan"
"blockConfigs" = "Pelindung"
"blockConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan protokol dan situs web yang diminta."
"blockCountryConfigs" = "Blokir Negara"
"blockCountryConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan negara yang diminta."
"directCountryConfigs" = "Langsung ke Negara"
"directCountryConfigsDesc" = "Koneksi langsung memastikan bahwa lalu lintas tertentu tidak diarahkan melalui server lain."
"blockConnectionsConfigs" = "Blokir Koneksi"
"blockConnectionsConfigsDesc" = "Opsi ini akan memblokir lalu lintas berdasarkan negara yang diminta."
"directConnectionsConfigs" = "Koneksi Langsung"
"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"
"ipv4ConfigsDesc" = "Opsi ini akan mengalihkan lalu lintas berdasarkan tujuan tertentu melalui IPv4."
"warpConfigs" = "Pengalihan WARP"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Memblokir situs web malware, phishing, dan penambang kripto."
"Speedtest" = "Blokir 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"
"GoogleIPv4Desc" = "Rute lalu lintas ke Google melalui IPv4."
"NetflixIPv4" = "Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "Jalur file untuk log akses. Nilai khusus 'tidak ada' menonaktifkan log akses"
"errorLog" = "Catatan eror"
"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]
"first" = "Pertama"
@@ -485,6 +460,7 @@
"add" = "Tambahkan Server"
"edit" = "Sunting Server"
"domains" = "Domains"
"expectIPs" = "IP yang Diharapkan"
[pages.xray.fakedns]
"add" = "Tambahkan DNS Palsu"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Konfirmasi menambahkan: {{ .Num }}"
"limitTraffic" = "🚧 Batas Lalu Lintas"
"getBanLogs" = "Dapatkan Log Pemblokiran"
"allClients" = "Semua Klien"
[tgbot.answers]
"successfulOperation" = "✅ Operasi berhasil!"
"errorOperation" = "❗ Kesalahan dalam operasi."
"getInboundsFailed" = "❌ Gagal mendapatkan inbounds."
"getClientsFailed" = "❌ Gagal mendapatkan klien."
"canceled" = "❌ {{ .Email }}: Operasi dibatalkan."
"clientRefreshSuccess" = "✅ {{ .Email }}: Klien diperbarui dengan berhasil."
"IpRefreshSuccess" = "✅ {{ .Email }}: IP diperbarui dengan berhasil."
@@ -637,4 +615,6 @@
"removedTGUserSuccess" = "✅ {{ .Email }}: Pengguna Telegram dihapus dengan berhasil."
"enableSuccess" = "✅ {{ .Email }}: Diaktifkan 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" = "Порт назначения"
"targetAddress" = "Целевой адрес"
"monitorDesc" = "Оставьте пустым по умолчанию"
"meansNoLimit" = " = Без ограничений (значение: ГБ)"
"meansNoLimit" = "= Без ограничений (значение: ГБ)"
"totalFlow" = "Общий расход"
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
"noRecommendKeepDefault" = "Нет требований для сохранения настроек по умолчанию"
@@ -225,9 +225,6 @@
"requestHeader" = "Заголовок запроса"
"responseHeader" = "Заголовок ответа"
[pages.inbounds.stream.quic]
"encryption" = "Шифрование"
[pages.settings]
"title" = "Настройки"
"save" = "Сохранить"
@@ -268,7 +265,7 @@
"telegramTokenDesc" = "Необходимо получить токен у менеджера ботов Telegram @botfather"
"telegramProxy" = "Прокси Socks5"
"telegramProxyDesc" = "Если для подключения к Telegram вам нужен прокси Socks5. Настройте его параметры согласно руководству."
"telegramChatId" = "Telegram ID админа бота"
"telegramChatId" = "Telegram ChatID админа бота"
"telegramChatIdDesc" = "Множественные идентификаторы чата, разделенные запятыми. Чтобы получить свои идентификаторы чатов, используйте @userinfobot или команду '/id' в боте."
"telegramNotifyTime" = "Частота уведомлений бота Telegram"
"telegramNotifyTimeDesc" = "Используйте формат времени Crontab"
@@ -312,12 +309,14 @@
"fragment" = "Фрагментация"
"fragmentDesc" = "Включить фрагментацию для пакета приветствия TLS"
"fragmentSett" = "Настройки фрагментации"
"noisesDesc" = "Включить Noises."
"noisesSett" = "Настройки Noises"
"mux" = "Mux"
"muxDesc" = "Передача нескольких независимых потоков данных в рамках установленного потока данных."
"muxSett" = "Mux Настройки"
"direct" = "Прямая связь"
"directDesc" = "Напрямую устанавливает соединения с доменами или диапазонами IP конкретной страны."
"directSett" = "Варианты прямого подключения"
[pages.xray]
"title" = "Настройки Xray"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Журналы могут повлиять на эффективность вашего сервера. Рекомендуется включать их с умом только в случае ваших нужд!"
"blockConfigs" = "Блокировка конфигураций"
"blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам"
"blockCountryConfigs" = "Конфигурации блокировки страны"
"blockCountryConfigsDesc" = "Эти параметры не позволят пользователям подключаться к доменам определенной страны"
"directCountryConfigs" = "Настройки прямого подключения для страны"
"directCountryConfigsDesc" = "Прямое подключение обеспечивает, что конкретный трафик не направляется через другой сервер."
"blockConnectionsConfigs" = "Блокировать соединения"
"blockConnectionsConfigsDesc" = "Эти параметры будут блокировать трафик в зависимости от запрашиваемой страны."
"directConnectionsConfigs" = "Прямые соединения"
"directConnectionsConfigsDesc" = "Прямое соединение гарантирует, что определенный трафик не будет перенаправлен через другой сервер."
"blockips" = "Блокировать IP"
"blockdomains" = "Блокировать домены"
"directips" = "Прямые IP"
"directdomains" = "Прямые домены"
"ipv4Configs" = "Настройки IPv4"
"ipv4ConfigsDesc" = "Эти параметры позволят пользователям маршрутизироваться к целевым доменам только через IPv4"
"warpConfigs" = "Настройки WARP"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Изменение шаблона конфигурации для защиты безопасности."
"Speedtest" = "Блокировать сайты для проверки скорости"
"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"
"GoogleIPv4Desc" = "Добавить маршрутизацию для Google для подключения к IPv4"
"NetflixIPv4" = "Использовать IPv4 для Netflix"
@@ -398,7 +369,7 @@
"OpenAIWARP" = "OpenAI (ChatGPT)"
"OpenAIWARPDesc" = "Направляет трафик в OpenAI (ChatGPT) через WARP."
"NetflixWARP" = "Netflix"
"NetflixWARPDesc" = "Направляет трафик в Apple через WARP."
"NetflixWARPDesc" = "Направляет трафик в Netflix через WARP."
"MetaWARP" = "Мета"
"MetaWARPDesc" = "Направляет трафик в Meta (Instagram, Facebook, WhatsApp, Threads...) через WARP."
"AppleWARP" = "Apple"
@@ -423,6 +394,10 @@
"accessLogDesc" = "Путь к файлу журнала доступа. Специальное значение «none» отключило журналы доступа."
"errorLog" = "Журнал ошибок"
"errorLogDesc" = "Путь к файлу журнала ошибок. Специальное значение «none» отключает журналы ошибок."
"dnsLog" = "DNS Журнал"
"dnsLogDesc" = "Включить логи запросов DNS"
"maskAddress" = "Маскировать Адрес"
"maskAddressDesc" = "Маска IP-адреса, при активации автоматически заменяет IP-адрес, который появляется в логе."
[pages.xray.rules]
"first" = "Первый"
@@ -485,6 +460,7 @@
"add" = "Добавить сервер"
"edit" = "Редактировать сервер"
"domains" = "Домены"
"expectIPs" = "Ожидаемые IP"
[pages.xray.fakedns]
"add" = "Добавить поддельный DNS"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Подтвердить добавление: {{ .Num }}"
"limitTraffic" = "🚧 Лимит трафика"
"getBanLogs" = "Логи блокировок"
"allClients" = "Все клиенты"
[tgbot.answers]
"successfulOperation" = "✅ Успешный!"
"errorOperation" = "❗ Ошибка в операции."
"getInboundsFailed" = "❌ Не удалось получить входящие потоки."
"getClientsFailed" = "❌ Не удалось получить клиентов."
"canceled" = "❌ {{ .Email }}: Операция отменена."
"clientRefreshSuccess" = "✅ {{ .Email }}: Клиент успешно обновлен."
"IpRefreshSuccess" = "✅ {{ .Email }}: IP-адреса успешно обновлены."
@@ -638,3 +616,5 @@
"enableSuccess" = "✅ {{ .Email }}: Включено успешно."
"disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Выберите пользователя для подключения {{ .Inbound }}"
"chooseInbound" = "Выберите подключение"

View File

@@ -143,7 +143,7 @@
"destinationPort" = "Hedef Port"
"targetAddress" = "Hedef Adres"
"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ış"
"leaveBlankToNeverExpire" = "Hiçbir zaman sona ermemesi için boş bırakın"
"noRecommendKeepDefault" = "Varsayılanı korumanız önerilir"
@@ -225,9 +225,6 @@
"requestHeader" = "İstek Başlığı"
"responseHeader" = "Yanıt Başlığı"
[pages.inbounds.stream.quic]
"encryption" = "Şifreleme"
[pages.settings]
"title" = "Panel Ayarları"
"save" = "Kaydet"
@@ -312,12 +309,14 @@
"fragment" = "Parçalama"
"fragmentDesc" = "TLS merhaba paketinin parçalanmasını etkinleştir."
"fragmentSett" = "Parçalama Ayarları"
"noisesDesc" = "Noises'i Etkinleştir."
"noisesSett" = "Noises Ayarları"
"mux" = "Mux"
"muxDesc" = "Kurulmuş bir veri akışında birden çok bağımsız veri akışını iletir."
"muxSett" = "Mux Ayarları"
"direct" = "Doğrudan Bağlantı"
"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]
"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"
"blockConfigs" = "Koruma Kalkanı"
"blockConfigsDesc" = "Bu seçenekler belirli istek protokolleri ve web siteleri temelinde trafiği engeller."
"blockCountryConfigs" = "Ülke Engelleme"
"blockCountryConfigsDesc" = "Bu seçenekler belirli istek ülkesi temelinde trafiği engeller."
"directCountryConfigs" = "Doğrudan Ülke"
"directCountryConfigsDesc" = "Doğrudan bağlantı, belirli trafiğin başka bir sunucu üzerinden yönlendirilmemesini sağlar."
"blockConnectionsConfigs" = "Bağlantıları Engelle"
"blockConnectionsConfigsDesc" = "Bu seçenekler belirli bir istenen ülkeye göre trafiği engelleyecektir."
"directConnectionsConfigs" = "Doğrudan Bağlantı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"
"ipv4ConfigsDesc" = "Bu seçenekler belirli bir varış yerine IPv4 üzerinden trafiği yönlendirir."
"warpConfigs" = "WARP Yönlendirme"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Kötü amaçlı yazılım, kimlik avı ve kripto madencilik web sitelerini engeller."
"Speedtest" = "Speedtest Bağlantısını Engelle"
"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"
"GoogleIPv4Desc" = "Google'a trafiği IPv4 üzerinden yönlendirir."
"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"
"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"
"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]
"first" = "İlk"
@@ -485,6 +460,7 @@
"add" = "Sunucu Ekle"
"edit" = "Sunucuyu Düzenle"
"domains" = "Alan Adları"
"expectIPs" = "Beklenen IP'ler"
[pages.xray.fakedns]
"add" = "Sahte DNS Ekle"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Ekleme onayı: {{ .Num }}"
"limitTraffic" = "🚧 Trafik Sınırı"
"getBanLogs" = "Yasak Günlüklerini Al"
"allClients" = "Tüm Müşteriler"
[tgbot.answers]
"successfulOperation" = "✅ İşlem başarılı!"
"errorOperation" = "❗ İşlemde hata."
"getInboundsFailed" = "❌ Gelenler alınamadı."
"getClientsFailed" = "❌ Müşteriler alınamadı."
"canceled" = "❌ {{ .Email }}: İşlem iptal edildi."
"clientRefreshSuccess" = "✅ {{ .Email }}: Müşteri 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ı."
"enableSuccess" = "✅ {{ .Email }}: Başarıyla etkinleştirildi."
"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" = "Доменне ім`я"
"monitor" = "Слухати IP"
"certificate" = "Цифровий сертифікат"
"fail" = " Помилка"
"success" = " Успішно"
"fail" = "Помилка"
"success" = "Успішно"
"getVersion" = "Отримати версію"
"install" = "Встановити"
"clients" = "Клієнти"
@@ -143,7 +143,7 @@
"destinationPort" = "Порт призначення"
"targetAddress" = "Цільова адреса"
"monitorDesc" = "Залиште порожнім, щоб слухати всі IP-адреси"
"meansNoLimit" = " = Необмежено. (одиниця: ГБ)"
"meansNoLimit" = "= Необмежено. (одиниця: ГБ)"
"totalFlow" = "Загальна витрата"
"leaveBlankToNeverExpire" = "Залиште порожнім, щоб ніколи не закінчувався"
"noRecommendKeepDefault" = "Рекомендується зберегти значення за замовчуванням"
@@ -225,9 +225,6 @@
"requestHeader" = "Заголовок запиту"
"responseHeader" = "Заголовок відповіді"
[pages.inbounds.stream.quic]
"encryption" = "Шифрування"
[pages.settings]
"title" = "Параметри панелі"
"save" = "Зберегти"
@@ -312,12 +309,14 @@
"fragment" = "Фрагментація"
"fragmentDesc" = "Увімкнути фрагментацію для пакету привітання TLS"
"fragmentSett" = "Параметри фрагментації"
"noisesDesc" = "Увімкнути Noises."
"noisesSett" = "Налаштування Noises"
"mux" = "Mux"
"muxDesc" = "Передавати кілька незалежних потоків даних у межах встановленого потоку даних."
"muxSett" = "Налаштування Mux"
"direct" = "Пряме підключення"
"directDesc" = "Безпосередньо встановлює з’єднання з доменами або діапазонами IP певної країни."
"directSett" = "Параметри прямого підключення"
[pages.xray]
"title" = "Xray конфігурації"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "Журнали можуть вплинути на ефективність вашого сервера. Рекомендується вмикати його з розумом лише у випадку ваших потреб"
"blockConfigs" = "Захисний екран"
"blockConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретних запитуваних протоколів і веб-сайтів."
"blockCountryConfigs" = "Заблокувати країну"
"blockCountryConfigsDesc" = "Ці параметри блокуватимуть трафік на основі конкретної запитуваної країни."
"directCountryConfigs" = "Пряма країна"
"directCountryConfigsDesc" = "Пряме підключення забезпечує, що конкретний трафік не маршрутизується через інший сервер."
"blockConnectionsConfigs" = "Блокувати з'єднання"
"blockConnectionsConfigsDesc" = "Ці параметри блокуватимуть трафік на основі запитаних країн."
"directConnectionsConfigs" = "Прямі з'єднання"
"directConnectionsConfigsDesc" = "Пряме з'єднання гарантує, що певний трафік не буде маршрутизовано через інший сервер."
"blockips" = "Блокувати IP"
"blockdomains" = "Блокувати домени"
"directips" = "Прямі IP"
"directdomains" = "Прямі домени"
"ipv4Configs" = "Маршрутизація IPv4"
"ipv4ConfigsDesc" = "Ці параметри спрямовуватимуть трафік на основі певного призначення через IPv4."
"warpConfigs" = "WARP маршрутизація"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Блокує веб-сайти шкідливого програмного забезпечення, фішингу та майнерів."
"Speedtest" = "Заблокувати Speedtest"
"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"
"GoogleIPv4Desc" = "Направляє трафік до Google через IPv4."
"NetflixIPv4" = "Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "Шлях до файлу журналу доступу. Спеціальне значення 'none' вимикає журнали доступу"
"errorLog" = "Журнал помилок"
"errorLogDesc" = "Шлях до файлу журналу помилок. Спеціальне значення 'none' вимикає журнали помилок"
"dnsLog" = "Журнал DNS"
"dnsLogDesc" = "Чи включити журнали запитів DNS"
"maskAddress" = "Маскувати Адресу"
"maskAddressDesc" = "Маска IP-адреси, при активації автоматично замінює IP-адресу, яка з'являється у журналі."
[pages.xray.rules]
"first" = "Перший"
@@ -485,6 +460,7 @@
"add" = "Додати сервер"
"edit" = "Редагувати сервер"
"domains" = "Домени"
"expectIPs" = "Очікувані IP"
[pages.xray.fakedns]
"add" = "Додати підроблений DNS"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Підтвердити додавання: {{ .Num }}"
"limitTraffic" = "🚧 Ліміт трафіку"
"getBanLogs" = "Отримати журнали заборон"
"allClients" = "Всі Клієнти"
[tgbot.answers]
"successfulOperation" = "✅ Операція успішна!"
"errorOperation" = "❗ Помилка в роботі."
"getInboundsFailed" = "❌ Не вдалося отримати вхідні повідомлення."
"getClientsFailed" = "❌ Не вдалося отримати клієнтів."
"canceled" = "❌ {{ .Email }}: Операцію скасовано."
"clientRefreshSuccess" = "✅ {{ .Email }}: Клієнт успішно оновлено."
"IpRefreshSuccess" = "✅ {{ .Email }}: IP-адреси успішно оновлено."
@@ -638,3 +616,5 @@
"enableSuccess" = "✅ {{ .Email }}: Увімкнути успішно."
"disableSuccess" = "✅ {{ .Email }}: Успішно вимкнено."
"askToAddUserId" = "Вашу конфігурацію не знайдено!\r\nБудь ласка, попросіть свого адміністратора використовувати ваш ідентифікатор Telegram у вашій конфігурації.\r\n\r\nВаш ідентифікатор користувача: <code>{{ .TgUserID }}</code>"
"chooseClient" = "Виберіть клієнта для Вхідного {{ .Inbound }}"
"chooseInbound" = "Виберіть Вхідний"

View File

@@ -143,7 +143,7 @@
"destinationPort" = "Cổng đích"
"targetAddress" = "Địa chỉ mục tiêu"
"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"
"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"
@@ -225,9 +225,6 @@
"requestHeader" = "Header yêu cầu"
"responseHeader" = "Header phản hồi"
[pages.inbounds.stream.quic]
"encryption" = "Mã hóa"
[pages.settings]
"title" = "Cài đặt"
"save" = "Lưu"
@@ -312,12 +309,14 @@
"fragment" = "Sự phân mảnh"
"fragmentDesc" = "Kích hoạt phân mảnh cho gói TLS hello"
"fragmentSett" = "Cài đặt phân mảnh"
"noisesDesc" = "Bật Noises."
"noisesSett" = "Cài đặt Noises"
"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."
"muxSett" = "Mux Cài đặt"
"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ể."
"directSett" = "Tùy chọn kết nối trực tiếp"
[pages.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"
"blockConfigs" = "Cấu hình Chặn"
"blockConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các giao thức và trang web cụ thể."
"blockCountryConfigs" = "Cấu hình Chặn Quốc gia"
"blockCountryConfigsDesc" = "Những tùy chọn này sẽ ngăn người dùng kết nối đến các tên miền quốc gia cụ thể."
"directCountryConfigs" = "Cấu hình Kết nối Trực tiếp Quốc gia"
"directCountryConfigsDesc" = "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."
"blockConnectionsConfigs" = "Chặn Kết Nối"
"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ể."
"directConnectionsConfigs" = "Kết Nối Trực Tiếp"
"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"
"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"
@@ -357,38 +360,6 @@
"SecurityDesc" = "Thay đổi mẫu cấu hình để bảo vệ Bảo mật."
"Speedtest" = "Chặn Trang web Speedtest"
"SpeedtestDesc" = "Thay đổi mẫu cấu hình để tránh kết nối đến các trang web Speedtest."
"IRIp" = "Vô hiệu hóa kết nối đến dải IP của Iran"
"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"
"GoogleIPv4Desc" = "Thêm định tuyến cho Google để kết nối qua IPv4."
"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'"
"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'"
"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]
"first" = "Đầu tiên"
@@ -485,6 +460,7 @@
"add" = "Thêm máy chủ"
"edit" = "Chỉnh sửa máy chủ"
"domains" = "Tên miền"
"expectIPs" = "Các IP Dự Kiến"
[pages.xray.fakedns]
"add" = "Thêm DNS giả"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ Xác nhận thêm: {{ .Num }}"
"limitTraffic" = "🚧 Giới hạn lưu lượng"
"getBanLogs" = "Cấm nhật ký"
"allClients" = "Tất cả Khách hàng"
[tgbot.answers]
"successfulOperation" = "✅ Thành công!"
"errorOperation" = "❗ Lỗi Trong Quá Trình Thực Hiện."
"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."
"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."
@@ -638,3 +616,5 @@
"enableSuccess" = "✅ {{ .Email }} : Đã Bật Thành Công."
"disableSuccess" = "✅ {{ .Email }} : Đã Tắt Thành Công."
"askToAddUserId" = "Cấu hình của bạn không được tìm thấy!\r\nVui lòng yêu cầu Quản trị viên sử dụng ID người dùng telegram của bạn trong cấu hình của bạn.\r\n\r\nID người dùng của bạn: <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" = "目标端口"
"targetAddress" = "目标地址"
"monitorDesc" = "留空表示监听所有 IP"
"meansNoLimit" = " = 无限制单位GB)"
"meansNoLimit" = "= 无限制单位GB)"
"totalFlow" = "总流量"
"leaveBlankToNeverExpire" = "留空表示永不过期"
"noRecommendKeepDefault" = "建议保留默认值"
@@ -225,9 +225,6 @@
"requestHeader" = "请求头"
"responseHeader" = "响应头"
[pages.inbounds.stream.quic]
"encryption" = "加密"
[pages.settings]
"title" = "面板设置"
"save" = "保存"
@@ -312,12 +309,14 @@
"fragment" = "分片"
"fragmentDesc" = "启用 TLS hello 数据包分片"
"fragmentSett" = "设置"
"noisesDesc" = "启用 Noises."
"noisesSett" = "Noises 设置"
"mux" = "多路复用器"
"muxDesc" = "在已建立的数据流内传输多个独立的数据流"
"muxSett" = "复用器设置"
"direct" = "直接连接"
"directDesc" = "直接与特定国家的域或IP范围建立连接"
"directSett" = "直接连接选项"
[pages.xray]
"title" = "Xray 配置"
@@ -331,10 +330,14 @@
"logConfigsDesc" = "日志可能会影响服务器的性能,建议仅在需要时启用"
"blockConfigs" = "防护屏蔽"
"blockConfigsDesc" = "这些选项将阻止用户连接到特定协议和网站"
"blockCountryConfigs" = "屏蔽国家/地区"
"blockCountryConfigsDesc" = "这些选项将阻止用户连接到特定国家/地区"
"directCountryConfigs" = "直连国家/地区"
"directCountryConfigsDesc" = "直接连接确保特定流量不会通过其他服务器路由"
"blockConnectionsConfigs" = "阻止连接"
"blockConnectionsConfigsDesc" = "这些选项将根据特定的请求国家阻止流量。"
"directConnectionsConfigs" = "直接连接"
"directConnectionsConfigsDesc" = "直接连接确保特定流量不会通过其他服务器路由"
"blockips" = "阻止IP"
"blockdomains" = "阻止域名"
"directips" = "直接IP"
"directdomains" = "直接域名"
"ipv4Configs" = "IPv4 路由"
"ipv4ConfigsDesc" = "此选项将仅通过 IPv4 路由到目标域"
"warpConfigs" = "WARP 路由"
@@ -357,38 +360,6 @@
"SecurityDesc" = "屏蔽恶意软件、网络钓鱼和挖矿网站"
"Speedtest" = "屏蔽测速网站"
"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"
"GoogleIPv4Desc" = "通过 IPv4 将流量路由到谷歌"
"NetflixIPv4" = "Netflix"
@@ -423,6 +394,10 @@
"accessLogDesc" = "访问日志的文件路径。特殊值 'none' 禁用访问日志"
"errorLog" = "错误日志"
"errorLogDesc" = "错误日志的文件路径。特殊值 'none' 禁用错误日志"
"dnsLog" = "DNS 日志"
"dnsLogDesc" = "是否启用 DNS 查询日志"
"maskAddress" = "隐藏地址"
"maskAddressDesc" = "IP 地址掩码,启用时会自动替换日志中出现的 IP 地址。"
[pages.xray.rules]
"first" = "置顶"
@@ -485,6 +460,7 @@
"add" = "添加服务器"
"edit" = "编辑服务器"
"domains" = "域"
"expectIPs" = "预期 IP"
[pages.xray.fakedns]
"add" = "添加假 DNS"
@@ -498,7 +474,7 @@
"loginSecurity" = "登录安全"
"loginSecurityDesc" = "添加额外的身份验证以提高安全性"
"secretToken" = "安全令牌"
"secretTokenDesc" = "请将此令牌存储在安全的地方。此令牌用于登录,丢失无法恢复。"
"secretTokenDesc" = "请将此令牌存储在安全的地方。此令牌用于登录,丢失无法恢复。"
[pages.settings.toasts]
"modifySettings" = "修改设置"
@@ -618,11 +594,13 @@
"confirmNumberAdd" = "✅ 确认添加:{{ .Num }}"
"limitTraffic" = "🚧 流量限制"
"getBanLogs" = "禁止日志"
"allClients" = "所有客户"
[tgbot.answers]
"successfulOperation" = "✅ 成功!"
"errorOperation" = "❗ 操作错误。"
"getInboundsFailed" = "❌ 获取入站信息失败。"
"getClientsFailed" = "❌ 获取客户失败。"
"canceled" = "❌ {{ .Email }}:操作已取消。"
"clientRefreshSuccess" = "✅ {{ .Email }}:客户端刷新成功。"
"IpRefreshSuccess" = "✅ {{ .Email }}IP 刷新成功。"
@@ -637,4 +615,6 @@
"removedTGUserSuccess" = "✅ {{ .Email }}Telegram 用户已成功移除。"
"enableSuccess" = "✅ {{ .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"
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
echo "Your OS is Arch Linux"
elif [[ "${release}" == "parch" ]]; then
echo "Your OS is Parch linux"
echo "Your OS is Parch Linux"
elif [[ "${release}" == "manjaro" ]]; then
echo "Your OS is Manjaro"
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
fi
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
fi
elif [[ "${release}" == "fedora" ]]; then
if [[ ${os_version} -lt 36 ]]; then
echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1
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
if [[ ${os_version} -lt 11 ]]; then
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "almalinux" ]]; then
if [[ ${os_version} -lt 9 ]]; then
echo -e "${red} Please use AlmaLinux 9 or higher ${plain}\n" && exit 1
if [[ ${os_version} -lt 80 ]]; then
echo -e "${red} Please use AlmaLinux 8.0 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "rocky" ]]; then
if [[ ${os_version} -lt 9 ]]; then
echo -e "${red} Please use Rocky Linux 9 or higher ${plain}\n" && exit 1
if [[ ${os_version} -lt 8 ]]; then
echo -e "${red} Please use Rocky Linux 8 or higher ${plain}\n" && exit 1
fi
elif [[ "${release}" == "oracle" ]]; then
if [[ ${os_version} -lt 8 ]]; then
@@ -87,12 +91,12 @@ else
echo "- Parch Linux"
echo "- Manjaro"
echo "- Armbian"
echo "- AlmaLinux 9+"
echo "- Rocky Linux 9+"
echo "- AlmaLinux 8.0+"
echo "- Rocky Linux 8+"
echo "- Oracle Linux 8+"
echo "- OpenSUSE Tumbleweed"
echo "- Amazon Linux 2023"
exit 1
fi
# Declare Variables
@@ -474,7 +478,7 @@ enable_bbr() {
centos | almalinux | rocky | oracle)
yum -y update && yum -y install ca-certificates
;;
fedora)
fedora | amzn)
dnf -y update && dnf -y install ca-certificates
;;
arch | manjaro | parch)
@@ -814,7 +818,7 @@ ssl_cert_issue() {
centos | almalinux | rocky | oracle)
yum -y update && yum -y install socat
;;
fedora)
fedora | amzn)
dnf -y update && dnf -y install socat
;;
arch | manjaro | parch)
@@ -978,33 +982,6 @@ ssl_cert_issue_CF() {
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() {
# Check if Speedtest is already installed
if ! command -v speedtest &>/dev/null; then
@@ -1192,7 +1169,7 @@ install_iplimit() {
yum update -y && yum install epel-release -y
yum -y install fail2ban
;;
fedora)
fedora | amzn)
dnf -y update && dnf -y install fail2ban
;;
arch | manjaro | parch)
@@ -1273,7 +1250,7 @@ remove_iplimit() {
yum remove fail2ban -y
yum autoremove -y
;;
fedora)
fedora | amzn)
dnf remove fail2ban -y
dnf autoremove -y
;;
@@ -1349,15 +1326,14 @@ show_menu() {
${green}18.${plain} SSL Certificate Management
${green}19.${plain} Cloudflare SSL Certificate
${green}20.${plain} IP Limit Management
${green}21.${plain} WARP Management
${green}22.${plain} Firewall Management
${green}21.${plain} Firewall Management
————————————————
${green}23.${plain} Enable BBR
${green}24.${plain} Update Geo Files
${green}25.${plain} Speedtest by Ookla
${green}22.${plain} Enable BBR
${green}23.${plain} Update Geo Files
${green}24.${plain} Speedtest by Ookla
"
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
0)
@@ -1424,22 +1400,19 @@ show_menu() {
iplimit_main
;;
21)
warp_cloudflare
;;
22)
firewall_menu
;;
23)
22)
bbr_menu
;;
24)
23)
update_geo
;;
25)
24)
run_speedtest
;;
*)
LOGE "Please enter the correct number [0-25]"
LOGE "Please enter the correct number [0-24]"
;;
esac
}

View File

@@ -14,6 +14,7 @@ type InboundConfig struct {
StreamSettings json_util.RawMessage `json:"streamSettings"`
Tag string `json:"tag"`
Sniffing json_util.RawMessage `json:"sniffing"`
Allocate json_util.RawMessage `json:"allocate"`
}
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) {
return false
}
if !bytes.Equal(c.Allocate, other.Allocate) {
return false
}
return true
}