#!/usr/bin/env bash # Nexus NX — 统一运维菜单(安装 + 升级 + 日常运维) # # nx 交互菜单 # nx update 一键更新(等同 deploy/update.sh) # nx update --no-cache 无缓存重建镜像 # nx install-fresh 全新安装 # nx health 健康检查 # set -euo pipefail NX_SELF="$(readlink -f "${BASH_SOURCE[0]}")" NX_DIR="$(cd "$(dirname "$NX_SELF")" && pwd)" # shellcheck source=./nexus-1panel.sh source "${NX_DIR}/nexus-1panel.sh" pause_enter() { echo "" read -r -p "按 Enter 继续..." } clear_screen() { if [[ -t 1 ]]; then clear 2>/dev/null || true fi } is_stack_installed() { [[ -d "${NEXUS_ROOT}/.git" && -f "${NEXUS_ROOT}/${ENV_PROD}" ]] } detect_runtime() { if is_stack_installed && docker compose version >/dev/null 2>&1; then local prof q prof="$(profile_env_file "$NEXUS_ROOT")" if [[ -f "$prof" && -f "$NEXUS_ROOT/$ENV_PROD" ]]; then local -a det_args=(-f "$COMPOSE_FILE") if has_1panel_network && [[ -f "$NEXUS_ROOT/$COMPOSE_1PANEL" ]]; then det_args+=(-f "$COMPOSE_1PANEL") fi q="$(cd "$NEXUS_ROOT" && docker compose "${det_args[@]}" \ --env-file "$ENV_PROD" --env-file "$prof" ps -q nexus 2>/dev/null || true)" if [[ -n "$q" ]]; then NX_RUNTIME="docker" return 0 fi fi fi if command -v supervisorctl >/dev/null 2>&1 && supervisorctl status nexus >/dev/null 2>&1; then NX_RUNTIME="supervisor" return 0 fi NX_RUNTIME="none" } install_nx_cli() { NEXUS_ROOT="$(cd "${NX_DIR}/.." && pwd)" \ bash "${NX_DIR}/install-nx-cli.sh" 2>/dev/null || true } menu_header() { local title="$1" clear_screen echo "" echo "╔══════════════════════════════════════════════════════════╗" printf "║ Nexus NX — %-44s║\n" "$title" echo "╠══════════════════════════════════════════════════════════╣" echo "║ 域名: ${NEXUS_DOMAIN} 目录: ${NEXUS_ROOT}" if is_stack_installed; then detect_runtime echo "║ 运行: ${NX_RUNTIME} 档位: $(cat "${NEXUS_ROOT}/${HOST_PROFILE_FILE}" 2>/dev/null || echo "${NEXUS_PROFILE}")" if [[ -d "${NEXUS_ROOT}/.git" ]]; then echo "║ 版本: $(git -C "${NEXUS_ROOT}" log -1 --oneline 2>/dev/null || echo '?')" fi else echo "║ 状态: 未安装" fi echo "╚══════════════════════════════════════════════════════════╝" echo "" } run_install() { local profile="${1:-2c8g}" extra="${2:-}" NEXUS_PROFILE="$profile" SKIP_CLONE=false FRESH_INSTALL=false NO_BACKUP=false PRUNE_IMAGES=false FROM_ENV="${NEXUS_FROM_ENV}" case "$extra" in skip-clone) SKIP_CLONE=true ;; fresh) FRESH_INSTALL=true ;; from-env) FROM_ENV="${NEXUS_FROM_ENV}" ;; esac load_secrets cmd_install install_nx_cli pause_enter } menu_install() { while true; do menu_header "安装" cat <<'EOF' [1] 一键安装(默认 2核8G / 2c8g) [2] 安装 — 4核16G(4c16g) [3] 安装 — 1核4G(1c4g) [4] 自定义规格(输入 CPU 与内存 GB) [5] 已 clone 到 /opt/nexus,仅 pull + 启动(--skip-clone) [6] 全新安装(install-nexus-fresh.sh → /app/install.html) [7] 检查端口 / 防火墙(不安装) [0] 返回 EOF read -r -p "请选择: " choice case "$choice" in 1) run_install 2c8g "" ;; 2) run_install 4c16g "" ;; 3) run_install 1c4g "" ;; 4) read -r -p "CPU 核数: " cpus read -r -p "内存 GB: " mem CUSTOM_CPUS="$cpus" CUSTOM_MEM_GB="$mem" run_install 2c8g "" ;; 5) run_install 2c8g skip-clone ;; 6) if [[ -x "${NX_DIR}/install-nexus-fresh.sh" ]]; then bash "${NX_DIR}/install-nexus-fresh.sh" --profile "${NEXUS_PROFILE:-2c8g}" else run_install 2c8g fresh fi ;; 7) require_root load_secrets check_ports_preflight "$NEXUS_ROOT" if is_stack_installed; then check_ports_post "$NEXUS_ROOT" fi pause_enter ;; 0) return 0 ;; *) warn "无效选项" ; sleep 1 ;; esac done } ops_restart_nexus() { step "重启 Nexus(Python)..." if [[ "$NX_RUNTIME" == "docker" ]]; then compose_cmd "$NEXUS_ROOT" restart nexus elif [[ "$NX_RUNTIME" == "supervisor" ]]; then supervisorctl restart nexus else error "未检测到运行中的 Nexus" return 1 fi sleep 3 verify_health "$NEXUS_ROOT" "重启" || true } ops_restart_stack() { step "重启 Compose 栈..." compose_cmd "$NEXUS_ROOT" restart sleep 5 verify_health "$NEXUS_ROOT" "重启" || true } ops_stop_stack() { warn "将停止 Nexus 容器(MySQL/Redis 为外置服务,不受影响)" read -r -p "确认? [y/N]: " c [[ "$c" == "y" || "$c" == "Y" ]] || return 0 compose_cmd "$NEXUS_ROOT" stop info "已停止" } ops_start_stack() { compose_cmd "$NEXUS_ROOT" up -d --remove-orphans verify_nexus_1panel_network "$NEXUS_ROOT" || return 1 verify_health "$NEXUS_ROOT" "启动" || true } ops_logs() { local svc="${1:-nexus}" echo "" read -r -p "跟踪日志行数 [80]: " n n="${n:-80}" compose_cmd "$NEXUS_ROOT" logs --tail="$n" -f "$svc" } ops_status() { if [[ "$NX_RUNTIME" == "docker" ]]; then compose_cmd "$NEXUS_ROOT" ps elif [[ "$NX_RUNTIME" == "supervisor" ]]; then supervisorctl status nexus fi echo "" local port h code port="$(nexus_publish_port "$NEXUS_ROOT")" h=$(curl -sf "http://127.0.0.1:${port}/health" 2>/dev/null || echo "fail") info "/health → ${h}" local cname cname="$(nexus_container_name 2>/dev/null || true)" code=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:${port}/app/install.html" 2>/dev/null || echo "000") if [[ -n "$cname" ]] && nexus_container_has_install_lock "$cname"; then if [[ "$code" == "404" ]]; then info "/app/install.html → 已归档 (HTTP 404)" else warn "/app/install.html → 应已归档但 HTTP ${code}" fi else info "/app/install.html → HTTP ${code}" fi } ops_backup_now() { NO_BACKUP=false backup_mysql "$NEXUS_ROOT" } ops_install_cron() { bash "${NX_DIR}/install_ops_cron.sh" } ops_update() { local args=() PRUNE_IMAGES=false NO_BACKUP=false NO_CACHE=false read -r -p "跳过 MySQL 备份? [y/N]: " nb [[ "$nb" == "y" || "$nb" == "Y" ]] && args+=(--no-backup) read -r -p "无缓存重建镜像? [y/N]: " nc [[ "$nc" == "y" || "$nc" == "Y" ]] && args+=(--no-cache) read -r -p "升级后清理悬空镜像? [y/N]: " pr [[ "$pr" == "y" || "$pr" == "Y" ]] && args+=(--prune) bash "${NX_DIR}/update.sh" "${args[@]}" pause_enter } require_installed() { if ! is_stack_installed; then warn "尚未安装,请先使用菜单 [1] 安装" pause_enter return 1 fi require_root load_secrets load_saved_profile "$NEXUS_ROOT" detect_runtime return 0 } menu_main() { load_secrets while true; do menu_header "运维菜单" cat <<'EOF' ── 安装 ── [1] 安装向导(新机 / 迁机 / 档位) [2] 全新安装 → /app/install.html ── 日常运维(需已安装)── [3] 状态 + 健康检查 [4] 一键更新(pull + 重建镜像)★ [5] 重启 Nexus(Python) [6] 查看 Nexus 日志 [7] 启动 / 停止 Compose 栈 [8] 重建镜像并启动(--build nexus) [9] 备份 MySQL [e] 安装巡检/备份 cron(health_monitor + db_backup) [v] 1Panel 安装/运维验收脚本 ── 其它 ── [a] 检查 Git 是否有更新 [b] 端口 / 防火墙检查 [c] 进入 Nexus 容器 shell [d] 1Panel 反代说明 [0] 退出 EOF read -r -p "请选择: " choice case "$choice" in 1) menu_install ;; 2) require_root if is_stack_installed; then warn "将执行全新安装(清除 nexus-state 卷内旧配置并重建容器)" read -r -p "确认继续? [y/N]: " fresh_ok [[ "$fresh_ok" == "y" || "$fresh_ok" == "Y" ]] || continue fi exec bash "${NX_DIR}/install-nexus-fresh.sh" --profile "${NEXUS_PROFILE:-2c8g}" ;; 3) require_root if is_stack_installed; then load_saved_profile "$NEXUS_ROOT" detect_runtime ops_status else check_ports_preflight "$NEXUS_ROOT" fi pause_enter ;; 4) require_installed || continue ops_update ;; 5) require_installed || continue ops_restart_nexus pause_enter ;; 6) require_installed || continue ops_logs nexus ;; 7) require_installed || continue echo " [1] 启动 [2] 停止 [3] 重启栈" read -r -p "选: " s case "$s" in 1) ops_start_stack ;; 2) ops_stop_stack ;; 3) ops_restart_stack ;; *) warn "无效" ;; esac pause_enter ;; 8) require_installed || continue read -r -p "无缓存重建? [y/N]: " nc if [[ "$nc" == "y" || "$nc" == "Y" ]]; then compose_cmd "$NEXUS_ROOT" build --no-cache nexus fi compose_cmd "$NEXUS_ROOT" up -d --build --remove-orphans nexus verify_nexus_1panel_network "$NEXUS_ROOT" || true verify_health "$NEXUS_ROOT" "重建" || true pause_enter ;; 9) require_installed || continue ops_backup_now pause_enter ;; e|E) require_root ops_install_cron pause_enter ;; v|V) require_root bash "${NX_DIR}/verify-1panel-install-wizard.sh" pause_enter ;; a|A) require_root cmd_check pause_enter ;; b|B) require_root check_ports_preflight "$NEXUS_ROOT" is_stack_installed && check_ports_post "$NEXUS_ROOT" || true pause_enter ;; c|C) require_installed || continue compose_cmd "$NEXUS_ROOT" exec nexus bash || compose_cmd "$NEXUS_ROOT" exec nexus sh ;; d|D) info "1Panel → 网站 → 创建站点 ${NEXUS_DOMAIN}" info "反向代理: http://127.0.0.1:$(nexus_publish_port "$NEXUS_ROOT")" info "示例: ${NEXUS_ROOT}/deploy/1panel/openresty-nexus.conf.example" pause_enter ;; 0) exit 0 ;; *) warn "无效选项"; sleep 1 ;; esac done } nx_main() { local sub="${1:-}" install_nx_cli load_secrets case "$sub" in ""|menu|god|ops|admin) # god/ops 已合并进主菜单,保留别名兼容 [[ "$sub" == "god" || "$sub" == "ops" || "$sub" == "admin" ]] && \ warn "「上帝菜单」已合并进 nx 主菜单,直接选对应项即可" menu_main ;; install) menu_install ;; update|upgrade) require_root shift || true exec bash "${NX_DIR}/update.sh" "$@" ;; install-fresh|fresh) require_root exec bash "${NX_DIR}/install-nexus-fresh.sh" "$@" ;; install-auto|auto) require_root if is_stack_installed; then warn "已安装,install-auto 将执行升级而非重复安装" exec bash "${NX_DIR}/update.sh" else run_install "${NEXUS_PROFILE:-2c8g}" "" fi ;; health) require_root is_stack_installed && ops_status || check_ports_preflight "$NEXUS_ROOT" ;; cron) require_root ops_install_cron ;; verify) require_root bash "${NX_DIR}/verify-1panel-install-wizard.sh" ;; -h|--help) cat </dev/null || true nx_main "$@"