mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-03-19 17:15:49 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
236dddf482 | ||
|
|
8e472838d8 | ||
|
|
b75a1ef5e1 | ||
|
|
d956f78347 | ||
|
|
8ef447a997 | ||
|
|
520f7a2d15 | ||
|
|
3ded4ee658 | ||
|
|
bea19a263d | ||
|
|
878e0d02cd | ||
|
|
b15ea1f74d | ||
|
|
431d7350a5 | ||
|
|
127bea7f73 | ||
|
|
7c58bcbb46 | ||
|
|
fec9b25248 | ||
|
|
728166bd1a | ||
|
|
d376ce057c | ||
|
|
5e6e900e64 | ||
|
|
19f7938617 | ||
|
|
fe791b6e99 | ||
|
|
a02bf3195d | ||
|
|
3ea05d30c1 | ||
|
|
40ebf2902e | ||
|
|
14253c3586 | ||
|
|
7b15274c84 | ||
|
|
6545d8b61d | ||
|
|
6f4eefe601 | ||
|
|
510c35f450 | ||
|
|
00addb0dd9 | ||
|
|
db140a1e9b | ||
|
|
db945e2fbd | ||
|
|
667fac15f4 | ||
|
|
29033a7828 |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
|||||||
cd x-ui/bin
|
cd x-ui/bin
|
||||||
|
|
||||||
# Download dependencies
|
# Download dependencies
|
||||||
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.3.6/"
|
Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.3.31/"
|
||||||
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
if [ "${{ matrix.platform }}" == "amd64" ]; then
|
||||||
wget -q ${Xray_URL}Xray-linux-64.zip
|
wget -q ${Xray_URL}Xray-linux-64.zip
|
||||||
unzip Xray-linux-64.zip
|
unzip Xray-linux-64.zip
|
||||||
|
|||||||
37
.gitignore
vendored
37
.gitignore
vendored
@@ -1,16 +1,37 @@
|
|||||||
.idea
|
# Ignore editor and IDE settings
|
||||||
.vscode
|
.idea/
|
||||||
.cache
|
.vscode/
|
||||||
|
.cache/
|
||||||
.sync*
|
.sync*
|
||||||
*.tar.gz
|
|
||||||
|
# Ignore log files
|
||||||
*.log
|
*.log
|
||||||
access.log
|
|
||||||
error.log
|
# Ignore temporary files
|
||||||
tmp
|
tmp/
|
||||||
main
|
*.tar.gz
|
||||||
|
|
||||||
|
# Ignore build and distribution directories
|
||||||
backup/
|
backup/
|
||||||
bin/
|
bin/
|
||||||
dist/
|
dist/
|
||||||
release/
|
release/
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Ignore compiled binaries
|
||||||
|
main
|
||||||
|
|
||||||
|
# Ignore script and executable files
|
||||||
/release.sh
|
/release.sh
|
||||||
/x-ui
|
/x-ui
|
||||||
|
|
||||||
|
# Ignore OS specific files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Ignore Go specific files
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
|
||||||
|
# Ignore Docker specific files
|
||||||
|
docker-compose.override.yml
|
||||||
@@ -27,7 +27,7 @@ case $1 in
|
|||||||
esac
|
esac
|
||||||
mkdir -p build/bin
|
mkdir -p build/bin
|
||||||
cd build/bin
|
cd build/bin
|
||||||
wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.3.6/Xray-linux-${ARCH}.zip"
|
wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.3.31/Xray-linux-${ARCH}.zip"
|
||||||
unzip "Xray-linux-${ARCH}.zip"
|
unzip "Xray-linux-${ARCH}.zip"
|
||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||||
mv xray "xray-linux-${FNAME}"
|
mv xray "xray-linux-${FNAME}"
|
||||||
|
|||||||
569
README.ar_EG.md
Normal file
569
README.ar_EG.md
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.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
|
||||||
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
## تثبيت النسخة القديمة (مش موصى بيها)
|
||||||
|
|
||||||
|
لو عايز تثبت نسخة معينة، استخدم الأمر ده، مثلاً نسخة `v1.7.9`:
|
||||||
|
```bash
|
||||||
|
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. هتلاقي الخيارات دي:
|
||||||
|
- **Get SSL:** الحصول على شهادة SSL.
|
||||||
|
- **Revoke:** إلغاء شهادة SSL موجودة.
|
||||||
|
- **Force Renew:** تجديد شهادة SSL بالقوة.
|
||||||
|
- **Show Existing Domains:** عرض كل شهادات الدومين المتوفرة على السيرفر.
|
||||||
|
- **Set Certificate Paths for the Panel:** تعيين مسارات الشهادة عشان البانل يستخدمها.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
- الـ Global API Key بتاع Cloudflare.
|
||||||
|
- الدومين لازم يكون مربوط للسيرفر الحالي عن طريق Cloudflare.
|
||||||
|
|
||||||
|
**كيفية الحصول على Global API Key من Cloudflare:**
|
||||||
|
|
||||||
|
1. شغّل أمر `x-ui` في الترمينال واختار "Cloudflare SSL Certificate".
|
||||||
|
2. ادخل على [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens).
|
||||||
|
3. دوس على "View Global API Key" (شوف الصورة التوضيحية):
|
||||||
|

|
||||||
|
4. يمكن تحتاج تعيد تسجيل الدخول، وبعدها هتظهر الـ API Key (شوف الصورة التوضيحية):
|
||||||
|

|
||||||
|
|
||||||
|
عند الاستخدام، ادخل اسم الدومين، البريد الإلكتروني، وAPI Key. المخطط كالتالي:
|
||||||
|

|
||||||
|
|
||||||
|
</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
|
||||||
|
```
|
||||||
|
ممكن تضيف الخيار ```--pull always``` عشان Docker يسحب أحدث صورة لو موجودة. (راجع [مستندات Docker](https://docs.docker.com/reference/cli/docker/container/run/#pull) للمزيد من التفاصيل).
|
||||||
|
|
||||||
|
**أو**
|
||||||
|
```sh
|
||||||
|
docker run -itd \
|
||||||
|
-e XRAY_VMESS_AEAD_FORCED=false \
|
||||||
|
-v $PWD/db/:/etc/x-ui/ \
|
||||||
|
-v $PWD/cert/:/root/cert/ \
|
||||||
|
--network=host \
|
||||||
|
--restart=unless-stopped \
|
||||||
|
--name 3x-ui \
|
||||||
|
ghcr.io/mhsanaei/3x-ui:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **التحديث إلى أحدث نسخة:**
|
||||||
|
```sh
|
||||||
|
cd 3x-ui
|
||||||
|
docker compose down
|
||||||
|
docker compose pull 3x-ui
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **إزالة 3x-ui من Docker:**
|
||||||
|
```sh
|
||||||
|
docker stop 3x-ui
|
||||||
|
docker rm 3x-ui
|
||||||
|
cd --
|
||||||
|
rm -r 3x-ui
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## إعدادات Nginx
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>اضغط لعرض إعدادات البروكسي العكسي (Reverse Proxy)</summary>
|
||||||
|
|
||||||
|
#### Reverse Proxy باستخدام Nginx
|
||||||
|
```nginx
|
||||||
|
location / {
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Range $http_range;
|
||||||
|
proxy_set_header If-Range $http_if_range;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_pass http://127.0.0.1:2053;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### استخدام Nginx مع sub-path
|
||||||
|
- تأكد إن "URI Path" في إعدادات `/sub` للبانل متطابق.
|
||||||
|
- لازم يكون رابط `url` في إعدادات البانل منتهي بـ `/`.
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /sub {
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Range $http_range;
|
||||||
|
proxy_set_header If-Range $http_if_range;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_pass http://127.0.0.1:2053;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## نظام التشغيل الموصى به
|
||||||
|
|
||||||
|
- Ubuntu 22.04+
|
||||||
|
- Debian 12+
|
||||||
|
- CentOS 8+
|
||||||
|
- OpenEuler 22.03+
|
||||||
|
- Fedora 36+
|
||||||
|
- Arch Linux
|
||||||
|
- Parch Linux
|
||||||
|
- Manjaro
|
||||||
|
- Armbian
|
||||||
|
- AlmaLinux 9.5+
|
||||||
|
- Rocky Linux 9.5+
|
||||||
|
- Oracle Linux 8+
|
||||||
|
- OpenSUSE Tubleweed
|
||||||
|
- Amazon Linux 2023
|
||||||
|
- Virtuozzo Linux 8+
|
||||||
|
- Windows x64
|
||||||
|
|
||||||
|
## المعماريات والأجهزة المدعومة
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>اضغط لعرض تفاصيل المعماريات والأجهزة المدعومة</summary>
|
||||||
|
|
||||||
|
منصتنا بتدعم مجموعة متنوعة من المعماريات والأجهزة عشان تناسب بيئات مختلفة. أبرز المعماريات هي:
|
||||||
|
|
||||||
|
- **amd64:** المعمارية القياسية للكمبيوترات الشخصية والسيرفرات.
|
||||||
|
- **x86 / i386:** مستخدمة على نطاق واسع في أجهزة الديسكتوب واللاب توب.
|
||||||
|
- **armv8 / arm64 / aarch64:** موجهة للأجهزة المحمولة والمضمنة زي Raspberry Pi 4, Raspberry Pi 3, Raspberry Pi Zero 2/Zero 2 W, Orange Pi 3 LTS، وغيرها.
|
||||||
|
- **armv7 / arm / arm32:** للأجهزة المحمولة والأجهزة المضمنة القديمة، مثل Orange Pi Zero LTS, Orange Pi PC Plus, Raspberry Pi 2.
|
||||||
|
- **armv6 / arm / arm32:** للأجهزة المضمنة القديمة جداً، زي Raspberry Pi 1, Raspberry Pi Zero/Zero W.
|
||||||
|
- **armv5 / arm / arm32:** معمارية أقدم مرتبطة بالأنظمة المضمنة القديمة.
|
||||||
|
- **s390x:** مستخدمة في الحواسيب الرئيسية من IBM وتوفر أداء عالي واعتمادية للمشاريع الكبيرة.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## اللغات
|
||||||
|
|
||||||
|
- English (إنجليزي)
|
||||||
|
- Persian (فارسي)
|
||||||
|
- Traditional Chinese (الصيني التقليدي)
|
||||||
|
- Simplified Chinese (الصيني المبسط)
|
||||||
|
- Japanese (ياباني)
|
||||||
|
- Russian (روسي)
|
||||||
|
- Vietnamese (فيتنامي)
|
||||||
|
- Spanish (إسباني)
|
||||||
|
- Indonesian (إندونيسي)
|
||||||
|
- Ukrainian (أوكراني)
|
||||||
|
- Turkish (تركي)
|
||||||
|
- Português (Brazil) (برتغالي - البرازيل)
|
||||||
|
|
||||||
|
## الميزات
|
||||||
|
|
||||||
|
- مراقبة حالة النظام
|
||||||
|
- البحث داخل كل الإدخالات والعملاء
|
||||||
|
- ثيم داكن وفاتح
|
||||||
|
- دعم تعدد المستخدمين والبروتوكولات
|
||||||
|
- دعم بروتوكولات زي VMESS، VLESS، Trojan، Shadowsocks، Dokodemo-door، Socks، HTTP، wireguard
|
||||||
|
- دعم بروتوكولات XTLS الأصلية مثل RPRX-Direct، Vision، REALITY
|
||||||
|
- إحصائيات الترافيك، تحديد حد للترافيك، وتحديد فترة انتهاء الصلاحية
|
||||||
|
- قوالب إعدادات Xray قابلة للتخصيص
|
||||||
|
- دعم لوحة تحكم عبر HTTPS (بإستخدام الدومين الخاص وشهادة SSL)
|
||||||
|
- دعم التقديم على شهادات SSL بلمسة واحدة والتجديد التلقائي
|
||||||
|
- لمزيد من إعدادات التخصيص المتقدمة، راجع إعدادات البانل
|
||||||
|
- إصلاح مسارات الـ API (سيتم إنشاء إعدادات للمستخدم مع الـ API)
|
||||||
|
- دعم تغيير الإعدادات من خلال الخيارات المتوفرة في البانل
|
||||||
|
- دعم تصدير/استيراد قاعدة البيانات من خلال البانل
|
||||||
|
|
||||||
|
## إعدادات البانل الافتراضية
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>اضغط لعرض تفاصيل الإعدادات الافتراضية</summary>
|
||||||
|
|
||||||
|
### اسم المستخدم، الباسورد، البورت ومسار الويب الأساسي
|
||||||
|
|
||||||
|
لو مش هتعدل الإعدادات دي، هتتولد تلقائياً (ده مش بينطبق على Docker).
|
||||||
|
|
||||||
|
**الإعدادات الافتراضية لـ Docker:**
|
||||||
|
- **اسم المستخدم:** admin
|
||||||
|
- **الباسورد:** admin
|
||||||
|
- **البورت:** 2053
|
||||||
|
|
||||||
|
### إدارة قاعدة البيانات:
|
||||||
|
|
||||||
|
ممكن تعمل نسخ احتياطية واسترجاع لقاعدة البيانات مباشرة من البانل.
|
||||||
|
|
||||||
|
- **مسار قاعدة البيانات:**
|
||||||
|
- `/etc/x-ui/x-ui.db`
|
||||||
|
|
||||||
|
### المسار الأساسي للويب
|
||||||
|
|
||||||
|
1. **إعادة تعيين المسار الأساسي:**
|
||||||
|
- افتح الترمينال.
|
||||||
|
- نفذ أمر `x-ui`.
|
||||||
|
- اختار خيار "إعادة تعيين المسار الأساسي للويب".
|
||||||
|
|
||||||
|
2. **توليد أو تخصيص المسار:**
|
||||||
|
- المسار هيتولد تلقائياً، أو ممكن تدخل مسار مخصص.
|
||||||
|
|
||||||
|
3. **عرض الإعدادات الحالية:**
|
||||||
|
- لمشاهدة الإعدادات الحالية، نفذ أمر `x-ui settings` في الترمينال أو استخدم خيار "عرض الإعدادات الحالية" في البانل.
|
||||||
|
|
||||||
|
### توصية الأمان:
|
||||||
|
- لتحسين الأمان، استخدم كلمة طويلة وعشوائية في مسار URL الخاص بالبانل.
|
||||||
|
|
||||||
|
**مثال:**
|
||||||
|
- `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` وما بعدها:**
|
||||||
|
|
||||||
|
لتفعيل وظيفة تحديد IP، هتحتاج تثبيت `fail2ban` والملفات المطلوبة من خلال الخطوات دي:
|
||||||
|
|
||||||
|
1. شغل أمر `x-ui` في الترمينال واختار "إدارة تحديد IP".
|
||||||
|
2. هتلاقي الخيارات التالية:
|
||||||
|
- **تغيير مدة الحظر:** لتعديل مدة الحظر.
|
||||||
|
- **رفع الحظر عن الجميع:** لإلغاء كل الحظر الحالي.
|
||||||
|
- **عرض السجلات:** لمراجعة السجلات.
|
||||||
|
- **حالة Fail2ban:** لمراجعة حالة fail2ban.
|
||||||
|
- **إعادة تشغيل Fail2ban:** لإعادة تشغيل خدمة fail2ban.
|
||||||
|
- **إلغاء تثبيت Fail2ban:** لإلغاء تثبيت fail2ban مع إعداداته.
|
||||||
|
|
||||||
|
3. حدد مسار سجل الوصول على البانل من خلال ضبط `Xray Configs/log/Access log` على `./access.log` ثم احفظ وأعد تشغيل x-ui.
|
||||||
|
|
||||||
|
- **للإصدارات قبل `v2.1.3`:**
|
||||||
|
- هتحتاج تضبط مسار سجل الوصول يدويًا في إعدادات Xray:
|
||||||
|
```sh
|
||||||
|
"log": {
|
||||||
|
"access": "./access.log",
|
||||||
|
"dnsLog": false,
|
||||||
|
"loglevel": "warning"
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
- **للإصدارات `v2.1.3` وما بعدها:**
|
||||||
|
- في خيار لضبط `access.log` مباشرة من البانل.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## بوت Telegram
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>اضغط لعرض تفاصيل بوت Telegram</summary>
|
||||||
|
|
||||||
|
#### الاستخدام
|
||||||
|
|
||||||
|
تدعم لوحة التحكم إشعارات بترافيك يومي، تسجيل الدخول للبانل، نسخ احتياطية للقاعدة، حالة النظام، معلومات العملاء، وغيرها من الوظائف عن طريق بوت Telegram. عشان تستخدم البوت، لازم تضبط معلمات البوت في البانل، ومن ضمنهم:
|
||||||
|
- توكن Telegram
|
||||||
|
- ID شات الأدمن (يمكن إدخال أكثر من واحد بفواصل)
|
||||||
|
- وقت الإشعار (باستخدام صيغة cron)
|
||||||
|
- إشعار بتاريخ انتهاء الصلاحية
|
||||||
|
- إشعار حد الترافيك
|
||||||
|
- نسخ احتياطية للقاعدة
|
||||||
|
- إشعار حمل المعالج
|
||||||
|
|
||||||
|
**صيغة مرجعية:**
|
||||||
|
|
||||||
|
- `30 * * * * *` - إشعار عند الـ 30 ثانية من كل دقيقة.
|
||||||
|
- `0 */10 * * * *` - إشعار عند أول ثانية من كل 10 دقايق.
|
||||||
|
- `@hourly` - إشعار كل ساعة.
|
||||||
|
- `@daily` - إشعار يومي (في تمام منتصف الليل).
|
||||||
|
- `@weekly` - إشعار أسبوعي.
|
||||||
|
- `@every 8h` - إشعار كل 8 ساعات.
|
||||||
|
|
||||||
|
### ميزات بوت Telegram
|
||||||
|
|
||||||
|
- تقارير دورية.
|
||||||
|
- إشعارات عند تسجيل الدخول.
|
||||||
|
- إشعار عند تجاوز حمل المعالج.
|
||||||
|
- تنبيهات قبل انتهاء الصلاحية أو وصول حد الترافيك.
|
||||||
|
- دعم قوائم تقارير العملاء لو تم إضافة اسم مستخدم Telegram للمستخدم في الإعدادات.
|
||||||
|
- إمكانية البحث عن تقرير الترافيك باستخدام UUID (VMESS/VLESS) أو الباسورد (TROJAN) بشكل مجهول.
|
||||||
|
- بوت يعتمد على القوائم.
|
||||||
|
- البحث عن العملاء بالإيميل (للأدمن فقط).
|
||||||
|
- استعراض كافة الإدخالات.
|
||||||
|
- عرض حالة السيرفر.
|
||||||
|
- استعراض العملاء المستنفدين.
|
||||||
|
- استقبال النسخ الاحتياطية عند الطلب وفي التقارير الدورية.
|
||||||
|
- بوت متعدد اللغات.
|
||||||
|
|
||||||
|
### إعداد بوت Telegram
|
||||||
|
|
||||||
|
- ابدأ [Botfather](https://t.me/BotFather) في حساب Telegram بتاعك:
|
||||||
|

|
||||||
|
|
||||||
|
- أنشئ بوت جديد باستخدام أمر /newbot: هيسألك سؤالين، اسم للبوت واسم مستخدم (لازم ينتهي بكلمة "bot").
|
||||||
|

|
||||||
|
|
||||||
|
- شغل البوت اللي أنشأته. هتلاقي رابط البوت بعد كده.
|
||||||
|

|
||||||
|
|
||||||
|
- ادخل على البانل واضبط إعدادات بوت Telegram زي ما هو موضح:
|
||||||
|

|
||||||
|
|
||||||
|
ادخل توكن البوت في الحقل رقم 3.
|
||||||
|
ادخل ID المستخدم في الحقل رقم 4. الحسابات اللي بالـ ID ده هيبقى ليها صلاحية الأدمن. (يمكن إدخال أكثر من واحد بفواصل)
|
||||||
|
|
||||||
|
- كيفية الحصول على ID حساب Telegram؟ استخدم [هذا البوت](https://t.me/useridinfobot). شغله وهيدي الـ ID بتاعك.
|
||||||
|

|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## مسارات API
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>اضغط لعرض تفاصيل مسارات API</summary>
|
||||||
|
|
||||||
|
#### الاستخدام
|
||||||
|
|
||||||
|
- [توثيق الـ API](https://www.postman.com/hsanaei/3x-ui/collection/q1l5l0u/3x-ui)
|
||||||
|
- `/login` مع بيانات المستخدم باستخدام `POST`: `{username: '', password: ''}` لتسجيل الدخول.
|
||||||
|
- المسار الأساسي لـ `/panel/api/inbounds` للعمليات التالية:
|
||||||
|
|
||||||
|
| الطريقة | المسار | الفعل |
|
||||||
|
| :-----: | ------------------------------------ | ------------------------------------------ |
|
||||||
|
| `GET` | `"/list"` | استرجاع كل الإدخالات |
|
||||||
|
| `GET` | `"/get/:id"` | استرجاع إدخال بالـ id |
|
||||||
|
| `GET` | `"/getClientTraffics/:email"` | استرجاع ترافيك عميل بالإيميل |
|
||||||
|
| `GET` | `"/getClientTrafficsById/:id"` | استرجاع ترافيك عميل بالـ id |
|
||||||
|
| `GET` | `"/createbackup"` | البوت بيرسل نسخة احتياطية للأدمن |
|
||||||
|
| `POST` | `"/add"` | إضافة إدخال |
|
||||||
|
| `POST` | `"/del/:id"` | حذف إدخال |
|
||||||
|
| `POST` | `"/update/:id"` | تحديث إدخال |
|
||||||
|
| `POST` | `"/clientIps/:email"` | استرجاع عنوان IP للعميل |
|
||||||
|
| `POST` | `"/clearClientIps/:email"` | مسح عنوان IP للعميل |
|
||||||
|
| `POST` | `"/addClient"` | إضافة عميل للإدخال |
|
||||||
|
| `POST` | `"/:id/delClient/:clientId"` | حذف عميل باستخدام clientId\* |
|
||||||
|
| `POST` | `"/updateClient/:clientId"` | تحديث بيانات عميل باستخدام clientId\* |
|
||||||
|
| `POST` | `"/:id/resetClientTraffic/:email"` | إعادة ضبط ترافيك عميل |
|
||||||
|
| `POST` | `"/resetAllTraffics"` | إعادة ضبط الترافيك لكل الإدخالات |
|
||||||
|
| `POST` | `"/resetAllClientTraffics/:id"` | إعادة ضبط ترافيك كل العملاء في إدخال معين |
|
||||||
|
| `POST` | `"/delDepletedClients/:id"` | حذف العملاء المستنفدين في الإدخال (-1: الكل) |
|
||||||
|
| `POST` | `"/onlines"` | استرجاع قائمة العملاء الأونلاين (الإيميلات) |
|
||||||
|
|
||||||
|
\*- بالنسبة لحقل `clientId`:
|
||||||
|
- استخدم `client.id` لـ VMESS و VLESS.
|
||||||
|
- استخدم `client.password` لـ TROJAN.
|
||||||
|
- استخدم `client.email` لـ Shadowsocks.
|
||||||
|
|
||||||
|
- [](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"` |
|
||||||
|
| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` |
|
||||||
|
| XUI_LOG_FOLDER | `string` | `"/var/log"` |
|
||||||
|
|
||||||
|
مثال:
|
||||||
|
```sh
|
||||||
|
XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## المعاينة
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
## شكر خاص لـ
|
||||||
|
|
||||||
|
- [alireza0](https://github.com/alireza0/)
|
||||||
|
|
||||||
|
## التحيات
|
||||||
|
|
||||||
|
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (الرخصة: **GPL-3.0**): _قواعد محدثة لتوجيهات v2ray/xray مع تضمين دومينات إيرانية وتركيز على الأمان وحجب الإعلانات._
|
||||||
|
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (الرخصة: **GPL-3.0**): _المستودع ده بيحتوي على قواعد توجيه v2ray/xray محدثة تلقائيًا بناءً على بيانات الدومينات والعناوين المحظورة في روسيا._
|
||||||
|
|
||||||
|
## عدد النجوم مع مرور الوقت
|
||||||
|
|
||||||
|
[](https://starchart.cc/MHSanaei/3x-ui)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
@@ -245,7 +245,7 @@ location /sub {
|
|||||||
## SO Recomendados
|
## SO Recomendados
|
||||||
|
|
||||||
- Ubuntu 22.04+
|
- Ubuntu 22.04+
|
||||||
- Debian 11+
|
- Debian 12+
|
||||||
- CentOS 8+
|
- CentOS 8+
|
||||||
- OpenEuler 22.03+
|
- OpenEuler 22.03+
|
||||||
- Fedora 36+
|
- Fedora 36+
|
||||||
@@ -253,8 +253,8 @@ location /sub {
|
|||||||
- Parch Linux
|
- Parch Linux
|
||||||
- Manjaro
|
- Manjaro
|
||||||
- Armbian
|
- Armbian
|
||||||
- AlmaLinux 8.0+
|
- AlmaLinux 9.5+
|
||||||
- Rocky Linux 8+
|
- Rocky Linux 9.5+
|
||||||
- Oracle Linux 8+
|
- Oracle Linux 8+
|
||||||
- OpenSUSE Tubleweed
|
- OpenSUSE Tubleweed
|
||||||
- Amazon Linux 2023
|
- Amazon Linux 2023
|
||||||
@@ -283,6 +283,7 @@ Nuestra plataforma ofrece compatibilidad con una amplia gama de arquitecturas y
|
|||||||
|
|
||||||
## Idiomas
|
## Idiomas
|
||||||
|
|
||||||
|
- Arabic (Árabe)
|
||||||
- English (inglés)
|
- English (inglés)
|
||||||
- Persian (persa)
|
- Persian (persa)
|
||||||
- Traditional Chinese (chino tradicional)
|
- Traditional Chinese (chino tradicional)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
@@ -246,7 +246,7 @@ location /sub {
|
|||||||
## سیستمعاملهای توصیه شده
|
## سیستمعاملهای توصیه شده
|
||||||
|
|
||||||
- Ubuntu 22.04+
|
- Ubuntu 22.04+
|
||||||
- Debian 11+
|
- Debian 12+
|
||||||
- CentOS 8+
|
- CentOS 8+
|
||||||
- OpenEuler 22.03+
|
- OpenEuler 22.03+
|
||||||
- Fedora 36+
|
- Fedora 36+
|
||||||
@@ -254,8 +254,8 @@ location /sub {
|
|||||||
- Parch Linux
|
- Parch Linux
|
||||||
- Manjaro
|
- Manjaro
|
||||||
- Armbian
|
- Armbian
|
||||||
- AlmaLinux 8.0+
|
- AlmaLinux 9.5+
|
||||||
- Rocky Linux 8+
|
- Rocky Linux 9.5+
|
||||||
- Oracle Linux 8+
|
- Oracle Linux 8+
|
||||||
- OpenSUSE Tubleweed
|
- OpenSUSE Tubleweed
|
||||||
- Amazon Linux 2023
|
- Amazon Linux 2023
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
@@ -250,7 +250,7 @@ location /sub {
|
|||||||
## Recommended OS
|
## Recommended OS
|
||||||
|
|
||||||
- Ubuntu 22.04+
|
- Ubuntu 22.04+
|
||||||
- Debian 11+
|
- Debian 12+
|
||||||
- CentOS 8+
|
- CentOS 8+
|
||||||
- OpenEuler 22.03+
|
- OpenEuler 22.03+
|
||||||
- Fedora 36+
|
- Fedora 36+
|
||||||
@@ -258,8 +258,8 @@ location /sub {
|
|||||||
- Parch Linux
|
- Parch Linux
|
||||||
- Manjaro
|
- Manjaro
|
||||||
- Armbian
|
- Armbian
|
||||||
- AlmaLinux 8.0+
|
- AlmaLinux 9.5+
|
||||||
- Rocky Linux 8+
|
- Rocky Linux 9.5+
|
||||||
- Oracle Linux 8+
|
- Oracle Linux 8+
|
||||||
- OpenSUSE Tubleweed
|
- OpenSUSE Tubleweed
|
||||||
- Amazon Linux 2023
|
- Amazon Linux 2023
|
||||||
@@ -290,6 +290,7 @@ Our platform offers compatibility with a diverse range of architectures and devi
|
|||||||
|
|
||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
|
- Arabic
|
||||||
- English
|
- English
|
||||||
- Persian
|
- Persian
|
||||||
- Traditional Chinese
|
- Traditional Chinese
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
@@ -249,7 +249,7 @@ location /sub {
|
|||||||
## Рекомендуемые ОС
|
## Рекомендуемые ОС
|
||||||
|
|
||||||
- Ubuntu 22.04+
|
- Ubuntu 22.04+
|
||||||
- Debian 11+
|
- Debian 12+
|
||||||
- CentOS 8+
|
- CentOS 8+
|
||||||
- OpenEuler 22.03+
|
- OpenEuler 22.03+
|
||||||
- Fedora 36+
|
- Fedora 36+
|
||||||
@@ -257,8 +257,8 @@ location /sub {
|
|||||||
- Parch Linux
|
- Parch Linux
|
||||||
- Manjaro
|
- Manjaro
|
||||||
- Armbian
|
- Armbian
|
||||||
- AlmaLinux 8.0+
|
- AlmaLinux 9.5+
|
||||||
- Rocky Linux 8+
|
- Rocky Linux 9.5+
|
||||||
- Oracle Linux 8+
|
- Oracle Linux 8+
|
||||||
- OpenSUSE Tubleweed
|
- OpenSUSE Tubleweed
|
||||||
- Amazon Linux 2023
|
- Amazon Linux 2023
|
||||||
@@ -289,6 +289,7 @@ location /sub {
|
|||||||
|
|
||||||
## Языки
|
## Языки
|
||||||
|
|
||||||
|
- Arabic (арабский)
|
||||||
- English (английский)
|
- English (английский)
|
||||||
- Persian (персидский)
|
- Persian (персидский)
|
||||||
- Traditional Chinese (традиционный китайский)
|
- Traditional Chinese (традиционный китайский)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[English](/README.md) | [فارسی](/README.fa_IR.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
[English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [中文](/README.zh_CN.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
@@ -246,7 +246,7 @@ location /sub {
|
|||||||
## 建议使用的操作系统
|
## 建议使用的操作系统
|
||||||
|
|
||||||
- Ubuntu 22.04+
|
- Ubuntu 22.04+
|
||||||
- Debian 11+
|
- Debian 12+
|
||||||
- CentOS 8+
|
- CentOS 8+
|
||||||
- OpenEuler 22.03+
|
- OpenEuler 22.03+
|
||||||
- Fedora 36+
|
- Fedora 36+
|
||||||
@@ -254,8 +254,8 @@ location /sub {
|
|||||||
- Parch Linux
|
- Parch Linux
|
||||||
- Manjaro
|
- Manjaro
|
||||||
- Armbian
|
- Armbian
|
||||||
- AlmaLinux 8.0+
|
- AlmaLinux 9.5+
|
||||||
- Rocky Linux 8+
|
- Rocky Linux 9.5+
|
||||||
- Oracle Linux 8+
|
- Oracle Linux 8+
|
||||||
- OpenSUSE Tubleweed
|
- OpenSUSE Tubleweed
|
||||||
- Amazon Linux 2023
|
- Amazon Linux 2023
|
||||||
@@ -283,6 +283,7 @@ location /sub {
|
|||||||
|
|
||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
|
- Arabic (阿拉伯)
|
||||||
- English(英语)
|
- English(英语)
|
||||||
- Persian(波斯语)
|
- Persian(波斯语)
|
||||||
- Traditional Chinese(繁体中文)
|
- Traditional Chinese(繁体中文)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.5.6
|
2.5.7
|
||||||
65
go.mod
65
go.mod
@@ -1,45 +1,46 @@
|
|||||||
module x-ui
|
module x-ui
|
||||||
|
|
||||||
go 1.24.1
|
go 1.24.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/gzip v1.2.2
|
github.com/gin-contrib/gzip v1.2.3
|
||||||
github.com/gin-contrib/sessions v1.0.2
|
github.com/gin-contrib/sessions v1.0.3
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/goccy/go-json v0.10.5
|
github.com/goccy/go-json v0.10.5
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
github.com/mymmrac/telego v0.32.0
|
github.com/mymmrac/telego v0.32.0
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.5.1
|
github.com/nicksnyder/go-i18n/v2 v2.6.0
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3
|
github.com/pelletier/go-toml/v2 v2.2.4
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/shirou/gopsutil/v4 v4.25.2
|
github.com/shirou/gopsutil/v4 v4.25.3
|
||||||
github.com/valyala/fasthttp v1.59.0
|
github.com/valyala/fasthttp v1.60.0
|
||||||
github.com/xtls/xray-core v1.250306.0
|
github.com/xtls/xray-core v1.250306.1-0.20250331123338-ab5d7cf3d2d6
|
||||||
go.uber.org/atomic v1.11.0
|
go.uber.org/atomic v1.11.0
|
||||||
golang.org/x/text v0.23.0
|
golang.org/x/text v0.24.0
|
||||||
google.golang.org/grpc v1.71.0
|
google.golang.org/grpc v1.71.1
|
||||||
gorm.io/driver/sqlite v1.5.7
|
gorm.io/driver/sqlite v1.5.7
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
github.com/bytedance/sonic v1.13.1 // indirect
|
github.com/bytedance/sonic v1.13.2 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||||
github.com/cloudflare/circl v1.6.0 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect
|
github.com/dgryski/go-metro v0.0.0-20250106013310-edb8663e5e33 // indirect
|
||||||
github.com/ebitengine/purego v0.8.2 // indirect
|
github.com/ebitengine/purego v0.8.2 // indirect
|
||||||
github.com/fasthttp/router v1.5.4 // indirect
|
github.com/fasthttp/router v1.5.4 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.25.0 // indirect
|
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/google/btree v1.1.3 // indirect
|
github.com/google/btree v1.1.3 // indirect
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
|
||||||
github.com/gorilla/context v1.1.2 // indirect
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/sessions v1.4.0 // indirect
|
github.com/gorilla/sessions v1.4.0 // indirect
|
||||||
@@ -50,24 +51,23 @@ require (
|
|||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 // 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/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
github.com/mattn/go-sqlite3 v1.14.27 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.23.1 // indirect
|
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
||||||
github.com/pires/go-proxyproto v0.8.0 // indirect
|
github.com/pires/go-proxyproto v0.8.0 // indirect
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/quic-go/quic-go v0.50.0 // indirect
|
github.com/quic-go/quic-go v0.50.1 // indirect
|
||||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||||
github.com/sagernet/sing v0.6.3 // indirect
|
github.com/sagernet/sing v0.6.6 // indirect
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
|
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287 // indirect
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 // indirect
|
||||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||||
@@ -80,22 +80,23 @@ require (
|
|||||||
github.com/vishvananda/netns v0.0.5 // indirect
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
|
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
go.uber.org/mock v0.5.0 // indirect
|
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||||
|
go.uber.org/mock v0.5.1 // indirect
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
golang.org/x/arch v0.15.0 // indirect
|
golang.org/x/arch v0.16.0 // indirect
|
||||||
golang.org/x/crypto v0.36.0 // indirect
|
golang.org/x/crypto v0.37.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
golang.org/x/mod v0.24.0 // indirect
|
||||||
golang.org/x/net v0.37.0 // indirect
|
golang.org/x/net v0.39.0 // indirect
|
||||||
golang.org/x/sync v0.12.0 // indirect
|
golang.org/x/sync v0.13.0 // indirect
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
golang.org/x/sys v0.32.0 // indirect
|
||||||
golang.org/x/time v0.11.0 // indirect
|
golang.org/x/time v0.11.0 // indirect
|
||||||
golang.org/x/tools v0.31.0 // indirect
|
golang.org/x/tools v0.32.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a // indirect
|
||||||
google.golang.org/protobuf v1.36.5 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0 // indirect
|
gvisor.dev/gvisor v0.0.0-20250403230555-2b1f43f26fbb // indirect
|
||||||
lukechampine.com/blake3 v1.4.0 // indirect
|
lukechampine.com/blake3 v1.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
139
go.sum
139
go.sum
@@ -1,20 +1,19 @@
|
|||||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I=
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I=
|
||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
||||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g=
|
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||||
github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||||
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
|
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||||
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
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/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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -29,12 +28,12 @@ github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3G
|
|||||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
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 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||||
github.com/gin-contrib/gzip v1.2.2 h1:iUU/EYCM8ENfkjmZaVrxbjF/ZC267Iqv5S0MMCMEliI=
|
github.com/gin-contrib/gzip v1.2.3 h1:dAhT722RuEG330ce2agAs75z7yB+NKvX/ZM1r8w0u2U=
|
||||||
github.com/gin-contrib/gzip v1.2.2/go.mod h1:C1a5cacjlDsS20cKnHlZRCPUu57D3qH6B2pV0rl+Y/s=
|
github.com/gin-contrib/gzip v1.2.3/go.mod h1:ad72i4Bzmaypk8M762gNXa2wkxxjbz0icRNnuLJ9a/c=
|
||||||
github.com/gin-contrib/sessions v1.0.2 h1:UaIjUvTH1cMeOdj3in6dl+Xb6It8RiKRF9Z1anbUyCA=
|
github.com/gin-contrib/sessions v1.0.3 h1:AZ4j0AalLsGqdrKNbbrKcXx9OJZqViirvNGsJTxcQps=
|
||||||
github.com/gin-contrib/sessions v1.0.2/go.mod h1:KxKxWqWP5LJVDCInulOl4WbLzK2KSPlLesfZ66wRvMs=
|
github.com/gin-contrib/sessions v1.0.3/go.mod h1:5i4XMx4KPtQihnzxEqG9u1K446lO3G19jAi2GtbfsAI=
|
||||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
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/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 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
@@ -50,8 +49,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
|||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
|
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
||||||
github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
|
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
@@ -67,8 +66,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX
|
|||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
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/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 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||||
@@ -103,10 +102,10 @@ github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr32
|
|||||||
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/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 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
github.com/mattn/go-sqlite3 v1.14.27 h1:drZCnuvf37yPfs95E5jd9s3XhdVWLal+6BOK6qrv6IU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.27/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ=
|
||||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -114,28 +113,30 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mymmrac/telego v0.32.0 h1:4X8C1l3k+opkk86r95+eQE8DxiS2LYlR61L/G7yreDY=
|
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/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.6.0 h1:C/m2NNWNiTB6SK4Ao8df5EWm3JETSTIGNXBpMJTxzxQ=
|
||||||
github.com/nicksnyder/go-i18n/v2 v2.5.1/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
|
github.com/nicksnyder/go-i18n/v2 v2.6.0/go.mod h1:88sRqr0C6OPyJn0/KRNaEz1uWorjxIKP7rUUcvycecE=
|
||||||
github.com/onsi/ginkgo/v2 v2.23.1 h1:Ox0cOPv/t8RzKJUfDo9ZKtRvBOJY369sFJnl00CjqwY=
|
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||||
github.com/onsi/ginkgo/v2 v2.23.1/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
|
||||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
|
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
|
||||||
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
|
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
|
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||||
|
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo=
|
github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q=
|
||||||
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
|
||||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||||
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||||
@@ -144,27 +145,25 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
|||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
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/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/sagernet/sing v0.6.3 h1:J1spMc6LMlqUvRjWjvNMAcbvACDneqxB9zxfLuS0UTE=
|
github.com/sagernet/sing v0.6.6 h1:3JkvJ0vqDj/jJcx0a+ve/6lMOrSzZm30I3wrIuZtmRE=
|
||||||
github.com/sagernet/sing v0.6.3/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.6/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc=
|
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287 h1:qIQ0tWF9vxGtkJa24bR+2i53WBCz1nW/Pc47oVYauC4=
|
||||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
github.com/savsgio/gotils v0.0.0-20250408102913-196191ec6287/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771 h1:emzAzMZ1L9iaKCTxdy3Em8Wv4ChIAGnfiz18Cda70g4=
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771/go.mod h1:bR6DqgcAl1zTcOX8/pE2Qkj9XO00eCNqmKb7lXP8EAg=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.2 h1:NMscG3l2CqtWFS86kj3vP7soOczqrQYIEhO/pMvvQkk=
|
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
|
||||||
github.com/shirou/gopsutil/v4 v4.25.2/go.mod h1:34gBYJzyqCDT11b6bMHP0XCvWeU3J61XRT7a2EmCRTA=
|
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
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/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||||
@@ -179,8 +178,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/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
github.com/valyala/fasthttp v1.60.0 h1:kBRYS0lOhVJ6V+bYN8PqAHELKHtXqwq9zNMLKx1MBsw=
|
||||||
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
github.com/valyala/fasthttp v1.60.0/go.mod h1:iY4kDgV3Gc6EqhRZ8icqcmlG6bqhcDXfuHgTO4FXCvc=
|
||||||
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||||
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
||||||
@@ -190,8 +189,8 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd
|
|||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
|
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
|
||||||
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
|
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
|
||||||
github.com/xtls/xray-core v1.250306.0 h1:XZyZvSgcpAoVEGnFnxNdoHbSF7Kp77A/0TPk4lhv6rM=
|
github.com/xtls/xray-core v1.250306.1-0.20250331123338-ab5d7cf3d2d6 h1:12QXC7rYztOQhq/3ooiHrEc5X98478076aUiIW8mzR8=
|
||||||
github.com/xtls/xray-core v1.250306.0/go.mod h1:clXnUOnX6CKWBGgJY4ePYhb/EtTdSrUC7vPfT6m5p4c=
|
github.com/xtls/xray-core v1.250306.1-0.20250331123338-ab5d7cf3d2d6/go.mod h1:O+FFC64bjnOukaGHPdZ+wqGHTrgPDN+qH0U+YWCzbEo=
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
@@ -210,46 +209,48 @@ go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC
|
|||||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
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 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||||
|
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
|
||||||
|
go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw=
|
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
|
||||||
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
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/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.13.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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a h1:GIqLhp/cYUkuGuiT+vJk8vhOP86L4+SP5j8yXgeVpvI=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
|
||||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
@@ -263,8 +264,8 @@ gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I=
|
|||||||
gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4=
|
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 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0 h1:P+U/06iIKPQ3DLcg+zBfSCia1luZ2msPZrJ8jYDFPs0=
|
gvisor.dev/gvisor v0.0.0-20250403230555-2b1f43f26fbb h1:rOQHoZqzW4aOUPdXb3HpJmJkEUYqASpXKy4W3sUQfYE=
|
||||||
gvisor.dev/gvisor v0.0.0-20240320123526-dc6abceb7ff0/go.mod h1:NQHVAzMwvZ+Qe3ElSiHmq9RUm1MdNHpUZ52fiEqvn+0=
|
gvisor.dev/gvisor v0.0.0-20250403230555-2b1f43f26fbb/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
|
||||||
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
|
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
|
||||||
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
|
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
|
|||||||
91
install.sh
91
install.sh
@@ -37,83 +37,20 @@ arch() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "arch: $(arch)"
|
echo "Arch: $(arch)"
|
||||||
|
|
||||||
os_version=""
|
check_glibc_version() {
|
||||||
os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.')
|
glibc_version=$(ldd --version | head -n1 | awk '{print $NF}')
|
||||||
|
|
||||||
if [[ "${release}" == "arch" ]]; then
|
required_version="2.32"
|
||||||
echo "Your OS is Arch Linux"
|
if [[ "$(printf '%s\n' "$required_version" "$glibc_version" | sort -V | head -n1)" != "$required_version" ]]; then
|
||||||
elif [[ "${release}" == "parch" ]]; then
|
echo -e "${red}GLIBC version $glibc_version is too old! Required: 2.32 or higher${plain}"
|
||||||
echo "Your OS is Parch Linux"
|
echo "Please upgrade to a newer version of your operating system to get a higher GLIBC version."
|
||||||
elif [[ "${release}" == "manjaro" ]]; then
|
exit 1
|
||||||
echo "Your OS is Manjaro"
|
|
||||||
elif [[ "${release}" == "armbian" ]]; then
|
|
||||||
echo "Your OS is Armbian"
|
|
||||||
elif [[ "${release}" == "alpine" ]]; then
|
|
||||||
echo "Your OS is Alpine Linux"
|
|
||||||
elif [[ "${release}" == "opensuse-tumbleweed" ]]; then
|
|
||||||
echo "Your OS is OpenSUSE Tumbleweed"
|
|
||||||
elif [[ "${release}" == "openEuler" ]]; then
|
|
||||||
if [[ ${os_version} -lt 2203 ]]; then
|
|
||||||
echo -e "${red} Please use OpenEuler 22.03 or higher ${plain}\n" && exit 1
|
|
||||||
fi
|
fi
|
||||||
elif [[ "${release}" == "centos" ]]; then
|
echo "GLIBC version: $glibc_version (meets requirement of 2.32+)"
|
||||||
if [[ ${os_version} -lt 8 ]]; then
|
}
|
||||||
echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1
|
check_glibc_version
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "ubuntu" ]]; then
|
|
||||||
if [[ ${os_version} -lt 2204 ]]; then
|
|
||||||
echo -e "${red} Please use Ubuntu 22 or higher version!${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "fedora" ]]; then
|
|
||||||
if [[ ${os_version} -lt 36 ]]; then
|
|
||||||
echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "amzn" ]]; then
|
|
||||||
if [[ ${os_version} != "2023" ]]; then
|
|
||||||
echo -e "${red} Please use Amazon Linux 2023!${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "debian" ]]; then
|
|
||||||
if [[ ${os_version} -lt 11 ]]; then
|
|
||||||
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "almalinux" ]]; then
|
|
||||||
if [[ ${os_version} -lt 80 ]]; then
|
|
||||||
echo -e "${red} Please use AlmaLinux 8.0 or higher ${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "rocky" ]]; then
|
|
||||||
if [[ ${os_version} -lt 8 ]]; then
|
|
||||||
echo -e "${red} Please use Rocky Linux 8 or higher ${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
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:"
|
|
||||||
echo "- Ubuntu 22.04+"
|
|
||||||
echo "- Debian 11+"
|
|
||||||
echo "- CentOS 8+"
|
|
||||||
echo "- OpenEuler 22.03+"
|
|
||||||
echo "- Fedora 36+"
|
|
||||||
echo "- Arch Linux"
|
|
||||||
echo "- Parch Linux"
|
|
||||||
echo "- Manjaro"
|
|
||||||
echo "- Armbian"
|
|
||||||
echo "- AlmaLinux 8.0+"
|
|
||||||
echo "- Rocky Linux 8+"
|
|
||||||
echo "- Oracle Linux 8+"
|
|
||||||
echo "- OpenSUSE Tumbleweed"
|
|
||||||
echo "- Amazon Linux 2023"
|
|
||||||
echo "- Virtuozzo Linux 8+"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
install_base() {
|
install_base() {
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
@@ -157,9 +94,9 @@ config_after_install() {
|
|||||||
local config_username=$(gen_random_string 10)
|
local config_username=$(gen_random_string 10)
|
||||||
local config_password=$(gen_random_string 10)
|
local config_password=$(gen_random_string 10)
|
||||||
|
|
||||||
read -p "Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: " config_confirm
|
read -rp "Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: " config_confirm
|
||||||
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then
|
||||||
read -p "Please set up the panel port: " config_port
|
read -rp "Please set up the panel port: " config_port
|
||||||
echo -e "${yellow}Your Panel Port is: ${config_port}${plain}"
|
echo -e "${yellow}Your Panel Port is: ${config_port}${plain}"
|
||||||
else
|
else
|
||||||
local config_port=$(shuf -i 1024-62000 -n 1)
|
local config_port=$(shuf -i 1024-62000 -n 1)
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
@media only screen and (max-width:767px){.hidden-xs-only{display:none!important}}@media only screen and (min-width:768px){.hidden-sm-and-up{display:none!important}}@media only screen and (min-width:768px) and (max-width:991px){.hidden-sm-only{display:none!important}}@media only screen and (max-width:991px){.hidden-sm-and-down{display:none!important}}@media only screen and (min-width:992px){.hidden-md-and-up{display:none!important}}@media only screen and (min-width:992px) and (max-width:1199px){.hidden-md-only{display:none!important}}@media only screen and (max-width:1199px){.hidden-md-and-down{display:none!important}}@media only screen and (min-width:1200px){.hidden-lg-and-up{display:none!important}}@media only screen and (min-width:1200px) and (max-width:1919px){.hidden-lg-only{display:none!important}}@media only screen and (max-width:1919px){.hidden-lg-and-down{display:none!important}}@media only screen and (min-width:1920px){.hidden-xl-only{display:none!important}}
|
|
||||||
@@ -83,7 +83,7 @@ class PromiseUtil {
|
|||||||
class RandomUtil {
|
class RandomUtil {
|
||||||
static getSeq({ type = "default", hasNumbers = true, hasLowercase = true, hasUppercase = true } = {}) {
|
static getSeq({ type = "default", hasNumbers = true, hasLowercase = true, hasUppercase = true } = {}) {
|
||||||
let seq = '';
|
let seq = '';
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "hex":
|
case "hex":
|
||||||
seq += "0123456789abcdef";
|
seq += "0123456789abcdef";
|
||||||
@@ -488,7 +488,7 @@ class ClipboardManager {
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
try {
|
try {
|
||||||
const textarea = window.document.createElement('textarea');
|
const textarea = window.document.createElement('textarea');
|
||||||
|
|
||||||
textarea.style.fontSize = '12pt';
|
textarea.style.fontSize = '12pt';
|
||||||
textarea.style.border = '0';
|
textarea.style.border = '0';
|
||||||
textarea.style.padding = '0';
|
textarea.style.padding = '0';
|
||||||
@@ -498,14 +498,14 @@ class ClipboardManager {
|
|||||||
textarea.style.top = `${window.pageYOffset || document.documentElement.scrollTop}px`;
|
textarea.style.top = `${window.pageYOffset || document.documentElement.scrollTop}px`;
|
||||||
textarea.setAttribute('readonly', '');
|
textarea.setAttribute('readonly', '');
|
||||||
textarea.value = content;
|
textarea.value = content;
|
||||||
|
|
||||||
window.document.body.appendChild(textarea);
|
window.document.body.appendChild(textarea);
|
||||||
|
|
||||||
textarea.select();
|
textarea.select();
|
||||||
window.document.execCommand("copy");
|
window.document.execCommand("copy");
|
||||||
|
|
||||||
window.document.body.removeChild(textarea);
|
window.document.body.removeChild(textarea);
|
||||||
|
|
||||||
resolve(true)
|
resolve(true)
|
||||||
} catch {
|
} catch {
|
||||||
resolve(false)
|
resolve(false)
|
||||||
@@ -558,7 +558,7 @@ class CPUFormatter {
|
|||||||
static cpuSpeedFormat(speed) {
|
static cpuSpeedFormat(speed) {
|
||||||
return speed > 1000 ? (speed / 1000).toFixed(2) + " GHz" : speed.toFixed(2) + " MHz";
|
return speed > 1000 ? (speed / 1000).toFixed(2) + " GHz" : speed.toFixed(2) + " MHz";
|
||||||
}
|
}
|
||||||
|
|
||||||
static cpuCoreFormat(cores) {
|
static cpuCoreFormat(cores) {
|
||||||
return cores === 1 ? "1 Core" : cores + " Cores";
|
return cores === 1 ? "1 Core" : cores + " Cores";
|
||||||
}
|
}
|
||||||
@@ -579,7 +579,7 @@ class NumberFormatter {
|
|||||||
static addZero(num) {
|
static addZero(num) {
|
||||||
return num < 10 ? "0" + num : num;
|
return num < 10 ? "0" + num : num;
|
||||||
}
|
}
|
||||||
|
|
||||||
static toFixed(num, n) {
|
static toFixed(num, n) {
|
||||||
n = Math.pow(10, n);
|
n = Math.pow(10, n);
|
||||||
return Math.floor(num * n) / n;
|
return Math.floor(num * n) / n;
|
||||||
@@ -610,7 +610,7 @@ class CookieManager {
|
|||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
static setCookie(cname, cvalue, exdays) {
|
static setCookie(cname, cvalue, exdays) {
|
||||||
const d = new Date();
|
const d = new Date();
|
||||||
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
|
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
|
||||||
@@ -630,7 +630,7 @@ class ColorUtils {
|
|||||||
default: return "red";
|
default: return "red";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static clientUsageColor(clientStats, trafficDiff) {
|
static clientUsageColor(clientStats, trafficDiff) {
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case !clientStats || clientStats.total == 0: return "#7a316f";
|
case !clientStats || clientStats.total == 0: return "#7a316f";
|
||||||
@@ -639,7 +639,7 @@ class ColorUtils {
|
|||||||
default: return "#cf3c3c";
|
default: return "#cf3c3c";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static userExpiryColor(threshold, client, isDark = false) {
|
static userExpiryColor(threshold, client, isDark = false) {
|
||||||
if (!client.enable) return isDark ? '#2c3950' : '#bcbcbc';
|
if (!client.enable) return isDark ? '#2c3950' : '#bcbcbc';
|
||||||
let now = new Date().getTime(), expiry = client.expiryTime;
|
let now = new Date().getTime(), expiry = client.expiryTime;
|
||||||
@@ -665,7 +665,7 @@ class URLBuilder {
|
|||||||
if (!host || host.length === 0) host = window.location.hostname;
|
if (!host || host.length === 0) host = window.location.hostname;
|
||||||
if (!port || port.length === 0) port = window.location.port;
|
if (!port || port.length === 0) port = window.location.port;
|
||||||
if (isTLS === undefined) isTLS = window.location.protocol === "https:";
|
if (isTLS === undefined) isTLS = window.location.protocol === "https:";
|
||||||
|
|
||||||
const protocol = isTLS ? "https:" : "http:";
|
const protocol = isTLS ? "https:" : "http:";
|
||||||
port = String(port);
|
port = String(port);
|
||||||
if (port === "" || (isTLS && port === "443") || (!isTLS && port === "80")) {
|
if (port === "" || (isTLS && port === "443") || (!isTLS && port === "80")) {
|
||||||
@@ -673,13 +673,18 @@ class URLBuilder {
|
|||||||
} else {
|
} else {
|
||||||
port = `:${port}`;
|
port = `:${port}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${protocol}//${host}${port}${base}${path}`;
|
return `${protocol}//${host}${port}${base}${path}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LanguageManager {
|
class LanguageManager {
|
||||||
static supportedLanguages = [
|
static supportedLanguages = [
|
||||||
|
{
|
||||||
|
name: "العربية",
|
||||||
|
value: "ar-EG",
|
||||||
|
icon: "🇪🇬",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "English",
|
name: "English",
|
||||||
value: "en-US",
|
value: "en-US",
|
||||||
@@ -744,11 +749,11 @@ class LanguageManager {
|
|||||||
|
|
||||||
static getLanguage() {
|
static getLanguage() {
|
||||||
let lang = CookieManager.getCookie("lang");
|
let lang = CookieManager.getCookie("lang");
|
||||||
|
|
||||||
if (!lang) {
|
if (!lang) {
|
||||||
if (window.navigator) {
|
if (window.navigator) {
|
||||||
lang = window.navigator.language || window.navigator.userLanguage;
|
lang = window.navigator.language || window.navigator.userLanguage;
|
||||||
|
|
||||||
if (LanguageManager.isSupportLanguage(lang)) {
|
if (LanguageManager.isSupportLanguage(lang)) {
|
||||||
CookieManager.setCookie("lang", lang, 150);
|
CookieManager.setCookie("lang", lang, 150);
|
||||||
} else {
|
} else {
|
||||||
@@ -760,24 +765,63 @@ class LanguageManager {
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lang;
|
return lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
static setLanguage(language) {
|
static setLanguage(language) {
|
||||||
if (!LanguageManager.isSupportLanguage(language)) {
|
if (!LanguageManager.isSupportLanguage(language)) {
|
||||||
language = "en-US";
|
language = "en-US";
|
||||||
}
|
}
|
||||||
|
|
||||||
CookieManager.setCookie("lang", language, 150);
|
CookieManager.setCookie("lang", language, 150);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
static isSupportLanguage(language) {
|
static isSupportLanguage(language) {
|
||||||
const languageFilter = LanguageManager.supportedLanguages.filter((lang) => {
|
const languageFilter = LanguageManager.supportedLanguages.filter((lang) => {
|
||||||
return lang.value === language
|
return lang.value === language
|
||||||
})
|
})
|
||||||
|
|
||||||
return languageFilter.length > 0;
|
return languageFilter.length > 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MediaQueryMixin = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isMobile: window.innerWidth <= 768,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateDeviceType() {
|
||||||
|
this.isMobile = window.innerWidth <= 768;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
window.addEventListener('resize', this.updateDeviceType);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('resize', this.updateDeviceType);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
class FileManager {
|
||||||
|
static downloadTextFile(content, filename='file.txt', options = { type: "text/plain" }) {
|
||||||
|
let link = window.document.createElement('a');
|
||||||
|
|
||||||
|
link.download = filename;
|
||||||
|
link.style.border = '0';
|
||||||
|
link.style.padding = '0';
|
||||||
|
link.style.margin = '0';
|
||||||
|
link.style.position = 'absolute';
|
||||||
|
link.style.left = '-9999px';
|
||||||
|
link.style.top = `${window.pageYOffset || window.document.documentElement.scrollTop}px`;
|
||||||
|
link.href = URL.createObjectURL(new Blob([content], options));
|
||||||
|
link.click();
|
||||||
|
|
||||||
|
URL.revokeObjectURL(link.href);
|
||||||
|
|
||||||
|
link.remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="renderer" content="webkit">
|
<meta name="renderer" content="webkit">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta name="robots" content="noindex,nofollow">
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue/antd.min.css">
|
<link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue/antd.min.css">
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/element-ui/theme-chalk/display.css">
|
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/css/custom.min.css?{{ .cur_ver }}">
|
<link rel="stylesheet" href="{{ .base_path }}assets/css/custom.min.css?{{ .cur_ver }}">
|
||||||
<style>
|
<style>
|
||||||
[v-cloak] {
|
[v-cloak] {
|
||||||
@@ -26,7 +25,7 @@
|
|||||||
'Segoe UI Emoji', 'Segoe UI Symbol';
|
'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<title>{{ .host }}-{{ i18n .title}}</title>
|
<title>{{ .host }} – {{ i18n .title}}</title>
|
||||||
</head>
|
</head>
|
||||||
<div id="message"></div>
|
<div id="message"></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,31 +1,31 @@
|
|||||||
{{define "client_table"}}
|
{{define "component/aClientTable"}}
|
||||||
<template slot="actions" slot-scope="text, client, index">
|
<template slot="actions" slot-scope="text, client, index">
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">{{ i18n "qrCode" }}</template>
|
<template slot="title">{{ i18n "qrCode" }}</template>
|
||||||
<a-icon style="font-size: 24px;" class="normal-icon" type="qrcode" v-if="record.hasLink()" @click="showQrcode(record.id,client);"></a-icon>
|
<a-icon :style="{ fontSize: '24px' }" class="normal-icon" type="qrcode" v-if="record.hasLink()" @click="showQrcode(record.id,client);"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">{{ i18n "pages.client.edit" }}</template>
|
<template slot="title">{{ i18n "pages.client.edit" }}</template>
|
||||||
<a-icon style="font-size: 24px;" class="normal-icon" type="edit" @click="openEditClient(record.id,client);"></a-icon>
|
<a-icon :style="{ fontSize: '24px' }" class="normal-icon" type="edit" @click="openEditClient(record.id,client);"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">{{ i18n "info" }}</template>
|
<template slot="title">{{ i18n "info" }}</template>
|
||||||
<a-icon style="font-size: 24px;" class="normal-icon" type="info-circle" @click="showInfo(record.id,client);"></a-icon>
|
<a-icon :style="{ fontSize: '24px' }" class="normal-icon" type="info-circle" @click="showInfo(record.id,client);"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||||
<a-popconfirm @confirm="resetClientTraffic(client,record.id,false)" title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
|
<a-popconfirm @confirm="resetClientTraffic(client,record.id,false)" title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
|
||||||
<a-icon slot="icon" type="question-circle-o" :style="themeSwitcher.isDarkTheme ? 'color: var(--color-primary-100)' : 'color: var(--color-primary-100)'"></a-icon>
|
<a-icon slot="icon" type="question-circle-o" :style="{ color: 'var(--color-primary-100)'}"></a-icon>
|
||||||
<a-icon style="font-size: 24px; cursor: pointer;" class="normal-icon" type="retweet" v-if="client.email.length > 0"></a-icon>
|
<a-icon :style="{ fontSize: '24px', cursor: 'pointer' }" class="normal-icon" type="retweet" v-if="client.email.length > 0"></a-icon>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span style="color: #FF4D4F"> {{ i18n "delete"}}</span>
|
<span :style="{ color: '#FF4D4F' }"> {{ i18n "delete"}}</span>
|
||||||
</template>
|
</template>
|
||||||
<a-popconfirm @confirm="delClient(record.id,client,false)" title='{{ i18n "pages.inbounds.deleteClientContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "delete"}}' ok-type="danger" cancel-text='{{ i18n "cancel"}}'>
|
<a-popconfirm @confirm="delClient(record.id,client,false)" title='{{ i18n "pages.inbounds.deleteClientContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "delete"}}' ok-type="danger" cancel-text='{{ i18n "cancel"}}'>
|
||||||
<a-icon slot="icon" type="question-circle-o" style="color: #e04141"></a-icon>
|
<a-icon slot="icon" type="question-circle-o" :style="{ color: '#e04141' }"></a-icon>
|
||||||
<a-icon style="font-size: 24px; cursor: pointer;" class="delete-icon" type="delete" v-if="isRemovable(record.id)"></a-icon>
|
<a-icon :style="{ fontSize: '24px', cursor: 'pointer' }" class="delete-icon" type="delete" v-if="isRemovable(record.id)"></a-icon>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
@@ -124,9 +124,9 @@
|
|||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="min-width: 50px; border: none;" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
<a-tag :style="{ minWidth: '50px', border: 'none' }" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-tag v-else :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)" style="border: none;" class="infinite-tag">
|
<a-tag v-else :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)" :style="{ border: 'none' }" class="infinite-tag">
|
||||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||||
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -135,27 +135,27 @@
|
|||||||
</template>
|
</template>
|
||||||
<template slot="actionMenu" slot-scope="text, client, index">
|
<template slot="actionMenu" slot-scope="text, client, index">
|
||||||
<a-dropdown :trigger="['click']">
|
<a-dropdown :trigger="['click']">
|
||||||
<a-icon @click="e => e.preventDefault()" type="ellipsis" style="font-size: 20px;"></a-icon>
|
<a-icon @click="e => e.preventDefault()" type="ellipsis" :style="{ fontSize: '20px' }"></a-icon>
|
||||||
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||||
<a-menu-item v-if="record.hasLink()" @click="showQrcode(record.id,client);">
|
<a-menu-item v-if="record.hasLink()" @click="showQrcode(record.id,client);">
|
||||||
<a-icon style="font-size: 14px;" type="qrcode"></a-icon>
|
<a-icon :style="{ fontSize: '14px' }" type="qrcode"></a-icon>
|
||||||
{{ i18n "qrCode" }}
|
{{ i18n "qrCode" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item @click="openEditClient(record.id,client);">
|
<a-menu-item @click="openEditClient(record.id,client);">
|
||||||
<a-icon style="font-size: 14px;" type="edit"></a-icon>
|
<a-icon :style="{ fontSize: '14px' }" type="edit"></a-icon>
|
||||||
{{ i18n "pages.client.edit" }}
|
{{ i18n "pages.client.edit" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item @click="showInfo(record.id,client);">
|
<a-menu-item @click="showInfo(record.id,client);">
|
||||||
<a-icon style="font-size: 14px;" type="info-circle"></a-icon>
|
<a-icon :style="{ fontSize: '14px' }" type="info-circle"></a-icon>
|
||||||
{{ i18n "info" }}
|
{{ i18n "info" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item @click="resetClientTraffic(client,record.id)" v-if="client.email.length > 0">
|
<a-menu-item @click="resetClientTraffic(client,record.id)" v-if="client.email.length > 0">
|
||||||
<a-icon style="font-size: 14px;" type="retweet"></a-icon>
|
<a-icon :style="{ fontSize: '14px' }" type="retweet"></a-icon>
|
||||||
{{ i18n "pages.inbounds.resetTraffic" }}
|
{{ i18n "pages.inbounds.resetTraffic" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item v-if="isRemovable(record.id)" @click="delClient(record.id,client)">
|
<a-menu-item v-if="isRemovable(record.id)" @click="delClient(record.id,client)">
|
||||||
<a-icon style="font-size: 14px;" type="delete"></a-icon>
|
<a-icon :style="{ fontSize: '14px' }" type="delete"></a-icon>
|
||||||
<span style="color: #FF4D4F"> {{ i18n "delete"}}</span>
|
<span :style="{ color: '#FF4D4F' }"> {{ i18n "delete"}}</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item>
|
<a-menu-item>
|
||||||
<a-switch v-model="client.enable" size="small" @change="switchEnableClient(record.id,client)"></a-switch>
|
<a-switch v-model="client.enable" size="small" @change="switchEnableClient(record.id,client)"></a-switch>
|
||||||
@@ -169,10 +169,10 @@
|
|||||||
<template slot="content">
|
<template slot="content">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="3" style="text-align: center;">{{ i18n "pages.inbounds.traffic" }}</td>
|
<td colspan="3" :style="{ textAlign: 'center' }">{{ i18n "pages.inbounds.traffic" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td width="80px" style="margin:0; text-align: right;font-size: 1em;"> [[ SizeFormatter.sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] </td>
|
<td width="80px" :style="{ margin: '0', textAlign: 'right', fontSize: '1em' }"> [[ SizeFormatter.sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] </td>
|
||||||
<td width="120px" v-if="!client.enable">
|
<td width="120px" v-if="!client.enable">
|
||||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
||||||
</td>
|
</td>
|
||||||
@@ -202,14 +202,14 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="3" style="text-align: center;">
|
<td colspan="3" :style="{ textAlign: 'center' }">
|
||||||
<a-divider style="margin: 0; border-collapse: separate;"></a-divider>
|
<a-divider :style="{ margin: '0', borderCollapse: 'separate' }"></a-divider>
|
||||||
{{ i18n "pages.inbounds.expireDate" }}
|
{{ i18n "pages.inbounds.expireDate" }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<template v-if="client.expiryTime !=0 && client.reset >0">
|
<template v-if="client.expiryTime !=0 && client.reset >0">
|
||||||
<td width="80px" style="margin:0; text-align: right;font-size: 1em;"> [[ remainedDays(client.expiryTime) ]] </td>
|
<td width="80px" :style="{ margin: '0', textAlign: 'right', fontSize: '1em' }"> [[ remainedDays(client.expiryTime) ]] </td>
|
||||||
<td width="120px" class="infinite-bar">
|
<td width="120px" class="infinite-bar">
|
||||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
@@ -230,7 +230,7 @@
|
|||||||
<td width="60px">[[ client.reset + "d" ]]</td>
|
<td width="60px">[[ client.reset + "d" ]]</td>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<td colspan="3" style="text-align: center;">
|
<td colspan="3" :style="{ textAlign: 'center' }">
|
||||||
<a-popover v-if="client.expiryTime != 0" :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover v-if="client.expiryTime != 0" :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="min-width: 50px; border: none;" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
<a-tag :style="{ minWidth: '50px', border: 'none' }" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ remainedDays(client.expiryTime) ]] </a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-tag v-else :color="client.enable ? 'purple' : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'" class="infinite-tag">
|
<a-tag v-else :color="client.enable ? 'purple' : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'" class="infinite-tag">
|
||||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||||
@@ -257,8 +257,8 @@
|
|||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
<a-badge>
|
<a-badge>
|
||||||
<a-icon v-if="!client.enable" slot="count" type="pause-circle" theme="filled" :style="'color: ' + themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-icon>
|
<a-icon v-if="!client.enable" slot="count" type="pause-circle" theme="filled" :style="{ color: themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc' }"></a-icon>
|
||||||
<a-button shape="round" size="small" style="font-size: 14px; padding: 0 10px;">
|
<a-button shape="round" size="small" :style="{ fontSize: '14px', padding: '0 10px' }">
|
||||||
<a-icon type="solution"></a-icon>
|
<a-icon type="solution"></a-icon>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-badge>
|
</a-badge>
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
@input="$emit('input', convertToGregorian($event.target.value)); jalaliDatepicker.hide();"
|
@input="$emit('input', convertToGregorian($event.target.value)); jalaliDatepicker.hide();"
|
||||||
:placeholder="placeholder">
|
:placeholder="placeholder">
|
||||||
<template #addonAfter>
|
<template #addonAfter>
|
||||||
<a-icon type="calendar" style="font-size: 14px; opacity: 0.5;" />
|
<a-icon type="calendar" :style="{ fontSize: '14px', opacity: '0.5' }" />
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
</div>
|
</div>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{{define "component/settingListItem"}}
|
{{define "component/settingListItem"}}
|
||||||
<a-list-item :style="{ padding: padding }">
|
<a-list-item :style="{ padding: padding }">
|
||||||
<a-row>
|
<a-row :gutter="[8,16]">
|
||||||
<a-col :lg="24" :xl="12">
|
<a-col :lg="24" :xl="12">
|
||||||
<a-list-item-meta>
|
<a-list-item-meta>
|
||||||
<template #title>
|
<template #title>
|
||||||
103
web/html/component/aSidebar.html
Normal file
103
web/html/component/aSidebar.html
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
{{define "component/sidebar/content"}}
|
||||||
|
<template>
|
||||||
|
<div class="ant-sidebar">
|
||||||
|
<a-layout-sider :theme="themeSwitcher.currentTheme" collapsible :collapsed="collapsed"
|
||||||
|
@collapse="(isCollapsed, type) => collapseHandle(isCollapsed, type)" breakpoint="md">
|
||||||
|
<a-theme-switch></a-theme-switch>
|
||||||
|
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="activeTab"
|
||||||
|
@click="({key}) => openLink(key)">
|
||||||
|
<a-menu-item v-for="tab in tabs" :key="tab.key">
|
||||||
|
<a-icon :type="tab.icon"></a-icon>
|
||||||
|
<span v-text="tab.title"></span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-layout-sider>
|
||||||
|
<a-drawer placement="left" :closable="false" @close="closeDrawer" :visible="visible"
|
||||||
|
:wrap-class-name="themeSwitcher.currentTheme" :wrap-style="{ padding: 0 }" :style="{ height: '100%' }">
|
||||||
|
<div class="drawer-handle" @click="toggleDrawer" slot="handle">
|
||||||
|
<a-icon :type="visible ? 'close' : 'menu-fold'"></a-icon>
|
||||||
|
</div>
|
||||||
|
<a-theme-switch></a-theme-switch>
|
||||||
|
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="activeTab"
|
||||||
|
@click="({key}) => openLink(key)">
|
||||||
|
<a-menu-item v-for="tab in tabs" :key="tab.key">
|
||||||
|
<a-icon :type="tab.icon"></a-icon>
|
||||||
|
<span v-text="tab.title"></span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "component/aSidebar"}}
|
||||||
|
<style>
|
||||||
|
.ant-sidebar>.ant-layout-sider {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const SIDEBAR_COLLAPSED_KEY = "isSidebarCollapsed"
|
||||||
|
|
||||||
|
Vue.component('a-sidebar', {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
key: '{{ .base_path }}panel/',
|
||||||
|
icon: 'dashboard',
|
||||||
|
title: '{{ i18n "menu.dashboard"}}'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '{{ .base_path }}panel/inbounds',
|
||||||
|
icon: 'user',
|
||||||
|
title: '{{ i18n "menu.inbounds"}}'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '{{ .base_path }}panel/settings',
|
||||||
|
icon: 'setting',
|
||||||
|
title: '{{ i18n "menu.settings"}}'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '{{ .base_path }}panel/xray',
|
||||||
|
icon: 'tool',
|
||||||
|
title: '{{ i18n "menu.xray"}}'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '{{ .base_path }}logout/',
|
||||||
|
icon: 'logout',
|
||||||
|
title: '{{ i18n "menu.logout"}}'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
activeTab: [
|
||||||
|
'{{ .request_uri }}'
|
||||||
|
],
|
||||||
|
visible: false,
|
||||||
|
collapsed: JSON.parse(localStorage.getItem(SIDEBAR_COLLAPSED_KEY)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openLink(key) {
|
||||||
|
return key.startsWith('http') ?
|
||||||
|
window.open(key) :
|
||||||
|
location.href = key
|
||||||
|
},
|
||||||
|
closeDrawer() {
|
||||||
|
this.visible = false;
|
||||||
|
},
|
||||||
|
toggleDrawer() {
|
||||||
|
this.visible = !this.visible;
|
||||||
|
},
|
||||||
|
collapseHandle(collapsed, type) {
|
||||||
|
if (type === "clickTrigger") {
|
||||||
|
localStorage.setItem(SIDEBAR_COLLAPSED_KEY, collapsed);
|
||||||
|
|
||||||
|
this.collapsed = JSON.parse(localStorage.getItem(SIDEBAR_COLLAPSED_KEY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: `{{template "component/sidebar/content"}}`,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{end}}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{{define "component/sortableTableTrigger"}}
|
{{define "component/sortableTableTrigger"}}
|
||||||
<a-icon type="drag" class="sortable-icon" style="cursor: move;" @mouseup="mouseUpHandler" @mousedown="mouseDownHandler"
|
<a-icon type="drag" class="sortable-icon" :style="{ cursor: 'move' }" @mouseup="mouseUpHandler" @mousedown="mouseDownHandler"
|
||||||
@click="clickHandler" />
|
@click="clickHandler" />
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
@@ -6,13 +6,16 @@
|
|||||||
<a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>
|
<a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>
|
||||||
<span>{{ i18n "menu.theme" }}</span>
|
<span>{{ i18n "menu.theme" }}</span>
|
||||||
</span>
|
</span>
|
||||||
<a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()">
|
<a-menu-item id="change-theme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOff()">
|
||||||
<span>{{ i18n "menu.dark" }}</span>
|
<span>{{ i18n "menu.dark" }}</span>
|
||||||
<a-switch style="margin-left: 2px;" size="small" :default-checked="themeSwitcher.isDarkTheme" @change="themeSwitcher.toggleTheme()"></a-switch>
|
<a-switch :style="{ marginLeft: '2px' }" size="small" :default-checked="themeSwitcher.isDarkTheme"
|
||||||
|
@change="themeSwitcher.toggleTheme()"></a-switch>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch" @mousedown="themeSwitcher.animationsOffUltra()">
|
<a-menu-item id="change-theme-ultra" v-if="themeSwitcher.isDarkTheme" class="ant-menu-theme-switch"
|
||||||
|
@mousedown="themeSwitcher.animationsOffUltra()">
|
||||||
<span>{{ i18n "menu.ultraDark" }}</span>
|
<span>{{ i18n "menu.ultraDark" }}</span>
|
||||||
<a-checkbox style="margin-left: 2px;" :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
|
<a-checkbox :style="{ marginLeft: '2px' }" :checked="themeSwitcher.isUltra"
|
||||||
|
@click="themeSwitcher.toggleUltra()"></a-checkbox>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
</a-sub-menu>
|
</a-sub-menu>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
@@ -21,18 +24,16 @@
|
|||||||
|
|
||||||
{{define "component/themeSwitchTemplateLogin"}}
|
{{define "component/themeSwitchTemplateLogin"}}
|
||||||
<template>
|
<template>
|
||||||
<a-menu @mousedown="themeSwitcher.animationsOff()" id="change-theme" :theme="themeSwitcher.currentTheme" mode="inline"
|
<a-space direction="vertical" :size="10" :style="{ width: '100%' }">
|
||||||
selected-keys="">
|
<a-space direction="horizontal" size="small">
|
||||||
<a-menu-item mode="inline" class="ant-menu-theme-switch">
|
<a-switch size="small" :default-checked="themeSwitcher.isDarkTheme" @change="themeSwitcher.toggleTheme()"></a-switch>
|
||||||
<a-icon type="bulb" :theme="themeSwitcher.isDarkTheme ? 'filled' : 'outlined'"></a-icon>
|
<span>{{ i18n "menu.dark" }}</span>
|
||||||
<a-switch size="small" :default-checked="themeSwitcher.isDarkTheme"
|
</a-space>
|
||||||
@change="themeSwitcher.toggleTheme()"></a-switch>
|
<a-space v-if="themeSwitcher.isDarkTheme" direction="horizontal" size="small">
|
||||||
<template v-if="themeSwitcher.isDarkTheme">
|
<a-checkbox :checked="themeSwitcher.isUltra" @click="themeSwitcher.toggleUltra()"></a-checkbox>
|
||||||
<a-checkbox style="margin-left: 1rem; vertical-align: middle;" :checked="themeSwitcher.isUltra"
|
<span>{{ i18n "menu.ultraDark" }}</span>
|
||||||
@click="themeSwitcher.toggleUltra()">Ultra</a-checkbox>
|
</a-space>
|
||||||
</template>
|
</a-space>
|
||||||
</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</template>
|
</template>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
<a-icon type="question-circle"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-input-number style="width: 50%" v-model.number="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>
|
||||||
<a-form-item v-if="client.email" label='{{ i18n "comment" }}'>
|
<a-form-item v-if="client.email" label='{{ i18n "comment" }}'>
|
||||||
<a-input v-model.trim="client.comment"></a-input>
|
<a-input v-model.trim="client.comment"></a-input>
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
|
<span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
|
||||||
</template>
|
</template>
|
||||||
<span style="color: #FF4D4F">
|
<span :style="{ color: '#FF4D4F' }">
|
||||||
<a-icon type="delete" @click="clearDBClientIps(client.email)"></a-icon>
|
<a-icon type="delete" @click="clearDBClientIps(client.email)"></a-icon>
|
||||||
</span>
|
</span>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
<a-icon type="question-circle"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-date-picker style="width: 100%;" v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }"
|
<a-date-picker :style="{ width: '100%' }" v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }"
|
||||||
format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
|
format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
v-model="dbInbound._expiryTime"></a-date-picker>
|
v-model="dbInbound._expiryTime"></a-date-picker>
|
||||||
<a-persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
<a-persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{{define "form/outbound"}}
|
{{define "form/outbound"}}
|
||||||
<!-- base -->
|
<!-- base -->
|
||||||
<a-tabs :active-key="outModal.activeKey" style="padding: 0; background-color: transparent;" @change="(activeKey) => {outModal.toggleJson(activeKey == '2'); }">
|
<a-tabs :active-key="outModal.activeKey" :style="{ padding: '0', backgroundColor: 'transparent' }" @change="(activeKey) => {outModal.toggleJson(activeKey == '2'); }">
|
||||||
<a-tab-pane key="1" tab="Form">
|
<a-tab-pane key="1" tab="Form">
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label='{{ i18n "protocol" }}'>
|
<a-form-item label='{{ i18n "protocol" }}'>
|
||||||
@@ -60,9 +60,9 @@
|
|||||||
<!-- Noise Configurations -->
|
<!-- Noise Configurations -->
|
||||||
<a-form v-for="(noise, index) in outbound.settings.noises" :key="index" :colon="false" :label-col="{ md: {span:8} }"
|
<a-form v-for="(noise, index) in outbound.settings.noises" :key="index" :colon="false" :label-col="{ md: {span:8} }"
|
||||||
:wrapper-col="{ md: {span:14} }">
|
:wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider style="margin:0;"> Noise [[ index + 1 ]]
|
<a-divider :style="{ margin: '0' }"> Noise [[ index + 1 ]]
|
||||||
<a-icon v-if="outbound.settings.noises.length > 1" type="delete" @click="() => outbound.settings.delNoise(index)"
|
<a-icon v-if="outbound.settings.noises.length > 1" type="delete" @click="() => outbound.settings.delNoise(index)"
|
||||||
style="color: rgb(255, 77, 79); cursor: pointer;"></a-icon>
|
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<a-form-item label='Type'>
|
<a-form-item label='Type'>
|
||||||
<a-select v-model="noise.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="noise.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
<a-button icon="plus" type="primary" size="small" @click="outbound.settings.addPeer()"></a-button>
|
<a-button icon="plus" type="primary" size="small" @click="outbound.settings.addPeer()"></a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form v-for="(peer, index) in outbound.settings.peers" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form v-for="(peer, index) in outbound.settings.peers" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider style="margin:0;"> Peer [[ index + 1 ]] <a-icon v-if="outbound.settings.peers.length>1" type="delete" @click="() => outbound.settings.delPeer(index)" style="color: rgb(255, 77, 79);cursor: pointer;"></a-icon>
|
<a-divider :style="{ margin: '0' }"> Peer [[ index + 1 ]] <a-icon v-if="outbound.settings.peers.length>1" type="delete" @click="() => outbound.settings.delPeer(index)" :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<a-form-item label='{{ i18n "pages.xray.wireguard.endpoint" }}'>
|
<a-form-item label='{{ i18n "pages.xray.wireguard.endpoint" }}'>
|
||||||
<a-input v-model.trim="peer.endpoint"></a-input>
|
<a-input v-model.trim="peer.endpoint"></a-input>
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
{{ i18n "pages.xray.wireguard.allowedIPs" }}
|
{{ i18n "pages.xray.wireguard.allowedIPs" }}
|
||||||
<a-button icon="plus" type="primary" size="small" @click="peer.allowedIPs.push('')"></a-button>
|
<a-button icon="plus" type="primary" size="small" @click="peer.allowedIPs.push('')"></a-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-for="(aip, index) in peer.allowedIPs" style="margin-bottom: 10px;">
|
<template v-for="(aip, index) in peer.allowedIPs" :style="{ marginBottom: '10px' }">
|
||||||
<a-input v-model.trim="peer.allowedIPs[index]">
|
<a-input v-model.trim="peer.allowedIPs[index]">
|
||||||
<a-button icon="minus" v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)"></a-button>
|
<a-button icon="minus" v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)"></a-button>
|
||||||
</a-input>
|
</a-input>
|
||||||
@@ -444,10 +444,10 @@
|
|||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Short ID">
|
<a-form-item label="Short ID">
|
||||||
<a-input v-model.trim="outbound.stream.reality.shortId" style="width:250px"></a-input>
|
<a-input v-model.trim="outbound.stream.reality.shortId" :style="{ width: '250px' }"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="SpiderX">
|
<a-form-item label="SpiderX">
|
||||||
<a-input v-model.trim="outbound.stream.reality.spiderX" style="width:250px"></a-input>
|
<a-input v-model.trim="outbound.stream.reality.spiderX" :style="{ width: '250px' }"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Public Key">
|
<a-form-item label="Public Key">
|
||||||
<a-input v-model.trim="outbound.stream.reality.publicKey"></a-input>
|
<a-input v-model.trim="outbound.stream.reality.publicKey"></a-input>
|
||||||
@@ -506,11 +506,11 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="2" tab="JSON" force-render="true">
|
<a-tab-pane key="2" tab="JSON" force-render="true">
|
||||||
<a-space direction="vertical" :size="10" style="margin-top: 10px;">
|
<a-space direction="vertical" :size="10" :style="{ marginTop: '10px' }">
|
||||||
<a-input addon-before='{{ i18n "pages.xray.outbound.link" }}' v-model.trim="outModal.link" placeholder="vmess:// vless:// trojan:// ss://">
|
<a-input addon-before='{{ i18n "pages.xray.outbound.link" }}' v-model.trim="outModal.link" placeholder="vmess:// vless:// trojan:// ss://">
|
||||||
<a-icon slot="addonAfter" type="form" @click="convertLink"></a-icon>
|
<a-icon slot="addonAfter" type="form" @click="convertLink"></a-icon>
|
||||||
</a-input>
|
</a-input>
|
||||||
<textarea style="position:absolute; left: -800px;" id="outboundJson"></textarea>
|
<textarea :style="{ position: 'absolute', left: '-800px' }" id="outboundJson"></textarea>
|
||||||
</a-space>
|
</a-space>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{{define "form/http"}}
|
{{define "form/http"}}
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<table style="width: 100%; text-align: center; margin: 1rem 0;">
|
<table :style="{ width: '100%', textAlign: 'center', margin: '1rem 0' }">
|
||||||
<tr>
|
<tr>
|
||||||
<td width="45%">{{ i18n "username" }}</td>
|
<td width="45%">{{ i18n "username" }}</td>
|
||||||
<td width="45%">{{ i18n "password" }}</td>
|
<td width="45%">{{ i18n "password" }}</td>
|
||||||
@@ -9,11 +9,11 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
|
<a-input-group compact v-for="(account, index) in inbound.settings.accounts" :style="{ marginBottom: '10px' }">
|
||||||
<a-input style="width: 50%" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
|
||||||
<template slot="addonAfter">
|
<template slot="addonAfter">
|
||||||
<a-button icon="minus" size="small" @click="inbound.settings.delAccount(index)"></a-button>
|
<a-button icon="minus" size="small" @click="inbound.settings.delAccount(index)"></a-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
<a-input v-model.trim="inbound.settings.password"></a-input>
|
<a-input v-model.trim="inbound.settings.password"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
||||||
<a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="inbound.settings.network" :style="{ width: '100px' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="tcp,udp">TCP,UDP</a-select-option>
|
<a-select-option value="tcp,udp">TCP,UDP</a-select-option>
|
||||||
<a-select-option value="tcp">TCP</a-select-option>
|
<a-select-option value="tcp">TCP</a-select-option>
|
||||||
<a-select-option value="udp">UDP</a-select-option>
|
<a-select-option value="udp">UDP</a-select-option>
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<a-switch :checked="inbound.settings.auth === 'password'" @change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
|
<a-switch :checked="inbound.settings.auth === 'password'" @change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<template v-if="inbound.settings.auth === 'password'">
|
<template v-if="inbound.settings.auth === 'password'">
|
||||||
<table style="width: 100%; text-align: center; margin: 1rem 0;">
|
<table :style="{ width: '100%', textAlign: 'center', margin: '1rem 0' }">
|
||||||
<tr>
|
<tr>
|
||||||
<td width="45%">{{ i18n "username" }}</td>
|
<td width="45%">{{ i18n "username" }}</td>
|
||||||
<td width="45%">{{ i18n "password" }}</td>
|
<td width="45%">{{ i18n "password" }}</td>
|
||||||
@@ -19,11 +19,11 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
|
<a-input-group compact v-for="(account, index) in inbound.settings.accounts" :style="{ marginBottom: '10px' }">
|
||||||
<a-input style="width: 50%" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
|
||||||
<template slot="addonAfter">
|
<template slot="addonAfter">
|
||||||
<a-button icon="minus" size="small" @click="inbound.settings.delAccount(index)"></a-button>
|
<a-button icon="minus" size="small" @click="inbound.settings.delAccount(index)"></a-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<!-- trojan fallbacks -->
|
<!-- trojan fallbacks -->
|
||||||
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider style="margin:0;"> Fallback [[ index + 1 ]] <a-icon type="delete" @click="() => inbound.settings.delFallback(index)" style="color: rgb(255, 77, 79);cursor: pointer;"></a-icon>
|
<a-divider :style="{ margin: '0' }"> Fallback [[ index + 1 ]] <a-icon type="delete" @click="() => inbound.settings.delFallback(index)" :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<a-form-item label='SNI'>
|
<a-form-item label='SNI'>
|
||||||
<a-input v-model="fallback.name"></a-input>
|
<a-input v-model="fallback.name"></a-input>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<!-- vless fallbacks -->
|
<!-- vless fallbacks -->
|
||||||
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form v-for="(fallback, index) in inbound.settings.fallbacks" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider style="margin:0;"> Fallback [[ index + 1 ]] <a-icon type="delete" @click="() => inbound.settings.delFallback(index)" style="color: rgb(255, 77, 79);cursor: pointer;"></a-icon>
|
<a-divider :style="{ margin: '0' }"> Fallback [[ index + 1 ]] <a-icon type="delete" @click="() => inbound.settings.delFallback(index)" :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<a-form-item label='SNI'>
|
<a-form-item label='SNI'>
|
||||||
<a-input v-model="fallback.name"></a-input>
|
<a-input v-model="fallback.name"></a-input>
|
||||||
@@ -45,6 +45,6 @@
|
|||||||
<a-input-number v-model.number="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-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
<a-divider style="margin:5px 0;"></a-divider>
|
<a-divider :style="{ margin: '5px 0' }"></a-divider>
|
||||||
</template>
|
</template>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<a-button icon="plus" type="primary" size="small" @click="inbound.settings.addPeer()"></a-button>
|
<a-button icon="plus" type="primary" size="small" @click="inbound.settings.addPeer()"></a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form v-for="(peer, index) in inbound.settings.peers" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form v-for="(peer, index) in inbound.settings.peers" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider style="margin:0;"> Peer [[ index + 1 ]] <a-icon v-if="inbound.settings.peers.length>1" type="delete" @click="() => inbound.settings.delPeer(index)" style="color: rgb(255, 77, 79);cursor: pointer;"></a-icon>
|
<a-divider :style="{ margin: '0' }"> Peer [[ index + 1 ]] <a-icon v-if="inbound.settings.peers.length>1" type="delete" @click="() => inbound.settings.delPeer(index)" :style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<template slot="label">
|
<template slot="label">
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
{{ i18n "pages.xray.wireguard.allowedIPs" }}
|
{{ i18n "pages.xray.wireguard.allowedIPs" }}
|
||||||
<a-button icon="plus" type="primary" size="small" @click="peer.allowedIPs.push('')"></a-button>
|
<a-button icon="plus" type="primary" size="small" @click="peer.allowedIPs.push('')"></a-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-for="(aip, index) in peer.allowedIPs" style="margin-bottom: 10px;">
|
<template v-for="(aip, index) in peer.allowedIPs" :style="{ marginBottom: '10px' }">
|
||||||
<a-input v-model.trim="peer.allowedIPs[index]">
|
<a-input v-model.trim="peer.allowedIPs[index]">
|
||||||
<a-button icon="minus" v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)"></a-button>
|
<a-button icon="minus" v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)"></a-button>
|
||||||
</a-input>
|
</a-input>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
<a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number>
|
<a-input-number v-model.number="inbound.stream.reality.xver" :min="0"></a-input-number>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='uTLS'>
|
<a-form-item label='uTLS'>
|
||||||
<a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 100%"
|
<a-select v-model="inbound.stream.reality.settings.fingerprint" :style="{ width: '100%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
{{define "form/externalProxy"}}
|
{{define "form/externalProxy"}}
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider style="margin:5px 0 0;"></a-divider>
|
<a-divider :style="{ margin: '5px 0 0' }"></a-divider>
|
||||||
<a-form-item label="External Proxy">
|
<a-form-item label="External Proxy">
|
||||||
<a-switch v-model="externalProxy"></a-switch>
|
<a-switch v-model="externalProxy"></a-switch>
|
||||||
<a-button icon="plus" v-if="externalProxy" type="primary" style="margin-left: 10px" size="small" @click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', port: 443, remark: ''})"></a-button>
|
<a-button icon="plus" v-if="externalProxy" type="primary" :style="{ marginLeft: '10px' }" size="small" @click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', port: 443, remark: ''})"></a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-input-group style="margin: 8px 0;" compact v-for="(row, index) in inbound.stream.externalProxy">
|
<a-input-group :style="{ margin: '8px 0' }" compact v-for="(row, index) in inbound.stream.externalProxy">
|
||||||
<template>
|
<template>
|
||||||
<a-tooltip title="Force TLS">
|
<a-tooltip title="Force TLS">
|
||||||
<a-select v-model="row.forceTls" style="width:20%; margin: 0px" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="row.forceTls" :style="{ width: '20%', margin: '0px' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="same">{{ i18n "pages.inbounds.same" }}</a-select-option>
|
<a-select-option value="same">{{ i18n "pages.inbounds.same" }}</a-select-option>
|
||||||
<a-select-option value="none">{{ i18n "none" }}</a-select-option>
|
<a-select-option value="none">{{ i18n "none" }}</a-select-option>
|
||||||
<a-select-option value="tls">TLS</a-select-option>
|
<a-select-option value="tls">TLS</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-input style="width: 35%" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input>
|
<a-input :style="{ width: '30%' }" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input>
|
||||||
<a-tooltip title='{{ i18n "pages.inbounds.port" }}'>
|
<a-tooltip title='{{ i18n "pages.inbounds.port" }}'>
|
||||||
<a-input-number style="width: 15%;" v-model.number="row.port" min="1" max="65531"></a-input-number>
|
<a-input-number :style="{ width: '15%' }" v-model.number="row.port" min="1" max="65531"></a-input-number>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-input style="width: 30%; top: 0;" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'>
|
<a-input :style="{ width: '30%', top: '0' }" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'>
|
||||||
<template slot="addonAfter">
|
<template slot="addonAfter">
|
||||||
<a-button icon="minus" size="small" @click="inbound.stream.externalProxy.splice(index, 1)"></a-button>
|
<a-button icon="minus" size="small" @click="inbound.stream.externalProxy.splice(index, 1)"></a-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -14,10 +14,10 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :wrapper-col="{span:24}">
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.httpupgrade.headers">
|
<a-input-group compact v-for="(header, index) in inbound.stream.httpupgrade.headers">
|
||||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
<a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.httpupgrade.removeHeader(index)"></a-button>
|
<a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.httpupgrade.removeHeader(index)"></a-button>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{{define "form/streamKCP"}}
|
{{define "form/streamKCP"}}
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label='{{ i18n "camouflage" }}'>
|
<a-form-item label='{{ i18n "camouflage" }}'>
|
||||||
<a-select v-model="inbound.stream.kcp.type" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="inbound.stream.kcp.type" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="none">None</a-select-option>
|
<a-select-option value="none">None</a-select-option>
|
||||||
<a-select-option value="srtp">SRTP</a-select-option>
|
<a-select-option value="srtp">SRTP</a-select-option>
|
||||||
<a-select-option value="utp">uTP</a-select-option>
|
<a-select-option value="utp">uTP</a-select-option>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<!-- select stream network -->
|
<!-- select stream network -->
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label='{{ i18n "transmission" }}'>
|
<a-form-item label='{{ i18n "transmission" }}'>
|
||||||
<a-select v-model="inbound.stream.network" style="width: 75%" @change="streamNetworkChange"
|
<a-select v-model="inbound.stream.network" :style="{ width: '75%' }" @change="streamNetworkChange"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="tcp">TCP (RAW)</a-select-option>
|
<a-select-option value="tcp">TCP (RAW)</a-select-option>
|
||||||
<a-select-option value="kcp">mKCP</a-select-option>
|
<a-select-option value="kcp">mKCP</a-select-option>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{{define "form/streamSockopt"}}
|
{{define "form/streamSockopt"}}
|
||||||
<a-divider style="margin:5px 0 0;"></a-divider>
|
<a-divider :style="{ margin: '5px 0 0' }"></a-divider>
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label="Sockopt">
|
<a-form-item label="Sockopt">
|
||||||
<a-switch v-model="inbound.stream.sockoptSwitch"></a-switch>
|
<a-switch v-model="inbound.stream.sockoptSwitch"></a-switch>
|
||||||
@@ -39,17 +39,17 @@
|
|||||||
<a-switch v-model.trim="inbound.stream.sockopt.V6Only"></a-switch>
|
<a-switch v-model.trim="inbound.stream.sockopt.V6Only"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Domain Strategy'>
|
<a-form-item label='Domain Strategy'>
|
||||||
<a-select v-model="inbound.stream.sockopt.domainStrategy" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="inbound.stream.sockopt.domainStrategy" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in DOMAIN_STRATEGY_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in DOMAIN_STRATEGY_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='TCP Congestion'>
|
<a-form-item label='TCP Congestion'>
|
||||||
<a-select v-model="inbound.stream.sockopt.tcpcongestion" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="inbound.stream.sockopt.tcpcongestion" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in TCP_CONGESTION_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in TCP_CONGESTION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="TProxy">
|
<a-form-item label="TProxy">
|
||||||
<a-select v-model="inbound.stream.sockopt.tproxy" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="inbound.stream.sockopt.tproxy" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="off">Off</a-select-option>
|
<a-select-option value="off">Off</a-select-option>
|
||||||
<a-select-option value="redirect">Redirect</a-select-option>
|
<a-select-option value="redirect">Redirect</a-select-option>
|
||||||
<a-select-option value="tproxy">TProxy</a-select-option>
|
<a-select-option value="tproxy">TProxy</a-select-option>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
<a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<!-- tcp request -->
|
<!-- tcp request -->
|
||||||
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.request" }}</a-divider>
|
<a-divider :style="{ margin: '0' }">{{ i18n "pages.inbounds.stream.general.request" }}</a-divider>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
||||||
<a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
|
<a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -33,17 +33,17 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :wrapper-col="{span:24}">
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
|
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
|
||||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
<a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)"></a-button>
|
<a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)"></a-button>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<!-- tcp response -->
|
<!-- tcp response -->
|
||||||
<a-divider style="margin:0;">{{ i18n "pages.inbounds.stream.general.response" }}</a-divider>
|
<a-divider :style="{ margin: '0' }">{{ i18n "pages.inbounds.stream.general.response" }}</a-divider>
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
||||||
<a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
|
<a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -58,10 +58,10 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :wrapper-col="{span:24}">
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers">
|
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers">
|
||||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
<template slot="addonAfter">
|
<template slot="addonAfter">
|
||||||
<a-button icon="minus" size="small" @click="inbound.stream.tcp.response.removeHeader(index)"></a-button>
|
<a-button icon="minus" size="small" @click="inbound.stream.tcp.response.removeHeader(index)"></a-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :wrapper-col="{span:24}">
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.ws.headers">
|
<a-input-group compact v-for="(header, index) in inbound.stream.ws.headers">
|
||||||
<a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
<a-input :style="{ width: '50%' }" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
<a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.ws.removeHeader(index)"></a-button>
|
<a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.ws.removeHeader(index)"></a-button>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
@@ -11,18 +11,18 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :wrapper-col="{span:24}">
|
<a-form-item :wrapper-col="{span:24}">
|
||||||
<a-input-group compact v-for="(header, index) in inbound.stream.xhttp.headers">
|
<a-input-group compact v-for="(header, index) in inbound.stream.xhttp.headers">
|
||||||
<a-input style="width: 50%" v-model.trim="header.name"
|
<a-input :style="{ width: '50%' }" v-model.trim="header.name"
|
||||||
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input style="width: 50%" v-model.trim="header.value"
|
<a-input :style="{ width: '50%' }" v-model.trim="header.value"
|
||||||
placeholder='{{ i18n "pages.inbounds.stream.general.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-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.xhttp.removeHeader(index)"></a-button>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Mode'>
|
<a-form-item label='Mode'>
|
||||||
<a-select v-model="inbound.stream.xhttp.mode" style="width: 50%"
|
<a-select v-model="inbound.stream.xhttp.mode" :style="{ width: '50%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{{define "form/tlsSettings"}}
|
{{define "form/tlsSettings"}}
|
||||||
<!-- tls enable -->
|
<!-- tls enable -->
|
||||||
<a-form v-if="inbound.canEnableTls()" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form v-if="inbound.canEnableTls()" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider style="margin:3px 0;"></a-divider>
|
<a-divider :style="{ margin: '3px 0' }"></a-divider>
|
||||||
<a-form-item label='{{ i18n "security" }}'>
|
<a-form-item label='{{ i18n "security" }}'>
|
||||||
<a-radio-group v-model="inbound.stream.security" button-style="solid">
|
<a-radio-group v-model="inbound.stream.security" button-style="solid">
|
||||||
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
||||||
@@ -23,18 +23,18 @@
|
|||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Min/Max Version">
|
<a-form-item label="Min/Max Version">
|
||||||
<a-input-group compact>
|
<a-input-group compact>
|
||||||
<a-select v-model="inbound.stream.tls.minVersion" style="width: 50%"
|
<a-select v-model="inbound.stream.tls.minVersion" :style="{ width: '50%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select v-model="inbound.stream.tls.maxVersion" style="width: 50%"
|
<a-select v-model="inbound.stream.tls.maxVersion" :style="{ width: '50%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="uTLS">
|
<a-form-item label="uTLS">
|
||||||
<a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 100%"
|
<a-select v-model="inbound.stream.tls.settings.fingerprint" :style="{ width: '100%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value=''>None</a-select-option>
|
<a-select-option value=''>None</a-select-option>
|
||||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
|
||||||
@@ -67,9 +67,9 @@
|
|||||||
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
<a-button icon="plus" v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()"
|
<a-button icon="plus" v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()"
|
||||||
style="margin-left: 10px"></a-button>
|
:style="{ marginLeft: '10px' }"></a-button>
|
||||||
<a-button icon="minus" v-if="inbound.stream.tls.certs.length>1" type="primary" size="small"
|
<a-button icon="minus" v-if="inbound.stream.tls.certs.length>1" type="primary" size="small"
|
||||||
@click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px"></a-button>
|
@click="inbound.stream.tls.removeCert(index)" :style="{ marginLeft: '10px' }"></a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<template v-if="cert.useFile">
|
<template v-if="cert.useFile">
|
||||||
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
|
<a-form-item label='{{ i18n "pages.inbounds.publicKey" }}'>
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
<a-switch v-model="cert.oneTimeLoading"></a-switch>
|
<a-switch v-model="cert.oneTimeLoading"></a-switch>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Usage Option'>
|
<a-form-item label='Usage Option'>
|
||||||
<a-select v-model="cert.usage" style="width: 50%" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="cert.usage" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in USAGE_OPTION" :value="key">[[ key ]]</a-select-option>
|
<a-select-option v-for="key in USAGE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -133,12 +133,12 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
{{ template "commonSider" . }}
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-alert type="error" v-if="showAlert" style="margin-bottom: 10px"
|
<a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }"
|
||||||
message='{{ i18n "secAlertTitle" }}'
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
color="red"
|
color="red"
|
||||||
description='{{ i18n "secAlertSsl" }}'
|
description='{{ i18n "secAlertSsl" }}'
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
</a-alert>
|
</a-alert>
|
||||||
</transition>
|
</transition>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-card size="small" style="padding: 16px;" hoverable>
|
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :sm="12" :md="6">
|
<a-col :sm="12" :md="6">
|
||||||
<a-custom-statistic title='{{ i18n "pages.inbounds.totalDownUp" }}' :value="`${SizeFormatter.sizeFormat(total.up)} / ${SizeFormatter.sizeFormat(total.down)}`">
|
<a-custom-statistic title='{{ i18n "pages.inbounds.totalDownUp" }}' :value="`${SizeFormatter.sizeFormat(total.up)} / ${SizeFormatter.sizeFormat(total.down)}`">
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
<a-icon type="file-done"></a-icon>
|
<a-icon type="file-done"></a-icon>
|
||||||
{{ i18n "pages.inbounds.resetAllClientTraffics" }}
|
{{ i18n "pages.inbounds.resetAllClientTraffics" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="delDepletedClients" style="color: #FF4D4F;">
|
<a-menu-item key="delDepletedClients" :style="{ color: '#FF4D4F' }">
|
||||||
<a-icon type="rest"></a-icon>
|
<a-icon type="rest"></a-icon>
|
||||||
{{ i18n "pages.inbounds.delDepletedClients" }}
|
{{ i18n "pages.inbounds.delDepletedClients" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
<span>{{ i18n "pages.inbounds.autoRefreshInterval" }}</span>
|
<span>{{ i18n "pages.inbounds.autoRefreshInterval" }}</span>
|
||||||
<a-select v-model="refreshInterval"
|
<a-select v-model="refreshInterval"
|
||||||
:disabled="!isRefreshEnabled"
|
:disabled="!isRefreshEnabled"
|
||||||
style="width: 100%;"
|
:style="{ width: '100%' }"
|
||||||
@change="changeRefreshInterval"
|
@change="changeRefreshInterval"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option>
|
<a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option>
|
||||||
@@ -276,14 +276,14 @@
|
|||||||
</a-button-group>
|
</a-button-group>
|
||||||
</template>
|
</template>
|
||||||
<a-space direction="vertical">
|
<a-space direction="vertical">
|
||||||
<div :style="isMobile ? '' : 'display: flex; align-items: center; justify-content: flex-start;'">
|
<div :style="isMobile ? {} : { display: 'flex', alignItems: 'center', justifyContent: 'flex-start' }">
|
||||||
<a-switch v-model="enableFilter"
|
<a-switch v-model="enableFilter"
|
||||||
:style="isMobile ? 'margin-bottom: .5rem; display: flex;' : 'margin-right: .5rem;'"
|
:style="isMobile ? { marginBottom: '.5rem', display: 'flex' } : { marginRight: '.5rem' }"
|
||||||
@change="toggleFilter">
|
@change="toggleFilter">
|
||||||
<a-icon slot="checkedChildren" type="search"></a-icon>
|
<a-icon slot="checkedChildren" type="search"></a-icon>
|
||||||
<a-icon slot="unCheckedChildren" type="filter"></a-icon>
|
<a-icon slot="unCheckedChildren" type="filter"></a-icon>
|
||||||
</a-switch>
|
</a-switch>
|
||||||
<a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus style="max-width: 300px" :size="isMobile ? 'small' : ''"></a-input>
|
<a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus :style="{ maxWidth: '300px' }" :size="isMobile ? 'small' : ''"></a-input>
|
||||||
<a-radio-group v-if="enableFilter" v-model="filterBy" @change="filterInbounds" button-style="solid" :size="isMobile ? 'small' : ''">
|
<a-radio-group v-if="enableFilter" v-model="filterBy" @change="filterInbounds" button-style="solid" :size="isMobile ? 'small' : ''">
|
||||||
<a-radio-button value="">{{ i18n "none" }}</a-radio-button>
|
<a-radio-button value="">{{ i18n "none" }}</a-radio-button>
|
||||||
<a-radio-button value="deactive">{{ i18n "disabled" }}</a-radio-button>
|
<a-radio-button value="deactive">{{ i18n "disabled" }}</a-radio-button>
|
||||||
@@ -301,10 +301,10 @@
|
|||||||
:expand-icon-column-index="0"
|
:expand-icon-column-index="0"
|
||||||
:indent-size="0"
|
:indent-size="0"
|
||||||
:row-class-name="dbInbound => (dbInbound.isMultiUser() ? '' : 'hideExpandIcon')"
|
:row-class-name="dbInbound => (dbInbound.isMultiUser() ? '' : 'hideExpandIcon')"
|
||||||
style="margin-top: 10px">
|
:style="{ marginTop: '10px' }">
|
||||||
<template slot="action" slot-scope="text, dbInbound">
|
<template slot="action" slot-scope="text, dbInbound">
|
||||||
<a-dropdown :trigger="['click']">
|
<a-dropdown :trigger="['click']">
|
||||||
<a-icon @click="e => e.preventDefault()" type="more" style="font-size: 20px; text-decoration: solid;"></a-icon>
|
<a-icon @click="e => e.preventDefault()" type="more" :style="{ fontSize: '20px', textDecoration: 'solid' }"></a-icon>
|
||||||
<a-menu slot="overlay" @click="a => clickAction(a, dbInbound)" :theme="themeSwitcher.currentTheme">
|
<a-menu slot="overlay" @click="a => clickAction(a, dbInbound)" :theme="themeSwitcher.currentTheme">
|
||||||
<a-menu-item key="edit">
|
<a-menu-item key="edit">
|
||||||
<a-icon type="edit"></a-icon>
|
<a-icon type="edit"></a-icon>
|
||||||
@@ -335,7 +335,7 @@
|
|||||||
<a-icon type="export"></a-icon>
|
<a-icon type="export"></a-icon>
|
||||||
{{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }}
|
{{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="delDepletedClients" style="color: #FF4D4F;">
|
<a-menu-item key="delDepletedClients" :style="{ color: '#FF4D4F' }">
|
||||||
<a-icon type="rest"></a-icon>
|
<a-icon type="rest"></a-icon>
|
||||||
{{ i18n "pages.inbounds.delDepletedClients" }}
|
{{ i18n "pages.inbounds.delDepletedClients" }}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
@@ -357,7 +357,7 @@
|
|||||||
<a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}}
|
<a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}}
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-item key="delete">
|
<a-menu-item key="delete">
|
||||||
<span style="color: #FF4D4F">
|
<span :style="{ color: '#FF4D4F' }">
|
||||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||||
</span>
|
</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
@@ -369,39 +369,39 @@
|
|||||||
</a-dropdown>
|
</a-dropdown>
|
||||||
</template>
|
</template>
|
||||||
<template slot="protocol" slot-scope="text, dbInbound">
|
<template slot="protocol" slot-scope="text, dbInbound">
|
||||||
<a-tag style="margin:0;" color="purple">[[ dbInbound.protocol ]]</a-tag>
|
<a-tag :style="{ margin: '0' }" color="purple">[[ dbInbound.protocol ]]</a-tag>
|
||||||
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||||
<a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
|
<a-tag :style="{ margin: '0' }" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
|
||||||
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag>
|
<a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isTls" color="blue">TLS</a-tag>
|
||||||
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag>
|
<a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isReality" color="blue">Reality</a-tag>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template slot="clients" slot-scope="text, dbInbound">
|
<template slot="clients" slot-scope="text, dbInbound">
|
||||||
<template v-if="clientCount[dbInbound.id]">
|
<template v-if="clientCount[dbInbound.id]">
|
||||||
<a-tag style="margin:0;" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
<a-tag :style="{ margin: '0' }" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
||||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<div v-for="clientEmail in clientCount[dbInbound.id].deactive"><span>[[ clientEmail ]]</span></div>
|
<div v-for="clientEmail in clientCount[dbInbound.id].deactive"><span>[[ clientEmail ]]</span></div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="margin:0; padding: 0 2px;" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<div v-for="clientEmail in clientCount[dbInbound.id].depleted"><span>[[ clientEmail ]]</span></div>
|
<div v-for="clientEmail in clientCount[dbInbound.id].depleted"><span>[[ clientEmail ]]</span></div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="margin:0; padding: 0 2px;" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<div v-for="clientEmail in clientCount[dbInbound.id].expiring"><span>[[ clientEmail ]]</span></div>
|
<div v-for="clientEmail in clientCount[dbInbound.id].expiring"><span>[[ clientEmail ]]</span></div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="margin:0; padding: 0 2px;" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<div v-for="clientEmail in clientCount[dbInbound.id].online"><span>[[ clientEmail ]]</span></div>
|
<div v-for="clientEmail in clientCount[dbInbound.id].online"><span>[[ clientEmail ]]</span></div>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="margin:0; padding: 0 2px;" color="blue" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="blue" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
@@ -443,7 +443,7 @@
|
|||||||
<template v-else slot="content">
|
<template v-else slot="content">
|
||||||
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
|
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="min-width: 50px;" :color="ColorUtils.usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
|
<a-tag :style="{ minWidth: '50px' }" :color="ColorUtils.usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
|
||||||
[[ remainedDays(dbInbound._expiryTime) ]]
|
[[ remainedDays(dbInbound._expiryTime) ]]
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
@@ -460,11 +460,11 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "pages.inbounds.protocol" }}</td>
|
<td>{{ i18n "pages.inbounds.protocol" }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a-tag style="margin:0;" color="purple">[[ dbInbound.protocol ]]</a-tag>
|
<a-tag :style="{ margin: '0' }" color="purple">[[ dbInbound.protocol ]]</a-tag>
|
||||||
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||||
<a-tag style="margin:0;" color="blue">[[ dbInbound.toInbound().stream.network ]]</a-tag>
|
<a-tag :style="{ margin: '0' }" color="blue">[[ dbInbound.toInbound().stream.network ]]</a-tag>
|
||||||
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="green">tls</a-tag>
|
<a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isTls" color="green">tls</a-tag>
|
||||||
<a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="green">reality</a-tag>
|
<a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isReality" color="green">reality</a-tag>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -475,30 +475,30 @@
|
|||||||
<tr v-if="clientCount[dbInbound.id]">
|
<tr v-if="clientCount[dbInbound.id]">
|
||||||
<td>{{ i18n "clients" }}</td>
|
<td>{{ i18n "clients" }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a-tag style="margin:0;" color="blue">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
<a-tag :style="{ margin: '0' }" color="blue">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
||||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail ]]</p>
|
<p v-for="clientEmail in clientCount[dbInbound.id].deactive">[[ clientEmail ]]</p>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="margin:0; padding: 0 2px;" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" v-if="clientCount[dbInbound.id].deactive.length">[[ clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail ]]</p>
|
<p v-for="clientEmail in clientCount[dbInbound.id].depleted">[[ clientEmail ]]</p>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="margin:0; padding: 0 2px;" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="red" v-if="clientCount[dbInbound.id].depleted.length">[[ clientCount[dbInbound.id].depleted.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail ]]</p>
|
<p v-for="clientEmail in clientCount[dbInbound.id].expiring">[[ clientEmail ]]</p>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="margin:0; padding: 0 2px;" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange" v-if="clientCount[dbInbound.id].expiring.length">[[ clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail ]]</p>
|
<p v-for="clientEmail in clientCount[dbInbound.id].online">[[ clientEmail ]]</p>
|
||||||
</template>
|
</template>
|
||||||
<a-tag style="margin:0; padding: 0 2px;" color="green" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="green" v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length ]]</a-tag>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -535,7 +535,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "pages.inbounds.expireDate" }}</td>
|
<td>{{ i18n "pages.inbounds.expireDate" }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a-tag style="min-width: 50px; text-align: center;" v-if="dbInbound.expiryTime > 0"
|
<a-tag :style="{ minWidth: '50px', textAlign: 'center' }" v-if="dbInbound.expiryTime > 0"
|
||||||
:color="dbInbound.isExpiry? 'red': 'blue'">
|
:color="dbInbound.isExpiry? 'red': 'blue'">
|
||||||
<template v-if="app.datepicker === 'gregorian'">
|
<template v-if="app.datepicker === 'gregorian'">
|
||||||
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
|
[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]
|
||||||
@@ -544,7 +544,7 @@
|
|||||||
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
|
[[ DateUtil.convertToJalalian(moment(dbInbound.expiryTime)) ]]
|
||||||
</template>
|
</template>
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<a-tag v-else style="text-align: center;" color="purple" class="infinite-tag">
|
<a-tag v-else :style="{ textAlign: 'center' }" color="purple" class="infinite-tag">
|
||||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||||
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -554,8 +554,8 @@
|
|||||||
</table>
|
</table>
|
||||||
</template>
|
</template>
|
||||||
<a-badge>
|
<a-badge>
|
||||||
<a-icon v-if="!dbInbound.enable" slot="count" type="pause-circle" :style="'color: ' + themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-icon>
|
<a-icon v-if="!dbInbound.enable" slot="count" type="pause-circle" :style="{ color: themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc' }"></a-icon>
|
||||||
<a-button shape="round" size="small" style="font-size: 14px; padding: 0 10px;">
|
<a-button shape="round" size="small" :style="{ fontSize: '14px', padding: '0 10px' }">
|
||||||
<a-icon type="info"></a-icon>
|
<a-icon type="info"></a-icon>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-badge>
|
</a-badge>
|
||||||
@@ -567,8 +567,8 @@
|
|||||||
:columns="isMobile ? innerMobileColumns : innerColumns"
|
:columns="isMobile ? innerMobileColumns : innerColumns"
|
||||||
:data-source="getInboundClients(record)"
|
:data-source="getInboundClients(record)"
|
||||||
:pagination=pagination(getInboundClients(record))
|
:pagination=pagination(getInboundClients(record))
|
||||||
:style="isMobile ? 'margin: -10px 2px -11px;' : 'margin: -10px 22px -11px;'">
|
:style="{ margin: `-10px ${isMobile ? '2px' : '22px'} -11px` }">
|
||||||
{{template "client_table"}}
|
{{template "component/aClientTable"}}
|
||||||
</a-table>
|
</a-table>
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
</a-table>
|
||||||
@@ -584,6 +584,7 @@
|
|||||||
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
|
||||||
|
{{template "component/aSidebar" .}}
|
||||||
{{template "component/aThemeSwitch" .}}
|
{{template "component/aThemeSwitch" .}}
|
||||||
{{template "component/aCustomStatistic" .}}
|
{{template "component/aCustomStatistic" .}}
|
||||||
{{template "component/aPersianDatepicker" .}}
|
{{template "component/aPersianDatepicker" .}}
|
||||||
@@ -677,8 +678,8 @@
|
|||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#app',
|
el: '#app',
|
||||||
|
mixins: [MediaQueryMixin],
|
||||||
data: {
|
data: {
|
||||||
siderDrawer,
|
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
persianDatepicker,
|
persianDatepicker,
|
||||||
spinning: false,
|
spinning: false,
|
||||||
@@ -709,7 +710,6 @@
|
|||||||
showAlert: false,
|
showAlert: false,
|
||||||
ipLimitEnable: false,
|
ipLimitEnable: false,
|
||||||
pageSize: 50,
|
pageSize: 50,
|
||||||
isMobile: window.innerWidth <= 768,
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loading(spinning = true) {
|
loading(spinning = true) {
|
||||||
@@ -1471,9 +1471,6 @@
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
|
||||||
onResize() {
|
|
||||||
this.isMobile = window.innerWidth <= 768;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -1485,8 +1482,6 @@
|
|||||||
if (window.location.protocol !== "https:") {
|
if (window.location.protocol !== "https:") {
|
||||||
this.showAlert = true;
|
this.showAlert = true;
|
||||||
}
|
}
|
||||||
window.addEventListener('resize', this.onResize);
|
|
||||||
this.onResize();
|
|
||||||
this.loading();
|
this.loading();
|
||||||
this.getDefaultSettings();
|
this.getDefaultSettings();
|
||||||
if (this.isRefreshEnabled) {
|
if (this.isRefreshEnabled) {
|
||||||
@@ -1524,12 +1519,12 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{{template "inboundModal"}}
|
{{template "modals/inboundModal"}}
|
||||||
{{template "promptModal"}}
|
{{template "modals/promptModal"}}
|
||||||
{{template "qrcodeModal"}}
|
{{template "modals/qrcodeModal"}}
|
||||||
{{template "textModal"}}
|
{{template "modals/textModal"}}
|
||||||
{{template "inboundInfoModal"}}
|
{{template "modals/inboundInfoModal"}}
|
||||||
{{template "clientsModal"}}
|
{{template "modals/clientsModal"}}
|
||||||
{{template "clientsBulkModal"}}
|
{{template "modals/clientsBulkModal"}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -22,6 +22,11 @@
|
|||||||
.ant-backup-list-item {
|
.ant-backup-list-item {
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
.ant-xray-version-list-item {
|
||||||
|
--padding: 12px;
|
||||||
|
padding: var(--padding) !important;
|
||||||
|
gap: var(--padding);
|
||||||
|
}
|
||||||
.dark .ant-backup-list-item svg,
|
.dark .ant-backup-list-item svg,
|
||||||
.dark .ant-badge-status-text,
|
.dark .ant-badge-status-text,
|
||||||
.dark .ant-card-extra {
|
.dark .ant-card-extra {
|
||||||
@@ -74,12 +79,12 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
{{ template "commonSider" . }}
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="200" :tip="loadingTip">
|
<a-spin :spinning="spinning" :delay="200" :tip="loadingTip">
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-alert type="error" v-if="showAlert" style="margin-bottom: 10px"
|
<a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }"
|
||||||
message='{{ i18n "secAlertTitle" }}'
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
color="red"
|
color="red"
|
||||||
description='{{ i18n "secAlertSsl" }}'
|
description='{{ i18n "secAlertSsl" }}'
|
||||||
@@ -89,8 +94,8 @@
|
|||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<template>
|
<template>
|
||||||
<a-row v-if="!status.isLoaded">
|
<a-row v-if="!status.isLoaded">
|
||||||
<a-card hoverable style="text-align: center; padding: 30px 0; margin-top: 10px; background: transparent;">
|
<a-card hoverable :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent' }">
|
||||||
<a-spin tip="Loading..."></a-spin>
|
<a-spin tip='{{ i18n "loading" }}'></a-spin>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row v-else>
|
<a-row v-else>
|
||||||
@@ -99,19 +104,22 @@
|
|||||||
<a-row>
|
<a-row>
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="12" style="text-align: center">
|
<a-col :span="12" :style="{ textAlign: 'center' }">
|
||||||
<a-progress type="dashboard" status="normal"
|
<a-progress type="dashboard" status="normal"
|
||||||
:stroke-color="status.cpu.color"
|
:stroke-color="status.cpu.color"
|
||||||
:percent="status.cpu.percent"></a-progress>
|
:percent="status.cpu.percent"></a-progress>
|
||||||
<div><b>CPU:</b> [[ CPUFormatter.cpuCoreFormat(status.cpuCores) ]] <a-tooltip>
|
<div>
|
||||||
<a-icon type="area-chart"></a-icon>
|
<b>{{ i18n "pages.index.cpu" }}:</b> [[ CPUFormatter.cpuCoreFormat(status.cpuCores) ]]
|
||||||
<template slot="title">
|
<a-tooltip>
|
||||||
<div><b>Logical Processors:</b> [[ (status.logicalPro) ]]</div>
|
<a-icon type="area-chart"></a-icon>
|
||||||
<div><b>Speed:</b> [[ CPUFormatter.cpuSpeedFormat(status.cpuSpeedMhz) ]]</div>
|
<template slot="title">
|
||||||
</template>
|
<div><b>{{ i18n "pages.index.logicalProcessors" }}:</b> [[ (status.logicalPro) ]]</div>
|
||||||
</a-tooltip></div>
|
<div><b>{{ i18n "pages.index.frequency" }}:</b> [[ CPUFormatter.cpuSpeedFormat(status.cpuSpeedMhz) ]]</div>
|
||||||
|
</template>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12" style="text-align: center">
|
<a-col :span="12" :style="{ textAlign: 'center' }">
|
||||||
<a-progress type="dashboard" status="normal"
|
<a-progress type="dashboard" status="normal"
|
||||||
:stroke-color="status.mem.color"
|
:stroke-color="status.mem.color"
|
||||||
:percent="status.mem.percent"></a-progress>
|
:percent="status.mem.percent"></a-progress>
|
||||||
@@ -123,20 +131,20 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :md="12">
|
<a-col :sm="24" :md="12">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="12" style="text-align: center">
|
<a-col :span="12" :style="{ textAlign: 'center' }">
|
||||||
<a-progress type="dashboard" status="normal"
|
<a-progress type="dashboard" status="normal"
|
||||||
:stroke-color="status.swap.color"
|
:stroke-color="status.swap.color"
|
||||||
:percent="status.swap.percent"></a-progress>
|
:percent="status.swap.percent"></a-progress>
|
||||||
<div>
|
<div>
|
||||||
<b>Swap:</b> [[ SizeFormatter.sizeFormat(status.swap.current) ]] / [[ SizeFormatter.sizeFormat(status.swap.total) ]]
|
<b>{{ i18n "pages.index.swap" }}:</b> [[ SizeFormatter.sizeFormat(status.swap.current) ]] / [[ SizeFormatter.sizeFormat(status.swap.total) ]]
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12" style="text-align: center">
|
<a-col :span="12" :style="{ textAlign: 'center' }">
|
||||||
<a-progress type="dashboard" status="normal"
|
<a-progress type="dashboard" status="normal"
|
||||||
:stroke-color="status.disk.color"
|
:stroke-color="status.disk.color"
|
||||||
:percent="status.disk.percent"></a-progress>
|
:percent="status.disk.percent"></a-progress>
|
||||||
<div>
|
<div>
|
||||||
<b>{{ i18n "pages.index.hard"}}:</b> [[ SizeFormatter.sizeFormat(status.disk.current) ]] / [[ SizeFormatter.sizeFormat(status.disk.total) ]]
|
<b>{{ i18n "pages.index.storage"}}:</b> [[ SizeFormatter.sizeFormat(status.disk.current) ]] / [[ SizeFormatter.sizeFormat(status.disk.total) ]]
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -155,31 +163,38 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<template v-if="status.xray.state != State.Error">
|
<template v-if="status.xray.state != 'error'">
|
||||||
<a-badge status="processing" class="running-animation" :text="status.xray.state" :color="status.xray.color" style="text-transform: capitalize;"/>
|
<a-badge status="processing" class="running-animation" :text="status.xray.stateMsg" :color="status.xray.color"/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<span slot="title" style="font-size: 12pt">An error occurred while running Xray
|
<span slot="title">
|
||||||
<a-tag color="purple" style="cursor: pointer; float: right;" @click="openLogs()">{{ i18n "pages.index.logs" }}</a-tag>
|
<a-row type="flex" align="middle" justify="space-between">
|
||||||
|
<a-col>
|
||||||
|
<span>{{ i18n "pages.index.xrayErrorPopoverTitle" }}</span>
|
||||||
|
</a-col>
|
||||||
|
<a-col>
|
||||||
|
<a-icon type="bars" :style="{ cursor: 'pointer', float: 'right' }" @click="openLogs()"></a-tag>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
</span>
|
</span>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p style="max-width: 400px" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</p>
|
<span :style="{ maxWidth: '400px' }" v-for="line in status.xray.errorMsg.split('\n')">[[ line ]]</span>
|
||||||
</template>
|
</template>
|
||||||
<a-badge :text="status.xray.state" :color="status.xray.color" style="text-transform: capitalize;"/>
|
<a-badge :text="status.xray.stateMsg" :color="status.xray.color"/>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a-space direction="horizontal" @click="stopXrayService" style="justify-content: center;">
|
<a-space direction="horizontal" @click="stopXrayService" :style="{ justifyContent: 'center' }">
|
||||||
<a-icon type="poweroff"></a-icon>
|
<a-icon type="poweroff"></a-icon>
|
||||||
<span v-if="!isMobile">{{ i18n "pages.index.stopXray" }}</span>
|
<span v-if="!isMobile">{{ i18n "pages.index.stopXray" }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
<a-space direction="horizontal" @click="restartXrayService" style="justify-content: center;">
|
<a-space direction="horizontal" @click="restartXrayService" :style="{ justifyContent: 'center' }">
|
||||||
<a-icon type="reload"></a-icon>
|
<a-icon type="reload"></a-icon>
|
||||||
<span v-if="!isMobile">{{ i18n "pages.index.restartXray" }}</span>
|
<span v-if="!isMobile">{{ i18n "pages.index.restartXray" }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
<a-space direction="horizontal" @click="openSelectV2rayVersion" style="justify-content: center;">
|
<a-space direction="horizontal" @click="openSelectV2rayVersion" :style="{ justifyContent: 'center' }">
|
||||||
<a-icon type="tool"></a-icon>
|
<a-icon type="tool"></a-icon>
|
||||||
<span v-if="!isMobile">
|
<span v-if="!isMobile">
|
||||||
[[ status.xray.version != 'Unknown' ? `v${status.xray.version}` : '{{ i18n "pages.index.xraySwitch" }}' ]]
|
[[ status.xray.version != 'Unknown' ? `v${status.xray.version}` : '{{ i18n "pages.index.xraySwitch" }}' ]]
|
||||||
@@ -191,15 +206,15 @@
|
|||||||
<a-col :sm="24" :lg="12">
|
<a-col :sm="24" :lg="12">
|
||||||
<a-card title='{{ i18n "menu.link" }}' hoverable>
|
<a-card title='{{ i18n "menu.link" }}' hoverable>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<a-space direction="horizontal" @click="openLogs()" style="justify-content: center;">
|
<a-space direction="horizontal" @click="openLogs()" :style="{ justifyContent: 'center' }">
|
||||||
<a-icon type="bars"></a-icon>
|
<a-icon type="bars"></a-icon>
|
||||||
<span v-if="!isMobile">{{ i18n "pages.index.logs" }}</span>
|
<span v-if="!isMobile">{{ i18n "pages.index.logs" }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
<a-space direction="horizontal" @click="openConfig" style="justify-content: center;">
|
<a-space direction="horizontal" @click="openConfig" :style="{ justifyContent: 'center' }">
|
||||||
<a-icon type="control"></a-icon>
|
<a-icon type="control"></a-icon>
|
||||||
<span v-if="!isMobile">{{ i18n "pages.index.config" }}</span>
|
<span v-if="!isMobile">{{ i18n "pages.index.config" }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
<a-space direction="horizontal" @click="openBackup" style="justify-content: center;">
|
<a-space direction="horizontal" @click="openBackup" :style="{ justifyContent: 'center' }">
|
||||||
<a-icon type="cloud-server"></a-icon>
|
<a-icon type="cloud-server"></a-icon>
|
||||||
<span v-if="!isMobile">{{ i18n "pages.index.backup" }}</span>
|
<span v-if="!isMobile">{{ i18n "pages.index.backup" }}</span>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -232,13 +247,13 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :lg="12">
|
<a-col :sm="24" :lg="12">
|
||||||
<a-card title='{{ i18n "usage"}}' hoverable>
|
<a-card title='{{ i18n "usage"}}' hoverable>
|
||||||
<a-tag color="green"> RAM: [[ SizeFormatter.sizeFormat(status.appStats.mem) ]] </a-tag>
|
<a-tag color="green"> {{ i18n "pages.index.memory" }}: [[ SizeFormatter.sizeFormat(status.appStats.mem) ]] </a-tag>
|
||||||
<a-tag color="green"> Threads: [[ status.appStats.threads ]] </a-tag>
|
<a-tag color="green"> {{ i18n "pages.index.threads" }}: [[ status.appStats.threads ]] </a-tag>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :lg="12">
|
<a-col :sm="24" :lg="12">
|
||||||
<a-card title='{{ i18n "pages.index.overallSpeed" }}' hoverable>
|
<a-card title='{{ i18n "pages.index.overallSpeed" }}' hoverable>
|
||||||
<a-row>
|
<a-row :gutter="isMobile ? [8,8] : 0">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-custom-statistic title='{{ i18n "pages.index.upload" }}' :value="SizeFormatter.sizeFormat(status.netIO.up)">
|
<a-custom-statistic title='{{ i18n "pages.index.upload" }}' :value="SizeFormatter.sizeFormat(status.netIO.up)">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
@@ -264,7 +279,7 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :lg="12">
|
<a-col :sm="24" :lg="12">
|
||||||
<a-card title='{{ i18n "pages.index.totalData" }}' hoverable>
|
<a-card title='{{ i18n "pages.index.totalData" }}' hoverable>
|
||||||
<a-row>
|
<a-row :gutter="isMobile ? [8,8] : 0">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-custom-statistic title='{{ i18n "pages.index.sent" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.sent)">
|
<a-custom-statistic title='{{ i18n "pages.index.sent" }}' :value="SizeFormatter.sizeFormat(status.netTraffic.sent)">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
@@ -292,15 +307,15 @@
|
|||||||
<a-icon :type="showIp ? 'eye' : 'eye-invisible'" :style="{ fontSize: '1rem' }" @click="showIp = !showIp"></a-icon>
|
<a-icon :type="showIp ? 'eye' : 'eye-invisible'" :style="{ fontSize: '1rem' }" @click="showIp = !showIp"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-row :class="showIp ? 'ip-visible' : 'ip-hidden'">
|
<a-row :class="showIp ? 'ip-visible' : 'ip-hidden'" :gutter="isMobile ? [8,8] : 0">
|
||||||
<a-col :xs="24" :xxl="12" :style="{ marginTop: isMobile ? '10px' : 0 }">
|
<a-col :span="isMobile ? 24 : 12">
|
||||||
<a-custom-statistic title="IPv4" :value="status.publicIP.ipv4">
|
<a-custom-statistic title="IPv4" :value="status.publicIP.ipv4">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<a-icon type="global" />
|
<a-icon type="global" />
|
||||||
</template>
|
</template>
|
||||||
</a-custom-statistic>
|
</a-custom-statistic>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :xs="24" :xxl="12" :style="{ marginTop: isMobile ? '10px' : 0 }">
|
<a-col :span="isMobile ? 24 : 12">
|
||||||
<a-custom-statistic title="IPv6" :value="status.publicIP.ipv6">
|
<a-custom-statistic title="IPv6" :value="status.publicIP.ipv6">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<a-icon type="global" />
|
<a-icon type="global" />
|
||||||
@@ -312,7 +327,7 @@
|
|||||||
</a-col>
|
</a-col>
|
||||||
<a-col :sm="24" :lg="12">
|
<a-col :sm="24" :lg="12">
|
||||||
<a-card title='{{ i18n "pages.index.connectionCount" }}' hoverable>
|
<a-card title='{{ i18n "pages.index.connectionCount" }}' hoverable>
|
||||||
<a-row>
|
<a-row :gutter="isMobile ? [8,8] : 0">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-custom-statistic title="TCP" :value="status.tcpCount">
|
<a-custom-statistic title="TCP" :value="status.tcpCount">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
@@ -338,9 +353,9 @@
|
|||||||
</a-layout>
|
</a-layout>
|
||||||
<a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}' :closable="true"
|
<a-modal id="version-modal" v-model="versionModal.visible" title='{{ i18n "pages.index.xraySwitch" }}' :closable="true"
|
||||||
@ok="() => versionModal.visible = false" :class="themeSwitcher.currentTheme" footer="">
|
@ok="() => versionModal.visible = false" :class="themeSwitcher.currentTheme" footer="">
|
||||||
<a-alert type="warning" style="margin-bottom: 12px; width: 100%;"
|
<a-alert type="warning" :style="{ marginBottom: '12px', width: '100%' }"
|
||||||
message='{{ i18n "pages.index.xraySwitchClickDesk" }}' show-icon></a-alert>
|
message='{{ i18n "pages.index.xraySwitchClickDesk" }}' show-icon></a-alert>
|
||||||
<a-list class="ant-xray-version-list" bordered style="width: 100%;">
|
<a-list class="ant-xray-version-list" bordered :style="{ width: '100%' }">
|
||||||
<a-list-item class="ant-xray-version-list-item" v-for="version, index in versionModal.versions">
|
<a-list-item class="ant-xray-version-list-item" v-for="version, index in versionModal.versions">
|
||||||
<a-tag :color="index % 2 == 0 ? 'purple' : 'green'">[[ version ]]</a-tag>
|
<a-tag :color="index % 2 == 0 ? 'purple' : 'green'">[[ version ]]</a-tag>
|
||||||
<a-radio :class="themeSwitcher.currentTheme" :checked="version === `v${status.xray.version}`" @click="switchV2rayVersion(version)"></a-radio>
|
<a-radio :class="themeSwitcher.currentTheme" :checked="version === `v${status.xray.version}`" @click="switchV2rayVersion(version)"></a-radio>
|
||||||
@@ -355,15 +370,15 @@
|
|||||||
{{ i18n "pages.index.logs" }}
|
{{ i18n "pages.index.logs" }}
|
||||||
<a-icon :spin="logModal.loading"
|
<a-icon :spin="logModal.loading"
|
||||||
type="sync"
|
type="sync"
|
||||||
style="vertical-align: middle; margin-left: 10px;"
|
:style="{ verticalAlign: 'middle', marginLeft: '10px' }"
|
||||||
:disabled="logModal.loading"
|
:disabled="logModal.loading"
|
||||||
@click="openLogs()">
|
@click="openLogs()">
|
||||||
</a-icon>
|
</a-icon>
|
||||||
</template>
|
</template>
|
||||||
<a-form layout="inline">
|
<a-form layout="inline">
|
||||||
<a-form-item style="margin-right: 0.5rem;">
|
<a-form-item :style="{ marginRight: '0.5rem' }">
|
||||||
<a-input-group compact>
|
<a-input-group compact>
|
||||||
<a-select size="small" v-model="logModal.rows" style="width:70px;"
|
<a-select size="small" v-model="logModal.rows" :style="{ width: '70px' }"
|
||||||
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="10">10</a-select-option>
|
<a-select-option value="10">10</a-select-option>
|
||||||
<a-select-option value="20">20</a-select-option>
|
<a-select-option value="20">20</a-select-option>
|
||||||
@@ -371,7 +386,7 @@
|
|||||||
<a-select-option value="100">100</a-select-option>
|
<a-select-option value="100">100</a-select-option>
|
||||||
<a-select-option value="500">500</a-select-option>
|
<a-select-option value="500">500</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
<a-select size="small" v-model="logModal.level" style="width:95px;"
|
<a-select size="small" v-model="logModal.level" :style="{ width: '95px' }"
|
||||||
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
@change="openLogs()" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option value="debug">Debug</a-select-option>
|
<a-select-option value="debug">Debug</a-select-option>
|
||||||
<a-select-option value="info">Info</a-select-option>
|
<a-select-option value="info">Info</a-select-option>
|
||||||
@@ -384,13 +399,11 @@
|
|||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-checkbox v-model="logModal.syslog" @change="openLogs()">SysLog</a-checkbox>
|
<a-checkbox v-model="logModal.syslog" @change="openLogs()">SysLog</a-checkbox>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item style="float: right;">
|
<a-form-item :style="{ float: 'right' }">
|
||||||
<a-button type="primary" icon="download"
|
<a-button type="primary" icon="download" @click="FileManager.downloadTextFile(logModal.logs?.join('\n'), 'x-ui.log')"></a-button>
|
||||||
:href="'data:application/text;charset=utf-8,' + encodeURIComponent(logModal.logs?.join('\n'))" download="x-ui.log">
|
|
||||||
</a-button>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
<div class="ant-input" style="height: auto; max-height: 500px; overflow: auto; margin-top: 0.5rem;" v-html="logModal.formattedLogs"></div>
|
<div class="ant-input" :style="{ height: 'auto', maxHeight: '500px', overflow: 'auto', marginTop: '0.5rem' }" v-html="logModal.formattedLogs"></div>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
<a-modal id="backup-modal"
|
<a-modal id="backup-modal"
|
||||||
v-model="backupModal.visible"
|
v-model="backupModal.visible"
|
||||||
@@ -398,7 +411,7 @@
|
|||||||
:closable="true"
|
:closable="true"
|
||||||
footer=""
|
footer=""
|
||||||
:class="themeSwitcher.currentTheme">
|
:class="themeSwitcher.currentTheme">
|
||||||
<a-list class="ant-backup-list" bordered style="width: 100%;">
|
<a-list class="ant-backup-list" bordered :style="{ width: '100%' }">
|
||||||
<a-list-item class="ant-backup-list-item">
|
<a-list-item class="ant-backup-list-item">
|
||||||
<a-list-item-meta>
|
<a-list-item-meta>
|
||||||
<template #title>{{ i18n "pages.index.exportDatabase" }}</template>
|
<template #title>{{ i18n "pages.index.exportDatabase" }}</template>
|
||||||
@@ -417,17 +430,11 @@
|
|||||||
</a-modal>
|
</a-modal>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "js" .}}
|
||||||
|
{{template "component/aSidebar" .}}
|
||||||
{{template "component/aThemeSwitch" .}}
|
{{template "component/aThemeSwitch" .}}
|
||||||
{{template "component/aCustomStatistic" .}}
|
{{template "component/aCustomStatistic" .}}
|
||||||
{{template "textModal"}}
|
{{template "modals/textModal"}}
|
||||||
<script>
|
<script>
|
||||||
const State = {
|
|
||||||
Running: "running",
|
|
||||||
Stop: "stop",
|
|
||||||
Error: "error",
|
|
||||||
}
|
|
||||||
Object.freeze(State);
|
|
||||||
|
|
||||||
class CurTotal {
|
class CurTotal {
|
||||||
|
|
||||||
constructor(current, total) {
|
constructor(current, total) {
|
||||||
@@ -472,7 +479,8 @@
|
|||||||
this.uptime = 0;
|
this.uptime = 0;
|
||||||
this.appUptime = 0;
|
this.appUptime = 0;
|
||||||
this.appStats = {threads: 0, mem: 0, uptime: 0};
|
this.appStats = {threads: 0, mem: 0, uptime: 0};
|
||||||
this.xray = { state: State.Stop, errorMsg: "", version: "", color: "" };
|
|
||||||
|
this.xray = { state: 'stop', stateMsg: "", errorMsg: "", version: "", color: "" };
|
||||||
|
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return;
|
return;
|
||||||
@@ -497,17 +505,22 @@
|
|||||||
this.appStats = data.appStats;
|
this.appStats = data.appStats;
|
||||||
this.xray = data.xray;
|
this.xray = data.xray;
|
||||||
switch (this.xray.state) {
|
switch (this.xray.state) {
|
||||||
case State.Running:
|
case 'running':
|
||||||
this.xray.color = "green";
|
this.xray.color = "green";
|
||||||
|
this.xray.stateMsg = '{{ i18n "pages.index.xrayStatusRunning" }}';
|
||||||
break;
|
break;
|
||||||
case State.Stop:
|
case 'stop':
|
||||||
this.xray.color = "orange";
|
this.xray.color = "orange";
|
||||||
|
this.xray.stateMsg = '{{ i18n "pages.index.xrayStatusStop" }}';
|
||||||
break;
|
break;
|
||||||
case State.Error:
|
case 'error':
|
||||||
this.xray.color = "red";
|
this.xray.color = "red";
|
||||||
|
this.xray.stateMsg ='{{ i18n "pages.index.xrayStatusError" }}';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.xray.color = "gray";
|
this.xray.color = "gray";
|
||||||
|
this.xray.stateMsg = '{{ i18n "pages.index.xrayStatusUnknown" }}';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -590,8 +603,8 @@
|
|||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#app',
|
el: '#app',
|
||||||
|
mixins: [MediaQueryMixin],
|
||||||
data: {
|
data: {
|
||||||
siderDrawer,
|
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
status: new Status(),
|
status: new Status(),
|
||||||
versionModal,
|
versionModal,
|
||||||
@@ -600,8 +613,7 @@
|
|||||||
spinning: false,
|
spinning: false,
|
||||||
loadingTip: '{{ i18n "loading"}}',
|
loadingTip: '{{ i18n "loading"}}',
|
||||||
showAlert: false,
|
showAlert: false,
|
||||||
showIp: false,
|
showIp: false
|
||||||
isMobile: window.innerWidth <= 768
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loading(spinning, tip = '{{ i18n "loading"}}') {
|
loading(spinning, tip = '{{ i18n "loading"}}') {
|
||||||
@@ -6,27 +6,33 @@
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
/* margin: 20px 0 50px 0;*/
|
/* margin: 20px 0 50px 0;*/
|
||||||
height: 110px;
|
height: 110px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-btn,
|
.ant-btn,
|
||||||
.ant-input {
|
.ant-input {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-radius: 30px;
|
border-radius: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-input-group-addon {
|
.ant-input-group-addon {
|
||||||
border-radius: 0 30px 30px 0;
|
border-radius: 0 30px 30px 0;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-input-affix-wrapper .ant-input-prefix {
|
.ant-input-affix-wrapper .ant-input-prefix {
|
||||||
left: 23px;
|
left: 23px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-input-affix-wrapper .ant-input:not(:first-child) {
|
.ant-input-affix-wrapper .ant-input:not(:first-child) {
|
||||||
padding-left: 50px;
|
padding-left: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.centered {
|
.centered {
|
||||||
display: flex;
|
display: flex;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -34,68 +40,76 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title b {
|
.title b {
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#login {
|
#login {
|
||||||
animation: charge 0.5s both;
|
animation: charge 0.5s both;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 2rem;
|
border-radius: 2rem;
|
||||||
padding: 3rem;
|
padding: 3rem;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
user-select:none;
|
user-select: none;
|
||||||
-webkit-user-select:none;
|
-webkit-user-select: none;
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#login:hover {
|
#login:hover {
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes charge {
|
@keyframes charge {
|
||||||
from {
|
from {
|
||||||
transform: translateY(5rem);
|
transform: translateY(5rem);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.under {
|
.under {
|
||||||
background-color: #c7ebe2;
|
background-color: #c7ebe2;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .under {
|
.dark .under {
|
||||||
background-color: var(--dark-color-login-wave);
|
background-color: var(--dark-color-login-wave);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark #login {
|
.dark #login {
|
||||||
background-color: var(--dark-color-surface-100);
|
background-color: var(--dark-color-surface-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark h1 {
|
.dark h1 {
|
||||||
color: rgba(255, 255, 255);
|
color: rgba(255, 255, 255);
|
||||||
}
|
}
|
||||||
.ant-form-item {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
.ant-btn-primary-login {
|
.ant-btn-primary-login {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-btn-primary-login:focus,
|
.ant-btn-primary-login:focus,
|
||||||
.ant-btn-primary-login:hover {
|
.ant-btn-primary-login:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #006655;
|
background-color: #006655;
|
||||||
border-color: #006655;
|
border-color: #006655;
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(270deg,
|
||||||
270deg,
|
rgba(123, 199, 77, 0) 30%,
|
||||||
rgba(123, 199, 77, 0) 30%,
|
#009980,
|
||||||
#009980,
|
rgba(123, 199, 77, 0) 100%);
|
||||||
rgba(123, 199, 77, 0) 100%
|
|
||||||
);
|
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
animation: ma-bg-move ease-in-out 5s infinite;
|
animation: ma-bg-move ease-in-out 5s infinite;
|
||||||
background-position-x: -500px;
|
background-position-x: -500px;
|
||||||
@@ -103,29 +117,35 @@
|
|||||||
animation-delay: -0.5s;
|
animation-delay: -0.5s;
|
||||||
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
|
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-btn-primary-login.active,
|
.ant-btn-primary-login.active,
|
||||||
.ant-btn-primary-login:active {
|
.ant-btn-primary-login:active {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #006655;
|
background-color: #006655;
|
||||||
border-color: #006655;
|
border-color: #006655;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes ma-bg-move {
|
@keyframes ma-bg-move {
|
||||||
0% {
|
0% {
|
||||||
background-position: -500px 0;
|
background-position: -500px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
background-position: 1000px 0;
|
background-position: 1000px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
background-position: 1000px 0;
|
background-position: 1000px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wave-btn-bg {
|
.wave-btn-bg {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: all 0.3s cubic-bezier(.645,.045,.355,1);
|
transition: all 0.3s cubic-bezier(.645, .045, .355, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .wave-btn-bg {
|
.dark .wave-btn-bg {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -137,15 +157,21 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.dark .wave-btn-bg:hover {animation: wave-btn-tara 4s ease infinite;}
|
|
||||||
|
.dark .wave-btn-bg:hover {
|
||||||
|
animation: wave-btn-tara 4s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.dark .wave-btn-bg-cl {
|
.dark .wave-btn-bg-cl {
|
||||||
background-image: linear-gradient(rgba(13, 14, 33, 0), rgba(13, 14, 33, 0)),
|
background-image: linear-gradient(rgba(13, 14, 33, 0), rgba(13, 14, 33, 0)),
|
||||||
radial-gradient(circle at left top, #006655, #009980, #006655) !important;
|
radial-gradient(circle at left top, #006655, #009980, #006655) !important;
|
||||||
border-radius: 3em;
|
border-radius: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .wave-btn-bg-cl:hover {
|
.dark .wave-btn-bg-cl:hover {
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .wave-btn-bg-cl:before {
|
.dark .wave-btn-bg-cl:before {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
content: "";
|
content: "";
|
||||||
@@ -160,24 +186,25 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: 0.5s;
|
transition: 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .wave-btn-bg-cl:hover::before {
|
.dark .wave-btn-bg-cl:hover::before {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
filter: blur(20px);
|
filter: blur(20px);
|
||||||
animation: wave-btn-tara 8s linear infinite;
|
animation: wave-btn-tara 8s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes wave-btn-tara {
|
@keyframes wave-btn-tara {
|
||||||
to {
|
to {
|
||||||
background-position: 300%;
|
background-position: 300%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .ant-btn-primary-login {
|
.dark .ant-btn-primary-login {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(rgba(13, 14, 33, 0.45),
|
||||||
rgba(13, 14, 33, 0.45),
|
rgba(13, 14, 33, 0.35));
|
||||||
rgba(13, 14, 33, 0.35)
|
|
||||||
);
|
|
||||||
border-radius: 2rem;
|
border-radius: 2rem;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -193,6 +220,7 @@
|
|||||||
background-position-x: 0;
|
background-position-x: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.waves-header {
|
.waves-header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -201,68 +229,83 @@
|
|||||||
color: white;
|
color: white;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .waves-header {
|
.dark .waves-header {
|
||||||
background-color: var(--dark-color-login-background);
|
background-color: var(--dark-color-login-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.waves-inner-header {
|
.waves-inner-header {
|
||||||
height: 50vh;
|
height: 50vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.waves {
|
.waves {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 15vh;
|
height: 15vh;
|
||||||
margin-bottom: -8px; /*Fix for safari gap*/
|
margin-bottom: -8px;
|
||||||
|
/*Fix for safari gap*/
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
max-height: 150px;
|
max-height: 150px;
|
||||||
}
|
}
|
||||||
.parallax > use {
|
|
||||||
|
.parallax>use {
|
||||||
animation: move-forever 25s cubic-bezier(0.55, 0.5, 0.45, 0.5) infinite;
|
animation: move-forever 25s cubic-bezier(0.55, 0.5, 0.45, 0.5) infinite;
|
||||||
}
|
}
|
||||||
.dark .parallax > use {
|
|
||||||
|
.dark .parallax>use {
|
||||||
fill: var(--dark-color-login-wave);
|
fill: var(--dark-color-login-wave);
|
||||||
}
|
}
|
||||||
.parallax > use:nth-child(1) {
|
|
||||||
|
.parallax>use:nth-child(1) {
|
||||||
animation-delay: -2s;
|
animation-delay: -2s;
|
||||||
animation-duration: 4s;
|
animation-duration: 4s;
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
.parallax > use:nth-child(2) {
|
|
||||||
|
.parallax>use:nth-child(2) {
|
||||||
animation-delay: -3s;
|
animation-delay: -3s;
|
||||||
animation-duration: 7s;
|
animation-duration: 7s;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
.parallax > use:nth-child(3) {
|
|
||||||
|
.parallax>use:nth-child(3) {
|
||||||
animation-delay: -4s;
|
animation-delay: -4s;
|
||||||
animation-duration: 10s;
|
animation-duration: 10s;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
.parallax > use:nth-child(4) {
|
|
||||||
animation-delay: -5s;
|
.parallax>use:nth-child(4) {
|
||||||
animation-duration: 13s;
|
animation-delay: -5s;
|
||||||
|
animation-duration: 13s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes move-forever {
|
@keyframes move-forever {
|
||||||
0% {
|
0% {
|
||||||
transform: translate3d(-90px, 0, 0);
|
transform: translate3d(-90px, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: translate3d(85px, 0, 0);
|
transform: translate3d(85px, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.waves {
|
.waves {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
min-height: 40px;
|
min-height: 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.words-wrapper {
|
.words-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.words-wrapper b {
|
.words-wrapper b {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -270,33 +313,40 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.words-wrapper b.is-visible {
|
.words-wrapper b.is-visible {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline.zoom .words-wrapper {
|
.headline.zoom .words-wrapper {
|
||||||
-webkit-perspective: 300px;
|
-webkit-perspective: 300px;
|
||||||
-moz-perspective: 300px;
|
-moz-perspective: 300px;
|
||||||
perspective: 300px;
|
perspective: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline {
|
.headline {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline.zoom b {
|
.headline.zoom b {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline.zoom b.is-visible {
|
.headline.zoom b.is-visible {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
-webkit-animation: zoom-in 0.8s;
|
-webkit-animation: zoom-in 0.8s;
|
||||||
-moz-animation: zoom-in 0.8s;
|
-moz-animation: zoom-in 0.8s;
|
||||||
animation: cubic-bezier(0.215, 0.610, 0.355, 1.000) zoom-in 0.8s;
|
animation: cubic-bezier(0.215, 0.610, 0.355, 1.000) zoom-in 0.8s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headline.zoom b.is-hidden {
|
.headline.zoom b.is-hidden {
|
||||||
-webkit-animation: zoom-out 0.8s;
|
-webkit-animation: zoom-out 0.8s;
|
||||||
-moz-animation: zoom-out 0.8s;
|
-moz-animation: zoom-out 0.8s;
|
||||||
animation: cubic-bezier(0.215, 0.610, 0.355, 1.000) zoom-out 0.4s;
|
animation: cubic-bezier(0.215, 0.610, 0.355, 1.000) zoom-out 0.4s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes zoom-in {
|
@-webkit-keyframes zoom-in {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -308,16 +358,19 @@
|
|||||||
-webkit-transform: translateZ(0);
|
-webkit-transform: translateZ(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@-moz-keyframes zoom-in {
|
@-moz-keyframes zoom-in {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-moz-transform: translateZ(100px);
|
-moz-transform: translateZ(100px);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
-moz-transform: translateZ(0);
|
-moz-transform: translateZ(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes zoom-in {
|
@keyframes zoom-in {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -327,6 +380,7 @@
|
|||||||
-o-transform: translateZ(100px);
|
-o-transform: translateZ(100px);
|
||||||
transform: translateZ(100px);
|
transform: translateZ(100px);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
-webkit-transform: translateZ(0);
|
-webkit-transform: translateZ(0);
|
||||||
@@ -336,26 +390,31 @@
|
|||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes zoom-out {
|
@-webkit-keyframes zoom-out {
|
||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
-webkit-transform: translateZ(0);
|
-webkit-transform: translateZ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transform: translateZ(-100px);
|
-webkit-transform: translateZ(-100px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@-moz-keyframes zoom-out {
|
@-moz-keyframes zoom-out {
|
||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
-moz-transform: translateZ(0);
|
-moz-transform: translateZ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-moz-transform: translateZ(-100px);
|
-moz-transform: translateZ(-100px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes zoom-out {
|
@keyframes zoom-out {
|
||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -365,6 +424,7 @@
|
|||||||
-o-transform: translateZ(0);
|
-o-transform: translateZ(0);
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transform: translateZ(-100px);
|
-webkit-transform: translateZ(-100px);
|
||||||
@@ -374,21 +434,28 @@
|
|||||||
transform: translateZ(-100px);
|
transform: translateZ(-100px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ant-menu-item .anticon {
|
|
||||||
margin-right: 4px;
|
.setting-section {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 24px;
|
||||||
}
|
}
|
||||||
.ant-menu-inline .ant-menu-item {
|
|
||||||
padding: 0 16px !important;
|
.setting-section > .ant-btn {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-layout-content class="under" style="min-height: 0;">
|
<a-layout-content class="under" :style="{ minHeight: '0' }">
|
||||||
<div class="waves-header">
|
<div class="waves-header">
|
||||||
<div class="waves-inner-header"></div>
|
<div class="waves-inner-header"></div>
|
||||||
<svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
<svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
|
viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto">
|
||||||
<defs>
|
<defs>
|
||||||
<path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />
|
<path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z" />
|
||||||
</defs>
|
</defs>
|
||||||
@@ -400,70 +467,69 @@
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<a-row type="flex" justify="center" align="middle" style="height: 100%; overflow: auto; overflow-x: hidden;">
|
<div class="setting-section">
|
||||||
<a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" style="margin: 3rem 0;">
|
<a-popover :overlay-class-name="themeSwitcher.currentTheme" title='{{ i18n "menu.settings" }}' placement="bottomRight" trigger="click">
|
||||||
|
<template slot="content">
|
||||||
|
<a-space direction="vertical" :size="10">
|
||||||
|
<a-theme-switch-login></a-theme-switch-login>
|
||||||
|
<span>{{ i18n "pages.settings.language" }}</span>
|
||||||
|
<a-select ref="selectLang" :style="{ width: '100%' }" v-model="lang" @change="LanguageManager.setLanguage(lang)" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option :value="l.value" label="English" v-for="l in LanguageManager.supportedLanguages">
|
||||||
|
<span role="img" aria-label="l.name" v-text="l.icon"></span>
|
||||||
|
<span v-text="l.name"></span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<a-button shape="circle" icon="setting"></a-button>
|
||||||
|
</a-popover>
|
||||||
|
</div>
|
||||||
|
<a-row type="flex" justify="center" align="middle" :style="{ height: '100%', overflow: 'auto', overflowX: 'hidden' }">
|
||||||
|
<a-col :xs="22" :sm="20" :md="14" :lg="10" :xl="8" :xxl="6" id="login" :style="{ margin: '3rem 0' }">
|
||||||
<a-row type="flex" justify="center">
|
<a-row type="flex" justify="center">
|
||||||
<a-col style="width: 100%;">
|
<a-col :style="{ width: '100%' }">
|
||||||
<h1 class="title headline zoom">
|
<h2 class="title headline zoom">
|
||||||
<span class="words-wrapper">
|
<span class="words-wrapper">
|
||||||
<b class="is-visible">{{ i18n "pages.login.hello" }}</b>
|
<b class="is-visible">{{ i18n "pages.login.hello" }}</b>
|
||||||
<b>{{ i18n "pages.login.title" }}</b>
|
<b>{{ i18n "pages.login.title" }}</b>
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h2>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
<a-row type="flex" justify="center">
|
<a-row type="flex" justify="center">
|
||||||
<a-col span="24">
|
<a-col span="24">
|
||||||
<a-form>
|
<a-form>
|
||||||
<a-form-item>
|
<a-space direction="vertical" size="middle">
|
||||||
<a-input autocomplete="username" name="username" v-model.trim="user.username" placeholder='{{ i18n "username" }}'
|
<a-form-item>
|
||||||
@keydown.enter.native="login" autofocus>
|
<a-input autocomplete="username" name="username" v-model.trim="user.username"
|
||||||
<a-icon slot="prefix" type="user" style="font-size: 16px;"></a-icon>
|
placeholder='{{ i18n "username" }}' @keydown.enter.native="login" autofocus>
|
||||||
</a-input>
|
<a-icon slot="prefix" type="user" :style="{ fontSize: '16px' }"></a-icon>
|
||||||
</a-form-item>
|
</a-input>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-password-input autocomplete="password" name="password" icon="lock" v-model.trim="user.password"
|
<a-form-item>
|
||||||
placeholder='{{ i18n "password" }}'
|
<a-input-password autocomplete="password" name="password" icon="lock" v-model.trim="user.password"
|
||||||
@keydown.enter.native="login">
|
placeholder='{{ i18n "password" }}' @keydown.enter.native="login">
|
||||||
</a-password-input>
|
<a-icon slot="prefix" type="lock" :style="{ fontSize: '16px' }"></a-icon>
|
||||||
</a-form-item>
|
</a-input-password>
|
||||||
<a-form-item v-if="secretEnable">
|
</a-form-item>
|
||||||
<a-password-input autocomplete="secret" name="secret" icon="key" v-model.trim="user.loginSecret"
|
<a-form-item v-if="secretEnable">
|
||||||
placeholder='{{ i18n "secretToken" }}'
|
<a-input-password autocomplete="secret" name="secret" icon="lock" v-model.trim="user.loginSecret"
|
||||||
@keydown.enter.native="login">
|
placeholder='{{ i18n "secretToken" }}' @keydown.enter.native="login">
|
||||||
</a-password-input>
|
<a-icon slot="prefix" type="key" :style="{ fontSize: '16px' }"></a-icon>
|
||||||
</a-form-item>
|
</a-input-password>
|
||||||
<a-form-item>
|
</a-form-item>
|
||||||
<a-row justify="center" class="centered">
|
<a-form-item>
|
||||||
<div style="height: 50px;" class="wave-btn-bg wave-btn-bg-cl"
|
<a-row justify="center" class="centered">
|
||||||
:style="loading ? { width: '52px' } : { display: 'inline-block' }">
|
<div :style="{ height: '50px', marginTop: '16px' }" class="wave-btn-bg wave-btn-bg-cl"
|
||||||
<a-button class="ant-btn-primary-login" type="primary"
|
:style="loading ? { width: '52px' } : { display: 'inline-block' }">
|
||||||
:loading="loading" @click="login"
|
<a-button class="ant-btn-primary-login" type="primary" :loading="loading" @click="login"
|
||||||
:icon="loading ? 'poweroff' : undefined">
|
:icon="loading ? 'poweroff' : undefined">
|
||||||
[[ loading ? '' : '{{ i18n "login" }}' ]]
|
[[ loading ? '' : '{{ i18n "login" }}' ]]
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
</a-space>
|
||||||
<a-row justify="center" class="centered">
|
|
||||||
<a-col :span="24">
|
|
||||||
<a-select ref="selectLang" v-model="lang"
|
|
||||||
@change="LanguageManager.setLanguage(lang)" style="width: 200px;"
|
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option :value="l.value" label="English" v-for="l in LanguageManager.supportedLanguages">
|
|
||||||
<span role="img" aria-label="l.name" v-text="l.icon"></span>
|
|
||||||
<span v-text="l.name"></span>
|
|
||||||
</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item>
|
|
||||||
<a-row justify="center" class="centered">
|
|
||||||
<a-theme-switch-login></a-theme-switch-login>
|
|
||||||
</a-row>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
@@ -472,86 +538,84 @@
|
|||||||
</a-layout-content>
|
</a-layout-content>
|
||||||
</transition>
|
</transition>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "js" .}}
|
||||||
{{template "component/aThemeSwitch" .}}
|
{{template "component/aThemeSwitch" .}}
|
||||||
{{template "component/aPasswordInput" .}}
|
<script>
|
||||||
<script>
|
const app = new Vue({
|
||||||
class User {
|
delimiters: ['[[', ']]'],
|
||||||
constructor() {
|
el: '#app',
|
||||||
this.username = "";
|
data: {
|
||||||
this.password = "";
|
themeSwitcher,
|
||||||
}
|
loading: false,
|
||||||
}
|
user: {
|
||||||
const app = new Vue({
|
username: "",
|
||||||
delimiters: ['[[', ']]'],
|
password: "",
|
||||||
el: '#app',
|
loginSecret: ""
|
||||||
data: {
|
},
|
||||||
themeSwitcher,
|
secretEnable: false,
|
||||||
loading: false,
|
lang: ""
|
||||||
user: new User(),
|
|
||||||
secretEnable: false,
|
|
||||||
lang: ""
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
this.lang = LanguageManager.getLanguage();
|
|
||||||
this.secretEnable = await this.getSecretStatus();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async login() {
|
|
||||||
this.loading = true;
|
|
||||||
const msg = await HttpUtil.post('/login', this.user);
|
|
||||||
this.loading = false;
|
|
||||||
if (msg.success) {
|
|
||||||
location.href = basePath + 'panel/';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async getSecretStatus() {
|
async mounted() {
|
||||||
this.loading = true;
|
this.lang = LanguageManager.getLanguage();
|
||||||
const msg = await HttpUtil.post('/getSecretStatus');
|
this.secretEnable = await this.getSecretStatus();
|
||||||
this.loading = false;
|
|
||||||
if (msg.success) {
|
|
||||||
this.secretEnable = msg.obj;
|
|
||||||
return msg.obj;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
methods: {
|
||||||
});
|
async login() {
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
this.loading = true;
|
||||||
var animationDelay = 2000;
|
const msg = await HttpUtil.post('/login', this.user);
|
||||||
initHeadline();
|
this.loading = false;
|
||||||
|
if (msg.success) {
|
||||||
|
location.href = basePath + 'panel/';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getSecretStatus() {
|
||||||
|
this.loading = true;
|
||||||
|
const msg = await HttpUtil.post('/getSecretStatus');
|
||||||
|
this.loading = false;
|
||||||
|
if (msg.success) {
|
||||||
|
this.secretEnable = msg.obj;
|
||||||
|
return msg.obj;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
function initHeadline() {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
animateHeadline(document.querySelectorAll('.headline'));
|
var animationDelay = 2000;
|
||||||
}
|
initHeadline();
|
||||||
|
|
||||||
function animateHeadline(headlines) {
|
function initHeadline() {
|
||||||
var duration = animationDelay;
|
animateHeadline(document.querySelectorAll('.headline'));
|
||||||
headlines.forEach(function(headline) {
|
}
|
||||||
setTimeout(function() {
|
|
||||||
hideWord(headline.querySelector('.is-visible'));
|
|
||||||
}, duration);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideWord(word) {
|
function animateHeadline(headlines) {
|
||||||
var nextWord = takeNext(word);
|
var duration = animationDelay;
|
||||||
switchWord(word, nextWord);
|
headlines.forEach(function (headline) {
|
||||||
setTimeout(function() {
|
setTimeout(function () {
|
||||||
hideWord(nextWord);
|
hideWord(headline.querySelector('.is-visible'));
|
||||||
}, animationDelay);
|
}, duration);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function takeNext(word) {
|
function hideWord(word) {
|
||||||
return (word.nextElementSibling) ? word.nextElementSibling : word.parentElement.firstElementChild;
|
var nextWord = takeNext(word);
|
||||||
}
|
switchWord(word, nextWord);
|
||||||
|
setTimeout(function () {
|
||||||
|
hideWord(nextWord);
|
||||||
|
}, animationDelay);
|
||||||
|
}
|
||||||
|
|
||||||
function switchWord(oldWord, newWord) {
|
function takeNext(word) {
|
||||||
oldWord.classList.remove('is-visible');
|
return word.nextElementSibling ? word.nextElementSibling : word.parentElement.firstElementChild;
|
||||||
oldWord.classList.add('is-hidden');
|
}
|
||||||
newWord.classList.remove('is-hidden');
|
|
||||||
newWord.classList.add('is-visible');
|
function switchWord(oldWord, newWord) {
|
||||||
}
|
oldWord.classList.remove('is-visible');
|
||||||
});
|
oldWord.classList.add('is-hidden');
|
||||||
</script>
|
newWord.classList.remove('is-hidden');
|
||||||
|
newWord.classList.add('is-visible');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "clientsBulkModal"}}
|
{{define "modals/clientsBulkModal"}}
|
||||||
<a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title"
|
<a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title"
|
||||||
@ok="clientsBulkModal.ok" :confirm-loading="clientsBulkModal.confirmLoading" :closable="true" :mask-closable="false"
|
@ok="clientsBulkModal.ok" :confirm-loading="clientsBulkModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||||
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
<a-icon type="question-circle"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</template>
|
</template>
|
||||||
<a-input-number style="width: 50%" v-model.number="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>
|
||||||
<a-form-item v-if="app.ipLimitEnable">
|
<a-form-item v-if="app.ipLimitEnable">
|
||||||
<template slot="label">
|
<template slot="label">
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
{{define "clientsModal"}}
|
{{define "modals/clientsModal"}}
|
||||||
<a-modal id="client-modal" v-model="clientModal.visible" :title="clientModal.title" @ok="clientModal.ok"
|
<a-modal id="client-modal" v-model="clientModal.visible" :title="clientModal.title" @ok="clientModal.ok"
|
||||||
:confirm-loading="clientModal.confirmLoading" :closable="true" :mask-closable="false"
|
:confirm-loading="clientModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||||
:class="themeSwitcher.currentTheme"
|
:class="themeSwitcher.currentTheme"
|
||||||
:ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
|
:ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
|
||||||
<template v-if="isEdit">
|
<template v-if="isEdit">
|
||||||
<a-tag v-if="isExpiry || isTrafficExhausted" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
|
<a-tag v-if="isExpiry || isTrafficExhausted" color="red" :style="{ marginBottom: '10px', display: 'block', textAlign: 'center' }">Account is (Expired|Traffic Ended) And Disabled</a-tag>
|
||||||
</template>
|
</template>
|
||||||
{{template "form/client"}}
|
{{template "form/client"}}
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "dnsModal"}}
|
{{define "modals/dnsModal"}}
|
||||||
<a-modal id="dns-modal" v-model="dnsModal.visible" :title="dnsModal.title" @ok="dnsModal.ok" :closable="true"
|
<a-modal id="dns-modal" v-model="dnsModal.visible" :title="dnsModal.title" @ok="dnsModal.ok" :closable="true"
|
||||||
:mask-closable="false" :ok-text="dnsModal.okText" cancel-text='{{ i18n "close" }}'
|
:mask-closable="false" :ok-text="dnsModal.okText" cancel-text='{{ i18n "close" }}'
|
||||||
:class="themeSwitcher.currentTheme">
|
:class="themeSwitcher.currentTheme">
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='{{ i18n "pages.xray.dns.strategy" }}' v-if="isAdvanced">
|
<a-form-item label='{{ i18n "pages.xray.dns.strategy" }}' v-if="isAdvanced">
|
||||||
<a-select v-model="dnsModal.dnsServer.queryStrategy" style="width: 100%"
|
<a-select v-model="dnsModal.dnsServer.queryStrategy" :style="{ width: '100%' }"
|
||||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
<a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']"> [[ l ]] </a-select-option>
|
<a-select-option :value="l" :label="l" v-for="l in ['UseIP', 'UseIPv4', 'UseIPv6']"> [[ l ]] </a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "fakednsModal"}}
|
{{define "modals/fakednsModal"}}
|
||||||
<a-modal id="fakedns-modal" v-model="fakednsModal.visible" :title="fakednsModal.title" @ok="fakednsModal.ok"
|
<a-modal id="fakedns-modal" v-model="fakednsModal.visible" :title="fakednsModal.title" @ok="fakednsModal.ok"
|
||||||
:closable="true" :mask-closable="false"
|
:closable="true" :mask-closable="false"
|
||||||
:ok-text="fakednsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
:ok-text="fakednsModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "inboundInfoModal"}}
|
{{define "modals/inboundInfoModal"}}
|
||||||
<a-modal id="inbound-info-modal" v-model="infoModal.visible" title='{{ i18n "pages.inbounds.details"}}' :closable="true" :mask-closable="true" :footer="null" width="600px" :class="themeSwitcher.currentTheme">
|
<a-modal id="inbound-info-modal" v-model="infoModal.visible" title='{{ i18n "pages.inbounds.details"}}' :closable="true" :mask-closable="true" :footer="null" width="600px" :class="themeSwitcher.currentTheme">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col :xs="24" :md="12">
|
<a-col :xs="24" :md="12">
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
<a-tag v-else color="orange">{{ i18n "none" }}</a-tag>
|
<a-tag v-else color="orange">{{ i18n "none" }}</a-tag>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<table v-if="dbInbound.isSS" style="margin-bottom: 10px; width: 100%;">
|
<table v-if="dbInbound.isSS" :style="{ marginBottom: '10px', width: '100%' }">
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "encryption" }}</td>
|
<td>{{ i18n "encryption" }}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<template v-if="infoModal.clientSettings">
|
<template v-if="infoModal.clientSettings">
|
||||||
<a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
|
<a-divider>{{ i18n "pages.inbounds.client" }}</a-divider>
|
||||||
<table style="margin-bottom: 10px;">
|
<table :style="{ marginBottom: '10px' }">
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ i18n "pages.inbounds.email" }}</td>
|
<td>{{ i18n "pages.inbounds.email" }}</td>
|
||||||
<td v-if="infoModal.clientSettings.email">
|
<td v-if="infoModal.clientSettings.email">
|
||||||
@@ -203,7 +203,7 @@
|
|||||||
<td>{{ i18n "pages.inbounds.IPLimitlog" }}</td>
|
<td>{{ i18n "pages.inbounds.IPLimitlog" }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a-tag>[[ infoModal.clientIps ]]</a-tag>
|
<a-tag>[[ infoModal.clientIps ]]</a-tag>
|
||||||
<a-icon type="sync" :spin="refreshing" @click="refreshIPs" style="margin: 0 5px;"></a-icon>
|
<a-icon type="sync" :spin="refreshing" @click="refreshIPs" :style="{ margin: '0 5px' }"></a-icon>
|
||||||
<a-tooltip :title="[[ dbInbound.address ]]">
|
<a-tooltip :title="[[ dbInbound.address ]]">
|
||||||
<template slot="title">
|
<template slot="title">
|
||||||
<span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
|
<span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<table style="display: inline-table; margin-block: 10px; width: 100%; text-align: center;">
|
<table :style="{ display: 'inline-table', marginBlock: '10px', width: '100%', textAlign: 'center' }">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{ i18n "remained" }}</th>
|
<th>{{ i18n "remained" }}</th>
|
||||||
<th>{{ i18n "pages.inbounds.totalFlow" }}</th>
|
<th>{{ i18n "pages.inbounds.totalFlow" }}</th>
|
||||||
@@ -290,7 +290,7 @@
|
|||||||
<tr-info-title class="tr-info-title">
|
<tr-info-title class="tr-info-title">
|
||||||
<a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
|
<a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button style="min-width: 24px;" size="small" icon="snippets" @click="copy(link.link)"></a-button>
|
<a-button :style="{ minWidth: '24px' }" size="small" icon="snippets" @click="copy(link.link)"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
<code>[[ link.link ]]</code>
|
<code>[[ link.link ]]</code>
|
||||||
@@ -304,7 +304,7 @@
|
|||||||
<tr-info-title class="tr-info-title">
|
<tr-info-title class="tr-info-title">
|
||||||
<a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
|
<a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag>
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button style="min-width: 24px;" size="small" icon="snippets" @click="copy(link.link)"></a-button>
|
<a-button :style="{ minWidth: '24px' }" size="small" icon="snippets" @click="copy(link.link)"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
<code>[[ link.link ]]</code>
|
<code>[[ link.link ]]</code>
|
||||||
@@ -431,10 +431,10 @@
|
|||||||
<tr-info-title class="tr-info-title">
|
<tr-info-title class="tr-info-title">
|
||||||
<a-tag color="blue">Config</a-tag>
|
<a-tag color="blue">Config</a-tag>
|
||||||
<a-tooltip title='{{ i18n "copy" }}'>
|
<a-tooltip title='{{ i18n "copy" }}'>
|
||||||
<a-button style="min-width: 24px;" size="small" icon="snippets" @click="copy(infoModal.links[index])"></a-button>
|
<a-button :style="{ minWidth: '24px' }" size="small" icon="snippets" @click="copy(infoModal.links[index])"></a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</tr-info-title>
|
</tr-info-title>
|
||||||
<div v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)" style="border-radius: 1rem; padding: 0.5rem;" class="client-table-odd-row">
|
<div v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)" :style="{ borderRadius: '1rem', padding: '0.5rem' }" class="client-table-odd-row">
|
||||||
</div>
|
</div>
|
||||||
</tr-info-row>
|
</tr-info-row>
|
||||||
</td>
|
</td>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "inboundModal"}}
|
{{define "modals/inboundModal"}}
|
||||||
<a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title"
|
<a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title"
|
||||||
:dialog-style="{ top: '20px' }" @ok="inModal.ok"
|
:dialog-style="{ top: '20px' }" @ok="inModal.ok"
|
||||||
:confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
|
:confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "promptModal"}}
|
{{define "modals/promptModal"}}
|
||||||
<a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title"
|
<a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title"
|
||||||
:closable="true" @ok="promptModal.ok" :mask-closable="false"
|
:closable="true" @ok="promptModal.ok" :mask-closable="false"
|
||||||
:confirm-loading="promptModal.confirmLoading"
|
:confirm-loading="promptModal.confirmLoading"
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{{define "qrcodeModal"}}
|
{{define "modals/qrcodeModal"}}
|
||||||
<a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title"
|
<a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title"
|
||||||
:dialog-style="isMobileQr ? { top: '18px' } : {}"
|
:dialog-style="isMobile ? { top: '18px' } : {}"
|
||||||
:closable="true"
|
:closable="true"
|
||||||
:class="themeSwitcher.currentTheme"
|
:class="themeSwitcher.currentTheme"
|
||||||
:footer="null" width="fit-content">
|
:footer="null" width="fit-content">
|
||||||
@@ -35,7 +35,6 @@
|
|||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const isMobileQr = window.innerWidth <= 768;
|
|
||||||
const qrModal = {
|
const qrModal = {
|
||||||
title: '',
|
title: '',
|
||||||
dbInbound: new DBInbound(),
|
dbInbound: new DBInbound(),
|
||||||
@@ -74,6 +73,7 @@
|
|||||||
const qrModalApp = new Vue({
|
const qrModalApp = new Vue({
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#qrcode-modal',
|
el: '#qrcode-modal',
|
||||||
|
mixins: [MediaQueryMixin],
|
||||||
data: {
|
data: {
|
||||||
qrModal: qrModal,
|
qrModal: qrModal,
|
||||||
},
|
},
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
{{define "textModal"}}
|
{{define "modals/textModal"}}
|
||||||
<a-modal id="text-modal" v-model="txtModal.visible" :title="txtModal.title"
|
<a-modal id="text-modal" v-model="txtModal.visible" :title="txtModal.title" :closable="true"
|
||||||
:closable="true"
|
:class="themeSwitcher.currentTheme">
|
||||||
:class="themeSwitcher.currentTheme">
|
<a-input :style="{ overflowY: 'auto' }" type="textarea" v-model="txtModal.content"
|
||||||
|
:autosize="{ minRows: 10, maxRows: 20}"></a-input>
|
||||||
<template slot="footer">
|
<template slot="footer">
|
||||||
<a-button v-if="!ObjectUtil.isEmpty(txtModal.fileName)" icon="download"
|
<a-button v-if="!ObjectUtil.isEmpty(txtModal.fileName)" icon="download"
|
||||||
:href="'data:application/text;charset=utf-8,' + encodeURIComponent(txtModal.content)"
|
@click="FileManager.downloadTextFile(txtModal.content, txtModal.fileName)">
|
||||||
:download="txtModal.fileName">[[ txtModal.fileName ]]
|
<span>[[ txtModal.fileName ]]</span>
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" icon="copy" @click="txtModal.copy(txtModal.content)">
|
||||||
|
<span>{{ i18n "copy" }}</span>
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="primary" @click="txtModal.copy(txtModal.content)">{{ i18n "copy" }}</a-button>
|
|
||||||
</template>
|
</template>
|
||||||
<a-input style="overflow-y: auto;" type="textarea" v-model="txtModal.content"
|
|
||||||
:autosize="{ minRows: 10, maxRows: 20}"></a-input>
|
|
||||||
</a-modal>
|
</a-modal>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
const txtModal = {
|
const txtModal = {
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
@@ -47,6 +47,5 @@
|
|||||||
txtModal: txtModal,
|
txtModal: txtModal,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "warpModal"}}
|
{{define "modals/warpModal"}}
|
||||||
<a-modal id="warp-modal" v-model="warpModal.visible" title="Cloudflare WARP"
|
<a-modal id="warp-modal" v-model="warpModal.visible" title="Cloudflare WARP"
|
||||||
:confirm-loading="warpModal.confirmLoading" :closable="true" :mask-closable="true"
|
:confirm-loading="warpModal.confirmLoading" :closable="true" :mask-closable="true"
|
||||||
:footer="null" :class="themeSwitcher.currentTheme">
|
:footer="null" :class="themeSwitcher.currentTheme">
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<a-button icon="api" @click="register" :loading="warpModal.confirmLoading">{{ i18n "pages.inbounds.create" }}</a-button>
|
<a-button icon="api" @click="register" :loading="warpModal.confirmLoading">{{ i18n "pages.inbounds.create" }}</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<table style="margin: 5px 0; width: 100%;">
|
<table :style="{ margin: '5px 0', width: '100%' }">
|
||||||
<tr class="client-table-odd-row">
|
<tr class="client-table-odd-row">
|
||||||
<td>Access Token</td>
|
<td>Access Token</td>
|
||||||
<td>[[ warpModal.warpData.access_token ]]</td>
|
<td>[[ warpModal.warpData.access_token ]]</td>
|
||||||
@@ -25,8 +25,8 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<a-button @click="delConfig" :loading="warpModal.confirmLoading" type="danger">{{ i18n "delete" }}</a-button>
|
<a-button @click="delConfig" :loading="warpModal.confirmLoading" type="danger">{{ i18n "delete" }}</a-button>
|
||||||
<a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.settings" }}</a-divider>
|
<a-divider :style="{ margin: '0' }">{{ i18n "pages.xray.outbound.settings" }}</a-divider>
|
||||||
<a-collapse style="margin: 10px 0;">
|
<a-collapse :style="{ margin: '10px 0' }">
|
||||||
<a-collapse-panel header='WARP/WARP+ License Key'>
|
<a-collapse-panel header='WARP/WARP+ License Key'>
|
||||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label="Key">
|
<a-form-item label="Key">
|
||||||
@@ -37,11 +37,11 @@
|
|||||||
</a-form>
|
</a-form>
|
||||||
</a-collapse-panel>
|
</a-collapse-panel>
|
||||||
</a-collapse>
|
</a-collapse>
|
||||||
<a-divider style="margin: 0;">{{ i18n "pages.xray.outbound.accountInfo" }}</a-divider>
|
<a-divider :style="{ margin: '0' }">{{ i18n "pages.xray.outbound.accountInfo" }}</a-divider>
|
||||||
<a-button icon="sync" @click="getConfig" style="margin-top: 5px; margin-bottom: 10px;"
|
<a-button icon="sync" @click="getConfig" :style="{ marginTop: '5px', marginBottom: '10px' }"
|
||||||
:loading="warpModal.confirmLoading" type="primary">{{ i18n "info" }}</a-button>
|
:loading="warpModal.confirmLoading" type="primary">{{ i18n "info" }}</a-button>
|
||||||
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig)">
|
<template v-if="!ObjectUtil.isEmpty(warpModal.warpConfig)">
|
||||||
<table style="width: 100%">
|
<table :style="{ width: '100%' }">
|
||||||
<tr class="client-table-odd-row">
|
<tr class="client-table-odd-row">
|
||||||
<td>Device Name</td>
|
<td>Device Name</td>
|
||||||
<td>[[ warpModal.warpConfig.name ]]</td>
|
<td>[[ warpModal.warpConfig.name ]]</td>
|
||||||
@@ -77,14 +77,14 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</table>
|
</table>
|
||||||
<a-divider style="margin: 10px 0;">{{ i18n "pages.xray.outbound.outboundStatus" }}</a-divider>
|
<a-divider :style="{ margin: '10px 0' }">{{ i18n "pages.xray.outbound.outboundStatus" }}</a-divider>
|
||||||
<a-form :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<template v-if="warpOutboundIndex>=0">
|
<template v-if="warpOutboundIndex>=0">
|
||||||
<a-tag color="green" style="line-height: 31px;">{{ i18n "enabled" }}</a-tag>
|
<a-tag color="green" :style="{ lineHeight: '31px' }">{{ i18n "enabled" }}</a-tag>
|
||||||
<a-button @click="resetOutbound" :loading="warpModal.confirmLoading" type="danger">{{ i18n "reset" }}</a-button>
|
<a-button @click="resetOutbound" :loading="warpModal.confirmLoading" type="danger">{{ i18n "reset" }}</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-tag color="orange" style="line-height: 31px;">{{ i18n "disabled" }}</a-tag>
|
<a-tag color="orange" :style="{ lineHeight: '31px' }">{{ i18n "disabled" }}</a-tag>
|
||||||
<a-button @click="addOutbound" :loading="warpModal.confirmLoading" type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
<a-button @click="addOutbound" :loading="warpModal.confirmLoading" type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||||
</template>
|
</template>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "balancerModal"}}
|
{{define "modals/balancerModal"}}
|
||||||
<a-modal
|
<a-modal
|
||||||
id="balancer-modal"
|
id="balancer-modal"
|
||||||
v-model="balancerModal.visible"
|
v-model="balancerModal.visible"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{{define "outModal"}}
|
{{define "modals/outModal"}}
|
||||||
<a-modal id="out-modal" v-model="outModal.visible" :title="outModal.title" @ok="outModal.ok"
|
<a-modal id="out-modal" v-model="outModal.visible" :title="outModal.title" @ok="outModal.ok"
|
||||||
:confirm-loading="outModal.confirmLoading" :closable="true" :mask-closable="false"
|
:confirm-loading="outModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||||
:ok-button-props="{ props: { disabled: !outModal.isValid } }" style="overflow: hidden;"
|
:ok-button-props="{ props: { disabled: !outModal.isValid } }" :style="{ overflow: 'hidden' }"
|
||||||
:ok-text="outModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
:ok-text="outModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||||
{{template "form/outbound"}}
|
{{template "form/outbound"}}
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "reverseModal"}}
|
{{define "modals/reverseModal"}}
|
||||||
<a-modal id="reverse-modal" v-model="reverseModal.visible" :title="reverseModal.title" @ok="reverseModal.ok"
|
<a-modal id="reverse-modal" v-model="reverseModal.visible" :title="reverseModal.title" @ok="reverseModal.ok"
|
||||||
:confirm-loading="reverseModal.confirmLoading" :closable="true" :mask-closable="false"
|
:confirm-loading="reverseModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||||
:ok-text="reverseModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
:ok-text="reverseModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{{define "ruleModal"}}
|
{{define "modals/ruleModal"}}
|
||||||
<a-modal id="rule-modal" v-model="ruleModal.visible" :title="ruleModal.title" @ok="ruleModal.ok" :confirm-loading="ruleModal.confirmLoading" :closable="true" :mask-closable="false" :ok-text="ruleModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
<a-modal id="rule-modal" v-model="ruleModal.visible" :title="ruleModal.title" @ok="ruleModal.ok" :confirm-loading="ruleModal.confirmLoading" :closable="true" :mask-closable="false" :ok-text="ruleModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-form-item label='Domain Matcher'>
|
<a-form-item label='Domain Matcher'>
|
||||||
@@ -37,14 +37,14 @@
|
|||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Attributes'>
|
<a-form-item label='Attributes'>
|
||||||
<a-button icon="plus" size="small" style="margin-left: 10px" @click="ruleModal.rule.attrs.push(['', ''])"></a-button>
|
<a-button icon="plus" size="small" :style="{ marginLeft: '10px' }" @click="ruleModal.rule.attrs.push(['', ''])"></a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :wrapper-col="{span: 24}">
|
<a-form-item :wrapper-col="{span: 24}">
|
||||||
<a-input-group compact v-for="(attr,index) in ruleModal.rule.attrs">
|
<a-input-group compact v-for="(attr,index) in ruleModal.rule.attrs">
|
||||||
<a-input style="width: 50%" v-model="attr[0]" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
<a-input :style="{ width: '50%' }" v-model="attr[0]" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-input style="width: 50%" v-model="attr[1]" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
<a-input :style="{ width: '50%' }" v-model="attr[1]" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||||
<a-button icon="minus" slot="addonAfter" size="small" @click="ruleModal.rule.attrs.splice(index,1)"></a-button>
|
<a-button icon="minus" slot="addonAfter" size="small" @click="ruleModal.rule.attrs.splice(index,1)"></a-button>
|
||||||
</a-input>
|
</a-input>
|
||||||
</a-input-group>
|
</a-input-group>
|
||||||
545
web/html/settings.html
Normal file
545
web/html/settings.html
Normal file
@@ -0,0 +1,545 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
{{template "head" .}}
|
||||||
|
<style>
|
||||||
|
@media (min-width: 769px) {
|
||||||
|
.ant-layout-content {
|
||||||
|
margin: 24px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.ant-tabs-nav .ant-tabs-tab {
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-tabs-bar {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.ant-list-item {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.alert-msg {
|
||||||
|
color: rgb(194, 117, 18);
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: .5rem 1rem;
|
||||||
|
text-align: center;
|
||||||
|
background: rgb(255 145 0 / 15%);
|
||||||
|
margin: 1.5rem 2.5rem 0rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
transition: all 0.5s;
|
||||||
|
animation: signal 3s cubic-bezier(0.18, 0.89, 0.32, 1.28) infinite;
|
||||||
|
}
|
||||||
|
.alert-msg:hover {
|
||||||
|
cursor: default;
|
||||||
|
transition-duration: .3s;
|
||||||
|
animation: signal 0.9s ease infinite;
|
||||||
|
}
|
||||||
|
@keyframes signal {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0 rgba(194, 118, 18, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 0 6px rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 6px rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alert-msg>i {
|
||||||
|
color: inherit;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.dark .ant-input-password-icon {
|
||||||
|
color: var(--dark-color-text-primary);
|
||||||
|
}
|
||||||
|
.ant-collapse-content-box .ant-alert {
|
||||||
|
margin-block-end: 12px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
|
<a-sidebar></a-sidebar>
|
||||||
|
<a-layout id="content-layout">
|
||||||
|
<a-layout-content>
|
||||||
|
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
||||||
|
<transition name="list" appear>
|
||||||
|
<a-alert type="error" v-if="confAlerts.length>0" :style="{ marginBottom: '10px' }"
|
||||||
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
|
color="red"
|
||||||
|
show-icon closable>
|
||||||
|
<template slot="description">
|
||||||
|
<b>{{ i18n "secAlertConf" }}</b>
|
||||||
|
<ul><li v-for="a in confAlerts">[[ a ]]</li></ul>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</transition>
|
||||||
|
<a-space direction="vertical">
|
||||||
|
<a-card hoverable :style="{ marginBottom: '.5rem', overflowX: 'hidden' }">
|
||||||
|
<a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
|
||||||
|
<a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
|
||||||
|
<a-space direction="horizontal">
|
||||||
|
<a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n "pages.settings.save" }}</a-button>
|
||||||
|
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-col>
|
||||||
|
<a-col :xs="24" :sm="14">
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top>
|
||||||
|
<a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }"
|
||||||
|
message='{{ i18n "pages.settings.infoDesc" }}'
|
||||||
|
show-icon>
|
||||||
|
</a-alert>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
<a-tabs default-active-key="1">
|
||||||
|
<a-tab-pane key="1" tab='{{ i18n "pages.settings.panelSettings" }}' :style="{ paddingTop: '20px' }">
|
||||||
|
{{ template "settings/panel/general" . }}
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="2" tab='{{ i18n "pages.settings.securitySettings" }}' :style="{ paddingTop: '20px' }">
|
||||||
|
{{ template "settings/panel/security" . }}
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="3" tab='{{ i18n "pages.settings.TGBotSettings" }}' :style="{ paddingTop: '20px' }">
|
||||||
|
{{ template "settings/panel/telegram" . }}
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="4" tab='{{ i18n "pages.settings.subSettings" }}' :style="{ paddingTop: '20px' }">
|
||||||
|
{{ template "settings/panel/subscription/general" . }}
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="5" tab='{{ i18n "pages.settings.subSettings" }} Json' v-if="allSetting.subEnable" :style="{ paddingTop: '20px' }">
|
||||||
|
{{ template "settings/panel/subscription/json" . }}
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</a-space>
|
||||||
|
</a-spin>
|
||||||
|
</a-layout-content>
|
||||||
|
</a-layout>
|
||||||
|
</a-layout>
|
||||||
|
{{template "js" .}}
|
||||||
|
<script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script>
|
||||||
|
{{template "component/aSidebar" .}}
|
||||||
|
{{template "component/aThemeSwitch" .}}
|
||||||
|
{{template "component/aSettingListItem" .}}
|
||||||
|
<script>
|
||||||
|
const app = new Vue({
|
||||||
|
delimiters: ['[[', ']]'],
|
||||||
|
el: '#app',
|
||||||
|
data: {
|
||||||
|
themeSwitcher,
|
||||||
|
spinning: false,
|
||||||
|
changeSecret: false,
|
||||||
|
oldAllSetting: new AllSetting(),
|
||||||
|
allSetting: new AllSetting(),
|
||||||
|
saveBtnDisable: true,
|
||||||
|
user: {},
|
||||||
|
lang: LanguageManager.getLanguage(),
|
||||||
|
remarkModels: { i: 'Inbound', e: 'Email', o: 'Other' },
|
||||||
|
remarkSeparators: [' ', '-', '_', '@', ':', '~', '|', ',', '.', '/'],
|
||||||
|
datepickerList: [{ name: 'Gregorian (Standard)', value: 'gregorian' }, { name: 'Jalalian (شمسی)', value: 'jalalian' }],
|
||||||
|
remarkSample: '',
|
||||||
|
defaultFragment: {
|
||||||
|
tag: "fragment",
|
||||||
|
protocol: "freedom",
|
||||||
|
settings: {
|
||||||
|
domainStrategy: "AsIs",
|
||||||
|
fragment: {
|
||||||
|
packets: "tlshello",
|
||||||
|
length: "100-200",
|
||||||
|
interval: "10-20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
streamSettings: {
|
||||||
|
sockopt: {
|
||||||
|
tcpKeepAliveIdle: 100,
|
||||||
|
tcpMptcp: true,
|
||||||
|
penetrate: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultNoises: {
|
||||||
|
tag: "noises",
|
||||||
|
protocol: "freedom",
|
||||||
|
settings: {
|
||||||
|
domainStrategy: "AsIs",
|
||||||
|
noises: [
|
||||||
|
{ type: "rand", packet: "10-20", delay: "10-16" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultMux: {
|
||||||
|
enabled: true,
|
||||||
|
concurrency: 8,
|
||||||
|
xudpConcurrency: 16,
|
||||||
|
xudpProxyUDP443: "reject"
|
||||||
|
},
|
||||||
|
defaultRules: [
|
||||||
|
{
|
||||||
|
type: "field",
|
||||||
|
outboundTag: "direct",
|
||||||
|
domain: [
|
||||||
|
"geosite:category-ir"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "field",
|
||||||
|
outboundTag: "direct",
|
||||||
|
ip: [
|
||||||
|
"geoip:private",
|
||||||
|
"geoip:ir"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
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' },
|
||||||
|
],
|
||||||
|
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;
|
||||||
|
return rm.length > 1 ? rm.substring(1).split('') : [];
|
||||||
|
},
|
||||||
|
set remarkModel(value) {
|
||||||
|
rs = this.allSetting.remarkModel[0];
|
||||||
|
this.allSetting.remarkModel = rs + value.join('');
|
||||||
|
this.changeRemarkSample();
|
||||||
|
},
|
||||||
|
get remarkSeparator() {
|
||||||
|
return this.allSetting.remarkModel.length > 1 ? this.allSetting.remarkModel.charAt(0) : '-';
|
||||||
|
},
|
||||||
|
set remarkSeparator(value) {
|
||||||
|
this.allSetting.remarkModel = value + this.allSetting.remarkModel.substring(1);
|
||||||
|
this.changeRemarkSample();
|
||||||
|
},
|
||||||
|
get datepicker() {
|
||||||
|
return this.allSetting.datepicker ? this.allSetting.datepicker : 'gregorian';
|
||||||
|
},
|
||||||
|
set datepicker(value) {
|
||||||
|
this.allSetting.datepicker = value;
|
||||||
|
},
|
||||||
|
changeRemarkSample() {
|
||||||
|
sample = []
|
||||||
|
this.remarkModel.forEach(r => sample.push(this.remarkModels[r]));
|
||||||
|
this.remarkSample = sample.length == 0 ? '' : sample.join(this.remarkSeparator);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loading(spinning = true) {
|
||||||
|
this.spinning = spinning;
|
||||||
|
},
|
||||||
|
async getAllSetting() {
|
||||||
|
this.loading(true);
|
||||||
|
const msg = await HttpUtil.post("/panel/setting/all");
|
||||||
|
this.loading(false);
|
||||||
|
if (msg.success) {
|
||||||
|
this.oldAllSetting = new AllSetting(msg.obj);
|
||||||
|
this.allSetting = new AllSetting(msg.obj);
|
||||||
|
app.changeRemarkSample();
|
||||||
|
this.saveBtnDisable = true;
|
||||||
|
}
|
||||||
|
await this.fetchUserSecret();
|
||||||
|
},
|
||||||
|
async updateAllSetting() {
|
||||||
|
this.loading(true);
|
||||||
|
const msg = await HttpUtil.post("/panel/setting/update", this.allSetting);
|
||||||
|
this.loading(false);
|
||||||
|
if (msg.success) {
|
||||||
|
await this.getAllSetting();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async updateUser() {
|
||||||
|
this.loading(true);
|
||||||
|
const msg = await HttpUtil.post("/panel/setting/updateUser", this.user);
|
||||||
|
this.loading(false);
|
||||||
|
if (msg.success) {
|
||||||
|
this.user = {};
|
||||||
|
window.location.replace(basePath + "logout");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async restartPanel() {
|
||||||
|
await new Promise(resolve => {
|
||||||
|
this.$confirm({
|
||||||
|
title: '{{ i18n "pages.settings.restartPanel" }}',
|
||||||
|
content: '{{ i18n "pages.settings.restartPanelDesc" }}',
|
||||||
|
class: themeSwitcher.currentTheme,
|
||||||
|
okText: '{{ i18n "sure" }}',
|
||||||
|
cancelText: '{{ i18n "cancel" }}',
|
||||||
|
onOk: () => resolve(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.loading(true);
|
||||||
|
const msg = await HttpUtil.post("/panel/setting/restartPanel");
|
||||||
|
this.loading(false);
|
||||||
|
if (msg.success) {
|
||||||
|
this.loading(true);
|
||||||
|
await PromiseUtil.sleep(5000);
|
||||||
|
var { webCertFile, webKeyFile, webDomain: host, webPort: port, webBasePath: base } = this.allSetting;
|
||||||
|
if (host == this.oldAllSetting.webDomain) host = null;
|
||||||
|
if (port == this.oldAllSetting.webPort) port = null;
|
||||||
|
const isTLS = webCertFile !== "" || webKeyFile !== "";
|
||||||
|
const url = URLBuilder.buildURL({ host, port, isTLS, base, path: "panel/settings" });
|
||||||
|
window.location.replace(url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async fetchUserSecret() {
|
||||||
|
this.loading(true);
|
||||||
|
const userMessage = await HttpUtil.post("/panel/setting/getUserSecret", this.user);
|
||||||
|
if (userMessage.success) {
|
||||||
|
this.user = userMessage.obj;
|
||||||
|
}
|
||||||
|
this.loading(false);
|
||||||
|
},
|
||||||
|
async updateSecret() {
|
||||||
|
this.loading(true);
|
||||||
|
const msg = await HttpUtil.post("/panel/setting/updateUserSecret", this.user);
|
||||||
|
if (msg && msg.obj) {
|
||||||
|
this.user = msg.obj;
|
||||||
|
}
|
||||||
|
this.loading(false);
|
||||||
|
await this.updateAllSetting();
|
||||||
|
},
|
||||||
|
generateRandomString(length) {
|
||||||
|
var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||||
|
let randomString = "";
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
randomString += chars[Math.floor(Math.random() * chars.length)];
|
||||||
|
}
|
||||||
|
return randomString;
|
||||||
|
},
|
||||||
|
async getNewSecret() {
|
||||||
|
if (!this.changeSecret) {
|
||||||
|
this.changeSecret = true;
|
||||||
|
this.user.loginSecret = '';
|
||||||
|
const newSecret = this.generateRandomString(64);
|
||||||
|
await PromiseUtil.sleep(1000);
|
||||||
|
this.user.loginSecret = newSecret;
|
||||||
|
this.changeSecret = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async toggleToken(value) {
|
||||||
|
if (value) {
|
||||||
|
await this.getNewSecret();
|
||||||
|
} else {
|
||||||
|
this.user.loginSecret = "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addNoise() {
|
||||||
|
const newNoise = { type: "rand", packet: "10-20", delay: "10-16" };
|
||||||
|
this.noisesArray = [...this.noisesArray, newNoise];
|
||||||
|
},
|
||||||
|
removeNoise(index) {
|
||||||
|
const newNoises = [...this.noisesArray];
|
||||||
|
newNoises.splice(index, 1);
|
||||||
|
this.noisesArray = newNoises;
|
||||||
|
},
|
||||||
|
updateNoiseType(index, value) {
|
||||||
|
const updatedNoises = [...this.noisesArray];
|
||||||
|
updatedNoises[index] = { ...updatedNoises[index], type: value };
|
||||||
|
this.noisesArray = updatedNoises;
|
||||||
|
},
|
||||||
|
updateNoisePacket(index, value) {
|
||||||
|
const updatedNoises = [...this.noisesArray];
|
||||||
|
updatedNoises[index] = { ...updatedNoises[index], packet: value };
|
||||||
|
this.noisesArray = updatedNoises;
|
||||||
|
},
|
||||||
|
updateNoiseDelay(index, value) {
|
||||||
|
const updatedNoises = [...this.noisesArray];
|
||||||
|
updatedNoises[index] = { ...updatedNoises[index], delay: value };
|
||||||
|
this.noisesArray = updatedNoises;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
fragment: {
|
||||||
|
get: function () { return this.allSetting?.subJsonFragment != ""; },
|
||||||
|
set: function (v) {
|
||||||
|
this.allSetting.subJsonFragment = v ? JSON.stringify(this.defaultFragment) : "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fragmentPackets: {
|
||||||
|
get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.packets : ""; },
|
||||||
|
set: function (v) {
|
||||||
|
if (v != "") {
|
||||||
|
newFragment = JSON.parse(this.allSetting.subJsonFragment);
|
||||||
|
newFragment.settings.fragment.packets = v;
|
||||||
|
this.allSetting.subJsonFragment = JSON.stringify(newFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fragmentLength: {
|
||||||
|
get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.length : ""; },
|
||||||
|
set: function (v) {
|
||||||
|
if (v != "") {
|
||||||
|
newFragment = JSON.parse(this.allSetting.subJsonFragment);
|
||||||
|
newFragment.settings.fragment.length = v;
|
||||||
|
this.allSetting.subJsonFragment = JSON.stringify(newFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fragmentInterval: {
|
||||||
|
get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.interval : ""; },
|
||||||
|
set: function (v) {
|
||||||
|
if (v != "") {
|
||||||
|
newFragment = JSON.parse(this.allSetting.subJsonFragment);
|
||||||
|
newFragment.settings.fragment.interval = v;
|
||||||
|
this.allSetting.subJsonFragment = JSON.stringify(newFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
noises: {
|
||||||
|
get() {
|
||||||
|
return this.allSetting?.subJsonNoises != "";
|
||||||
|
},
|
||||||
|
set(v) {
|
||||||
|
if (v) {
|
||||||
|
this.allSetting.subJsonNoises = JSON.stringify(this.defaultNoises);
|
||||||
|
} else {
|
||||||
|
this.allSetting.subJsonNoises = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
noisesArray: {
|
||||||
|
get() {
|
||||||
|
return this.noises ? JSON.parse(this.allSetting.subJsonNoises).settings.noises : [];
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
if (this.noises) {
|
||||||
|
const newNoises = JSON.parse(this.allSetting.subJsonNoises);
|
||||||
|
newNoises.settings.noises = value;
|
||||||
|
this.allSetting.subJsonNoises = JSON.stringify(newNoises);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enableMux: {
|
||||||
|
get: function () { return this.allSetting?.subJsonMux != ""; },
|
||||||
|
set: function (v) {
|
||||||
|
this.allSetting.subJsonMux = v ? JSON.stringify(this.defaultMux) : "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
muxConcurrency: {
|
||||||
|
get: function () { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).concurrency : -1; },
|
||||||
|
set: function (v) {
|
||||||
|
newMux = JSON.parse(this.allSetting.subJsonMux);
|
||||||
|
newMux.concurrency = v;
|
||||||
|
this.allSetting.subJsonMux = JSON.stringify(newMux);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
muxXudpConcurrency: {
|
||||||
|
get: function () { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpConcurrency : -1; },
|
||||||
|
set: function (v) {
|
||||||
|
newMux = JSON.parse(this.allSetting.subJsonMux);
|
||||||
|
newMux.xudpConcurrency = v;
|
||||||
|
this.allSetting.subJsonMux = JSON.stringify(newMux);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
muxXudpProxyUDP443: {
|
||||||
|
get: function () { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpProxyUDP443 : "reject"; },
|
||||||
|
set: function (v) {
|
||||||
|
newMux = JSON.parse(this.allSetting.subJsonMux);
|
||||||
|
newMux.xudpProxyUDP443 = v;
|
||||||
|
this.allSetting.subJsonMux = JSON.stringify(newMux);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enableDirect: {
|
||||||
|
get: function () { return this.allSetting?.subJsonRules != ""; },
|
||||||
|
set: function (v) {
|
||||||
|
this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directIPs: {
|
||||||
|
get: function () {
|
||||||
|
if (!this.enableDirect) return [];
|
||||||
|
const rules = JSON.parse(this.allSetting.subJsonRules);
|
||||||
|
if (!Array.isArray(rules)) return [];
|
||||||
|
const ipRule = rules.find(r => r.ip);
|
||||||
|
return ipRule?.ip ?? [];
|
||||||
|
},
|
||||||
|
set: function (v) {
|
||||||
|
let rules = JSON.parse(this.allSetting.subJsonRules);
|
||||||
|
if (!Array.isArray(rules)) return;
|
||||||
|
|
||||||
|
if (v.length == 0) {
|
||||||
|
rules = rules.filter(r => !r.ip);
|
||||||
|
} else {
|
||||||
|
let ruleIndex = rules.findIndex(r => r.ip);
|
||||||
|
if (ruleIndex == -1) ruleIndex = rules.push(this.defaultRules[1]) - 1;
|
||||||
|
|
||||||
|
rules[ruleIndex].ip = [];
|
||||||
|
v.forEach(d => {
|
||||||
|
rules[ruleIndex].ip.push(d);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.allSetting.subJsonRules = JSON.stringify(rules);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
directDomains: {
|
||||||
|
get: function () {
|
||||||
|
if (!this.enableDirect) return [];
|
||||||
|
const rules = JSON.parse(this.allSetting.subJsonRules);
|
||||||
|
if (!Array.isArray(rules)) return [];
|
||||||
|
const domainRule = rules.find(r => r.domain);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
this.allSetting.subJsonRules = JSON.stringify(rules);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confAlerts: {
|
||||||
|
get: function () {
|
||||||
|
if (!this.allSetting) return [];
|
||||||
|
var alerts = []
|
||||||
|
if (window.location.protocol !== "https:") alerts.push('{{ i18n "secAlertSSL" }}');
|
||||||
|
if (this.allSetting.webPort == 54321) alerts.push('{{ i18n "secAlertPanelPort" }}');
|
||||||
|
panelPath = window.location.pathname.split('/').length < 4
|
||||||
|
if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "secAlertPanelURI" }}');
|
||||||
|
if (this.allSetting.subEnable) {
|
||||||
|
subPath = this.allSetting.subURI.length > 0 ? new URL(this.allSetting.subURI).pathname : this.allSetting.subPath;
|
||||||
|
if (subPath == '/sub/') alerts.push('{{ i18n "secAlertSubURI" }}');
|
||||||
|
subJsonPath = this.allSetting.subJsonURI.length > 0 ? new URL(this.allSetting.subJsonURI).pathname : this.allSetting.subJsonPath;
|
||||||
|
if (subJsonPath == '/json/') alerts.push('{{ i18n "secAlertSubJsonURI" }}');
|
||||||
|
}
|
||||||
|
return alerts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
await this.getAllSetting();
|
||||||
|
while (true) {
|
||||||
|
await PromiseUtil.sleep(1000);
|
||||||
|
this.saveBtnDisable = this.oldAllSetting.equals(this.allSetting);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
149
web/html/settings/panel/general.html
Normal file
149
web/html/settings/panel/general.html
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
{{define "settings/panel/general"}}
|
||||||
|
<a-collapse default-active-key="1">
|
||||||
|
<a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>
|
||||||
|
{{ i18n "pages.settings.remarkModel"}}
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
{{ i18n "pages.settings.sampleRemark"}}: <i>#[[ remarkSample ]]</i>
|
||||||
|
</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-group :style="{ width: '100%' }">
|
||||||
|
<a-select :style="{ paddingRight: '.5rem', minWidth: '80%', width: 'auto' }" mode="multiple"
|
||||||
|
v-model="remarkModel" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="(value, key) in remarkModels" :value="key">[[ value ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
<a-select :style="{ width: '20%' }" v-model="remarkSeparator"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-for="key in remarkSeparators" :value="key">[[ key ]]</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-input-group>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.panelListeningIP"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.panelListeningIPDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.webListen"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.panelListeningDomain"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.panelListeningDomainDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.webDomain"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.panelPort"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.panelPortDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number :min="1" :min="65531" v-model="allSetting.webPort" :style="{ width: '100%' }"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.panelUrlPath"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.panelUrlPathDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.webBasePath"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.sessionMaxAge" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.sessionMaxAgeDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number :min="60" v-model="allSetting.sessionMaxAge" :style="{ width: '100%' }"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.pageSize" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.pageSizeDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number :min="0" step="5" v-model="allSetting.pageSize" :style="{ width: '100%' }"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.language"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select ref="selectLang" v-model="lang" @change="LanguageManager.setLanguage(lang)"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
|
||||||
|
<a-select-option :value="l.value" :label="l.value" v-for="l in LanguageManager.supportedLanguages">
|
||||||
|
<span role="img" :aria-label="l.name" v-text="l.icon"></span> <span
|
||||||
|
v-text="l.name"></span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="2" header='{{ i18n "pages.settings.notifications" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.expireTimeDiff" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.expireTimeDiffDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number :min="0" v-model="allSetting.expireDiff" :style="{ width: '100%' }"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.trafficDiff" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.trafficDiffDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number :min="0" v-model="allSetting.trafficDiff" :style="{ width: '100%' }"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="3" header='{{ i18n "pages.settings.certs" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.publicKeyPath"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.publicKeyPathDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.webCertFile"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.privateKeyPath"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.privateKeyPathDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.webKeyFile"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="4" header='{{ i18n "pages.settings.externalTraffic" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.externalTrafficInformEnable"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.externalTrafficInformEnableDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="allSetting.externalTrafficInformEnable"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.externalTrafficInformURI"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.externalTrafficInformURIDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" placeholder="(http|https)://domain[:port]/path/"
|
||||||
|
v-model="allSetting.externalTrafficInformURI"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="5" header='{{ i18n "pages.settings.dateAndTime" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.timeZone"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.timeZoneDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.timeLocation"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.datepicker"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.datepickerDescription"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select :style="{ width: '100%' }" :dropdown-class-name="themeSwitcher.currentTheme" v-model="datepicker">
|
||||||
|
<a-select-option v-for="item in datepickerList" :value="item.value">
|
||||||
|
<span v-text="item.name"></span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
{{end}}
|
||||||
60
web/html/settings/panel/security.html
Normal file
60
web/html/settings/panel/security.html
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
{{define "settings/panel/security"}}
|
||||||
|
<a-collapse default-active-key="1">
|
||||||
|
<a-collapse-panel key="1" header='{{ i18n "pages.settings.security.admin"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.oldUsername"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input autocomplete="username" v-model="user.oldUsername"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.currentPassword"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-password autocomplete="current-password" v-model="user.oldPassword"></a-input-password>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.newUsername"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input v-model="user.newUsername"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.newPassword"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-password autocomplete="new-password" v-model="user.newPassword"></a-input-password>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-space direction="horizontal" :style="{ padding: '0 20px' }">
|
||||||
|
<a-button type="primary" @click="updateUser">{{ i18n "confirm" }}</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="2" header='{{ i18n "pages.settings.security.secret"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.security.loginSecurity" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.security.loginSecurityDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch @change="toggleToken(allSetting.secretEnable)" v-model="allSetting.secretEnable"></a-switch>
|
||||||
|
<a-icon :style="{ marginLeft: '1rem' }" v-if="allSetting.secretEnable" :spin="this.changeSecret" type="sync"
|
||||||
|
@click="getNewSecret"></a-icon>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.security.secretToken" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.security.secretTokenDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-textarea type="text" :disabled="!allSetting.secretEnable" v-model="user.loginSecret"></a-textarea>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-list-item>
|
||||||
|
<a-space direction="horizontal" :style="{ padding: '0 20px' }">
|
||||||
|
<a-button type="primary" :loading="this.changeSecret" @click="updateSecret">
|
||||||
|
<span>{{ i18n "confirm"}}</span>
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
{{end}}
|
||||||
98
web/html/settings/panel/subscription/general.html
Normal file
98
web/html/settings/panel/subscription/general.html
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
{{define "settings/panel/subscription/general"}}
|
||||||
|
<a-collapse default-active-key="1">
|
||||||
|
<a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subEnable"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subEnableDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="allSetting.subEnable"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subTitle"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subTitleDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subTitle"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subListen"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subListenDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subListen"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subDomain"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subDomainDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subDomain"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subPort"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subPortDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number v-model="allSetting.subPort" :min="1" :min="65531"
|
||||||
|
:style="{ width: '100%' }"></a-input-number>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subPath"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subPathDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subPath"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subURI"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subURIDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" placeholder="(http|https)://domain[:port]/path/"
|
||||||
|
v-model="allSetting.subURI"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="2" header='{{ i18n "pages.settings.information" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subEncrypt"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subEncryptDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="allSetting.subEncrypt"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subShowInfo"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subShowInfoDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="allSetting.subShowInfo"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="3" header='{{ i18n "pages.settings.certs" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subCertPath"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subCertPathDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subCertFile"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subKeyPath"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subKeyPathDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subKeyFile"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="4" header='{{ i18n "pages.settings.intervals"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subUpdates"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subUpdatesDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number :min="1" v-model="allSetting.subUpdates" :style="{ width: '100%' }"></a-input-number>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
{{end}}
|
||||||
180
web/html/settings/panel/subscription/json.html
Normal file
180
web/html/settings/panel/subscription/json.html
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
{{define "settings/panel/subscription/json"}}
|
||||||
|
<a-collapse default-active-key="1">
|
||||||
|
<a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subPath"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subPathDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.subJsonPath"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.subURI"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.subURIDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" placeholder="(http|https)://domain[:port]/path/"
|
||||||
|
v-model="allSetting.subJsonURI"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="2" header='{{ i18n "pages.settings.fragment"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.fragment"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.fragmentDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="fragment"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-list-item v-if="fragment" :style="{ padding: '10px 20px' }">
|
||||||
|
<a-collapse>
|
||||||
|
<a-collapse-panel header='{{ i18n "pages.settings.fragmentSett"}}' v-if="fragment">
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>Packets</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="fragmentPackets"
|
||||||
|
placeholder="1-1 | 1-3 | tlshello | ..."></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>Length</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="fragmentLength" placeholder="100-200"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>Interval</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="fragmentInterval" placeholder="10-20"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
</a-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="3" header="Noises">
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>Noises</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.noisesDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="noises"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-list-item v-if="noises" :style="{ padding: '10px 20px' }">
|
||||||
|
<a-collapse>
|
||||||
|
<a-collapse-panel v-for="(noise, index) in noisesArray" :key="index" :header="`Noise №${index + 1}`">
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>Type</template>
|
||||||
|
<template #control>
|
||||||
|
<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', 'hex']" :key="p">
|
||||||
|
<span>[[ p ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>Packet</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" :value="noise.packet"
|
||||||
|
@input="(value) => updateNoisePacket(index, event.target.value)"
|
||||||
|
placeholder="5-10"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>Delay (ms)</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" :value="noise.delay"
|
||||||
|
@input="(value) => updateNoiseDelay(index, event.target.value)"
|
||||||
|
placeholder="10-20"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-space direction="horizontal" :style="{ padding: '10px 20px' }">
|
||||||
|
<a-button v-if="noisesArray.length > 1" type="danger"
|
||||||
|
@click="removeNoise(index)">Remove</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
<a-button v-if="noises" type="primary" @click="addNoise" :style="{ marginTop: '10px' }">Add Noise</a-button>
|
||||||
|
</a-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="4" header='{{ i18n "pages.settings.mux"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.mux"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.muxDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="enableMux"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-list-item v-if="enableMux" :style="{ padding: '10px 20px' }">
|
||||||
|
<a-collapse>
|
||||||
|
<a-collapse-panel header='{{ i18n "pages.settings.muxSett"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>Concurrency</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number v-model="muxConcurrency" :min="-1" :max="1024"
|
||||||
|
:style="{ width: '100%' }"></a-input-number>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>xudp Concurrency</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number v-model="muxXudpConcurrency" :min="-1" :max="1024"
|
||||||
|
:style="{ width: '100%' }"></a-input-number>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>xudp UDP 443</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select v-model="muxXudpProxyUDP443" :style="{ width: '100%' }"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option :value="p" :label="p" v-for="p in ['reject', 'allow', 'skip']">
|
||||||
|
<span>[[ p ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
</a-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="5" header='{{ i18n "pages.settings.direct" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.direct"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.directDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="enableDirect"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-list-item v-if="enableDirect" :style="{ padding: '10px 20px' }">
|
||||||
|
<a-collapse>
|
||||||
|
<a-collapse-panel header='{{ i18n "pages.settings.direct"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.directips" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<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">
|
||||||
|
<span>[[ p.label ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.directdomains" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<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">
|
||||||
|
<span>[[ p.label ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
</a-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
{{end}}
|
||||||
87
web/html/settings/panel/telegram.html
Normal file
87
web/html/settings/panel/telegram.html
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
{{define "settings/panel/telegram"}}
|
||||||
|
<a-collapse default-active-key="1">
|
||||||
|
<a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.telegramBotEnable" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.telegramBotEnableDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="allSetting.tgBotEnable"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.telegramToken"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.telegramTokenDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.tgBotToken"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.telegramChatId"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.telegramChatIdDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.tgBotChatId"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.telegramBotLanguage"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select ref="selectBotLang" v-model="allSetting.tgLang"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
|
||||||
|
<a-select-option :value="l.value" :label="l.value" v-for="l in LanguageManager.supportedLanguages">
|
||||||
|
<span role="img" :aria-label="l.name" v-text="l.icon"></span> <span
|
||||||
|
v-text="l.name"></span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="2" header='{{ i18n "pages.settings.notifications" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.telegramNotifyTime"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.telegramNotifyTimeDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="allSetting.tgRunTime"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.tgNotifyBackup" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.tgNotifyBackupDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="allSetting.tgBotBackup"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.tgNotifyLogin" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.tgNotifyLoginDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="allSetting.tgBotLoginNotify"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.tgNotifyCpu" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.tgNotifyCpuDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input-number :min="0" :min="100" v-model="allSetting.tgCpu" :style="{ width: '100%' }"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="3" header='{{ i18n "pages.settings.proxyAndServer" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.telegramProxy"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.telegramProxyDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" placeholder="socks5://user:pass@host:port"
|
||||||
|
v-model="allSetting.tgBotProxy"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.settings.telegramAPIServer"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.settings.telegramAPIServerDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" placeholder="https://api.example.com"
|
||||||
|
v-model="allSetting.tgBotAPIServer"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
{{end}}
|
||||||
14
web/html/settings/xray/advanced.html
Normal file
14
web/html/settings/xray/advanced.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{{define "settings/xray/advanced"}}
|
||||||
|
<a-space direction="vertical" size="small">
|
||||||
|
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}'
|
||||||
|
description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
|
||||||
|
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" :style="{ margin: '10px 0' }"
|
||||||
|
:size="isMobile ? 'small' : ''">
|
||||||
|
<a-radio-button value="xraySetting">{{ i18n "pages.xray.completeTemplate"}}</a-radio-button>
|
||||||
|
<a-radio-button value="inboundSettings">{{ i18n "pages.xray.Inbounds" }}</a-radio-button>
|
||||||
|
<a-radio-button value="outboundSettings">{{ i18n "pages.xray.Outbounds" }}</a-radio-button>
|
||||||
|
<a-radio-button value="routingRuleSettings">{{ i18n "pages.xray.Routings" }}</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
<textarea :style="{ position: 'absolute', left: '-800px' }" id="xraySetting"></textarea>
|
||||||
|
</a-space>
|
||||||
|
{{end}}
|
||||||
53
web/html/settings/xray/balancers.html
Normal file
53
web/html/settings/xray/balancers.html
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{{define "settings/xray/balancers"}}
|
||||||
|
<template v-if="balancersData.length > 0">
|
||||||
|
<a-space direction="vertical" size="middle">
|
||||||
|
<a-button type="primary" icon="plus" @click="addBalancer()">
|
||||||
|
<span>{{ i18n "pages.xray.balancer.addBalancer"}}</span>
|
||||||
|
</a-button>
|
||||||
|
<a-table :columns="balancerColumns" bordered :row-key="r => r.key" :data-source="balancersData"
|
||||||
|
:scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0">
|
||||||
|
<template slot="action" slot-scope="text, balancer, index">
|
||||||
|
<span>[[ index+1 ]]</span>
|
||||||
|
<a-dropdown :trigger="['click']">
|
||||||
|
<a-icon @click="e => e.preventDefault()" type="more"
|
||||||
|
:style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
|
||||||
|
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||||
|
<a-menu-item @click="editBalancer(index)">
|
||||||
|
<a-icon type="edit"></a-icon>
|
||||||
|
<span>{{ i18n "edit" }}</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="deleteBalancer(index)">
|
||||||
|
<span :style="{ color: '#FF4D4F' }">
|
||||||
|
<a-icon type="delete"></a-icon>
|
||||||
|
<span>{{ i18n "delete"}}</span>
|
||||||
|
</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
<template slot="strategy" slot-scope="text, balancer, index">
|
||||||
|
<a-tag :style="{ margin: '0' }" v-if="balancer.strategy=='random'" color="purple">Random</a-tag>
|
||||||
|
<a-tag :style="{ margin: '0' }" v-if="balancer.strategy=='roundRobin'" color="green">Round Robin</a-tag>
|
||||||
|
<a-tag :style="{ margin: '0' }" v-if="balancer.strategy=='leastLoad'" color="green">Least Load</a-tag>
|
||||||
|
<a-tag :style="{ margin: '0' }" v-if="balancer.strategy=='leastPing'" color="green">Least Ping</a-tag>
|
||||||
|
</template>
|
||||||
|
<template slot="selector" slot-scope="text, balancer, index">
|
||||||
|
<a-tag class="info-large-tag" :style="{ margin: '1' }" v-for="sel in balancer.selector">[[ sel ]]</a-tag>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
<a-radio-group v-if="observatoryEnable || burstObservatoryEnable" v-model="obsSettings" @change="changeObsCode"
|
||||||
|
button-style="solid" :size="isMobile ? 'small' : ''">
|
||||||
|
<a-radio-button value="observatory" v-if="observatoryEnable">Observatory</a-radio-button>
|
||||||
|
<a-radio-button value="burstObservatory" v-if="burstObservatoryEnable">Burst Observatory</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
<textarea :style="{ position: 'absolute', left: '-800px' }" id="obsSetting"></textarea>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-empty description='{{ i18n "emptyBalancersDesc" }}' :style="{ margin: '10px' }">
|
||||||
|
<a-button type="primary" icon="plus" @click="addBalancer()" :style="{ marginTop: '10px' }">
|
||||||
|
<span>{{ i18n "pages.xray.balancer.addBalancer"}}</span>
|
||||||
|
</a-button>
|
||||||
|
</a-empty>
|
||||||
|
</template>
|
||||||
|
{{end}}
|
||||||
275
web/html/settings/xray/basics.html
Normal file
275
web/html/settings/xray/basics.html
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
{{define "settings/xray/basics"}}
|
||||||
|
<a-collapse default-active-key="1">
|
||||||
|
<a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||||
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
|
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||||
|
<template slot="message">
|
||||||
|
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.generalConfigsDesc" }}</span>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</a-row>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.FreedomStrategy" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.FreedomStrategyDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select v-model="freedomStrategy" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
|
:style="{ width: '100%' }">
|
||||||
|
<a-select-option v-for="s in OutboundDomainStrategies" :value="s">
|
||||||
|
<span>[[ s ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.RoutingStrategy" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.RoutingStrategyDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select v-model="routingStrategy" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
|
:style="{ width: '100%' }">
|
||||||
|
<a-select-option v-for="s in routingDomainStrategies" :value="s">
|
||||||
|
<span>[[ s ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="2" header='{{ i18n "pages.xray.statistics" }}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.statsInboundUplink" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.statsInboundUplinkDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="statsInboundUplink"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.statsInboundDownlink" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.statsInboundDownlinkDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="statsInboundDownlink"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.statsOutboundUplink" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.statsOutboundUplinkDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="statsOutboundUplink"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.statsOutboundDownlink" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.statsOutboundDownlinkDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="statsOutboundDownlink"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="3" header='{{ i18n "pages.xray.logConfigs" }}'>
|
||||||
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
|
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||||
|
<template slot="message">
|
||||||
|
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.logConfigsDesc" }}</span>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</a-row>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.logLevel" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.logLevelDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select v-model="logLevel" :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
|
||||||
|
<a-select-option v-for="s in log.loglevel" :value="s">
|
||||||
|
<span>[[ s ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.accessLog" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.accessLogDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select v-model="accessLog" :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
|
||||||
|
<a-select-option value=''>
|
||||||
|
<span>Empty</span>
|
||||||
|
</a-select-option>
|
||||||
|
<a-select-option v-for="s in log.access" :value="s">
|
||||||
|
<span>[[ s ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.errorLog" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.errorLogDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select v-model="errorLog" :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }">
|
||||||
|
<a-select-option value=''>
|
||||||
|
<span>Empty</span>
|
||||||
|
</a-select-option>
|
||||||
|
<a-select-option v-for="s in log.error" :value="s">
|
||||||
|
<span>[[ s ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.maskAddress" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.maskAddressDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select v-model="maskAddressLog" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||||
|
:style="{ width: '100%' }">
|
||||||
|
<a-select-option value=''>
|
||||||
|
<span>Empty</span>
|
||||||
|
</a-select-option>
|
||||||
|
<a-select-option v-for="s in log.maskAddress" :value="s">
|
||||||
|
<span>[[ s ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.dnsLog"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.dnsLogDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="dnslog"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="4" header='{{ i18n "pages.xray.blockConfigs"}}'>
|
||||||
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
|
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||||
|
<template slot="message">
|
||||||
|
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.blockConfigsDesc" }}</span>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</a-row>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.Torrent"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.TorrentDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="torrentSettings"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.Family"}}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.FamilyDesc"}}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="familyProtectSettings"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="5" header='{{ i18n "pages.xray.basicRouting"}}'>
|
||||||
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
|
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||||
|
<template slot="message">
|
||||||
|
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.blockConnectionsConfigsDesc" }}</span>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</a-row>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.blockips" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select mode="tags" v-model="blockedIPs" :style="{ width: '100%' }"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.IPsOptions">
|
||||||
|
<span>[[ p.label ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.blockdomains" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select mode="tags" v-model="blockedDomains" :style="{ width: '100%' }"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.BlockDomainsOptions">
|
||||||
|
<span>[[ p.label ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
|
<a-alert type="warning" :style="{ textAlign: 'center', marginTop: '20px' }">
|
||||||
|
<template slot="message">
|
||||||
|
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.directConnectionsConfigsDesc" }}</span>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</a-row>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.directips" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<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 settingsData.IPsOptions">
|
||||||
|
<span>[[ p.label ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.directdomains" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<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 settingsData.DomainsOptions">
|
||||||
|
<span>[[ p.label ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
|
<a-alert type="warning" :style="{ textAlign: 'center', marginTop: '20px' }">
|
||||||
|
<template slot="message">
|
||||||
|
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.ipv4RoutingDesc" }}</span>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</a-row>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.ipv4Routing" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-select mode="tags" :style="{ width: '100%' }" v-model="ipv4Domains"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.ServicesOptions">
|
||||||
|
<span>[[ p.label ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-row :xs="24" :sm="24" :lg="12">
|
||||||
|
<a-alert type="warning" :style="{ textAlign: 'center', marginTop: '20px' }">
|
||||||
|
<template slot="message">
|
||||||
|
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||||
|
{{ i18n "pages.xray.warpRoutingDesc" }}
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</a-row>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.warpRouting" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<template v-if="WarpExist">
|
||||||
|
<a-select mode="tags" :style="{ width: '100%' }" v-model="warpDomains"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.ServicesOptions">
|
||||||
|
<span>[[ p.label ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-button type="primary" icon="cloud" @click="showWarp()">WARP</a-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="6" header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
|
||||||
|
<a-space direction="horizontal" :style="{ padding: '0 20px' }">
|
||||||
|
<a-button type="danger" @click="resetXrayConfigToDefault">
|
||||||
|
<span>{{ i18n "pages.settings.resetDefaultConfig" }}</span>
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
{{end}}
|
||||||
149
web/html/settings/xray/dns.html
Normal file
149
web/html/settings/xray/dns.html
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
{{define "settings/xray/dns"}}
|
||||||
|
<a-collapse default-active-key="1">
|
||||||
|
<a-collapse-panel key="1" header='{{ i18n "pages.xray.generalConfigs"}}'>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.dns.enable" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.dns.enableDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="enableDNS"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<template v-if="enableDNS">
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.dns.tag" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.dns.tagDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="dnsTag"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.dns.clientIp" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.dns.clientIpDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-input type="text" v-model="dnsClientIp"></a-input>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.dns.strategy" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.dns.strategyDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<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']">
|
||||||
|
<span>[[ l ]]</span>
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.dns.disableCache" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.dns.disableCacheDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="dnsDisableCache"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.dns.disableFallback" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.dns.disableFallbackDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="dnsDisableFallback"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
<a-setting-list-item paddings="small">
|
||||||
|
<template #title>{{ i18n "pages.xray.dns.disableFallbackIfMatch" }}</template>
|
||||||
|
<template #description>{{ i18n "pages.xray.dns.disableFallbackIfMatchDesc" }}</template>
|
||||||
|
<template #control>
|
||||||
|
<a-switch v-model="dnsDisableFallbackIfMatch"></a-switch>
|
||||||
|
</template>
|
||||||
|
</a-setting-list-item>
|
||||||
|
</template>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<template v-if="enableDNS">
|
||||||
|
<a-collapse-panel key="2" header='DNS'>
|
||||||
|
<template v-if="dnsServers.length > 0">
|
||||||
|
<a-space direction="vertical" size="middle">
|
||||||
|
<a-button type="primary" icon="plus" @click="addDNSServer()">
|
||||||
|
<span>{{ i18n "pages.xray.dns.add" }}</span>
|
||||||
|
</a-button>
|
||||||
|
<a-table :columns="dnsColumns" bordered :row-key="r => r.key" :data-source="dnsServers"
|
||||||
|
:scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0">
|
||||||
|
<template slot="action" slot-scope="text,dns,index">
|
||||||
|
<span>[[ index+1 ]]</span>
|
||||||
|
<a-dropdown :trigger="['click']">
|
||||||
|
<a-icon @click="e => e.preventDefault()" type="more"
|
||||||
|
:style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
|
||||||
|
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||||
|
<a-menu-item @click="editDNSServer(index)">
|
||||||
|
<a-icon type="edit"></a-icon>
|
||||||
|
<span>{{ i18n "edit" }}</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="deleteDNSServer(index)">
|
||||||
|
<span :style="{ color: '#FF4D4F' }">
|
||||||
|
<a-icon type="delete"></a-icon>
|
||||||
|
<span>{{ i18n "delete"}}</span>
|
||||||
|
</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-space>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-empty description='{{ i18n "emptyDnsDesc" }}' :style="{ margin: '10px' }">
|
||||||
|
<a-button type="primary" icon="plus" @click="addDNSServer()" :style="{ marginTop: '10px' }">
|
||||||
|
<span>{{ i18n "pages.xray.dns.add" }}</span>
|
||||||
|
</a-button>
|
||||||
|
</a-empty>
|
||||||
|
</template>
|
||||||
|
</a-collapse-panel>
|
||||||
|
<a-collapse-panel key="3" header='Fake DNS'>
|
||||||
|
<template v-if="fakeDns && fakeDns.length > 0">
|
||||||
|
<a-space direction="vertical" size="middle">
|
||||||
|
<a-button type="primary" icon="plus" @click="addFakedns()">{{ i18n "pages.xray.fakedns.add"
|
||||||
|
}}</a-button>
|
||||||
|
<a-table :columns="fakednsColumns" bordered :row-key="r => r.key" :data-source="fakeDns"
|
||||||
|
:scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0">
|
||||||
|
<template slot="action" slot-scope="text,fakedns,index">
|
||||||
|
<span>[[ index+1 ]]</span>
|
||||||
|
<a-dropdown :trigger="['click']">
|
||||||
|
<a-icon @click="e => e.preventDefault()" type="more"
|
||||||
|
:style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
|
||||||
|
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||||
|
<a-menu-item @click="editFakedns(index)">
|
||||||
|
<a-icon type="edit"></a-icon>
|
||||||
|
<span>{{ i18n "edit" }}</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="deleteFakedns(index)">
|
||||||
|
<span :style="{ color: '#FF4D4F' }">
|
||||||
|
<a-icon type="delete"></a-icon>
|
||||||
|
<span>{{ i18n "delete"}}</span>
|
||||||
|
</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-empty description='{{ i18n "emptyFakeDnsDesc" }}' :style="{ margin: '20px' }">
|
||||||
|
<a-button type="primary" icon="plus" @click="addFakedns()" :style="{ marginTop: '10px' }">
|
||||||
|
<span>{{ i18n "pages.xray.fakedns.add" }}</span>
|
||||||
|
</a-button>
|
||||||
|
</a-empty>
|
||||||
|
</template>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</template>
|
||||||
|
</a-collapse>
|
||||||
|
{{end}}
|
||||||
74
web/html/settings/xray/outbounds.html
Normal file
74
web/html/settings/xray/outbounds.html
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
{{define "settings/xray/outbounds"}}
|
||||||
|
<a-space direction="vertical" size="middle">
|
||||||
|
<a-row>
|
||||||
|
<a-col :xs="12" :sm="12" :lg="12">
|
||||||
|
<a-space direction="horizontal" size="small">
|
||||||
|
<a-button type="primary" icon="plus" @click="addOutbound()">
|
||||||
|
{{ i18n "pages.xray.outbound.addOutbound" }}
|
||||||
|
</a-button>
|
||||||
|
<a-button type="primary" icon="cloud" @click="showWarp()">WARP</a-button>
|
||||||
|
</a-space>
|
||||||
|
</a-col>
|
||||||
|
<a-col :xs="12" :sm="12" :lg="12" :style="{ textAlign: 'right' }">
|
||||||
|
<a-button-group>
|
||||||
|
<a-button icon="sync" @click="refreshOutboundTraffic()" :loading="refreshing"></a-button>
|
||||||
|
<a-popconfirm placement="topRight" @confirm="resetOutboundTraffic(-1)"
|
||||||
|
title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme"
|
||||||
|
ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
|
||||||
|
<a-icon slot="icon" type="question-circle-o"
|
||||||
|
:style="{ color: themeSwitcher.isDarkTheme ? '#008771' : '#008771' }"></a-icon>
|
||||||
|
<a-button icon="retweet"></a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-button-group>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-table :columns="outboundColumns" bordered :row-key="r => r.key" :data-source="outboundData"
|
||||||
|
:scroll="isMobile ? {} : { x: 800 }" :pagination="false" :indent-size="0">
|
||||||
|
<template slot="action" slot-scope="text, outbound, index">
|
||||||
|
<span>[[ index+1 ]]</span>
|
||||||
|
<a-dropdown :trigger="['click']">
|
||||||
|
<a-icon @click="e => e.preventDefault()" type="more"
|
||||||
|
:style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
|
||||||
|
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||||
|
<a-menu-item v-if="index>0" @click="setFirstOutbound(index)">
|
||||||
|
<a-icon type="vertical-align-top"></a-icon>
|
||||||
|
<span>{{ i18n "pages.xray.rules.first"}}</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="editOutbound(index)">
|
||||||
|
<a-icon type="edit"></a-icon>
|
||||||
|
<span>{{ i18n "edit" }}</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="resetOutboundTraffic(index)">
|
||||||
|
<span>
|
||||||
|
<a-icon type="retweet"></a-icon>
|
||||||
|
<span>{{ i18n "pages.inbounds.resetTraffic"}}</span>
|
||||||
|
</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="deleteOutbound(index)">
|
||||||
|
<span :style="{ color: '#FF4D4F' }">
|
||||||
|
<a-icon type="delete"></a-icon>
|
||||||
|
<span>{{ i18n "delete"}}</span>
|
||||||
|
</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
<template slot="address" slot-scope="text, outbound, index">
|
||||||
|
<p :style="{ margin: '0 5px' }" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
|
||||||
|
</template>
|
||||||
|
<template slot="protocol" slot-scope="text, outbound, index">
|
||||||
|
<a-tag :style="{ margin: '0' }" color="purple">[[ outbound.protocol ]]</a-tag>
|
||||||
|
<template
|
||||||
|
v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
|
||||||
|
<a-tag :style="{ margin: '0' }" color="blue">[[ outbound.streamSettings.network ]]</a-tag>
|
||||||
|
<a-tag :style="{ margin: '0' }" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
|
||||||
|
<a-tag :style="{ margin: '0' }" v-if="outbound.streamSettings.security=='reality'"
|
||||||
|
color="green">reality</a-tag>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template slot="traffic" slot-scope="text, outbound, index">
|
||||||
|
<a-tag color="green">[[ findOutboundTraffic(outbound) ]]</a-tag>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-space>
|
||||||
|
{{end}}
|
||||||
38
web/html/settings/xray/reverse.html
Normal file
38
web/html/settings/xray/reverse.html
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{{define "settings/xray/reverse"}}
|
||||||
|
<template v-if="reverseData.length > 0">
|
||||||
|
<a-space direction="vertical" size="middle">
|
||||||
|
<a-button type="primary" icon="plus" @click="addReverse()">
|
||||||
|
<span>{{ i18n "pages.xray.outbound.addReverse" }}</span>
|
||||||
|
</a-button>
|
||||||
|
<a-table :columns="reverseColumns" bordered :row-key="r => r.key" :data-source="reverseData"
|
||||||
|
:scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0">
|
||||||
|
<template slot="action" slot-scope="text, reverse, index">
|
||||||
|
<span>[[ index+1 ]]</span>
|
||||||
|
<a-dropdown :trigger="['click']">
|
||||||
|
<a-icon @click="e => e.preventDefault()" type="more"
|
||||||
|
:style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
|
||||||
|
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||||
|
<a-menu-item @click="editReverse(index)">
|
||||||
|
<a-icon type="edit"></a-icon>
|
||||||
|
<span>{{ i18n "edit" }}</span>
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="deleteReverse(index)">
|
||||||
|
<span :style="{ color: '#FF4D4F' }">
|
||||||
|
<a-icon type="delete"></a-icon>
|
||||||
|
<span>{{ i18n "delete"}}</span>
|
||||||
|
</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<a-empty description='{{ i18n "emptyReverseDesc" }}' :style="{ margin: '10px' }">
|
||||||
|
<a-button type="primary" icon="plus" @click="addReverse()" :style="{ marginTop: '10px' }">
|
||||||
|
{{ i18n "pages.xray.outbound.addReverse" }}
|
||||||
|
</a-button>
|
||||||
|
</a-empty>
|
||||||
|
</template>
|
||||||
|
{{end}}
|
||||||
119
web/html/settings/xray/routing.html
Normal file
119
web/html/settings/xray/routing.html
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
{{define "settings/xray/routing"}}
|
||||||
|
<a-space direction="vertical" size="middle">
|
||||||
|
<a-button type="primary" icon="plus" @click="addRule">{{ i18n "pages.xray.rules.add" }}</a-button>
|
||||||
|
<a-table-sortable :columns="isMobile ? rulesMobileColumns : rulesColumns" bordered :row-key="r => r.key"
|
||||||
|
:data-source="routingRuleData" :scroll="isMobile ? {} : { x: 1000 }" :pagination="false" :indent-size="0"
|
||||||
|
v-on:onSort="replaceRule">
|
||||||
|
<template slot="action" slot-scope="text, rule, index">
|
||||||
|
<a-table-sort-trigger :item-index="index"></a-table-sort-trigger>
|
||||||
|
<span class="ant-table-row-index"> [[ index+1 ]] </span>
|
||||||
|
<a-dropdown :trigger="['click']">
|
||||||
|
<a-icon @click="e => e.preventDefault()" type="more"
|
||||||
|
:style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
|
||||||
|
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||||
|
<a-menu-item v-if="index>0" @click="replaceRule(index,0)">
|
||||||
|
<a-icon type="vertical-align-top"></a-icon>
|
||||||
|
{{ i18n "pages.xray.rules.first"}}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item v-if="index>0" @click="replaceRule(index,index-1)">
|
||||||
|
<a-icon type="arrow-up"></a-icon>
|
||||||
|
{{ i18n "pages.xray.rules.up"}}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item v-if="index<routingRuleData.length-1" @click="replaceRule(index,index+1)">
|
||||||
|
<a-icon type="arrow-down"></a-icon>
|
||||||
|
{{ i18n "pages.xray.rules.down"}}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item v-if="index<routingRuleData.length-1"
|
||||||
|
@click="replaceRule(index,routingRuleData.length-1)">
|
||||||
|
<a-icon type="vertical-align-bottom"></a-icon>
|
||||||
|
{{ i18n "pages.xray.rules.last"}}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="editRule(index)">
|
||||||
|
<a-icon type="edit"></a-icon>
|
||||||
|
{{ i18n "edit" }}
|
||||||
|
</a-menu-item>
|
||||||
|
<a-menu-item @click="deleteRule(index)">
|
||||||
|
<span :style="{ color: '#FF4D4F' }">
|
||||||
|
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
||||||
|
</span>
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu>
|
||||||
|
</a-dropdown>
|
||||||
|
</template>
|
||||||
|
<template slot="inbound" slot-scope="text, rule, index">
|
||||||
|
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template slot="content">
|
||||||
|
<p v-if="rule.inboundTag">Inbound Tag: [[ rule.inboundTag ]]</p>
|
||||||
|
<p v-if="rule.user">User email: [[ rule.user ]]</p>
|
||||||
|
</template>
|
||||||
|
[[ [rule.inboundTag,rule.user].join('\n') ]]
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
<template slot="outbound" slot-scope="text, rule, index">
|
||||||
|
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template slot="content">
|
||||||
|
<p v-if="rule.outboundTag">Outbound Tag: [[ rule.outboundTag ]]</p>
|
||||||
|
</template>
|
||||||
|
[[ rule.outboundTag ]]
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
<template slot="balancer" slot-scope="text, rule, index">
|
||||||
|
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||||
|
<template slot="content">
|
||||||
|
<p v-if="rule.balancerTag">Balancer Tag: [[ rule.balancerTag ]]</p>
|
||||||
|
</template>
|
||||||
|
[[ rule.balancerTag ]]
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
<template slot="info" slot-scope="text, rule, index">
|
||||||
|
<a-popover placement="bottomRight"
|
||||||
|
v-if="(rule.source+rule.sourcePort+rule.network+rule.protocol+rule.attrs+rule.ip+rule.domain+rule.port).length>0"
|
||||||
|
:overlay-class-name="themeSwitcher.currentTheme" trigger="click">
|
||||||
|
<template slot="content">
|
||||||
|
<table cellpadding="2" :style="{ maxWidth: '300px' }">
|
||||||
|
<tr v-if="rule.source">
|
||||||
|
<td>Source</td>
|
||||||
|
<td><a-tag color="blue" v-for="r in rule.source.split(',')">[[ r ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="rule.sourcePort">
|
||||||
|
<td>Source Port</td>
|
||||||
|
<td><a-tag color="green" v-for="r in rule.sourcePort.split(',')">[[ r ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="rule.network">
|
||||||
|
<td>Network</td>
|
||||||
|
<td><a-tag color="blue" v-for="r in rule.network.split(',')">[[ r ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="rule.protocol">
|
||||||
|
<td>Protocol</td>
|
||||||
|
<td><a-tag color="green" v-for="r in rule.protocol.split(',')">[[ r ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="rule.attrs">
|
||||||
|
<td>Attrs</td>
|
||||||
|
<td><a-tag color="blue" v-for="r in rule.attrs.split(',')">[[ r ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="rule.ip">
|
||||||
|
<td>IP</td>
|
||||||
|
<td><a-tag color="green" v-for="r in rule.ip.split(',')">[[ r ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="rule.domain">
|
||||||
|
<td>Domain</td>
|
||||||
|
<td><a-tag color="blue" v-for="r in rule.domain.split(',')">[[ r ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="rule.port">
|
||||||
|
<td>Port</td>
|
||||||
|
<td><a-tag color="green" v-for="r in rule.port.split(',')">[[ r ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="rule.balancerTag">
|
||||||
|
<td>Balancer Tag</td>
|
||||||
|
<td><a-tag color="blue">[[ rule.balancerTag ]]</a-tag></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
<a-button shape="round" size="small" :style="{ fontSize: '14px', padding: '0 10px' }">
|
||||||
|
<a-icon type="info"></a-icon>
|
||||||
|
</a-button>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
|
</a-table-sortable>
|
||||||
|
</a-space>
|
||||||
|
{{end}}
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.min.css?{{ .cur_ver }}">
|
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/xq.min.css?{{ .cur_ver }}">
|
||||||
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css">
|
<link rel="stylesheet" href="{{ .base_path }}assets/codemirror/lint/lint.css">
|
||||||
|
|
||||||
<script src="{{ .base_path }}assets/base64/base64.min.js"></script>
|
|
||||||
<script src="{{ .base_path }}assets/js/model/outbound.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/js/model/outbound.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/codemirror/codemirror.min.js?{{ .cur_ver }}"></script>
|
<script src="{{ .base_path }}assets/codemirror/codemirror.min.js?{{ .cur_ver }}"></script>
|
||||||
<script src="{{ .base_path }}assets/codemirror/javascript.js"></script>
|
<script src="{{ .base_path }}assets/codemirror/javascript.js"></script>
|
||||||
@@ -40,32 +39,21 @@
|
|||||||
.ant-list-item {
|
.ant-list-item {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.collapse-title {
|
|
||||||
color: inherit;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 18px;
|
|
||||||
padding: 10px 20px;
|
|
||||||
border-bottom: 2px solid;
|
|
||||||
}
|
|
||||||
.collapse-title>i {
|
|
||||||
color: inherit;
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
.ant-collapse-content-box>li {
|
|
||||||
padding: 12px 0 0 !important;
|
|
||||||
}
|
|
||||||
.ant-list-item>li {
|
.ant-list-item>li {
|
||||||
padding: 10px 20px !important;
|
padding: 10px 20px !important;
|
||||||
}
|
}
|
||||||
|
.ant-collapse-content-box .ant-alert {
|
||||||
|
margin-block-end: 12px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<body>
|
<body>
|
||||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
|
||||||
{{ template "commonSider" . }}
|
<a-sidebar></a-sidebar>
|
||||||
<a-layout id="content-layout">
|
<a-layout id="content-layout">
|
||||||
<a-layout-content>
|
<a-layout-content>
|
||||||
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
<a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
|
||||||
<transition name="list" appear>
|
<transition name="list" appear>
|
||||||
<a-alert type="error" v-if="showAlert" style="margin-bottom: 10px"
|
<a-alert type="error" v-if="showAlert" :style="{ marginBottom: '10px' }"
|
||||||
message='{{ i18n "secAlertTitle" }}'
|
message='{{ i18n "secAlertTitle" }}'
|
||||||
color="red"
|
color="red"
|
||||||
description='{{ i18n "secAlertSsl" }}'
|
description='{{ i18n "secAlertSsl" }}'
|
||||||
@@ -73,17 +61,17 @@
|
|||||||
</a-alert>
|
</a-alert>
|
||||||
</transition>
|
</transition>
|
||||||
<a-space direction="vertical">
|
<a-space direction="vertical">
|
||||||
<a-card hoverable style="margin-bottom: .5rem;">
|
<a-card hoverable :style="{ marginBottom: '.5rem' }">
|
||||||
<a-row style="display: flex; flex-wrap: wrap; align-items: center;">
|
<a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
|
||||||
<a-col :xs="24" :sm="10" style="padding: 4px;">
|
<a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
|
||||||
<a-space direction="horizontal">
|
<a-space direction="horizontal">
|
||||||
<a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button>
|
<a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button>
|
||||||
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button>
|
<a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button>
|
||||||
<a-popover v-if="restartResult"
|
<a-popover v-if="restartResult"
|
||||||
:overlay-class-name="themeSwitcher.currentTheme">
|
:overlay-class-name="themeSwitcher.currentTheme">
|
||||||
<span slot="title" style="font-size: 12pt">Error in running xray-core</span>
|
<span slot="title">{{ i18n "pages.index.xrayErrorPopoverTitle" }}</span>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p style="max-width: 400px" v-for="line in restartResult.split('\n')">[[ line ]]</p>
|
<span :style="{ maxWidth: '400px' }" v-for="line in restartResult.split('\n')">[[ line ]]</span>
|
||||||
</template>
|
</template>
|
||||||
<a-icon type="question-circle"></a-icon>
|
<a-icon type="question-circle"></a-icon>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
@@ -93,7 +81,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top>
|
<a-back-top :target="() => document.getElementById('content-layout')" visibility-height="200"></a-back-top>
|
||||||
<a-alert type="warning" style="float: right; width: fit-content" message='{{ i18n "pages.settings.infoDesc" }}' show-icon>
|
<a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }" message='{{ i18n "pages.settings.infoDesc" }}' show-icon>
|
||||||
</a-alert>
|
</a-alert>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -103,683 +91,26 @@
|
|||||||
<a-tabs class="ant-card-dark-box-nohover" default-active-key="1"
|
<a-tabs class="ant-card-dark-box-nohover" default-active-key="1"
|
||||||
@change="(activeKey) => { this.changePage(activeKey); }"
|
@change="(activeKey) => { this.changePage(activeKey); }"
|
||||||
:class="themeSwitcher.currentTheme">
|
:class="themeSwitcher.currentTheme">
|
||||||
<a-tab-pane key="tpl-basic" tab='{{ i18n "pages.xray.basicTemplate"}}' style="padding-top: 20px;">
|
<a-tab-pane key="tpl-basic" tab='{{ i18n "pages.xray.basicTemplate"}}' :style="{ paddingTop: '20px' }">
|
||||||
<a-collapse>
|
{{ template "settings/xray/basics" . }}
|
||||||
<a-collapse-panel header='{{ i18n "pages.xray.generalConfigs"}}'>
|
|
||||||
<a-row :xs="24" :sm="24" :lg="12">
|
|
||||||
<a-alert type="warning" style="text-align: center;">
|
|
||||||
<template slot="message">
|
|
||||||
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
|
||||||
{{ i18n "pages.xray.generalConfigsDesc" }}
|
|
||||||
</template>
|
|
||||||
</a-alert>
|
|
||||||
</a-row>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.FreedomStrategy" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.FreedomStrategyDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-select v-model="freedomStrategy" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
|
|
||||||
<a-select-option v-for="s in OutboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.RoutingStrategy" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.RoutingStrategyDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-select v-model="routingStrategy" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
|
|
||||||
<a-select-option v-for="s in routingDomainStrategies" :value="s">[[ s ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
</a-collapse-panel>
|
|
||||||
<a-collapse-panel header='{{ i18n "pages.xray.statistics" }}'>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.statsInboundUplink" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.statsInboundUplinkDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="statsInboundUplink"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.statsInboundDownlink" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.statsInboundDownlinkDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="statsInboundDownlink"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.statsOutboundUplink" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.statsOutboundUplinkDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="statsOutboundUplink"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.statsOutboundDownlink" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.statsOutboundDownlinkDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="statsOutboundDownlink"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-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;">
|
|
||||||
<template slot="message">
|
|
||||||
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
|
||||||
{{ i18n "pages.xray.logConfigsDesc" }}
|
|
||||||
</template>
|
|
||||||
</a-alert>
|
|
||||||
</a-row>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.logLevel" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.logLevelDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-select v-model="logLevel" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
|
|
||||||
<a-select-option v-for="s in log.loglevel" :value="s">[[ s ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.accessLog" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.accessLogDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-select v-model="accessLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
|
|
||||||
<a-select-option value=''>Empty</a-select-option>
|
|
||||||
<a-select-option v-for="s in log.access" :value="s">[[ s ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.errorLog" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.errorLogDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-select v-model="errorLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
|
|
||||||
<a-select-option value=''>Empty</a-select-option>
|
|
||||||
<a-select-option v-for="s in log.error" :value="s">[[ s ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.maskAddress" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.maskAddressDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-select v-model="maskAddressLog" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100%">
|
|
||||||
<a-select-option value=''>Empty</a-select-option>
|
|
||||||
<a-select-option v-for="s in log.maskAddress" :value="s">[[ s ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.dnsLog"}}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.dnsLogDesc"}}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="dnslog"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
</a-collapse-panel>
|
|
||||||
<a-collapse-panel header='{{ i18n "pages.xray.blockConfigs"}}'>
|
|
||||||
<a-row :xs="24" :sm="24" :lg="12">
|
|
||||||
<a-alert type="warning" style="text-align: center;">
|
|
||||||
<template slot="message">
|
|
||||||
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
|
||||||
{{ i18n "pages.xray.blockConfigsDesc" }}
|
|
||||||
</template>
|
|
||||||
</a-alert>
|
|
||||||
</a-row>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.Torrent"}}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.TorrentDesc"}}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="torrentSettings"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.Family"}}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.FamilyDesc"}}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="familyProtectSettings"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
</a-collapse-panel>
|
|
||||||
<a-collapse-panel header='{{ i18n "pages.xray.basicRouting"}}'>
|
|
||||||
<a-row :xs="24" :sm="24" :lg="12">
|
|
||||||
<a-alert type="warning" style="text-align: center;">
|
|
||||||
<template slot="message">
|
|
||||||
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
|
||||||
{{ i18n "pages.xray.blockConnectionsConfigsDesc" }}
|
|
||||||
</template>
|
|
||||||
</a-alert>
|
|
||||||
</a-row>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.blockips" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-select mode="tags" v-model="blockedIPs" style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.IPsOptions">[[ p.label ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.blockdomains" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-select mode="tags" v-model="blockedDomains" style="width: 100%" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.BlockDomainsOptions">[[ p.label ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-row :xs="24" :sm="24" :lg="12">
|
|
||||||
<a-alert type="warning" style="text-align: center; margin-top: 20px;">
|
|
||||||
<template slot="message">
|
|
||||||
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
|
||||||
{{ i18n "pages.xray.directConnectionsConfigsDesc" }}
|
|
||||||
</template>
|
|
||||||
</a-alert>
|
|
||||||
</a-row>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.directips" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<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 settingsData.IPsOptions">[[ p.label ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.directdomains" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<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 settingsData.DomainsOptions">[[ p.label ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-row :xs="24" :sm="24" :lg="12">
|
|
||||||
<a-alert type="warning" style="text-align: center; margin-top: 20px;">
|
|
||||||
<template slot="message">
|
|
||||||
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
|
||||||
{{ i18n "pages.xray.ipv4RoutingDesc" }}
|
|
||||||
</template>
|
|
||||||
</a-alert>
|
|
||||||
</a-row>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.ipv4Routing" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-select mode="tags" style="width: 100%" v-model="ipv4Domains" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.ServicesOptions">[[ p.label ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-row :xs="24" :sm="24" :lg="12">
|
|
||||||
<a-alert type="warning" style="text-align: center; margin-top: 20px;">
|
|
||||||
<template slot="message">
|
|
||||||
<a-icon type="exclamation-circle" theme="filled" style="color: #FFA031"></a-icon>
|
|
||||||
{{ i18n "pages.xray.warpRoutingDesc" }}
|
|
||||||
</template>
|
|
||||||
</a-alert>
|
|
||||||
</a-row>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.warpRouting" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<template v-if="WarpExist">
|
|
||||||
<a-select mode="tags" style="width: 100%" v-model="warpDomains" :dropdown-class-name="themeSwitcher.currentTheme">
|
|
||||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.ServicesOptions">[[ p.label ]]</a-select-option>
|
|
||||||
</a-select>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<a-button type="primary" icon="cloud" @click="showWarp()">WARP</a-button>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
</a-collapse-panel>
|
|
||||||
<a-collapse-panel header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
|
|
||||||
<a-space direction="horizontal" style="padding: 0 20px">
|
|
||||||
<a-button type="danger" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
|
|
||||||
</a-space>
|
|
||||||
</a-collapse-panel>
|
|
||||||
</a-collapse>
|
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-routing" tab='{{ i18n "pages.xray.Routings"}}' style="padding-top: 20px;">
|
<a-tab-pane key="tpl-routing" tab='{{ i18n "pages.xray.Routings"}}' :style="{ paddingTop: '20px' }">
|
||||||
<a-space direction="vertical" size="middle">
|
{{ template "settings/xray/routing" . }}
|
||||||
<a-button type="primary" icon="plus" @click="addRule">{{ i18n "pages.xray.rules.add" }}</a-button>
|
|
||||||
<a-table-sortable :columns="isMobile ? rulesMobileColumns : rulesColumns" bordered
|
|
||||||
:row-key="r => r.key"
|
|
||||||
:data-source="routingRuleData"
|
|
||||||
:scroll="isMobile ? {} : { x: 1000 }"
|
|
||||||
:pagination="false"
|
|
||||||
:indent-size="0"
|
|
||||||
v-on:onSort="replaceRule">
|
|
||||||
<template slot="action" slot-scope="text, rule, index">
|
|
||||||
<a-table-sort-trigger :item-index="index"></a-table-sort-trigger>
|
|
||||||
<span class="ant-table-row-index"> [[ index+1 ]] </span>
|
|
||||||
<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 v-if="index>0" @click="replaceRule(index,0)">
|
|
||||||
<a-icon type="vertical-align-top"></a-icon>
|
|
||||||
{{ i18n "pages.xray.rules.first"}}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item v-if="index>0" @click="replaceRule(index,index-1)">
|
|
||||||
<a-icon type="arrow-up"></a-icon>
|
|
||||||
{{ i18n "pages.xray.rules.up"}}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item v-if="index<routingRuleData.length-1" @click="replaceRule(index,index+1)">
|
|
||||||
<a-icon type="arrow-down"></a-icon>
|
|
||||||
{{ i18n "pages.xray.rules.down"}}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item v-if="index<routingRuleData.length-1" @click="replaceRule(index,routingRuleData.length-1)">
|
|
||||||
<a-icon type="vertical-align-bottom"></a-icon>
|
|
||||||
{{ i18n "pages.xray.rules.last"}}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item @click="editRule(index)">
|
|
||||||
<a-icon type="edit"></a-icon>
|
|
||||||
{{ i18n "edit" }}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item @click="deleteRule(index)">
|
|
||||||
<span style="color: #FF4D4F">
|
|
||||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
|
||||||
</span>
|
|
||||||
</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</a-dropdown>
|
|
||||||
</template>
|
|
||||||
<template slot="inbound" slot-scope="text, rule, index">
|
|
||||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
|
||||||
<template slot="content">
|
|
||||||
<p v-if="rule.inboundTag">Inbound Tag: [[ rule.inboundTag ]]</p>
|
|
||||||
<p v-if="rule.user">User email: [[ rule.user ]]</p>
|
|
||||||
</template>
|
|
||||||
[[ [rule.inboundTag,rule.user].join('\n') ]]
|
|
||||||
</a-popover>
|
|
||||||
</template>
|
|
||||||
<template slot="outbound" slot-scope="text, rule, index">
|
|
||||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
|
||||||
<template slot="content">
|
|
||||||
<p v-if="rule.outboundTag">Outbound Tag: [[ rule.outboundTag ]]</p>
|
|
||||||
</template>
|
|
||||||
[[ rule.outboundTag ]]
|
|
||||||
</a-popover>
|
|
||||||
</template>
|
|
||||||
<template slot="balancer" slot-scope="text, rule, index">
|
|
||||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
|
||||||
<template slot="content">
|
|
||||||
<p v-if="rule.balancerTag">Balancer Tag: [[ rule.balancerTag ]]</p>
|
|
||||||
</template>
|
|
||||||
[[ rule.balancerTag ]]
|
|
||||||
</a-popover>
|
|
||||||
</template>
|
|
||||||
<template slot="info" slot-scope="text, rule, index">
|
|
||||||
<a-popover placement="bottomRight"
|
|
||||||
v-if="(rule.source+rule.sourcePort+rule.network+rule.protocol+rule.attrs+rule.ip+rule.domain+rule.port).length>0"
|
|
||||||
:overlay-class-name="themeSwitcher.currentTheme" trigger="click">
|
|
||||||
<template slot="content">
|
|
||||||
<table cellpadding="2" style="max-width: 300px;">
|
|
||||||
<tr v-if="rule.source">
|
|
||||||
<td>Source</td>
|
|
||||||
<td><a-tag color="blue" v-for="r in rule.source.split(',')">[[ r ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="rule.sourcePort">
|
|
||||||
<td>Source Port</td>
|
|
||||||
<td><a-tag color="green" v-for="r in rule.sourcePort.split(',')">[[ r ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="rule.network">
|
|
||||||
<td>Network</td>
|
|
||||||
<td><a-tag color="blue" v-for="r in rule.network.split(',')">[[ r ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="rule.protocol">
|
|
||||||
<td>Protocol</td>
|
|
||||||
<td><a-tag color="green" v-for="r in rule.protocol.split(',')">[[ r ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="rule.attrs">
|
|
||||||
<td>Attrs</td>
|
|
||||||
<td><a-tag color="blue" v-for="r in rule.attrs.split(',')">[[ r ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="rule.ip">
|
|
||||||
<td>IP</td>
|
|
||||||
<td><a-tag color="green" v-for="r in rule.ip.split(',')">[[ r ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="rule.domain">
|
|
||||||
<td>Domain</td>
|
|
||||||
<td><a-tag color="blue" v-for="r in rule.domain.split(',')">[[ r ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="rule.port">
|
|
||||||
<td>Port</td>
|
|
||||||
<td><a-tag color="green" v-for="r in rule.port.split(',')">[[ r ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
<tr v-if="rule.balancerTag">
|
|
||||||
<td>Balancer Tag</td>
|
|
||||||
<td><a-tag color="blue">[[ rule.balancerTag ]]</a-tag></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</template>
|
|
||||||
<a-button shape="round" size="small" style="font-size: 14px; padding: 0 10px;">
|
|
||||||
<a-icon type="info"></a-icon>
|
|
||||||
</a-button>
|
|
||||||
</a-popover>
|
|
||||||
</template>
|
|
||||||
</a-table-sortable>
|
|
||||||
</a-space>
|
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-outbound" tab='{{ i18n "pages.xray.Outbounds"}}' force-render="true">
|
<a-tab-pane key="tpl-outbound" tab='{{ i18n "pages.xray.Outbounds"}}' force-render="true">
|
||||||
<a-space direction="vertical" size="middle">
|
{{ template "settings/xray/outbounds" . }}
|
||||||
<a-row>
|
|
||||||
<a-col :xs="12" :sm="12" :lg="12">
|
|
||||||
<a-space direction="horizontal" size="small">
|
|
||||||
<a-button type="primary" icon="plus" @click="addOutbound()">
|
|
||||||
{{ i18n "pages.xray.outbound.addOutbound" }}
|
|
||||||
</a-button>
|
|
||||||
<a-button type="primary" icon="cloud" @click="showWarp()">WARP</a-button>
|
|
||||||
</a-space>
|
|
||||||
</a-col>
|
|
||||||
<a-col :xs="12" :sm="12" :lg="12" style="text-align: right;">
|
|
||||||
<a-icon type="sync" :spin="refreshing" @click="refreshOutboundTraffic()" style="margin: 0 5px;"></a-icon>
|
|
||||||
<a-popconfirm placement="topRight" @confirm="resetOutboundTraffic(-1)"
|
|
||||||
title='{{ i18n "pages.inbounds.resetTrafficContent"}}'
|
|
||||||
:overlay-class-name="themeSwitcher.currentTheme"
|
|
||||||
ok-text='{{ i18n "reset"}}'
|
|
||||||
cancel-text='{{ i18n "cancel"}}'>
|
|
||||||
<a-icon slot="icon" type="question-circle-o" :style="themeSwitcher.isDarkTheme ? 'color: #008771' : 'color: #008771'"></a-icon>
|
|
||||||
<a-icon type="retweet" style="cursor: pointer;"></a-icon>
|
|
||||||
</a-popconfirm>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
<a-table :columns="outboundColumns" bordered
|
|
||||||
:row-key="r => r.key"
|
|
||||||
:data-source="outboundData"
|
|
||||||
:scroll="isMobile ? {} : { x: 800 }"
|
|
||||||
:pagination="false"
|
|
||||||
:indent-size="0">
|
|
||||||
<template slot="action" slot-scope="text, outbound, 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 v-if="index>0" @click="setFirstOutbound(index)">
|
|
||||||
<a-icon type="vertical-align-top"></a-icon>
|
|
||||||
{{ i18n "pages.xray.rules.first"}}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item @click="editOutbound(index)">
|
|
||||||
<a-icon type="edit"></a-icon>
|
|
||||||
{{ i18n "edit" }}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item @click="resetOutboundTraffic(index)">
|
|
||||||
<span>
|
|
||||||
<a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic"}}
|
|
||||||
</span>
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item @click="deleteOutbound(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="text, outbound, index">
|
|
||||||
<p style="margin: 0 5px;" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
|
|
||||||
</template>
|
|
||||||
<template slot="protocol" slot-scope="text, outbound, index">
|
|
||||||
<a-tag style="margin:0;" color="purple">[[ outbound.protocol ]]</a-tag>
|
|
||||||
<template v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
|
|
||||||
<a-tag style="margin:0;" color="blue">[[ outbound.streamSettings.network ]]</a-tag>
|
|
||||||
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
|
|
||||||
<a-tag style="margin:0;" v-if="outbound.streamSettings.security=='reality'" color="green">reality</a-tag>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
<template slot="traffic" slot-scope="text, outbound, index">
|
|
||||||
<a-tag color="green">[[ findOutboundTraffic(outbound) ]]</a-tag>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
</a-space>
|
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-reverse" tab='{{ i18n "pages.xray.outbound.reverse"}}' style="padding-top: 20px;" force-render="true">
|
<a-tab-pane key="tpl-reverse" tab='{{ i18n "pages.xray.outbound.reverse"}}' :style="{ paddingTop: '20px' }" force-render="true">
|
||||||
<template v-if="reverseData.length > 0">
|
{{ template "settings/xray/reverse" . }}
|
||||||
<a-space direction="vertical" size="middle">
|
|
||||||
<a-button type="primary" icon="plus" @click="addReverse()">
|
|
||||||
{{ i18n "pages.xray.outbound.addReverse" }}
|
|
||||||
</a-button>
|
|
||||||
<a-table :columns="reverseColumns" bordered
|
|
||||||
:row-key="r => r.key"
|
|
||||||
:data-source="reverseData"
|
|
||||||
:scroll="isMobile ? {} : { x: 200 }"
|
|
||||||
:pagination="false"
|
|
||||||
:indent-size="0">
|
|
||||||
<template slot="action" slot-scope="text, reverse, 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="editReverse(index)">
|
|
||||||
<a-icon type="edit"></a-icon>
|
|
||||||
{{ i18n "edit" }}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item @click="deleteReverse(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-space>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<a-empty description='{{ i18n "emptyReverseDesc" }}' style="margin: 10px;">
|
|
||||||
<a-button type="primary" icon="plus" @click="addReverse()" style="margin-top: 10px;">
|
|
||||||
{{ i18n "pages.xray.outbound.addReverse" }}
|
|
||||||
</a-button>
|
|
||||||
</a-empty>
|
|
||||||
</template>
|
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-balancer" tab='{{ i18n "pages.xray.Balancers"}}' style="padding-top: 20px;" force-render="true">
|
<a-tab-pane key="tpl-balancer" tab='{{ i18n "pages.xray.Balancers"}}' :style="{ paddingTop: '20px' }" force-render="true">
|
||||||
<template v-if="balancersData.length > 0">
|
{{ template "settings/xray/balancers" . }}
|
||||||
<a-space direction="vertical" size="middle">
|
|
||||||
<a-button type="primary" icon="plus" @click="addBalancer()">
|
|
||||||
{{ i18n "pages.xray.balancer.addBalancer"}}
|
|
||||||
</a-button>
|
|
||||||
<a-table :columns="balancerColumns" bordered
|
|
||||||
:row-key="r => r.key"
|
|
||||||
:data-source="balancersData"
|
|
||||||
:scroll="isMobile ? {} : { x: 200 }"
|
|
||||||
:pagination="false"
|
|
||||||
:indent-size="0">
|
|
||||||
<template slot="action" slot-scope="text, balancer, 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="editBalancer(index)">
|
|
||||||
<a-icon type="edit"></a-icon>
|
|
||||||
{{ i18n "edit" }}
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item @click="deleteBalancer(index)">
|
|
||||||
<span style="color: #FF4D4F">
|
|
||||||
<a-icon type="delete"></a-icon> {{ i18n "delete"}}
|
|
||||||
</span>
|
|
||||||
</a-menu-item>
|
|
||||||
</a-menu>
|
|
||||||
</a-dropdown>
|
|
||||||
</template>
|
|
||||||
<template slot="strategy" slot-scope="text, balancer, index">
|
|
||||||
<a-tag style="margin:0;" v-if="balancer.strategy=='random'" color="purple">Random</a-tag>
|
|
||||||
<a-tag style="margin:0;" v-if="balancer.strategy=='roundRobin'" color="green">Round Robin</a-tag>
|
|
||||||
<a-tag style="margin:0;" v-if="balancer.strategy=='leastLoad'" color="green">Least Load</a-tag>
|
|
||||||
<a-tag style="margin:0;" v-if="balancer.strategy=='leastPing'" color="green">Least Ping</a-tag>
|
|
||||||
</template>
|
|
||||||
<template slot="selector" slot-scope="text, balancer, index">
|
|
||||||
<a-tag class="info-large-tag" style="margin:1;" v-for="sel in balancer.selector">[[ sel ]]</a-tag>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
<a-radio-group
|
|
||||||
v-if="observatoryEnable || burstObservatoryEnable"
|
|
||||||
v-model="obsSettings"
|
|
||||||
@change="changeObsCode"
|
|
||||||
button-style="solid"
|
|
||||||
:size="isMobile ? 'small' : ''">
|
|
||||||
<a-radio-button value="observatory" v-if="observatoryEnable">Observatory</a-radio-button>
|
|
||||||
<a-radio-button value="burstObservatory" v-if="burstObservatoryEnable">Burst Observatory</a-radio-button>
|
|
||||||
</a-radio-group>
|
|
||||||
<textarea style="position:absolute; left: -800px;" id="obsSetting"></textarea>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<a-empty description='{{ i18n "emptyBalancersDesc" }}' style="margin: 10px;">
|
|
||||||
<a-button type="primary" icon="plus" @click="addBalancer()" style="margin-top: 10px;">
|
|
||||||
{{ i18n "pages.xray.balancer.addBalancer"}}
|
|
||||||
</a-button>
|
|
||||||
</a-empty>
|
|
||||||
</template>
|
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="tpl-dns" tab='DNS' style="padding-top: 20px;" force-render="true">
|
<a-tab-pane key="tpl-dns" tab='DNS' :style="{ paddingTop: '20px' }" force-render="true">
|
||||||
<a-collapse>
|
{{ template "settings/xray/dns" . }}
|
||||||
<a-collapse-panel header='{{ i18n "pages.xray.generalConfigs"}}'>
|
</a-tab-pane>
|
||||||
<a-setting-list-item paddings="small">
|
<a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' :style="{ paddingTop: '20px' }" force-render="true">
|
||||||
<template #title>{{ i18n "pages.xray.dns.enable" }}</template>
|
{{ template "settings/xray/advanced" . }}
|
||||||
<template #description>{{ i18n "pages.xray.dns.enableDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="enableDNS"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<template v-if="enableDNS">
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.dns.tag" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.dns.tagDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-input type="text" v-model="dnsTag"></a-input>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.dns.clientIp" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.dns.clientIpDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-input type="text" v-model="dnsClientIp"></a-input>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.dns.strategy" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.dns.strategyDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<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>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.dns.disableCache" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.dns.disableCacheDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="dnsDisableCache"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.dns.disableFallback" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.dns.disableFallbackDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="dnsDisableFallback"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
<a-setting-list-item paddings="small">
|
|
||||||
<template #title>{{ i18n "pages.xray.dns.disableFallbackIfMatch" }}</template>
|
|
||||||
<template #description>{{ i18n "pages.xray.dns.disableFallbackIfMatchDesc" }}</template>
|
|
||||||
<template #control>
|
|
||||||
<a-switch v-model="dnsDisableFallbackIfMatch"></a-switch>
|
|
||||||
</template>
|
|
||||||
</a-setting-list-item>
|
|
||||||
</template>
|
|
||||||
</a-collapse-panel>
|
|
||||||
<template v-if="enableDNS">
|
|
||||||
<a-collapse-panel header='DNS'>
|
|
||||||
<template v-if="dnsServers.length > 0">
|
|
||||||
<a-space direction="vertical" size="middle">
|
|
||||||
<a-button type="primary" icon="plus" @click="addDNSServer()">{{ i18n
|
|
||||||
"pages.xray.dns.add" }}</a-button>
|
|
||||||
<a-table :columns="dnsColumns" bordered :row-key="r => r.key"
|
|
||||||
:data-source="dnsServers" :scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0">
|
|
||||||
<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-space>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<a-empty description='{{ i18n "emptyDnsDesc" }}' style="margin: 10px;">
|
|
||||||
<a-button type="primary" icon="plus" @click="addDNSServer()" style="margin-top: 10px;">{{ i18n "pages.xray.dns.add" }}</a-button>
|
|
||||||
</a-empty>
|
|
||||||
</template>
|
|
||||||
</a-collapse-panel>
|
|
||||||
<a-collapse-panel header='Fake DNS'>
|
|
||||||
<template v-if="fakeDns && fakeDns.length > 0">
|
|
||||||
<a-space direction="vertical" size="middle">
|
|
||||||
<a-button type="primary" icon="plus" @click="addFakedns()">{{ i18n "pages.xray.fakedns.add" }}</a-button>
|
|
||||||
<a-table :columns="fakednsColumns" bordered :row-key="r => r.key"
|
|
||||||
:data-source="fakeDns" :scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0">
|
|
||||||
<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-space>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<a-empty description='{{ i18n "emptyFakeDnsDesc" }}' style="margin: 20px;">
|
|
||||||
<a-button type="primary" icon="plus" @click="addFakedns()" style="margin-top: 10px;">{{ i18n "pages.xray.fakedns.add" }}</a-button>
|
|
||||||
</a-empty>
|
|
||||||
</template>
|
|
||||||
</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-space direction="vertical" size="small">
|
|
||||||
<a-list-item-meta title='{{ i18n "pages.xray.Template"}}' description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta>
|
|
||||||
<a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;" :size="isMobile ? 'small' : ''">
|
|
||||||
<a-radio-button value="xraySetting">{{ i18n "pages.xray.completeTemplate"}}</a-radio-button>
|
|
||||||
<a-radio-button value="inboundSettings">{{ i18n "pages.xray.Inbounds" }}</a-radio-button>
|
|
||||||
<a-radio-button value="outboundSettings">{{ i18n "pages.xray.Outbounds" }}</a-radio-button>
|
|
||||||
<a-radio-button value="routingRuleSettings">{{ i18n "pages.xray.Routings" }}</a-radio-button>
|
|
||||||
</a-radio-group>
|
|
||||||
<textarea style="position:absolute; left: -800px;" id="xraySetting"></textarea>
|
|
||||||
</a-space>
|
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
</a-space>
|
</a-space>
|
||||||
@@ -788,16 +119,17 @@
|
|||||||
</a-layout>
|
</a-layout>
|
||||||
</a-layout>
|
</a-layout>
|
||||||
{{template "js" .}}
|
{{template "js" .}}
|
||||||
|
{{template "component/aSidebar" .}}
|
||||||
{{template "component/aThemeSwitch" .}}
|
{{template "component/aThemeSwitch" .}}
|
||||||
{{template "component/aTableSortable" .}}
|
{{template "component/aTableSortable" .}}
|
||||||
{{template "component/aSettingListItem" .}}
|
{{template "component/aSettingListItem" .}}
|
||||||
{{template "ruleModal"}}
|
{{template "modals/ruleModal"}}
|
||||||
{{template "outModal"}}
|
{{template "modals/outModal"}}
|
||||||
{{template "reverseModal"}}
|
{{template "modals/reverseModal"}}
|
||||||
{{template "balancerModal"}}
|
{{template "modals/balancerModal"}}
|
||||||
{{template "dnsModal"}}
|
{{template "modals/dnsModal"}}
|
||||||
{{template "fakednsModal"}}
|
{{template "modals/fakednsModal"}}
|
||||||
{{template "warpModal"}}
|
{{template "modals/warpModal"}}
|
||||||
<script>
|
<script>
|
||||||
const rulesColumns = [
|
const rulesColumns = [
|
||||||
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
|
{ title: "#", align: 'center', width: 15, scopedSlots: { customRender: 'action' } },
|
||||||
@@ -863,9 +195,9 @@
|
|||||||
|
|
||||||
const app = new Vue({
|
const app = new Vue({
|
||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
|
mixins: [MediaQueryMixin],
|
||||||
el: '#app',
|
el: '#app',
|
||||||
data: {
|
data: {
|
||||||
siderDrawer,
|
|
||||||
themeSwitcher,
|
themeSwitcher,
|
||||||
isDarkTheme: themeSwitcher.isDarkTheme,
|
isDarkTheme: themeSwitcher.isDarkTheme,
|
||||||
spinning: false,
|
spinning: false,
|
||||||
@@ -877,7 +209,6 @@
|
|||||||
refreshing: false,
|
refreshing: false,
|
||||||
restartResult: '',
|
restartResult: '',
|
||||||
showAlert: false,
|
showAlert: false,
|
||||||
isMobile: window.innerWidth <= 768,
|
|
||||||
advSettings: 'xraySetting',
|
advSettings: 'xraySetting',
|
||||||
obsSettings: '',
|
obsSettings: '',
|
||||||
cm: null,
|
cm: null,
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
{{define "menuItems"}}
|
|
||||||
<a-menu-item key="{{ .base_path }}panel/">
|
|
||||||
<a-icon type="dashboard"></a-icon>
|
|
||||||
<span>{{ i18n "menu.dashboard"}}</span>
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item key="{{ .base_path }}panel/inbounds">
|
|
||||||
<a-icon type="user"></a-icon>
|
|
||||||
<span>{{ i18n "menu.inbounds"}}</span>
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item key="{{ .base_path }}panel/settings">
|
|
||||||
<a-icon type="setting"></a-icon>
|
|
||||||
<span>{{ i18n "menu.settings"}}</span>
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item key="{{ .base_path }}panel/xray">
|
|
||||||
<a-icon type="tool"></a-icon>
|
|
||||||
<span>{{ i18n "menu.xray"}}</span>
|
|
||||||
</a-menu-item>
|
|
||||||
<a-menu-item key="{{ .base_path }}logout">
|
|
||||||
<a-icon type="logout"></a-icon>
|
|
||||||
<span>{{ i18n "menu.logout"}}</span>
|
|
||||||
</a-menu-item>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
|
|
||||||
{{define "commonSider"}}
|
|
||||||
<a-layout-sider :theme="themeSwitcher.currentTheme" id="sider" collapsible breakpoint="md">
|
|
||||||
<a-theme-switch></a-theme-switch>
|
|
||||||
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="['{{ .request_uri }}']" @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key">
|
|
||||||
{{template "menuItems" .}}
|
|
||||||
</a-menu>
|
|
||||||
</a-layout-sider>
|
|
||||||
<a-drawer id="sider-drawer" placement="left" :closable="false" @close="siderDrawer.close()" :visible="siderDrawer.visible" :wrap-class-name="themeSwitcher.currentTheme" :wrap-style="{ padding: 0 }">
|
|
||||||
<div class="drawer-handle" @click="siderDrawer.change()" slot="handle">
|
|
||||||
<a-icon :type="siderDrawer.visible ? 'close' : 'menu-fold'"></a-icon>
|
|
||||||
</div>
|
|
||||||
<a-theme-switch></a-theme-switch>
|
|
||||||
<a-menu :theme="themeSwitcher.currentTheme" mode="inline" :selected-keys="['{{ .request_uri }}']" @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key">
|
|
||||||
{{template "menuItems" .}}
|
|
||||||
</a-menu>
|
|
||||||
</a-drawer>
|
|
||||||
<script>
|
|
||||||
const siderDrawer = {
|
|
||||||
visible: false,
|
|
||||||
show() {
|
|
||||||
this.visible = true;
|
|
||||||
},
|
|
||||||
close() {
|
|
||||||
this.visible = false;
|
|
||||||
},
|
|
||||||
change() {
|
|
||||||
this.visible = !this.visible;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
{{end}}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
{{define "component/passwordInput"}}
|
|
||||||
<template>
|
|
||||||
<a-input :value="value" :type="showPassword ? 'text' : 'password'" :placeholder="placeholder"
|
|
||||||
:autocomplete="autocomplete" :name="name" @input="$emit('input', $event.target.value)">
|
|
||||||
<template v-if="icon" #prefix>
|
|
||||||
<a-icon :type="icon" style="font-size: 16px;" />
|
|
||||||
</template>
|
|
||||||
<template #addonAfter>
|
|
||||||
<a-icon :type="showPassword ? 'eye-invisible' : 'eye'" @click="toggleShowPassword" style="font-size: 16px;" />
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
</template>
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{define "component/aPasswordInput"}}
|
|
||||||
<script>
|
|
||||||
Vue.component('a-password-input', {
|
|
||||||
props: {
|
|
||||||
'title': {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
'value': {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
'placeholder': {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
'autocomplete': {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
'name': {
|
|
||||||
type: String,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
'icon': {
|
|
||||||
type: undefined,
|
|
||||||
required: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `{{template "component/passwordInput"}}`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showPassword: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleShowPassword() {
|
|
||||||
this.showPassword = !this.showPassword;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
{{end}}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
673
web/translation/translate.ar_EG.toml
Normal file
673
web/translation/translate.ar_EG.toml
Normal file
@@ -0,0 +1,673 @@
|
|||||||
|
"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" = "المسمع IP"
|
||||||
|
"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" = "مسار URI الافتراضي لاشتراك JSON مش آمن. ياريت تضبط مسار URI معقد."
|
||||||
|
"emptyDnsDesc" = "مفيش سيرفر DNS مضاف."
|
||||||
|
"emptyFakeDnsDesc" = "مفيش سيرفر Fake DNS مضاف."
|
||||||
|
"emptyBalancersDesc" = "مفيش موازن تحميل مضاف."
|
||||||
|
"emptyReverseDesc" = "مفيش بروكسي عكسي مضاف."
|
||||||
|
|
||||||
|
[menu]
|
||||||
|
"theme" = "الثيم"
|
||||||
|
"dark" = "داكن"
|
||||||
|
"ultraDark" = "داكن جدًا"
|
||||||
|
"dashboard" = "نظرة عامة"
|
||||||
|
"inbounds" = "الإدخالات"
|
||||||
|
"settings" = "إعدادات البانل"
|
||||||
|
"xray" = "إعدادات Xray"
|
||||||
|
"logout" = "تسجيل خروج"
|
||||||
|
"link" = "إدارة"
|
||||||
|
|
||||||
|
[pages.login]
|
||||||
|
"hello" = "أهلا"
|
||||||
|
"title" = "أهلاً وسهلاً"
|
||||||
|
"loginAgain" = "انتهت صلاحية الجلسة، سجل دخول تاني"
|
||||||
|
|
||||||
|
[pages.login.toasts]
|
||||||
|
"invalidFormData" = "تنسيق البيانات المدخلة مش صحيح."
|
||||||
|
"emptyUsername" = "اسم المستخدم مطلوب"
|
||||||
|
"emptyPassword" = "الباسورد مطلوب"
|
||||||
|
"wrongUsernameOrPassword" = "اسم المستخدم أو الباسورد أو السر مش صحيح."
|
||||||
|
"successLogin" = "تسجيل دخول ناجح"
|
||||||
|
|
||||||
|
[pages.index]
|
||||||
|
"title" = "نظرة عامة"
|
||||||
|
"cpu" = "المعالج"
|
||||||
|
"logicalProcessors" = "المعالجات المنطقية"
|
||||||
|
"frequency" = "التردد"
|
||||||
|
"swap" = "Swap"
|
||||||
|
"storage" = "تخزين"
|
||||||
|
"memory" = "رام"
|
||||||
|
"threads" = "خيوط المعالجة"
|
||||||
|
"xrayStatus" = "Xray"
|
||||||
|
"stopXray" = "إيقاف"
|
||||||
|
"restartXray" = "إعادة تشغيل"
|
||||||
|
"xraySwitch" = "النسخة"
|
||||||
|
"xraySwitchClick" = "اختار النسخة اللي عايز تتحول لها."
|
||||||
|
"xraySwitchClickDesk" = "اختار بحذر، النسخ القديمة ممكن ما تتوافقش مع الإعدادات الحالية."
|
||||||
|
"xrayStatusUnknown" = "مش معروف"
|
||||||
|
"xrayStatusRunning" = "شغالة"
|
||||||
|
"xrayStatusStop" = "متوقفة"
|
||||||
|
"xrayStatusError" = "فيها غلطة"
|
||||||
|
"xrayErrorPopoverTitle" = "حصل خطأ أثناء تشغيل Xray"
|
||||||
|
"operationHours" = "مدة التشغيل"
|
||||||
|
"systemLoad" = "تحميل النظام"
|
||||||
|
"systemLoadDesc" = "متوسط تحميل النظام في الدقائق 1, 5, و15"
|
||||||
|
"connectionCount" = "إحصائيات الاتصال"
|
||||||
|
"ipAddresses" = "عناوين IP"
|
||||||
|
"toggleIpVisibility" = "بدل إظهار IP"
|
||||||
|
"overallSpeed" = "السرعة الكلية"
|
||||||
|
"upload" = "رفع"
|
||||||
|
"download" = "تنزيل"
|
||||||
|
"totalData" = "إجمالي البيانات"
|
||||||
|
"sent" = "مرسل"
|
||||||
|
"received" = "مستقبل"
|
||||||
|
"xraySwitchVersionDialog" = "تغيير نسخة Xray"
|
||||||
|
"xraySwitchVersionDialogDesc" = "متأكد إنك عايز تغير نسخة Xray لـ"
|
||||||
|
"dontRefresh" = "التثبيت شغال، متعملش Refresh للصفحة"
|
||||||
|
"logs" = "السجلات"
|
||||||
|
"config" = "الإعدادات"
|
||||||
|
"backup" = "نسخة احتياطية"
|
||||||
|
"backupTitle" = "نسخة احتياطية واسترجاع قاعدة البيانات"
|
||||||
|
"exportDatabase" = "اخزن نسخة"
|
||||||
|
"exportDatabaseDesc" = "اضغط عشان تحمل ملف .db يحتوي على نسخة احتياطية لقاعدة البيانات الحالية على جهازك."
|
||||||
|
"importDatabase" = "استرجاع"
|
||||||
|
"importDatabaseDesc" = "اضغط عشان تختار وتحمل ملف .db من جهازك لاسترجاع قاعدة البيانات من نسخة احتياطية."
|
||||||
|
|
||||||
|
[pages.inbounds]
|
||||||
|
"title" = "الإدخالات"
|
||||||
|
"totalDownUp" = "إجمالي المرسل/المستقبل"
|
||||||
|
"totalUsage" = "إجمالي الاستخدام"
|
||||||
|
"inboundCount" = "عدد الإدخالات"
|
||||||
|
"operate" = "القائمة"
|
||||||
|
"enable" = "مفعل"
|
||||||
|
"remark" = "ملاحظة"
|
||||||
|
"protocol" = "بروتوكول"
|
||||||
|
"port" = "بورت"
|
||||||
|
"traffic" = "الترافيك"
|
||||||
|
"details" = "تفاصيل"
|
||||||
|
"transportConfig" = "نقل"
|
||||||
|
"expireDate" = "المدة"
|
||||||
|
"resetTraffic" = "إعادة ضبط الترافيك"
|
||||||
|
"addInbound" = "أضف إدخال"
|
||||||
|
"generalActions" = "إجراءات عامة"
|
||||||
|
"autoRefresh" = "تحديث تلقائي"
|
||||||
|
"autoRefreshInterval" = "الفاصل"
|
||||||
|
"create" = "إنشاء"
|
||||||
|
"update" = "تحديث"
|
||||||
|
"modifyInbound" = "تعديل الإدخال"
|
||||||
|
"deleteInbound" = "حذف الإدخال"
|
||||||
|
"deleteInboundContent" = "متأكد إنك عايز تحذف الإدخال؟"
|
||||||
|
"deleteClient" = "حذف العميل"
|
||||||
|
"deleteClientContent" = "متأكد إنك عايز تحذف العميل؟"
|
||||||
|
"resetTrafficContent" = "متأكد إنك عايز تعيد ضبط الترافيك؟"
|
||||||
|
"copyLink" = "انسخ الرابط"
|
||||||
|
"address" = "العنوان"
|
||||||
|
"network" = "الشبكة"
|
||||||
|
"destinationPort" = "بورت الوجهة"
|
||||||
|
"targetAddress" = "عنوان الهدف"
|
||||||
|
"monitorDesc" = "سيبها فاضية لو عايز تستمع على كل الـ IPs"
|
||||||
|
"meansNoLimit" = "= غير محدود. (الوحدة: جيجابايت)"
|
||||||
|
"totalFlow" = "إجمالي التدفق"
|
||||||
|
"leaveBlankToNeverExpire" = "سيبها فاضية عشان ماتنتهيش"
|
||||||
|
"noRecommendKeepDefault" = "ننصح باستخدام الافتراضي"
|
||||||
|
"certificatePath" = "مسار الملف"
|
||||||
|
"certificateContent" = "محتوى الملف"
|
||||||
|
"publicKey" = "المفتاح العام"
|
||||||
|
"privatekey" = "المفتاح الخاص"
|
||||||
|
"clickOnQRcode" = "اضغط على كود QR للنسخ"
|
||||||
|
"client" = "عميل"
|
||||||
|
"export" = "تصدير كل الروابط"
|
||||||
|
"clone" = "استنساخ"
|
||||||
|
"cloneInbound" = "استنساخ الإدخال"
|
||||||
|
"cloneInboundContent" = "كل إعدادات الإدخال ده، غير البورت، IP الاستماع، والعملاء، هتتطبق على الاستنساخ."
|
||||||
|
"cloneInboundOk" = "استنساخ"
|
||||||
|
"resetAllTraffic" = "إعادة ضبط ترافيك كل الإدخالات"
|
||||||
|
"resetAllTrafficTitle" = "إعادة ضبط ترافيك كل الإدخالات"
|
||||||
|
"resetAllTrafficContent" = "متأكد إنك عايز تعيد ضبط الترافيك لكل الإدخالات؟"
|
||||||
|
"resetInboundClientTraffics" = "إعادة ضبط ترافيك العملاء"
|
||||||
|
"resetInboundClientTrafficTitle" = "إعادة ضبط ترافيك العملاء"
|
||||||
|
"resetInboundClientTrafficContent" = "متأكد إنك عايز تعيد ضبط ترافيك عملاء الإدخال ده؟"
|
||||||
|
"resetAllClientTraffics" = "إعادة ضبط ترافيك كل العملاء"
|
||||||
|
"resetAllClientTrafficTitle" = "إعادة ضبط ترافيك كل العملاء"
|
||||||
|
"resetAllClientTrafficContent" = "متأكد إنك عايز تعيد ضبط ترافيك كل العملاء؟"
|
||||||
|
"delDepletedClients" = "حذف العملاء اللي خلصت"
|
||||||
|
"delDepletedClientsTitle" = "حذف العملاء اللي خلصت"
|
||||||
|
"delDepletedClientsContent" = "متأكد إنك عايز تحذف كل العملاء اللي خلصت؟"
|
||||||
|
"email" = "الإيميل"
|
||||||
|
"emailDesc" = "ادخل إيميل فريد."
|
||||||
|
"IPLimit" = "تحديد IP"
|
||||||
|
"IPLimitDesc" = "بيعطل الإدخال لو العدد زاد عن القيمة المحددة. (0 = تعطيل)"
|
||||||
|
"IPLimitlog" = "سجل IP"
|
||||||
|
"IPLimitlogDesc" = "سجل تاريخ الـ IPs. (عشان تفعل الإدخال بعد التعطيل، امسح السجل)"
|
||||||
|
"IPLimitlogclear" = "امسح السجل"
|
||||||
|
"setDefaultCert" = "استخدم شهادة البانل"
|
||||||
|
"telegramDesc" = "ادخل ID شات Telegram. (استخدم '/id' في البوت) أو (@userinfobot)"
|
||||||
|
"subscriptionDesc" = "عشان تلاقي رابط الاشتراك، ادخل على 'التفاصيل'. وكمان ممكن تستخدم نفس الاسم لعدة عملاء."
|
||||||
|
"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 للبانل. (سيبه فاضي عشان يستمع على كل الـ IPs)"
|
||||||
|
"panelListeningDomain" = "دومين الاستماع"
|
||||||
|
"panelListeningDomainDesc" = "اسم الدومين للبانل. (سيبه فاضي عشان يستمع على كل الدومينات والـ IPs)"
|
||||||
|
"panelPort" = "بورت الاستماع"
|
||||||
|
"panelPortDesc" = "رقم البورت للبانل. (لازم يكون بورت فاضي)"
|
||||||
|
"publicKeyPath" = "مسار المفتاح العام"
|
||||||
|
"publicKeyPathDesc" = "مسار ملف المفتاح العام للبانل. (يبدأ بـ '/')"
|
||||||
|
"privateKeyPath" = "مسار المفتاح الخاص"
|
||||||
|
"privateKeyPathDesc" = "مسار ملف المفتاح الخاص للبانل. (يبدأ بـ '/')"
|
||||||
|
"panelUrlPath" = "مسار URI"
|
||||||
|
"panelUrlPathDesc" = "مسار URI للبانل. (يبدأ بـ '/' وبينتهي بـ '/')"
|
||||||
|
"pageSize" = "حجم الصفحة"
|
||||||
|
"pageSizeDesc" = "حدد حجم الصفحة لجدول الإدخالات. (0 = تعطيل)"
|
||||||
|
"remarkModel" = "نموذج الملاحظة وحرف الفصل"
|
||||||
|
"datepicker" = "نوع التقويم"
|
||||||
|
"datepickerPlaceholder" = "اختار التاريخ"
|
||||||
|
"datepickerDescription" = "المهام المجدولة هتشتغل بناءً على التقويم ده."
|
||||||
|
"sampleRemark" = "مثال للملاحظة"
|
||||||
|
"oldUsername" = "اسم المستخدم الحالي"
|
||||||
|
"currentPassword" = "الباسورد الحالي"
|
||||||
|
"newUsername" = "اسم المستخدم الجديد"
|
||||||
|
"newPassword" = "الباسورد الجديد"
|
||||||
|
"telegramBotEnable" = "تفعيل بوت Telegram"
|
||||||
|
"telegramBotEnableDesc" = "يفعل بوت Telegram."
|
||||||
|
"telegramToken" = "توكن Telegram"
|
||||||
|
"telegramTokenDesc" = "توكن البوت اللي جبت من '@BotFather'."
|
||||||
|
"telegramProxy" = "بروكسي SOCKS"
|
||||||
|
"telegramProxyDesc" = "يفعل بروكسي SOCKS5 للاتصال بـ Telegram. (اضبط الإعدادات حسب الدليل)"
|
||||||
|
"telegramAPIServer" = "سيرفر Telegram API"
|
||||||
|
"telegramAPIServerDesc" = "سيرفر Telegram API المستخدم. سيبه فاضي لاستخدام الافتراضي."
|
||||||
|
"telegramChatId" = "ID شات الأدمن"
|
||||||
|
"telegramChatIdDesc" = "ID شات الأدمن في Telegram. (مفصول بفواصل)(تقدر تجيبه من @userinfobot) أو (استخدم '/id' في البوت)"
|
||||||
|
"telegramNotifyTime" = "وقت الإشعار"
|
||||||
|
"telegramNotifyTimeDesc" = "وقت إشعار البوت للتقارير الدورية. (استخدم صيغة وقت crontab)"
|
||||||
|
"tgNotifyBackup" = "نسخة احتياطية لقاعدة البيانات"
|
||||||
|
"tgNotifyBackupDesc" = "ابعت ملف النسخة الاحتياطية لقاعدة البيانات مع التقرير."
|
||||||
|
"tgNotifyLogin" = "إشعار بتسجيل الدخول"
|
||||||
|
"tgNotifyLoginDesc" = "استقبل إشعار بكل محاولة تسجيل دخول للبانل مع اسم المستخدم، الـ IP، والوقت."
|
||||||
|
"sessionMaxAge" = "مدة الجلسة"
|
||||||
|
"sessionMaxAgeDesc" = "المدة اللي تفضل فيها مسجل دخول. (الوحدة: دقيقة)"
|
||||||
|
"expireTimeDiff" = "تنبيه بتاريخ الانتهاء"
|
||||||
|
"expireTimeDiffDesc" = "استقبل تنبيه قبل ما توصل لتاريخ الانتهاء بالمدة المحددة. (الوحدة: يوم)"
|
||||||
|
"trafficDiff" = "تنبيه حد الترافيك"
|
||||||
|
"trafficDiffDesc" = "استقبل تنبيه عند وصول الترافيك للحد المحدد. (الوحدة: جيجابايت)"
|
||||||
|
"tgNotifyCpu" = "تنبيه حمل المعالج"
|
||||||
|
"tgNotifyCpuDesc" = "استقبل تنبيه لو حمل المعالج عدى الحد المحدد. (الوحدة: %)"
|
||||||
|
"timeZone" = "المنطقة الزمنية"
|
||||||
|
"timeZoneDesc" = "المهام المجدولة هتشتغل بناءً على المنطقة الزمنية دي."
|
||||||
|
"subSettings" = "الاشتراك"
|
||||||
|
"subEnable" = "تفعيل خدمة الاشتراك"
|
||||||
|
"subEnableDesc" = "يفعل خدمة الاشتراك."
|
||||||
|
"subTitle" = "عنوان الاشتراك"
|
||||||
|
"subTitleDesc" = "العنوان اللي هيظهر في عميل VPN"
|
||||||
|
"subListen" = "IP الاستماع"
|
||||||
|
"subListenDesc" = "عنوان IP لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الـ IPs)"
|
||||||
|
"subPort" = "بورت الاستماع"
|
||||||
|
"subPortDesc" = "رقم البورت لخدمة الاشتراك. (لازم يكون بورت فاضي)"
|
||||||
|
"subCertPath" = "مسار المفتاح العام"
|
||||||
|
"subCertPathDesc" = "مسار ملف المفتاح العام لخدمة الاشتراك. (يبدأ بـ '/')"
|
||||||
|
"subKeyPath" = "مسار المفتاح الخاص"
|
||||||
|
"subKeyPathDesc" = "مسار ملف المفتاح الخاص لخدمة الاشتراك. (يبدأ بـ '/')"
|
||||||
|
"subPath" = "مسار URI"
|
||||||
|
"subPathDesc" = "مسار URI لخدمة الاشتراك. (يبدأ بـ '/' وبينتهي بـ '/')"
|
||||||
|
"subDomain" = "دومين الاستماع"
|
||||||
|
"subDomainDesc" = "اسم الدومين لخدمة الاشتراك. (سيبه فاضي عشان يستمع على كل الدومينات والـ IPs)"
|
||||||
|
"subUpdates" = "فترات التحديث"
|
||||||
|
"subUpdatesDesc" = "فترات تحديث رابط الاشتراك في تطبيقات العملاء. (الوحدة: ساعة)"
|
||||||
|
"subEncrypt" = "تشفير"
|
||||||
|
"subEncryptDesc" = "المحتوى اللي هيترجع من خدمة الاشتراك هيكون مشفر بـ Base64."
|
||||||
|
"subShowInfo" = "اظهر معلومات الاستخدام"
|
||||||
|
"subShowInfoDesc" = "هيظهر الترافيك المتبقي والتاريخ في تطبيقات العملاء."
|
||||||
|
"subURI" = "مسار البروكسي العكسي"
|
||||||
|
"subURIDesc" = "مسار URI لرابط الاشتراك عشان تستخدمه ورا البروكسي."
|
||||||
|
"externalTrafficInformEnable" = "تنبيه الترافيك الخارجي"
|
||||||
|
"externalTrafficInformEnableDesc" = "يبعت تنبيه لـ API خارجي مع كل تحديث للترافيك."
|
||||||
|
"externalTrafficInformURI" = "مسار تنبيه الترافيك الخارجي"
|
||||||
|
"externalTrafficInformURIDesc" = "تحديثات الترافيك هتتبعت للمسار ده."
|
||||||
|
"fragment" = "تجزئة"
|
||||||
|
"fragmentDesc" = "يفعل تجزئة لحزمة TLS hello."
|
||||||
|
"fragmentSett" = "إعدادات التجزئة"
|
||||||
|
"noisesDesc" = "يفعل التشويش."
|
||||||
|
"noisesSett" = "إعدادات التشويش"
|
||||||
|
"mux" = "MUX"
|
||||||
|
"muxDesc" = "ينقل أكثر من تيار بيانات مستقل خلال تيار بيانات واحد قائم."
|
||||||
|
"muxSett" = "إعدادات MUX"
|
||||||
|
"direct" = "اتصال مباشر"
|
||||||
|
"directDesc" = "ينشئ اتصال مباشر مع الدومينات أو نطاقات IP لدولة معينة."
|
||||||
|
"notifications" = "الإشعارات"
|
||||||
|
"certs" = "الشهادات"
|
||||||
|
"externalTraffic" = "الترافيك الخارجي"
|
||||||
|
"dateAndTime" = "التاريخ والوقت"
|
||||||
|
"proxyAndServer" = "البروكسي والسيرفر"
|
||||||
|
"intervals" = "الفترات"
|
||||||
|
"information" = "المعلومات"
|
||||||
|
"language" = "اللغة"
|
||||||
|
"telegramBotLanguage" = "لغة بوت Telegram"
|
||||||
|
|
||||||
|
[pages.xray]
|
||||||
|
"title" = "إعدادات Xray"
|
||||||
|
"save" = "احفظ"
|
||||||
|
"restart" = "أعد تشغيل Xray"
|
||||||
|
"basicTemplate" = "أساسي"
|
||||||
|
"advancedTemplate" = "متقدم"
|
||||||
|
"generalConfigs" = "إعدادات عامة"
|
||||||
|
"generalConfigsDesc" = "الخيارات دي هتحدد التعديلات العامة."
|
||||||
|
"logConfigs" = "السجلات"
|
||||||
|
"logConfigsDesc" = "السجلات ممكن تأثر على كفاءة السيرفر. ننصح بتفعيلها بحكمة لما تكون محتاجها."
|
||||||
|
"blockConfigs" = "درع الحماية"
|
||||||
|
"blockConfigsDesc" = "الخيارات دي هتحجب الترافيك بناءً على بروتوكولات ومواقع محددة."
|
||||||
|
"basicRouting" = "توجيه أساسي"
|
||||||
|
"blockConnectionsConfigsDesc" = "الخيارات دي هتحجب الترافيك بناءً على الدولة المطلوبة."
|
||||||
|
"directConnectionsConfigsDesc" = "الاتصال المباشر بيضمن إن الترافيك المعين مايمرش من سيرفر تاني."
|
||||||
|
"blockips" = "حظر IPs"
|
||||||
|
"blockdomains" = "حظر دومينات"
|
||||||
|
"directips" = "اتصالات مباشرة لـ IPs"
|
||||||
|
"directdomains" = "اتصالات مباشرة للدومينات"
|
||||||
|
"ipv4Routing" = "توجيه IPv4"
|
||||||
|
"ipv4RoutingDesc" = "الخيارات دي هتوجه الترافيك بناءً على وجهة معينة عبر IPv4."
|
||||||
|
"warpRouting" = "توجيه WARP"
|
||||||
|
"warpRoutingDesc" = "الخيارات دي هتوجه الترافيك بناءً على وجهة معينة عبر WARP."
|
||||||
|
"Template" = "قالب إعدادات Xray المتقدم"
|
||||||
|
"TemplateDesc" = "ملف إعدادات Xray النهائي هيتولد بناءً على القالب ده."
|
||||||
|
"FreedomStrategy" = "استراتيجية بروتوكول الحرية"
|
||||||
|
"FreedomStrategyDesc" = "اختار استراتيجية المخرجات للشبكة في بروتوكول الحرية."
|
||||||
|
"RoutingStrategy" = "استراتيجية التوجيه العامة"
|
||||||
|
"RoutingStrategyDesc" = "حدد استراتيجية التوجيه الإجمالية لحل كل الطلبات."
|
||||||
|
"Torrent" = "حظر بروتوكول التورنت"
|
||||||
|
"TorrentDesc" = "بيحجب بروتوكول التورنت."
|
||||||
|
"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" = "بوابة"
|
||||||
|
"link" = "رابط"
|
||||||
|
"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"
|
||||||
|
"disableCache" = "تعطيل الكاش"
|
||||||
|
"disableCacheDesc" = "بيعطل تخزين نتائج DNS مؤقتاً"
|
||||||
|
"disableFallback" = "تعطيل النسخ الاحتياطي"
|
||||||
|
"disableFallbackDesc" = "بيعطل استعلامات DNS الاحتياطية"
|
||||||
|
"disableFallbackIfMatch" = "تعطيل النسخ الاحتياطي عند التطابق"
|
||||||
|
"disableFallbackIfMatchDesc" = "بيعطل استعلامات DNS الاحتياطية لما يتحقق تطابق مع قائمة الدومينات"
|
||||||
|
"strategy" = "استراتيجية الاستعلام"
|
||||||
|
"strategyDesc" = "الاستراتيجية العامة لحل أسماء الدومين"
|
||||||
|
"add" = "أضف سيرفر"
|
||||||
|
"edit" = "عدل السيرفر"
|
||||||
|
"domains" = "الدومينات"
|
||||||
|
"expectIPs" = "العناوين المتوقعة"
|
||||||
|
|
||||||
|
[pages.xray.fakedns]
|
||||||
|
"add" = "أضف Fake DNS"
|
||||||
|
"edit" = "عدل Fake DNS"
|
||||||
|
"ipPool" = "نطاق IP Pool"
|
||||||
|
"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" = "🤖 أهلا بيك في البوت! البوت ده معمول عشان يديك بيانات معينة من البانل ويسمحلك بالتعديلات."
|
||||||
|
"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</code>\r\n\r\nعشان تدور على إيميل عميل:\r\n<code>/usage [Email]</code>\r\n\r\nعشان تدور على إدخالات (مع إحصائيات العملاء):\r\n<code>/inbound [Remark]</code>\r\n\r\nID شات Telegram:\r\n<code>/id</code>"
|
||||||
|
"helpClientCommands" = "عشان تدور على الإحصائيات، استخدم الأمر ده:\r\n\r\n<code>/usage [Email]</code>\r\n\r\nID شات Telegram:\r\n<code>/id</code>"
|
||||||
|
"restartUsage" = "\r\n\r\n<code>/restart</code>"
|
||||||
|
"restartSuccess" = "✅ العملية نجحت!"
|
||||||
|
"restartFailed" = "❗ حصل خطأ في العملية.\r\n\r\n<code>Error: {{ .Error }}</code>."
|
||||||
|
"xrayNotRunning" = "❗ Xray Core مش شغال."
|
||||||
|
|
||||||
|
[tgbot.messages]
|
||||||
|
"cpuThreshold" = "🔴 حمل المعالج {{ .Percent }}% عدى الحد المسموح ({{ .Threshold }}%)"
|
||||||
|
"selectUserFailed" = "❌ حصل خطأ في اختيار المستخدم!"
|
||||||
|
"userSaved" = "✅ حفظت بيانات مستخدم Telegram."
|
||||||
|
"loginSuccess" = "✅ تسجيل الدخول للبانل تم بنجاح.\r\n"
|
||||||
|
"loginFailed" = "❗️فشل محاولة تسجيل الدخول للبانل.\r\n"
|
||||||
|
"report" = "🕰 التقارير المجدولة: {{ .RunTime }}\r\n"
|
||||||
|
"datetime" = "⏰ التاريخ والوقت: {{ .DateTime }}\r\n"
|
||||||
|
"hostname" = "💻 السيرفر: {{ .Hostname }}\r\n"
|
||||||
|
"version" = "🚀 نسخة 3X-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" = "ℹ️ الحالة: {{ .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" = "❌ لأ"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 الـ ID اتحدث."
|
||||||
|
"received_password" = "🔑📥 الباسورد اتحدث."
|
||||||
|
"received_email" = "📧📥 الإيميل اتحدث."
|
||||||
|
"received_comment" = "💬📥 التعليق اتحدث."
|
||||||
|
"id_prompt" = "🔑 الـ ID الافتراضي: {{ .ClientId }}\n\nادخل الـ ID بتاعك."
|
||||||
|
"pass_prompt" = "🔑 الباسورد الافتراضي: {{ .ClientPassword }}\n\nادخل الباسورد بتاعك."
|
||||||
|
"email_prompt" = "📧 الإيميل الافتراضي: {{ .ClientEmail }}\n\nادخل الإيميل بتاعك."
|
||||||
|
"comment_prompt" = "💬 التعليق الافتراضي: {{ .ClientComment }}\n\nادخل تعليقك."
|
||||||
|
"inbound_client_data_id" = "🔄 للإدخال: {{ .InboundRemark }}\n\n🔑 الـ ID: {{ .ClientId }}\n📧 الإيميل: {{ .ClientEmail }}\n📊 الترافيك: {{ .ClientTraffic }}\n📅 تاريخ الانتهاء: {{ .ClientExp }}\n💬 التعليق: {{ .ClientComment }}\n\nممكن تضيف العميل للإدخال دلوقتي!"
|
||||||
|
"inbound_client_data_pass" = "🔄 للإدخال: {{ .InboundRemark }}\n\n🔑 الباسورد: {{ .ClientPass }}\n📧 الإيميل: {{ .ClientEmail }}\n📊 الترافيك: {{ .ClientTraffic }}\n📅 تاريخ الانتهاء: {{ .ClientExp }}\n💬 التعليق: {{ .ClientComment }}\n\nممكن تضيف العميل للإدخال دلوقتي!"
|
||||||
|
"cancel" = "❌ العملية اتلغت! \n\nممكن تبدأ من /start في أي وقت. 🔄"
|
||||||
|
"error_add_client" = "⚠️ حصل خطأ:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "تمام، هشيل على القيمة الافتراضية. 😊"
|
||||||
|
"incorrect_input" = "المدخلات مش صحيحة.\nالكلمات لازم تكون متصلة من غير فراغات.\nمثال صحيح: aaaaaa\nمثال غلط: aaa aaa 🚫"
|
||||||
|
|
||||||
|
[tgbot.buttons]
|
||||||
|
"closeKeyboard" = "❌ اقفل الكيبورد"
|
||||||
|
"cancel" = "❌ إلغاء"
|
||||||
|
"cancelReset" = "❌ إلغاء إعادة الضبط"
|
||||||
|
"cancelIpLimit" = "❌ إلغاء حد الـ IP"
|
||||||
|
"confirmResetTraffic" = "✅ تأكيد إعادة ضبط الترافيك؟"
|
||||||
|
"confirmClearIps" = "✅ تأكيد مسح الـ IPs؟"
|
||||||
|
"confirmRemoveTGUser" = "✅ تأكيد حذف مستخدم Telegram؟"
|
||||||
|
"confirmToggle" = "✅ تأكيد تفعيل/تعطيل المستخدم؟"
|
||||||
|
"dbBackup" = "احصل على نسخة DB"
|
||||||
|
"serverUsage" = "استخدام السيرفر"
|
||||||
|
"getInbounds" = "احصل على الإدخالات"
|
||||||
|
"depleteSoon" = "هينتهي قريب"
|
||||||
|
"clientUsage" = "استخدام العميل"
|
||||||
|
"onlines" = "العملاء الأونلاين"
|
||||||
|
"commands" = "الأوامر"
|
||||||
|
"refresh" = "🔄 تجديد"
|
||||||
|
"clearIPs" = "❌ مسح الـ IPs"
|
||||||
|
"removeTGUser" = "❌ حذف مستخدم Telegram"
|
||||||
|
"selectTGUser" = "👤 اختار مستخدم Telegram"
|
||||||
|
"selectOneTGUser" = "👤 اختار مستخدم Telegram:"
|
||||||
|
"resetTraffic" = "📈 إعادة ضبط الترافيك"
|
||||||
|
"resetExpire" = "📅 تغيير تاريخ الانتهاء"
|
||||||
|
"ipLog" = "🔢 سجل الـ IP"
|
||||||
|
"ipLimit" = "🔢 حد الـ IP"
|
||||||
|
"setTGUser" = "👤 ضبط مستخدم Telegram"
|
||||||
|
"toggle" = "🔘 تفعيل / تعطيل"
|
||||||
|
"custom" = "🔢 مخصص"
|
||||||
|
"confirmNumber" = "✅ تأكيد: {{ .Num }}"
|
||||||
|
"confirmNumberAdd" = "✅ تأكيد إضافة: {{ .Num }}"
|
||||||
|
"limitTraffic" = "🚧 حد الترافيك"
|
||||||
|
"getBanLogs" = "احصل على سجلات الحظر"
|
||||||
|
"allClients" = "كل العملاء"
|
||||||
|
|
||||||
|
"addClient" = "أضف عميل"
|
||||||
|
"submitDisable" = "اعتمد على إنه معطل ✅"
|
||||||
|
"use_default" = "🏷️ استخدم الافتراضي"
|
||||||
|
"change_id" = "⚙️🔑 تغيير الـ ID"
|
||||||
|
"change_password" = "⚙️🔑 تغيير الباسورد"
|
||||||
|
"change_email" = "⚙️📧 تغيير الإيميل"
|
||||||
|
"change_comment" = "⚙️💬 تغيير التعليق"
|
||||||
|
|
||||||
|
[tgbot.answers]
|
||||||
|
"successfulOperation" = "✅ العملية نجحت!"
|
||||||
|
"errorOperation" = "❗ حصل خطأ في العملية."
|
||||||
|
"getInboundsFailed" = "❌ فشل الحصول على الإدخالات."
|
||||||
|
"getClientsFailed" = "❌ فشل الحصول على العملاء."
|
||||||
|
"canceled" = "❌ {{ .Email }}: العملية اتلغت."
|
||||||
|
"clientRefreshSuccess" = "✅ {{ .Email }}: العميل اتحدث بنجاح."
|
||||||
|
"IpRefreshSuccess" = "✅ {{ .Email }}: الـ IPs اتحدثت بنجاح."
|
||||||
|
"TGIdRefreshSuccess" = "✅ {{ .Email }}: مستخدم Telegram اتحدث بنجاح."
|
||||||
|
"resetTrafficSuccess" = "✅ {{ .Email }}: الترافيك اتظبط بنجاح."
|
||||||
|
"setTrafficLimitSuccess" = "✅ {{ .Email }}: حد الترافيك اتسجل بنجاح."
|
||||||
|
"expireResetSuccess" = "✅ {{ .Email }}: أيام الانتهاء اتظبطت بنجاح."
|
||||||
|
"resetIpSuccess" = "✅ {{ .Email }}: حد الـ IP ({{ .Count }}) اتسجل بنجاح."
|
||||||
|
"clearIpSuccess" = "✅ {{ .Email }}: الـ IPs اتمسحت بنجاح."
|
||||||
|
"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" = "اختار الإدخال"
|
||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Overview"
|
"title" = "Overview"
|
||||||
|
"cpu" = "CPU"
|
||||||
|
"logicalProcessors" = "Logical Processors"
|
||||||
|
"frequency" = "Frequency"
|
||||||
|
"swap" = "Swap"
|
||||||
|
"storage" = "Storage"
|
||||||
"memory" = "RAM"
|
"memory" = "RAM"
|
||||||
"hard" = "Disk"
|
"threads" = "Threads"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "Stop"
|
"stopXray" = "Stop"
|
||||||
"restartXray" = "Restart"
|
"restartXray" = "Restart"
|
||||||
"xraySwitch" = "Version"
|
"xraySwitch" = "Version"
|
||||||
"xraySwitchClick" = "Choose the version you want to switch to."
|
"xraySwitchClick" = "Choose the version you want to switch to."
|
||||||
"xraySwitchClickDesk" = "Choose carefully, as older versions may not be compatible with current configurations."
|
"xraySwitchClickDesk" = "Choose carefully, as older versions may not be compatible with current configurations."
|
||||||
|
"xrayStatusUnknown" = "Unknown"
|
||||||
|
"xrayStatusRunning" = "Running"
|
||||||
|
"xrayStatusStop" = "Stop"
|
||||||
|
"xrayStatusError" = "Error"
|
||||||
|
"xrayErrorPopoverTitle" = "An error occurred while running Xray"
|
||||||
"operationHours" = "Uptime"
|
"operationHours" = "Uptime"
|
||||||
"systemLoad" = "System Load"
|
"systemLoad" = "System Load"
|
||||||
"systemLoadDesc" = "System load average for the past 1, 5, and 15 minutes"
|
"systemLoadDesc" = "System load average for the past 1, 5, and 15 minutes"
|
||||||
@@ -582,6 +592,21 @@
|
|||||||
"yes" = "✅ Yes"
|
"yes" = "✅ Yes"
|
||||||
"no" = "❌ No"
|
"no" = "❌ No"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 ID updated."
|
||||||
|
"received_password" = "🔑📥 Password updated."
|
||||||
|
"received_email" = "📧📥 Email updated."
|
||||||
|
"received_comment" = "💬📥 Comment updated."
|
||||||
|
"id_prompt" = "🔑 Default ID: {{ .ClientId }}\n\nEnter your id."
|
||||||
|
"pass_prompt" = "🔑 Default Password: {{ .ClientPassword }}\n\nEnter your password."
|
||||||
|
"email_prompt" = "📧 Default Email: {{ .ClientEmail }}\n\nEnter your email."
|
||||||
|
"comment_prompt" = "💬 Default Comment: {{ .ClientComment }}\n\nEnter your Comment."
|
||||||
|
"inbound_client_data_id" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!"
|
||||||
|
"inbound_client_data_pass" = "🔄 Inbound: {{ .InboundRemark }}\n\n🔑 Password: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Traffic: {{ .ClientTraffic }}\n📅 Expire Date: {{ .ClientExp }}\n💬 Comment: {{ .ClientComment }}\n\nYou can add the client to inbound now!"
|
||||||
|
"cancel" = "❌ Process Canceled! \n\nYou can /start again anytime. 🔄"
|
||||||
|
"error_add_client" = "⚠️ Error:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "Okay, I'll stick with the default value. 😊"
|
||||||
|
"incorrect_input" ="Your input is not valid.\nThe phrases should be continuous without spaces.\nCorrect example: aaaaaa\nIncorrect example: aaa aaa 🚫"
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ Close Keyboard"
|
"closeKeyboard" = "❌ Close Keyboard"
|
||||||
"cancel" = "❌ Cancel"
|
"cancel" = "❌ Cancel"
|
||||||
@@ -616,6 +641,14 @@
|
|||||||
"getBanLogs" = "Get Ban Logs"
|
"getBanLogs" = "Get Ban Logs"
|
||||||
"allClients" = "All Clients"
|
"allClients" = "All Clients"
|
||||||
|
|
||||||
|
"addClient" = "Add Client"
|
||||||
|
"submitDisable" = "Submit As Disable ✅"
|
||||||
|
"use_default" = "🏷️ Use default"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 Password"
|
||||||
|
"change_email" = "⚙️📧 Email"
|
||||||
|
"change_comment" = "⚙️💬 Comment"
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ Operation successful!"
|
"successfulOperation" = "✅ Operation successful!"
|
||||||
"errorOperation" = "❗ Error in operation."
|
"errorOperation" = "❗ Error in operation."
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Estado del Sistema"
|
"title" = "Estado del Sistema"
|
||||||
"memory" = "Memoria"
|
"cpu" = "CPU"
|
||||||
"hard" = "Disco Duro"
|
"logicalProcessors" = "Procesadores lógicos"
|
||||||
|
"frequency" = "Frecuencia"
|
||||||
|
"swap" = "Intercambio"
|
||||||
|
"storage" = "Almacenamiento"
|
||||||
|
"memory" = "RAM"
|
||||||
|
"threads" = "Hilos"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "Detener"
|
"stopXray" = "Detener"
|
||||||
"restartXray" = "Reiniciar"
|
"restartXray" = "Reiniciar"
|
||||||
"xraySwitch" = "Versión"
|
"xraySwitch" = "Versión"
|
||||||
"xraySwitchClick" = "Elige la versión a la que deseas cambiar."
|
"xraySwitchClick" = "Elige la versión a la que deseas cambiar."
|
||||||
"xraySwitchClickDesk" = "Elige sabiamente, ya que las versiones anteriores pueden no ser compatibles con las configuraciones actuales."
|
"xraySwitchClickDesk" = "Elige sabiamente, ya que las versiones anteriores pueden no ser compatibles con las configuraciones actuales."
|
||||||
|
"xrayStatusUnknown" = "Desconocido"
|
||||||
|
"xrayStatusRunning" = "En ejecución"
|
||||||
|
"xrayStatusStop" = "Detenido"
|
||||||
|
"xrayStatusError" = "Error"
|
||||||
|
"xrayErrorPopoverTitle" = "Se produjo un error al ejecutar Xray"
|
||||||
"operationHours" = "Tiempo de Funcionamiento"
|
"operationHours" = "Tiempo de Funcionamiento"
|
||||||
"systemLoad" = "Carga del Sistema"
|
"systemLoad" = "Carga del Sistema"
|
||||||
"systemLoadDesc" = "promedio de carga del sistema en los últimos 1, 5 y 15 minutos"
|
"systemLoadDesc" = "promedio de carga del sistema en los últimos 1, 5 y 15 minutos"
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ Sí"
|
"yes" = "✅ Sí"
|
||||||
"no" = "❌ No"
|
"no" = "❌ No"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 ID actualizado."
|
||||||
|
"received_password" = "🔑📥 Contraseña actualizada."
|
||||||
|
"received_email" = "📧📥 Correo electrónico actualizado."
|
||||||
|
"received_comment" = "💬📥 Comentario actualizado."
|
||||||
|
"id_prompt" = "🔑 ID predeterminado: {{ .ClientId }}\n\nIntroduce tu ID."
|
||||||
|
"pass_prompt" = "🔑 Contraseña predeterminada: {{ .ClientPassword }}\n\nIntroduce tu contraseña."
|
||||||
|
"email_prompt" = "📧 Correo electrónico predeterminado: {{ .ClientEmail }}\n\nIntroduce tu correo electrónico."
|
||||||
|
"comment_prompt" = "💬 Comentario predeterminado: {{ .ClientComment }}\n\nIntroduce tu comentario."
|
||||||
|
"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de vencimiento: {{ .ClientExp }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!"
|
||||||
|
"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Contraseña: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfico: {{ .ClientTraffic }}\n📅 Fecha de vencimiento: {{ .ClientExp }}\n💬 Comentario: {{ .ClientComment }}\n\n¡Ahora puedes agregar al cliente a la entrada!"
|
||||||
|
"cancel" = "❌ ¡Proceso cancelado! \n\nPuedes /start de nuevo en cualquier momento. 🔄"
|
||||||
|
"error_add_client" = "⚠️ Error:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "Está bien, me quedaré con el valor predeterminado. 😊"
|
||||||
|
"incorrect_input" ="Tu entrada no es válida.\nLas frases deben ser continuas sin espacios.\nEjemplo correcto: aaaaaa\nEjemplo incorrecto: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ Cerrar Teclado"
|
"closeKeyboard" = "❌ Cerrar Teclado"
|
||||||
"cancel" = "❌ Cancelar"
|
"cancel" = "❌ Cancelar"
|
||||||
@@ -618,6 +644,15 @@
|
|||||||
"getBanLogs" = "Registros de prohibición"
|
"getBanLogs" = "Registros de prohibición"
|
||||||
"allClients" = "Todos los Clientes"
|
"allClients" = "Todos los Clientes"
|
||||||
|
|
||||||
|
"addClient" = "Añadir Cliente"
|
||||||
|
"submitDisable" = "Enviar como Deshabilitado ✅"
|
||||||
|
"use_default" = "🏷️ Usar predeterminado"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 Contraseña"
|
||||||
|
"change_email" = "⚙️📧 Correo electrónico"
|
||||||
|
"change_comment" = "⚙️💬 Comentario"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ ¡Exitosa!"
|
"successfulOperation" = "✅ ¡Exitosa!"
|
||||||
"errorOperation" = "❗ Error en la Operación."
|
"errorOperation" = "❗ Error en la Operación."
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "نمای کلی"
|
"title" = "نمای کلی"
|
||||||
"memory" = "RAM"
|
"cpu" = "پردازنده"
|
||||||
"hard" = "Disk"
|
"logicalProcessors" = "پردازندههای منطقی"
|
||||||
|
"frequency" = "فرکانس"
|
||||||
|
"swap" = "سواپ"
|
||||||
|
"storage" = "ذخیرهسازی"
|
||||||
|
"memory" = "حافظه رم"
|
||||||
|
"threads" = "رشتهها"
|
||||||
"xrayStatus" = "ایکسری"
|
"xrayStatus" = "ایکسری"
|
||||||
"stopXray" = "توقف"
|
"stopXray" = "توقف"
|
||||||
"restartXray" = "شروعمجدد"
|
"restartXray" = "شروعمجدد"
|
||||||
"xraySwitch" = "نسخه"
|
"xraySwitch" = "نسخه"
|
||||||
"xraySwitchClick" = "نسخه مورد نظر را انتخاب کنید"
|
"xraySwitchClick" = "نسخه مورد نظر را انتخاب کنید"
|
||||||
"xraySwitchClickDesk" = "لطفا بادقت انتخاب کنید. درصورت انتخاب نسخه قدیمیتر، امکان ناهماهنگی با پیکربندی فعلی وجود دارد"
|
"xraySwitchClickDesk" = "لطفا بادقت انتخاب کنید. درصورت انتخاب نسخه قدیمیتر، امکان ناهماهنگی با پیکربندی فعلی وجود دارد"
|
||||||
|
"xrayStatusUnknown" = "ناشناخته"
|
||||||
|
"xrayStatusRunning" = "در حال اجرا"
|
||||||
|
"xrayStatusStop" = "متوقف"
|
||||||
|
"xrayStatusError" = "خطا"
|
||||||
|
"xrayErrorPopoverTitle" = "خطا در هنگام اجرای Xray رخ داد"
|
||||||
"operationHours" = "مدتکارکرد"
|
"operationHours" = "مدتکارکرد"
|
||||||
"systemLoad" = "بارسیستم"
|
"systemLoad" = "بارسیستم"
|
||||||
"systemLoadDesc" = "میانگین بار سیستم برای 1، 5 و 15 دقیقه گذشته"
|
"systemLoadDesc" = "میانگین بار سیستم برای 1، 5 و 15 دقیقه گذشته"
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ بله"
|
"yes" = "✅ بله"
|
||||||
"no" = "❌ خیر"
|
"no" = "❌ خیر"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 شناسه بهروزرسانی شد."
|
||||||
|
"received_password" = "🔑📥 رمز عبور بهروزرسانی شد."
|
||||||
|
"received_email" = "📧📥 ایمیل بهروزرسانی شد."
|
||||||
|
"received_comment" = "💬📥 نظر بهروزرسانی شد."
|
||||||
|
"id_prompt" = "🔑 شناسه پیشفرض: {{ .ClientId }}\n\nشناسه خود را وارد کنید."
|
||||||
|
"pass_prompt" = "🔑 رمز عبور پیشفرض: {{ .ClientPassword }}\n\nرمز عبور خود را وارد کنید."
|
||||||
|
"email_prompt" = "📧 ایمیل پیشفرض: {{ .ClientEmail }}\n\nایمیل خود را وارد کنید."
|
||||||
|
"comment_prompt" = "💬 نظر پیشفرض: {{ .ClientComment }}\n\nنظر خود را وارد کنید."
|
||||||
|
"inbound_client_data_id" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 شناسه: {{ .ClientId }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n💬 نظر: {{ .ClientComment }}\n\nحالا میتوانید مشتری را به ورودی اضافه کنید!"
|
||||||
|
"inbound_client_data_pass" = "🔄 ورودی: {{ .InboundRemark }}\n\n🔑 رمز عبور: {{ .ClientPass }}\n📧 ایمیل: {{ .ClientEmail }}\n📊 ترافیک: {{ .ClientTraffic }}\n📅 تاریخ انقضا: {{ .ClientExp }}\n💬 نظر: {{ .ClientComment }}\n\nحالا میتوانید مشتری را به ورودی اضافه کنید!"
|
||||||
|
"cancel" = "❌ فرآیند لغو شد! \n\nمیتوانید هر زمان که خواستید /start را دوباره اجرا کنید. 🔄"
|
||||||
|
"error_add_client" = "⚠️ خطا:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "باشه، از مقدار پیشفرض استفاده میکنم. 😊"
|
||||||
|
"incorrect_input" ="ورودی شما معتبر نیست.\nعبارتها باید بدون فاصله باشند.\nمثال صحیح: aaaaaa\nمثال نادرست: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ بستن کیبورد"
|
"closeKeyboard" = "❌ بستن کیبورد"
|
||||||
"cancel" = "❌ لغو"
|
"cancel" = "❌ لغو"
|
||||||
@@ -618,6 +644,15 @@
|
|||||||
"getBanLogs" = "گزارش های بلوک را دریافت کنید"
|
"getBanLogs" = "گزارش های بلوک را دریافت کنید"
|
||||||
"allClients" = "همه مشتریان"
|
"allClients" = "همه مشتریان"
|
||||||
|
|
||||||
|
"addClient" = "اضافه کردن مشتری"
|
||||||
|
"submitDisable" = "ارسال به عنوان غیرفعال ✅"
|
||||||
|
"use_default" = "🏷️ استفاده از پیشفرض"
|
||||||
|
"change_id" = "⚙️🔑 شناسه"
|
||||||
|
"change_password" = "⚙️🔑 رمز عبور"
|
||||||
|
"change_email" = "⚙️📧 ایمیل"
|
||||||
|
"change_comment" = "⚙️💬 نظر"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ انجام شد!"
|
"successfulOperation" = "✅ انجام شد!"
|
||||||
"errorOperation" = "❗ خطا در عملیات."
|
"errorOperation" = "❗ خطا در عملیات."
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Ikhtisar"
|
"title" = "Ikhtisar"
|
||||||
|
"cpu" = "CPU"
|
||||||
|
"logicalProcessors" = "Prosesor logis"
|
||||||
|
"frequency" = "Frekuensi"
|
||||||
|
"swap" = "Swap"
|
||||||
|
"storage" = "Penyimpanan"
|
||||||
"memory" = "RAM"
|
"memory" = "RAM"
|
||||||
"hard" = "Disk"
|
"threads" = "Thread"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "Stop"
|
"stopXray" = "Stop"
|
||||||
"restartXray" = "Restart"
|
"restartXray" = "Restart"
|
||||||
"xraySwitch" = "Versi"
|
"xraySwitch" = "Versi"
|
||||||
"xraySwitchClick" = "Pilih versi yang ingin Anda pindah."
|
"xraySwitchClick" = "Pilih versi yang ingin Anda pindah."
|
||||||
"xraySwitchClickDesk" = "Pilih dengan hati-hati, karena versi yang lebih lama mungkin tidak kompatibel dengan konfigurasi saat ini."
|
"xraySwitchClickDesk" = "Pilih dengan hati-hati, karena versi yang lebih lama mungkin tidak kompatibel dengan konfigurasi saat ini."
|
||||||
|
"xrayStatusUnknown" = "Tidak diketahui"
|
||||||
|
"xrayStatusRunning" = "Berjalan"
|
||||||
|
"xrayStatusStop" = "Berhenti"
|
||||||
|
"xrayStatusError" = "Kesalahan"
|
||||||
|
"xrayErrorPopoverTitle" = "Terjadi kesalahan saat menjalankan Xray"
|
||||||
"operationHours" = "Waktu Aktif"
|
"operationHours" = "Waktu Aktif"
|
||||||
"systemLoad" = "Beban Sistem"
|
"systemLoad" = "Beban Sistem"
|
||||||
"systemLoadDesc" = "Rata-rata beban sistem selama 1, 5, dan 15 menit terakhir"
|
"systemLoadDesc" = "Rata-rata beban sistem selama 1, 5, dan 15 menit terakhir"
|
||||||
@@ -322,6 +332,7 @@
|
|||||||
"subShowInfo" = "Tampilkan Info Penggunaan"
|
"subShowInfo" = "Tampilkan Info Penggunaan"
|
||||||
"subShowInfoDesc" = "Sisa traffic dan tanggal akan ditampilkan di aplikasi klien."
|
"subShowInfoDesc" = "Sisa traffic dan tanggal akan ditampilkan di aplikasi klien."
|
||||||
"subURI" = "URI Proxy Terbalik"
|
"subURI" = "URI Proxy Terbalik"
|
||||||
|
"subURIDesc" = "Path URI dari URL langganan untuk digunakan di belakang proxy."
|
||||||
"externalTrafficInformEnable" = "Informasikan API eksternal pada setiap pembaruan lalu lintas."
|
"externalTrafficInformEnable" = "Informasikan API eksternal pada setiap pembaruan lalu lintas."
|
||||||
"externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
|
"externalTrafficInformEnableDesc" = "Inform external API on every traffic update."
|
||||||
"externalTrafficInformURI" = "Lalu Lintas Eksternal Menginformasikan URI"
|
"externalTrafficInformURI" = "Lalu Lintas Eksternal Menginformasikan URI"
|
||||||
@@ -583,6 +594,22 @@
|
|||||||
"yes" = "✅ Ya"
|
"yes" = "✅ Ya"
|
||||||
"no" = "❌ Tidak"
|
"no" = "❌ Tidak"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 ID diperbarui."
|
||||||
|
"received_password" = "🔑📥 Kata sandi diperbarui."
|
||||||
|
"received_email" = "📧📥 Email diperbarui."
|
||||||
|
"received_comment" = "💬📥 Komentar diperbarui."
|
||||||
|
"id_prompt" = "🔑 ID Default: {{ .ClientId }}\n\nMasukkan ID Anda."
|
||||||
|
"pass_prompt" = "🔑 Kata Sandi Default: {{ .ClientPassword }}\n\nMasukkan kata sandi Anda."
|
||||||
|
"email_prompt" = "📧 Email Default: {{ .ClientEmail }}\n\nMasukkan email Anda."
|
||||||
|
"comment_prompt" = "💬 Komentar Default: {{ .ClientComment }}\n\nMasukkan komentar Anda."
|
||||||
|
"inbound_client_data_id" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu Lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang Anda bisa menambahkan klien ke masuk!"
|
||||||
|
"inbound_client_data_pass" = "🔄 Masuk: {{ .InboundRemark }}\n\n🔑 Kata Sandi: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Lalu Lintas: {{ .ClientTraffic }}\n📅 Tanggal Kedaluwarsa: {{ .ClientExp }}\n💬 Komentar: {{ .ClientComment }}\n\nSekarang Anda bisa menambahkan klien ke masuk!"
|
||||||
|
"cancel" = "❌ Proses Dibatalkan! \n\nAnda dapat /start lagi kapan saja. 🔄"
|
||||||
|
"error_add_client" = "⚠️ Kesalahan:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "Oke, saya akan tetap menggunakan nilai default. 😊"
|
||||||
|
"incorrect_input" ="Masukan Anda tidak valid.\nFrasa harus berlanjut tanpa spasi.\nContoh benar: aaaaaa\nContoh salah: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ Tutup Papan Ketik"
|
"closeKeyboard" = "❌ Tutup Papan Ketik"
|
||||||
"cancel" = "❌ Batal"
|
"cancel" = "❌ Batal"
|
||||||
@@ -617,6 +644,15 @@
|
|||||||
"getBanLogs" = "Dapatkan Log Pemblokiran"
|
"getBanLogs" = "Dapatkan Log Pemblokiran"
|
||||||
"allClients" = "Semua Klien"
|
"allClients" = "Semua Klien"
|
||||||
|
|
||||||
|
"addClient" = "Tambah Klien"
|
||||||
|
"submitDisable" = "Kirim Sebagai Nonaktif ✅"
|
||||||
|
"use_default" = "🏷️ Gunakan default"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 Kata Sandi"
|
||||||
|
"change_email" = "⚙️📧 Email"
|
||||||
|
"change_comment" = "⚙️💬 Komentar"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ Operasi berhasil!"
|
"successfulOperation" = "✅ Operasi berhasil!"
|
||||||
"errorOperation" = "❗ Kesalahan dalam operasi."
|
"errorOperation" = "❗ Kesalahan dalam operasi."
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "システムステータス"
|
"title" = "システムステータス"
|
||||||
"memory" = "メモリ"
|
"cpu" = "CPU"
|
||||||
"hard" = "ハードディスク"
|
"logicalProcessors" = "論理プロセッサ"
|
||||||
|
"frequency" = "周波数"
|
||||||
|
"swap" = "スワップ"
|
||||||
|
"storage" = "ストレージ"
|
||||||
|
"memory" = "RAM"
|
||||||
|
"threads" = "スレッド"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "停止"
|
"stopXray" = "停止"
|
||||||
"restartXray" = "再起動"
|
"restartXray" = "再起動"
|
||||||
"xraySwitch" = "バージョン"
|
"xraySwitch" = "バージョン"
|
||||||
"xraySwitchClick" = "切り替えるバージョンを選択してください"
|
"xraySwitchClick" = "切り替えるバージョンを選択してください"
|
||||||
"xraySwitchClickDesk" = "慎重に選択してください。古いバージョンは現在の設定と互換性がない可能性があります。"
|
"xraySwitchClickDesk" = "慎重に選択してください。古いバージョンは現在の設定と互換性がない可能性があります。"
|
||||||
|
"xrayStatusUnknown" = "不明"
|
||||||
|
"xrayStatusRunning" = "実行中"
|
||||||
|
"xrayStatusStop" = "停止"
|
||||||
|
"xrayStatusError" = "エラー"
|
||||||
|
"xrayErrorPopoverTitle" = "Xrayの実行中にエラーが発生しました"
|
||||||
"operationHours" = "システム稼働時間"
|
"operationHours" = "システム稼働時間"
|
||||||
"systemLoad" = "システム負荷"
|
"systemLoad" = "システム負荷"
|
||||||
"systemLoadDesc" = "過去1、5、15分間のシステム平均負荷"
|
"systemLoadDesc" = "過去1、5、15分間のシステム平均負荷"
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ はい"
|
"yes" = "✅ はい"
|
||||||
"no" = "❌ いいえ"
|
"no" = "❌ いいえ"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 IDが更新されました。"
|
||||||
|
"received_password" = "🔑📥 パスワードが更新されました。"
|
||||||
|
"received_email" = "📧📥 メールが更新されました。"
|
||||||
|
"received_comment" = "💬📥 コメントが更新されました。"
|
||||||
|
"id_prompt" = "🔑 デフォルトID: {{ .ClientId }}\n\nIDを入力してください。"
|
||||||
|
"pass_prompt" = "🔑 デフォルトパスワード: {{ .ClientPassword }}\n\nパスワードを入力してください。"
|
||||||
|
"email_prompt" = "📧 デフォルトメール: {{ .ClientEmail }}\n\nメールを入力してください。"
|
||||||
|
"comment_prompt" = "💬 デフォルトコメント: {{ .ClientComment }}\n\nコメントを入力してください。"
|
||||||
|
"inbound_client_data_id" = "🔄 入力: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 期限日: {{ .ClientExp }}\n💬 コメント: {{ .ClientComment }}\n\n今すぐクライアントをインバウンドに追加できます!"
|
||||||
|
"inbound_client_data_pass" = "🔄 入力: {{ .InboundRemark }}\n\n🔑 パスワード: {{ .ClientPass }}\n📧 メール: {{ .ClientEmail }}\n📊 トラフィック: {{ .ClientTraffic }}\n📅 期限日: {{ .ClientExp }}\n💬 コメント: {{ .ClientComment }}\n\n今すぐクライアントをインバウンドに追加できます!"
|
||||||
|
"cancel" = "❌ プロセスがキャンセルされました!\n\nいつでも /start で再開できます。 🔄"
|
||||||
|
"error_add_client" = "⚠️ エラー:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "わかりました、デフォルト値を使用します。 😊"
|
||||||
|
"incorrect_input" ="入力が無効です。\nフレーズはスペースなしで続けて入力してください。\n正しい例: aaaaaa\n間違った例: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ キーボードを閉じる"
|
"closeKeyboard" = "❌ キーボードを閉じる"
|
||||||
"cancel" = "❌ キャンセル"
|
"cancel" = "❌ キャンセル"
|
||||||
@@ -618,6 +644,15 @@
|
|||||||
"getBanLogs" = "禁止ログ"
|
"getBanLogs" = "禁止ログ"
|
||||||
"allClients" = "すべてのクライアント"
|
"allClients" = "すべてのクライアント"
|
||||||
|
|
||||||
|
"addClient" = "クライアントを追加"
|
||||||
|
"submitDisable" = "無効として送信 ✅"
|
||||||
|
"use_default" = "🏷️ デフォルトを使用"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 パスワード"
|
||||||
|
"change_email" = "⚙️📧 メール"
|
||||||
|
"change_comment" = "⚙️💬 コメント"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ 成功!"
|
"successfulOperation" = "✅ 成功!"
|
||||||
"errorOperation" = "❗ 操作エラー。"
|
"errorOperation" = "❗ 操作エラー。"
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Visão Geral"
|
"title" = "Visão Geral"
|
||||||
"memory" = "Memória RAM"
|
"cpu" = "CPU"
|
||||||
"hard" = "Disco"
|
"logicalProcessors" = "Processadores lógicos"
|
||||||
|
"frequency" = "Frequência"
|
||||||
|
"swap" = "Swap"
|
||||||
|
"storage" = "Armazenamento"
|
||||||
|
"memory" = "RAM"
|
||||||
|
"threads" = "Threads"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "Parar"
|
"stopXray" = "Parar"
|
||||||
"restartXray" = "Reiniciar"
|
"restartXray" = "Reiniciar"
|
||||||
"xraySwitch" = "Versão"
|
"xraySwitch" = "Versão"
|
||||||
"xraySwitchClick" = "Escolha a versão para a qual deseja alternar."
|
"xraySwitchClick" = "Escolha a versão para a qual deseja alternar."
|
||||||
"xraySwitchClickDesk" = "Escolha com cuidado, pois versões mais antigas podem não ser compatíveis com as configurações atuais."
|
"xraySwitchClickDesk" = "Escolha com cuidado, pois versões mais antigas podem não ser compatíveis com as configurações atuais."
|
||||||
|
"xrayStatusUnknown" = "Desconhecido"
|
||||||
|
"xrayStatusRunning" = "Em execução"
|
||||||
|
"xrayStatusStop" = "Parado"
|
||||||
|
"xrayStatusError" = "Erro"
|
||||||
|
"xrayErrorPopoverTitle" = "Ocorreu um erro ao executar o Xray"
|
||||||
"operationHours" = "Tempo de Atividade"
|
"operationHours" = "Tempo de Atividade"
|
||||||
"systemLoad" = "Carga do Sistema"
|
"systemLoad" = "Carga do Sistema"
|
||||||
"systemLoadDesc" = "Média de carga do sistema nos últimos 1, 5 e 15 minutos"
|
"systemLoadDesc" = "Média de carga do sistema nos últimos 1, 5 e 15 minutos"
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ Sim"
|
"yes" = "✅ Sim"
|
||||||
"no" = "❌ Não"
|
"no" = "❌ Não"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 ID atualizado."
|
||||||
|
"received_password" = "🔑📥 Senha atualizada."
|
||||||
|
"received_email" = "📧📥 E-mail atualizado."
|
||||||
|
"received_comment" = "💬📥 Comentário atualizado."
|
||||||
|
"id_prompt" = "🔑 ID Padrão: {{ .ClientId }}\n\nDigite seu ID."
|
||||||
|
"pass_prompt" = "🔑 Senha Padrão: {{ .ClientPassword }}\n\nDigite sua senha."
|
||||||
|
"email_prompt" = "📧 E-mail Padrão: {{ .ClientEmail }}\n\nDigite seu e-mail."
|
||||||
|
"comment_prompt" = "💬 Comentário Padrão: {{ .ClientComment }}\n\nDigite seu comentário."
|
||||||
|
"inbound_client_data_id" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!"
|
||||||
|
"inbound_client_data_pass" = "🔄 Entrada: {{ .InboundRemark }}\n\n🔑 Senha: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Tráfego: {{ .ClientTraffic }}\n📅 Data de expiração: {{ .ClientExp }}\n💬 Comentário: {{ .ClientComment }}\n\nAgora você pode adicionar o cliente à entrada!"
|
||||||
|
"cancel" = "❌ Processo Cancelado! \n\nVocê pode iniciar novamente a qualquer momento com /start. 🔄"
|
||||||
|
"error_add_client" = "⚠️ Erro:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "Tudo bem, vou manter o valor padrão. 😊"
|
||||||
|
"incorrect_input" ="Sua entrada não é válida.\nAs frases devem ser contínuas, sem espaços.\nExemplo correto: aaaaaa\nExemplo incorreto: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ Fechar teclado"
|
"closeKeyboard" = "❌ Fechar teclado"
|
||||||
"cancel" = "❌ Cancelar"
|
"cancel" = "❌ Cancelar"
|
||||||
@@ -618,6 +644,15 @@
|
|||||||
"getBanLogs" = "Obter logs de banimento"
|
"getBanLogs" = "Obter logs de banimento"
|
||||||
"allClients" = "Todos os clientes"
|
"allClients" = "Todos os clientes"
|
||||||
|
|
||||||
|
"addClient" = "Adicionar Cliente"
|
||||||
|
"submitDisable" = "Enviar como Desativado ✅"
|
||||||
|
"use_default" = "🏷️ Usar padrão"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 Senha"
|
||||||
|
"change_email" = "⚙️📧 E-mail"
|
||||||
|
"change_comment" = "⚙️💬 Comentário"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ Operação bem-sucedida!"
|
"successfulOperation" = "✅ Operação bem-sucedida!"
|
||||||
"errorOperation" = "❗ Erro na operação."
|
"errorOperation" = "❗ Erro na operação."
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
"copySuccess" = "Скопировано"
|
"copySuccess" = "Скопировано"
|
||||||
"sure" = "Да"
|
"sure" = "Да"
|
||||||
"encryption" = "Шифрование"
|
"encryption" = "Шифрование"
|
||||||
"transmission" = "Протокол передачи"
|
"transmission" = "Протокол"
|
||||||
"host" = "Хост"
|
"host" = "Хост"
|
||||||
"path" = "Путь"
|
"path" = "Путь"
|
||||||
"camouflage" = "Маскировка"
|
"camouflage" = "Маскировка"
|
||||||
@@ -67,11 +67,11 @@
|
|||||||
"emptyReverseDesc" = "Нет добавленных обратных прокси."
|
"emptyReverseDesc" = "Нет добавленных обратных прокси."
|
||||||
|
|
||||||
[menu]
|
[menu]
|
||||||
"theme" = "Тема"
|
"theme" = "Тема оформления"
|
||||||
"dark" = "Темная"
|
"dark" = "Темная"
|
||||||
"ultraDark" = "Ультра темная"
|
"ultraDark" = "Ультра темная"
|
||||||
"dashboard" = "Статус системы"
|
"dashboard" = "Статус системы"
|
||||||
"inbounds" = "Подключения"
|
"inbounds" = "Входящие подключения"
|
||||||
"settings" = "Настройки панели"
|
"settings" = "Настройки панели"
|
||||||
"xray" = "Настройки Xray"
|
"xray" = "Настройки Xray"
|
||||||
"logout" = "Выход"
|
"logout" = "Выход"
|
||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Статус системы"
|
"title" = "Статус системы"
|
||||||
"memory" = "Память"
|
"cpu" = "ЦП"
|
||||||
"hard" = "Жесткий диск"
|
"logicalProcessors" = "Логические процессоры"
|
||||||
|
"frequency" = "Частота"
|
||||||
|
"swap" = "Файл подкачки"
|
||||||
|
"storage" = "Хранилище"
|
||||||
|
"memory" = "ОЗУ"
|
||||||
|
"threads" = "Потоки"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "Остановить"
|
"stopXray" = "Остановить"
|
||||||
"restartXray" = "Перезапустить"
|
"restartXray" = "Перезапустить"
|
||||||
"xraySwitch" = "Выбор версии"
|
"xraySwitch" = "Выбор версии"
|
||||||
"xraySwitchClick" = "Выберите желаемую версию"
|
"xraySwitchClick" = "Выберите желаемую версию"
|
||||||
"xraySwitchClickDesk" = "Обратите внимание: старые версии могут не поддерживать текущие настройки"
|
"xraySwitchClickDesk" = "Обратите внимание: старые версии могут не поддерживать текущие настройки"
|
||||||
|
"xrayStatusUnknown" = "Неизвестно"
|
||||||
|
"xrayStatusRunning" = "Запущен"
|
||||||
|
"xrayStatusStop" = "Остановлен"
|
||||||
|
"xrayStatusError" = "Ошибка"
|
||||||
|
"xrayErrorPopoverTitle" = "Произошла ошибка при запуске Xray"
|
||||||
"operationHours" = "Время работы системы"
|
"operationHours" = "Время работы системы"
|
||||||
"systemLoad" = "Нагрузка на систему"
|
"systemLoad" = "Нагрузка на систему"
|
||||||
"systemLoadDesc" = "Средняя загрузка системы за последние 1, 5 и 15 минут"
|
"systemLoadDesc" = "Средняя загрузка системы за последние 1, 5 и 15 минут"
|
||||||
@@ -116,7 +126,7 @@
|
|||||||
"xraySwitchVersionDialog" = "Переключить версию Xray"
|
"xraySwitchVersionDialog" = "Переключить версию Xray"
|
||||||
"xraySwitchVersionDialogDesc" = "Вы точно хотите сменить версию Xray?"
|
"xraySwitchVersionDialogDesc" = "Вы точно хотите сменить версию Xray?"
|
||||||
"dontRefresh" = "Установка в процессе. Не обновляйте страницу"
|
"dontRefresh" = "Установка в процессе. Не обновляйте страницу"
|
||||||
"logs" = "Логи"
|
"logs" = "Журнал"
|
||||||
"config" = "Конфигурация"
|
"config" = "Конфигурация"
|
||||||
"backup" = "Резервная копия"
|
"backup" = "Резервная копия"
|
||||||
"backupTitle" = "База данных резервных копий"
|
"backupTitle" = "База данных резервных копий"
|
||||||
@@ -126,10 +136,10 @@
|
|||||||
"importDatabaseDesc" = "Нажмите, чтобы выбрать и загрузить файл .db с вашего устройства для восстановления базы данных из резервной копии."
|
"importDatabaseDesc" = "Нажмите, чтобы выбрать и загрузить файл .db с вашего устройства для восстановления базы данных из резервной копии."
|
||||||
|
|
||||||
[pages.inbounds]
|
[pages.inbounds]
|
||||||
"title" = "Подключения"
|
"title" = "Входящие подключения"
|
||||||
"totalDownUp" = "Общий объем отправленного/полученного трафика"
|
"totalDownUp" = "Объем отправленного/полученного трафика"
|
||||||
"totalUsage" = "Всего использовано"
|
"totalUsage" = "Всего использовано"
|
||||||
"inboundCount" = "Количество подключений"
|
"inboundCount" = "Всего входящих подключений"
|
||||||
"operate" = "Меню"
|
"operate" = "Меню"
|
||||||
"enable" = "Включить"
|
"enable" = "Включить"
|
||||||
"remark" = "Примечание"
|
"remark" = "Примечание"
|
||||||
@@ -139,20 +149,20 @@
|
|||||||
"details" = "Подробнее"
|
"details" = "Подробнее"
|
||||||
"transportConfig" = "Транспорт"
|
"transportConfig" = "Транспорт"
|
||||||
"expireDate" = "Дата окончания"
|
"expireDate" = "Дата окончания"
|
||||||
"resetTraffic" = "Сбросить трафик"
|
"resetTraffic" = "Сброс статистики трафика"
|
||||||
"addInbound" = "Добавить подключение"
|
"addInbound" = "Создать входящее подключение"
|
||||||
"generalActions" = "Общие действия"
|
"generalActions" = "Общие действия"
|
||||||
"autoRefresh" = "Автообновление"
|
"autoRefresh" = "Автообновление"
|
||||||
"autoRefreshInterval" = "Интервал"
|
"autoRefreshInterval" = "Интервал"
|
||||||
"create" = "Создать"
|
"create" = "Создать"
|
||||||
"update" = "Обновить"
|
"update" = "Обновить"
|
||||||
"modifyInbound" = "Изменить подключение"
|
"modifyInbound" = "Изменить входящее подключение"
|
||||||
"deleteInbound" = "Удалить подключение"
|
"deleteInbound" = "Удалить входящее подключение"
|
||||||
"deleteInboundContent" = "Вы уверены, что хотите удалить подключение?"
|
"deleteInboundContent" = "Вы уверены, что хотите удалить входящее подключение?"
|
||||||
"deleteClient" = "Удалить клиента"
|
"deleteClient" = "Удалить клиента"
|
||||||
"deleteClientContent" = "Вы уверены, что хотите удалить клиента?"
|
"deleteClientContent" = "Вы уверены, что хотите удалить клиента?"
|
||||||
"resetTrafficContent" = "Вы уверены, что хотите сбросить трафик?"
|
"resetTrafficContent" = "Вы уверены, что хотите сбросить трафик?"
|
||||||
"copyLink" = "Копировать ключ"
|
"copyLink" = "Копировать ссылку"
|
||||||
"address" = "Адрес"
|
"address" = "Адрес"
|
||||||
"network" = "Сеть"
|
"network" = "Сеть"
|
||||||
"destinationPort" = "Порт назначения"
|
"destinationPort" = "Порт назначения"
|
||||||
@@ -162,36 +172,36 @@
|
|||||||
"totalFlow" = "Общий расход"
|
"totalFlow" = "Общий расход"
|
||||||
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
|
"leaveBlankToNeverExpire" = "Оставьте пустым, чтобы не истекало"
|
||||||
"noRecommendKeepDefault" = "Рекомендуется оставить настройки по умолчанию"
|
"noRecommendKeepDefault" = "Рекомендуется оставить настройки по умолчанию"
|
||||||
"certificatePath" = "Путь к файлу"
|
"certificatePath" = "Путь к сертификату"
|
||||||
"certificateContent" = "Содержимое файла"
|
"certificateContent" = "Содержимое сертификата"
|
||||||
"publicKey" = "Публичный ключ"
|
"publicKey" = "Публичный ключ"
|
||||||
"privatekey" = "Закрытый ключ"
|
"privatekey" = "Закрытый ключ"
|
||||||
"clickOnQRcode" = "Нажмите на QR-код, чтобы скопировать"
|
"clickOnQRcode" = "Нажмите на QR-код, чтобы скопировать"
|
||||||
"client" = "Клиент"
|
"client" = "Клиент"
|
||||||
"export" = "Экспорт ключей"
|
"export" = "Экспорт ссылок"
|
||||||
"clone" = "Клонировать"
|
"clone" = "Клонировать"
|
||||||
"cloneInbound" = "Клонировать"
|
"cloneInbound" = "Клонировать"
|
||||||
"cloneInboundContent" = "Будут клонированы все настройки подключений, за исключением списка клиентов, порта и IP-адреса прослушивания"
|
"cloneInboundContent" = "Будут клонированы все настройки входящих подключений, за исключением списка клиентов, порта и IP-адреса прослушивания"
|
||||||
"cloneInboundOk" = "Клонировано"
|
"cloneInboundOk" = "Клонировано"
|
||||||
"resetAllTraffic" = "Сбросить трафик всех подключений"
|
"resetAllTraffic" = "Сброс статистики всего трафика"
|
||||||
"resetAllTrafficTitle" = "Сброс трафика всех подключений"
|
"resetAllTrafficTitle" = "Сброс трафика всех подключений"
|
||||||
"resetAllTrafficContent" = "Вы уверены, что хотите сбросить трафик всех подключений?"
|
"resetAllTrafficContent" = "Вы уверены, что хотите сбросить трафик всех входящих подключений?"
|
||||||
"resetInboundClientTraffics" = "Сбросить трафик пользователей"
|
"resetInboundClientTraffics" = "Сброс входящего трафика клиента"
|
||||||
"resetInboundClientTrafficTitle" = "Сброс трафика пользователей"
|
"resetInboundClientTrafficTitle" = "Сброс трафика клиентов"
|
||||||
"resetInboundClientTrafficContent" = "Вы уверены, что хотите сбросить весь трафик для этих пользователей?"
|
"resetInboundClientTrafficContent" = "Вы уверены, что хотите сбросить весь трафик для этих клиентов?"
|
||||||
"resetAllClientTraffics" = "Сбросить трафик всех пользователей"
|
"resetAllClientTraffics" = "Сброс трафик всех клиентов"
|
||||||
"resetAllClientTrafficTitle" = "Сброс трафика всех пользователей"
|
"resetAllClientTrafficTitle" = "Сброс трафика всех клиентов"
|
||||||
"resetAllClientTrafficContent" = "Вы уверены, что хотите сбросить трафик всех пользователей?"
|
"resetAllClientTrafficContent" = "Вы уверены, что хотите сбросить трафик всех клиентов?"
|
||||||
"delDepletedClients" = "Удалить отключенных пользователей"
|
"delDepletedClients" = "Удалить отключенных клиентов"
|
||||||
"delDepletedClientsTitle" = "Удаление отключенных пользователей"
|
"delDepletedClientsTitle" = "Удаление отключенных клиентов"
|
||||||
"delDepletedClientsContent" = "Вы уверены, что хотите удалить всех отключенных пользователей?"
|
"delDepletedClientsContent" = "Вы уверены, что хотите удалить всех отключенных клиентов?"
|
||||||
"email" = "Email"
|
"email" = "Email"
|
||||||
"emailDesc" = "Пожалуйста, укажите уникальный Email"
|
"emailDesc" = "Пожалуйста, укажите уникальный Email"
|
||||||
"IPLimit" = "Лимит по IP"
|
"IPLimit" = "Лимит по IP"
|
||||||
"IPLimitDesc" = "Ограничение количества подключений с одного IP (0 – отключить)"
|
"IPLimitDesc" = "Ограничение количества подключений с одного IP (0 – отключить)"
|
||||||
"IPLimitlog" = "Лог IP-адресов"
|
"IPLimitlog" = "Лог IP-адресов"
|
||||||
"IPLimitlogDesc" = "Лог IP-адресов (перед включением лога IP-адресов, вы должны очистить список)"
|
"IPLimitlogDesc" = "Лог IP-адресов (перед включением лога IP-адресов, вы должны очистить список)"
|
||||||
"IPLimitlogclear" = "Очистить лог"
|
"IPLimitlogclear" = "Очистить журнал"
|
||||||
"setDefaultCert" = "Установить сертификат с панели"
|
"setDefaultCert" = "Установить сертификат с панели"
|
||||||
"telegramDesc" = "Пожалуйста, укажите ID чата Telegram. (используйте команду '/id' в боте) или (@userinfobot)"
|
"telegramDesc" = "Пожалуйста, укажите ID чата Telegram. (используйте команду '/id' в боте) или (@userinfobot)"
|
||||||
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
|
"subscriptionDesc" = "Вы можете найти свою ссылку подписки в разделе 'Подробнее', также вы можете использовать одно и то же имя для нескольких конфигураций"
|
||||||
@@ -200,14 +210,14 @@
|
|||||||
"inboundData" = "Входящие данные"
|
"inboundData" = "Входящие данные"
|
||||||
"exportInbound" = "Экспорт входящих"
|
"exportInbound" = "Экспорт входящих"
|
||||||
"import" = "Импортировать"
|
"import" = "Импортировать"
|
||||||
"importInbound" = "Импортировать подключение"
|
"importInbound" = "Импорт входящего подключения"
|
||||||
|
|
||||||
[pages.client]
|
[pages.client]
|
||||||
"add" = "Добавить пользователя"
|
"add" = "Создать клиента"
|
||||||
"edit" = "Редактировать пользователя"
|
"edit" = "Редактировать клиента"
|
||||||
"submitAdd" = "Добавить пользователя"
|
"submitAdd" = "Добавить"
|
||||||
"submitEdit" = "Сохранить изменения"
|
"submitEdit" = "Сохранить"
|
||||||
"clientCount" = "Количество пользователей"
|
"clientCount" = "Количество клиентов"
|
||||||
"bulk" = "Добавить несколько"
|
"bulk" = "Добавить несколько"
|
||||||
"method" = "Метод"
|
"method" = "Метод"
|
||||||
"first" = "Первый"
|
"first" = "Первый"
|
||||||
@@ -344,12 +354,12 @@
|
|||||||
"proxyAndServer" = "Прокси и сервер"
|
"proxyAndServer" = "Прокси и сервер"
|
||||||
"intervals" = "Интервалы"
|
"intervals" = "Интервалы"
|
||||||
"information" = "Информация"
|
"information" = "Информация"
|
||||||
"language" = "Язык"
|
"language" = "Язык интерфейса"
|
||||||
"telegramBotLanguage" = "Язык Telegram-бота"
|
"telegramBotLanguage" = "Язык Telegram-бота"
|
||||||
|
|
||||||
[pages.xray]
|
[pages.xray]
|
||||||
"title" = "Настройки Xray"
|
"title" = "Настройки Xray"
|
||||||
"save" = "Сохранить настройки"
|
"save" = "Сохранить"
|
||||||
"restart" = "Перезапустить Xray"
|
"restart" = "Перезапустить Xray"
|
||||||
"basicTemplate" = "Базовый шаблон"
|
"basicTemplate" = "Базовый шаблон"
|
||||||
"advancedTemplate" = "Расширенный шаблон"
|
"advancedTemplate" = "Расширенный шаблон"
|
||||||
@@ -358,16 +368,16 @@
|
|||||||
"logConfigs" = "Журнал"
|
"logConfigs" = "Журнал"
|
||||||
"logConfigsDesc" = "Логи могут замедлять работу сервера. Включайте только нужные вам виды логов при необходимости!"
|
"logConfigsDesc" = "Логи могут замедлять работу сервера. Включайте только нужные вам виды логов при необходимости!"
|
||||||
"blockConfigs" = "Блокировка конфигураций"
|
"blockConfigs" = "Блокировка конфигураций"
|
||||||
"blockConfigsDesc" = "Эти параметры не позволят пользователям подключаться к определенным протоколам и веб-сайтам"
|
"blockConfigsDesc" = "Эти параметры не позволят клиентам подключаться к определенным протоколам и веб-сайтам"
|
||||||
"basicRouting" = "Базовые соединения"
|
"basicRouting" = "Базовые соединения"
|
||||||
"blockConnectionsConfigsDesc" = "Эти параметры будут блокировать трафик в зависимости от запрашиваемой страны."
|
"blockConnectionsConfigsDesc" = "Эти параметры будут блокировать трафик в зависимости от запрашиваемой страны."
|
||||||
"directConnectionsConfigsDesc" = "Прямое соединение гарантирует, что определенный трафик не будет перенаправлен через другой сервер."
|
"directConnectionsConfigsDesc" = "Прямое соединение гарантирует, что определенный трафик не будет перенаправлен через другой сервер."
|
||||||
"blockips" = "Блокировать IP-адреса"
|
"blockips" = "Заблокированные IP-адреса"
|
||||||
"blockdomains" = "Блокировать домены"
|
"blockdomains" = "Заблокированные домены"
|
||||||
"directips" = "Прямые IP-адреса"
|
"directips" = "Прямые IP-адреса"
|
||||||
"directdomains" = "Прямые домены"
|
"directdomains" = "Прямые домены"
|
||||||
"ipv4Routing" = "Правила IPv4"
|
"ipv4Routing" = "Правила IPv4"
|
||||||
"ipv4RoutingDesc" = "Эти параметры позволят пользователям маршрутизироваться к целевым доменам только через IPv4"
|
"ipv4RoutingDesc" = "Эти параметры позволят клиентам маршрутизироваться к целевым доменам только через IPv4"
|
||||||
"warpRouting" = "Правила WARP"
|
"warpRouting" = "Правила WARP"
|
||||||
"warpRoutingDesc" = "Внимание: перед использованием этих параметров установите WARP в режиме прокси-сервера socks5 на свой сервер, следуя инструкциям на GitHub панели. WARP будет направлять трафик на веб-сайты через серверы Cloudflare"
|
"warpRoutingDesc" = "Внимание: перед использованием этих параметров установите WARP в режиме прокси-сервера socks5 на свой сервер, следуя инструкциям на GitHub панели. WARP будет направлять трафик на веб-сайты через серверы Cloudflare"
|
||||||
"Template" = "Шаблон конфигурации Xray"
|
"Template" = "Шаблон конфигурации Xray"
|
||||||
@@ -376,16 +386,16 @@
|
|||||||
"FreedomStrategyDesc" = "Установка стратегии вывода сети в протоколе Freedom"
|
"FreedomStrategyDesc" = "Установка стратегии вывода сети в протоколе Freedom"
|
||||||
"RoutingStrategy" = "Настройка стратегии маршрутизации доменов"
|
"RoutingStrategy" = "Настройка стратегии маршрутизации доменов"
|
||||||
"RoutingStrategyDesc" = "Установка общей стратегии маршрутизации разрешения DNS"
|
"RoutingStrategyDesc" = "Установка общей стратегии маршрутизации разрешения DNS"
|
||||||
"Torrent" = "Запрет использования BitTorrent"
|
"Torrent" = "Заблокировать BitTorrent"
|
||||||
"TorrentDesc" = "Изменение шаблона конфигурации для предупреждения использования BitTorrent пользователями"
|
"TorrentDesc" = "Запретить входящий/исходящий трафик, в котором фигурирует протокол BitTorrent"
|
||||||
"Family" = "Блокируйте вредоносное ПО и контент для взрослых"
|
"Family" = "Семейный режим"
|
||||||
"FamilyDesc" = "DNS-преобразователи Cloudflare для блокировки вредоносного ПО и контента для взрослых в целях защиты семьи."
|
"FamilyDesc" = "Использовать DNS-сервера Cloudflare для блокировки вредоносного ПО и контента для взрослых в целях защиты семьи."
|
||||||
"Inbounds" = "Входящие"
|
"Inbounds" = "Входящее соединение"
|
||||||
"InboundsDesc" = "Изменение шаблона конфигурации для подключения определенных пользователей"
|
"InboundsDesc" = "Изменение шаблона конфигурации для подключения определенных клиентов"
|
||||||
"Outbounds" = "Исходящие"
|
"Outbounds" = "Исходящее соединение"
|
||||||
"Balancers" = "Балансировщик нагрузки"
|
"Balancers" = "Балансировщик"
|
||||||
"OutboundsDesc" = "Изменение шаблона конфигурации, чтобы определить исходящие пути для этого сервера"
|
"OutboundsDesc" = "Изменение шаблона конфигурации, чтобы определить исходящие пути для этого сервера"
|
||||||
"Routings" = "Правила маршрутизации"
|
"Routings" = "Маршрутизация"
|
||||||
"RoutingsDesc" = "Важен приоритет каждого правила!"
|
"RoutingsDesc" = "Важен приоритет каждого правила!"
|
||||||
"completeTemplate" = "Все"
|
"completeTemplate" = "Все"
|
||||||
"logLevel" = "Уровень журнала"
|
"logLevel" = "Уровень журнала"
|
||||||
@@ -394,9 +404,9 @@
|
|||||||
"accessLogDesc" = "Путь к файлу журнала доступа. Специальное значение «none» отключает журналы доступа."
|
"accessLogDesc" = "Путь к файлу журнала доступа. Специальное значение «none» отключает журналы доступа."
|
||||||
"errorLog" = "Журнал ошибок"
|
"errorLog" = "Журнал ошибок"
|
||||||
"errorLogDesc" = "Путь к файлу журнала ошибок. Специальное значение «none» отключает журналы ошибок."
|
"errorLogDesc" = "Путь к файлу журнала ошибок. Специальное значение «none» отключает журналы ошибок."
|
||||||
"dnsLog" = "DNS Журнал"
|
"dnsLog" = "Журнал DNS"
|
||||||
"dnsLogDesc" = "Включить логи запросов DNS"
|
"dnsLogDesc" = "Включить логи запросов DNS"
|
||||||
"maskAddress" = "Маскировать Адрес"
|
"maskAddress" = "Маскировка адреса"
|
||||||
"maskAddressDesc" = "При активации реальный IP-адрес заменяется на маскировочный в логах."
|
"maskAddressDesc" = "При активации реальный IP-адрес заменяется на маскировочный в логах."
|
||||||
"statistics" = "Статистика"
|
"statistics" = "Статистика"
|
||||||
"statsInboundUplink" = "Статистика входящего аплинка"
|
"statsInboundUplink" = "Статистика входящего аплинка"
|
||||||
@@ -411,23 +421,23 @@
|
|||||||
[pages.xray.rules]
|
[pages.xray.rules]
|
||||||
"first" = "Первый"
|
"first" = "Первый"
|
||||||
"last" = "Последний"
|
"last" = "Последний"
|
||||||
"up" = "Вверх"
|
"up" = "Поднять вверх"
|
||||||
"down" = "Вниз"
|
"down" = "Опустить вниз"
|
||||||
"source" = "Источник"
|
"source" = "Источник"
|
||||||
"dest" = "Пункт назначения"
|
"dest" = "Пункт назначения"
|
||||||
"inbound" = "Входящий"
|
"inbound" = "Входящее соединение"
|
||||||
"outbound" = "Исходящий"
|
"outbound" = "Исходящее соединение"
|
||||||
"balancer" = "Балансировщик"
|
"balancer" = "Балансировщик"
|
||||||
"info" = "Информация"
|
"info" = "Информация"
|
||||||
"add" = "Добавить правило"
|
"add" = "Создать правило"
|
||||||
"edit" = "Редактировать правило"
|
"edit" = "Редактировать правило"
|
||||||
"useComma" = "Элементы, разделённые запятыми"
|
"useComma" = "Элементы, разделённые запятыми"
|
||||||
|
|
||||||
[pages.xray.outbound]
|
[pages.xray.outbound]
|
||||||
"addOutbound" = "Добавить исходящий"
|
"addOutbound" = "Создать исходящее соединение"
|
||||||
"addReverse" = "Добавить реверс"
|
"addReverse" = "Создать обратный прокси"
|
||||||
"editOutbound" = "Изменить исходящий"
|
"editOutbound" = "Изменить исходящее соединение"
|
||||||
"editReverse" = "Редактировать реверс"
|
"editReverse" = "Редактировать обратное прокси"
|
||||||
"tag" = "Тег"
|
"tag" = "Тег"
|
||||||
"tagDesc" = "Уникальный тег"
|
"tagDesc" = "Уникальный тег"
|
||||||
"address" = "Адрес"
|
"address" = "Адрес"
|
||||||
@@ -444,7 +454,7 @@
|
|||||||
"sendThrough" = "Отправить через"
|
"sendThrough" = "Отправить через"
|
||||||
|
|
||||||
[pages.xray.balancer]
|
[pages.xray.balancer]
|
||||||
"addBalancer" = "Добавить балансировщик"
|
"addBalancer" = "Создать балансировщик"
|
||||||
"editBalancer" = "Редактировать балансировщик"
|
"editBalancer" = "Редактировать балансировщик"
|
||||||
"balancerStrategy" = "Стратегия"
|
"balancerStrategy" = "Стратегия"
|
||||||
"balancerSelectors" = "Селекторы"
|
"balancerSelectors" = "Селекторы"
|
||||||
@@ -463,7 +473,7 @@
|
|||||||
[pages.xray.dns]
|
[pages.xray.dns]
|
||||||
"enable" = "Включить DNS"
|
"enable" = "Включить DNS"
|
||||||
"enableDesc" = "Включить встроенный DNS-сервер"
|
"enableDesc" = "Включить встроенный DNS-сервер"
|
||||||
"tag" = "Входящий тег DNS"
|
"tag" = "Название тега DNS"
|
||||||
"tagDesc" = "Этот тег будет доступен как входящий тег в правилах маршрутизации."
|
"tagDesc" = "Этот тег будет доступен как входящий тег в правилах маршрутизации."
|
||||||
"clientIp" = "IP клиента"
|
"clientIp" = "IP клиента"
|
||||||
"clientIpDesc" = "Используется для уведомления сервера о указанном местоположении IP во время DNS-запросов"
|
"clientIpDesc" = "Используется для уведомления сервера о указанном местоположении IP во время DNS-запросов"
|
||||||
@@ -475,14 +485,14 @@
|
|||||||
"disableFallbackIfMatchDesc" = "Отключает резервные DNS-запросы при совпадении списка доменов DNS-сервера"
|
"disableFallbackIfMatchDesc" = "Отключает резервные DNS-запросы при совпадении списка доменов DNS-сервера"
|
||||||
"strategy" = "Стратегия запроса"
|
"strategy" = "Стратегия запроса"
|
||||||
"strategyDesc" = "Общая стратегия разрешения доменных имен"
|
"strategyDesc" = "Общая стратегия разрешения доменных имен"
|
||||||
"add" = "Добавить сервер"
|
"add" = "Создать DNS"
|
||||||
"edit" = "Редактировать сервер"
|
"edit" = "Редактировать DNS"
|
||||||
"domains" = "Домены"
|
"domains" = "Домены"
|
||||||
"expectIPs" = "Ожидаемые IP"
|
"expectIPs" = "Ожидаемые IP"
|
||||||
|
|
||||||
[pages.xray.fakedns]
|
[pages.xray.fakedns]
|
||||||
"add" = "Добавить поддельный DNS"
|
"add" = "Создать Fake DNS"
|
||||||
"edit" = "Редактировать поддельный DNS"
|
"edit" = "Редактировать Fake DNS"
|
||||||
"ipPool" = "Подсеть пула IP"
|
"ipPool" = "Подсеть пула IP"
|
||||||
"poolSize" = "Размер пула"
|
"poolSize" = "Размер пула"
|
||||||
|
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ Да"
|
"yes" = "✅ Да"
|
||||||
"no" = "❌ Нет"
|
"no" = "❌ Нет"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 ID обновлён."
|
||||||
|
"received_password" = "🔑📥 Пароль обновлён."
|
||||||
|
"received_email" = "📧📥 Электронная почта обновлена."
|
||||||
|
"received_comment" = "💬📥 Комментарий обновлён."
|
||||||
|
"id_prompt" = "🔑 Стандартный ID: {{ .ClientId }}\n\nВведите ваш ID."
|
||||||
|
"pass_prompt" = "🔑 Стандартный пароль: {{ .ClientPassword }}\n\nВведите ваш пароль."
|
||||||
|
"email_prompt" = "📧 Стандартный email: {{ .ClientEmail }}\n\nВведите ваш email."
|
||||||
|
"comment_prompt" = "💬 Стандартный комментарий: {{ .ClientComment }}\n\nВведите ваш комментарий."
|
||||||
|
"inbound_client_data_id" = "🔄 Входящие: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата истечения: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента во входящие!"
|
||||||
|
"inbound_client_data_pass" = "🔄 Входящие: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафик: {{ .ClientTraffic }}\n📅 Дата истечения: {{ .ClientExp }}\n💬 Комментарий: {{ .ClientComment }}\n\nТеперь вы можете добавить клиента во входящие!"
|
||||||
|
"cancel" = "❌ Процесс отменён! \n\nВы можете снова начать с /start в любое время. 🔄"
|
||||||
|
"error_add_client" = "⚠️ Ошибка:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "Хорошо, оставлю значение по умолчанию. 😊"
|
||||||
|
"incorrect_input" ="Ваш ввод недействителен.\nФразы должны быть непрерывными без пробелов.\nПравильный пример: aaaaaa\nНеправильный пример: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ Закрыть клавиатуру"
|
"closeKeyboard" = "❌ Закрыть клавиатуру"
|
||||||
"cancel" = "❌ Отмена"
|
"cancel" = "❌ Отмена"
|
||||||
@@ -615,9 +641,18 @@
|
|||||||
"confirmNumber" = "✅ Подтвердить: {{ .Num }}"
|
"confirmNumber" = "✅ Подтвердить: {{ .Num }}"
|
||||||
"confirmNumberAdd" = "✅ Подтвердить добавление: {{ .Num }}"
|
"confirmNumberAdd" = "✅ Подтвердить добавление: {{ .Num }}"
|
||||||
"limitTraffic" = "🚧 Лимит трафика"
|
"limitTraffic" = "🚧 Лимит трафика"
|
||||||
"getBanLogs" = "Логи блокировок"
|
"getBanLogs" = "Журнал блокировок"
|
||||||
"allClients" = "Все клиенты"
|
"allClients" = "Все клиенты"
|
||||||
|
|
||||||
|
"addClient" = "Добавить клиента"
|
||||||
|
"submitDisable" = "Отправить отключенным ✅"
|
||||||
|
"use_default" = "🏷️ Использовать по умолчанию"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 Пароль"
|
||||||
|
"change_email" = "⚙️📧 Электронная почта"
|
||||||
|
"change_comment" = "⚙️💬 Комментарий"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ Успешно!"
|
"successfulOperation" = "✅ Успешно!"
|
||||||
"errorOperation" = "❗ Ошибка в операции."
|
"errorOperation" = "❗ Ошибка в операции."
|
||||||
@@ -638,5 +673,5 @@
|
|||||||
"enableSuccess" = "✅ {{ .Email }}: Включено успешно."
|
"enableSuccess" = "✅ {{ .Email }}: Включено успешно."
|
||||||
"disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
|
"disableSuccess" = "✅ {{ .Email }}: Отключено успешно."
|
||||||
"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <code>{{ .TgUserID }}</code>"
|
"askToAddUserId" = "Ваша конфигурация не найдена!\r\nПожалуйста, попросите администратора использовать ваш идентификатор пользователя Telegram в ваших конфигурациях.\r\n\r\nВаш идентификатор пользователя: <code>{{ .TgUserID }}</code>"
|
||||||
"chooseClient" = "Выберите пользователя для подключения {{ .Inbound }}"
|
"chooseClient" = "Выберите клиента для подключения {{ .Inbound }}"
|
||||||
"chooseInbound" = "Выберите подключение"
|
"chooseInbound" = "Выберите подключение"
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Genel Bakış"
|
"title" = "Genel Bakış"
|
||||||
|
"cpu" = "İşlemci"
|
||||||
|
"logicalProcessors" = "Mantıksal işlemciler"
|
||||||
|
"frequency" = "Frekans"
|
||||||
|
"swap" = "Takas"
|
||||||
|
"storage" = "Depolama"
|
||||||
"memory" = "RAM"
|
"memory" = "RAM"
|
||||||
"hard" = "Disk"
|
"threads" = "İş parçacıkları"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "Durdur"
|
"stopXray" = "Durdur"
|
||||||
"restartXray" = "Yeniden Başlat"
|
"restartXray" = "Yeniden Başlat"
|
||||||
"xraySwitch" = "Sürüm"
|
"xraySwitch" = "Sürüm"
|
||||||
"xraySwitchClick" = "Geçiş yapmak istediğiniz sürümü seçin."
|
"xraySwitchClick" = "Geçiş yapmak istediğiniz sürümü seçin."
|
||||||
"xraySwitchClickDesk" = "Dikkatli seçin, eski sürümler mevcut yapılandırmalarla uyumlu olmayabilir."
|
"xraySwitchClickDesk" = "Dikkatli seçin, eski sürümler mevcut yapılandırmalarla uyumlu olmayabilir."
|
||||||
|
"xrayStatusUnknown" = "Bilinmiyor"
|
||||||
|
"xrayStatusRunning" = "Çalışıyor"
|
||||||
|
"xrayStatusStop" = "Durduruldu"
|
||||||
|
"xrayStatusError" = "Hata"
|
||||||
|
"xrayErrorPopoverTitle" = "Xray çalıştırılırken bir hata oluştu"
|
||||||
"operationHours" = "Çalışma Süresi"
|
"operationHours" = "Çalışma Süresi"
|
||||||
"systemLoad" = "Sistem Yükü"
|
"systemLoad" = "Sistem Yükü"
|
||||||
"systemLoadDesc" = "Geçmiş 1, 5 ve 15 dakika için sistem yük ortalaması"
|
"systemLoadDesc" = "Geçmiş 1, 5 ve 15 dakika için sistem yük ortalaması"
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ Evet"
|
"yes" = "✅ Evet"
|
||||||
"no" = "❌ Hayır"
|
"no" = "❌ Hayır"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 Kimlik güncellendi."
|
||||||
|
"received_password" = "🔑📥 Şifre güncellendi."
|
||||||
|
"received_email" = "📧📥 E-posta güncellendi."
|
||||||
|
"received_comment" = "💬📥 Yorum güncellendi."
|
||||||
|
"id_prompt" = "🔑 Varsayılan Kimlik: {{ .ClientId }}\n\nKimliğinizi girin."
|
||||||
|
"pass_prompt" = "🔑 Varsayılan Şifre: {{ .ClientPassword }}\n\nŞifrenizi girin."
|
||||||
|
"email_prompt" = "📧 Varsayılan E-posta: {{ .ClientEmail }}\n\nE-postanızı girin."
|
||||||
|
"comment_prompt" = "💬 Varsayılan Yorum: {{ .ClientComment }}\n\nYorumunuzu girin."
|
||||||
|
"inbound_client_data_id" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Son Kullanım Tarihi: {{ .ClientExp }}\n💬 Yorum: {{ .ClientComment }}\n\nŞimdi müşteriyi girişe ekleyebilirsiniz!"
|
||||||
|
"inbound_client_data_pass" = "🔄 Giriş: {{ .InboundRemark }}\n\n🔑 Şifre: {{ .ClientPass }}\n📧 E-posta: {{ .ClientEmail }}\n📊 Trafik: {{ .ClientTraffic }}\n📅 Son Kullanım Tarihi: {{ .ClientExp }}\n💬 Yorum: {{ .ClientComment }}\n\nŞimdi müşteriyi girişe ekleyebilirsiniz!"
|
||||||
|
"cancel" = "❌ İşlem iptal edildi! \n\nİstediğiniz zaman /start ile yeniden başlayabilirsiniz. 🔄"
|
||||||
|
"error_add_client" = "⚠️ Hata:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "Tamam, varsayılan değeri kullanacağım. 😊"
|
||||||
|
"incorrect_input" ="Girdiğiniz değer geçerli değil.\nKelime öbekleri boşluk olmadan devam etmelidir.\nDoğru örnek: aaaaaa\nYanlış örnek: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ Klavyeyi Kapat"
|
"closeKeyboard" = "❌ Klavyeyi Kapat"
|
||||||
"cancel" = "❌ İptal"
|
"cancel" = "❌ İptal"
|
||||||
@@ -618,6 +644,15 @@
|
|||||||
"getBanLogs" = "Yasak Günlüklerini Al"
|
"getBanLogs" = "Yasak Günlüklerini Al"
|
||||||
"allClients" = "Tüm Müşteriler"
|
"allClients" = "Tüm Müşteriler"
|
||||||
|
|
||||||
|
"addClient" = "Müşteri Ekle"
|
||||||
|
"submitDisable" = "Devre Dışı Olarak Gönder ✅"
|
||||||
|
"use_default" = "🏷️ Varsayılanı Kullan"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 Şifre"
|
||||||
|
"change_email" = "⚙️📧 E-posta"
|
||||||
|
"change_comment" = "⚙️💬 Yorum"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ İşlem başarılı!"
|
"successfulOperation" = "✅ İşlem başarılı!"
|
||||||
"errorOperation" = "❗ İşlemde hata."
|
"errorOperation" = "❗ İşlemde hata."
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Огляд"
|
"title" = "Огляд"
|
||||||
"memory" = "Пам'ять"
|
"cpu" = "ЦП"
|
||||||
"hard" = "Диск"
|
"logicalProcessors" = "Логічні процесори"
|
||||||
|
"frequency" = "Частота"
|
||||||
|
"swap" = "Своп"
|
||||||
|
"storage" = "Сховище"
|
||||||
|
"memory" = "ОЗП"
|
||||||
|
"threads" = "Потоки"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "Зупинити"
|
"stopXray" = "Зупинити"
|
||||||
"restartXray" = "Перезапустити"
|
"restartXray" = "Перезапустити"
|
||||||
"xraySwitch" = "Версія"
|
"xraySwitch" = "Версія"
|
||||||
"xraySwitchClick" = "Виберіть версію, на яку ви хочете перейти."
|
"xraySwitchClick" = "Виберіть версію, на яку ви хочете перейти."
|
||||||
"xraySwitchClickDesk" = "Вибирайте уважно, оскільки старіші версії можуть бути несумісними з поточними конфігураціями."
|
"xraySwitchClickDesk" = "Вибирайте уважно, оскільки старіші версії можуть бути несумісними з поточними конфігураціями."
|
||||||
|
"xrayStatusUnknown" = "Невідомо"
|
||||||
|
"xrayStatusRunning" = "Запущено"
|
||||||
|
"xrayStatusStop" = "Зупинено"
|
||||||
|
"xrayStatusError" = "Помилка"
|
||||||
|
"xrayErrorPopoverTitle" = "Під час роботи Xray сталася помилка"
|
||||||
"operationHours" = "Час роботи"
|
"operationHours" = "Час роботи"
|
||||||
"systemLoad" = "Завантаження системи"
|
"systemLoad" = "Завантаження системи"
|
||||||
"systemLoadDesc" = "Середнє завантаження системи за останні 1, 5 і 15 хвилин"
|
"systemLoadDesc" = "Середнє завантаження системи за останні 1, 5 і 15 хвилин"
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ Так"
|
"yes" = "✅ Так"
|
||||||
"no" = "❌ Ні"
|
"no" = "❌ Ні"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 ID оновлено."
|
||||||
|
"received_password" = "🔑📥 Пароль оновлено."
|
||||||
|
"received_email" = "📧📥 Електронна пошта оновлена."
|
||||||
|
"received_comment" = "💬📥 Коментар оновлено."
|
||||||
|
"id_prompt" = "🔑 Стандартний ID: {{ .ClientId }}\n\nВведіть ваш ID."
|
||||||
|
"pass_prompt" = "🔑 Стандартний пароль: {{ .ClientPassword }}\n\nВведіть ваш пароль."
|
||||||
|
"email_prompt" = "📧 Стандартний email: {{ .ClientEmail }}\n\nВведіть ваш email."
|
||||||
|
"comment_prompt" = "💬 Стандартний коментар: {{ .ClientComment }}\n\nВведіть ваш коментар."
|
||||||
|
"inbound_client_data_id" = "🔄 Вхідні: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Термін придатності: {{ .ClientExp }}\n💬 Коментар: {{ .ClientComment }}\n\nТепер ви можете додати клієнта до вхідних!"
|
||||||
|
"inbound_client_data_pass" = "🔄 Вхідні: {{ .InboundRemark }}\n\n🔑 Пароль: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Трафік: {{ .ClientTraffic }}\n📅 Термін придатності: {{ .ClientExp }}\n💬 Коментар: {{ .ClientComment }}\n\nТепер ви можете додати клієнта до вхідних!"
|
||||||
|
"cancel" = "❌ Процес скасовано! \n\nВи можете знову розпочати, використовуючи /start у будь-який час. 🔄"
|
||||||
|
"error_add_client" = "⚠️ Помилка:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "Гаразд, залишу значення за замовчуванням. 😊"
|
||||||
|
"incorrect_input" ="Ваш ввід невірний.\nФрази повинні бути без пробілів.\nПравильний приклад: aaaaaa\nНеправильний приклад: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ Закрити клавіатуру"
|
"closeKeyboard" = "❌ Закрити клавіатуру"
|
||||||
"cancel" = "❌ Скасувати"
|
"cancel" = "❌ Скасувати"
|
||||||
@@ -618,6 +644,15 @@
|
|||||||
"getBanLogs" = "Отримати журнали заборон"
|
"getBanLogs" = "Отримати журнали заборон"
|
||||||
"allClients" = "Всі Клієнти"
|
"allClients" = "Всі Клієнти"
|
||||||
|
|
||||||
|
"addClient" = "Додати клієнта"
|
||||||
|
"submitDisable" = "Надіслати відключеним ✅"
|
||||||
|
"use_default" = "🏷️ Використовувати за замовчуванням"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 Пароль"
|
||||||
|
"change_email" = "⚙️📧 Електронна пошта"
|
||||||
|
"change_comment" = "⚙️💬 Коментар"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ Операція успішна!"
|
"successfulOperation" = "✅ Операція успішна!"
|
||||||
"errorOperation" = "❗ Помилка в роботі."
|
"errorOperation" = "❗ Помилка в роботі."
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "Trạng thái hệ thống"
|
"title" = "Trạng thái hệ thống"
|
||||||
"memory" = "Ram"
|
"cpu" = "CPU"
|
||||||
"hard" = "Dung lượng"
|
"logicalProcessors" = "Bộ xử lý logic"
|
||||||
|
"frequency" = "Tần số"
|
||||||
|
"swap" = "Swap"
|
||||||
|
"storage" = "Lưu trữ"
|
||||||
|
"memory" = "RAM"
|
||||||
|
"threads" = "Luồng"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "Dừng lại"
|
"stopXray" = "Dừng lại"
|
||||||
"restartXray" = "Khởi động lại"
|
"restartXray" = "Khởi động lại"
|
||||||
"xraySwitch" = "Phiên bản"
|
"xraySwitch" = "Phiên bản"
|
||||||
"xraySwitchClick" = "Chọn phiên bản mà bạn muốn chuyển đổi sang."
|
"xraySwitchClick" = "Chọn phiên bản mà bạn muốn chuyển đổi sang."
|
||||||
"xraySwitchClickDesk" = "Hãy lựa chọn thận trọng, vì các phiên bản cũ có thể không tương thích với các cấu hình hiện tại."
|
"xraySwitchClickDesk" = "Hãy lựa chọn thận trọng, vì các phiên bản cũ có thể không tương thích với các cấu hình hiện tại."
|
||||||
|
"xrayStatusUnknown" = "Không xác định"
|
||||||
|
"xrayStatusRunning" = "Đang chạy"
|
||||||
|
"xrayStatusStop" = "Dừng"
|
||||||
|
"xrayStatusError" = "Lỗi"
|
||||||
|
"xrayErrorPopoverTitle" = "Đã xảy ra lỗi khi chạy Xray"
|
||||||
"operationHours" = "Thời gian hoạt động"
|
"operationHours" = "Thời gian hoạt động"
|
||||||
"systemLoad" = "Tải hệ thống"
|
"systemLoad" = "Tải hệ thống"
|
||||||
"systemLoadDesc" = "trung bình tải hệ thống trong 1, 5 và 15 phút qua"
|
"systemLoadDesc" = "trung bình tải hệ thống trong 1, 5 và 15 phút qua"
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ Có"
|
"yes" = "✅ Có"
|
||||||
"no" = "❌ Không"
|
"no" = "❌ Không"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 ID đã được cập nhật."
|
||||||
|
"received_password" = "🔑📥 Mật khẩu đã được cập nhật."
|
||||||
|
"received_email" = "📧📥 Email đã được cập nhật."
|
||||||
|
"received_comment" = "💬📥 Bình luận đã được cập nhật."
|
||||||
|
"id_prompt" = "🔑 ID mặc định: {{ .ClientId }}\n\nVui lòng nhập ID của bạn."
|
||||||
|
"pass_prompt" = "🔑 Mật khẩu mặc định: {{ .ClientPassword }}\n\nVui lòng nhập mật khẩu của bạn."
|
||||||
|
"email_prompt" = "📧 Email mặc định: {{ .ClientEmail }}\n\nVui lòng nhập email của bạn."
|
||||||
|
"comment_prompt" = "💬 Bình luận mặc định: {{ .ClientComment }}\n\nVui lòng nhập bình luận của bạn."
|
||||||
|
"inbound_client_data_id" = "🔄 Vào: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 Email: {{ .ClientEmail }}\n📊 Lưu lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n💬 Bình luận: {{ .ClientComment }}\n\nBạn có thể thêm khách hàng vào vào ngay bây giờ!"
|
||||||
|
"inbound_client_data_pass" = "🔄 Vào: {{ .InboundRemark }}\n\n🔑 Mật khẩu: {{ .ClientPass }}\n📧 Email: {{ .ClientEmail }}\n📊 Lưu lượng: {{ .ClientTraffic }}\n📅 Ngày hết hạn: {{ .ClientExp }}\n💬 Bình luận: {{ .ClientComment }}\n\nBạn có thể thêm khách hàng vào vào ngay bây giờ!"
|
||||||
|
"cancel" = "❌ Quá trình đã bị hủy! \n\nBạn có thể bắt đầu lại bất cứ lúc nào bằng cách nhập /start. 🔄"
|
||||||
|
"error_add_client" = "⚠️ Lỗi:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "Được rồi, tôi sẽ sử dụng giá trị mặc định. 😊"
|
||||||
|
"incorrect_input" ="Dữ liệu bạn nhập không hợp lệ.\nCác chuỗi phải liền mạch và không có dấu cách.\nVí dụ đúng: aaaaaa\nVí dụ sai: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ Đóng Bàn Phím"
|
"closeKeyboard" = "❌ Đóng Bàn Phím"
|
||||||
"cancel" = "❌ Hủy"
|
"cancel" = "❌ Hủy"
|
||||||
@@ -618,6 +644,15 @@
|
|||||||
"getBanLogs" = "Cấm nhật ký"
|
"getBanLogs" = "Cấm nhật ký"
|
||||||
"allClients" = "Tất cả Khách hàng"
|
"allClients" = "Tất cả Khách hàng"
|
||||||
|
|
||||||
|
"addClient" = "Thêm Khách Hàng"
|
||||||
|
"submitDisable" = "Gửi Dưới Dạng Tắt ✅"
|
||||||
|
"use_default" = "🏷️ Sử dụng mặc định"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 Mật khẩu"
|
||||||
|
"change_email" = "⚙️📧 Email"
|
||||||
|
"change_comment" = "⚙️💬 Bình luận"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ Thành công!"
|
"successfulOperation" = "✅ Thành công!"
|
||||||
"errorOperation" = "❗ Lỗi Trong Quá Trình Thực Hiện."
|
"errorOperation" = "❗ Lỗi Trong Quá Trình Thực Hiện."
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "系统状态"
|
"title" = "系统状态"
|
||||||
|
"cpu" = "CPU"
|
||||||
|
"logicalProcessors" = "逻辑处理器"
|
||||||
|
"frequency" = "频率"
|
||||||
|
"swap" = "交换分区"
|
||||||
|
"storage" = "存储"
|
||||||
"memory" = "内存"
|
"memory" = "内存"
|
||||||
"hard" = "磁盘"
|
"threads" = "线程"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "停止"
|
"stopXray" = "停止"
|
||||||
"restartXray" = "重启"
|
"restartXray" = "重启"
|
||||||
"xraySwitch" = "版本"
|
"xraySwitch" = "版本"
|
||||||
"xraySwitchClick" = "选择你要切换到的版本"
|
"xraySwitchClick" = "选择你要切换到的版本"
|
||||||
"xraySwitchClickDesk" = "请谨慎选择,因为较旧版本可能与当前配置不兼容"
|
"xraySwitchClickDesk" = "请谨慎选择,因为较旧版本可能与当前配置不兼容"
|
||||||
|
"xrayStatusUnknown" = "未知"
|
||||||
|
"xrayStatusRunning" = "运行中"
|
||||||
|
"xrayStatusStop" = "停止"
|
||||||
|
"xrayStatusError" = "错误"
|
||||||
|
"xrayErrorPopoverTitle" = "运行Xray时发生错误"
|
||||||
"operationHours" = "系统正常运行时间"
|
"operationHours" = "系统正常运行时间"
|
||||||
"systemLoad" = "系统负载"
|
"systemLoad" = "系统负载"
|
||||||
"systemLoadDesc" = "过去 1、5 和 15 分钟的系统平均负载"
|
"systemLoadDesc" = "过去 1、5 和 15 分钟的系统平均负载"
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ 是的"
|
"yes" = "✅ 是的"
|
||||||
"no" = "❌ 没有"
|
"no" = "❌ 没有"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 ID 已更新。"
|
||||||
|
"received_password" = "🔑📥 密码已更新。"
|
||||||
|
"received_email" = "📧📥 邮箱已更新。"
|
||||||
|
"received_comment" = "💬📥 评论已更新。"
|
||||||
|
"id_prompt" = "🔑 默认 ID: {{ .ClientId }}\n\n请输入您的 ID。"
|
||||||
|
"pass_prompt" = "🔑 默认密码: {{ .ClientPassword }}\n\n请输入您的密码。"
|
||||||
|
"email_prompt" = "📧 默认邮箱: {{ .ClientEmail }}\n\n请输入您的邮箱。"
|
||||||
|
"comment_prompt" = "💬 默认评论: {{ .ClientComment }}\n\n请输入您的评论。"
|
||||||
|
"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 过期日期: {{ .ClientExp }}\n💬 评论: {{ .ClientComment }}\n\n您现在可以将客户添加到入站!"
|
||||||
|
"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密码: {{ .ClientPass }}\n📧 邮箱: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 过期日期: {{ .ClientExp }}\n💬 评论: {{ .ClientComment }}\n\n您现在可以将客户添加到入站!"
|
||||||
|
"cancel" = "❌ 进程已取消!\n\n您可以随时使用 /start 重新开始。 🔄"
|
||||||
|
"error_add_client" = "⚠️ 错误:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "好的,我会使用默认值。 😊"
|
||||||
|
"incorrect_input" ="您的输入无效。\n短语应连续输入,不能有空格。\n正确示例: aaaaaa\n错误示例: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ 关闭键盘"
|
"closeKeyboard" = "❌ 关闭键盘"
|
||||||
"cancel" = "❌ 取消"
|
"cancel" = "❌ 取消"
|
||||||
@@ -618,6 +644,15 @@
|
|||||||
"getBanLogs" = "禁止日志"
|
"getBanLogs" = "禁止日志"
|
||||||
"allClients" = "所有客户"
|
"allClients" = "所有客户"
|
||||||
|
|
||||||
|
"addClient" = "添加客户"
|
||||||
|
"submitDisable" = "提交为禁用 ✅"
|
||||||
|
"use_default" = "🏷️ 使用默认"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 密码"
|
||||||
|
"change_email" = "⚙️📧 邮箱"
|
||||||
|
"change_comment" = "⚙️💬 评论"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ 成功!"
|
"successfulOperation" = "✅ 成功!"
|
||||||
"errorOperation" = "❗ 操作错误。"
|
"errorOperation" = "❗ 操作错误。"
|
||||||
|
|||||||
@@ -91,14 +91,24 @@
|
|||||||
|
|
||||||
[pages.index]
|
[pages.index]
|
||||||
"title" = "系統狀態"
|
"title" = "系統狀態"
|
||||||
|
"cpu" = "CPU"
|
||||||
|
"logicalProcessors" = "邏輯處理器"
|
||||||
|
"frequency" = "頻率"
|
||||||
|
"swap" = "交換空間"
|
||||||
|
"storage" = "儲存"
|
||||||
"memory" = "記憶體"
|
"memory" = "記憶體"
|
||||||
"hard" = "磁碟"
|
"threads" = "執行緒"
|
||||||
"xrayStatus" = "Xray"
|
"xrayStatus" = "Xray"
|
||||||
"stopXray" = "停止"
|
"stopXray" = "停止"
|
||||||
"restartXray" = "重啟"
|
"restartXray" = "重啟"
|
||||||
"xraySwitch" = "版本"
|
"xraySwitch" = "版本"
|
||||||
"xraySwitchClick" = "選擇你要切換到的版本"
|
"xraySwitchClick" = "選擇你要切換到的版本"
|
||||||
"xraySwitchClickDesk" = "請謹慎選擇,因為較舊版本可能與當前配置不相容"
|
"xraySwitchClickDesk" = "請謹慎選擇,因為較舊版本可能與當前配置不相容"
|
||||||
|
"xrayStatusUnknown" = "未知"
|
||||||
|
"xrayStatusRunning" = "運行中"
|
||||||
|
"xrayStatusStop" = "停止"
|
||||||
|
"xrayStatusError" = "錯誤"
|
||||||
|
"xrayErrorPopoverTitle" = "執行Xray時發生錯誤"
|
||||||
"operationHours" = "系統正常執行時間"
|
"operationHours" = "系統正常執行時間"
|
||||||
"systemLoad" = "系統負載"
|
"systemLoad" = "系統負載"
|
||||||
"systemLoadDesc" = "過去 1、5 和 15 分鐘的系統平均負載"
|
"systemLoadDesc" = "過去 1、5 和 15 分鐘的系統平均負載"
|
||||||
@@ -584,6 +594,22 @@
|
|||||||
"yes" = "✅ 是的"
|
"yes" = "✅ 是的"
|
||||||
"no" = "❌ 沒有"
|
"no" = "❌ 沒有"
|
||||||
|
|
||||||
|
"received_id" = "🔑📥 ID 已更新。"
|
||||||
|
"received_password" = "🔑📥 密碼已更新。"
|
||||||
|
"received_email" = "📧📥 電子郵件已更新。"
|
||||||
|
"received_comment" = "💬📥 評論已更新。"
|
||||||
|
"id_prompt" = "🔑 預設 ID: {{ .ClientId }}\n\n請輸入您的 ID。"
|
||||||
|
"pass_prompt" = "🔑 預設密碼: {{ .ClientPassword }}\n\n請輸入您的密碼。"
|
||||||
|
"email_prompt" = "📧 預設電子郵件: {{ .ClientEmail }}\n\n請輸入您的電子郵件。"
|
||||||
|
"comment_prompt" = "💬 預設評論: {{ .ClientComment }}\n\n請輸入您的評論。"
|
||||||
|
"inbound_client_data_id" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 ID: {{ .ClientId }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n💬 評論: {{ .ClientComment }}\n\n您現在可以將客戶新增至入站!"
|
||||||
|
"inbound_client_data_pass" = "🔄 入站: {{ .InboundRemark }}\n\n🔑 密碼: {{ .ClientPass }}\n📧 電子郵件: {{ .ClientEmail }}\n📊 流量: {{ .ClientTraffic }}\n📅 到期日: {{ .ClientExp }}\n💬 評論: {{ .ClientComment }}\n\n您現在可以將客戶新增至入站!"
|
||||||
|
"cancel" = "❌ 程序已取消!\n\n您可以隨時使用 /start 重新開始。 🔄"
|
||||||
|
"error_add_client" = "⚠️ 錯誤:\n\n {{ .error }}"
|
||||||
|
"using_default_value" = "好的,我會使用預設值。 😊"
|
||||||
|
"incorrect_input" ="您的輸入無效。\n短語應連續輸入,不能有空格。\n正確示例: aaaaaa\n錯誤示例: aaa aaa 🚫"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.buttons]
|
[tgbot.buttons]
|
||||||
"closeKeyboard" = "❌ 關閉鍵盤"
|
"closeKeyboard" = "❌ 關閉鍵盤"
|
||||||
"cancel" = "❌ 取消"
|
"cancel" = "❌ 取消"
|
||||||
@@ -618,6 +644,15 @@
|
|||||||
"getBanLogs" = "禁止日誌"
|
"getBanLogs" = "禁止日誌"
|
||||||
"allClients" = "所有客戶"
|
"allClients" = "所有客戶"
|
||||||
|
|
||||||
|
"addClient" = "新增客戶"
|
||||||
|
"submitDisable" = "提交為停用 ✅"
|
||||||
|
"use_default" = "🏷️ 使用預設"
|
||||||
|
"change_id" = "⚙️🔑 ID"
|
||||||
|
"change_password" = "⚙️🔑 密碼"
|
||||||
|
"change_email" = "⚙️📧 電子郵件"
|
||||||
|
"change_comment" = "⚙️💬 評論"
|
||||||
|
|
||||||
|
|
||||||
[tgbot.answers]
|
[tgbot.answers]
|
||||||
"successfulOperation" = "✅ 成功!"
|
"successfulOperation" = "✅ 成功!"
|
||||||
"errorOperation" = "❗ 操作錯誤。"
|
"errorOperation" = "❗ 操作錯誤。"
|
||||||
|
|||||||
142
x-ui.sh
142
x-ui.sh
@@ -33,85 +33,11 @@ else
|
|||||||
echo "Failed to check the system OS, please contact the author!" >&2
|
echo "Failed to check the system OS, please contact the author!" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "The OS release is: $release"
|
echo "The OS release is: $release"
|
||||||
|
|
||||||
os_version=""
|
os_version=""
|
||||||
os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.')
|
os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.')
|
||||||
|
|
||||||
if [[ "${release}" == "arch" ]]; then
|
|
||||||
echo "Your OS is Arch Linux"
|
|
||||||
elif [[ "${release}" == "parch" ]]; then
|
|
||||||
echo "Your OS is Parch Linux"
|
|
||||||
elif [[ "${release}" == "manjaro" ]]; then
|
|
||||||
echo "Your OS is Manjaro"
|
|
||||||
elif [[ "${release}" == "armbian" ]]; then
|
|
||||||
echo "Your OS is Armbian"
|
|
||||||
elif [[ "${release}" == "alpine" ]]; then
|
|
||||||
echo "Your OS is Alpine Linux"
|
|
||||||
elif [[ "${release}" == "opensuse-tumbleweed" ]]; then
|
|
||||||
echo "Your OS is OpenSUSE Tumbleweed"
|
|
||||||
elif [[ "${release}" == "openEuler" ]]; then
|
|
||||||
if [[ ${os_version} -lt 2203 ]]; then
|
|
||||||
echo -e "${red} Please use OpenEuler 22.03 or higher ${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "centos" ]]; then
|
|
||||||
if [[ ${os_version} -lt 8 ]]; then
|
|
||||||
echo -e "${red} Please use CentOS 8 or higher ${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "ubuntu" ]]; then
|
|
||||||
if [[ ${os_version} -lt 2204 ]]; then
|
|
||||||
echo -e "${red} Please use Ubuntu 22 or higher version!${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "fedora" ]]; then
|
|
||||||
if [[ ${os_version} -lt 36 ]]; then
|
|
||||||
echo -e "${red} Please use Fedora 36 or higher version!${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "amzn" ]]; then
|
|
||||||
if [[ ${os_version} != "2023" ]]; then
|
|
||||||
echo -e "${red} Please use Amazon Linux 2023!${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "debian" ]]; then
|
|
||||||
if [[ ${os_version} -lt 11 ]]; then
|
|
||||||
echo -e "${red} Please use Debian 11 or higher ${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "almalinux" ]]; then
|
|
||||||
if [[ ${os_version} -lt 80 ]]; then
|
|
||||||
echo -e "${red} Please use AlmaLinux 8.0 or higher ${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
elif [[ "${release}" == "rocky" ]]; then
|
|
||||||
if [[ ${os_version} -lt 8 ]]; then
|
|
||||||
echo -e "${red} Please use Rocky Linux 8 or higher ${plain}\n" && exit 1
|
|
||||||
fi
|
|
||||||
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:"
|
|
||||||
echo "- Ubuntu 22.04+"
|
|
||||||
echo "- Debian 11+"
|
|
||||||
echo "- CentOS 8+"
|
|
||||||
echo "- OpenEuler 22.03+"
|
|
||||||
echo "- Fedora 36+"
|
|
||||||
echo "- Arch Linux"
|
|
||||||
echo "- Parch Linux"
|
|
||||||
echo "- Manjaro"
|
|
||||||
echo "- Armbian"
|
|
||||||
echo "- AlmaLinux 8.0+"
|
|
||||||
echo "- Rocky Linux 8+"
|
|
||||||
echo "- Oracle Linux 8+"
|
|
||||||
echo "- OpenSUSE Tumbleweed"
|
|
||||||
echo "- Amazon Linux 2023"
|
|
||||||
echo "- Virtuozzo Linux 8+"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Declare Variables
|
# Declare Variables
|
||||||
log_folder="${XUI_LOG_FOLDER:=/var/log}"
|
log_folder="${XUI_LOG_FOLDER:=/var/log}"
|
||||||
iplimit_log_path="${log_folder}/3xipl.log"
|
iplimit_log_path="${log_folder}/3xipl.log"
|
||||||
@@ -119,12 +45,12 @@ iplimit_banned_log_path="${log_folder}/3xipl-banned.log"
|
|||||||
|
|
||||||
confirm() {
|
confirm() {
|
||||||
if [[ $# > 1 ]]; then
|
if [[ $# > 1 ]]; then
|
||||||
echo && read -p "$1 [Default $2]: " temp
|
echo && read -rp "$1 [Default $2]: " temp
|
||||||
if [[ "${temp}" == "" ]]; then
|
if [[ "${temp}" == "" ]]; then
|
||||||
temp=$2
|
temp=$2
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
read -p "$1 [y/n]: " temp
|
read -rp "$1 [y/n]: " temp
|
||||||
fi
|
fi
|
||||||
if [[ "${temp}" == "y" || "${temp}" == "Y" ]]; then
|
if [[ "${temp}" == "y" || "${temp}" == "Y" ]]; then
|
||||||
return 0
|
return 0
|
||||||
@@ -191,7 +117,7 @@ update_menu() {
|
|||||||
|
|
||||||
if [[ $? == 0 ]]; then
|
if [[ $? == 0 ]]; then
|
||||||
echo -e "${green}Update successful. The panel has automatically restarted.${plain}"
|
echo -e "${green}Update successful. The panel has automatically restarted.${plain}"
|
||||||
before_show_menu
|
exit 0
|
||||||
else
|
else
|
||||||
echo -e "${red}Failed to update the menu.${plain}"
|
echo -e "${red}Failed to update the menu.${plain}"
|
||||||
return 1
|
return 1
|
||||||
@@ -436,7 +362,7 @@ show_log() {
|
|||||||
echo -e "${green}\t1.${plain} Debug Log"
|
echo -e "${green}\t1.${plain} Debug Log"
|
||||||
echo -e "${green}\t2.${plain} Clear All logs"
|
echo -e "${green}\t2.${plain} Clear All logs"
|
||||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
read -p "Choose an option: " choice
|
read -rp "Choose an option: " choice
|
||||||
|
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
0)
|
0)
|
||||||
@@ -496,7 +422,7 @@ bbr_menu() {
|
|||||||
echo -e "${green}\t1.${plain} Enable BBR"
|
echo -e "${green}\t1.${plain} Enable BBR"
|
||||||
echo -e "${green}\t2.${plain} Disable BBR"
|
echo -e "${green}\t2.${plain} Disable BBR"
|
||||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
read -p "Choose an option: " choice
|
read -rp "Choose an option: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
0)
|
0)
|
||||||
show_menu
|
show_menu
|
||||||
@@ -696,7 +622,7 @@ firewall_menu() {
|
|||||||
echo -e "${green}\t6.${plain} ${red}Disable${plain} Firewall"
|
echo -e "${green}\t6.${plain} ${red}Disable${plain} Firewall"
|
||||||
echo -e "${green}\t7.${plain} Firewall Status"
|
echo -e "${green}\t7.${plain} Firewall Status"
|
||||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
read -p "Choose an option: " choice
|
read -rp "Choose an option: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
0)
|
0)
|
||||||
show_menu
|
show_menu
|
||||||
@@ -764,7 +690,7 @@ install_firewall() {
|
|||||||
|
|
||||||
open_ports() {
|
open_ports() {
|
||||||
# Prompt the user to enter the ports they want to open
|
# 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
|
read -rp "Enter the ports you want to open (e.g. 80,443,2053 or range 400-500): " ports
|
||||||
|
|
||||||
# Check if the input is valid
|
# Check if the input is valid
|
||||||
if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then
|
if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then
|
||||||
@@ -812,11 +738,11 @@ delete_ports() {
|
|||||||
echo "Do you want to delete rules by:"
|
echo "Do you want to delete rules by:"
|
||||||
echo "1) Rule numbers"
|
echo "1) Rule numbers"
|
||||||
echo "2) Ports"
|
echo "2) Ports"
|
||||||
read -p "Enter your choice (1 or 2): " choice
|
read -rp "Enter your choice (1 or 2): " choice
|
||||||
|
|
||||||
if [[ $choice -eq 1 ]]; then
|
if [[ $choice -eq 1 ]]; then
|
||||||
# Deleting by rule numbers
|
# Deleting by rule numbers
|
||||||
read -p "Enter the rule numbers you want to delete (1, 2, etc.): " rule_numbers
|
read -rp "Enter the rule numbers you want to delete (1, 2, etc.): " rule_numbers
|
||||||
|
|
||||||
# Validate the input
|
# Validate the input
|
||||||
if ! [[ $rule_numbers =~ ^([0-9]+)(,[0-9]+)*$ ]]; then
|
if ! [[ $rule_numbers =~ ^([0-9]+)(,[0-9]+)*$ ]]; then
|
||||||
@@ -835,7 +761,7 @@ delete_ports() {
|
|||||||
|
|
||||||
elif [[ $choice -eq 2 ]]; then
|
elif [[ $choice -eq 2 ]]; then
|
||||||
# Deleting by ports
|
# Deleting by ports
|
||||||
read -p "Enter the ports you want to delete (e.g. 80,443,2053 or range 400-500): " ports
|
read -rp "Enter the ports you want to delete (e.g. 80,443,2053 or range 400-500): " ports
|
||||||
|
|
||||||
# Validate the input
|
# Validate the input
|
||||||
if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then
|
if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then
|
||||||
@@ -883,7 +809,7 @@ update_geo() {
|
|||||||
echo -e "${green}\t2.${plain} chocolate4u (geoip_IR.dat, geosite_IR.dat)"
|
echo -e "${green}\t2.${plain} chocolate4u (geoip_IR.dat, geosite_IR.dat)"
|
||||||
echo -e "${green}\t3.${plain} runetfreedom (geoip_RU.dat, geosite_RU.dat)"
|
echo -e "${green}\t3.${plain} runetfreedom (geoip_RU.dat, geosite_RU.dat)"
|
||||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
read -p "Choose an option: " choice
|
read -rp "Choose an option: " choice
|
||||||
|
|
||||||
cd /usr/local/x-ui/bin
|
cd /usr/local/x-ui/bin
|
||||||
|
|
||||||
@@ -953,7 +879,7 @@ ssl_cert_issue_main() {
|
|||||||
echo -e "${green}\t5.${plain} Set Cert paths for the panel"
|
echo -e "${green}\t5.${plain} Set Cert paths for the panel"
|
||||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
|
|
||||||
read -p "Choose an option: " choice
|
read -rp "Choose an option: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
0)
|
0)
|
||||||
show_menu
|
show_menu
|
||||||
@@ -969,7 +895,7 @@ ssl_cert_issue_main() {
|
|||||||
else
|
else
|
||||||
echo "Existing domains:"
|
echo "Existing domains:"
|
||||||
echo "$domains"
|
echo "$domains"
|
||||||
read -p "Please enter a domain from the list to revoke the certificate: " domain
|
read -rp "Please enter a domain from the list to revoke the certificate: " domain
|
||||||
if echo "$domains" | grep -qw "$domain"; then
|
if echo "$domains" | grep -qw "$domain"; then
|
||||||
~/.acme.sh/acme.sh --revoke -d ${domain}
|
~/.acme.sh/acme.sh --revoke -d ${domain}
|
||||||
LOGI "Certificate revoked for domain: $domain"
|
LOGI "Certificate revoked for domain: $domain"
|
||||||
@@ -986,7 +912,7 @@ ssl_cert_issue_main() {
|
|||||||
else
|
else
|
||||||
echo "Existing domains:"
|
echo "Existing domains:"
|
||||||
echo "$domains"
|
echo "$domains"
|
||||||
read -p "Please enter a domain from the list to renew the SSL certificate: " domain
|
read -rp "Please enter a domain from the list to renew the SSL certificate: " domain
|
||||||
if echo "$domains" | grep -qw "$domain"; then
|
if echo "$domains" | grep -qw "$domain"; then
|
||||||
~/.acme.sh/acme.sh --renew -d ${domain} --force
|
~/.acme.sh/acme.sh --renew -d ${domain} --force
|
||||||
LOGI "Certificate forcefully renewed for domain: $domain"
|
LOGI "Certificate forcefully renewed for domain: $domain"
|
||||||
@@ -1023,7 +949,7 @@ ssl_cert_issue_main() {
|
|||||||
else
|
else
|
||||||
echo "Available domains:"
|
echo "Available domains:"
|
||||||
echo "$domains"
|
echo "$domains"
|
||||||
read -p "Please choose a domain to set the panel paths: " domain
|
read -rp "Please choose a domain to set the panel paths: " domain
|
||||||
|
|
||||||
if echo "$domains" | grep -qw "$domain"; then
|
if echo "$domains" | grep -qw "$domain"; then
|
||||||
local webCertFile="/root/cert/${domain}/fullchain.pem"
|
local webCertFile="/root/cert/${domain}/fullchain.pem"
|
||||||
@@ -1093,7 +1019,7 @@ ssl_cert_issue() {
|
|||||||
|
|
||||||
# get the domain here, and we need to verify it
|
# get the domain here, and we need to verify it
|
||||||
local domain=""
|
local domain=""
|
||||||
read -p "Please enter your domain name: " domain
|
read -rp "Please enter your domain name: " domain
|
||||||
LOGD "Your domain is: ${domain}, checking it..."
|
LOGD "Your domain is: ${domain}, checking it..."
|
||||||
|
|
||||||
# check if there already exists a certificate
|
# check if there already exists a certificate
|
||||||
@@ -1118,7 +1044,7 @@ ssl_cert_issue() {
|
|||||||
|
|
||||||
# get the port number for the standalone server
|
# get the port number for the standalone server
|
||||||
local WebPort=80
|
local WebPort=80
|
||||||
read -p "Please choose which port to use (default is 80): " WebPort
|
read -rp "Please choose which port to use (default is 80): " WebPort
|
||||||
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
|
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
|
||||||
LOGE "Your input ${WebPort} is invalid, will use default port 80."
|
LOGE "Your input ${WebPort} is invalid, will use default port 80."
|
||||||
WebPort=80
|
WebPort=80
|
||||||
@@ -1140,12 +1066,12 @@ ssl_cert_issue() {
|
|||||||
|
|
||||||
LOGI "Default --reloadcmd for ACME is: ${yellow}x-ui restart"
|
LOGI "Default --reloadcmd for ACME is: ${yellow}x-ui restart"
|
||||||
LOGI "This command will run on every certificate issue and renew."
|
LOGI "This command will run on every certificate issue and renew."
|
||||||
read -p "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd
|
read -rp "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd
|
||||||
if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then
|
if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then
|
||||||
echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; x-ui restart"
|
echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; x-ui restart"
|
||||||
echo -e "${green}\t2.${plain} Input your own command"
|
echo -e "${green}\t2.${plain} Input your own command"
|
||||||
echo -e "${green}\t0.${plain} Keep default reloadcmd"
|
echo -e "${green}\t0.${plain} Keep default reloadcmd"
|
||||||
read -p "Choose an option: " choice
|
read -rp "Choose an option: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1)
|
1)
|
||||||
LOGI "Reloadcmd is: systemctl reload nginx ; x-ui restart"
|
LOGI "Reloadcmd is: systemctl reload nginx ; x-ui restart"
|
||||||
@@ -1153,7 +1079,7 @@ ssl_cert_issue() {
|
|||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
LOGD "It's recommended to put x-ui restart at the end, so it won't raise an error if other services fails"
|
LOGD "It's recommended to put x-ui restart at the end, so it won't raise an error if other services fails"
|
||||||
read -p "Please enter your reloadcmd (example: systemctl reload nginx ; x-ui restart): " reloadCmd
|
read -rp "Please enter your reloadcmd (example: systemctl reload nginx ; x-ui restart): " reloadCmd
|
||||||
LOGI "Your reloadcmd is: ${reloadCmd}"
|
LOGI "Your reloadcmd is: ${reloadCmd}"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -1189,7 +1115,7 @@ ssl_cert_issue() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Prompt user to set panel paths after successful certificate installation
|
# Prompt user to set panel paths after successful certificate installation
|
||||||
read -p "Would you like to set this certificate for the panel? (y/n): " setPanel
|
read -rp "Would you like to set this certificate for the panel? (y/n): " setPanel
|
||||||
if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
|
if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
|
||||||
local webCertFile="/root/cert/${domain}/fullchain.pem"
|
local webCertFile="/root/cert/${domain}/fullchain.pem"
|
||||||
local webKeyFile="/root/cert/${domain}/privkey.pem"
|
local webKeyFile="/root/cert/${domain}/privkey.pem"
|
||||||
@@ -1236,18 +1162,18 @@ ssl_cert_issue_CF() {
|
|||||||
CF_Domain=""
|
CF_Domain=""
|
||||||
|
|
||||||
LOGD "Please set a domain name:"
|
LOGD "Please set a domain name:"
|
||||||
read -p "Input your domain here: " CF_Domain
|
read -rp "Input your domain here: " CF_Domain
|
||||||
LOGD "Your domain name is set to: ${CF_Domain}"
|
LOGD "Your domain name is set to: ${CF_Domain}"
|
||||||
|
|
||||||
# Set up Cloudflare API details
|
# Set up Cloudflare API details
|
||||||
CF_GlobalKey=""
|
CF_GlobalKey=""
|
||||||
CF_AccountEmail=""
|
CF_AccountEmail=""
|
||||||
LOGD "Please set the API key:"
|
LOGD "Please set the API key:"
|
||||||
read -p "Input your key here: " CF_GlobalKey
|
read -rp "Input your key here: " CF_GlobalKey
|
||||||
LOGD "Your API key is: ${CF_GlobalKey}"
|
LOGD "Your API key is: ${CF_GlobalKey}"
|
||||||
|
|
||||||
LOGD "Please set up registered email:"
|
LOGD "Please set up registered email:"
|
||||||
read -p "Input your email here: " CF_AccountEmail
|
read -rp "Input your email here: " CF_AccountEmail
|
||||||
LOGD "Your registered email address is: ${CF_AccountEmail}"
|
LOGD "Your registered email address is: ${CF_AccountEmail}"
|
||||||
|
|
||||||
# Set the default CA to Let's Encrypt
|
# Set the default CA to Let's Encrypt
|
||||||
@@ -1285,12 +1211,12 @@ ssl_cert_issue_CF() {
|
|||||||
|
|
||||||
LOGI "Default --reloadcmd for ACME is: ${yellow}x-ui restart"
|
LOGI "Default --reloadcmd for ACME is: ${yellow}x-ui restart"
|
||||||
LOGI "This command will run on every certificate issue and renew."
|
LOGI "This command will run on every certificate issue and renew."
|
||||||
read -p "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd
|
read -rp "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd
|
||||||
if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then
|
if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then
|
||||||
echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; x-ui restart"
|
echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; x-ui restart"
|
||||||
echo -e "${green}\t2.${plain} Input your own command"
|
echo -e "${green}\t2.${plain} Input your own command"
|
||||||
echo -e "${green}\t0.${plain} Keep default reloadcmd"
|
echo -e "${green}\t0.${plain} Keep default reloadcmd"
|
||||||
read -p "Choose an option: " choice
|
read -rp "Choose an option: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1)
|
1)
|
||||||
LOGI "Reloadcmd is: systemctl reload nginx ; x-ui restart"
|
LOGI "Reloadcmd is: systemctl reload nginx ; x-ui restart"
|
||||||
@@ -1298,7 +1224,7 @@ ssl_cert_issue_CF() {
|
|||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
LOGD "It's recommended to put x-ui restart at the end, so it won't raise an error if other services fails"
|
LOGD "It's recommended to put x-ui restart at the end, so it won't raise an error if other services fails"
|
||||||
read -p "Please enter your reloadcmd (example: systemctl reload nginx ; x-ui restart): " reloadCmd
|
read -rp "Please enter your reloadcmd (example: systemctl reload nginx ; x-ui restart): " reloadCmd
|
||||||
LOGI "Your reloadcmd is: ${reloadCmd}"
|
LOGI "Your reloadcmd is: ${reloadCmd}"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -1329,7 +1255,7 @@ ssl_cert_issue_CF() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Prompt user to set panel paths after successful certificate installation
|
# Prompt user to set panel paths after successful certificate installation
|
||||||
read -p "Would you like to set this certificate for the panel? (y/n): " setPanel
|
read -rp "Would you like to set this certificate for the panel? (y/n): " setPanel
|
||||||
if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
|
if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
|
||||||
local webCertFile="${certPath}/fullchain.pem"
|
local webCertFile="${certPath}/fullchain.pem"
|
||||||
local webKeyFile="${certPath}/privkey.pem"
|
local webKeyFile="${certPath}/privkey.pem"
|
||||||
@@ -1486,7 +1412,7 @@ iplimit_main() {
|
|||||||
echo -e "${green}\t9.${plain} Service Restart"
|
echo -e "${green}\t9.${plain} Service Restart"
|
||||||
echo -e "${green}\t10.${plain} Uninstall Fail2ban and IP Limit"
|
echo -e "${green}\t10.${plain} Uninstall Fail2ban and IP Limit"
|
||||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
read -p "Choose an option: " choice
|
read -rp "Choose an option: " choice
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
0)
|
0)
|
||||||
show_menu
|
show_menu
|
||||||
@@ -1647,7 +1573,7 @@ remove_iplimit() {
|
|||||||
echo -e "${green}\t1.${plain} Only remove IP Limit configurations"
|
echo -e "${green}\t1.${plain} Only remove IP Limit configurations"
|
||||||
echo -e "${green}\t2.${plain} Uninstall Fail2ban and IP Limit"
|
echo -e "${green}\t2.${plain} Uninstall Fail2ban and IP Limit"
|
||||||
echo -e "${green}\t0.${plain} Back to Main Menu"
|
echo -e "${green}\t0.${plain} Back to Main Menu"
|
||||||
read -p "Choose an option: " num
|
read -rp "Choose an option: " num
|
||||||
case "$num" in
|
case "$num" in
|
||||||
1)
|
1)
|
||||||
rm -f /etc/fail2ban/filter.d/3x-ipl.conf
|
rm -f /etc/fail2ban/filter.d/3x-ipl.conf
|
||||||
@@ -1729,7 +1655,7 @@ SSH_port_forwarding() {
|
|||||||
echo -e "${green}1.${plain} Set listen IP"
|
echo -e "${green}1.${plain} Set listen IP"
|
||||||
echo -e "${green}2.${plain} Clear listen IP"
|
echo -e "${green}2.${plain} Clear listen IP"
|
||||||
echo -e "${green}0.${plain} Back to Main Menu"
|
echo -e "${green}0.${plain} Back to Main Menu"
|
||||||
read -p "Choose an option: " num
|
read -rp "Choose an option: " num
|
||||||
|
|
||||||
case "$num" in
|
case "$num" in
|
||||||
1)
|
1)
|
||||||
@@ -1737,10 +1663,10 @@ SSH_port_forwarding() {
|
|||||||
echo -e "\nNo listenIP configured. Choose an option:"
|
echo -e "\nNo listenIP configured. Choose an option:"
|
||||||
echo -e "1. Use default IP (127.0.0.1)"
|
echo -e "1. Use default IP (127.0.0.1)"
|
||||||
echo -e "2. Set a custom IP"
|
echo -e "2. Set a custom IP"
|
||||||
read -p "Select an option (1 or 2): " listen_choice
|
read -rp "Select an option (1 or 2): " listen_choice
|
||||||
|
|
||||||
config_listenIP="127.0.0.1"
|
config_listenIP="127.0.0.1"
|
||||||
[[ "$listen_choice" == "2" ]] && read -p "Enter custom IP to listen on: " config_listenIP
|
[[ "$listen_choice" == "2" ]] && read -rp "Enter custom IP to listen on: " config_listenIP
|
||||||
|
|
||||||
/usr/local/x-ui/x-ui setting -listenIP "${config_listenIP}" >/dev/null 2>&1
|
/usr/local/x-ui/x-ui setting -listenIP "${config_listenIP}" >/dev/null 2>&1
|
||||||
echo -e "${green}listen IP has been set to ${config_listenIP}.${plain}"
|
echo -e "${green}listen IP has been set to ${config_listenIP}.${plain}"
|
||||||
@@ -1832,7 +1758,7 @@ show_menu() {
|
|||||||
╚────────────────────────────────────────────────╝
|
╚────────────────────────────────────────────────╝
|
||||||
"
|
"
|
||||||
show_status
|
show_status
|
||||||
echo && read -p "Please enter your selection [0-25]: " num
|
echo && read -rp "Please enter your selection [0-25]: " num
|
||||||
|
|
||||||
case "${num}" in
|
case "${num}" in
|
||||||
0)
|
0)
|
||||||
|
|||||||
Reference in New Issue
Block a user