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
+46 -51
View File
@@ -451,31 +451,28 @@
<option value="">— Válasszon —</option>
{{range .Backup.AppDataInfo}}
{{if and .HasHDDData .BackupEnabled}}
<option value="{{.StackName}}" data-paths="{{range $i, $p := .HDDPaths}}{{if $i}},{{end}}{{$p.HostPath}}{{end}}">{{.DisplayName}}</option>
<option value="{{.StackName}}">{{.DisplayName}}</option>
{{end}}
{{end}}
</select>
</div>
<div class="restore-form-row">
<label class="restore-label">Pillanatkép:</label>
<select id="restore-snapshot" class="restore-select">
<option value="">Betöltés...</option>
<select id="restore-snapshot" class="restore-select" onchange="onRestoreConfirmChange()">
<option value="">Válasszon alkalmazást</option>
</select>
</div>
<div id="restore-paths" class="restore-paths" style="display:none;">
<span class="restore-label">Visszaállítandó útvonalak:</span>
<ul id="restore-paths-list" class="repo-path-list"></ul>
<div id="restore-no-snapshots" class="restore-warning" style="display:none;">
Még nincs mentés felhasználói adattal.
</div>
<div class="restore-warning">
<strong>FIGYELMEZTETÉS</strong><br>
A visszaállítás FELÜLÍRJA a kiválasztott alkalmazás jelenlegi adatait a mentés pillanatának állapotával.
Ez a művelet NEM vonható vissza!<br>
Javasoljuk az alkalmazás leállítását a visszaállítás előtt.
⚠ A visszaállítás felülírja az alkalmazás jelenlegi adatait a kiválasztott mentés állapotával.
Az alkalmazás a folyamat során automatikusan leáll és újraindul.
</div>
<div class="restore-confirm">
<label>
<input type="checkbox" id="restore-confirm-cb" onchange="onRestoreConfirmChange()">
Megértettem, visszaállítás saját felelősségre.
Megértettem, visszaállítás indítása.
</label>
</div>
<div class="restore-actions">
@@ -595,46 +592,48 @@ function copyResticPw() {
}
// Restore section
var huDays = ['vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök', 'péntek', 'szombat'];
function formatSnapshot(s) {
var t = new Date(s.time);
var pad = function(n) { return n < 10 ? '0' + n : '' + n; };
return t.getFullYear() + '-' + pad(t.getMonth()+1) + '-' + pad(t.getDate()) +
' ' + huDays[t.getDay()] + ' ' + pad(t.getHours()) + ':' + pad(t.getMinutes()) +
' (' + s.short_id + ')';
}
function onRestoreAppChange() {
var sel = document.getElementById('restore-app');
var opt = sel.options[sel.selectedIndex];
var pathsDiv = document.getElementById('restore-paths');
var pathsList = document.getElementById('restore-paths-list');
if (!opt.value) {
pathsDiv.style.display = 'none';
return;
}
var paths = (opt.getAttribute('data-paths') || '').split(',').filter(Boolean);
pathsList.innerHTML = '';
paths.forEach(function(p) {
var li = document.createElement('li');
li.className = 'mono';
li.textContent = p;
pathsList.appendChild(li);
});
pathsDiv.style.display = 'block';
// Load snapshots
fetch('/api/backup/snapshots')
.then(function(r) { return r.json(); })
.then(function(data) {
var snapSel = document.getElementById('restore-snapshot');
snapSel.innerHTML = '<option value="">— Válasszon —</option>';
if (data.ok && data.data) {
data.data.forEach(function(s) {
var o = document.createElement('option');
o.value = s.short_id;
var t = new Date(s.time);
o.textContent = s.short_id + ' — ' + t.toLocaleString('hu-HU');
snapSel.appendChild(o);
});
}
});
var appName = sel.value;
var snapSel = document.getElementById('restore-snapshot');
var noSnaps = document.getElementById('restore-no-snapshots');
document.getElementById('restore-confirm-cb').checked = false;
document.getElementById('restore-btn').disabled = true;
noSnaps.style.display = 'none';
if (!appName) {
snapSel.innerHTML = '<option value="">— Válasszon alkalmazást —</option>';
return;
}
snapSel.innerHTML = '<option value="">— Betöltés... —</option>';
fetch('/api/backup/snapshots?stack=' + encodeURIComponent(appName))
.then(function(r) { return r.json(); })
.then(function(data) {
snapSel.innerHTML = '<option value="">— Válasszon —</option>';
if (data.ok && data.data && data.data.length > 0) {
data.data.forEach(function(s) {
var o = document.createElement('option');
o.value = s.short_id;
o.textContent = formatSnapshot(s);
snapSel.appendChild(o);
});
} else {
snapSel.innerHTML = '<option value="">— Nincs elérhető mentés —</option>';
noSnaps.style.display = 'block';
}
});
}
function onRestoreConfirmChange() {
@@ -674,11 +673,7 @@ function submitRestore() {
startBackupPolling();
{{end}}{{end}}
// Wire up snapshot selection change for restore confirm
document.addEventListener('DOMContentLoaded', function() {
var snapSel = document.getElementById('restore-snapshot');
if (snapSel) snapSel.addEventListener('change', onRestoreConfirmChange);
});
</script>
{{template "layout_end" .}}