# 使用 SSH 密钥的部署脚本(更安全) param( [string]$Server = "45.249.208.118", [string]$User = "root", [string]$SshKeyPath = "$env:USERPROFILE\.ssh\id_rsa", # 默认 SSH 密钥路径 [string]$ProjectPath = "/root/youyidian-3d", [switch]$NoCache = $false ) $ErrorActionPreference = "Stop" Write-Host "========================================" -ForegroundColor Cyan Write-Host " 安全部署脚本 - 使用 SSH 密钥" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" # 检查工具 $plinkPath = "C:\Program Files\PuTTY\plink.exe" $pscpPath = "C:\Program Files\PuTTY\pscp.exe" if (-not (Test-Path $plinkPath)) { Write-Host "错误: 未找到 plink.exe,请安装 PuTTY" -ForegroundColor Red exit 1 } # 检查 SSH 密钥文件 if (-not (Test-Path $SshKeyPath)) { Write-Host "错误: 未找到 SSH 密钥文件: $SshKeyPath" -ForegroundColor Red Write-Host "" Write-Host "请执行以下步骤创建 SSH 密钥:" -ForegroundColor Yellow Write-Host " 1. 生成密钥对: ssh-keygen -t rsa -b 4096 -C `"deploy@youyidian`"" -ForegroundColor Gray Write-Host " 2. 将公钥复制到服务器: ssh-copy-id -i ~/.ssh/id_rsa.pub ${User}@${Server}" -ForegroundColor Gray Write-Host " 3. 或者手动复制公钥内容到服务器的 ~/.ssh/authorized_keys" -ForegroundColor Gray Write-Host "" Write-Host "如果你使用 PuTTY,可以:" -ForegroundColor Yellow Write-Host " 1. 使用 PuTTYgen 生成密钥对" -ForegroundColor Gray Write-Host " 2. 将公钥添加到服务器的 ~/.ssh/authorized_keys" -ForegroundColor Gray Write-Host " 3. 在 Pageant 中加载私钥,或使用 -i 参数指定私钥文件" -ForegroundColor Gray exit 1 } Write-Host "使用 SSH 密钥: $SshKeyPath" -ForegroundColor Green Write-Host "" # 需要上传的关键文件 $filesToUpload = @( "app/page.tsx", "app/login/page.tsx", "app/api/network-status/route.ts", "next.config.js", "README.md", "lib/config.ts", "lib/configLoader.ts", "config-bianliang.env", "Dockerfile", "docker-compose.yml", "package.json", "package-lock.json" ) # 检查文件是否存在 $missingFiles = @() foreach ($file in $filesToUpload) { if (-not (Test-Path $file)) { $missingFiles += $file } } if ($missingFiles.Count -gt 0) { Write-Host "警告: 以下文件不存在,将跳过:" -ForegroundColor Yellow $missingFiles | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow } } # 上传文件 Write-Host "[1/5] 上传文件到服务器..." -ForegroundColor Yellow $uploadedCount = 0 foreach ($file in $filesToUpload) { if (Test-Path $file) { $remotePath = "$ProjectPath/$file" $remoteDir = Split-Path $remotePath -Parent # 确保远程目录存在 & $plinkPath -ssh -i $SshKeyPath -batch "${User}@${Server}" "mkdir -p `"$remoteDir`"" | Out-Null # 上传文件 & $pscpPath -i $SshKeyPath -batch -q $file "${User}@${Server}:$remotePath" 2>&1 | Out-Null if ($LASTEXITCODE -eq 0) { $uploadedCount++ Write-Host " ✓ $file" -ForegroundColor Green } else { Write-Host " ✗ $file (上传失败)" -ForegroundColor Red } } } Write-Host "已上传 $uploadedCount/$($filesToUpload.Count) 个文件" -ForegroundColor $(if ($uploadedCount -eq $filesToUpload.Count) { "Green" } else { "Yellow" }) # 创建部署脚本 Write-Host "[2/5] 创建部署脚本..." -ForegroundColor Yellow $buildFlag = if ($NoCache) { "--no-cache" } else { "" } $timestamp = Get-Date -Format "yyyyMMdd-HHmmss" $deployScript = @" #!/bin/bash set -e cd $ProjectPath echo "========================================" echo "开始部署..." echo "========================================" # 备份数据 echo "[1/6] 备份数据..." if [ -d "uploads" ]; then mkdir -p backups backup_dir="backups/uploads-$(date +%Y%m%d-%H%M%S)" cp -r uploads "`$backup_dir" 2>/dev/null || true echo " ✓ 上传目录已备份到 `$backup_dir" fi # 检查环境 echo "[2/6] 检查环境..." if ! command -v docker &> /dev/null; then echo "错误: Docker 未安装" >&2 exit 1 fi if docker compose version &> /dev/null; then DOCKER_COMPOSE="docker compose" elif docker-compose version &> /dev/null; then DOCKER_COMPOSE="docker-compose" else echo "错误: Docker Compose 未安装" >&2 exit 1 fi echo " ✓ 使用: `$DOCKER_COMPOSE" # 停止旧容器 echo "[3/6] 停止旧容器..." `$DOCKER_COMPOSE down --timeout 30 2>/dev/null || true echo " ✓ 旧容器已停止" # 构建并启动 echo "[4/6] 构建并启动容器..." if [ -n "$buildFlag" ]; then echo " ⚠ 使用 --no-cache 模式" `$DOCKER_COMPOSE build $buildFlag app && `$DOCKER_COMPOSE up -d app else echo " ✓ 使用智能构建缓存" `$DOCKER_COMPOSE up -d --build app fi if [ `$? -ne 0 ]; then echo "错误: 容器构建或启动失败" >&2 exit 1 fi echo " ✓ 容器构建并启动完成" # 等待容器就绪 echo "[5/6] 等待容器就绪..." sleep 10 max_attempts=30 attempt=0 while [ `$attempt -lt `$max_attempts ]; do if `$DOCKER_COMPOSE ps app | grep -q "Up"; then echo " ✓ 容器运行中" break fi attempt=`$((attempt + 1)) sleep 2 done # 数据库迁移 echo "[6/6] 检查数据库迁移..." if [ -f prisma/schema.prisma ]; then `$DOCKER_COMPOSE exec -T app npx prisma db push --accept-data-loss --schema=/app/prisma/schema.prisma 2>/dev/null || echo " ⚠ 数据库迁移跳过或已完成" fi # 显示结果 echo "" echo "========================================" echo "部署完成!" echo "========================================" echo "" echo "容器状态:" `$DOCKER_COMPOSE ps echo "" echo "应用日志(最后 30 行):" `$DOCKER_COMPOSE logs app --tail=30 "@ $localDeployScript = Join-Path $env:TEMP "deploy-ssh-$timestamp.sh" $utf8NoBom = New-Object System.Text.UTF8Encoding $false [System.IO.File]::WriteAllText($localDeployScript, $deployScript, $utf8NoBom) # 上传部署脚本 Write-Host "[3/5] 上传部署脚本..." -ForegroundColor Yellow $remoteDeployScript = "/tmp/deploy-ssh-$timestamp.sh" & $pscpPath -i $SshKeyPath -batch $localDeployScript "${User}@${Server}:$remoteDeployScript" if ($LASTEXITCODE -ne 0) { Write-Host "上传部署脚本失败" -ForegroundColor Red exit 1 } Write-Host " ✓ 部署脚本已上传" -ForegroundColor Green # 执行部署 Write-Host "[4/5] 执行部署..." -ForegroundColor Yellow Write-Host "" & $plinkPath -ssh -i $SshKeyPath -batch "${User}@${Server}" "chmod +x $remoteDeployScript; bash $remoteDeployScript; rm -f $remoteDeployScript" $deployExitCode = $LASTEXITCODE # 清理 Remove-Item $localDeployScript -Force -ErrorAction SilentlyContinue if ($deployExitCode -ne 0) { Write-Host "" Write-Host "部署失败,退出码: $deployExitCode" -ForegroundColor Red exit 1 } Write-Host "" Write-Host "========================================" -ForegroundColor Green Write-Host " 部署成功完成!" -ForegroundColor Green Write-Host "========================================" -ForegroundColor Green Write-Host "" Write-Host "提示:" -ForegroundColor Yellow Write-Host " - 使用 -SshKeyPath 参数指定自定义 SSH 密钥路径" -ForegroundColor Gray Write-Host " - 使用 -NoCache 参数可以强制重新构建" -ForegroundColor Gray Write-Host ""