377 lines
12 KiB
Bash
Executable File
377 lines
12 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# 服务器迁移脚本
|
||
# 从旧服务器迁移到新服务器(包括数据库数据)
|
||
|
||
set -e # 遇到错误立即退出
|
||
|
||
# 配置
|
||
OLD_SERVER="156.225.27.52"
|
||
OLD_PORT="5204"
|
||
OLD_USER="root"
|
||
OLD_PASS="${DEPLOY_SSH_PASSWORD:?set DEPLOY_SSH_PASSWORD}"
|
||
|
||
NEW_SERVER="114.66.31.51"
|
||
NEW_PORT="22"
|
||
NEW_USER="root"
|
||
NEW_PASS="vsvhSIHN5531"
|
||
|
||
PROJECT_DIR="/var/www/ai-chatbot"
|
||
BACKUP_DIR="/tmp/ai-chatbot-migration"
|
||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||
|
||
echo "=========================================="
|
||
echo "AI Chatbot 服务器迁移脚本"
|
||
echo "=========================================="
|
||
echo "旧服务器: $OLD_USER@$OLD_SERVER:$OLD_PORT"
|
||
echo "新服务器: $NEW_USER@$NEW_SERVER:$NEW_PORT"
|
||
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
|
||
|
||
# 测试连接
|
||
echo "🔍 测试服务器连接..."
|
||
echo "测试旧服务器连接..."
|
||
if sshpass -p "$OLD_PASS" ssh -p "$OLD_PORT" -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$OLD_USER@$OLD_SERVER" "echo '连接成功'" > /dev/null 2>&1; then
|
||
echo "✅ 旧服务器连接正常"
|
||
else
|
||
echo "❌ 无法连接到旧服务器"
|
||
exit 1
|
||
fi
|
||
|
||
echo "测试新服务器连接..."
|
||
if sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" -o StrictHostKeyChecking=no -o ConnectTimeout=10 "$NEW_USER@$NEW_SERVER" "echo '连接成功'" > /dev/null 2>&1; then
|
||
echo "✅ 新服务器连接正常"
|
||
else
|
||
echo "❌ 无法连接到新服务器"
|
||
exit 1
|
||
fi
|
||
|
||
# 步骤1: 在旧服务器上备份数据库
|
||
echo ""
|
||
echo "📦 步骤 1/9: 在旧服务器上备份数据库..."
|
||
sshpass -p "$OLD_PASS" ssh -p "$OLD_PORT" "$OLD_USER@$OLD_SERVER" << 'EOF'
|
||
cd /var/www/ai-chatbot
|
||
if [ ! -f .env.local ]; then
|
||
echo "❌ 未找到 .env.local 文件"
|
||
exit 1
|
||
fi
|
||
|
||
# 读取 POSTGRES_URL
|
||
POSTGRES_URL=$(grep "^POSTGRES_URL=" .env.local | cut -d '=' -f2- | tr -d '"' | tr -d "'")
|
||
|
||
if [ -z "$POSTGRES_URL" ]; then
|
||
echo "❌ 无法读取 POSTGRES_URL"
|
||
exit 1
|
||
fi
|
||
|
||
# 解析数据库连接信息
|
||
DB_NAME=$(echo "$POSTGRES_URL" | sed -n 's|.*/\([^?]*\)$|\1|p')
|
||
DB_USER=$(echo "$POSTGRES_URL" | sed -n 's|.*://\([^:]*\):.*|\1|p')
|
||
DB_PASS=$(echo "$POSTGRES_URL" | sed -n 's|.*://[^:]*:\([^@]*\)@.*|\1|p')
|
||
DB_HOST=$(echo "$POSTGRES_URL" | sed -n 's|.*@\([^:]*\):.*|\1|p')
|
||
DB_PORT=$(echo "$POSTGRES_URL" | sed -n 's|.*:\([0-9]*\)/.*|\1|p')
|
||
|
||
# 如果端口为空,使用默认端口
|
||
if [ -z "$DB_PORT" ]; then
|
||
DB_PORT=5432
|
||
fi
|
||
|
||
echo "数据库信息: $DB_USER@$DB_HOST:$DB_PORT/$DB_NAME"
|
||
|
||
# 创建备份目录
|
||
mkdir -p /tmp/ai-chatbot-backup
|
||
|
||
# 使用 pg_dump 备份数据库
|
||
export PGPASSWORD="$DB_PASS"
|
||
BACKUP_FILE="/tmp/ai-chatbot-backup/database_backup_$(date +%Y%m%d_%H%M%S).dump"
|
||
pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -F c -f "$BACKUP_FILE" 2>&1
|
||
|
||
if [ $? -eq 0 ]; then
|
||
echo "✅ 数据库备份完成: $BACKUP_FILE"
|
||
ls -lh "$BACKUP_FILE"
|
||
else
|
||
echo "❌ 数据库备份失败"
|
||
exit 1
|
||
fi
|
||
EOF
|
||
|
||
# 步骤2: 下载备份文件
|
||
echo ""
|
||
echo "📥 步骤 2/9: 下载数据库备份文件..."
|
||
mkdir -p "$BACKUP_DIR"
|
||
LATEST_BACKUP=$(sshpass -p "$OLD_PASS" ssh -p "$OLD_PORT" "$OLD_USER@$OLD_SERVER" "ls -t /tmp/ai-chatbot-backup/*.dump 2>/dev/null | head -1")
|
||
if [ -z "$LATEST_BACKUP" ]; then
|
||
echo "❌ 未找到备份文件"
|
||
exit 1
|
||
fi
|
||
echo "正在下载: $LATEST_BACKUP"
|
||
sshpass -p "$OLD_PASS" scp -P "$OLD_PORT" "$OLD_USER@$OLD_SERVER:$LATEST_BACKUP" "$BACKUP_DIR/database.dump"
|
||
if [ $? -eq 0 ]; then
|
||
echo "✅ 数据库备份文件已下载 ($(du -h "$BACKUP_DIR/database.dump" | cut -f1))"
|
||
else
|
||
echo "❌ 数据库备份文件下载失败"
|
||
exit 1
|
||
fi
|
||
|
||
# 步骤3: 下载项目文件和配置
|
||
echo ""
|
||
echo "📥 步骤 3/9: 下载项目文件..."
|
||
sshpass -p "$OLD_PASS" ssh -p "$OLD_PORT" "$OLD_USER@$OLD_SERVER" << 'EOF'
|
||
cd /var/www/ai-chatbot
|
||
# 创建项目打包(排除 node_modules 和 .next)
|
||
tar --exclude='node_modules' \
|
||
--exclude='.next' \
|
||
--exclude='.git' \
|
||
--exclude='*.log' \
|
||
-czf /tmp/ai-chatbot-backup/project.tar.gz .
|
||
echo "✅ 项目文件打包完成"
|
||
EOF
|
||
|
||
sshpass -p "$OLD_PASS" scp -P "$OLD_PORT" "$OLD_USER@$OLD_SERVER:/tmp/ai-chatbot-backup/project.tar.gz" "$BACKUP_DIR/"
|
||
echo "✅ 项目文件已下载"
|
||
|
||
# 下载环境变量文件
|
||
echo ""
|
||
echo "📥 步骤 4/9: 下载环境变量文件..."
|
||
sshpass -p "$OLD_PASS" scp -P "$OLD_PORT" "$OLD_USER@$OLD_SERVER:/var/www/ai-chatbot/.env.local" "$BACKUP_DIR/.env.local" 2>/dev/null || echo "⚠️ 未找到 .env.local 文件(将在新服务器上手动配置)"
|
||
echo "✅ 环境变量文件已下载"
|
||
|
||
# 步骤5: 在新服务器上初始化环境
|
||
echo ""
|
||
echo "🔧 步骤 5/9: 在新服务器上初始化环境..."
|
||
sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" "$NEW_USER@$NEW_SERVER" << 'ENVEOF'
|
||
# 更新系统
|
||
apt-get update -qq
|
||
|
||
# 安装必要的工具
|
||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
||
curl \
|
||
wget \
|
||
git \
|
||
build-essential \
|
||
postgresql-client \
|
||
postgresql \
|
||
nginx \
|
||
certbot \
|
||
python3-certbot-nginx
|
||
|
||
# 安装 Node.js 20.x
|
||
if ! command -v node &> /dev/null; then
|
||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
||
apt-get install -y -qq nodejs
|
||
fi
|
||
|
||
# 安装 PM2
|
||
if ! command -v pm2 &> /dev/null; then
|
||
npm install -g pm2
|
||
fi
|
||
|
||
# 安装 pnpm
|
||
if ! command -v pnpm &> /dev/null; then
|
||
npm install -g pnpm
|
||
fi
|
||
|
||
echo "✅ 环境初始化完成"
|
||
ENVEOF
|
||
|
||
# 步骤6: 创建数据库
|
||
echo ""
|
||
echo "🗄️ 步骤 6/9: 在新服务器上创建数据库..."
|
||
sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" "$NEW_USER@$NEW_SERVER" << 'DBEOF'
|
||
# 启动 PostgreSQL
|
||
systemctl start postgresql
|
||
systemctl enable 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 'chatbot_password_change_me';
|
||
END IF;
|
||
END
|
||
\$\$;
|
||
|
||
-- 授权
|
||
GRANT ALL PRIVILEGES ON DATABASE ai_chatbot TO chatbot_user;
|
||
ALTER DATABASE ai_chatbot OWNER TO chatbot_user;
|
||
PSQLEOF
|
||
|
||
echo "✅ 数据库创建完成"
|
||
DBEOF
|
||
|
||
# 步骤7: 上传文件到新服务器
|
||
echo ""
|
||
echo "📤 步骤 7/9: 上传文件到新服务器..."
|
||
sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" "$NEW_USER@$NEW_SERVER" "mkdir -p $PROJECT_DIR"
|
||
sshpass -p "$NEW_PASS" scp -P "$NEW_PORT" "$BACKUP_DIR/project.tar.gz" "$NEW_USER@$NEW_SERVER:/tmp/"
|
||
sshpass -p "$NEW_PASS" scp -P "$NEW_PORT" "$BACKUP_DIR/database.dump" "$NEW_USER@$NEW_SERVER:/tmp/"
|
||
|
||
# 在新服务器上解压项目
|
||
sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" "$NEW_USER@$NEW_SERVER" << 'EXTRACTEOF'
|
||
cd /var/www/ai-chatbot
|
||
tar -xzf /tmp/project.tar.gz
|
||
chown -R root:root .
|
||
echo "✅ 项目文件已解压"
|
||
EXTRACTEOF
|
||
|
||
# 步骤8: 在新服务器上配置和恢复
|
||
echo ""
|
||
echo "⚙️ 步骤 8/9: 在新服务器上配置和恢复..."
|
||
sshpass -p "$NEW_PASS" ssh -p "$NEW_PORT" "$NEW_USER@$NEW_SERVER" << 'CONFIGEOF'
|
||
cd /var/www/ai-chatbot
|
||
|
||
# 如果存在旧的环境变量文件,需要更新数据库连接信息
|
||
if [ -f .env.local ]; then
|
||
# 更新 POSTGRES_URL 为新服务器的数据库
|
||
sed -i 's|POSTGRES_URL=.*|POSTGRES_URL=postgresql://chatbot_user:chatbot_password_change_me@localhost:5432/ai_chatbot|g' .env.local
|
||
# 更新 AUTH_URL 为新服务器地址
|
||
sed -i "s|AUTH_URL=.*|AUTH_URL=http://114.66.31.51|g" .env.local
|
||
else
|
||
# 创建新的环境变量文件
|
||
cat > .env.local << 'ENVFILE'
|
||
POSTGRES_URL=postgresql://chatbot_user:chatbot_password_change_me@localhost:5432/ai_chatbot
|
||
AUTH_SECRET=$(openssl rand -base64 32)
|
||
AUTH_URL=http://114.66.31.51
|
||
NODE_ENV=production
|
||
ENVFILE
|
||
fi
|
||
|
||
# 导入数据库
|
||
echo "正在导入数据库..."
|
||
export PGPASSWORD="chatbot_password_change_me"
|
||
# 尝试恢复,忽略已存在的错误
|
||
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 "✅ 项目构建完成"
|
||
|
||
# 配置 PM2
|
||
echo "正在配置 PM2..."
|
||
cat > ecosystem.config.js << 'PM2EOF'
|
||
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: 3000
|
||
},
|
||
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'
|
||
}]
|
||
};
|
||
PM2EOF
|
||
|
||
# 启动应用
|
||
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 配置完成"
|
||
|
||
# 配置 Nginx
|
||
echo "正在配置 Nginx..."
|
||
cat > /etc/nginx/sites-available/ai-chatbot << 'NGINXEOF'
|
||
server {
|
||
listen 80;
|
||
server_name 114.66.31.51;
|
||
|
||
location / {
|
||
proxy_pass http://localhost:3000;
|
||
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;
|
||
}
|
||
}
|
||
NGINXEOF
|
||
|
||
ln -sf /etc/nginx/sites-available/ai-chatbot /etc/nginx/sites-enabled/
|
||
rm -f /etc/nginx/sites-enabled/default
|
||
nginx -t && systemctl reload nginx
|
||
echo "✅ Nginx 配置完成"
|
||
|
||
# 清理临时文件
|
||
rm -f /tmp/project.tar.gz /tmp/database.dump
|
||
echo "✅ 临时文件已清理"
|
||
|
||
echo ""
|
||
echo "=========================================="
|
||
echo "✅ 迁移完成!"
|
||
echo "=========================================="
|
||
echo "新服务器地址: http://114.66.31.51"
|
||
echo "请检查以下内容:"
|
||
echo "1. 检查 .env.local 文件中的配置(特别是数据库密码)"
|
||
echo "2. 检查 PM2 状态: pm2 status"
|
||
echo "3. 检查日志: pm2 logs ai-chatbot"
|
||
echo "4. 访问网站验证功能"
|
||
echo "=========================================="
|
||
CONFIGEOF
|
||
|
||
echo ""
|
||
echo "✅ 步骤 9/9: 清理临时文件..."
|
||
rm -rf "$BACKUP_DIR"
|
||
echo "✅ 临时文件已清理"
|
||
|
||
echo ""
|
||
echo "🎉 迁移脚本执行完成!"
|
||
echo ""
|
||
echo "📝 后续步骤:"
|
||
echo "1. 连接到新服务器: ssh -p $NEW_PORT $NEW_USER@$NEW_SERVER"
|
||
echo "2. 检查数据库密码: cd $PROJECT_DIR && cat .env.local"
|
||
echo "3. 如果需要,更新数据库密码并更新 .env.local"
|
||
echo "4. 检查服务状态: pm2 status && pm2 logs"
|
||
echo "5. 访问网站: http://$NEW_SERVER"
|
||
echo ""
|
||
echo "⚠️ 重要提示:"
|
||
echo "- 请更新数据库密码(.env.local 中的 POSTGRES_URL)"
|
||
echo "- 如果使用域名,请更新 AUTH_URL"
|
||
echo "- 检查所有环境变量是否正确配置"
|
||
|