#!/usr/bin/env bash # Full production deploy: push (optional) + backend upgrade + frontend + health # # 自动识别远程 Docker(/opt/nexus)或 Supervisor(/www/wwwroot/...)运行时。 # # Prerequisites on YOUR machine: # - SSH: ~/.ssh/config Host nexus OR export NEXUS_SSH="azureuser@20.24.218.235" # - Key: export NEXUS_SSH_KEY=~/.ssh/id_rsa_nexus # # Usage: # cd "$NEXUS_ROOT" # bash deploy/pre_deploy_check.sh # bash deploy/deploy-production.sh set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "${ROOT}" SECRETS="${ROOT}/deploy/nexus-1panel.secrets.sh" if [[ -f "$SECRETS" ]]; then # shellcheck disable=SC1090 source "$SECRETS" fi NEXUS_SSH_HOST="${NEXUS_SSH:-nexus}" SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=15) if [[ -n "${NEXUS_SSH_KEY:-}" ]]; then SSH_OPTS+=(-i "${NEXUS_SSH_KEY}") fi ssh_cmd() { ssh "${SSH_OPTS[@]}" "${NEXUS_SSH_HOST}" "$@"; } scp_cmd() { scp "${SSH_OPTS[@]}" "$@"; } echo "═══ Nexus production deploy ═══" echo "SSH target: ${NEXUS_SSH_HOST}" if ! ssh_cmd "echo SSH OK" >/dev/null 2>&1; then echo "ERROR: Cannot SSH to ${NEXUS_SSH_HOST}" >&2 echo " Configure ~/.ssh/config (Host nexus) or set NEXUS_SSH / NEXUS_SSH_KEY" >&2 exit 1 fi REMOTE_DETECT=$(ssh_cmd 'bash -s' <<'REMOTE' set -euo pipefail docker_cmd() { if docker "$@" 2>/dev/null; then return 0; fi sudo docker "$@" 2>/dev/null } compose_cmd() { if docker compose "$@" 2>/dev/null; then return 0; fi sudo docker compose "$@" 2>/dev/null } for d in /opt/nexus /www/wwwroot/api.synaglobal.vip; do [[ -d "$d/server" ]] || continue echo "ROOT=$d" if [[ -f "$d/docker/.env.prod" ]] && compose_cmd version >/dev/null 2>&1; then prof="$d/docker/.host-profile" [[ -f "$prof" ]] || prof="$d/docker/profiles/2c8g.env" if [[ -f "$prof" ]]; then args=(-f "$d/docker/docker-compose.prod.yml") docker_cmd network inspect 1panel-network >/dev/null 2>&1 && \ [[ -f "$d/docker/docker-compose.1panel.yml" ]] && \ args+=(-f "$d/docker/docker-compose.1panel.yml") q=$(cd "$d" && compose_cmd "${args[@]}" \ --env-file "$d/docker/.env.prod" --env-file "$prof" ps -q nexus 2>/dev/null || true) [[ -n "$q" ]] && echo RUNTIME=docker && exit 0 fi fi if [[ -f "$d/deploy/nexus-1panel.sh" ]] && command -v nx >/dev/null 2>&1; then q=$(docker_cmd ps --format '{{.Names}}' 2>/dev/null | grep -E '^nexus-prod-nexus-' || true) [[ -n "$q" ]] && echo RUNTIME=docker && exit 0 fi if command -v supervisorctl >/dev/null 2>&1 && supervisorctl status nexus >/dev/null 2>&1; then echo RUNTIME=supervisor exit 0 fi echo RUNTIME=unknown exit 0 done echo RUNTIME=none REMOTE ) DEPLOY_PATH="${NEXUS_DEPLOY_PATH:-$(echo "$REMOTE_DETECT" | sed -n 's/^ROOT=//p' | head -1)}" RUNTIME="$(echo "$REMOTE_DETECT" | sed -n 's/^RUNTIME=//p' | head -1)" if [[ -z "$DEPLOY_PATH" ]]; then DEPLOY_PATH="${NEXUS_DEPLOY_PATH:-/opt/nexus}" fi if [[ -z "$RUNTIME" || "$RUNTIME" == "none" ]]; then RUNTIME="unknown" fi echo "Deploy path: ${DEPLOY_PATH}" echo "Runtime: ${RUNTIME}" if [[ "$RUNTIME" == "docker" ]]; then echo "▶ Docker: git pull + 镜像重建(前端在镜像内 vite build)..." ssh_cmd "cd ${DEPLOY_PATH} && sudo git fetch --all && sudo git reset --hard origin/main && \ sudo env NEXUS_ROOT=${DEPLOY_PATH} bash ${DEPLOY_PATH}/deploy/nexus-1panel.sh upgrade" elif [[ "$RUNTIME" == "supervisor" ]]; then echo "▶ Supervisor: git pull + restart..." ssh_cmd "cd ${DEPLOY_PATH} && git fetch --all && git reset --hard origin/main && supervisorctl restart nexus" echo "▶ Frontend: build + upload..." cd frontend && npx vite build && cd .. tar czf /tmp/nexus-frontend.tar.gz -C web/app index.html assets/ scp_cmd /tmp/nexus-frontend.tar.gz "${NEXUS_SSH_HOST}:/tmp/nexus-frontend.tar.gz" ssh_cmd "cd ${DEPLOY_PATH}/web/app && tar xzf /tmp/nexus-frontend.tar.gz && rm /tmp/nexus-frontend.tar.gz" if ssh_cmd "test -f ${DEPLOY_PATH}/deploy/prune_frontend_assets.py"; then ssh_cmd "python3 ${DEPLOY_PATH}/deploy/prune_frontend_assets.py --dry-run" ssh_cmd "python3 ${DEPLOY_PATH}/deploy/prune_frontend_assets.py" fi else echo "ERROR: 无法识别远程运行时(非 Docker 也非 Supervisor)" >&2 echo " 请手动: ssh ${NEXUS_SSH_HOST} 'sudo nx update'" >&2 exit 1 fi if [[ "$RUNTIME" == "docker" ]]; then # Host web/app must match image build before sync_webapp_to_container (else stale assets overwrite container). echo "▶ Frontend: local vite build + upload to host web/app..." cd frontend && npx vite build && cd .. tar czf /tmp/nexus-frontend.tar.gz -C web/app index.html assets/ scp_cmd /tmp/nexus-frontend.tar.gz "${NEXUS_SSH_HOST}:/tmp/nexus-frontend.tar.gz" ssh_cmd "cd ${DEPLOY_PATH}/web/app && sudo tar xzf /tmp/nexus-frontend.tar.gz && rm /tmp/nexus-frontend.tar.gz" echo "▶ Sync host web/app into container..." ssh_cmd "sudo env NEXUS_ROOT=${DEPLOY_PATH} NEXUS_PUBLISH_PORT=\$(grep -E '^NEXUS_PUBLISH_PORT=' ${DEPLOY_PATH}/docker/.env.prod 2>/dev/null | cut -d= -f2 | tr -d '\"' || echo 8600) bash ${DEPLOY_PATH}/deploy/sync_webapp_to_container.sh" || { echo "WARN: web/app sync failed — run sync_webapp_to_container.sh on server manually" >&2 } fi sleep 3 PORT="$(ssh_cmd "grep -E '^NEXUS_PUBLISH_PORT=' ${DEPLOY_PATH}/docker/.env.prod 2>/dev/null | cut -d= -f2 | tr -d '\"'" 2>/dev/null || true)" PORT="${PORT:-8600}" HEALTH=$(ssh_cmd "curl -s http://127.0.0.1:${PORT}/health" 2>/dev/null || true) SPA=$(ssh_cmd "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:${PORT}/app/" 2>/dev/null || echo "000") echo " /health → ${HEALTH}" echo " /app/ → ${SPA}" if [[ "${HEALTH}" == "ok" && "${SPA}" == "200" ]]; then echo "✓ Deploy successful" if ssh_cmd "command -v crontab >/dev/null && crontab -l 2>/dev/null | grep -q nexus-health-monitor" 2>/dev/null; then echo "✓ Ops cron (health_monitor) already installed" else echo "▶ Installing ops cron (health_monitor + db_backup)…" if ssh_cmd "sudo env NEXUS_ROOT=${DEPLOY_PATH} bash ${DEPLOY_PATH}/deploy/install_ops_cron.sh" 2>/dev/null; then echo "✓ Ops cron installed" else echo " 提示: 远程 cron 安装失败,可在服务器执行: sudo nx cron" fi fi else echo "✗ Verification failed — check logs on server" >&2 exit 1 fi