feat(scripts/3m-ui): expand management console with ops features
Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow feat(scripts/3m-ui): expand management console with ops features Reimplemented for 3m-ui (not a copy of third-party scripts): - enable/disable on boot, follow logs, access URI view - port / public_url / web_path / sub_path / sub_port editors - update to version, update this script from repo - BBR status/enable/disable, Geo, firewall hints - SSL cert helper (certbot/acme.sh command templates) - backup entry, healthcheck, confirm + pause menu flow
This commit is contained in:
1 parent
eadd5255b0
commit
aa08f34be4
1 file changed
+531
-207
+531
-207
@@ -1,7 +1,8 @@
|
||||
#!/bin/sh
|
||||
# 3m-ui unified management entrypoint.
|
||||
# Installed as /usr/local/bin/3m-ui.
|
||||
# The actual Go application binary is /usr/local/lib/3m-ui/3m-ui-bin.
|
||||
# 3m-ui unified management entrypoint (/usr/local/bin/3m-ui).
|
||||
# Application binary: /usr/local/lib/3m-ui/3m-ui-bin
|
||||
# Original work for 3m-ui (EPL-2.0). Ideas inspired by common panel ops menus;
|
||||
# not a derivative of any third-party management script source.
|
||||
|
||||
set -eu
|
||||
|
||||
@@ -13,68 +14,54 @@ UPDATER="$BASE/update.sh"
|
||||
UNINSTALLER="$BASE/uninstall.sh"
|
||||
APP_BIN="$BASE/3m-ui-bin"
|
||||
CONFIG_FILE="$ROOT/etc/3m-ui/config.yaml"
|
||||
REPO_RAW="${THREE_M_UI_REPO_RAW:-https://raw.githubusercontent.com/kazeyukiro/3m-ui/main}"
|
||||
DATA_DIR="${THREE_M_UI_DATA_DIR:-}"
|
||||
if [ -z "$DATA_DIR" ] && [ -s "$BASE/DATA_DIRECTORY" ]; then DATA_DIR="$(cat "$BASE/DATA_DIRECTORY")"; fi
|
||||
if [ -z "$DATA_DIR" ] && [ -s "$BASE/DATA_DIRECTORY" ]; then
|
||||
DATA_DIR="$(cat "$BASE/DATA_DIRECTORY")"
|
||||
fi
|
||||
DATA_DIR="${DATA_DIR:-$ROOT/var/lib/3m-ui}"
|
||||
|
||||
# --- helpers -----------------------------------------------------------------
|
||||
|
||||
say() { printf '%s\n' "$*"; }
|
||||
err() { printf '%s\n' "$*" >&2; }
|
||||
|
||||
need_root() {
|
||||
[ "$(id -u)" -eq 0 ] || { err '请使用 root 运行 3m-ui 管理命令。 / Run as root.'; exit 1; }
|
||||
}
|
||||
|
||||
confirm() {
|
||||
# usage: confirm "prompt" [default y|n]
|
||||
prompt="$1"
|
||||
def="${2:-n}"
|
||||
printf '%s [%s]: ' "$prompt" "$def"
|
||||
IFS= read -r ans || return 1
|
||||
[ -z "$ans" ] && ans="$def"
|
||||
case "$ans" in y|Y|yes|YES) return 0 ;; *) return 1 ;; esac
|
||||
}
|
||||
|
||||
pause() {
|
||||
printf '\n%s' '按回车返回菜单 / Press Enter to return... '
|
||||
IFS= read -r _ || true
|
||||
}
|
||||
|
||||
run_script() {
|
||||
script="$1"
|
||||
shift
|
||||
[ -x "$script" ] || {
|
||||
printf '%s\n' "3m-ui management script is missing: $script" >&2
|
||||
err "管理脚本不存在: $script"
|
||||
return 1
|
||||
}
|
||||
# Keep menu alive after child scripts finish.
|
||||
"$script" "$@"
|
||||
}
|
||||
|
||||
menu() {
|
||||
while :; do
|
||||
printf '\n'
|
||||
printf '%s\n' '╭──────────────────────────────────────────────╮'
|
||||
printf '%s\n' '│ 3m-ui 管理控制台 / Management Console │'
|
||||
printf '%s\n' '│ Mihomo Core Management UI │'
|
||||
printf '%s\n' '╰──────────────────────────────────────────────╯'
|
||||
printf '\n'
|
||||
printf '%s\n' ' 1. 安装/修复 | Install / Repair'
|
||||
printf '%s\n' ' 2. 更新 | Update'
|
||||
printf '%s\n' ' 3. 运行状态 | Status'
|
||||
printf '%s\n' ' 4. 启动 | Start'
|
||||
printf '%s\n' ' 5. 重启 | Restart'
|
||||
printf '%s\n' ' 6. 停止 | Stop'
|
||||
printf '%s\n' ' 7. 日志 | Logs'
|
||||
printf '%s\n' ' 8. 版本 | Version'
|
||||
printf '%s\n' ' 9. 修改面板端口 | Change panel port'
|
||||
printf '%s\n' ' 10. 端口与 public_url | Port & public_url'
|
||||
printf '%s\n' ' 11. 重置管理员密码 | Reset admin password'
|
||||
printf '%s\n' ' 12. 启用 BBR | Enable BBR (Linux)'
|
||||
printf '%s\n' ' 13. 更新 Geo | Update Geo rules'
|
||||
printf '%s\n' ' 14. 设置 public_url | Set public_url'
|
||||
printf '%s\n' ' a. 卸载 | Uninstall'
|
||||
printf '%s\n' ' 0. 退出 | Exit'
|
||||
printf '\n请选择 / Select: '
|
||||
IFS= read -r choice || exit 0
|
||||
case "$choice" in
|
||||
# || true: set -e must not tear down the whole menu on a failed action
|
||||
1) run_script "$INSTALLER" || true ;;
|
||||
2) run_script "$UPDATER" || true ;;
|
||||
3) status || true ;;
|
||||
4) service_action start || true ;;
|
||||
5) service_action restart || true ;;
|
||||
6) service_action stop || true ;;
|
||||
7) logs || true ;;
|
||||
8) version || true ;;
|
||||
9) change_port || true ;;
|
||||
10) show_access || true ;;
|
||||
11) reset_admin || true ;;
|
||||
12) enable_bbr || true ;;
|
||||
13) update_geo || true ;;
|
||||
14) set_public_url || true ;;
|
||||
a|A) run_script "$UNINSTALLER" || true ;;
|
||||
0) exit 0 ;;
|
||||
*) printf '%s\n' '无效选择,请重新输入。' ;;
|
||||
esac
|
||||
done
|
||||
run_app() {
|
||||
cmd="$1"
|
||||
if [ ! -x "$APP_BIN" ]; then
|
||||
err "未找到程序: $APP_BIN"
|
||||
return 1
|
||||
fi
|
||||
(cd "$DATA_DIR" && THREE_M_UI_CONFIG="$CONFIG_FILE" THREE_M_UI_DATA_DIR="$DATA_DIR" "$APP_BIN" "$cmd")
|
||||
}
|
||||
|
||||
init_system() {
|
||||
@@ -92,7 +79,7 @@ service_action() {
|
||||
case "$(init_system)" in
|
||||
systemd) systemctl "$action" 3m-ui ;;
|
||||
openrc) rc-service 3m-ui "$action" ;;
|
||||
*) printf '%s\n' '未检测到 systemd 或 OpenRC。' >&2; return 1 ;;
|
||||
*) err '未检测到 systemd 或 OpenRC。'; return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -104,26 +91,105 @@ service_is_active() {
|
||||
esac
|
||||
}
|
||||
|
||||
service_enable() {
|
||||
case "$(init_system)" in
|
||||
systemd) systemctl enable 3m-ui && say '已设置开机自启 / Enabled on boot.' ;;
|
||||
openrc) rc-update add 3m-ui default && say '已设置开机自启 / Enabled on boot.' ;;
|
||||
*) err '不支持的 init'; return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
service_disable() {
|
||||
case "$(init_system)" in
|
||||
systemd) systemctl disable 3m-ui && say '已取消开机自启 / Disabled on boot.' ;;
|
||||
openrc) rc-update del 3m-ui default && say '已取消开机自启 / Disabled on boot.' ;;
|
||||
*) err '不支持的 init'; return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
yaml_get() {
|
||||
# yaml_get KEY_LINE_PREFIX e.g. " port:"
|
||||
key="$1"
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
return 1
|
||||
fi
|
||||
awk -v k="$key" 'index($0, k) == 1 { sub(/^[^:]+:[[:space:]]*/, ""); gsub(/["\047]/, ""); print; exit }' "$CONFIG_FILE"
|
||||
}
|
||||
|
||||
yaml_set() {
|
||||
# yaml_set " port:" "8080" — sets or inserts under server:
|
||||
key="$1"
|
||||
val="$2"
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
err "配置不存在: $CONFIG_FILE"
|
||||
return 1
|
||||
fi
|
||||
tmp="${CONFIG_FILE}.tmp.$$"
|
||||
if grep -q "^${key}" "$CONFIG_FILE" 2>/dev/null; then
|
||||
# Escape for sed
|
||||
esc=$(printf '%s' "$val" | sed -e 's/[\\|&]/\\&/g')
|
||||
sed "s|^${key}.*|${key} ${esc}|" "$CONFIG_FILE" > "$tmp" && mv "$tmp" "$CONFIG_FILE"
|
||||
else
|
||||
awk -v k="$key" -v v="$val" '
|
||||
BEGIN { done=0 }
|
||||
{ print }
|
||||
/^server:/ && !done { print k " " v; done=1 }
|
||||
' "$CONFIG_FILE" > "$tmp" && mv "$tmp" "$CONFIG_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- service ops -------------------------------------------------------------
|
||||
|
||||
status() {
|
||||
case "$(init_system)" in
|
||||
systemd) systemctl --no-pager --full status 3m-ui || true ;;
|
||||
openrc) rc-service 3m-ui status || true ;;
|
||||
*) printf '%s\n' 'init: unsupported' ;;
|
||||
*) say 'init: unsupported' ;;
|
||||
esac
|
||||
printf '\n'
|
||||
if service_is_active; then
|
||||
say '状态: 运行中 / running'
|
||||
else
|
||||
say '状态: 未运行 / stopped'
|
||||
fi
|
||||
case "$(init_system)" in
|
||||
systemd)
|
||||
if systemctl is-enabled --quiet 3m-ui 2>/dev/null; then
|
||||
say '开机自启: 是 / enabled'
|
||||
else
|
||||
say '开机自启: 否 / disabled'
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
if [ -x "$APP_BIN" ]; then
|
||||
printf '%s\n' "应用程序: $APP_BIN"
|
||||
say "应用程序: $APP_BIN"
|
||||
"$APP_BIN" --version 2>/dev/null || true
|
||||
else
|
||||
printf '%s\n' '应用程序二进制不存在。'
|
||||
say '应用程序二进制不存在。'
|
||||
fi
|
||||
}
|
||||
|
||||
logs() {
|
||||
case "$(init_system)" in
|
||||
systemd) journalctl -u 3m-ui -n 100 --no-pager ;;
|
||||
openrc) tail -n 100 /var/log/3m-ui/3m-ui.log 2>/dev/null || printf '%s\n' '未找到日志文件。' ;;
|
||||
*) printf '%s\n' '未找到可用的日志系统。' ;;
|
||||
systemd)
|
||||
if [ "${1:-}" = "-f" ] || [ "${1:-}" = "follow" ]; then
|
||||
journalctl -u 3m-ui -f --no-pager
|
||||
else
|
||||
journalctl -u 3m-ui -n 100 --no-pager
|
||||
fi
|
||||
;;
|
||||
openrc)
|
||||
if [ -f /var/log/3m-ui/3m-ui.log ]; then
|
||||
if [ "${1:-}" = "-f" ] || [ "${1:-}" = "follow" ]; then
|
||||
tail -n 50 -f /var/log/3m-ui/3m-ui.log
|
||||
else
|
||||
tail -n 100 /var/log/3m-ui/3m-ui.log
|
||||
fi
|
||||
else
|
||||
say '未找到日志文件 /var/log/3m-ui/3m-ui.log'
|
||||
fi
|
||||
;;
|
||||
*) say '未找到可用的日志系统。' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -131,85 +197,208 @@ version() {
|
||||
printed=0
|
||||
if [ -x "$APP_BIN" ]; then
|
||||
out="$("$APP_BIN" --version 2>/dev/null || true)"
|
||||
if [ -z "$out" ]; then
|
||||
out="$("$APP_BIN" version 2>/dev/null || true)"
|
||||
fi
|
||||
[ -z "$out" ] && out="$("$APP_BIN" version 2>/dev/null || true)"
|
||||
if [ -n "$out" ]; then
|
||||
printf '%s\n' "$out"
|
||||
printed=1
|
||||
fi
|
||||
fi
|
||||
if [ -s "$BASE/VERSION" ]; then
|
||||
printf 'Installed release: %s
|
||||
' "$(cat "$BASE/VERSION")"
|
||||
printed=1
|
||||
fi
|
||||
if [ -s "$BASE/CHANNEL" ]; then
|
||||
printf 'Update channel: %s
|
||||
' "$(cat "$BASE/CHANNEL")"
|
||||
printed=1
|
||||
fi
|
||||
if [ -s "$BASE/MIHOMO_VERSION" ]; then
|
||||
printf 'Bundled Mihomo: %s
|
||||
' "$(cat "$BASE/MIHOMO_VERSION")"
|
||||
printed=1
|
||||
fi
|
||||
if [ -s "$BASE/REPOSITORY" ]; then
|
||||
printf 'Release source: %s
|
||||
' "$(cat "$BASE/REPOSITORY")"
|
||||
printed=1
|
||||
fi
|
||||
for f in VERSION CHANNEL MIHOMO_VERSION REPOSITORY; do
|
||||
if [ -s "$BASE/$f" ]; then
|
||||
case "$f" in
|
||||
VERSION) printf 'Installed release: %s\n' "$(cat "$BASE/$f")" ;;
|
||||
CHANNEL) printf 'Update channel: %s\n' "$(cat "$BASE/$f")" ;;
|
||||
MIHOMO_VERSION) printf 'Bundled Mihomo: %s\n' "$(cat "$BASE/$f")" ;;
|
||||
REPOSITORY) printf 'Release source: %s\n' "$(cat "$BASE/$f")" ;;
|
||||
esac
|
||||
printed=1
|
||||
fi
|
||||
done
|
||||
if [ -x "$APP_BIN" ]; then
|
||||
printf 'Binary: %s
|
||||
' "$APP_BIN"
|
||||
printf 'Binary: %s\n' "$APP_BIN"
|
||||
printed=1
|
||||
fi
|
||||
if [ "$printed" -eq 0 ]; then
|
||||
printf '%s
|
||||
' '3m-ui 未安装或无法读取版本信息。' >&2
|
||||
exit 1
|
||||
err '3m-ui 未安装或无法读取版本信息。'
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
need_root() {
|
||||
[ "$(id -u)" -eq 0 ] || { printf '%s\n' '请使用 root 运行 3m-ui 管理命令。' >&2; exit 1; }
|
||||
}
|
||||
|
||||
# --- config / access ---------------------------------------------------------
|
||||
|
||||
show_access() {
|
||||
port=$(awk '/^ port:/ {print $2; exit}' "$CONFIG_FILE" 2>/dev/null || echo "8080")
|
||||
pub=$(awk '/^ public_url:/ {print $2; exit}' "$CONFIG_FILE" 2>/dev/null | tr -d '"' || true)
|
||||
printf '%s\n' "配置端口: $port"
|
||||
port=$(yaml_get ' port:' 2>/dev/null || true)
|
||||
port="${port:-8080}"
|
||||
listen=$(yaml_get ' listen:' 2>/dev/null || true)
|
||||
pub=$(yaml_get ' public_url:' 2>/dev/null || true)
|
||||
web=$(yaml_get ' web_path:' 2>/dev/null || true)
|
||||
subp=$(yaml_get ' sub_path:' 2>/dev/null || true)
|
||||
subport=$(yaml_get ' sub_port:' 2>/dev/null || true)
|
||||
|
||||
say "配置文件: $CONFIG_FILE"
|
||||
say "面板端口 / port: $port"
|
||||
[ -n "$listen" ] && say "监听 / listen: $listen"
|
||||
[ -n "$pub" ] && [ "$pub" != "null" ] && say "public_url: $pub"
|
||||
[ -n "$web" ] && say "web_path: $web"
|
||||
[ -n "$subp" ] && say "sub_path: $subp"
|
||||
[ -n "$subport" ] && [ "$subport" != "0" ] && say "sub_port: $subport"
|
||||
|
||||
path_suffix=""
|
||||
case "$web" in
|
||||
/*) path_suffix="$web" ;;
|
||||
"") path_suffix="" ;;
|
||||
*) path_suffix="/$web" ;;
|
||||
esac
|
||||
|
||||
if [ -n "$pub" ] && [ "$pub" != "null" ]; then
|
||||
printf '%s\n' "public_url: $pub"
|
||||
# strip trailing slash for display
|
||||
base=$(printf '%s' "$pub" | sed 's|/*$||')
|
||||
say "面板访问 / Panel URI: ${base}${path_suffix}/"
|
||||
else
|
||||
say "面板访问示例 (请换成公网 IP/域名):"
|
||||
say " http://<IP>:${port}${path_suffix}/"
|
||||
say " https://<域名>/ (若已启用面板 SSL 且监听 443)"
|
||||
fi
|
||||
printf '%s\n' '面板启用 HTTPS 后,请使用面板中配置的域名和 HTTPS 端口。'
|
||||
if (cd "$DATA_DIR" && THREE_M_UI_CONFIG="$CONFIG_FILE" "$APP_BIN" healthcheck) >/dev/null 2>&1; then
|
||||
printf '%s\n' '面板健康检查通过。'
|
||||
say '面板启用 HTTPS 后,请使用面板中配置的域名和 HTTPS 端口。'
|
||||
if run_app healthcheck >/dev/null 2>&1; then
|
||||
say '面板健康检查通过 / healthcheck OK'
|
||||
else
|
||||
say '面板健康检查未通过或未就绪 (可稍后重试)'
|
||||
fi
|
||||
say '管理命令: 3m-ui'
|
||||
say '初始密码在首次安装时随机生成;遗失可用菜单「重置管理员密码」。'
|
||||
}
|
||||
|
||||
change_port() {
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
err "配置文件不存在: $CONFIG_FILE"
|
||||
return 1
|
||||
fi
|
||||
current=$(yaml_get ' port:' 2>/dev/null || echo "8080")
|
||||
printf '当前面板端口: %s\n' "$current"
|
||||
printf '输入新端口 (1-65535, 回车取消): '
|
||||
IFS= read -r new_port || return 0
|
||||
[ -z "$new_port" ] && return 0
|
||||
case "$new_port" in
|
||||
''|*[!0-9]*) err '无效端口。'; return 1 ;;
|
||||
esac
|
||||
[ "$new_port" -ge 1 ] && [ "$new_port" -le 65535 ] || {
|
||||
err '端口必须在 1-65535 范围内。'
|
||||
return 1
|
||||
}
|
||||
[ "$new_port" = "$current" ] && { say '端口未变更。'; return 0; }
|
||||
yaml_set ' port:' "$new_port"
|
||||
say "端口已更改: $current → $new_port"
|
||||
say '正在重启 3m-ui...'
|
||||
service_action restart
|
||||
sleep 1
|
||||
if service_is_active; then
|
||||
say "✓ 已重启。配置端口: $new_port"
|
||||
else
|
||||
err '✗ 启动失败,请检查: 3m-ui logs'
|
||||
fi
|
||||
}
|
||||
|
||||
set_public_url() {
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
err "配置不存在: $CONFIG_FILE"
|
||||
return 1
|
||||
fi
|
||||
cur=$(yaml_get ' public_url:' 2>/dev/null || true)
|
||||
printf '当前 public_url: %s\n' "${cur:-<empty>}"
|
||||
printf '输入新的 public_url (例如 https://example.com ,回车取消): '
|
||||
IFS= read -r url || return 0
|
||||
[ -z "$url" ] && return 0
|
||||
yaml_set ' public_url:' "\"$url\""
|
||||
say '已写入 public_url。'
|
||||
if confirm '是否立即重启面板?' y; then
|
||||
service_action restart
|
||||
sleep 1
|
||||
service_is_active && say '✓ 已重启' || err '✗ 重启后未运行'
|
||||
fi
|
||||
}
|
||||
|
||||
set_panel_paths() {
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
err "配置不存在: $CONFIG_FILE"
|
||||
return 1
|
||||
fi
|
||||
say '留空则保持现有值。 / Leave blank to keep current.'
|
||||
cur_web=$(yaml_get ' web_path:' 2>/dev/null || true)
|
||||
cur_sub=$(yaml_get ' sub_path:' 2>/dev/null || true)
|
||||
cur_sp=$(yaml_get ' sub_port:' 2>/dev/null || true)
|
||||
cur_listen=$(yaml_get ' listen:' 2>/dev/null || true)
|
||||
printf 'web_path (面板路径前缀, 现: %s): ' "${cur_web:-<empty>}"
|
||||
IFS= read -r web || true
|
||||
printf 'sub_path (订阅路径前缀, 现: %s): ' "${cur_sub:-<empty>}"
|
||||
IFS= read -r sub || true
|
||||
printf 'sub_port (0=与面板同端口, 现: %s): ' "${cur_sp:-0}"
|
||||
IFS= read -r sp || true
|
||||
printf 'listen (空=全部接口, 现: %s): ' "${cur_listen:-<empty>}"
|
||||
IFS= read -r lis || true
|
||||
|
||||
changed=0
|
||||
if [ -n "${web:-}" ]; then
|
||||
yaml_set ' web_path:' "\"$web\""
|
||||
changed=1
|
||||
fi
|
||||
if [ -n "${sub:-}" ]; then
|
||||
yaml_set ' sub_path:' "\"$sub\""
|
||||
changed=1
|
||||
fi
|
||||
if [ -n "${sp:-}" ]; then
|
||||
case "$sp" in
|
||||
''|*[!0-9]*) err 'sub_port 无效' ;;
|
||||
*) yaml_set ' sub_port:' "$sp"; changed=1 ;;
|
||||
esac
|
||||
fi
|
||||
if [ -n "${lis:-}" ]; then
|
||||
yaml_set ' listen:' "\"$lis\""
|
||||
changed=1
|
||||
fi
|
||||
if [ "$changed" -eq 0 ]; then
|
||||
say '未修改。'
|
||||
return 0
|
||||
fi
|
||||
say '已写入配置。'
|
||||
if confirm '是否立即重启面板?' y; then
|
||||
service_action restart
|
||||
sleep 1
|
||||
service_is_active && say '✓ 已重启' || err '✗ 重启后未运行'
|
||||
fi
|
||||
printf '%s\n' '管理命令: 3m-ui'
|
||||
printf '%s\n' '初始密码在首次安装时随机生成;若遗失,可用 3m-ui reset-admin 生成新密码。'
|
||||
}
|
||||
|
||||
reset_admin() {
|
||||
if [ ! -x "$APP_BIN" ]; then
|
||||
printf '%s\n' "未找到程序: $APP_BIN" >&2
|
||||
err "未找到程序: $APP_BIN"
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' '将随机生成新的管理员密码,并要求下次登录修改密码。'
|
||||
printf '确认? [y/N] '
|
||||
IFS= read -r ans || return 0
|
||||
case "$ans" in y|Y|yes|YES) ;; *) printf '%s\n' '已取消。'; return 0 ;; esac
|
||||
(cd "$DATA_DIR" && THREE_M_UI_CONFIG="$CONFIG_FILE" "$APP_BIN" reset-admin) || return 1
|
||||
say '将随机生成新的管理员密码,并要求下次登录修改密码。'
|
||||
if ! confirm '确认继续?' n; then
|
||||
say '已取消。'
|
||||
return 0
|
||||
fi
|
||||
run_app reset-admin || return 1
|
||||
}
|
||||
|
||||
# --- BBR ---------------------------------------------------------------------
|
||||
|
||||
bbr_status() {
|
||||
if [ "$(uname -s)" != Linux ]; then
|
||||
err '仅支持 Linux。'
|
||||
return 1
|
||||
fi
|
||||
sysctl net.ipv4.tcp_congestion_control 2>/dev/null || true
|
||||
sysctl net.core.default_qdisc 2>/dev/null || true
|
||||
}
|
||||
|
||||
enable_bbr() {
|
||||
if [ "$(uname -s)" != Linux ]; then
|
||||
printf '%s\n' '仅支持 Linux。' >&2
|
||||
err '仅支持 Linux。'
|
||||
return 1
|
||||
fi
|
||||
if sysctl net.ipv4.tcp_congestion_control 2>/dev/null | grep -q bbr; then
|
||||
printf '%s\n' 'BBR 已启用。'
|
||||
say 'BBR 已启用。'
|
||||
return 0
|
||||
fi
|
||||
modprobe tcp_bbr 2>/dev/null || true
|
||||
@@ -218,103 +407,250 @@ enable_bbr() {
|
||||
printf '%s\n' 'net.ipv4.tcp_congestion_control=bbr' >> /etc/sysctl.d/99-3m-ui-bbr.conf
|
||||
fi
|
||||
sysctl --system >/dev/null 2>&1 || sysctl -p /etc/sysctl.d/99-3m-ui-bbr.conf 2>/dev/null || true
|
||||
sysctl net.ipv4.tcp_congestion_control 2>/dev/null || true
|
||||
printf '%s\n' '已尝试启用 BBR。'
|
||||
bbr_status
|
||||
say '已尝试启用 BBR。'
|
||||
}
|
||||
|
||||
disable_bbr() {
|
||||
if [ "$(uname -s)" != Linux ]; then
|
||||
err '仅支持 Linux。'
|
||||
return 1
|
||||
fi
|
||||
rm -f /etc/sysctl.d/99-3m-ui-bbr.conf
|
||||
sysctl -w net.ipv4.tcp_congestion_control=cubic >/dev/null 2>&1 || true
|
||||
sysctl -w net.core.default_qdisc=fq_codel >/dev/null 2>&1 || true
|
||||
bbr_status
|
||||
say '已尝试恢复默认拥塞控制 (cubic)。重启后仍可能被其它 sysctl 覆盖。'
|
||||
}
|
||||
|
||||
bbr_menu() {
|
||||
say ''
|
||||
say ' 1) 状态 / Status'
|
||||
say ' 2) 启用 BBR / Enable'
|
||||
say ' 3) 关闭 BBR / Disable'
|
||||
say ' 0) 返回'
|
||||
printf '选择: '
|
||||
IFS= read -r c || return 0
|
||||
case "$c" in
|
||||
1) bbr_status ;;
|
||||
2) enable_bbr ;;
|
||||
3) disable_bbr ;;
|
||||
*) ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- Geo / firewall / SSL helpers --------------------------------------------
|
||||
|
||||
update_geo() {
|
||||
dir="$DATA_DIR/mihomo"
|
||||
mkdir -p "$dir"
|
||||
base="https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest"
|
||||
printf '%s\n' "下载 Geo 到 $dir ..."
|
||||
say "下载 Geo 到 $dir ..."
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL -o "$dir/geoip.metadb" "$base/geoip.metadb" || curl -fsSL -o "$dir/geoip.dat" "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat" || true
|
||||
curl -fsSL -o "$dir/geoip.metadb" "$base/geoip.metadb" || \
|
||||
curl -fsSL -o "$dir/geoip.dat" "$base/geoip.dat" || true
|
||||
curl -fsSL -o "$dir/geosite.dat" "$base/geosite.dat" || true
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$dir/geoip.metadb" "$base/geoip.metadb" || true
|
||||
wget -q -O "$dir/geosite.dat" "$base/geosite.dat" || true
|
||||
else
|
||||
printf '%s\n' '需要 curl 或 wget' >&2
|
||||
err '需要 curl 或 wget'
|
||||
return 1
|
||||
fi
|
||||
ls -la "$dir"/geoip* "$dir"/geosite* 2>/dev/null || true
|
||||
printf '%s\n' '完成。如内核已在运行,可在面板重启 Mihomo 使规则生效。'
|
||||
say '完成。可在面板重启 Mihomo 使规则生效。'
|
||||
}
|
||||
|
||||
set_public_url() {
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
printf '%s\n' "配置不存在: $CONFIG_FILE" >&2
|
||||
return 1
|
||||
fi
|
||||
cur=$(awk '/^ public_url:/ {print $2; exit}' "$CONFIG_FILE" 2>/dev/null | tr -d '"' || true)
|
||||
printf '当前 public_url: %s\n' "${cur:-<empty>}"
|
||||
printf '输入新的 public_url (例如 https://example.com:8443 ,回车取消): '
|
||||
IFS= read -r url || return 0
|
||||
[ -z "$url" ] && return 0
|
||||
# Escape for sed replacement (|, &, \)
|
||||
esc=$(printf '%s' "$url" | sed -e 's/[\\|&]/\\\\&/g')
|
||||
if grep -q '^ public_url:' "$CONFIG_FILE" 2>/dev/null; then
|
||||
sed -i "s|^ public_url:.*| public_url: \"$esc\"|" "$CONFIG_FILE"
|
||||
elif grep -q '^server:' "$CONFIG_FILE" 2>/dev/null; then
|
||||
# Portable insert: rewrite via temp file (works on GNU and BusyBox sed)
|
||||
tmp="${CONFIG_FILE}.tmp.$$"
|
||||
awk -v u="$url" '
|
||||
BEGIN { done=0 }
|
||||
{ print }
|
||||
/^server:/ && !done { print " public_url: \"" u "\""; done=1 }
|
||||
' "$CONFIG_FILE" > "$tmp" && mv "$tmp" "$CONFIG_FILE"
|
||||
open_firewall() {
|
||||
say '尝试放行常用端口 (22, 80, 443, 面板端口)...'
|
||||
port=$(yaml_get ' port:' 2>/dev/null || echo "8080")
|
||||
if command -v ufw >/dev/null 2>&1; then
|
||||
ufw allow 22/tcp || true
|
||||
ufw allow 80/tcp || true
|
||||
ufw allow 443/tcp || true
|
||||
ufw allow "${port}/tcp" || true
|
||||
say '已向 ufw 提交规则 (请自行 ufw enable / status)。'
|
||||
elif command -v firewall-cmd >/dev/null 2>&1; then
|
||||
firewall-cmd --permanent --add-port=80/tcp || true
|
||||
firewall-cmd --permanent --add-port=443/tcp || true
|
||||
firewall-cmd --permanent --add-port="${port}/tcp" || true
|
||||
firewall-cmd --reload || true
|
||||
say '已尝试 firewalld 放行。'
|
||||
else
|
||||
printf '%s\n' "配置中未找到 server: 段,请手动编辑 $CONFIG_FILE" >&2
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "已写入 public_url。重启面板后订阅外链将使用该地址。"
|
||||
service_action restart
|
||||
if service_is_active; then
|
||||
printf '%s\n' '✓ 服务已重启。'
|
||||
else
|
||||
printf '%s\n' '✗ 服务重启后未处于运行状态,请检查: 3m-ui logs' >&2
|
||||
say '未检测到 ufw/firewalld。请在云安全组放行 80/443 与面板端口。'
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
change_port() {
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
printf '%s\n' "配置文件不存在: $CONFIG_FILE" >&2
|
||||
return 1
|
||||
fi
|
||||
current=$(awk '/^ port:/ {print $2; exit}' "$CONFIG_FILE" 2>/dev/null || echo "8080")
|
||||
printf '当前面板端口: %s\n' "$current"
|
||||
printf '输入新端口 (1-65535, 回车取消): '
|
||||
IFS= read -r new_port || return 0
|
||||
[ -z "$new_port" ] && return 0
|
||||
case "$new_port" in
|
||||
''|*[!0-9]*) printf '%s\n' '无效端口。' >&2; return 1 ;;
|
||||
ssl_cert_menu() {
|
||||
say ''
|
||||
say '面板 HTTPS 推荐在「系统设置 → SSL」使用内置 ACME。'
|
||||
say '以下为可选:用外部工具申请 PEM,供节点或反代使用。'
|
||||
say ''
|
||||
say ' 1) 打印 certbot 命令 (standalone)'
|
||||
say ' 2) 打印 acme.sh 命令 (standalone)'
|
||||
say ' 3) 打印 Cloudflare DNS (acme.sh) 示例'
|
||||
say ' 0) 返回'
|
||||
printf '选择: '
|
||||
IFS= read -r c || return 0
|
||||
printf '域名 (例 dzx.qzz.io): '
|
||||
IFS= read -r dom || return 0
|
||||
[ -z "$dom" ] && return 0
|
||||
printf '邮箱 (可空): '
|
||||
IFS= read -r em || true
|
||||
case "$c" in
|
||||
1)
|
||||
say ''
|
||||
say '# 申请前请确保 80 未被占用(可临时: systemctl stop 3m-ui)'
|
||||
if [ -n "$em" ]; then
|
||||
say "certbot certonly --standalone -d $dom --email $em --agree-tos --non-interactive"
|
||||
else
|
||||
say "certbot certonly --standalone -d $dom --register-unsafely-without-email --agree-tos --non-interactive"
|
||||
fi
|
||||
say "# 证书: /etc/letsencrypt/live/$dom/fullchain.pem"
|
||||
say "# 私钥: /etc/letsencrypt/live/$dom/privkey.pem"
|
||||
;;
|
||||
2)
|
||||
say ''
|
||||
say 'curl https://get.acme.sh | sh'
|
||||
say "~/.acme.sh/acme.sh --set-default-ca --server letsencrypt"
|
||||
say "~/.acme.sh/acme.sh --issue -d $dom --standalone"
|
||||
say "~/.acme.sh/acme.sh --install-cert -d $dom \\"
|
||||
say " --key-file /etc/3m-ui/certs/${dom}.key \\"
|
||||
say " --fullchain-file /etc/3m-ui/certs/${dom}.crt \\"
|
||||
say " --reloadcmd \"systemctl restart 3m-ui\""
|
||||
;;
|
||||
3)
|
||||
say ''
|
||||
say 'export CF_Token="your_api_token"'
|
||||
say "~/.acme.sh/acme.sh --issue -d $dom --dns dns_cf"
|
||||
say "# 不占用 80,适合面板已占用 ACME 端口时给节点另签证书"
|
||||
;;
|
||||
*) ;;
|
||||
esac
|
||||
[ "$new_port" -ge 1 ] 2>/dev/null && [ "$new_port" -le 65535 ] 2>/dev/null || {
|
||||
printf '%s\n' '端口必须在 1-65535 范围内。' >&2
|
||||
return 1
|
||||
}
|
||||
[ "$new_port" = "$current" ] && {
|
||||
printf '%s\n' '端口未变更。'
|
||||
return 0
|
||||
}
|
||||
if grep -q '^ port:' "$CONFIG_FILE" 2>/dev/null; then
|
||||
sed -i "s/^ port: .*/ port: $new_port/" "$CONFIG_FILE"
|
||||
else
|
||||
printf '%s\n' "配置中未找到 ' port:' 字段,请手动编辑 $CONFIG_FILE" >&2
|
||||
return 1
|
||||
fi
|
||||
printf '%s\n' "端口已更改: $current → $new_port"
|
||||
printf '%s\n' '正在重启 3m-ui...'
|
||||
service_action restart
|
||||
sleep 1
|
||||
if service_is_active; then
|
||||
printf '%s\n' "✓ 3m-ui 已重启。配置端口: $new_port(独立 HTTPS 端口以面板设置为准)"
|
||||
else
|
||||
printf '%s\n' '✗ 3m-ui 启动失败,请检查日志: 3m-ui logs' >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# --- update management script itself -----------------------------------------
|
||||
|
||||
update_self() {
|
||||
say "从仓库更新管理脚本: $REPO_RAW/scripts/3m-ui"
|
||||
if ! confirm '确认下载并覆盖 /usr/local/bin/3m-ui 与 $BASE/3m-ui ?' n; then
|
||||
say '已取消。'
|
||||
return 0
|
||||
fi
|
||||
tmp=$(mktemp)
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL "$REPO_RAW/scripts/3m-ui" -o "$tmp" || { err '下载失败'; rm -f "$tmp"; return 1; }
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -q -O "$tmp" "$REPO_RAW/scripts/3m-ui" || { err '下载失败'; rm -f "$tmp"; return 1; }
|
||||
else
|
||||
err '需要 curl 或 wget'
|
||||
return 1
|
||||
fi
|
||||
if ! head -1 "$tmp" | grep -q '^#!'; then
|
||||
err '下载内容不像 shell 脚本'
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
install -m 0755 "$tmp" /usr/local/bin/3m-ui
|
||||
if [ -d "$BASE" ]; then
|
||||
install -m 0755 "$tmp" "$BASE/3m-ui"
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
say '管理脚本已更新。请重新运行: 3m-ui'
|
||||
}
|
||||
|
||||
update_to_version() {
|
||||
printf '输入版本号 (如 v1.1.1,回车取消): '
|
||||
IFS= read -r ver || return 0
|
||||
[ -z "$ver" ] && return 0
|
||||
case "$ver" in
|
||||
v*) ;;
|
||||
*) ver="v$ver" ;;
|
||||
esac
|
||||
say "将更新到 $ver ..."
|
||||
run_script "$UPDATER" "$ver" || return 1
|
||||
}
|
||||
|
||||
# --- menu --------------------------------------------------------------------
|
||||
|
||||
menu() {
|
||||
while :; do
|
||||
printf '\n'
|
||||
printf '%s\n' '╭──────────────────────────────────────────────╮'
|
||||
printf '%s\n' '│ 3m-ui 管理控制台 / Management Console │'
|
||||
printf '%s\n' '│ Mihomo Core Management UI │'
|
||||
printf '%s\n' '╰──────────────────────────────────────────────╯'
|
||||
printf '\n'
|
||||
printf '%s\n' ' 1. 安装/修复 | Install / Repair'
|
||||
printf '%s\n' ' 2. 更新到最新 | Update latest'
|
||||
printf '%s\n' ' 3. 更新到指定版本 | Update to version'
|
||||
printf '%s\n' ' 4. 运行状态 | Status'
|
||||
printf '%s\n' ' 5. 启动 | Start'
|
||||
printf '%s\n' ' 6. 重启 | Restart'
|
||||
printf '%s\n' ' 7. 停止 | Stop'
|
||||
printf '%s\n' ' 8. 开机自启 开/关 | Enable/Disable'
|
||||
printf '%s\n' ' 9. 日志 | Logs'
|
||||
printf '%s\n' ' 10. 版本 | Version'
|
||||
printf '%s\n' ' 11. 访问信息 / URI | Access info'
|
||||
printf '%s\n' ' 12. 修改面板端口 | Change port'
|
||||
printf '%s\n' ' 13. 设置 public_url | Public URL'
|
||||
printf '%s\n' ' 14. 路径/订阅端口 | web_path sub_path sub_port'
|
||||
printf '%s\n' ' 15. 重置管理员密码 | Reset admin'
|
||||
printf '%s\n' ' 16. 健康检查 | Healthcheck'
|
||||
printf '%s\n' ' 17. BBR | BBR menu'
|
||||
printf '%s\n' ' 18. 更新 Geo | Update Geo'
|
||||
printf '%s\n' ' 19. 防火墙放行提示 | Firewall'
|
||||
printf '%s\n' ' 20. SSL 证书助手 | SSL cert helper'
|
||||
printf '%s\n' ' 21. 备份 | Backup'
|
||||
printf '%s\n' ' 22. 更新本管理脚本 | Update this script'
|
||||
printf '%s\n' ' a. 卸载 | Uninstall'
|
||||
printf '%s\n' ' 0. 退出 | Exit'
|
||||
printf '\n请选择 / Select: '
|
||||
IFS= read -r choice || exit 0
|
||||
case "$choice" in
|
||||
1) run_script "$INSTALLER" || true; pause ;;
|
||||
2) run_script "$UPDATER" || true; pause ;;
|
||||
3) update_to_version || true; pause ;;
|
||||
4) status || true; pause ;;
|
||||
5) service_action start || true; pause ;;
|
||||
6) service_action restart || true; pause ;;
|
||||
7) service_action stop || true; pause ;;
|
||||
8)
|
||||
printf 'e=启用 enable / d=禁用 disable: '
|
||||
IFS= read -r ed || true
|
||||
case "$ed" in
|
||||
e|E) service_enable || true ;;
|
||||
d|D) service_disable || true ;;
|
||||
esac
|
||||
pause
|
||||
;;
|
||||
9)
|
||||
printf '跟随输出? [y/N]: '
|
||||
IFS= read -r fl || true
|
||||
case "$fl" in y|Y) logs -f || true ;; *) logs || true; pause ;; esac
|
||||
;;
|
||||
10) version || true; pause ;;
|
||||
11) show_access || true; pause ;;
|
||||
12) change_port || true; pause ;;
|
||||
13) set_public_url || true; pause ;;
|
||||
14) set_panel_paths || true; pause ;;
|
||||
15) reset_admin || true; pause ;;
|
||||
16) run_app healthcheck || true; pause ;;
|
||||
17) bbr_menu || true; pause ;;
|
||||
18) update_geo || true; pause ;;
|
||||
19) open_firewall || true; pause ;;
|
||||
20) ssl_cert_menu || true; pause ;;
|
||||
21) run_script "$INSTALLER" --backup || true; pause ;;
|
||||
22) update_self || true; pause ;;
|
||||
a|A) run_script "$UNINSTALLER" || true; pause ;;
|
||||
0) exit 0 ;;
|
||||
*) say '无效选择,请重新输入。' ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# --- main --------------------------------------------------------------------
|
||||
|
||||
main() {
|
||||
cmd=${1:-}
|
||||
case "$cmd" in
|
||||
@@ -326,12 +662,14 @@ main() {
|
||||
start) service_action start ;;
|
||||
restart) service_action restart ;;
|
||||
stop) service_action stop ;;
|
||||
logs) logs ;;
|
||||
enable) service_enable ;;
|
||||
disable) service_disable ;;
|
||||
logs) shift; logs "$@" ;;
|
||||
version) version ;;
|
||||
config) shift; run_script "$MANAGER" config "$@" ;;
|
||||
backup) shift; run_script "$INSTALLER" --backup "$@" ;;
|
||||
restore) shift; run_script "$INSTALLER" --restore "$@" ;;
|
||||
init|reset-admin|healthcheck) (cd "$DATA_DIR" && THREE_M_UI_CONFIG="$CONFIG_FILE" THREE_M_UI_DATA_DIR="$DATA_DIR" "$APP_BIN" "$cmd") ;;
|
||||
init|reset-admin|healthcheck) run_app "$cmd" ;;
|
||||
update|upgrade)
|
||||
shift
|
||||
run_script "$UPDATER" "$@"
|
||||
@@ -344,36 +682,22 @@ main() {
|
||||
shift
|
||||
run_script "$UNINSTALLER" "$@"
|
||||
;;
|
||||
menu)
|
||||
menu
|
||||
;;
|
||||
menu) menu ;;
|
||||
help|-h|--help)
|
||||
printf '%s\n' '用法: 3m-ui <命令> [参数]'
|
||||
printf '%s\n' ''
|
||||
printf '%s\n' '服务:'
|
||||
printf '%s\n' ' status | start | restart | stop | logs | version'
|
||||
printf '%s\n' '配置:'
|
||||
printf '%s\n' ' config show 查看配置'
|
||||
printf '%s\n' ' config port <端口> 修改端口并重启'
|
||||
printf '%s\n' '安装与更新:'
|
||||
printf '%s\n' ' update [vX.Y.Z] [--yes] 更新到指定版本或所选通道最新版本'
|
||||
printf '%s\n' ' install 安装 / 修复'
|
||||
printf '%s\n' ' uninstall [--purge] 卸载,默认保留配置、密钥和数据'
|
||||
printf '%s\n' '备份与恢复:'
|
||||
printf '%s\n' ' backup 停服创建完整快照,然后恢复运行'
|
||||
printf '%s\n' ' restore /path/snapshot.tar.gz [--yes] 恢复快照'
|
||||
printf '%s\n' ' reset-admin 生成并显示新的随机管理员密码'
|
||||
printf '%s\n' ' healthcheck 检查实际 HTTP/HTTPS 面板健康状态'
|
||||
printf '%s\n' '其他:'
|
||||
printf '%s\n' ' menu 交互式管理菜单'
|
||||
printf '%s\n' ' help 显示本帮助'
|
||||
printf '%s\n' '服务: status | start | restart | stop | enable | disable | logs | version'
|
||||
printf '%s\n' '配置: config show | config port <端口>'
|
||||
printf '%s\n' '安装: update [vX.Y.Z] | install | uninstall [--purge]'
|
||||
printf '%s\n' '数据: backup | restore <snapshot.tar.gz> | reset-admin | healthcheck'
|
||||
printf '%s\n' '其他: menu | help'
|
||||
printf '%s\n' ''
|
||||
printf '%s\n' '不带参数时进入交互式菜单(等同于 menu)。'
|
||||
printf '%s\n' '不带参数时进入交互式菜单。'
|
||||
;;
|
||||
'') menu ;;
|
||||
*)
|
||||
printf '%s\n' "未知命令: $cmd" >&2
|
||||
printf '%s\n' '运行: 3m-ui help' >&2
|
||||
err "未知命令: $cmd"
|
||||
err '运行: 3m-ui help'
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in new issue
Block a user