3x-ui/docs/x-ui-logic.md

938 lines
27 KiB
Markdown
Raw Normal View History

# x-ui.sh 逻辑文档
## 概述
`x-ui.sh` 是 3x-ui 面板的管理脚本,提供 26 个交互式菜单选项和 15 个子命令涵盖面板的安装、更新、卸载、凭据管理、服务控制、SSL 证书、防火墙、Fail2ban IP 限制、BBR 加速、Geo 文件更新等功能。
---
## 全局配置
### 颜色变量
| 变量 | 值 | 用途 |
|---------|----------------|----------|
| `red` | `\033[0;31m` | 红色 |
| `green` | `\033[0;32m` | 绿色 |
| `blue` | `\033[0;34m` | 蓝色 |
| `yellow`| `\033[0;33m` | 黄色 |
| `plain` | `\033[0m` | 重置 |
### 日志函数
| 函数 | 前缀 | 用途 |
|---------|-----------|------------|
| `LOGD()` | `[调试]` | 调试信息 |
| `LOGE()` | `[错误]` | 错误信息 |
| `LOGI()` | `[信息]` | 普通信息 |
### 路径变量
| 变量 | 默认值 | 说明 |
|-------------------------|---------------------------|-------------------------|
| `xui_folder` | `/usr/local/x-ui` | x-ui 安装目录 |
| `xui_service` | `/etc/systemd/system` | systemd 服务文件目录 |
| `log_folder` | `/var/log/x-ui` | 日志目录 |
| `iplimit_log_path` | `.../3xipl.log` | IP 限制日志 |
| `iplimit_banned_log_path`| `.../3xipl-banned.log` | IP 封禁日志 |
### 辅助函数
| 函数 | 功能 |
|-----------------------|----------------------------------------------|
| `confirm()` | 通用确认提示,支持自定义默认值 |
| `confirm_restart()` | 确认后重启面板(重启 x-ui 也会重启 xray |
| `before_show_menu()` | 按回车返回主菜单 |
| `gen_random_string()` | 通过 openssl 生成指定长度的随机字母数字字符串 |
| `is_port_in_use()` | 端口占用检测ss → netstat → lsof |
| `is_ipv4/is_ipv6/is_ip/is_domain()` | IP/域名格式验证 |
---
## 入口流程
```
x-ui.sh 被执行
├─ 检查 root 权限
├─ 检测操作系统发行版和版本号
├─ 初始化路径和日志目录
├─ 有命令行参数 → 执行对应子命令(不显示菜单)
└─ 无参数 → 显示交互式菜单 show_menu()
├─ 显示当前状态(运行/停止/未安装 + 开机自启 + xray 状态)
├─ 读取用户输入 [0-26]
└─ 根据选择调用对应功能
```
---
## 主菜单 (show_menu)
```
╔────────────────────────────────────────────────╗
│ 0. 退出脚本 │
│────────────────────────────────────────────────│
│ 1. 安装 2. 更新 3. 更新菜单 │
│ 4. 安装旧版本 5. 卸载 │
│────────────────────────────────────────────────│
│ 6. 重置用户名和密码 7. 重置 Web 路径 │
│ 8. 重置设置 9. 修改端口 │
│ 10. 查看当前设置 │
│────────────────────────────────────────────────│
│ 11. 启动 12. 停止 13. 重启 │
│ 14. 重启 Xray 15. 查看状态 │
│ 16. 日志管理 │
│────────────────────────────────────────────────│
│ 17. 设置开机自启 18. 取消开机自启 │
│────────────────────────────────────────────────│
│ 19. SSL 证书管理 20. Cloudflare SSL │
│ 21. IP 限制管理 22. 防火墙管理 │
│ 23. SSH 端口转发管理 │
│────────────────────────────────────────────────│
│ 24. BBR 管理 25. 更新 Geo 文件 │
│ 26. 网速测试 (Speedtest) │
╚────────────────────────────────────────────────╝
```
大部分选项在执行前调用 `check_install`(检查面板是否已安装)或 `check_uninstall`(检查面板是否未安装),防止误操作。
---
## 状态检测函数
| 函数 | 返回值 | 逻辑 |
|------------------------|---------------------------|-------------------------------------------|
| `check_status()` | 0=运行中, 1=未运行, 2=未安装 | Alpine 检查 init.d其他检查 systemd |
| `check_enabled()` | 0=已启用, 1=未启用 | Alpine 检查 rc-update其他检查 systemctl |
| `check_xray_status()` | 0=运行中, 1=未运行 | ps 查找 xray-linux 进程 |
| `check_install()` | 前置检查 | 未安装则提示并返回菜单 |
| `check_uninstall()` | 前置检查 | 已安装则提示"勿重复安装"并返回菜单 |
---
## 菜单选项详解
### 选项 0退出脚本
```bash
exit 0
```
直接退出,无额外逻辑。
---
### 选项 1安装
**函数**`install()`
```
下载并执行 install.sh从 GitHub raw 文件)
└─ 成功后自动调用 start()
```
- 执行 `bash <(curl -Ls https://raw.githubusercontent.com/Sora39831/3x-ui/main/install.sh)`
- 安装成功后自动启动面板
---
### 选项 2更新
**函数**`update()`
```
确认提示:"更新所有 x-ui 组件到最新版本,数据不会丢失"
├─ 取消 → 返回菜单
└─ 确认 → 执行 update.sh从 GitHub 下载)
└─ 成功 → "更新完成,面板已自动重启"
```
---
### 选项 3更新菜单
**函数**`update_menu()`
```
确认提示
└─ 确认 → 下载最新 x-ui.sh 到 /usr/bin/x-ui
└─ 成功 → "更新成功" 并 exit 0
```
仅更新管理脚本自身,不影响面板程序。
---
### 选项 4安装旧版本
**函数**`legacy_version()`
```
提示用户输入版本号(如 2.4.0
├─ 空 → 退出
└─ 有效 → 执行对应版本的 install.sh传入版本参数
```
- 下载指定 tag 的 install.sh`v$tag_version/install.sh`
- 传入参数 `v$tag_version` 进行安装
- install.sh 内部会验证版本 ≥ v2.3.5
---
### 选项 5卸载
**函数**`uninstall()`
```
确认:"卸载面板xray 也会被卸载!"(默认 n
├─ 取消 → 返回菜单
└─ 确认 →
Alpine: rc-service stop → rc-update del → rm init.d
其他: systemctl stop → disable → rm service → daemon-reload → reset-failed
删除 /etc/x-ui/ 和 ${xui_folder}/
显示重装命令
删除脚本自身trap SIGTERM → rm $0
```
---
### 选项 6重置用户名和密码
**函数**`reset_user()`
```
确认提示(默认 n
└─ 确认 →
输入用户名(默认随机 10 位)
输入密码(默认随机 18 位)
询问是否禁用双因素认证
├─ 是 → -resetTwoFactor true
└─ 否 → -resetTwoFactor false
应用设置x-ui setting -username ... -password ...
确认后重启面板
```
---
### 选项 7重置 Web 路径
**函数**`reset_webbasepath()`
```
确认提示
└─ 确认 → 生成随机 18 位字符串
应用x-ui setting -webBasePath ...
重启面板
```
---
### 选项 8重置设置
**函数**`reset_config()`
```
确认:"重置所有面板设置?账户数据不会丢失,用户名和密码不会改变"(默认 n
└─ 确认 → x-ui setting -reset
重启面板
```
仅重置面板配置,不影响账户数据库。
---
### 选项 9修改端口
**函数**`set_port()`
```
输入端口号 [1-65535]
├─ 空 → 取消
└─ 有效 → x-ui setting -port ${port}
确认后重启面板
```
---
### 选项 10查看当前设置
**函数**`check_config()`
```
获取面板设置x-ui setting -show true
获取公网 IPapi.ipify.org → 4.ident.me
检查是否有证书:
├─ 有证书 → 从证书路径提取域名,显示 https://域名:端口/路径
└─ 无证书 →
显示警告
询问是否为 IP 生成 SSL 证书
├─ 是 → 停止面板 → ssl_cert_issue_for_ip() → 启动面板
└─ 否 → 显示 http://IP:端口/路径,建议使用选项 19
```
---
### 选项 11启动
**函数**`start()`
```
检查当前状态
├─ 运行中 → "面板正在运行,无需重复启动"
└─ 未运行 →
Alpine: rc-service x-ui start
其他: systemctl start x-ui
等待 2 秒后再次检查状态
├─ 成功 → "x-ui 启动成功"
└─ 失败 → "面板启动失败,可能是因为启动时间超过两秒"
```
---
### 选项 12停止
**函数**`stop()`
```
检查当前状态
├─ 已停止 → "面板已停止,无需重复停止!"
└─ 运行中 →
Alpine: rc-service x-ui stop
其他: systemctl stop x-ui
等待 2 秒后检查状态
├─ 成功 → "x-ui 和 xray 已停止"
└─ 失败 → "面板停止失败"
```
---
### 选项 13重启
**函数**`restart()`
```
Alpine: rc-service x-ui restart
其他: systemctl restart x-ui
等待 2 秒后检查状态
├─ 成功 → "x-ui 和 xray 重启成功"
└─ 失败 → "面板重启失败"
```
---
### 选项 14重启 Xray
**函数**`restart_xray()`
```
systemctl reload x-ui ← 发送 reload 信号,不重启面板本身
"已发送重启信号,请查看日志确认"
等待 2 秒 → 显示 xray 运行状态
```
与选项 13 的区别:选项 13 重启整个 x-ui 服务,选项 14 仅重载 xray-core。
---
### 选项 15查看状态
**函数**`status()`
```
Alpine: rc-service x-ui status
其他: systemctl status x-ui -l
```
显示完整的 systemd/服务状态信息。
---
### 选项 16日志管理
**函数**`show_log()`
```
Alpine:
1. 调试日志 → grep 'x-ui[' /var/log/messages
0. 返回
其他 (systemd):
1. 调试日志 → journalctl -u x-ui -e --no-pager -f -p debug
2. 清除所有日志 → journalctl --rotate → --vacuum-time=1s → 重启面板
0. 返回
```
---
### 选项 17设置开机自启
**函数**`enable()`
```
Alpine: rc-update add x-ui default
其他: systemctl enable x-ui
```
---
### 选项 18取消开机自启
**函数**`disable()`
```
Alpine: rc-update del x-ui
其他: systemctl disable x-ui
```
---
### 选项 19SSL 证书管理
**函数**`ssl_cert_issue_main()` — 子菜单入口
#### 子菜单
```
1. 获取 SSL域名
2. 吊销证书
3. 强制续期
4. 查看已有域名
5. 为面板设置证书路径
6. 为 IP 地址获取 SSL6 天证书,自动续期)
0. 返回主菜单
```
#### 子选项 1获取 SSL域名证书
**函数**`ssl_cert_issue()`
```
检查/安装 acme.sh
按发行版安装 socat
获取并验证域名(循环直到有效)
检查是否已有该域名的证书acme.sh --list
创建证书目录 /root/cert/${domain}/
选择端口(默认 80
签发证书:
acme.sh --issue -d ${domain} --listen-v6 --standalone --httpport ${WebPort} --force
↳ 失败 → 清理并退出
设置 reloadcmd
默认x-ui restart
可选systemctl reload nginx ; x-ui restart
可选:自定义命令
安装证书:
acme.sh --installcert
--key-file /root/cert/${domain}/privkey.pem
--fullchain-file /root/cert/${domain}/fullchain.pem
--reloadcmd ${reloadCmd}
启用自动续期acme.sh --upgrade --auto-upgrade
设置文件权限privkey.pem → 600, fullchain.pem → 644
询问是否为面板设置证书:
├─ 是 → x-ui cert -webCert ... -webCertKey ... → 重启
└─ 否 → 跳过
```
#### 子选项 2吊销证书
```
列出 /root/cert/ 下所有域名目录
选择域名 → acme.sh --revoke -d ${domain}
```
#### 子选项 3强制续期
```
列出所有域名
选择域名 → acme.sh --renew -d ${domain} --force
```
#### 子选项 4查看已有域名
```
遍历 /root/cert/ 下的域名目录
显示每个域名的 fullchain.pem 和 privkey.pem 路径
缺失文件的标记为"证书或密钥缺失"
```
#### 子选项 5为面板设置证书路径
```
列出所有域名
选择域名 → 验证文件存在
x-ui cert -webCert ... -webCertKey ...
重启面板
```
#### 子选项 6为 IP 地址获取 SSL
**函数**`ssl_cert_issue_for_ip()`
```
获取服务器公网 IPapi.ipify.org → 4.ident.me
询问是否包含 IPv6 地址
检查/安装 acme.sh
按发行版安装 socat
创建证书目录 /root/cert/ip/
构建域名参数:-d ${server_ip} [-d ${ipv6}]
选择 HTTP-01 监听端口(默认 80
└─ 循环检测端口占用,被占用则提示换端口
签发证书:
acme.sh --issue
-d ${server_ip} [-d ${ipv6}]
--standalone --server letsencrypt
--certificate-profile shortlived
--days 6 --httpport ${WebPort} --force
安装证书(不依赖退出码,通过检查文件判断成功)
启用自动续期
设置文件权限
为面板设置证书路径 → 显示 https://IP:端口/路径 → 重启面板
```
---
### 选项 20Cloudflare SSL 证书
**函数**`ssl_cert_issue_CF()`
```
显示使用说明(需要:邮箱、全局 API 密钥、域名)
确认提示
检查/安装 acme.sh
输入域名 (CF_Domain)
输入 API 密钥 (CF_GlobalKey)
输入注册邮箱 (CF_AccountEmail)
设置 CA 为 Let's Encrypt
导出环境变量CF_Key, CF_Email
签发通配符证书:
acme.sh --issue --dns dns_cf -d ${domain} -d *.${domain} --force
↳ 使用 Cloudflare DNS 验证
创建证书目录 /root/cert/${domain}/
设置 reloadcmd同域名证书流程
安装证书(含 *.${domain} 通配符)
启用自动续期
询问是否为面板设置证书 → 同域名证书流程
```
**特点**:支持通配符证书 `*.domain.com`,不需要开放 80 端口(使用 DNS 验证)。
---
### 选项 21IP 限制管理Fail2ban
**函数**`iplimit_main()` — 子菜单入口
#### 子菜单
```
1. 安装 Fail2ban 并配置 IP 限制
2. 修改封禁时长
3. 解封所有人
4. 封禁日志
5. 封禁指定 IP 地址
6. 解封指定 IP 地址
7. 实时日志
8. 服务状态
9. 重启服务
10. 卸载 Fail2ban 和 IP 限制
0. 返回主菜单
```
#### 子选项 1安装 Fail2ban
**函数**`install_iplimit()`
```
检查 Fail2ban 是否已安装
└─ 未安装 → 按发行版安装:
Ubuntu ≥ 24: 额外安装 python3-pip + pyasynchat
Debian ≥ 12: 额外安装 python3-systemd
CentOS 7: 先装 epel-release
清除 jail 配置冲突iplimit_remove_conflicts
创建日志文件3xipl.log, 3xipl-banned.log
创建 jail 配置create_iplimit_jails
启动并启用 Fail2ban 服务
```
**Jail 配置详情** (`create_iplimit_jails`)
```ini
# /etc/fail2ban/jail.d/3x-ipl.conf
[3x-ipl]
enabled=true
backend=auto
filter=3x-ipl
action=3x-ipl
logpath=/var/log/x-ui/3xipl.log
maxretry=2
findtime=32
bantime=30m # 默认 30 分钟,可通过子选项 2 修改
```
**过滤器**:匹配 `[LIMIT_IP] Email=... || Disconnecting OLD IP=... || Timestamp=...` 格式的日志行。
**动作**:使用 iptables 封禁/解封 IP同时写入封禁日志文件。
#### 子选项 2修改封禁时长
```
输入新的封禁时长(分钟)
重新生成 jail 配置 → 重启 Fail2ban
```
#### 子选项 3解封所有人
```
fail2ban-client reload --restart --unban 3x-ipl
清空封禁日志文件
```
#### 子选项 5/6手动封禁/解封 IP
```
输入 IP 地址 → 正则验证IPv4/IPv6
fail2ban-client set 3x-ipl banip/unbanip "$ip"
```
#### 子选项 10卸载
```
选项 1仅移除 IP 限制配置(保留 Fail2ban
删除 filter.d/3x-ipl.conf, action.d/3x-ipl.conf, jail.d/3x-ipl.conf
重启 Fail2ban
选项 2完全卸载
删除 /etc/fail2ban
停止服务
按发行版卸载 fail2ban 包 + autoremove
```
---
### 选项 22防火墙管理
**函数**`firewall_menu()` — 子菜单入口(基于 UFW
#### 子菜单
```
1. 安装防火墙
2. 端口列表 [带编号]
3. 开放端口
4. 删除列表中的端口
5. 启用防火墙
6. 禁用防火墙
7. 防火墙状态
0. 返回主菜单
```
#### 子选项 1安装防火墙
**函数**`install_firewall()`
```
检查 ufw 是否安装 → 未安装则 apt-get install ufw
检查防火墙是否激活 → 未激活则:
ufw allow ssh
ufw allow http
ufw allow https
ufw allow 2053/tcp ← webPort
ufw allow 2096/tcp ← subport
ufw --force enable
```
#### 子选项 3开放端口
**函数**`open_ports()`
```
输入端口(逗号分隔或范围,如 80,443,2053 或 400-500
验证输入格式
逐个处理:
范围 → ufw allow start:end/tcp + ufw allow start:end/udp
单端口 → ufw allow port
确认显示已开放的端口
```
#### 子选项 4删除端口
**函数**`delete_ports()`
```
显示当前规则ufw status numbered
选择删除方式:
1. 按规则编号删除 → ufw delete $number
2. 按端口号删除 → ufw delete allow $port
确认显示已删除的端口
```
**注意**:原始代码中选项 4 有一个已知 bug`firewall_wall_menu` 应为 `firewall_menu`),这会导致删除端口后不返回菜单。
---
### 选项 23SSH 端口转发管理
**函数**`SSH_port_forwarding()`
```
获取服务器公网 IP多 API 轮询)
读取当前面板设置:
- webBasePath, port, listenIP, cert, key
判断状态:
├─ 已有证书+密钥 → "面板已配置 SSL安全" → 返回
├─ 无证书且 listenIP 为空或 0.0.0.0 → "面板不安全" 警告
└─ listenIP 已设置且非 0.0.0.0 → 显示 SSH 转发命令
子菜单:
1. 设置监听 IP
├─ 默认 127.0.0.1 或自定义
├─ x-ui setting -listenIP ${ip}
└─ 显示 SSH 转发命令:
ssh -L 2222:${listenIP}:${port} root@${server_ip}
访问 http://localhost:2222${webBasePath}
2. 清除监听 IP
└─ x-ui setting -listenIP 0.0.0.0 → 重启
0. 返回
```
**用途**:将面板绑定到 127.0.0.1,只能通过 SSH 隧道访问,提高安全性。
---
### 选项 24BBR 管理
**函数**`bbr_menu()` — 子菜单入口
#### 子菜单
```
1. 启用 BBR
2. 禁用 BBR
0. 返回主菜单
```
#### 启用 BBR
**函数**`enable_bbr()`
```
检查是否已启用tcp_congestion_control == bbr 且 default_qdisc 为 fq/cake
├─ 已启用 → 直接返回
└─ 未启用 →
有 /etc/sysctl.d/ →
创建 /etc/sysctl.d/99-bbr-x-ui.conf
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
注释 sysctl.conf 中的旧设置
sysctl --system
无 /etc/sysctl.d/ →
直接修改 /etc/sysctl.conf
sysctl -p
验证tcp_congestion_control == bbr → "BBR 已成功启用"
```
**特性**:启用前会备份当前设置(写入注释行 `#旧qdisc:旧拥塞控制`),以便禁用时恢复。
#### 禁用 BBR
**函数**`disable_bbr()`
```
检查是否已启用 → 未启用则返回
有 99-bbr-x-ui.conf →
读取备份的旧设置
恢复 net.core.default_qdisc 和 net.ipv4.tcp_congestion_control
删除配置文件
sysctl --system
无 99-bbr-x-ui.conf →
将 sysctl.conf 中的 fq→pfifo_fast, bbr→cubic
sysctl -p
验证tcp_congestion_control != bbr → "BBR 已成功替换为 CUBIC"
```
---
### 选项 25更新 Geo 文件
**函数**`update_geo()` — 子菜单入口
#### 子菜单
```
1. Loyalsoldier (geoip.dat, geosite.dat)
2. chocolate4u (geoip_IR.dat, geosite_IR.dat)
3. runetfreedom (geoip_RU.dat, geosite_RU.dat)
4. 全部更新
0. 返回主菜单
```
#### 数据源
| 选项 | 数据源 | 文件 | 用途 |
|------|---------------------------------------|------------------------------|------------------|
| 1 | Loyalsoldier/v2ray-rules-dat | geoip.dat, geosite.dat | 通用规则 |
| 2 | chocolate4u/Iran-v2ray-rules | geoip_IR.dat, geosite_IR.dat | 伊朗规则 |
| 3 | runetfreedom/russia-v2ray-rules-dat | geoip_RU.dat, geosite_RU.dat | 俄罗斯规则 |
| 4 | 以上全部 | 全部 6 个文件 | 一键更新 |
**下载逻辑** (`update_geofiles`)
```
每个文件:
curl -fLRo ${xui_folder}/bin/${dat}.dat
-z ${xui_folder}/bin/${dat}.dat ← 仅在远程更新时下载
https://github.com/${source}/releases/latest/download/${remote_file}.dat
```
`-z` 参数确保只有远程文件比本地新时才下载,节省带宽。
更新后自动重启面板以加载新规则。
---
### 选项 26网速测试 (Speedtest)
**函数**`run_speedtest()`
```
检查 speedtest 命令是否存在
└─ 不存在 →
有 snap → snap install speedtest
无 snap → 按包管理器安装:
dnf/yum → rpm 包源
apt-get/apt → deb 包源
curl 安装脚本 → 包管理器安装
执行 speedtest
```
---
## 子命令(命令行模式)
当脚本以参数调用时(如 `x-ui start`),跳过交互菜单直接执行:
| 子命令 | 对应菜单 | 附加行为 |
|------------------------|----------|-------------------------------|
| `start` | 11 | 执行后不返回菜单 |
| `stop` | 12 | 执行后不返回菜单 |
| `restart` | 13 | 执行后不返回菜单 |
| `restart-xray` | 14 | 执行后不返回菜单 |
| `status` | 15 | 执行后不返回菜单 |
| `settings` | 10 | 执行后不返回菜单 |
| `enable` | 17 | 执行后不返回菜单 |
| `disable` | 18 | 执行后不返回菜单 |
| `log` | 16 | 执行后不返回菜单 |
| `banlog` | 4(限制) | 执行后不返回菜单 |
| `update` | 2 | 执行后不返回菜单 |
| `legacy` | 4 | 执行后不返回菜单 |
| `install` | 1 | 使用 check_uninstall 前置检查 |
| `uninstall` | 5 | 执行后不返回菜单 |
| `update-all-geofiles` | 25-4 | 更新后自动重启 |
| 无效参数 | — | 显示用法帮助 |
所有子命令传递参数 `0` 给功能函数,使其执行后不调用 `before_show_menu()` 返回菜单。
---
## 调用关系总览
```
x-ui.sh
├─ show_menu()
│ ├─ show_status() → check_status() + show_enable_status() + show_xray_status()
│ ├─ 0: exit
│ ├─ 1: install() → install.sh → start()
│ ├─ 2: update() → update.sh
│ ├─ 3: update_menu() → 下载 x-ui.sh
│ ├─ 4: legacy_version() → install.sh v$version
│ ├─ 5: uninstall() → 停止服务 + 删除文件
│ ├─ 6: reset_user() → x-ui setting -username/-password
│ ├─ 7: reset_webbasepath() → x-ui setting -webBasePath
│ ├─ 8: reset_config() → x-ui setting -reset
│ ├─ 9: set_port() → x-ui setting -port
│ ├─ 10: check_config() → x-ui setting -show + ssl_cert_issue_for_ip()
│ ├─ 11: start() → systemctl/rc-service start
│ ├─ 12: stop() → systemctl/rc-service stop
│ ├─ 13: restart() → systemctl/rc-service restart
│ ├─ 14: restart_xray() → systemctl reload
│ ├─ 15: status() → systemctl/rc-service status
│ ├─ 16: show_log() → journalctl/grep messages
│ ├─ 17: enable() → systemctl/rc-update enable
│ ├─ 18: disable() → systemctl/rc-update disable
│ ├─ 19: ssl_cert_issue_main()
│ │ ├─ 1: ssl_cert_issue() → acme.sh 域名证书
│ │ ├─ 2: 吊销证书 → acme.sh --revoke
│ │ ├─ 3: 强制续期 → acme.sh --renew --force
│ │ ├─ 4: 查看已有域名
│ │ ├─ 5: 设置面板证书路径
│ │ └─ 6: ssl_cert_issue_for_ip() → acme.sh IP 短期证书
│ ├─ 20: ssl_cert_issue_CF() → acme.sh Cloudflare DNS 通配符证书
│ ├─ 21: iplimit_main()
│ │ ├─ 1: install_iplimit() → install fail2ban + create_iplimit_jails()
│ │ ├─ 2: 修改封禁时长
│ │ ├─ 3: 解封所有人
│ │ ├─ 4: show_banlog()
│ │ ├─ 5/6: 手动封禁/解封 IP
│ │ ├─ 7: tail -f fail2ban.log
│ │ ├─ 8/9: 服务状态/重启
│ │ └─ 10: remove_iplimit()
│ ├─ 22: firewall_menu() → UFW 防火墙管理
│ ├─ 23: SSH_port_forwarding() → 设置 listenIP 为 127.0.0.1
│ ├─ 24: bbr_menu() → enable_bbr() / disable_bbr()
│ ├─ 25: update_geo() → update_geofiles() → 下载 geoip/geosite .dat
│ └─ 26: run_speedtest() → speedtest
└─ 子命令模式($# > 0
└─ case $1 in "start"|"stop"|... → 对应函数 0
```
---
## 关键设计决策
1. **Alpine 兼容**:所有服务管理操作都区分 Alpine (OpenRC) 和其他系统 (systemd),通过 `$release` 变量判断。
2. **操作确认**:危险操作(卸载、重置凭据等)默认为 "n",防止误操作。安全操作(更新等)默认为 "y"。
3. **子命令模式**:支持 `x-ui start` 等非交互式调用,传递参数 `0` 抑制 `before_show_menu()` 的回车等待。
4. **状态前置检查**:大多数菜单选项先调用 `check_install``check_uninstall`,确保操作的前提条件满足。
5. **等待机制**start/stop/restart 后等待 2 秒再检查状态,给 systemd/init.d 足够时间完成操作。
6. **Geo 文件条件下载**:使用 `curl -z` 参数,仅在远程文件比本地新时才下载,节省带宽和时间。
7. **BBR 备份恢复**:启用 BBR 前将当前设置备份到注释行中,禁用时精确恢复原始值。
8. **Fail2ban jail 隔离**IP 限制使用独立的 `3x-ipl` jail与系统默认 jail 分离,互不影响。