02650e3202
Controller: - internal/web/csrf.go (new): CsrfProtect middleware, csrfToken/csrfField helpers - auth.go: per-session CSRF token (csrfToken field, csrfTokenForSession method) - server.go: executeTemplate wrapper auto-injects CSRFField+CSRFToken - main.go: wire CsrfProtect on all routes; bump to v0.23.0 - handlers.go, storage_handlers.go, handler_restore.go: executeTemplate - All templates: CSRFField in forms, meta csrf-token, csrfHeaders() JS helper, fetch calls updated; sendBeacon→fetch+keepalive in storage_attach.html Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
265 lines
11 KiB
HTML
265 lines
11 KiB
HTML
{{define "migrate"}}
|
|
{{template "layout_start" .}}
|
|
|
|
<div class="page-header">
|
|
<div style="display:flex;align-items:center;gap:.5rem">
|
|
<a href="/stacks/{{.Meta.Slug}}/deploy" class="btn btn-sm btn-outline">← Vissza</a>
|
|
<h2>{{.Meta.DisplayName}} — Adatáthelyezés</h2>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-card" id="migrate-form-card">
|
|
<h3>Adatok áthelyezése másik tárolóra</h3>
|
|
|
|
<div class="settings-grid" style="margin-bottom:1.5rem">
|
|
<div class="settings-row">
|
|
<span class="settings-label">Jelenlegi tárhely</span>
|
|
<span class="settings-value mono">{{.CurrentLabel}} ({{.CurrentHDDPath}})</span>
|
|
</div>
|
|
{{if .DataSizeHuman}}
|
|
<div class="settings-row">
|
|
<span class="settings-label">Adatméret</span>
|
|
<span class="settings-value mono">{{.DataSizeHuman}}</span>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="target-path">Cél tárhely <span class="required">*</span></label>
|
|
<select id="target-path" class="form-control">
|
|
{{range .OtherPaths}}
|
|
<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>Az alkalmazás a mozgatás idejére leáll</li>
|
|
<li>Nagy adatmennyiségnél ez percekig tarthat</li>
|
|
<li>DB mentés fájlok is átkerülnek</li>
|
|
<li>A migráció után azonnal lefut egy biztonsági mentés az új meghajtón</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div style="margin-bottom:1.5rem">
|
|
<label style="display:flex;align-items:center;gap:.5rem;cursor:pointer">
|
|
<input type="checkbox" id="auto-delete" checked>
|
|
<span>Régi adatok törlése a forrás meghajtóról</span>
|
|
</label>
|
|
<span class="form-hint" style="margin-left:1.5rem">Ha bekapcsolva, a forrás meghajtóról az alkalmazás adatai és DB mentései automatikusan törlődnek a sikeres áthelyezés után.</span>
|
|
</div>
|
|
|
|
<div id="migrate-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="startMigrate()">📦 Mozgatás indítása</button>
|
|
<a href="/stacks/{{.Meta.Slug}}/deploy" class="btn btn-outline">Mégsem</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-card" id="migrate-progress-card" style="display:none">
|
|
<h3>Adatok áthelyezése...</h3>
|
|
|
|
<div class="disk-progress-steps" id="mig-steps">
|
|
<div class="disk-step" id="mstep-stopping"><span class="disk-step-icon">○</span> Alkalmazás leállítása</div>
|
|
<div class="disk-step" id="mstep-copying"><span class="disk-step-icon">○</span> Adatok másolása</div>
|
|
<div class="disk-step" id="mstep-updating"><span class="disk-step-icon">○</span> Konfiguráció frissítése</div>
|
|
<div class="disk-step" id="mstep-starting"><span class="disk-step-icon">○</span> Alkalmazás indítása</div>
|
|
<div class="disk-step" id="mstep-cleaning"><span class="disk-step-icon">○</span> Régi adatok törlése</div>
|
|
<div class="disk-step" id="mstep-backing_up"><span class="disk-step-icon">○</span> Biztonsági mentés</div>
|
|
<div class="disk-step" id="mstep-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="mig-progress-bar" style="width:0%;transition:width .4s ease;height:12px;border-radius:6px"></div>
|
|
</div>
|
|
<span class="mono form-hint" id="mig-progress-pct">0%</span>
|
|
</div>
|
|
|
|
<div id="mig-progress-msg" class="form-hint" style="margin-top:.75rem"></div>
|
|
<div id="mig-elapsed" class="form-hint mono" style="margin-top:.25rem"></div>
|
|
<div id="mig-progress-error" class="alert alert-error" style="display:none;margin-top:1rem"></div>
|
|
</div>
|
|
|
|
<div class="settings-card" id="migrate-done-card" style="display:none">
|
|
<h3>✅ Adatáthelyezés kész!</h3>
|
|
<p id="done-msg" style="margin-top:.75rem;color:var(--text-secondary)">
|
|
Az alkalmazás az új tárolóról fut.
|
|
</p>
|
|
<div id="done-tier2-warning" class="alert alert-warning" style="display:none;margin-top:1rem">
|
|
A 2. szintű mentés törlésre került, mert a cél meghajtó megegyezett a mentési céllal.
|
|
<a href="/stacks/{{.Meta.Slug}}/deploy">Újrakonfigurálás →</a>
|
|
</div>
|
|
<div id="done-manual-steps" class="alert alert-warning" style="margin-top:1rem">
|
|
<strong>Javasolt lépések:</strong>
|
|
<ol style="margin:.5rem 0 0 1rem;padding:0">
|
|
<li>Ellenőrizd, hogy az alkalmazás megfelelően működik</li>
|
|
<li>Győződj meg róla, hogy minden adat megtalálható</li>
|
|
</ol>
|
|
</div>
|
|
<div style="margin-top:1.5rem;display:flex;gap:.75rem;flex-wrap:wrap">
|
|
<a href="/stacks/{{.Meta.Slug}}/deploy" class="btn btn-primary">Alkalmazások megtekintése</a>
|
|
<button id="migrate-delete-old-btn" class="btn btn-outline btn-danger" onclick="deleteOldMigrationData()" style="display:none">
|
|
🗑️ Korábbi adatok törlése
|
|
</button>
|
|
<a href="/settings" class="btn btn-outline">Beállítások</a>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var stackName = '{{.Stack.Name}}';
|
|
var migPollTimer = null;
|
|
|
|
function startMigrate() {
|
|
var targetPath = document.getElementById('target-path').value;
|
|
if (!targetPath) {
|
|
document.getElementById('migrate-error').textContent = 'Válasszon cél tárhelyet.';
|
|
document.getElementById('migrate-error').style.display = 'block';
|
|
return;
|
|
}
|
|
|
|
document.getElementById('migrate-form-card').style.display = 'none';
|
|
document.getElementById('migrate-progress-card').style.display = 'block';
|
|
|
|
var autoDelete = document.getElementById('auto-delete').checked;
|
|
|
|
fetch('/api/storage/migrate', {
|
|
method: 'POST',
|
|
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
|
body: JSON.stringify({stack_name: stackName, target_path: targetPath, auto_delete_stale: autoDelete})
|
|
})
|
|
.then(function(r){ return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.ok) {
|
|
showMigError(data.error || 'Ismeretlen hiba');
|
|
return;
|
|
}
|
|
migPollTimer = setInterval(pollMigProgress, 2000);
|
|
})
|
|
.catch(function(e) {
|
|
showMigError('Hálózati hiba: ' + e.message);
|
|
});
|
|
}
|
|
|
|
var migStepOrder = ['stopping','copying','updating','starting','cleaning','backing_up','done'];
|
|
|
|
function pollMigProgress() {
|
|
fetch('/api/storage/migrate/status')
|
|
.then(function(r){ return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.ok) return;
|
|
updateMigUI(data);
|
|
if (data.done) {
|
|
clearInterval(migPollTimer);
|
|
if (data.step === 'done') {
|
|
showMigDone();
|
|
}
|
|
}
|
|
})
|
|
.catch(function(){});
|
|
}
|
|
|
|
function updateMigUI(data) {
|
|
var currentIdx = migStepOrder.indexOf(data.step);
|
|
if (currentIdx < 0 && data.step === 'rolling_back') {
|
|
currentIdx = 1; // show during copy step
|
|
}
|
|
|
|
migStepOrder.forEach(function(s, i) {
|
|
var el = document.getElementById('mstep-' + s);
|
|
if (!el) return;
|
|
var icon = el.querySelector('.disk-step-icon');
|
|
if (i < currentIdx) {
|
|
el.className = 'disk-step disk-step-done';
|
|
icon.textContent = '✅';
|
|
} else if (i === currentIdx) {
|
|
el.className = 'disk-step disk-step-active';
|
|
icon.textContent = (data.step === 'error' || data.step === 'rolling_back') ? '❌' : '⏳';
|
|
} else {
|
|
el.className = 'disk-step';
|
|
icon.textContent = '○';
|
|
}
|
|
});
|
|
|
|
var pct = data.pct || 0;
|
|
document.getElementById('mig-progress-bar').style.width = pct + '%';
|
|
document.getElementById('mig-progress-pct').textContent = pct + '%';
|
|
document.getElementById('mig-progress-msg').textContent = data.msg || '';
|
|
|
|
if (data.elapsed_sec) {
|
|
document.getElementById('mig-elapsed').textContent = data.elapsed_sec + ' másodperce fut';
|
|
}
|
|
|
|
if (data.step === 'error' || (data.error && data.error !== '')) {
|
|
showMigError(data.error || data.msg || 'Ismeretlen hiba');
|
|
}
|
|
}
|
|
|
|
function showMigError(msg) {
|
|
clearInterval(migPollTimer);
|
|
document.getElementById('mig-progress-error').textContent = 'Hiba: ' + msg;
|
|
document.getElementById('mig-progress-error').style.display = 'block';
|
|
document.getElementById('migrate-progress-card').querySelector('h3').textContent = 'Áthelyezés sikertelen';
|
|
}
|
|
|
|
function showMigDone() {
|
|
document.getElementById('migrate-progress-card').style.display = 'none';
|
|
document.getElementById('migrate-done-card').style.display = 'block';
|
|
document.getElementById('migrate-done-card').scrollIntoView({behavior:'smooth'});
|
|
|
|
var autoDeleteChecked = document.getElementById('auto-delete').checked;
|
|
if (autoDeleteChecked) {
|
|
document.getElementById('done-msg').textContent =
|
|
'Az alkalmazás az új tárolóról fut. A régi adatok automatikusan törölve lettek.';
|
|
} else {
|
|
document.getElementById('done-msg').innerHTML =
|
|
'Az alkalmazás az új tárolóról fut.<br>A régi adatok a korábbi helyen megmaradtak.';
|
|
document.getElementById('migrate-delete-old-btn').style.display = '';
|
|
}
|
|
}
|
|
|
|
function deleteOldMigrationData() {
|
|
var oldPath = '{{.CurrentHDDPath}}';
|
|
if (!confirm('Biztosan törölni szeretnéd a korábbi adatokat?\n\nTárhely: ' + oldPath + '\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;
|
|
}
|
|
if (!confirm('UTOLSÓ FIGYELMEZTETÉS!\n\nA törlés visszavonhatatlan. Biztosan folytatod?')) {
|
|
return;
|
|
}
|
|
|
|
var btn = document.getElementById('migrate-delete-old-btn');
|
|
btn.disabled = true;
|
|
btn.textContent = 'Törlés folyamatban...';
|
|
|
|
fetch('/api/storage/stale-cleanup', {
|
|
method: 'POST',
|
|
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
|
body: JSON.stringify({stack_name: stackName, stale_path: oldPath})
|
|
})
|
|
.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;
|
|
}
|
|
btn.textContent = '✅ Korábbi adatok törölve (' + (data.freed_human || '') + ')';
|
|
btn.classList.remove('btn-danger');
|
|
btn.classList.add('btn-outline');
|
|
btn.onclick = null;
|
|
})
|
|
.catch(function(e) {
|
|
alert('Hálózati hiba: ' + e.message);
|
|
btn.disabled = false;
|
|
btn.textContent = '🗑️ Korábbi adatok törlése';
|
|
});
|
|
}
|
|
</script>
|
|
|
|
{{template "layout_end" .}}
|
|
{{end}}
|