#!/bin/bash # 服务器迁移脚本 - 从Git仓库拉取源码和备份数据库 # 新服务器配置:45.192.99.48,端口3125,域名ai.yydd.ai,HTTPS set -e # 遇到错误立即退出 # 新服务器配置 NEW_SERVER="45.192.99.48" NEW_PORT="22" NEW_USER="root" NEW_PASS="O1zqP731oZ" DOMAIN="ai.yydd.ai" APP_PORT="3125" # Git仓库配置 GIT_REPO="http://git.1080p.hk/chenpei/ai-chatbot.git" GIT_USER="chenpei" GIT_PASS="jsjj654321" # 本地配置(仅用于数据库备份) PROJECT_DIR="/Users/lanmu/Desktop/ai-chatbot-main" BACKUP_DIR="$PROJECT_DIR/migration-backup-complete" DB_BACKUP_FILE="$BACKUP_DIR/database_complete.dump" # 服务器项目目录 SERVER_PROJECT_DIR="/var/www/ai-chatbot" echo "==========================================" echo "AI Chatbot 服务器迁移脚本" echo "==========================================" echo "新服务器: $NEW_USER@$NEW_SERVER:$NEW_PORT" echo "域名: $DOMAIN" echo "应用端口: $APP_PORT" echo "从Git仓库拉取源码和备份数据库" echo "时间: $(date)" echo "==========================================" echo "" # 检查 sshpass 是否安装 if ! command -v sshpass &> /dev/null; then echo "❌ sshpass 未安装,正在安装..." if [[ "$OSTYPE" == "darwin"* ]]; then brew install hudochenkov/sshpass/sshpass || echo "请手动安装: brew install hudochenkov/sshpass/sshpass" else sudo apt-get update && sudo apt-get install -y sshpass fi fi # 检查本地备份文件 if [ ! -f "$DB_BACKUP_FILE" ]; then echo "❌ 未找到数据库备份文件: $DB_BACKUP_FILE" echo "请确保备份文件存在" exit 1 fi echo "✅ 找到数据库备份文件: $DB_BACKUP_FILE ($(du -h "$DB_BACKUP_FILE" | cut -f1))" # 检查Git仓库配置 echo "✅ Git仓库: $GIT_REPO" # SSH选项(保持连接) SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=10 -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o TCPKeepAlive=yes" # 测试新服务器连接 echo "" echo "🔍 测试新服务器连接..." if sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" $SSH_OPTS "$NEW_USER@$NEW_SERVER" "echo '连接成功'" > /dev/null 2>&1; then echo "✅ 新服务器连接正常" else echo "❌ 无法连接到新服务器" exit 1 fi # 步骤1: 在新服务器上安装必要软件 echo "" echo "📦 步骤 1/8: 在新服务器上安装必要软件..." sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" $SSH_OPTS "$NEW_USER@$NEW_SERVER" << 'INSTALLEOF' set -e echo "检测系统类型..." if [ -f /etc/redhat-release ]; then # CentOS/RHEL系统 echo "检测到CentOS/RHEL系统,使用dnf包管理器" echo "跳过系统更新,直接安装所需软件..." echo "安装基础工具..." dnf install -y --nogpgcheck curl wget git gcc gcc-c++ make 2>&1 | tail -3 || dnf install -y curl wget git gcc gcc-c++ make 2>&1 | tail -3 echo "安装PostgreSQL..." if ! command -v psql &> /dev/null; then dnf install -y --nogpgcheck postgresql-server postgresql-contrib 2>&1 | tail -3 || dnf install -y postgresql-server postgresql-contrib 2>&1 | tail -3 postgresql-setup --initdb 2>/dev/null || /usr/bin/postgresql-setup --initdb || echo "⚠️ PostgreSQL初始化跳过" systemctl start postgresql 2>/dev/null || true systemctl enable postgresql 2>/dev/null || true fi echo "安装Node.js 20.x..." echo "使用NodeSource安装Node.js 20..." curl -fsSL https://rpm.nodesource.com/setup_20.x | bash - dnf install -y --nogpgcheck nodejs 2>&1 | tail -3 || dnf install -y nodejs 2>&1 | tail -3 echo "Node.js安装完成" echo "安装PM2和pnpm..." if ! command -v pm2 &> /dev/null; then npm install -g pm2 fi if ! command -v pnpm &> /dev/null; then npm install -g pnpm fi echo "安装Nginx..." if ! command -v nginx &> /dev/null; then dnf install -y --nogpgcheck nginx 2>&1 | tail -3 || dnf install -y nginx 2>&1 | tail -3 systemctl start nginx 2>/dev/null || true systemctl enable nginx 2>/dev/null || true fi echo "安装Certbot (Let's Encrypt)..." if ! command -v certbot &> /dev/null; then # 安装EPEL仓库(CentOS需要) dnf install -y --nogpgcheck epel-release 2>&1 | tail -2 || dnf install -y epel-release 2>&1 | tail -2 dnf install -y --nogpgcheck certbot python3-certbot-nginx 2>&1 | tail -3 || dnf install -y certbot python3-certbot-nginx 2>&1 | tail -3 fi else # Ubuntu/Debian系统 echo "检测到Ubuntu/Debian系统,使用apt-get包管理器" echo "更新系统包..." export DEBIAN_FRONTEND=noninteractive apt-get update -qq echo "安装基础工具..." apt-get install -y -qq curl wget git build-essential software-properties-common echo "安装PostgreSQL..." if ! command -v psql &> /dev/null; then apt-get install -y -qq postgresql postgresql-contrib systemctl start postgresql systemctl enable postgresql fi echo "安装Node.js 20.x..." if ! command -v node &> /dev/null || [ "$(node -v | cut -d'v' -f2 | cut -d'.' -f1)" -lt 20 ]; then curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt-get install -y -qq nodejs fi echo "安装PM2和pnpm..." if ! command -v pm2 &> /dev/null; then npm install -g pm2 fi if ! command -v pnpm &> /dev/null; then npm install -g pnpm fi echo "安装Nginx..." if ! command -v nginx &> /dev/null; then apt-get install -y -qq nginx systemctl start nginx systemctl enable nginx fi echo "安装Certbot (Let's Encrypt)..." if ! command -v certbot &> /dev/null; then apt-get install -y -qq certbot python3-certbot-nginx fi fi echo "✅ 所有软件安装完成" INSTALLEOF # 步骤2: 从Git仓库克隆项目 echo "" echo "📥 步骤 2/8: 从Git仓库克隆项目..." sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" $SSH_OPTS "$NEW_USER@$NEW_SERVER" << CLONEEOF set -e # 如果目录已存在,先删除 if [ -d "$SERVER_PROJECT_DIR" ]; then echo "删除现有目录..." rm -rf $SERVER_PROJECT_DIR fi # 从Git仓库克隆(使用用户名和密码) echo "正在从Git仓库克隆..." GIT_URL="http://${GIT_USER}:${GIT_PASS}@git.1080p.hk/chenpei/ai-chatbot.git" git clone "\$GIT_URL" $SERVER_PROJECT_DIR || { echo "⚠️ 使用URL中的凭据克隆失败,尝试直接使用凭据..." git clone http://${GIT_USER}:${GIT_PASS}@git.1080p.hk/chenpei/ai-chatbot.git $SERVER_PROJECT_DIR } cd $SERVER_PROJECT_DIR chown -R root:root . echo "✅ 项目代码已从Git仓库克隆" CLONEEOF # 步骤3: 上传数据库备份 echo "" echo "📤 步骤 3/8: 上传数据库备份..." sshpass -p "$NEW_PASS" scp -P "$NEW_PORT" -o StrictHostKeyChecking=no -o ServerAliveInterval=60 "$DB_BACKUP_FILE" "$NEW_USER@$NEW_SERVER:/tmp/database.dump" echo "✅ 数据库备份已上传" # 步骤4: 创建数据库 echo "" echo "🗄️ 步骤 4/8: 创建数据库..." sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" $SSH_OPTS "$NEW_USER@$NEW_SERVER" << 'DBEOF' set -e # 生成随机数据库密码 DB_PASSWORD=$(openssl rand -base64 24 | tr -d "=+/" | cut -c1-25) echo "生成的数据库密码: $DB_PASSWORD" echo "$DB_PASSWORD" > /root/db_password.txt # 启动PostgreSQL systemctl start postgresql # 创建数据库和用户 sudo -u postgres psql << PSQLEOF -- 创建数据库(如果不存在) SELECT 'CREATE DATABASE ai_chatbot' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'ai_chatbot')\gexec -- 创建用户(如果不存在) DO \$\$ BEGIN IF NOT EXISTS (SELECT FROM pg_user WHERE usename = 'chatbot_user') THEN CREATE USER chatbot_user WITH PASSWORD '$DB_PASSWORD'; ELSE ALTER USER chatbot_user WITH PASSWORD '$DB_PASSWORD'; END IF; END \$\$; -- 授权 GRANT ALL PRIVILEGES ON DATABASE ai_chatbot TO chatbot_user; ALTER DATABASE ai_chatbot OWNER TO chatbot_user; PSQLEOF echo "✅ 数据库创建完成" DBEOF # 步骤5: 配置环境变量和导入数据库 echo "" echo "⚙️ 步骤 5/8: 配置环境变量和导入数据库..." sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" $SSH_OPTS "$NEW_USER@$NEW_SERVER" << CONFIGEOF set -e cd /var/www/ai-chatbot # 读取数据库密码 DB_PASSWORD=\$(cat /root/db_password.txt) # 生成AUTH_SECRET AUTH_SECRET=\$(openssl rand -base64 32) # 创建环境变量文件 cat > .env.local << ENVFILE POSTGRES_URL=postgresql://chatbot_user:\${DB_PASSWORD}@localhost:5432/ai_chatbot AUTH_SECRET=\${AUTH_SECRET} AUTH_URL=https://${DOMAIN} NODE_ENV=production PORT=${APP_PORT} ENVFILE echo "✅ 环境变量文件已创建" # 导入数据库 echo "正在导入数据库..." export PGPASSWORD="\$DB_PASSWORD" pg_restore -h localhost -p 5432 -U chatbot_user -d ai_chatbot -c --no-owner --no-privileges /tmp/database.dump 2>&1 | grep -v -E "(already exists|does not exist|ERROR.*already)" || true echo "✅ 数据库导入完成(已忽略重复项)" # 安装依赖 echo "正在安装依赖..." pnpm install --frozen-lockfile echo "✅ 依赖安装完成" # 运行数据库迁移(如果需要) echo "正在运行数据库迁移..." pnpm db:migrate || echo "⚠️ 数据库迁移跳过或失败" echo "✅ 数据库迁移完成" # 构建项目 echo "正在构建项目..." pnpm build echo "✅ 项目构建完成" CONFIGEOF # 步骤6: 配置PM2 echo "" echo "🚀 步骤 6/8: 配置PM2..." sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" $SSH_OPTS "$NEW_USER@$NEW_SERVER" << 'PM2EOF' set -e cd /var/www/ai-chatbot # 创建PM2配置文件 cat > ecosystem.config.js << 'PM2CONFIG' module.exports = { apps: [{ name: 'ai-chatbot', script: 'node_modules/next/dist/bin/next', args: 'start', cwd: '/var/www/ai-chatbot', instances: 1, exec_mode: 'fork', env: { NODE_ENV: 'production', PORT: 3125 }, error_file: '/var/log/pm2/ai-chatbot-error.log', out_file: '/var/log/pm2/ai-chatbot-out.log', log_date_format: 'YYYY-MM-DD HH:mm:ss Z', merge_logs: true, autorestart: true, max_memory_restart: '1G' }] }; PM2CONFIG # 创建日志目录 mkdir -p /var/log/pm2 # 启动应用 pm2 delete ai-chatbot 2>/dev/null || true pm2 start ecosystem.config.js pm2 save pm2 startup systemd -u root --hp /root || true echo "✅ PM2 配置完成" PM2EOF # 步骤7: 配置Nginx(HTTP,稍后配置HTTPS) echo "" echo "🌐 步骤 7/8: 配置Nginx..." sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" $SSH_OPTS "$NEW_USER@$NEW_SERVER" << 'NGINXEOF' set -e DOMAIN="ai.yydd.ai" APP_PORT="3125" # 创建Nginx配置(HTTP,用于Certbot验证) cat > /etc/nginx/sites-available/ai-chatbot << 'NGINXCONFIG' server { listen 80; server_name ai.yydd.ai; # 关闭缓存 proxy_cache off; proxy_buffering off; proxy_no_cache 1; proxy_cache_bypass 1; add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; add_header Pragma "no-cache"; add_header Expires "0"; location / { proxy_pass http://localhost:3125; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 300s; proxy_connect_timeout 75s; # 禁用缓存 proxy_set_header Cache-Control "no-cache, no-store, must-revalidate"; } } NGINXCONFIG # 启用站点 ln -sf /etc/nginx/sites-available/ai-chatbot /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default # 测试配置 nginx -t # 重载Nginx systemctl reload nginx echo "✅ Nginx HTTP配置完成" NGINXEOF # 步骤8: 配置HTTPS和强制HTTPS echo "" echo "🔒 步骤 8/8: 配置HTTPS证书和强制HTTPS..." sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" $SSH_OPTS "$NEW_USER@$NEW_SERVER" << 'HTTPSEOF' set -e DOMAIN="ai.yydd.ai" APP_PORT="3125" # 获取Let's Encrypt证书 echo "正在获取SSL证书..." certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --email admin@yydd.ai --redirect || { echo "⚠️ Certbot执行失败,可能需要手动配置" echo "手动运行: certbot --nginx -d $DOMAIN" } # 更新Nginx配置以强制HTTPS和关闭缓存 cat > /etc/nginx/sites-available/ai-chatbot << 'NGINXHTTPS' # HTTP重定向到HTTPS server { listen 80; server_name ai.yydd.ai; return 301 https://$server_name$request_uri; } # HTTPS配置 server { listen 443 ssl http2; server_name ai.yydd.ai; # SSL证书配置(Certbot会自动添加) ssl_certificate /etc/letsencrypt/live/ai.yydd.ai/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/ai.yydd.ai/privkey.pem; # SSL优化配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 关闭缓存 proxy_cache off; proxy_buffering off; proxy_no_cache 1; proxy_cache_bypass 1; add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always; add_header Pragma "no-cache" always; add_header Expires "0" always; location / { proxy_pass http://localhost:3125; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 300s; proxy_connect_timeout 75s; # 禁用缓存 proxy_set_header Cache-Control "no-cache, no-store, must-revalidate"; proxy_hide_header Cache-Control; proxy_hide_header Expires; } } NGINXHTTPS # 测试配置 nginx -t && systemctl reload nginx echo "✅ HTTPS配置完成" HTTPSEOF # 清理临时文件 echo "" echo "🧹 清理临时文件..." sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" $SSH_OPTS "$NEW_USER@$NEW_SERVER" "rm -f /tmp/project.tar.gz /tmp/database.dump" echo "" echo "==========================================" echo "✅ 迁移完成!" echo "==========================================" echo "新服务器地址: https://$DOMAIN" echo "应用端口: $APP_PORT" echo "" echo "📝 重要信息:" echo "数据库密码已保存在: /root/db_password.txt" echo "" echo "🔍 验证步骤:" echo "1. 检查PM2状态: ssh -p $NEW_PORT $NEW_USER@$NEW_SERVER 'pm2 status'" echo "2. 检查日志: ssh -p $NEW_PORT $NEW_USER@$NEW_SERVER 'pm2 logs ai-chatbot'" echo "3. 检查Nginx状态: ssh -p $NEW_PORT $NEW_USER@$NEW_SERVER 'systemctl status nginx'" echo "4. 检查SSL证书: ssh -p $NEW_PORT $NEW_USER@$NEW_SERVER 'certbot certificates'" echo "5. 访问网站: https://$DOMAIN" echo "" echo "⚠️ 注意事项:" echo "- 如果Certbot失败,请手动运行: certbot --nginx -d $DOMAIN" echo "- 确保域名 $DOMAIN 的DNS已指向 $NEW_SERVER" echo "- 检查防火墙是否开放80和443端口" echo "=========================================="