v0.51.0: offsite-backup UI (felhom-pbs DR) + Model-A double-nest fix
- Backups page: whole-guest backup shown as real DR — target label "Biztonsági szerver – külön hardver (PBS)"; app-data "Távoli mentés" card now reflects the PBS offsite tier (guestBackupView.Offsite) instead of "nincs beállítva". - Model-A double-nest fix: appbackup path helpers take a felhom-data NAMESPACE ROOT (no internal felhom-data join); backup.Manager.namespaceRoot/AppNamespaceRoot resolve HDD-vs-systemDataPath provenance so a drive-resident app's backups land single-nested (<drive>/backups/... on the guest = <drive>/felhom-data/backups/... on the host) instead of .../felhom-data/felhom-data/.... Writes, deletion (GetStackBackupData/RemoveStack/ ProtectedHDDPaths), wipe-warning scan, and export updated coherently; legacy double-nest dirs kept protected. New appbackup test asserts no doubled segment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,8 @@ type guestBackupView struct {
|
||||
Success bool
|
||||
StartedAt time.Time
|
||||
SizeBytes int64
|
||||
Target string // human label: "Biztonsági szerver (PBS)" / "Helyi tároló (local)"
|
||||
Target string // human label: "Biztonsági szerver – külön hardver (PBS)" / "Helyi tároló (local)"
|
||||
Offsite bool // the whole-guest backup landed on the PBS offsite tier (separate hardware)
|
||||
Archive string
|
||||
Mode string // snapshot | stop
|
||||
StopMode bool // mode == stop → full app downtime during the backup (warn)
|
||||
@@ -73,6 +74,7 @@ func (s *Server) loadGuestBackup(ctx context.Context) *guestBackupView {
|
||||
v.Mode = st.Backup.Mode
|
||||
v.StopMode = st.Backup.Mode == "stop"
|
||||
v.Target = backupTargetLabel(st.Backup)
|
||||
v.Offsite = backupIsPBS(st.Backup)
|
||||
if t, perr := time.Parse(time.RFC3339, st.Backup.StartedAt); perr == nil {
|
||||
v.StartedAt = t
|
||||
}
|
||||
@@ -97,13 +99,20 @@ func (s *Server) loadGuestBackup(ctx context.Context) *guestBackupView {
|
||||
return v
|
||||
}
|
||||
|
||||
// backupTargetLabel maps the agent's backup target to a customer-facing Hungarian label, surfacing
|
||||
// whether the backup landed on the PBS offsite tier or local host storage (from the archive volid /
|
||||
// target id — "felhom-pbs"/"pbs:" ⇒ PBS, else local host storage).
|
||||
func backupTargetLabel(b *agentapi.BackupRecord) string {
|
||||
// backupIsPBS reports whether a whole-guest backup landed on the PBS offsite tier (separate
|
||||
// hardware), inferred from the target id / archive volid ("felhom-pbs"/"pbs:" ⇒ PBS).
|
||||
func backupIsPBS(b *agentapi.BackupRecord) bool {
|
||||
id := strings.ToLower(b.TargetID)
|
||||
if strings.Contains(id, "pbs") || strings.HasPrefix(strings.ToLower(b.Archive), "felhom-pbs") || strings.Contains(strings.ToLower(b.Archive), "pbs:") {
|
||||
return "Biztonsági szerver (PBS)"
|
||||
arc := strings.ToLower(b.Archive)
|
||||
return strings.Contains(id, "pbs") || strings.HasPrefix(arc, "felhom-pbs") || strings.Contains(arc, "pbs:")
|
||||
}
|
||||
|
||||
// backupTargetLabel maps the agent's backup target to a customer-facing Hungarian label. The PBS
|
||||
// case calls out that the backup is on SEPARATE HARDWARE (real disaster recovery — survives a host
|
||||
// disk/hardware failure), which is the whole point of re-pointing the backup offsite.
|
||||
func backupTargetLabel(b *agentapi.BackupRecord) string {
|
||||
if backupIsPBS(b) {
|
||||
return "Biztonsági szerver – külön hardver (PBS)"
|
||||
}
|
||||
if b.TargetID != "" {
|
||||
return "Helyi tároló (" + b.TargetID + ")"
|
||||
|
||||
@@ -324,12 +324,13 @@ func (s *Server) handleStorageImpact(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// backupCopiesOnPath lists the apps whose CROSS-DRIVE (secondary) backup copies are stored on the
|
||||
// drive mounted at `where` (slice 10 P4) — the felhom-data/backups/secondary/<app> dirs. A wipe of
|
||||
// this drive removes these copies. Best-effort filesystem scan; empty until the cross-drive backup
|
||||
// ENGINE (a follow-on slice) actually writes here. Shared/aggregate dirs (restic repo, _infra) are
|
||||
// not apps and are skipped.
|
||||
// drive mounted at `where` (slice 10 P4) — the backups/secondary/<app> dirs. A wipe of this drive
|
||||
// removes these copies. Best-effort filesystem scan; empty until the cross-drive backup ENGINE (a
|
||||
// follow-on slice) actually writes here. Shared/aggregate dirs (restic repo, _infra) are not apps
|
||||
// and are skipped. Model A: `where` is the in-guest drive mount, which IS the felhom-data namespace
|
||||
// root, so backups/ sits directly under it (no felhom-data segment — avoids the double-nest).
|
||||
func backupCopiesOnPath(where string) []string {
|
||||
secondary := filepath.Join(where, appbackup.FelhomDataDir, "backups", "secondary")
|
||||
secondary := filepath.Join(appbackup.NamespaceRoot(where, true), "backups", "secondary")
|
||||
entries, err := os.ReadDir(secondary)
|
||||
if err != nil {
|
||||
return nil // no secondary backups here (or the path isn't readable) — nothing to warn about
|
||||
|
||||
@@ -140,10 +140,17 @@
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if and .GuestBackup .GuestBackup.Offsite}}
|
||||
<div class="stat-card stat-running">
|
||||
<div class="stat-value">✓</div>
|
||||
<div class="stat-label">Távoli mentés<br><span class="relative-time">külön hardveren (PBS)</span></div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="stat-card" style="border-left-color: var(--gray);">
|
||||
<div class="stat-value" style="background:var(--gray);-webkit-background-clip:text;background-clip:text;">–</div>
|
||||
<div class="stat-label">Távoli mentés<br><span class="relative-time">nincs beállítva</span></div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div class="stat-card stat-total">
|
||||
<div class="stat-value">
|
||||
|
||||
Reference in New Issue
Block a user