Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43713fbdf8 | ||
|
|
dc29e858c9 | ||
|
|
c30c6f08f3 | ||
|
|
7c892ac051 | ||
|
|
75dd7b93f5 | ||
|
|
d5de8e1bf3 | ||
|
|
16b4795956 | ||
|
|
e5835c299c | ||
|
|
4fdef3cfde | ||
|
|
cf6a8bd463 | ||
|
|
59a84e844c | ||
|
|
6b0c9a5fad | ||
|
|
77edea5419 | ||
|
|
521870df0a | ||
|
|
dc1c1eb998 | ||
|
|
e78427245a | ||
|
|
fbcab5bc52 | ||
|
|
89c79c3ec3 | ||
|
|
566cd9e9c4 | ||
|
|
ad78cec7c7 | ||
|
|
176ab5f48e | ||
|
|
9c4fa23931 | ||
|
|
2938694c45 | ||
|
|
88d0fb9753 | ||
|
|
d23a7f81ef | ||
|
|
2e363445fc | ||
|
|
33d983bc20 | ||
|
|
3e7c7831bc | ||
|
|
374d49eb92 | ||
|
|
663cf5649f | ||
|
|
095ebccbb0 | ||
|
|
f4bb6b0517 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
cd x-ui/bin
|
||||
|
||||
# Download dependencies
|
||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.9.7/"
|
||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.9.19/"
|
||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||
wget ${Xray_URL}Xray-linux-64.zip
|
||||
unzip Xray-linux-64.zip
|
||||
|
||||
@@ -27,7 +27,7 @@ case $1 in
|
||||
esac
|
||||
mkdir -p build/bin
|
||||
cd build/bin
|
||||
wget "https://github.com/XTLS/Xray-core/releases/download/v24.9.7/Xray-linux-${ARCH}.zip"
|
||||
wget "https://github.com/XTLS/Xray-core/releases/download/v24.9.19/Xray-linux-${ARCH}.zip"
|
||||
unzip "Xray-linux-${ARCH}.zip"
|
||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||
mv xray "xray-linux-${FNAME}"
|
||||
|
||||
@@ -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.4.0`:
|
||||
Para instalar la versión deseada, agrega la versión al final del comando de instalación. Por ejemplo, ver `v2.4.2`:
|
||||
|
||||
```
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.0
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.2
|
||||
```
|
||||
|
||||
## Certificado SSL
|
||||
@@ -225,6 +225,7 @@ location /sub {
|
||||
- AlmaLinux 9+
|
||||
- Rockylinux 9+
|
||||
- OpenSUSE Tubleweed
|
||||
- Amazon Linux 2023
|
||||
|
||||
## Arquitecturas y Dispositivos Compatibles
|
||||
|
||||
|
||||
@@ -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.4.0`:
|
||||
To install your desired version, add the version to the end of the installation command. e.g., ver `v2.4.2`:
|
||||
|
||||
```
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.0
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.2
|
||||
```
|
||||
|
||||
## 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
|
||||
@@ -252,6 +254,7 @@ location /sub {
|
||||
- Rocky Linux 9+
|
||||
- Oracle Linux 8+
|
||||
- OpenSUSE Tubleweed
|
||||
- Amazon Linux 2023
|
||||
|
||||
## Supported Architectures and Devices
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
> **Отказ от ответственности:** Этот проект предназначен только для личного обучения и общения. Пожалуйста, не используйте его в незаконных целях и не применяйте в производственной среде.
|
||||
|
||||
**Если этот проект оказался полезным для вас, вы можете оценить его, постативив звёздочку** :star2:
|
||||
**Если этот проект оказался полезным для вас, вы можете оценить его, поставив звёздочку** :star2:
|
||||
|
||||
<p align="left">
|
||||
<a href="https://buymeacoffee.com/mhsanaei" target="_blank">
|
||||
@@ -32,10 +32,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
||||
|
||||
## Установка определённой версии
|
||||
|
||||
Чтобы установить нужную вам версию, добавьте номер версии в конец команды установки. Например, `v2.4.0`:
|
||||
Чтобы установить нужную вам версию, добавьте номер версии в конец команды установки. Например, `v2.4.2`:
|
||||
|
||||
```
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.0
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.2
|
||||
```
|
||||
|
||||
## SSL Сертификат
|
||||
@@ -53,7 +53,7 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
||||
|
||||
- **Get SSL:** Получить SSL сертификаты.
|
||||
- **Revoke:** Отозвать существующие SSL сертификаты.
|
||||
- **Force Renew:** Принудительно превыпустить SSL сертификаты.
|
||||
- **Force Renew:** Принудительно перевыпустить SSL сертификаты.
|
||||
|
||||
### Certbot
|
||||
|
||||
@@ -76,7 +76,7 @@ certbot renew --dry-run
|
||||
**Как получить глобальный API-ключ Cloudflare:**
|
||||
|
||||
1. Выполните команду `x-ui` в терминале, затем выберите `Cloudflare SSL Certificate`.
|
||||
2. Посетите ссылку: [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens).
|
||||
2. Перейдите по ссылке: [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens).
|
||||
3. Нажмите на "View Global API Key" (см. скриншот ниже):
|
||||

|
||||
4. Возможно, вам потребуется повторно пройти аутентификацию. После этого ключ API будет отображён (см. скриншот ниже):
|
||||
@@ -168,6 +168,8 @@ systemctl restart x-ui
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Добавьте параметр ```--pull always``` для автоматического обновления контейнера, когда публикуется новый образ. Подробности: https://docs.docker.com/reference/cli/docker/container/run/#pull
|
||||
|
||||
**ИЛИ**
|
||||
|
||||
```sh
|
||||
@@ -251,6 +253,7 @@ location /sub {
|
||||
- Rocky Linux 9+
|
||||
- Oracle Linux 8+
|
||||
- OpenSUSE Tubleweed
|
||||
- Amazon Linux 2023
|
||||
|
||||
## Поддерживаемые архитектуры и устройства
|
||||
|
||||
@@ -285,6 +288,7 @@ location /sub {
|
||||
- Индонезийский
|
||||
- Украинский
|
||||
- Турецкий
|
||||
- Португальский (Бразилия)
|
||||
|
||||
## Возможности
|
||||
|
||||
@@ -329,7 +333,7 @@ location /sub {
|
||||
- Выберите опцию `Reset Web Base Path`.
|
||||
|
||||
2. **Генерация или настройка пути:**
|
||||
- Путь будет случайным образом сгенерирован, или вы можете ввести пользовательский путь.
|
||||
- Путь будет сгенерирован случайным образом, или вы можете ввести собственный путь.
|
||||
|
||||
3. **Просмотр текущих настроек:**
|
||||
- Чтобы просмотреть текущие настройки, используйте команду `x-ui settings` в терминале или опцию `View Current Settings` в `x-ui`.
|
||||
@@ -430,7 +434,7 @@ WARP встроен, и дополнительная установка не т
|
||||
|
||||
- Периодические отчеты
|
||||
- Уведомления о входе
|
||||
- Уведомления о пороге CPU
|
||||
- Уведомления о пороге загруженности процессора
|
||||
- Уведомления о времени истечения и трафике заранее
|
||||
- Поддерживает меню отчетов клиента, если имя пользователя телеграм клиента добавлено в конфигурации пользователя
|
||||
- Поддержка отчета о трафике через Telegram, поиск по UUID (VMESS/VLESS) или паролю (TROJAN) - анонимно
|
||||
@@ -444,7 +448,7 @@ WARP встроен, и дополнительная установка не т
|
||||
|
||||
### Настройка телеграм-бота
|
||||
|
||||
- Запустить [Botfather](https://t.me/BotFather) в вашем аккаунте Telegram:
|
||||
- Запустите [Botfather](https://t.me/BotFather) в вашем аккаунте Telegram:
|
||||

|
||||
|
||||
- Создайте нового бота с помощью команды /newbot: у вас спросят 2 вопроса: отображаемое имя и имя пользователя для вашего бота. Обратите внимание, что имя пользователя должно заканчиваться на слово "bot".
|
||||
@@ -459,7 +463,7 @@ WARP встроен, и дополнительная установка не т
|
||||
Введите токен вашего бота в поле ввода номер 3.
|
||||
Введите ID пользователя в поле ввода номер 4. Telegram-аккаунты с этим ID будут администраторами бота. (Вы можете ввести несколько ID, разделяя их запятой)
|
||||
|
||||
- Как получить ID пользователя Telegram? Используйте этого [бота](https://t.me/useridinfobot). Запустите бота, и он предоставит вам ваше ID пользователя Telegram.
|
||||
- Как получить ID пользователя Telegram? Используйте этот [бот](https://t.me/useridinfobot). Запустите бота, и он отобразит ваш ID пользователя Telegram.
|
||||

|
||||
|
||||
</details>
|
||||
@@ -493,7 +497,7 @@ WARP встроен, и дополнительная установка не т
|
||||
| `POST` | `"/resetAllTraffics"` | Сбросить трафик всех входящих соединений
|
||||
| `POST` | `"/resetAllClientTraffics/:id"` | Сбросить трафик всех клиентов в входящем соединении
|
||||
| `POST` | `"/delDepletedClients/:id"` | Удалить истекших клиентов в входящем соединении (-1: всех)
|
||||
| `POST` | `"/onlines"` | Получить пользователей, которые онлайн (список email'ов)
|
||||
| `POST` | `"/onlines"` | Получить пользователей, которые находятся онлайн (список email'ов)
|
||||
|
||||
\*- Поле `clientId` должно быть заполнено следующим образом:
|
||||
|
||||
|
||||
@@ -32,10 +32,10 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
||||
|
||||
## 安装指定版本
|
||||
|
||||
要安装所需的版本,请将该版本添加到安装命令的末尾。 e.g., ver `v2.4.0`:
|
||||
要安装所需的版本,请将该版本添加到安装命令的末尾。 e.g., ver `v2.4.2`:
|
||||
|
||||
```
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.0
|
||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh) v2.4.2
|
||||
```
|
||||
|
||||
### SSL证书
|
||||
@@ -248,6 +248,7 @@ location /sub {
|
||||
- AlmaLinux 9+
|
||||
- Rockylinux 9+
|
||||
- OpenSUSE Tubleweed
|
||||
- Amazon Linux 2023
|
||||
|
||||
## 支持的架构和设备
|
||||
<details>
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.4.0
|
||||
2.4.2
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
24
go.mod
24
go.mod
@@ -7,7 +7,7 @@ require (
|
||||
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.2
|
||||
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.3
|
||||
@@ -17,14 +17,14 @@ require (
|
||||
github.com/xtls/xray-core v1.8.24
|
||||
go.uber.org/atomic v1.11.0
|
||||
golang.org/x/text v0.18.0
|
||||
google.golang.org/grpc v1.66.0
|
||||
google.golang.org/grpc v1.67.0
|
||||
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.2 // indirect
|
||||
github.com/bytedance/sonic v1.12.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||
github.com/cloudflare/circl v1.4.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
@@ -40,7 +40,7 @@ require (
|
||||
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.3 // indirect
|
||||
github.com/google/pprof v0.0.0-20240903155634-a8630aee4ab9 // indirect
|
||||
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.4.0 // indirect
|
||||
@@ -49,10 +49,10 @@ require (
|
||||
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-20240819163618-b1d8f4d146e7 // 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.23 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
@@ -64,8 +64,8 @@ require (
|
||||
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
|
||||
@@ -79,19 +79,19 @@ require (
|
||||
github.com/valyala/fastjson v1.6.4 // 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.10.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // 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.25.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.24.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-20240826202546-f6391c0de4c7 // indirect
|
||||
|
||||
42
go.sum
42
go.sum
@@ -20,6 +20,8 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT
|
||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg=
|
||||
github.com/bytedance/sonic v1.12.2/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=
|
||||
@@ -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-20240903155634-a8630aee4ab9 h1:q5g0N9eal4bmJwXHC5z0QCKs8qhS35hFfq0BAYsIwZI=
|
||||
github.com/google/pprof v0.0.0-20240903155634-a8630aee4ab9/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ=
|
||||
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/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=
|
||||
@@ -127,6 +129,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
||||
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,8 +144,8 @@ 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-20240819163618-b1d8f4d146e7 h1:5RK988zAqB3/AN3opGfRpoQgAVqr6/A5+qRTi67VUZY=
|
||||
github.com/lufia/plan9stats v0.0.0-20240819163618-b1d8f4d146e7/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=
|
||||
@@ -158,8 +162,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
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.2 h1:srvQOQtb5ZswmqIr03VuAkIF076bi25n7fyQ51Ifstw=
|
||||
github.com/mymmrac/telego v0.31.2/go.mod h1:dyuyrOIagRstnm2ZNWuVilPdsslQyEgwYww9zkDqdJU=
|
||||
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=
|
||||
@@ -198,9 +202,11 @@ 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=
|
||||
@@ -273,8 +279,8 @@ github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQ
|
||||
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/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=
|
||||
@@ -296,8 +302,8 @@ golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACk
|
||||
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-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
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=
|
||||
@@ -350,8 +356,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.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
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=
|
||||
@@ -374,8 +380,8 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE
|
||||
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.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
|
||||
google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw=
|
||||
google.golang.org/grpc v1.67.0/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=
|
||||
@@ -392,8 +398,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=
|
||||
|
||||
10
install.sh
10
install.sh
@@ -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 '"')
|
||||
|
||||
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
|
||||
@@ -63,6 +63,10 @@ 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
|
||||
@@ -94,8 +98,8 @@ else
|
||||
echo "- Rocky Linux 9+"
|
||||
echo "- Oracle Linux 8+"
|
||||
echo "- OpenSUSE Tumbleweed"
|
||||
echo "- Amazon Linux 2023"
|
||||
exit 1
|
||||
|
||||
fi
|
||||
|
||||
install_base() {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"destOverride": [
|
||||
"http",
|
||||
"tls",
|
||||
"quic",
|
||||
"fakedns"
|
||||
],
|
||||
"enabled": true
|
||||
|
||||
@@ -92,9 +92,9 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||
SubJsonFragment = ""
|
||||
}
|
||||
|
||||
SubJsonNoise, err := s.settingService.GetSubJsonNoise()
|
||||
SubJsonNoises, err := s.settingService.GetSubJsonNoises()
|
||||
if err != nil {
|
||||
SubJsonNoise = ""
|
||||
SubJsonNoises = ""
|
||||
}
|
||||
|
||||
SubJsonMux, err := s.settingService.GetSubJsonMux()
|
||||
@@ -111,7 +111,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
|
||||
|
||||
s.sub = NewSUBController(
|
||||
g, LinksPath, JsonPath, Encrypt, ShowInfo, RemarkModel, SubUpdates,
|
||||
SubJsonFragment, SubJsonNoise, SubJsonMux, SubJsonRules)
|
||||
SubJsonFragment, SubJsonNoises, SubJsonMux, SubJsonRules)
|
||||
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ type SubJsonService struct {
|
||||
configJson map[string]interface{}
|
||||
defaultOutbounds []json_util.RawMessage
|
||||
fragment string
|
||||
noise string
|
||||
noises string
|
||||
mux string
|
||||
|
||||
inboundService service.InboundService
|
||||
SubService *SubService
|
||||
}
|
||||
|
||||
func NewSubJsonService(fragment string, noise 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)
|
||||
@@ -53,15 +53,15 @@ func NewSubJsonService(fragment string, noise string, mux string, rules string,
|
||||
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(fragment))
|
||||
}
|
||||
|
||||
if noise != "" {
|
||||
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(noise))
|
||||
if noises != "" {
|
||||
defaultOutbounds = append(defaultOutbounds, json_util.RawMessage(noises))
|
||||
}
|
||||
|
||||
return &SubJsonService{
|
||||
configJson: configJson,
|
||||
defaultOutbounds: defaultOutbounds,
|
||||
fragment: fragment,
|
||||
noise: noise,
|
||||
noises: noises,
|
||||
mux: mux,
|
||||
SubService: subService,
|
||||
}
|
||||
|
||||
@@ -851,39 +851,44 @@ Outbound.Settings = class extends CommonClass {
|
||||
Outbound.FreedomSettings = class extends CommonClass {
|
||||
constructor(
|
||||
domainStrategy = '',
|
||||
timeout = '',
|
||||
redirect = '',
|
||||
fragment = {},
|
||||
noise = {}
|
||||
noises = []
|
||||
) {
|
||||
super();
|
||||
this.domainStrategy = domainStrategy;
|
||||
this.timeout = timeout;
|
||||
this.redirect = redirect;
|
||||
this.fragment = fragment;
|
||||
this.noise = noise;
|
||||
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.timeout,
|
||||
json.redirect,
|
||||
json.fragment ? Outbound.FreedomSettings.Fragment.fromJson(json.fragment) : undefined,
|
||||
json.noise ? Outbound.FreedomSettings.Noise.fromJson(json.noise) : 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,
|
||||
timeout: this.timeout,
|
||||
redirect: this.redirect,
|
||||
fragment: Object.keys(this.fragment).length === 0 ? undefined : this.fragment,
|
||||
noise: Object.keys(this.noise).length === 0 ? undefined : this.noise,
|
||||
noises: Outbound.FreedomSettings.Noise.toJsonArray(this.noises),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Outbound.FreedomSettings.Fragment = class extends CommonClass {
|
||||
constructor(packets = '1-3', length = '', interval = '') {
|
||||
super();
|
||||
@@ -900,19 +905,38 @@ Outbound.FreedomSettings.Fragment = class extends CommonClass {
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Outbound.FreedomSettings.Noise = class extends CommonClass {
|
||||
constructor(packet = '', delay = '') {
|
||||
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 {
|
||||
@@ -934,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 = {}) {
|
||||
@@ -946,6 +978,8 @@ Outbound.DNSSettings = class extends CommonClass {
|
||||
json.network,
|
||||
json.address,
|
||||
json.port,
|
||||
json.nonIPQuery,
|
||||
json.blockTypes,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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,7 +38,7 @@ class AllSetting {
|
||||
this.subURI = "";
|
||||
this.subJsonURI = "";
|
||||
this.subJsonFragment = "";
|
||||
this.subJsonNoise = "";
|
||||
this.subJsonNoises = "";
|
||||
this.subJsonMux = "";
|
||||
this.subJsonRules = "";
|
||||
|
||||
|
||||
@@ -529,6 +529,12 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
||||
scMinPostsIntervalMs = "10-50",
|
||||
noSSEHeader = false,
|
||||
xPaddingBytes = "100-1000",
|
||||
xmux = {
|
||||
maxConcurrency: 0,
|
||||
maxConnections: 0,
|
||||
cMaxReuseTimes: 0,
|
||||
cMaxLifetimeMs: 0
|
||||
}
|
||||
) {
|
||||
super();
|
||||
this.path = path;
|
||||
@@ -539,6 +545,7 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
||||
this.scMinPostsIntervalMs = scMinPostsIntervalMs;
|
||||
this.noSSEHeader = noSSEHeader;
|
||||
this.xPaddingBytes = xPaddingBytes;
|
||||
this.xmux = xmux;
|
||||
}
|
||||
|
||||
addHeader(name, value) {
|
||||
@@ -559,6 +566,7 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
||||
json.scMinPostsIntervalMs,
|
||||
json.noSSEHeader,
|
||||
json.xPaddingBytes,
|
||||
json.xmux,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -572,6 +580,12 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
||||
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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1197,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),
|
||||
@@ -1206,6 +1241,7 @@ class Inbound extends XrayCommonClass {
|
||||
streamSettings = new StreamSettings(),
|
||||
tag = '',
|
||||
sniffing = new Sniffing(),
|
||||
allocate = new Allocate(),
|
||||
clientStats = '',
|
||||
) {
|
||||
super();
|
||||
@@ -1216,6 +1252,7 @@ class Inbound extends XrayCommonClass {
|
||||
this.stream = streamSettings;
|
||||
this.tag = tag;
|
||||
this.sniffing = sniffing;
|
||||
this.allocate = allocate;
|
||||
this.clientStats = clientStats;
|
||||
}
|
||||
getClientStats() {
|
||||
@@ -1406,6 +1443,7 @@ 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, security) {
|
||||
@@ -1885,6 +1923,7 @@ class Inbound extends XrayCommonClass {
|
||||
StreamSettings.fromJson(json.streamSettings),
|
||||
json.tag,
|
||||
Sniffing.fromJson(json.sniffing),
|
||||
Allocate.fromJson(json.allocate),
|
||||
json.clientStats
|
||||
)
|
||||
}
|
||||
@@ -1902,6 +1941,7 @@ class Inbound extends XrayCommonClass {
|
||||
streamSettings: streamSettings,
|
||||
tag: this.tag,
|
||||
sniffing: this.sniffing.toJson(),
|
||||
allocate: this.allocate.toJson(),
|
||||
clientStats: this.clientStats
|
||||
};
|
||||
}
|
||||
@@ -2473,15 +2513,13 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
|
||||
address,
|
||||
port,
|
||||
network = 'tcp,udp',
|
||||
followRedirect = false,
|
||||
timeout = 30
|
||||
followRedirect = false
|
||||
) {
|
||||
super(protocol);
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.network = network;
|
||||
this.followRedirect = followRedirect;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
@@ -2491,7 +2529,6 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
|
||||
json.port,
|
||||
json.network,
|
||||
json.followRedirect,
|
||||
json.timeout,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2501,7 +2538,6 @@ Inbound.DokodemoSettings = class extends Inbound.Settings {
|
||||
port: this.port,
|
||||
network: this.network,
|
||||
followRedirect: this.followRedirect,
|
||||
timeout: this.timeout,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -2561,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) {
|
||||
@@ -2578,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,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -52,7 +52,7 @@ type AllSetting struct {
|
||||
SubJsonPath string `json:"subJsonPath" form:"subJsonPath"`
|
||||
SubJsonURI string `json:"subJsonURI" form:"subJsonURI"`
|
||||
SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"`
|
||||
SubJsonNoise string `json:"subJsonNoise" form:"subJsonNoise"`
|
||||
SubJsonNoises string `json:"subJsonNoises" form:"subJsonNoises"`
|
||||
SubJsonMux string `json:"subJsonMux" form:"subJsonMux"`
|
||||
SubJsonRules string `json:"subJsonRules" form:"subJsonRules"`
|
||||
Datepicker string `json:"datepicker" form:"datepicker"`
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<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 v-if="inbound.protocol === Protocols.VMESS" label='Security'>
|
||||
<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>
|
||||
|
||||
16
web/html/xui/form/allocate.html
Normal file
16
web/html/xui/form/allocate.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{{define "form/allocate"}}
|
||||
<a-divider style="margin:5px 0 0;">Allocate</a-divider>
|
||||
<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}}
|
||||
@@ -39,7 +39,7 @@
|
||||
</template>
|
||||
<a-input v-model.trim="client.id"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.protocol === Protocols.VMESS" label='Security'>
|
||||
<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>
|
||||
|
||||
@@ -118,4 +118,10 @@
|
||||
<template>
|
||||
{{template "form/sniffing"}}
|
||||
</template>
|
||||
|
||||
<!-- allocate -->
|
||||
<template>
|
||||
{{template "form/allocate"}}
|
||||
</template>
|
||||
|
||||
{{end}}
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
<a-select-option v-for="s in OutboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Timeout'>
|
||||
<a-input-number v-model.number="outbound.settings.timeout" min="0" ></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='Redirect'>
|
||||
<a-input v-model="outbound.settings.redirect"></a-input>
|
||||
</a-form-item>
|
||||
@@ -46,16 +43,39 @@
|
||||
<a-input v-model.trim="outbound.settings.fragment.interval"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label='Noise'>
|
||||
<a-switch :checked="Object.keys(outbound.settings.noise).length >0" @change="checked => outbound.settings.noise = checked ? new Outbound.FreedomSettings.Noise() : {}"></a-switch>
|
||||
|
||||
<!-- 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>
|
||||
<template v-if="Object.keys(outbound.settings.noise).length >0">
|
||||
<a-form-item label='Packet'>
|
||||
<a-input v-model.trim="outbound.settings.noise.packet"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Delay'>
|
||||
<a-input v-model.trim="outbound.settings.noise.delay"></a-input>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
@@ -75,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 -->
|
||||
@@ -179,11 +207,15 @@
|
||||
<a-form-item label='ID'>
|
||||
<a-input v-model.trim="outbound.settings.id"></a-input>
|
||||
</a-form-item>
|
||||
<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>
|
||||
|
||||
<!-- 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()">
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<tr class="client-table-header">
|
||||
<th>{{ i18n "pages.inbounds.email" }}</th>
|
||||
<th>ID</th>
|
||||
<th>Security</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>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{{define "form/sniffing"}}
|
||||
<a-divider style="margin:5px 0 0;"></a-divider>
|
||||
<a-divider style="margin:5px 0 0;">Sniffing</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>
|
||||
|
||||
@@ -34,5 +34,17 @@
|
||||
<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}}
|
||||
|
||||
@@ -184,6 +184,9 @@
|
||||
<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>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
|
||||
@@ -139,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">
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
@@ -305,20 +305,38 @@
|
||||
<a-list-item style="padding: 20px">
|
||||
<a-row>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='Noise'>
|
||||
<template slot="description">{{ i18n "pages.settings.noiseDesc"}}</template>
|
||||
<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="noise"></a-switch>
|
||||
<a-switch v-model="noises"></a-switch>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-collapse v-if="noise" style="margin-top: 14px;">
|
||||
<a-collapse-panel header='{{ i18n "pages.settings.noiseSett"}}' v-if="noise">
|
||||
<setting-list-item style="padding: 10px 20px" type="text" title='Packet (ms)' v-model="noisePacket" placeholder="rand:5-10"></setting-list-item>
|
||||
<setting-list-item style="padding: 10px 20px" type="text" title='Delay (ms)' v-model="noiseDelay" placeholder="10-20"></setting-list-item>
|
||||
<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>
|
||||
@@ -362,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='Geo IP'>
|
||||
<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" name="Geo IP" :options="geoIPOptions"></a-checkbox-group>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='Geo Site'>
|
||||
<a-list-item style="padding: 10px 20px">
|
||||
<a-checkbox-group v-model="geoSite" name="Geo Site" :options="geoSiteOptions"></a-checkbox-group>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
@@ -419,15 +442,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultNoise: {
|
||||
tag: "noise",
|
||||
defaultNoises: {
|
||||
tag: "noises",
|
||||
protocol: "freedom",
|
||||
settings: {
|
||||
domainStrategy: "AsIs",
|
||||
noise: {
|
||||
packet: "rand:5-10",
|
||||
delay: "10-20",
|
||||
}
|
||||
noises: [
|
||||
{ type: "rand", packet: "10-20", delay: "10-16" },
|
||||
],
|
||||
},
|
||||
},
|
||||
defaultMux: {
|
||||
@@ -441,8 +463,7 @@
|
||||
type: "field",
|
||||
outboundTag: "direct",
|
||||
domain: [
|
||||
"geosite:category-ir",
|
||||
"geosite:cn"
|
||||
"geosite:category-ir"
|
||||
],
|
||||
"enabled": true
|
||||
},
|
||||
@@ -451,17 +472,30 @@
|
||||
outboundTag: "direct",
|
||||
ip: [
|
||||
"geoip:private",
|
||||
"geoip:ir",
|
||||
"geoip:cn"
|
||||
"geoip:ir"
|
||||
],
|
||||
enabled: true
|
||||
},
|
||||
],
|
||||
countryOptions: [
|
||||
geoIPOptions: [
|
||||
{ label: 'Private IP/Domain', 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;
|
||||
@@ -591,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: {
|
||||
@@ -629,29 +687,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
noise: {
|
||||
get: function () { return this.allSetting?.subJsonNoise != ""; },
|
||||
set: function (v) {
|
||||
this.allSetting.subJsonNoise = v ? JSON.stringify(this.defaultNoise) : "";
|
||||
}
|
||||
},
|
||||
noisePacket: {
|
||||
get: function () { return this.noise ? JSON.parse(this.allSetting.subJsonNoise).settings.noise.packet : ""; },
|
||||
set: function (v) {
|
||||
if (v != "") {
|
||||
newNoise = JSON.parse(this.allSetting.subJsonNoise);
|
||||
newNoise.settings.noise.packet = v;
|
||||
this.allSetting.subJsonNoise = JSON.stringify(newNoise);
|
||||
noises: {
|
||||
get() {
|
||||
return this.allSetting?.subJsonNoises != "";
|
||||
},
|
||||
set(v) {
|
||||
if (v) {
|
||||
this.allSetting.subJsonNoises = JSON.stringify(this.defaultNoises);
|
||||
} else {
|
||||
this.allSetting.subJsonNoises = "";
|
||||
}
|
||||
}
|
||||
},
|
||||
noiseDelay: {
|
||||
get: function () { return this.noise ? JSON.parse(this.allSetting.subJsonNoise).settings.noise.delay : ""; },
|
||||
set: function (v) {
|
||||
if (v != "") {
|
||||
newNoise = JSON.parse(this.allSetting.subJsonNoise);
|
||||
newNoise.settings.noise.delay = v;
|
||||
this.allSetting.subJsonNoise = JSON.stringify(newNoise);
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -691,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);
|
||||
}
|
||||
|
||||
@@ -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"}}'>
|
||||
@@ -791,9 +809,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"],
|
||||
@@ -828,10 +850,11 @@
|
||||
"regexp:.*\\.cn$"
|
||||
],
|
||||
ru: [
|
||||
"geosite:category-gov-ru",
|
||||
"geosite:category-ru", //https://github.com/v2fly/domain-list-community/blob/master/data/category-ru
|
||||
"regexp:.*\\.ru$"
|
||||
],
|
||||
ir: [
|
||||
"geosite:category-ir", // https://github.com/v2fly/domain-list-community/blob/master/data/category-ir
|
||||
"regexp:.*\\.ir$",
|
||||
"regexp:.*\\.xn--mgba3a4f16a$", // .ایران
|
||||
"ext:geosite_IR.dat:ir"
|
||||
@@ -1519,27 +1542,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 +1695,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 +1728,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" });
|
||||
|
||||
@@ -36,17 +36,12 @@ func (j *CheckClientIpJob) Run() {
|
||||
}
|
||||
|
||||
shouldClearAccessLog := false
|
||||
f2bInstalled := j.checkFail2BanInstalled()
|
||||
isAccessLogAvailable := j.checkAccessLogAvailable()
|
||||
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) {
|
||||
@@ -123,7 +118,7 @@ func (j *CheckClientIpJob) processLogFile() bool {
|
||||
line := scanner.Text()
|
||||
|
||||
ipRegx, _ := regexp.Compile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
|
||||
emailRegx, _ := regexp.Compile(`email:.+`)
|
||||
emailRegx, _ := regexp.Compile(`email: (\S+)$`)
|
||||
|
||||
matches := ipRegx.FindStringSubmatch(line)
|
||||
if len(matches) > 1 {
|
||||
@@ -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,26 +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() bool {
|
||||
isAvailable := true
|
||||
func (j *CheckClientIpJob) checkAccessLogAvailable(iplimitActive bool) bool {
|
||||
accessLogPath, err := xray.GetAccessLogPath()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch accessLogPath {
|
||||
case "none", "":
|
||||
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
|
||||
}
|
||||
|
||||
return isAvailable
|
||||
return true
|
||||
}
|
||||
|
||||
func (j *CheckClientIpJob) checkError(e error) {
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
"log": {
|
||||
"access": "none",
|
||||
"dnsLog": false,
|
||||
"error": "./error.log",
|
||||
"loglevel": "warning"
|
||||
"error": "",
|
||||
"loglevel": "warning",
|
||||
"maskAddress": ""
|
||||
},
|
||||
"api": {
|
||||
"tag": "api",
|
||||
|
||||
@@ -331,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 {
|
||||
@@ -2031,9 +2032,9 @@ func validateEmail(email string) (bool, error) {
|
||||
return false, errors.New("email contains uppercase letters, please convert to lowercase")
|
||||
}
|
||||
|
||||
emailPattern := `^[a-z0-9._-]+$`
|
||||
emailPattern := `^[a-z0-9@._-]+$`
|
||||
if !regexp.MustCompile(emailPattern).MatchString(email) {
|
||||
return false, errors.New("email contains invalid characters, please use only lowercase letters, digits, dots, dashes, and underscores")
|
||||
return false, errors.New("email contains invalid characters, please use only lowercase letters, digits, and @._-")
|
||||
}
|
||||
|
||||
return true, nil
|
||||
|
||||
@@ -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,7 +62,7 @@ var defaultValueMap = map[string]string{
|
||||
"subJsonPath": "/json/",
|
||||
"subJsonURI": "",
|
||||
"subJsonFragment": "",
|
||||
"subJsonNoise": "",
|
||||
"subJsonNoises": "",
|
||||
"subJsonMux": "",
|
||||
"subJsonRules": "",
|
||||
"datepicker": "gregorian",
|
||||
@@ -459,8 +459,8 @@ func (s *SettingService) GetSubJsonFragment() (string, error) {
|
||||
return s.getString("subJsonFragment")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubJsonNoise() (string, error) {
|
||||
return s.getString("subJsonNoise")
|
||||
func (s *SettingService) GetSubJsonNoises() (string, error) {
|
||||
return s.getString("subJsonNoises")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubJsonMux() (string, error) {
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
"fragment" = "Fragmentation"
|
||||
"fragmentDesc" = "Enable fragmentation for TLS hello packet."
|
||||
"fragmentSett" = "Fragmentation Settings"
|
||||
"noiseDesc" = "Enable Noise."
|
||||
"noiseSett" = "Noise 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"
|
||||
@@ -422,6 +422,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"
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
"fragment" = "Fragmentación"
|
||||
"fragmentDesc" = "Habilitar la fragmentación para el paquete de saludo de TLS"
|
||||
"fragmentSett" = "Configuración de Fragmentación"
|
||||
"noiseDesc" = "Activar Noise."
|
||||
"noiseSett" = "Configuración de Noise"
|
||||
"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"
|
||||
@@ -422,6 +422,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"
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
"fragment" = "فرگمنت"
|
||||
"fragmentDesc" = "فعال کردن فرگمنت برای بستهی نخست تیالاس"
|
||||
"fragmentSett" = "تنظیمات فرگمنت"
|
||||
"noiseDesc" = "فعال کردن Noise."
|
||||
"noiseSett" = "تنظیمات Noise"
|
||||
"noisesDesc" = "فعال کردن Noises."
|
||||
"noisesSett" = "تنظیمات Noises"
|
||||
"mux" = "ماکس"
|
||||
"muxDesc" = "چندین جریان داده مستقل را در یک جریان داده ثابت منتقل می کند"
|
||||
"muxSett" = "تنظیمات ماکس"
|
||||
"direct" = "اتصال مستقیم"
|
||||
"directDesc" = "به طور مستقیم با دامنه ها یا محدوده آیپی یک کشور خاص ارتباط برقرار می کند"
|
||||
"directSett" = "گزینه های اتصال مستقیم"
|
||||
|
||||
|
||||
[pages.xray]
|
||||
"title" = "پیکربندی ایکسری"
|
||||
@@ -422,6 +422,10 @@
|
||||
"accessLogDesc" = "مسیر فایل برای گزارش دسترسی. مقدار ویژه «هیچ» گزارشهای دسترسی را غیرفعال میکند."
|
||||
"errorLog" = "گزارش خطا"
|
||||
"errorLogDesc" = "مسیر فایل برای ورود به سیستم خطا. مقدار ویژه «هیچ» گزارش های خطا را غیرفعال میکند"
|
||||
"dnsLog" = "گزارش DNS"
|
||||
"dnsLogDesc" = "آیا ثبتهای درخواست DNS را فعال کنید"
|
||||
"maskAddress" = "پنهان کردن آدرس"
|
||||
"maskAddressDesc" = "پوشش آدرس IP، هنگامی که فعال میشود، به طور خودکار آدرس IP که در لاگ ظاهر میشود را جایگزین میکند."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "اولین"
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
"fragment" = "Fragmentasi"
|
||||
"fragmentDesc" = "Aktifkan fragmentasi untuk paket hello TLS"
|
||||
"fragmentSett" = "Pengaturan Fragmentasi"
|
||||
"noiseDesc" = "Aktifkan Noise."
|
||||
"noiseSett" = "Pengaturan Noise"
|
||||
"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"
|
||||
@@ -422,6 +422,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"
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
"fragment" = "Fragmentação"
|
||||
"fragmentDesc" = "Ativa a fragmentação para o pacote TLS hello."
|
||||
"fragmentSett" = "Configurações de Fragmentação"
|
||||
"noiseDesc" = "Ativar Noise."
|
||||
"noiseSett" = "Configurações de Noise"
|
||||
"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."
|
||||
"directSett" = "Opções de Conexão Direta"
|
||||
|
||||
|
||||
[pages.xray]
|
||||
"title" = "Configurações Xray"
|
||||
@@ -422,6 +422,10 @@
|
||||
"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"
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
"fragment" = "Фрагментация"
|
||||
"fragmentDesc" = "Включить фрагментацию для пакета приветствия TLS"
|
||||
"fragmentSett" = "Настройки фрагментации"
|
||||
"noiseDesc" = "Включить Noise."
|
||||
"noiseSett" = "Настройки Noise"
|
||||
"noisesDesc" = "Включить Noises."
|
||||
"noisesSett" = "Настройки Noises"
|
||||
"mux" = "Mux"
|
||||
"muxDesc" = "Передача нескольких независимых потоков данных в рамках установленного потока данных."
|
||||
"muxSett" = "Mux Настройки"
|
||||
"direct" = "Прямая связь"
|
||||
"directDesc" = "Напрямую устанавливает соединения с доменами или диапазонами IP конкретной страны."
|
||||
"directSett" = "Варианты прямого подключения"
|
||||
|
||||
|
||||
[pages.xray]
|
||||
"title" = "Настройки Xray"
|
||||
@@ -422,6 +422,10 @@
|
||||
"accessLogDesc" = "Путь к файлу журнала доступа. Специальное значение «none» отключило журналы доступа."
|
||||
"errorLog" = "Журнал ошибок"
|
||||
"errorLogDesc" = "Путь к файлу журнала ошибок. Специальное значение «none» отключает журналы ошибок."
|
||||
"dnsLog" = "DNS Журнал"
|
||||
"dnsLogDesc" = "Включить логи запросов DNS"
|
||||
"maskAddress" = "Маскировать Адрес"
|
||||
"maskAddressDesc" = "Маска IP-адреса, при активации автоматически заменяет IP-адрес, который появляется в логе."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Первый"
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
"fragment" = "Parçalama"
|
||||
"fragmentDesc" = "TLS merhaba paketinin parçalanmasını etkinleştir."
|
||||
"fragmentSett" = "Parçalama Ayarları"
|
||||
"noiseDesc" = "Noise'i Etkinleştir."
|
||||
"noiseSett" = "Noise 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ı"
|
||||
@@ -422,6 +422,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"
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
"fragment" = "Фрагментація"
|
||||
"fragmentDesc" = "Увімкнути фрагментацію для пакету привітання TLS"
|
||||
"fragmentSett" = "Параметри фрагментації"
|
||||
"noiseDesc" = "Увімкнути Noise."
|
||||
"noiseSett" = "Налаштування Noise"
|
||||
"noisesDesc" = "Увімкнути Noises."
|
||||
"noisesSett" = "Налаштування Noises"
|
||||
"mux" = "Mux"
|
||||
"muxDesc" = "Передавати кілька незалежних потоків даних у межах встановленого потоку даних."
|
||||
"muxSett" = "Налаштування Mux"
|
||||
"direct" = "Пряме підключення"
|
||||
"directDesc" = "Безпосередньо встановлює з’єднання з доменами або діапазонами IP певної країни."
|
||||
"directSett" = "Параметри прямого підключення"
|
||||
|
||||
|
||||
[pages.xray]
|
||||
"title" = "Xray конфігурації"
|
||||
@@ -422,6 +422,10 @@
|
||||
"accessLogDesc" = "Шлях до файлу журналу доступу. Спеціальне значення 'none' вимикає журнали доступу"
|
||||
"errorLog" = "Журнал помилок"
|
||||
"errorLogDesc" = "Шлях до файлу журналу помилок. Спеціальне значення 'none' вимикає журнали помилок"
|
||||
"dnsLog" = "Журнал DNS"
|
||||
"dnsLogDesc" = "Чи включити журнали запитів DNS"
|
||||
"maskAddress" = "Маскувати Адресу"
|
||||
"maskAddressDesc" = "Маска IP-адреси, при активації автоматично замінює IP-адресу, яка з'являється у журналі."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Перший"
|
||||
|
||||
@@ -309,14 +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"
|
||||
"noiseDesc" = "Bật Noise."
|
||||
"noiseSett" = "Cài đặt Noise"
|
||||
"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"
|
||||
@@ -422,6 +422,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"
|
||||
|
||||
@@ -309,14 +309,14 @@
|
||||
"fragment" = "分片"
|
||||
"fragmentDesc" = "启用 TLS hello 数据包分片"
|
||||
"fragmentSett" = "设置"
|
||||
"noiseDesc" = "启用 Noise."
|
||||
"noiseSett" = "Noise 设置"
|
||||
"noisesDesc" = "启用 Noises."
|
||||
"noisesSett" = "Noises 设置"
|
||||
"mux" = "多路复用器"
|
||||
"muxDesc" = "在已建立的数据流内传输多个独立的数据流"
|
||||
"muxSett" = "复用器设置"
|
||||
"direct" = "直接连接"
|
||||
"directDesc" = "直接与特定国家的域或IP范围建立连接"
|
||||
"directSett" = "直接连接选项"
|
||||
|
||||
|
||||
[pages.xray]
|
||||
"title" = "Xray 配置"
|
||||
@@ -422,6 +422,10 @@
|
||||
"accessLogDesc" = "访问日志的文件路径。特殊值 'none' 禁用访问日志"
|
||||
"errorLog" = "错误日志"
|
||||
"errorLogDesc" = "错误日志的文件路径。特殊值 'none' 禁用错误日志"
|
||||
"dnsLog" = "DNS 日志"
|
||||
"dnsLogDesc" = "是否启用 DNS 查询日志"
|
||||
"maskAddress" = "隐藏地址"
|
||||
"maskAddressDesc" = "IP 地址掩码,启用时会自动替换日志中出现的 IP 地址。"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "置顶"
|
||||
|
||||
10
x-ui.sh
10
x-ui.sh
@@ -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 '"')
|
||||
|
||||
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
|
||||
@@ -60,6 +60,10 @@ 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
|
||||
@@ -91,8 +95,8 @@ else
|
||||
echo "- Rocky Linux 9+"
|
||||
echo "- Oracle Linux 8+"
|
||||
echo "- OpenSUSE Tumbleweed"
|
||||
echo "- Amazon Linux 2023"
|
||||
exit 1
|
||||
|
||||
fi
|
||||
|
||||
# Declare Variables
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user