feat: drive migration & Tier 2 restic deprecation (v0.18.0)
Phase 1: Deprecate restic as Tier 2 method (rsync only), auto-migrate on startup Phase 2: Enhanced per-app migration with backup awareness, DB dump copy, auto-cleanup Phase 3: Full drive migration with decommissioned state, rollback support, wizard UI Phase 4: Hub report includes decommissioned drive state Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
{{define "migrate_drive"}}
|
||||
{{template "layout_start" .}}
|
||||
|
||||
<div class="page-header">
|
||||
<div style="display:flex;align-items:center;gap:.5rem">
|
||||
<a href="/settings" class="btn btn-sm btn-outline">← Vissza</a>
|
||||
<h2>Meghajtó kiváltása</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card" id="drive-mig-form-card">
|
||||
<h3>Összes adat átköltöztetése másik meghajtóra</h3>
|
||||
|
||||
<div class="settings-grid" style="margin-bottom:1.5rem">
|
||||
<div class="settings-row">
|
||||
<span class="settings-label">Forrás meghajtó</span>
|
||||
<span class="settings-value mono">{{.SourceLabel}} ({{.SourcePath}})</span>
|
||||
</div>
|
||||
{{if .SourceDiskInfo}}
|
||||
<div class="settings-row">
|
||||
<span class="settings-label">Használat</span>
|
||||
<span class="settings-value mono">{{.SourceDiskInfo.UsedHuman}} / {{.SourceDiskInfo.TotalHuman}}</span>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="settings-row">
|
||||
<span class="settings-label">Alkalmazások</span>
|
||||
<span class="settings-value">{{range $i, $app := .AppsOnSource}}{{if $i}}, {{end}}{{$app.DisplayName}}{{end}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="dest-path">Cél meghajtó <span class="required">*</span></label>
|
||||
<select id="dest-path" class="form-control">
|
||||
{{range .DestPaths}}
|
||||
<option value="{{.Path}}">{{.Label}} ({{.Path}}) — {{.FreeHuman}} szabad</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning" style="margin-bottom:1.5rem">
|
||||
<strong>Figyelmeztetések:</strong>
|
||||
<ul style="margin:.5rem 0 0 1rem;padding:0">
|
||||
<li>Minden alkalmazás leáll a mozgatás idejére</li>
|
||||
<li>Nagy adatmennyiségnél ez hosszabb ideig tarthat</li>
|
||||
<li>A restic mentés repók NEM kerülnek átmásolásra (helyet spórolunk)</li>
|
||||
<li>A forrás meghajtó "Kiváltva" állapotba kerül</li>
|
||||
<li>A 2. szintű mentések automatikusan átirányításra kerülnek</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{if .Tier2Impact}}
|
||||
<div class="alert alert-info" style="margin-bottom:1.5rem">
|
||||
<strong>Mentési hatás:</strong>
|
||||
<ul style="margin:.5rem 0 0 1rem;padding:0">
|
||||
{{range .Tier2Impact}}
|
||||
<li>{{.}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<div id="drive-mig-error" class="alert alert-error" style="display:none;margin-bottom:1rem"></div>
|
||||
|
||||
<div class="form-actions" style="gap:.75rem">
|
||||
<button class="btn btn-primary" onclick="startDriveMigrate()">📦 Meghajtó kiváltás indítása</button>
|
||||
<a href="/settings" class="btn btn-outline">Mégsem</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card" id="drive-mig-progress-card" style="display:none">
|
||||
<h3>Meghajtó kiváltás folyamatban...</h3>
|
||||
|
||||
<div class="disk-progress-steps" id="dm-steps">
|
||||
<div class="disk-step" id="dmstep-validating"><span class="disk-step-icon">○</span> Ellenőrzés</div>
|
||||
<div class="disk-step" id="dmstep-stopping"><span class="disk-step-icon">○</span> Alkalmazások leállítása</div>
|
||||
<div class="disk-step" id="dmstep-copying"><span class="disk-step-icon">○</span> Adatok másolása</div>
|
||||
<div class="disk-step" id="dmstep-verifying"><span class="disk-step-icon">○</span> Ellenőrzés</div>
|
||||
<div class="disk-step" id="dmstep-configuring"><span class="disk-step-icon">○</span> Konfiguráció</div>
|
||||
<div class="disk-step" id="dmstep-starting"><span class="disk-step-icon">○</span> Alkalmazások indítása</div>
|
||||
<div class="disk-step" id="dmstep-backup"><span class="disk-step-icon">○</span> Biztonsági mentés</div>
|
||||
<div class="disk-step" id="dmstep-done"><span class="disk-step-icon">○</span> Kész</div>
|
||||
</div>
|
||||
|
||||
<div class="disk-progress-bar-wrap" style="margin-top:1.5rem">
|
||||
<div class="system-bar" style="height:12px;border-radius:6px">
|
||||
<div class="system-bar-fill system-bar-green" id="dm-progress-bar" style="width:0%;transition:width .4s ease;height:12px;border-radius:6px"></div>
|
||||
</div>
|
||||
<span class="mono form-hint" id="dm-progress-pct">0%</span>
|
||||
</div>
|
||||
|
||||
<div id="dm-progress-msg" class="form-hint" style="margin-top:.75rem"></div>
|
||||
<div id="dm-progress-detail" class="form-hint mono" style="margin-top:.25rem;font-size:.85rem"></div>
|
||||
<div id="dm-elapsed" class="form-hint mono" style="margin-top:.25rem"></div>
|
||||
<div id="dm-progress-error" class="alert alert-error" style="display:none;margin-top:1rem"></div>
|
||||
</div>
|
||||
|
||||
<div class="settings-card" id="drive-mig-done-card" style="display:none">
|
||||
<h3>Meghajtó kiváltás kész!</h3>
|
||||
<p id="dm-done-msg" style="margin-top:.75rem;color:var(--text-secondary)"></p>
|
||||
<div class="alert alert-info" style="margin-top:1rem">
|
||||
<strong>A forrás meghajtó biztonságosan eltávolítható.</strong>
|
||||
Ha nem szándékozod újrafelhasználni, a Beállítások oldalon eltávolíthatod a rendszerből.
|
||||
</div>
|
||||
<div style="margin-top:1.5rem;display:flex;gap:.75rem;flex-wrap:wrap">
|
||||
<a href="/settings" class="btn btn-primary">Beállítások</a>
|
||||
<a href="/backups" class="btn btn-outline">Mentések</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var sourcePath = '{{.SourcePath}}';
|
||||
var dmPollTimer = null;
|
||||
|
||||
function startDriveMigrate() {
|
||||
var destPath = document.getElementById('dest-path').value;
|
||||
if (!destPath) {
|
||||
document.getElementById('drive-mig-error').textContent = 'Válasszon cél meghajtót.';
|
||||
document.getElementById('drive-mig-error').style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm('Biztosan ki szeretné váltani a forrás meghajtót?\n\nMinden alkalmazás leáll a migráció idejére.\nEz a művelet nem vonható vissza egyszerűen.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('drive-mig-form-card').style.display = 'none';
|
||||
document.getElementById('drive-mig-progress-card').style.display = 'block';
|
||||
|
||||
fetch('/api/storage/migrate-drive', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({source_path: sourcePath, dest_path: destPath})
|
||||
})
|
||||
.then(function(r){ return r.json(); })
|
||||
.then(function(data) {
|
||||
if (!data.ok) {
|
||||
showDMError(data.error || 'Ismeretlen hiba');
|
||||
return;
|
||||
}
|
||||
dmPollTimer = setInterval(pollDMProgress, 2000);
|
||||
})
|
||||
.catch(function(e) {
|
||||
showDMError('Hálózati hiba: ' + e.message);
|
||||
});
|
||||
}
|
||||
|
||||
var dmStepOrder = ['validating','stopping','copying','verifying','configuring','starting','backup','done'];
|
||||
|
||||
function pollDMProgress() {
|
||||
fetch('/api/storage/migrate-drive/status')
|
||||
.then(function(r){ return r.json(); })
|
||||
.then(function(data) {
|
||||
if (!data.ok) return;
|
||||
updateDMUI(data);
|
||||
if (data.done) {
|
||||
clearInterval(dmPollTimer);
|
||||
if (data.step === 'done') {
|
||||
showDMDone(data.msg);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function(){});
|
||||
}
|
||||
|
||||
function updateDMUI(data) {
|
||||
var currentIdx = dmStepOrder.indexOf(data.step);
|
||||
if (currentIdx < 0 && data.step === 'rolling_back') {
|
||||
currentIdx = dmStepOrder.indexOf('copying');
|
||||
}
|
||||
|
||||
dmStepOrder.forEach(function(s, i) {
|
||||
var el = document.getElementById('dmstep-' + s);
|
||||
if (!el) return;
|
||||
var icon = el.querySelector('.disk-step-icon');
|
||||
if (i < currentIdx) {
|
||||
el.className = 'disk-step disk-step-done';
|
||||
icon.textContent = '\u2705';
|
||||
} else if (i === currentIdx) {
|
||||
el.className = 'disk-step disk-step-active';
|
||||
icon.textContent = (data.step === 'error' || data.step === 'rolling_back') ? '\u274C' : '\u23F3';
|
||||
} else {
|
||||
el.className = 'disk-step';
|
||||
icon.textContent = '\u25CB';
|
||||
}
|
||||
});
|
||||
|
||||
var pct = data.pct || 0;
|
||||
document.getElementById('dm-progress-bar').style.width = pct + '%';
|
||||
document.getElementById('dm-progress-pct').textContent = pct + '%';
|
||||
document.getElementById('dm-progress-msg').textContent = data.msg || '';
|
||||
document.getElementById('dm-progress-detail').textContent = data.detail || '';
|
||||
|
||||
if (data.elapsed_sec) {
|
||||
document.getElementById('dm-elapsed').textContent = data.elapsed_sec + ' másodperce fut';
|
||||
}
|
||||
|
||||
if (data.step === 'error' || (data.error && data.error !== '')) {
|
||||
showDMError(data.error || data.msg || 'Ismeretlen hiba');
|
||||
}
|
||||
}
|
||||
|
||||
function showDMError(msg) {
|
||||
clearInterval(dmPollTimer);
|
||||
document.getElementById('dm-progress-error').textContent = 'Hiba: ' + msg;
|
||||
document.getElementById('dm-progress-error').style.display = 'block';
|
||||
document.getElementById('drive-mig-progress-card').querySelector('h3').textContent = 'Meghajtó kiváltás sikertelen';
|
||||
}
|
||||
|
||||
function showDMDone(msg) {
|
||||
document.getElementById('drive-mig-progress-card').style.display = 'none';
|
||||
document.getElementById('drive-mig-done-card').style.display = 'block';
|
||||
document.getElementById('dm-done-msg').textContent = msg || 'A meghajtó sikeresen kiváltva.';
|
||||
document.getElementById('drive-mig-done-card').scrollIntoView({behavior:'smooth'});
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "layout_end" .}}
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user