mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 21:24:10 +00:00
feat: 优化项目并添加多语言和GitHub加速
Co-authored-by: traeagent <traeagent@users.noreply.github.com>
This commit is contained in:
parent
8834e5fbbe
commit
6fd3e9553f
13 changed files with 339 additions and 1315 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
[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)
|
[中文](/README.zh_CN.md) | [English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
|
|
@ -7,50 +7,45 @@
|
||||||
</picture>
|
</picture>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases)
|
**3X-UI** — لوحة تحكم متقدمة مفتوحة المصدر لإدارة خوادم Xray-core. توفر واجهة سهلة الاستخدام لتكوين ومراقبة بروتوكولات VPN والخوادم الوكيلة المختلفة.
|
||||||
[](https://github.com/MHSanaei/3x-ui/actions)
|
|
||||||
[](#)
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases/latest)
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
|
||||||
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
|
|
||||||
**3X-UI** — لوحة تحكم متقدمة مفتوحة المصدر تعتمد على الويب مصممة لإدارة خادم Xray-core. توفر واجهة سهلة الاستخدام لتكوين ومراقبة بروتوكولات VPN والوكيل المختلفة.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> هذا المشروع مخصص للاستخدام الشخصي والاتصال فقط، يرجى عدم استخدامه لأغراض غير قانونية، يرجى عدم استخدامه في بيئة الإنتاج.
|
> هذا المشروع مخصص للاستخدام الشخصي والبحث في الاتصالات فقط. يرجى عدم استخدامه لأغراض غير قانونية أو في بيئة الإنتاج.
|
||||||
|
|
||||||
كمشروع محسن من مشروع X-UI الأصلي، يوفر 3X-UI استقرارًا محسنًا ودعمًا أوسع للبروتوكولات وميزات إضافية.
|
كمحاكاة محسنة من مشروع X-UI الأصلي، يوفر 3X-UI استقرارًا أفضل ودعمًا أوسع للبروتوكولات وميزات إضافية.
|
||||||
|
|
||||||
## البدء السريع
|
## البدء السريع
|
||||||
|
|
||||||
```
|
### سكريبت التثبيت بنقرة واحدة
|
||||||
|
|
||||||
|
**المصدر الرسمي:**
|
||||||
|
```bash
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
للحصول على الوثائق الكاملة، يرجى زيارة [ويكي المشروع](https://github.com/MHSanaei/3x-ui/wiki).
|
**تسريع GitHub (موصى به للمستخدمين في الصين):**
|
||||||
|
|
||||||
## شكر خاص إلى
|
الطريقة الأولى (استخدام عنوان التسريع الافتراضي):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://gh.kejilion.pro/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
الطريقة الثانية (استخدام عنوان تسريع مخصص):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://عنوان-المرآة-المخصص.com/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
للوثائق الكاملة، يرجى زيارة [ويكي المشروع](https://github.com/MHSanaei/3x-ui/wiki).
|
||||||
|
|
||||||
|
## شكر خاص
|
||||||
|
|
||||||
- [alireza0](https://github.com/alireza0/)
|
- [alireza0](https://github.com/alireza0/)
|
||||||
|
|
||||||
## الاعتراف
|
## الإشادة
|
||||||
|
|
||||||
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (الترخيص: **GPL-3.0**): _قواعد توجيه v2ray/xray و v2ray/xray-clients المحسنة مع النطاقات الإيرانية المدمجة وتركيز على الأمان وحظر الإعلانات._
|
- [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 محدثة تلقائيًا بناءً على بيانات النطاقات والعناوين المحظورة في روسيا._
|
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (الترخيص: **GPL-3.0**): _قواعد V2Ray المحدثة تلقائيًا بناءً على النطاقات المحظورة في روسيا._
|
||||||
|
|
||||||
## دعم المشروع
|
## دعم المشروع
|
||||||
|
|
||||||
**إذا كان هذا المشروع مفيدًا لك، فقد ترغب في إعطائه**:star2:
|
**إذا كان هذا المشروع مفيدًا لك، يمكنك إعطائه**:star2:
|
||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
|
|
||||||
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
|
|
||||||
</a>
|
|
||||||
</br>
|
|
||||||
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
|
|
||||||
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
## النجوم عبر الزمن
|
|
||||||
|
|
||||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[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)
|
[中文](/README.zh_CN.md) | [English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
|
|
@ -7,28 +7,35 @@
|
||||||
</picture>
|
</picture>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases)
|
**3X-UI** — panel de control web avanzado de código abierto diseñado para gestionar servidores Xray-core. Ofrece una interfaz fácil de usar para configurar y monitorear varios protocolos VPN y proxy.
|
||||||
[](https://github.com/MHSanaei/3x-ui/actions)
|
|
||||||
[](#)
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases/latest)
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
|
||||||
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
|
|
||||||
**3X-UI** — panel de control avanzado basado en web de código abierto diseñado para gestionar el servidor Xray-core. Ofrece una interfaz fácil de usar para configurar y monitorear varios protocolos VPN y proxy.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> Este proyecto es solo para uso personal y comunicación, por favor no lo use para fines ilegales, por favor no lo use en un entorno de producción.
|
> Este proyecto es solo para uso personal e investigación en comunicaciones. Por favor, no lo use para fines ilegales ni en entornos de producción.
|
||||||
|
|
||||||
Como una versión mejorada del proyecto X-UI original, 3X-UI proporciona mayor estabilidad, soporte más amplio de protocolos y características adicionales.
|
Como una versión mejorada del proyecto X-UI original, 3X-UI proporciona mayor estabilidad, soporte más amplio de protocolos y características adicionales.
|
||||||
|
|
||||||
## Inicio Rápido
|
## Inicio Rápido
|
||||||
|
|
||||||
```
|
### Script de Instalación con Un Clic
|
||||||
|
|
||||||
|
**Fuente Oficial:**
|
||||||
|
```bash
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
Para documentación completa, visita la [Wiki del proyecto](https://github.com/MHSanaei/3x-ui/wiki).
|
**Aceleración de GitHub (Recomendado para usuarios en China):**
|
||||||
|
|
||||||
|
Método 1 (Usando la dirección de aceleración predeterminada):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://gh.kejilion.pro/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
Método 2 (Usando una dirección de aceleración personalizada):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://su-direccion-mirror.com/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
Para documentación completa, visite la [Wiki del proyecto](https://github.com/MHSanaei/3x-ui/wiki).
|
||||||
|
|
||||||
## Un Agradecimiento Especial a
|
## Un Agradecimiento Especial a
|
||||||
|
|
||||||
|
|
@ -36,22 +43,9 @@ Para documentación completa, visita la [Wiki del proyecto](https://github.com/M
|
||||||
|
|
||||||
## Reconocimientos
|
## Reconocimientos
|
||||||
|
|
||||||
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (Licencia: **GPL-3.0**): _Reglas de enrutamiento mejoradas para v2ray/xray y v2ray/xray-clients con dominios iraníes incorporados y un enfoque en seguridad y bloqueo de anuncios._
|
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (Licencia: **GPL-3.0**): _Reglas de enrutamiento mejoradas para v2ray/xray con dominios iraníes._
|
||||||
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (Licencia: **GPL-3.0**): _Este repositorio contiene reglas de enrutamiento V2Ray actualizadas automáticamente basadas en datos de dominios y direcciones bloqueadas en Rusia._
|
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (Licencia: **GPL-3.0**): _Reglas V2Ray actualizadas automáticamente basadas en dominios bloqueados en Rusia._
|
||||||
|
|
||||||
## Apoyar el Proyecto
|
## Apoyar el Proyecto
|
||||||
|
|
||||||
**Si este proyecto te es útil, puedes darle una**:star2:
|
**Si este proyecto te es útil, puedes darle una**:star2:
|
||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
|
|
||||||
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</br>
|
|
||||||
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
|
|
||||||
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
## Estrellas a lo Largo del Tiempo
|
|
||||||
|
|
||||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[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)
|
[中文](/README.zh_CN.md) | [English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
|
|
@ -7,51 +7,45 @@
|
||||||
</picture>
|
</picture>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases)
|
**3X-UI** — پنل مدیریت وب پیشرفته متنباز برای مدیریت سرورهای Xray-core. این پنل یک رابط کاربری آسان برای پیکربندی و نظارت بر پروتکلهای مختلف VPN و پروکسی فراهم میکند.
|
||||||
[](https://github.com/MHSanaei/3x-ui/actions)
|
|
||||||
[](#)
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases/latest)
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
|
||||||
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
|
|
||||||
**3X-UI** — یک پنل کنترل پیشرفته مبتنی بر وب با کد باز که برای مدیریت سرور Xray-core طراحی شده است. این پنل یک رابط کاربری آسان برای پیکربندی و نظارت بر پروتکلهای مختلف VPN و پراکسی ارائه میدهد.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> این پروژه فقط برای استفاده شخصی و ارتباطات است، لطفاً از آن برای اهداف غیرقانونی استفاده نکنید، لطفاً از آن در محیط تولید استفاده نکنید.
|
> این پروژه فقط برای استفاده شخصی و تحقیقات ارتباطی است. لطفاً از آن برای اهداف غیرقانونی استفاده نکنید و در محیط تولید به کار نبرید.
|
||||||
|
|
||||||
به عنوان یک نسخه بهبود یافته از پروژه اصلی X-UI، 3X-UI پایداری بهتر، پشتیبانی گستردهتر از پروتکلها و ویژگیهای اضافی را ارائه میدهد.
|
به عنوان یک نسخه بهبود یافته از پروژه اصلی X-UI، 3X-UI پایداری بهتر، پشتیبانی گستردهتر از پروتکلها و ویژگیهای اضافی را ارائه میدهد.
|
||||||
|
|
||||||
## شروع سریع
|
## شروع سریع
|
||||||
|
|
||||||
```
|
### اسکریپت نصب با یک کلیک
|
||||||
|
|
||||||
|
**منبع رسمی:**
|
||||||
|
```bash
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**شتابدهی GitHub (برای کاربران چینی توصیه میشود):**
|
||||||
|
|
||||||
|
روش اول (استفاده از آدرس شتاب پیشفرض):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://gh.kejilion.pro/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
روش دوم (استفاده از آدرس شتاب سفارشی):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://آدرس-آینه-سفارشی.com/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
برای مستندات کامل، لطفاً به [ویکی پروژه](https://github.com/MHSanaei/3x-ui/wiki) مراجعه کنید.
|
برای مستندات کامل، لطفاً به [ویکی پروژه](https://github.com/MHSanaei/3x-ui/wiki) مراجعه کنید.
|
||||||
|
|
||||||
## تشکر ویژه از
|
## تقدیر ویژه
|
||||||
|
|
||||||
- [alireza0](https://github.com/alireza0/)
|
- [alireza0](https://github.com/alireza0/)
|
||||||
|
|
||||||
## قدردانی
|
## تشکر و قدردانی
|
||||||
|
|
||||||
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (مجوز: **GPL-3.0**): _قوانین مسیریابی بهبود یافته v2ray/xray و v2ray/xray-clients با دامنههای ایرانی داخلی و تمرکز بر امنیت و مسدود کردن تبلیغات._
|
- [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 بهروزرسانی شده خودکار بر اساس دادههای دامنهها و آدرسهای مسدود شده در روسیه است._
|
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (مجوز: **GPL-3.0**): _قوانین V2Ray که بر اساس دامنههای مسدود شده در روسیه بهروزرسانی میشوند._
|
||||||
|
|
||||||
## پشتیبانی از پروژه
|
## حمایت از پروژه
|
||||||
|
|
||||||
**اگر این پروژه برای شما مفید است، میتوانید به آن یک**:star2: بدهید
|
**اگر این پروژه برای شما مفید است، میتوانید به آن**:star2: **بدهید**
|
||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
|
|
||||||
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</br>
|
|
||||||
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
|
|
||||||
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
## ستارهها در طول زمان
|
|
||||||
|
|
||||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
|
||||||
|
|
|
||||||
46
README.md
46
README.md
|
|
@ -1,4 +1,4 @@
|
||||||
[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)
|
[中文](/README.zh_CN.md) | [English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
|
|
@ -7,27 +7,34 @@
|
||||||
</picture>
|
</picture>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases)
|
**3X-UI** — An advanced, open-source web-based control panel designed for managing Xray-core servers. It offers a user-friendly interface for configuring and monitoring various VPN and proxy protocols.
|
||||||
[](https://github.com/MHSanaei/3x-ui/actions)
|
|
||||||
[](#)
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases/latest)
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
|
||||||
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
|
|
||||||
**3X-UI** — advanced, open-source web-based control panel designed for managing Xray-core server. It offers a user-friendly interface for configuring and monitoring various VPN and proxy protocols.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> This project is only for personal usage, please do not use it for illegal purposes, and please do not use it in a production environment.
|
> This project is for personal learning and communication research only. Please do not use it for illegal purposes or in production environments.
|
||||||
|
|
||||||
As an enhanced fork of the original X-UI project, 3X-UI provides improved stability, broader protocol support, and additional features.
|
As an enhanced version of the original X-UI project, 3X-UI provides better stability, broader protocol support, and additional features.
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
|
### One-Click Installation Script
|
||||||
|
|
||||||
|
**Official Source:**
|
||||||
```bash
|
```bash
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**GitHub Acceleration (Recommended for users in China):**
|
||||||
|
|
||||||
|
Method 1 (Using default acceleration address):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://gh.kejilion.pro/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
Method 2 (Using custom acceleration address):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://your-custom-mirror.com/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
For full documentation, please visit the [project Wiki](https://github.com/MHSanaei/3x-ui/wiki).
|
For full documentation, please visit the [project Wiki](https://github.com/MHSanaei/3x-ui/wiki).
|
||||||
|
|
||||||
## A Special Thanks to
|
## A Special Thanks to
|
||||||
|
|
@ -39,19 +46,6 @@ For full documentation, please visit the [project Wiki](https://github.com/MHSan
|
||||||
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._
|
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._
|
||||||
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (License: **GPL-3.0**): _This repository contains automatically updated V2Ray routing rules based on data on blocked domains and addresses in Russia._
|
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (License: **GPL-3.0**): _This repository contains automatically updated V2Ray routing rules based on data on blocked domains and addresses in Russia._
|
||||||
|
|
||||||
## Support project
|
## Support Project
|
||||||
|
|
||||||
**If this project is helpful to you, you may wish to give it a**:star2:
|
**If this project is helpful to you, you may wish to give it a**:star2:
|
||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
|
|
||||||
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</br>
|
|
||||||
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
|
|
||||||
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
## Stargazers over Time
|
|
||||||
|
|
||||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[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)
|
[中文](/README.zh_CN.md) | [English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
|
|
@ -7,51 +7,45 @@
|
||||||
</picture>
|
</picture>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases)
|
**3X-UI** — Панель управления веб-интерфейса Xray-core. Она предлагает удобный интерфейс для настройки и мониторинга различных VPN и прокси-протоколов.
|
||||||
[](https://github.com/MHSanaei/3x-ui/actions)
|
|
||||||
[](#)
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases/latest)
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
|
||||||
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
|
|
||||||
**3X-UI** — продвинутая панель управления с открытым исходным кодом на основе веб-интерфейса, разработанная для управления сервером Xray-core. Предоставляет удобный интерфейс для настройки и мониторинга различных VPN и прокси-протоколов.
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> Этот проект предназначен только для личного использования, пожалуйста, не используйте его в незаконных целях и в производственной среде.
|
> Этот проект предназначен только для личного использования и исследований связи. Пожалуйста, не используйте его в незаконных целях и не используйте в производственной среде.
|
||||||
|
|
||||||
Как улучшенная версия оригинального проекта X-UI, 3X-UI обеспечивает повышенную стабильность, более широкую поддержку протоколов и дополнительные функции.
|
В качестве улучшенной версии оригинального проекта X-UI, 3X-UI обеспечивает лучшую стабильность, более широкую поддержку протоколов и дополнительные функции.
|
||||||
|
|
||||||
## Быстрый старт
|
## Быстрый старт
|
||||||
|
|
||||||
```
|
### Скрипт быстрой установки
|
||||||
|
|
||||||
|
**Официальный источник:**
|
||||||
|
```bash
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
Полную документацию смотрите в [вики проекта](https://github.com/MHSanaei/3x-ui/wiki).
|
**Ускорение GitHub (рекомендуется для пользователей из Китая):**
|
||||||
|
|
||||||
|
Способ 1 (Использование адреса ускорения по умолчанию):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://gh.kejilion.pro/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
Способ 2 (Использование пользовательского адреса ускорения):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://ваш-адрес-зеркала.com/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
Полную документацию смотрите в [Wiki проекта](https://github.com/MHSanaei/3x-ui/wiki).
|
||||||
|
|
||||||
## Особая благодарность
|
## Особая благодарность
|
||||||
|
|
||||||
- [alireza0](https://github.com/alireza0/)
|
- [alireza0](https://github.com/alireza0/)
|
||||||
|
|
||||||
## Благодарности
|
## Признание
|
||||||
|
|
||||||
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (Лицензия: **GPL-3.0**): _Улучшенные правила маршрутизации для v2ray/xray и v2ray/xray-clients со встроенными иранскими доменами и фокусом на безопасность и блокировку рекламы._
|
- [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 на основе данных о заблокированных доменах и адресах в России._
|
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (Лицензия: **GPL-3.0**): _Автоматически обновляемые правила V2Ray на основе заблокированных в России доменов._
|
||||||
|
|
||||||
## Поддержка проекта
|
## Поддержать проект
|
||||||
|
|
||||||
**Если этот проект полезен для вас, вы можете поставить ему**:star2:
|
**Если этот проект полезен для вас, вы можете поставить**:star2:
|
||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
|
|
||||||
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</br>
|
|
||||||
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
|
|
||||||
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
## Звезды с течением времени
|
|
||||||
|
|
||||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[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)
|
[中文](/README.zh_CN.md) | [English](/README.md) | [فارسی](/README.fa_IR.md) | [العربية](/README.ar_EG.md) | [Español](/README.es_ES.md) | [Русский](/README.ru_RU.md)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<picture>
|
<picture>
|
||||||
|
|
@ -7,27 +7,34 @@
|
||||||
</picture>
|
</picture>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases)
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/actions)
|
|
||||||
[](#)
|
|
||||||
[](https://github.com/MHSanaei/3x-ui/releases/latest)
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
|
||||||
[](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
[](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v3)
|
|
||||||
|
|
||||||
**3X-UI** — 一个基于网页的高级开源控制面板,专为管理 Xray-core 服务器而设计。它提供了用户友好的界面,用于配置和监控各种 VPN 和代理协议。
|
**3X-UI** — 一个基于网页的高级开源控制面板,专为管理 Xray-core 服务器而设计。它提供了用户友好的界面,用于配置和监控各种 VPN 和代理协议。
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> 本项目仅用于个人使用和通信,请勿将其用于非法目的,请勿在生产环境中使用。
|
> 本项目仅用于个人学习和通信研究,请勿将其用于非法目的,请勿在生产环境中使用。
|
||||||
|
|
||||||
作为原始 X-UI 项目的增强版本,3X-UI 提供了更好的稳定性、更广泛的协议支持和额外的功能。
|
作为原始 X-UI 项目的增强版本,3X-UI 提供了更好的稳定性、更广泛的协议支持和额外的功能。
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
```
|
### 一键安装脚本
|
||||||
|
|
||||||
|
**官方源安装:**
|
||||||
|
```bash
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**GitHub 加速安装(推荐国内用户):**
|
||||||
|
|
||||||
|
方式一(使用默认加速地址):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://gh.kejilion.pro/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
|
方式二(使用自定义加速地址):
|
||||||
|
```bash
|
||||||
|
bash <(curl -Ls https://your-custom-mirror.com/https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
|
||||||
|
```
|
||||||
|
|
||||||
完整文档请参阅 [项目Wiki](https://github.com/MHSanaei/3x-ui/wiki)。
|
完整文档请参阅 [项目Wiki](https://github.com/MHSanaei/3x-ui/wiki)。
|
||||||
|
|
||||||
## 特别感谢
|
## 特别感谢
|
||||||
|
|
@ -42,16 +49,3 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
|
||||||
## 支持项目
|
## 支持项目
|
||||||
|
|
||||||
**如果这个项目对您有帮助,您可以给它一个**:star2:
|
**如果这个项目对您有帮助,您可以给它一个**:star2:
|
||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/MHSanaei" target="_blank">
|
|
||||||
<img src="./media/default-yellow.png" alt="Buy Me A Coffee" style="height: 70px !important;width: 277px !important;" >
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</br>
|
|
||||||
<a href="https://nowpayments.io/donation/hsanaei" target="_blank" rel="noreferrer noopener">
|
|
||||||
<img src="./media/donation-button-black.svg" alt="Crypto donation button by NOWPayments">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
## 随时间变化的星标数
|
|
||||||
|
|
||||||
[](https://starchart.cc/MHSanaei/3x-ui)
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
x-ui
|
3x-ui
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "3x-ui-frontend",
|
"name": "3x-ui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import { LanguageManager } from '@/utils';
|
||||||
// "lazy" here effectively means "load only what this page needs for
|
// "lazy" here effectively means "load only what this page needs for
|
||||||
// its lifetime."
|
// its lifetime."
|
||||||
const FALLBACK = 'en-US';
|
const FALLBACK = 'en-US';
|
||||||
|
const DEFAULT_LOCALE = 'zh-CN';
|
||||||
const lazyModules = import.meta.glob('../../../web/translation/*.json');
|
const lazyModules = import.meta.glob('../../../web/translation/*.json');
|
||||||
const eagerModules = import.meta.glob('../../../web/translation/*.json', { eager: true });
|
const eagerModules = import.meta.glob('../../../web/translation/*.json', { eager: true });
|
||||||
|
|
||||||
|
|
@ -38,10 +39,10 @@ function moduleKeyFor(code) {
|
||||||
|
|
||||||
// Resolve the active locale via LanguageManager so the cookie set on
|
// Resolve the active locale via LanguageManager so the cookie set on
|
||||||
// the legacy panel keeps working after a user upgrades. Falls back
|
// the legacy panel keeps working after a user upgrades. Falls back
|
||||||
// to en-US when the cookie names a language we don't have.
|
// to zh-CN (default) when the cookie names a language we don't have.
|
||||||
let active = LanguageManager.getLanguage();
|
let active = LanguageManager.getLanguage();
|
||||||
if (!Object.prototype.hasOwnProperty.call(lazyModules, moduleKeyFor(active))) {
|
if (!Object.prototype.hasOwnProperty.call(lazyModules, moduleKeyFor(active))) {
|
||||||
active = FALLBACK;
|
active = DEFAULT_LOCALE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = {};
|
const messages = {};
|
||||||
|
|
|
||||||
|
|
@ -840,11 +840,11 @@ export class LanguageManager {
|
||||||
if (LanguageManager.isSupportLanguage(lang)) {
|
if (LanguageManager.isSupportLanguage(lang)) {
|
||||||
CookieManager.setCookie("lang", lang);
|
CookieManager.setCookie("lang", lang);
|
||||||
} else {
|
} else {
|
||||||
CookieManager.setCookie("lang", "en-US");
|
CookieManager.setCookie("lang", "zh-CN");
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CookieManager.setCookie("lang", "en-US");
|
CookieManager.setCookie("lang", "zh-CN");
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -854,7 +854,7 @@ export class LanguageManager {
|
||||||
|
|
||||||
static setLanguage(language) {
|
static setLanguage(language) {
|
||||||
if (!LanguageManager.isSupportLanguage(language)) {
|
if (!LanguageManager.isSupportLanguage(language)) {
|
||||||
language = "en-US";
|
language = "zh-CN";
|
||||||
}
|
}
|
||||||
|
|
||||||
CookieManager.setCookie("lang", language);
|
CookieManager.setCookie("lang", language);
|
||||||
|
|
|
||||||
126
install.sh
126
install.sh
|
|
@ -11,10 +11,12 @@ cur_dir=$(pwd)
|
||||||
xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}"
|
xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}"
|
||||||
xui_service="${XUI_SERVICE:=/etc/systemd/system}"
|
xui_service="${XUI_SERVICE:=/etc/systemd/system}"
|
||||||
|
|
||||||
# check root
|
GITHUB_MIRROR_DEFAULT="https://gh.kejilion.pro"
|
||||||
|
GITHUB_RAW_DEFAULT="https://raw.githubusercontent.com"
|
||||||
|
|
||||||
|
check root
|
||||||
[[ $EUID -ne 0 ]] && echo -e "${red}Fatal error: ${plain} Please run this script with root privilege \n " && exit 1
|
[[ $EUID -ne 0 ]] && echo -e "${red}Fatal error: ${plain} Please run this script with root privilege \n " && exit 1
|
||||||
|
|
||||||
# Check OS and set release variable
|
|
||||||
if [[ -f /etc/os-release ]]; then
|
if [[ -f /etc/os-release ]]; then
|
||||||
source /etc/os-release
|
source /etc/os-release
|
||||||
release=$ID
|
release=$ID
|
||||||
|
|
@ -42,7 +44,6 @@ arch() {
|
||||||
|
|
||||||
echo "Arch: $(arch)"
|
echo "Arch: $(arch)"
|
||||||
|
|
||||||
# Simple helpers
|
|
||||||
is_ipv4() {
|
is_ipv4() {
|
||||||
[[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && return 0 || return 1
|
[[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && return 0 || return 1
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +57,6 @@ is_domain() {
|
||||||
[[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+(xn--[a-z0-9]{2,}|[A-Za-z]{2,})$ ]] && return 0 || return 1
|
[[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+(xn--[a-z0-9]{2,}|[A-Za-z]{2,})$ ]] && return 0 || return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Port helpers
|
|
||||||
is_port_in_use() {
|
is_port_in_use() {
|
||||||
local port="$1"
|
local port="$1"
|
||||||
if command -v ss > /dev/null 2>&1; then
|
if command -v ss > /dev/null 2>&1; then
|
||||||
|
|
@ -73,6 +73,24 @@ is_port_in_use() {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_github_mirror() {
|
||||||
|
local custom_mirror="${GITHUB_MIRROR:-}"
|
||||||
|
if [[ -n "$custom_mirror" ]]; then
|
||||||
|
echo "$custom_mirror"
|
||||||
|
else
|
||||||
|
echo "$GITHUB_MIRROR_DEFAULT"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_github_raw() {
|
||||||
|
local mirror=$(get_github_mirror)
|
||||||
|
if [[ "$mirror" == "$GITHUB_MIRROR_DEFAULT" ]]; then
|
||||||
|
echo "$GITHUB_RAW_DEFAULT"
|
||||||
|
else
|
||||||
|
echo "$mirror"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
install_base() {
|
install_base() {
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
|
|
@ -131,7 +149,6 @@ setup_ssl_certificate() {
|
||||||
|
|
||||||
echo -e "${green}Setting up SSL certificate...${plain}"
|
echo -e "${green}Setting up SSL certificate...${plain}"
|
||||||
|
|
||||||
# Check if acme.sh is installed
|
|
||||||
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
||||||
install_acme
|
install_acme
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
|
@ -140,11 +157,9 @@ setup_ssl_certificate() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create certificate directory
|
|
||||||
local certPath="/root/cert/${domain}"
|
local certPath="/root/cert/${domain}"
|
||||||
mkdir -p "$certPath"
|
mkdir -p "$certPath"
|
||||||
|
|
||||||
# Issue certificate
|
|
||||||
echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
|
echo -e "${green}Issuing SSL certificate for ${domain}...${plain}"
|
||||||
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
|
echo -e "${yellow}Note: Port 80 must be open and accessible from the internet${plain}"
|
||||||
|
|
||||||
|
|
@ -159,7 +174,6 @@ setup_ssl_certificate() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install certificate
|
|
||||||
~/.acme.sh/acme.sh --installcert -d ${domain} \
|
~/.acme.sh/acme.sh --installcert -d ${domain} \
|
||||||
--key-file /root/cert/${domain}/privkey.pem \
|
--key-file /root/cert/${domain}/privkey.pem \
|
||||||
--fullchain-file /root/cert/${domain}/fullchain.pem \
|
--fullchain-file /root/cert/${domain}/fullchain.pem \
|
||||||
|
|
@ -170,13 +184,10 @@ setup_ssl_certificate() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Enable auto-renew
|
|
||||||
~/.acme.sh/acme.sh --upgrade --auto-upgrade > /dev/null 2>&1
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade > /dev/null 2>&1
|
||||||
# Secure permissions: private key readable only by owner
|
|
||||||
chmod 600 $certPath/privkey.pem 2> /dev/null
|
chmod 600 $certPath/privkey.pem 2> /dev/null
|
||||||
chmod 644 $certPath/fullchain.pem 2> /dev/null
|
chmod 644 $certPath/fullchain.pem 2> /dev/null
|
||||||
|
|
||||||
# Set certificate for panel
|
|
||||||
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"
|
||||||
|
|
||||||
|
|
@ -190,17 +201,14 @@ setup_ssl_certificate() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Issue Let's Encrypt IP certificate with shortlived profile (~6 days validity)
|
|
||||||
# Requires acme.sh and port 80 open for HTTP-01 challenge
|
|
||||||
setup_ip_certificate() {
|
setup_ip_certificate() {
|
||||||
local ipv4="$1"
|
local ipv4="$1"
|
||||||
local ipv6="$2" # optional
|
local ipv6="$2"
|
||||||
|
|
||||||
echo -e "${green}Setting up Let's Encrypt IP certificate (shortlived profile)...${plain}"
|
echo -e "${green}Setting up Let's Encrypt IP certificate (shortlived profile)...${plain}"
|
||||||
echo -e "${yellow}Note: IP certificates are valid for ~6 days and will auto-renew.${plain}"
|
echo -e "${yellow}Note: IP certificates are valid for ~6 days and will auto-renew.${plain}"
|
||||||
echo -e "${yellow}Default listener is port 80. If you choose another port, ensure external port 80 forwards to it.${plain}"
|
echo -e "${yellow}Default listener is port 80. If you choose another port, ensure external port 80 forwards to it.${plain}"
|
||||||
|
|
||||||
# Check for acme.sh
|
|
||||||
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
||||||
install_acme
|
install_acme
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
|
@ -209,7 +217,6 @@ setup_ip_certificate() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Validate IP address
|
|
||||||
if [[ -z "$ipv4" ]]; then
|
if [[ -z "$ipv4" ]]; then
|
||||||
echo -e "${red}IPv4 address is required${plain}"
|
echo -e "${red}IPv4 address is required${plain}"
|
||||||
return 1
|
return 1
|
||||||
|
|
@ -220,21 +227,17 @@ setup_ip_certificate() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create certificate directory
|
|
||||||
local certDir="/root/cert/ip"
|
local certDir="/root/cert/ip"
|
||||||
mkdir -p "$certDir"
|
mkdir -p "$certDir"
|
||||||
|
|
||||||
# Build domain arguments
|
|
||||||
local domain_args="-d ${ipv4}"
|
local domain_args="-d ${ipv4}"
|
||||||
if [[ -n "$ipv6" ]] && is_ipv6 "$ipv6"; then
|
if [[ -n "$ipv6" ]] && is_ipv6 "$ipv6"; then
|
||||||
domain_args="${domain_args} -d ${ipv6}"
|
domain_args="${domain_args} -d ${ipv6}"
|
||||||
echo -e "${green}Including IPv6 address: ${ipv6}${plain}"
|
echo -e "${green}Including IPv6 address: ${ipv6}${plain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set reload command for auto-renewal (add || true so it doesn't fail during first install)
|
|
||||||
local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null || true"
|
local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null || true"
|
||||||
|
|
||||||
# Choose port for HTTP-01 listener (default 80, prompt override)
|
|
||||||
local WebPort=""
|
local WebPort=""
|
||||||
read -rp "Port to use for ACME HTTP-01 listener (default 80): " WebPort
|
read -rp "Port to use for ACME HTTP-01 listener (default 80): " WebPort
|
||||||
WebPort="${WebPort:-80}"
|
WebPort="${WebPort:-80}"
|
||||||
|
|
@ -247,7 +250,6 @@ setup_ip_certificate() {
|
||||||
echo -e "${yellow}Reminder: Let's Encrypt still connects on port 80; forward external port 80 to ${WebPort}.${plain}"
|
echo -e "${yellow}Reminder: Let's Encrypt still connects on port 80; forward external port 80 to ${WebPort}.${plain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ensure chosen port is available
|
|
||||||
while true; do
|
while true; do
|
||||||
if is_port_in_use "${WebPort}"; then
|
if is_port_in_use "${WebPort}"; then
|
||||||
echo -e "${yellow}Port ${WebPort} is in use.${plain}"
|
echo -e "${yellow}Port ${WebPort} is in use.${plain}"
|
||||||
|
|
@ -271,7 +273,6 @@ setup_ip_certificate() {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Issue certificate with shortlived profile
|
|
||||||
echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
|
echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force > /dev/null 2>&1
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force > /dev/null 2>&1
|
||||||
|
|
||||||
|
|
@ -287,7 +288,6 @@ setup_ip_certificate() {
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo -e "${red}Failed to issue IP certificate${plain}"
|
echo -e "${red}Failed to issue IP certificate${plain}"
|
||||||
echo -e "${yellow}Please ensure port ${WebPort} is reachable (or forwarded from external port 80)${plain}"
|
echo -e "${yellow}Please ensure port ${WebPort} is reachable (or forwarded from external port 80)${plain}"
|
||||||
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
|
||||||
rm -rf ~/.acme.sh/${ipv4} 2> /dev/null
|
rm -rf ~/.acme.sh/${ipv4} 2> /dev/null
|
||||||
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null
|
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null
|
||||||
rm -rf ${certDir} 2> /dev/null
|
rm -rf ${certDir} 2> /dev/null
|
||||||
|
|
@ -296,18 +296,13 @@ setup_ip_certificate() {
|
||||||
|
|
||||||
echo -e "${green}Certificate issued successfully, installing...${plain}"
|
echo -e "${green}Certificate issued successfully, installing...${plain}"
|
||||||
|
|
||||||
# Install certificate
|
|
||||||
# Note: acme.sh may report "Reload error" and exit non-zero if reloadcmd fails,
|
|
||||||
# but the cert files are still installed. We check for files instead of exit code.
|
|
||||||
~/.acme.sh/acme.sh --installcert -d ${ipv4} \
|
~/.acme.sh/acme.sh --installcert -d ${ipv4} \
|
||||||
--key-file "${certDir}/privkey.pem" \
|
--key-file "${certDir}/privkey.pem" \
|
||||||
--fullchain-file "${certDir}/fullchain.pem" \
|
--fullchain-file "${certDir}/fullchain.pem" \
|
||||||
--reloadcmd "${reloadCmd}" 2>&1 || true
|
--reloadcmd "${reloadCmd}" 2>&1 || true
|
||||||
|
|
||||||
# Verify certificate files exist (don't rely on exit code - reloadcmd failure causes non-zero)
|
|
||||||
if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
|
if [[ ! -f "${certDir}/fullchain.pem" || ! -f "${certDir}/privkey.pem" ]]; then
|
||||||
echo -e "${red}Certificate files not found after installation${plain}"
|
echo -e "${red}Certificate files not found after installation${plain}"
|
||||||
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
|
||||||
rm -rf ~/.acme.sh/${ipv4} 2> /dev/null
|
rm -rf ~/.acme.sh/${ipv4} 2> /dev/null
|
||||||
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null
|
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null
|
||||||
rm -rf ${certDir} 2> /dev/null
|
rm -rf ${certDir} 2> /dev/null
|
||||||
|
|
@ -316,14 +311,11 @@ setup_ip_certificate() {
|
||||||
|
|
||||||
echo -e "${green}Certificate files installed successfully${plain}"
|
echo -e "${green}Certificate files installed successfully${plain}"
|
||||||
|
|
||||||
# Enable auto-upgrade for acme.sh (ensures cron job runs)
|
|
||||||
~/.acme.sh/acme.sh --upgrade --auto-upgrade > /dev/null 2>&1
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade > /dev/null 2>&1
|
||||||
|
|
||||||
# Secure permissions: private key readable only by owner
|
|
||||||
chmod 600 ${certDir}/privkey.pem 2> /dev/null
|
chmod 600 ${certDir}/privkey.pem 2> /dev/null
|
||||||
chmod 644 ${certDir}/fullchain.pem 2> /dev/null
|
chmod 644 ${certDir}/fullchain.pem 2> /dev/null
|
||||||
|
|
||||||
# Configure panel to use the certificate
|
|
||||||
echo -e "${green}Setting certificate paths for the panel...${plain}"
|
echo -e "${green}Setting certificate paths for the panel...${plain}"
|
||||||
${xui_folder}/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem"
|
${xui_folder}/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem"
|
||||||
|
|
||||||
|
|
@ -342,12 +334,10 @@ setup_ip_certificate() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Comprehensive manual SSL certificate issuance via acme.sh
|
|
||||||
ssl_cert_issue() {
|
ssl_cert_issue() {
|
||||||
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep 'webBasePath:' | awk -F': ' '{print $2}' | tr -d '[:space:]' | sed 's#^/##')
|
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep 'webBasePath:' | awk -F': ' '{print $2}' | tr -d '[:space:]' | sed 's#^/##')
|
||||||
local existing_port=$(${xui_folder}/x-ui setting -show true | grep 'port:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
|
local existing_port=$(${xui_folder}/x-ui setting -show true | grep 'port:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
|
||||||
|
|
||||||
# check for acme.sh first
|
|
||||||
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
||||||
echo "acme.sh could not be found. Installing now..."
|
echo "acme.sh could not be found. Installing now..."
|
||||||
cd ~ || return 1
|
cd ~ || return 1
|
||||||
|
|
@ -360,11 +350,10 @@ ssl_cert_issue() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# get the domain here, and we need to verify it
|
|
||||||
local domain=""
|
local domain=""
|
||||||
while true; do
|
while true; do
|
||||||
read -rp "Please enter your domain name: " domain
|
read -rp "Please enter your domain name: " domain
|
||||||
domain="${domain// /}" # Trim whitespace
|
domain="${domain// /}"
|
||||||
|
|
||||||
if [[ -z "$domain" ]]; then
|
if [[ -z "$domain" ]]; then
|
||||||
echo -e "${red}Domain name cannot be empty. Please try again.${plain}"
|
echo -e "${red}Domain name cannot be empty. Please try again.${plain}"
|
||||||
|
|
@ -381,7 +370,6 @@ ssl_cert_issue() {
|
||||||
echo -e "${green}Your domain is: ${domain}, checking it...${plain}"
|
echo -e "${green}Your domain is: ${domain}, checking it...${plain}"
|
||||||
SSL_ISSUED_DOMAIN="${domain}"
|
SSL_ISSUED_DOMAIN="${domain}"
|
||||||
|
|
||||||
# detect existing certificate and reuse it if present
|
|
||||||
local cert_exists=0
|
local cert_exists=0
|
||||||
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
|
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
|
||||||
cert_exists=1
|
cert_exists=1
|
||||||
|
|
@ -392,7 +380,6 @@ ssl_cert_issue() {
|
||||||
echo -e "${green}Your domain is ready for issuing certificates now...${plain}"
|
echo -e "${green}Your domain is ready for issuing certificates now...${plain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# create a directory for the certificate
|
|
||||||
certPath="/root/cert/${domain}"
|
certPath="/root/cert/${domain}"
|
||||||
if [ ! -d "$certPath" ]; then
|
if [ ! -d "$certPath" ]; then
|
||||||
mkdir -p "$certPath"
|
mkdir -p "$certPath"
|
||||||
|
|
@ -401,7 +388,6 @@ ssl_cert_issue() {
|
||||||
mkdir -p "$certPath"
|
mkdir -p "$certPath"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# get the port number for the standalone server
|
|
||||||
local WebPort=80
|
local WebPort=80
|
||||||
read -rp "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
|
||||||
|
|
@ -410,12 +396,10 @@ ssl_cert_issue() {
|
||||||
fi
|
fi
|
||||||
echo -e "${green}Will use port: ${WebPort} to issue certificates. Please make sure this port is open.${plain}"
|
echo -e "${green}Will use port: ${WebPort} to issue certificates. Please make sure this port is open.${plain}"
|
||||||
|
|
||||||
# Stop panel temporarily
|
|
||||||
echo -e "${yellow}Stopping panel temporarily...${plain}"
|
echo -e "${yellow}Stopping panel temporarily...${plain}"
|
||||||
systemctl stop x-ui 2> /dev/null || rc-service x-ui stop 2> /dev/null
|
systemctl stop x-ui 2> /dev/null || rc-service x-ui stop 2> /dev/null
|
||||||
|
|
||||||
if [[ ${cert_exists} -eq 0 ]]; then
|
if [[ ${cert_exists} -eq 0 ]]; then
|
||||||
# issue the certificate
|
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
|
@ -430,7 +414,6 @@ ssl_cert_issue() {
|
||||||
echo -e "${green}Using existing certificate, installing certificates...${plain}"
|
echo -e "${green}Using existing certificate, installing certificates...${plain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Setup reload command
|
|
||||||
reloadCmd="systemctl restart x-ui || rc-service x-ui restart"
|
reloadCmd="systemctl restart x-ui || rc-service x-ui restart"
|
||||||
echo -e "${green}Default --reloadcmd for ACME is: ${yellow}systemctl restart x-ui || rc-service x-ui restart${plain}"
|
echo -e "${green}Default --reloadcmd for ACME is: ${yellow}systemctl restart x-ui || rc-service x-ui restart${plain}"
|
||||||
echo -e "${green}This command will run on every certificate issue and renew.${plain}"
|
echo -e "${green}This command will run on every certificate issue and renew.${plain}"
|
||||||
|
|
@ -456,7 +439,6 @@ ssl_cert_issue() {
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# install the certificate
|
|
||||||
local installOutput=""
|
local installOutput=""
|
||||||
installOutput=$(~/.acme.sh/acme.sh --installcert -d ${domain} \
|
installOutput=$(~/.acme.sh/acme.sh --installcert -d ${domain} \
|
||||||
--key-file /root/cert/${domain}/privkey.pem \
|
--key-file /root/cert/${domain}/privkey.pem \
|
||||||
|
|
@ -480,26 +462,21 @@ ssl_cert_issue() {
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# enable auto-renew
|
|
||||||
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
echo -e "${yellow}Auto renew setup had issues, certificate details:${plain}"
|
echo -e "${yellow}Auto renew setup had issues, certificate details:${plain}"
|
||||||
ls -lah /root/cert/${domain}/
|
ls -lah /root/cert/${domain}/
|
||||||
# Secure permissions: private key readable only by owner
|
|
||||||
chmod 600 $certPath/privkey.pem 2> /dev/null
|
chmod 600 $certPath/privkey.pem 2> /dev/null
|
||||||
chmod 644 $certPath/fullchain.pem 2> /dev/null
|
chmod 644 $certPath/fullchain.pem 2> /dev/null
|
||||||
else
|
else
|
||||||
echo -e "${green}Auto renew succeeded, certificate details:${plain}"
|
echo -e "${green}Auto renew succeeded, certificate details:${plain}"
|
||||||
ls -lah /root/cert/${domain}/
|
ls -lah /root/cert/${domain}/
|
||||||
# Secure permissions: private key readable only by owner
|
|
||||||
chmod 600 $certPath/privkey.pem 2> /dev/null
|
chmod 600 $certPath/privkey.pem 2> /dev/null
|
||||||
chmod 644 $certPath/fullchain.pem 2> /dev/null
|
chmod 644 $certPath/fullchain.pem 2> /dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# start panel
|
|
||||||
systemctl start x-ui 2> /dev/null || rc-service x-ui start 2> /dev/null
|
systemctl start x-ui 2> /dev/null || rc-service x-ui start 2> /dev/null
|
||||||
|
|
||||||
# Prompt user to set panel paths after successful certificate installation
|
|
||||||
read -rp "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"
|
||||||
|
|
@ -524,8 +501,6 @@ ssl_cert_issue() {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Reusable interactive SSL setup (domain or IP)
|
|
||||||
# Sets global `SSL_HOST` to the chosen domain/IP for Access URL usage
|
|
||||||
prompt_and_setup_ssl() {
|
prompt_and_setup_ssl() {
|
||||||
local panel_port="$1"
|
local panel_port="$1"
|
||||||
local web_base_path="$2"
|
local web_base_path="$2"
|
||||||
|
|
@ -542,16 +517,14 @@ prompt_and_setup_ssl() {
|
||||||
echo -e "${blue}Note:${plain} Options 1 & 2 require port 80 open. Option 3 requires manual paths."
|
echo -e "${blue}Note:${plain} Options 1 & 2 require port 80 open. Option 3 requires manual paths."
|
||||||
echo -e "${blue}Note:${plain} Option 4 serves the panel over plain HTTP — only safe behind nginx/Caddy or an SSH tunnel."
|
echo -e "${blue}Note:${plain} Option 4 serves the panel over plain HTTP — only safe behind nginx/Caddy or an SSH tunnel."
|
||||||
read -rp "Choose an option (default 2 for IP): " ssl_choice
|
read -rp "Choose an option (default 2 for IP): " ssl_choice
|
||||||
ssl_choice="${ssl_choice// /}" # Trim whitespace
|
ssl_choice="${ssl_choice// /}"
|
||||||
|
|
||||||
# Default to 2 (IP cert) if input is empty or invalid (not 1, 3 or 4)
|
|
||||||
if [[ "$ssl_choice" != "1" && "$ssl_choice" != "3" && "$ssl_choice" != "4" ]]; then
|
if [[ "$ssl_choice" != "1" && "$ssl_choice" != "3" && "$ssl_choice" != "4" ]]; then
|
||||||
ssl_choice="2"
|
ssl_choice="2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$ssl_choice" in
|
case "$ssl_choice" in
|
||||||
1)
|
1)
|
||||||
# User chose Let's Encrypt domain option
|
|
||||||
echo -e "${green}Using Let's Encrypt for domain certificate...${plain}"
|
echo -e "${green}Using Let's Encrypt for domain certificate...${plain}"
|
||||||
if ssl_cert_issue; then
|
if ssl_cert_issue; then
|
||||||
local cert_domain="${SSL_ISSUED_DOMAIN}"
|
local cert_domain="${SSL_ISSUED_DOMAIN}"
|
||||||
|
|
@ -572,15 +545,12 @@ prompt_and_setup_ssl() {
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
2)
|
2)
|
||||||
# User chose Let's Encrypt IP certificate option
|
|
||||||
echo -e "${green}Using Let's Encrypt for IP certificate (shortlived profile)...${plain}"
|
echo -e "${green}Using Let's Encrypt for IP certificate (shortlived profile)...${plain}"
|
||||||
|
|
||||||
# Ask for optional IPv6
|
|
||||||
local ipv6_addr=""
|
local ipv6_addr=""
|
||||||
read -rp "Do you have an IPv6 address to include? (leave empty to skip): " ipv6_addr
|
read -rp "Do you have an IPv6 address to include? (leave empty to skip): " ipv6_addr
|
||||||
ipv6_addr="${ipv6_addr// /}" # Trim whitespace
|
ipv6_addr="${ipv6_addr// /}"
|
||||||
|
|
||||||
# Stop panel if running (port 80 needed)
|
|
||||||
if [[ $release == "alpine" ]]; then
|
if [[ $release == "alpine" ]]; then
|
||||||
rc-service x-ui stop > /dev/null 2>&1
|
rc-service x-ui stop > /dev/null 2>&1
|
||||||
else
|
else
|
||||||
|
|
@ -597,20 +567,16 @@ prompt_and_setup_ssl() {
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
3)
|
3)
|
||||||
# User chose Custom Paths (User Provided) option
|
|
||||||
echo -e "${green}Using custom existing certificate...${plain}"
|
echo -e "${green}Using custom existing certificate...${plain}"
|
||||||
local custom_cert=""
|
local custom_cert=""
|
||||||
local custom_key=""
|
local custom_key=""
|
||||||
local custom_domain=""
|
local custom_domain=""
|
||||||
|
|
||||||
# 3.1 Request Domain to compose Panel URL later
|
|
||||||
read -rp "Please enter domain name certificate issued for: " custom_domain
|
read -rp "Please enter domain name certificate issued for: " custom_domain
|
||||||
custom_domain="${custom_domain// /}" # Remove spaces
|
custom_domain="${custom_domain// /}"
|
||||||
|
|
||||||
# 3.2 Loop for Certificate Path
|
|
||||||
while true; do
|
while true; do
|
||||||
read -rp "Input certificate path (keywords: .crt / fullchain): " custom_cert
|
read -rp "Input certificate path (keywords: .crt / fullchain): " custom_cert
|
||||||
# Strip quotes if present
|
|
||||||
custom_cert=$(echo "$custom_cert" | tr -d '"' | tr -d "'")
|
custom_cert=$(echo "$custom_cert" | tr -d '"' | tr -d "'")
|
||||||
|
|
||||||
if [[ -f "$custom_cert" && -r "$custom_cert" && -s "$custom_cert" ]]; then
|
if [[ -f "$custom_cert" && -r "$custom_cert" && -s "$custom_cert" ]]; then
|
||||||
|
|
@ -624,10 +590,8 @@ prompt_and_setup_ssl() {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# 3.3 Loop for Private Key Path
|
|
||||||
while true; do
|
while true; do
|
||||||
read -rp "Input private key path (keywords: .key / privatekey): " custom_key
|
read -rp "Input private key path (keywords: .key / privatekey): " custom_key
|
||||||
# Strip quotes if present
|
|
||||||
custom_key=$(echo "$custom_key" | tr -d '"' | tr -d "'")
|
custom_key=$(echo "$custom_key" | tr -d '"' | tr -d "'")
|
||||||
|
|
||||||
if [[ -f "$custom_key" && -r "$custom_key" && -s "$custom_key" ]]; then
|
if [[ -f "$custom_key" && -r "$custom_key" && -s "$custom_key" ]]; then
|
||||||
|
|
@ -641,10 +605,8 @@ prompt_and_setup_ssl() {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# 3.4 Apply Settings via x-ui binary
|
|
||||||
${xui_folder}/x-ui cert -webCert "$custom_cert" -webCertKey "$custom_key" > /dev/null 2>&1
|
${xui_folder}/x-ui cert -webCert "$custom_cert" -webCertKey "$custom_key" > /dev/null 2>&1
|
||||||
|
|
||||||
# Set SSL_HOST for composing Panel URL
|
|
||||||
if [[ -n "$custom_domain" ]]; then
|
if [[ -n "$custom_domain" ]]; then
|
||||||
SSL_HOST="$custom_domain"
|
SSL_HOST="$custom_domain"
|
||||||
else
|
else
|
||||||
|
|
@ -702,7 +664,6 @@ config_after_install() {
|
||||||
local existing_hasDefaultCredential=$(${xui_folder}/x-ui setting -show true | grep -Eo 'hasDefaultCredential: .+' | awk '{print $2}')
|
local existing_hasDefaultCredential=$(${xui_folder}/x-ui setting -show true | grep -Eo 'hasDefaultCredential: .+' | awk '{print $2}')
|
||||||
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}' | sed 's#^/##')
|
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}' | sed 's#^/##')
|
||||||
local existing_port=$(${xui_folder}/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
local existing_port=$(${xui_folder}/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
||||||
# Properly detect empty cert by checking if cert: line exists and has content after it
|
|
||||||
local existing_cert=$(${xui_folder}/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
|
local existing_cert=$(${xui_folder}/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
|
||||||
local URL_lists=(
|
local URL_lists=(
|
||||||
"https://api4.ipify.org"
|
"https://api4.ipify.org"
|
||||||
|
|
@ -763,7 +724,6 @@ config_after_install() {
|
||||||
|
|
||||||
prompt_and_setup_ssl "${config_port}" "${config_webBasePath}" "${server_ip}"
|
prompt_and_setup_ssl "${config_port}" "${config_webBasePath}" "${server_ip}"
|
||||||
|
|
||||||
# Display final credentials and access information
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${green}═══════════════════════════════════════════${plain}"
|
echo -e "${green}═══════════════════════════════════════════${plain}"
|
||||||
echo -e "${green} Panel Installation Complete! ${plain}"
|
echo -e "${green} Panel Installation Complete! ${plain}"
|
||||||
|
|
@ -786,7 +746,6 @@ config_after_install() {
|
||||||
${xui_folder}/x-ui setting -webBasePath "${config_webBasePath}"
|
${xui_folder}/x-ui setting -webBasePath "${config_webBasePath}"
|
||||||
echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
|
echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}"
|
||||||
|
|
||||||
# If the panel is already installed but no certificate is configured, prompt for SSL now
|
|
||||||
if [[ -z "${existing_cert}" ]]; then
|
if [[ -z "${existing_cert}" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${green}═══════════════════════════════════════════${plain}"
|
echo -e "${green}═══════════════════════════════════════════${plain}"
|
||||||
|
|
@ -797,7 +756,6 @@ config_after_install() {
|
||||||
prompt_and_setup_ssl "${existing_port}" "${config_webBasePath}" "${server_ip}"
|
prompt_and_setup_ssl "${existing_port}" "${config_webBasePath}" "${server_ip}"
|
||||||
echo -e "${green}Access URL: ${SSL_SCHEME}://${SSL_HOST}:${existing_port}/${config_webBasePath}${plain}"
|
echo -e "${green}Access URL: ${SSL_SCHEME}://${SSL_HOST}:${existing_port}/${config_webBasePath}${plain}"
|
||||||
else
|
else
|
||||||
# If a cert already exists, just show the access URL
|
|
||||||
echo -e "${green}Access URL: https://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
|
echo -e "${green}Access URL: https://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
@ -817,8 +775,6 @@ config_after_install() {
|
||||||
echo -e "${green}Username, Password, and WebBasePath are properly set.${plain}"
|
echo -e "${green}Username, Password, and WebBasePath are properly set.${plain}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Existing install: if no cert configured, prompt user for SSL setup
|
|
||||||
# Properly detect empty cert by checking if cert: line exists and has content after it
|
|
||||||
existing_cert=$(${xui_folder}/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
|
existing_cert=$(${xui_folder}/x-ui setting -getCert true | grep 'cert:' | awk -F': ' '{print $2}' | tr -d '[:space:]')
|
||||||
if [[ -z "$existing_cert" ]]; then
|
if [[ -z "$existing_cert" ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
|
|
@ -840,19 +796,20 @@ config_after_install() {
|
||||||
install_x-ui() {
|
install_x-ui() {
|
||||||
cd ${xui_folder%/x-ui}/
|
cd ${xui_folder%/x-ui}/
|
||||||
|
|
||||||
# Download resources
|
local github_raw=$(get_github_raw)
|
||||||
|
|
||||||
if [ $# == 0 ]; then
|
if [ $# == 0 ]; then
|
||||||
tag_version=$(curl -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
tag_version=$(curl -Ls "https://api.github.com/repos/mhsanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
||||||
if [[ ! -n "$tag_version" ]]; then
|
if [[ ! -n "$tag_version" ]]; then
|
||||||
echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
|
echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
|
||||||
tag_version=$(curl -4 -Ls "https://api.github.com/repos/MHSanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
tag_version=$(curl -4 -Ls "https://api.github.com/repos/mhsanaei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
|
||||||
if [[ ! -n "$tag_version" ]]; then
|
if [[ ! -n "$tag_version" ]]; then
|
||||||
echo -e "${red}Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later${plain}"
|
echo -e "${red}Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
|
echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
|
||||||
curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
|
curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz https://github.com/mhsanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
echo -e "${red}Downloading x-ui failed, please be sure that your server can access GitHub ${plain}"
|
echo -e "${red}Downloading x-ui failed, please be sure that your server can access GitHub ${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -867,7 +824,7 @@ install_x-ui() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
url="https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz"
|
url="https://github.com/mhsanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz"
|
||||||
echo -e "Beginning to install x-ui $1"
|
echo -e "Beginning to install x-ui $1"
|
||||||
curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz ${url}
|
curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz ${url}
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
|
|
@ -875,13 +832,12 @@ install_x-ui() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
curl -4fLRo /usr/bin/x-ui-temp https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
|
curl -4fLRo /usr/bin/x-ui-temp ${github_raw}/mhsanaei/3x-ui/main/x-ui.sh
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
echo -e "${red}Failed to download x-ui.sh${plain}"
|
echo -e "${red}Failed to download x-ui.sh${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Stop x-ui service and remove old resources
|
|
||||||
if [[ -e ${xui_folder}/ ]]; then
|
if [[ -e ${xui_folder}/ ]]; then
|
||||||
if [[ $release == "alpine" ]]; then
|
if [[ $release == "alpine" ]]; then
|
||||||
rc-service x-ui stop
|
rc-service x-ui stop
|
||||||
|
|
@ -891,7 +847,6 @@ install_x-ui() {
|
||||||
rm ${xui_folder}/ -rf
|
rm ${xui_folder}/ -rf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extract resources and set permissions
|
|
||||||
tar zxvf x-ui-linux-$(arch).tar.gz
|
tar zxvf x-ui-linux-$(arch).tar.gz
|
||||||
rm x-ui-linux-$(arch).tar.gz -f
|
rm x-ui-linux-$(arch).tar.gz -f
|
||||||
|
|
||||||
|
|
@ -899,20 +854,17 @@ install_x-ui() {
|
||||||
chmod +x x-ui
|
chmod +x x-ui
|
||||||
chmod +x x-ui.sh
|
chmod +x x-ui.sh
|
||||||
|
|
||||||
# Check the system's architecture and rename the file accordingly
|
|
||||||
if [[ $(arch) == "armv5" || $(arch) == "armv6" || $(arch) == "armv7" ]]; then
|
if [[ $(arch) == "armv5" || $(arch) == "armv6" || $(arch) == "armv7" ]]; then
|
||||||
mv bin/xray-linux-$(arch) bin/xray-linux-arm
|
mv bin/xray-linux-$(arch) bin/xray-linux-arm
|
||||||
chmod +x bin/xray-linux-arm
|
chmod +x bin/xray-linux-arm
|
||||||
fi
|
fi
|
||||||
chmod +x x-ui bin/xray-linux-$(arch)
|
chmod +x x-ui bin/xray-linux-$(arch)
|
||||||
|
|
||||||
# Update x-ui cli and se set permission
|
|
||||||
mv -f /usr/bin/x-ui-temp /usr/bin/x-ui
|
mv -f /usr/bin/x-ui-temp /usr/bin/x-ui
|
||||||
chmod +x /usr/bin/x-ui
|
chmod +x /usr/bin/x-ui
|
||||||
mkdir -p /var/log/x-ui
|
mkdir -p /var/log/x-ui
|
||||||
config_after_install
|
config_after_install
|
||||||
|
|
||||||
# Etckeeper compatibility
|
|
||||||
if [ -d "/etc/.git" ]; then
|
if [ -d "/etc/.git" ]; then
|
||||||
if [ -f "/etc/.gitignore" ]; then
|
if [ -f "/etc/.gitignore" ]; then
|
||||||
if ! grep -q "x-ui/x-ui.db" "/etc/.gitignore"; then
|
if ! grep -q "x-ui/x-ui.db" "/etc/.gitignore"; then
|
||||||
|
|
@ -927,7 +879,7 @@ install_x-ui() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $release == "alpine" ]]; then
|
if [[ $release == "alpine" ]]; then
|
||||||
curl -4fLRo /etc/init.d/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.rc
|
curl -4fLRo /etc/init.d/x-ui ${github_raw}/mhsanaei/3x-ui/main/x-ui.rc
|
||||||
if [[ $? -ne 0 ]]; then
|
if [[ $? -ne 0 ]]; then
|
||||||
echo -e "${red}Failed to download x-ui.rc${plain}"
|
echo -e "${red}Failed to download x-ui.rc${plain}"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -936,7 +888,6 @@ install_x-ui() {
|
||||||
rc-update add x-ui
|
rc-update add x-ui
|
||||||
rc-service x-ui start
|
rc-service x-ui start
|
||||||
else
|
else
|
||||||
# Install systemd service file
|
|
||||||
service_installed=false
|
service_installed=false
|
||||||
|
|
||||||
if [ -f "x-ui.service" ]; then
|
if [ -f "x-ui.service" ]; then
|
||||||
|
|
@ -979,18 +930,17 @@ install_x-ui() {
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If service file not found in tar.gz, download from GitHub
|
|
||||||
if [ "$service_installed" = false ]; then
|
if [ "$service_installed" = false ]; then
|
||||||
echo -e "${yellow}Service files not found in tar.gz, downloading from GitHub...${plain}"
|
echo -e "${yellow}Service files not found in tar.gz, downloading from GitHub...${plain}"
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.debian > /dev/null 2>&1
|
curl -4fLRo ${xui_service}/x-ui.service ${github_raw}/mhsanaei/3x-ui/main/x-ui.service.debian > /dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
arch | manjaro | parch)
|
arch | manjaro | parch)
|
||||||
curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.arch > /dev/null 2>&1
|
curl -4fLRo ${xui_service}/x-ui.service ${github_raw}/mhsanaei/3x-ui/main/x-ui.service.arch > /dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
curl -4fLRo ${xui_service}/x-ui.service https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.service.rhel > /dev/null 2>&1
|
curl -4fLRo ${xui_service}/x-ui.service ${github_raw}/mhsanaei/3x-ui/main/x-ui.service.rhel > /dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
|
||||||
240
x-ui.sh
240
x-ui.sh
|
|
@ -6,7 +6,9 @@ blue='\033[0;34m'
|
||||||
yellow='\033[0;33m'
|
yellow='\033[0;33m'
|
||||||
plain='\033[0m'
|
plain='\033[0m'
|
||||||
|
|
||||||
#Add some basic function here
|
GITHUB_MIRROR_DEFAULT="https://gh.kejilion.pro"
|
||||||
|
GITHUB_RAW_DEFAULT="https://raw.githubusercontent.com"
|
||||||
|
|
||||||
function LOGD() {
|
function LOGD() {
|
||||||
echo -e "${yellow}[DEG] $* ${plain}"
|
echo -e "${yellow}[DEG] $* ${plain}"
|
||||||
}
|
}
|
||||||
|
|
@ -19,7 +21,33 @@ function LOGI() {
|
||||||
echo -e "${green}[INF] $* ${plain}"
|
echo -e "${green}[INF] $* ${plain}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Port helpers: detect listener and owning process (best effort)
|
get_github_mirror() {
|
||||||
|
local custom_mirror="${GITHUB_MIRROR:-}"
|
||||||
|
if [[ -n "$custom_mirror" ]]; then
|
||||||
|
echo "$custom_mirror"
|
||||||
|
else
|
||||||
|
echo "$GITHUB_MIRROR_DEFAULT"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_github_raw() {
|
||||||
|
local mirror=$(get_github_mirror)
|
||||||
|
if [[ "$mirror" == "$GITHUB_MIRROR_DEFAULT" ]]; then
|
||||||
|
echo "$GITHUB_RAW_DEFAULT"
|
||||||
|
else
|
||||||
|
echo "$mirror"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_github_download_url() {
|
||||||
|
local mirror=$(get_github_mirror)
|
||||||
|
if [[ "$mirror" == "$GITHUB_MIRROR_DEFAULT" ]]; then
|
||||||
|
echo "https://github.com"
|
||||||
|
else
|
||||||
|
echo "$mirror"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
is_port_in_use() {
|
is_port_in_use() {
|
||||||
local port="$1"
|
local port="$1"
|
||||||
if command -v ss > /dev/null 2>&1; then
|
if command -v ss > /dev/null 2>&1; then
|
||||||
|
|
@ -36,7 +64,6 @@ is_port_in_use() {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Simple helpers for domain/IP validation
|
|
||||||
is_ipv4() {
|
is_ipv4() {
|
||||||
[[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && return 0 || return 1
|
[[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] && return 0 || return 1
|
||||||
}
|
}
|
||||||
|
|
@ -50,10 +77,8 @@ is_domain() {
|
||||||
[[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+(xn--[a-z0-9]{2,}|[A-Za-z]{2,})$ ]] && return 0 || return 1
|
[[ "$1" =~ ^([A-Za-z0-9](-*[A-Za-z0-9])*\.)+(xn--[a-z0-9]{2,}|[A-Za-z]{2,})$ ]] && return 0 || return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# check root
|
|
||||||
[[ $EUID -ne 0 ]] && LOGE "ERROR: You must be root to run this script! \n" && exit 1
|
[[ $EUID -ne 0 ]] && LOGE "ERROR: You must be root to run this script! \n" && exit 1
|
||||||
|
|
||||||
# Check OS and set release variable
|
|
||||||
if [[ -f /etc/os-release ]]; then
|
if [[ -f /etc/os-release ]]; then
|
||||||
source /etc/os-release
|
source /etc/os-release
|
||||||
release=$ID
|
release=$ID
|
||||||
|
|
@ -69,7 +94,6 @@ 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 '.')
|
||||||
|
|
||||||
# Declare Variables
|
|
||||||
xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}"
|
xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}"
|
||||||
xui_service="${XUI_SERVICE:=/etc/systemd/system}"
|
xui_service="${XUI_SERVICE:=/etc/systemd/system}"
|
||||||
log_folder="${XUI_LOG_FOLDER:=/var/log/x-ui}"
|
log_folder="${XUI_LOG_FOLDER:=/var/log/x-ui}"
|
||||||
|
|
@ -77,6 +101,9 @@ mkdir -p "${log_folder}"
|
||||||
iplimit_log_path="${log_folder}/3xipl.log"
|
iplimit_log_path="${log_folder}/3xipl.log"
|
||||||
iplimit_banned_log_path="${log_folder}/3xipl-banned.log"
|
iplimit_banned_log_path="${log_folder}/3xipl-banned.log"
|
||||||
|
|
||||||
|
REPO_OWNER="mhsanaei"
|
||||||
|
REPO_NAME="3x-ui"
|
||||||
|
|
||||||
confirm() {
|
confirm() {
|
||||||
if [[ $# > 1 ]]; then
|
if [[ $# > 1 ]]; then
|
||||||
echo && read -rp "$1 [Default $2]: " temp
|
echo && read -rp "$1 [Default $2]: " temp
|
||||||
|
|
@ -108,7 +135,8 @@ before_show_menu() {
|
||||||
}
|
}
|
||||||
|
|
||||||
install() {
|
install() {
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh)
|
local github_raw=$(get_github_raw)
|
||||||
|
bash <(curl -Ls ${github_raw}/${REPO_OWNER}/${REPO_NAME}/main/install.sh)
|
||||||
if [[ $? == 0 ]]; then
|
if [[ $? == 0 ]]; then
|
||||||
if [[ $# == 0 ]]; then
|
if [[ $# == 0 ]]; then
|
||||||
start
|
start
|
||||||
|
|
@ -127,7 +155,8 @@ update() {
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/update.sh)
|
local github_raw=$(get_github_raw)
|
||||||
|
bash <(curl -Ls ${github_raw}/${REPO_OWNER}/${REPO_NAME}/main/update.sh)
|
||||||
if [[ $? == 0 ]]; then
|
if [[ $? == 0 ]]; then
|
||||||
LOGI "Update is complete, Panel has automatically restarted "
|
LOGI "Update is complete, Panel has automatically restarted "
|
||||||
before_show_menu
|
before_show_menu
|
||||||
|
|
@ -145,7 +174,8 @@ update_menu() {
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
curl -fLRo /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh
|
local github_raw=$(get_github_raw)
|
||||||
|
curl -fLRo /usr/bin/x-ui ${github_raw}/${REPO_OWNER}/${REPO_NAME}/main/x-ui.sh
|
||||||
chmod +x ${xui_folder}/x-ui.sh
|
chmod +x ${xui_folder}/x-ui.sh
|
||||||
chmod +x /usr/bin/x-ui
|
chmod +x /usr/bin/x-ui
|
||||||
|
|
||||||
|
|
@ -166,16 +196,14 @@ legacy_version() {
|
||||||
echo "Panel version cannot be empty. Exiting."
|
echo "Panel version cannot be empty. Exiting."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# Use the entered panel version in the download link
|
local install_command="bash <(curl -Ls ${github_raw}/${REPO_OWNER}/${REPO_NAME}/v$tag_version/install.sh) v$tag_version"
|
||||||
install_command="bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/v$tag_version/install.sh") v$tag_version"
|
|
||||||
|
|
||||||
echo "Downloading and installing panel version $tag_version..."
|
echo "Downloading and installing panel version $tag_version..."
|
||||||
eval $install_command
|
eval $install_command
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to handle the deletion of the script file
|
|
||||||
delete_script() {
|
delete_script() {
|
||||||
rm "$0" # Remove the script file itself
|
rm "$0"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,9 +234,9 @@ uninstall() {
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "Uninstalled Successfully.\n"
|
echo -e "Uninstalled Successfully.\n"
|
||||||
echo "If you need to install this panel again, you can use below command:"
|
echo "If you need to install this panel again, you can use below command:"
|
||||||
echo -e "${green}bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)${plain}"
|
local github_raw=$(get_github_raw)
|
||||||
|
echo -e "${green}bash <(curl -Ls ${github_raw}/${REPO_OWNER}/${REPO_NAME}/main/install.sh)${plain}"
|
||||||
echo ""
|
echo ""
|
||||||
# Trap the SIGTERM signal
|
|
||||||
trap delete_script SIGTERM
|
trap delete_script SIGTERM
|
||||||
delete_script
|
delete_script
|
||||||
}
|
}
|
||||||
|
|
@ -259,7 +287,6 @@ reset_webbasepath() {
|
||||||
|
|
||||||
config_webBasePath=$(gen_random_string 18)
|
config_webBasePath=$(gen_random_string 18)
|
||||||
|
|
||||||
# Apply the new web base path setting
|
|
||||||
${xui_folder}/x-ui setting -webBasePath "${config_webBasePath}" > /dev/null 2>&1
|
${xui_folder}/x-ui setting -webBasePath "${config_webBasePath}" > /dev/null 2>&1
|
||||||
|
|
||||||
echo -e "Web base path has been reset to: ${green}${config_webBasePath}${plain}"
|
echo -e "Web base path has been reset to: ${green}${config_webBasePath}${plain}"
|
||||||
|
|
@ -340,7 +367,6 @@ check_config() {
|
||||||
ssl_cert_issue_for_ip
|
ssl_cert_issue_for_ip
|
||||||
if [[ $? -eq 0 ]]; then
|
if [[ $? -eq 0 ]]; then
|
||||||
echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
|
echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}"
|
||||||
# ssl_cert_issue_for_ip already restarts the panel, but ensure it's running
|
|
||||||
start 0 > /dev/null 2>&1
|
start 0 > /dev/null 2>&1
|
||||||
else
|
else
|
||||||
LOGE "IP certificate setup failed."
|
LOGE "IP certificate setup failed."
|
||||||
|
|
@ -579,7 +605,6 @@ disable_bbr() {
|
||||||
rm /etc/sysctl.d/99-bbr-x-ui.conf
|
rm /etc/sysctl.d/99-bbr-x-ui.conf
|
||||||
sysctl --system
|
sysctl --system
|
||||||
else
|
else
|
||||||
# Replace BBR with CUBIC configurations
|
|
||||||
if [ -f "/etc/sysctl.conf" ]; then
|
if [ -f "/etc/sysctl.conf" ]; then
|
||||||
sed -i 's/net.core.default_qdisc=fq/net.core.default_qdisc=pfifo_fast/' /etc/sysctl.conf
|
sed -i 's/net.core.default_qdisc=fq/net.core.default_qdisc=pfifo_fast/' /etc/sysctl.conf
|
||||||
sed -i 's/net.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf
|
sed -i 's/net.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf
|
||||||
|
|
@ -600,7 +625,6 @@ enable_bbr() {
|
||||||
before_show_menu
|
before_show_menu
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Enable BBR
|
|
||||||
if [ -d "/etc/sysctl.d/" ]; then
|
if [ -d "/etc/sysctl.d/" ]; then
|
||||||
{
|
{
|
||||||
echo "#$(sysctl -n net.core.default_qdisc):$(sysctl -n net.ipv4.tcp_congestion_control)"
|
echo "#$(sysctl -n net.core.default_qdisc):$(sysctl -n net.ipv4.tcp_congestion_control)"
|
||||||
|
|
@ -608,7 +632,6 @@ enable_bbr() {
|
||||||
echo "net.ipv4.tcp_congestion_control = bbr"
|
echo "net.ipv4.tcp_congestion_control = bbr"
|
||||||
} > "/etc/sysctl.d/99-bbr-x-ui.conf"
|
} > "/etc/sysctl.d/99-bbr-x-ui.conf"
|
||||||
if [ -f "/etc/sysctl.conf" ]; then
|
if [ -f "/etc/sysctl.conf" ]; then
|
||||||
# Backup old settings from sysctl.conf, if any
|
|
||||||
sed -i 's/^net.core.default_qdisc/# &/' /etc/sysctl.conf
|
sed -i 's/^net.core.default_qdisc/# &/' /etc/sysctl.conf
|
||||||
sed -i 's/^net.ipv4.tcp_congestion_control/# &/' /etc/sysctl.conf
|
sed -i 's/^net.ipv4.tcp_congestion_control/# &/' /etc/sysctl.conf
|
||||||
fi
|
fi
|
||||||
|
|
@ -621,7 +644,6 @@ enable_bbr() {
|
||||||
sysctl -p
|
sysctl -p
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify that BBR is enabled
|
|
||||||
if [[ $(sysctl -n net.ipv4.tcp_congestion_control) == "bbr" ]]; then
|
if [[ $(sysctl -n net.ipv4.tcp_congestion_control) == "bbr" ]]; then
|
||||||
echo -e "${green}BBR has been enabled successfully.${plain}"
|
echo -e "${green}BBR has been enabled successfully.${plain}"
|
||||||
else
|
else
|
||||||
|
|
@ -630,7 +652,9 @@ enable_bbr() {
|
||||||
}
|
}
|
||||||
|
|
||||||
update_shell() {
|
update_shell() {
|
||||||
curl -fLRo /usr/bin/x-ui -z /usr/bin/x-ui https://github.com/MHSanaei/3x-ui/raw/main/x-ui.sh
|
local github_raw=$(get_github_raw)
|
||||||
|
local github_download=$(get_github_download_url)
|
||||||
|
curl -fLRo /usr/bin/x-ui -z /usr/bin/x-ui ${github_raw}/${REPO_OWNER}/${REPO_NAME}/main/x-ui.sh
|
||||||
if [[ $? != 0 ]]; then
|
if [[ $? != 0 ]]; then
|
||||||
echo ""
|
echo ""
|
||||||
LOGE "Failed to download script, Please check whether the machine can connect Github"
|
LOGE "Failed to download script, Please check whether the machine can connect Github"
|
||||||
|
|
@ -642,7 +666,6 @@ update_shell() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 0: running, 1: not running, 2: not installed
|
|
||||||
check_status() {
|
check_status() {
|
||||||
if [[ $release == "alpine" ]]; then
|
if [[ $release == "alpine" ]]; then
|
||||||
if [[ ! -f /etc/init.d/x-ui ]]; then
|
if [[ ! -f /etc/init.d/x-ui ]]; then
|
||||||
|
|
@ -814,130 +837,103 @@ install_firewall() {
|
||||||
echo "ufw firewall is already installed"
|
echo "ufw firewall is already installed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if the firewall is inactive
|
|
||||||
if ufw status | grep -q "Status: active"; then
|
if ufw status | grep -q "Status: active"; then
|
||||||
echo "Firewall is already active"
|
echo "Firewall is already active"
|
||||||
else
|
else
|
||||||
echo "Activating firewall..."
|
echo "Activating firewall..."
|
||||||
# Open the necessary ports
|
|
||||||
ufw allow ssh
|
ufw allow ssh
|
||||||
ufw allow http
|
ufw allow http
|
||||||
ufw allow https
|
ufw allow https
|
||||||
ufw allow 2053/tcp #webPort
|
ufw allow 2053/tcp
|
||||||
ufw allow 2096/tcp #subport
|
ufw allow 2096/tcp
|
||||||
|
|
||||||
# Enable the firewall
|
|
||||||
ufw --force enable
|
ufw --force enable
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
open_ports() {
|
open_ports() {
|
||||||
# Prompt the user to enter the ports they want to open
|
|
||||||
read -rp "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
|
|
||||||
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
|
||||||
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
|
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Open the specified ports using ufw
|
|
||||||
IFS=',' read -ra PORT_LIST <<< "$ports"
|
IFS=',' read -ra PORT_LIST <<< "$ports"
|
||||||
for port in "${PORT_LIST[@]}"; do
|
for port in "${PORT_LIST[@]}"; do
|
||||||
if [[ $port == *-* ]]; then
|
if [[ $port == *-* ]]; then
|
||||||
# Split the range into start and end ports
|
|
||||||
start_port=$(echo $port | cut -d'-' -f1)
|
start_port=$(echo $port | cut -d'-' -f1)
|
||||||
end_port=$(echo $port | cut -d'-' -f2)
|
end_port=$(echo $port | cut -d'-' -f2)
|
||||||
# Open the port range
|
|
||||||
ufw allow $start_port:$end_port/tcp
|
ufw allow $start_port:$end_port/tcp
|
||||||
ufw allow $start_port:$end_port/udp
|
ufw allow $start_port:$end_port/udp
|
||||||
else
|
else
|
||||||
# Open the single port
|
|
||||||
ufw allow "$port"
|
ufw allow "$port"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Confirm that the ports are opened
|
|
||||||
echo "Opened the specified ports:"
|
echo "Opened the specified ports:"
|
||||||
for port in "${PORT_LIST[@]}"; do
|
for port in "${PORT_LIST[@]}"; do
|
||||||
if [[ $port == *-* ]]; then
|
if [[ $port == *-* ]]; then
|
||||||
start_port=$(echo $port | cut -d'-' -f1)
|
start_port=$(echo $port | cut -d'-' -f1)
|
||||||
end_port=$(echo $port | cut -d'-' -f2)
|
end_port=$(echo $port | cut -d'-' -f2)
|
||||||
# Check if the port range has been successfully opened
|
|
||||||
(ufw status | grep -q "$start_port:$end_port") && echo "$start_port-$end_port"
|
(ufw status | grep -q "$start_port:$end_port") && echo "$start_port-$end_port"
|
||||||
else
|
else
|
||||||
# Check if the individual port has been successfully opened
|
|
||||||
(ufw status | grep -q "$port") && echo "$port"
|
(ufw status | grep -q "$port") && echo "$port"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_ports() {
|
delete_ports() {
|
||||||
# Display current rules with numbers
|
|
||||||
echo "Current UFW rules:"
|
echo "Current UFW rules:"
|
||||||
ufw status numbered
|
ufw status numbered
|
||||||
|
|
||||||
# Ask the user how they want to delete rules
|
|
||||||
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 -rp "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
|
|
||||||
read -rp "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
|
|
||||||
if ! [[ $rule_numbers =~ ^([0-9]+)(,[0-9]+)*$ ]]; then
|
if ! [[ $rule_numbers =~ ^([0-9]+)(,[0-9]+)*$ ]]; then
|
||||||
echo "Error: Invalid input. Please enter a comma-separated list of rule numbers." >&2
|
echo "Error: Invalid input. Please enter a comma-separated list of rule numbers." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Split numbers into an array
|
|
||||||
IFS=',' read -ra RULE_NUMBERS <<< "$rule_numbers"
|
IFS=',' read -ra RULE_NUMBERS <<< "$rule_numbers"
|
||||||
for rule_number in "${RULE_NUMBERS[@]}"; do
|
for rule_number in "${RULE_NUMBERS[@]}"; do
|
||||||
# Delete the rule by number
|
|
||||||
ufw delete "$rule_number" || echo "Failed to delete rule number $rule_number"
|
ufw delete "$rule_number" || echo "Failed to delete rule number $rule_number"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Selected rules have been deleted."
|
echo "Selected rules have been deleted."
|
||||||
|
|
||||||
elif [[ $choice -eq 2 ]]; then
|
elif [[ $choice -eq 2 ]]; then
|
||||||
# Deleting by ports
|
|
||||||
read -rp "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
|
|
||||||
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
|
||||||
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
|
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Split ports into an array
|
|
||||||
IFS=',' read -ra PORT_LIST <<< "$ports"
|
IFS=',' read -ra PORT_LIST <<< "$ports"
|
||||||
for port in "${PORT_LIST[@]}"; do
|
for port in "${PORT_LIST[@]}"; do
|
||||||
if [[ $port == *-* ]]; then
|
if [[ $port == *-* ]]; then
|
||||||
# Split the port range
|
|
||||||
start_port=$(echo $port | cut -d'-' -f1)
|
start_port=$(echo $port | cut -d'-' -f1)
|
||||||
end_port=$(echo $port | cut -d'-' -f2)
|
end_port=$(echo $port | cut -d'-' -f2)
|
||||||
# Delete the port range
|
|
||||||
ufw delete allow $start_port:$end_port/tcp
|
ufw delete allow $start_port:$end_port/tcp
|
||||||
ufw delete allow $start_port:$end_port/udp
|
ufw delete allow $start_port:$end_port/udp
|
||||||
else
|
else
|
||||||
# Delete a single port
|
|
||||||
ufw delete allow "$port"
|
ufw delete allow "$port"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Confirmation of deletion
|
|
||||||
echo "Deleted the specified ports:"
|
echo "Deleted the specified ports:"
|
||||||
for port in "${PORT_LIST[@]}"; do
|
for port in "${PORT_LIST[@]}"; do
|
||||||
if [[ $port == *-* ]]; then
|
if [[ $port == *-* ]]; then
|
||||||
start_port=$(echo $port | cut -d'-' -f1)
|
start_port=$(echo $port | cut -d'-' -f1)
|
||||||
end_port=$(echo $port | cut -d'-' -f2)
|
end_port=$(echo $port | cut -d'-' -f2)
|
||||||
# Check if the port range has been deleted
|
|
||||||
(ufw status | grep -q "$start_port:$end_port") || echo "$start_port-$end_port"
|
(ufw status | grep -q "$start_port:$end_port") || echo "$start_port-$end_port"
|
||||||
else
|
else
|
||||||
# Check if the individual port has been deleted
|
|
||||||
(ufw status | grep -q "$port") || echo "$port"
|
(ufw status | grep -q "$port") || echo "$port"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
@ -969,7 +965,6 @@ update_geofiles() {
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
for dat in "${dat_files[@]}"; do
|
for dat in "${dat_files[@]}"; do
|
||||||
# Remove suffix for remote filename (e.g., geoip_IR -> geoip)
|
|
||||||
remote_file="${dat%%_*}"
|
remote_file="${dat%%_*}"
|
||||||
curl -fLRo ${xui_folder}/bin/${dat}.dat -z ${xui_folder}/bin/${dat}.dat \
|
curl -fLRo ${xui_folder}/bin/${dat}.dat -z ${xui_folder}/bin/${dat}.dat \
|
||||||
https://github.com/${dat_source}/releases/latest/download/${remote_file}.dat
|
https://github.com/${dat_source}/releases/latest/download/${remote_file}.dat
|
||||||
|
|
@ -1018,14 +1013,13 @@ update_geo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
install_acme() {
|
install_acme() {
|
||||||
# Check if acme.sh is already installed
|
|
||||||
if command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
if command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
||||||
LOGI "acme.sh is already installed."
|
LOGI "acme.sh is already installed."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LOGI "Installing acme.sh..."
|
LOGI "Installing acme.sh..."
|
||||||
cd ~ || return 1 # Ensure you can change to the home directory
|
cd ~ || return 1
|
||||||
|
|
||||||
curl -s https://get.acme.sh | sh
|
curl -s https://get.acme.sh | sh
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
|
@ -1164,7 +1158,6 @@ ssl_cert_issue_for_ip() {
|
||||||
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
||||||
local existing_port=$(${xui_folder}/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
local existing_port=$(${xui_folder}/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
||||||
|
|
||||||
# Get server IP
|
|
||||||
local URL_lists=(
|
local URL_lists=(
|
||||||
"https://api4.ipify.org"
|
"https://api4.ipify.org"
|
||||||
"https://ipv4.icanhazip.com"
|
"https://ipv4.icanhazip.com"
|
||||||
|
|
@ -1198,12 +1191,10 @@ ssl_cert_issue_for_ip() {
|
||||||
|
|
||||||
LOGI "Server IP detected: ${server_ip}"
|
LOGI "Server IP detected: ${server_ip}"
|
||||||
|
|
||||||
# Ask for optional IPv6
|
|
||||||
local ipv6_addr=""
|
local ipv6_addr=""
|
||||||
read -rp "Do you have an IPv6 address to include? (leave empty to skip): " ipv6_addr
|
read -rp "Do you have an IPv6 address to include? (leave empty to skip): " ipv6_addr
|
||||||
ipv6_addr="${ipv6_addr// /}" # Trim whitespace
|
ipv6_addr="${ipv6_addr// /}"
|
||||||
|
|
||||||
# check for acme.sh first
|
|
||||||
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
||||||
LOGI "acme.sh not found, installing..."
|
LOGI "acme.sh not found, installing..."
|
||||||
install_acme
|
install_acme
|
||||||
|
|
@ -1213,7 +1204,6 @@ ssl_cert_issue_for_ip() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# install socat
|
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
apt-get update > /dev/null 2>&1 && apt-get install socat -y > /dev/null 2>&1
|
apt-get update > /dev/null 2>&1 && apt-get install socat -y > /dev/null 2>&1
|
||||||
|
|
@ -1238,22 +1228,18 @@ ssl_cert_issue_for_ip() {
|
||||||
apk add socat curl openssl > /dev/null 2>&1
|
apk add socat curl openssl > /dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
LOGW "Unsupported OS for automatic socat installation"
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Create certificate directory
|
|
||||||
certPath="/root/cert/ip"
|
certPath="/root/cert/ip"
|
||||||
mkdir -p "$certPath"
|
mkdir -p "$certPath"
|
||||||
|
|
||||||
# Build domain arguments
|
|
||||||
local domain_args="-d ${server_ip}"
|
local domain_args="-d ${server_ip}"
|
||||||
if [[ -n "$ipv6_addr" ]] && is_ipv6 "$ipv6_addr"; then
|
if [[ -n "$ipv6_addr" ]] && is_ipv6 "$ipv6_addr"; then
|
||||||
domain_args="${domain_args} -d ${ipv6_addr}"
|
domain_args="${domain_args} -d ${ipv6_addr}"
|
||||||
LOGI "Including IPv6 address: ${ipv6_addr}"
|
LOGI "Including IPv6 address: ${ipv6_addr}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Choose port for HTTP-01 listener (default 80, allow override)
|
|
||||||
local WebPort=""
|
local WebPort=""
|
||||||
read -rp "Port to use for ACME HTTP-01 listener (default 80): " WebPort
|
read -rp "Port to use for ACME HTTP-01 listener (default 80): " WebPort
|
||||||
WebPort="${WebPort:-80}"
|
WebPort="${WebPort:-80}"
|
||||||
|
|
@ -1289,10 +1275,8 @@ ssl_cert_issue_for_ip() {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Reload command - restarts panel after renewal
|
|
||||||
local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null"
|
local reloadCmd="systemctl restart x-ui 2>/dev/null || rc-service x-ui restart 2>/dev/null"
|
||||||
|
|
||||||
# issue the certificate for IP with shortlived profile
|
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||||
~/.acme.sh/acme.sh --issue \
|
~/.acme.sh/acme.sh --issue \
|
||||||
${domain_args} \
|
${domain_args} \
|
||||||
|
|
@ -1306,7 +1290,6 @@ ssl_cert_issue_for_ip() {
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Failed to issue certificate for IP: ${server_ip}"
|
LOGE "Failed to issue certificate for IP: ${server_ip}"
|
||||||
LOGE "Make sure port ${WebPort} is open and the server is accessible from the internet"
|
LOGE "Make sure port ${WebPort} is open and the server is accessible from the internet"
|
||||||
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
|
||||||
rm -rf ~/.acme.sh/${server_ip} 2> /dev/null
|
rm -rf ~/.acme.sh/${server_ip} 2> /dev/null
|
||||||
[[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} 2> /dev/null
|
[[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} 2> /dev/null
|
||||||
rm -rf ${certPath} 2> /dev/null
|
rm -rf ${certPath} 2> /dev/null
|
||||||
|
|
@ -1315,18 +1298,13 @@ ssl_cert_issue_for_ip() {
|
||||||
LOGI "Certificate issued successfully for IP: ${server_ip}"
|
LOGI "Certificate issued successfully for IP: ${server_ip}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install the certificate
|
|
||||||
# Note: acme.sh may report "Reload error" and exit non-zero if reloadcmd fails,
|
|
||||||
# but the cert files are still installed. We check for files instead of exit code.
|
|
||||||
~/.acme.sh/acme.sh --installcert -d ${server_ip} \
|
~/.acme.sh/acme.sh --installcert -d ${server_ip} \
|
||||||
--key-file "${certPath}/privkey.pem" \
|
--key-file "${certPath}/privkey.pem" \
|
||||||
--fullchain-file "${certPath}/fullchain.pem" \
|
--fullchain-file "${certPath}/fullchain.pem" \
|
||||||
--reloadcmd "${reloadCmd}" 2>&1 || true
|
--reloadcmd "${reloadCmd}" 2>&1 || true
|
||||||
|
|
||||||
# Verify certificate files exist (don't rely on exit code - reloadcmd failure causes non-zero)
|
|
||||||
if [[ ! -f "${certPath}/fullchain.pem" || ! -f "${certPath}/privkey.pem" ]]; then
|
if [[ ! -f "${certPath}/fullchain.pem" || ! -f "${certPath}/privkey.pem" ]]; then
|
||||||
LOGE "Certificate files not found after installation"
|
LOGE "Certificate files not found after installation"
|
||||||
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
|
|
||||||
rm -rf ~/.acme.sh/${server_ip} 2> /dev/null
|
rm -rf ~/.acme.sh/${server_ip} 2> /dev/null
|
||||||
[[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} 2> /dev/null
|
[[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} 2> /dev/null
|
||||||
rm -rf ${certPath} 2> /dev/null
|
rm -rf ${certPath} 2> /dev/null
|
||||||
|
|
@ -1335,12 +1313,10 @@ ssl_cert_issue_for_ip() {
|
||||||
|
|
||||||
LOGI "Certificate files installed successfully"
|
LOGI "Certificate files installed successfully"
|
||||||
|
|
||||||
# enable auto-renew
|
|
||||||
~/.acme.sh/acme.sh --upgrade --auto-upgrade > /dev/null 2>&1
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade > /dev/null 2>&1
|
||||||
chmod 600 $certPath/privkey.pem 2> /dev/null
|
chmod 600 $certPath/privkey.pem 2> /dev/null
|
||||||
chmod 644 $certPath/fullchain.pem 2> /dev/null
|
chmod 644 $certPath/fullchain.pem 2> /dev/null
|
||||||
|
|
||||||
# Set certificate paths for the panel
|
|
||||||
local webCertFile="${certPath}/fullchain.pem"
|
local webCertFile="${certPath}/fullchain.pem"
|
||||||
local webKeyFile="${certPath}/privkey.pem"
|
local webKeyFile="${certPath}/privkey.pem"
|
||||||
|
|
||||||
|
|
@ -1363,7 +1339,6 @@ ssl_cert_issue_for_ip() {
|
||||||
ssl_cert_issue() {
|
ssl_cert_issue() {
|
||||||
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
local existing_webBasePath=$(${xui_folder}/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}')
|
||||||
local existing_port=$(${xui_folder}/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
local existing_port=$(${xui_folder}/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
|
||||||
# check for acme.sh first
|
|
||||||
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
||||||
echo "acme.sh could not be found. we will install it"
|
echo "acme.sh could not be found. we will install it"
|
||||||
install_acme
|
install_acme
|
||||||
|
|
@ -1373,7 +1348,6 @@ ssl_cert_issue() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# install socat
|
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
ubuntu | debian | armbian)
|
ubuntu | debian | armbian)
|
||||||
apt-get update > /dev/null 2>&1 && apt-get install socat -y > /dev/null 2>&1
|
apt-get update > /dev/null 2>&1 && apt-get install socat -y > /dev/null 2>&1
|
||||||
|
|
@ -1398,7 +1372,6 @@ ssl_cert_issue() {
|
||||||
apk add socat curl openssl > /dev/null 2>&1
|
apk add socat curl openssl > /dev/null 2>&1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
LOGW "Unsupported OS for automatic socat installation"
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
|
@ -1408,11 +1381,10 @@ ssl_cert_issue() {
|
||||||
LOGI "install socat succeed..."
|
LOGI "install socat succeed..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# get the domain here, and we need to verify it
|
|
||||||
local domain=""
|
local domain=""
|
||||||
while true; do
|
while true; do
|
||||||
read -rp "Please enter your domain name: " domain
|
read -rp "Please enter your domain name: " domain
|
||||||
domain="${domain// /}" # Trim whitespace
|
domain="${domain// /}"
|
||||||
|
|
||||||
if [[ -z "$domain" ]]; then
|
if [[ -z "$domain" ]]; then
|
||||||
LOGE "Domain name cannot be empty. Please try again."
|
LOGE "Domain name cannot be empty. Please try again."
|
||||||
|
|
@ -1429,7 +1401,6 @@ ssl_cert_issue() {
|
||||||
LOGD "Your domain is: ${domain}, checking it..."
|
LOGD "Your domain is: ${domain}, checking it..."
|
||||||
SSL_ISSUED_DOMAIN="${domain}"
|
SSL_ISSUED_DOMAIN="${domain}"
|
||||||
|
|
||||||
# detect existing certificate and reuse it if present
|
|
||||||
local cert_exists=0
|
local cert_exists=0
|
||||||
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
|
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
|
||||||
cert_exists=1
|
cert_exists=1
|
||||||
|
|
@ -1440,7 +1411,6 @@ ssl_cert_issue() {
|
||||||
LOGI "Your domain is ready for issuing certificates now..."
|
LOGI "Your domain is ready for issuing certificates now..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# create a directory for the certificate
|
|
||||||
certPath="/root/cert/${domain}"
|
certPath="/root/cert/${domain}"
|
||||||
if [ ! -d "$certPath" ]; then
|
if [ ! -d "$certPath" ]; then
|
||||||
mkdir -p "$certPath"
|
mkdir -p "$certPath"
|
||||||
|
|
@ -1449,7 +1419,6 @@ ssl_cert_issue() {
|
||||||
mkdir -p "$certPath"
|
mkdir -p "$certPath"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# get the port number for the standalone server
|
|
||||||
local WebPort=80
|
local WebPort=80
|
||||||
read -rp "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
|
||||||
|
|
@ -1459,7 +1428,6 @@ ssl_cert_issue() {
|
||||||
LOGI "Will use port: ${WebPort} to issue certificates. Please make sure this port is open."
|
LOGI "Will use port: ${WebPort} to issue certificates. Please make sure this port is open."
|
||||||
|
|
||||||
if [[ ${cert_exists} -eq 0 ]]; then
|
if [[ ${cert_exists} -eq 0 ]]; then
|
||||||
# issue the certificate
|
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||||
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
|
|
@ -1499,7 +1467,6 @@ ssl_cert_issue() {
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# install the certificate
|
|
||||||
local installOutput=""
|
local installOutput=""
|
||||||
installOutput=$(~/.acme.sh/acme.sh --installcert -d ${domain} \
|
installOutput=$(~/.acme.sh/acme.sh --installcert -d ${domain} \
|
||||||
--key-file /root/cert/${domain}/privkey.pem \
|
--key-file /root/cert/${domain}/privkey.pem \
|
||||||
|
|
@ -1522,7 +1489,6 @@ ssl_cert_issue() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# enable auto-renew
|
|
||||||
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Auto renew failed, certificate details:"
|
LOGE "Auto renew failed, certificate details:"
|
||||||
|
|
@ -1537,7 +1503,6 @@ ssl_cert_issue() {
|
||||||
chmod 644 $certPath/fullchain.pem
|
chmod 644 $certPath/fullchain.pem
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Prompt user to set panel paths after successful certificate installation
|
|
||||||
read -rp "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"
|
||||||
|
|
@ -1572,7 +1537,6 @@ ssl_cert_issue_CF() {
|
||||||
confirm "Do you confirm the information and wish to proceed? [y/n]" "y"
|
confirm "Do you confirm the information and wish to proceed? [y/n]" "y"
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
# Check for acme.sh first
|
|
||||||
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
|
||||||
echo "acme.sh could not be found. We will install it."
|
echo "acme.sh could not be found. We will install it."
|
||||||
install_acme
|
install_acme
|
||||||
|
|
@ -1588,7 +1552,6 @@ ssl_cert_issue_CF() {
|
||||||
read -rp "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
|
|
||||||
CF_GlobalKey=""
|
CF_GlobalKey=""
|
||||||
CF_AccountEmail=""
|
CF_AccountEmail=""
|
||||||
LOGD "Please set the API key:"
|
LOGD "Please set the API key:"
|
||||||
|
|
@ -1599,7 +1562,6 @@ ssl_cert_issue_CF() {
|
||||||
read -rp "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
|
|
||||||
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Default CA, Let'sEncrypt fail, script exiting..."
|
LOGE "Default CA, Let'sEncrypt fail, script exiting..."
|
||||||
|
|
@ -1609,7 +1571,6 @@ ssl_cert_issue_CF() {
|
||||||
export CF_Key="${CF_GlobalKey}"
|
export CF_Key="${CF_GlobalKey}"
|
||||||
export CF_Email="${CF_AccountEmail}"
|
export CF_Email="${CF_AccountEmail}"
|
||||||
|
|
||||||
# Issue the certificate using Cloudflare DNS
|
|
||||||
~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log --force
|
~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log --force
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Certificate issuance failed, script exiting..."
|
LOGE "Certificate issuance failed, script exiting..."
|
||||||
|
|
@ -1618,7 +1579,6 @@ ssl_cert_issue_CF() {
|
||||||
LOGI "Certificate issued successfully, Installing..."
|
LOGI "Certificate issued successfully, Installing..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Install the certificate
|
|
||||||
certPath="/root/cert/${CF_Domain}"
|
certPath="/root/cert/${CF_Domain}"
|
||||||
if [ -d "$certPath" ]; then
|
if [ -d "$certPath" ]; then
|
||||||
rm -rf ${certPath}
|
rm -rf ${certPath}
|
||||||
|
|
@ -1666,7 +1626,6 @@ ssl_cert_issue_CF() {
|
||||||
LOGI "Certificate installed successfully, Turning on automatic updates..."
|
LOGI "Certificate installed successfully, Turning on automatic updates..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Enable auto-update
|
|
||||||
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
~/.acme.sh/acme.sh --upgrade --auto-upgrade
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
LOGE "Auto update setup failed, script exiting..."
|
LOGE "Auto update setup failed, script exiting..."
|
||||||
|
|
@ -1678,7 +1637,6 @@ ssl_cert_issue_CF() {
|
||||||
chmod 644 ${certPath}/fullchain.pem
|
chmod 644 ${certPath}/fullchain.pem
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Prompt user to set panel paths after successful certificate installation
|
|
||||||
read -rp "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"
|
||||||
|
|
@ -1703,15 +1661,11 @@ ssl_cert_issue_CF() {
|
||||||
}
|
}
|
||||||
|
|
||||||
run_speedtest() {
|
run_speedtest() {
|
||||||
# Check if Speedtest is already installed
|
|
||||||
if ! command -v speedtest &> /dev/null; then
|
if ! command -v speedtest &> /dev/null; then
|
||||||
# If not installed, determine installation method
|
|
||||||
if command -v snap &> /dev/null; then
|
if command -v snap &> /dev/null; then
|
||||||
# Use snap to install Speedtest
|
|
||||||
echo "Installing Speedtest using snap..."
|
echo "Installing Speedtest using snap..."
|
||||||
snap install speedtest
|
snap install speedtest
|
||||||
else
|
else
|
||||||
# Fallback to using package managers
|
|
||||||
local pkg_manager=""
|
local pkg_manager=""
|
||||||
local speedtest_install_script=""
|
local speedtest_install_script=""
|
||||||
|
|
||||||
|
|
@ -1745,7 +1699,7 @@ run_speedtest() {
|
||||||
|
|
||||||
ip_validation() {
|
ip_validation() {
|
||||||
ipv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
|
ipv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
|
||||||
ipv4_regex="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)$"
|
ipv4_regex="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|0)\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|0)$"
|
||||||
}
|
}
|
||||||
|
|
||||||
iplimit_main() {
|
iplimit_main() {
|
||||||
|
|
@ -1856,14 +1810,6 @@ install_iplimit() {
|
||||||
if ! command -v fail2ban-client &> /dev/null; then
|
if ! command -v fail2ban-client &> /dev/null; then
|
||||||
echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n"
|
echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n"
|
||||||
|
|
||||||
# Install fail2ban together with nftables. Recent fail2ban packages
|
|
||||||
# default to `banaction = nftables-multiport` in /etc/fail2ban/jail.conf,
|
|
||||||
# but the `nftables` package isn't pulled in as a dependency on most
|
|
||||||
# minimal server images (Debian 12+, Ubuntu 24+, fresh RHEL-family).
|
|
||||||
# Without `nft` in PATH the default sshd jail fails to ban with
|
|
||||||
# stderr: '/bin/sh: 1: nft: not found'
|
|
||||||
# even though our own 3x-ipl jail uses iptables. Bundling the binary
|
|
||||||
# at install time prevents that confusing log spam for new installs.
|
|
||||||
case "${release}" in
|
case "${release}" in
|
||||||
ubuntu)
|
ubuntu)
|
||||||
apt-get update
|
apt-get update
|
||||||
|
|
@ -1918,24 +1864,18 @@ install_iplimit() {
|
||||||
|
|
||||||
echo -e "${green}Configuring IP Limit...${plain}\n"
|
echo -e "${green}Configuring IP Limit...${plain}\n"
|
||||||
|
|
||||||
# make sure there's no conflict for jail files
|
|
||||||
iplimit_remove_conflicts
|
iplimit_remove_conflicts
|
||||||
|
|
||||||
# Check if log file exists
|
|
||||||
if ! test -f "${iplimit_banned_log_path}"; then
|
if ! test -f "${iplimit_banned_log_path}"; then
|
||||||
touch ${iplimit_banned_log_path}
|
touch ${iplimit_banned_log_path}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if service log file exists so fail2ban won't return error
|
|
||||||
if ! test -f "${iplimit_log_path}"; then
|
if ! test -f "${iplimit_log_path}"; then
|
||||||
touch ${iplimit_log_path}
|
touch ${iplimit_log_path}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create the iplimit jail files
|
|
||||||
# we didn't pass the bantime here to use the default value
|
|
||||||
create_iplimit_jails
|
create_iplimit_jails
|
||||||
|
|
||||||
# Launching fail2ban
|
|
||||||
if [[ $release == "alpine" ]]; then
|
if [[ $release == "alpine" ]]; then
|
||||||
if [[ $(rc-service fail2ban status | grep -F 'status: started' -c) == 0 ]]; then
|
if [[ $(rc-service fail2ban status | grep -F 'status: started' -c) == 0 ]]; then
|
||||||
rc-service fail2ban start
|
rc-service fail2ban start
|
||||||
|
|
@ -2063,13 +2003,10 @@ show_banlog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
create_iplimit_jails() {
|
create_iplimit_jails() {
|
||||||
# Use default bantime if not passed => 30 minutes
|
|
||||||
local bantime="${1:-30}"
|
local bantime="${1:-30}"
|
||||||
|
|
||||||
# Uncomment 'allowipv6 = auto' in fail2ban.conf
|
|
||||||
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
|
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
|
||||||
|
|
||||||
# On Debian 12+ fail2ban's default backend should be changed to systemd
|
|
||||||
if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then
|
if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then
|
||||||
sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf
|
sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf
|
||||||
fi
|
fi
|
||||||
|
|
@ -2130,7 +2067,6 @@ iplimit_remove_conflicts() {
|
||||||
)
|
)
|
||||||
|
|
||||||
for file in "${jail_files[@]}"; do
|
for file in "${jail_files[@]}"; do
|
||||||
# Check for [3x-ipl] config in jail file then remove it
|
|
||||||
if test -f "${file}" && grep -qw '3x-ipl' ${file}; then
|
if test -f "${file}" && grep -qw '3x-ipl' ${file}; then
|
||||||
sed -i "/\[3x-ipl\]/,/^$/d" ${file}
|
sed -i "/\[3x-ipl\]/,/^$/d" ${file}
|
||||||
echo -e "${yellow}Removing conflicts of [3x-ipl] in jail (${file})!${plain}\n"
|
echo -e "${yellow}Removing conflicts of [3x-ipl] in jail (${file})!${plain}\n"
|
||||||
|
|
@ -2270,42 +2206,42 @@ show_usage() {
|
||||||
|
|
||||||
show_menu() {
|
show_menu() {
|
||||||
echo -e "
|
echo -e "
|
||||||
╔────────────────────────────────────────────────╗
|
╔════════════════════════════════════════════════════╗
|
||||||
│ ${green}3X-UI Panel Management Script${plain} │
|
║ ${green}3X-UI Panel Management Script${plain} ║
|
||||||
│ ${green}0.${plain} Exit Script │
|
║ ${green}0.${plain} Exit Script ║
|
||||||
│────────────────────────────────────────────────│
|
║──────────────────────────────────────────────────║
|
||||||
│ ${green}1.${plain} Install │
|
║ ${green}1.${plain} Install ║
|
||||||
│ ${green}2.${plain} Update │
|
║ ${green}2.${plain} Update ║
|
||||||
│ ${green}3.${plain} Update Menu │
|
║ ${green}3.${plain} Update Menu ║
|
||||||
│ ${green}4.${plain} Legacy Version │
|
║ ${green}4.${plain} Legacy Version ║
|
||||||
│ ${green}5.${plain} Uninstall │
|
║ ${green}5.${plain} Uninstall ║
|
||||||
│────────────────────────────────────────────────│
|
║──────────────────────────────────────────────────║
|
||||||
│ ${green}6.${plain} Reset Username & Password │
|
║ ${green}6.${plain} Reset Username & Password ║
|
||||||
│ ${green}7.${plain} Reset Web Base Path │
|
║ ${green}7.${plain} Reset Web Base Path ║
|
||||||
│ ${green}8.${plain} Reset Settings │
|
║ ${green}8.${plain} Reset Settings ║
|
||||||
│ ${green}9.${plain} Change Port │
|
║ ${green}9.${plain} Change Port ║
|
||||||
│ ${green}10.${plain} View Current Settings │
|
║ ${green}10.${plain} View Current Settings ║
|
||||||
│────────────────────────────────────────────────│
|
║──────────────────────────────────────────────────║
|
||||||
│ ${green}11.${plain} Start │
|
║ ${green}11.${plain} Start ║
|
||||||
│ ${green}12.${plain} Stop │
|
║ ${green}12.${plain} Stop ║
|
||||||
│ ${green}13.${plain} Restart │
|
║ ${green}13.${plain} Restart ║
|
||||||
| ${green}14.${plain} Restart Xray │
|
║ ${green}14.${plain} Restart Xray ║
|
||||||
│ ${green}15.${plain} Check Status │
|
║ ${green}15.${plain} Check Status ║
|
||||||
│ ${green}16.${plain} Logs Management │
|
║ ${green}16.${plain} Logs Management ║
|
||||||
│────────────────────────────────────────────────│
|
║──────────────────────────────────────────────────║
|
||||||
│ ${green}17.${plain} Enable Autostart │
|
║ ${green}17.${plain} Enable Autostart ║
|
||||||
│ ${green}18.${plain} Disable Autostart │
|
║ ${green}18.${plain} Disable Autostart ║
|
||||||
│────────────────────────────────────────────────│
|
║──────────────────────────────────────────────────║
|
||||||
│ ${green}19.${plain} SSL Certificate Management │
|
║ ${green}19.${plain} SSL Certificate Management ║
|
||||||
│ ${green}20.${plain} Cloudflare SSL Certificate │
|
║ ${green}20.${plain} Cloudflare SSL Certificate ║
|
||||||
│ ${green}21.${plain} IP Limit Management │
|
║ ${green}21.${plain} IP Limit Management ║
|
||||||
│ ${green}22.${plain} Firewall Management │
|
║ ${green}22.${plain} Firewall Management ║
|
||||||
│ ${green}23.${plain} SSH Port Forwarding Management │
|
║ ${green}23.${plain} SSH Port Forwarding Management ║
|
||||||
│────────────────────────────────────────────────│
|
║──────────────────────────────────────────────────║
|
||||||
│ ${green}24.${plain} Enable BBR │
|
║ ${green}24.${plain} Enable BBR ║
|
||||||
│ ${green}25.${plain} Update Geo Files │
|
║ ${green}25.${plain} Update Geo Files ║
|
||||||
│ ${green}26.${plain} Speedtest by Ookla │
|
║ ${green}26.${plain} Speedtest by Ookla ║
|
||||||
╚────────────────────────────────────────────────╝
|
╚════════════════════════════════════════════════════╝
|
||||||
"
|
"
|
||||||
show_status
|
show_status
|
||||||
echo && read -rp "Please enter your selection [0-26]: " num
|
echo && read -rp "Please enter your selection [0-26]: " num
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue