Files
CRM_ENVR/scripts/backup.ps1
T
2026-06-26 14:31:38 +02:00

126 lines
4.5 KiB
PowerShell

param(
[string]$BackupDir = "$PSScriptRoot\..\backups",
[int]$RetentionDays = 30
)
$ErrorActionPreference = "Stop"
$startTime = Get-Date
# Ensure backup directory exists
if (-not (Test-Path $BackupDir)) {
New-Item -ItemType Directory -Path $BackupDir -Force | Out-Null
}
# Determine database URL from env or .env.local
$dbUrl = $env:DATABASE_URL
if (-not $dbUrl -and (Test-Path "$PSScriptRoot\..\.env.local")) {
$envContent = Get-Content "$PSScriptRoot\..\.env.local" -Raw
if ($envContent -match 'DATABASE_URL=(.+)') {
$dbUrl = $Matches[1].Trim()
}
}
if (-not $dbUrl) {
Write-Error "DATABASE_URL not found. Set it as an environment variable or in .env.local"
exit 1
}
# Parse the database URL
$uri = [System.Uri]$dbUrl
$pgUser = $uri.UserInfo.Split(':')[0]
$pgPass = $uri.UserInfo.Split(':')[1]
$pgHost = $uri.Host
$pgPort = $uri.Port
$pgDb = $uri.AbsolutePath.TrimStart('/')
# Set PGPASSWORD for pg_dump
$env:PGPASSWORD = $pgPass
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$filename = "crm_backup_$timestamp.sql"
$filepath = Join-Path $BackupDir $filename
Write-Output "Starting backup of database '$pgDb' to $filepath"
Write-Output "Database: $pgHost:$pgPort/$pgDb"
try {
# Run pg_dump
$pgDumpPath = if (Get-Command pg_dump -ErrorAction SilentlyContinue) {
"pg_dump"
} else {
"$env:TEMP\pg\pgsql\bin\pg_dump.exe"
}
$output = & $pgDumpPath -h $pgHost -p $pgPort -U $pgUser -d $pgDb --format=custom --verbose --file $filename 2>&1
$exitCode = $LASTEXITCODE
if ($exitCode -ne 0 -or -not (Test-Path $filename)) {
throw "pg_dump failed with exit code $exitCode"
}
Move-Item -Path $filename -Destination $filepath -Force
$fileInfo = Get-Item $filepath
$fileSize = $fileInfo.Length
# Verify backup by checking if file is valid custom format
$verifyOutput = & $pgDumpPath -h $pgHost -p $pgPort -U $pgUser -d $pgDb --format=custom --schema-only --file "$filename.verify" 2>&1
$verifyExitCode = $LASTEXITCODE
if (Test-Path "$filename.verify") { Remove-Item "$filename.verify" -Force }
$verificationStatus = if ($verifyExitCode -eq 0) { "verified" } else { "failed" }
# Calculate duration
$endTime = Get-Date
$durationSeconds = [math]::Round(($endTime - $startTime).TotalSeconds)
# Log the backup
$logQuery = @"
INSERT INTO backup_logs (backup_type, status, file_name, file_size_bytes, pg_dump_exit_code, verification_status, started_at, completed_at, duration_seconds, retention_days)
VALUES ('full', 'completed', '$filename', $fileSize, $exitCode, '$verificationStatus', '$($startTime.ToString("yyyy-MM-dd HH:mm:ss"))', '$($endTime.ToString("yyyy-MM-dd HH:mm:ss"))', $durationSeconds, $RetentionDays);
"@
# Try to log to database (may fail if db is not reachable for logging, that's OK)
try {
$env:PGPASSWORD = $pgPass
& $env:TEMP\pg\pgsql\bin\psql.exe -h $pgHost -p $pgPort -U $pgUser -d $pgDb -c $logQuery 2>&1 | Out-Null
} catch {
Write-Warning "Could not log backup to database: $_"
}
Write-Output "Backup completed successfully:"
Write-Output " File: $filename"
Write-Output " Size: $([math]::Round($fileSize / 1MB, 2)) MB"
Write-Output " Duration: ${durationSeconds}s"
Write-Output " Status: $verificationStatus"
# Cleanup old backups (beyond retention period)
$cutoff = (Get-Date).AddDays(-$RetentionDays)
$oldBackups = Get-ChildItem $BackupDir -Filter "crm_backup_*.sql" | Where-Object {
$_.CreationTime -lt $cutoff
}
foreach ($old in $oldBackups) {
Remove-Item $old.FullName -Force
Write-Output "Removed old backup: $($old.Name)"
}
} catch {
Write-Error "Backup failed: $_"
# Log failure
$endTime = Get-Date
$durationSeconds = [math]::Round(($endTime - $startTime).TotalSeconds)
$errorMsg = $_.ToString().Replace("'", "''")
$failQuery = @"
INSERT INTO backup_logs (backup_type, status, file_name, error_message, pg_dump_exit_code, verification_status, started_at, completed_at, duration_seconds, retention_days)
VALUES ('full', 'failed', '$filename', '$errorMsg', $exitCode, 'failed', '$($startTime.ToString("yyyy-MM-dd HH:mm:ss"))', '$($endTime.ToString("yyyy-MM-dd HH:mm:ss"))', $durationSeconds, $RetentionDays);
"@
try {
$env:PGPASSWORD = $pgPass
& $env:TEMP\pg\pgsql\bin\psql.exe -h $pgHost -p $pgPort -U $pgUser -d $pgDb -c $failQuery 2>&1 | Out-Null
} catch {}
exit 1
} finally {
Remove-Item "Env:PGPASSWORD" -ErrorAction SilentlyContinue
}