#!/usr/bin/env bash # ============================================================ # 中华文明全图鉴 —— 一键部署脚本 # # 用法: # ./deploy.sh # 同步源码 + 安装 + 构建 + 重启(默认全量) # ./deploy.sh --migrate # 额外执行数据库 migration # ./deploy.sh --seed # 额外执行 migration + seed(首次部署用) # ./deploy.sh --api # 只构建并重启后端 api # ./deploy.sh --web # 只构建并重启前端 web # ./deploy.sh --admin # 只构建管理后台(静态) # ./deploy.sh --restart # 不构建,仅重启 pm2 进程 # ./deploy.sh --no-sync # 跳过 rsync(用服务器现有代码构建) # # 鉴权: # 优先使用 SSH 密钥(推荐,配好后免密)。 # 如需密码登录,设置环境变量 DEPLOY_PASS 后运行(需本机装 sshpass): # DEPLOY_PASS='xxxx' ./deploy.sh # # 可用环境变量覆盖目标(默认指向生产机): # REMOTE_USER REMOTE_HOST REMOTE_DIR SSH_PORT # ============================================================ set -euo pipefail # ---- 目标服务器(可用环境变量覆盖)---- REMOTE_USER="${REMOTE_USER:-ubuntu}" REMOTE_HOST="${REMOTE_HOST:-152.136.182.184}" REMOTE_DIR="${REMOTE_DIR:-/home/ubuntu/wenwumap}" SSH_PORT="${SSH_PORT:-22}" # ---- 本地项目根(脚本所在目录)---- LOCAL_DIR="$(cd "$(dirname "$0")" && pwd)" # ---- 解析参数 ---- DO_SYNC=1; DO_INSTALL=1 BUILD_API=0; BUILD_WEB=0; BUILD_ADMIN=0 DO_MIGRATE=0; DO_SEED=0; RESTART_ONLY=0 SELECTIVE=0 for arg in "$@"; do case "$arg" in --no-sync) DO_SYNC=0 ;; --migrate) DO_MIGRATE=1 ;; --seed) DO_SEED=1; DO_MIGRATE=1 ;; --api) BUILD_API=1; SELECTIVE=1 ;; --web) BUILD_WEB=1; SELECTIVE=1 ;; --admin) BUILD_ADMIN=1; SELECTIVE=1 ;; --restart) RESTART_ONLY=1 ;; -h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "未知参数:$arg(用 --help 查看用法)"; exit 1 ;; esac done # 默认(未指定 --api/--web/--admin)则三者全构建 if [ "$SELECTIVE" -eq 0 ]; then BUILD_API=1; BUILD_WEB=1; BUILD_ADMIN=1; fi # ---- 组装 ssh / rsync 鉴权 ---- SSH_BASE=(ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -p "$SSH_PORT") if [ -n "${DEPLOY_PASS:-}" ]; then if ! command -v sshpass >/dev/null 2>&1; then echo "✗ 设置了 DEPLOY_PASS 但未安装 sshpass。请 brew install hudochenkov/sshpass/sshpass 或改用 SSH 密钥。" exit 1 fi SSH=(sshpass -p "$DEPLOY_PASS" "${SSH_BASE[@]}") else SSH=("${SSH_BASE[@]}") fi REMOTE=("${SSH[@]}" "${REMOTE_USER}@${REMOTE_HOST}") run_remote() { "${REMOTE[@]}" "bash -s"; } echo "============================================================" echo " 部署目标 : ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}" echo " 同步源码 : $([ $DO_SYNC -eq 1 ] && echo 是 || echo 否)" echo " 构建 : api=$BUILD_API web=$BUILD_WEB admin=$BUILD_ADMIN" echo " 迁移/灌种: migrate=$DO_MIGRATE seed=$DO_SEED" echo "============================================================" # ============================================================ # 1) 同步源码(关键:绝不覆盖服务器 .env / node_modules / 构建产物) # ============================================================ if [ "$RESTART_ONLY" -eq 0 ] && [ "$DO_SYNC" -eq 1 ]; then echo "▶ [1/5] rsync 源码到服务器(排除 .env 等)..." RSYNC_RSH="${SSH[*]}" rsync -az --delete \ --exclude='.git/' \ --exclude='.env' \ --exclude='**/.env' \ --exclude='node_modules/' \ --exclude='**/node_modules/' \ --exclude='**/dist/' \ --exclude='**/.next/' \ --exclude='**/.cache/' \ --exclude='apps/api/.cache/' \ --exclude='e2e/videos/' \ --exclude='*.zip' \ --exclude='.DS_Store' \ -e "${RSYNC_RSH}" \ "${LOCAL_DIR}/" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/" echo " ✓ 源码已同步" fi # ============================================================ # 2) 远程:检查 .env、安装依赖、迁移、构建、重启 # ============================================================ run_remote <&1 | tail -4 fi if [ "\$DO_MIGRATE" -eq 1 ]; then echo "▶ 数据库 migration ..." pnpm --filter @wenwumap/db migrate 2>&1 | tail -6 fi if [ "\$DO_SEED" -eq 1 ]; then echo "▶ 数据库 seed ..." pnpm --filter @wenwumap/db seed 2>&1 | tail -4 fi # 关键:构建前加载 .env,使 NEXT_PUBLIC_* / VITE_API_URL / ADMIN_BASE 正确注入产物 set -a; . ./.env; set +a export NODE_OPTIONS="--max-old-space-size=1536" if [ "\$BUILD_API" -eq 1 ]; then echo "▶ [3/5] 构建 api ..." pnpm --filter @wenwumap/api build 2>&1 | tail -3 fi if [ "\$BUILD_ADMIN" -eq 1 ]; then echo "▶ 构建 admin (base=\${ADMIN_BASE:-/}) ..." pnpm --filter @wenwumap/admin build 2>&1 | tail -3 fi if [ "\$BUILD_WEB" -eq 1 ]; then echo "▶ 构建 web (API=\${NEXT_PUBLIC_API_URL:-?}) ..." pnpm --filter @wenwumap/web build 2>&1 | tail -4 fi fi echo "▶ [4/5] 重启 pm2 进程 ..." if [ -f ecosystem.config.js ]; then pm2 startOrReload ecosystem.config.js --update-env >/dev/null 2>&1 || pm2 restart wenwumap-api wenwumap-web --update-env else pm2 restart wenwumap-api wenwumap-web --update-env fi pm2 save >/dev/null 2>&1 || true echo "▶ [5/5] 健康检查 ..." sleep 3 PORT=\$(grep -E '^PORT=' .env | cut -d= -f2 | tr -d '[:space:]'); PORT=\${PORT:-3101} echo -n " api : "; curl -s -m 8 -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:\${PORT}/api/v1/map/stats" || echo "FAIL" pm2 list | grep -E "wenwumap" || true echo "✓ 远程部署完成" REMOTE_SCRIPT echo "============================================================" echo " ✅ 部署完成:https://ww.all8ai.top" echo "============================================================"