feat: 0.11.7 — Stale data cleanup + FileBrowser sync after migration + deploy title fix

- Detect stale data on non-active storage paths after migration; show on
  deploy/settings page with size info and two-step delete confirmation
- Add POST /api/storage/stale-cleanup handler with safety checks (active
  path protection, registered-path validation, ProtectedHDDPaths guard)
- Export ProtectedHDDPaths() from stacks package for reuse in web handlers
- Sync FileBrowser mounts after successful app data migration
- Deploy page title/h2 now shows "Beállítások" for already-deployed apps
  instead of always showing "Telepítés"
- Also add delete-old-data button on migration-done card in migrate.html

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 12:45:08 +01:00
parent 264855fb0d
commit fba2eb3631
7 changed files with 376 additions and 8 deletions
+77 -1
View File
@@ -4,7 +4,7 @@
<div class="page-header">
<div style="display:flex;align-items:center;gap:.5rem">
<a href="/stacks" class="btn btn-sm btn-outline">← Vissza</a>
<h2>{{.Meta.DisplayName}} — Telepítés</h2>
<h2>{{.Meta.DisplayName}} — {{if .AlreadyDeployed}}Beállítások{{else}}Telepítés{{end}}</h2>
</div>
<a href="/apps/{{.Meta.Slug}}" class="btn btn-sm btn-outline">️ Részletek</a>
</div>
@@ -58,6 +58,36 @@
{{end}}
</div>
{{end}}
{{if .StaleData}}
<div class="deploy-stale-data">
<h4>🗑️ Korábbi adatok</h4>
<p class="form-hint" style="margin-bottom:1rem">
Az alkalmazás adatainak másolata megtalálható egy másik tárolón is.
Ez általában áthelyezés után marad hátra.
</p>
{{range .StaleData}}
<div class="stale-data-item">
<div class="settings-grid" style="margin-bottom:.75rem">
<div class="settings-row">
<span class="settings-label">Tárhely</span>
<span class="settings-value">{{.Label}} <span class="mono" style="color:var(--text-secondary)">({{.Path}})</span></span>
</div>
<div class="settings-row">
<span class="settings-label">Méret</span>
<span class="settings-value mono">{{.SizeHuman}}</span>
</div>
<div class="settings-row">
<span class="settings-label">Mappák</span>
<span class="settings-value mono" style="font-size:.85rem">{{range .Mounts}}{{.}}<br>{{end}}</span>
</div>
</div>
<button class="btn btn-sm btn-danger" onclick="deleteStaleData('{{$.Meta.Slug}}', '{{.Path}}', this)">
🗑️ Korábbi adatok törlése
</button>
</div>
{{end}}
</div>
{{end}}
{{end}}
{{if and (not .AlreadyDeployed) .MemoryInfo}}
@@ -236,6 +266,52 @@ function generatePassword(fieldId) {
document.getElementById(fieldId).value = pass;
}
function deleteStaleData(stackName, stalePath, btn) {
if (!confirm('Biztosan törölni szeretnéd a korábbi adatokat?\n\nTárhely: ' + stalePath + '\n\n⚠️ Ez a művelet visszavonhatatlan!\nElőtte győződj meg róla, hogy az alkalmazás az új tárolóról megfelelően működik.')) {
return;
}
// Second confirmation
if (!confirm('UTOLSÓ FIGYELMEZTETÉS!\n\nA törlés visszavonhatatlan. Biztosan folytatod?')) {
return;
}
btn.disabled = true;
btn.textContent = 'Törlés folyamatban...';
fetch('/api/storage/stale-cleanup', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({stack_name: stackName, stale_path: stalePath})
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (!data.ok) {
alert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
btn.disabled = false;
btn.textContent = '🗑️ Korábbi adatok törlése';
return;
}
var msg = '✅ Korábbi adatok törölve!\n\nFelszabadított hely: ' + (data.freed_human || '?');
if (data.errors && data.errors.length > 0) {
msg += '\n\n⚠️ Néhány hiba történt:\n' + data.errors.join('\n');
}
alert(msg);
// Remove the stale data card from DOM
var item = btn.closest('.stale-data-item');
if (item) item.remove();
// If no more stale items, remove the whole section
var container = document.querySelector('.deploy-stale-data');
if (container && container.querySelectorAll('.stale-data-item').length === 0) {
container.remove();
}
})
.catch(function(e) {
alert('Hálózati hiba: ' + e.message);
btn.disabled = false;
btn.textContent = '🗑️ Korábbi adatok törlése';
});
}
document.getElementById('deploy-form').addEventListener('submit', async function(e) {
e.preventDefault();