# 飞牛OS部署脚本 # 用途:一键部署到飞牛OS服务器 (192.168.42.252) # 使用:.\scripts\deploy-feiniu.ps1 param( [string]$ServerIP = "192.168.42.252", [string]$ServerUser = "admin", [string]$ServerPassword = "admin123", [string]$RemotePath = "/opt/furniture-3d-system", [switch]$SkipUpload = $false, [switch]$SkipBuild = $false ) Write-Host "=== 飞牛OS部署脚本 ===" -ForegroundColor Cyan Write-Host "目标服务器: $ServerIP" -ForegroundColor Yellow Write-Host "部署路径: $RemotePath`n" -ForegroundColor Yellow # 检查必要的工具 Write-Host "检查必要工具..." -ForegroundColor Yellow # 检查SSH客户端(Windows 10+自带OpenSSH) $sshAvailable = Get-Command ssh -ErrorAction SilentlyContinue if (-not $sshAvailable) { Write-Host "❌ 未找到SSH客户端" -ForegroundColor Red Write-Host "请安装OpenSSH客户端或使用WinSCP等工具手动上传文件" -ForegroundColor Yellow exit 1 } Write-Host "✅ SSH客户端可用" -ForegroundColor Green # 检查SCP(用于文件传输) $scpAvailable = Get-Command scp -ErrorAction SilentlyContinue if (-not $scpAvailable) { Write-Host "⚠️ 未找到SCP客户端,将使用SSH命令传输" -ForegroundColor Yellow $useScp = $false } else { Write-Host "✅ SCP客户端可用" -ForegroundColor Green $useScp = $true } # 检查项目目录 Write-Host "`n检查项目目录..." -ForegroundColor Yellow $projectPath = "d:\3danzhuang\furniture-3d-system" if (-not (Test-Path $projectPath)) { Write-Host "❌ 项目目录不存在: $projectPath" -ForegroundColor Red exit 1 } Write-Host "✅ 项目目录存在: $projectPath" -ForegroundColor Green # 检查必要的文件 $requiredFiles = @( "docker-compose.prod.yml", "Dockerfile", "package.json" ) foreach ($file in $requiredFiles) { $filePath = Join-Path $projectPath $file if (-not (Test-Path $filePath)) { Write-Host "❌ 缺少必要文件: $file" -ForegroundColor Red exit 1 } } Write-Host "✅ 必要文件检查通过" -ForegroundColor Green # 测试SSH连接 Write-Host "`n测试SSH连接..." -ForegroundColor Yellow $sshTest = ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 "$ServerUser@$ServerIP" "echo 'SSH连接成功'" 2>&1 if ($LASTEXITCODE -ne 0) { Write-Host "❌ SSH连接失败" -ForegroundColor Red Write-Host "请检查:" -ForegroundColor Yellow Write-Host " 1. 服务器IP地址是否正确: $ServerIP" -ForegroundColor Gray Write-Host " 2. 用户名和密码是否正确" -ForegroundColor Gray Write-Host " 3. 服务器是否可访问" -ForegroundColor Gray Write-Host "`n提示: 首次连接可能需要手动输入密码" -ForegroundColor Yellow exit 1 } Write-Host "✅ SSH连接成功" -ForegroundColor Green # 上传文件(如果未跳过) if (-not $SkipUpload) { Write-Host "`n上传项目文件到服务器..." -ForegroundColor Yellow # 创建临时压缩包 $tempZip = "$env:TEMP\furniture-3d-system-$(Get-Date -Format 'yyyyMMdd-HHmmss').zip" Write-Host "创建临时压缩包: $tempZip" -ForegroundColor Gray # 排除不需要的文件 $excludePatterns = @( "node_modules", ".next", ".git", "backups", "*.log" ) # 使用PowerShell压缩(需要.NET 4.5+) try { Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::CreateFromDirectory($projectPath, $tempZip) Write-Host "✅ 压缩包创建成功" -ForegroundColor Green } catch { Write-Host "❌ 压缩失败: $_" -ForegroundColor Red Write-Host "请手动打包项目文件并上传到服务器" -ForegroundColor Yellow exit 1 } # 上传压缩包 Write-Host "上传压缩包到服务器..." -ForegroundColor Gray if ($useScp) { # 使用SCP上传 scp -o StrictHostKeyChecking=no "$tempZip" "${ServerUser}@${ServerIP}:${RemotePath}.zip" 2>&1 | Out-Null } else { # 使用SSH + base64传输(备用方案) Write-Host "⚠️ SCP不可用,请手动上传压缩包到服务器" -ForegroundColor Yellow Write-Host "压缩包位置: $tempZip" -ForegroundColor Gray Write-Host "服务器路径: $RemotePath.zip" -ForegroundColor Gray $manualUpload = Read-Host "已手动上传?(Y/N)" if ($manualUpload -ne 'Y' -and $manualUpload -ne 'y') { exit 0 } } if ($LASTEXITCODE -eq 0 -or $manualUpload -eq 'Y' -or $manualUpload -eq 'y') { Write-Host "✅ 文件上传成功" -ForegroundColor Green # 在服务器上解压 Write-Host "在服务器上解压文件..." -ForegroundColor Gray ssh -o StrictHostKeyChecking=no "$ServerUser@$ServerIP" "mkdir -p $RemotePath && unzip -q -o ${RemotePath}.zip -d $RemotePath && rm ${RemotePath}.zip" 2>&1 | Out-Null Write-Host "✅ 文件解压完成" -ForegroundColor Green } else { Write-Host "❌ 文件上传失败" -ForegroundColor Red Write-Host "请手动上传项目文件到服务器: $RemotePath" -ForegroundColor Yellow exit 1 } # 清理临时文件 Remove-Item $tempZip -ErrorAction SilentlyContinue } else { Write-Host "`n跳过文件上传(使用 -SkipUpload 参数)" -ForegroundColor Yellow } # 在服务器上执行部署 Write-Host "`n在服务器上执行部署..." -ForegroundColor Yellow # 创建部署脚本(使用单引号here-string避免PowerShell解析bash变量) $deployScript = @' #!/bin/bash set -e REMOTE_PATH="'@ + $RemotePath + @'" SERVER_IP="'@ + $ServerIP + @'" cd "$REMOTE_PATH" echo "=== 检查Docker环境 ===" docker --version || { echo "❌ Docker未安装"; exit 1; } docker-compose --version || { echo "❌ Docker Compose未安装"; exit 1; } echo "=== 检查环境变量文件 ===" if [ ! -f .env ]; then echo "⚠️ .env文件不存在,创建默认配置..." cat > .env << EOF DATABASE_URL=postgresql://furniture:furniture123@postgres:5432/furniture_db?schema=public S3_ENDPOINT=http://minio:9000 S3_PUBLIC_ENDPOINT=http://${SERVER_IP}:9000 S3_REGION=us-east-1 S3_ACCESS_KEY=minioadmin S3_SECRET_KEY=minioadmin123 S3_BUCKET=furniture-3d-models JWT_SECRET=change-me-in-production-use-strong-random-string NODE_ENV=production NEXT_TELEMETRY_DISABLED=1 PORT=3000 WEB_PORT=3000 EOF echo "✅ 已创建默认.env文件" echo "⚠️ 请修改.env文件中的JWT_SECRET为强密码!" else echo "✅ .env文件已存在" fi echo "=== 停止现有服务 ===" docker-compose -f docker-compose.prod.yml down || true echo "=== 构建并启动服务 ===" if [ "$SKIP_BUILD" = "true" ]; then echo "跳过构建,直接启动..." docker-compose -f docker-compose.prod.yml up -d else echo "构建应用镜像..." docker-compose -f docker-compose.prod.yml build app echo "启动所有服务..." docker-compose -f docker-compose.prod.yml up -d fi echo "=== 等待服务启动 ===" sleep 10 echo "=== 检查服务状态 ===" docker-compose -f docker-compose.prod.yml ps echo "=== 检查健康状态 ===" sleep 5 curl -f http://localhost:3000/api/health || echo "⚠️ 健康检查失败,请查看日志" echo "" echo "=== 部署完成 ===" echo "访问地址: http://${SERVER_IP}:3001" echo "MinIO控制台: http://${SERVER_IP}:9003" echo "" echo "查看日志: docker-compose -f docker-compose.prod.yml logs -f" '@ # 将部署脚本写入临时文件并上传 $scriptFile = "$env:TEMP\deploy-feiniu-$(Get-Date -Format 'yyyyMMdd-HHmmss').sh" $deployScript | Out-File -FilePath $scriptFile -Encoding UTF8 Write-Host "上传部署脚本..." -ForegroundColor Gray if ($useScp) { scp -o StrictHostKeyChecking=no "$scriptFile" "${ServerUser}@${ServerIP}:/tmp/deploy-feiniu.sh" 2>&1 | Out-Null } else { Write-Host "⚠️ 请手动上传部署脚本到服务器: $scriptFile" -ForegroundColor Yellow $manualScript = Read-Host "已手动上传?(Y/N)" if ($manualScript -ne 'Y' -and $manualScript -ne 'y') { exit 0 } } # 执行部署脚本 Write-Host "执行部署脚本..." -ForegroundColor Gray $buildFlag = if ($SkipBuild) { "true" } else { "false" } ssh -o StrictHostKeyChecking=no "$ServerUser@$ServerIP" "chmod +x /tmp/deploy-feiniu.sh && SKIP_BUILD=$buildFlag bash /tmp/deploy-feiniu.sh" if ($LASTEXITCODE -eq 0) { Write-Host "`n✅ 部署完成!" -ForegroundColor Green Write-Host "`n访问地址:" -ForegroundColor Cyan Write-Host " 应用: http://$ServerIP:3000" -ForegroundColor White Write-Host " MinIO控制台: http://$ServerIP:9001" -ForegroundColor White Write-Host "`n常用命令:" -ForegroundColor Cyan Write-Host " 查看日志: ssh $ServerUser@$ServerIP 'cd $RemotePath && docker-compose -f docker-compose.prod.yml logs -f'" -ForegroundColor Gray Write-Host " 查看状态: ssh $ServerUser@$ServerIP 'cd $RemotePath && docker-compose -f docker-compose.prod.yml ps'" -ForegroundColor Gray Write-Host " 停止服务: ssh $ServerUser@$ServerIP 'cd $RemotePath && docker-compose -f docker-compose.prod.yml down'" -ForegroundColor Gray } else { Write-Host "`n❌ 部署过程中出现错误" -ForegroundColor Red Write-Host "请查看服务器日志进行排查" -ForegroundColor Yellow exit 1 } # 清理临时文件 Remove-Item $scriptFile -ErrorAction SilentlyContinue Write-Host "`n✅ 脚本执行完成!" -ForegroundColor Green