controller v0.242.0: a removed app is listed with its kept backup; five small ones (R-487 R-491 R-490 R-489 R-476 R-456)
gates / gates (push) Successful in 14s

R-487: the local backup lists are keyed on the drives, not on what is
deployed — a removed app whose unit was kept is listed with the restore
that reinstalls it, the picker answers for it, and the restore opens the
unit where it sits. R-491: a removal clears the app's update hold.
R-490: /api/system/info reaches the API router and reads the default
storage path. R-489: volumes_removed is the real before/after difference,
[] when none. R-476: a Tier-2 copy is dated by its data, not its manifest.
R-456: the boot-orphan rule is pinned. Every fix red-proofed.
This commit is contained in:
2026-09-13 22:50:18 +02:00
parent c1f62ddae8
commit d698ce343b
22 changed files with 838 additions and 13 deletions
@@ -147,7 +147,10 @@
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='{{logoPNGURL .Slug}}';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
<span class="app-backup-row-name">{{.DisplayName}}</span>
<div class="app-backup-row-meta">
{{if .DriveDisconnected}}
{{if .Removed}}
<span class="tag tag-warn">Eltávolítva — visszaállítható</span>
{{if .StorageLabel}}<span class="meta-badge meta-badge-storage">{{.StorageLabel}}</span>{{end}}
{{else if .DriveDisconnected}}
<span class="tag tag-warn">Meghajtó leválasztva</span>
{{else if .HasHDDData}}
{{if .StorageLabel}}<span class="meta-badge meta-badge-storage">{{.StorageLabel}}</span>{{end}}
@@ -162,6 +165,26 @@
<span class="expand-icon">▶</span>
</div>
<div class="app-backup-row-detail" style="display:none">
{{if .Removed}}
{{/* R-487: a removed app's kept unit. One action — the same restore the picker on the
Visszaállítás page runs (POST /backup/restore, snapshot „helyi"); it reinstalls the app
from the unit (R-253). No tier rows: nothing is scheduled for an app that is not here. */}}
<div class="backup-layers">
<div class="backup-layer-row">
<span class="tier-label">Mentés</span>
<span class="layer-badge">Megtartva</span>
<span class="tier-location">helyi</span>
<span class="tier-contents">Az alkalmazást eltávolítottad, a mentése megmaradt. Legutóbbi mentés: {{fmtTimeStr .RemovedUnitTime}}.</span>
<div class="layer-actions">
<form method="POST" action="/backup/restore" style="display:inline">{{$.CSRFField}}
<input type="hidden" name="stack_name" value="{{.StackName}}">
<input type="hidden" name="snapshot_id" value="helyi">
<button type="submit" class="btn btn-xs btn-outline" data-confirm="Újratelepíti a(z) {{.DisplayName}} alkalmazást a megtartott mentésből ({{fmtTimeStr .RemovedUnitTime}})? Az adatok a mentésből állnak vissza.">Visszaállítás a mentésből</button>
</form>
</div>
</div>
</div>
{{else}}
<div class="backup-layers">
<!-- Tier 1: Nightly backup (mandatory, same drive) -->
<div class="backup-layer-row">
@@ -332,6 +355,7 @@
{{end}}
</div>
{{end}}
{{end}}
</div>
</div>
{{end}}
@@ -25,6 +25,14 @@
{{range .Backup.AppDataInfo}}
<option value="{{.StackName}}" data-has-hdd="{{.HasHDDData}}" data-has-db="{{.HasDBDump}}" data-has-volumes="{{.HasVolumeData}}">{{.DisplayName}}</option>
{{end}}
{{/* R-487: removed apps whose unit was kept — the restore reinstalls them (R-253). */}}
{{if .RemovedApps}}
<optgroup label="Eltávolított alkalmazások (mentés megtartva)">
{{range .RemovedApps}}
<option value="{{.StackName}}" data-removed="true" data-has-hdd="false" data-has-db="false" data-has-volumes="true">{{.DisplayName}} — eltávolítva</option>
{{end}}
</optgroup>
{{end}}
</select>
</div>
<div class="restore-form-row">