Compare commits
135 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6d05a9b4d | ||
|
|
3caace2cb6 | ||
|
|
99f26be30d | ||
|
|
b0edd24c52 | ||
|
|
f0cfd48f66 | ||
|
|
f5aea03765 | ||
|
|
14cdde371f | ||
|
|
99a23f25d5 | ||
|
|
653ec90451 | ||
|
|
91a84db479 | ||
|
|
0f97eca314 | ||
|
|
fb79081aa1 | ||
|
|
ea19fb8ff6 | ||
|
|
f4cfe9eb63 | ||
|
|
4def70a006 | ||
|
|
e0e9e2681a | ||
|
|
31e1581d6b | ||
|
|
018e98a510 | ||
|
|
21ea673c30 | ||
|
|
08b55da408 | ||
|
|
7a3ee69a7f | ||
|
|
664bd9b596 | ||
|
|
ceb1217121 | ||
|
|
e754523689 | ||
|
|
e84503feec | ||
|
|
1bbf31df9f | ||
|
|
49bfff9fa5 | ||
|
|
d18a1a37ce | ||
|
|
04c6b2722b | ||
|
|
94d651fc93 | ||
|
|
aae0cb37b7 | ||
|
|
b922d986d6 | ||
|
|
8a7cffd63f | ||
|
|
c8e8c97afc | ||
|
|
46ba4c4518 | ||
|
|
a787ab497c | ||
|
|
3be204f272 | ||
|
|
de13729a97 | ||
|
|
468eb8b908 | ||
|
|
e95a748e77 | ||
|
|
34e2d961f5 | ||
|
|
b4a1d81444 | ||
|
|
46ef506aa6 | ||
|
|
51220917c4 | ||
|
|
b34956647b | ||
|
|
5c4e2dfd39 | ||
|
|
dd4c2adb37 | ||
|
|
2dec7f48f5 | ||
|
|
04cf250a54 | ||
|
|
ac9ab828b5 | ||
|
|
4dd40f6f19 | ||
|
|
7911eeb69f | ||
|
|
6e9180a665 | ||
|
|
66fe84181b | ||
|
|
7b7eb98acb | ||
|
|
7eb5afdd8d | ||
|
|
522ccda71c | ||
|
|
f780efb430 | ||
|
|
783f1a073e | ||
|
|
a4c38ec8ae | ||
|
|
0c47771671 | ||
|
|
67920a1962 | ||
|
|
49d3957c07 | ||
|
|
ee946ceab2 | ||
|
|
b650064177 | ||
|
|
9fb9d7201e | ||
|
|
4a3b9b913d | ||
|
|
da674d44cf | ||
|
|
cc3252531b | ||
|
|
284731deeb | ||
|
|
9bc5c1d070 | ||
|
|
26a7700557 | ||
|
|
b6a919218a | ||
|
|
6cc07254e0 | ||
|
|
4ad5a5aba4 | ||
|
|
7ab8164de4 | ||
|
|
195effd177 | ||
|
|
747ad3b9c8 | ||
|
|
cf879f9527 | ||
|
|
04c658f1a0 | ||
|
|
2ab1a174db | ||
|
|
dfca2af997 | ||
|
|
0a7d15b48c | ||
|
|
518bc72f90 | ||
|
|
3c65209ce9 | ||
|
|
174535b05d | ||
|
|
fff54fe7f3 | ||
|
|
0859d230b0 | ||
|
|
02998c5467 | ||
|
|
3f38c42852 | ||
|
|
49295661fd | ||
|
|
e4301f8d93 | ||
|
|
c6586f8df2 | ||
|
|
8e81008cdc | ||
|
|
e33ad809d6 | ||
|
|
d804043a18 | ||
|
|
0fb0df7056 | ||
|
|
73e90e0eaa | ||
|
|
44ef1ac9a6 | ||
|
|
aeac7f2c8b | ||
|
|
39ef172b87 | ||
|
|
0df85cc3d9 | ||
|
|
f0f4f082ae | ||
|
|
b29bd993d4 | ||
|
|
127eaf69b6 | ||
|
|
36b0289bc6 | ||
|
|
0abd0be725 | ||
|
|
918a2b1533 | ||
|
|
88a17cd227 | ||
|
|
b60387accb | ||
|
|
049177024b | ||
|
|
9c63638af1 | ||
|
|
67dfe664a6 | ||
|
|
4efcdb3e01 | ||
|
|
ddc2cfacb9 | ||
|
|
337729529a | ||
|
|
4c4cc362b3 | ||
|
|
4e0aca16c2 | ||
|
|
749a426a71 | ||
|
|
b859327b8a | ||
|
|
3e8fc59213 | ||
|
|
462e02140d | ||
|
|
6b41df2d89 | ||
|
|
eb5ed5c0dd | ||
|
|
e0bbacf013 | ||
|
|
34af7f8bfa | ||
|
|
b569c21fec | ||
|
|
c4a5c059e3 | ||
|
|
c21ed90da0 | ||
|
|
9b58277945 | ||
|
|
5a4a42aeb8 | ||
|
|
9476472bf6 | ||
|
|
2ce9c3cc81 | ||
|
|
0a8bfc2725 | ||
|
|
c5bbb6b632 |
10
.github/dependabot.yml
vendored
@@ -1,10 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
4
.github/workflows/docker.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -31,6 +31,8 @@ jobs:
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
install: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
|
||||
30
.github/workflows/release.yml
vendored
@@ -72,7 +72,7 @@ jobs:
|
||||
export GOARCH=s390x
|
||||
export CC=s390x-linux-gnu-gcc
|
||||
fi
|
||||
go build -o xui-release -v main.go
|
||||
go build -ldflags "-w -s" -o xui-release -v main.go
|
||||
|
||||
mkdir x-ui
|
||||
cp xui-release x-ui/
|
||||
@@ -83,43 +83,43 @@ jobs:
|
||||
cd x-ui/bin
|
||||
|
||||
# Download dependencies
|
||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v24.11.11/"
|
||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.3.3/"
|
||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||
wget ${Xray_URL}Xray-linux-64.zip
|
||||
wget -q ${Xray_URL}Xray-linux-64.zip
|
||||
unzip Xray-linux-64.zip
|
||||
rm -f Xray-linux-64.zip
|
||||
elif [ "${{ matrix.platform }}" == "arm64" ]; then
|
||||
wget ${Xray_URL}Xray-linux-arm64-v8a.zip
|
||||
wget -q ${Xray_URL}Xray-linux-arm64-v8a.zip
|
||||
unzip Xray-linux-arm64-v8a.zip
|
||||
rm -f Xray-linux-arm64-v8a.zip
|
||||
elif [ "${{ matrix.platform }}" == "armv7" ]; then
|
||||
wget ${Xray_URL}Xray-linux-arm32-v7a.zip
|
||||
wget -q ${Xray_URL}Xray-linux-arm32-v7a.zip
|
||||
unzip Xray-linux-arm32-v7a.zip
|
||||
rm -f Xray-linux-arm32-v7a.zip
|
||||
elif [ "${{ matrix.platform }}" == "armv6" ]; then
|
||||
wget ${Xray_URL}Xray-linux-arm32-v6.zip
|
||||
wget -q ${Xray_URL}Xray-linux-arm32-v6.zip
|
||||
unzip Xray-linux-arm32-v6.zip
|
||||
rm -f Xray-linux-arm32-v6.zip
|
||||
elif [ "${{ matrix.platform }}" == "386" ]; then
|
||||
wget ${Xray_URL}Xray-linux-32.zip
|
||||
wget -q ${Xray_URL}Xray-linux-32.zip
|
||||
unzip Xray-linux-32.zip
|
||||
rm -f Xray-linux-32.zip
|
||||
elif [ "${{ matrix.platform }}" == "armv5" ]; then
|
||||
wget ${Xray_URL}Xray-linux-arm32-v5.zip
|
||||
wget -q ${Xray_URL}Xray-linux-arm32-v5.zip
|
||||
unzip Xray-linux-arm32-v5.zip
|
||||
rm -f Xray-linux-arm32-v5.zip
|
||||
elif [ "${{ matrix.platform }}" == "s390x" ]; then
|
||||
wget ${Xray_URL}Xray-linux-s390x.zip
|
||||
wget -q ${Xray_URL}Xray-linux-s390x.zip
|
||||
unzip Xray-linux-s390x.zip
|
||||
rm -f Xray-linux-s390x.zip
|
||||
fi
|
||||
rm -f geoip.dat geosite.dat
|
||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
wget -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||
wget -O geoip_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
|
||||
wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
wget -q -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -q -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||
wget -q -O geoip_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget -q -O geosite_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
mv xray xray-linux-${{ matrix.platform }}
|
||||
cd ../..
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Start fail2ban
|
||||
fail2ban-client -x start
|
||||
[ $X_UI_ENABLE_FAIL2BAN == "true" ] && fail2ban-client -x start
|
||||
|
||||
# Run x-ui
|
||||
exec /app/x-ui
|
||||
|
||||
@@ -27,14 +27,14 @@ case $1 in
|
||||
esac
|
||||
mkdir -p build/bin
|
||||
cd build/bin
|
||||
wget "https://github.com/XTLS/Xray-core/releases/download/v24.11.11/Xray-linux-${ARCH}.zip"
|
||||
wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.3.3/Xray-linux-${ARCH}.zip"
|
||||
unzip "Xray-linux-${ARCH}.zip"
|
||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||
mv xray "xray-linux-${FNAME}"
|
||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
wget -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||
wget -O geoip_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
|
||||
wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
wget -q -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -q -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||
wget -q -O geoip_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget -q -O geosite_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
cd ../../
|
||||
@@ -1,7 +1,7 @@
|
||||
# ========================================================
|
||||
# Stage: Builder
|
||||
# ========================================================
|
||||
FROM golang:1.23-alpine AS builder
|
||||
FROM golang:1.24-alpine AS builder
|
||||
WORKDIR /app
|
||||
ARG TARGETARCH
|
||||
|
||||
@@ -15,7 +15,7 @@ COPY . .
|
||||
|
||||
ENV CGO_ENABLED=1
|
||||
ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"
|
||||
RUN go build -o build/x-ui main.go
|
||||
RUN go build -ldflags "-w -s" -o build/x-ui main.go
|
||||
RUN ./DockerInit.sh "$TARGETARCH"
|
||||
|
||||
# ========================================================
|
||||
@@ -48,6 +48,7 @@ RUN chmod +x \
|
||||
/app/x-ui \
|
||||
/usr/bin/x-ui
|
||||
|
||||
ENV X_UI_ENABLE_FAIL2BAN="true"
|
||||
VOLUME [ "/etc/x-ui" ]
|
||||
CMD [ "./x-ui" ]
|
||||
ENTRYPOINT [ "/app/DockerEntrypoint.sh" ]
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
[English](/README.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||
|
||||
<p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p>
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
|
||||
<img alt="3x-ui" src="./media/3x-ui-light.png">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
**Un Panel Web Avanzado • Construido sobre Xray Core**
|
||||
|
||||
@@ -277,14 +282,18 @@ Nuestra plataforma ofrece compatibilidad con una amplia gama de arquitecturas y
|
||||
|
||||
## Idiomas
|
||||
|
||||
- Inglés
|
||||
- Farsi
|
||||
- Chino
|
||||
- Ruso
|
||||
- Vietnamita
|
||||
- Español
|
||||
- Indonesio
|
||||
- Ucraniano
|
||||
- English (inglés)
|
||||
- Persian (persa)
|
||||
- Traditional Chinese (chino tradicional)
|
||||
- Simplified Chinese (chino simplificado)
|
||||
- Japanese (japonés)
|
||||
- Russian (ruso)
|
||||
- Vietnamese (vietnamita)
|
||||
- Spanish (español)
|
||||
- Indonesian (indonesio)
|
||||
- Ukrainian (ucraniano)
|
||||
- Turkish (turco)
|
||||
- Português (Brazil) (portugués (Brasil))
|
||||
|
||||
|
||||
## Características
|
||||
@@ -535,13 +544,34 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
|
||||
## Vista previa
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/01-overview-dark.png">
|
||||
<img alt="3x-ui" src="./media/01-overview-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/02-inbounds-dark.png">
|
||||
<img alt="3x-ui" src="./media/02-inbounds-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/03-add-inbound-dark.png">
|
||||
<img alt="3x-ui" src="./media/03-add-inbound-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/04-add-client-dark.png">
|
||||
<img alt="3x-ui" src="./media/04-add-client-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/05-settings-dark.png">
|
||||
<img alt="3x-ui" src="./media/05-settings-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/06-configs-dark.png">
|
||||
<img alt="3x-ui" src="./media/06-configs-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/07-bot-dark.png">
|
||||
<img alt="3x-ui" src="./media/07-bot-light.png">
|
||||
</picture>
|
||||
|
||||
## Un agradecimiento especial a
|
||||
|
||||
@@ -550,8 +580,8 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
## Reconocimientos
|
||||
|
||||
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (Licencia: **GPL-3.0**): _Reglas de enrutamiento mejoradas de v2ray/xray y v2ray/xray-clients con dominios iraníes integrados y un enfoque en seguridad y bloqueo de anuncios._
|
||||
- [Vietnam Adblock rules](https://github.com/vuong2023/vn-v2ray-rules) (License: **GPL-3.0**): _Un dominio alojado en Vietnam y una lista de bloqueo con la máxima eficiencia para vietnamitas._
|
||||
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (License: **GPL-3.0**): _Este repositorio contiene reglas de enrutamiento de V2Ray actualizadas automáticamente basadas en datos de dominios y direcciones bloqueados en Rusia._
|
||||
|
||||
## Estrellas a lo largo del tiempo
|
||||
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
527
README.fa_IR.md
Normal file
@@ -0,0 +1,527 @@
|
||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
|
||||
<img alt="3x-ui" src="./media/3x-ui-light.png">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
**یک پنل وب پیشرفته • ساخته شده بر پایه Xray Core**
|
||||
|
||||
[](https://github.com/MHSanaei/3x-ui/releases)
|
||||
[](#)
|
||||
[](#)
|
||||
[](#)
|
||||
[](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)
|
||||
```
|
||||
|
||||
## نصب نسخههای قدیمی (توصیه نمیشود)
|
||||
|
||||
برای نصب نسخه خاصی از دستور زیر استفاده کنید. مثال برای نسخه `v1.7.9`:
|
||||
|
||||
```
|
||||
VERSION=v1.7.9 && bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/$VERSION/install.sh") $VERSION
|
||||
```
|
||||
|
||||
## گواهی SSL
|
||||
|
||||
<details>
|
||||
<summary>جزئیات گواهی SSL</summary>
|
||||
|
||||
### ACME
|
||||
|
||||
برای مدیریت گواهیهای SSL با استفاده از ACME:
|
||||
|
||||
1. اطمینان حاصل کنید دامنه شما به درستی به سرور متصل است.
|
||||
2. دستور `x-ui` را در ترمینال اجرا کرده و گزینه `مدیریت گواهی SSL` را انتخاب کنید.
|
||||
3. گزینههای زیر نمایش داده میشوند:
|
||||
|
||||
- **دریافت 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
|
||||
```
|
||||
|
||||
### Cloudflare
|
||||
|
||||
اسکریپت داخلی برای دریافت گواهی SSL از Cloudflare. نیازمند:
|
||||
|
||||
- ایمیل ثبتشده در Cloudflare
|
||||
- کلید API جهانی Cloudflare
|
||||
- دامنه باید از طریق Cloudflare به سرور متصل باشد
|
||||
|
||||
**دریافت کلید API جهانی Cloudflare:**
|
||||
|
||||
1. دستور `x-ui` را اجرا و گزینه `گواهی SSL کلادفلر` را انتخاب کنید.
|
||||
2. به لینک [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens) مراجعه کنید.
|
||||
3. روی "View Global API Key" کلیک کنید:
|
||||

|
||||
4. پس از احراز هویت، کلید API نمایش داده میشود:
|
||||

|
||||
|
||||
در هنگام استفاده، نام دامنه، ایمیل و کلید API را وارد کنید:
|
||||

|
||||
|
||||
</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. نصب یا ارتقا:
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
یا
|
||||
|
||||
```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. **حذف:**
|
||||
|
||||
```sh
|
||||
docker stop 3x-ui
|
||||
docker rm 3x-ui
|
||||
cd --
|
||||
rm -r 3x-ui
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## تنظیمات Nginx
|
||||
<details>
|
||||
<summary>پیکربندی Reverse Proxy</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
|
||||
- اطمینان حاصل کنید "URI Path" در تنظیمات پنل یکسان باشد.
|
||||
- `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+
|
||||
- OpenEuler 22.03+
|
||||
- Fedora 36+
|
||||
- Arch Linux
|
||||
- Parch Linux
|
||||
- Manjaro
|
||||
- Armbian
|
||||
- AlmaLinux 8.0+
|
||||
- Rocky Linux 8+
|
||||
- Oracle Linux 8+
|
||||
- OpenSUSE Tubleweed
|
||||
- Amazon Linux 2023
|
||||
- Windows x64
|
||||
|
||||
## معماریها و دستگاههای پشتیبانی شده
|
||||
|
||||
<details>
|
||||
<summary>جزئیات معماریها و دستگاهها</summary>
|
||||
|
||||
- **amd64**: معماری استاندارد برای کامپیوترهای شخصی و سرورها
|
||||
- **x86 / i386**: سیستمهای دسکتاپ و لپتاپ
|
||||
- **armv8 / arm64 / aarch64**: دستگاههای موبایل و embedded مانند Raspberry Pi 4
|
||||
- **armv7 / arm / arm32**: دستگاههای قدیمی مانند Orange Pi Zero
|
||||
- **armv6 / arm / arm32**: دستگاههای بسیار قدیمی مانند Raspberry Pi 1
|
||||
- **armv5 / arm / arm32**: سیستمهای embedded قدیمی
|
||||
- **s390x**: کامپیوترهای IBM mainframe
|
||||
</details>
|
||||
|
||||
## زبانهای پشتیبانی شده
|
||||
|
||||
- انگلیسی
|
||||
- فارسی
|
||||
- چینی سنتی
|
||||
- چینی سادهشده
|
||||
- ژاپنی
|
||||
- روسی
|
||||
- ویتنامی
|
||||
- اسپانیایی
|
||||
- اندونزیایی
|
||||
- اوکراینی
|
||||
- ترکی
|
||||
- پرتغالی (برزیل)
|
||||
|
||||
## ویژگیها
|
||||
|
||||
- مانیتورینگ وضعیت سیستم
|
||||
- جستجو در بین inboundها و کلاینتها
|
||||
- تم تاریک/روشن
|
||||
- پشتیبانی از چند کاربر و پروتکل
|
||||
- پروتکلهای VMESS، VLESS، Trojan، Shadowsocks، Dokodemo-door، Socks، HTTP، WireGuard
|
||||
- پشتیبانی از XTLS شامل RPRX-Direct، Vision، REALITY
|
||||
- آمار ترافیک، محدودیت ترافیک، محدودیت زمانی
|
||||
- تنظیمات سفارشی Xray
|
||||
- پشتیبانی از HTTPS برای پنل
|
||||
- دریافت خودکار گواهی SSL
|
||||
- مسیرهای API اصلاح شده
|
||||
- پشتیبانی از تغییر تنظیمات از طریق پنل
|
||||
- امکان export/import دیتابیس
|
||||
|
||||
## تنظیمات پیشفرض پنل
|
||||
|
||||
<details>
|
||||
<summary>جزئیات تنظیمات پیشفرض</summary>
|
||||
|
||||
### نام کاربری، رمز عبور، پورت و مسیر وب
|
||||
|
||||
در صورت عدم تغییر، این موارد به صورت تصادفی ایجاد میشوند (به جز Docker).
|
||||
|
||||
**تنظیمات پیشفرض Docker:**
|
||||
- **نام کاربری:** admin
|
||||
- **رمز عبور:** admin
|
||||
- **پورت:** 2053
|
||||
|
||||
### مدیریت دیتابیس:
|
||||
|
||||
امکان Backup و Restore دیتابیس از طریق پنل.
|
||||
|
||||
- **مسیر دیتابیس:**
|
||||
- `/etc/x-ui/x-ui.db`
|
||||
|
||||
### مسیر پایه وب
|
||||
|
||||
1. **بازنشانی مسیر:**
|
||||
- اجرای دستور `x-ui`
|
||||
- انتخاب گزینه `Reset Web Base Path`
|
||||
|
||||
2. **ساخت یا تنظیم مسیر:**
|
||||
- مسیر به صورت تصادفی ساخته شده یا قابل تنظیم است
|
||||
|
||||
3. **مشاهده تنظیمات فعلی:**
|
||||
- استفاده از دستور `x-ui settings` یا `View Current Settings` در `x-ui`
|
||||
|
||||
**توصیه امنیتی:**
|
||||
- استفاده از مسیرهای طولانی و تصادفی برای افزایش امنیت
|
||||
|
||||
**مثال:**
|
||||
- `http://ip:port/*webbasepath*/panel`
|
||||
- `http://domain:port/*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` و جدیدتر:**
|
||||
|
||||
برای فعالسازی نیاز به نصب `fail2ban` است:
|
||||
|
||||
1. اجرای دستور `x-ui` و انتخاب `مدیریت محدودیت IP`
|
||||
2. گزینههای موجود:
|
||||
|
||||
- **تغییر مدت زمان Ban**
|
||||
- **حذف تمام Banها**
|
||||
- **مشاهده لاگها**
|
||||
- **وضعیت Fail2ban**
|
||||
- **راهاندازی مجدد Fail2ban**
|
||||
- **حذف Fail2ban**
|
||||
|
||||
3. تنظیم مسیر `Access log` در پنل به `./access.log` و ذخیره و راهاندازی مجدد Xray
|
||||
|
||||
- **قبل از نسخه `v2.1.3`:**
|
||||
- تنظیم دستی `access.log` در تنظیمات Xray:
|
||||
|
||||
```sh
|
||||
"log": {
|
||||
"access": "./access.log",
|
||||
"dnsLog": false,
|
||||
"loglevel": "warning"
|
||||
},
|
||||
```
|
||||
|
||||
- **از نسخه `v2.1.3`:**
|
||||
- امکان تنظیم `access.log` از طریق پنل
|
||||
|
||||
</details>
|
||||
|
||||
## ربات تلگرام
|
||||
|
||||
<details>
|
||||
<summary>جزئیات ربات تلگرام</summary>
|
||||
|
||||
#### استفاده
|
||||
|
||||
ربات تلگرام برای اطلاعرسانی ترافیک، ورود به پنل، Backup دیتابیس و ... استفاده میشود. نیازمند تنظیم:
|
||||
|
||||
- توکن تلگرام
|
||||
- Chat ID ادمینها
|
||||
- زمان اطلاعرسانی (Cron syntax)
|
||||
- اطلاعرسانی انقضا
|
||||
- اطلاعرسانی ترافیک
|
||||
- Backup دیتابیس
|
||||
- اطلاعرسانی مصرف CPU
|
||||
|
||||
**سینتکس نمونه:**
|
||||
|
||||
- `30 \* \* \* \* \*` - اطلاع در ثانیه 30 هر دقیقه
|
||||
- `@hourly` - هر ساعت
|
||||
- `@daily` - هر روز
|
||||
|
||||
### ویژگیهای ربات
|
||||
|
||||
- گزارش دورهای
|
||||
- اطلاع ورود به پنل
|
||||
- اطلاع مصرف CPU
|
||||
- اطلاع پیشاز موعد انقضا و ترافیک
|
||||
- گزارش ترافیک کلاینتها
|
||||
- منوی مبتنی بر دستور
|
||||
- جستجوی کلاینت بر اساس ایمیل
|
||||
- بررسی inboundها
|
||||
- بررسی وضعیت سرور
|
||||
- دریافت Backup
|
||||
- چندزبانه
|
||||
|
||||
### راهاندازی ربات
|
||||
|
||||
- شروع [Botfather](https://t.me/BotFather) در تلگرام:
|
||||

|
||||
|
||||
- ساخت ربات جدید با دستور /newbot:
|
||||

|
||||
|
||||
- شروع ربات ساخته شده:
|
||||

|
||||
|
||||
- تنظیمات پنل:
|
||||

|
||||
|
||||
وارد کردن توکن و Chat ID (دریافت از [این ربات](https://t.me/useridinfobot)):
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
## مسیرهای API
|
||||
|
||||
<details>
|
||||
<summary>جزئیات API</summary>
|
||||
|
||||
#### استفاده
|
||||
|
||||
- [مستندات API](https://www.postman.com/hsanaei/3x-ui/collection/q1l5l0u/3x-ui)
|
||||
- `/login` با `POST` داده کاربر: `{username: '', password: ''}`
|
||||
|
||||
| Method | مسیر | عملکرد |
|
||||
| :----: | ---------------------------------- | ------------------------------------------- |
|
||||
| `GET` | `"/list"` | دریافت تمام inboundها |
|
||||
| `GET` | `"/get/:id"` | دریافت inbound بر اساس id |
|
||||
| `POST` | `"/add"` | افزودن inbound |
|
||||
| `POST` | `"/del/:id"` | حذف inbound |
|
||||
|
||||
- [<img src="https://run.pstmn.io/button.svg" alt="Run In Postman" style="width: 128px; height: 32px;">](https://app.getpostman.com/run-collection/5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D5146551-dda3cab3-0e33-485f-96f9-d4262f437ac5%26entityType%3Dcollection%26workspaceId%3Dd64f609f-485a-4951-9b8f-876b3f917124)
|
||||
</details>
|
||||
|
||||
## متغیرهای محیطی
|
||||
|
||||
<details>
|
||||
<summary>جزئیات متغیرها</summary>
|
||||
|
||||
#### استفاده
|
||||
|
||||
| متغیر | نوع | پیشفرض |
|
||||
| ------------- | :--------------------------------------------: | :------------ |
|
||||
| XUI_LOG_LEVEL | `"debug"` \| `"info"` \| `"warn"` \| `"error"` | `"info"` |
|
||||
| XUI_DEBUG | `boolean` | `false` |
|
||||
| XUI_BIN_FOLDER| `string` | `"bin"` |
|
||||
|
||||
مثال:
|
||||
|
||||
```sh
|
||||
XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## پیشنمایش
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/01-overview-dark.png">
|
||||
<img alt="3x-ui" src="./media/01-overview-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/02-inbounds-dark.png">
|
||||
<img alt="3x-ui" src="./media/02-inbounds-light.png">
|
||||
</picture>
|
||||
|
||||
## قدردانی ویژه از
|
||||
|
||||
- [alireza0](https://github.com/alireza0/)
|
||||
|
||||
## تشکر و قدردانی
|
||||
|
||||
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (مجوز: **GPL-3.0**)
|
||||
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (مجوز: **GPL-3.0**)
|
||||
|
||||
## Stargazers over Time
|
||||
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
51
README.md
@@ -1,6 +1,11 @@
|
||||
[English](/README.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||
|
||||
<p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p>
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
|
||||
<img alt="3x-ui" src="./media/3x-ui-light.png">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
**An Advanced Web Panel • Built on Xray Core**
|
||||
|
||||
@@ -285,9 +290,10 @@ Our platform offers compatibility with a diverse range of architectures and devi
|
||||
## Languages
|
||||
|
||||
- English
|
||||
- Farsi
|
||||
- Persian
|
||||
- Traditional Chinese
|
||||
- Simplified Chinese
|
||||
- Japanese
|
||||
- Russian
|
||||
- Vietnamese
|
||||
- Spanish
|
||||
@@ -547,13 +553,34 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
|
||||
## Preview
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/01-overview-dark.png">
|
||||
<img alt="3x-ui" src="./media/01-overview-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/02-inbounds-dark.png">
|
||||
<img alt="3x-ui" src="./media/02-inbounds-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/03-add-inbound-dark.png">
|
||||
<img alt="3x-ui" src="./media/03-add-inbound-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/04-add-client-dark.png">
|
||||
<img alt="3x-ui" src="./media/04-add-client-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/05-settings-dark.png">
|
||||
<img alt="3x-ui" src="./media/05-settings-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/06-configs-dark.png">
|
||||
<img alt="3x-ui" src="./media/06-configs-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/07-bot-dark.png">
|
||||
<img alt="3x-ui" src="./media/07-bot-light.png">
|
||||
</picture>
|
||||
|
||||
## A Special Thanks to
|
||||
|
||||
@@ -562,8 +589,8 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
## Acknowledgment
|
||||
|
||||
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._
|
||||
- [Vietnam Adblock rules](https://github.com/vuong2023/vn-v2ray-rules) (License: **GPL-3.0**): _A hosted domain hosted in Vietnam and blocklist with the most efficiency for Vietnamese._
|
||||
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (License: **GPL-3.0**): _This repository contains automatically updated V2Ray routing rules based on data on blocked domains and addresses in Russia._
|
||||
|
||||
## Stargazers over Time
|
||||
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
@@ -1,6 +1,11 @@
|
||||
[English](/README.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||
|
||||
<p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p>
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
|
||||
<img alt="3x-ui" src="./media/3x-ui-light.png">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
**Продвинутая веб-панель • Построена на основе Xray Core**
|
||||
|
||||
@@ -283,16 +288,18 @@ location /sub {
|
||||
|
||||
## Языки
|
||||
|
||||
- Английский
|
||||
- Фарси
|
||||
- Китайский
|
||||
- Русский
|
||||
- Вьетнамский
|
||||
- Испанский
|
||||
- Индонезийский
|
||||
- Украинский
|
||||
- Турецкий
|
||||
- Португальский (Бразилия)
|
||||
- English (английский)
|
||||
- Persian (персидский)
|
||||
- Traditional Chinese (традиционный китайский)
|
||||
- Simplified Chinese (упрощенный китайский)
|
||||
- Japanese (японский)
|
||||
- Russian (русский)
|
||||
- Vietnamese (вьетнамский)
|
||||
- Spanish (испанский)
|
||||
- Indonesian (индонезийский)
|
||||
- Ukrainian (украинский)
|
||||
- Turkish (турецкий)
|
||||
- Português (Brazil) (португальский (Бразилия))
|
||||
|
||||
## Возможности
|
||||
|
||||
@@ -544,13 +551,34 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
|
||||
## Предварительный Просмотр
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/01-overview-dark.png">
|
||||
<img alt="3x-ui" src="./media/01-overview-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/02-inbounds-dark.png">
|
||||
<img alt="3x-ui" src="./media/02-inbounds-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/03-add-inbound-dark.png">
|
||||
<img alt="3x-ui" src="./media/03-add-inbound-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/04-add-client-dark.png">
|
||||
<img alt="3x-ui" src="./media/04-add-client-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/05-settings-dark.png">
|
||||
<img alt="3x-ui" src="./media/05-settings-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/06-configs-dark.png">
|
||||
<img alt="3x-ui" src="./media/06-configs-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/07-bot-dark.png">
|
||||
<img alt="3x-ui" src="./media/07-bot-light.png">
|
||||
</picture>
|
||||
|
||||
## Особая благодарность
|
||||
|
||||
@@ -559,8 +587,8 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
## Благодарности
|
||||
|
||||
- [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._
|
||||
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (License: **GPL-3.0**): _Этот репозиторий содержит автоматически обновляемые правила маршрутизации V2Ray на основе данных о заблокированных доменах и адресах в России._
|
||||
|
||||
## Число звёзд со временем
|
||||
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
@@ -1,6 +1,11 @@
|
||||
[English](/README.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||
|
||||
<p align="center"><a href="#"><img src="./media/3X-UI.png" alt="Image"></a></p>
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/3x-ui-dark.png">
|
||||
<img alt="3x-ui" src="./media/3x-ui-light.png">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
**一个更好的面板 • 基于Xray Core构建**
|
||||
|
||||
@@ -277,14 +282,18 @@ location /sub {
|
||||
|
||||
## Languages
|
||||
|
||||
- English(英语)
|
||||
- Farsi(伊朗语)
|
||||
- Chinese(中文)
|
||||
- Russian(俄语)
|
||||
- Vietnamese(越南语)
|
||||
- Spanish(西班牙语)
|
||||
- Indonesian (印度尼西亚语)
|
||||
- Ukrainian(乌克兰语)
|
||||
- English(英语)
|
||||
- Persian(波斯语)
|
||||
- Traditional Chinese(繁体中文)
|
||||
- Simplified Chinese(简体中文)
|
||||
- Japanese(日语)
|
||||
- Russian(俄语)
|
||||
- Vietnamese(越南语)
|
||||
- Spanish(西班牙语)
|
||||
- Indonesian(印尼语)
|
||||
- Ukrainian(乌克兰语)
|
||||
- Turkish(土耳其语)
|
||||
- Português (Brazil)(葡萄牙语(巴西))
|
||||
|
||||
|
||||
## Features
|
||||
@@ -535,13 +544,34 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
|
||||
## 预览
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/01-overview-dark.png">
|
||||
<img alt="3x-ui" src="./media/01-overview-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/02-inbounds-dark.png">
|
||||
<img alt="3x-ui" src="./media/02-inbounds-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/03-add-inbound-dark.png">
|
||||
<img alt="3x-ui" src="./media/03-add-inbound-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/04-add-client-dark.png">
|
||||
<img alt="3x-ui" src="./media/04-add-client-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/05-settings-dark.png">
|
||||
<img alt="3x-ui" src="./media/05-settings-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/06-configs-dark.png">
|
||||
<img alt="3x-ui" src="./media/06-configs-light.png">
|
||||
</picture>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./media/07-bot-dark.png">
|
||||
<img alt="3x-ui" src="./media/07-bot-light.png">
|
||||
</picture>
|
||||
|
||||
## 特别感谢
|
||||
|
||||
@@ -550,8 +580,8 @@ XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||
## 致谢
|
||||
|
||||
- [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._
|
||||
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (License: **GPL-3.0**): _This repository contains automatically updated V2Ray routing rules based on data on blocked domains and addresses in Russia._
|
||||
|
||||
## Star趋势
|
||||
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||
@@ -1 +1 @@
|
||||
2.4.7
|
||||
2.5.4
|
||||
@@ -98,5 +98,6 @@ type Client struct {
|
||||
Enable bool `json:"enable" form:"enable"`
|
||||
TgID int64 `json:"tgId" form:"tgId"`
|
||||
SubID string `json:"subId" form:"subId"`
|
||||
Comment string `json:"comment" form:"comment"`
|
||||
Reset int `json:"reset" form:"reset"`
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ services:
|
||||
- $PWD/cert/:/root/cert/
|
||||
environment:
|
||||
XRAY_VMESS_AEAD_FORCED: "false"
|
||||
X_UI_ENABLE_FAIL2BAN: "true"
|
||||
tty: true
|
||||
network_mode: host
|
||||
restart: unless-stopped
|
||||
|
||||
87
go.mod
@@ -1,46 +1,45 @@
|
||||
module x-ui
|
||||
|
||||
go 1.23.3
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/gzip v1.0.1
|
||||
github.com/gin-contrib/sessions v1.0.1
|
||||
github.com/gin-contrib/gzip v1.2.2
|
||||
github.com/gin-contrib/sessions v1.0.2
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/goccy/go-json v0.10.3
|
||||
github.com/mymmrac/telego v0.31.4
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.1
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/mymmrac/telego v0.32.0
|
||||
github.com/nicksnyder/go-i18n/v2 v2.5.1
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
github.com/pelletier/go-toml/v2 v2.2.3
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/shirou/gopsutil/v4 v4.24.10
|
||||
github.com/valyala/fasthttp v1.57.0
|
||||
github.com/xtls/xray-core v1.8.25-0.20241111042233-0df2446f824d
|
||||
github.com/shirou/gopsutil/v4 v4.25.2
|
||||
github.com/valyala/fasthttp v1.59.0
|
||||
github.com/xtls/xray-core v1.8.25-0.20250303153022-e15dff94b5bd
|
||||
go.uber.org/atomic v1.11.0
|
||||
golang.org/x/text v0.20.0
|
||||
google.golang.org/grpc v1.68.0
|
||||
gorm.io/driver/sqlite v1.5.6
|
||||
golang.org/x/text v0.22.0
|
||||
google.golang.org/grpc v1.71.0
|
||||
gorm.io/driver/sqlite v1.5.7
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.4 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.1 // indirect
|
||||
github.com/cloudflare/circl v1.5.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.1 // indirect
|
||||
github.com/fasthttp/router v1.5.2 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/bytedance/sonic v1.12.10 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
||||
github.com/cloudflare/circl v1.6.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
github.com/fasthttp/router v1.5.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.25.0 // 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-20241101162523-b92577c0c142 // indirect
|
||||
github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 // 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,24 +48,24 @@ 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.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.48.1 // indirect
|
||||
github.com/quic-go/quic-go v0.50.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.13.1 // indirect
|
||||
github.com/sagernet/sing v0.5.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/sagernet/sing v0.6.2 // 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
|
||||
@@ -83,20 +82,20 @@ require (
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
golang.org/x/arch v0.12.0 // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.31.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
golang.org/x/tools v0.27.0 // indirect
|
||||
golang.org/x/arch v0.15.0 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.36.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.30.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-20241104194629-dd2ea8efbc28 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20231202080848-1f7806d17489 // indirect
|
||||
lukechampine.com/blake3 v1.3.0 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0 // indirect
|
||||
lukechampine.com/blake3 v1.4.0 // indirect
|
||||
)
|
||||
|
||||
203
go.sum
@@ -4,42 +4,43 @@ github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJS
|
||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/bytedance/sonic v1.12.4 h1:9Csb3c9ZJhfUWeMtpCDCq6BUoH5ogfDFLUgQ/jG+R0k=
|
||||
github.com/bytedance/sonic v1.12.4/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.12.10 h1:uVCQr6oS5669E9ZVW0HyksTLfNS7Q/9hV6IVS4nEMsI=
|
||||
github.com/bytedance/sonic v1.12.10/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
|
||||
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
||||
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
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=
|
||||
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
|
||||
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
||||
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
|
||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||
github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE=
|
||||
github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/fasthttp/router v1.5.2 h1:ckJCCdV7hWkkrMeId3WfEhz+4Gyyf6QPwxi/RHIMZ6I=
|
||||
github.com/fasthttp/router v1.5.2/go.mod h1:C8EY53ozOwpONyevc/V7Gr8pqnEjwnkFFqPo1alAGs0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
|
||||
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 h1:ucRHb6/lvW/+mTEIGbvhcYU3S8+uSNkuMjx/qZFfhtM=
|
||||
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/fasthttp/router v1.5.4 h1:oxdThbBwQgsDIYZ3wR1IavsNl6ZS9WdjKukeMikOnC8=
|
||||
github.com/fasthttp/router v1.5.4/go.mod h1:3/hysWq6cky7dTfzaaEPZGdptwjwx0qzTgFCKEWRjgc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
github.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE=
|
||||
github.com/gin-contrib/gzip v1.0.1/go.mod h1:njt428fdUNRvjuJf16tZMYZ2Yl+WQB53X5wmhDwXvC4=
|
||||
github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI=
|
||||
github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-contrib/gzip v1.2.2 h1:iUU/EYCM8ENfkjmZaVrxbjF/ZC267Iqv5S0MMCMEliI=
|
||||
github.com/gin-contrib/gzip v1.2.2/go.mod h1:C1a5cacjlDsS20cKnHlZRCPUu57D3qH6B2pV0rl+Y/s=
|
||||
github.com/gin-contrib/sessions v1.0.2 h1:UaIjUvTH1cMeOdj3in6dl+Xb6It8RiKRF9Z1anbUyCA=
|
||||
github.com/gin-contrib/sessions v1.0.2/go.mod h1:KxKxWqWP5LJVDCInulOl4WbLzK2KSPlLesfZ66wRvMs=
|
||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
@@ -49,25 +50,27 @@ 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.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
|
||||
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
||||
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=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs=
|
||||
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7 h1:+J3r2e8+RsmN3vKfo75g0YSY61ms37qzPglu4p0sGro=
|
||||
github.com/google/pprof v0.0.0-20250302191652-9094ed2288e7/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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=
|
||||
@@ -84,11 +87,11 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@@ -96,27 +99,27 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
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-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d h1:fjMbDVUGsMQiVZnSQsmouYJvMdwsGiDipOZoN66v844=
|
||||
github.com/lufia/plan9stats v0.0.0-20250303091104-876f3ea5145d/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||
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.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
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/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
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.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mymmrac/telego v0.31.4 h1:NpiNl0P/8eydknka/k6XaaaWVj5BKMlM3Ibba63QTBU=
|
||||
github.com/mymmrac/telego v0.31.4/go.mod h1:T12js1PgbYDYznvoN05MSMuPMfWTYo7D9LKl5cPFWiI=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.1 h1:zwzjtX4uYyiaU02K5Ia3zSkpJZrByARkRB4V3YPrr0g=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.4.1/go.mod h1:++Pl70FR6Cki7hdzZRnEEqdc2dJt+SAGotyFg/SvZMk=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
||||
github.com/mymmrac/telego v0.32.0 h1:4X8C1l3k+opkk86r95+eQE8DxiS2LYlR61L/G7yreDY=
|
||||
github.com/mymmrac/telego v0.32.0/go.mod h1:qS6NaRhJgcuEEBEMVCV79S2xCAuHq9O+ixwfLuRW31M=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.5.1 h1:IxtPxYsR9Gp60cGXjfuR/llTqV8aYMsC472zD0D1vHk=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
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/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
@@ -131,37 +134,39 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/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.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
|
||||
github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||
github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo=
|
||||
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||
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.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/sagernet/sing v0.5.0 h1:soo2wVwLcieKWWKIksFNK6CCAojUgAppqQVwyRYGkEM=
|
||||
github.com/sagernet/sing v0.5.0/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/sagernet/sing v0.6.2 h1:TR9WeH0yDJMjSFThqgFYe/i2pdH69Gb0tDJzJLPuVec=
|
||||
github.com/sagernet/sing v0.6.2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
|
||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||
github.com/shirou/gopsutil/v4 v4.24.10 h1:7VOzPtfw/5YDU+jLEoBwXwxJbQetULywoSV4RYY7HkM=
|
||||
github.com/shirou/gopsutil/v4 v4.24.10/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8=
|
||||
github.com/shirou/gopsutil/v4 v4.25.2 h1:NMscG3l2CqtWFS86kj3vP7soOczqrQYIEhO/pMvvQkk=
|
||||
github.com/shirou/gopsutil/v4 v4.25.2/go.mod h1:34gBYJzyqCDT11b6bMHP0XCvWeU3J61XRT7a2EmCRTA=
|
||||
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
|
||||
@@ -174,8 +179,8 @@ 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.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg=
|
||||
github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE=
|
||||
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
||||
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
||||
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
||||
@@ -185,54 +190,66 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd
|
||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
|
||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
|
||||
github.com/xtls/xray-core v1.8.25-0.20241111042233-0df2446f824d h1:00+ceMqRs5YdBMm4NLdsupYnxkP5Ghka9Lzw2ciVhgk=
|
||||
github.com/xtls/xray-core v1.8.25-0.20241111042233-0df2446f824d/go.mod h1:n8wJBW828YcDQ+9+BnAtbofXVt31VMvz4TwbZK0BaEE=
|
||||
github.com/xtls/xray-core v1.8.25-0.20250303153022-e15dff94b5bd h1:xzZCYhdr1pL1kZe7GysAMuVNtlzXqsQKTFbc1xvR+bI=
|
||||
github.com/xtls/xray-core v1.8.25-0.20250303153022-e15dff94b5bd/go.mod h1:0n4A2nJD1yZlxuXexV5rJODKcJJo8zpbTFcESVg8fgM=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||
golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg=
|
||||
golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
|
||||
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c=
|
||||
golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
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=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
|
||||
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
@@ -242,12 +259,12 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
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=
|
||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0 h1:P+U/06iIKPQ3DLcg+zBfSCia1luZ2msPZrJ8jYDFPs0=
|
||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
|
||||
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
|
||||
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
|
||||
50
install.sh
@@ -2,6 +2,7 @@
|
||||
|
||||
red='\033[0;31m'
|
||||
green='\033[0;32m'
|
||||
blue='\033[0;34m'
|
||||
yellow='\033[0;33m'
|
||||
plain='\033[0m'
|
||||
|
||||
@@ -89,6 +90,10 @@ elif [[ "${release}" == "ol" ]]; then
|
||||
if [[ ${os_version} -lt 8 ]]; then
|
||||
echo -e "${red} Please use Oracle Linux 8 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
elif [[ "${release}" == "virtuozzo" ]]; then
|
||||
if [[ ${os_version} -lt 8 ]]; then
|
||||
echo -e "${red} Please use Virtuozzo Linux 8 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${red}Your operating system is not supported by this script.${plain}\n"
|
||||
echo "Please ensure you are using one of the following supported operating systems:"
|
||||
@@ -106,6 +111,7 @@ else
|
||||
echo "- Oracle Linux 8+"
|
||||
echo "- OpenSUSE Tumbleweed"
|
||||
echo "- Amazon Linux 2023"
|
||||
echo "- Virtuozzo Linux 8+"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -117,7 +123,7 @@ install_base() {
|
||||
centos | almalinux | rocky | ol)
|
||||
yum -y update && yum install -y -q wget curl tar tzdata
|
||||
;;
|
||||
fedora | amzn)
|
||||
fedora | amzn | virtuozzo)
|
||||
dnf -y update && dnf install -y -q wget curl tar tzdata
|
||||
;;
|
||||
arch | manjaro | parch)
|
||||
@@ -208,7 +214,7 @@ install_x-ui() {
|
||||
exit 1
|
||||
fi
|
||||
echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
|
||||
wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
|
||||
wget -N -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo -e "${red}Downloading x-ui failed, please be sure that your server can access GitHub ${plain}"
|
||||
exit 1
|
||||
@@ -225,7 +231,7 @@ install_x-ui() {
|
||||
|
||||
url="https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz"
|
||||
echo -e "Beginning to install x-ui $1"
|
||||
wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch).tar.gz ${url}
|
||||
wget -N -O /usr/local/x-ui-linux-$(arch).tar.gz ${url}
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo -e "${red}Download x-ui $1 failed, please check if the version exists ${plain}"
|
||||
exit 1
|
||||
@@ -250,7 +256,7 @@ install_x-ui() {
|
||||
|
||||
chmod +x x-ui bin/xray-linux-$(arch)
|
||||
cp -f x-ui.service /etc/systemd/system/
|
||||
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
|
||||
wget -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
|
||||
chmod +x /usr/local/x-ui/x-ui.sh
|
||||
chmod +x /usr/bin/x-ui
|
||||
config_after_install
|
||||
@@ -260,24 +266,24 @@ install_x-ui() {
|
||||
systemctl start x-ui
|
||||
echo -e "${green}x-ui ${tag_version}${plain} installation finished, it is running now..."
|
||||
echo -e ""
|
||||
echo -e "x-ui control menu usages: "
|
||||
echo -e "----------------------------------------------"
|
||||
echo -e "SUBCOMMANDS:"
|
||||
echo -e "x-ui - Admin Management Script"
|
||||
echo -e "x-ui start - Start"
|
||||
echo -e "x-ui stop - Stop"
|
||||
echo -e "x-ui restart - Restart"
|
||||
echo -e "x-ui status - Current Status"
|
||||
echo -e "x-ui settings - Current Settings"
|
||||
echo -e "x-ui enable - Enable Autostart on OS Startup"
|
||||
echo -e "x-ui disable - Disable Autostart on OS Startup"
|
||||
echo -e "x-ui log - Check logs"
|
||||
echo -e "x-ui banlog - Check Fail2ban ban logs"
|
||||
echo -e "x-ui update - Update"
|
||||
echo -e "x-ui legacy - legacy version"
|
||||
echo -e "x-ui install - Install"
|
||||
echo -e "x-ui uninstall - Uninstall"
|
||||
echo -e "----------------------------------------------"
|
||||
echo -e "┌───────────────────────────────────────────────────────┐
|
||||
│ ${blue}x-ui control menu usages (subcommands):${plain} │
|
||||
│ │
|
||||
│ ${blue}x-ui${plain} - Admin Management Script │
|
||||
│ ${blue}x-ui start${plain} - Start │
|
||||
│ ${blue}x-ui stop${plain} - Stop │
|
||||
│ ${blue}x-ui restart${plain} - Restart │
|
||||
│ ${blue}x-ui status${plain} - Current Status │
|
||||
│ ${blue}x-ui settings${plain} - Current Settings │
|
||||
│ ${blue}x-ui enable${plain} - Enable Autostart on OS Startup │
|
||||
│ ${blue}x-ui disable${plain} - Disable Autostart on OS Startup │
|
||||
│ ${blue}x-ui log${plain} - Check logs │
|
||||
│ ${blue}x-ui banlog${plain} - Check Fail2ban ban logs │
|
||||
│ ${blue}x-ui update${plain} - Update │
|
||||
│ ${blue}x-ui legacy${plain} - legacy version │
|
||||
│ ${blue}x-ui install${plain} - Install │
|
||||
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
||||
└───────────────────────────────────────────────────────┘"
|
||||
}
|
||||
|
||||
echo -e "${green}Running...${plain}"
|
||||
|
||||
BIN
media/01-overview-dark.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
media/01-overview-light.png
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
media/02-inbounds-dark.png
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
media/02-inbounds-light.png
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
media/03-add-inbound-dark.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
media/03-add-inbound-light.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
media/04-add-client-dark.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
media/04-add-client-light.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
media/05-settings-dark.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
media/05-settings-light.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
media/06-configs-dark.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
media/06-configs-light.png
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
media/07-bot-dark.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
media/07-bot-light.png
Normal file
|
After Width: | Height: | Size: 255 KiB |
BIN
media/1.png
|
Before Width: | Height: | Size: 59 KiB |
BIN
media/2.png
|
Before Width: | Height: | Size: 91 KiB |
BIN
media/3.png
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 226 KiB |
BIN
media/3x-ui-light.png
Normal file
|
After Width: | Height: | Size: 224 KiB |
BIN
media/4.png
|
Before Width: | Height: | Size: 26 KiB |
BIN
media/5.png
|
Before Width: | Height: | Size: 71 KiB |
BIN
media/6.png
|
Before Width: | Height: | Size: 42 KiB |
BIN
media/7.png
|
Before Width: | Height: | Size: 60 KiB |
@@ -47,7 +47,7 @@
|
||||
"tag": "direct",
|
||||
"protocol": "freedom",
|
||||
"settings": {
|
||||
"domainStrategy": "UseIP",
|
||||
"domainStrategy": "AsIs",
|
||||
"redirect": "",
|
||||
"noises": []
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ type SubJsonService struct {
|
||||
configJson map[string]interface{}
|
||||
defaultOutbounds []json_util.RawMessage
|
||||
fragment string
|
||||
noises string
|
||||
noises string
|
||||
mux string
|
||||
|
||||
inboundService service.InboundService
|
||||
@@ -61,7 +61,7 @@ func NewSubJsonService(fragment string, noises string, mux string, rules string,
|
||||
configJson: configJson,
|
||||
defaultOutbounds: defaultOutbounds,
|
||||
fragment: fragment,
|
||||
noises: noises,
|
||||
noises: noises,
|
||||
mux: mux,
|
||||
SubService: subService,
|
||||
}
|
||||
@@ -217,7 +217,7 @@ func (s *SubJsonService) streamData(stream string) map[string]interface{} {
|
||||
delete(streamSettings, "sockopt")
|
||||
|
||||
if s.fragment != "" {
|
||||
streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "tcpMptcp": true, "tcpNoDelay": true}`)
|
||||
streamSettings["sockopt"] = json_util.RawMessage(`{"dialerProxy": "fragment", "tcpKeepAliveIdle": 100, "tcpMptcp": true, "penetrate": true}`)
|
||||
}
|
||||
|
||||
// remove proxy protocol
|
||||
|
||||
@@ -208,11 +208,6 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
||||
headers, _ := ws["headers"].(map[string]interface{})
|
||||
obj["host"] = searchHost(headers)
|
||||
}
|
||||
case "http":
|
||||
obj["net"] = "h2"
|
||||
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||
obj["path"], _ = http["path"].(string)
|
||||
obj["host"] = searchHost(http)
|
||||
case "grpc":
|
||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||
obj["path"] = grpc["serviceName"].(string)
|
||||
@@ -229,16 +224,16 @@ func (s *SubService) genVmessLink(inbound *model.Inbound, email string) string {
|
||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
||||
obj["host"] = searchHost(headers)
|
||||
}
|
||||
case "splithttp":
|
||||
splithttp, _ := stream["splithttpSettings"].(map[string]interface{})
|
||||
obj["path"] = splithttp["path"].(string)
|
||||
if host, ok := splithttp["host"].(string); ok && len(host) > 0 {
|
||||
case "xhttp":
|
||||
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
||||
obj["path"] = xhttp["path"].(string)
|
||||
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
||||
obj["host"] = host
|
||||
} else {
|
||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
||||
headers, _ := xhttp["headers"].(map[string]interface{})
|
||||
obj["host"] = searchHost(headers)
|
||||
}
|
||||
obj["mode"] = splithttp["mode"].(string)
|
||||
obj["mode"] = xhttp["mode"].(string)
|
||||
}
|
||||
security, _ := stream["security"].(string)
|
||||
obj["tls"] = security
|
||||
@@ -361,10 +356,6 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
headers, _ := ws["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
}
|
||||
case "http":
|
||||
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||
params["path"] = http["path"].(string)
|
||||
params["host"] = searchHost(http)
|
||||
case "grpc":
|
||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||
params["serviceName"] = grpc["serviceName"].(string)
|
||||
@@ -381,16 +372,16 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string {
|
||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
}
|
||||
case "splithttp":
|
||||
splithttp, _ := stream["splithttpSettings"].(map[string]interface{})
|
||||
params["path"] = splithttp["path"].(string)
|
||||
if host, ok := splithttp["host"].(string); ok && len(host) > 0 {
|
||||
case "xhttp":
|
||||
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
||||
params["path"] = xhttp["path"].(string)
|
||||
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
||||
params["host"] = host
|
||||
} else {
|
||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
||||
headers, _ := xhttp["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
}
|
||||
params["mode"] = splithttp["mode"].(string)
|
||||
params["mode"] = xhttp["mode"].(string)
|
||||
}
|
||||
security, _ := stream["security"].(string)
|
||||
if security == "tls" {
|
||||
@@ -559,10 +550,6 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||
headers, _ := ws["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
}
|
||||
case "http":
|
||||
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||
params["path"] = http["path"].(string)
|
||||
params["host"] = searchHost(http)
|
||||
case "grpc":
|
||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||
params["serviceName"] = grpc["serviceName"].(string)
|
||||
@@ -579,16 +566,16 @@ func (s *SubService) genTrojanLink(inbound *model.Inbound, email string) string
|
||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
}
|
||||
case "splithttp":
|
||||
splithttp, _ := stream["splithttpSettings"].(map[string]interface{})
|
||||
params["path"] = splithttp["path"].(string)
|
||||
if host, ok := splithttp["host"].(string); ok && len(host) > 0 {
|
||||
case "xhttp":
|
||||
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
||||
params["path"] = xhttp["path"].(string)
|
||||
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
||||
params["host"] = host
|
||||
} else {
|
||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
||||
headers, _ := xhttp["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
}
|
||||
params["mode"] = splithttp["mode"].(string)
|
||||
params["mode"] = xhttp["mode"].(string)
|
||||
}
|
||||
security, _ := stream["security"].(string)
|
||||
if security == "tls" {
|
||||
@@ -757,10 +744,6 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
||||
headers, _ := ws["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
}
|
||||
case "http":
|
||||
http, _ := stream["httpSettings"].(map[string]interface{})
|
||||
params["path"] = http["path"].(string)
|
||||
params["host"] = searchHost(http)
|
||||
case "grpc":
|
||||
grpc, _ := stream["grpcSettings"].(map[string]interface{})
|
||||
params["serviceName"] = grpc["serviceName"].(string)
|
||||
@@ -777,16 +760,16 @@ func (s *SubService) genShadowsocksLink(inbound *model.Inbound, email string) st
|
||||
headers, _ := httpupgrade["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
}
|
||||
case "splithttp":
|
||||
splithttp, _ := stream["splithttpSettings"].(map[string]interface{})
|
||||
params["path"] = splithttp["path"].(string)
|
||||
if host, ok := splithttp["host"].(string); ok && len(host) > 0 {
|
||||
case "xhttp":
|
||||
xhttp, _ := stream["xhttpSettings"].(map[string]interface{})
|
||||
params["path"] = xhttp["path"].(string)
|
||||
if host, ok := xhttp["host"].(string); ok && len(host) > 0 {
|
||||
params["host"] = host
|
||||
} else {
|
||||
headers, _ := splithttp["headers"].(map[string]interface{})
|
||||
headers, _ := xhttp["headers"].(map[string]interface{})
|
||||
params["host"] = searchHost(headers)
|
||||
}
|
||||
params["mode"] = splithttp["mode"].(string)
|
||||
params["mode"] = xhttp["mode"].(string)
|
||||
}
|
||||
|
||||
security, _ := stream["security"].(string)
|
||||
|
||||
2
web/assets/axios/axios.min.js
vendored
2
web/assets/css/custom.min.css
vendored
@@ -19,6 +19,11 @@ const supportLangs = [
|
||||
value: "zh-TW",
|
||||
icon: "🇹🇼",
|
||||
},
|
||||
{
|
||||
name: "日本語",
|
||||
value: "ja-JP",
|
||||
icon: "🇯🇵",
|
||||
},
|
||||
{
|
||||
name: "Русский",
|
||||
value: "ru-RU",
|
||||
|
||||
@@ -34,10 +34,6 @@ const TLS_VERSION_OPTION = {
|
||||
};
|
||||
|
||||
const TLS_CIPHER_OPTION = {
|
||||
RSA_AES_128_CBC: "TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||
RSA_AES_256_CBC: "TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||
RSA_AES_128_GCM: "TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||
RSA_AES_256_GCM: "TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||
AES_128_GCM: "TLS_AES_128_GCM_SHA256",
|
||||
AES_256_GCM: "TLS_AES_256_GCM_SHA384",
|
||||
CHACHA20_POLY1305: "TLS_CHACHA20_POLY1305_SHA256",
|
||||
@@ -64,6 +60,8 @@ const UTLS_FINGERPRINT = {
|
||||
UTLS_QQ: "qq",
|
||||
UTLS_RANDOM: "random",
|
||||
UTLS_RANDOMIZED: "randomized",
|
||||
UTLS_RONDOMIZEDNOALPN: "randomizednoalpn",
|
||||
UTLS_UNSAFE: "unsafe",
|
||||
};
|
||||
|
||||
const ALPN_OPTION = {
|
||||
@@ -117,6 +115,7 @@ const MODE_OPTION = {
|
||||
AUTO: "auto",
|
||||
PACKET_UP: "packet-up",
|
||||
STREAM_UP: "stream-up",
|
||||
STREAM_ONE: "stream-one",
|
||||
};
|
||||
|
||||
Object.freeze(Protocols);
|
||||
@@ -378,13 +377,15 @@ class WsStreamSettings extends XrayCommonClass {
|
||||
acceptProxyProtocol = false,
|
||||
path = '/',
|
||||
host = '',
|
||||
headers = []
|
||||
headers = [],
|
||||
heartbeatPeriod = 0,
|
||||
) {
|
||||
super();
|
||||
this.acceptProxyProtocol = acceptProxyProtocol;
|
||||
this.path = path;
|
||||
this.host = host;
|
||||
this.headers = headers;
|
||||
this.heartbeatPeriod = heartbeatPeriod;
|
||||
}
|
||||
|
||||
addHeader(name, value) {
|
||||
@@ -401,6 +402,7 @@ class WsStreamSettings extends XrayCommonClass {
|
||||
json.path,
|
||||
json.host,
|
||||
XrayCommonClass.toHeaders(json.headers),
|
||||
json.heartbeatPeriod,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -410,46 +412,11 @@ class WsStreamSettings extends XrayCommonClass {
|
||||
path: this.path,
|
||||
host: this.host,
|
||||
headers: XrayCommonClass.toV2Headers(this.headers, false),
|
||||
heartbeatPeriod: this.heartbeatPeriod,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class HttpStreamSettings extends XrayCommonClass {
|
||||
constructor(
|
||||
path = '/',
|
||||
host = [''],
|
||||
) {
|
||||
super();
|
||||
this.path = path;
|
||||
this.host = host.length === 0 ? [''] : host;
|
||||
}
|
||||
|
||||
addHost(host) {
|
||||
this.host.push(host);
|
||||
}
|
||||
|
||||
removeHost(index) {
|
||||
this.host.splice(index, 1);
|
||||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
return new HttpStreamSettings(json.path, json.host);
|
||||
}
|
||||
|
||||
toJson() {
|
||||
let host = [];
|
||||
for (let i = 0; i < this.host.length; ++i) {
|
||||
if (!ObjectUtil.isEmpty(this.host[i])) {
|
||||
host.push(this.host[i]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
path: this.path,
|
||||
host: host,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GrpcStreamSettings extends XrayCommonClass {
|
||||
constructor(
|
||||
serviceName = "",
|
||||
@@ -520,34 +487,27 @@ class HTTPUpgradeStreamSettings extends XrayCommonClass {
|
||||
}
|
||||
}
|
||||
|
||||
class SplitHTTPStreamSettings extends XrayCommonClass {
|
||||
class xHTTPStreamSettings extends XrayCommonClass {
|
||||
constructor(
|
||||
path = '/',
|
||||
host = '',
|
||||
headers = [],
|
||||
scMaxConcurrentPosts = "100-200",
|
||||
scMaxEachPostBytes = "1000000-2000000",
|
||||
scMinPostsIntervalMs = "10-50",
|
||||
scMaxBufferedPosts = 30,
|
||||
scMaxEachPostBytes = "1000000",
|
||||
scStreamUpServerSecs = "20-80",
|
||||
noSSEHeader = false,
|
||||
xPaddingBytes = "100-1000",
|
||||
xmux = {
|
||||
maxConcurrency: "16-32",
|
||||
maxConnections: 0,
|
||||
cMaxReuseTimes: "64-128",
|
||||
cMaxLifetimeMs: 0
|
||||
},
|
||||
mode = MODE_OPTION.AUTO,
|
||||
) {
|
||||
super();
|
||||
this.path = path;
|
||||
this.host = host;
|
||||
this.headers = headers;
|
||||
this.scMaxConcurrentPosts = scMaxConcurrentPosts;
|
||||
this.scMaxBufferedPosts = scMaxBufferedPosts;
|
||||
this.scMaxEachPostBytes = scMaxEachPostBytes;
|
||||
this.scMinPostsIntervalMs = scMinPostsIntervalMs;
|
||||
this.scStreamUpServerSecs = scStreamUpServerSecs;
|
||||
this.noSSEHeader = noSSEHeader;
|
||||
this.xPaddingBytes = xPaddingBytes;
|
||||
this.xmux = xmux;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@@ -560,16 +520,15 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
||||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
return new SplitHTTPStreamSettings(
|
||||
return new xHTTPStreamSettings(
|
||||
json.path,
|
||||
json.host,
|
||||
XrayCommonClass.toHeaders(json.headers),
|
||||
json.scMaxConcurrentPosts,
|
||||
json.scMaxBufferedPosts,
|
||||
json.scMaxEachPostBytes,
|
||||
json.scMinPostsIntervalMs,
|
||||
json.scStreamUpServerSecs,
|
||||
json.noSSEHeader,
|
||||
json.xPaddingBytes,
|
||||
json.xmux,
|
||||
json.mode,
|
||||
);
|
||||
}
|
||||
@@ -579,17 +538,11 @@ class SplitHTTPStreamSettings extends XrayCommonClass {
|
||||
path: this.path,
|
||||
host: this.host,
|
||||
headers: XrayCommonClass.toV2Headers(this.headers, false),
|
||||
scMaxConcurrentPosts: this.scMaxConcurrentPosts,
|
||||
scMaxBufferedPosts: this.scMaxBufferedPosts,
|
||||
scMaxEachPostBytes: this.scMaxEachPostBytes,
|
||||
scMinPostsIntervalMs: this.scMinPostsIntervalMs,
|
||||
scStreamUpServerSecs: this.scStreamUpServerSecs,
|
||||
noSSEHeader: this.noSSEHeader,
|
||||
xPaddingBytes: this.xPaddingBytes,
|
||||
xmux: {
|
||||
maxConcurrency: this.xmux.maxConcurrency,
|
||||
maxConnections: this.xmux.maxConnections,
|
||||
cMaxReuseTimes: this.xmux.cMaxReuseTimes,
|
||||
cMaxLifetimeMs: this.xmux.cMaxLifetimeMs
|
||||
},
|
||||
mode: this.mode,
|
||||
};
|
||||
}
|
||||
@@ -602,6 +555,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
maxVersion = TLS_VERSION_OPTION.TLS13,
|
||||
cipherSuites = '',
|
||||
rejectUnknownSni = false,
|
||||
verifyPeerCertInNames = ['dns.google', 'cloudflare-dns.com'],
|
||||
disableSystemRoot = false,
|
||||
enableSessionResumption = false,
|
||||
certificates = [new TlsStreamSettings.Cert()],
|
||||
@@ -614,6 +568,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
this.maxVersion = maxVersion;
|
||||
this.cipherSuites = cipherSuites;
|
||||
this.rejectUnknownSni = rejectUnknownSni;
|
||||
this.verifyPeerCertInNames = Array.isArray(verifyPeerCertInNames) ? verifyPeerCertInNames.join(",") : verifyPeerCertInNames;
|
||||
this.disableSystemRoot = disableSystemRoot;
|
||||
this.enableSessionResumption = enableSessionResumption;
|
||||
this.certs = certificates;
|
||||
@@ -645,6 +600,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
json.maxVersion,
|
||||
json.cipherSuites,
|
||||
json.rejectUnknownSni,
|
||||
json.verifyPeerCertInNames,
|
||||
json.disableSystemRoot,
|
||||
json.enableSessionResumption,
|
||||
certs,
|
||||
@@ -660,6 +616,7 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
maxVersion: this.maxVersion,
|
||||
cipherSuites: this.cipherSuites,
|
||||
rejectUnknownSni: this.rejectUnknownSni,
|
||||
verifyPeerCertInNames: this.verifyPeerCertInNames.split(","),
|
||||
disableSystemRoot: this.disableSystemRoot,
|
||||
enableSessionResumption: this.enableSessionResumption,
|
||||
certificates: TlsStreamSettings.toJsonArray(this.certs),
|
||||
@@ -741,7 +698,10 @@ TlsStreamSettings.Cert = class extends XrayCommonClass {
|
||||
};
|
||||
|
||||
TlsStreamSettings.Settings = class extends XrayCommonClass {
|
||||
constructor(allowInsecure = false, fingerprint = '') {
|
||||
constructor(
|
||||
allowInsecure = false,
|
||||
fingerprint = UTLS_FINGERPRINT.UTLS_CHROME,
|
||||
) {
|
||||
super();
|
||||
this.allowInsecure = allowInsecure;
|
||||
this.fingerprint = fingerprint;
|
||||
@@ -807,7 +767,7 @@ class RealityStreamSettings extends XrayCommonClass {
|
||||
json.maxClient,
|
||||
json.maxTimediff,
|
||||
json.shortIds,
|
||||
json.settings,
|
||||
settings,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -830,7 +790,7 @@ class RealityStreamSettings extends XrayCommonClass {
|
||||
RealityStreamSettings.Settings = class extends XrayCommonClass {
|
||||
constructor(
|
||||
publicKey = '',
|
||||
fingerprint = UTLS_FINGERPRINT.UTLS_RANDOM,
|
||||
fingerprint = UTLS_FINGERPRINT.UTLS_CHROME,
|
||||
serverName = '',
|
||||
spiderX = '/'
|
||||
) {
|
||||
@@ -865,7 +825,7 @@ class SockoptStreamSettings extends XrayCommonClass {
|
||||
mark = 0,
|
||||
tproxy = "off",
|
||||
tcpMptcp = false,
|
||||
tcpNoDelay = false,
|
||||
penetrate = false,
|
||||
domainStrategy = DOMAIN_STRATEGY_OPTION.USE_IP,
|
||||
tcpMaxSeg = 1440,
|
||||
dialerProxy = "",
|
||||
@@ -883,7 +843,7 @@ class SockoptStreamSettings extends XrayCommonClass {
|
||||
this.mark = mark;
|
||||
this.tproxy = tproxy;
|
||||
this.tcpMptcp = tcpMptcp;
|
||||
this.tcpNoDelay = tcpNoDelay;
|
||||
this.penetrate = penetrate;
|
||||
this.domainStrategy = domainStrategy;
|
||||
this.tcpMaxSeg = tcpMaxSeg;
|
||||
this.dialerProxy = dialerProxy;
|
||||
@@ -904,7 +864,7 @@ class SockoptStreamSettings extends XrayCommonClass {
|
||||
json.mark,
|
||||
json.tproxy,
|
||||
json.tcpMptcp,
|
||||
json.tcpNoDelay,
|
||||
json.penetrate,
|
||||
json.domainStrategy,
|
||||
json.tcpMaxSeg,
|
||||
json.dialerProxy,
|
||||
@@ -925,7 +885,7 @@ class SockoptStreamSettings extends XrayCommonClass {
|
||||
mark: this.mark,
|
||||
tproxy: this.tproxy,
|
||||
tcpMptcp: this.tcpMptcp,
|
||||
tcpNoDelay: this.tcpNoDelay,
|
||||
penetrate: this.penetrate,
|
||||
domainStrategy: this.domainStrategy,
|
||||
tcpMaxSeg: this.tcpMaxSeg,
|
||||
dialerProxy: this.dialerProxy,
|
||||
@@ -949,10 +909,9 @@ class StreamSettings extends XrayCommonClass {
|
||||
tcpSettings = new TcpStreamSettings(),
|
||||
kcpSettings = new KcpStreamSettings(),
|
||||
wsSettings = new WsStreamSettings(),
|
||||
httpSettings = new HttpStreamSettings(),
|
||||
grpcSettings = new GrpcStreamSettings(),
|
||||
httpupgradeSettings = new HTTPUpgradeStreamSettings(),
|
||||
splithttpSettings = new SplitHTTPStreamSettings(),
|
||||
xhttpSettings = new xHTTPStreamSettings(),
|
||||
sockopt = undefined,
|
||||
) {
|
||||
super();
|
||||
@@ -964,10 +923,9 @@ class StreamSettings extends XrayCommonClass {
|
||||
this.tcp = tcpSettings;
|
||||
this.kcp = kcpSettings;
|
||||
this.ws = wsSettings;
|
||||
this.http = httpSettings;
|
||||
this.grpc = grpcSettings;
|
||||
this.httpupgrade = httpupgradeSettings;
|
||||
this.splithttp = splithttpSettings;
|
||||
this.xhttp = xhttpSettings;
|
||||
this.sockopt = sockopt;
|
||||
}
|
||||
|
||||
@@ -1014,10 +972,9 @@ class StreamSettings extends XrayCommonClass {
|
||||
TcpStreamSettings.fromJson(json.tcpSettings),
|
||||
KcpStreamSettings.fromJson(json.kcpSettings),
|
||||
WsStreamSettings.fromJson(json.wsSettings),
|
||||
HttpStreamSettings.fromJson(json.httpSettings),
|
||||
GrpcStreamSettings.fromJson(json.grpcSettings),
|
||||
HTTPUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
||||
SplitHTTPStreamSettings.fromJson(json.splithttpSettings),
|
||||
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
||||
SockoptStreamSettings.fromJson(json.sockopt),
|
||||
);
|
||||
}
|
||||
@@ -1033,10 +990,9 @@ class StreamSettings extends XrayCommonClass {
|
||||
tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
|
||||
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
|
||||
wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
|
||||
httpSettings: network === 'http' ? this.http.toJson() : undefined,
|
||||
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
|
||||
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
||||
splithttpSettings: network === 'splithttp' ? this.splithttp.toJson() : undefined,
|
||||
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
||||
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
||||
};
|
||||
}
|
||||
@@ -1165,16 +1121,12 @@ class Inbound extends XrayCommonClass {
|
||||
return this.network === "grpc";
|
||||
}
|
||||
|
||||
get isH2() {
|
||||
return this.network === "http";
|
||||
}
|
||||
|
||||
get isHttpupgrade() {
|
||||
return this.network === "httpupgrade";
|
||||
}
|
||||
|
||||
get isSplithttp() {
|
||||
return this.network === "splithttp";
|
||||
get isXHTTP() {
|
||||
return this.network === "xhttp";
|
||||
}
|
||||
|
||||
// Shadowsocks
|
||||
@@ -1213,12 +1165,10 @@ class Inbound extends XrayCommonClass {
|
||||
return this.getHeader(this.stream.tcp.request, 'host');
|
||||
} else if (this.isWs) {
|
||||
return this.stream.ws.host?.length > 0 ? this.stream.ws.host : this.getHeader(this.stream.ws, 'host');
|
||||
} else if (this.isH2) {
|
||||
return this.stream.http.host[0];
|
||||
} else if (this.isHttpupgrade) {
|
||||
return this.stream.httpupgrade.host?.length > 0 ? this.stream.httpupgrade.host : this.getHeader(this.stream.httpupgrade, 'host');
|
||||
} else if (this.isSplithttp) {
|
||||
return this.stream.splithttp.host?.length > 0 ? this.stream.splithttp.host : this.getHeader(this.stream.splithttp, 'host');
|
||||
} else if (this.isXHTTP) {
|
||||
return this.stream.xhttp.host?.length > 0 ? this.stream.xhttp.host : this.getHeader(this.stream.xhttp, 'host');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1228,12 +1178,10 @@ class Inbound extends XrayCommonClass {
|
||||
return this.stream.tcp.request.path[0];
|
||||
} else if (this.isWs) {
|
||||
return this.stream.ws.path;
|
||||
} else if (this.isH2) {
|
||||
return this.stream.http.path;
|
||||
} else if (this.isHttpupgrade) {
|
||||
return this.stream.httpupgrade.path;
|
||||
} else if (this.isSplithttp) {
|
||||
return this.stream.splithttp.path;
|
||||
} else if (this.isXHTTP) {
|
||||
return this.stream.xhttp.path;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1257,7 +1205,7 @@ class Inbound extends XrayCommonClass {
|
||||
|
||||
canEnableTls() {
|
||||
if (![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false;
|
||||
return ["tcp", "ws", "http", "grpc", "httpupgrade", "splithttp"].includes(this.network);
|
||||
return ["tcp", "ws", "http", "grpc", "httpupgrade", "xhttp"].includes(this.network);
|
||||
}
|
||||
|
||||
//this is used for xtls-rprx-vision
|
||||
@@ -1270,7 +1218,7 @@ class Inbound extends XrayCommonClass {
|
||||
|
||||
canEnableReality() {
|
||||
if (![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
||||
return ["tcp", "http", "grpc", "splithttp"].includes(this.network);
|
||||
return ["tcp", "http", "grpc", "xhttp"].includes(this.network);
|
||||
}
|
||||
|
||||
canEnableStream() {
|
||||
@@ -1322,10 +1270,6 @@ class Inbound extends XrayCommonClass {
|
||||
const ws = this.stream.ws;
|
||||
obj.path = ws.path;
|
||||
obj.host = ws.host?.length > 0 ? ws.host : this.getHeader(ws, 'host');
|
||||
} else if (network === 'http') {
|
||||
obj.net = 'h2';
|
||||
obj.path = this.stream.http.path;
|
||||
obj.host = this.stream.http.host.join(',');
|
||||
} else if (network === 'grpc') {
|
||||
obj.path = this.stream.grpc.serviceName;
|
||||
obj.authority = this.stream.grpc.authority;
|
||||
@@ -1336,14 +1280,14 @@ class Inbound extends XrayCommonClass {
|
||||
const httpupgrade = this.stream.httpupgrade;
|
||||
obj.path = httpupgrade.path;
|
||||
obj.host = httpupgrade.host?.length > 0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host');
|
||||
} else if (network === 'splithttp') {
|
||||
const splithttp = this.stream.splithttp;
|
||||
obj.path = splithttp.path;
|
||||
obj.host = splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host');
|
||||
obj.mode = splithttp.mode;
|
||||
} else if (network === 'xhttp') {
|
||||
const xhttp = this.stream.xhttp;
|
||||
obj.path = xhttp.path;
|
||||
obj.host = xhttp.host?.length > 0 ? xhttp.host : this.getHeader(xhttp, 'host');
|
||||
obj.mode = xhttp.mode;
|
||||
}
|
||||
|
||||
if (security === 'tls') {
|
||||
if (tls === 'tls') {
|
||||
if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
|
||||
obj.sni = this.stream.tls.sni;
|
||||
}
|
||||
@@ -1391,11 +1335,6 @@ class Inbound extends XrayCommonClass {
|
||||
params.set("path", ws.path);
|
||||
params.set("host", ws.host?.length > 0 ? ws.host : this.getHeader(ws, 'host'));
|
||||
break;
|
||||
case "http":
|
||||
const http = this.stream.http;
|
||||
params.set("path", http.path);
|
||||
params.set("host", http.host);
|
||||
break;
|
||||
case "grpc":
|
||||
const grpc = this.stream.grpc;
|
||||
params.set("serviceName", grpc.serviceName);
|
||||
@@ -1409,11 +1348,11 @@ class Inbound extends XrayCommonClass {
|
||||
params.set("path", httpupgrade.path);
|
||||
params.set("host", httpupgrade.host?.length > 0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
||||
break;
|
||||
case "splithttp":
|
||||
const splithttp = this.stream.splithttp;
|
||||
params.set("path", splithttp.path);
|
||||
params.set("host", splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
||||
params.set("mode", splithttp.mode);
|
||||
case "xhttp":
|
||||
const xhttp = this.stream.xhttp;
|
||||
params.set("path", xhttp.path);
|
||||
params.set("host", xhttp.host?.length > 0 ? xhttp.host : this.getHeader(xhttp, 'host'));
|
||||
params.set("mode", xhttp.mode);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1495,11 +1434,6 @@ class Inbound extends XrayCommonClass {
|
||||
params.set("path", ws.path);
|
||||
params.set("host", ws.host?.length > 0 ? ws.host : this.getHeader(ws, 'host'));
|
||||
break;
|
||||
case "http":
|
||||
const http = this.stream.http;
|
||||
params.set("path", http.path);
|
||||
params.set("host", http.host);
|
||||
break;
|
||||
case "grpc":
|
||||
const grpc = this.stream.grpc;
|
||||
params.set("serviceName", grpc.serviceName);
|
||||
@@ -1513,11 +1447,11 @@ class Inbound extends XrayCommonClass {
|
||||
params.set("path", httpupgrade.path);
|
||||
params.set("host", httpupgrade.host?.length > 0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
||||
break;
|
||||
case "splithttp":
|
||||
const splithttp = this.stream.splithttp;
|
||||
params.set("path", splithttp.path);
|
||||
params.set("host", splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
||||
params.set("mode", splithttp.mode);
|
||||
case "xhttp":
|
||||
const xhttp = this.stream.xhttp;
|
||||
params.set("path", xhttp.path);
|
||||
params.set("host", xhttp.host?.length > 0 ? xhttp.host : this.getHeader(xhttp, 'host'));
|
||||
params.set("mode", xhttp.mode);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1578,11 +1512,6 @@ class Inbound extends XrayCommonClass {
|
||||
params.set("path", ws.path);
|
||||
params.set("host", ws.host?.length > 0 ? ws.host : this.getHeader(ws, 'host'));
|
||||
break;
|
||||
case "http":
|
||||
const http = this.stream.http;
|
||||
params.set("path", http.path);
|
||||
params.set("host", http.host);
|
||||
break;
|
||||
case "grpc":
|
||||
const grpc = this.stream.grpc;
|
||||
params.set("serviceName", grpc.serviceName);
|
||||
@@ -1596,11 +1525,11 @@ class Inbound extends XrayCommonClass {
|
||||
params.set("path", httpupgrade.path);
|
||||
params.set("host", httpupgrade.host?.length > 0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
||||
break;
|
||||
case "splithttp":
|
||||
const splithttp = this.stream.splithttp;
|
||||
params.set("path", splithttp.path);
|
||||
params.set("host", splithttp.host?.length > 0 ? splithttp.host : this.getHeader(splithttp, 'host'));
|
||||
params.set("mode", splithttp.mode);
|
||||
case "xhttp":
|
||||
const xhttp = this.stream.xhttp;
|
||||
params.set("path", xhttp.path);
|
||||
params.set("host", xhttp.host?.length > 0 ? xhttp.host : this.getHeader(xhttp, 'host'));
|
||||
params.set("mode", xhttp.mode);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1858,6 +1787,7 @@ Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
|
||||
enable = true,
|
||||
tgId = '',
|
||||
subId = RandomUtil.randomLowerAndNum(16),
|
||||
comment = '',
|
||||
reset = 0
|
||||
) {
|
||||
super();
|
||||
@@ -1870,6 +1800,7 @@ Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
|
||||
this.enable = enable;
|
||||
this.tgId = tgId;
|
||||
this.subId = subId;
|
||||
this.comment = comment;
|
||||
this.reset = reset;
|
||||
}
|
||||
|
||||
@@ -1884,6 +1815,7 @@ Inbound.VmessSettings.VMESS = class extends XrayCommonClass {
|
||||
json.enable,
|
||||
json.tgId,
|
||||
json.subId,
|
||||
json.comment,
|
||||
json.reset,
|
||||
);
|
||||
}
|
||||
@@ -1964,6 +1896,7 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
|
||||
enable = true,
|
||||
tgId = '',
|
||||
subId = RandomUtil.randomLowerAndNum(16),
|
||||
comment = '',
|
||||
reset = 0
|
||||
) {
|
||||
super();
|
||||
@@ -1976,6 +1909,7 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
|
||||
this.enable = enable;
|
||||
this.tgId = tgId;
|
||||
this.subId = subId;
|
||||
this.comment = comment;
|
||||
this.reset = reset;
|
||||
}
|
||||
|
||||
@@ -1990,6 +1924,7 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {
|
||||
json.enable,
|
||||
json.tgId,
|
||||
json.subId,
|
||||
json.comment,
|
||||
json.reset,
|
||||
);
|
||||
}
|
||||
@@ -2100,6 +2035,7 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
||||
enable = true,
|
||||
tgId = '',
|
||||
subId = RandomUtil.randomLowerAndNum(16),
|
||||
comment = '',
|
||||
reset = 0
|
||||
) {
|
||||
super();
|
||||
@@ -2111,6 +2047,7 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
||||
this.enable = enable;
|
||||
this.tgId = tgId;
|
||||
this.subId = subId;
|
||||
this.comment = comment;
|
||||
this.reset = reset;
|
||||
}
|
||||
|
||||
@@ -2124,6 +2061,7 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
||||
enable: this.enable,
|
||||
tgId: this.tgId,
|
||||
subId: this.subId,
|
||||
comment: this.comment,
|
||||
reset: this.reset,
|
||||
};
|
||||
}
|
||||
@@ -2138,6 +2076,7 @@ Inbound.TrojanSettings.Trojan = class extends XrayCommonClass {
|
||||
json.enable,
|
||||
json.tgId,
|
||||
json.subId,
|
||||
json.comment,
|
||||
json.reset,
|
||||
);
|
||||
}
|
||||
@@ -2257,6 +2196,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
||||
enable = true,
|
||||
tgId = '',
|
||||
subId = RandomUtil.randomLowerAndNum(16),
|
||||
comment = '',
|
||||
reset = 0
|
||||
) {
|
||||
super();
|
||||
@@ -2269,6 +2209,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
||||
this.enable = enable;
|
||||
this.tgId = tgId;
|
||||
this.subId = subId;
|
||||
this.comment = comment;
|
||||
this.reset = reset;
|
||||
}
|
||||
|
||||
@@ -2283,6 +2224,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
||||
enable: this.enable,
|
||||
tgId: this.tgId,
|
||||
subId: this.subId,
|
||||
comment: this.comment,
|
||||
reset: this.reset,
|
||||
};
|
||||
}
|
||||
@@ -2298,6 +2240,7 @@ Inbound.ShadowsocksSettings.Shadowsocks = class extends XrayCommonClass {
|
||||
json.enable,
|
||||
json.tgId,
|
||||
json.subId,
|
||||
json.comment,
|
||||
json.reset,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ const UTLS_FINGERPRINT = {
|
||||
UTLS_QQ: "qq",
|
||||
UTLS_RANDOM: "random",
|
||||
UTLS_RANDOMIZED: "randomized",
|
||||
UTLS_RONDOMIZEDNOALPN: "randomizednoalpn",
|
||||
UTLS_UNSAFE: "unsafe",
|
||||
};
|
||||
|
||||
const ALPN_OPTION = {
|
||||
@@ -81,6 +83,17 @@ const MODE_OPTION = {
|
||||
AUTO: "auto",
|
||||
PACKET_UP: "packet-up",
|
||||
STREAM_UP: "stream-up",
|
||||
STREAM_ONE: "stream-one",
|
||||
};
|
||||
|
||||
const Address_Port_Strategy = {
|
||||
NONE: "none",
|
||||
SrvPortOnly: "srvportonly",
|
||||
SrvAddressOnly: "srvaddressonly",
|
||||
SrvPortAndAddress: "srvportandaddress",
|
||||
TxtPortOnly: "txtportonly",
|
||||
TxtAddressOnly: "txtaddressonly",
|
||||
TxtPortAndAddress: "txtportandaddress"
|
||||
};
|
||||
|
||||
Object.freeze(Protocols);
|
||||
@@ -92,7 +105,7 @@ Object.freeze(OutboundDomainStrategies);
|
||||
Object.freeze(WireguardDomainStrategy);
|
||||
Object.freeze(USERS_SECURITY);
|
||||
Object.freeze(MODE_OPTION);
|
||||
|
||||
Object.freeze(Address_Port_Strategy);
|
||||
|
||||
class CommonClass {
|
||||
|
||||
@@ -205,16 +218,23 @@ class KcpStreamSettings extends CommonClass {
|
||||
}
|
||||
|
||||
class WsStreamSettings extends CommonClass {
|
||||
constructor(path = '/', host = '') {
|
||||
constructor(
|
||||
path = '/',
|
||||
host = '',
|
||||
heartbeatPeriod = 0,
|
||||
|
||||
) {
|
||||
super();
|
||||
this.path = path;
|
||||
this.host = host;
|
||||
this.heartbeatPeriod = heartbeatPeriod;
|
||||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
return new WsStreamSettings(
|
||||
json.path,
|
||||
json.host,
|
||||
json.heartbeatPeriod,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -222,63 +242,11 @@ class WsStreamSettings extends CommonClass {
|
||||
return {
|
||||
path: this.path,
|
||||
host: this.host,
|
||||
heartbeatPeriod: this.heartbeatPeriod
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class HttpStreamSettings extends CommonClass {
|
||||
constructor(path = '/', host = '') {
|
||||
super();
|
||||
this.path = path;
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
return new HttpStreamSettings(
|
||||
json.path,
|
||||
json.host ? json.host.join(',') : '',
|
||||
);
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
path: this.path,
|
||||
host: ObjectUtil.isEmpty(this.host) ? [''] : this.host.split(','),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class QuicStreamSettings extends CommonClass {
|
||||
constructor(
|
||||
security = 'none',
|
||||
key = '',
|
||||
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 CommonClass {
|
||||
constructor(
|
||||
serviceName = "",
|
||||
@@ -326,23 +294,39 @@ class HttpUpgradeStreamSettings extends CommonClass {
|
||||
}
|
||||
}
|
||||
|
||||
class SplitHTTPStreamSettings extends CommonClass {
|
||||
class xHTTPStreamSettings extends CommonClass {
|
||||
constructor(
|
||||
path = '/',
|
||||
host = '',
|
||||
mode = '',
|
||||
noGRPCHeader = false,
|
||||
scMinPostsIntervalMs = "30",
|
||||
xmux = {
|
||||
maxConcurrency: "16-32",
|
||||
maxConnections: 0,
|
||||
cMaxReuseTimes: 0,
|
||||
hMaxRequestTimes: "600-900",
|
||||
hMaxReusableSecs: "1800-3000",
|
||||
hKeepAlivePeriod: 0,
|
||||
},
|
||||
) {
|
||||
super();
|
||||
this.path = path;
|
||||
this.host = host;
|
||||
this.mode = mode;
|
||||
this.noGRPCHeader = noGRPCHeader;
|
||||
this.scMinPostsIntervalMs = scMinPostsIntervalMs;
|
||||
this.xmux = xmux;
|
||||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
return new SplitHTTPStreamSettings(
|
||||
return new xHTTPStreamSettings(
|
||||
json.path,
|
||||
json.host,
|
||||
json.mode,
|
||||
json.noGRPCHeader,
|
||||
json.scMinPostsIntervalMs,
|
||||
json.xmux
|
||||
);
|
||||
}
|
||||
|
||||
@@ -351,6 +335,16 @@ class SplitHTTPStreamSettings extends CommonClass {
|
||||
path: this.path,
|
||||
host: this.host,
|
||||
mode: this.mode,
|
||||
noGRPCHeader: this.noGRPCHeader,
|
||||
scMinPostsIntervalMs: this.scMinPostsIntervalMs,
|
||||
xmux: {
|
||||
maxConcurrency: this.xmux.maxConcurrency,
|
||||
maxConnections: this.xmux.maxConnections,
|
||||
cMaxReuseTimes: this.xmux.cMaxReuseTimes,
|
||||
hMaxRequestTimes: this.xmux.hMaxRequestTimes,
|
||||
hMaxReusableSecs: this.xmux.hMaxReusableSecs,
|
||||
hKeepAlivePeriod: this.xmux.hKeepAlivePeriod,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -428,14 +422,16 @@ class SockoptStreamSettings extends CommonClass {
|
||||
tcpFastOpen = false,
|
||||
tcpKeepAliveInterval = 0,
|
||||
tcpMptcp = false,
|
||||
tcpNoDelay = false
|
||||
penetrate = false,
|
||||
addressPortStrategy = Address_Port_Strategy.NONE,
|
||||
) {
|
||||
super();
|
||||
this.dialerProxy = dialerProxy;
|
||||
this.tcpFastOpen = tcpFastOpen;
|
||||
this.tcpKeepAliveInterval = tcpKeepAliveInterval;
|
||||
this.tcpMptcp = tcpMptcp;
|
||||
this.tcpNoDelay = tcpNoDelay;
|
||||
this.penetrate = penetrate;
|
||||
this.addressPortStrategy = addressPortStrategy;
|
||||
}
|
||||
|
||||
static fromJson(json = {}) {
|
||||
@@ -445,7 +441,8 @@ class SockoptStreamSettings extends CommonClass {
|
||||
json.tcpFastOpen,
|
||||
json.tcpKeepAliveInterval,
|
||||
json.tcpMptcp,
|
||||
json.tcpNoDelay,
|
||||
json.penetrate,
|
||||
json.addressPortStrategy
|
||||
);
|
||||
}
|
||||
|
||||
@@ -455,7 +452,8 @@ class SockoptStreamSettings extends CommonClass {
|
||||
tcpFastOpen: this.tcpFastOpen,
|
||||
tcpKeepAliveInterval: this.tcpKeepAliveInterval,
|
||||
tcpMptcp: this.tcpMptcp,
|
||||
tcpNoDelay: this.tcpNoDelay,
|
||||
penetrate: this.penetrate,
|
||||
addressPortStrategy: this.addressPortStrategy
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -469,11 +467,9 @@ class StreamSettings extends CommonClass {
|
||||
tcpSettings = new TcpStreamSettings(),
|
||||
kcpSettings = new KcpStreamSettings(),
|
||||
wsSettings = new WsStreamSettings(),
|
||||
httpSettings = new HttpStreamSettings(),
|
||||
quicSettings = new QuicStreamSettings(),
|
||||
grpcSettings = new GrpcStreamSettings(),
|
||||
httpupgradeSettings = new HttpUpgradeStreamSettings(),
|
||||
splithttpSettings = new SplitHTTPStreamSettings(),
|
||||
xhttpSettings = new xHTTPStreamSettings(),
|
||||
sockopt = undefined,
|
||||
) {
|
||||
super();
|
||||
@@ -484,10 +480,9 @@ class StreamSettings extends CommonClass {
|
||||
this.tcp = tcpSettings;
|
||||
this.kcp = kcpSettings;
|
||||
this.ws = wsSettings;
|
||||
this.http = httpSettings;
|
||||
this.grpc = grpcSettings;
|
||||
this.httpupgrade = httpupgradeSettings;
|
||||
this.splithttp = splithttpSettings;
|
||||
this.xhttp = xhttpSettings;
|
||||
this.sockopt = sockopt;
|
||||
}
|
||||
|
||||
@@ -516,11 +511,9 @@ class StreamSettings extends CommonClass {
|
||||
TcpStreamSettings.fromJson(json.tcpSettings),
|
||||
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),
|
||||
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
||||
SockoptStreamSettings.fromJson(json.sockopt),
|
||||
);
|
||||
}
|
||||
@@ -535,10 +528,9 @@ class StreamSettings extends CommonClass {
|
||||
tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
|
||||
kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
|
||||
wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
|
||||
httpSettings: network === 'http' ? this.http.toJson() : undefined,
|
||||
grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
|
||||
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
||||
splithttpSettings: network === 'splithttp' ? this.splithttp.toJson() : undefined,
|
||||
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
||||
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
||||
};
|
||||
}
|
||||
@@ -576,7 +568,7 @@ class Mux extends CommonClass {
|
||||
class Outbound extends CommonClass {
|
||||
constructor(
|
||||
tag = '',
|
||||
protocol = Protocols.VMess,
|
||||
protocol = Protocols.VLESS,
|
||||
settings = null,
|
||||
streamSettings = new StreamSettings(),
|
||||
sendThrough,
|
||||
@@ -603,7 +595,7 @@ class Outbound extends CommonClass {
|
||||
|
||||
canEnableTls() {
|
||||
if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false;
|
||||
return ["tcp", "ws", "http", "grpc", "httpupgrade", "splithttp"].includes(this.stream.network);
|
||||
return ["tcp", "ws", "http", "grpc", "httpupgrade", "xhttp"].includes(this.stream.network);
|
||||
}
|
||||
|
||||
//this is used for xtls-rprx-vision
|
||||
@@ -616,7 +608,7 @@ class Outbound extends CommonClass {
|
||||
|
||||
canEnableReality() {
|
||||
if (![Protocols.VLESS, Protocols.Trojan].includes(this.protocol)) return false;
|
||||
return ["tcp", "http", "grpc", "splithttp"].includes(this.stream.network);
|
||||
return ["tcp", "http", "grpc", "xhttp"].includes(this.stream.network);
|
||||
}
|
||||
|
||||
canEnableStream() {
|
||||
@@ -714,17 +706,12 @@ class Outbound extends CommonClass {
|
||||
stream.seed = json.path;
|
||||
} else if (network === 'ws') {
|
||||
stream.ws = new WsStreamSettings(json.path, json.host);
|
||||
} else if (network === 'http' || network == 'h2') {
|
||||
stream.network = 'http'
|
||||
stream.http = new HttpStreamSettings(
|
||||
json.path,
|
||||
json.host);
|
||||
} else if (network === 'grpc') {
|
||||
stream.grpc = new GrpcStreamSettings(json.path, json.authority, json.type == 'multi');
|
||||
} else if (network === 'httpupgrade') {
|
||||
stream.httpupgrade = new HttpUpgradeStreamSettings(json.path, json.host);
|
||||
} else if (network === 'splithttp') {
|
||||
stream.splithttp = new SplitHTTPStreamSettings(json.path, json.host, json.mode);
|
||||
} else if (network === 'xhttp') {
|
||||
stream.xhttp = new xHTTPStreamSettings(json.path, json.host, json.mode);
|
||||
}
|
||||
|
||||
if (json.tls && json.tls == 'tls') {
|
||||
@@ -749,6 +736,7 @@ class Outbound extends CommonClass {
|
||||
let headerType = url.searchParams.get('headerType') ?? undefined;
|
||||
let host = url.searchParams.get('host') ?? undefined;
|
||||
let path = url.searchParams.get('path') ?? undefined;
|
||||
let mode = url.searchParams.get('mode') ?? undefined;
|
||||
|
||||
if (type === 'tcp' || type === 'none') {
|
||||
stream.tcp = new TcpStreamSettings(headerType ?? 'none', host, path);
|
||||
@@ -758,8 +746,6 @@ class Outbound extends CommonClass {
|
||||
stream.kcp.seed = path;
|
||||
} else if (type === 'ws') {
|
||||
stream.ws = new WsStreamSettings(path, host);
|
||||
} else if (type === 'http' || type == 'h2') {
|
||||
stream.http = new HttpStreamSettings(path, host);
|
||||
} else if (type === 'grpc') {
|
||||
stream.grpc = new GrpcStreamSettings(
|
||||
url.searchParams.get('serviceName') ?? '',
|
||||
@@ -767,8 +753,8 @@ class Outbound extends CommonClass {
|
||||
url.searchParams.get('mode') == 'multi');
|
||||
} else if (type === 'httpupgrade') {
|
||||
stream.httpupgrade = new HttpUpgradeStreamSettings(path, host);
|
||||
} else if (type === 'splithttp') {
|
||||
stream.splithttp = new SplitHTTPStreamSettings(path, host, mode);
|
||||
} else if (type === 'xhttp') {
|
||||
stream.xhttp = new xHTTPStreamSettings(path, host, mode);
|
||||
}
|
||||
|
||||
if (security == 'tls') {
|
||||
|
||||
@@ -31,6 +31,8 @@ class AllSetting {
|
||||
this.subPath = "/sub/";
|
||||
this.subJsonPath = "/json/";
|
||||
this.subDomain = "";
|
||||
this.externalTrafficInformEnable = false;
|
||||
this.externalTrafficInformURI = "";
|
||||
this.subCertFile = "";
|
||||
this.subKeyFile = "";
|
||||
this.subUpdates = 12;
|
||||
@@ -43,7 +45,7 @@ class AllSetting {
|
||||
this.subJsonMux = "";
|
||||
this.subJsonRules = "";
|
||||
|
||||
this.timeLocation = "Asia/Tehran";
|
||||
this.timeLocation = "Local";
|
||||
|
||||
if (data == null) {
|
||||
return
|
||||
|
||||
3
web/assets/moment/moment.min.js
vendored
@@ -9,6 +9,7 @@ import (
|
||||
"x-ui/web/service"
|
||||
"x-ui/web/session"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -49,8 +50,8 @@ func (a *IndexController) index(c *gin.Context) {
|
||||
|
||||
func (a *IndexController) login(c *gin.Context) {
|
||||
var form LoginForm
|
||||
err := c.ShouldBind(&form)
|
||||
if err != nil {
|
||||
|
||||
if err := c.ShouldBind(&form); err != nil {
|
||||
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.invalidFormData"))
|
||||
return
|
||||
}
|
||||
@@ -68,29 +69,31 @@ func (a *IndexController) login(c *gin.Context) {
|
||||
safeUser := template.HTMLEscapeString(form.Username)
|
||||
safePass := template.HTMLEscapeString(form.Password)
|
||||
safeSecret := template.HTMLEscapeString(form.LoginSecret)
|
||||
|
||||
if user == nil {
|
||||
logger.Warningf("wrong username or password or secret: \"%s\" \"%s\" \"%s\"", safeUser, safePass, safeSecret)
|
||||
logger.Warningf("wrong username: \"%s\", password: \"%s\", secret: \"%s\", IP: \"%s\"", safeUser, safePass, safeSecret, getRemoteIp(c))
|
||||
a.tgbot.UserLoginNotify(safeUser, safePass, getRemoteIp(c), timeStr, 0)
|
||||
pureJsonMsg(c, http.StatusOK, false, I18nWeb(c, "pages.login.toasts.wrongUsernameOrPassword"))
|
||||
return
|
||||
} else {
|
||||
logger.Infof("%s logged in successfully, Ip Address: %s\n", safeUser, getRemoteIp(c))
|
||||
a.tgbot.UserLoginNotify(safeUser, ``, getRemoteIp(c), timeStr, 1)
|
||||
}
|
||||
|
||||
logger.Infof("%s logged in successfully, Ip Address: %s\n", safeUser, getRemoteIp(c))
|
||||
a.tgbot.UserLoginNotify(safeUser, ``, getRemoteIp(c), timeStr, 1)
|
||||
|
||||
sessionMaxAge, err := a.settingService.GetSessionMaxAge()
|
||||
if err != nil {
|
||||
logger.Warning("Unable to get session's max age from DB")
|
||||
}
|
||||
|
||||
err = session.SetMaxAge(c, sessionMaxAge*60)
|
||||
if err != nil {
|
||||
logger.Warning("Unable to set session's max age")
|
||||
session.SetMaxAge(c, sessionMaxAge*60)
|
||||
session.SetLoginUser(c, user)
|
||||
if err := sessions.Default(c).Save(); err != nil {
|
||||
logger.Warning("Unable to save session: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = session.SetLoginUser(c, user)
|
||||
logger.Infof("%s logged in successfully", user.Username)
|
||||
jsonMsg(c, I18nWeb(c, "pages.login.toasts.successLogin"), err)
|
||||
logger.Infof("%s logged in successfully", safeUser)
|
||||
jsonMsg(c, I18nWeb(c, "pages.login.toasts.successLogin"), nil)
|
||||
}
|
||||
|
||||
func (a *IndexController) logout(c *gin.Context) {
|
||||
@@ -99,6 +102,9 @@ func (a *IndexController) logout(c *gin.Context) {
|
||||
logger.Infof("%s logged out successfully", user.Username)
|
||||
}
|
||||
session.ClearSession(c)
|
||||
if err := sessions.Default(c).Save(); err != nil {
|
||||
logger.Warning("Unable to save session after clearing:", err)
|
||||
}
|
||||
c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path"))
|
||||
}
|
||||
|
||||
|
||||
@@ -16,47 +16,49 @@ type Msg struct {
|
||||
}
|
||||
|
||||
type AllSetting struct {
|
||||
WebListen string `json:"webListen" form:"webListen"`
|
||||
WebDomain string `json:"webDomain" form:"webDomain"`
|
||||
WebPort int `json:"webPort" form:"webPort"`
|
||||
WebCertFile string `json:"webCertFile" form:"webCertFile"`
|
||||
WebKeyFile string `json:"webKeyFile" form:"webKeyFile"`
|
||||
WebBasePath string `json:"webBasePath" form:"webBasePath"`
|
||||
SessionMaxAge int `json:"sessionMaxAge" form:"sessionMaxAge"`
|
||||
PageSize int `json:"pageSize" form:"pageSize"`
|
||||
ExpireDiff int `json:"expireDiff" form:"expireDiff"`
|
||||
TrafficDiff int `json:"trafficDiff" form:"trafficDiff"`
|
||||
RemarkModel string `json:"remarkModel" form:"remarkModel"`
|
||||
TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"`
|
||||
TgBotToken string `json:"tgBotToken" form:"tgBotToken"`
|
||||
TgBotProxy string `json:"tgBotProxy" form:"tgBotProxy"`
|
||||
TgBotAPIServer string `json:"tgBotAPIServer" form:"tgBotAPIServer"`
|
||||
TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"`
|
||||
TgRunTime string `json:"tgRunTime" form:"tgRunTime"`
|
||||
TgBotBackup bool `json:"tgBotBackup" form:"tgBotBackup"`
|
||||
TgBotLoginNotify bool `json:"tgBotLoginNotify" form:"tgBotLoginNotify"`
|
||||
TgCpu int `json:"tgCpu" form:"tgCpu"`
|
||||
TgLang string `json:"tgLang" form:"tgLang"`
|
||||
TimeLocation string `json:"timeLocation" form:"timeLocation"`
|
||||
SecretEnable bool `json:"secretEnable" form:"secretEnable"`
|
||||
SubEnable bool `json:"subEnable" form:"subEnable"`
|
||||
SubListen string `json:"subListen" form:"subListen"`
|
||||
SubPort int `json:"subPort" form:"subPort"`
|
||||
SubPath string `json:"subPath" form:"subPath"`
|
||||
SubDomain string `json:"subDomain" form:"subDomain"`
|
||||
SubCertFile string `json:"subCertFile" form:"subCertFile"`
|
||||
SubKeyFile string `json:"subKeyFile" form:"subKeyFile"`
|
||||
SubUpdates int `json:"subUpdates" form:"subUpdates"`
|
||||
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"`
|
||||
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
|
||||
SubURI string `json:"subURI" form:"subURI"`
|
||||
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"`
|
||||
WebListen string `json:"webListen" form:"webListen"`
|
||||
WebDomain string `json:"webDomain" form:"webDomain"`
|
||||
WebPort int `json:"webPort" form:"webPort"`
|
||||
WebCertFile string `json:"webCertFile" form:"webCertFile"`
|
||||
WebKeyFile string `json:"webKeyFile" form:"webKeyFile"`
|
||||
WebBasePath string `json:"webBasePath" form:"webBasePath"`
|
||||
SessionMaxAge int `json:"sessionMaxAge" form:"sessionMaxAge"`
|
||||
PageSize int `json:"pageSize" form:"pageSize"`
|
||||
ExpireDiff int `json:"expireDiff" form:"expireDiff"`
|
||||
TrafficDiff int `json:"trafficDiff" form:"trafficDiff"`
|
||||
RemarkModel string `json:"remarkModel" form:"remarkModel"`
|
||||
TgBotEnable bool `json:"tgBotEnable" form:"tgBotEnable"`
|
||||
TgBotToken string `json:"tgBotToken" form:"tgBotToken"`
|
||||
TgBotProxy string `json:"tgBotProxy" form:"tgBotProxy"`
|
||||
TgBotAPIServer string `json:"tgBotAPIServer" form:"tgBotAPIServer"`
|
||||
TgBotChatId string `json:"tgBotChatId" form:"tgBotChatId"`
|
||||
TgRunTime string `json:"tgRunTime" form:"tgRunTime"`
|
||||
TgBotBackup bool `json:"tgBotBackup" form:"tgBotBackup"`
|
||||
TgBotLoginNotify bool `json:"tgBotLoginNotify" form:"tgBotLoginNotify"`
|
||||
TgCpu int `json:"tgCpu" form:"tgCpu"`
|
||||
TgLang string `json:"tgLang" form:"tgLang"`
|
||||
TimeLocation string `json:"timeLocation" form:"timeLocation"`
|
||||
SecretEnable bool `json:"secretEnable" form:"secretEnable"`
|
||||
SubEnable bool `json:"subEnable" form:"subEnable"`
|
||||
SubListen string `json:"subListen" form:"subListen"`
|
||||
SubPort int `json:"subPort" form:"subPort"`
|
||||
SubPath string `json:"subPath" form:"subPath"`
|
||||
SubDomain string `json:"subDomain" form:"subDomain"`
|
||||
SubCertFile string `json:"subCertFile" form:"subCertFile"`
|
||||
SubKeyFile string `json:"subKeyFile" form:"subKeyFile"`
|
||||
SubUpdates int `json:"subUpdates" form:"subUpdates"`
|
||||
ExternalTrafficInformEnable bool `json:"externalTrafficInformEnable" form:"externalTrafficInformEnable"`
|
||||
ExternalTrafficInformURI string `json:"externalTrafficInformURI" form:"externalTrafficInformURI"`
|
||||
SubEncrypt bool `json:"subEncrypt" form:"subEncrypt"`
|
||||
SubShowInfo bool `json:"subShowInfo" form:"subShowInfo"`
|
||||
SubURI string `json:"subURI" form:"subURI"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func (s *AllSetting) CheckValid() error {
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.first" }}' v-if="clientsBulkModal.emailMethod>1">
|
||||
<a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
|
||||
<a-input-number v-model.number="clientsBulkModal.firstNum" :min="1"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.last" }}' v-if="clientsBulkModal.emailMethod>1">
|
||||
<a-input-number v-model="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
|
||||
<a-input-number v-model.number="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.prefix" }}' v-if="clientsBulkModal.emailMethod>0">
|
||||
<a-input v-model.trim="clientsBulkModal.emailPrefix"></a-input>
|
||||
@@ -26,7 +26,7 @@
|
||||
<a-input v-model.trim="clientsBulkModal.emailPostfix"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.clientCount" }}' v-if="clientsBulkModal.emailMethod < 2">
|
||||
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
|
||||
<a-input-number v-model.number="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">
|
||||
@@ -61,7 +61,7 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number style="width: 50%" v-model="clientsBulkModal.tgId" min="0"></a-input-number>
|
||||
<a-input-number style="width: 50%" v-model.number="clientsBulkModal.tgId" min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="app.ipLimitEnable">
|
||||
<template slot="label">
|
||||
@@ -73,7 +73,7 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model="clientsBulkModal.limitIp" min="0"></a-input-number>
|
||||
<a-input-number v-model.number="clientsBulkModal.limitIp" min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
@@ -85,7 +85,7 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="clientsBulkModal.totalGB" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
|
||||
<a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<a-input v-model.trim="fakednsModal.fakeDns.ipPool"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.fakedns.poolSize" }}'>
|
||||
<a-input-number style="width: 100%;" type="number" min="1" v-model.trim="fakednsModal.fakeDns.poolSize"></a-input-number>
|
||||
<a-input-number v-model.number="fakednsModal.fakeDns.poolSize" :min="1"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
||||
@@ -66,7 +66,10 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number style="width: 50%" v-model="client.tgId" min="0"></a-input-number>
|
||||
<a-input-number style="width: 50%" v-model.number="client.tgId" min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="client.email" label='{{ i18n "comment" }}'>
|
||||
<a-input v-model.trim="client.comment"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="app.ipLimitEnable">
|
||||
<template slot="label">
|
||||
@@ -78,7 +81,7 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model="client.limitIp" min="0"></a-input-number>
|
||||
<a-input-number v-model.number="client.limitIp" min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="app.ipLimitEnable && client.limitIp > 0 && client.email && isEdit">
|
||||
<template slot="label">
|
||||
@@ -120,7 +123,7 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="client._totalGB" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="isEdit && clientStats" label='{{ i18n "usage" }}'>
|
||||
<a-tag :color="clientUsageColor(clientStats, app.trafficDiff)">
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="dbInbound.totalGB" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item>
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
</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-option v-for="s in ['rand','base64','str', 'hex']" :value="s">[[ s ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Packet'>
|
||||
@@ -270,10 +270,9 @@
|
||||
<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">HTTP</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 (XHTTP)</a-select-option>
|
||||
<a-select-option value="xhttp">XHTTP</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<template v-if="outbound.stream.network === 'tcp'">
|
||||
@@ -337,15 +336,8 @@
|
||||
<a-form-item label='{{ i18n "path" }}'>
|
||||
<a-input v-model.trim="outbound.stream.ws.path"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<!-- http -->
|
||||
<template v-if="outbound.stream.network === 'http'">
|
||||
<a-form-item label='{{ i18n "host" }}'>
|
||||
<a-input v-model.trim="outbound.stream.http.host"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "path" }}'>
|
||||
<a-input v-model.trim="outbound.stream.http.path"></a-input>
|
||||
<a-form-item label='Heartbeat Period'>
|
||||
<a-input-number v-model.number="outbound.stream.ws.heartbeatPeriod" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
@@ -372,19 +364,43 @@
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
<!-- splithttp -->
|
||||
<template v-if="outbound.stream.network === 'splithttp'">
|
||||
<!-- xhttp -->
|
||||
<template v-if="outbound.stream.network === 'xhttp'">
|
||||
<a-form-item label='{{ i18n "host" }}'>
|
||||
<a-input v-model="outbound.stream.splithttp.host"></a-input>
|
||||
<a-input v-model="outbound.stream.xhttp.host"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "path" }}'>
|
||||
<a-input v-model.trim="outbound.stream.splithttp.path"></a-input>
|
||||
<a-input v-model.trim="outbound.stream.xhttp.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Mode'>
|
||||
<a-select v-model="outbound.stream.splithttp.mode" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select v-model="outbound.stream.xhttp.mode" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="No gRPC Header" v-if="outbound.stream.xhttp.mode === 'stream-up' || outbound.stream.xhttp.mode === 'stream-one'">
|
||||
<a-switch v-model="outbound.stream.xhttp.noGRPCHeader"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="Min Upload Interval (Ms)" v-if="outbound.stream.xhttp.mode === 'packet-up'">
|
||||
<a-input v-model.trim="outbound.stream.xhttp.scMinPostsIntervalMs"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Concurrency" v-if="!outbound.stream.xhttp.xmux.maxConnections">
|
||||
<a-input v-model="outbound.stream.xhttp.xmux.maxConcurrency"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Connections" v-if="!outbound.stream.xhttp.xmux.maxConcurrency">
|
||||
<a-input v-model="outbound.stream.xhttp.xmux.maxConnections"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Reuse Times">
|
||||
<a-input v-model="outbound.stream.xhttp.xmux.cMaxReuseTimes"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Request Times">
|
||||
<a-input v-model="outbound.stream.xhttp.xmux.hMaxRequestTimes"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Reusable Secs">
|
||||
<a-input v-model="outbound.stream.xhttp.xmux.hMaxReusableSecs"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Keep Alive Period'>
|
||||
<a-input-number v-model.number="outbound.stream.xhttp.xmux.hKeepAlivePeriod"></a-input-number>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -449,17 +465,22 @@
|
||||
<a-select-option v-for="tag in ['', ...outModal.tags]" :value="tag">[[ tag ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Fast Open">
|
||||
<a-switch v-model="outbound.stream.sockopt.tcpFastOpen"></a-switch>
|
||||
<a-form-item label='Address Port Strategy'>
|
||||
<a-select v-model="outbound.stream.sockopt.addressPortStrategy" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in Address_Port_Strategy" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Keep Alive Interval">
|
||||
<a-input-number v-model="outbound.stream.sockopt.tcpKeepAliveInterval" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="outbound.stream.sockopt.tcpKeepAliveInterval" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Fast Open">
|
||||
<a-switch v-model="outbound.stream.sockopt.tcpFastOpen"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="Multipath TCP">
|
||||
<a-switch v-model.trim="outbound.stream.sockopt.tcpMptcp"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP No-Delay" v-if="outbound.stream.sockopt.tcpMptcp">
|
||||
<a-switch v-model="outbound.stream.sockopt.tcpNoDelay"></a-switch>
|
||||
<a-form-item label="Penetrate">
|
||||
<a-switch v-model="outbound.stream.sockopt.penetrate"></a-switch>
|
||||
</a-form-item>
|
||||
</template>
|
||||
|
||||
@@ -470,10 +491,10 @@
|
||||
</a-form-item>
|
||||
<template v-if="outbound.mux.enabled">
|
||||
<a-form-item label="Concurrency">
|
||||
<a-input-number v-model="outbound.mux.concurrency" :min="-1" :max="1024"></a-input-number>
|
||||
<a-input-number v-model.number="outbound.mux.concurrency" :min="-1" :max="1024"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="xudp Concurrency">
|
||||
<a-input-number v-model="outbound.mux.xudpConcurrency" :min="-1" :max="1024"></a-input-number>
|
||||
<a-input-number v-model.number="outbound.mux.xudpConcurrency" :min="-1" :max="1024"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="xudp UDP 443">
|
||||
<a-select v-model="outbound.mux.xudpProxyUDP443" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</table>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<template v-if="inbound.isTcp && !inbound.stream.isReality">
|
||||
<template v-if="inbound.isTcp">
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="Fallbacks">
|
||||
<a-button icon="plus" type="primary" size="small" @click="inbound.settings.addFallback()"></a-button>
|
||||
@@ -42,7 +42,7 @@
|
||||
<a-input v-model="fallback.dest"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='xVer'>
|
||||
<a-input-number v-model="fallback.xver" :min="0" :max="2"></a-input-number>
|
||||
<a-input-number v-model.number="fallback.xver" :min="0" :max="2"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-divider style="margin:5px 0;"></a-divider>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</table>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<template v-if="inbound.isTcp && !inbound.stream.isReality">
|
||||
<template v-if="inbound.isTcp">
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="Fallbacks">
|
||||
<a-button icon="plus" type="primary" size="small" @click="inbound.settings.addFallback()"></a-button>
|
||||
@@ -42,7 +42,7 @@
|
||||
<a-input v-model="fallback.dest"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='xVer'>
|
||||
<a-input-number v-model="fallback.xver" :min="0" :max="2"></a-input-number>
|
||||
<a-input-number v-model.number="fallback.xver" :min="0" :max="2"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-divider style="margin:5px 0;"></a-divider>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<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%"
|
||||
<a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 100%"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
@@ -43,11 +43,11 @@
|
||||
<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-input v-model="inbound.stream.reality.settings.publicKey"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.privatekey" }}'>
|
||||
<a-input type="password" v-model="inbound.stream.reality.privateKey"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{{define "form/streamHTTP"}}
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "path" }}'>
|
||||
<a-input v-model.trim="inbound.stream.http.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">{{ i18n "host" }}
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.http.addHost()"></a-button>
|
||||
</template>
|
||||
<template v-for="(host, index) in inbound.stream.http.host">
|
||||
<a-input v-model.trim="inbound.stream.http.host[index]">
|
||||
<a-button icon="minus" size="small" slot="addonAfter" @click="inbound.stream.http.removeHost(index)" v-if="inbound.stream.http.host.length>1"></a-button>
|
||||
</a-input>
|
||||
</template>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
@@ -10,7 +10,7 @@
|
||||
<a-input v-model.trim="inbound.stream.httpupgrade.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.httpupgrade.addHeader('host', '')"></a-button>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.httpupgrade.addHeader('', '')"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.httpupgrade.headers">
|
||||
|
||||
@@ -7,10 +7,9 @@
|
||||
<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">HTTP</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 (XHTTP)</a-select-option>
|
||||
<a-select-option value="xhttp">XHTTP</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
@@ -30,11 +29,6 @@
|
||||
{{template "form/streamWS"}}
|
||||
</template>
|
||||
|
||||
<!-- http -->
|
||||
<template v-if="inbound.stream.network === 'http'">
|
||||
{{template "form/streamHTTP"}}
|
||||
</template>
|
||||
|
||||
<!-- grpc -->
|
||||
<template v-if="inbound.stream.network === 'grpc'">
|
||||
{{template "form/streamGRPC"}}
|
||||
@@ -45,9 +39,9 @@
|
||||
{{template "form/streamHTTPUpgrade"}}
|
||||
</template>
|
||||
|
||||
<!-- splithttp -->
|
||||
<template v-if="inbound.stream.network === 'splithttp'">
|
||||
{{template "form/streamSplitHTTP"}}
|
||||
<!-- xhttp -->
|
||||
<template v-if="inbound.stream.network === 'xhttp'">
|
||||
{{template "form/streamXHTTP"}}
|
||||
</template>
|
||||
|
||||
<!-- sockopt -->
|
||||
|
||||
@@ -6,22 +6,22 @@
|
||||
</a-form-item>
|
||||
<template v-if="inbound.stream.sockoptSwitch">
|
||||
<a-form-item label="Route Mark">
|
||||
<a-input-number v-model="inbound.stream.sockopt.mark" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.mark" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Keep Alive Interval">
|
||||
<a-input-number v-model="inbound.stream.sockopt.tcpKeepAliveInterval" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpKeepAliveInterval" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Keep Alive Idle">
|
||||
<a-input-number v-model="inbound.stream.sockopt.tcpKeepAliveIdle" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpKeepAliveIdle" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Max Seg">
|
||||
<a-input-number v-model="inbound.stream.sockopt.tcpMaxSeg" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpMaxSeg" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP User Timeout">
|
||||
<a-input-number v-model="inbound.stream.sockopt.tcpUserTimeout" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpUserTimeout" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Window Clamp">
|
||||
<a-input-number v-model="inbound.stream.sockopt.tcpWindowClamp" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpWindowClamp" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="Proxy Protocol">
|
||||
<a-switch v-model="inbound.stream.sockopt.acceptProxyProtocol"></a-switch>
|
||||
@@ -32,8 +32,8 @@
|
||||
<a-form-item label="Multipath TCP">
|
||||
<a-switch v-model.trim="inbound.stream.sockopt.tcpMptcp"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP No-Delay" v-if="inbound.stream.sockopt.tcpMptcp">
|
||||
<a-switch v-model.trim="inbound.stream.sockopt.tcpNoDelay"></a-switch>
|
||||
<a-form-item label="Penetrate">
|
||||
<a-switch v-model.trim="inbound.stream.sockopt.penetrate"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="V6 Only">
|
||||
<a-switch v-model.trim="inbound.stream.sockopt.V6Only"></a-switch>
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
{{define "form/streamSplitHTTP"}}
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "host" }}'>
|
||||
<a-input v-model.trim="inbound.stream.splithttp.host"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "path" }}'>
|
||||
<a-input v-model.trim="inbound.stream.splithttp.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.splithttp.addHeader('host', '')"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.splithttp.headers">
|
||||
<a-input style="width: 50%" v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||
</a-input>
|
||||
<a-input style="width: 50%" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button slot="addonAfter" size="small"
|
||||
@click="inbound.stream.splithttp.removeHeader(index)">-</a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item label='Mode'>
|
||||
<a-select v-model="inbound.stream.splithttp.mode" style="width: 50%"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Concurrent Upload">
|
||||
<a-input v-model.trim="inbound.stream.splithttp.scMaxConcurrentPosts"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Upload Size (Byte)">
|
||||
<a-input v-model.trim="inbound.stream.splithttp.scMaxEachPostBytes"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Min Upload Interval (Ms)">
|
||||
<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" v-if="!inbound.stream.splithttp.xmux.maxConnections">
|
||||
<a-input v-model="inbound.stream.splithttp.xmux.maxConcurrency"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Connections" v-if="!inbound.stream.splithttp.xmux.maxConcurrency">
|
||||
<a-input v-model="inbound.stream.splithttp.xmux.maxConnections"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Reuse Times">
|
||||
<a-input v-model="inbound.stream.splithttp.xmux.cMaxReuseTimes"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Lifetime (ms)">
|
||||
<a-input v-model="inbound.stream.splithttp.xmux.cMaxLifetimeMs"></a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
@@ -9,8 +9,11 @@
|
||||
<a-form-item label='{{ i18n "path" }}'>
|
||||
<a-input v-model.trim="inbound.stream.ws.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='Heartbeat Period'>
|
||||
<a-input-number v-model.number="inbound.stream.ws.heartbeatPeriod" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.ws.addHeader('host', '')"></a-button>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.ws.addHeader('', '')"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.ws.headers">
|
||||
|
||||
46
web/html/xui/form/stream/stream_xhttp.html
Normal file
@@ -0,0 +1,46 @@
|
||||
{{define "form/streamXHTTP"}}
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "host" }}'>
|
||||
<a-input v-model.trim="inbound.stream.xhttp.host"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "path" }}'>
|
||||
<a-input v-model.trim="inbound.stream.xhttp.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.xhttp.addHeader('', '')"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.xhttp.headers">
|
||||
<a-input style="width: 50%" v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||
</a-input>
|
||||
<a-input style="width: 50%" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.xhttp.removeHeader(index)"></a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item label='Mode'>
|
||||
<a-select v-model="inbound.stream.xhttp.mode" style="width: 50%"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Buffered Upload" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||
<a-input-number v-model.number="inbound.stream.xhttp.scMaxBufferedPosts"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Upload Size (Byte)" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||
<a-input v-model.trim="inbound.stream.xhttp.scMaxEachPostBytes"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Stream-Up Server" v-if="inbound.stream.xhttp.mode === 'stream-up'">
|
||||
<a-input v-model.trim="inbound.stream.xhttp.scStreamUpServerSecs"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Padding Bytes">
|
||||
<a-input v-model.trim="inbound.stream.xhttp.xPaddingBytes"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="No SSE Header">
|
||||
<a-switch v-model="inbound.stream.xhttp.noSSEHeader"></a-switch>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
@@ -34,7 +34,7 @@
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="uTLS">
|
||||
<a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 50%"
|
||||
<a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 100%"
|
||||
: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>
|
||||
@@ -57,6 +57,9 @@
|
||||
<a-form-item label="Session Resumption">
|
||||
<a-switch v-model="inbound.stream.tls.enableSessionResumption"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="VerifyPeerCertInNames">
|
||||
<a-input v-model.trim="inbound.stream.tls.verifyPeerCertInNames"></a-input>
|
||||
</a-form-item>
|
||||
<template v-for="cert,index in inbound.stream.tls.certs">
|
||||
<a-form-item label='{{ i18n "certificate" }}'>
|
||||
<a-radio-group v-model="cert.useFile" button-style="solid">
|
||||
@@ -82,10 +85,10 @@
|
||||
</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-input 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-input type="password" v-model="cert.key"></a-input>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label='OCSP stapling'>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<a-tag color="green">[[ inbound.network ]]</a-tag>
|
||||
</td>
|
||||
</tr>
|
||||
<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2 || inbound.isHttpupgrade || inbound.isSplithttp">
|
||||
<template v-if="inbound.isTcp || inbound.isWs || inbound.isHttpupgrade || inbound.isXHTTP">
|
||||
<tr>
|
||||
<td>{{ i18n "host" }}</td>
|
||||
<td v-if="inbound.host">
|
||||
@@ -58,11 +58,11 @@
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<template v-if="inbound.isSplithttp">
|
||||
<template v-if="inbound.isXHTTP">
|
||||
<tr>
|
||||
<td>Mode</td>
|
||||
<td>
|
||||
<a-tag>[[ inbound.stream.splithttp.mode ]]</a-tag>
|
||||
<a-tag>[[ inbound.stream.xhttp.mode ]]</a-tag>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
@@ -185,6 +185,33 @@
|
||||
<a-tag>↑ [[ sizeFormat(infoModal.clientStats.up) ]] / [[ sizeFormat(infoModal.clientStats.down) ]] ↓</a-tag>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="infoModal.clientSettings.comment">
|
||||
<td>{{ i18n "comment" }}</td>
|
||||
<td>
|
||||
<a-tooltip :title="[[ infoModal.clientSettings.comment ]]">
|
||||
<a-tag class="info-large-tag">[[ infoModal.clientSettings.comment ]]</a-tag>
|
||||
</a-tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="app.ipLimitEnable">
|
||||
<td>{{ i18n "pages.inbounds.IPLimit" }}</td>
|
||||
<td>
|
||||
<a-tag>[[ infoModal.clientSettings.limitIp ]]</a-tag>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="app.ipLimitEnable">
|
||||
<td>{{ i18n "pages.inbounds.IPLimitlog" }}</td>
|
||||
<td>
|
||||
<a-tag>[[ infoModal.clientIps ]]</a-tag>
|
||||
<a-icon type="sync" :spin="refreshing" @click="refreshIPs" style="margin: 0 5px;"></a-icon>
|
||||
<a-tooltip :title="[[ dbInbound.address ]]">
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
|
||||
</template>
|
||||
<a-icon type="delete" @click="clearClientIps"></a-icon>
|
||||
</a-tooltip>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="display: inline-table; margin-block: 10px; width: 100%; text-align: center;">
|
||||
<tr>
|
||||
@@ -400,7 +427,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<tr-info-row v-for="(link,index) in infoModal.links" class="tr-info-row">
|
||||
<tr-info-row class="tr-info-row">
|
||||
<tr-info-title class="tr-info-title">
|
||||
<a-tag color="blue">Config</a-tag>
|
||||
<a-tooltip title='{{ i18n "copy" }}'>
|
||||
@@ -417,6 +444,18 @@
|
||||
</template>
|
||||
</a-modal>
|
||||
<script>
|
||||
function refreshIPs(email) {
|
||||
return HttpUtil.post(`/panel/inbound/clientIps/${email}`).then((msg) => {
|
||||
if (msg.success) {
|
||||
try {
|
||||
return JSON.parse(msg.obj).join(', ');
|
||||
} catch (e) {
|
||||
return msg.obj;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const infoModal = {
|
||||
visible: false,
|
||||
inbound: new Inbound(),
|
||||
@@ -431,6 +470,7 @@
|
||||
isExpired: false,
|
||||
subLink: '',
|
||||
subJsonLink: '',
|
||||
clientIps: '',
|
||||
show(dbInbound, index) {
|
||||
this.index = index;
|
||||
this.inbound = dbInbound.toInbound();
|
||||
@@ -438,6 +478,12 @@
|
||||
this.clientSettings = this.inbound.clients ? this.inbound.clients[index] : null;
|
||||
this.isExpired = this.inbound.clients ? this.inbound.isExpiry(index) : this.dbInbound.isExpiry;
|
||||
this.clientStats = this.inbound.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
||||
|
||||
if (app.ipLimitEnable && this.clientSettings.limitIp) {
|
||||
refreshIPs(this.clientStats.email).then((ips) => {
|
||||
this.clientIps = ips;
|
||||
})
|
||||
}
|
||||
if (this.inbound.protocol == Protocols.WIREGUARD) {
|
||||
this.links = this.inbound.genInboundLinks(dbInbound.remark).split('\r\n')
|
||||
} else {
|
||||
@@ -466,6 +512,7 @@
|
||||
el: '#inbound-info-modal',
|
||||
data: {
|
||||
infoModal,
|
||||
refreshing: false,
|
||||
get dbInbound() {
|
||||
return this.infoModal.dbInbound;
|
||||
},
|
||||
@@ -502,6 +549,26 @@
|
||||
remained = this.infoModal.clientStats.total - this.infoModal.clientStats.up - this.infoModal.clientStats.down;
|
||||
return remained > 0 ? sizeFormat(remained) : '-';
|
||||
},
|
||||
refreshIPs() {
|
||||
this.refreshing = true;
|
||||
refreshIPs(this.infoModal.clientStats.email)
|
||||
.then((ips) => {
|
||||
this.infoModal.clientIps = ips;
|
||||
})
|
||||
.finally(() => {
|
||||
this.refreshing = false;
|
||||
});
|
||||
},
|
||||
clearClientIps() {
|
||||
HttpUtil.post(`/panel/inbound/clearClientIps/${this.infoModal.clientStats.email}`)
|
||||
.then((msg) => {
|
||||
if (!msg.success) {
|
||||
return;
|
||||
}
|
||||
this.infoModal.clientIps = 'No IP Record';
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1108,6 +1108,36 @@
|
||||
this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`);
|
||||
}
|
||||
},
|
||||
getSubGroupClients(dbInbounds, currentClient) {
|
||||
const response = {
|
||||
inbounds: [],
|
||||
clients: [],
|
||||
editIds: []
|
||||
}
|
||||
if (dbInbounds && dbInbounds.length > 0 && currentClient) {
|
||||
dbInbounds.forEach((dbInboundItem) => {
|
||||
const dbInbound = new DBInbound(dbInboundItem);
|
||||
if (dbInbound) {
|
||||
const inbound = dbInbound.toInbound();
|
||||
if (inbound) {
|
||||
const clients = inbound.clients;
|
||||
if (clients.length > 0) {
|
||||
clients.forEach((client) => {
|
||||
if (client['subId'] === currentClient['subId']) {
|
||||
client['inboundId'] = dbInboundItem.id
|
||||
client['clientId'] = this.getClientId(dbInbound.protocol, client)
|
||||
response.inbounds.push(dbInboundItem.id)
|
||||
response.clients.push(client)
|
||||
response.editIds.push(client['clientId'])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return response;
|
||||
},
|
||||
getClientId(protocol, client) {
|
||||
switch (protocol) {
|
||||
case Protocols.TROJAN: return client.password;
|
||||
@@ -1310,7 +1340,7 @@
|
||||
if (clients != null){
|
||||
clients.forEach(c => {
|
||||
if (c.subId && c.subId.length>0){
|
||||
subLinks.push(this.subSettings.subURI + c.subId + "?name=" + c.subId)
|
||||
subLinks.push(this.subSettings.subURI + c.subId)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1337,7 +1367,7 @@
|
||||
if (clients != null){
|
||||
clients.forEach(c => {
|
||||
if (c.subId && c.subId.length>0){
|
||||
subLinks.push(this.subSettings.subURI + c.subId + "?name=" + c.subId)
|
||||
subLinks.push(this.subSettings.subURI + c.subId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -142,6 +142,8 @@
|
||||
<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>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.settings.externalTrafficInformEnable"}}' desc='{{ i18n "pages.settings.externalTrafficInformEnableDesc"}}' v-model="allSetting.externalTrafficInformEnable"></setting-list-item>
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.externalTrafficInformURI"}}' desc='{{ i18n "pages.settings.externalTrafficInformURIDesc"}}' v-model="allSetting.externalTrafficInformURI" placeholder="(http|https)://domain[:port]/path/"></setting-list-item>
|
||||
<setting-list-item type="text" title='{{ i18n "pages.settings.timeZone"}}' desc='{{ i18n "pages.settings.timeZoneDesc"}}' v-model="allSetting.timeLocation"></setting-list-item>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 20px">
|
||||
@@ -183,25 +185,89 @@
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings"}}' style="padding: 20px;">
|
||||
<a-divider>{{ i18n "pages.settings.security.admin"}}</a-divider>
|
||||
<a-form layout="horizontal" :colon="false" style="float: left; margin-bottom: 2rem;" :label-col="{ md: {span:10} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.settings.oldUsername"}}'>
|
||||
<a-input autocomplete="username" v-model="user.oldUsername"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.settings.currentPassword"}}'>
|
||||
<password-input autocomplete="current-password" v-model="user.oldPassword"></password-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.settings.newUsername"}}'>
|
||||
<a-input v-model="user.newUsername"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.settings.newPassword"}}'>
|
||||
<password-input autocomplete="new-password" v-model="user.newPassword"></password-input>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings"}}' style="padding-top: 20px;">
|
||||
<a-collapse>
|
||||
<a-collapse-panel header='{{ i18n "pages.settings.security.admin"}}'>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 0 20px; padding-bottom: 10px;">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.settings.oldUsername"}}'></a-list-item-meta>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<a-input autocomplete="username" v-model="user.oldUsername"></a-input>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="padding: 10px 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.settings.currentPassword"}}'></a-list-item-meta>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<password-input autocomplete="current-password" v-model="user.oldPassword"></password-input>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="padding: 10px 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.settings.newUsername"}}'></a-list-item-meta>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<a-input v-model="user.newUsername"></a-input>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="padding: 10px 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.settings.newPassword"}}'></a-list-item-meta>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<password-input autocomplete="new-password" v-model="user.newPassword"></password-input>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-space direction="horizontal" style="padding: 0 20px; padding-top: 20px;">
|
||||
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
||||
</a-space>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.settings.security.secret"}}'>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 0 20px; padding-bottom: 10px;">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.settings.security.loginSecurity" }}'
|
||||
description='{{ i18n "pages.settings.security.loginSecurityDesc" }}'>
|
||||
</a-list-item-meta>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<a-switch @change="toggleToken(allSetting.secretEnable)" v-model="allSetting.secretEnable"></a-switch>
|
||||
<a-icon style="margin-left: 1rem;" v-if="allSetting.secretEnable" :spin="this.changeSecret" type="sync" @click="getNewSecret"></a-icon>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="padding: 10px 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.settings.security.secretToken" }}'
|
||||
description='{{ i18n "pages.settings.security.secretTokenDesc" }}'>
|
||||
</a-list-item-meta>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<a-textarea type="text" :disabled="!allSetting.secretEnable" v-model="user.loginSecret"></a-textarea>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-space direction="horizontal" style="padding: 0 20px; padding-top: 20px;">
|
||||
<a-button type="primary" :loading="this.changeSecret" @click="updateSecret">{{ i18n "confirm" }}</a-button>
|
||||
</a-space>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<!--
|
||||
<a-divider>{{ i18n "pages.settings.security.secret"}}</a-divider>
|
||||
<a-form style="padding: 0 20px;">
|
||||
<a-list-item>
|
||||
@@ -219,22 +285,9 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
<a-list-item>
|
||||
<a-row>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.settings.security.secretToken" }}'
|
||||
description='{{ i18n "pages.settings.security.secretTokenDesc" }}'>
|
||||
</a-list-item-meta>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<template>
|
||||
<a-textarea type="text" :disabled="!allSetting.secretEnable" v-model="user.loginSecret"></a-textarea>
|
||||
</template>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
|
||||
<a-button type="primary" :loading="this.changeSecret" @click="updateSecret">{{ i18n "confirm" }}</a-button>
|
||||
</a-form>
|
||||
</a-form> -->
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="3" tab='{{ i18n "pages.settings.TGBotSettings"}}'>
|
||||
<a-list item-layout="horizontal">
|
||||
@@ -324,7 +377,7 @@
|
||||
<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">
|
||||
<a-select-option :value="p" :label="p" v-for="p in ['rand', 'base64', 'str', 'hex']" :key="p">
|
||||
[[ p ]] </a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
@@ -381,14 +434,40 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-collapse v-if="enableDirect" style="margin-top: 14px;">
|
||||
<a-collapse-panel header='{{ i18n "pages.xray.directips"}}'>
|
||||
<a-list-item style="padding: 10px 20px">
|
||||
<a-checkbox-group v-model="directIPs" :options="IPsOptions"></a-checkbox-group>
|
||||
<a-collapse-panel header='{{ i18n "pages.settings.direct"}}'>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 0 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta
|
||||
title='{{ i18n "pages.xray.directips" }}'/>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-select mode="tags" style="width: 100%"
|
||||
v-model="directIPs"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="p.value" :label="p.label"
|
||||
v-for="p in directIPsOptions"> [[ p.label ]]
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</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="directDomains" :options="DomainsOptions"></a-checkbox-group>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 0 20px">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta
|
||||
title='{{ i18n "pages.xray.directdomains" }}'/>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-select mode="tags" style="width: 100%"
|
||||
v-model="directDomains"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="p.value" :label="p.label"
|
||||
v-for="p in diretDomainsOptions"> [[ p.label ]]
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
@@ -439,7 +518,7 @@
|
||||
sockopt: {
|
||||
tcpKeepAliveIdle: 100,
|
||||
tcpMptcp: true,
|
||||
tcpNoDelay: true
|
||||
penetrate: true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -476,25 +555,26 @@
|
||||
]
|
||||
},
|
||||
],
|
||||
IPsOptions: [
|
||||
{ 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' },
|
||||
directIPsOptions: [
|
||||
{ label: 'Private IP', value: 'geoip:private' },
|
||||
{ label: '🇮🇷 Iran', value: 'geoip:ir' },
|
||||
{ label: '🇨🇳 China', value: 'geoip:cn' },
|
||||
{ label: '🇷🇺 Russia', value: 'geoip:ru' },
|
||||
{ label: '🇻🇳 Vietnam', value: 'geoip:vn' },
|
||||
{ label: '🇪🇸 Spain', value: 'geoip:es' },
|
||||
{ label: '🇮🇩 Indonesia', value: 'geoip:id' },
|
||||
{ label: '🇺🇦 Ukraine', value: 'geoip:ua' },
|
||||
{ label: '🇹🇷 Türkiye', value: 'geoip:tr' },
|
||||
{ label: '🇧🇷 Brazil', value: 'geoip:br' },
|
||||
],
|
||||
DomainsOptions: [
|
||||
{ label: '🇮🇷 Iran', value: 'ir' },
|
||||
{ label: '🇨🇳 China', value: 'cn' },
|
||||
{ label: '🇷🇺 Russia', value: 'ru' },
|
||||
{ label: 'Apple', value: 'apple' },
|
||||
{ label: 'Meta', value: 'meta' },
|
||||
{ label: 'Google', value: 'google' },
|
||||
diretDomainsOptions: [
|
||||
{ label: 'Private DNS', value: 'geosite:private' },
|
||||
{ label: '🇮🇷 Iran', value: 'geosite:category-ir' },
|
||||
{ label: '🇨🇳 China', value: 'geosite:cn' },
|
||||
{ label: '🇷🇺 Russia', value: 'geosite:category-ru' },
|
||||
{ label: 'Apple', value: 'geosite:apple' },
|
||||
{ label: 'Meta', value: 'geosite:meta' },
|
||||
{ label: 'Google', value: 'geosite:google' },
|
||||
],
|
||||
get remarkModel() {
|
||||
rm = this.allSetting.remarkModel;
|
||||
@@ -752,7 +832,7 @@
|
||||
const rules = JSON.parse(this.allSetting.subJsonRules);
|
||||
if (!Array.isArray(rules)) return [];
|
||||
const ipRule = rules.find(r => r.ip);
|
||||
return ipRule?.ip.map(d => d.replace("geoip:", "")) ?? [];
|
||||
return ipRule?.ip ?? [];
|
||||
},
|
||||
set: function (v) {
|
||||
let rules = JSON.parse(this.allSetting.subJsonRules);
|
||||
@@ -766,7 +846,7 @@
|
||||
|
||||
rules[ruleIndex].ip = [];
|
||||
v.forEach(d => {
|
||||
rules[ruleIndex].ip.push("geoip:" + d);
|
||||
rules[ruleIndex].ip.push(d);
|
||||
});
|
||||
}
|
||||
this.allSetting.subJsonRules = JSON.stringify(rules);
|
||||
@@ -778,34 +858,18 @@
|
||||
const rules = JSON.parse(this.allSetting.subJsonRules);
|
||||
if (!Array.isArray(rules)) return [];
|
||||
const domainRule = rules.find(r => r.domain);
|
||||
return domainRule?.domain.map(d => {
|
||||
if (d.startsWith("geosite:category-")) {
|
||||
return d.replace("geosite:category-", "");
|
||||
}
|
||||
return d.replace("geosite:", "");
|
||||
})
|
||||
?? [];
|
||||
return domainRule?.domain ?? [];
|
||||
},
|
||||
set: function (v) {
|
||||
let rules = JSON.parse(this.allSetting.subJsonRules);
|
||||
if (!Array.isArray(rules)) return;
|
||||
|
||||
if (v.length == 0) {
|
||||
rules = rules.filter(r => !r.domain);
|
||||
} else {
|
||||
let ruleIndex = rules.findIndex(r => r.domain);
|
||||
if (ruleIndex == -1) ruleIndex = rules.push(this.defaultRules[0]) - 1;
|
||||
|
||||
rules[ruleIndex].domain = [];
|
||||
v.forEach(d => {
|
||||
let category = '';
|
||||
if (["cn", "apple", "meta", "google"].includes(d)) {
|
||||
category = "";
|
||||
} else if (["ru", "ir"].includes(d)) {
|
||||
category = "category-";
|
||||
}
|
||||
rules[ruleIndex].domain.push("geosite:" + category + d);
|
||||
});
|
||||
rules[ruleIndex].domain = v;
|
||||
}
|
||||
this.allSetting.subJsonRules = JSON.stringify(rules);
|
||||
}
|
||||
@@ -838,4 +902,4 @@
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -145,6 +145,14 @@
|
||||
</a-row>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.xray.statistics" }}'>
|
||||
<a-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.statsInboundUplink" }}' desc='{{ i18n "pages.xray.statsInboundUplinkDesc" }}' style="padding-top: 0 !important;" v-model="statsInboundUplink"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.statsInboundDownlink" }}' desc='{{ i18n "pages.xray.statsInboundDownlinkDesc" }}' v-model="statsInboundDownlink"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.statsOutboundUplink" }}' desc='{{ i18n "pages.xray.statsOutboundUplinkDesc" }}' v-model="statsOutboundUplink"></setting-list-item>
|
||||
<setting-list-item type="switch" title='{{ i18n "pages.xray.statsOutboundDownlink" }}' desc='{{ i18n "pages.xray.statsOutboundDownlinkDesc" }}' v-model="statsOutboundDownlink"></setting-list-item>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel header='{{ i18n "pages.xray.logConfigs" }}'>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning" style="text-align: center;">
|
||||
@@ -660,88 +668,146 @@
|
||||
<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>
|
||||
<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>
|
||||
<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-collapse>
|
||||
<a-collapse-panel header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||
<a-list-item>
|
||||
<a-row style="padding: 10px 20px; padding-top: 0;">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.xray.dns.enable" }}'
|
||||
description='{{ i18n "pages.xray.dns.enableDesc" }}' />
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-switch v-model="enableDNS"></a-switch>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<template v-if="enableDNS">
|
||||
<a-row style="padding: 10px 20px;">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.xray.dns.tag" }}'
|
||||
description='{{ i18n "pages.xray.dns.tagDesc" }}' />
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-input type="text" v-model="dnsTag"></a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="padding: 10px 20px;">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.xray.dns.clientIp" }}'
|
||||
description='{{ i18n "pages.xray.dns.clientIpDesc" }}'/>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-input type="text" v-model="dnsClientIp"></a-input>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="padding: 10px 20px;">
|
||||
<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-row style="padding: 10px 20px;">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.xray.dns.disableCache" }}'
|
||||
description='{{ i18n "pages.xray.dns.disableCacheDesc" }}'/>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-switch v-model="dnsDisableCache"></a-switch>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="padding: 10px 20px;">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.xray.dns.disableFallback" }}'
|
||||
description='{{ i18n "pages.xray.dns.disableFallbackDesc" }}'/>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-switch v-model="dnsDisableFallback"></a-switch>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-row style="padding: 10px 20px;">
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-list-item-meta title='{{ i18n "pages.xray.dns.disableFallbackIfMatch" }}'
|
||||
description='{{ i18n "pages.xray.dns.disableFallbackIfMatchDesc" }}'/>
|
||||
</a-col>
|
||||
<a-col :lg="24" :xl="12">
|
||||
<a-switch v-model="dnsDisableFallbackIfMatch"></a-switch>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</a-collapse-panel>
|
||||
<template v-if="enableDNS">
|
||||
<a-collapse-panel header='DNS'>
|
||||
<a-button type="primary" icon="plus" @click="addDNSServer()" style="margin: 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="margin: 10px; margin-top: 10px;">
|
||||
<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-collapse-panel>
|
||||
<a-collapse-panel header='FakeDNS'>
|
||||
<a-button type="primary" icon="plus" @click="addFakedns()" style="margin: 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="margin: 10px; margin-top: 10px;">
|
||||
<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>
|
||||
</a-collapse-panel>
|
||||
</template>
|
||||
</a-collapse>
|
||||
</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>
|
||||
@@ -901,8 +967,8 @@
|
||||
{ label: 'Private IPs', value: 'geoip:private' },
|
||||
{ label: '🇮🇷 Iran', value: 'ext:geoip_IR.dat:ir' },
|
||||
{ label: '🇨🇳 China', value: 'geoip:cn' },
|
||||
{ label: '🇷🇺 Russia', value: 'geoip:ru' },
|
||||
{ label: '🇻🇳 Vietnam', value: 'ext:geoip_VN.dat:vn' },
|
||||
{ label: '🇷🇺 Russia', value: 'ext:geoip_RU.dat:ru' },
|
||||
{ label: '🇻🇳 Vietnam', value: 'geoip:vn' },
|
||||
{ label: '🇪🇸 Spain', value: 'geoip:es' },
|
||||
{ label: '🇮🇩 Indonesia', value: 'geoip:id' },
|
||||
{ label: '🇺🇦 Ukraine', value: 'geoip:ua' },
|
||||
@@ -915,15 +981,16 @@
|
||||
{ label: '🇮🇷 .ایران', value: 'regexp:.*\\.xn--mgba3a4f16a$' },
|
||||
{ label: '🇨🇳 China', value: 'geosite:cn' },
|
||||
{ label: '🇨🇳 .cn', value: 'regexp:.*\\.cn$' },
|
||||
{ label: '🇷🇺 Russia', value: 'geosite:category-ru' },
|
||||
{ label: '🇷🇺 .ru', value: 'regexp:.*\\.ru' },
|
||||
{ label: '🇻🇳 Vietnam', value: 'ext:geosite_VN.dat:vn' },
|
||||
{ label: '🇷🇺 Russia', value: 'ext:geosite_RU.dat:ru-available-only-inside' },
|
||||
{ label: '🇷🇺 .ru', value: 'regexp:.*\\.ru$' },
|
||||
{ label: '🇷🇺 .su', value: 'regexp:.*\\.su$' },
|
||||
{ label: '🇷🇺 .рф', value: 'regexp:.*\\.xn--p1ai$' },
|
||||
{ label: '🇻🇳 .vn', value: 'regexp:.*\\.vn$' },
|
||||
],
|
||||
BlockDomainsOptions: [
|
||||
{ label: 'Ads All', value: 'geosite:category-ads-all' },
|
||||
{ label: 'Ads IR 🇮🇷', value: 'ext:geosite_IR.dat:category-ads-all' },
|
||||
{ label: 'Ads VN 🇻🇳', value: 'ext:geosite_VN.dat:ads' },
|
||||
{ label: 'Ads RU 🇷🇺', value: 'ext:geosite_RU.dat:category-ads-all' },
|
||||
{ label: 'Malware 🇮🇷', value: 'ext:geosite_IR.dat:malware' },
|
||||
{ label: 'Phishing 🇮🇷', value: 'ext:geosite_IR.dat:phishing' },
|
||||
{ label: 'Cryptominers 🇮🇷', value: 'ext:geosite_IR.dat:cryptominers' },
|
||||
@@ -932,9 +999,10 @@
|
||||
{ label: '🇮🇷 .ایران', value: 'regexp:.*\\.xn--mgba3a4f16a$' },
|
||||
{ label: '🇨🇳 China', value: 'geosite:cn' },
|
||||
{ label: '🇨🇳 .cn', value: 'regexp:.*\\.cn$' },
|
||||
{ label: '🇷🇺 Russia', value: 'geosite:category-ru' },
|
||||
{ label: '🇷🇺 Russia', value: 'ext:geosite_RU.dat:ru-available-only-inside' },
|
||||
{ label: '🇷🇺 .ru', value: 'regexp:.*\\.ru' },
|
||||
{ label: '🇻🇳 Vietnam', value: 'ext:geosite_VN.dat:vn' },
|
||||
{ label: '🇷🇺 .su', value: 'regexp:.*\\.su$' },
|
||||
{ label: '🇷🇺 .рф', value: 'regexp:.*\\.xn--p1ai$' },
|
||||
{ label: '🇻🇳 .vn', value: 'regexp:.*\\.vn$' },
|
||||
],
|
||||
ServicesOptions: [
|
||||
@@ -1821,6 +1889,50 @@
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
statsInboundUplink: {
|
||||
get: function () {
|
||||
if (!this.templateSettings || !this.templateSettings.policy.system || !this.templateSettings.policy.system.statsInboundUplink) return false;
|
||||
return this.templateSettings.policy.system.statsInboundUplink;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.policy.system.statsInboundUplink = newValue;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
statsInboundDownlink: {
|
||||
get: function () {
|
||||
if (!this.templateSettings || !this.templateSettings.policy.system || !this.templateSettings.policy.system.statsInboundDownlink) return false;
|
||||
return this.templateSettings.policy.system.statsInboundDownlink;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.policy.system.statsInboundDownlink = newValue;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
statsOutboundUplink: {
|
||||
get: function () {
|
||||
if (!this.templateSettings || !this.templateSettings.policy.system || !this.templateSettings.policy.system.statsOutboundUplink) return false;
|
||||
return this.templateSettings.policy.system.statsOutboundUplink;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.policy.system.statsOutboundUplink = newValue;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
statsOutboundDownlink: {
|
||||
get: function () {
|
||||
if (!this.templateSettings || !this.templateSettings.policy.system || !this.templateSettings.policy.system.statsOutboundDownlink) return false;
|
||||
return this.templateSettings.policy.system.statsOutboundDownlink;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.policy.system.statsOutboundDownlink = newValue;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
maskAddressLog: {
|
||||
get: function () {
|
||||
if (!this.templateSettings || !this.templateSettings.log || !this.templateSettings.log.maskAddress) return "";
|
||||
@@ -1930,7 +2042,11 @@
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
if (newValue) {
|
||||
newTemplateSettings.dns = { servers: [], queryStrategy: "UseIP", tag: "dns_inbound" };
|
||||
newTemplateSettings.dns = {
|
||||
servers: [],
|
||||
queryStrategy: "UseIP",
|
||||
tag: "dns_inbound"
|
||||
};
|
||||
newTemplateSettings.fakedns = null;
|
||||
} else {
|
||||
delete newTemplateSettings.dns;
|
||||
@@ -1945,7 +2061,63 @@
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
newTemplateSettings.dns.tag = newValue;
|
||||
newTemplateSettings.dns.tag = newValue;
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
dnsClientIp: {
|
||||
get: function () {
|
||||
return this.enableDNS ? this.templateSettings.dns.clientIp : null;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
if (newValue) {
|
||||
newTemplateSettings.dns.clientIp = newValue;
|
||||
} else {
|
||||
delete newTemplateSettings.dns.clientIp;
|
||||
}
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
dnsDisableCache: {
|
||||
get: function () {
|
||||
return this.enableDNS ? this.templateSettings.dns.disableCache : false;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
if (newValue) {
|
||||
newTemplateSettings.dns.disableCache = newValue;
|
||||
} else {
|
||||
delete newTemplateSettings.dns.disableCache
|
||||
}
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
dnsDisableFallback: {
|
||||
get: function () {
|
||||
return this.enableDNS ? this.templateSettings.dns.disableFallback : false;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
if (newValue) {
|
||||
newTemplateSettings.dns.disableFallback = newValue;
|
||||
} else {
|
||||
delete newTemplateSettings.dns.disableFallback
|
||||
}
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
dnsDisableFallbackIfMatch: {
|
||||
get: function () {
|
||||
return this.enableDNS ? this.templateSettings.dns.disableFallbackIfMatch : false;
|
||||
},
|
||||
set: function (newValue) {
|
||||
newTemplateSettings = this.templateSettings;
|
||||
if (newValue) {
|
||||
newTemplateSettings.dns.disableFallbackIfMatch = newValue;
|
||||
} else {
|
||||
delete newTemplateSettings.dns.disableFallbackIfMatch
|
||||
}
|
||||
this.templateSettings = newTemplateSettings;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
</a-form-item>
|
||||
<a-form-item label='Protocol'>
|
||||
<a-select v-model="ruleModal.rule.protocol" mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in ['http','tls','bittorrent']" :value="x">[[ x ]]</a-select-option>
|
||||
<a-select-option v-for="x in ['http','tls','bittorrent','quic']" :value="x">[[ x ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Attributes'>
|
||||
|
||||
@@ -106,7 +106,7 @@ func (j *CheckClientIpJob) hasLimitIp() bool {
|
||||
|
||||
func (j *CheckClientIpJob) processLogFile() bool {
|
||||
|
||||
ipRegex := regexp.MustCompile(`from \[?([0-9a-fA-F:.]+)\]?:\d+ accepted`)
|
||||
ipRegex := regexp.MustCompile(`from (?:tcp:|udp:)?\[?([0-9a-fA-F\.:]+)\]?:\d+ accepted`)
|
||||
emailRegex := regexp.MustCompile(`email: (.+)$`)
|
||||
|
||||
accessLogPath, _ := xray.GetAccessLogPath()
|
||||
@@ -151,13 +151,13 @@ func (j *CheckClientIpJob) processLogFile() bool {
|
||||
}
|
||||
sort.Strings(ips)
|
||||
|
||||
inboundClientIps, err := j.getInboundClientIps(email)
|
||||
clientIpsRecord, err := j.getInboundClientIps(email)
|
||||
if err != nil {
|
||||
j.addInboundClientIps(email, ips)
|
||||
continue
|
||||
}
|
||||
|
||||
shouldCleanLog = j.updateInboundClientIps(inboundClientIps, email, ips) || shouldCleanLog
|
||||
shouldCleanLog = j.updateInboundClientIps(clientIpsRecord, email, ips) || shouldCleanLog
|
||||
}
|
||||
|
||||
return shouldCleanLog
|
||||
@@ -309,12 +309,12 @@ func (j *CheckClientIpJob) updateInboundClientIps(inboundClientIps *model.Inboun
|
||||
|
||||
func (j *CheckClientIpJob) getInboundByEmail(clientEmail string) (*model.Inbound, error) {
|
||||
db := database.GetDB()
|
||||
var inbounds *model.Inbound
|
||||
inbound := &model.Inbound{}
|
||||
|
||||
err := db.Model(model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").Find(&inbounds).Error
|
||||
err := db.Model(&model.Inbound{}).Where("settings LIKE ?", "%"+clientEmail+"%").First(inbound).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return inbounds, nil
|
||||
return inbound, nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
package job
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"x-ui/logger"
|
||||
"x-ui/web/service"
|
||||
"x-ui/xray"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
type XrayTrafficJob struct {
|
||||
settingService service.SettingService
|
||||
xrayService service.XrayService
|
||||
inboundService service.InboundService
|
||||
outboundService service.OutboundService
|
||||
@@ -31,7 +36,36 @@ func (j *XrayTrafficJob) Run() {
|
||||
if err != nil {
|
||||
logger.Warning("add outbound traffic failed:", err)
|
||||
}
|
||||
if ExternalTrafficInformEnable, err := j.settingService.GetExternalTrafficInformEnable(); ExternalTrafficInformEnable {
|
||||
j.informTrafficToExternalAPI(traffics, clientTraffics)
|
||||
} else if err != nil {
|
||||
logger.Warning("get ExternalTrafficInformEnable failed:", err)
|
||||
}
|
||||
if needRestart0 || needRestart1 {
|
||||
j.xrayService.SetToNeedRestart()
|
||||
}
|
||||
}
|
||||
|
||||
func (j *XrayTrafficJob) informTrafficToExternalAPI(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) {
|
||||
informURL, err := j.settingService.GetExternalTrafficInformURI()
|
||||
if err != nil {
|
||||
logger.Warning("get ExternalTrafficInformURI failed:", err)
|
||||
return
|
||||
}
|
||||
requestBody, err := json.Marshal(map[string]interface{}{"clientTraffics": clientTraffics, "inboundTraffics": inboundTraffics})
|
||||
if err != nil {
|
||||
logger.Warning("parse client/inbound traffic failed:", err)
|
||||
return
|
||||
}
|
||||
request := fasthttp.AcquireRequest()
|
||||
defer fasthttp.ReleaseRequest(request)
|
||||
request.Header.SetMethod("POST")
|
||||
request.Header.SetContentType("application/json; charset=UTF-8")
|
||||
request.SetBody([]byte(requestBody))
|
||||
request.SetRequestURI(informURL)
|
||||
response := fasthttp.AcquireResponse()
|
||||
defer fasthttp.ReleaseResponse(response)
|
||||
if err := fasthttp.Do(request, response); err != nil {
|
||||
logger.Warning("POST ExternalTrafficInformURI failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"tag": "direct",
|
||||
"protocol": "freedom",
|
||||
"settings": {
|
||||
"domainStrategy": "UseIP",
|
||||
"domainStrategy": "AsIs",
|
||||
"redirect": "",
|
||||
"noises": []
|
||||
}
|
||||
@@ -51,8 +51,8 @@
|
||||
"system": {
|
||||
"statsInboundDownlink": true,
|
||||
"statsInboundUplink": true,
|
||||
"statsOutboundDownlink": true,
|
||||
"statsOutboundUplink": true
|
||||
"statsOutboundDownlink": false,
|
||||
"statsOutboundUplink": false
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
|
||||
@@ -588,8 +588,12 @@ func (s *InboundService) DelInboundClient(inboundId int, clientId string) (bool,
|
||||
logger.Debug("Client deleted by api:", email)
|
||||
needRestart = false
|
||||
} else {
|
||||
logger.Debug("Unable to del client by api:", err1)
|
||||
needRestart = true
|
||||
if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", email)) {
|
||||
logger.Debug("User is already deleted. Nothing to do more...")
|
||||
} else {
|
||||
logger.Debug("Error in deleting client by api:", err1)
|
||||
needRestart = true
|
||||
}
|
||||
}
|
||||
s.xrayApi.Close()
|
||||
}
|
||||
@@ -713,10 +717,14 @@ func (s *InboundService) UpdateInboundClient(data *model.Inbound, clientId strin
|
||||
if oldClients[clientIndex].Enable {
|
||||
err1 := s.xrayApi.RemoveUser(oldInbound.Tag, oldEmail)
|
||||
if err1 == nil {
|
||||
logger.Debug("Old client deleted by api:", clients[0].Email)
|
||||
logger.Debug("Old client deleted by api:", oldEmail)
|
||||
} else {
|
||||
logger.Debug("Error in deleting client by api:", err1)
|
||||
needRestart = true
|
||||
if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", oldEmail)) {
|
||||
logger.Debug("User is already deleted. Nothing to do more...")
|
||||
} else {
|
||||
logger.Debug("Error in deleting client by api:", err1)
|
||||
needRestart = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if clients[0].Enable {
|
||||
@@ -1037,12 +1045,8 @@ func (s *InboundService) disableInvalidInbounds(tx *gorm.DB) (bool, int64, error
|
||||
if err1 == nil {
|
||||
logger.Debug("Inbound disabled by api:", tag)
|
||||
} else {
|
||||
if strings.Contains(err1.Error(), fmt.Sprintf("User %s not found.", tag)) {
|
||||
logger.Debug("User is already disabled. Nothing to do more...")
|
||||
} else {
|
||||
logger.Debug("Error in disabling client by api:", err1)
|
||||
needRestart = true
|
||||
}
|
||||
logger.Debug("Error in disabling inbound by api:", err1)
|
||||
needRestart = true
|
||||
}
|
||||
}
|
||||
s.xrayApi.Close()
|
||||
|
||||
@@ -285,9 +285,7 @@ func (s *ServerService) GetXrayVersions() ([]string, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (major == 1 && minor == 8 && patch == 24) ||
|
||||
(major == 24 && ((minor > 11) || (minor == 11 && patch >= 11))) ||
|
||||
(major > 24) {
|
||||
if major > 25 || (major == 25 && minor > 3) || (major == 25 && minor == 3 && patch >= 3) {
|
||||
versions = append(versions, release.TagName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,50 +24,52 @@ import (
|
||||
var xrayTemplateConfig string
|
||||
|
||||
var defaultValueMap = map[string]string{
|
||||
"xrayTemplateConfig": xrayTemplateConfig,
|
||||
"webListen": "",
|
||||
"webDomain": "",
|
||||
"webPort": "2053",
|
||||
"webCertFile": "",
|
||||
"webKeyFile": "",
|
||||
"secret": random.Seq(32),
|
||||
"webBasePath": "/",
|
||||
"sessionMaxAge": "60",
|
||||
"pageSize": "50",
|
||||
"expireDiff": "0",
|
||||
"trafficDiff": "0",
|
||||
"remarkModel": "-ieo",
|
||||
"timeLocation": "Asia/Tehran",
|
||||
"tgBotEnable": "false",
|
||||
"tgBotToken": "",
|
||||
"tgBotProxy": "",
|
||||
"tgBotAPIServer": "",
|
||||
"tgBotChatId": "",
|
||||
"tgRunTime": "@daily",
|
||||
"tgBotBackup": "false",
|
||||
"tgBotLoginNotify": "true",
|
||||
"tgCpu": "80",
|
||||
"tgLang": "en-US",
|
||||
"secretEnable": "false",
|
||||
"subEnable": "false",
|
||||
"subListen": "",
|
||||
"subPort": "2096",
|
||||
"subPath": "/sub/",
|
||||
"subDomain": "",
|
||||
"subCertFile": "",
|
||||
"subKeyFile": "",
|
||||
"subUpdates": "12",
|
||||
"subEncrypt": "true",
|
||||
"subShowInfo": "true",
|
||||
"subURI": "",
|
||||
"subJsonPath": "/json/",
|
||||
"subJsonURI": "",
|
||||
"subJsonFragment": "",
|
||||
"subJsonNoises": "",
|
||||
"subJsonMux": "",
|
||||
"subJsonRules": "",
|
||||
"datepicker": "gregorian",
|
||||
"warp": "",
|
||||
"xrayTemplateConfig": xrayTemplateConfig,
|
||||
"webListen": "",
|
||||
"webDomain": "",
|
||||
"webPort": "2053",
|
||||
"webCertFile": "",
|
||||
"webKeyFile": "",
|
||||
"secret": random.Seq(32),
|
||||
"webBasePath": "/",
|
||||
"sessionMaxAge": "60",
|
||||
"pageSize": "50",
|
||||
"expireDiff": "0",
|
||||
"trafficDiff": "0",
|
||||
"remarkModel": "-ieo",
|
||||
"timeLocation": "Local",
|
||||
"tgBotEnable": "false",
|
||||
"tgBotToken": "",
|
||||
"tgBotProxy": "",
|
||||
"tgBotAPIServer": "",
|
||||
"tgBotChatId": "",
|
||||
"tgRunTime": "@daily",
|
||||
"tgBotBackup": "false",
|
||||
"tgBotLoginNotify": "true",
|
||||
"tgCpu": "80",
|
||||
"tgLang": "en-US",
|
||||
"secretEnable": "false",
|
||||
"subEnable": "false",
|
||||
"subListen": "",
|
||||
"subPort": "2096",
|
||||
"subPath": "/sub/",
|
||||
"subDomain": "",
|
||||
"subCertFile": "",
|
||||
"subKeyFile": "",
|
||||
"subUpdates": "12",
|
||||
"subEncrypt": "true",
|
||||
"subShowInfo": "true",
|
||||
"subURI": "",
|
||||
"subJsonPath": "/json/",
|
||||
"subJsonURI": "",
|
||||
"subJsonFragment": "",
|
||||
"subJsonNoises": "",
|
||||
"subJsonMux": "",
|
||||
"subJsonRules": "",
|
||||
"datepicker": "gregorian",
|
||||
"warp": "",
|
||||
"externalTrafficInformEnable": "false",
|
||||
"externalTrafficInformURI": "",
|
||||
}
|
||||
|
||||
type SettingService struct{}
|
||||
@@ -496,6 +498,22 @@ func (s *SettingService) SetWarp(data string) error {
|
||||
return s.setString("warp", data)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetExternalTrafficInformEnable() (bool, error) {
|
||||
return s.getBool("externalTrafficInformEnable")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetExternalTrafficInformEnable(value bool) error {
|
||||
return s.setBool("externalTrafficInformEnable", value)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetExternalTrafficInformURI() (string, error) {
|
||||
return s.getString("externalTrafficInformURI")
|
||||
}
|
||||
|
||||
func (s *SettingService) SetExternalTrafficInformURI(InformURI string) error {
|
||||
return s.setString("externalTrafficInformURI", InformURI)
|
||||
}
|
||||
|
||||
func (s *SettingService) GetIpLimitEnable() (bool, error) {
|
||||
accessLogPath, err := xray.GetAccessLogPath()
|
||||
if err != nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
"x-ui/logger"
|
||||
"x-ui/util/common"
|
||||
)
|
||||
|
||||
type WarpService struct {
|
||||
@@ -150,13 +151,23 @@ func (s *WarpService) SetWarpLicense(license string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.Unmarshal(buffer.Bytes(), &response)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if response["success"] == false {
|
||||
errorArr, _ := response["errors"].([]interface{})
|
||||
errorObj := errorArr[0].(map[string]interface{})
|
||||
return "", common.NewError(errorObj["code"], errorObj["message"])
|
||||
}
|
||||
|
||||
warpData["license_key"] = license
|
||||
newWarpData, err := json.MarshalIndent(warpData, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s.SettingService.SetWarp(string(newWarpData))
|
||||
println(string(newWarpData))
|
||||
|
||||
return string(newWarpData), nil
|
||||
}
|
||||
|
||||
@@ -10,38 +10,41 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
loginUser = "LOGIN_USER"
|
||||
defaultPath = "/"
|
||||
loginUserKey = "LOGIN_USER"
|
||||
defaultPath = "/"
|
||||
)
|
||||
|
||||
func init() {
|
||||
gob.Register(model.User{})
|
||||
}
|
||||
|
||||
func SetLoginUser(c *gin.Context, user *model.User) error {
|
||||
func SetLoginUser(c *gin.Context, user *model.User) {
|
||||
if user == nil {
|
||||
return
|
||||
}
|
||||
s := sessions.Default(c)
|
||||
s.Set(loginUser, user)
|
||||
return s.Save()
|
||||
s.Set(loginUserKey, *user)
|
||||
}
|
||||
|
||||
func SetMaxAge(c *gin.Context, maxAge int) error {
|
||||
func SetMaxAge(c *gin.Context, maxAge int) {
|
||||
s := sessions.Default(c)
|
||||
s.Options(sessions.Options{
|
||||
Path: defaultPath,
|
||||
MaxAge: maxAge,
|
||||
HttpOnly: true,
|
||||
})
|
||||
return s.Save()
|
||||
}
|
||||
|
||||
func GetLoginUser(c *gin.Context) *model.User {
|
||||
s := sessions.Default(c)
|
||||
obj := s.Get(loginUser)
|
||||
obj := s.Get(loginUserKey)
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
user, ok := obj.(model.User)
|
||||
if !ok {
|
||||
|
||||
s.Delete(loginUserKey)
|
||||
return nil
|
||||
}
|
||||
return &user
|
||||
@@ -51,7 +54,7 @@ func IsLogin(c *gin.Context) bool {
|
||||
return GetLoginUser(c) != nil
|
||||
}
|
||||
|
||||
func ClearSession(c *gin.Context) error {
|
||||
func ClearSession(c *gin.Context) {
|
||||
s := sessions.Default(c)
|
||||
s.Clear()
|
||||
s.Options(sessions.Options{
|
||||
@@ -59,5 +62,4 @@ func ClearSession(c *gin.Context) error {
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
})
|
||||
return s.Save()
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "Listen IP"
|
||||
"certificate" = "Digital Certificate"
|
||||
"fail" = "Failed"
|
||||
"comment" = "Comment"
|
||||
"success" = "Successfully"
|
||||
"getVersion" = "Get Version"
|
||||
"install" = "Install"
|
||||
@@ -306,6 +307,10 @@
|
||||
"subShowInfoDesc" = "The remaining traffic and date will be displayed in the client apps."
|
||||
"subURI" = "Reverse Proxy URI"
|
||||
"subURIDesc" = "The URI path of the subscription URL for use behind proxies."
|
||||
"externalTrafficInformEnable" = "External Traffic Inform"
|
||||
"externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
|
||||
"externalTrafficInformURI" = "External Traffic Inform URI"
|
||||
"externalTrafficInformURIDesc" = "Traffic updates are sent to this URI."
|
||||
"fragment" = "Fragmentation"
|
||||
"fragmentDesc" = "Enable fragmentation for TLS hello packet."
|
||||
"fragmentSett" = "Fragmentation Settings"
|
||||
@@ -369,6 +374,15 @@
|
||||
"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."
|
||||
"statistics" = "Statistics"
|
||||
"statsInboundUplink" = "Inbound Upload Statistics"
|
||||
"statsInboundUplinkDesc" = "Enables the statistics collection for upstream traffic of all inbound proxies."
|
||||
"statsInboundDownlink" = "Inbound Download Statistics"
|
||||
"statsInboundDownlinkDesc" = "Enables the statistics collection for downstream traffic of all inbound proxies."
|
||||
"statsOutboundUplink" = "Outbound Upload Statistics"
|
||||
"statsOutboundUplinkDesc" = "Enables the statistics collection for upstream traffic of all outbound proxies."
|
||||
"statsOutboundDownlink" = "Outbound Download Statistics"
|
||||
"statsOutboundDownlinkDesc" = "Enables the statistics collection for downstream traffic of all outbound proxies."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "First"
|
||||
@@ -426,6 +440,14 @@
|
||||
"enableDesc" = "Enable built-in DNS server"
|
||||
"tag" = "DNS Inbound Tag"
|
||||
"tagDesc" = "This tag will be available as an Inbound tag in routing rules."
|
||||
"clientIp" = "Client IP"
|
||||
"clientIpDesc" = "Used to notify the server of the specified IP location during DNS queries"
|
||||
"disableCache" = "Disable cache"
|
||||
"disableCacheDesc" = "Disables DNS caching"
|
||||
"disableFallback" = "Disable Fallback"
|
||||
"disableFallbackDesc" = "Disables fallback DNS queries"
|
||||
"disableFallbackIfMatch" = "Disable Fallback If Match"
|
||||
"disableFallbackIfMatchDesc" = "Disables fallback DNS queries when the matching domain list of the DNS server is hit"
|
||||
"strategy" = "Query Strategy"
|
||||
"strategyDesc" = "Overall strategy to resolve domain names"
|
||||
"add" = "Add Server"
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "Listening IP"
|
||||
"certificate" = "Certificado Digital"
|
||||
"fail" = "Falló"
|
||||
"comment" = "Comentario"
|
||||
"success" = "Éxito"
|
||||
"getVersion" = "Obtener versión"
|
||||
"install" = "Instalar"
|
||||
@@ -305,6 +306,10 @@
|
||||
"subShowInfo" = "Mostrar información de uso"
|
||||
"subShowInfoDesc" = "Mostrar tráfico restante y fecha después del nombre de configuración."
|
||||
"subURI" = "URI de proxy inverso"
|
||||
"externalTrafficInformEnable" = "Informe de tráfico externo"
|
||||
"externalTrafficInformEnableDesc" = "Informar a la API externa sobre cada actualización de tráfico."
|
||||
"externalTrafficInformURI" = "URI de información de tráfico externo"
|
||||
"externalTrafficInformURIDesc" = "Las actualizaciones de tráfico se envían a este URI."
|
||||
"subURIDesc" = "Cambiar el URI base de la URL de suscripción para usar detrás de los servidores proxy"
|
||||
"fragment" = "Fragmentación"
|
||||
"fragmentDesc" = "Habilitar la fragmentación para el paquete de saludo de TLS"
|
||||
@@ -369,6 +374,15 @@
|
||||
"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."
|
||||
"statistics" = "Estadísticas"
|
||||
"statsInboundUplink" = "Estadísticas de Subida de Entrada"
|
||||
"statsInboundUplinkDesc" = "Habilita la recopilación de estadísticas para el tráfico ascendente de todos los proxies de entrada."
|
||||
"statsInboundDownlink" = "Estadísticas de Bajada de Entrada"
|
||||
"statsInboundDownlinkDesc" = "Habilita la recopilación de estadísticas para el tráfico descendente de todos los proxies de entrada."
|
||||
"statsOutboundUplink" = "Estadísticas de Subida de Salida"
|
||||
"statsOutboundUplinkDesc" = "Habilita la recopilación de estadísticas para el tráfico ascendente de todos los proxies de salida."
|
||||
"statsOutboundDownlink" = "Estadísticas de Bajada de Salida"
|
||||
"statsOutboundDownlinkDesc" = "Habilita la recopilación de estadísticas para el tráfico descendente de todos los proxies de salida."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Primero"
|
||||
@@ -426,6 +440,14 @@
|
||||
"enableDesc" = "Habilitar servidor DNS incorporado"
|
||||
"tag" = "Etiqueta de Entrada DNS"
|
||||
"tagDesc" = "Esta etiqueta estará disponible como una etiqueta de entrada en las reglas de enrutamiento."
|
||||
"clientIp" = "IP del cliente"
|
||||
"clientIpDesc" = "Se utiliza para notificar al servidor la ubicación IP especificada durante las consultas DNS"
|
||||
"disableCache" = "Desactivar caché"
|
||||
"disableCacheDesc" = "Desactiva el almacenamiento en caché de DNS"
|
||||
"disableFallback" = "Desactivar respaldo"
|
||||
"disableFallbackDesc" = "Desactiva las consultas DNS de respaldo"
|
||||
"disableFallbackIfMatch" = "Desactivar respaldo si coincide"
|
||||
"disableFallbackIfMatchDesc" = "Desactiva las consultas DNS de respaldo cuando se acierta en la lista de dominios coincidentes del servidor DNS"
|
||||
"strategy" = "Estrategia de Consulta"
|
||||
"strategyDesc" = "Estrategia general para resolver nombres de dominio"
|
||||
"add" = "Agregar Servidor"
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "آیپی اتصال"
|
||||
"certificate" = "گواهی دیجیتال"
|
||||
"fail" = "ناموفق"
|
||||
"comment" = "توضیحات"
|
||||
"success" = "موفق"
|
||||
"getVersion" = "دریافت نسخه"
|
||||
"install" = "نصب"
|
||||
@@ -300,6 +301,10 @@
|
||||
"subDomainDesc" = "آدرس دامنه برای سرویس سابسکریپشن. برای گوش دادن به تمام دامنهها و آیپیها خالیبگذارید"
|
||||
"subUpdates" = "فاصله بروزرسانی سابسکریپشن"
|
||||
"subUpdatesDesc" = "(فاصله مابین بروزرسانی در برنامههای کاربری. (واحد: ساعت"
|
||||
"externalTrafficInformEnable" = "اطلاع رسانی خارجی مصرف ترافیک"
|
||||
"externalTrafficInformEnableDesc" = "مصرف ترافیک به سرویس خارجی ارسال می شود"
|
||||
"externalTrafficInformURI" = "لینک اطلاع رسانی خارجی مصرف ترافیک"
|
||||
"externalTrafficInformURIDesc" = "ترافیک های مصرفی به این لینک هم ارسال می شود"
|
||||
"subEncrypt" = "کدگذاری"
|
||||
"subEncryptDesc" = "کدگذاری خواهدشد Base64 محتوای برگشتی سرویس سابسکریپشن برپایه"
|
||||
"subShowInfo" = "نمایش اطلاعات مصرف"
|
||||
@@ -369,6 +374,15 @@
|
||||
"dnsLogDesc" = "آیا ثبتهای درخواست DNS را فعال کنید"
|
||||
"maskAddress" = "پنهان کردن آدرس"
|
||||
"maskAddressDesc" = "پوشش آدرس IP، هنگامی که فعال میشود، به طور خودکار آدرس IP که در لاگ ظاهر میشود را جایگزین میکند."
|
||||
"statistics" = "آمار"
|
||||
"statsInboundUplink" = "آمار آپلود ورودی"
|
||||
"statsInboundUplinkDesc" = "جمعآوری آمار برای ترافیک بالارو (آپلود) تمام پروکسیهای ورودی را فعال میکند."
|
||||
"statsInboundDownlink" = "آمار دانلود ورودی"
|
||||
"statsInboundDownlinkDesc" = "جمعآوری آمار برای ترافیک پایینرو (دانلود) تمام پروکسیهای ورودی را فعال میکند."
|
||||
"statsOutboundUplink" = "آمار آپلود خروجی"
|
||||
"statsOutboundUplinkDesc" = "جمعآوری آمار برای ترافیک بالارو (آپلود) تمام پروکسیهای خروجی را فعال میکند."
|
||||
"statsOutboundDownlink" = "آمار دانلود خروجی"
|
||||
"statsOutboundDownlinkDesc" = "جمعآوری آمار برای ترافیک پایینرو (دانلود) تمام پروکسیهای خروجی را فعال میکند."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "اولین"
|
||||
@@ -426,6 +440,14 @@
|
||||
"enableDesc" = "سرور حل دامنه داخلی را فعال کنید"
|
||||
"tag" = "برچسب"
|
||||
"tagDesc" = "این برچسب در قوانین مسیریابی به عنوان یک برچسب ورودی قابل استفاده خواهد بود"
|
||||
"clientIp" = "آیپی کلاینت"
|
||||
"clientIpDesc" = "برای اطلاعرسانی به سرور درباره مکان IP مشخصشده در طول درخواستهای DNS استفاده میشود"
|
||||
"disableCache" = "غیرفعالسازی کش"
|
||||
"disableCacheDesc" = "کش DNS را غیرفعال میکند"
|
||||
"disableFallback" = "غیرفعالسازی Fallback"
|
||||
"disableFallbackDesc" = "درخواستهای DNS Fallback را غیرفعال میکند"
|
||||
"disableFallbackIfMatch" = "غیرفعالسازی Fallback در صورت تطابق"
|
||||
"disableFallbackIfMatchDesc" = "درخواستهای DNS Fallback را زمانی که لیست دامنههای مطابقتیافته سرور DNS فعال است، غیرفعال میکند"
|
||||
"strategy" = "استراتژی پرسوجو"
|
||||
"strategyDesc" = "استراتژی کلی برای حل نام دامنه"
|
||||
"add" = "افزودن سرور"
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "IP Pemantauan"
|
||||
"certificate" = "Sertifikat Digital"
|
||||
"fail" = "Gagal"
|
||||
"comment" = "Komentar"
|
||||
"success" = "Berhasil"
|
||||
"getVersion" = "Dapatkan Versi"
|
||||
"install" = "Instal"
|
||||
@@ -305,7 +306,10 @@
|
||||
"subShowInfo" = "Tampilkan Info Penggunaan"
|
||||
"subShowInfoDesc" = "Sisa traffic dan tanggal akan ditampilkan di aplikasi klien."
|
||||
"subURI" = "URI Proxy Terbalik"
|
||||
"subURIDesc" = "URI path URL langganan untuk penggunaan di belakang proxy."
|
||||
"externalTrafficInformEnable" = "Informasikan API eksternal pada setiap pembaruan lalu lintas."
|
||||
"externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
|
||||
"externalTrafficInformURI" = "Lalu Lintas Eksternal Menginformasikan URI"
|
||||
"externalTrafficInformURIDesc" = "Pembaruan lalu lintas dikirim ke URI ini."
|
||||
"fragment" = "Fragmentasi"
|
||||
"fragmentDesc" = "Aktifkan fragmentasi untuk paket hello TLS"
|
||||
"fragmentSett" = "Pengaturan Fragmentasi"
|
||||
@@ -369,6 +373,15 @@
|
||||
"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."
|
||||
"statistics" = "Statistik"
|
||||
"statsInboundUplink" = "Statistik Unggah Masuk"
|
||||
"statsInboundUplinkDesc" = "Mengaktifkan pengumpulan statistik untuk lalu lintas unggah dari semua proxy masuk."
|
||||
"statsInboundDownlink" = "Statistik Unduh Masuk"
|
||||
"statsInboundDownlinkDesc" = "Mengaktifkan pengumpulan statistik untuk lalu lintas unduh dari semua proxy masuk."
|
||||
"statsOutboundUplink" = "Statistik Unggah Keluar"
|
||||
"statsOutboundUplinkDesc" = "Mengaktifkan pengumpulan statistik untuk lalu lintas unggah dari semua proxy keluar."
|
||||
"statsOutboundDownlink" = "Statistik Unduh Keluar"
|
||||
"statsOutboundDownlinkDesc" = "Mengaktifkan pengumpulan statistik untuk lalu lintas unduh dari semua proxy keluar."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Pertama"
|
||||
@@ -426,6 +439,14 @@
|
||||
"enableDesc" = "Aktifkan server DNS bawaan"
|
||||
"tag" = "Tanda DNS Masuk"
|
||||
"tagDesc" = "Tanda ini akan tersedia sebagai tanda masuk dalam aturan penataan."
|
||||
"clientIp" = "IP Klien"
|
||||
"clientIpDesc" = "Digunakan untuk memberi tahu server tentang lokasi IP yang ditentukan selama kueri DNS"
|
||||
"disableCache" = "Nonaktifkan cache"
|
||||
"disableCacheDesc" = "Menonaktifkan caching DNS"
|
||||
"disableFallback" = "Nonaktifkan Fallback"
|
||||
"disableFallbackDesc" = "Menonaktifkan kueri DNS fallback"
|
||||
"disableFallbackIfMatch" = "Nonaktifkan Fallback Jika Cocok"
|
||||
"disableFallbackIfMatchDesc" = "Menonaktifkan kueri DNS fallback ketika daftar domain yang cocok dari server DNS terpenuhi"
|
||||
"strategy" = "Strategi Kueri"
|
||||
"strategyDesc" = "Strategi keseluruhan untuk menyelesaikan nama domain"
|
||||
"add" = "Tambahkan Server"
|
||||
|
||||
617
web/translation/translate.ja_JP.toml
Normal file
@@ -0,0 +1,617 @@
|
||||
"username" = "ユーザー名"
|
||||
"password" = "パスワード"
|
||||
"login" = "ログイン"
|
||||
"confirm" = "確認"
|
||||
"cancel" = "キャンセル"
|
||||
"close" = "閉じる"
|
||||
"copy" = "コピー"
|
||||
"copied" = "コピー済み"
|
||||
"download" = "ダウンロード"
|
||||
"remark" = "備考"
|
||||
"enable" = "有効化"
|
||||
"protocol" = "プロトコル"
|
||||
"search" = "検索"
|
||||
"filter" = "フィルター"
|
||||
"loading" = "読み込み中..."
|
||||
"second" = "秒"
|
||||
"minute" = "分"
|
||||
"hour" = "時間"
|
||||
"day" = "日"
|
||||
"check" = "確認"
|
||||
"indefinite" = "無期限"
|
||||
"unlimited" = "無制限"
|
||||
"none" = "なし"
|
||||
"qrCode" = "QRコード"
|
||||
"info" = "詳細情報"
|
||||
"edit" = "編集"
|
||||
"delete" = "削除"
|
||||
"reset" = "リセット"
|
||||
"copySuccess" = "コピー成功"
|
||||
"sure" = "確定"
|
||||
"encryption" = "暗号化"
|
||||
"transmission" = "伝送"
|
||||
"host" = "ホスト"
|
||||
"path" = "パス"
|
||||
"camouflage" = "偽装"
|
||||
"status" = "ステータス"
|
||||
"enabled" = "有効"
|
||||
"disabled" = "無効"
|
||||
"depleted" = "消耗済み"
|
||||
"depletingSoon" = "間もなく消耗"
|
||||
"offline" = "オフライン"
|
||||
"online" = "オンライン"
|
||||
"domainName" = "ドメイン名"
|
||||
"monitor" = "監視"
|
||||
"certificate" = "証明書"
|
||||
"fail" = "失敗"
|
||||
"comment" = "コメント"
|
||||
"success" = "成功"
|
||||
"getVersion" = "バージョン取得"
|
||||
"install" = "インストール"
|
||||
"clients" = "クライアント"
|
||||
"usage" = "利用状況"
|
||||
"secretToken" = "シークレットトークン"
|
||||
"remained" = "残り"
|
||||
"security" = "セキュリティ"
|
||||
"secAlertTitle" = "セキュリティアラート"
|
||||
"secAlertSsl" = "この接続は安全ではありません。TLSを有効にしてデータ保護を行うまで、機密情報を入力しないでください。"
|
||||
"secAlertConf" = "一部の設定は脆弱です。潜在的な脆弱性を防ぐために、セキュリティプロトコルを強化することをお勧めします。"
|
||||
"secAlertSSL" = "セキュアな接続がありません。データ保護のためにTLS証明書をインストールしてください。"
|
||||
"secAlertPanelPort" = "デフォルトのポートにはセキュリティリスクがあります。ランダムなポートまたは特定のポートを設定してください。"
|
||||
"secAlertPanelURI" = "デフォルトのURIパスは安全ではありません。複雑なURIパスを設定してください。"
|
||||
"secAlertSubURI" = "サブスクリプションのデフォルトURIパスは安全ではありません。複雑なURIパスを設定してください。"
|
||||
"secAlertSubJsonURI" = "JSONサブスクリプションのデフォルトURIパスは安全ではありません。複雑なURIパスを設定してください。"
|
||||
|
||||
[menu]
|
||||
"dashboard" = "ダッシュボード"
|
||||
"inbounds" = "インバウンド一覧"
|
||||
"settings" = "パネル設定"
|
||||
"xray" = "Xray設定"
|
||||
"logout" = "ログアウト"
|
||||
"link" = "リンク管理"
|
||||
|
||||
[pages.login]
|
||||
"hello" = "こんにちは"
|
||||
"title" = "ようこそ"
|
||||
"loginAgain" = "ログインセッションが切れました。再度ログインしてください。"
|
||||
|
||||
[pages.login.toasts]
|
||||
"invalidFormData" = "データ形式エラー"
|
||||
"emptyUsername" = "ユーザー名を入力してください"
|
||||
"emptyPassword" = "パスワードを入力してください"
|
||||
"wrongUsernameOrPassword" = "ユーザー名またはパスワードが間違っています"
|
||||
"successLogin" = "ログイン成功"
|
||||
|
||||
[pages.index]
|
||||
"title" = "システムステータス"
|
||||
"memory" = "メモリ"
|
||||
"hard" = "ハードディスク"
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "停止"
|
||||
"restartXray" = "再起動"
|
||||
"xraySwitch" = "バージョン"
|
||||
"xraySwitchClick" = "切り替えるバージョンを選択してください"
|
||||
"xraySwitchClickDesk" = "慎重に選択してください。古いバージョンは現在の設定と互換性がない可能性があります。"
|
||||
"operationHours" = "システム稼働時間"
|
||||
"systemLoad" = "システム負荷"
|
||||
"systemLoadDesc" = "過去1、5、15分間のシステム平均負荷"
|
||||
"connectionTcpCountDesc" = "システム内のすべてのTCP接続数"
|
||||
"connectionUdpCountDesc" = "システム内のすべてのUDP接続数"
|
||||
"connectionCount" = "接続数"
|
||||
"upSpeed" = "総アップロード速度"
|
||||
"downSpeed" = "総ダウンロード速度"
|
||||
"totalSent" = "システム起動以降の送信データ量"
|
||||
"totalReceive" = "システム起動以降の受信データ量"
|
||||
"xraySwitchVersionDialog" = "Xrayバージョン切り替え"
|
||||
"xraySwitchVersionDialogDesc" = "Xrayのバージョンを切り替えますか?"
|
||||
"dontRefresh" = "インストール中、このページをリロードしないでください"
|
||||
"logs" = "ログ"
|
||||
"config" = "設定"
|
||||
"backup" = "バックアップと復元"
|
||||
"backupTitle" = "データベースのバックアップと復元"
|
||||
"backupDescription" = "データベースを復元する前にバックアップすることをお勧めします"
|
||||
"exportDatabase" = "バックアップ"
|
||||
"importDatabase" = "復元"
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "インバウンド一覧"
|
||||
"totalDownUp" = "総アップロード / ダウンロード"
|
||||
"totalUsage" = "総使用量"
|
||||
"inboundCount" = "インバウンド数"
|
||||
"operate" = "メニュー"
|
||||
"enable" = "有効化"
|
||||
"remark" = "備考"
|
||||
"protocol" = "プロトコル"
|
||||
"port" = "ポート"
|
||||
"traffic" = "トラフィック"
|
||||
"details" = "詳細情報"
|
||||
"transportConfig" = "トランスポート設定"
|
||||
"expireDate" = "有効期限"
|
||||
"resetTraffic" = "トラフィックリセット"
|
||||
"addInbound" = "インバウンド追加"
|
||||
"generalActions" = "一般操作"
|
||||
"create" = "追加"
|
||||
"update" = "更新"
|
||||
"modifyInbound" = "インバウンド修正"
|
||||
"deleteInbound" = "インバウンド削除"
|
||||
"deleteInboundContent" = "インバウンドを削除してもよろしいですか?"
|
||||
"deleteClient" = "クライアント削除"
|
||||
"deleteClientContent" = "クライアントを削除してもよろしいですか?"
|
||||
"resetTrafficContent" = "トラフィックをリセットしてもよろしいですか?"
|
||||
"copyLink" = "リンクをコピー"
|
||||
"address" = "アドレス"
|
||||
"network" = "ネットワーク"
|
||||
"destinationPort" = "宛先ポート"
|
||||
"targetAddress" = "宛先アドレス"
|
||||
"monitorDesc" = "空白にするとすべてのIPを監視"
|
||||
"meansNoLimit" = "= 無制限(単位:GB)"
|
||||
"totalFlow" = "総トラフィック"
|
||||
"leaveBlankToNeverExpire" = "空白にすると期限なし"
|
||||
"noRecommendKeepDefault" = "デフォルト値を保持することをお勧めします"
|
||||
"certificatePath" = "ファイルパス"
|
||||
"certificateContent" = "ファイル内容"
|
||||
"publicKey" = "公開鍵"
|
||||
"privatekey" = "秘密鍵"
|
||||
"clickOnQRcode" = "QRコードをクリックしてコピー"
|
||||
"client" = "クライアント"
|
||||
"export" = "リンクエクスポート"
|
||||
"clone" = "複製"
|
||||
"cloneInbound" = "複製"
|
||||
"cloneInboundContent" = "このインバウンドルールは、ポート(Port)、リスニングIP(Listening IP)、クライアント(Clients)を除くすべての設定がクローンされます"
|
||||
"cloneInboundOk" = "クローン作成"
|
||||
"resetAllTraffic" = "すべてのインバウンドトラフィックをリセット"
|
||||
"resetAllTrafficTitle" = "すべてのインバウンドトラフィックをリセット"
|
||||
"resetAllTrafficContent" = "すべてのインバウンドトラフィックをリセットしてもよろしいですか?"
|
||||
"resetInboundClientTraffics" = "クライアントトラフィックをリセット"
|
||||
"resetInboundClientTrafficTitle" = "すべてのクライアントトラフィックをリセット"
|
||||
"resetInboundClientTrafficContent" = "このインバウンドクライアントのすべてのトラフィックをリセットしてもよろしいですか?"
|
||||
"resetAllClientTraffics" = "すべてのクライアントトラフィックをリセット"
|
||||
"resetAllClientTrafficTitle" = "すべてのクライアントトラフィックをリセット"
|
||||
"resetAllClientTrafficContent" = "すべてのクライアントのトラフィックをリセットしてもよろしいですか?"
|
||||
"delDepletedClients" = "トラフィックが尽きたクライアントを削除"
|
||||
"delDepletedClientsTitle" = "トラフィックが尽きたクライアントを削除"
|
||||
"delDepletedClientsContent" = "トラフィックが尽きたすべてのクライアントを削除してもよろしいですか?"
|
||||
"email" = "メールアドレス"
|
||||
"emailDesc" = "メールアドレスは一意でなければなりません"
|
||||
"IPLimit" = "IP制限"
|
||||
"IPLimitDesc" = "設定値を超えるとインバウンドトラフィックが無効になります。(0 = 無効)"
|
||||
"IPLimitlog" = "IPログ"
|
||||
"IPLimitlogDesc" = "IP履歴ログ(無効なインバウンドトラフィックを有効にするには、ログをクリアしてください)"
|
||||
"IPLimitlogclear" = "ログをクリア"
|
||||
"setDefaultCert" = "パネル設定から証明書を設定"
|
||||
"telegramDesc" = "TelegramチャットIDを提供してください。(ボットで'/id'コマンドを使用)または(@userinfobot)"
|
||||
"subscriptionDesc" = "サブスクリプションURLを見つけるには、“詳細情報”に移動してください。また、複数のクライアントに同じ名前を使用することができます。"
|
||||
"info" = "情報"
|
||||
"same" = "同じ"
|
||||
"inboundData" = "インバウンドデータ"
|
||||
"exportInbound" = "インバウンドルールをエクスポート"
|
||||
"import" = "インポート"
|
||||
"importInbound" = "インバウンドルールをインポート"
|
||||
|
||||
[pages.client]
|
||||
"add" = "クライアント追加"
|
||||
"edit" = "クライアント編集"
|
||||
"submitAdd" = "クライアント追加"
|
||||
"submitEdit" = "変更を保存"
|
||||
"clientCount" = "クライアント数"
|
||||
"bulk" = "一括作成"
|
||||
"method" = "方法"
|
||||
"first" = "最初"
|
||||
"last" = "最後"
|
||||
"prefix" = "プレフィックス"
|
||||
"postfix" = "サフィックス"
|
||||
"delayedStart" = "初回使用後に開始"
|
||||
"expireDays" = "期間"
|
||||
"days" = "日"
|
||||
"renew" = "自動更新"
|
||||
"renewDesc" = "期限が切れた後に自動更新。(0 = 無効)(単位:日)"
|
||||
|
||||
[pages.inbounds.toasts]
|
||||
"obtain" = "取得"
|
||||
|
||||
[pages.inbounds.stream.general]
|
||||
"request" = "リクエスト"
|
||||
"response" = "レスポンス"
|
||||
"name" = "名前"
|
||||
"value" = "値"
|
||||
|
||||
[pages.inbounds.stream.tcp]
|
||||
"version" = "バージョン"
|
||||
"method" = "方法"
|
||||
"path" = "パス"
|
||||
"status" = "ステータス"
|
||||
"statusDescription" = "ステータス説明"
|
||||
"requestHeader" = "リクエストヘッダー"
|
||||
"responseHeader" = "レスポンスヘッダー"
|
||||
|
||||
[pages.settings]
|
||||
"title" = "パネル設定"
|
||||
"save" = "保存"
|
||||
"infoDesc" = "ここでのすべての変更は、保存してパネルを再起動する必要があります"
|
||||
"restartPanel" = "パネル再起動"
|
||||
"restartPanelDesc" = "パネルを再起動してもよろしいですか?再起動後にパネルにアクセスできない場合は、サーバーでパネルログを確認してください"
|
||||
"actions" = "操作"
|
||||
"resetDefaultConfig" = "デフォルト設定にリセット"
|
||||
"panelSettings" = "一般"
|
||||
"securitySettings" = "セキュリティ設定"
|
||||
"TGBotSettings" = "Telegramボット設定"
|
||||
"panelListeningIP" = "パネル監視IP"
|
||||
"panelListeningIPDesc" = "デフォルトではすべてのIPを監視する"
|
||||
"panelListeningDomain" = "パネル監視ドメイン"
|
||||
"panelListeningDomainDesc" = "デフォルトで空白の場合、すべてのドメインとIPアドレスを監視する"
|
||||
"panelPort" = "パネル監視ポート"
|
||||
"panelPortDesc" = "再起動で有効"
|
||||
"publicKeyPath" = "パネル証明書公開鍵ファイルパス"
|
||||
"publicKeyPathDesc" = "'/'で始まる絶対パスを入力"
|
||||
"privateKeyPath" = "パネル証明書秘密鍵ファイルパス"
|
||||
"privateKeyPathDesc" = "'/'で始まる絶対パスを入力"
|
||||
"panelUrlPath" = "パネルURLルートパス"
|
||||
"panelUrlPathDesc" = "'/'で始まり、'/'で終わる必要があります"
|
||||
"pageSize" = "ページサイズ"
|
||||
"pageSizeDesc" = "インバウンドテーブルのページサイズを定義します。0を設定すると無効化されます"
|
||||
"remarkModel" = "備考モデルと区切り記号"
|
||||
"datepicker" = "日付ピッカー"
|
||||
"datepickerPlaceholder" = "日付を選択"
|
||||
"datepickerDescription" = "日付選択カレンダーで有効期限を指定する"
|
||||
"sampleRemark" = "備考の例"
|
||||
"oldUsername" = "旧ユーザー名"
|
||||
"currentPassword" = "旧パスワード"
|
||||
"newUsername" = "新しいユーザー名"
|
||||
"newPassword" = "新しいパスワード"
|
||||
"telegramBotEnable" = "Telegramボットを有効にする"
|
||||
"telegramBotEnableDesc" = "Telegramボット機能を有効にする"
|
||||
"telegramToken" = "Telegramボットトークン"
|
||||
"telegramTokenDesc" = "'@BotFather'から取得したTelegramボットトークン"
|
||||
"telegramProxy" = "SOCKS5プロキシ"
|
||||
"telegramProxyDesc" = "SOCKS5プロキシを有効にしてTelegramに接続する(ガイドに従って設定を調整)"
|
||||
"telegramAPIServer" = "Telegram APIサーバー"
|
||||
"telegramAPIServerDesc" = "使用するTelegram APIサーバー。空白の場合はデフォルトサーバーを使用する"
|
||||
"telegramChatId" = "管理者チャットID"
|
||||
"telegramChatIdDesc" = "Telegram管理者チャットID(複数の場合はカンマで区切る)@userinfobotで取得するか、ボットで'/id'コマンドを使用して取得する"
|
||||
"telegramNotifyTime" = "通知時間"
|
||||
"telegramNotifyTimeDesc" = "定期的なTelegramボット通知時間を設定する(crontab時間形式を使用)"
|
||||
"tgNotifyBackup" = "データベースバックアップ"
|
||||
"tgNotifyBackupDesc" = "レポート付きのデータベースバックアップファイルを送信"
|
||||
"tgNotifyLogin" = "ログイン通知"
|
||||
"tgNotifyLoginDesc" = "誰かがパネルにログインしようとしたときに、ユーザー名、IPアドレス、時間を表示する"
|
||||
"sessionMaxAge" = "セッション期間"
|
||||
"sessionMaxAgeDesc" = "ログイン状態を保持する期間(単位:分)"
|
||||
"expireTimeDiff" = "有効期限通知のしきい値"
|
||||
"expireTimeDiffDesc" = "このしきい値に達した場合、有効期限に関する通知を受け取る(単位:日)"
|
||||
"trafficDiff" = "トラフィック消耗しきい値"
|
||||
"trafficDiffDesc" = "このしきい値に達した場合、トラフィック消耗に関する通知を受け取る(単位:GB)"
|
||||
"tgNotifyCpu" = "CPU負荷通知しきい値"
|
||||
"tgNotifyCpuDesc" = "CPU負荷がこのしきい値を超えた場合、通知を受け取る(単位:%)"
|
||||
"timeZone" = "タイムゾーン"
|
||||
"timeZoneDesc" = "定時タスクはこのタイムゾーンの時間に従って実行される"
|
||||
"subSettings" = "サブスクリプション設定"
|
||||
"subEnable" = "サブスクリプションサービスを有効にする"
|
||||
"subEnableDesc" = "サブスクリプションサービス機能を有効にする"
|
||||
"subListen" = "監視IP"
|
||||
"subListenDesc" = "サブスクリプションサービスが監視するIPアドレス(空白にするとすべてのIPを監視)"
|
||||
"subPort" = "監視ポート"
|
||||
"subPortDesc" = "サブスクリプションサービスが監視するポート番号(使用されていないポートである必要があります)"
|
||||
"subCertPath" = "公開鍵パス"
|
||||
"subCertPathDesc" = "サブスクリプションサービスで使用する公開鍵ファイルのパス('/'で始まる)"
|
||||
"subKeyPath" = "秘密鍵パス"
|
||||
"subKeyPathDesc" = "サブスクリプションサービスで使用する秘密鍵ファイルのパス('/'で始まる)"
|
||||
"subPath" = "URIパス"
|
||||
"subPathDesc" = "サブスクリプションサービスで使用するURIパス('/'で始まり、'/'で終わる)"
|
||||
"subDomain" = "監視ドメイン"
|
||||
"subDomainDesc" = "サブスクリプションサービスが監視するドメイン(空白にするとすべてのドメインとIPを監視)"
|
||||
"subUpdates" = "更新間隔"
|
||||
"subUpdatesDesc" = "クライアントアプリケーションでサブスクリプションURLの更新間隔(単位:時間)"
|
||||
"subEncrypt" = "エンコード"
|
||||
"subEncryptDesc" = "サブスクリプションサービスが返す内容をBase64エンコードする"
|
||||
"subShowInfo" = "利用情報を表示"
|
||||
"subShowInfoDesc" = "クライアントアプリで残りのトラフィックと日付情報を表示する"
|
||||
"subURI" = "リバースプロキシURI"
|
||||
"subURIDesc" = "プロキシ後ろのサブスクリプションURLのURIパスに使用する"
|
||||
"externalTrafficInformEnable" = "外部トラフィック情報"
|
||||
"externalTrafficInformEnableDesc" = "トラフィックの更新ごとに外部 API に通知します。"
|
||||
"externalTrafficInformURI" = "外部トラフィック通知 URI"
|
||||
"externalTrafficInformURIDesc" = "トラフィックの更新ごとに外部 API に通知します。"
|
||||
"fragment" = "フラグメント"
|
||||
"fragmentDesc" = "TLS helloパケットのフラグメントを有効にする"
|
||||
"fragmentSett" = "設定"
|
||||
"noisesDesc" = "Noisesを有効にする"
|
||||
"noisesSett" = "Noises設定"
|
||||
"mux" = "マルチプレクサ"
|
||||
"muxDesc" = "確立されたストリーム内で複数の独立したストリームを伝送する"
|
||||
"muxSett" = "マルチプレクサ設定"
|
||||
"direct" = "直接接続"
|
||||
"directDesc" = "特定の国のドメインまたはIP範囲に直接接続する"
|
||||
|
||||
|
||||
[pages.xray]
|
||||
"title" = "Xray 設定"
|
||||
"save" = "保存"
|
||||
"restart" = "Xray 再起動"
|
||||
"basicTemplate" = "基本設定"
|
||||
"advancedTemplate" = "高度な設定"
|
||||
"generalConfigs" = "一般設定"
|
||||
"generalConfigsDesc" = "これらのオプションは一般設定を決定します"
|
||||
"logConfigs" = "ログ"
|
||||
"logConfigsDesc" = "ログはサーバーのパフォーマンスに影響を与える可能性があるため、必要な場合にのみ有効にすることをお勧めします"
|
||||
"blockConfigs" = "防御フィルター"
|
||||
"blockConfigsDesc" = "これらのオプションは、特定のプロトコルやウェブサイトへのユーザー接続をブロックします"
|
||||
"basicRouting" = "基本ルーティング"
|
||||
"blockConnectionsConfigsDesc" = "これらのオプションにより、特定のリクエスト元の国に基づいてトラフィックをブロックします。"
|
||||
"directConnectionsConfigsDesc" = "直接接続により、特定のトラフィックが他のサーバーを経由しないようにします。"
|
||||
"blockips" = "IPをブロック"
|
||||
"blockdomains" = "ドメインをブロック"
|
||||
"directips" = "直接IP"
|
||||
"directdomains" = "直接ドメイン"
|
||||
"ipv4Routing" = "IPv4 ルーティング"
|
||||
"ipv4RoutingDesc" = "このオプションはIPv4のみを介してターゲットドメインへルーティングします"
|
||||
"warpRouting" = "WARP ルーティング"
|
||||
"warpRoutingDesc" = "注意:これらのオプションを使用する前に、パネルのGitHubの手順に従って、サーバーにsocks5プロキシモードでWARPをインストールしてください。WARPはCloudflareサーバー経由でトラフィックをウェブサイトにルーティングします。"
|
||||
"Template" = "高度なXray設定テンプレート"
|
||||
"TemplateDesc" = "最終的なXray設定ファイルはこのテンプレートに基づいて生成されます"
|
||||
"FreedomStrategy" = "Freedom プロトコル戦略"
|
||||
"FreedomStrategyDesc" = "Freedomプロトコル内のネットワークの出力戦略を設定する"
|
||||
"RoutingStrategy" = "ルーティングドメイン戦略設定"
|
||||
"RoutingStrategyDesc" = "DNS解決の全体的なルーティング戦略を設定する"
|
||||
"Torrent" = "BitTorrent プロトコルをブロック"
|
||||
"TorrentDesc" = "BitTorrentの使用を禁止する"
|
||||
"Family" = "ファミリー保護"
|
||||
"FamilyDesc" = "アダルトコンテンツや悪意のあるサイトをブロックする"
|
||||
"Inbounds" = "インバウンドルール"
|
||||
"InboundsDesc" = "特定のクライアントからのトラフィックを受け入れる"
|
||||
"Outbounds" = "アウトバウンドルール"
|
||||
"Balancers" = "負荷分散"
|
||||
"OutboundsDesc" = "アウトバウンドトラフィックの送信方法を設定する"
|
||||
"Routings" = "ルーティングルール"
|
||||
"RoutingsDesc" = "各ルールの優先順位が重要です"
|
||||
"completeTemplate" = "すべて"
|
||||
"logLevel" = "ログレベル"
|
||||
"logLevelDesc" = "エラーログのレベルを指定し、記録する情報を示します"
|
||||
"accessLog" = "アクセスログ"
|
||||
"accessLogDesc" = "アクセスログのファイルパス。特殊値 'none' はアクセスログを無効にします"
|
||||
"errorLog" = "エラーログ"
|
||||
"errorLogDesc" = "エラーログのファイルパス。特殊値 'none' はエラーログを無効にします"
|
||||
"dnsLog" = "DNS ログ"
|
||||
"dnsLogDesc" = "DNSクエリのログを有効にするかどうか"
|
||||
"maskAddress" = "アドレスをマスク"
|
||||
"maskAddressDesc" = "IPアドレスをマスクし、有効にするとログに表示されるIPアドレスを自動的に置き換えます"
|
||||
"statistics" = "統計"
|
||||
"statsInboundUplink" = "インバウンドアップロード統計"
|
||||
"statsInboundUplinkDesc" = "すべてのインバウンドプロキシのアップストリームトラフィックの統計収集を有効にします。"
|
||||
"statsInboundDownlink" = "インバウンドダウンロード統計"
|
||||
"statsInboundDownlinkDesc" = "すべてのインバウンドプロキシのダウンストリームトラフィックの統計収集を有効にします。"
|
||||
"statsOutboundUplink" = "アウトバウンドアップロード統計"
|
||||
"statsOutboundUplinkDesc" = "すべてのアウトバウンドプロキシのアップストリームトラフィックの統計収集を有効にします。"
|
||||
"statsOutboundDownlink" = "アウトバウンドダウンロード統計"
|
||||
"statsOutboundDownlinkDesc" = "すべてのアウトバウンドプロキシのダウンストリームトラフィックの統計収集を有効にします。"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "最初"
|
||||
"last" = "最後"
|
||||
"up" = "上へ"
|
||||
"down" = "下へ"
|
||||
"source" = "ソース"
|
||||
"dest" = "宛先アドレス"
|
||||
"inbound" = "インバウンド"
|
||||
"outbound" = "アウトバウンド"
|
||||
"balancer" = "負荷分散"
|
||||
"info" = "情報"
|
||||
"add" = "ルール追加"
|
||||
"edit" = "ルール編集"
|
||||
"useComma" = "カンマ区切りの項目"
|
||||
|
||||
[pages.xray.outbound]
|
||||
"addOutbound" = "アウトバウンド追加"
|
||||
"addReverse" = "リバース追加"
|
||||
"editOutbound" = "アウトバウンド編集"
|
||||
"editReverse" = "リバース編集"
|
||||
"tag" = "タグ"
|
||||
"tagDesc" = "一意のタグ"
|
||||
"address" = "アドレス"
|
||||
"reverse" = "リバース"
|
||||
"domain" = "ドメイン"
|
||||
"type" = "タイプ"
|
||||
"bridge" = "ブリッジ"
|
||||
"portal" = "ポータル"
|
||||
"intercon" = "インターコネクション"
|
||||
"settings" = "設定"
|
||||
"accountInfo" = "アカウント情報"
|
||||
"outboundStatus" = "アウトバウンドステータス"
|
||||
"sendThrough" = "送信経路"
|
||||
|
||||
[pages.xray.balancer]
|
||||
"addBalancer" = "負荷分散追加"
|
||||
"editBalancer" = "負荷分散編集"
|
||||
"balancerStrategy" = "戦略"
|
||||
"balancerSelectors" = "セレクター"
|
||||
"tag" = "タグ"
|
||||
"tagDesc" = "一意のタグ"
|
||||
"balancerDesc" = "balancerTagとoutboundTagは同時に使用できません。同時に使用された場合、outboundTagのみが有効になります。"
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "シークレットキー"
|
||||
"publicKey" = "公開鍵"
|
||||
"allowedIPs" = "許可されたIP"
|
||||
"endpoint" = "エンドポイント"
|
||||
"psk" = "共有キー"
|
||||
"domainStrategy" = "ドメイン戦略"
|
||||
|
||||
[pages.xray.dns]
|
||||
"enable" = "DNSを有効にする"
|
||||
"enableDesc" = "組み込みDNSサーバーを有効にする"
|
||||
"tag" = "DNSインバウンドタグ"
|
||||
"tagDesc" = "このタグはルーティングルールでインバウンドタグとして使用できます"
|
||||
"clientIp" = "クライアントIP"
|
||||
"clientIpDesc" = "DNSクエリ中に指定されたIPの位置をサーバーに通知するために使用されます"
|
||||
"disableCache" = "キャッシュを無効にする"
|
||||
"disableCacheDesc" = "DNSキャッシュを無効にします"
|
||||
"disableFallback" = "フォールバックを無効にする"
|
||||
"disableFallbackDesc" = "フォールバックDNSクエリを無効にします"
|
||||
"disableFallbackIfMatch" = "一致した場合にフォールバックを無効にする"
|
||||
"disableFallbackIfMatchDesc" = "DNSサーバーの一致するドメインリストにヒットした場合、フォールバックDNSクエリを無効にします"
|
||||
"strategy" = "クエリ戦略"
|
||||
"strategyDesc" = "ドメイン名解決の全体的な戦略"
|
||||
"add" = "サーバー追加"
|
||||
"edit" = "サーバー編集"
|
||||
"domains" = "ドメイン"
|
||||
"expectIPs" = "期待されるIP"
|
||||
|
||||
[pages.xray.fakedns]
|
||||
"add" = "フェイクDNS追加"
|
||||
"edit" = "フェイクDNS編集"
|
||||
"ipPool" = "IPプールサブネット"
|
||||
"poolSize" = "プールサイズ"
|
||||
|
||||
[pages.settings.security]
|
||||
"admin" = "管理者"
|
||||
"secret" = "セキュリティトークン"
|
||||
"loginSecurity" = "ログインセキュリティ"
|
||||
"loginSecurityDesc" = "追加の認証を追加してセキュリティを向上させる"
|
||||
"secretToken" = "セキュリティトークン"
|
||||
"secretTokenDesc" = "このトークンを安全な場所に保管してください。このトークンはログインに使用され、紛失すると回復できません。"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "設定を変更"
|
||||
"getSettings" = "設定を取得"
|
||||
"modifyUser" = "管理者を変更"
|
||||
"originalUserPassIncorrect" = "旧ユーザー名または旧パスワードが間違っています"
|
||||
"userPassMustBeNotEmpty" = "新しいユーザー名と新しいパスワードは空にできません"
|
||||
|
||||
[tgbot]
|
||||
"keyboardClosed" = "❌ カスタムキーボードが閉じられました!"
|
||||
"noResult" = "❗ 結果がありません!"
|
||||
"noQuery" = "❌ クエリが見つかりませんでした!もう一度コマンドを使用してください!"
|
||||
"wentWrong" = "❌ 問題が発生しました!"
|
||||
"noIpRecord" = "❗ IP記録がありません!"
|
||||
"noInbounds" = "❗ インバウンド接続が見つかりません!"
|
||||
"unlimited" = "♾ 無制限"
|
||||
"add" = "追加"
|
||||
"month" = "月"
|
||||
"months" = "月"
|
||||
"day" = "日"
|
||||
"days" = "日"
|
||||
"hours" = "時間"
|
||||
"unknown" = "不明"
|
||||
"inbounds" = "インバウンド接続"
|
||||
"clients" = "クライアント"
|
||||
"offline" = "🔴 オフライン"
|
||||
"online" = "🟢 オンライン"
|
||||
|
||||
[tgbot.commands]
|
||||
"unknown" = "❗ 不明なコマンド"
|
||||
"pleaseChoose" = "👇 選択してください:\r\n"
|
||||
"help" = "🤖 このボットをご利用いただきありがとうございます!サーバーから特定のデータを提供し、必要な変更を行うことができます。\r\n\r\n"
|
||||
"start" = "👋 こんにちは、<i>{{ .Firstname }}</i>。\r\n"
|
||||
"welcome" = "🤖 <b>{{ .Hostname }}</b> 管理ボットへようこそ。\r\n"
|
||||
"status" = "✅ ボットは正常に動作しています!"
|
||||
"usage" = "❗ 検索するテキストを入力してください!"
|
||||
"getID" = "🆔 あなたのIDは:<code>{{ .ID }}</code>"
|
||||
"helpAdminCommands" = "Xray Coreを再起動するには:\r\n<code>/restart force</code>\r\n\r\nクライアントの電子メールを検索するには:\r\n<code>/usage [電子メール]</code>\r\n\r\nインバウンド(クライアントの統計情報を含む)を検索するには:\r\n<code>/inbound [備考]</code>\r\n\r\nTelegramチャットID:\r\n<code>/id</code>"
|
||||
"helpClientCommands" = "統計情報を検索するには、次のコマンドを使用してください:\r\n<code>/usage [電子メール]</code>\r\n\r\nTelegramチャットID:\r\n<code>/id</code>"
|
||||
"restartUsage" = "\r\n\r\n<code>/restart force</code>"
|
||||
"restartSuccess" = "✅ 操作成功!"
|
||||
"restartFailed" = "❗ 操作エラー。\r\n\r\n<code>エラー: {{ .Error }}</code>"
|
||||
"xrayNotRunning" = "❗ Xray Core は動作していません。"
|
||||
|
||||
[tgbot.messages]
|
||||
"cpuThreshold" = "🔴 CPU使用率は{{ .Percent }}%、しきい値{{ .Threshold }}%を超えました"
|
||||
"selectUserFailed" = "❌ ユーザーの選択に失敗しました!"
|
||||
"userSaved" = "✅ Telegramユーザーが保存されました。"
|
||||
"loginSuccess" = "✅ パネルに正常にログインしました。\r\n"
|
||||
"loginFailed" = "❗️ パネルのログインに失敗しました。\r\n"
|
||||
"report" = "🕰 定期報告:{{ .RunTime }}\r\n"
|
||||
"datetime" = "⏰ 日時:{{ .DateTime }}\r\n"
|
||||
"hostname" = "💻 ホスト名:{{ .Hostname }}\r\n"
|
||||
"version" = "🚀 X-UI バージョン:{{ .Version }}\r\n"
|
||||
"xrayVersion" = "📡 Xray バージョン: {{ .XrayVersion }}\r\n"
|
||||
"ipv6" = "🌐 IPv6:{{ .IPv6 }}\r\n"
|
||||
"ipv4" = "🌐 IPv4:{{ .IPv4 }}\r\n"
|
||||
"ip" = "🌐 IP:{{ .IP }}\r\n"
|
||||
"ips" = "🔢 IPアドレス:\r\n{{ .IPs }}\r\n"
|
||||
"serverUpTime" = "⏳ サーバー稼働時間:{{ .UpTime }} {{ .Unit }}\r\n"
|
||||
"serverLoad" = "📈 サーバー負荷:{{ .Load1 }}, {{ .Load2 }}, {{ .Load3 }}\r\n"
|
||||
"serverMemory" = "📋 サーバーメモリ:{{ .Current }}/{{ .Total }}\r\n"
|
||||
"tcpCount" = "🔹 TCP接続数:{{ .Count }}\r\n"
|
||||
"udpCount" = "🔸 UDP接続数:{{ .Count }}\r\n"
|
||||
"traffic" = "🚦 トラフィック:{{ .Total }} (↑{{ .Upload }},↓{{ .Download }})\r\n"
|
||||
"xrayStatus" = "ℹ️ Xrayステータス:{{ .State }}\r\n"
|
||||
"username" = "👤 ユーザー名:{{ .Username }}\r\n"
|
||||
"password" = "👤 パスワード: {{ .Password }}\r\n"
|
||||
"time" = "⏰ 時間:{{ .Time }}\r\n"
|
||||
"inbound" = "📍 インバウンド:{{ .Remark }}\r\n"
|
||||
"port" = "🔌 ポート:{{ .Port }}\r\n"
|
||||
"expire" = "📅 有効期限:{{ .Time }}\r\n"
|
||||
"expireIn" = "📅 残り時間:{{ .Time }}\r\n"
|
||||
"active" = "💡 有効:{{ .Enable }}\r\n"
|
||||
"enabled" = "🚨 有効化済み:{{ .Enable }}\r\n"
|
||||
"online" = "🌐 接続ステータス:{{ .Status }}\r\n"
|
||||
"email" = "📧 メール:{{ .Email }}\r\n"
|
||||
"upload" = "🔼 アップロード↑:{{ .Upload }}\r\n"
|
||||
"download" = "🔽 ダウンロード↓:{{ .Download }}\r\n"
|
||||
"total" = "📊 合計:{{ .UpDown }} / {{ .Total }}\r\n"
|
||||
"TGUser" = "👤 Telegramユーザー:{{ .TelegramID }}\r\n"
|
||||
"exhaustedMsg" = "🚨 消耗済みの {{ .Type }}:\r\n"
|
||||
"exhaustedCount" = "🚨 消耗済みの {{ .Type }} 数量:\r\n"
|
||||
"onlinesCount" = "🌐 オンラインクライアント:{{ .Count }}\r\n"
|
||||
"disabled" = "🛑 無効化:{{ .Disabled }}\r\n"
|
||||
"depleteSoon" = "🔜 間もなく消耗:{{ .Deplete }}\r\n\r\n"
|
||||
"backupTime" = "🗄 バックアップ時間:{{ .Time }}\r\n"
|
||||
"refreshedOn" = "\r\n📋🔄 更新時間:{{ .Time }}\r\n\r\n"
|
||||
"yes" = "✅ はい"
|
||||
"no" = "❌ いいえ"
|
||||
|
||||
[tgbot.buttons]
|
||||
"closeKeyboard" = "❌ キーボードを閉じる"
|
||||
"cancel" = "❌ キャンセル"
|
||||
"cancelReset" = "❌ リセットをキャンセル"
|
||||
"cancelIpLimit" = "❌ IP制限をキャンセル"
|
||||
"confirmResetTraffic" = "✅ トラフィックをリセットしますか?"
|
||||
"confirmClearIps" = "✅ IPをクリアしますか?"
|
||||
"confirmRemoveTGUser" = "✅ Telegramユーザーを削除しますか?"
|
||||
"confirmToggle" = "✅ ユーザーを有効/無効にしますか?"
|
||||
"dbBackup" = "データベースバックアップを取得"
|
||||
"serverUsage" = "サーバーの使用状況"
|
||||
"getInbounds" = "インバウンド情報を取得"
|
||||
"depleteSoon" = "間もなく消耗"
|
||||
"clientUsage" = "使用状況を取得"
|
||||
"onlines" = "オンラインクライアント"
|
||||
"commands" = "コマンド"
|
||||
"refresh" = "🔄 更新"
|
||||
"clearIPs" = "❌ IPをクリア"
|
||||
"removeTGUser" = "❌ Telegramユーザーを削除"
|
||||
"selectTGUser" = "👤 Telegramユーザーを選択"
|
||||
"selectOneTGUser" = "👤 1人のTelegramユーザーを選択:"
|
||||
"resetTraffic" = "📈 トラフィックをリセット"
|
||||
"resetExpire" = "📅 有効期限を変更"
|
||||
"ipLog" = "🔢 IPログ"
|
||||
"ipLimit" = "🔢 IP制限"
|
||||
"setTGUser" = "👤 Telegramユーザーを設定"
|
||||
"toggle" = "🔘 有効/無効"
|
||||
"custom" = "🔢 カスタム"
|
||||
"confirmNumber" = "✅ 確認: {{ .Num }}"
|
||||
"confirmNumberAdd" = "✅ 追加を確認:{{ .Num }}"
|
||||
"limitTraffic" = "🚧 トラフィック制限"
|
||||
"getBanLogs" = "禁止ログ"
|
||||
"allClients" = "すべてのクライアント"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ 成功!"
|
||||
"errorOperation" = "❗ 操作エラー。"
|
||||
"getInboundsFailed" = "❌ インバウンド情報の取得に失敗しました。"
|
||||
"getClientsFailed" = "❌ クライアントの取得に失敗しました。"
|
||||
"canceled" = "❌ {{ .Email }}:操作がキャンセルされました。"
|
||||
"clientRefreshSuccess" = "✅ {{ .Email }}:クライアントが正常に更新されました。"
|
||||
"IpRefreshSuccess" = "✅ {{ .Email }}:IPが正常に更新されました。"
|
||||
"TGIdRefreshSuccess" = "✅ {{ .Email }}:クライアントのTelegramユーザーが正常に更新されました。"
|
||||
"resetTrafficSuccess" = "✅ {{ .Email }}:トラフィックが正常にリセットされました。"
|
||||
"setTrafficLimitSuccess" = "✅ {{ .Email }}:トラフィック制限が正常に保存されました。"
|
||||
"expireResetSuccess" = "✅ {{ .Email }}:有効期限の日数が正常にリセットされました。"
|
||||
"resetIpSuccess" = "✅ {{ .Email }}:IP制限数が正常に保存されました:{{ .Count }}。"
|
||||
"clearIpSuccess" = "✅ {{ .Email }}:IPが正常にクリアされました。"
|
||||
"getIpLog" = "✅ {{ .Email }}:IPログの取得。"
|
||||
"getUserInfo" = "✅ {{ .Email }}:Telegramユーザー情報の取得。"
|
||||
"removedTGUserSuccess" = "✅ {{ .Email }}:Telegramユーザーが正常に削除されました。"
|
||||
"enableSuccess" = "✅ {{ .Email }}:正常に有効化されました。"
|
||||
"disableSuccess" = "✅ {{ .Email }}:正常に無効化されました。"
|
||||
"askToAddUserId" = "設定が見つかりませんでした!\r\n管理者に問い合わせて、設定にTelegramユーザーのChatIDを使用してください。\r\n\r\nあなたのユーザーChatID:<code>{{ .TgUserID }}</code>"
|
||||
"chooseClient" = "インバウンド {{ .Inbound }} のクライアントを選択"
|
||||
"chooseInbound" = "インバウンドを選択"
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "IP de Escuta"
|
||||
"certificate" = "Certificado Digital"
|
||||
"fail" = "Falhou"
|
||||
"comment" = "Comentário"
|
||||
"success" = "Com Sucesso"
|
||||
"getVersion" = "Obter Versão"
|
||||
"install" = "Instalar"
|
||||
@@ -306,6 +307,10 @@
|
||||
"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."
|
||||
"externalTrafficInformEnable" = "Informações de tráfego externo"
|
||||
"externalTrafficInformEnableDesc" = "Informar a API externa sobre cada atualização de tráfego."
|
||||
"externalTrafficInformURI" = "URI de informação de tráfego externo"
|
||||
"externalTrafficInformURIDesc" = "As atualizações de tráfego são enviadas para este URI."
|
||||
"fragment" = "Fragmentação"
|
||||
"fragmentDesc" = "Ativa a fragmentação para o pacote TLS hello."
|
||||
"fragmentSett" = "Configurações de Fragmentação"
|
||||
@@ -369,6 +374,15 @@
|
||||
"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."
|
||||
"statistics" = "Estatísticas"
|
||||
"statsInboundUplink" = "Estatísticas de Upload de Entrada"
|
||||
"statsInboundUplinkDesc" = "Habilita a coleta de estatísticas para o tráfego de upload de todos os proxies de entrada."
|
||||
"statsInboundDownlink" = "Estatísticas de Download de Entrada"
|
||||
"statsInboundDownlinkDesc" = "Habilita a coleta de estatísticas para o tráfego de download de todos os proxies de entrada."
|
||||
"statsOutboundUplink" = "Estatísticas de Upload de Saída"
|
||||
"statsOutboundUplinkDesc" = "Habilita a coleta de estatísticas para o tráfego de upload de todos os proxies de saída."
|
||||
"statsOutboundDownlink" = "Estatísticas de Download de Saída"
|
||||
"statsOutboundDownlinkDesc" = "Habilita a coleta de estatísticas para o tráfego de download de todos os proxies de saída."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Primeiro"
|
||||
@@ -426,6 +440,14 @@
|
||||
"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."
|
||||
"clientIp" = "IP do Cliente"
|
||||
"clientIpDesc" = "Usado para notificar o servidor sobre a localização IP especificada durante consultas DNS"
|
||||
"disableCache" = "Desativar cache"
|
||||
"disableCacheDesc" = "Desativa o cache de DNS"
|
||||
"disableFallback" = "Desativar Fallback"
|
||||
"disableFallbackDesc" = "Desativa consultas DNS de fallback"
|
||||
"disableFallbackIfMatch" = "Desativar Fallback Se Corresponder"
|
||||
"disableFallbackIfMatchDesc" = "Desativa consultas DNS de fallback quando a lista de domínios correspondentes do servidor DNS é atingida"
|
||||
"strategy" = "Estratégia de Consulta"
|
||||
"strategyDesc" = "Estratégia geral para resolver nomes de domínio"
|
||||
"add" = "Adicionar Servidor"
|
||||
|
||||
@@ -41,16 +41,17 @@
|
||||
"offline" = "Офлайн"
|
||||
"online" = "Онлайн"
|
||||
"domainName" = "Домен"
|
||||
"monitor" = "Порт IP"
|
||||
"monitor" = "Слушать IP"
|
||||
"certificate" = "Цифровой сертификат"
|
||||
"fail" = "Неудачно"
|
||||
"fail" = "Ошибка"
|
||||
"comment" = "Комментарий"
|
||||
"success" = "Успешно"
|
||||
"getVersion" = "Узнать версию"
|
||||
"install" = "Установка"
|
||||
"clients" = "Клиенты"
|
||||
"usage" = "Использование"
|
||||
"secretToken" = "Секретный токен"
|
||||
"remained" = "остались"
|
||||
"remained" = "Остаток"
|
||||
"security" = "Безопасность"
|
||||
"secAlertTitle" = "Предупреждение системы безопасности"
|
||||
"secAlertSsl" = "Это соединение не защищено. Пожалуйста, воздержитесь от ввода конфиденциальной информации до тех пор, пока не будет активирован TLS для защиты данных"
|
||||
@@ -78,7 +79,7 @@
|
||||
"invalidFormData" = "Недопустимый формат данных"
|
||||
"emptyUsername" = "Введите имя пользователя"
|
||||
"emptyPassword" = "Введите пароль"
|
||||
"wrongUsernameOrPassword" = "Неверное имя пользователя или пароль"
|
||||
"wrongUsernameOrPassword" = "Неверное имя пользователя, пароль или секретный токен."
|
||||
"successLogin" = "Успешный вход"
|
||||
|
||||
[pages.index]
|
||||
@@ -88,33 +89,33 @@
|
||||
"xrayStatus" = "Xray"
|
||||
"stopXray" = "Остановить"
|
||||
"restartXray" = "Перезапустить"
|
||||
"xraySwitch" = "Версия"
|
||||
"xraySwitch" = "Выбор версии"
|
||||
"xraySwitchClick" = "Выберите желаемую версию"
|
||||
"xraySwitchClickDesk" = "Выбирайте внимательно, так как старые версии могут быть несовместимы с текущими конфигурациями"
|
||||
"xraySwitchClickDesk" = "Обратите внимание: старые версии могут не поддерживать текущие настройки"
|
||||
"operationHours" = "Время работы системы"
|
||||
"systemLoad" = "Системная нагрузка"
|
||||
"systemLoad" = "Нагрузка на систему"
|
||||
"systemLoadDesc" = "Средняя загрузка системы за последние 1, 5 и 15 минут"
|
||||
"connectionTcpCountDesc" = "Общее количество подключений TCP по всем сетевым картам."
|
||||
"connectionUdpCountDesc" = "Общее количество подключений UDP по всем сетевым картам."
|
||||
"connectionCount" = "Количество соединений"
|
||||
"upSpeed" = "Общая скорость upload для всех сетей"
|
||||
"downSpeed" = "Общая скорость download для всех сетей"
|
||||
"totalSent" = "Общий объем загруженных данных для всех сетей с момента запуска системы"
|
||||
"upSpeed" = "Общая скорость отправки для всех сетей"
|
||||
"downSpeed" = "Общая скорость загрузки для всех сетей"
|
||||
"totalSent" = "Общий объем отправляемых данных с момента запуска системы"
|
||||
"totalReceive" = "Общий объем полученных данных для всех сетей с момента запуска системы."
|
||||
"xraySwitchVersionDialog" = "Переключить версию Xray"
|
||||
"xraySwitchVersionDialogDesc" = "Вы точно хотите сменить версию Xray?"
|
||||
"dontRefresh" = "Идёт установка. Пожалуйста, не обновляйте эту страницу"
|
||||
"dontRefresh" = "Установка в процессе. Не обновляйте страницу"
|
||||
"logs" = "Логи"
|
||||
"config" = "Конфигурация"
|
||||
"backup" = "Бэкап и восстановление"
|
||||
"backupTitle" = "База данных бэкапа и восстановления"
|
||||
"backupDescription" = "Не забудьте сделать резервную копию перед импортом новой базы данных"
|
||||
"backup" = "Резервное копирование и восстановление"
|
||||
"backupTitle" = "База данных резервных копий"
|
||||
"backupDescription" = "Рекомендуется сделать резервную копию перед восстановлением базы данных."
|
||||
"exportDatabase" = "Экспорт базы данных"
|
||||
"importDatabase" = "Импорт базы данных"
|
||||
|
||||
[pages.inbounds]
|
||||
"title" = "Подключения"
|
||||
"totalDownUp" = "Всего uploads/downloads"
|
||||
"totalDownUp" = "Общий объем отправленного/полученного трафика"
|
||||
"totalUsage" = "Всего использовано"
|
||||
"inboundCount" = "Количество подключений"
|
||||
"operate" = "Меню"
|
||||
@@ -146,17 +147,17 @@
|
||||
"meansNoLimit" = "= Без ограничений (значение: ГБ)"
|
||||
"totalFlow" = "Общий расход"
|
||||
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
|
||||
"noRecommendKeepDefault" = "Не рекомендуется оставлять настройки по умолчанию"
|
||||
"noRecommendKeepDefault" = "Рекомендуется оставить настройки по умолчанию"
|
||||
"certificatePath" = "Путь к файлу"
|
||||
"certificateContent" = "Содержимое файла"
|
||||
"publicKey" = "Публичный ключ"
|
||||
"privatekey" = "Приватный ключ"
|
||||
"privatekey" = "Закрытый ключ"
|
||||
"clickOnQRcode" = "Нажмите на QR-код, чтобы скопировать"
|
||||
"client" = "Клиент"
|
||||
"export" = "Экспорт ключей"
|
||||
"clone" = "Клонировать"
|
||||
"cloneInbound" = "Клонировать"
|
||||
"cloneInboundContent" = "Все настройки этого подключения, кроме порта, IP-адреса прослушки и клиентов, будут клонированы"
|
||||
"cloneInboundContent" = "Будут клонированы все настройки подключений, за исключением списка клиентов, порта и IP-адреса прослушивания"
|
||||
"cloneInboundOk" = "Клонировано"
|
||||
"resetAllTraffic" = "Сбросить трафик всех подключений"
|
||||
"resetAllTrafficTitle" = "Сброс трафика всех подключений"
|
||||
@@ -172,8 +173,8 @@
|
||||
"delDepletedClientsContent" = "Вы уверены, что хотите удалить всех отключенных пользователей?"
|
||||
"email" = "Email"
|
||||
"emailDesc" = "Пожалуйста, укажите уникальный Email"
|
||||
"IPLimit" = "Ограничение по IP"
|
||||
"IPLimitDesc" = "Сбросить подключение, если подключено больше введенного значения (введите 0, чтобы отключить ограничение IP-адресов)"
|
||||
"IPLimit" = "Лимит по IP"
|
||||
"IPLimitDesc" = "Ограничение количества подключений с одного IP (0 – отключить)"
|
||||
"IPLimitlog" = "IP лог"
|
||||
"IPLimitlogDesc" = "Лог IP-адресов (перед включением лога IP-адресов, вы должны очистить список)"
|
||||
"IPLimitlogclear" = "Очистить лог"
|
||||
@@ -228,18 +229,18 @@
|
||||
"save" = "Сохранить"
|
||||
"infoDesc" = "Каждое выполненное изменение необходимо сохранить. Пожалуйста, перезапустите панель, чтобы изменения вступили в силу"
|
||||
"restartPanel" = "Перезапуск панели"
|
||||
"restartPanelDesc" = "Вы уверены, что хотите перезапустить панель? Нажмите ОК для перезапуска панели через 3 сек. Если вы не можете пользоваться панелью после перезапуска, пожалуйста, посмотрите лог панели на сервере"
|
||||
"restartPanelDesc" = "Вы уверены, что хотите перезапустить панель? Подтвердите, и перезапуск произойдёт через 3 секунды. Если панель станет недоступной, проверьте лог сервера"
|
||||
"actions" = "Действия"
|
||||
"resetDefaultConfig" = "Сбросить на конфигурацию по умолчанию"
|
||||
"resetDefaultConfig" = "Восстановить настройки по умолчанию"
|
||||
"panelSettings" = "Настройки панели"
|
||||
"securitySettings" = "Настройки безопасности"
|
||||
"TGBotSettings" = "Настройки Telegram бота"
|
||||
"panelListeningIP" = "IP-адрес панели"
|
||||
"panelListeningIPDesc" = "Оставьте пустым для подключения с любого IP"
|
||||
"panelListeningDomain" = "Домен прослушивания панели"
|
||||
"panelListeningDomain" = "Домен панели"
|
||||
"panelListeningDomainDesc" = "По умолчанию оставьте пустым, чтобы отслеживать все домены и IP-адреса"
|
||||
"panelPort" = "Порт панели"
|
||||
"panelPortDesc" = "Порт, используемый для отображения этой панели"
|
||||
"panelPortDesc" = "Порт, на котором работает панель"
|
||||
"publicKeyPath" = "Путь к файлу публичного ключа сертификата панели"
|
||||
"publicKeyPathDesc" = "Введите полный путь, начинающийся с '/'"
|
||||
"privateKeyPath" = "Путь к файлу приватного ключа сертификата панели"
|
||||
@@ -251,14 +252,14 @@
|
||||
"remarkModel" = "Модель примечания и символ разделения"
|
||||
"datepicker" = "Выбор даты"
|
||||
"datepickerPlaceholder" = "Выберите дату"
|
||||
"datepickerDescription" = "Запланированные задачи выполняются в соответствии с данным календарём"
|
||||
"datepickerDescription" = "Запланированные задачи будут выполняться в выбранное время"
|
||||
"sampleRemark" = "Пример замечания"
|
||||
"oldUsername" = "Текущий логин"
|
||||
"currentPassword" = "Текущий пароль"
|
||||
"newUsername" = "Новый логин"
|
||||
"newPassword" = "Новый пароль"
|
||||
"telegramBotEnable" = "Включить Telegram бота"
|
||||
"telegramBotEnableDesc" = "Подключайтесь к функциям этой панели через Telegram бота"
|
||||
"telegramBotEnableDesc" = "Получайте доступ к функциям панели через Telegram-бота"
|
||||
"telegramToken" = "Токен Telegram бота"
|
||||
"telegramTokenDesc" = "Необходимо получить токен у менеджера ботов Telegram @botfather"
|
||||
"telegramProxy" = "Прокси Socks5"
|
||||
@@ -268,14 +269,14 @@
|
||||
"telegramChatId" = "Идентификатор Telegram администратора бота"
|
||||
"telegramChatIdDesc" = "Один или несколько идентификаторов администратора бота. Чтобы получить идентификатор, используйте @userinfobot или команду '/id' в боте."
|
||||
"telegramNotifyTime" = "Частота уведомлений бота Telegram"
|
||||
"telegramNotifyTimeDesc" = "Используйте формат времени Crontab"
|
||||
"telegramNotifyTimeDesc" = "Укажите интервал уведомлений в формате Crontab"
|
||||
"tgNotifyBackup" = "Резервное копирование базы данных"
|
||||
"tgNotifyBackupDesc" = "Включать файл резервной копии базы данных с уведомлением об отчете"
|
||||
"tgNotifyBackupDesc" = "Отправлять уведомление с файлом резервной копии базы данных"
|
||||
"tgNotifyLogin" = "Уведомление о входе"
|
||||
"tgNotifyLoginDesc" = "Отображает имя пользователя, IP-адрес и время, когда кто-то пытается войти в вашу панель."
|
||||
"sessionMaxAge" = "Продолжительность сессии"
|
||||
"sessionMaxAgeDesc" = "Продолжительность сессии в системе (значение: минута)"
|
||||
"expireTimeDiff" = "Порог истечения срока сессии для уведомления"
|
||||
"expireTimeDiff" = "Задержка уведомления об истечении сессии"
|
||||
"expireTimeDiffDesc" = "Получение уведомления об истечении срока действия сессии до достижения порогового значения (значение: день)"
|
||||
"trafficDiff" = "Порог трафика для уведомления"
|
||||
"trafficDiffDesc" = "Получение уведомления об исчерпании трафика до достижения порога (значение: ГБ)"
|
||||
@@ -303,19 +304,23 @@
|
||||
"subEncrypt" = "Шифровать конфиги"
|
||||
"subEncryptDesc" = "Шифровать возвращенные конфиги в подписке"
|
||||
"subShowInfo" = "Показать информацию об использовании"
|
||||
"subShowInfoDesc" = "Показывать оставшиеся трафик и дату после имени конфигурации"
|
||||
"subShowInfoDesc" = "Отображать остаток трафика и дату окончания после имени конфигурации"
|
||||
"subURI" = "URI обратного прокси"
|
||||
"subURIDesc" = "Изменить базовый URI URL-адреса подписки для использования за прокси-серверами"
|
||||
"externalTrafficInformEnable" = "Информация о внешнем трафике"
|
||||
"externalTrafficInformEnableDesc" = "Информировать внешний API о каждом обновлении трафика"
|
||||
"externalTrafficInformURI" = "URI информации о внешнем трафике"
|
||||
"externalTrafficInformURIDesc" = "Обновления трафика отправляются на этот URI"
|
||||
"fragment" = "Фрагментация"
|
||||
"fragmentDesc" = "Включить фрагментацию для пакета приветствия TLS"
|
||||
"fragmentDesc" = "Включить фрагментацию TLS-приветствия"
|
||||
"fragmentSett" = "Настройки фрагментации"
|
||||
"noisesDesc" = "Включить Noises."
|
||||
"noisesSett" = "Настройки Noises"
|
||||
"mux" = "Mux"
|
||||
"muxDesc" = "Передача нескольких независимых потоков данных в рамках установленного потока данных."
|
||||
"muxDesc" = "Передача нескольких независимых потоков данных в одном соединении."
|
||||
"muxSett" = "Mux Настройки"
|
||||
"direct" = "Прямая связь"
|
||||
"directDesc" = "Напрямую устанавливает соединения с доменами или диапазонами IP конкретной страны."
|
||||
"directDesc" = "Устанавливает прямые соединения с доменами или IP-адресами определённой страны."
|
||||
|
||||
|
||||
[pages.xray]
|
||||
@@ -327,7 +332,7 @@
|
||||
"generalConfigs" = "Основные настройки"
|
||||
"generalConfigsDesc" = "Эти параметры описывают общие настройки"
|
||||
"logConfigs" = "Журнал"
|
||||
"logConfigsDesc" = "Журналы могут повлиять на эффективность вашего сервера. Рекомендуется включать их только в случае необходимости!"
|
||||
"logConfigsDesc" = "Логи могут замедлять работу сервера. Включайте только нужные вам виды логов при необходимости!"
|
||||
"blockConfigs" = "Блокировка конфигураций"
|
||||
"blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам"
|
||||
"basicRouting" = "Базовые соединения"
|
||||
@@ -368,7 +373,16 @@
|
||||
"dnsLog" = "DNS Журнал"
|
||||
"dnsLogDesc" = "Включить логи запросов DNS"
|
||||
"maskAddress" = "Маскировать Адрес"
|
||||
"maskAddressDesc" = "Маска IP-адреса, при активации автоматически заменяет IP-адрес, который появляется в логе."
|
||||
"maskAddressDesc" = "При активации реальный IP-адрес заменяется на маскировочный в логах."
|
||||
"statistics" = "Статистика"
|
||||
"statsInboundUplink" = "Статистика входящего аплинка"
|
||||
"statsInboundUplinkDesc" = "Включает сбор статистики для исходящего трафика всех входящих прокси."
|
||||
"statsInboundDownlink" = "Статистика входящего даунлинка"
|
||||
"statsInboundDownlinkDesc" = "Включает сбор статистики для входящего трафика всех входящих прокси."
|
||||
"statsOutboundUplink" = "Статистика исходящего аплинка"
|
||||
"statsOutboundUplinkDesc" = "Включает сбор статистики для исходящего трафика всех исходящих прокси."
|
||||
"statsOutboundDownlink" = "Статистика исходящего даунлинка"
|
||||
"statsOutboundDownlinkDesc" = "Включает сбор статистики для входящего трафика всех исходящих прокси."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Первый"
|
||||
@@ -414,7 +428,7 @@
|
||||
"balancerDesc" = "Невозможно одновременно использовать balancerTag и outboundTag. При одновременном использовании будет работать только outboundTag."
|
||||
|
||||
[pages.xray.wireguard]
|
||||
"secretKey" = "Приватный ключ"
|
||||
"secretKey" = "Закрытый ключ"
|
||||
"publicKey" = "Публичный ключ"
|
||||
"allowedIPs" = "Разрешенные IP-адреса"
|
||||
"endpoint" = "Конечная точка"
|
||||
@@ -426,6 +440,14 @@
|
||||
"enableDesc" = "Включить встроенный DNS-сервер"
|
||||
"tag" = "Входящий тег DNS"
|
||||
"tagDesc" = "Этот тег будет доступен как входящий тег в правилах маршрутизации."
|
||||
"clientIp" = "IP клиента"
|
||||
"clientIpDesc" = "Используется для уведомления сервера о указанном местоположении IP во время DNS-запросов"
|
||||
"disableCache" = "Отключить кэш"
|
||||
"disableCacheDesc" = "Отключает кэширование DNS"
|
||||
"disableFallback" = "Отключить резервный DNS"
|
||||
"disableFallbackDesc" = "Отключает резервные DNS-запросы"
|
||||
"disableFallbackIfMatch" = "Отключить резервный DNS при совпадении"
|
||||
"disableFallbackIfMatchDesc" = "Отключает резервные DNS-запросы при совпадении списка доменов DNS-сервера"
|
||||
"strategy" = "Стратегия запроса"
|
||||
"strategyDesc" = "Общая стратегия разрешения доменных имен"
|
||||
"add" = "Добавить сервер"
|
||||
@@ -448,7 +470,7 @@
|
||||
"secretTokenDesc" = "Пожалуйста, скопируйте и сохраните этот токен в безопасном месте. Этот токен необходим для входа в систему и не может быть восстановлен с помощью инструмента x-ui"
|
||||
|
||||
[pages.settings.toasts]
|
||||
"modifySettings" = "Изменение настроек"
|
||||
"modifySettings" = "Настройки изменены"
|
||||
"getSettings" = "Просмотр настроек"
|
||||
"modifyUser" = "Изменение пользователя"
|
||||
"originalUserPassIncorrect" = "Неверное имя пользователя или пароль"
|
||||
@@ -572,7 +594,7 @@
|
||||
"allClients" = "Все клиенты"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ Успешный!"
|
||||
"successfulOperation" = "✅ Успешно!"
|
||||
"errorOperation" = "❗ Ошибка в операции."
|
||||
"getInboundsFailed" = "❌ Не удалось получить входящие потоки."
|
||||
"getClientsFailed" = "❌ Не удалось получить клиентов."
|
||||
@@ -581,8 +603,8 @@
|
||||
"IpRefreshSuccess" = "✅ {{ .Email }}: IP-адреса успешно обновлены."
|
||||
"TGIdRefreshSuccess" = "✅ {{ .Email }}: Пользователь Telegram клиента успешно обновлен."
|
||||
"resetTrafficSuccess" = "✅ {{ .Email }}: Трафик успешно сброшен."
|
||||
"setTrafficLimitSuccess" = "✅ {{ .Email }}: Лимит Трафик успешно сохранен."
|
||||
"expireResetSuccess" = "✅ {{ .Email }}: Дни истечения успешно сброшены."
|
||||
"setTrafficLimitSuccess" = "✅ {{ .Email }}: Лимит трафика успешно установлен."
|
||||
"expireResetSuccess" = "✅ {{ .Email }}: Срок действия успешно сброшен."
|
||||
"resetIpSuccess" = "✅ {{ .Email }}: Лимит IP ({{ .Count }}) успешно сохранен."
|
||||
"clearIpSuccess" = "✅ {{ .Email }}: IP-адреса успешно очищены."
|
||||
"getIpLog" = "✅ {{ .Email }}: Получен лог IP."
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "Dinleme IP"
|
||||
"certificate" = "Dijital Sertifika"
|
||||
"fail" = "Başarısız"
|
||||
"comment" = "Yorum"
|
||||
"success" = "Başarılı"
|
||||
"getVersion" = "Sürümü Al"
|
||||
"install" = "Yükle"
|
||||
@@ -306,6 +307,10 @@
|
||||
"subShowInfoDesc" = "Kalan trafik ve tarih müşteri uygulamalarında görüntülenir."
|
||||
"subURI" = "Ters Proxy URI"
|
||||
"subURIDesc" = "Proxy arkasında kullanılacak abonelik URL'sinin URI yolu."
|
||||
"externalTrafficInformEnable" = "Harici Trafik Bilgisi"
|
||||
"externalTrafficInformEnableDesc" = "Her trafik güncellemesinde harici API'yi bilgilendirin."
|
||||
"externalTrafficInformURI" = "Harici Trafik Bilgisi URI'si"
|
||||
"externalTrafficInformURIDesc" = "Trafik güncellemeleri bu URI'ye gönderildi."
|
||||
"fragment" = "Parçalama"
|
||||
"fragmentDesc" = "TLS merhaba paketinin parçalanmasını etkinleştir."
|
||||
"fragmentSett" = "Parçalama Ayarları"
|
||||
@@ -369,6 +374,15 @@
|
||||
"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."
|
||||
"statistics" = "İstatistikler"
|
||||
"statsInboundUplink" = "Gelen Yükleme İstatistikleri"
|
||||
"statsInboundUplinkDesc" = "Tüm gelen proxy'lerin yükleme trafiği için istatistik toplamayı etkinleştirir."
|
||||
"statsInboundDownlink" = "Gelen İndirme İstatistikleri"
|
||||
"statsInboundDownlinkDesc" = "Tüm gelen proxy'lerin indirme trafiği için istatistik toplamayı etkinleştirir."
|
||||
"statsOutboundUplink" = "Giden Yükleme İstatistikleri"
|
||||
"statsOutboundUplinkDesc" = "Tüm giden proxy'lerin yükleme trafiği için istatistik toplamayı etkinleştirir."
|
||||
"statsOutboundDownlink" = "Giden İndirme İstatistikleri"
|
||||
"statsOutboundDownlinkDesc" = "Tüm giden proxy'lerin indirme trafiği için istatistik toplamayı etkinleştirir."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "İlk"
|
||||
@@ -426,6 +440,14 @@
|
||||
"enableDesc" = "Dahili DNS sunucusunu etkinleştir"
|
||||
"tag" = "DNS Gelen Etiketi"
|
||||
"tagDesc" = "Bu etiket, yönlendirme kurallarında Gelen etiketi olarak kullanılabilir."
|
||||
"clientIp" = "İstemci IP"
|
||||
"clientIpDesc" = "DNS sorguları sırasında belirtilen IP konumunu sunucuya bildirmek için kullanılır"
|
||||
"disableCache" = "Önbelleği devre dışı bırak"
|
||||
"disableCacheDesc" = "DNS önbelleğini devre dışı bırakır"
|
||||
"disableFallback" = "Yedeklemeyi devre dışı bırak"
|
||||
"disableFallbackDesc" = "Yedek DNS sorgularını devre dışı bırakır"
|
||||
"disableFallbackIfMatch" = "Eşleşirse Yedeklemeyi Devre Dışı Bırak"
|
||||
"disableFallbackIfMatchDesc" = "DNS sunucusunun eşleşen alan adı listesi vurulduğunda yedek DNS sorgularını devre dışı bırakır"
|
||||
"strategy" = "Sorgu Stratejisi"
|
||||
"strategyDesc" = "Alan adlarını çözmek için genel strateji"
|
||||
"add" = "Sunucu Ekle"
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "Слухати IP"
|
||||
"certificate" = "Цифровий сертифікат"
|
||||
"fail" = "Помилка"
|
||||
"comment" = "Коментар"
|
||||
"success" = "Успішно"
|
||||
"getVersion" = "Отримати версію"
|
||||
"install" = "Встановити"
|
||||
@@ -306,6 +307,10 @@
|
||||
"subShowInfoDesc" = "Залишок трафіку та дата відображатимуться в клієнтських програмах."
|
||||
"subURI" = "URI зворотного проксі"
|
||||
"subURIDesc" = "URI до URL-адреси підписки для використання за проксі."
|
||||
"externalTrafficInformEnable" = "Інформація про зовнішній трафік"
|
||||
"externalTrafficInformEnableDesc" = "Інформувати зовнішній API про кожне оновлення трафіку."
|
||||
"externalTrafficInformURI" = "Інформаційний URI зовнішнього трафіку"
|
||||
"externalTrafficInformURIDesc" = "Оновлення трафіку надсилаються на цей URI."
|
||||
"fragment" = "Фрагментація"
|
||||
"fragmentDesc" = "Увімкнути фрагментацію для пакету привітання TLS"
|
||||
"fragmentSett" = "Параметри фрагментації"
|
||||
@@ -369,6 +374,15 @@
|
||||
"dnsLogDesc" = "Чи включити журнали запитів DNS"
|
||||
"maskAddress" = "Маскувати Адресу"
|
||||
"maskAddressDesc" = "Маска IP-адреси, при активації автоматично замінює IP-адресу, яка з'являється у журналі."
|
||||
"statistics" = "Статистика"
|
||||
"statsInboundUplink" = "Статистика вхідного аплінку"
|
||||
"statsInboundUplinkDesc" = "Увімкнення збору статистики для вхідного трафіку всіх вхідних проксі."
|
||||
"statsInboundDownlink" = "Статистика вхідного даунлінку"
|
||||
"statsInboundDownlinkDesc" = "Увімкнення збору статистики для вихідного трафіку всіх вхідних проксі."
|
||||
"statsOutboundUplink" = "Статистика вихідного аплінку"
|
||||
"statsOutboundUplinkDesc" = "Увімкнення збору статистики для вхідного трафіку всіх вихідних проксі."
|
||||
"statsOutboundDownlink" = "Статистика вихідного даунлінку"
|
||||
"statsOutboundDownlinkDesc" = "Увімкнення збору статистики для вихідного трафіку всіх вихідних проксі."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Перший"
|
||||
@@ -426,6 +440,14 @@
|
||||
"enableDesc" = "Увімкнути вбудований DNS-сервер"
|
||||
"tag" = "Мітка вхідного DNS"
|
||||
"tagDesc" = "Ця мітка буде доступна як вхідна мітка в правилах маршрутизації."
|
||||
"clientIp" = "IP клієнта"
|
||||
"clientIpDesc" = "Використовується для повідомлення серверу про вказане місцезнаходження IP під час DNS-запитів"
|
||||
"disableCache" = "Вимкнути кеш"
|
||||
"disableCacheDesc" = "Вимкнути кешування DNS"
|
||||
"disableFallback" = "Вимкнути резервний DNS"
|
||||
"disableFallbackDesc" = "Вимкнути резервні DNS-запити"
|
||||
"disableFallbackIfMatch" = "Вимкнути резервний DNS при збігу"
|
||||
"disableFallbackIfMatchDesc" = "Вимкнути резервні DNS-запити при збігу списку доменів DNS-сервера"
|
||||
"strategy" = "Стратегія запиту"
|
||||
"strategyDesc" = "Загальна стратегія вирішення доменних імен"
|
||||
"add" = "Додати сервер"
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "Listening IP"
|
||||
"certificate" = "Chứng chỉ số"
|
||||
"fail" = "Thất bại"
|
||||
"comment" = "Bình luận"
|
||||
"success" = "Thành công"
|
||||
"getVersion" = "Lấy phiên bản"
|
||||
"install" = "Cài đặt"
|
||||
@@ -306,6 +307,10 @@
|
||||
"subShowInfoDesc" = "Hiển thị lưu lượng truy cập còn lại và ngày sau tên cấu hình"
|
||||
"subURI" = "URI proxy trung gian"
|
||||
"subURIDesc" = "Thay đổi URI cơ sở của URL gói đăng ký để sử dụng cho proxy trung gian"
|
||||
"externalTrafficInformEnable" = "Thông báo giao thông bên ngoài"
|
||||
"externalTrafficInformEnableDesc" = "Thông báo cho API bên ngoài về mọi cập nhật lưu lượng truy cập."
|
||||
"externalTrafficInformURI" = "URI thông báo lưu lượng truy cập bên ngoài"
|
||||
"externalTrafficInformURIDesc" = "Cập nhật lưu lượng truy cập được gửi tới URI này."
|
||||
"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"
|
||||
@@ -369,6 +374,15 @@
|
||||
"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ý."
|
||||
"statistics" = "Thống kê"
|
||||
"statsInboundUplink" = "Thống kê tải lên đầu vào"
|
||||
"statsInboundUplinkDesc" = "Kích hoạt thu thập thống kê cho lưu lượng tải lên của tất cả các proxy đầu vào."
|
||||
"statsInboundDownlink" = "Thống kê tải xuống đầu vào"
|
||||
"statsInboundDownlinkDesc" = "Kích hoạt thu thập thống kê cho lưu lượng tải xuống của tất cả các proxy đầu vào."
|
||||
"statsOutboundUplink" = "Thống kê tải lên đầu ra"
|
||||
"statsOutboundUplinkDesc" = "Kích hoạt thu thập thống kê cho lưu lượng tải lên của tất cả các proxy đầu ra."
|
||||
"statsOutboundDownlink" = "Thống kê tải xuống đầu ra"
|
||||
"statsOutboundDownlinkDesc" = "Kích hoạt thu thập thống kê cho lưu lượng tải xuống của tất cả các proxy đầu ra."
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "Đầu tiên"
|
||||
@@ -426,6 +440,14 @@
|
||||
"enableDesc" = "Kích hoạt máy chủ DNS tích hợp"
|
||||
"tag" = "Thẻ gửi đến DNS"
|
||||
"tagDesc" = "Thẻ này sẽ có sẵn dưới dạng thẻ Gửi đến trong quy tắc định tuyến."
|
||||
"clientIp" = "IP khách hàng"
|
||||
"clientIpDesc" = "Được sử dụng để thông báo cho máy chủ về vị trí IP được chỉ định trong các truy vấn DNS"
|
||||
"disableCache" = "Tắt bộ nhớ đệm"
|
||||
"disableCacheDesc" = "Tắt bộ nhớ đệm DNS"
|
||||
"disableFallback" = "Tắt Fallback"
|
||||
"disableFallbackDesc" = "Tắt các truy vấn DNS Fallback"
|
||||
"disableFallbackIfMatch" = "Tắt Fallback Nếu Khớp"
|
||||
"disableFallbackIfMatchDesc" = "Tắt các truy vấn DNS Fallback khi danh sách tên miền khớp của máy chủ DNS được kích hoạt"
|
||||
"strategy" = "Chiến lược truy vấn"
|
||||
"strategyDesc" = "Chiến lược tổng thể để phân giải tên miền"
|
||||
"add" = "Thêm máy chủ"
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "监听"
|
||||
"certificate" = "数字证书"
|
||||
"fail" = "失败"
|
||||
"comment" = "评论"
|
||||
"success" = "成功"
|
||||
"getVersion" = "获取版本"
|
||||
"install" = "安装"
|
||||
@@ -306,6 +307,10 @@
|
||||
"subShowInfoDesc" = "客户端应用中将显示剩余流量和日期信息"
|
||||
"subURI" = "反向代理 URI"
|
||||
"subURIDesc" = "用于代理后面的订阅 URL 的 URI 路径"
|
||||
"externalTrafficInformEnable" = "外部交通通知"
|
||||
"externalTrafficInformEnableDesc" = "每次流量更新时通知外部 API"
|
||||
"externalTrafficInformURI" = "外部流量通知 URI"
|
||||
"externalTrafficInformURIDesc" = "流量更新将发送到此 URI"
|
||||
"fragment" = "分片"
|
||||
"fragmentDesc" = "启用 TLS hello 数据包分片"
|
||||
"fragmentSett" = "设置"
|
||||
@@ -369,6 +374,15 @@
|
||||
"dnsLogDesc" = "是否启用 DNS 查询日志"
|
||||
"maskAddress" = "隐藏地址"
|
||||
"maskAddressDesc" = "IP 地址掩码,启用时会自动替换日志中出现的 IP 地址。"
|
||||
"statistics" = "统计"
|
||||
"statsInboundUplink" = "入站上传统计"
|
||||
"statsInboundUplinkDesc" = "启用所有入站代理的上行流量统计收集。"
|
||||
"statsInboundDownlink" = "入站下载统计"
|
||||
"statsInboundDownlinkDesc" = "启用所有入站代理的下行流量统计收集。"
|
||||
"statsOutboundUplink" = "出站上传统计"
|
||||
"statsOutboundUplinkDesc" = "启用所有出站代理的上行流量统计收集。"
|
||||
"statsOutboundDownlink" = "出站下载统计"
|
||||
"statsOutboundDownlinkDesc" = "启用所有出站代理的下行流量统计收集。"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "置顶"
|
||||
@@ -426,6 +440,14 @@
|
||||
"enableDesc" = "启用内置 DNS 服务器"
|
||||
"tag" = "DNS 入站标签"
|
||||
"tagDesc" = "此标签将在路由规则中可用作入站标签"
|
||||
"clientIp" = "客户端IP"
|
||||
"clientIpDesc" = "用于在DNS查询期间通知服务器指定的IP位置"
|
||||
"disableCache" = "禁用缓存"
|
||||
"disableCacheDesc" = "禁用DNS缓存"
|
||||
"disableFallback" = "禁用回退"
|
||||
"disableFallbackDesc" = "禁用回退DNS查询"
|
||||
"disableFallbackIfMatch" = "匹配时禁用回退"
|
||||
"disableFallbackIfMatchDesc" = "当DNS服务器的匹配域名列表命中时,禁用回退DNS查询"
|
||||
"strategy" = "查询策略"
|
||||
"strategyDesc" = "解析域名的总体策略"
|
||||
"add" = "添加服务器"
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"monitor" = "監聽"
|
||||
"certificate" = "憑證"
|
||||
"fail" = "失敗"
|
||||
"comment" = "評論"
|
||||
"success" = "成功"
|
||||
"getVersion" = "獲取版本"
|
||||
"install" = "安裝"
|
||||
@@ -306,6 +307,10 @@
|
||||
"subShowInfoDesc" = "客戶端應用中將顯示剩餘流量和日期資訊"
|
||||
"subURI" = "反向代理 URI"
|
||||
"subURIDesc" = "用於代理後面的訂閱 URL 的 URI 路徑"
|
||||
"externalTrafficInformEnable" = "外部交通通知"
|
||||
"externalTrafficInformEnableDesc" = "每次流量更新時通知外部 API"
|
||||
"externalTrafficInformURI" = "外部流量通知 URI"
|
||||
"externalTrafficInformURIDesc" = "流量更新將會傳送到此 URI"
|
||||
"fragment" = "分片"
|
||||
"fragmentDesc" = "啟用 TLS hello 資料包分片"
|
||||
"fragmentSett" = "設定"
|
||||
@@ -369,6 +374,15 @@
|
||||
"dnsLogDesc" = "是否啟用 DNS 查詢日誌"
|
||||
"maskAddress" = "隱藏地址"
|
||||
"maskAddressDesc" = "IP 地址掩碼,啟用時會自動替換日誌中出現的 IP 地址。"
|
||||
"statistics" = "統計"
|
||||
"statsInboundUplink" = "入站上傳統計"
|
||||
"statsInboundUplinkDesc" = "啟用所有入站代理的上行流量統計收集。"
|
||||
"statsInboundDownlink" = "入站下載統計"
|
||||
"statsInboundDownlinkDesc" = "啟用所有入站代理的下行流量統計收集。"
|
||||
"statsOutboundUplink" = "出站上傳統計"
|
||||
"statsOutboundUplinkDesc" = "啟用所有出站代理的上行流量統計收集。"
|
||||
"statsOutboundDownlink" = "出站下載統計"
|
||||
"statsOutboundDownlinkDesc" = "啟用所有出站代理的下行流量統計收集。"
|
||||
|
||||
[pages.xray.rules]
|
||||
"first" = "置頂"
|
||||
@@ -426,6 +440,14 @@
|
||||
"enableDesc" = "啟用內建 DNS 伺服器"
|
||||
"tag" = "DNS 入站標籤"
|
||||
"tagDesc" = "此標籤將在路由規則中可用作入站標籤"
|
||||
"clientIp" = "客戶端IP"
|
||||
"clientIpDesc" = "用於在DNS查詢期間通知伺服器指定的IP位置"
|
||||
"disableCache" = "禁用快取"
|
||||
"disableCacheDesc" = "禁用DNS快取"
|
||||
"disableFallback" = "禁用回退"
|
||||
"disableFallbackDesc" = "禁用回退DNS查詢"
|
||||
"disableFallbackIfMatch" = "匹配時禁用回退"
|
||||
"disableFallbackIfMatchDesc" = "當DNS伺服器的匹配域名列表命中時,禁用回退DNS查詢"
|
||||
"strategy" = "查詢策略"
|
||||
"strategyDesc" = "解析域名的總體策略"
|
||||
"add" = "新增伺服器"
|
||||
|
||||
434
x-ui.sh
@@ -2,6 +2,7 @@
|
||||
|
||||
red='\033[0;31m'
|
||||
green='\033[0;32m'
|
||||
blue='\033[0;34m'
|
||||
yellow='\033[0;33m'
|
||||
plain='\033[0m'
|
||||
|
||||
@@ -86,6 +87,10 @@ elif [[ "${release}" == "ol" ]]; then
|
||||
if [[ ${os_version} -lt 8 ]]; then
|
||||
echo -e "${red} Please use Oracle Linux 8 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
elif [[ "${release}" == "virtuozzo" ]]; then
|
||||
if [[ ${os_version} -lt 8 ]]; then
|
||||
echo -e "${red} Please use Virtuozzo Linux 8 or higher ${plain}\n" && exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${red}Your operating system is not supported by this script.${plain}\n"
|
||||
echo "Please ensure you are using one of the following supported operating systems:"
|
||||
@@ -103,6 +108,7 @@ else
|
||||
echo "- Oracle Linux 8+"
|
||||
echo "- OpenSUSE Tumbleweed"
|
||||
echo "- Amazon Linux 2023"
|
||||
echo "- Virtuozzo Linux 8+"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -179,7 +185,7 @@ update_menu() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
|
||||
wget -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
|
||||
chmod +x /usr/local/x-ui/x-ui.sh
|
||||
chmod +x /usr/bin/x-ui
|
||||
|
||||
@@ -279,7 +285,7 @@ reset_webbasepath() {
|
||||
|
||||
# Apply the new web base path setting
|
||||
/usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}" >/dev/null 2>&1
|
||||
|
||||
|
||||
echo -e "Web base path has been reset to: ${green}${config_webBasePath}${plain}"
|
||||
echo -e "${green}Please use the new web base path to access the panel.${plain}"
|
||||
restart
|
||||
@@ -439,7 +445,7 @@ show_log() {
|
||||
1)
|
||||
journalctl -u x-ui -e --no-pager -f -p debug
|
||||
if [[ $# == 0 ]]; then
|
||||
before_show_menu
|
||||
before_show_menu
|
||||
fi
|
||||
;;
|
||||
2)
|
||||
@@ -457,9 +463,9 @@ show_log() {
|
||||
|
||||
show_banlog() {
|
||||
local system_log="/var/log/fail2ban.log"
|
||||
|
||||
|
||||
echo -e "${green}Checking ban logs...${plain}\n"
|
||||
|
||||
|
||||
if ! systemctl is-active --quiet fail2ban; then
|
||||
echo -e "${red}Fail2ban service is not running!${plain}\n"
|
||||
return 1
|
||||
@@ -503,7 +509,7 @@ bbr_menu() {
|
||||
disable_bbr
|
||||
bbr_menu
|
||||
;;
|
||||
*)
|
||||
*)
|
||||
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||
bbr_menu
|
||||
;;
|
||||
@@ -546,7 +552,7 @@ enable_bbr() {
|
||||
centos | almalinux | rocky | ol)
|
||||
yum -y update && yum -y install ca-certificates
|
||||
;;
|
||||
fedora | amzn)
|
||||
fedora | amzn | virtuozzo)
|
||||
dnf -y update && dnf -y install ca-certificates
|
||||
;;
|
||||
arch | manjaro | parch)
|
||||
@@ -574,14 +580,14 @@ enable_bbr() {
|
||||
}
|
||||
|
||||
update_shell() {
|
||||
wget -O /usr/bin/x-ui -N --no-check-certificate https://github.com/MHSanaei/3x-ui/raw/main/x-ui.sh
|
||||
wget -O /usr/bin/x-ui -N https://github.com/MHSanaei/3x-ui/raw/main/x-ui.sh
|
||||
if [[ $? != 0 ]]; then
|
||||
echo ""
|
||||
LOGE "Failed to download script, Please check whether the machine can connect Github"
|
||||
before_show_menu
|
||||
else
|
||||
chmod +x /usr/bin/x-ui
|
||||
LOGI "Upgrade script succeeded, Please rerun the script"
|
||||
LOGI "Upgrade script succeeded, Please rerun the script"
|
||||
before_show_menu
|
||||
fi
|
||||
}
|
||||
@@ -682,10 +688,13 @@ show_xray_status() {
|
||||
}
|
||||
|
||||
firewall_menu() {
|
||||
echo -e "${green}\t1.${plain} Install Firewall & open ports"
|
||||
echo -e "${green}\t2.${plain} Allowed List"
|
||||
echo -e "${green}\t3.${plain} Delete Ports from List"
|
||||
echo -e "${green}\t4.${plain} Disable Firewall"
|
||||
echo -e "${green}\t1.${plain} ${green}Install${plain} Firewall"
|
||||
echo -e "${green}\t2.${plain} Port List [numbered]"
|
||||
echo -e "${green}\t3.${plain} ${green}Open${plain} Ports"
|
||||
echo -e "${green}\t4.${plain} ${red}Delete${plain} Ports from List"
|
||||
echo -e "${green}\t5.${plain} ${green}Enable${plain} Firewall"
|
||||
echo -e "${green}\t6.${plain} ${red}Disable${plain} Firewall"
|
||||
echo -e "${green}\t7.${plain} Firewall Status"
|
||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||
read -p "Choose an option: " choice
|
||||
case "$choice" in
|
||||
@@ -693,29 +702,41 @@ firewall_menu() {
|
||||
show_menu
|
||||
;;
|
||||
1)
|
||||
open_ports
|
||||
install_firewall
|
||||
firewall_menu
|
||||
;;
|
||||
2)
|
||||
sudo ufw status
|
||||
ufw status numbered
|
||||
firewall_menu
|
||||
;;
|
||||
3)
|
||||
delete_ports
|
||||
open_ports
|
||||
firewall_menu
|
||||
;;
|
||||
4)
|
||||
sudo ufw disable
|
||||
delete_ports
|
||||
firewall_menu
|
||||
;;
|
||||
*)
|
||||
5)
|
||||
ufw enable
|
||||
firewall_menu
|
||||
;;
|
||||
6)
|
||||
ufw disable
|
||||
firewall_menu
|
||||
;;
|
||||
7)
|
||||
ufw status verbose
|
||||
firewall_menu
|
||||
;;
|
||||
*)
|
||||
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||
firewall_menu
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
open_ports() {
|
||||
install_firewall() {
|
||||
if ! command -v ufw &>/dev/null; then
|
||||
echo "ufw firewall is not installed. Installing now..."
|
||||
apt-get update
|
||||
@@ -733,13 +754,16 @@ open_ports() {
|
||||
ufw allow ssh
|
||||
ufw allow http
|
||||
ufw allow https
|
||||
ufw allow 2053/tcp
|
||||
ufw allow 2053/tcp #webPort
|
||||
ufw allow 2096/tcp #subport
|
||||
|
||||
# Enable the firewall
|
||||
ufw --force enable
|
||||
fi
|
||||
}
|
||||
|
||||
# Prompt the user to enter a list of ports
|
||||
open_ports() {
|
||||
# Prompt the user to enter the ports they want to open
|
||||
read -p "Enter the ports you want to open (e.g. 80,443,2053 or range 400-500): " ports
|
||||
|
||||
# Check if the input is valid
|
||||
@@ -755,70 +779,112 @@ open_ports() {
|
||||
# Split the range into start and end ports
|
||||
start_port=$(echo $port | cut -d'-' -f1)
|
||||
end_port=$(echo $port | cut -d'-' -f2)
|
||||
# Open the port range
|
||||
ufw allow $start_port:$end_port/tcp
|
||||
ufw allow $start_port:$end_port/udp
|
||||
else
|
||||
# Open the single port
|
||||
ufw allow "$port"
|
||||
fi
|
||||
done
|
||||
|
||||
# Confirm that the ports are open
|
||||
echo "The following ports are now open:"
|
||||
ufw status | grep "ALLOW" | grep -Eo "[0-9]+(/[a-z]+)?"
|
||||
|
||||
echo "Firewall status:"
|
||||
ufw status verbose
|
||||
# Confirm that the ports are opened
|
||||
echo "Opened the specified ports:"
|
||||
for port in "${PORT_LIST[@]}"; do
|
||||
if [[ $port == *-* ]]; then
|
||||
start_port=$(echo $port | cut -d'-' -f1)
|
||||
end_port=$(echo $port | cut -d'-' -f2)
|
||||
# Check if the port range has been successfully opened
|
||||
(ufw status | grep -q "$start_port:$end_port") && echo "$start_port-$end_port"
|
||||
else
|
||||
# Check if the individual port has been successfully opened
|
||||
(ufw status | grep -q "$port") && echo "$port"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
delete_ports() {
|
||||
# Prompt the user to enter the ports they want to delete
|
||||
read -p "Enter the ports you want to delete (e.g. 80,443,2053 or range 400-500): " ports
|
||||
# Display current rules with numbers
|
||||
echo "Current UFW rules:"
|
||||
ufw status numbered
|
||||
|
||||
# Check if the input is valid
|
||||
if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then
|
||||
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
|
||||
# Ask the user how they want to delete rules
|
||||
echo "Do you want to delete rules by:"
|
||||
echo "1) Rule numbers"
|
||||
echo "2) Ports"
|
||||
read -p "Enter your choice (1 or 2): " choice
|
||||
|
||||
if [[ $choice -eq 1 ]]; then
|
||||
# Deleting by rule numbers
|
||||
read -p "Enter the rule numbers you want to delete (1, 2, etc.): " rule_numbers
|
||||
|
||||
# Validate the input
|
||||
if ! [[ $rule_numbers =~ ^([0-9]+)(,[0-9]+)*$ ]]; then
|
||||
echo "Error: Invalid input. Please enter a comma-separated list of rule numbers." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Split numbers into an array
|
||||
IFS=',' read -ra RULE_NUMBERS <<<"$rule_numbers"
|
||||
for rule_number in "${RULE_NUMBERS[@]}"; do
|
||||
# Delete the rule by number
|
||||
ufw delete "$rule_number" || echo "Failed to delete rule number $rule_number"
|
||||
done
|
||||
|
||||
echo "Selected rules have been deleted."
|
||||
|
||||
elif [[ $choice -eq 2 ]]; then
|
||||
# Deleting by ports
|
||||
read -p "Enter the ports you want to delete (e.g. 80,443,2053 or range 400-500): " ports
|
||||
|
||||
# Validate the input
|
||||
if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then
|
||||
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Split ports into an array
|
||||
IFS=',' read -ra PORT_LIST <<<"$ports"
|
||||
for port in "${PORT_LIST[@]}"; do
|
||||
if [[ $port == *-* ]]; then
|
||||
# Split the port range
|
||||
start_port=$(echo $port | cut -d'-' -f1)
|
||||
end_port=$(echo $port | cut -d'-' -f2)
|
||||
# Delete the port range
|
||||
ufw delete allow $start_port:$end_port/tcp
|
||||
ufw delete allow $start_port:$end_port/udp
|
||||
else
|
||||
# Delete a single port
|
||||
ufw delete allow "$port"
|
||||
fi
|
||||
done
|
||||
|
||||
# Confirmation of deletion
|
||||
echo "Deleted the specified ports:"
|
||||
for port in "${PORT_LIST[@]}"; do
|
||||
if [[ $port == *-* ]]; then
|
||||
start_port=$(echo $port | cut -d'-' -f1)
|
||||
end_port=$(echo $port | cut -d'-' -f2)
|
||||
# Check if the port range has been deleted
|
||||
(ufw status | grep -q "$start_port:$end_port") || echo "$start_port-$end_port"
|
||||
else
|
||||
# Check if the individual port has been deleted
|
||||
(ufw status | grep -q "$port") || echo "$port"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "${red}Error:${plain} Invalid choice. Please enter 1 or 2." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Delete the specified ports using ufw
|
||||
IFS=',' read -ra PORT_LIST <<<"$ports"
|
||||
for port in "${PORT_LIST[@]}"; do
|
||||
if [[ $port == *-* ]]; then
|
||||
# Split the range into start and end ports
|
||||
start_port=$(echo $port | cut -d'-' -f1)
|
||||
end_port=$(echo $port | cut -d'-' -f2)
|
||||
# Delete the port range
|
||||
ufw delete allow $start_port:$end_port/tcp
|
||||
ufw delete allow $start_port:$end_port/udp
|
||||
else
|
||||
ufw delete allow "$port"
|
||||
fi
|
||||
done
|
||||
|
||||
# Confirm that the ports are deleted
|
||||
|
||||
echo "Deleted the specified ports:"
|
||||
for port in "${PORT_LIST[@]}"; do
|
||||
if [[ $port == *-* ]]; then
|
||||
start_port=$(echo $port | cut -d'-' -f1)
|
||||
end_port=$(echo $port | cut -d'-' -f2)
|
||||
# Check if the port range has been successfully deleted
|
||||
(ufw status | grep -q "$start_port:$end_port") || echo "$start_port-$end_port"
|
||||
else
|
||||
# Check if the individual port has been successfully deleted
|
||||
(ufw status | grep -q "$port") || echo "$port"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
update_geo() {
|
||||
echo -e "${green}\t1.${plain} Loyalsoldier (geoip.dat, geosite.dat)"
|
||||
echo -e "${green}\t2.${plain} chocolate4u (geoip_IR.dat, geosite_IR.dat)"
|
||||
echo -e "${green}\t3.${plain} vuong2023 (geoip_VN.dat, geosite_VN.dat)"
|
||||
echo -e "${green}\t3.${plain} runetfreedom (geoip_RU.dat, geosite_RU.dat)"
|
||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||
read -p "Choose an option: " choice
|
||||
|
||||
systemctl stop x-ui
|
||||
cd /usr/local/x-ui/bin
|
||||
|
||||
case "$choice" in
|
||||
@@ -826,6 +892,7 @@ update_geo() {
|
||||
show_menu
|
||||
;;
|
||||
1)
|
||||
systemctl stop x-ui
|
||||
rm -f geoip.dat geosite.dat
|
||||
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
@@ -833,6 +900,7 @@ update_geo() {
|
||||
restart
|
||||
;;
|
||||
2)
|
||||
systemctl stop x-ui
|
||||
rm -f geoip_IR.dat geosite_IR.dat
|
||||
wget -O geoip_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||
@@ -840,10 +908,11 @@ update_geo() {
|
||||
restart
|
||||
;;
|
||||
3)
|
||||
rm -f geoip_VN.dat geosite_VN.dat
|
||||
wget -O geoip_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -O geosite_VN.dat -N https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat
|
||||
echo -e "${green}vuong2023 datasets have been updated successfully!${plain}"
|
||||
systemctl stop x-ui
|
||||
rm -f geoip_RU.dat geosite_RU.dat
|
||||
wget -O geoip_RU.dat -N https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget -O geosite_RU.dat -N https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
echo -e "${green}runetfreedom datasets have been updated successfully!${plain}"
|
||||
restart
|
||||
;;
|
||||
*)
|
||||
@@ -852,7 +921,6 @@ update_geo() {
|
||||
;;
|
||||
esac
|
||||
|
||||
systemctl start x-ui
|
||||
before_show_menu
|
||||
}
|
||||
|
||||
@@ -1005,7 +1073,7 @@ ssl_cert_issue() {
|
||||
centos | almalinux | rocky | ol)
|
||||
yum -y update && yum -y install socat
|
||||
;;
|
||||
fedora | amzn)
|
||||
fedora | amzn | virtuozzo)
|
||||
dnf -y update && dnf -y install socat
|
||||
;;
|
||||
arch | manjaro | parch)
|
||||
@@ -1183,8 +1251,14 @@ ssl_cert_issue_CF() {
|
||||
fi
|
||||
|
||||
# Install the certificate
|
||||
mkdir -p ${certPath}/${CF_Domain}
|
||||
if [ $? -ne 0 ]; then
|
||||
LOGE "Failed to create directory: ${certPath}/${CF_Domain}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} \
|
||||
--cert-file ${certPath}/${CF_Domain}/fullchain.pem \
|
||||
--fullchain-file ${certPath}/${CF_Domain}/fullchain.pem \
|
||||
--key-file ${certPath}/${CF_Domain}/privkey.pem
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
@@ -1232,40 +1306,47 @@ ssl_cert_issue_CF() {
|
||||
run_speedtest() {
|
||||
# Check if Speedtest is already installed
|
||||
if ! command -v speedtest &>/dev/null; then
|
||||
# If not installed, install it
|
||||
local pkg_manager=""
|
||||
local speedtest_install_script=""
|
||||
|
||||
if command -v dnf &>/dev/null; then
|
||||
pkg_manager="dnf"
|
||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
|
||||
elif command -v yum &>/dev/null; then
|
||||
pkg_manager="yum"
|
||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
|
||||
elif command -v apt-get &>/dev/null; then
|
||||
pkg_manager="apt-get"
|
||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
|
||||
elif command -v apt &>/dev/null; then
|
||||
pkg_manager="apt"
|
||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
|
||||
fi
|
||||
|
||||
if [[ -z $pkg_manager ]]; then
|
||||
echo "Error: Package manager not found. You may need to install Speedtest manually."
|
||||
return 1
|
||||
# If not installed, determine installation method
|
||||
if command -v snap &>/dev/null; then
|
||||
# Use snap to install Speedtest
|
||||
echo "Installing Speedtest using snap..."
|
||||
snap install speedtest
|
||||
else
|
||||
curl -s $speedtest_install_script | bash
|
||||
$pkg_manager install -y speedtest
|
||||
# Fallback to using package managers
|
||||
local pkg_manager=""
|
||||
local speedtest_install_script=""
|
||||
|
||||
if command -v dnf &>/dev/null; then
|
||||
pkg_manager="dnf"
|
||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
|
||||
elif command -v yum &>/dev/null; then
|
||||
pkg_manager="yum"
|
||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh"
|
||||
elif command -v apt-get &>/dev/null; then
|
||||
pkg_manager="apt-get"
|
||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
|
||||
elif command -v apt &>/dev/null; then
|
||||
pkg_manager="apt"
|
||||
speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh"
|
||||
fi
|
||||
|
||||
if [[ -z $pkg_manager ]]; then
|
||||
echo "Error: Package manager not found. You may need to install Speedtest manually."
|
||||
return 1
|
||||
else
|
||||
echo "Installing Speedtest using $pkg_manager..."
|
||||
curl -s $speedtest_install_script | bash
|
||||
$pkg_manager install -y speedtest
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run Speedtest
|
||||
speedtest
|
||||
}
|
||||
|
||||
create_iplimit_jails() {
|
||||
# Use default bantime if not passed => 15 minutes
|
||||
local bantime="${1:-15}"
|
||||
# Use default bantime if not passed => 30 minutes
|
||||
local bantime="${1:-30}"
|
||||
|
||||
# Uncomment 'allowipv6 = auto' in fail2ban.conf
|
||||
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
|
||||
@@ -1280,7 +1361,7 @@ create_iplimit_jails() {
|
||||
enabled=true
|
||||
backend=auto
|
||||
filter=3x-ipl
|
||||
action = %(known/action)s[name=%(__name__)s, protocol="%(protocol)s", chain="%(chain)s"]
|
||||
action=3x-ipl
|
||||
logpath=${iplimit_log_path}
|
||||
maxretry=2
|
||||
findtime=32
|
||||
@@ -1296,7 +1377,7 @@ EOF
|
||||
|
||||
cat << EOF > /etc/fail2ban/action.d/3x-ipl.conf
|
||||
[INCLUDES]
|
||||
before = iptables-common.conf
|
||||
before = iptables-allports.conf
|
||||
|
||||
[Definition]
|
||||
actionstart = <iptables> -N f2b-<name>
|
||||
@@ -1316,8 +1397,6 @@ actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
|
||||
echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = <F-USER> [IP] = <ip> unbanned." >> ${iplimit_banned_log_path}
|
||||
|
||||
[Init]
|
||||
# Use default settings from iptables-common.conf
|
||||
# This will automatically handle both IPv4 and IPv6
|
||||
name = default
|
||||
protocol = tcp
|
||||
chain = INPUT
|
||||
@@ -1341,15 +1420,22 @@ iplimit_remove_conflicts() {
|
||||
done
|
||||
}
|
||||
|
||||
ip_validation() {
|
||||
ipv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
|
||||
ipv4_regex="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)$"
|
||||
}
|
||||
|
||||
iplimit_main() {
|
||||
echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit"
|
||||
echo -e "${green}\t2.${plain} Change Ban Duration"
|
||||
echo -e "${green}\t3.${plain} Unban Everyone"
|
||||
echo -e "${green}\t4.${plain} Ban Logs"
|
||||
echo -e "${green}\t5.${plain} Real-Time Logs"
|
||||
echo -e "${green}\t6.${plain} Service Status"
|
||||
echo -e "${green}\t7.${plain} Service Restart"
|
||||
echo -e "${green}\t8.${plain} Uninstall Fail2ban and IP Limit"
|
||||
echo -e "${green}\t5.${plain} Ban an IP Address"
|
||||
echo -e "${green}\t6.${plain} Unban an IP Address"
|
||||
echo -e "${green}\t7.${plain} Real-Time Logs"
|
||||
echo -e "${green}\t8.${plain} Service Status"
|
||||
echo -e "${green}\t9.${plain} Service Restart"
|
||||
echo -e "${green}\t10.${plain} Uninstall Fail2ban and IP Limit"
|
||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||
read -p "Choose an option: " choice
|
||||
case "$choice" in
|
||||
@@ -1391,22 +1477,44 @@ iplimit_main() {
|
||||
iplimit_main
|
||||
;;
|
||||
5)
|
||||
tail -f /var/log/fail2ban.log
|
||||
read -rp "Enter the IP address you want to ban: " ban_ip
|
||||
ip_validation
|
||||
if [[ $ban_ip =~ $ipv4_regex || $ban_ip =~ $ipv6_regex ]]; then
|
||||
fail2ban-client set 3x-ipl banip "$ban_ip"
|
||||
echo -e "${green}IP Address ${ban_ip} has been banned successfully.${plain}"
|
||||
else
|
||||
echo -e "${red}Invalid IP address format! Please try again.${plain}"
|
||||
fi
|
||||
iplimit_main
|
||||
;;
|
||||
6)
|
||||
service fail2ban status
|
||||
read -rp "Enter the IP address you want to unban: " unban_ip
|
||||
ip_validation
|
||||
if [[ $unban_ip =~ $ipv4_regex || $unban_ip =~ $ipv6_regex ]]; then
|
||||
fail2ban-client set 3x-ipl unbanip "$unban_ip"
|
||||
echo -e "${green}IP Address ${unban_ip} has been unbanned successfully.${plain}"
|
||||
else
|
||||
echo -e "${red}Invalid IP address format! Please try again.${plain}"
|
||||
fi
|
||||
iplimit_main
|
||||
;;
|
||||
7)
|
||||
systemctl restart fail2ban
|
||||
tail -f /var/log/fail2ban.log
|
||||
iplimit_main
|
||||
;;
|
||||
8)
|
||||
service fail2ban status
|
||||
iplimit_main
|
||||
;;
|
||||
9)
|
||||
systemctl restart fail2ban
|
||||
iplimit_main
|
||||
;;
|
||||
10)
|
||||
remove_iplimit
|
||||
iplimit_main
|
||||
;;
|
||||
*)
|
||||
*)
|
||||
echo -e "${red}Invalid option. Please select a valid number.${plain}\n"
|
||||
iplimit_main
|
||||
;;
|
||||
@@ -1433,7 +1541,7 @@ install_iplimit() {
|
||||
yum update -y && yum install epel-release -y
|
||||
yum -y install fail2ban
|
||||
;;
|
||||
fedora | amzn)
|
||||
fedora | amzn | virtuozzo)
|
||||
dnf -y update && dnf -y install fail2ban
|
||||
;;
|
||||
arch | manjaro | parch)
|
||||
@@ -1514,7 +1622,7 @@ remove_iplimit() {
|
||||
yum remove fail2ban -y
|
||||
yum autoremove -y
|
||||
;;
|
||||
fedora | amzn)
|
||||
fedora | amzn | virtuozzo)
|
||||
dnf remove fail2ban -y
|
||||
dnf autoremove -y
|
||||
;;
|
||||
@@ -1617,61 +1725,63 @@ SSH_port_forwarding() {
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
echo "x-ui control menu usages: "
|
||||
echo "------------------------------------------"
|
||||
echo -e "SUBCOMMANDS:"
|
||||
echo -e "x-ui - Admin Management Script"
|
||||
echo -e "x-ui start - Start"
|
||||
echo -e "x-ui stop - Stop"
|
||||
echo -e "x-ui restart - Restart"
|
||||
echo -e "x-ui status - Current Status"
|
||||
echo -e "x-ui settings - Current Settings"
|
||||
echo -e "x-ui enable - Enable Autostart on OS Startup"
|
||||
echo -e "x-ui disable - Disable Autostart on OS Startup"
|
||||
echo -e "x-ui log - Check logs"
|
||||
echo -e "x-ui banlog - Check Fail2ban ban logs"
|
||||
echo -e "x-ui update - Update"
|
||||
echo -e "x-ui custom - custom version"
|
||||
echo -e "x-ui install - Install"
|
||||
echo -e "x-ui uninstall - Uninstall"
|
||||
echo "------------------------------------------"
|
||||
echo -e "┌───────────────────────────────────────────────────────┐
|
||||
│ ${blue}x-ui control menu usages (subcommands):${plain} │
|
||||
│ │
|
||||
│ ${blue}x-ui${plain} - Admin Management Script │
|
||||
│ ${blue}x-ui start${plain} - Start │
|
||||
│ ${blue}x-ui stop${plain} - Stop │
|
||||
│ ${blue}x-ui restart${plain} - Restart │
|
||||
│ ${blue}x-ui status${plain} - Current Status │
|
||||
│ ${blue}x-ui settings${plain} - Current Settings │
|
||||
│ ${blue}x-ui enable${plain} - Enable Autostart on OS Startup │
|
||||
│ ${blue}x-ui disable${plain} - Disable Autostart on OS Startup │
|
||||
│ ${blue}x-ui log${plain} - Check logs │
|
||||
│ ${blue}x-ui banlog${plain} - Check Fail2ban ban logs │
|
||||
│ ${blue}x-ui update${plain} - Update │
|
||||
│ ${blue}x-ui legacy${plain} - legacy version │
|
||||
│ ${blue}x-ui install${plain} - Install │
|
||||
│ ${blue}x-ui uninstall${plain} - Uninstall │
|
||||
└───────────────────────────────────────────────────────┘"
|
||||
}
|
||||
|
||||
show_menu() {
|
||||
echo -e "
|
||||
${green}3X-UI Panel Management Script${plain}
|
||||
${green}0.${plain} Exit Script
|
||||
————————————————
|
||||
${green}1.${plain} Install
|
||||
${green}2.${plain} Update
|
||||
${green}3.${plain} Update Menu
|
||||
${green}4.${plain} Legacy Version
|
||||
${green}5.${plain} Uninstall
|
||||
————————————————
|
||||
${green}6.${plain} Reset Username & Password & Secret Token
|
||||
${green}7.${plain} Reset Web Base Path
|
||||
${green}8.${plain} Reset Settings
|
||||
${green}9.${plain} Change Port
|
||||
${green}10.${plain} View Current Settings
|
||||
————————————————
|
||||
${green}11.${plain} Start
|
||||
${green}12.${plain} Stop
|
||||
${green}13.${plain} Restart
|
||||
${green}14.${plain} Check Status
|
||||
${green}15.${plain} Logs Management
|
||||
————————————————
|
||||
${green}16.${plain} Enable Autostart
|
||||
${green}17.${plain} Disable Autostart
|
||||
————————————————
|
||||
${green}18.${plain} SSL Certificate Management
|
||||
${green}19.${plain} Cloudflare SSL Certificate
|
||||
${green}20.${plain} IP Limit Management
|
||||
${green}21.${plain} Firewall Management
|
||||
${green}22.${plain} SSH Port Forwarding Management
|
||||
————————————————
|
||||
${green}23.${plain} Enable BBR
|
||||
${green}24.${plain} Update Geo Files
|
||||
${green}25.${plain} Speedtest by Ookla
|
||||
╔────────────────────────────────────────────────╗
|
||||
│ ${green}3X-UI Panel Management Script${plain} │
|
||||
│ ${green}0.${plain} Exit Script │
|
||||
│────────────────────────────────────────────────│
|
||||
│ ${green}1.${plain} Install │
|
||||
│ ${green}2.${plain} Update │
|
||||
│ ${green}3.${plain} Update Menu │
|
||||
│ ${green}4.${plain} Legacy Version │
|
||||
│ ${green}5.${plain} Uninstall │
|
||||
│────────────────────────────────────────────────│
|
||||
│ ${green}6.${plain} Reset Username & Password & Secret Token │
|
||||
│ ${green}7.${plain} Reset Web Base Path │
|
||||
│ ${green}8.${plain} Reset Settings │
|
||||
│ ${green}9.${plain} Change Port │
|
||||
│ ${green}10.${plain} View Current Settings │
|
||||
│────────────────────────────────────────────────│
|
||||
│ ${green}11.${plain} Start │
|
||||
│ ${green}12.${plain} Stop │
|
||||
│ ${green}13.${plain} Restart │
|
||||
│ ${green}14.${plain} Check Status │
|
||||
│ ${green}15.${plain} Logs Management │
|
||||
│────────────────────────────────────────────────│
|
||||
│ ${green}16.${plain} Enable Autostart │
|
||||
│ ${green}17.${plain} Disable Autostart │
|
||||
│────────────────────────────────────────────────│
|
||||
│ ${green}18.${plain} SSL Certificate Management │
|
||||
│ ${green}19.${plain} Cloudflare SSL Certificate │
|
||||
│ ${green}20.${plain} IP Limit Management │
|
||||
│ ${green}21.${plain} Firewall Management │
|
||||
│ ${green}22.${plain} SSH Port Forwarding Management │
|
||||
│────────────────────────────────────────────────│
|
||||
│ ${green}23.${plain} Enable BBR │
|
||||
│ ${green}24.${plain} Update Geo Files │
|
||||
│ ${green}25.${plain} Speedtest by Ookla │
|
||||
╚────────────────────────────────────────────────╝
|
||||
"
|
||||
show_status
|
||||
echo && read -p "Please enter your selection [0-25]: " num
|
||||
|
||||
@@ -16,11 +16,24 @@ type LogWriter struct {
|
||||
}
|
||||
|
||||
func (lw *LogWriter) Write(m []byte) (n int, err error) {
|
||||
regex := regexp.MustCompile(`^(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \[([^\]]+)\] (.+)$`)
|
||||
crashRegex := regexp.MustCompile(`(?i)(panic|exception|stack trace|fatal error)`)
|
||||
|
||||
// Convert the data to a string
|
||||
message := strings.TrimSpace(string(m))
|
||||
|
||||
// Check if the message contains a crash
|
||||
if crashRegex.MatchString(message) {
|
||||
logger.Debug("Core crash detected:\n", message)
|
||||
lw.lastLine = message
|
||||
err1 := writeCrachReport(m)
|
||||
if err1 != nil {
|
||||
logger.Error("Unable to write crash report:", err1)
|
||||
}
|
||||
return len(m), nil
|
||||
}
|
||||
|
||||
regex := regexp.MustCompile(`^(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \[([^\]]+)\] (.+)$`)
|
||||
messages := strings.Split(message, "\n")
|
||||
lw.lastLine = messages[len(messages)-1]
|
||||
|
||||
for _, msg := range messages {
|
||||
matches := regex.FindStringSubmatch(msg)
|
||||
@@ -42,9 +55,10 @@ func (lw *LogWriter) Write(m []byte) (n int, err error) {
|
||||
default:
|
||||
logger.Debug("XRAY: " + msg)
|
||||
}
|
||||
lw.lastLine = ""
|
||||
} else if msg != "" {
|
||||
logger.Debug("XRAY: " + msg)
|
||||
return len(m), nil
|
||||
lw.lastLine = msg
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -241,3 +241,8 @@ func (p *process) Stop() error {
|
||||
}
|
||||
return p.cmd.Process.Signal(syscall.SIGTERM)
|
||||
}
|
||||
|
||||
func writeCrachReport(m []byte) error {
|
||||
crashReportPath := config.GetBinFolderPath() + "/core_crash_" + time.Now().Format("20060102_150405") + ".log"
|
||||
return os.WriteFile(crashReportPath, m, os.ModePerm)
|
||||
}
|
||||
|
||||