- Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell) docs: 18 language READMEs; main README language hub; 3m-ui menu CN/EN only - Add README.<lang>.md for en, zh-CN, zh-TW, ja, ko, es, fr, de, ru, pt-BR, vi, id, th, tr, ar, hi, pl, uk - Main README: language switcher + docs/site links; English body moved to README.en.md - Management menu bilingual Chinese|English only (no extra locales in shell)
319 lines
12 KiB
Bash
319 lines
12 KiB
Bash
#!/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.
|
|
|
|
set -eu
|
|
|
|
ROOT="${THREE_M_UI_ROOT:-}"
|
|
BASE="$ROOT/usr/local/lib/3m-ui"
|
|
MANAGER="$BASE/3m-ui.sh"
|
|
INSTALLER="$BASE/install.sh"
|
|
UPDATER="$BASE/update.sh"
|
|
UNINSTALLER="$BASE/uninstall.sh"
|
|
APP_BIN="$BASE/3m-ui-bin"
|
|
CONFIG_FILE="$ROOT/etc/3m-ui/config.yaml"
|
|
DATA_DIR="${THREE_M_UI_DATA_DIR:-}"
|
|
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}"
|
|
|
|
run_script() {
|
|
script="$1"
|
|
shift
|
|
[ -x "$script" ] || {
|
|
printf '%s\n' "3m-ui management script is missing: $script" >&2
|
|
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
|
|
1) run_script "$INSTALLER" ;;
|
|
2) run_script "$UPDATER" ;;
|
|
3) run_script "$MANAGER" status ;;
|
|
4) run_script "$MANAGER" start ;;
|
|
5) run_script "$MANAGER" restart ;;
|
|
6) run_script "$MANAGER" stop ;;
|
|
7) run_script "$MANAGER" logs ;;
|
|
8) run_script "$MANAGER" version ;;
|
|
9) change_port ;;
|
|
10) show_access ;;
|
|
11) reset_admin ;;
|
|
12) enable_bbr ;;
|
|
13) update_geo ;;
|
|
14) set_public_url ;;
|
|
a|A) run_script "$UNINSTALLER" ;;
|
|
0) exit 0 ;;
|
|
*) printf '%s\n' '无效选择,请重新输入。' ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
init_system() {
|
|
if [ -d "$ROOT/run/systemd/system" ] && command -v systemctl >/dev/null 2>&1; then
|
|
printf '%s' systemd
|
|
elif command -v rc-service >/dev/null 2>&1; then
|
|
printf '%s' openrc
|
|
else
|
|
printf '%s' unsupported
|
|
fi
|
|
}
|
|
|
|
service_action() {
|
|
action="$1"
|
|
case "$(init_system)" in
|
|
systemd) systemctl "$action" 3m-ui ;;
|
|
openrc) rc-service 3m-ui "$action" ;;
|
|
*) printf '%s\n' '未检测到 systemd 或 OpenRC。' >&2; exit 1 ;;
|
|
esac
|
|
}
|
|
|
|
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' ;;
|
|
esac
|
|
printf '\n'
|
|
if [ -x "$APP_BIN" ]; then
|
|
printf '%s\n' "应用程序: $APP_BIN"
|
|
"$APP_BIN" --version 2>/dev/null || true
|
|
else
|
|
printf '%s\n' '应用程序二进制不存在。'
|
|
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' '未找到可用的日志系统。' ;;
|
|
esac
|
|
}
|
|
|
|
version() {
|
|
if [ -x "$APP_BIN" ]; then
|
|
"$APP_BIN" --version
|
|
if [ -s "$BASE/MIHOMO_VERSION" ]; then printf 'Bundled Mihomo: %s\n' "$(cat "$BASE/MIHOMO_VERSION")"; fi
|
|
if [ -s "$BASE/CHANNEL" ]; then printf 'Update channel: %s\n' "$(cat "$BASE/CHANNEL")"; fi
|
|
if [ -s "$BASE/REPOSITORY" ]; then printf 'Release source: %s\n' "$(cat "$BASE/REPOSITORY")"; fi
|
|
else
|
|
printf '%s\n' '3m-ui 未安装。' >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
need_root() {
|
|
[ "$(id -u)" -eq 0 ] || { printf '%s\n' '请使用 root 运行 3m-ui 管理命令。' >&2; exit 1; }
|
|
}
|
|
|
|
|
|
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"
|
|
if [ -n "$pub" ] && [ "$pub" != "null" ]; then
|
|
printf '%s\n' "public_url: $pub"
|
|
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' '面板健康检查通过。'
|
|
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
|
|
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
|
|
}
|
|
|
|
enable_bbr() {
|
|
if [ "$(uname -s)" != Linux ]; then
|
|
printf '%s\n' '仅支持 Linux。' >&2
|
|
return 1
|
|
fi
|
|
if sysctl net.ipv4.tcp_congestion_control 2>/dev/null | grep -q bbr; then
|
|
printf '%s\n' 'BBR 已启用。'
|
|
return 0
|
|
fi
|
|
modprobe tcp_bbr 2>/dev/null || true
|
|
if [ ! -f /etc/sysctl.d/99-3m-ui-bbr.conf ]; then
|
|
printf '%s\n' 'net.core.default_qdisc=fq' > /etc/sysctl.d/99-3m-ui-bbr.conf
|
|
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。'
|
|
}
|
|
|
|
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 ..."
|
|
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/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
|
|
return 1
|
|
fi
|
|
ls -la "$dir"/geoip* "$dir"/geosite* 2>/dev/null || true
|
|
printf '%s\n' '完成。如内核已在运行,可在面板重启 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
|
|
if grep -q '^ public_url:' "$CONFIG_FILE" 2>/dev/null; then
|
|
sed -i "s|^ public_url:.*| public_url: \"$url\"|" "$CONFIG_FILE"
|
|
else
|
|
# insert under server:
|
|
sed -i "/^server:/a\\ public_url: \"$url\"" "$CONFIG_FILE"
|
|
fi
|
|
printf '%s\n' "已写入 public_url。重启面板后订阅外链将使用该地址。"
|
|
service_action restart
|
|
}
|
|
|
|
|
|
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 ;;
|
|
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
|
|
}
|
|
sed -i "s/^ port: .*/ port: $new_port/" "$CONFIG_FILE"
|
|
printf '%s\n' "端口已更改: $current → $new_port"
|
|
printf '%s\n' '正在重启 3m-ui...'
|
|
service_action restart
|
|
sleep 1
|
|
if service_action status >/dev/null 2>&1; then
|
|
printf '%s\n' "✓ 3m-ui 已重启。配置端口: $new_port(独立 HTTPS 端口以面板设置为准)"
|
|
else
|
|
printf '%s\n' '✗ 3m-ui 启动失败,请检查日志。' >&2
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
need_root
|
|
cmd=${1:-}
|
|
case "$cmd" in
|
|
status) status ;;
|
|
start) service_action start ;;
|
|
restart) service_action restart ;;
|
|
stop) service_action stop ;;
|
|
logs) 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") ;;
|
|
update|upgrade)
|
|
shift
|
|
run_script "$UPDATER" "$@"
|
|
;;
|
|
install|fix)
|
|
shift
|
|
run_script "$INSTALLER" "$@"
|
|
;;
|
|
uninstall|remove)
|
|
shift
|
|
run_script "$UNINSTALLER" "$@"
|
|
;;
|
|
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' ''
|
|
printf '%s\n' '不带参数时进入交互式菜单(等同于 menu)。'
|
|
;;
|
|
'') menu ;;
|
|
*)
|
|
printf '%s\n' "未知命令: $cmd" >&2
|
|
printf '%s\n' '运行: 3m-ui help' >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|