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:
@@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -17,7 +18,8 @@ type StackDataProvider interface {
|
||||
GetStackComposePath(name string) (composePath string, ok bool)
|
||||
ListDeployedStacks() []StackSummary
|
||||
GetStackHDDMounts(name string) []string
|
||||
GetStackHDDPath(name string) string // raw HDD_PATH from app.yaml (empty if no HDD)
|
||||
GetStackHDDPath(name string) string // raw HDD_PATH from app.yaml (empty if no HDD)
|
||||
GetDockerVolumes(name string) []string // full Docker volume names (project-prefixed)
|
||||
StopStack(name string) error
|
||||
StartStack(name string) error
|
||||
}
|
||||
@@ -28,6 +30,7 @@ type StackSummary struct {
|
||||
DisplayName string
|
||||
ComposePath string
|
||||
NeedsHDD bool
|
||||
HasVolumes bool
|
||||
}
|
||||
|
||||
// AppBackupInfo holds backup-relevant data paths for a deployed app.
|
||||
@@ -42,6 +45,7 @@ type AppBackupInfo struct {
|
||||
BackupEnabled bool
|
||||
HasHDDData bool
|
||||
HasDBDump bool
|
||||
HasVolumeData bool
|
||||
StorageLabel string // resolved from registered storage paths
|
||||
}
|
||||
|
||||
@@ -91,6 +95,7 @@ func DiscoverAppData(provider StackDataProvider, discoveredDBs []DiscoveredDB) [
|
||||
|
||||
// Discover Docker named volumes from compose
|
||||
info.DockerVolumes = ParseComposeNamedVolumes(stack.ComposePath)
|
||||
info.HasVolumeData = len(info.DockerVolumes) > 0
|
||||
|
||||
// Check if app has a DB container (already backed up via DB dump)
|
||||
for _, db := range discoveredDBs {
|
||||
@@ -137,6 +142,21 @@ func ParseComposeNamedVolumes(composePath string) []AppDockerVolume {
|
||||
return volumes
|
||||
}
|
||||
|
||||
// ResolveDockerVolumeNames returns full Docker volume names with the compose project prefix.
|
||||
// Docker Compose V2 creates volumes as <project>_<volumeName> where project = directory name.
|
||||
func ResolveDockerVolumeNames(composePath string) []string {
|
||||
vols := ParseComposeNamedVolumes(composePath)
|
||||
if len(vols) == 0 {
|
||||
return nil
|
||||
}
|
||||
project := filepath.Base(filepath.Dir(composePath))
|
||||
names := make([]string, 0, len(vols))
|
||||
for _, v := range vols {
|
||||
names = append(names, project+"_"+v.Name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// appDirSize returns the total byte count and a human-readable string for a directory.
|
||||
// H2/H3: Single du invocation with 30s timeout replaces two separate calls.
|
||||
func appDirSize(path string) (int64, string) {
|
||||
|
||||
Reference in New Issue
Block a user