feat: 优化项目并添加多语言和GitHub加速

Co-authored-by: traeagent <traeagent@users.noreply.github.com>
This commit is contained in:
ruyawwj 2026-05-11 04:20:49 +00:00
parent 8834e5fbbe
commit 6fd3e9553f
13 changed files with 339 additions and 1315 deletions

View file

@ -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">
<picture>
@ -7,50 +7,45 @@
</picture>
</p>
[![Release](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases)
[![Build](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](https://github.com/MHSanaei/3x-ui/actions)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](https://github.com/MHSanaei/3x-ui/releases/latest)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
[![Go Reference](https://pkg.go.dev/badge/github.com/mhsanaei/3x-ui/v3.svg)](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
[![Go Report Card](https://goreportcard.com/badge/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]
> هذا المشروع مخصص للاستخدام الشخصي والاتصال فقط، يرجى عدم استخدامه لأغراض غير قانونية، يرجى عدم استخدامه في بيئة الإنتاج.
> هذا المشروع مخصص للاستخدام الشخصي والبحث في الاتصالات فقط. يرجى عدم استخدامه لأغراض غير قانونية أو في بيئة الإنتاج.
كمشروع محسن من مشروع X-UI الأصلي، يوفر 3X-UI استقرارًا محسنًا ودعمًا أوسع للبروتوكولات وميزات إضافية.
كمحاكاة محسنة من مشروع X-UI الأصلي، يوفر 3X-UI استقرارًا أفضل ودعمًا أوسع للبروتوكولات وميزات إضافية.
## البدء السريع
```
### سكريبت التثبيت بنقرة واحدة
**المصدر الرسمي:**
```bash
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/)
## الاعتراف
## الإشادة
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (الترخيص: **GPL-3.0**): _قواعد توجيه v2ray/xray و v2ray/xray-clients المحسنة مع النطاقات الإيرانية المدمجة وتركيز على الأمان وحظر الإعلانات._
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (الترخيص: **GPL-3.0**): _يحتوي هذا المستودع على قواعد توجيه V2Ray محدثة تلقائيًا بناءً على بيانات النطاقات والعناوين المحظورة في روسيا._
- [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 المحدثة تلقائيًا بناءً على النطاقات المحظورة في روسيا._
## دعم المشروع
**إذا كان هذا المشروع مفيدًا لك، فقد ترغب في إعطائه**: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.svg?variant=adaptive)](https://starchart.cc/MHSanaei/3x-ui)
**إذا كان هذا المشروع مفيدًا لك، يمكنك إعطائه**:star2:

View file

@ -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">
<picture>
@ -7,28 +7,35 @@
</picture>
</p>
[![Release](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases)
[![Build](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](https://github.com/MHSanaei/3x-ui/actions)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](https://github.com/MHSanaei/3x-ui/releases/latest)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
[![Go Reference](https://pkg.go.dev/badge/github.com/mhsanaei/3x-ui/v3.svg)](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
[![Go Report Card](https://goreportcard.com/badge/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.
**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.
> [!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.
## 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)
```
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
@ -36,22 +43,9 @@ Para documentación completa, visita la [Wiki del proyecto](https://github.com/M
## 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._
- [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._
- [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**): _Reglas V2Ray actualizadas automáticamente basadas en dominios bloqueados en Rusia._
## Apoyar el Proyecto
**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
[![Stargazers over time](https://starchart.cc/MHSanaei/3x-ui.svg?variant=adaptive)](https://starchart.cc/MHSanaei/3x-ui)

View file

@ -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">
<picture>
@ -7,51 +7,45 @@
</picture>
</p>
[![Release](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases)
[![Build](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](https://github.com/MHSanaei/3x-ui/actions)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](https://github.com/MHSanaei/3x-ui/releases/latest)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
[![Go Reference](https://pkg.go.dev/badge/github.com/mhsanaei/3x-ui/v3.svg)](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
[![Go Report Card](https://goreportcard.com/badge/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]
> این پروژه فقط برای استفاده شخصی و ارتباطات است، لطفاً از آن برای اهداف غیرقانونی استفاده نکنید، لطفاً از آن در محیط تولید استفاده نکنید.
> این پروژه فقط برای استفاده شخصی و تحقیقات ارتباطی است. لطفاً از آن برای اهداف غیرقانونی استفاده نکنید و در محیط تولید به کار نبرید.
به عنوان یک نسخه بهبود یافته از پروژه اصلی X-UI، 3X-UI پایداری بهتر، پشتیبانی گسترده‌تر از پروتکل‌ها و ویژگی‌های اضافی را ارائه می‌دهد.
## شروع سریع
```
### اسکریپت نصب با یک کلیک
**منبع رسمی:**
```bash
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) مراجعه کنید.
## تشکر ویژه از
## تقدیر ویژه
- [alireza0](https://github.com/alireza0/)
## قدردانی
## تشکر و قدردانی
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (مجوز: **GPL-3.0**): _قوانین مسیریابی بهبود یافته v2ray/xray و v2ray/xray-clients با دامنه‌های ایرانی داخلی و تمرکز بر امنیت و مسدود کردن تبلیغات._
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (مجوز: **GPL-3.0**): _این مخزن شامل قوانین مسیریابی V2Ray به‌روزرسانی شده خودکار بر اساس داده‌های دامنه‌ها و آدرس‌های مسدود شده در روسیه است._
- [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 که بر اساس دامنه‌های مسدود شده در روسیه به‌روزرسانی می‌شوند._
## پشتیبانی از پروژه
## حمایت از پروژه
**اگر این پروژه برای شما مفید است، می‌توانید به آن یک**: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.svg?variant=adaptive)](https://starchart.cc/MHSanaei/3x-ui)
**اگر این پروژه برای شما مفید است، می‌توانید به آن**:star2: **بدهید**

View file

@ -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">
<picture>
@ -7,27 +7,34 @@
</picture>
</p>
[![Release](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases)
[![Build](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](https://github.com/MHSanaei/3x-ui/actions)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](https://github.com/MHSanaei/3x-ui/releases/latest)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
[![Go Reference](https://pkg.go.dev/badge/github.com/mhsanaei/3x-ui/v3.svg)](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
[![Go Report Card](https://goreportcard.com/badge/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.
**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.
> [!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
### One-Click Installation Script
**Official Source:**
```bash
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).
## 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._
- [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:
<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
[![Stargazers over time](https://starchart.cc/MHSanaei/3x-ui.svg?variant=adaptive)](https://starchart.cc/MHSanaei/3x-ui)

View file

@ -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">
<picture>
@ -7,51 +7,45 @@
</picture>
</p>
[![Release](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases)
[![Build](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](https://github.com/MHSanaei/3x-ui/actions)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](https://github.com/MHSanaei/3x-ui/releases/latest)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
[![Go Reference](https://pkg.go.dev/badge/github.com/mhsanaei/3x-ui/v3.svg)](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
[![Go Report Card](https://goreportcard.com/badge/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]
> Этот проект предназначен только для личного использования, пожалуйста, не используйте его в незаконных целях и в производственной среде.
> Этот проект предназначен только для личного использования и исследований связи. Пожалуйста, не используйте его в незаконных целях и не используйте в производственной среде.
Как улучшенная версия оригинального проекта X-UI, 3X-UI обеспечивает повышенную стабильность, более широкую поддержку протоколов и дополнительные функции.
В качестве улучшенной версии оригинального проекта X-UI, 3X-UI обеспечивает лучшую стабильность, более широкую поддержку протоколов и дополнительные функции.
## Быстрый старт
```
### Скрипт быстрой установки
**Официальный источник:**
```bash
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/)
## Благодарности
## Признание
- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (Лицензия: **GPL-3.0**): _Улучшенные правила маршрутизации для v2ray/xray и v2ray/xray-clients со встроенными иранскими доменами и фокусом на безопасность и блокировку рекламы._
- [Russia v2ray rules](https://github.com/runetfreedom/russia-v2ray-rules-dat) (Лицензия: **GPL-3.0**): _Этот репозиторий содержит автоматически обновляемые правила маршрутизации V2Ray на основе данных о заблокированных доменах и адресах в России._
- [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 на основе заблокированных в России доменов._
## Поддержка проекта
## Поддержать проект
**Если этот проект полезен для вас, вы можете поставить ему**: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.svg?variant=adaptive)](https://starchart.cc/MHSanaei/3x-ui)
**Если этот проект полезен для вас, вы можете поставить**:star2:

View file

@ -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">
<picture>
@ -7,27 +7,34 @@
</picture>
</p>
[![Release](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg)](https://github.com/MHSanaei/3x-ui/releases)
[![Build](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg)](https://github.com/MHSanaei/3x-ui/actions)
[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg)](#)
[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg)](https://github.com/MHSanaei/3x-ui/releases/latest)
[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html)
[![Go Reference](https://pkg.go.dev/badge/github.com/mhsanaei/3x-ui/v3.svg)](https://pkg.go.dev/github.com/mhsanaei/3x-ui/v3)
[![Go Report Card](https://goreportcard.com/badge/github.com/mhsanaei/3x-ui/v3)](https://goreportcard.com/report/github.com/mhsanaei/3x-ui/v3)
**3X-UI** — 一个基于网页的高级开源控制面板,专为管理 Xray-core 服务器而设计。它提供了用户友好的界面,用于配置和监控各种 VPN 和代理协议。
> [!IMPORTANT]
> 本项目仅用于个人使用和通信,请勿将其用于非法目的,请勿在生产环境中使用。
> 本项目仅用于个人学习和通信研究,请勿将其用于非法目的,请勿在生产环境中使用。
作为原始 X-UI 项目的增强版本3X-UI 提供了更好的稳定性、更广泛的协议支持和额外的功能。
## 快速开始
```
### 一键安装脚本
**官方源安装:**
```bash
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)。
## 特别感谢
@ -42,16 +49,3 @@ bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.
## 支持项目
**如果这个项目对您有帮助,您可以给它一个**: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.svg?variant=adaptive)](https://starchart.cc/MHSanaei/3x-ui)

View file

@ -1 +1 @@
x-ui
3x-ui

View file

@ -1,5 +1,5 @@
{
"name": "3x-ui-frontend",
"name": "3x-ui",
"private": true,
"version": "0.0.1",
"type": "module",

View file

@ -29,6 +29,7 @@ import { LanguageManager } from '@/utils';
// "lazy" here effectively means "load only what this page needs for
// its lifetime."
const FALLBACK = 'en-US';
const DEFAULT_LOCALE = 'zh-CN';
const lazyModules = import.meta.glob('../../../web/translation/*.json');
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
// 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();
if (!Object.prototype.hasOwnProperty.call(lazyModules, moduleKeyFor(active))) {
active = FALLBACK;
active = DEFAULT_LOCALE;
}
const messages = {};

View file

@ -840,11 +840,11 @@ export class LanguageManager {
if (LanguageManager.isSupportLanguage(lang)) {
CookieManager.setCookie("lang", lang);
} else {
CookieManager.setCookie("lang", "en-US");
CookieManager.setCookie("lang", "zh-CN");
window.location.reload();
}
} else {
CookieManager.setCookie("lang", "en-US");
CookieManager.setCookie("lang", "zh-CN");
window.location.reload();
}
}
@ -854,7 +854,7 @@ export class LanguageManager {
static setLanguage(language) {
if (!LanguageManager.isSupportLanguage(language)) {
language = "en-US";
language = "zh-CN";
}
CookieManager.setCookie("lang", language);

View file

@ -11,10 +11,12 @@ cur_dir=$(pwd)
xui_folder="${XUI_MAIN_FOLDER:=/usr/local/x-ui}"
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
# Check OS and set release variable
if [[ -f /etc/os-release ]]; then
source /etc/os-release
release=$ID
@ -42,7 +44,6 @@ arch() {
echo "Arch: $(arch)"
# Simple helpers
is_ipv4() {
[[ "$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
}
# Port helpers
is_port_in_use() {
local port="$1"
if command -v ss > /dev/null 2>&1; then
@ -73,6 +73,24 @@ is_port_in_use() {
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() {
case "${release}" in
ubuntu | debian | armbian)
@ -131,7 +149,6 @@ setup_ssl_certificate() {
echo -e "${green}Setting up SSL certificate...${plain}"
# Check if acme.sh is installed
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
install_acme
if [ $? -ne 0 ]; then
@ -140,11 +157,9 @@ setup_ssl_certificate() {
fi
fi
# Create certificate directory
local certPath="/root/cert/${domain}"
mkdir -p "$certPath"
# Issue certificate
echo -e "${green}Issuing SSL certificate for ${domain}...${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
fi
# Install certificate
~/.acme.sh/acme.sh --installcert -d ${domain} \
--key-file /root/cert/${domain}/privkey.pem \
--fullchain-file /root/cert/${domain}/fullchain.pem \
@ -170,13 +184,10 @@ setup_ssl_certificate() {
return 1
fi
# Enable auto-renew
~/.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 644 $certPath/fullchain.pem 2> /dev/null
# Set certificate for panel
local webCertFile="/root/cert/${domain}/fullchain.pem"
local webKeyFile="/root/cert/${domain}/privkey.pem"
@ -190,17 +201,14 @@ setup_ssl_certificate() {
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() {
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 "${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}"
# Check for acme.sh
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
install_acme
if [ $? -ne 0 ]; then
@ -209,7 +217,6 @@ setup_ip_certificate() {
fi
fi
# Validate IP address
if [[ -z "$ipv4" ]]; then
echo -e "${red}IPv4 address is required${plain}"
return 1
@ -220,21 +227,17 @@ setup_ip_certificate() {
return 1
fi
# Create certificate directory
local certDir="/root/cert/ip"
mkdir -p "$certDir"
# Build domain arguments
local domain_args="-d ${ipv4}"
if [[ -n "$ipv6" ]] && is_ipv6 "$ipv6"; then
domain_args="${domain_args} -d ${ipv6}"
echo -e "${green}Including IPv6 address: ${ipv6}${plain}"
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"
# Choose port for HTTP-01 listener (default 80, prompt override)
local WebPort=""
read -rp "Port to use for ACME HTTP-01 listener (default 80): " WebPort
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}"
fi
# Ensure chosen port is available
while true; do
if is_port_in_use "${WebPort}"; then
echo -e "${yellow}Port ${WebPort} is in use.${plain}"
@ -271,7 +273,6 @@ setup_ip_certificate() {
fi
done
# Issue certificate with shortlived profile
echo -e "${green}Issuing IP certificate for ${ipv4}...${plain}"
~/.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
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}"
# Cleanup acme.sh data for both IPv4 and IPv6 if specified
rm -rf ~/.acme.sh/${ipv4} 2> /dev/null
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null
rm -rf ${certDir} 2> /dev/null
@ -296,18 +296,13 @@ setup_ip_certificate() {
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} \
--key-file "${certDir}/privkey.pem" \
--fullchain-file "${certDir}/fullchain.pem" \
--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
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
[[ -n "$ipv6" ]] && rm -rf ~/.acme.sh/${ipv6} 2> /dev/null
rm -rf ${certDir} 2> /dev/null
@ -316,14 +311,11 @@ setup_ip_certificate() {
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
# Secure permissions: private key readable only by owner
chmod 600 ${certDir}/privkey.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}"
${xui_folder}/x-ui cert -webCert "${certDir}/fullchain.pem" -webCertKey "${certDir}/privkey.pem"
@ -342,12 +334,10 @@ setup_ip_certificate() {
return 0
}
# Comprehensive manual SSL certificate issuance via acme.sh
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_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
echo "acme.sh could not be found. Installing now..."
cd ~ || return 1
@ -360,11 +350,10 @@ ssl_cert_issue() {
fi
fi
# get the domain here, and we need to verify it
local domain=""
while true; do
read -rp "Please enter your domain name: " domain
domain="${domain// /}" # Trim whitespace
domain="${domain// /}"
if [[ -z "$domain" ]]; then
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}"
SSL_ISSUED_DOMAIN="${domain}"
# detect existing certificate and reuse it if present
local cert_exists=0
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
cert_exists=1
@ -392,7 +380,6 @@ ssl_cert_issue() {
echo -e "${green}Your domain is ready for issuing certificates now...${plain}"
fi
# create a directory for the certificate
certPath="/root/cert/${domain}"
if [ ! -d "$certPath" ]; then
mkdir -p "$certPath"
@ -401,7 +388,6 @@ ssl_cert_issue() {
mkdir -p "$certPath"
fi
# get the port number for the standalone server
local WebPort=80
read -rp "Please choose which port to use (default is 80): " WebPort
if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then
@ -410,12 +396,10 @@ ssl_cert_issue() {
fi
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}"
systemctl stop x-ui 2> /dev/null || rc-service x-ui stop 2> /dev/null
if [[ ${cert_exists} -eq 0 ]]; then
# issue the certificate
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
if [ $? -ne 0 ]; then
@ -430,7 +414,6 @@ ssl_cert_issue() {
echo -e "${green}Using existing certificate, installing certificates...${plain}"
fi
# Setup reload command
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}This command will run on every certificate issue and renew.${plain}"
@ -456,7 +439,6 @@ ssl_cert_issue() {
esac
fi
# install the certificate
local installOutput=""
installOutput=$(~/.acme.sh/acme.sh --installcert -d ${domain} \
--key-file /root/cert/${domain}/privkey.pem \
@ -480,26 +462,21 @@ ssl_cert_issue() {
return 1
fi
# enable auto-renew
~/.acme.sh/acme.sh --upgrade --auto-upgrade
if [ $? -ne 0 ]; then
echo -e "${yellow}Auto renew setup had issues, certificate details:${plain}"
ls -lah /root/cert/${domain}/
# Secure permissions: private key readable only by owner
chmod 600 $certPath/privkey.pem 2> /dev/null
chmod 644 $certPath/fullchain.pem 2> /dev/null
else
echo -e "${green}Auto renew succeeded, certificate details:${plain}"
ls -lah /root/cert/${domain}/
# Secure permissions: private key readable only by owner
chmod 600 $certPath/privkey.pem 2> /dev/null
chmod 644 $certPath/fullchain.pem 2> /dev/null
fi
# start panel
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
if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
local webCertFile="/root/cert/${domain}/fullchain.pem"
@ -524,8 +501,6 @@ ssl_cert_issue() {
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() {
local panel_port="$1"
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} 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
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
ssl_choice="2"
fi
case "$ssl_choice" in
1)
# User chose Let's Encrypt domain option
echo -e "${green}Using Let's Encrypt for domain certificate...${plain}"
if ssl_cert_issue; then
local cert_domain="${SSL_ISSUED_DOMAIN}"
@ -572,15 +545,12 @@ prompt_and_setup_ssl() {
fi
;;
2)
# User chose Let's Encrypt IP certificate option
echo -e "${green}Using Let's Encrypt for IP certificate (shortlived profile)...${plain}"
# Ask for optional IPv6
local 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
rc-service x-ui stop > /dev/null 2>&1
else
@ -597,20 +567,16 @@ prompt_and_setup_ssl() {
fi
;;
3)
# User chose Custom Paths (User Provided) option
echo -e "${green}Using custom existing certificate...${plain}"
local custom_cert=""
local custom_key=""
local custom_domain=""
# 3.1 Request Domain to compose Panel URL later
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
read -rp "Input certificate path (keywords: .crt / fullchain): " custom_cert
# Strip quotes if present
custom_cert=$(echo "$custom_cert" | tr -d '"' | tr -d "'")
if [[ -f "$custom_cert" && -r "$custom_cert" && -s "$custom_cert" ]]; then
@ -624,10 +590,8 @@ prompt_and_setup_ssl() {
fi
done
# 3.3 Loop for Private Key Path
while true; do
read -rp "Input private key path (keywords: .key / privatekey): " custom_key
# Strip quotes if present
custom_key=$(echo "$custom_key" | tr -d '"' | tr -d "'")
if [[ -f "$custom_key" && -r "$custom_key" && -s "$custom_key" ]]; then
@ -641,10 +605,8 @@ prompt_and_setup_ssl() {
fi
done
# 3.4 Apply Settings via x-ui binary
${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
SSL_HOST="$custom_domain"
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_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}')
# 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 URL_lists=(
"https://api4.ipify.org"
@ -763,7 +724,6 @@ config_after_install() {
prompt_and_setup_ssl "${config_port}" "${config_webBasePath}" "${server_ip}"
# Display final credentials and access information
echo ""
echo -e "${green}═══════════════════════════════════════════${plain}"
echo -e "${green} Panel Installation Complete! ${plain}"
@ -786,7 +746,6 @@ config_after_install() {
${xui_folder}/x-ui setting -webBasePath "${config_webBasePath}"
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
echo ""
echo -e "${green}═══════════════════════════════════════════${plain}"
@ -797,7 +756,6 @@ config_after_install() {
prompt_and_setup_ssl "${existing_port}" "${config_webBasePath}" "${server_ip}"
echo -e "${green}Access URL: ${SSL_SCHEME}://${SSL_HOST}:${existing_port}/${config_webBasePath}${plain}"
else
# If a cert already exists, just show the access URL
echo -e "${green}Access URL: https://${server_ip}:${existing_port}/${config_webBasePath}${plain}"
fi
fi
@ -817,8 +775,6 @@ config_after_install() {
echo -e "${green}Username, Password, and WebBasePath are properly set.${plain}"
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:]')
if [[ -z "$existing_cert" ]]; then
echo ""
@ -840,19 +796,20 @@ config_after_install() {
install_x-ui() {
cd ${xui_folder%/x-ui}/
# Download resources
local github_raw=$(get_github_raw)
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
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
echo -e "${red}Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later${plain}"
exit 1
fi
fi
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
echo -e "${red}Downloading x-ui failed, please be sure that your server can access GitHub ${plain}"
exit 1
@ -867,7 +824,7 @@ install_x-ui() {
exit 1
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"
curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz ${url}
if [[ $? -ne 0 ]]; then
@ -875,13 +832,12 @@ install_x-ui() {
exit 1
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
echo -e "${red}Failed to download x-ui.sh${plain}"
exit 1
fi
# Stop x-ui service and remove old resources
if [[ -e ${xui_folder}/ ]]; then
if [[ $release == "alpine" ]]; then
rc-service x-ui stop
@ -891,7 +847,6 @@ install_x-ui() {
rm ${xui_folder}/ -rf
fi
# Extract resources and set permissions
tar zxvf x-ui-linux-$(arch).tar.gz
rm x-ui-linux-$(arch).tar.gz -f
@ -899,20 +854,17 @@ install_x-ui() {
chmod +x x-ui
chmod +x x-ui.sh
# Check the system's architecture and rename the file accordingly
if [[ $(arch) == "armv5" || $(arch) == "armv6" || $(arch) == "armv7" ]]; then
mv bin/xray-linux-$(arch) bin/xray-linux-arm
chmod +x bin/xray-linux-arm
fi
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
chmod +x /usr/bin/x-ui
mkdir -p /var/log/x-ui
config_after_install
# Etckeeper compatibility
if [ -d "/etc/.git" ]; then
if [ -f "/etc/.gitignore" ]; then
if ! grep -q "x-ui/x-ui.db" "/etc/.gitignore"; then
@ -927,7 +879,7 @@ install_x-ui() {
fi
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
echo -e "${red}Failed to download x-ui.rc${plain}"
exit 1
@ -936,7 +888,6 @@ install_x-ui() {
rc-update add x-ui
rc-service x-ui start
else
# Install systemd service file
service_installed=false
if [ -f "x-ui.service" ]; then
@ -979,18 +930,17 @@ install_x-ui() {
esac
fi
# If service file not found in tar.gz, download from GitHub
if [ "$service_installed" = false ]; then
echo -e "${yellow}Service files not found in tar.gz, downloading from GitHub...${plain}"
case "${release}" in
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)
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

964
update.sh

File diff suppressed because it is too large Load diff

240
x-ui.sh
View file

@ -6,7 +6,9 @@ blue='\033[0;34m'
yellow='\033[0;33m'
plain='\033[0m'
#Add some basic function here
GITHUB_MIRROR_DEFAULT="https://gh.kejilion.pro"
GITHUB_RAW_DEFAULT="https://raw.githubusercontent.com"
function LOGD() {
echo -e "${yellow}[DEG] $* ${plain}"
}
@ -19,7 +21,33 @@ function LOGI() {
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() {
local port="$1"
if command -v ss > /dev/null 2>&1; then
@ -36,7 +64,6 @@ is_port_in_use() {
return 1
}
# Simple helpers for domain/IP validation
is_ipv4() {
[[ "$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
}
# check root
[[ $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
source /etc/os-release
release=$ID
@ -69,7 +94,6 @@ echo "The OS release is: $release"
os_version=""
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_service="${XUI_SERVICE:=/etc/systemd/system}"
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_banned_log_path="${log_folder}/3xipl-banned.log"
REPO_OWNER="mhsanaei"
REPO_NAME="3x-ui"
confirm() {
if [[ $# > 1 ]]; then
echo && read -rp "$1 [Default $2]: " temp
@ -108,7 +135,8 @@ before_show_menu() {
}
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
start
@ -127,7 +155,8 @@ update() {
fi
return 0
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
LOGI "Update is complete, Panel has automatically restarted "
before_show_menu
@ -145,7 +174,8 @@ update_menu() {
return 0
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 /usr/bin/x-ui
@ -166,16 +196,14 @@ legacy_version() {
echo "Panel version cannot be empty. Exiting."
exit 1
fi
# Use the entered panel version in the download link
install_command="bash <(curl -Ls "https://raw.githubusercontent.com/mhsanaei/3x-ui/v$tag_version/install.sh") v$tag_version"
local install_command="bash <(curl -Ls ${github_raw}/${REPO_OWNER}/${REPO_NAME}/v$tag_version/install.sh) v$tag_version"
echo "Downloading and installing panel version $tag_version..."
eval $install_command
}
# Function to handle the deletion of the script file
delete_script() {
rm "$0" # Remove the script file itself
rm "$0"
exit 1
}
@ -206,9 +234,9 @@ uninstall() {
echo ""
echo -e "Uninstalled Successfully.\n"
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 ""
# Trap the SIGTERM signal
trap delete_script SIGTERM
delete_script
}
@ -259,7 +287,6 @@ reset_webbasepath() {
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
echo -e "Web base path has been reset to: ${green}${config_webBasePath}${plain}"
@ -340,7 +367,6 @@ check_config() {
ssl_cert_issue_for_ip
if [[ $? -eq 0 ]]; then
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
else
LOGE "IP certificate setup failed."
@ -579,7 +605,6 @@ disable_bbr() {
rm /etc/sysctl.d/99-bbr-x-ui.conf
sysctl --system
else
# Replace BBR with CUBIC configurations
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.ipv4.tcp_congestion_control=bbr/net.ipv4.tcp_congestion_control=cubic/' /etc/sysctl.conf
@ -600,7 +625,6 @@ enable_bbr() {
before_show_menu
fi
# Enable BBR
if [ -d "/etc/sysctl.d/" ]; then
{
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"
} > "/etc/sysctl.d/99-bbr-x-ui.conf"
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.ipv4.tcp_congestion_control/# &/' /etc/sysctl.conf
fi
@ -621,7 +644,6 @@ enable_bbr() {
sysctl -p
fi
# Verify that BBR is enabled
if [[ $(sysctl -n net.ipv4.tcp_congestion_control) == "bbr" ]]; then
echo -e "${green}BBR has been enabled successfully.${plain}"
else
@ -630,7 +652,9 @@ enable_bbr() {
}
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
echo ""
LOGE "Failed to download script, Please check whether the machine can connect Github"
@ -642,7 +666,6 @@ update_shell() {
fi
}
# 0: running, 1: not running, 2: not installed
check_status() {
if [[ $release == "alpine" ]]; then
if [[ ! -f /etc/init.d/x-ui ]]; then
@ -814,130 +837,103 @@ install_firewall() {
echo "ufw firewall is already installed"
fi
# Check if the firewall is inactive
if ufw status | grep -q "Status: active"; then
echo "Firewall is already active"
else
echo "Activating firewall..."
# Open the necessary ports
ufw allow ssh
ufw allow http
ufw allow https
ufw allow 2053/tcp #webPort
ufw allow 2096/tcp #subport
ufw allow 2053/tcp
ufw allow 2096/tcp
# Enable the firewall
ufw --force enable
fi
}
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
# Check if the input is valid
if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
exit 1
fi
# Open the specified ports using ufw
IFS=',' read -ra PORT_LIST <<< "$ports"
for port in "${PORT_LIST[@]}"; do
if [[ $port == *-* ]]; then
# Split the range into start and end ports
start_port=$(echo $port | cut -d'-' -f1)
end_port=$(echo $port | cut -d'-' -f2)
# Open the port range
ufw allow $start_port:$end_port/tcp
ufw allow $start_port:$end_port/udp
else
# Open the single port
ufw allow "$port"
fi
done
# Confirm that the ports are opened
echo "Opened the specified ports:"
for port in "${PORT_LIST[@]}"; do
if [[ $port == *-* ]]; then
start_port=$(echo $port | cut -d'-' -f1)
end_port=$(echo $port | cut -d'-' -f2)
# Check if the port range has been successfully opened
(ufw status | grep -q "$start_port:$end_port") && echo "$start_port-$end_port"
else
# Check if the individual port has been successfully opened
(ufw status | grep -q "$port") && echo "$port"
fi
done
}
delete_ports() {
# Display current rules with numbers
echo "Current UFW rules:"
ufw status numbered
# Ask the user how they want to delete rules
echo "Do you want to delete rules by:"
echo "1) Rule numbers"
echo "2) Ports"
read -rp "Enter your choice (1 or 2): " choice
if [[ $choice -eq 1 ]]; then
# Deleting by 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
echo "Error: Invalid input. Please enter a comma-separated list of rule numbers." >&2
exit 1
fi
# Split numbers into an array
IFS=',' read -ra RULE_NUMBERS <<< "$rule_numbers"
for rule_number in "${RULE_NUMBERS[@]}"; do
# Delete the rule by number
ufw delete "$rule_number" || echo "Failed to delete rule number $rule_number"
done
echo "Selected rules have been deleted."
elif [[ $choice -eq 2 ]]; then
# Deleting by ports
read -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
echo "Error: Invalid input. Please enter a comma-separated list of ports or a range of ports (e.g. 80,443,2053 or 400-500)." >&2
exit 1
fi
# Split ports into an array
IFS=',' read -ra PORT_LIST <<< "$ports"
for port in "${PORT_LIST[@]}"; do
if [[ $port == *-* ]]; then
# Split the port range
start_port=$(echo $port | cut -d'-' -f1)
end_port=$(echo $port | cut -d'-' -f2)
# Delete the port range
ufw delete allow $start_port:$end_port/tcp
ufw delete allow $start_port:$end_port/udp
else
# Delete a single port
ufw delete allow "$port"
fi
done
# Confirmation of deletion
echo "Deleted the specified ports:"
for port in "${PORT_LIST[@]}"; do
if [[ $port == *-* ]]; then
start_port=$(echo $port | cut -d'-' -f1)
end_port=$(echo $port | cut -d'-' -f2)
# Check if the port range has been deleted
(ufw status | grep -q "$start_port:$end_port") || echo "$start_port-$end_port"
else
# Check if the individual port has been deleted
(ufw status | grep -q "$port") || echo "$port"
fi
done
@ -969,7 +965,6 @@ update_geofiles() {
;;
esac
for dat in "${dat_files[@]}"; do
# Remove suffix for remote filename (e.g., geoip_IR -> geoip)
remote_file="${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
@ -1018,14 +1013,13 @@ update_geo() {
}
install_acme() {
# Check if acme.sh is already installed
if command -v ~/.acme.sh/acme.sh &> /dev/null; then
LOGI "acme.sh is already installed."
return 0
fi
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
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_port=$(${xui_folder}/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}')
# Get server IP
local URL_lists=(
"https://api4.ipify.org"
"https://ipv4.icanhazip.com"
@ -1198,12 +1191,10 @@ ssl_cert_issue_for_ip() {
LOGI "Server IP detected: ${server_ip}"
# Ask for optional IPv6
local 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
LOGI "acme.sh not found, installing..."
install_acme
@ -1213,7 +1204,6 @@ ssl_cert_issue_for_ip() {
fi
fi
# install socat
case "${release}" in
ubuntu | debian | armbian)
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
;;
*)
LOGW "Unsupported OS for automatic socat installation"
;;
esac
# Create certificate directory
certPath="/root/cert/ip"
mkdir -p "$certPath"
# Build domain arguments
local domain_args="-d ${server_ip}"
if [[ -n "$ipv6_addr" ]] && is_ipv6 "$ipv6_addr"; then
domain_args="${domain_args} -d ${ipv6_addr}"
LOGI "Including IPv6 address: ${ipv6_addr}"
fi
# Choose port for HTTP-01 listener (default 80, allow override)
local WebPort=""
read -rp "Port to use for ACME HTTP-01 listener (default 80): " WebPort
WebPort="${WebPort:-80}"
@ -1289,10 +1275,8 @@ ssl_cert_issue_for_ip() {
fi
done
# Reload command - restarts panel after renewal
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 --issue \
${domain_args} \
@ -1306,7 +1290,6 @@ ssl_cert_issue_for_ip() {
if [ $? -ne 0 ]; then
LOGE "Failed to issue certificate for IP: ${server_ip}"
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
[[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} 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}"
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} \
--key-file "${certPath}/privkey.pem" \
--fullchain-file "${certPath}/fullchain.pem" \
--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
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
[[ -n "$ipv6_addr" ]] && rm -rf ~/.acme.sh/${ipv6_addr} 2> /dev/null
rm -rf ${certPath} 2> /dev/null
@ -1335,12 +1313,10 @@ ssl_cert_issue_for_ip() {
LOGI "Certificate files installed successfully"
# enable auto-renew
~/.acme.sh/acme.sh --upgrade --auto-upgrade > /dev/null 2>&1
chmod 600 $certPath/privkey.pem 2> /dev/null
chmod 644 $certPath/fullchain.pem 2> /dev/null
# Set certificate paths for the panel
local webCertFile="${certPath}/fullchain.pem"
local webKeyFile="${certPath}/privkey.pem"
@ -1363,7 +1339,6 @@ ssl_cert_issue_for_ip() {
ssl_cert_issue() {
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}')
# check for acme.sh first
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
echo "acme.sh could not be found. we will install it"
install_acme
@ -1373,7 +1348,6 @@ ssl_cert_issue() {
fi
fi
# install socat
case "${release}" in
ubuntu | debian | armbian)
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
;;
*)
LOGW "Unsupported OS for automatic socat installation"
;;
esac
if [ $? -ne 0 ]; then
@ -1408,11 +1381,10 @@ ssl_cert_issue() {
LOGI "install socat succeed..."
fi
# get the domain here, and we need to verify it
local domain=""
while true; do
read -rp "Please enter your domain name: " domain
domain="${domain// /}" # Trim whitespace
domain="${domain// /}"
if [[ -z "$domain" ]]; then
LOGE "Domain name cannot be empty. Please try again."
@ -1429,7 +1401,6 @@ ssl_cert_issue() {
LOGD "Your domain is: ${domain}, checking it..."
SSL_ISSUED_DOMAIN="${domain}"
# detect existing certificate and reuse it if present
local cert_exists=0
if ~/.acme.sh/acme.sh --list 2> /dev/null | awk '{print $1}' | grep -Fxq "${domain}"; then
cert_exists=1
@ -1440,7 +1411,6 @@ ssl_cert_issue() {
LOGI "Your domain is ready for issuing certificates now..."
fi
# create a directory for the certificate
certPath="/root/cert/${domain}"
if [ ! -d "$certPath" ]; then
mkdir -p "$certPath"
@ -1449,7 +1419,6 @@ ssl_cert_issue() {
mkdir -p "$certPath"
fi
# get the port number for the standalone server
local WebPort=80
read -rp "Please choose which port to use (default is 80): " WebPort
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."
if [[ ${cert_exists} -eq 0 ]]; then
# issue the certificate
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt --force
~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
if [ $? -ne 0 ]; then
@ -1499,7 +1467,6 @@ ssl_cert_issue() {
esac
fi
# install the certificate
local installOutput=""
installOutput=$(~/.acme.sh/acme.sh --installcert -d ${domain} \
--key-file /root/cert/${domain}/privkey.pem \
@ -1522,7 +1489,6 @@ ssl_cert_issue() {
exit 1
fi
# enable auto-renew
~/.acme.sh/acme.sh --upgrade --auto-upgrade
if [ $? -ne 0 ]; then
LOGE "Auto renew failed, certificate details:"
@ -1537,7 +1503,6 @@ ssl_cert_issue() {
chmod 644 $certPath/fullchain.pem
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
if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
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"
if [ $? -eq 0 ]; then
# Check for acme.sh first
if ! command -v ~/.acme.sh/acme.sh &> /dev/null; then
echo "acme.sh could not be found. We will install it."
install_acme
@ -1588,7 +1552,6 @@ ssl_cert_issue_CF() {
read -rp "Input your domain here: " CF_Domain
LOGD "Your domain name is set to: ${CF_Domain}"
# Set up Cloudflare API details
CF_GlobalKey=""
CF_AccountEmail=""
LOGD "Please set the API key:"
@ -1599,7 +1562,6 @@ ssl_cert_issue_CF() {
read -rp "Input your email here: " 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
if [ $? -ne 0 ]; then
LOGE "Default CA, Let'sEncrypt fail, script exiting..."
@ -1609,7 +1571,6 @@ ssl_cert_issue_CF() {
export CF_Key="${CF_GlobalKey}"
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
if [ $? -ne 0 ]; then
LOGE "Certificate issuance failed, script exiting..."
@ -1618,7 +1579,6 @@ ssl_cert_issue_CF() {
LOGI "Certificate issued successfully, Installing..."
fi
# Install the certificate
certPath="/root/cert/${CF_Domain}"
if [ -d "$certPath" ]; then
rm -rf ${certPath}
@ -1666,7 +1626,6 @@ ssl_cert_issue_CF() {
LOGI "Certificate installed successfully, Turning on automatic updates..."
fi
# Enable auto-update
~/.acme.sh/acme.sh --upgrade --auto-upgrade
if [ $? -ne 0 ]; then
LOGE "Auto update setup failed, script exiting..."
@ -1678,7 +1637,6 @@ ssl_cert_issue_CF() {
chmod 644 ${certPath}/fullchain.pem
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
if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then
local webCertFile="${certPath}/fullchain.pem"
@ -1703,15 +1661,11 @@ ssl_cert_issue_CF() {
}
run_speedtest() {
# Check if Speedtest is already installed
if ! command -v speedtest &> /dev/null; then
# If not installed, determine installation method
if command -v snap &> /dev/null; then
# Use snap to install Speedtest
echo "Installing Speedtest using snap..."
snap install speedtest
else
# Fallback to using package managers
local pkg_manager=""
local speedtest_install_script=""
@ -1745,7 +1699,7 @@ run_speedtest() {
ip_validation() {
ipv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
ipv4_regex="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)$"
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() {
@ -1856,14 +1810,6 @@ install_iplimit() {
if ! command -v fail2ban-client &> /dev/null; then
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
ubuntu)
apt-get update
@ -1918,24 +1864,18 @@ install_iplimit() {
echo -e "${green}Configuring IP Limit...${plain}\n"
# make sure there's no conflict for jail files
iplimit_remove_conflicts
# Check if log file exists
if ! test -f "${iplimit_banned_log_path}"; then
touch ${iplimit_banned_log_path}
fi
# Check if service log file exists so fail2ban won't return error
if ! test -f "${iplimit_log_path}"; then
touch ${iplimit_log_path}
fi
# Create the iplimit jail files
# we didn't pass the bantime here to use the default value
create_iplimit_jails
# Launching fail2ban
if [[ $release == "alpine" ]]; then
if [[ $(rc-service fail2ban status | grep -F 'status: started' -c) == 0 ]]; then
rc-service fail2ban start
@ -2063,13 +2003,10 @@ show_banlog() {
}
create_iplimit_jails() {
# Use default bantime if not passed => 30 minutes
local bantime="${1:-30}"
# Uncomment 'allowipv6 = auto' in fail2ban.conf
sed -i 's/#allowipv6 = auto/allowipv6 = auto/g' /etc/fail2ban/fail2ban.conf
# On Debian 12+ fail2ban's default backend should be changed to systemd
if [[ "${release}" == "debian" && ${os_version} -ge 12 ]]; then
sed -i '0,/action =/s/backend = auto/backend = systemd/' /etc/fail2ban/jail.conf
fi
@ -2130,7 +2067,6 @@ iplimit_remove_conflicts() {
)
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
sed -i "/\[3x-ipl\]/,/^$/d" ${file}
echo -e "${yellow}Removing conflicts of [3x-ipl] in jail (${file})!${plain}\n"
@ -2270,42 +2206,42 @@ show_usage() {
show_menu() {
echo -e "
────────────────────────────────────────────────
${green}3X-UI Panel Management Script${plain}
${green}0.${plain} Exit Script
────────────────────────────────────────────────
${green}1.${plain} Install
${green}2.${plain} Update
${green}3.${plain} Update Menu
${green}4.${plain} Legacy Version
${green}5.${plain} Uninstall
────────────────────────────────────────────────
${green}6.${plain} Reset Username & Password
${green}7.${plain} Reset Web Base Path
${green}8.${plain} Reset Settings
${green}9.${plain} Change Port
${green}10.${plain} View Current Settings
────────────────────────────────────────────────
${green}11.${plain} Start
${green}12.${plain} Stop
${green}13.${plain} Restart
| ${green}14.${plain} Restart Xray
${green}15.${plain} Check Status
${green}16.${plain} Logs Management
────────────────────────────────────────────────
${green}17.${plain} Enable Autostart
${green}18.${plain} Disable Autostart
────────────────────────────────────────────────
${green}19.${plain} SSL Certificate Management
${green}20.${plain} Cloudflare SSL Certificate
${green}21.${plain} IP Limit Management
${green}22.${plain} Firewall Management
${green}23.${plain} SSH Port Forwarding Management
────────────────────────────────────────────────
${green}24.${plain} Enable BBR
${green}25.${plain} Update Geo Files
${green}26.${plain} Speedtest by Ookla
────────────────────────────────────────────────
════════════════════════════════════════════════════
${green}3X-UI Panel Management Script${plain}
${green}0.${plain} Exit Script
║──────────────────────────────────────────────────
${green}1.${plain} Install
${green}2.${plain} Update
${green}3.${plain} Update Menu
${green}4.${plain} Legacy Version
${green}5.${plain} Uninstall
║──────────────────────────────────────────────────
${green}6.${plain} Reset Username & Password
${green}7.${plain} Reset Web Base Path
${green}8.${plain} Reset Settings
${green}9.${plain} Change Port
${green}10.${plain} View Current Settings
║──────────────────────────────────────────────────
${green}11.${plain} Start
${green}12.${plain} Stop
${green}13.${plain} Restart
${green}14.${plain} Restart Xray
${green}15.${plain} Check Status
${green}16.${plain} Logs Management
║──────────────────────────────────────────────────
${green}17.${plain} Enable Autostart
${green}18.${plain} Disable Autostart
║──────────────────────────────────────────────────
${green}19.${plain} SSL Certificate Management
${green}20.${plain} Cloudflare SSL Certificate
${green}21.${plain} IP Limit Management
${green}22.${plain} Firewall Management
${green}23.${plain} SSH Port Forwarding Management
║──────────────────────────────────────────────────
${green}24.${plain} Enable BBR
${green}25.${plain} Update Geo Files
${green}26.${plain} Speedtest by Ookla
════════════════════════════════════════════════════
"
show_status
echo && read -rp "Please enter your selection [0-26]: " num