# Sichert die Datenbank. # # `VACUUM INTO` und nicht kopieren: die Datenbank laeuft im WAL-Modus und wird # waehrend des Streams beschrieben. Eine im Schreibvorgang kopierte Datei ist # unbrauchbar - VACUUM INTO schreibt dagegen einen in sich stimmigen Stand, # ohne den laufenden Betrieb anzuhalten. # # Die Sicherung enthaelt Chatverlaeufe und damit personenbezogene Daten. Sie # gehoert nicht ins Repo und nicht in eine Cloud ohne Absprache. [CmdletBinding()] param( [string]$DatenOrdner = (Join-Path $env:LOCALAPPDATA 'abdulive'), [string]$Ziel = (Join-Path $env:LOCALAPPDATA 'abdulive\backup'), [int]$Generationen = 30 ) $ErrorActionPreference = 'Stop' $db = Join-Path $DatenOrdner 'db\abdulive.db' if (-not (Test-Path $db)) { throw "Keine Datenbank unter $db" } $node = Join-Path $env:LOCALAPPDATA 'Programs\abdulive\node.exe' if (-not (Test-Path $node)) { $node = (Get-Command node -ErrorAction Stop).Source } New-Item -ItemType Directory -Force -Path $Ziel | Out-Null $stand = Get-Date -Format 'yyyy-MM-dd_HHmm' $datei = Join-Path $Ziel "abdulive-$stand.db" # node:sqlite kann VACUUM INTO direkt - dafuer braucht es kein sqlite3.exe. $skript = @' const { DatabaseSync } = require('node:sqlite'); const [quelle, ziel] = process.argv.slice(2); const db = new DatabaseSync(quelle, { readOnly: true }); db.exec(`VACUUM INTO '${ziel.replace(/'/g, "''")}'`); db.close(); '@ $tmp = Join-Path $env:TEMP "abdulive-backup-$PID.cjs" Set-Content -Path $tmp -Value $skript -Encoding utf8 try { & $node $tmp $db $datei if ($LASTEXITCODE -ne 0) { throw "VACUUM INTO fehlgeschlagen (Code $LASTEXITCODE)" } } finally { Remove-Item $tmp -ErrorAction SilentlyContinue } $mb = [math]::Round((Get-Item $datei).Length / 1MB, 1) Write-Host "Gesichert: $datei ($mb MB)" # Aeltere Generationen abraeumen $alt = Get-ChildItem $Ziel -Filter 'abdulive-*.db' | Sort-Object LastWriteTime -Descending | Select-Object -Skip $Generationen foreach ($f in $alt) { Remove-Item $f.FullName Write-Host " entfernt: $($f.Name)" }