d0be2fdcf1
使 deploy-production.sh 可读本机 secrets 并在非 docker 组用户下用 sudo 识别 1Panel 栈;修复 security_probe_external.sh 的 curl shift 误用与 WebSocket 自动 venv。
134 lines
5.0 KiB
Bash
134 lines
5.0 KiB
Bash
#!/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
|
||
|
||
sleep 3
|
||
PORT="$(ssh_cmd "grep -E '^NEXUS_PUBLISH_PORT=' ${DEPLOY_PATH}/docker/.env.prod 2>/dev/null | cut -d= -f2 | tr -d '\"' || echo 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"
|
||
ssh_cmd "command -v crontab >/dev/null && crontab -l 2>/dev/null | grep -q nexus-health-monitor" 2>/dev/null || \
|
||
echo " 提示: 远程未安装巡检 cron,可在服务器执行 sudo nx cron"
|
||
else
|
||
echo "✗ Verification failed — check logs on server" >&2
|
||
exit 1
|
||
fi
|