#!/usr/bin/env bash # Full production deploy: local rsync + backend upgrade + frontend + health # # 默认从本机工作区 rsync 到生产机,不 push Gitea、不远程 git pull。 # # Prerequisites on YOUR machine: # - rsync, SSH 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 # # 仍走 Gitea(仅当显式设置): # NEXUS_DEPLOY_VIA_GIT=1 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[@]}" "$@"; } DEPLOY_VIA_GIT="${NEXUS_DEPLOY_VIA_GIT:-0}" case "${DEPLOY_VIA_GIT}" in 1|true|yes|on) DEPLOY_VIA_GIT=1 ;; *) DEPLOY_VIA_GIT=0 ;; esac echo "═══ Nexus production deploy ═══" echo "SSH target: ${NEXUS_SSH_HOST}" echo "Mode: $([[ "$DEPLOY_VIA_GIT" == 1 ]] && echo 'Gitea git pull' || echo 'local rsync')" 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}" resolve_deploy_chown() { if [[ -n "${NEXUS_DEPLOY_CHOWN:-}" ]]; then echo "${NEXUS_DEPLOY_CHOWN}" return fi if ssh_cmd "getent passwd www >/dev/null 2>&1"; then echo "www:www" return fi ssh_cmd "stat -c '%U:%G' '${DEPLOY_PATH}'" 2>/dev/null || echo "azureuser:azureuser" } DEPLOY_CHOWN="$(resolve_deploy_chown)" echo "File owner: ${DEPLOY_CHOWN}" if [[ "$DEPLOY_VIA_GIT" == 1 ]]; then echo "▶ Git mode: remote git pull..." if [[ "$RUNTIME" == "docker" ]]; then 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 ssh_cmd "cd ${DEPLOY_PATH} && git fetch --all && git reset --hard origin/main && supervisorctl restart nexus" else echo "ERROR: 无法识别远程运行时" >&2 exit 1 fi else echo "▶ Local sync → ${DEPLOY_PATH}..." NEXUS_DEPLOY_PATH="${DEPLOY_PATH}" bash "${ROOT}/deploy/rsync-local-to-server.sh" if [[ "$RUNTIME" == "docker" ]]; then echo "▶ Docker: 重建镜像(--skip-git)..." ssh_cmd "sudo env NEXUS_ROOT=${DEPLOY_PATH} bash ${DEPLOY_PATH}/deploy/nexus-1panel.sh upgrade --skip-git" elif [[ "$RUNTIME" == "supervisor" ]]; then echo "▶ Supervisor: restart..." ssh_cmd "supervisorctl restart nexus" else echo "ERROR: 无法识别远程运行时(非 Docker 也非 Supervisor)" >&2 echo " 请手动: ssh ${NEXUS_SSH_HOST} 'sudo nx update'" >&2 exit 1 fi fi if [[ "$RUNTIME" == "docker" || "$RUNTIME" == "supervisor" ]]; then 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 && sudo chown -R ${DEPLOY_CHOWN} ${DEPLOY_PATH}/web/app" echo "▶ Frontend V2: local vite build:safe + upload to host web/app-v2..." cd frontend-v2 && npm run build:safe && cd .. tar czf /tmp/nexus-frontend-v2.tar.gz -C web/app-v2 index.html assets/ scp_cmd /tmp/nexus-frontend-v2.tar.gz "${NEXUS_SSH_HOST}:/tmp/nexus-frontend-v2.tar.gz" ssh_cmd "sudo mkdir -p ${DEPLOY_PATH}/web/app-v2 && cd ${DEPLOY_PATH}/web/app-v2 && sudo tar xzf /tmp/nexus-frontend-v2.tar.gz && rm /tmp/nexus-frontend-v2.tar.gz && sudo chown -R ${DEPLOY_CHOWN} ${DEPLOY_PATH}/web/app-v2" if ssh_cmd "test -f ${DEPLOY_PATH}/deploy/prune_frontend_assets.py"; then ssh_cmd "sudo python3 ${DEPLOY_PATH}/deploy/prune_frontend_assets.py ${DEPLOY_PATH}/web/app --dry-run" ssh_cmd "sudo python3 ${DEPLOY_PATH}/deploy/prune_frontend_assets.py ${DEPLOY_PATH}/web/app" fi fi if [[ "$RUNTIME" == "docker" ]]; then echo "▶ Sync host web/app and web/app-v2 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") SPA_V2=$(ssh_cmd "curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:${PORT}/app-v2/" 2>/dev/null || echo "000") echo " /health → ${HEALTH}" echo " /app/ → ${SPA}" echo " /app-v2/ → ${SPA_V2}" if [[ "${HEALTH}" == "ok" && "${SPA}" == "200" && "${SPA_V2}" == "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