feat: drive migration & Tier 2 restic deprecation (v0.18.0)

Phase 1: Deprecate restic as Tier 2 method (rsync only), auto-migrate on startup
Phase 2: Enhanced per-app migration with backup awareness, DB dump copy, auto-cleanup
Phase 3: Full drive migration with decommissioned state, rollback support, wizard UI
Phase 4: Hub report includes decommissioned drive state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 21:49:14 +01:00
parent bdbe170a54
commit 99bf3ca7a8
22 changed files with 1725 additions and 402 deletions
@@ -205,18 +205,20 @@ function pollUntilBack() {
{{if .StoragePaths}}
<div class="storage-paths-list">
{{range .StoragePaths}}
<div class="storage-path-item{{if .Disconnected}} storage-disconnected{{end}}">
<div class="storage-path-item{{if .Disconnected}} storage-disconnected{{else if .Decommissioned}} storage-decommissioned{{end}}">
<div class="storage-path-header">
<div class="storage-path-info">
<div class="storage-path-label-wrap" id="label-wrap-{{.Path}}">
<span class="storage-path-label" id="label-display-{{.Path}}">{{.Label}}</span>
{{if not .Disconnected}}<button class="btn btn-xs btn-ghost" onclick="editStorageLabel('{{.Path}}', '{{.Label}}')" title="Átnevezés">✏️</button>{{end}}
{{if not (or .Disconnected .Decommissioned)}}<button class="btn btn-xs btn-ghost" onclick="editStorageLabel('{{.Path}}', '{{.Label}}')" title="Átnevezés">✏️</button>{{end}}
</div>
<span class="storage-path-path mono">{{.Path}}</span>
</div>
<div class="storage-path-badges">
{{if .Disconnected}}
<span class="badge badge-error">Leválasztva</span>
{{else if .Decommissioned}}
<span class="badge state-gray">Kiváltva</span>
{{else}}
{{if .IsDefault}}<span class="badge state-green">Alapértelmezett</span>{{end}}
{{if .Schedulable}}<span class="badge" style="background:rgba(0,136,204,0.15);color:var(--accent-light)">Aktív</span>{{else}}<span class="badge state-gray">Inaktív</span>{{end}}
@@ -236,6 +238,20 @@ function pollUntilBack() {
<div class="storage-path-actions" id="storage-actions-{{.Path}}">
<button class="btn btn-xs btn-primary" onclick="storageReconnect('{{.Path}}')">Csatlakoztatás</button>
</div>
{{else if .Decommissioned}}
<div class="storage-path-details">
<div class="storage-disconnected-info">
<span class="form-hint">Adatok átköltöztetve ide: <strong>{{.MigratedToLabel}}</strong> ({{.MigratedTo}})</span>
{{if .DecommissionedAt}}<span class="form-hint">Időpont: {{.DecommissionedAt}}</span>{{end}}
</div>
</div>
<div class="storage-path-actions">
<form method="POST" action="/settings/storage/remove" style="display:inline"
onsubmit="return confirm('Biztosan eltávolítja a(z) {{.Label}} ({{.Path}}) meghajtót a rendszerből?\n\nA meghajtó adatai NEM törlődnek.')">
<input type="hidden" name="storage_path" value="{{.Path}}">
<button type="submit" class="btn btn-xs btn-outline">Eltávolítás a rendszerből</button>
</form>
</div>
{{else}}
<div class="storage-path-details">
{{if .DiskInfo}}
@@ -311,6 +327,9 @@ function pollUntilBack() {
<button type="submit" class="btn btn-xs btn-danger-outline">Eltávolítás</button>
</form>
{{end}}
{{if and (gt .AppCount 0) .HasOtherPaths}}
<a href="/settings/storage/migrate-drive?source={{.Path}}" class="btn btn-xs btn-outline">📦 Összes adat átköltöztetése</a>
{{end}}
</div>
{{end}}
</div>