# 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) 获取公网 IP(api.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 ``` --- ### 选项 19:SSL 证书管理 **函数**:`ssl_cert_issue_main()` — 子菜单入口 #### 子菜单 ``` 1. 获取 SSL(域名) 2. 吊销证书 3. 强制续期 4. 查看已有域名 5. 为面板设置证书路径 6. 为 IP 地址获取 SSL(6 天证书,自动续期) 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()` ``` 获取服务器公网 IP(api.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:端口/路径 → 重启面板 ``` --- ### 选项 20:Cloudflare 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 验证)。 --- ### 选项 21:IP 限制管理(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`),这会导致删除端口后不返回菜单。 --- ### 选项 23:SSH 端口转发管理 **函数**:`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 隧道访问,提高安全性。 --- ### 选项 24:BBR 管理 **函数**:`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 分离,互不影响。