WuYongJie 75db836d29 feat(3D核心功能): 完成所有必需实现任务
- 文件上传验证: ModelUploader组件增强、上传API错误处理
- GLTF解析器: 增强节点提取和元数据
- 板材编辑: PanelEditor客户端验证 + panels API服务端验证
- 装配步骤编辑: 修复未使用代码,添加预览按钮
- 装配动画播放: AssemblyPlayer步骤导航边界检查优化
- 分享链接: 完整CRUD API(创建、获取、删除),含验证和过期检查

跳过可选测试任务以加快MVP开发
2025-12-22 16:18:13 +08:00

316 lines
8.9 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ============================================
# 数据库备份脚本 (PowerShell)
# ============================================
# 功能:备份 PostgreSQL 数据库
# 使用:.\backup-db.ps1 [-BackupDir <目录>]
# ============================================
param(
[string]$BackupDir = ".\backups\database"
)
$ErrorActionPreference = "Stop"
# 配置
$Timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$BackupFile = "db-backup-$Timestamp.sql.gz"
$LogFile = "backup-db-$Timestamp.log"
# 日志函数
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogMessage = "[$TimeStamp] $($Level): $Message"
switch ($Level) {
"ERROR" { Write-Host $LogMessage -ForegroundColor Red }
"WARN" { Write-Host $LogMessage -ForegroundColor Yellow }
"SUCCESS" { Write-Host $LogMessage -ForegroundColor Green }
default { Write-Host $LogMessage }
}
Add-Content -Path "$BackupDir\$LogFile" -Value $LogMessage
}
# 检查依赖
function Test-Dependencies {
Write-Log "检查依赖..."
# 检查 pg_dump
if (-not (Get-Command pg_dump -ErrorAction SilentlyContinue)) {
Write-Log "pg_dump 未安装,请安装 PostgreSQL 客户端工具" "ERROR"
exit 1
}
# 检查 7-Zip 或 gzip
$global:CompressTool = $null
if (Get-Command 7z -ErrorAction SilentlyContinue) {
$global:CompressTool = "7z"
} elseif (Get-Command gzip -ErrorAction SilentlyContinue) {
$global:CompressTool = "gzip"
} else {
Write-Log "未找到压缩工具(7z 或 gzip)" "ERROR"
exit 1
}
Write-Log "✅ 依赖检查通过" "SUCCESS"
}
# 加载环境变量
function Load-EnvFile {
if (Test-Path .env) {
Get-Content .env | ForEach-Object {
if ($_ -match '^([^#][^=]+)=(.*)$') {
$name = $matches[1].Trim()
$value = $matches[2].Trim()
[Environment]::SetEnvironmentVariable($name, $value, "Process")
}
}
}
}
# 验证数据库连接
function Test-DatabaseConnection {
Write-Log "验证数据库连接..."
$DatabaseUrl = $env:DATABASE_URL
if (-not $DatabaseUrl) {
Write-Log "DATABASE_URL 环境变量未设置" "ERROR"
exit 1
}
# 解析数据库连接字符串
if ($DatabaseUrl -match 'postgresql://([^:]+):([^@]+)@([^:]+):([^/]+)/(.+)') {
$global:DbUser = $matches[1]
$global:DbPass = $matches[2]
$global:DbHost = $matches[3]
$global:DbPort = $matches[4]
$global:DbName = $matches[5]
} else {
Write-Log "DATABASE_URL 格式无效" "ERROR"
exit 1
}
# 测试连接
$env:PGPASSWORD = $global:DbPass
$testQuery = "SELECT 1"
$result = & psql -h $global:DbHost -p $global:DbPort -U $global:DbUser -d $global:DbName -c $testQuery 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Log "无法连接到数据库" "ERROR"
exit 1
}
Write-Log "✅ 数据库连接成功" "SUCCESS"
}
# 创建备份目录
function New-BackupDirectory {
Write-Log "创建备份目录: $BackupDir"
if (-not (Test-Path $BackupDir)) {
New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null
}
if (-not (Test-Path $BackupDir)) {
Write-Log "无法创建备份目录" "ERROR"
exit 1
}
Write-Log "✅ 备份目录已创建" "SUCCESS"
}
# 检查磁盘空间
function Test-DiskSpace {
Write-Log "检查磁盘空间..."
$drive = (Get-Item $BackupDir).PSDrive
$freeSpace = [math]::Round($drive.Free / 1MB, 2)
# 获取数据库大小
$env:PGPASSWORD = $global:DbPass
$sizeQuery = "SELECT pg_database_size('$($global:DbName)') / 1024 / 1024"
$dbSize = & psql -h $global:DbHost -p $global:DbPort -U $global:DbUser -d $global:DbName -t -c $sizeQuery 2>&1
$dbSize = [math]::Round([double]$dbSize, 0)
$requiredSpace = $dbSize * 2
Write-Log "数据库大小: $dbSize MB"
Write-Log "可用空间: $freeSpace MB"
Write-Log "需要空间: $requiredSpace MB"
if ($freeSpace -lt $requiredSpace) {
Write-Log "磁盘空间不足" "ERROR"
exit 1
}
Write-Log "✅ 磁盘空间充足" "SUCCESS"
}
# 执行备份
function Start-Backup {
Write-Log "开始备份数据库..."
Write-Log "备份文件: $BackupFile"
$startTime = Get-Date
# 设置密码环境变量
$env:PGPASSWORD = $global:DbPass
# 执行备份
$backupPath = Join-Path $BackupDir $BackupFile
$tempSqlFile = Join-Path $BackupDir "temp-$Timestamp.sql"
try {
# 导出数据库
& pg_dump -h $global:DbHost -p $global:DbPort -U $global:DbUser -d $global:DbName `
--format=plain `
--no-owner `
--no-acl `
--clean `
--if-exists `
-f $tempSqlFile
if ($LASTEXITCODE -ne 0) {
throw "pg_dump 执行失败"
}
# 压缩文件
if ($global:CompressTool -eq "7z") {
& 7z a -tgzip $backupPath $tempSqlFile | Out-Null
} else {
& gzip -c $tempSqlFile > $backupPath
}
if ($LASTEXITCODE -ne 0) {
throw "压缩失败"
}
# 删除临时文件
Remove-Item $tempSqlFile -Force
$endTime = Get-Date
$duration = ($endTime - $startTime).TotalSeconds
Write-Log "✅ 备份完成,耗时: $([math]::Round($duration, 2)) 秒" "SUCCESS"
}
catch {
Write-Log "备份失败: $_" "ERROR"
if (Test-Path $tempSqlFile) {
Remove-Item $tempSqlFile -Force
}
exit 1
}
}
# 验证备份文件
function Test-BackupFile {
Write-Log "验证备份文件..."
$backupPath = Join-Path $BackupDir $BackupFile
if (-not (Test-Path $backupPath)) {
Write-Log "备份文件不存在" "ERROR"
exit 1
}
# 检查文件大小
$fileSize = (Get-Item $backupPath).Length
$fileSizeMB = [math]::Round($fileSize / 1MB, 2)
Write-Log "备份文件大小: $fileSizeMB MB"
# 生成 checksum
$hash = Get-FileHash -Path $backupPath -Algorithm SHA256
$checksum = $hash.Hash
$checksum | Out-File -FilePath "$backupPath.sha256" -Encoding ASCII
Write-Log "✅ 备份文件完整性验证通过" "SUCCESS"
Write-Log "✅ Checksum: $checksum" "SUCCESS"
}
# 清理旧备份
function Remove-OldBackups {
Write-Log "清理旧备份..."
$keepCount = 7
# 获取所有备份文件
$backupFiles = Get-ChildItem -Path $BackupDir -Filter "db-backup-*.sql.gz" |
Sort-Object LastWriteTime -Descending
$backupCount = $backupFiles.Count
if ($backupCount -gt $keepCount) {
$deleteCount = $backupCount - $keepCount
Write-Log "发现 $backupCount 个备份,删除最旧的 $deleteCount 个"
$backupFiles | Select-Object -Skip $keepCount | ForEach-Object {
Write-Log "删除: $($_.Name)"
Remove-Item $_.FullName -Force
$checksumFile = "$($_.FullName).sha256"
if (Test-Path $checksumFile) {
Remove-Item $checksumFile -Force
}
}
Write-Log "✅ 清理完成" "SUCCESS"
} else {
Write-Log "当前有 $backupCount 个备份,无需清理"
}
}
# 生成备份摘要
function New-BackupSummary {
Write-Log "生成备份摘要..."
$backupPath = Join-Path $BackupDir $BackupFile
$fileSize = (Get-Item $backupPath).Length
$checksum = Get-Content "$backupPath.sha256"
$summary = @{
timestamp = (Get-Date -Format "o")
backup_file = $BackupFile
database = $global:DbName
host = $global:DbHost
size = $fileSize
checksum = $checksum
status = "success"
}
$summaryPath = Join-Path $BackupDir "backup-summary-$Timestamp.json"
$summary | ConvertTo-Json | Out-File -FilePath $summaryPath -Encoding UTF8
Write-Log "✅ 备份摘要已生成" "SUCCESS"
}
# 主函数
function Main {
Write-Host "========================================"
Write-Host " 数据库备份脚本"
Write-Host "========================================"
Write-Host ""
Load-EnvFile
Test-Dependencies
Test-DatabaseConnection
New-BackupDirectory
Test-DiskSpace
Start-Backup
Test-BackupFile
Remove-OldBackups
New-BackupSummary
Write-Host ""
Write-Host "========================================"
Write-Log "🎉 备份成功完成!" "SUCCESS"
Write-Host "========================================"
Write-Host ""
Write-Host "备份文件: $BackupDir\$BackupFile"
Write-Host "日志文件: $BackupDir\$LogFile"
Write-Host ""
}
# 执行主函数
Main