feat: Docker volume backup, Tier 2 restore, restore dropdown fixes (v0.33.0)
- Add Docker named volume backup to Tier 1 (dump to tar, include in restic) and Tier 2 (copy tars to rsync mirror _volumes/ dir) - Fix volume name resolution: use project-prefixed names (mealie_mealie_data) - Fix double Tier 1 in restore dropdown: filter snapshots by app's home drive - Add Tier 2 restore: RestoreAppFromTier2() restores from rsync mirror - Show Tier 2 entry in restore dropdown when cross-drive backup succeeded - Add .fab import link in restore section - Volume-aware restore type banners and backup content labels Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1094,7 +1094,13 @@ func (s *Server) backupRestoreHandler(w http.ResponseWriter, r *http.Request) {
|
||||
s.logger.Printf("[WARN] [web] Restore requested: stack=%s, snapshot=%s from %s", stackName, snapshotID, r.RemoteAddr)
|
||||
|
||||
start := time.Now()
|
||||
if err := s.backupMgr.RestoreApp(stackName, snapshotID); err != nil {
|
||||
var err error
|
||||
if snapshotID == "tier2-rsync" {
|
||||
err = s.backupMgr.RestoreAppFromTier2(stackName)
|
||||
} else {
|
||||
err = s.backupMgr.RestoreApp(stackName, snapshotID)
|
||||
}
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERROR] [web] Restore failed: %v", err)
|
||||
if s.isDebug() {
|
||||
s.logger.Printf("[DEBUG] [web] backupRestoreHandler: stack=%s failed after %s", stackName, time.Since(start))
|
||||
|
||||
@@ -268,6 +268,8 @@
|
||||
{{else if .HasHDDData}}
|
||||
{{if .StorageLabel}}<span class="meta-badge meta-badge-storage">{{.StorageLabel}}</span>{{end}}
|
||||
<span class="mono app-backup-size" style="font-size:.8rem">{{.HDDSizeHuman}}</span>
|
||||
{{else if .HasVolumeData}}
|
||||
<span class="meta-badge">Konfig{{if .HasDB}} + DB{{end}} + Adatok</span>
|
||||
{{else}}
|
||||
<span class="meta-badge">Konfig{{if .HasDB}} + DB{{end}}</span>
|
||||
{{end}}
|
||||
@@ -540,7 +542,7 @@
|
||||
<select id="restore-app" class="restore-select" onchange="onRestoreAppChange()">
|
||||
<option value="">— Válasszon —</option>
|
||||
{{range .Backup.AppDataInfo}}
|
||||
<option value="{{.StackName}}" data-has-hdd="{{.HasHDDData}}" data-has-db="{{.HasDBDump}}">{{.DisplayName}}</option>
|
||||
<option value="{{.StackName}}" data-has-hdd="{{.HasHDDData}}" data-has-db="{{.HasDBDump}}" data-has-volumes="{{.HasVolumeData}}">{{.DisplayName}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
@@ -568,6 +570,9 @@
|
||||
<div class="restore-actions">
|
||||
<button type="button" class="btn btn-sm btn-danger" id="restore-btn" disabled onclick="submitRestore()">Visszaállítás indítása</button>
|
||||
</div>
|
||||
<div style="margin-top: 1rem; text-align: center; border-top: 1px solid var(--border); padding-top: 1rem;">
|
||||
<a href="/import" class="btn btn-sm btn-outline">Importálás mentett csomagból (.fab)</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
@@ -731,8 +736,9 @@ function onRestoreAppChange() {
|
||||
var opt = sel.options[sel.selectedIndex];
|
||||
var hasHDD = opt.getAttribute('data-has-hdd') === 'true';
|
||||
var hasDB = opt.getAttribute('data-has-db') === 'true';
|
||||
var hasVolumes = opt.getAttribute('data-has-volumes') === 'true';
|
||||
|
||||
if (hasHDD) {
|
||||
if (hasHDD || hasVolumes) {
|
||||
typeInfo.innerHTML = '🔄 Teljes visszaállítás: adatbázis + konfiguráció + felhasználói adatok a kiválasztott pillanatképből.';
|
||||
typeInfo.className = 'restore-info';
|
||||
} else if (hasDB) {
|
||||
|
||||
Reference in New Issue
Block a user