diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index c6175c59..a3668641 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -10,5 +10,5 @@ liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry polar: # Replace with a single Polar username -buy_me_a_coffee: mhsanaei +buy_me_a_coffee: xeefeiz custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5313d60a..de0b13bd 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -19,8 +19,8 @@ jobs: uses: docker/metadata-action@v5 with: images: | - hsanaeii/3x-ui - ghcr.io/mhsanaei/3x-ui + xeefei/3x-ui + ghcr.io/xeefei/3x-ui tags: | type=ref,event=branch type=ref,event=tag @@ -54,4 +54,4 @@ jobs: push: true platforms: linux/amd64, linux/arm64/v8, linux/arm/v7, linux/arm/v6, linux/386 tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file + labels: ${{ steps.meta.outputs.labels }} diff --git a/README.md b/README.md index 7cda8370..96d87e5d 100644 --- a/README.md +++ b/README.md @@ -1,56 +1,756 @@ -[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) +

Image

-

- - - 3x-ui - -

+**---------------------------------------一个更好的面板 • 基于Xray Core构建------------------------------** -[![](https://img.shields.io/github/v/release/mhsanaei/3x-ui.svg?style=for-the-badge)](https://github.com/MHSanaei/3x-ui/releases) -[![](https://img.shields.io/github/actions/workflow/status/mhsanaei/3x-ui/release.yml.svg?style=for-the-badge)](https://github.com/MHSanaei/3x-ui/actions) -[![GO Version](https://img.shields.io/github/go-mod/go-version/mhsanaei/3x-ui.svg?style=for-the-badge)](#) -[![Downloads](https://img.shields.io/github/downloads/mhsanaei/3x-ui/total.svg?style=for-the-badge)](https://github.com/MHSanaei/3x-ui/releases/latest) + +[![](https://img.shields.io/github/v/release/xeefei/3x-ui.svg?style=for-the-badge)](https://github.com/xeefei/3x-ui/releases) +[![](https://img.shields.io/github/actions/workflow/status/xeefei/3x-ui/release.yml.svg?style=for-the-badge)](https://github.com/xeefei/3x-ui/actions) +[![GO Version](https://img.shields.io/github/go-mod/go-version/xeefei/3x-ui.svg?style=for-the-badge)](#) +[![Downloads](https://img.shields.io/github/downloads/xeefei/3x-ui/total.svg?style=for-the-badge)](https://github.com/xeefei/3x-ui/releases/latest) [![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true&style=for-the-badge)](https://www.gnu.org/licenses/gpl-3.0.en.html) -**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] -> This project is only for personal using, please do not use it for illegal purposes, please do not use it in a production environment. +> **注意:** 在使用此项目和〔教程〕过程中,若因违反以上声明使用规则而产生的一切后果由使用者自负。 -As an enhanced fork of the original X-UI project, 3X-UI provides improved stability, broader protocol support, and additional features. - -## Quick Start - -```bash -bash <(curl -Ls 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 - -- [alireza0](https://github.com/alireza0/) - -## Acknowledgment - -- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._ -- [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 - -**If this project is helpful to you, you may wish to give it a**:star2: +**如果此项目对你有用,请给一个**:star2:

- + Image

-- USDT (TRC20): `TXncxkvhkDWGts487Pjqq1qT9JmwRUz8CC` -- MATIC (polygon): `0x41C9548675D044c6Bfb425786C765bc37427256A` -- LTC (Litecoin): `ltc1q2ach7x6d2zq0n4l0t4zl7d7xe2s6fs7a3vspwv` +- 赞助地址(USDT/TRC20):`TYQEmQp1P65u9bG7KPehgJdvuokfb72YkZ` -## Stargazers over Time +## [【3X-UI】中文交流群:https://t.me/XUI_CN](https://t.me/XUI_CN) +## [【3X-UI】详细安装流程步骤:https://xeefei.github.io/xufei/2024/05/3x-ui/](https://xeefei.github.io/xufei/2024/05/3x-ui/) -[![Stargazers over time](https://starchart.cc/MHSanaei/3x-ui.svg?variant=adaptive)](https://starchart.cc/MHSanaei/3x-ui) +------------ +## ✰〔3X-UI优化版〕跟原版3X-UI的区别?✰ +### 大部分功能基于原版3X-UI进行汉化优化,主要的优化内容如下: +#### 1、最大限度地汉化了面板项目,更适合中文宝宝体质,包括: +##### ①优化在VPS中进行〔脚本安装过程〕的汉化提示,增加相应的安装中文提示,让中文用户能明白清楚自己安装到了哪个环节?在细节方面,增加了安装成功之后的〔用户设置信息〕提示,在脚本中加入〔面板登录地址〕显示, +##### ②管理后台进行了相应的〔图标和按钮〕汉化,让中文宝宝能够看得懂, +##### ③安装成功后〔自动更改〕后台管理界面和电报机器人界面默认为〔中文〕, +##### ④在管理后台中〔设置证书处〕,增加了acme方式填入路径的提示; +#### 2、优化了电报机器人响应〔按钮〕的名称和排序; +#### 3、创建了〔3X-UI〕中文交流群,各位中文宝宝可以一起讨论交流; +#### 4、管理后台中增加了〔实用导航〕页面,里面包含实用内容; +#### 5、优化了后台〔二维码〕显示模式,点击打开会更加丝滑美观; +#### 6、在创建reality协议时,更改uTLS指纹默认使用chrome; +#### 7、更新README内容添加备份&恢复操作说明,以及更多其他图文介绍; +#### 8、管理后台中增加〔端口检测〕和〔网络测速〕,点击可以跳转直达; +#### 9、增加了详细的项目〔安装配置教程〕,解决小白用户不懂配置的烦恼。 + +------------ +## ✰如何从其他x-ui版本迁移到〔3X-UI优化版〕?✰ +#### 1、若你用的是伊朗老哥的原版3X-UI,是可以直接〔覆盖安装〕的,因为〔3X-UI优化版〕是fork了原版3X-UI的项目,基于原有的功能进行优化的,大功能是没有变化的,主要是进行了脚本的〔汉化处理〕,其他诸如数据库文件等位置是没有改变的,所以直接覆盖安装,并不会影响你〔原有节点及配置〕等数据;安装命令如下: +``` +bash <(curl -Ls https://raw.githubusercontent.com/xeefei/3x-ui/master/install.sh) +``` +#### 2、若你之前用的是Docker方式安装,那先进入容器里面/命令:docker exec -it 容器id /bin/sh,再执行以上脚本命令直接【覆盖安装】即可, +#### 3、若你用的是之前F佬的x-ui或者其他分支版本,那直接覆盖安装的话,并不能确保一定就能够兼容?建议你先去备份〔数据库〕配置文件,再进行安装〔3X-UI优化版〕。 + + +------------ +## 安装之前的准备 +- 购买一台性能还不错的VPS,可通过本页底部链接购买, +- PS:若你不想升级系统,则可以跳过此步骤。 +- 若你需要更新/升级系统,Debian系统可用如下命令: + ``` + apt update + apt upgrade -y + apt dist-upgrade -y + apt autoclean + apt autoremove -y + ``` +- 查看系统当前版本: + ``` + cat /etc/debian_version + ``` +- 查看内核版本: + ``` + uname -r + ``` +- 列出所有内核: + ``` + dpkg --list | grep linux-image + ``` +- 更新完成后执行重新引导: + ``` + update-grub + ``` +- 完成以上步骤之后输入reboot重启系统 + +------------ +## 【搬瓦工】重装/升级系统之后SSH连不上如何解决? +- 【搬瓦工】重装/升级系统会恢复默认22端口,如果需要修改SSH的端口号,您需要进行以下步骤: +- 以管理员身份使用默认22端口登录到SSH服务器 +- 打开SSH服务器的配置文件进行编辑,SSH配置文件通常位于/etc/ssh/sshd_config +- 找到"Port"选项,并将其更改为您想要的端口号 +- Port <新端口号>,请将<新端口号>替换为您想要使用的端口号 +- 保存文件并退出编辑器 +- 重启服务器以使更改生效 + +------------ +## 安装 & 升级 +- 使用3x-ui脚本一般情况下,安装完成创建入站之后,端口是默认关闭的,所以必须进入脚本选择【22】去放行端口 +- 要使用【自动续签】证书功能,也必须放行【80】端口,保持80端口是打开的,才会每3个月自动续签一次 + +- 【全新安装】请执行以下脚本: +``` +bash <(curl -Ls https://raw.githubusercontent.com/xeefei/3x-ui/master/install.sh) +``` +#### 如果执行了上面的代码但是报错,证明你的系统里面没有curl这个软件,请执行以下命令先安装curl软件,安装curl之后再去执行上面代码, +``` +apt update -y&&apt install -y curl&&apt install -y socat +``` + +- 若要对版本进行升级,可直接通过脚本选择【2】,如下图: +![8](./media/8.png) +![10](./media/10.png) +- 在到这一步必须要注意:要保留旧设置的话,需要输入【n】 +![11](./media/11.png) + + +## 安装指定版本 + +若要安装指定的版本,请使用以下安装命令。 e.g., ver `v2.4.5`: + +``` +VERSION=v2.4.5 && bash <(curl -Ls "https://raw.githubusercontent.com/xeefei/3x-ui/$VERSION/install.sh") $VERSION +``` +------------ +## 若你的VPS默认有防火墙,请在安装完成之后放行指定端口 +- 放行【面板登录端口】 +- 放行出入站管理协议端口 +- 如果要申请安装证书并每3个月【自动续签】证书,请确保80和443端口是放行打开的 +- 可通过此脚本的第【22】选项去安装防火墙进行管理,如下图: +![9](./media/9.png) +- 若要一次性放行多个端口或一整个段的端口,用英文逗号隔开。 +#### PS:若你的VPS没有防火墙,则所有端口都是能够ping通的,可自行选择是否进入脚本安装防火墙保证安全,但安装了防火墙必须放行相应端口。 + +------------ +## 安装证书开启https方式实现域名登录访问管理面板/----->>偷自己 +#### PS:如果不需要以上功能或无域名,可以跳过这步, +##### 1、把自己的域名托管到CF,并解析到自己VPS的IP,不要开启【小云朵】, +##### 2、如果要申请安装证书并每3个月【自动续签】证书,请确保80和443端口是放行打开的, +##### 3、输入x-ui命令进入面板管理脚本,通过选择第【18】选项去进行安装, +##### 4、在安装证书的最后一步,请输入【Y】设置面板证书路径,如下图所示: +![27](./media/27.png) +##### 5、进入后台【面板设置】—–>【常规】中,会看到脚本已经自动填好了证书公钥、私钥路径, +##### 6、点击左上角的【保存】和【重启面板】,即可用自己域名进行登录管理;也可按照后续方法实现【自己偷自己】。 + +------------ +## 登录面板进行【常规】设置 +### 特别是如果在安装过程中,全部都是默认【回车键】安装的话,用户名/密码/访问路径是随机的,而面板监听端口默认是2053,最好进入面板更改, +##### 1、填写自己想要设置的【面板监听端口】,并去登录SSH放行, +##### 2、更改自己想要设置的【面板登录访问路径】,后续加上路径登录访问, +![25](./media/25.png) +##### 3、其他:安全设定和电报机器人等配置,可自行根据需求去进行设置, +##### 4、若申请了证书须填写证书公钥/私钥路径,建议配置电报机器人方便管理, +![26](./media/26.png) +##### 5、面板设置【改动保存】之后,都需要点击左上角【重启面板】,才能生效。 +#### PS:若你在正确完成了上述步骤之后,你没有安装证书的情况下,去用IP+端口号/路径的方式却不能访问面板,那请检查一下是不是你的浏览器自动默认开启了https模式,需要手动调整一下改成http方式,把“s”去掉,即可访问成功。 + +------------ +## 创建【入站协议】和添加【客户端】,并测试上网 +##### 1、点击左边【入站列表】,然后【添加入站】,传输方式保持【TCP】不变,尽量选择主流的vless+reality+vision协议组合, +![23](./media/23.png) +##### 2、在选择reality协议时,偷的域名可以使用默认的,要使用其他的,请替换尽量保持一致就行,比如Apple、Yahoo,VPS所在地区的旅游、学校网站等;如果要实现【偷自己】,请参看后续【如何偷自己】的说明部分;而私钥/公钥部分,可以直接点击下方的【Get New Cert】获取一个随机的, +##### 3、在创建reality协议过程中,至于其他诸如:PROXY Protocol,HTTP 伪装,TPROXY,External Proxy等等选项,若无特殊要求,保持默认设置即可,不用去动它们, +![24](./media/24.png) +##### 4、创建好入站协议之后,默认只有一个客户端,可根据自己需求继续添加;重点:并编辑客户端,选择【Flow流控】为xtls-rprx-vision-udp443, +![19](./media/19.png) +##### 5、其他:流量限制,到期时间,客户TG的ID等选项根据自己需求填写, +![4](./media/4.png) +##### 6、一定要放行端口之后,确保端口能够ping通,再导入软件, +##### 7、点击二维码或者复制链接导入到v2rayN等软件中进行测试。 + +------------ +## 备份与恢复/迁移数据库(以Debian系统为例) +#### 一、备份:通过配置好电报管理机器人,并去设置开启【自动备份】,每天凌晨12点会通过VPS管理机器人获取【备份配置】文件,有x-ui.db和config.json两个文件,可自行下载保存到自己电脑里面, +![14](./media/14.png) +#### 二、搭建:在新的VPS中全新安装好3x-ui面板,通过脚本放行之前配置的所有端口,一次性放行多个端口请用【英文逗号】分隔, +#### 三、若需要安装证书,则提前把域名解析到新的VPS对应的IP,并且去输入x-ui选择第【18】选项去安装,并记录公钥/私钥的路径,无域名则跳过这一步, +#### 四、恢复:SSH登录服务器找到/etc/x-ui/x-ui.db和/usr/local/x-ui/bin/config.json文件位置,上传之前的两个备份文件,进行覆盖, +![12](./media/12.png) +##### PS:把之前通过自动备份下载得到的两个文件上传覆盖掉旧文件,重启3x-ui面板即可【迁移成功】;即使迁移过程中出现问题,你是有备份文件的,不用担心,多试几次。 +![13](./media/13.png) +#### 五、若安装了证书,去核对/更改一下证书的路径,一般是同一个域名的话,位置在:/root/cert/域名/fullchain.pem,路径是相同的就不用更改, +#### 六、重启面板/重启服务器,让上述步骤生效即可,这时可以看到所有配置都是之前自己常用的,包括面板用户名、密码,入站、客户端,电报机器人配置等。 + +------------ +## 安装完成后如何设置调整成【中文界面】? +- 方法一:通过管理后台【登录页面】调整,登录时可以选择,如下图: +![15](./media/15.png) +- 方法二:通过在管理后台-->【面板设置】中去选择设置,如下图: +![16](./media/16.png) +- 【TG机器人】设置中文:通过在管理后台-->【面板设置】-->【机器人配置】中去选择设置,并建议打开数据库备份和登录通知,如下图: +![17](./media/17.png) + +------------ +## 用3x-ui如何实现【自己偷自己】? +- 其实很简单,只要你为面板设置了证书, +- 开启了HTTPS登录,就可以将3x-ui自身作为Web Server, +- 无需Nginx等,这里给一个示例: +- 其中目标网站(Dest)请填写面板监听端口, +- 可选域名(SNI)填写面板登录域名, +- 如果您使用其他web server(如nginx)等, +- 将目标网站改为对应监听端口也可。 +- 需要说明的是,如果您处于白名单地区,自己“偷”自己并不适合你; +- 其次,可选域名一项实际上可以填写任意SNI,只要客户端保持一致即可,不过并不推荐这样做。 +- 配置方法如下图所示: +![18](./media/18.png) + +------------ +## 〔子域名〕被墙针对特征 +#### 网络表现: +##### 1、可以Ping通域名和IP地址, +##### 2、子域名无法打开3X-UI管理界面, +##### 3、什么都正常就是不能上网; + +#### 问题: +##### 你的子域名被墙针对了:无法上网! + +#### 解决方案: +##### 1、更换为新的子域名, +##### 2、解析新的子域名到VPS的IP, +##### 3、重新去安装新证书, +##### 4、重启3X-UI和服务器, +##### 5、重新去获取链接并测试上网。 +#### PS:若通过以上步骤还是不能正常上网,则重装VPS服务器OS系统,以及3X-UI面板全部重新安装,之后就正常了! + +------------ +## 在自己的VPS服务器部署【订阅转换】功能 +### 如何把vless/vmess等协议转换成Clash/Surge等软件支持的格式? +##### 1、进入脚本输入x-ui命令调取面板,选择第【24】选项安装订阅转换模块,如下图: +![21](./media/21.png) +##### 2、等待安装【订阅转换】成功之后,访问地址:你的IP:18080(端口号)进行转换, +![22](./media/22.png) +##### 3、因为在转换过程中需要调取后端API,所以请确保端口25500是打开放行的, +##### 4、在得到【转换链接】之后,只要你的VPS服务器25500端口是能ping通的,就能导入Clash/Surge等软件成功下载配置, +##### 5、此功能集成到3x-ui面板中,是为了保证安全,通过调取24选项把【订阅转换】功能部署在自己的VPS中,不会造成链接泄露。 +### 【订阅转换】功能在自己的VPS中安装部署成功之后的界面如下图所示: +![20](./media/20.png) + +------------ +## 如何保护自己的IP不被墙被封? +##### 1、使用的代理协议要安全,加密是必备,推荐使用vless+reality+vision协议组合, +##### 2、因为有时节点会共享,在不同的地区,多个省份之间不要共同连接同一个IP, +##### 3、连接同一个IP就算了,不要同一个端口,不要同IP+同端口到处漫游,要分开, +##### 4、同一台VPS,不要在一天内一直大流量去下载东西使用,不要流量过高要切换, +##### 5、创建【入站协议】的时候,尽量用【高位端口】,比如40000--65000之间的端口号。 +#### 提醒:为什么在特殊时期,比如:两会,春节等被封得最严重最惨? +##### 尼玛同一个IP+同一个端口号,多个省份去漫游,跟开飞机场一样!不封你,封谁的IP和端口? +#### 总结:不要多终端/多省份/多个朋友/共同使用同一个IP和端口号!使用3x-ui多创建几个【入站】, +#### 多做几条备用,各用各的!各行其道才比较安全!GFW的思维模式是干掉机场,机场的特征个人用户不要去沾染,自然IP就保护好了。 + +------------ +## SSL 认证 + +
+ 点击查看 SSL 认证 + +### ACME + +要使用 ACME 管理 SSL 证书: + +1. 确保您的域名已正确解析到服务器, +2. 输入“x-ui”命令并选择“SSL 证书管理”, +3. 您将看到以下选项: + + - **获取证书** ----获取SSL证书 + - **吊销证书** ----吊销现有的SSL证书 + - **续签证书** ----强制续签SSL证书 + - **显示所有证书** ----显示服务器中所有能用的证书 + - **设置面板证书路径** ----指定面板要使用的证书 + + +### Certbot + +安装和使用 Certbot: + +```sh +apt-get install certbot -y +certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com +certbot renew --dry-run +``` + +### Cloudflare + +管理脚本具有用于 Cloudflare 的内置 SSL 证书应用程序。若要使用此脚本申请证书,需要满足以下条件: + +- Cloudflare 邮箱地址 +- Cloudflare Global API Key +- 域名已通过 cloudflare 解析到当前服务器 + +**如何获取 Cloudflare全局API密钥:** + +1. 在终端中输入“x-ui”命令,然后选择“CF SSL 证书”。 +2. 访问链接: [Cloudflare API Tokens](https://dash.cloudflare.com/profile/api-tokens). +3. 点击“查看全局 API 密钥”(如下图所示): + ![](media/APIKey1.PNG) +4. 您可能需要重新验证您的帐户。之后,将显示 API 密钥(请参见下面的屏幕截图): + ![](media/APIKey2.png) + +使用时,只需输入您的“域名”、“电子邮件”和“API KEY”即可。示意图如下: + ![](media/DetailEnter.png) + + +
+ +------------ +## 手动安装 & 升级 + +
+ 点击查看 手动安装 & 升级 + +#### 使用 + +1. 若要将最新版本的压缩包直接下载到服务器,请运行以下命令: + +```sh +ARCH=$(uname -m) +case "${ARCH}" in + x86_64 | x64 | amd64) XUI_ARCH="amd64" ;; + i*86 | x86) XUI_ARCH="386" ;; + armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;; + armv7* | armv7) XUI_ARCH="armv7" ;; + armv6* | armv6) XUI_ARCH="armv6" ;; + armv5* | armv5) XUI_ARCH="armv5" ;; + s390x) echo 's390x' ;; + *) XUI_ARCH="amd64" ;; +esac + + +wget https://github.com/xeefei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz +``` + +2. 下载压缩包后,执行以下命令安装或升级 x-ui: + +```sh +ARCH=$(uname -m) +case "${ARCH}" in + x86_64 | x64 | amd64) XUI_ARCH="amd64" ;; + i*86 | x86) XUI_ARCH="386" ;; + armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;; + armv7* | armv7) XUI_ARCH="armv7" ;; + armv6* | armv6) XUI_ARCH="armv6" ;; + armv5* | armv5) XUI_ARCH="armv5" ;; + s390x) echo 's390x' ;; + *) XUI_ARCH="amd64" ;; +esac + +cd /root/ +rm -rf x-ui/ /usr/local/x-ui/ /usr/bin/x-ui +tar zxvf x-ui-linux-${XUI_ARCH}.tar.gz +chmod +x x-ui/x-ui x-ui/bin/xray-linux-* x-ui/x-ui.sh +cp x-ui/x-ui.sh /usr/bin/x-ui +cp -f x-ui/x-ui.service /etc/systemd/system/ +mv x-ui/ /usr/local/ +systemctl daemon-reload +systemctl enable x-ui +systemctl restart x-ui +``` + +
+ +------------ +## 通过Docker安装 + +
+ 点击查看 通过Docker安装 + +#### 使用 + + +1. **安装Docker** + + ```sh + bash <(curl -sSL https://get.docker.com) + ``` + + +2. **克隆项目仓库** + + ```sh + git clone https://github.com/xeefei/3x-ui.git + cd 3x-ui + ``` + +3. **启动服务**: + + ```sh + docker compose up -d + ``` + + 添加 ```--pull always``` 标志使 docker 在拉取更新的镜像时自动重新创建容器。有关更多信息,请参阅:https://docs.docker.com/reference/cli/docker/container/run/#pull + + **或** + + ```sh + docker run -itd \ + -e XRAY_VMESS_AEAD_FORCED=false \ + -v $PWD/db/:/etc/x-ui/ \ + -v $PWD/cert/:/root/cert/ \ + --network=host \ + --restart=unless-stopped \ + --name 3x-ui \ + ghcr.io/xeefei/3x-ui:latest + ``` + +4. **更新至最新版本** + + ```sh + cd 3x-ui + docker compose down + docker compose pull 3x-ui + docker compose up -d + ``` + +5. **从Docker中删除3x-ui ** + + ```sh + docker stop 3x-ui + docker rm 3x-ui + cd -- + rm -r 3x-ui + ``` + +
+ +------------ +## 建议使用的操作系统 + +- Ubuntu 20.04+ +- Debian 11+ +- CentOS 8+ +- OpenEuler 22.03+ +- Fedora 36+ +- Arch Linux +- Manjaro +- Armbian +- AlmaLinux 8.0+ +- Rocky Linux 8+ +- Oracle Linux 8+ +- OpenSUSE Tubleweed +- Amazon Linux 2023 + +------------ +## 支持的架构和设备 +
+ 点击查看 支持的架构和设备 + +我们的平台提供与各种架构和设备的兼容性,确保在各种计算环境中的灵活性。以下是我们支持的关键架构: + +- **amd64**: 这种流行的架构是个人计算机和服务器的标准,可以无缝地适应大多数现代操作系统。 + +- **x86 / i386**: 这种架构在台式机和笔记本电脑中被广泛采用,得到了众多操作系统和应用程序的广泛支持,包括但不限于 Windows、macOS 和 Linux 系统。 + +- **armv8 / arm64 / aarch64**: 这种架构专为智能手机和平板电脑等当代移动和嵌入式设备量身定制,以 Raspberry Pi 4、Raspberry Pi 3、Raspberry Pi Zero 2/Zero 2 W、Orange Pi 3 LTS 等设备为例。 + +- **armv7 / arm / arm32**: 作为较旧的移动和嵌入式设备的架构,它仍然广泛用于Orange Pi Zero LTS、Orange Pi PC Plus、Raspberry Pi 2等设备。 + +- **armv6 / arm / arm32**: 这种架构面向非常老旧的嵌入式设备,虽然不太普遍,但仍在使用中。Raspberry Pi 1、Raspberry Pi Zero/Zero W 等设备都依赖于这种架构。 + +- **armv5 / arm / arm32**: 它是一种主要与早期嵌入式系统相关的旧架构,目前不太常见,但仍可能出现在早期 Raspberry Pi 版本和一些旧智能手机等传统设备中。 +
+ +------------ +## Languages + +- English(英语) +- Farsi(伊朗语) +- Simplified Chinese(简体中文) +- Traditional Chinese(繁体中文) +- Russian(俄语) +- Vietnamese(越南语) +- Spanish(西班牙语) +- Indonesian (印度尼西亚语) +- Ukrainian(乌克兰语) +- Turkish(土耳其语) +- Português (葡萄牙语) + +------------ +## 项目特点 + +- 系统状态查看与监控 +- 可搜索所有入站和客户端信息 +- 深色/浅色主题随意切换 +- 支持多用户和多协议 +- 支持多种协议,包括 VMess、VLESS、Trojan、Shadowsocks、Dokodemo-door、Socks、HTTP、wireguard +- 支持 XTLS 原生协议,包括 RPRX-Direct、Vision、REALITY +- 流量统计、流量限制、过期时间限制 +- 可自定义的 Xray配置模板 +- 支持HTTPS访问面板(自备域名+SSL证书) +- 支持一键式SSL证书申请和自动续签证书 +- 更多高级配置项目请参考面板去进行设定 +- 修复了 API 路由(用户设置将使用 API 创建) +- 支持通过面板中提供的不同项目更改配置。 +- 支持从面板导出/导入数据库 + +## 默认面板设置 + +
+ + 点击查看 默认设置 + + ### 默认信息 + +- **端口** + - 2053 +- **用户名 & 密码 & 访问路径** + - 当您跳过设置时,这些信息会随机生成, + - 您也可以在安装的时候自定义访问路径。 +- **数据库路径:** + - /etc/x-ui/x-ui.db +- **Xray 配置路径:** + - /usr/local/x-ui/bin/config.json +- **面板链接(无SSL):** + - http://ip:2053/访问路径/panel +- **面板链接(有SSL):** + - https://你的域名:2053/访问路径/panel + +
+ +------------ +## [WARP 配置](https://gitlab.com/fscarmen/warp) + +
+ 点击查看 WARP 配置 + +#### 使用 + +**对于版本 `v2.1.0` 及更高版本:** + +WARP 是内置的,无需额外安装;只需在面板中打开必要的配置即可。 + +**如果要在 v2.1.0 之前使用 WARP 路由**,请按照以下步骤操作: + +**1.** 在 **SOCKS Proxy Mode** 模式中安装Wrap + + - **Account Type (free, plus, team):** Choose the appropriate account type. + - **Enable/Disable WireProxy:** Toggle WireProxy on or off. + - **Uninstall WARP:** Remove the WARP application. + +**2.** 如果您已经安装了 warp,您可以使用以下命令卸载: + + ```sh + warp u + ``` + +**3.** 在面板中打开您需要的配置 + + 配置: + + - Block Ads + - Route Google, Netflix, Spotify, and OpenAI (ChatGPT) traffic to WARP + - Fix Google 403 error + + +
+ +------------ +## IP 限制 + +
+ 点击查看 IP 限制 + +#### 使用 + +**注意:** 使用 IP 隧道时,IP 限制无法正常工作。 + +- 对于 `v1.6.1`之前的版本 : + + - IP 限制 已被集成在面板中。 + +- 对于 `v1.7.0` 以及更新的版本: + + - 要使 IP 限制正常工作,您需要按照以下步骤安装 fail2ban 及其所需的文件: + + 1. 使用面板内置的 `x-ui` 指令 + 2. 选择 `IP Limit Management`. + 3. 根据您的需要选择合适的选项。 + + - 确保您的 Xray 配置上有 ./access.log 。在 v2.1.3 之后,我们有一个选项。 + + ```sh + "log": { + "access": "./access.log", + "dnsLog": false, + "loglevel": "warning" + }, + ``` + - 您需要在Xray配置中手动设置〔访问日志〕的路径。 + +
+ +------------ +## Telegram 机器人 + +
+ 点击查看 Telegram 机器人 + +#### 使用 + +Web 面板通过 Telegram Bot 支持每日流量、面板登录、数据库备份、系统状态、客户端信息等通知和功能。要使用机器人,您需要在面板中设置机器人相关参数,包括: + +- 电报令牌 +- 管理员聊天 ID +- 通知时间(cron 语法) +- 到期日期通知 +- 流量上限通知 +- 数据库备份 +- CPU 负载通知 + + +**参考:** + +- `30 \* \* \* \* \*` - 在每个点的 30 秒处通知 +- `0 \*/10 \* \* \* \*` - 每 10 分钟的第一秒通知 +- `@hourly` - 每小时通知 +- `@daily` - 每天通知 (00:00) +- `@weekly` - 每周通知 +- `@every 8h` - 每8小时通知 + +### Telegram Bot 功能 + +- 定期报告 +- 登录通知 +- CPU 阈值通知 +- 提前报告的过期时间和流量阈值 +- 如果将客户的电报用户名添加到用户的配置中,则支持客户端报告菜单 +- 支持使用UUID(VMESS/VLESS)或密码(TROJAN)搜索报文流量报告 - 匿名 +- 基于菜单的机器人 +- 通过电子邮件搜索客户端(仅限管理员) +- 检查所有入库 +- 检查服务器状态 +- 检查耗尽的用户 +- 根据请求和定期报告接收备份 +- 多语言机器人 + +### 注册 Telegram bot + +- 与 [Botfather](https://t.me/BotFather) 对话: + ![Botfather](./media/botfather.png) + +- 使用 /newbot 创建新机器人:你需要提供机器人名称以及用户名,注意名称中末尾要包含“bot” + ![创建机器人](./media/newbot.png) + +- 启动您刚刚创建的机器人。可以在此处找到机器人的链接。 + ![令牌](./media/token.png) + +- 输入您的面板并配置 Telegram 机器人设置,如下所示: + ![面板设置](./media/panel-bot-config.png) + +在输入字段编号 3 中输入机器人令牌。 +在输入字段编号 4 中输入用户 ID。具有此 id 的 Telegram 帐户将是机器人管理员。 (您可以输入多个,只需将它们用“ ,”分开即可) + +- 如何获取TG ID? 使用 [bot](https://t.me/useridinfobot), 启动机器人,它会给你 Telegram 用户 ID。 +![用户 ID](./media/user-id.png) + +
+ +------------ +## API 路由 + +
+ 点击查看 API 路由 + +#### 使用 + +- `/login` 使用 `POST` 用户名称 & 密码: `{username: '', password: ''}` 登录 +- `/panel/api/inbounds` 以下操作的基础: + +| 方法 | 路径 | 操作 | +| :----: | ---------------------------------- | ------------------------------------------- | +| `GET` | `"/list"` | 获取所有入站 | +| `GET` | `"/get/:id"` | 获取所有入站以及inbound.id | +| `GET` | `"/getClientTraffics/:email"` | 通过电子邮件获取客户端流量 | +| `GET` | `"/getClientTrafficsById/:id"` | 通过用户ID获取客户端流量 | +| `GET` | `"/createbackup"` | Telegram 机器人向管理员发送备份 | +| `POST` | `"/add"` | 添加入站 | +| `POST` | `"/del/:id"` | 删除入站 | +| `POST` | `"/update/:id"` | 更新入站 | +| `POST` | `"/clientIps/:email"` | 客户端 IP 地址 | +| `POST` | `"/clearClientIps/:email"` | 清除客户端 IP 地址 | +| `POST` | `"/addClient"` | 将客户端添加到入站 | +| `POST` | `"/:id/delClient/:clientId"` | 通过 clientId\* 删除客户端 | +| `POST` | `"/updateClient/:clientId"` | 通过 clientId\* 更新客户端 | +| `POST` | `"/:id/resetClientTraffic/:email"` | 重置客户端的流量 | +| `POST` | `"/resetAllTraffics"` | 重置所有入站的流量 | +| `POST` | `"/resetAllClientTraffics/:id"` | 重置入站中所有客户端的流量 | +| `POST` | `"/delDepletedClients/:id"` | 删除入站耗尽的客户端 (-1: all) | +| `POST` | `"/onlines"` | 获取在线用户 ( 电子邮件列表 ) | + +- 使用`clientId` 项应该填写下列数据: + +- `client.id` for VMESS and VLESS +- `client.password` for TROJAN +- `client.email` for Shadowsocks + + + +- [API 文档](https://documenter.getpostman.com/view/16802678/2s9YkgD5jm) + +- [Run In Postman](https://app.getpostman.com/run-collection/16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415%26entityType%3Dcollection%26workspaceId%3D2cd38c01-c851-4a15-a972-f181c23359d9) +
+ +------------ +## 环境变量 + +
+ 点击查看 环境变量 + +#### Usage + +| 变量 | Type | 默认 | +| -------------- | :--------------------------------------------: | :------------ | +| XUI_LOG_LEVEL | `"debug"` \| `"info"` \| `"warn"` \| `"error"` | `"info"` | +| XUI_DEBUG | `boolean` | `false` | +| XUI_BIN_FOLDER | `string` | `"bin"` | +| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` | +| XUI_LOG_FOLDER | `string` | `"/var/log"` | + +例子: + +```sh +XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go +``` + +
+ +------------ +## 预览 + +![1](./media/1.png) +![2](./media/2.png) +![3](./media/3.png) +![5](./media/5.png) +![6](./media/6.png) +![7](./media/7.png) + +------------ +## 广告赞助 +- 如果你觉得本项目对你有用,而且你也恰巧有这方面的需求,你也可以选择通过我的购买链接赞助我。 +- [搬瓦工GIA高端线路,仅推荐购买GIA套餐](https://bandwagonhost.com/aff.php?aff=75015) +- [Dmit高端GIA线路](https://www.dmit.io/aff.php?aff=9326) +- [白丝云【4837线路】实惠量大管饱](https://cloudsilk.io/aff.php?aff=706) + +------------ +## 特别感谢 + +- [alireza0](https://github.com/alireza0/) + +------------ +## 致谢 + +- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._ +- [Vietnam Adblock rules](https://github.com/vuong2023/vn-v2ray-rules) (License: **GPL-3.0**): _A hosted domain hosted in Vietnam and blocklist with the most efficiency for Vietnamese._ + +------------ +## Star 趋势 + +[![Stargazers over time](https://starchart.cc/xeefei/3x-ui.svg)](https://starchart.cc/xeefei/3x-ui) diff --git a/README.zh.md b/README.zh.md new file mode 100644 index 00000000..81644090 --- /dev/null +++ b/README.zh.md @@ -0,0 +1,476 @@ +[English](/README.md) | [Chinese](/README.zh.md) | [Español](/README.es_ES.md) + +

Image

+ +**一个更好的面板 • 基于Xray Core构建** + +[![](https://img.shields.io/github/v/release/xeefei/3x-ui.svg)](https://github.com/xeefei/3x-ui/releases) +[![](https://img.shields.io/github/actions/workflow/status/xeefei/3x-ui/release.yml.svg)](#) +[![GO Version](https://img.shields.io/github/go-mod/go-version/xeefei/3x-ui.svg)](#) +[![Downloads](https://img.shields.io/github/downloads/xeefei/3x-ui/total.svg)](#) +[![License](https://img.shields.io/badge/license-GPL%20V3-blue.svg?longCache=true)](https://www.gnu.org/licenses/gpl-3.0.en.html) + +> **Disclaimer:** 此项目仅供个人学习交流,请不要用于非法目的,请不要在生产环境中使用。 + +**如果此项目对你有用,请给一个**:star2: + +

+ + Image + +

+ +- USDT (TRC20): `TYQEmQp1P65u9bG7KPehgJdvuokfb72YkZ` + +## 安装 & 升级 + +``` +bash <(curl -Ls https://raw.githubusercontent.com/xeefei/3x-ui/master/install.sh) +``` + +## 安装指定版本 + +若需要安装指定的版本,请将该版本添加到安装命令的末尾。 e.g., ver `v2.3.13`: + +``` +bash <(curl -Ls https://raw.githubusercontent.com/xeefei/3x-ui/master/install.sh) v2.3.13 +``` + +## SSL 认证 + +
+ 点击查看 SSL 认证 + +### Cloudflare + +管理脚本具有用于 Cloudflare 的内置 SSL 证书应用程序。若要使用此脚本申请证书,需要满足以下条件: + +- Cloudflare 邮箱地址 +- Cloudflare Global API Key +- 域名已通过 cloudflare 解析到当前服务器 + +**1:** 在终端中运行`x-ui`, 选择 `Cloudflare SSL Certificate`. + + +### Certbot +``` +apt-get install certbot -y +certbot certonly --standalone --agree-tos --register-unsafely-without-email -d yourdomain.com +certbot renew --dry-run +``` + +***Tip:*** *管理脚本具有 Certbot 。使用 `x-ui` 命令, 选择 `SSL Certificate Management`.* + +
+ +## 手动安装 & 升级 + +
+ 点击查看 手动安装 & 升级 + +#### 使用 + +1. 若要将最新版本的压缩包直接下载到服务器,请运行以下命令: + +```sh +ARCH=$(uname -m) +case "${ARCH}" in + x86_64 | x64 | amd64) XUI_ARCH="amd64" ;; + i*86 | x86) XUI_ARCH="386" ;; + armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;; + armv7* | armv7) XUI_ARCH="armv7" ;; + armv6* | armv6) XUI_ARCH="armv6" ;; + armv5* | armv5) XUI_ARCH="armv5" ;; + *) XUI_ARCH="amd64" ;; +esac + + +wget https://github.com/xeefei/3x-ui/releases/latest/download/x-ui-linux-${XUI_ARCH}.tar.gz +``` + +2. 下载压缩包后,执行以下命令安装或升级 x-ui: + +```sh +ARCH=$(uname -m) +case "${ARCH}" in + x86_64 | x64 | amd64) XUI_ARCH="amd64" ;; + i*86 | x86) XUI_ARCH="386" ;; + armv8* | armv8 | arm64 | aarch64) XUI_ARCH="arm64" ;; + armv7* | armv7) XUI_ARCH="armv7" ;; + armv6* | armv6) XUI_ARCH="armv6" ;; + armv5* | armv5) XUI_ARCH="armv5" ;; + *) XUI_ARCH="amd64" ;; +esac + +cd /root/ +rm -rf x-ui/ /usr/local/x-ui/ /usr/bin/x-ui +tar zxvf x-ui-linux-${XUI_ARCH}.tar.gz +chmod +x x-ui/x-ui x-ui/bin/xray-linux-* x-ui/x-ui.sh +cp x-ui/x-ui.sh /usr/bin/x-ui +cp -f x-ui/x-ui.service /etc/systemd/system/ +mv x-ui/ /usr/local/ +systemctl daemon-reload +systemctl enable x-ui +systemctl restart x-ui +``` + +
+ +## 通过Docker安装 + +
+ 点击查看 通过Docker安装 + +#### 使用 + +1. 安装Docker: + + ```sh + bash <(curl -sSL https://get.docker.com) + ``` + +2. 克隆仓库: + + ```sh + git clone https://github.com/xeefei/3x-ui.git + cd 3x-ui + ``` + +3. 运行服务: + + ```sh + docker compose up -d + ``` + + 或 + + ```sh + docker run -itd \ + -e XRAY_VMESS_AEAD_FORCED=false \ + -v $PWD/db/:/etc/x-ui/ \ + -v $PWD/cert/:/root/cert/ \ + --network=host \ + --restart=unless-stopped \ + --name 3x-ui \ + ghcr.io/xeefei/3x-ui:latest + ``` + +更新至最新版本 + + ```sh + cd 3x-ui + docker compose down + docker compose pull 3x-ui + docker compose up -d + ``` + +从Docker中删除3x-ui + + ```sh + docker stop 3x-ui + docker rm 3x-ui + cd -- + rm -r 3x-ui + ``` + +
+ + +## 建议使用的操作系统 + +- Ubuntu 20.04+ +- Debian 11+ +- CentOS 8+ +- Fedora 36+ +- Arch Linux +- Manjaro +- Armbian +- AlmaLinux 9+ +- Rockylinux 9+ +- OpenSUSE Tubleweed + +## 支持的架构和设备 +
+ 点击查看 支持的架构和设备 + +我们的平台提供与各种架构和设备的兼容性,确保在各种计算环境中的灵活性。以下是我们支持的关键架构: + +- **amd64**: 这种流行的架构是个人计算机和服务器的标准,可以无缝地适应大多数现代操作系统。 + +- **x86 / i386**: 这种架构在台式机和笔记本电脑中被广泛采用,得到了众多操作系统和应用程序的广泛支持,包括但不限于 Windows、macOS 和 Linux 系统。 + +- **armv8 / arm64 / aarch64**: 这种架构专为智能手机和平板电脑等当代移动和嵌入式设备量身定制,以 Raspberry Pi 4、Raspberry Pi 3、Raspberry Pi Zero 2/Zero 2 W、Orange Pi 3 LTS 等设备为例。 + +- **armv7 / arm / arm32**: 作为较旧的移动和嵌入式设备的架构,它仍然广泛用于Orange Pi Zero LTS、Orange Pi PC Plus、Raspberry Pi 2等设备。 + +- **armv6 / arm / arm32**: 这种架构面向非常老旧的嵌入式设备,虽然不太普遍,但仍在使用中。Raspberry Pi 1、Raspberry Pi Zero/Zero W 等设备都依赖于这种架构。 + +- **armv5 / arm / arm32**: 它是一种主要与早期嵌入式系统相关的旧架构,目前不太常见,但仍可能出现在早期 Raspberry Pi 版本和一些旧智能手机等传统设备中。 +
+ +## Languages + +- English(英语) +- Farsi(伊朗语) +- Chinese(中文) +- Russian(俄语) +- Vietnamese(越南语) +- Spanish(西班牙语) +- Indonesian (印度尼西亚语) +- Ukrainian(乌克兰语) + + +## Features + +- 系统状态监控 +- 在所有入站和客户端中搜索 +- 深色/浅色主题 +- 支持多用户和多协议 +- 支持多种协议,包括 VMess、VLESS、Trojan、Shadowsocks、Dokodemo-door、Socks、HTTP、wireguard +- 支持 XTLS 原生协议,包括 RPRX-Direct、Vision、REALITY +- 流量统计、流量限制、过期时间限制 +- 可自定义的 Xray配置模板 +- 支持HTTPS访问面板(自建域名+SSL证书) +- 支持一键式SSL证书申请和自动续费 +- 更多高级配置项目请参考面板 +- 修复了 API 路由(用户设置将使用 API 创建) +- 支持通过面板中提供的不同项目更改配置。 +- 支持从面板导出/导入数据库 + + +## 默认设置 + +
+ 点击查看 默认设置 + + ### 信息 + +- **端口:** 2053 +- **用户名 & 密码:** 当您跳过设置时,此项会随机生成。 +- **数据库路径:** + - /etc/x-ui/x-ui.db +- **Xray 配置路径:** + - /usr/local/x-ui/bin/config.json +- **面板链接(无SSL):** + - http://ip:2053/panel + - http://domain:2053/panel +- **面板链接(有SSL):** + - https://domain:2053/panel + +
+ +## WARP 配置 + +
+ 点击查看 WARP 配置 + +#### 使用 + +如果要在 v2.1.0 之前使用 WARP 路由,请按照以下步骤操作: + +**1.** 在 **SOCKS Proxy Mode** 模式中安装Wrap + + ```sh + bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh) + ``` + +**2.** 如果您已经安装了 warp,您可以使用以下命令卸载: + + ```sh + warp u + ``` + +**3.** 在面板中打开您需要的配置 + + 配置: + + - Block Ads + - Route Google + Netflix + Spotify + OpenAI (ChatGPT) to WARP + - Fix Google 403 error + +
+ +## IP 限制 + +
+ 点击查看 IP 限制 + +#### 使用 + +**注意:** 使用 IP 隧道时,IP 限制无法正常工作。 + +- 适用于最高 `v1.6.1` : + + - IP 限制 已被集成在面板中。 + +- 适用于 `v1.7.0` 以及更新的版本: + + - 要使 IP 限制正常工作,您需要按照以下步骤安装 fail2ban 及其所需的文件: + + 1. 使用面板内置的 `x-ui` 指令 + 2. 选择 `IP Limit Management`. + 3. 根据您的需要选择合适的选项。 + + - 确保您的 Xray 配置上有 ./access.log 。在 v2.1.3 之后,我们有一个选项。 + + ```sh + "log": { + "access": "./access.log", + "dnsLog": false, + "loglevel": "warning" + }, + ``` + +
+ +## Telegram 机器人 + +
+ 点击查看 Telegram 机器人 + +#### 使用 + +Web 面板通过 Telegram Bot 支持每日流量、面板登录、数据库备份、系统状态、客户端信息等通知和功能。要使用机器人,您需要在面板中设置机器人相关参数,包括: + +- 电报令牌 +- 管理员聊天 ID +- 通知时间(cron 语法) +- 到期日期通知 +- 流量上限通知 +- 数据库备份 +- CPU 负载通知 + + +**参考:** + +- `30 \* \* \* \* \*` - 在每个点的 30 秒处通知 +- `0 \*/10 \* \* \* \*` - 每 10 分钟的第一秒通知 +- `@hourly` - 每小时通知 +- `@daily` - 每天通知 (00:00) +- `@weekly` - 每周通知 +- `@every 8h` - 每8小时通知 + +### Telegram Bot 功能 + +- 定期报告 +- 登录通知 +- CPU 阈值通知 +- 提前报告的过期时间和流量阈值 +- 如果将客户的电报用户名添加到用户的配置中,则支持客户端报告菜单 +- 支持使用UUID(VMESS/VLESS)或密码(TROJAN)搜索报文流量报告 - 匿名 +- 基于菜单的机器人 +- 通过电子邮件搜索客户端(仅限管理员) +- 检查所有入库 +- 检查服务器状态 +- 检查耗尽的用户 +- 根据请求和定期报告接收备份 +- 多语言机器人 + +### 注册 Telegram bot + +- 与 [Botfather](https://t.me/BotFather) 对话: + ![Botfather](./media/botfather.png) + +- 使用 /newbot 创建新机器人:你需要提供机器人名称以及用户名,注意名称中末尾要包含“bot” + ![创建机器人](./media/newbot.png) + +- 启动您刚刚创建的机器人。可以在此处找到机器人的链接。 + ![令牌](./media/token.png) + +- 输入您的面板并配置 Telegram 机器人设置,如下所示: + ![面板设置](./media/panel-bot-config.png) + +在输入字段编号 3 中输入机器人令牌。 +在输入字段编号 4 中输入用户 ID。具有此 id 的 Telegram 帐户将是机器人管理员。 (您可以输入多个,只需将它们用“ ,”分开即可) + +- 如何获取TG ID? 使用 [bot](https://t.me/useridinfobot), 启动机器人,它会给你 Telegram 用户 ID。 +![用户 ID](./media/user-id.png) + +
+ +## API 路由 + +
+ 点击查看 API 路由 + +#### 使用 + +- `/login` 使用 `POST` 用户名称 & 密码: `{username: '', password: ''}` 登录 +- `/panel/api/inbounds` 以下操作的基础: + +| 方法 | 路径 | 操作 | +| :----: | ---------------------------------- | --------------------------------- | +| `GET` | `"/list"` | 获取所有入站 | +| `GET` | `"/get/:id"` | 获取所有入站以及inbound.id | +| `GET` | `"/getClientTraffics/:email"` | 通过电子邮件获取客户端流量 | +| `GET` | `"/createbackup"` | Telegram 机器人向管理员发送备份 | +| `POST` | `"/add"` | 添加入站 | +| `POST` | `"/del/:id"` | 删除入站 | +| `POST` | `"/update/:id"` | 更新入站 | +| `POST` | `"/clientIps/:email"` | 客户端 IP 地址 | +| `POST` | `"/clearClientIps/:email"` | 清除客户端 IP 地址 | +| `POST` | `"/addClient"` | 将客户端添加到入站 | +| `POST` | `"/:id/delClient/:clientId"` | 通过 clientId\* 删除客户端 | +| `POST` | `"/updateClient/:clientId"` | 通过 clientId\* 更新客户端 | +| `POST` | `"/:id/resetClientTraffic/:email"` | 重置客户端的流量 | +| `POST` | `"/resetAllTraffics"` | 重置所有入站的流量 | +| `POST` | `"/resetAllClientTraffics/:id"` | 重置入站中所有客户端的流量 | +| `POST` | `"/delDepletedClients/:id"` | 删除入站耗尽的客户端 (-1: all) | +| `POST` | `"/onlines"` | 获取在线用户 ( 电子邮件列表 ) | + +\*- `clientId` 项应该使用下列数据 + +- `client.id` VMESS and VLESS +- `client.password` TROJAN +- `client.email` Shadowsocks + + +- [API 文档](https://documenter.getpostman.com/view/16802678/2s9YkgD5jm) +- [Run In Postman](https://app.getpostman.com/run-collection/16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D16802678-1a4c9270-ac77-40ed-959a-7aa56dc4a415%26entityType%3Dcollection%26workspaceId%3D2cd38c01-c851-4a15-a972-f181c23359d9) +
+ +## 环境变量 + +
+ 点击查看 环境变量 + +#### Usage + +| 变量 | Type | 默认 | +| -------------- | :--------------------------------------------: | :------------ | +| XUI_LOG_LEVEL | `"debug"` \| `"info"` \| `"warn"` \| `"error"` | `"info"` | +| XUI_DEBUG | `boolean` | `false` | +| XUI_BIN_FOLDER | `string` | `"bin"` | +| XUI_DB_FOLDER | `string` | `"/etc/x-ui"` | +| XUI_LOG_FOLDER | `string` | `"/var/log"` | + +例子: + +```sh +XUI_BIN_FOLDER="bin" XUI_DB_FOLDER="/etc/x-ui" go build main.go +``` + +
+ +## 预览 + +![1](./media/1.png) +![2](./media/2.png) +![3](./media/3.png) +![4](./media/4.png) +![5](./media/5.png) +![6](./media/6.png) +![7](./media/7.png) + +## 特别感谢 + +- [alireza0](https://github.com/alireza0/) + +## 致谢 + +- [Iran v2ray rules](https://github.com/chocolate4u/Iran-v2ray-rules) (License: **GPL-3.0**): _Enhanced v2ray/xray and v2ray/xray-clients routing rules with built-in Iranian domains and a focus on security and adblocking._ +- [Vietnam Adblock rules](https://github.com/vuong2023/vn-v2ray-rules) (License: **GPL-3.0**): _A hosted domain hosted in Vietnam and blocklist with the most efficiency for Vietnamese._ + +## Star趋势 + +[![Stargazers over time](https://starchart.cc/xeefei/3x-ui.svg)](https://starchart.cc/xeefei/3x-ui) diff --git a/install.sh b/install.sh index a1398712..63e13c2e 100644 --- a/install.sh +++ b/install.sh @@ -10,7 +10,7 @@ cur_dir=$(pwd) show_ip_service_lists=("https://api.ipify.org" "https://4.ident.me") # 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}致命错误: ${plain} 请使用 root 权限运行此脚本\n" && exit 1 # Check OS and set release variable if [[ -f /etc/os-release ]]; then @@ -20,39 +20,126 @@ elif [[ -f /usr/lib/os-release ]]; then source /usr/lib/os-release release=$ID else - echo "Failed to check the system OS, please contact the author!" >&2 + echo "" + echo -e "${red}检查服务器操作系统失败,请联系作者!${plain}" >&2 exit 1 fi -echo "The OS release is: $release" +echo "" +echo -e "${green}---------->>>>>目前服务器的操作系统为: $release${plain}" arch() { case "$(uname -m)" in - x86_64 | x64 | amd64) echo 'amd64' ;; - i*86 | x86) echo '386' ;; - armv8* | armv8 | arm64 | aarch64) echo 'arm64' ;; - armv7* | armv7 | arm) echo 'armv7' ;; - armv6* | armv6) echo 'armv6' ;; - armv5* | armv5) echo 'armv5' ;; - s390x) echo 's390x' ;; - *) echo -e "${green}Unsupported CPU architecture! ${plain}" && rm -f install.sh && exit 1 ;; + x86_64 | x64 | amd64 ) echo 'amd64' ;; + i*86 | x86 ) echo '386' ;; + armv8* | armv8 | arm64 | aarch64 ) echo 'arm64' ;; + armv7* | armv7 | arm ) echo 'armv7' ;; + armv6* | armv6 ) echo 'armv6' ;; + armv5* | armv5 ) echo 'armv5' ;; + s390x) echo 's390x' ;; + *) echo -e "${green}不支持的CPU架构! ${plain}" && rm -f install.sh && exit 1 ;; esac } -echo "Arch: $(arch)" - +echo "" check_glibc_version() { glibc_version=$(ldd --version | head -n1 | awk '{print $NF}') - + required_version="2.32" if [[ "$(printf '%s\n' "$required_version" "$glibc_version" | sort -V | head -n1)" != "$required_version" ]]; then - echo -e "${red}GLIBC version $glibc_version is too old! Required: 2.32 or higher${plain}" - echo "Please upgrade to a newer version of your operating system to get a higher GLIBC version." + echo -e "${red}------>>>GLIBC版本 $glibc_version 太旧了! 要求2.32或以上版本${plain}" + echo -e "${green}-------->>>>请升级到较新版本的操作系统以便获取更高版本的GLIBC${plain}" exit 1 fi - echo "GLIBC version: $glibc_version (meets requirement of 2.32+)" + echo -e "${green}-------->>>>GLIBC版本: $glibc_version(符合高于2.32的要求)${plain}" } check_glibc_version +echo "" +echo -e "${yellow}---------->>>>>当前系统的架构为: $(arch)${plain}" +echo "" +last_version=$(curl -Ls "https://api.github.com/repos/xeefei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') +# 获取 x-ui 版本 +xui_version=$(/usr/local/x-ui/x-ui -v) + +# 检查 xui_version 是否为空 +if [[ -z "$xui_version" ]]; then + echo "" + echo -e "${red}------>>>当前服务器没有安装任何 x-ui 系列代理面板${plain}" + echo "" + echo -e "${green}-------->>>>片刻之后脚本将会自动引导安装〔3X-UI优化版〕${plain}" +else + # 检查版本号中是否包含冒号 + if [[ "$xui_version" == *:* ]]; then + echo -e "${green}---------->>>>>当前代理面板的版本为: ${red}其他 x-ui 分支版本${plain}" + echo "" + echo -e "${green}-------->>>>片刻之后脚本将会自动引导安装〔3X-UI优化版〕${plain}" + else + echo -e "${green}---------->>>>>当前代理面板的版本为: ${red}〔3X-UI优化版〕v${xui_version}${plain}" + fi +fi +echo "" +echo -e "${yellow}---------------------->>>>>〔3X-UI优化版〕最新版为:${last_version}${plain}" +sleep 4 + +os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1) + +if [[ "${release}" == "arch" ]]; then + echo "您的操作系统是 ArchLinux" +elif [[ "${release}" == "manjaro" ]]; then + echo "您的操作系统是 Manjaro" +elif [[ "${release}" == "armbian" ]]; then + echo "您的操作系统是 Armbian" +elif [[ "${release}" == "alpine" ]]; then + echo "您的操作系统是 Alpine Linux" +elif [[ "${release}" == "opensuse-tumbleweed" ]]; then + echo "您的操作系统是 OpenSUSE Tumbleweed" +elif [[ "${release}" == "centos" ]]; then + if [[ ${os_version} -lt 8 ]]; then + echo -e "${red} 请使用 CentOS 8 或更高版本 ${plain}\n" && exit 1 + fi +elif [[ "${release}" == "ubuntu" ]]; then + if [[ ${os_version} -lt 20 ]]; then + echo -e "${red} 请使用 Ubuntu 20 或更高版本!${plain}\n" && exit 1 + fi +elif [[ "${release}" == "fedora" ]]; then + if [[ ${os_version} -lt 36 ]]; then + echo -e "${red} 请使用 Fedora 36 或更高版本!${plain}\n" && exit 1 + fi +elif [[ "${release}" == "debian" ]]; then + if [[ ${os_version} -lt 11 ]]; then + echo -e "${red} 请使用 Debian 11 或更高版本 ${plain}\n" && exit 1 + fi +elif [[ "${release}" == "almalinux" ]]; then + if [[ ${os_version} -lt 9 ]]; then + echo -e "${red} 请使用 AlmaLinux 9 或更高版本 ${plain}\n" && exit 1 + fi +elif [[ "${release}" == "rocky" ]]; then + if [[ ${os_version} -lt 9 ]]; then + echo -e "${red} 请使用 RockyLinux 9 或更高版本 ${plain}\n" && exit 1 + fi +elif [[ "${release}" == "oracle" ]]; then + if [[ ${os_version} -lt 8 ]]; then + echo -e "${red} 请使用 Oracle Linux 8 或更高版本 ${plain}\n" && exit 1 + fi +else + echo -e "${red}此脚本不支持您的操作系统。${plain}\n" + echo "请确保您使用的是以下受支持的操作系统之一:" + echo "- Ubuntu 20.04+" + echo "- Debian 11+" + echo "- CentOS 8+" + echo "- Fedora 36+" + echo "- Arch Linux" + echo "- Manjaro" + echo "- Armbian" + echo "- Alpine Linux" + echo "- AlmaLinux 9+" + echo "- Rocky Linux 9+" + echo "- Oracle Linux 8+" + echo "- OpenSUSE Tumbleweed" + exit 1 + +fi + install_base() { case "${release}" in ubuntu | debian | armbian) @@ -67,6 +154,9 @@ install_base() { arch | manjaro | parch) pacman -Syu && pacman -Syu --noconfirm wget curl tar tzdata ;; + alpine) + apk update && apk add --no-cache wget curl tar tzdata + ;; opensuse-tumbleweed) zypper refresh && zypper -q install -y wget curl tar timezone ;; @@ -82,100 +172,105 @@ gen_random_string() { echo "$random_string" } +# This function will be called when user installed x-ui out of security config_after_install() { - local existing_hasDefaultCredential=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'hasDefaultCredential: .+' | awk '{print $2}') - local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}') - local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}') - - for ip_service_addr in "${show_ip_service_lists[@]}"; do - local server_ip=$(curl -s --max-time 3 ${ip_service_addr} 2>/dev/null) - if [ -n "${server_ip}" ]; then - break - fi - done - - if [[ ${#existing_webBasePath} -lt 4 ]]; then - if [[ "$existing_hasDefaultCredential" == "true" ]]; then - local config_webBasePath=$(gen_random_string 18) - local config_username=$(gen_random_string 10) - local config_password=$(gen_random_string 10) - - read -rp "Would you like to customize the Panel Port settings? (If not, a random port will be applied) [y/n]: " config_confirm - if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then - read -rp "Please set up the panel port: " config_port - echo -e "${yellow}Your Panel Port is: ${config_port}${plain}" - else - local config_port=$(shuf -i 1024-62000 -n 1) - echo -e "${yellow}Generated random port: ${config_port}${plain}" - fi - - /usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" -port "${config_port}" -webBasePath "${config_webBasePath}" - echo -e "This is a fresh installation, generating random login info for security concerns:" - echo -e "###############################################" - echo -e "${green}Username: ${config_username}${plain}" - echo -e "${green}Password: ${config_password}${plain}" - echo -e "${green}Port: ${config_port}${plain}" - echo -e "${green}WebBasePath: ${config_webBasePath}${plain}" - echo -e "${green}Access URL: http://${server_ip}:${config_port}/${config_webBasePath}${plain}" - echo -e "###############################################" - else - local config_webBasePath=$(gen_random_string 18) - echo -e "${yellow}WebBasePath is missing or too short. Generating a new one...${plain}" - /usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}" - echo -e "${green}New WebBasePath: ${config_webBasePath}${plain}" - echo -e "${green}Access URL: http://${server_ip}:${existing_port}/${config_webBasePath}${plain}" - fi + echo -e "${yellow}安装/更新完成! 为了您的面板安全,建议修改面板设置 ${plain}" + echo "" + read -p "$(echo -e "${green}想继续修改吗?${red}选择“n”以保留旧设置${plain} [y/n]?--->>请输入:")" config_confirm + if [[ "${config_confirm}" == "y" || "${config_confirm}" == "Y" ]]; then + read -p "请设置您的用户名: " config_account + echo -e "${yellow}您的用户名将是: ${config_account}${plain}" + read -p "请设置您的密码: " config_password + echo -e "${yellow}您的密码将是: ${config_password}${plain}" + read -p "请设置面板端口: " config_port + echo -e "${yellow}您的面板端口号为: ${config_port}${plain}" + read -p "请设置面板登录访问路径(访问方式演示:ip:端口号/路径/): " config_webBasePath + echo -e "${yellow}您的面板访问路径为: ${config_webBasePath}${plain}" + echo -e "${yellow}正在初始化,请稍候...${plain}" + /usr/local/x-ui/x-ui setting -username ${config_account} -password ${config_password} + echo -e "${yellow}用户名和密码设置成功!${plain}" + /usr/local/x-ui/x-ui setting -port ${config_port} + echo -e "${yellow}面板端口号设置成功!${plain}" + /usr/local/x-ui/x-ui setting -webBasePath ${config_webBasePath} + echo -e "${yellow}面板登录访问路径设置成功!${plain}" + echo "" else - if [[ "$existing_hasDefaultCredential" == "true" ]]; then - local config_username=$(gen_random_string 10) - local config_password=$(gen_random_string 10) - - echo -e "${yellow}Default credentials detected. Security update required...${plain}" - /usr/local/x-ui/x-ui setting -username "${config_username}" -password "${config_password}" - echo -e "Generated new random login credentials:" + echo "" + sleep 1 + echo -e "${red}--------------->>>>Cancel...--------------->>>>>>>取消修改...${plain}" + echo "" + if [[ ! -f "/etc/x-ui/x-ui.db" ]]; then + local usernameTemp=$(head -c 6 /dev/urandom | base64) + local passwordTemp=$(head -c 6 /dev/urandom | base64) + local webBasePathTemp=$(gen_random_string 10) + /usr/local/x-ui/x-ui setting -username ${usernameTemp} -password ${passwordTemp} -webBasePath ${webBasePathTemp} + echo -e "${yellow}检测到为全新安装,出于安全考虑将生成随机登录信息:${plain}" echo -e "###############################################" - echo -e "${green}Username: ${config_username}${plain}" - echo -e "${green}Password: ${config_password}${plain}" + echo -e "${green}用户名: ${usernameTemp}${plain}" + echo -e "${green}密 码: ${passwordTemp}${plain}" + echo -e "${green}访问路径: ${webBasePathTemp}${plain}" echo -e "###############################################" + echo -e "${green}如果您忘记了登录信息,可以在安装后通过 x-ui 命令然后输入${red}数字 10 选项${green}进行查看${plain}" else - echo -e "${green}Username, Password, and WebBasePath are properly set. Exiting...${plain}" + echo -e "${green}此次操作属于版本升级,保留之前旧设置项,登录方式保持不变${plain}" + echo "" + echo -e "${green}如果您忘记了登录信息,您可以通过 x-ui 命令然后输入${red}数字 10 选项${green}进行查看${plain}" + echo "" + echo "" fi fi - + sleep 1 + echo -e ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" + echo "" /usr/local/x-ui/x-ui migrate } +echo "" install_x-ui() { cd /usr/local/ # Download resources if [ $# == 0 ]; then - 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 "${red}Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later${plain}" + last_version=$(curl -Ls "https://api.github.com/repos/xeefei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + if [[ ! -n "$last_version" ]]; then + echo -e "${red}获取 3x-ui 版本失败,可能是 Github API 限制,请稍后再试${plain}" exit 1 fi - echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..." - wget -N -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz + echo "" + echo -e "-----------------------------------------------------" + echo -e "${green}--------->>获取 3x-ui 最新版本:${yellow}${last_version}${plain}${green},开始安装...${plain}" + echo -e "-----------------------------------------------------" + echo "" + sleep 2 + echo -e "${green}---------------->>>>>>>>>安装进度50%${plain}" + sleep 3 + echo "" + echo -e "${green}---------------->>>>>>>>>>>>>>>>>>>>>安装进度100%${plain}" + echo "" + sleep 2 + wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch).tar.gz https://github.com/xeefei/3x-ui/releases/download/${last_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}" + echo -e "${red}下载 3x-ui 失败, 请检查服务器是否可以连接至 GitHub? ${plain}" exit 1 fi else - tag_version=$1 - tag_version_numeric=${tag_version#v} - min_version="2.3.5" - - if [[ "$(printf '%s\n' "$min_version" "$tag_version_numeric" | sort -V | head -n1)" != "$min_version" ]]; then - echo -e "${red}Please use a newer version (at least v2.3.5). Exiting installation.${plain}" - exit 1 - fi - - url="https://github.com/MHSanaei/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz" - echo -e "Beginning to install x-ui $1" - wget -N -O /usr/local/x-ui-linux-$(arch).tar.gz ${url} + last_version=$1 + url="https://github.com/xeefei/3x-ui/releases/download/${last_version}/x-ui-linux-$(arch).tar.gz" + echo "" + echo -e "--------------------------------------------" + echo -e "${green}---------------->>>>开始安装 3x-ui $1${plain}" + echo -e "--------------------------------------------" + echo "" + sleep 2 + echo -e "${green}---------------->>>>>>>>>安装进度50%${plain}" + sleep 3 + echo "" + echo -e "${green}---------------->>>>>>>>>>>>>>>>>>>>>安装进度100%${plain}" + echo "" + sleep 2 + wget -N --no-check-certificate -O /usr/local/x-ui-linux-$(arch).tar.gz ${url} if [[ $? -ne 0 ]]; then - echo -e "${red}Download x-ui $1 failed, please check if the version exists ${plain}" + echo -e "${red}下载 3x-ui $1 失败, 请检查此版本是否存在 ${plain}" exit 1 fi fi @@ -186,8 +281,10 @@ install_x-ui() { systemctl stop x-ui rm /usr/local/x-ui/ -rf fi - - # Extract resources and set permissions + + sleep 3 + echo -e "${green}------->>>>>>>>>>>检查并保存安装目录${plain}" + echo "" tar zxvf x-ui-linux-$(arch).tar.gz rm x-ui-linux-$(arch).tar.gz -f @@ -205,34 +302,82 @@ install_x-ui() { # 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 + sleep 2 + echo -e "${green}------->>>>>>>>>>>保存成功${plain}" + sleep 2 + echo "" config_after_install cp -f x-ui.service /etc/systemd/system/ systemctl daemon-reload systemctl enable x-ui systemctl start x-ui - echo -e "${green}x-ui ${tag_version}${plain} installation finished, it is running now..." - echo -e "" - echo -e "┌───────────────────────────────────────────────────────┐ -│ ${blue}x-ui control menu usages (subcommands):${plain} │ -│ │ -│ ${blue}x-ui${plain} - Admin Management Script │ -│ ${blue}x-ui start${plain} - Start │ -│ ${blue}x-ui stop${plain} - Stop │ -│ ${blue}x-ui restart${plain} - Restart │ -│ ${blue}x-ui status${plain} - Current Status │ -│ ${blue}x-ui settings${plain} - Current Settings │ -│ ${blue}x-ui enable${plain} - Enable Autostart on OS Startup │ -│ ${blue}x-ui disable${plain} - Disable Autostart on OS Startup │ -│ ${blue}x-ui log${plain} - Check logs │ -│ ${blue}x-ui banlog${plain} - Check Fail2ban ban logs │ -│ ${blue}x-ui update${plain} - Update │ -│ ${blue}x-ui legacy${plain} - legacy version │ -│ ${blue}x-ui install${plain} - Install │ -│ ${blue}x-ui uninstall${plain} - Uninstall │ -└───────────────────────────────────────────────────────┘" -} + systemctl stop warp-go >/dev/null 2>&1 + wg-quick down wgcf >/dev/null 2>&1 + ipv4=$(curl -s4m8 ip.p3terx.com -k | sed -n 1p) + ipv6=$(curl -s6m8 ip.p3terx.com -k | sed -n 1p) + systemctl start warp-go >/dev/null 2>&1 + wg-quick up wgcf >/dev/null 2>&1 -echo -e "${green}Running...${plain}" + echo "" + echo -e "------->>>>${green}3x-ui ${last_version}${plain}<<<<安装成功,正在启动..." + sleep 1 + echo "" + echo -e " ---------------------" + echo -e " |${green}3X-UI 控制菜单用法 ${plain}|${plain}" + echo -e " | ${yellow}一个更好的面板 ${plain}|${plain}" + echo -e " | ${yellow}基于Xray Core构建 ${plain}|${plain}" + echo -e "--------------------------------------------" + echo -e "x-ui - 进入管理脚本" + echo -e "x-ui start - 启动 3x-ui 面板" + echo -e "x-ui stop - 关闭 3x-ui 面板" + echo -e "x-ui restart - 重启 3x-ui 面板" + echo -e "x-ui status - 查看 3x-ui 状态" + echo -e "x-ui settings - 查看当前设置信息" + echo -e "x-ui enable - 启用 3x-ui 开机启动" + echo -e "x-ui disable - 禁用 3x-ui 开机启动" + echo -e "x-ui log - 查看 3x-ui 运行日志" + echo -e "x-ui banlog - 检查 Fail2ban 禁止日志" + echo -e "x-ui update - 更新 3x-ui 面板" + echo -e "x-ui custom - 自定义 3x-ui 版本" + echo -e "x-ui install - 安装 3x-ui 面板" + echo -e "x-ui uninstall - 卸载 3x-ui 面板" + echo -e "--------------------------------------------" + echo "" + # if [[ -n $ipv4 ]]; then + # echo -e "${yellow}面板 IPv4 访问地址为:${green}http://$ipv4:${config_port}/${config_webBasePath}${plain}" + # fi + # if [[ -n $ipv6 ]]; then + # echo -e "${yellow}面板 IPv6 访问地址为:${green}http://[$ipv6]:${config_port}/${config_webBasePath}${plain}" + # fi + # echo -e "请自行确保此端口没有被其他程序占用,${yellow}并且确保${red} ${config_port} ${yellow}端口已放行${plain}" + sleep 3 + echo -e ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" + echo "" + echo -e "${yellow}----->>>3X-UI面板和Xray启动成功<<<-----${plain}" +} install_base install_x-ui $1 +echo "" +echo -e "----------------------------------------------" +sleep 4 +info=$(/usr/local/x-ui/x-ui setting -show true) +echo -e "${info}${plain}" +echo "" +echo -e "若您忘记了上述面板信息,后期可通过x-ui命令进入脚本${red}输入数字〔10〕选项获取${plain}" +echo "" +echo -e "----------------------------------------------" +echo "" +sleep 2 +echo -e "${green}安装/更新完成,若在使用过程中有任何问题${plain}" +echo -e "${yellow}请先描述清楚所遇问题加〔3X-UI〕中文交流群${plain}" +echo -e "${yellow}在TG群中${red} https://t.me/XUI_CN ${yellow}截图进行反馈${plain}" +echo "" +echo -e "----------------------------------------------" +echo "" +echo -e "${green}〔3X-UI〕优化版项目地址:${yellow}https://github.com/xeefei/3x-ui${plain}" +echo "" +echo -e "${green} 详细安装教程:${yellow}https://xeefei.github.io/xufei/2024/05/3x-ui/${plain}" +echo "" +echo -e "----------------------------------------------" +echo "" diff --git a/main.go b/main.go index 9986ede1..3a87b63c 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,8 @@ import ( "log" "os" "os/signal" + "os/exec" + "strings" "syscall" _ "unsafe" @@ -121,23 +123,47 @@ func resetSetting() { settingService := service.SettingService{} err = settingService.ResetSettings() if err != nil { - fmt.Println("Failed to reset settings:", err) + fmt.Println("Failed to reset settings(重置设置失败):", err) } else { - fmt.Println("Settings successfully reset.") + fmt.Println("Settings successfully reset ---->>重置设置成功") } } func showSetting(show bool) { + // 执行 shell 命令获取 IPv4 地址 + cmdIPv4 := exec.Command("sh", "-c", "curl -s4m8 ip.p3terx.com -k | sed -n 1p") + outputIPv4, err := cmdIPv4.Output() + if err != nil { + log.Fatal(err) + } + + // 执行 shell 命令获取 IPv6 地址 + cmdIPv6 := exec.Command("sh", "-c", "curl -s6m8 ip.p3terx.com -k | sed -n 1p") + outputIPv6, err := cmdIPv6.Output() + if err != nil { + log.Fatal(err) + } + + // 去除命令输出中的换行符 + ipv4 := strings.TrimSpace(string(outputIPv4)) + ipv6 := strings.TrimSpace(string(outputIPv6)) + // 定义转义字符,定义不同颜色的转义字符 + const ( + Reset = "\033[0m" + Red = "\033[31m" + Green = "\033[32m" + Yellow = "\033[33m" + ) if show { settingService := service.SettingService{} port, err := settingService.GetPort() if err != nil { - fmt.Println("get current port failed, error info:", err) + fmt.Println("get current port failed, error info(获取当前端口失败,错误信息):", err) } webBasePath, err := settingService.GetBasePath() if err != nil { - fmt.Println("get webBasePath failed, error info:", err) + fmt.Println("get webBasePath failed, error info(获取访问路径失败,错误信息):", err) } certFile, err := settingService.GetCertFile() @@ -152,28 +178,77 @@ func showSetting(show bool) { userService := service.UserService{} userModel, err := userService.GetFirstUser() if err != nil { - fmt.Println("get current user info failed, error info:", err) + fmt.Println("get current user info failed, error info(获取当前用户信息失败,错误信息):", err) } if userModel.Username == "" || userModel.Password == "" { - fmt.Println("current username or password is empty") + fmt.Println("current username or password is empty --->>当前用户名或密码为空") } - fmt.Println("current panel settings as follows:") + fmt.Println("") + fmt.Println(Yellow + "----->>>以下为面板重要信息,请自行记录保存<<<-----" + Reset) + fmt.Println(Green + "Current panel settings as follows (当前面板设置如下):" + Reset) + fmt.Println("") if certFile == "" || keyFile == "" { - fmt.Println("Warning: Panel is not secure with SSL") + fmt.Println(Red + "------>> 警告:面板未安装证书进行SSL保护" + Reset) } else { - fmt.Println("Panel is secure with SSL") + fmt.Println(Green + "------>> 面板已安装证书采用SSL保护" + Reset) } - + fmt.Println("") hasDefaultCredential := func() bool { return userModel.Username == "admin" && crypto.CheckPasswordHash(userModel.Password, "admin") }() + if hasDefaultCredential == true { + fmt.Println(Red + "------>> 警告:使用了默认的admin账号/密码,容易被扫描" + Reset) + } else { + fmt.Println(Green + "------>> 为非默认admin账号/密码,请牢记" + Reset) + } + fmt.Println("") + fmt.Println(Green + fmt.Sprintf("port(端口号): %d", port) + Reset) + fmt.Println(Green + fmt.Sprintf("webBasePath(访问路径): %s", webBasePath) + Reset) + fmt.Println(Green + "PS:为安全起见,不显示账号和密码" + Reset) + fmt.Println(Green + "若您已经忘记账号/密码,请用脚本选项〔6〕重新设置" + Reset) - fmt.Println("hasDefaultCredential:", hasDefaultCredential) - fmt.Println("port:", port) - fmt.Println("webBasePath:", webBasePath) + fmt.Println("") + fmt.Println("--------------------------------------------------") + // 根据条件打印带颜色的字符串 + if ipv4 != "" { + fmt.Println("") + formattedIPv4 := fmt.Sprintf("%s %s%s:%d%s" + Reset, + Green+"面板 IPv4 访问地址------>>", + Yellow+"http://", + ipv4, + port, + Yellow+webBasePath + Reset) + fmt.Println(formattedIPv4) + fmt.Println("") } + + if ipv6 != "" { + fmt.Println("") + formattedIPv6 := fmt.Sprintf("%s %s[%s%s%s]:%d%s%s", + Green+"面板 IPv6 访问地址------>>", // 绿色的提示信息 + Yellow+"http://", // 黄色的 http:// 部分 + Yellow, // 黄色的[ 左方括号 + ipv6, // IPv6 地址 + Yellow, // 黄色的] 右方括号 + port, // 端口号 + Yellow+webBasePath, // 黄色的 Web 基础路径 + Reset) // 重置颜色 + fmt.Println(formattedIPv6) + fmt.Println("") + } + fmt.Println(Green + ">>>>>>>>注:若您安装了〔证书〕,请把IP换成您的域名用https方式登录" + Reset) + fmt.Println("") + fmt.Println("--------------------------------------------------") + fmt.Println("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑") + fmt.Println(fmt.Sprintf("%s请确保 %s%d%s 端口已打开放行%s",Green, Red, port, Green, Reset)) + fmt.Println("请自行确保此端口没有被其他程序占用") + fmt.Println(Green + "若要登录访问面板,请复制上面的地址到浏览器" + Reset) + fmt.Println("") + fmt.Println("--------------------------------------------------") + fmt.Println("") + } } func updateTgbotEnableSts(status bool) { @@ -198,7 +273,7 @@ func updateTgbotEnableSts(status bool) { func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime string) { err := database.InitDB(config.GetDBPath()) if err != nil { - fmt.Println("Error initializing database:", err) + fmt.Println("Error initializing database(初始化数据库出错):", err) return } @@ -207,35 +282,35 @@ func updateTgbotSetting(tgBotToken string, tgBotChatid string, tgBotRuntime stri if tgBotToken != "" { err := settingService.SetTgBotToken(tgBotToken) if err != nil { - fmt.Printf("Error setting Telegram bot token: %v\n", err) + fmt.Printf("Error setting Telegram bot token(设置TG电报机器人令牌出错): %v\n", err) return } - logger.Info("Successfully updated Telegram bot token.") + logger.Info("Successfully updated Telegram bot token ----->>已成功更新TG电报机器人令牌") } if tgBotRuntime != "" { err := settingService.SetTgbotRuntime(tgBotRuntime) if err != nil { - fmt.Printf("Error setting Telegram bot runtime: %v\n", err) + fmt.Printf("Error setting Telegram bot runtime(设置TG电报机器人通知周期出错): %v\n", err) return } - logger.Infof("Successfully updated Telegram bot runtime to [%s].", tgBotRuntime) + logger.Infof("Successfully updated Telegram bot runtime to (已成功将TG电报机器人通知周期设置为) [%s].", tgBotRuntime) } if tgBotChatid != "" { err := settingService.SetTgBotChatId(tgBotChatid) if err != nil { - fmt.Printf("Error setting Telegram bot chat ID: %v\n", err) + fmt.Printf("Error setting Telegram bot chat ID(设置TG电报机器人管理者聊天ID出错): %v\n", err) return } - logger.Info("Successfully updated Telegram bot chat ID.") + logger.Info("Successfully updated Telegram bot chat ID ----->>已成功更新TG电报机器人管理者聊天ID") } } func updateSetting(port int, username string, password string, webBasePath string, listenIP string, resetTwoFactor bool) { err := database.InitDB(config.GetDBPath()) if err != nil { - fmt.Println("Database initialization failed:", err) + fmt.Println("Database initialization failed(初始化数据库失败):", err) return } @@ -245,27 +320,27 @@ func updateSetting(port int, username string, password string, webBasePath strin if port > 0 { err := settingService.SetPort(port) if err != nil { - fmt.Println("Failed to set port:", err) + fmt.Println("Failed to set port(设置端口失败):", err) } else { - fmt.Printf("Port set successfully: %v\n", port) + fmt.Printf("Port set successfully(端口设置成功): %v\n", port) } } if username != "" || password != "" { err := userService.UpdateFirstUser(username, password) if err != nil { - fmt.Println("Failed to update username and password:", err) + fmt.Println("Failed to update username and password(更新用户名和密码失败):", err) } else { - fmt.Println("Username and password updated successfully") + fmt.Println("Username and password updated successfully ------>>用户名和密码更新成功") } } if webBasePath != "" { err := settingService.SetBasePath(webBasePath) if err != nil { - fmt.Println("Failed to set base URI path:", err) + fmt.Println("Failed to set base URI path(设置访问路径失败):", err) } else { - fmt.Println("Base URI path set successfully") + fmt.Println("Base URI path set successfully ------>>设置访问路径成功") } } @@ -273,19 +348,19 @@ func updateSetting(port int, username string, password string, webBasePath strin err := settingService.SetTwoFactorEnable(false) if err != nil { - fmt.Println("Failed to reset two-factor authentication:", err) + fmt.Println("Failed to reset two-factor authentication(设置两步验证失败):", err) } else { settingService.SetTwoFactorToken("") - fmt.Println("Two-factor authentication reset successfully") + fmt.Println("Two-factor authentication reset successfully --------->>设置两步验证成功") } } if listenIP != "" { err := settingService.SetListen(listenIP) if err != nil { - fmt.Println("Failed to set listen IP:", err) + fmt.Println("Failed to set listen IP(设置监听IP失败):", err) } else { - fmt.Printf("listen %v set successfully", listenIP) + fmt.Printf("listen %v set successfully --------->>设置监听IP成功", listenIP) } } } @@ -301,19 +376,19 @@ func updateCert(publicKey string, privateKey string) { settingService := service.SettingService{} err = settingService.SetCertFile(publicKey) if err != nil { - fmt.Println("set certificate public key failed:", err) + fmt.Println("set certificate public key failed(设置证书公钥失败):", err) } else { - fmt.Println("set certificate public key success") + fmt.Println("set certificate public key success --------->>设置证书公钥成功") } err = settingService.SetKeyFile(privateKey) if err != nil { - fmt.Println("set certificate private key failed:", err) + fmt.Println("set certificate private key failed(设置证书私钥失败):", err) } else { - fmt.Println("set certificate private key success") + fmt.Println("set certificate private key success --------->>设置证书私钥成功") } } else { - fmt.Println("both public and private key should be entered.") + fmt.Println("both public and private key should be entered ------>>必须同时输入证书公钥和私钥") } } @@ -355,9 +430,9 @@ func migrateDb() { if err != nil { log.Fatal(err) } - fmt.Println("Start migrating database...") + fmt.Println("Start migrating database... ---->>开始迁移数据库...") inboundService.MigrateDB() - fmt.Println("Migration done!") + fmt.Println("Migration done! ------------>>迁移完成!") } func main() { @@ -469,7 +544,7 @@ func main() { updateCert(webCertFile, webKeyFile) } default: - fmt.Println("Invalid subcommands") + fmt.Println("Invalid subcommands ----->>无效命令") fmt.Println() runCmd.Usage() fmt.Println() diff --git a/media/1.png b/media/1.png new file mode 100644 index 00000000..c2ca585f Binary files /dev/null and b/media/1.png differ diff --git a/media/10.png b/media/10.png new file mode 100644 index 00000000..f8012493 Binary files /dev/null and b/media/10.png differ diff --git a/media/11.png b/media/11.png new file mode 100644 index 00000000..f1e51635 Binary files /dev/null and b/media/11.png differ diff --git a/media/12.png b/media/12.png new file mode 100644 index 00000000..e0bb2d1d Binary files /dev/null and b/media/12.png differ diff --git a/media/13.png b/media/13.png new file mode 100644 index 00000000..4bb16b21 Binary files /dev/null and b/media/13.png differ diff --git a/media/14.png b/media/14.png new file mode 100644 index 00000000..9dd5b052 Binary files /dev/null and b/media/14.png differ diff --git a/media/15.png b/media/15.png new file mode 100644 index 00000000..d5165f19 Binary files /dev/null and b/media/15.png differ diff --git a/media/16.png b/media/16.png new file mode 100644 index 00000000..ccc279ab Binary files /dev/null and b/media/16.png differ diff --git a/media/17.png b/media/17.png new file mode 100644 index 00000000..5fd2713d Binary files /dev/null and b/media/17.png differ diff --git a/media/18.png b/media/18.png new file mode 100644 index 00000000..3c92b90e Binary files /dev/null and b/media/18.png differ diff --git a/media/19.png b/media/19.png new file mode 100644 index 00000000..6a93e9a6 Binary files /dev/null and b/media/19.png differ diff --git a/media/2.png b/media/2.png new file mode 100644 index 00000000..32f78193 Binary files /dev/null and b/media/2.png differ diff --git a/media/20.png b/media/20.png new file mode 100644 index 00000000..c65047d6 Binary files /dev/null and b/media/20.png differ diff --git a/media/21.png b/media/21.png new file mode 100644 index 00000000..2ffae772 Binary files /dev/null and b/media/21.png differ diff --git a/media/22.png b/media/22.png new file mode 100644 index 00000000..d3e55734 Binary files /dev/null and b/media/22.png differ diff --git a/media/23.png b/media/23.png new file mode 100644 index 00000000..eaee47bc Binary files /dev/null and b/media/23.png differ diff --git a/media/24.png b/media/24.png new file mode 100644 index 00000000..0bd08b67 Binary files /dev/null and b/media/24.png differ diff --git a/media/25.png b/media/25.png new file mode 100644 index 00000000..1c4ed00a Binary files /dev/null and b/media/25.png differ diff --git a/media/26.png b/media/26.png new file mode 100644 index 00000000..3e8d98d0 Binary files /dev/null and b/media/26.png differ diff --git a/media/27.png b/media/27.png new file mode 100644 index 00000000..cd9c94b2 Binary files /dev/null and b/media/27.png differ diff --git a/media/3.png b/media/3.png new file mode 100644 index 00000000..d97e4ffd Binary files /dev/null and b/media/3.png differ diff --git a/media/3X-UI.png b/media/3X-UI.png new file mode 100644 index 00000000..7aa7c3ef Binary files /dev/null and b/media/3X-UI.png differ diff --git a/media/4.png b/media/4.png new file mode 100644 index 00000000..3762a345 Binary files /dev/null and b/media/4.png differ diff --git a/media/5.png b/media/5.png new file mode 100644 index 00000000..a18cb4b3 Binary files /dev/null and b/media/5.png differ diff --git a/media/6.png b/media/6.png new file mode 100644 index 00000000..704726bf Binary files /dev/null and b/media/6.png differ diff --git a/media/7.png b/media/7.png new file mode 100644 index 00000000..b3cb8ebd Binary files /dev/null and b/media/7.png differ diff --git a/media/8.png b/media/8.png new file mode 100644 index 00000000..23cde0fd Binary files /dev/null and b/media/8.png differ diff --git a/media/9.png b/media/9.png new file mode 100644 index 00000000..b5b510c9 Binary files /dev/null and b/media/9.png differ diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js index 56c20b69..284d3d13 100644 --- a/web/assets/js/model/inbound.js +++ b/web/assets/js/model/inbound.js @@ -725,8 +725,8 @@ class RealityStreamSettings extends XrayCommonClass { constructor( show = false, xver = 0, - dest = 'yahoo.com:443', - serverNames = 'yahoo.com,www.yahoo.com', + dest = 'tesla.com:443', + serverNames = 'tesla.com,www.tesla.com', privateKey = '', minClient = '', maxClient = '', diff --git a/web/controller/xui.go b/web/controller/xui.go index 5b4c0a18..568e4a26 100644 --- a/web/controller/xui.go +++ b/web/controller/xui.go @@ -26,6 +26,7 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) { g.GET("/inbounds", a.inbounds) g.GET("/settings", a.settings) g.GET("/xray", a.xraySettings) + g.GET("/navigation", a.navigation) a.inboundController = NewInboundController(g) a.settingController = NewSettingController(g) @@ -47,3 +48,7 @@ func (a *XUIController) settings(c *gin.Context) { func (a *XUIController) xraySettings(c *gin.Context) { html(c, "xray.html", "pages.xray.title", nil) } + +func (a *XUIController) navigation(c *gin.Context) { + html(c, "navigation.html", "pages.navigation.title", nil) +} diff --git a/web/html/component/aSidebar.html b/web/html/component/aSidebar.html index b69c8f3f..6ecd8dbc 100644 --- a/web/html/component/aSidebar.html +++ b/web/html/component/aSidebar.html @@ -64,6 +64,11 @@ icon: 'tool', title: '{{ i18n "menu.xray"}}' }, + { + key: '{{ .base_path }}panel/navigation', + icon: 'link', + title: '{{ i18n "menu.navigation"}}' + }, { key: '{{ .base_path }}logout/', icon: 'logout', @@ -100,4 +105,4 @@ template: `{{template "component/sidebar/content"}}`, }); -{{end}} \ No newline at end of file +{{end}} diff --git a/web/html/form/reality_settings.html b/web/html/form/reality_settings.html index aebb6c42..1cf98fa7 100644 --- a/web/html/form/reality_settings.html +++ b/web/html/form/reality_settings.html @@ -50,7 +50,7 @@ - Get New Cert + Get New Cert(随机获取新证书) @@ -62,4 +62,4 @@ Get New Seed -{{end}} \ No newline at end of file +{{end}} diff --git a/web/html/index.html b/web/html/index.html index db678cd6..028bf73a 100644 --- a/web/html/index.html +++ b/web/html/index.html @@ -215,20 +215,20 @@ - - + + v{{ .cur_ver }} - + - @XrayUI + TG私聊交流 - + - {{ i18n "pages.index.documentation" }} + 〔3X-UI〕中文交流群 @@ -249,6 +249,8 @@ + 端口检测 + 网络测速 @@ -784,4 +786,4 @@ }, }); -{{ template "page/body_end" .}} \ No newline at end of file +{{ template "page/body_end" .}} diff --git a/web/html/navigation.html b/web/html/navigation.html new file mode 100644 index 00000000..ffd46990 --- /dev/null +++ b/web/html/navigation.html @@ -0,0 +1,165 @@ + + + + + + 实用导航&技巧 + + + + +
+
+

一、【3x-ui】中文交流群:https://t.me/XUI_CN

+

【3x-ui】详细安装流程步骤:https://xeefei.github.io/xufei/2024/05/3x-ui/

+ +

二、判断VPS服务器的IP是否【送中】?

+

***点击打开:https://music.youtube.com/,能正常打开访问,就代表【没送中】,反之就是送中了。

+

***如果送中了如何解决去【拉回来】?

+

1:关闭/取消登录了谷歌账户的APP定位权限/授权;2:将常用的一个谷歌账号的位置记录功能打开;3:在电脑上打开Chrome/谷歌浏览器,登录开了位置记录功能的谷歌账号,安装Location Guard拓展插件https://chrome.google.com/webstore/detail/location-guard/cfohepagpmnodfdmjliccbbigdkfcgia(也可在其他支持此插件的浏览器使用);4:打开Location Guard插件,选择Fixed Location,并在给出的地图上单击,即可标记上你想要IP所处的国家/地区 +Google IP定位错误,使用Location Guard修改;5:转换到Options选项,Default level默认设置为Use fixed location;6:打开谷歌地图google.com/maps,点击右下角定位授权图标,使google maps获取当前“我的GPS位置” +Google IP定位错误,使用Location Guard修改GPS位置地址;7:谷歌搜索my ip,即可看到谷歌IP定位到了刚才地图上标记的位置;8:在此网页向谷歌报告IP问题:https://support.google.com/websearch/workflow/9308722

+ +

三、在自己的VPS服务器部署【订阅转换】功能

+

如何把vless/vmess等协议转换成Clash/Surge等软件支持的格式? + 1、进入脚本输入x-ui命令调取面板,选择第【24】选项安装订阅转换模块, + 2、等待安装【订阅转换】成功之后,访问地址:你的IP:18080(端口号)进行转换, + 3、因为在转换过程中需要调取后端API,所以请确保端口25500是打开放行的, +4、在得到【转换链接】之后,只要你的VPS服务器25500端口是能ping通的,就能导入Clash/Surge等软件成功下载配置, +5、此功能集成到3x-ui面板中,是为了保证安全,通过调取24选项把【订阅转换】功能部署在自己的VPS中,不会造成链接泄露。

+ +

四、如何保护自己的IP不被墙被封?

+

1、使用的代理协议要安全,加密是必备,推荐使用vless+reality+vision协议组合, +2、因为有时节点会共享,在不同的地区,多个省份之间不要共同连接同一个IP, +3、连接同一个IP就算了,不要同一个端口,不要同IP+同端口到处漫游,要分开, +4、同一台VPS,不要在一天内一直大流量去下载东西使用,不要流量过高要切换, +5、创建【入站协议】的时候,尽量用【高位端口】,比如40000--65000之间的端口号。 +提醒:为什么在特殊时期,比如:两会,春节等被封得最严重最惨? +尼玛同一个IP+同一个端口号,多个省份去漫游,跟开飞机场一样!不封你,封谁的IP和端口? +总结:不要多终端/多省份/多个朋友/共同使用同一个IP和端口号!使用3x-ui多创建几个【入站】, +多做几条备用,各用各的!各行其道才比较安全!GFW的思维模式是干掉机场,机场的特征个人用户不要去沾染,自然IP就保护好了。

+ +

五、检测IP纯净度的方法:

+

网址:https://scamalytics.com/,输入IP进行检测,看【欺诈分数】,分数越高IP越脏。

+ +

六、常见的软件工具:

+
    +

    1、Windows系统v2rayN:https://github.com/2dust/v2rayN

    +

    2、安卓手机版【v2rayNG】:https://github.com/2dust/v2rayNG

    +

    3、苹果手机IOS【小火箭】:https://apple02.com/

    +

    4、苹果MacOS电脑【Clash Verge】:https://github.com/clash-verge-rev/clash-verge-rev/releases

    +
+ +

七、查看节点【指定端口】的网络连接数/命令:

+

netstat -ntu | grep :节点端口 | grep ESTABLISHED | awk '{print $5}'

+ +

八、用3x-ui如何实现【自己偷自己】?

+

其实很简单,只要你为面板设置了证书, + 开启了HTTPS登录,就可以将3x-ui自身作为web server, + 无需Nginx等,这里给一个示例: + 其中目标网站(Dest)请填写面板监听端口, + 可选域名(SNI)填写面板登录域名, + 如果您使用其他web server(如nginx)等, + 将目标网站改为对应监听端口也可。 + 需要说明的是,如果您处于白名单地区,自己“偷”自己并不适合你;其次,可选域名一项实际上可以填写任意SNI,只要客户端保持一致即可,不过并不推荐这样做。

+ +

九、【接码】网站:

+

网址:https://sms-activate.org/cn,直接注册账号购买。

+ +

十、一些MJJ经常逛的网站和群组:

+
    +

    1、NodeSeek论坛:https://www.nodeseek.com/

    +

    2、V2EX论坛:https://www.v2ex.com/

    +

    3、搬瓦工TG群:https://t.me/BWHOfficial

    +

    4、Xray的官方群:https://t.me/projectXray

    +

    5、Dmit交流群:https://t.me/DmitChat

    +

    6、白丝云用户群:https://t.me/+VHZLKELTQyzPNgOV

    +

    7、NameSilo域名注册:https://www.namesilo.com/

    +
+ +

十一、若此项目对你有帮助,你正想购买VPS的话,可以走一下我的AFF:

+
    +

    1、搬瓦工GIA线路:https://bandwagonhost.com/aff.php?aff=75015

    +

    2、Dmit高端GIA:https://www.dmit.io/aff.php?aff=9326

    +

    3、白丝云【4837】:https://cloudsilk.io/aff.php?aff=706

    +
+ +

十二、项目〔声明和注意〕

+
    +

    1、声明: 此项目仅供个人学习、交流使用,请遵守当地法律法规,勿用于非法用途;请勿用于生产环境;

    +

    2、注意: 在使用此项目和〔教程〕过程中,若因违反以上声明使用规则而产生的一切后果由使用者自负。

    +
+
+ + diff --git a/web/html/settings/panel/general.html b/web/html/settings/panel/general.html index df011521..c2adf570 100644 --- a/web/html/settings/panel/general.html +++ b/web/html/settings/panel/general.html @@ -1,4 +1,10 @@ {{define "settings/panel/general"}} + @@ -97,14 +103,14 @@ @@ -146,4 +152,4 @@ -{{end}} \ No newline at end of file +{{end}} diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 07cfb1af..4e8bb521 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -81,6 +81,7 @@ "xray" = "Xray Configs" "logout" = "Log Out" "link" = "Manage" +"navigation" = "navigation" [pages.login] "hello" = "Hello" @@ -446,6 +447,9 @@ "statsOutboundDownlink" = "Outbound Download Statistics" "statsOutboundDownlinkDesc" = "Enables the statistics collection for downstream traffic of all outbound proxies." +[pages.navigation] +"title" = "navigation" + [pages.xray.rules] "first" = "First" "last" = "Last" diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml index 68e4d1e8..a4cf1b58 100644 --- a/web/translation/translate.zh_CN.toml +++ b/web/translation/translate.zh_CN.toml @@ -54,6 +54,7 @@ "install" = "安装" "clients" = "客户端" "usage" = "使用情况" +"secretToken" = "安全密钥" "twoFactorCode" = "代码" "remained" = "剩余" "security" = "安全" @@ -78,9 +79,10 @@ "dashboard" = "系统状态" "inbounds" = "入站列表" "settings" = "面板设置" -"xray" = "Xray 设置" +"xray" = "Xray设置" "logout" = "退出登录" "link" = "管理" +"navigation" = "实用导航" [pages.login] "hello" = "你好" @@ -102,6 +104,7 @@ "swap" = "交换分区" "storage" = "存储" "memory" = "内存" +"hard" = "磁盘" "threads" = "线程" "xrayStatus" = "Xray" "stopXray" = "停止" @@ -138,7 +141,7 @@ "dontRefresh" = "安装中,请勿刷新此页面" "logs" = "日志" "config" = "配置" -"backup" = "备份" +"backup" = "备份和恢复" "backupTitle" = "备份和恢复数据库" "exportDatabase" = "备份" "exportDatabaseDesc" = "点击下载包含当前数据库备份的 .db 文件到您的设备。" @@ -169,6 +172,8 @@ "generalActions" = "通用操作" "autoRefresh" = "自动刷新" "autoRefreshInterval" = "间隔" +"create" = "添加" +"update" = "修改" "modifyInbound" = "修改入站" "deleteInbound" = "删除入站" "deleteInboundContent" = "确定要删除入站吗?" @@ -211,14 +216,14 @@ "delDepletedClientsTitle" = "删除流量耗尽的客户端" "delDepletedClientsContent" = "确定要删除所有流量耗尽的客户端吗?" "email" = "电子邮件" -"emailDesc" = "电子邮件必须完全唯一" +"emailDesc" = "电子邮件必须确保唯一" "IPLimit" = "IP 限制" "IPLimitDesc" = "如果数量超过设置值,则禁用入站流量。(0 = 禁用)" "IPLimitlog" = "IP 日志" "IPLimitlogDesc" = "IP 历史日志(要启用被禁用的入站流量,请清除日志)" "IPLimitlogclear" = "清除日志" "setDefaultCert" = "从面板设置证书" -"telegramDesc" = "请提供Telegram聊天ID。(在机器人中使用'/id'命令)或(@userinfobot" +"telegramDesc" = "请提供Telegram聊天ID。(在机器人中使用'/id'命令或跟@userinfobot机器人对话获取)" "subscriptionDesc" = "要找到你的订阅 URL,请导航到“详细信息”。此外,你可以为多个客户端使用相同的名称。" "info" = "信息" "same" = "相同" @@ -290,7 +295,7 @@ "resetDefaultConfig" = "重置为默认配置" "panelSettings" = "常规" "securitySettings" = "安全设定" -"TGBotSettings" = "Telegram 机器人配置" +"TGBotSettings" = "Telegram机器人配置" "panelListeningIP" = "面板监听 IP" "panelListeningIPDesc" = "默认留空监听所有 IP" "panelListeningDomain" = "面板监听域名" @@ -298,10 +303,12 @@ "panelPort" = "面板监听端口" "panelPortDesc" = "重启面板生效" "publicKeyPath" = "面板证书公钥文件路径" -"publicKeyPathDesc" = "填写一个 '/' 开头的绝对路径" +"DefaultpublicKeyPath" = "/root/.acme.sh/域名_ecc/域名.cer" +"publicKeyPathDesc" = "填写一个 '/' 开头的绝对路径,〔acme方式〕请自行在填入时修改域名" "privateKeyPath" = "面板证书密钥文件路径" -"privateKeyPathDesc" = "填写一个 '/' 开头的绝对路径" -"panelUrlPath" = "面板 url 根路径" +"DefaultprivateKeyPath" = "/root/.acme.sh/域名_ecc/域名.key" +"privateKeyPathDesc" = "填写一个 '/' 开头的绝对路径,〔acme方式〕请自行在填入时修改域名" +"panelUrlPath" = "面板登录访问路径" "panelUrlPathDesc" = "必须以 '/' 开头,以 '/' 结尾" "pageSize" = "分页大小" "pageSizeDesc" = "定义入站表的页面大小。设置 0 表示禁用" @@ -317,7 +324,7 @@ "telegramBotEnable" = "启用 Telegram 机器人" "telegramBotEnableDesc" = "启用 Telegram 机器人功能" "telegramToken" = "Telegram 机器人令牌(token)" -"telegramTokenDesc" = "从 '@BotFather' 获取的 Telegram 机器人令牌" +"telegramTokenDesc" = "跟 '@BotFather' 对话获取的 Telegram 机器人令牌" "telegramProxy" = "SOCKS5 Proxy" "telegramProxyDesc" = "启用 SOCKS5 代理连接到 Telegram(根据指南调整设置)" "telegramAPIServer" = "Telegram API Server" @@ -450,13 +457,16 @@ "statsOutboundDownlink" = "出站下载统计" "statsOutboundDownlinkDesc" = "启用所有出站代理的下行流量统计收集。" +[pages.navigation] +"title" = "实用导航" + [pages.xray.rules] "first" = "置顶" "last" = "置底" "up" = "向上" "down" = "向下" "source" = "来源" -"dest" = "目的地址" +"dest" = "目标地址" "inbound" = "入站" "outbound" = "出站" "balancer" = "负载均衡" @@ -464,6 +474,18 @@ "add" = "添加规则" "edit" = "编辑规则" "useComma" = "逗号分隔的项目" +"DomainMatcher" = "域匹配类型" +"SourceIPs" = "源IP" +"SourcePort" = "源端口" +"Network" = "网络类型" +"Protocol" = "传输协议" +"Attributes" = "属性" +"Domain" = "域地址" +"User" = "用户" +"Port" = "端口" +"InboundTag" = "入站 Tag" +"OutboundTag" = "出站 Tag" +"BalancerTag" = "负载均衡 Tag" [pages.xray.outbound] "addOutbound" = "添加出站" @@ -583,7 +605,7 @@ [tgbot.commands] "unknown" = "❗ 未知命令" -"pleaseChoose" = "👇 请选择:\r\n" +"pleaseChoose" = "👇请〔按照需求〕选择下方按钮 :\r\n" "help" = "🤖 欢迎使用本机器人!它旨在为您提供来自服务器的特定数据,并允许您进行必要的修改。\r\n\r\n" "start" = "👋 你好,{{ .Firstname }}。\r\n" "welcome" = "🤖 欢迎来到 {{ .Hostname }} 管理机器人。\r\n" @@ -610,7 +632,7 @@ "report" = "🕰 定时报告:{{ .RunTime }}\r\n" "datetime" = "⏰ 日期时间:{{ .DateTime }}\r\n" "hostname" = "💻 主机名:{{ .Hostname }}\r\n" -"version" = "🚀 X-UI 版本:{{ .Version }}\r\n" +"version" = "🚀 3X-UI 版本:{{ .Version }}\r\n" "xrayVersion" = "📡 Xray 版本: {{ .XrayVersion }}\r\n" "ipv6" = "🌐 IPv6:{{ .IPv6 }}\r\n" "ipv4" = "🌐 IPv4:{{ .IPv4 }}\r\n" @@ -633,7 +655,7 @@ "active" = "💡 激活:{{ .Enable }}\r\n" "enabled" = "🚨 已启用:{{ .Enable }}\r\n" "online" = "🌐 连接状态:{{ .Status }}\r\n" -"email" = "📧 邮箱:{{ .Email }}\r\n" +"email" = "📧 邮箱(用户):{{ .Email }}\r\n" "upload" = "🔼 上传↑:{{ .Upload }}\r\n" "download" = "🔽 下载↓:{{ .Download }}\r\n" "total" = "📊 总计:{{ .UpDown }} / {{ .Total }}\r\n" @@ -678,12 +700,12 @@ "confirmRemoveTGUser" = "✅ 确认移除 Telegram 用户?" "confirmToggle" = "✅ 确认启用/禁用用户?" "dbBackup" = "获取数据库备份" -"serverUsage" = "服务器使用情况" +"serverUsage" = "服务器状态" "getInbounds" = "获取入站信息" "depleteSoon" = "即将耗尽" "clientUsage" = "获取使用情况" "onlines" = "在线客户端" -"commands" = "命令" +"commands" = "常用命令" "refresh" = "🔄 刷新" "clearIPs" = "❌ 清除 IP" "removeTGUser" = "❌ 移除 Telegram 用户" @@ -695,7 +717,7 @@ "ipLimit" = "🔢 IP 限制" "setTGUser" = "👤 设置 Telegram 用户" "toggle" = "🔘 启用/禁用" -"custom" = "🔢 风俗" +"custom" = "🔢 自定义输入" "confirmNumber" = "✅ 确认: {{ .Num }}" "confirmNumberAdd" = "✅ 确认添加:{{ .Num }}" "limitTraffic" = "🚧 流量限制" diff --git a/x-ui.sh b/x-ui.sh index 97dc9102..1e923296 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -2,7 +2,6 @@ red='\033[0;31m' green='\033[0;32m' -blue='\033[0;34m' yellow='\033[0;33m' plain='\033[0m' @@ -20,7 +19,7 @@ function LOGI() { } # check root -[[ $EUID -ne 0 ]] && LOGE "ERROR: You must be root to run this script! \n" && exit 1 +[[ $EUID -ne 0 ]] && echo -e "${red}致命错误: ${plain} 请使用 root 权限运行此脚本\n" && exit 1 # Check OS and set release variable if [[ -f /etc/os-release ]]; then @@ -30,13 +29,82 @@ elif [[ -f /usr/lib/os-release ]]; then source /usr/lib/os-release release=$ID else - echo "Failed to check the system OS, please contact the author!" >&2 + echo -e "${red}检查服务器操作系统失败,请联系作者!${plain}" >&2 exit 1 fi -echo "The OS release is: $release" -os_version="" -os_version=$(grep "^VERSION_ID" /etc/os-release | cut -d '=' -f2 | tr -d '"' | tr -d '.') +echo -e "——————————————————————" +echo -e "当前服务器的操作系统为:${red} $release${plain}" +echo "" +xui_version=$(/usr/local/x-ui/x-ui -v) +last_version=$(curl -Ls "https://api.github.com/repos/xeefei/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') +echo -e "${green}当前代理面板的版本为: ${red}〔3X-UI优化版〕v${xui_version}${plain}" +echo "" +echo -e "${yellow}〔3X-UI优化版〕最新版为---------->>> ${last_version}${plain}" + +os_version=$(grep -i version_id /etc/os-release | cut -d \" -f2 | cut -d . -f1) + +if [[ "${release}" == "centos" ]]; then + if [[ ${os_version} -lt 8 ]]; then + echo -e "${red} 请使用 CentOS 8 或更高版本 ${plain}\n" && exit 1 + fi +elif [[ "${release}" == "ubuntu" ]]; then + if [[ ${os_version} -lt 20 ]]; then + echo -e "${red} 请使用 Ubuntu 20 或更高版本!${plain}\n" && exit 1 + fi + +elif [[ "${release}" == "fedora" ]]; then + if [[ ${os_version} -lt 36 ]]; then + echo -e "${red} 请使用 Fedora 36 或更高版本!${plain}\n" && exit 1 + fi + +elif [[ "${release}" == "debian" ]]; then + if [[ ${os_version} -lt 11 ]]; then + echo -e "${red} 请使用 Debian 11 或更高版本 ${plain}\n" && exit 1 + fi + +elif [[ "${release}" == "almalinux" ]]; then + if [[ ${os_version} -lt 9 ]]; then + echo -e "${red} 请使用 AlmaLinux 9 或更高版本 ${plain}\n" && exit 1 + fi + +elif [[ "${release}" == "rocky" ]]; then + if [[ ${os_version} -lt 9 ]]; then + echo -e "${red} 请使用 RockyLinux 9 或更高版本 ${plain}\n" && exit 1 + fi +elif [[ "${release}" == "arch" ]]; then + echo "您的操作系统是 ArchLinux" +elif [[ "${release}" == "manjaro" ]]; then + echo "您的操作系统是 Manjaro" +elif [[ "${release}" == "armbian" ]]; then + echo "您的操作系统是 Armbian" +elif [[ "${release}" == "alpine" ]]; then + echo "您的操作系统是 Alpine Linux" +elif [[ "${release}" == "opensuse-tumbleweed" ]]; then + echo "您的操作系统是 OpenSUSE Tumbleweed" +elif [[ "${release}" == "oracle" ]]; then + if [[ ${os_version} -lt 8 ]]; then + echo -e "${red} 请使用 Oracle Linux 8 或更高版本 ${plain}\n" && exit 1 + fi +else + echo -e "${red}此脚本不支持您的操作系统。${plain}\n" + echo "请确保您使用的是以下受支持的操作系统之一:" + echo "- Ubuntu 20.04+" + echo "- Debian 11+" + echo "- CentOS 8+" + echo "- Fedora 36+" + echo "- Arch Linux" + echo "- Parch Linux" + echo "- Manjaro" + echo "- Armbian" + echo "- Alpine Linux" + echo "- AlmaLinux 9+" + echo "- Rocky Linux 9+" + echo "- Oracle Linux 8+" + echo "- OpenSUSE Tumbleweed" + exit 1 + +fi # Declare Variables log_folder="${XUI_LOG_FOLDER:=/var/log}" @@ -45,12 +113,12 @@ iplimit_banned_log_path="${log_folder}/3xipl-banned.log" confirm() { if [[ $# > 1 ]]; then - echo && read -rp "$1 [Default $2]: " temp + echo && read -p "$1 [Default $2]: " temp if [[ "${temp}" == "" ]]; then temp=$2 fi else - read -rp "$1 [y/n]: " temp + read -p "$1 [y/n]: " temp fi if [[ "${temp}" == "y" || "${temp}" == "Y" ]]; then return 0 @@ -60,7 +128,7 @@ confirm() { } confirm_restart() { - confirm "Restart the panel, Attention: Restarting the panel will also restart xray" "y" + confirm "重启面板,注意:重启面板也会重启 Xray" "y" if [[ $? == 0 ]]; then restart else @@ -69,12 +137,12 @@ confirm_restart() { } before_show_menu() { - echo && echo -n -e "${yellow}Press enter to return to the main menu: ${plain}" && read -r temp + echo && echo -n -e "${yellow}按 Enter 键返回主菜单:${plain}" && read temp show_menu } install() { - bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh) + bash <(curl -Ls https://raw.githubusercontent.com/xeefei/3x-ui/main/install.sh) if [[ $? == 0 ]]; then if [[ $# == 0 ]]; then start @@ -85,24 +153,24 @@ install() { } update() { - confirm "This function will forcefully reinstall the latest version, and the data will not be lost. Do you want to continue?" "y" + confirm "$(echo -e "${green}该功能将强制安装最新版本,并且数据不会丢失。${red}你想继续吗?${plain}---->>请输入")" "y" if [[ $? != 0 ]]; then - LOGE "Cancelled" + LOGE "已取消" if [[ $# == 0 ]]; then before_show_menu fi return 0 fi - bash <(curl -Ls https://raw.githubusercontent.com/MHSanaei/3x-ui/main/install.sh) + bash <(curl -Ls https://raw.githubusercontent.com/xeefei/3x-ui/main/install.sh) if [[ $? == 0 ]]; then - LOGI "Update is complete, Panel has automatically restarted " - before_show_menu + LOGI "更新完成,面板已自动重启" + exit 0 fi } update_menu() { - echo -e "${yellow}Updating Menu${plain}" - confirm "This function will update the menu to the latest changes." "y" + echo -e "${yellow}更新菜单项${plain}" + confirm "此功能会将所有菜单项更新为最新显示状态" "y" if [[ $? != 0 ]]; then LOGE "Cancelled" if [[ $# == 0 ]]; then @@ -110,43 +178,46 @@ update_menu() { fi return 0 fi - - wget -O /usr/bin/x-ui https://raw.githubusercontent.com/MHSanaei/3x-ui/main/x-ui.sh + + wget --no-check-certificate -O /usr/bin/x-ui https://raw.githubusercontent.com/xeefei/3x-ui/main/x-ui.sh chmod +x /usr/local/x-ui/x-ui.sh chmod +x /usr/bin/x-ui - - if [[ $? == 0 ]]; then - echo -e "${green}Update successful. The panel has automatically restarted.${plain}" + + if [[ $? == 0 ]]; then + echo -e "${green}更新成功,面板已自动重启${plain}" exit 0 else - echo -e "${red}Failed to update the menu.${plain}" + echo -e "${red}更新菜单项失败${plain}" return 1 fi } -legacy_version() { - echo -n "Enter the panel version (like 2.4.0):" - read -r tag_version +custom_version() { + echo "输入面板版本 (例: 2.3.8):" + read panel_version - if [ -z "$tag_version" ]; then - echo "Panel version cannot be empty. Exiting." + if [ -z "$panel_version" ]; then + echo "面板版本不能为空。" 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" - echo "Downloading and installing panel version $tag_version..." + download_link="https://raw.githubusercontent.com/xeefei/3x-ui/master/install.sh" + + # Use the entered panel version in the download link + install_command="bash <(curl -Ls $download_link) v$panel_version" + + echo "下载并安装面板版本 $panel_version..." eval $install_command } # Function to handle the deletion of the script file delete_script() { - rm "$0" # Remove the script file itself + rm "$0" # Remove the script file itself exit 1 } uninstall() { - confirm "Are you sure you want to uninstall the panel? xray will also uninstalled!" "n" + confirm "您确定要卸载面板吗? Xray 也将被卸载!" "n" if [[ $? != 0 ]]; then if [[ $# == 0 ]]; then show_menu @@ -162,9 +233,9 @@ uninstall() { rm /usr/local/x-ui/ -rf 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}" + echo -e "卸载成功\n" + echo "如果您需要再次安装此面板,可以使用以下命令:" + echo -e "${green}bash <(curl -Ls https://raw.githubusercontent.com/xeefei/3x-ui/master/install.sh)${plain}" echo "" # Trap the SIGTERM signal trap delete_script SIGTERM @@ -172,30 +243,23 @@ uninstall() { } reset_user() { - confirm "Are you sure to reset the username and password of the panel?" "n" + confirm "您确定重置面板的用户名和密码吗?" "n" if [[ $? != 0 ]]; then if [[ $# == 0 ]]; then show_menu fi return 0 fi - - read -rp "Please set the login username [default is a random username]: " config_account + read -rp "请设置用户名 [默认为随机用户名]: " config_account [[ -z $config_account ]] && config_account=$(date +%s%N | md5sum | cut -c 1-8) - read -rp "Please set the login password [default is a random password]: " config_password + read -rp "请设置密码 [默认为随机密码]: " config_password [[ -z $config_password ]] && config_password=$(date +%s%N | md5sum | cut -c 1-8) - - read -rp "Do you want to disable currently configured two-factor authentication? (y/n): " twoFactorConfirm - if [[ $twoFactorConfirm != "y" && $twoFactorConfirm != "Y" ]]; then - /usr/local/x-ui/x-ui setting -username ${config_account} -password ${config_password} -resetTwoFactor false >/dev/null 2>&1 - else - /usr/local/x-ui/x-ui setting -username ${config_account} -password ${config_password} -resetTwoFactor true >/dev/null 2>&1 - echo -e "Two factor authentication has been disabled." - fi - - echo -e "Panel login username has been reset to: ${green} ${config_account} ${plain}" - echo -e "Panel login password has been reset to: ${green} ${config_password} ${plain}" - echo -e "${green} Please use the new login username and password to access the X-UI panel. Also remember them! ${plain}" + /usr/local/x-ui/x-ui setting -username ${config_account} -password ${config_password} >/dev/null 2>&1 + /usr/local/x-ui/x-ui setting -remove_secret >/dev/null 2>&1 + echo -e "面板登录用户名已重置为:${green} ${config_account} ${plain}" + echo -e "面板登录密码已重置为:${green} ${config_password} ${plain}" + echo -e "${yellow} 面板 Secret Token 已禁用 ${plain}" + echo -e "${green} 请使用新的登录用户名和密码访问 3X-UI 面板。也请记住它们!${plain}" confirm_restart } @@ -206,26 +270,26 @@ gen_random_string() { } reset_webbasepath() { - echo -e "${yellow}Resetting Web Base Path${plain}" - - read -rp "Are you sure you want to reset the web base path? (y/n): " confirm - if [[ $confirm != "y" && $confirm != "Y" ]]; then - echo -e "${yellow}Operation canceled.${plain}" - return + echo -e "${yellow}修改访问路径${plain}" + + # Prompt user to set a new web base path + read -rp "请设置新的访问路径(若回车默认或输入y则为随机路径): " config_webBasePath + + if [[ $config_webBasePath == "y" ]]; then + config_webBasePath=$(gen_random_string 18) fi - - config_webBasePath=$(gen_random_string 18) - + # Apply the new web base path setting /usr/local/x-ui/x-ui setting -webBasePath "${config_webBasePath}" >/dev/null 2>&1 - - echo -e "Web base path has been reset to: ${green}${config_webBasePath}${plain}" - echo -e "${green}Please use the new web base path to access the panel.${plain}" - restart + systemctl restart x-ui + + # Display confirmation message + echo -e "面板访问路径已重置为: ${green}${config_webBasePath}${plain}" + echo -e "${green}请使用新的路径登录访问面板${plain}" } reset_config() { - confirm "Are you sure you want to reset all panel settings, Account data will not be lost, Username and password will not change" "n" + confirm "您确定要重置所有面板设置,帐户数据不会丢失,用户名和密码不会更改" "n" if [[ $? != 0 ]]; then if [[ $# == 0 ]]; then show_menu @@ -233,49 +297,27 @@ reset_config() { return 0 fi /usr/local/x-ui/x-ui setting -reset - echo -e "All panel settings have been reset to default." - restart + echo -e "所有面板设置已重置为默认,请立即重新启动面板,并使用默认的${green}2053${plain}端口访问网页面板" + confirm_restart } check_config() { - local info=$(/usr/local/x-ui/x-ui setting -show true) + info=$(/usr/local/x-ui/x-ui setting -show true) if [[ $? != 0 ]]; then - LOGE "get current settings error, please check logs" + LOGE "获取当前设置错误,请检查日志" show_menu - return - fi - LOGI "${info}" - - local existing_webBasePath=$(echo "$info" | grep -Eo 'webBasePath: .+' | awk '{print $2}') - local existing_port=$(echo "$info" | grep -Eo 'port: .+' | awk '{print $2}') - local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'cert: .+' | awk '{print $2}') - local server_ip=$(curl -s --max-time 3 https://api.ipify.org) - if [ -z "$server_ip" ]; then - server_ip=$(curl -s --max-time 3 https://4.ident.me) - fi - - if [[ -n "$existing_cert" ]]; then - local domain=$(basename "$(dirname "$existing_cert")") - - if [[ "$domain" =~ ^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then - echo -e "${green}Access URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}" - else - echo -e "${green}Access URL: https://${server_ip}:${existing_port}${existing_webBasePath}${plain}" - fi - else - echo -e "${green}Access URL: http://${server_ip}:${existing_port}${existing_webBasePath}${plain}" fi + echo -e "${info}${plain}" } set_port() { - echo -n "Enter port number[1-65535]: " - read -r port + echo && echo -n -e "输入端口号 [1-65535]: " && read port if [[ -z "${port}" ]]; then LOGD "Cancelled" before_show_menu else /usr/local/x-ui/x-ui setting -port ${port} - echo -e "The port is set, Please restart the panel now, and use the new port ${green}${port}${plain} to access web panel" + echo -e "端口已设置,请立即重启面板,并使用新端口 ${green}${port}${plain} 以访问面板" confirm_restart fi } @@ -284,15 +326,15 @@ start() { check_status if [[ $? == 0 ]]; then echo "" - LOGI "Panel is running, No need to start again, If you need to restart, please select restart" + LOGI "面板正在运行,无需再次启动,如需重新启动,请选择重新启动" else systemctl start x-ui sleep 2 check_status if [[ $? == 0 ]]; then - LOGI "x-ui Started Successfully" + LOGI "3X-UI 已成功启动" else - LOGE "panel Failed to start, Probably because it takes longer than two seconds to start, Please check the log information later" + LOGE "面板启动失败,可能是启动时间超过两秒,请稍后查看日志信息" fi fi @@ -305,15 +347,15 @@ stop() { check_status if [[ $? == 1 ]]; then echo "" - LOGI "Panel stopped, No need to stop again!" + LOGI "面板已关闭,无需再次关闭!" else systemctl stop x-ui sleep 2 check_status if [[ $? == 1 ]]; then - LOGI "x-ui and xray stopped successfully" + LOGI "3X-UI 和 Xray 已成功关闭" else - LOGE "Panel stop failed, Probably because the stop time exceeds two seconds, Please check the log information later" + LOGE "面板关闭失败,可能是停止时间超过两秒,请稍后查看日志信息" fi fi @@ -327,9 +369,9 @@ restart() { sleep 2 check_status if [[ $? == 0 ]]; then - LOGI "x-ui and xray Restarted successfully" + LOGI "3X-UI 和 Xray 已成功重启" else - LOGE "Panel restart failed, Probably because it takes longer than two seconds to start, Please check the log information later" + LOGE "面板重启失败,可能是启动时间超过两秒,请稍后查看日志信息" fi if [[ $# == 0 ]]; then before_show_menu @@ -346,9 +388,9 @@ status() { enable() { systemctl enable x-ui if [[ $? == 0 ]]; then - LOGI "x-ui Set to boot automatically on startup successfully" + LOGI "x-ui 已成功设置开机启动" else - LOGE "x-ui Failed to set Autostart" + LOGE "x-ui 设置开机启动失败" fi if [[ $# == 0 ]]; then @@ -359,9 +401,9 @@ enable() { disable() { systemctl disable x-ui if [[ $? == 0 ]]; then - LOGI "x-ui Autostart Cancelled successfully" + LOGI "x-ui 已成功取消开机启动" else - LOGE "x-ui Failed to cancel autostart" + LOGE "x-ui 取消开机启动失败" fi if [[ $# == 0 ]]; then @@ -370,94 +412,47 @@ disable() { } show_log() { - echo -e "${green}\t1.${plain} Debug Log" - echo -e "${green}\t2.${plain} Clear All logs" - echo -e "${green}\t0.${plain} Back to Main Menu" - read -rp "Choose an option: " choice - - case "$choice" in - 0) - show_menu - ;; - 1) - journalctl -u x-ui -e --no-pager -f -p debug - if [[ $# == 0 ]]; then - before_show_menu - fi - ;; - 2) - sudo journalctl --rotate - sudo journalctl --vacuum-time=1s - echo "All Logs cleared." - restart - ;; - *) - echo -e "${red}Invalid option. Please select a valid number.${plain}\n" - show_log - ;; - esac + journalctl -u x-ui.service -e --no-pager -f + if [[ $# == 0 ]]; then + before_show_menu + fi } show_banlog() { - local system_log="/var/log/fail2ban.log" - - echo -e "${green}Checking ban logs...${plain}\n" - - if ! systemctl is-active --quiet fail2ban; then - echo -e "${red}Fail2ban service is not running!${plain}\n" - return 1 - fi - - if [[ -f "$system_log" ]]; then - echo -e "${green}Recent system ban activities from fail2ban.log:${plain}" - grep "3x-ipl" "$system_log" | grep -E "Ban|Unban" | tail -n 10 || echo -e "${yellow}No recent system ban activities found${plain}" - echo "" - fi - - if [[ -f "${iplimit_banned_log_path}" ]]; then - echo -e "${green}3X-IPL ban log entries:${plain}" + if test -f "${iplimit_banned_log_path}"; then if [[ -s "${iplimit_banned_log_path}" ]]; then - grep -v "INIT" "${iplimit_banned_log_path}" | tail -n 10 || echo -e "${yellow}No ban entries found${plain}" + cat ${iplimit_banned_log_path} else - echo -e "${yellow}Ban log file is empty${plain}" + echo -e "${red}日志文件为空${plain}\n" fi else - echo -e "${red}Ban log file not found at: ${iplimit_banned_log_path}${plain}" + echo -e "${red}未找到日志文件。 请先安装 Fail2ban 和 IP Limit${plain}\n" fi - - echo -e "\n${green}Current jail status:${plain}" - fail2ban-client status 3x-ipl || echo -e "${yellow}Unable to get jail status${plain}" } bbr_menu() { - echo -e "${green}\t1.${plain} Enable BBR" - echo -e "${green}\t2.${plain} Disable BBR" - echo -e "${green}\t0.${plain} Back to Main Menu" - read -rp "Choose an option: " choice + echo -e "${green}\t1.${plain} 启用 BBR" + echo -e "${green}\t2.${plain} 禁用 BBR" + echo -e "${green}\t0.${plain} 返回主菜单" + read -p "请输入选项: " choice case "$choice" in 0) show_menu ;; 1) enable_bbr - bbr_menu ;; 2) disable_bbr - bbr_menu - ;; - *) - echo -e "${red}Invalid option. Please select a valid number.${plain}\n" - bbr_menu ;; + *) echo "无效选项" ;; esac } disable_bbr() { - if ! grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf || ! grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then - echo -e "${yellow}BBR is not currently enabled.${plain}" - before_show_menu + echo -e "${yellow}BBR 当前未启用${plain}" + exit 0 fi # Replace BBR with CUBIC configurations @@ -469,16 +464,16 @@ disable_bbr() { # Verify that BBR is replaced with CUBIC if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "cubic" ]]; then - echo -e "${green}BBR has been replaced with CUBIC successfully.${plain}" + echo -e "${green}BBR 已成功替换为 CUBIC${plain}" else - echo -e "${red}Failed to replace BBR with CUBIC. Please check your system configuration.${plain}" + echo -e "${red}用 CUBIC 替换 BBR 失败,请检查您的系统配置。${plain}" fi } enable_bbr() { if grep -q "net.core.default_qdisc=fq" /etc/sysctl.conf && grep -q "net.ipv4.tcp_congestion_control=bbr" /etc/sysctl.conf; then - echo -e "${green}BBR is already enabled!${plain}" - before_show_menu + echo -e "${green}BBR 已经启用!${plain}" + exit 0 fi # Check the OS and install necessary packages @@ -486,17 +481,17 @@ enable_bbr() { ubuntu | debian | armbian) apt-get update && apt-get install -yqq --no-install-recommends ca-certificates ;; - centos | rhel | almalinux | rocky | ol) + centos | almalinux | rocky | oracle) yum -y update && yum -y install ca-certificates ;; - fedora | amzn | virtuozzo) + fedora) dnf -y update && dnf -y install ca-certificates ;; - arch | manjaro | parch) + arch | manjaro) pacman -Sy --noconfirm ca-certificates ;; *) - echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n" + echo -e "${red}不支持的操作系统。请检查脚本并手动安装必要的软件包${plain}\n" exit 1 ;; esac @@ -510,22 +505,21 @@ enable_bbr() { # Verify that BBR is enabled if [[ $(sysctl net.ipv4.tcp_congestion_control | awk '{print $3}') == "bbr" ]]; then - echo -e "${green}BBR has been enabled successfully.${plain}" + echo -e "${green}BBR 已成功启用${plain}" else - echo -e "${red}Failed to enable BBR. Please check your system configuration.${plain}" + echo -e "${red}启用 BBR 失败,请检查您的系统配置${plain}" fi } update_shell() { - wget -O /usr/bin/x-ui -N https://github.com/MHSanaei/3x-ui/raw/main/x-ui.sh + wget -O /usr/bin/x-ui -N --no-check-certificate https://github.com/xeefei/3x-ui/raw/main/x-ui.sh if [[ $? != 0 ]]; then echo "" - LOGE "Failed to download script, Please check whether the machine can connect Github" + LOGE "下载脚本失败,请检查机器是否可以连接至 GitHub" before_show_menu else chmod +x /usr/bin/x-ui - LOGI "Upgrade script succeeded, Please rerun the script" - before_show_menu + LOGI "升级脚本成功,请重新运行脚本" && exit 0 fi } @@ -555,7 +549,7 @@ check_uninstall() { check_status if [[ $? != 2 ]]; then echo "" - LOGE "Panel installed, Please do not reinstall" + LOGE "面板已安装,请勿重新安装" if [[ $# == 0 ]]; then before_show_menu fi @@ -569,7 +563,7 @@ check_install() { check_status if [[ $? == 2 ]]; then echo "" - LOGE "Please install the panel first" + LOGE "请先安装面板" if [[ $# == 0 ]]; then before_show_menu fi @@ -583,15 +577,15 @@ show_status() { check_status case $? in 0) - echo -e "Panel state: ${green}Running${plain}" + echo -e "面板状态: ${green}运行中${plain}" show_enable_status ;; 1) - echo -e "Panel state: ${yellow}Not Running${plain}" + echo -e "面板状态: ${yellow}未运行${plain}" show_enable_status ;; 2) - echo -e "Panel state: ${red}Not Installed${plain}" + echo -e "面板状态: ${red}未安装${plain}" ;; esac show_xray_status @@ -600,9 +594,9 @@ show_status() { show_enable_status() { check_enabled if [[ $? == 0 ]]; then - echo -e "Start automatically: ${green}Yes${plain}" + echo -e "开机启动: ${green}是${plain}" else - echo -e "Start automatically: ${red}No${plain}" + echo -e "开机启动: ${red}否${plain}" fi } @@ -618,94 +612,68 @@ check_xray_status() { show_xray_status() { check_xray_status if [[ $? == 0 ]]; then - echo -e "xray state: ${green}Running${plain}" + echo -e "Xray状态: ${green}运行中${plain}" else - echo -e "xray state: ${red}Not Running${plain}" + echo -e "Xray状态: ${red}未运行${plain}" fi } firewall_menu() { - echo -e "${green}\t1.${plain} ${green}Install${plain} Firewall" - echo -e "${green}\t2.${plain} Port List [numbered]" - echo -e "${green}\t3.${plain} ${green}Open${plain} Ports" - echo -e "${green}\t4.${plain} ${red}Delete${plain} Ports from List" - echo -e "${green}\t5.${plain} ${green}Enable${plain} Firewall" - echo -e "${green}\t6.${plain} ${red}Disable${plain} Firewall" - echo -e "${green}\t7.${plain} Firewall Status" - echo -e "${green}\t0.${plain} Back to Main Menu" - read -rp "Choose an option: " choice + echo -e "${green}\t1.${plain} 安装防火墙并开放端口" + echo -e "${green}\t2.${plain} 允许列表" + echo -e "${green}\t3.${plain} 从列表中删除端口" + echo -e "${green}\t4.${plain} 禁用防火墙" + echo -e "${green}\t0.${plain} 返回主菜单" + read -p "请输入选项: " choice case "$choice" in 0) show_menu ;; 1) - install_firewall - firewall_menu + open_ports ;; 2) - ufw status numbered - firewall_menu + sudo ufw status ;; 3) - open_ports - firewall_menu + delete_ports ;; 4) - delete_ports - firewall_menu - ;; - 5) - ufw enable - firewall_menu - ;; - 6) - ufw disable - firewall_menu - ;; - 7) - ufw status verbose - firewall_menu - ;; - *) - echo -e "${red}Invalid option. Please select a valid number.${plain}\n" - firewall_menu + sudo ufw disable ;; + *) echo "无效选项" ;; esac } -install_firewall() { +open_ports() { if ! command -v ufw &>/dev/null; then - echo "ufw firewall is not installed. Installing now..." + echo "ufw 防火墙未安装,正在安装..." apt-get update apt-get install -y ufw else - echo "ufw firewall is already installed" + echo "ufw 防火墙已安装" fi # Check if the firewall is inactive if ufw status | grep -q "Status: active"; then - echo "Firewall is already active" + echo "防火墙已经激活" 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 # 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 + # Prompt the user to enter a list of ports + read -p "输入您要打开的端口(例如 80,443,2053 或端口范围 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 + echo "错误:输入无效。请输入以英文逗号分隔的端口列表或端口范围(例如 80,443,2053 或 400-500)" >&2 exit 1 fi @@ -716,628 +684,592 @@ open_ports() { # Split the range into start and end ports start_port=$(echo $port | cut -d'-' -f1) end_port=$(echo $port | cut -d'-' -f2) - # Open the port range - ufw allow $start_port:$end_port/tcp - ufw allow $start_port:$end_port/udp + # Loop through the range and open each port + for ((i = start_port; i <= end_port; i++)); do + ufw allow $i + done 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 + # Confirm that the ports are open + ufw status | grep $ports } delete_ports() { - # Display current rules with numbers - echo "Current UFW rules:" - ufw status numbered + # Prompt the user to enter the ports they want to delete + read -p "输入要删除的端口(例如 80,443,2053 或范围 400-500): " ports - # 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 - else - echo "${red}Error:${plain} Invalid choice. Please enter 1 or 2." >&2 + # Check if the input is valid + if ! [[ $ports =~ ^([0-9]+|[0-9]+-[0-9]+)(,([0-9]+|[0-9]+-[0-9]+))*$ ]]; then + echo "错误:输入无效。请输入以英文逗号分隔的端口列表或端口范围(例如 80,443,2053 或 400-500)" >&2 exit 1 fi + + # Delete the specified ports using ufw + IFS=',' read -ra PORT_LIST <<<"$ports" + for port in "${PORT_LIST[@]}"; do + if [[ $port == *-* ]]; then + # Split the range into start and end ports + start_port=$(echo $port | cut -d'-' -f1) + end_port=$(echo $port | cut -d'-' -f2) + # Loop through the range and delete each port + for ((i = start_port; i <= end_port; i++)); do + ufw delete allow $i + done + else + ufw delete allow "$port" + fi + done + + # Confirm that the ports are deleted + echo "删除指定端口:" + ufw status | grep $ports } update_geo() { - echo -e "${green}\t1.${plain} Loyalsoldier (geoip.dat, geosite.dat)" - echo -e "${green}\t2.${plain} chocolate4u (geoip_IR.dat, geosite_IR.dat)" - echo -e "${green}\t3.${plain} runetfreedom (geoip_RU.dat, geosite_RU.dat)" - echo -e "${green}\t0.${plain} Back to Main Menu" - read -rp "Choose an option: " choice - - cd /usr/local/x-ui/bin - - case "$choice" in - 0) - show_menu - ;; - 1) - systemctl stop x-ui - rm -f geoip.dat geosite.dat - wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat - wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat - echo -e "${green}Loyalsoldier datasets have been updated successfully!${plain}" - restart - ;; - 2) - systemctl stop x-ui - rm -f geoip_IR.dat geosite_IR.dat - wget -O geoip_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat - wget -O geosite_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat - echo -e "${green}chocolate4u datasets have been updated successfully!${plain}" - restart - ;; - 3) - systemctl stop x-ui - rm -f geoip_RU.dat geosite_RU.dat - wget -O geoip_RU.dat -N https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat - wget -O geosite_RU.dat -N https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat - echo -e "${green}runetfreedom datasets have been updated successfully!${plain}" - restart - ;; - *) - echo -e "${red}Invalid option. Please select a valid number.${plain}\n" - update_geo - ;; - esac + local defaultBinFolder="/usr/local/x-ui/bin" + read -p "请输入 x-ui bin 文件夹路径,默认留空。(默认值:'${defaultBinFolder}')" binFolder + binFolder=${binFolder:-${defaultBinFolder}} + if [[ ! -d ${binFolder} ]]; then + LOGE "文件夹 ${binFolder} 不存在!" + LOGI "制作 bin 文件夹:${binFolder}..." + mkdir -p ${binFolder} + fi + systemctl stop x-ui + cd ${binFolder} + rm -f geoip.dat geosite.dat geoip_IR.dat geosite_IR.dat geoip_VN.dat geosite_VN.dat + wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat + wget -N https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat + wget -O geoip_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat + wget -O geosite_IR.dat -N https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat + wget -O geoip_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geoip.dat + wget -O geosite_VN.dat https://github.com/vuong2023/vn-v2ray-rules/releases/latest/download/geosite.dat + systemctl start x-ui + echo -e "${green}Geosite.dat + Geoip.dat + geoip_IR.dat + geosite_IR.dat 在 bin 文件夹: '${binfolder}' 中已经更新成功 !${plain}" before_show_menu } -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 +install_acme() { + # 检查是否已安装 acme.sh + if command -v ~/.acme.sh/acme.sh &>/dev/null; then + LOGI "acme.sh 已经安装。" + return 0 + fi + + LOGI "正在安装 acme.sh..." + cd ~ || return 1 # 确保可以切换到主目录 + + curl -s https://get.acme.sh | sh + if [ $? -ne 0 ]; then + LOGE "安装 acme.sh 失败。" + return 1 + else + LOGI "安装 acme.sh 成功。" + fi + + return 0 +} - LOGI "Installing acme.sh..." - cd ~ || return 1 # Ensure you can change to the home directory +ssl_cert_issue_main() { + echo -e "${green}\t1.${plain} 获取 SSL 证书" + echo -e "${green}\t2.${plain} 撤销证书" + echo -e "${green}\t3.${plain} 强制更新证书" + echo -e "${green}\t4.${plain} 显示现有域名" + echo -e "${green}\t5.${plain} 为面板设置证书路径" + echo -e "${green}\t0.${plain} 返回主菜单" + + read -rp "请选择一个选项:" choice + case "$choice" in + 0) + show_menu + ;; + 1) + ssl_cert_issue + ssl_cert_issue_main + ;; + 2) + local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) + if [ -z "$domains" ]; then + echo "未找到可撤销的证书。" + else + echo "现有域名:" + echo "$domains" + read -rp "请从列表中输入要撤销证书的域名:" domain + if echo "$domains" | grep -qw "$domain"; then + ~/.acme.sh/acme.sh --revoke -d ${domain} + LOGI "已撤销域名的证书:$domain" + else + echo "输入的域名无效。" + fi + fi + ssl_cert_issue_main + ;; + 3) + local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) + if [ -z "$domains" ]; then + echo "未找到可更新的证书。" + else + echo "现有域名:" + echo "$domains" + read -rp "请从列表中输入要强制更新 SSL 证书的域名:" domain + if echo "$domains" | grep -qw "$domain"; then + ~/.acme.sh/acme.sh --renew -d ${domain} --force + LOGI "已强制更新域名的证书:$domain" + else + echo "输入的域名无效。" + fi + fi + ssl_cert_issue_main + ;; + 4) + local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) + if [ -z "$domains" ]; then + echo "未找到证书。" + else + echo "现有域名及其路径:" + for domain in $domains; do + local cert_path="/root/cert/${domain}/fullchain.pem" + local key_path="/root/cert/${domain}/privkey.pem" + if [[ -f "${cert_path}" && -f "${key_path}" ]]; then + echo -e "域名:${domain}" + echo -e "\t证书路径:${cert_path}" + echo -e "\t私钥路径:${key_path}" + else + echo -e "域名:${domain} - 证书或私钥文件缺失。" + fi + done + fi + ssl_cert_issue_main + ;; + 5) + local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) + if [ -z "$domains" ]; then + echo "未找到证书。" + else + echo "可用域名:" + echo "$domains" + read -rp "请选择要为面板设置路径的域名:" domain + + if echo "$domains" | grep -qw "$domain"; then + local webCertFile="/root/cert/${domain}/fullchain.pem" + local webKeyFile="/root/cert/${domain}/privkey.pem" + + if [[ -f "${webCertFile}" && -f "${webKeyFile}" ]]; then + /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" + echo "已为域名设置面板路径:$domain" + echo " - 证书文件:$webCertFile" + echo " - 私钥文件:$webKeyFile" + restart + else + echo "未找到域名的证书或私钥:$domain" + fi + else + echo "输入的域名无效。" + fi + fi + ssl_cert_issue_main + ;; + + *) + echo -e "${red}无效选项。请选择有效的数字。${plain}\n" + ssl_cert_issue_main + ;; + esac +} - curl -s https://get.acme.sh | sh - if [ $? -ne 0 ]; then - LOGE "Installation of acme.sh failed." - return 1 - else - LOGI "Installation of acme.sh succeeded." - fi +ssl_cert_issue() { + local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath(访问路径): .+' | awk '{print $2}') + local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port(端口号): .+' | awk '{print $2}') + # 首先检查 acme.sh + if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then + echo "未找到 acme.sh,将进行安装" + install_acme + if [ $? -ne 0 ]; then + LOGE "安装 acme 失败,请检查日志" + exit 1 + fi + fi + + # 安装 socat + case "${release}" in + ubuntu | debian | armbian) + apt update && apt install socat -y + ;; + centos | rhel | almalinux | rocky | ol) + yum -y update && yum -y install socat + ;; + fedora | amzn | virtuozzo) + dnf -y update && dnf -y install socat + ;; + arch | manjaro | parch) + pacman -Sy --noconfirm socat + ;; + *) + echo -e "${red}不支持的操作系统。请检查脚本并手动安装必要的软件包。${plain}\n" + exit 1 + ;; + esac + if [ $? -ne 0 ]; then + LOGE "安装 socat 失败,请检查日志" + exit 1 + else + LOGI "安装 socat 成功..." + fi + + # 在这里获取域名,我们需要验证它 + local domain="" + read -rp "请输入您的域名: " domain + LOGD "您的域名是: ${domain}, 正在检查..." + + # 检查是否已存在证书 + local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}') + if [ "${currentCert}" == "${domain}" ]; then + local certInfo=$(~/.acme.sh/acme.sh --list) + LOGE "系统已存在此域名的证书。无法再次签发。当前证书详情:" + LOGI "$certInfo" + exit 1 + else + LOGI "您的域名现在可以签发证书了..." + fi + + # 为证书创建一个目录 + certPath="/root/cert/${domain}" + if [ ! -d "$certPath" ]; then + mkdir -p "$certPath" + else + rm -rf "$certPath" + mkdir -p "$certPath" + fi + + # 获取独立服务器的端口号 + local WebPort=80 + read -rp "请选择要使用的端口 (默认为 80): " WebPort + if [[ ${WebPort} -gt 65535 || ${WebPort} -lt 1 ]]; then + LOGE "您输入的 ${WebPort} 无效,将使用默认端口 80。" + WebPort=80 + fi + LOGI "将使用端口: ${WebPort} 来签发证书。请确保此端口已开放。" + + # 签发证书 + ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt + ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force + if [ $? -ne 0 ]; then + LOGE "签发证书失败,请检查日志。" + rm -rf ~/.acme.sh/${domain} + exit 1 + else + LOGE "签发证书成功,正在安装证书..." + fi + + reloadCmd="x-ui restart" + + LOGI "ACME 的默认 --reloadcmd 是: ${yellow}x-ui restart" + LOGI "此命令将在每次证书签发和续订时运行。" + read -rp "您想修改 ACME 的 --reloadcmd 吗? (y/n): " setReloadcmd + if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then + echo -e "\n${green}\t1.${plain} 预设: systemctl reload nginx ; x-ui restart" + echo -e "${green}\t2.${plain} 输入您自己的命令" + echo -e "${green}\t0.${plain} 保留默认的 reloadcmd" + read -rp "请选择一个选项: " choice + case "$choice" in + 1) + LOGI "Reloadcmd 是: systemctl reload nginx ; x-ui restart" + reloadCmd="systemctl reload nginx ; x-ui restart" + ;; + 2) + LOGD "建议将 x-ui restart 放在末尾,这样如果其他服务失败,它不会引发错误" + read -rp "请输入您的 reloadcmd (例如: systemctl reload nginx ; x-ui restart): " reloadCmd + LOGI "您的 reloadcmd 是: ${reloadCmd}" + ;; + *) + LOGI "保留默认的 reloadcmd" + ;; + esac + fi + + # 安装证书 + ~/.acme.sh/acme.sh --installcert -d ${domain} \ + --key-file /root/cert/${domain}/privkey.pem \ + --fullchain-file /root/cert/${domain}/fullchain.pem \ + --reloadcmd "${reloadCmd}" + + if [ $? -ne 0 ]; then + LOGE "安装证书失败,正在退出。" + rm -rf ~/.acme.sh/${domain} + exit 1 + else + LOGI "安装证书成功,正在启用自动续订..." + fi + + # 启用自动续订 + ~/.acme.sh/acme.sh --upgrade --auto-upgrade + if [ $? -ne 0 ]; then + LOGE "自动续订失败,证书详情:" + ls -lah cert/* + chmod 755 $certPath/* + exit 1 + else + LOGI "自动续订成功,证书详情:" + ls -lah cert/* + chmod 755 $certPath/* + fi + + # 成功安装证书后提示用户设置面板路径 + read -rp "您想为面板设置此证书吗? (y/n): " setPanel + if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then + local webCertFile="/root/cert/${domain}/fullchain.pem" + local webKeyFile="/root/cert/${domain}/privkey.pem" + + if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then + /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" + LOGI "已为域名设置面板路径: $domain" + echo "" + LOGI " - 证书文件: $webCertFile" + LOGI " - 私钥文件: $webKeyFile" + echo "" + echo -e "${green}登录访问面板URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}" + echo "" + echo -e "${green}PS:若您要登录访问面板,请复制上面的地址到浏览器即可${plain}" + echo "" + restart + else + LOGE "错误:未找到域名的证书或私钥文件: $domain。" + fi + else + LOGI "跳过面板路径设置。" + fi + } +ssl_cert_issue_CF() { + local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath(访问路径): .+' | awk '{print $2}') + local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port(端口号): .+' | awk '{print $2}') + LOGI "****** 使用说明 ******" + LOGI "请按照以下步骤完成操作:" + LOGI "1. 准备好在 Cloudflare 注册的电子邮箱。" + LOGI "2. 准备好 Cloudflare Global API 密钥。" + LOGI "3. 准备好域名。" + LOGI "4. 证书颁发后,系统将提示您为面板设置证书(可选)。" + LOGI "5. 安装后,脚本还支持自动续订 SSL 证书。" + + confirm "您确认信息并希望继续吗?[y/n]" "y" + + if [ $? -eq 0 ]; then + # 首先检查 acme.sh + if ! command -v ~/.acme.sh/acme.sh &>/dev/null; then + echo "未找到 acme.sh。我们将为您安装。" + install_acme + if [ $? -ne 0 ]; then + LOGE "安装 acme 失败,请检查日志。" + exit 1 + fi + fi + + CF_Domain="" + + LOGD "请设置一个域名:" + read -rp "在此输入您的域名: " CF_Domain + LOGD "您的域名设置为:${CF_Domain}" + + # 设置 Cloudflare API 详细信息 + CF_GlobalKey="" + CF_AccountEmail="" + LOGD "请设置 API 密钥:" + read -rp "在此输入您的密钥: " CF_GlobalKey + LOGD "您的 API 密钥是:${CF_GlobalKey}" + + LOGD "请设置注册的电子邮箱:" + read -rp "在此输入您的电子邮箱: " CF_AccountEmail + LOGD "您注册的电子邮箱地址是:${CF_AccountEmail}" + + # 将默认 CA 设置为 Let's Encrypt + ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt + if [ $? -ne 0 ]; then + LOGE "设置默认 CA 为 Let's Encrypt 失败,脚本正在退出..." + exit 1 + fi + + export CF_Key="${CF_GlobalKey}" + export CF_Email="${CF_AccountEmail}" + + # 使用 Cloudflare DNS 颁发证书 + ~/.acme.sh/acme.sh --issue --dns dns_cf -d ${CF_Domain} -d *.${CF_Domain} --log --force + if [ $? -ne 0 ]; then + LOGE "证书颁发失败,脚本正在退出..." + exit 1 + else + LOGI "证书颁发成功,正在安装..." + fi + + # 安装证书 + certPath="/root/cert/${CF_Domain}" + if [ -d "$certPath" ]; then + rm -rf ${certPath} + fi + + mkdir -p ${certPath} + if [ $? -ne 0 ]; then + LOGE "创建目录失败: ${certPath}" + exit 1 + fi + + reloadCmd="x-ui restart" + + LOGI "ACME 的默认 --reloadcmd 是: ${yellow}x-ui restart" + LOGI "此命令将在每次证书颁发和续订时运行。" + read -rp "您想修改 ACME 的 --reloadcmd 吗? (y/n): " setReloadcmd + if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then + echo -e "\n${green}\t1.${plain} 预设: systemctl reload nginx ; x-ui restart" + echo -e "${green}\t2.${plain} 输入您自己的命令" + echo -e "${green}\t0.${plain} 保留默认的 reloadcmd" + read -rp "请选择一个选项: " choice + case "$choice" in + 1) + LOGI "Reloadcmd 是: systemctl reload nginx ; x-ui restart" + reloadCmd="systemctl reload nginx ; x-ui restart" + ;; + 2) + LOGD "建议将 x-ui restart 放在末尾,这样如果其他服务失败,它不会引发错误" + read -rp "请输入您的 reloadcmd (例如: systemctl reload nginx ; x-ui restart): " reloadCmd + LOGI "您的 reloadcmd 是: ${reloadCmd}" + ;; + *) + LOGI "保留默认的 reloadcmd" + ;; + esac + fi + ~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} \ + --key-file ${certPath}/privkey.pem \ + --fullchain-file ${certPath}/fullchain.pem \ + --reloadcmd "${reloadCmd}" + + if [ $? -ne 0 ]; then + LOGE "证书安装失败,脚本正在退出..." + exit 1 + else + LOGI "证书安装成功,正在开启自动更新..." + fi + + # 启用自动更新 + ~/.acme.sh/acme.sh --upgrade --auto-upgrade + if [ $? -ne 0 ]; then + LOGE "自动更新设置失败,脚本正在退出..." + exit 1 + else + LOGI "证书已安装并开启自动续订。具体信息如下:" + ls -lah ${certPath}/* + chmod 755 ${certPath}/* + fi + + # 成功安装证书后提示用户设置面板路径 + read -rp "您想为面板设置此证书吗? (y/n): " setPanel + if [[ "$setPanel" == "y" || "$setPanel" == "Y" ]]; then + local webCertFile="${certPath}/fullchain.pem" + local webKeyFile="${certPath}/privkey.pem" + + if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then + /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" + LOGI "已为域名设置面板路径: $CF_Domain" + echo "" + LOGI " - 证书文件: $webCertFile" + LOGI " - 私钥文件: $webKeyFile" + echo "" + echo -e "${green}登录访问面板URL: https://${CF_Domain}:${existing_port}${existing_webBasePath}${plain}" + echo "" + echo -e "${green}PS:若您要登录访问面板,请复制上面的地址到浏览器即可${plain}" + echo "" + restart + else + LOGE "错误:未找到域名的证书或私钥文件: $CF_Domain。" + fi + else + LOGI "跳过面板路径设置。" + fi + else + show_menu + fi + } - return 0 -} - -ssl_cert_issue_main() { - echo -e "${green}\t1.${plain} Get SSL" - echo -e "${green}\t2.${plain} Revoke" - echo -e "${green}\t3.${plain} Force Renew" - echo -e "${green}\t4.${plain} Show Existing Domains" - echo -e "${green}\t5.${plain} Set Cert paths for the panel" - echo -e "${green}\t0.${plain} Back to Main Menu" - - read -rp "Choose an option: " choice +warp_cloudflare() { + echo -e "${green}\t1.${plain} 安装 WARP socks5 代理" + echo -e "${green}\t2.${plain} 账户类型 (free, plus, team)" + echo -e "${green}\t3.${plain} 开启 / 关闭 WireProxy" + echo -e "${green}\t4.${plain} 卸载 WARP" + echo -e "${green}\t0.${plain} 返回主菜单" + read -p "请输入选项: " choice case "$choice" in 0) show_menu ;; 1) - ssl_cert_issue - ssl_cert_issue_main + bash <(curl -sSL https://raw.githubusercontent.com/hamid-gh98/x-ui-scripts/main/install_warp_proxy.sh) ;; 2) - local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) - if [ -z "$domains" ]; then - echo "No certificates found to revoke." - else - echo "Existing domains:" - echo "$domains" - read -rp "Please enter a domain from the list to revoke the certificate: " domain - if echo "$domains" | grep -qw "$domain"; then - ~/.acme.sh/acme.sh --revoke -d ${domain} - LOGI "Certificate revoked for domain: $domain" - else - echo "Invalid domain entered." - fi - fi - ssl_cert_issue_main + warp a ;; 3) - local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) - if [ -z "$domains" ]; then - echo "No certificates found to renew." - else - echo "Existing domains:" - echo "$domains" - read -rp "Please enter a domain from the list to renew the SSL certificate: " domain - if echo "$domains" | grep -qw "$domain"; then - ~/.acme.sh/acme.sh --renew -d ${domain} --force - LOGI "Certificate forcefully renewed for domain: $domain" - else - echo "Invalid domain entered." - fi - fi - ssl_cert_issue_main + warp y ;; 4) - local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) - if [ -z "$domains" ]; then - echo "No certificates found." - else - echo "Existing domains and their paths:" - for domain in $domains; do - local cert_path="/root/cert/${domain}/fullchain.pem" - local key_path="/root/cert/${domain}/privkey.pem" - if [[ -f "${cert_path}" && -f "${key_path}" ]]; then - echo -e "Domain: ${domain}" - echo -e "\tCertificate Path: ${cert_path}" - echo -e "\tPrivate Key Path: ${key_path}" - else - echo -e "Domain: ${domain} - Certificate or Key missing." - fi - done - fi - ssl_cert_issue_main - ;; - 5) - local domains=$(find /root/cert/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) - if [ -z "$domains" ]; then - echo "No certificates found." - else - echo "Available domains:" - echo "$domains" - read -rp "Please choose a domain to set the panel paths: " domain - - if echo "$domains" | grep -qw "$domain"; then - local webCertFile="/root/cert/${domain}/fullchain.pem" - local webKeyFile="/root/cert/${domain}/privkey.pem" - - if [[ -f "${webCertFile}" && -f "${webKeyFile}" ]]; then - /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" - echo "Panel paths set for domain: $domain" - echo " - Certificate File: $webCertFile" - echo " - Private Key File: $webKeyFile" - restart - else - echo "Certificate or private key not found for domain: $domain." - fi - else - echo "Invalid domain entered." - fi - fi - ssl_cert_issue_main - ;; - - *) - echo -e "${red}Invalid option. Please select a valid number.${plain}\n" - ssl_cert_issue_main + warp u ;; + *) echo "无效选项" ;; esac } -ssl_cert_issue() { - local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}') - local existing_port=$(/usr/local/x-ui/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 - if [ $? -ne 0 ]; then - LOGE "install acme failed, please check logs" - exit 1 - fi - fi - - # install socat second - case "${release}" in - ubuntu | debian | armbian) - apt update && apt install socat -y - ;; - centos | rhel | almalinux | rocky | ol) - yum -y update && yum -y install socat - ;; - fedora | amzn | virtuozzo) - dnf -y update && dnf -y install socat - ;; - arch | manjaro | parch) - pacman -Sy --noconfirm socat - ;; - *) - echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n" - exit 1 - ;; - esac - if [ $? -ne 0 ]; then - LOGE "install socat failed, please check logs" - exit 1 - else - LOGI "install socat succeed..." - fi - - # get the domain here, and we need to verify it - local domain="" - read -rp "Please enter your domain name: " domain - LOGD "Your domain is: ${domain}, checking it..." - - # check if there already exists a certificate - local currentCert=$(~/.acme.sh/acme.sh --list | tail -1 | awk '{print $1}') - if [ "${currentCert}" == "${domain}" ]; then - local certInfo=$(~/.acme.sh/acme.sh --list) - LOGE "System already has certificates for this domain. Cannot issue again. Current certificate details:" - LOGI "$certInfo" - exit 1 - else - 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" - else - rm -rf "$certPath" - 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 - LOGE "Your input ${WebPort} is invalid, will use default port 80." - WebPort=80 - fi - LOGI "Will use port: ${WebPort} to issue certificates. Please make sure this port is open." - - # issue the certificate - ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt - ~/.acme.sh/acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force - if [ $? -ne 0 ]; then - LOGE "Issuing certificate failed, please check logs." - rm -rf ~/.acme.sh/${domain} - exit 1 - else - LOGE "Issuing certificate succeeded, installing certificates..." - fi - - reloadCmd="x-ui restart" - - LOGI "Default --reloadcmd for ACME is: ${yellow}x-ui restart" - LOGI "This command will run on every certificate issue and renew." - read -rp "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd - if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then - echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; x-ui restart" - echo -e "${green}\t2.${plain} Input your own command" - echo -e "${green}\t0.${plain} Keep default reloadcmd" - read -rp "Choose an option: " choice - case "$choice" in - 1) - LOGI "Reloadcmd is: systemctl reload nginx ; x-ui restart" - reloadCmd="systemctl reload nginx ; x-ui restart" - ;; - 2) - LOGD "It's recommended to put x-ui restart at the end, so it won't raise an error if other services fails" - read -rp "Please enter your reloadcmd (example: systemctl reload nginx ; x-ui restart): " reloadCmd - LOGI "Your reloadcmd is: ${reloadCmd}" - ;; - *) - LOGI "Keep default reloadcmd" - ;; - esac - fi - - # install the certificate - ~/.acme.sh/acme.sh --installcert -d ${domain} \ - --key-file /root/cert/${domain}/privkey.pem \ - --fullchain-file /root/cert/${domain}/fullchain.pem --reloadcmd "${reloadCmd}" - - if [ $? -ne 0 ]; then - LOGE "Installing certificate failed, exiting." - rm -rf ~/.acme.sh/${domain} - exit 1 - else - LOGI "Installing certificate succeeded, enabling auto renew..." - fi - - # enable auto-renew - ~/.acme.sh/acme.sh --upgrade --auto-upgrade - if [ $? -ne 0 ]; then - LOGE "Auto renew failed, certificate details:" - ls -lah cert/* - chmod 755 $certPath/* - exit 1 - else - LOGI "Auto renew succeeded, certificate details:" - ls -lah cert/* - chmod 755 $certPath/* - 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" - local webKeyFile="/root/cert/${domain}/privkey.pem" - - if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then - /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" - LOGI "Panel paths set for domain: $domain" - LOGI " - Certificate File: $webCertFile" - LOGI " - Private Key File: $webKeyFile" - echo -e "${green}Access URL: https://${domain}:${existing_port}${existing_webBasePath}${plain}" - restart - else - LOGE "Error: Certificate or private key file not found for domain: $domain." - fi - else - LOGI "Skipping panel path setting." - fi -} - -ssl_cert_issue_CF() { - local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}') - local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}') - LOGI "****** Instructions for Use ******" - LOGI "Follow the steps below to complete the process:" - LOGI "1. Cloudflare Registered E-mail." - LOGI "2. Cloudflare Global API Key." - LOGI "3. The Domain Name." - LOGI "4. Once the certificate is issued, you will be prompted to set the certificate for the panel (optional)." - LOGI "5. The script also supports automatic renewal of the SSL certificate after installation." - - 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 - if [ $? -ne 0 ]; then - LOGE "Install acme failed, please check logs." - exit 1 - fi - fi - - CF_Domain="" - - LOGD "Please set a domain name:" - 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:" - read -rp "Input your key here: " CF_GlobalKey - LOGD "Your API key is: ${CF_GlobalKey}" - - LOGD "Please set up registered email:" - 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 - if [ $? -ne 0 ]; then - LOGE "Default CA, Let'sEncrypt fail, script exiting..." - exit 1 - fi - - 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..." - exit 1 - else - LOGI "Certificate issued successfully, Installing..." - fi - - # Install the certificate - certPath="/root/cert/${CF_Domain}" - if [ -d "$certPath" ]; then - rm -rf ${certPath} - fi - - mkdir -p ${certPath} - if [ $? -ne 0 ]; then - LOGE "Failed to create directory: ${certPath}" - exit 1 - fi - - reloadCmd="x-ui restart" - - LOGI "Default --reloadcmd for ACME is: ${yellow}x-ui restart" - LOGI "This command will run on every certificate issue and renew." - read -rp "Would you like to modify --reloadcmd for ACME? (y/n): " setReloadcmd - if [[ "$setReloadcmd" == "y" || "$setReloadcmd" == "Y" ]]; then - echo -e "\n${green}\t1.${plain} Preset: systemctl reload nginx ; x-ui restart" - echo -e "${green}\t2.${plain} Input your own command" - echo -e "${green}\t0.${plain} Keep default reloadcmd" - read -rp "Choose an option: " choice - case "$choice" in - 1) - LOGI "Reloadcmd is: systemctl reload nginx ; x-ui restart" - reloadCmd="systemctl reload nginx ; x-ui restart" - ;; - 2) - LOGD "It's recommended to put x-ui restart at the end, so it won't raise an error if other services fails" - read -rp "Please enter your reloadcmd (example: systemctl reload nginx ; x-ui restart): " reloadCmd - LOGI "Your reloadcmd is: ${reloadCmd}" - ;; - *) - LOGI "Keep default reloadcmd" - ;; - esac - fi - ~/.acme.sh/acme.sh --installcert -d ${CF_Domain} -d *.${CF_Domain} \ - --key-file ${certPath}/privkey.pem \ - --fullchain-file ${certPath}/fullchain.pem --reloadcmd "${reloadCmd}" - - if [ $? -ne 0 ]; then - LOGE "Certificate installation failed, script exiting..." - exit 1 - else - 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..." - exit 1 - else - LOGI "The certificate is installed and auto-renewal is turned on. Specific information is as follows:" - ls -lah ${certPath}/* - chmod 755 ${certPath}/* - 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" - local webKeyFile="${certPath}/privkey.pem" - - if [[ -f "$webCertFile" && -f "$webKeyFile" ]]; then - /usr/local/x-ui/x-ui cert -webCert "$webCertFile" -webCertKey "$webKeyFile" - LOGI "Panel paths set for domain: $CF_Domain" - LOGI " - Certificate File: $webCertFile" - LOGI " - Private Key File: $webKeyFile" - echo -e "${green}Access URL: https://${CF_Domain}:${existing_port}${existing_webBasePath}${plain}" - restart - else - LOGE "Error: Certificate or private key file not found for domain: $CF_Domain." - fi - else - LOGI "Skipping panel path setting." - fi - else - show_menu - fi +subconverter() { + bash <(curl -fsSL https://get.docker.com | bash -s docker) + ipv4=$(curl -s4m8 ip.p3terx.com -k | sed -n 1p) + docker run -d --name sub --restart always -p 18080:80 -p 25500:25500 -v /PATH/sub/conf:/usr/share/nginx/html/conf stilleshan/sub + echo -e "${yellow}【链接转换模块】安装完成!!!" + echo -e "${green}【订阅转换功能】访问地址为:${plain}${green}http://$ipv4:18080" + echo -e "${green}【后端服务】拉取地址为:${plain}${green}http://$ipv4:25500" + show_menu } 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 + # If not installed, install it + local pkg_manager="" + local speedtest_install_script="" + + if command -v dnf &>/dev/null; then + pkg_manager="dnf" + speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh" + elif command -v yum &>/dev/null; then + pkg_manager="yum" + speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh" + elif command -v apt-get &>/dev/null; then + pkg_manager="apt-get" + speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh" + elif command -v apt &>/dev/null; then + pkg_manager="apt" + speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh" + fi + + if [[ -z $pkg_manager ]]; then + echo "错误:找不到包管理器。 您可能需要手动安装 Speedtest" + return 1 else - # Fallback to using package managers - local pkg_manager="" - local speedtest_install_script="" - - if command -v dnf &>/dev/null; then - pkg_manager="dnf" - speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh" - elif command -v yum &>/dev/null; then - pkg_manager="yum" - speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.rpm.sh" - elif command -v apt-get &>/dev/null; then - pkg_manager="apt-get" - speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh" - elif command -v apt &>/dev/null; then - pkg_manager="apt" - speedtest_install_script="https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh" - fi - - if [[ -z $pkg_manager ]]; then - echo "Error: Package manager not found. You may need to install Speedtest manually." - return 1 - else - echo "Installing Speedtest using $pkg_manager..." - curl -s $speedtest_install_script | bash - $pkg_manager install -y speedtest - fi + curl -s $speedtest_install_script | bash + $pkg_manager install -y speedtest fi fi + # Run Speedtest speedtest } create_iplimit_jails() { - # Use default bantime if not passed => 30 minutes - local bantime="${1:-30}" + # Use default bantime if not passed => 15 minutes + local bantime="${1:-15}" # 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 + #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 @@ -1383,12 +1315,9 @@ actionunban = -D f2b- -s -j echo "\$(date +"%%Y/%%m/%%d %%H:%%M:%%S") UNBAN [Email] = [IP] = unbanned." >> ${iplimit_banned_log_path} [Init] -name = default -protocol = tcp -chain = INPUT EOF - echo -e "${green}Ip Limit jail files created with a bantime of ${bantime} minutes.${plain}" + echo -e "${green}使用 ${bantime} 分钟的禁止时间以创建的 IP Limit 限制文件。${plain}" } iplimit_remove_conflicts() { @@ -1401,35 +1330,27 @@ iplimit_remove_conflicts() { # 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" + echo -e "${yellow}消除系统环境中 [3x-ipl] 的冲突 (${file})!${plain}\n" fi done } -ip_validation() { - ipv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$" - ipv4_regex="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)$" -} - iplimit_main() { - echo -e "\n${green}\t1.${plain} Install Fail2ban and configure IP Limit" - echo -e "${green}\t2.${plain} Change Ban Duration" - echo -e "${green}\t3.${plain} Unban Everyone" - echo -e "${green}\t4.${plain} Ban Logs" - echo -e "${green}\t5.${plain} Ban an IP Address" - echo -e "${green}\t6.${plain} Unban an IP Address" - echo -e "${green}\t7.${plain} Real-Time Logs" - echo -e "${green}\t8.${plain} Service Status" - echo -e "${green}\t9.${plain} Service Restart" - echo -e "${green}\t10.${plain} Uninstall Fail2ban and IP Limit" - echo -e "${green}\t0.${plain} Back to Main Menu" - read -rp "Choose an option: " choice + echo -e "\n${green}\t1.${plain} 安装 Fail2ban 并配置 IP 限制" + echo -e "${green}\t2.${plain} 更改禁止期限" + echo -e "${green}\t3.${plain} 解禁所有 IP" + echo -e "${green}\t4.${plain} 查看日志" + echo -e "${green}\t5.${plain} Fail2ban 状态" + echo -e "${green}\t6.${plain} 重启 Fail2ban" + echo -e "${green}\t7.${plain} 卸载 Fail2ban" + echo -e "${green}\t0.${plain} 返回主菜单" + read -p "请输入选项: " choice case "$choice" in 0) show_menu ;; 1) - confirm "Proceed with installation of Fail2ban & IP Limit?" "y" + confirm "继续安装 Fail2ban 和 IP 限制?" "y" if [[ $? == 0 ]]; then install_iplimit else @@ -1437,79 +1358,46 @@ iplimit_main() { fi ;; 2) - read -rp "Please enter new Ban Duration in Minutes [default 30]: " NUM + read -rp "请输入新的禁令持续时间(以分钟为单位)[默认 30]: " NUM if [[ $NUM =~ ^[0-9]+$ ]]; then create_iplimit_jails ${NUM} systemctl restart fail2ban else - echo -e "${red}${NUM} is not a number! Please, try again.${plain}" + echo -e "${red}${NUM} 不是一个数字! 请再试一次.${plain}" fi iplimit_main ;; 3) - confirm "Proceed with Unbanning everyone from IP Limit jail?" "y" + confirm "继续解除所有人的 IP 限制禁令?" "y" if [[ $? == 0 ]]; then fail2ban-client reload --restart --unban 3x-ipl truncate -s 0 "${iplimit_banned_log_path}" - echo -e "${green}All users Unbanned successfully.${plain}" + echo -e "${green}所有用户已成功解封${plain}" iplimit_main else - echo -e "${yellow}Cancelled.${plain}" + echo -e "${yellow}已取消${plain}" fi iplimit_main ;; 4) show_banlog - iplimit_main ;; 5) - read -rp "Enter the IP address you want to ban: " ban_ip - ip_validation - if [[ $ban_ip =~ $ipv4_regex || $ban_ip =~ $ipv6_regex ]]; then - fail2ban-client set 3x-ipl banip "$ban_ip" - echo -e "${green}IP Address ${ban_ip} has been banned successfully.${plain}" - else - echo -e "${red}Invalid IP address format! Please try again.${plain}" - fi - iplimit_main + service fail2ban status ;; 6) - read -rp "Enter the IP address you want to unban: " unban_ip - ip_validation - if [[ $unban_ip =~ $ipv4_regex || $unban_ip =~ $ipv6_regex ]]; then - fail2ban-client set 3x-ipl unbanip "$unban_ip" - echo -e "${green}IP Address ${unban_ip} has been unbanned successfully.${plain}" - else - echo -e "${red}Invalid IP address format! Please try again.${plain}" - fi - iplimit_main + systemctl restart fail2ban ;; 7) - tail -f /var/log/fail2ban.log - iplimit_main - ;; - 8) - service fail2ban status - iplimit_main - ;; - 9) - systemctl restart fail2ban - iplimit_main - ;; - 10) remove_iplimit - iplimit_main - ;; - *) - echo -e "${red}Invalid option. Please select a valid number.${plain}\n" - iplimit_main ;; + *) echo "无效选项" ;; esac } install_iplimit() { if ! command -v fail2ban-client &>/dev/null; then - echo -e "${green}Fail2ban is not installed. Installing now...!${plain}\n" + echo -e "${green}未安装 Fail2ban。正在安装...!${plain}\n" # Check the OS and install necessary packages case "${release}" in @@ -1523,33 +1411,33 @@ install_iplimit() { debian | armbian) apt update && apt install fail2ban -y ;; - centos | rhel | almalinux | rocky | ol) + centos | almalinux | rocky | oracle) yum update -y && yum install epel-release -y yum -y install fail2ban ;; - fedora | amzn | virtuozzo) + fedora) dnf -y update && dnf -y install fail2ban ;; arch | manjaro | parch) pacman -Syu --noconfirm fail2ban ;; *) - echo -e "${red}Unsupported operating system. Please check the script and install the necessary packages manually.${plain}\n" + echo -e "${red}不支持的操作系统,请检查脚本并手动安装必要的软件包.${plain}\n" exit 1 ;; esac if ! command -v fail2ban-client &>/dev/null; then - echo -e "${red}Fail2ban installation failed.${plain}\n" + echo -e "${red}Fail2ban 安装失败${plain}\n" exit 1 fi - echo -e "${green}Fail2ban installed successfully!${plain}\n" + echo -e "${green}Fail2ban 安装成功!${plain}\n" else - echo -e "${yellow}Fail2ban is already installed.${plain}\n" + echo -e "${yellow}Fail2ban 已安装${plain}\n" fi - echo -e "${green}Configuring IP Limit...${plain}\n" + echo -e "${green}配置 IP 限制中...${plain}\n" # make sure there's no conflict for jail files iplimit_remove_conflicts @@ -1571,27 +1459,28 @@ install_iplimit() { # Launching fail2ban if ! systemctl is-active --quiet fail2ban; then systemctl start fail2ban + systemctl enable fail2ban else systemctl restart fail2ban fi systemctl enable fail2ban - echo -e "${green}IP Limit installed and configured successfully!${plain}\n" + echo -e "${green}IP 限制安装并配置成功!${plain}\n" before_show_menu } remove_iplimit() { - echo -e "${green}\t1.${plain} Only remove IP Limit configurations" - echo -e "${green}\t2.${plain} Uninstall Fail2ban and IP Limit" - echo -e "${green}\t0.${plain} Back to Main Menu" - read -rp "Choose an option: " num + echo -e "${green}\t1.${plain} 仅删除 IP 限制配置" + echo -e "${green}\t2.${plain} 卸载 Fail2ban 和 IP 限制" + echo -e "${green}\t0.${plain} 终止" + read -p "请输入选项: " num case "$num" in 1) rm -f /etc/fail2ban/filter.d/3x-ipl.conf rm -f /etc/fail2ban/action.d/3x-ipl.conf rm -f /etc/fail2ban/jail.d/3x-ipl.conf systemctl restart fail2ban - echo -e "${green}IP Limit removed successfully!${plain}\n" + echo -e "${green}IP 限制成功解除!${plain}\n" before_show_menu ;; 2) @@ -1603,176 +1492,109 @@ remove_iplimit() { apt-get purge -y fail2ban -y apt-get autoremove -y ;; - centos | rhel | almalinux | rocky | ol) + centos | almalinux | rocky | oracle) yum remove fail2ban -y yum autoremove -y ;; - fedora | amzn | virtuozzo) + fedora) dnf remove fail2ban -y dnf autoremove -y ;; - arch | manjaro | parch) + arch | manjaro) pacman -Rns --noconfirm fail2ban ;; *) - echo -e "${red}Unsupported operating system. Please uninstall Fail2ban manually.${plain}\n" + echo -e "${red}不支持的操作系统,请手动卸载 Fail2ban.${plain}\n" exit 1 ;; esac - echo -e "${green}Fail2ban and IP Limit removed successfully!${plain}\n" + echo -e "${green}Fail2ban 和 IP 限制已成功删除!${plain}\n" before_show_menu ;; 0) - show_menu + echo -e "${yellow}已取消${plain}\n" + iplimit_main ;; *) - echo -e "${red}Invalid option. Please select a valid number.${plain}\n" + echo -e "${red}无效选项。 请选择一个有效的选项。${plain}\n" remove_iplimit ;; esac } -SSH_port_forwarding() { - local server_ip=$(curl -s --max-time 3 https://api.ipify.org) - if [ -z "$server_ip" ]; then - server_ip=$(curl -s --max-time 3 https://4.ident.me) - fi - local existing_webBasePath=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'webBasePath: .+' | awk '{print $2}') - local existing_port=$(/usr/local/x-ui/x-ui setting -show true | grep -Eo 'port: .+' | awk '{print $2}') - local existing_listenIP=$(/usr/local/x-ui/x-ui setting -getListen true | grep -Eo 'listenIP: .+' | awk '{print $2}') - local existing_cert=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'cert: .+' | awk '{print $2}') - local existing_key=$(/usr/local/x-ui/x-ui setting -getCert true | grep -Eo 'key: .+' | awk '{print $2}') - - local config_listenIP="" - local listen_choice="" - - if [[ -n "$existing_cert" && -n "$existing_key" ]]; then - echo -e "${green}Panel is secure with SSL.${plain}" - before_show_menu - fi - if [[ -z "$existing_cert" && -z "$existing_key" && (-z "$existing_listenIP" || "$existing_listenIP" == "0.0.0.0") ]]; then - echo -e "\n${red}Warning: No Cert and Key found! The panel is not secure.${plain}" - echo "Please obtain a certificate or set up SSH port forwarding." - fi - - if [[ -n "$existing_listenIP" && "$existing_listenIP" != "0.0.0.0" && (-z "$existing_cert" && -z "$existing_key") ]]; then - echo -e "\n${green}Current SSH Port Forwarding Configuration:${plain}" - echo -e "Standard SSH command:" - echo -e "${yellow}ssh -L 2222:${existing_listenIP}:${existing_port} root@${server_ip}${plain}" - echo -e "\nIf using SSH key:" - echo -e "${yellow}ssh -i -L 2222:${existing_listenIP}:${existing_port} root@${server_ip}${plain}" - echo -e "\nAfter connecting, access the panel at:" - echo -e "${yellow}http://localhost:2222${existing_webBasePath}${plain}" - fi - - echo -e "\nChoose an option:" - echo -e "${green}1.${plain} Set listen IP" - echo -e "${green}2.${plain} Clear listen IP" - echo -e "${green}0.${plain} Back to Main Menu" - read -rp "Choose an option: " num - - case "$num" in - 1) - if [[ -z "$existing_listenIP" || "$existing_listenIP" == "0.0.0.0" ]]; then - echo -e "\nNo listenIP configured. Choose an option:" - echo -e "1. Use default IP (127.0.0.1)" - echo -e "2. Set a custom IP" - read -rp "Select an option (1 or 2): " listen_choice - - config_listenIP="127.0.0.1" - [[ "$listen_choice" == "2" ]] && read -rp "Enter custom IP to listen on: " config_listenIP - - /usr/local/x-ui/x-ui setting -listenIP "${config_listenIP}" >/dev/null 2>&1 - echo -e "${green}listen IP has been set to ${config_listenIP}.${plain}" - echo -e "\n${green}SSH Port Forwarding Configuration:${plain}" - echo -e "Standard SSH command:" - echo -e "${yellow}ssh -L 2222:${config_listenIP}:${existing_port} root@${server_ip}${plain}" - echo -e "\nIf using SSH key:" - echo -e "${yellow}ssh -i -L 2222:${config_listenIP}:${existing_port} root@${server_ip}${plain}" - echo -e "\nAfter connecting, access the panel at:" - echo -e "${yellow}http://localhost:2222${existing_webBasePath}${plain}" - restart - else - config_listenIP="${existing_listenIP}" - echo -e "${green}Current listen IP is already set to ${config_listenIP}.${plain}" - fi - ;; - 2) - /usr/local/x-ui/x-ui setting -listenIP 0.0.0.0 >/dev/null 2>&1 - echo -e "${green}Listen IP has been cleared.${plain}" - restart - ;; - 0) - show_menu - ;; - *) - echo -e "${red}Invalid option. Please select a valid number.${plain}\n" - SSH_port_forwarding - ;; - esac -} - show_usage() { - echo -e "┌───────────────────────────────────────────────────────┐ -│ ${blue}x-ui control menu usages (subcommands):${plain} │ -│ │ -│ ${blue}x-ui${plain} - Admin Management Script │ -│ ${blue}x-ui start${plain} - Start │ -│ ${blue}x-ui stop${plain} - Stop │ -│ ${blue}x-ui restart${plain} - Restart │ -│ ${blue}x-ui status${plain} - Current Status │ -│ ${blue}x-ui settings${plain} - Current Settings │ -│ ${blue}x-ui enable${plain} - Enable Autostart on OS Startup │ -│ ${blue}x-ui disable${plain} - Disable Autostart on OS Startup │ -│ ${blue}x-ui log${plain} - Check logs │ -│ ${blue}x-ui banlog${plain} - Check Fail2ban ban logs │ -│ ${blue}x-ui update${plain} - Update │ -│ ${blue}x-ui legacy${plain} - legacy version │ -│ ${blue}x-ui install${plain} - Install │ -│ ${blue}x-ui uninstall${plain} - Uninstall │ -└───────────────────────────────────────────────────────┘" + echo -e " ---------------------" + echo -e " |${green}3X-UI 控制菜单用法 ${plain}|${plain}" + echo -e " | ${yellow}一个更好的面板 ${plain}|${plain}" + echo -e " | ${yellow}基于Xray Core构建 ${plain}|${plain}" + echo -e "--------------------------------------------" + echo -e "x-ui - 进入管理脚本" + echo -e "x-ui start - 启动 3x-ui 面板" + echo -e "x-ui stop - 关闭 3x-ui 面板" + echo -e "x-ui restart - 重启 3x-ui 面板" + echo -e "x-ui status - 查看 3x-ui 状态" + echo -e "x-ui settings - 查看当前设置信息" + echo -e "x-ui enable - 启用 3x-ui 开机启动" + echo -e "x-ui disable - 禁用 3x-ui 开机启动" + echo -e "x-ui log - 查看 3x-ui 运行日志" + echo -e "x-ui banlog - 检查 Fail2ban 禁止日志" + echo -e "x-ui update - 更新 3x-ui 面板" + echo -e "x-ui custom - 自定义 3x-ui 版本" + echo -e "x-ui install - 安装 3x-ui 面板" + echo -e "x-ui uninstall - 卸载 3x-ui 面板" + echo -e "--------------------------------------------" } 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} Check Status │ -│ ${green}15.${plain} Logs Management │ -│────────────────────────────────────────────────│ -│ ${green}16.${plain} Enable Autostart │ -│ ${green}17.${plain} Disable Autostart │ -│────────────────────────────────────────────────│ -│ ${green}18.${plain} SSL Certificate Management │ -│ ${green}19.${plain} Cloudflare SSL Certificate │ -│ ${green}20.${plain} IP Limit Management │ -│ ${green}21.${plain} Firewall Management │ -│ ${green}22.${plain} SSH Port Forwarding Management │ -│────────────────────────────────────────────────│ -│ ${green}23.${plain} Enable BBR │ -│ ${green}24.${plain} Update Geo Files │ -│ ${green}25.${plain} Speedtest by Ookla │ -╚────────────────────────────────────────────────╝ +—————————————————————— + ${green}3X-UI 面板管理脚本${plain} + ${yellow} 一个更好的面板${plain} + ${yellow} 基于Xray Core构建${plain} +—————————————————————— + ${green}0.${plain} 退出脚本 + ${green}1.${plain} 安装面板 + ${green}2.${plain} 更新面板 + ${green}3.${plain} 更新菜单项 + ${green}4.${plain} 自定义版本 + ${green}5.${plain} 卸载面板 +—————————————————————— + ${green}6.${plain} 重置用户名、密码和Secret Token + ${green}7.${plain} 修改访问路径 + ${green}8.${plain} 重置面板设置 + ${green}9.${plain} 修改面板端口 + ${green}10.${plain} 查看面板设置 +—————————————————————— + ${green}11.${plain} 启动面板 + ${green}12.${plain} 关闭面板 + ${green}13.${plain} 重启面板 + ${green}14.${plain} 检查面板状态 + ${green}15.${plain} 检查面板日志 +—————————————————————— + ${green}16.${plain} 启用开机启动 + ${green}17.${plain} 禁用开机启动 +—————————————————————— + ${green}18.${plain} SSL 证书管理 + ${green}19.${plain} CF SSL 证书 + ${green}20.${plain} IP 限制管理 + ${green}21.${plain} 防火墙管理 +—————————————————————— + ${green}22.${plain} 启用 BBR + ${green}23.${plain} 更新 Geo 文件 + ${green}24.${plain} Speedtest by Ookla + ${green}25.${plain} 安装订阅转换 +—————————————————————— + ${green}若在使用过程中有任何问题${plain} + ${yellow}请加入〔3X-UI〕中文交流群${plain} + ${red}https://t.me/XUI_CN ${yellow}截图进行反馈${plain} + ${green}〔3X-UI〕优化版项目地址${plain} + ${yellow}https://github.com/xeefei/3x-ui${plain} + ${green}详细〔安装配置〕教程${plain} + ${yellow}https://xeefei.github.io/xufei/2024/05/3x-ui${plain} +—————————————————————— " show_status - echo && read -rp "Please enter your selection [0-25]: " num + echo && read -p "请输入选项 [0-25]: " num case "${num}" in 0) @@ -1788,7 +1610,7 @@ show_menu() { check_install && update_menu ;; 4) - check_install && legacy_version + check_install && custom_version ;; 5) check_install && uninstall @@ -1842,19 +1664,19 @@ show_menu() { firewall_menu ;; 22) - SSH_port_forwarding - ;; - 23) bbr_menu ;; - 24) + 23) update_geo ;; - 25) + 24) run_speedtest ;; + 25) + subconverter + ;; *) - LOGE "Please enter the correct number [0-25]" + LOGE "请输入正确的数字选项 [0-25]" ;; esac } @@ -1891,8 +1713,8 @@ if [[ $# > 0 ]]; then "update") check_install 0 && update 0 ;; - "legacy") - check_install 0 && legacy_version 0 + "custom") + check_install 0 && custom_version 0 ;; "install") check_uninstall 0 && install 0