feat: add backup/restore of app configurations
All checks were successful
Check / check (pull_request) Successful in 3m25s

Add export and import functionality for app configurations:

- Export single app or all apps as versioned JSON backup bundle
- Import from backup file with name-conflict detection (skip duplicates)
- Fresh SSH keys and webhook secrets generated on import
- Preserves env vars, labels, volumes, and port mappings
- Web UI: export button on app detail, backup/restore page on dashboard
- REST API: GET /api/v1/apps/{id}/export, GET /api/v1/backup/export,
  POST /api/v1/backup/import
- Comprehensive test coverage for service and handler layers
This commit is contained in:
user
2026-03-17 02:17:34 -07:00
parent fd110e69db
commit bb91f314c5
11 changed files with 1740 additions and 6 deletions

View File

@@ -0,0 +1,62 @@
{{template "base" .}}
{{define "title"}}Import Backup - µPaaS{{end}}
{{define "content"}}
{{template "nav" .}}
<main class="max-w-4xl mx-auto px-4 py-8">
<div class="mb-6">
<a href="/" class="text-primary-600 hover:text-primary-800 inline-flex items-center">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
</svg>
Back to Dashboard
</a>
</div>
{{template "alert-success" .}}
{{template "alert-error" .}}
<h1 class="text-2xl font-medium text-gray-900 mb-6">Import Backup</h1>
<div class="card p-6 mb-6">
<h2 class="section-title mb-4">Restore from Backup File</h2>
<p class="text-sm text-gray-500 mb-4">
Upload a previously exported µPaaS backup file (JSON) to restore app configurations.
New apps will be created with fresh SSH keys and webhook secrets.
Apps whose names already exist will be skipped.
</p>
<form method="POST" action="/backup/import" enctype="multipart/form-data">
{{ .CSRFField }}
<div class="mb-4">
<label for="backup_file" class="form-label">Backup File</label>
<input type="file" id="backup_file" name="backup_file" accept=".json,application/json"
class="block w-full text-sm text-gray-500
file:mr-4 file:py-2 file:px-4
file:rounded file:border-0
file:text-sm file:font-medium
file:bg-primary-50 file:text-primary-700
hover:file:bg-primary-100
cursor-pointer">
</div>
<button type="submit" class="btn-primary">Import</button>
</form>
</div>
<div class="card p-6">
<h2 class="section-title mb-4">Export All Apps</h2>
<p class="text-sm text-gray-500 mb-4">
Download a backup of all app configurations. This includes app settings,
environment variables, labels, volumes, and port mappings.
Secrets (SSH keys, webhook tokens) are not included — they are regenerated on import.
</p>
<a href="/backup/export" class="btn-secondary">
<svg class="w-4 h-4 mr-1 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/>
</svg>
Export All Apps
</a>
</div>
</main>
{{end}}