e9c7e9df1a
- tv app/Hobbs-Family-Organizer-Self-Hosted-v1.0.0: release bundle (Next.js app + Shield APK v1.4 + source) - compose/hf-pdocker-02/hobbs-organizer: override (local-disk secrets, watchtower off), .env (port 8340), Shield provisioning script - CLAUDE.md: service tree + internal ports Trusted-LAN mode. Secrets live only on .100 /var/lib/hobbs-organizer/secrets (not in git). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E9NnVEico3WvFxLyjy1iW3
77 lines
3.4 KiB
PowerShell
77 lines
3.4 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[string]$DestinationDirectory
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$ProjectRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot ".."))
|
|
if (-not $DestinationDirectory) {
|
|
$DestinationDirectory = Join-Path $ProjectRoot "backups"
|
|
}
|
|
$DestinationDirectory = [IO.Path]::GetFullPath($DestinationDirectory)
|
|
|
|
$Sources = @{
|
|
Environment = Join-Path $ProjectRoot ".env"
|
|
Calendars = Join-Path $ProjectRoot "secrets/calendar_ical_urls.json"
|
|
Token = Join-Path $ProjectRoot "secrets/organizer_access_token.txt"
|
|
}
|
|
|
|
foreach ($source in $Sources.GetEnumerator()) {
|
|
if (-not (Test-Path -LiteralPath $source.Value -PathType Leaf)) {
|
|
throw "Required configuration is missing. Run scripts/configure.ps1 before creating a backup."
|
|
}
|
|
}
|
|
|
|
New-Item -ItemType Directory -Path $DestinationDirectory -Force | Out-Null
|
|
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
|
$suffix = [Guid]::NewGuid().ToString("N").Substring(0, 8)
|
|
$archivePath = Join-Path $DestinationDirectory "hobbs-organizer-config-$timestamp-$suffix.zip"
|
|
$stagingPath = Join-Path $DestinationDirectory (".backup-staging-" + [Guid]::NewGuid().ToString("N"))
|
|
$stagingFullPath = [IO.Path]::GetFullPath($stagingPath)
|
|
$destinationPrefix = $DestinationDirectory.TrimEnd([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) + [IO.Path]::DirectorySeparatorChar
|
|
|
|
if (-not $stagingFullPath.StartsWith($destinationPrefix, [StringComparison]::OrdinalIgnoreCase)) {
|
|
throw "Refusing to create a staging directory outside the selected backup directory."
|
|
}
|
|
|
|
try {
|
|
New-Item -ItemType Directory -Path (Join-Path $stagingPath "config") -Force | Out-Null
|
|
New-Item -ItemType Directory -Path (Join-Path $stagingPath "secrets") -Force | Out-Null
|
|
Copy-Item -LiteralPath $Sources.Environment -Destination (Join-Path $stagingPath "config/environment.env")
|
|
Copy-Item -LiteralPath $Sources.Calendars -Destination (Join-Path $stagingPath "secrets/calendar_ical_urls.json")
|
|
Copy-Item -LiteralPath $Sources.Token -Destination (Join-Path $stagingPath "secrets/organizer_access_token.txt")
|
|
|
|
$manifest = @(
|
|
"format=HOBBS_ORGANIZER_CONFIG_BACKUP_V1",
|
|
"created_utc=$((Get-Date).ToUniversalTime().ToString('o'))",
|
|
"contains_secrets=true"
|
|
) -join [Environment]::NewLine
|
|
[IO.File]::WriteAllText(
|
|
(Join-Path $stagingPath "manifest.txt"),
|
|
$manifest + [Environment]::NewLine,
|
|
(New-Object System.Text.UTF8Encoding($false))
|
|
)
|
|
|
|
Compress-Archive -Path (Join-Path $stagingPath "*") -DestinationPath $archivePath -CompressionLevel Optimal
|
|
|
|
if ($PSVersionTable.PSEdition -eq "Desktop" -or [Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([Runtime.InteropServices.OSPlatform]::Windows)) {
|
|
$userName = [Security.Principal.WindowsIdentity]::GetCurrent().Name
|
|
& icacls.exe $archivePath "/inheritance:r" "/grant:r" "${userName}:(F)" | Out-Null
|
|
if ($LASTEXITCODE -ne 0) { throw "Could not restrict backup archive permissions." }
|
|
}
|
|
else {
|
|
& chmod 600 -- $archivePath
|
|
if ($LASTEXITCODE -ne 0) { throw "Could not restrict backup archive permissions." }
|
|
}
|
|
}
|
|
finally {
|
|
if (Test-Path -LiteralPath $stagingFullPath) {
|
|
Remove-Item -LiteralPath $stagingFullPath -Recurse -Force
|
|
}
|
|
}
|
|
|
|
Write-Host "Created a protected configuration backup at: $archivePath"
|
|
Write-Host "The archive contains secrets. Store it in an encrypted or access-controlled backup system."
|