v0.12.2: restore section simplification — snapshot filtering, auto-stop/restart, UI cleanup

- StackDataProvider interface extended with StopStack/StartStack
- backup.Manager.GetStackHDDMounts() delegates to stackProvider
- RestoreApp() auto-stops app before restic restore, restarts after (even on failure)
- stackAdapter in main.go wires StopStack/StartStack through to stacks.Manager
- GET /api/backup/snapshots?stack={name} filters snapshots by app HDD paths via filterSnapshotsByPaths()
- Restore section simplified: no path list, per-app filtered snapshots, human-friendly timestamp format, single calm warning, empty-result inline message

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 19:19:23 +01:00
parent fde8e84d82
commit 62992e0e04
8 changed files with 115 additions and 53 deletions
+28 -1
View File
@@ -445,7 +445,7 @@ func (r *Router) triggerBackup(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, http.StatusOK, apiResponse{OK: true, Message: "Mentés elindítva"})
}
func (r *Router) backupSnapshots(w http.ResponseWriter, _ *http.Request) {
func (r *Router) backupSnapshots(w http.ResponseWriter, req *http.Request) {
if r.backupMgr == nil {
writeJSON(w, http.StatusOK, apiResponse{OK: true, Data: []interface{}{}})
return
@@ -456,12 +456,39 @@ func (r *Router) backupSnapshots(w http.ResponseWriter, _ *http.Request) {
writeJSON(w, http.StatusInternalServerError, apiResponse{OK: false, Error: err.Error()})
return
}
// Filter by stack if requested — only return snapshots that include the app's HDD paths.
if stackName := req.URL.Query().Get("stack"); stackName != "" {
mounts := r.backupMgr.GetStackHDDMounts(stackName)
if len(mounts) > 0 {
snapshots = filterSnapshotsByPaths(snapshots, mounts)
}
}
if snapshots == nil {
snapshots = []backup.SnapshotInfo{}
}
writeJSON(w, http.StatusOK, apiResponse{OK: true, Data: snapshots})
}
// filterSnapshotsByPaths returns only snapshots whose Paths overlap with requiredPaths.
// A snapshot matches if any of its paths is a prefix of (or prefixed by) any required path.
func filterSnapshotsByPaths(snapshots []backup.SnapshotInfo, requiredPaths []string) []backup.SnapshotInfo {
var filtered []backup.SnapshotInfo
outer:
for _, snap := range snapshots {
for _, required := range requiredPaths {
for _, sp := range snap.Paths {
if strings.HasPrefix(required, sp) || strings.HasPrefix(sp, required) {
filtered = append(filtered, snap)
continue outer
}
}
}
}
return filtered
}
// --- Metrics handlers ---
func (r *Router) metricsSystem(w http.ResponseWriter, req *http.Request) {