b4bda38fa1
Detect and offer to format empty (no filesystem) partitions on the system disk. Adds IsSystemPartition() for granular per-partition safety checks instead of blocking the entire system disk. Init wizard shows formatable partitions with appropriate warnings. Add felhotest demo node to docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
366 lines
16 KiB
HTML
366 lines
16 KiB
HTML
{{define "storage_init"}}
|
|
{{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>Új meghajtó inicializálása</h2>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-card" id="wizard-scan">
|
|
<h3>1. Meghajtók keresése</h3>
|
|
<p class="settings-card-desc">Keresse meg a rendszerhez csatlakoztatott, még nem inicializált meghajtókat.</p>
|
|
|
|
<button class="btn btn-primary" onclick="scanDisks()" id="scan-btn">🔍 Meghajtók keresése</button>
|
|
<div id="scan-error" class="alert alert-error" style="display:none;margin-top:1rem"></div>
|
|
|
|
<div id="scan-result" style="display:none;margin-top:1.5rem">
|
|
<div id="available-disks"></div>
|
|
<div id="system-disks-note" style="display:none;margin-top:1rem"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-card" id="wizard-configure" style="display:none">
|
|
<h3>2. Konfiguráció</h3>
|
|
<p class="settings-card-desc">Adja meg az inicializálás paramétereit.</p>
|
|
|
|
<form id="init-form">
|
|
<input type="hidden" id="selected-device" name="device_path">
|
|
<input type="hidden" id="create-partition" name="create_partition" value="true">
|
|
|
|
<div class="form-group">
|
|
<label>Kiválasztott eszköz</label>
|
|
<span class="settings-value mono" id="selected-device-display"></span>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="mount-name">Csatlakoztatási név <span class="required">*</span></label>
|
|
<div class="form-inline">
|
|
<span class="mono" style="opacity:.6">/mnt/</span>
|
|
<input type="text" id="mount-name" class="form-control" placeholder="hdd_1"
|
|
pattern="[a-zA-Z0-9_]+" required style="max-width:160px">
|
|
</div>
|
|
<span class="form-hint">Pl. hdd_1 → a meghajtó a /mnt/hdd_1 útvonalra kerül</span>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="storage-label">Megnevezés</label>
|
|
<input type="text" id="storage-label" class="form-control" placeholder="Külső HDD 1TB" maxlength="50">
|
|
</div>
|
|
|
|
<label class="toggle" style="margin-bottom:1.5rem">
|
|
<input type="checkbox" id="set-default" checked>
|
|
<span class="toggle-label">Beállítás alapértelmezett adattárolóként új telepítéseknél</span>
|
|
</label>
|
|
|
|
<div class="alert alert-error" style="margin-bottom:1.5rem">
|
|
<strong>⚠️ FIGYELEM:</strong> A meghajtó <strong>ÖSSZES</strong> adata törlődik!<br>
|
|
Ez a művelet <strong>NEM vonható vissza.</strong>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="confirm-input">A folytatáshoz írja be: <strong>FORMÁZÁS</strong></label>
|
|
<input type="text" id="confirm-input" class="form-control" placeholder="FORMÁZÁS"
|
|
autocomplete="off" style="max-width:200px">
|
|
</div>
|
|
|
|
<div class="form-actions" style="gap:.75rem">
|
|
<button type="submit" class="btn btn-danger-outline" id="init-btn" disabled>
|
|
Inicializálás indítása
|
|
</button>
|
|
<button type="button" class="btn btn-outline" onclick="resetWizard()">Mégsem</button>
|
|
</div>
|
|
<div id="init-error" class="alert alert-error" style="display:none;margin-top:1rem"></div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="settings-card" id="wizard-progress" style="display:none">
|
|
<h3>3. Inicializálás folyamatban...</h3>
|
|
|
|
<div class="disk-progress-steps" id="progress-steps">
|
|
<div class="disk-step" id="pstep-validating"><span class="disk-step-icon">○</span> Eszköz ellenőrzése</div>
|
|
<div class="disk-step" id="pstep-partitioning"><span class="disk-step-icon">○</span> Partíció létrehozása</div>
|
|
<div class="disk-step" id="pstep-formatting"><span class="disk-step-icon">○</span> Fájlrendszer formázása (ext4)</div>
|
|
<div class="disk-step" id="pstep-mounting"><span class="disk-step-icon">○</span> Csatlakoztatás</div>
|
|
<div class="disk-step" id="pstep-permissions"><span class="disk-step-icon">○</span> Mappák és jogosultságok</div>
|
|
<div class="disk-step" id="pstep-done"><span class="disk-step-icon">○</span> Regisztráció</div>
|
|
</div>
|
|
|
|
<div style="margin-top:1.5rem;display:flex;align-items:center;gap:1rem">
|
|
<div class="progress-bar-task" style="flex:1">
|
|
<div class="progress-fill" id="progress-fill" style="width:0%"></div>
|
|
</div>
|
|
<span id="progress-percent" style="font-size:0.9rem;color:var(--text-muted);font-family:'JetBrains Mono',monospace;white-space:nowrap">0%</span>
|
|
</div>
|
|
|
|
<div id="progress-msg" class="form-hint" style="margin-top:.75rem"></div>
|
|
<div id="progress-error" class="alert alert-error" style="display:none;margin-top:1rem"></div>
|
|
</div>
|
|
|
|
<div class="settings-card" id="wizard-done" style="display:none">
|
|
<h3>✅ Meghajtó sikeresen inicializálva!</h3>
|
|
<div id="done-info" class="settings-grid" style="margin-top:1rem">
|
|
<div class="settings-row">
|
|
<span class="settings-label">Útvonal</span>
|
|
<span class="settings-value mono" id="done-path"></span>
|
|
</div>
|
|
</div>
|
|
<a href="/settings" class="btn btn-primary" style="margin-top:1.5rem">← Vissza a Beállításokhoz</a>
|
|
</div>
|
|
|
|
<script>
|
|
var selectedDevice = null;
|
|
var pollTimer = null;
|
|
|
|
function scanDisks() {
|
|
var btn = document.getElementById('scan-btn');
|
|
var errEl = document.getElementById('scan-error');
|
|
var resultEl = document.getElementById('scan-result');
|
|
btn.textContent = 'Keresés...';
|
|
btn.disabled = true;
|
|
errEl.style.display = 'none';
|
|
resultEl.style.display = 'none';
|
|
|
|
fetch('/api/storage/scan', {method:'POST', headers: csrfHeaders()})
|
|
.then(function(r){ return r.json(); })
|
|
.then(function(data) {
|
|
btn.textContent = '🔍 Meghajtók keresése';
|
|
btn.disabled = false;
|
|
if (!data.ok) {
|
|
errEl.textContent = data.error || 'Ismeretlen hiba';
|
|
errEl.style.display = 'block';
|
|
return;
|
|
}
|
|
renderScanResult(data);
|
|
resultEl.style.display = 'block';
|
|
})
|
|
.catch(function(e) {
|
|
btn.textContent = '🔍 Meghajtók keresése';
|
|
btn.disabled = false;
|
|
errEl.textContent = 'Hálózati hiba: ' + e.message;
|
|
errEl.style.display = 'block';
|
|
});
|
|
}
|
|
|
|
function renderScanResult(data) {
|
|
var availEl = document.getElementById('available-disks');
|
|
var sysEl = document.getElementById('system-disks-note');
|
|
var hasAvail = data.available && data.available.length > 0;
|
|
var hasFP = data.formatable_partitions && data.formatable_partitions.length > 0;
|
|
|
|
if (!hasAvail && !hasFP) {
|
|
availEl.innerHTML = '<div class="empty-state" style="padding:1rem">Nem található inicializálható meghajtó vagy partíció.</div>';
|
|
} else {
|
|
var html = '';
|
|
if (hasAvail) {
|
|
html += '<h4 style="margin-bottom:.75rem">Talált meghajtók (' + data.available.length + '):</h4>';
|
|
data.available.forEach(function(disk) {
|
|
var partInfo = '';
|
|
if (disk.Partitions && disk.Partitions.length > 0) {
|
|
partInfo = disk.Partitions.map(function(p) {
|
|
return p.Name + (p.FSType ? ' (' + p.FSType + ')' : ' (nincs fájlrendszer)') + (p.MountPoint ? ' → ' + p.MountPoint : '');
|
|
}).join(', ');
|
|
}
|
|
html += '<div class="storage-path-item" style="cursor:pointer;border:2px solid transparent" ' +
|
|
'onclick="selectDisk(this, \'' + disk.Path + '\', true)" ' +
|
|
'data-path="' + disk.Path + '" id="disk-' + disk.Name + '">' +
|
|
'<div class="storage-path-header"><div class="storage-path-info">' +
|
|
'<span class="storage-path-label">○ ' + disk.Path + ' — ' + (disk.Size || '?') + '</span>' +
|
|
(disk.Model ? '<span class="storage-path-path">' + disk.Model + '</span>' : '') +
|
|
(partInfo ? '<span class="form-hint">' + partInfo + '</span>' : '<span class="form-hint">Nincs partíció</span>') +
|
|
'</div></div></div>';
|
|
});
|
|
}
|
|
|
|
if (hasFP) {
|
|
html += '<h4 style="margin-bottom:.75rem;margin-top:1.5rem">Formázható partíciók a rendszermeghajtón (' +
|
|
data.formatable_partitions.length + '):</h4>';
|
|
html += '<div class="alert alert-info" style="margin-bottom:.75rem;font-size:.85rem">' +
|
|
'Az alábbi partíciók a rendszermeghajtón találhatók, de nincsenek használatban. ' +
|
|
'Formázás után adattárolóként használhatók.</div>';
|
|
data.formatable_partitions.forEach(function(fp) {
|
|
var parentInfo = fp.ParentDiskPath + ' (' + fp.ParentDiskSize + ')';
|
|
if (fp.ParentDiskModel) parentInfo += ' — ' + fp.ParentDiskModel;
|
|
html += '<div class="storage-path-item" style="cursor:pointer;border:2px solid transparent" ' +
|
|
'onclick="selectDisk(this, \'' + fp.Path + '\', false)" ' +
|
|
'data-path="' + fp.Path + '">' +
|
|
'<div class="storage-path-header"><div class="storage-path-info">' +
|
|
'<span class="storage-path-label">○ ' + fp.Path + ' — ' + (fp.Size || '?') + '</span>' +
|
|
'<span class="form-hint">Rendszermeghajtó partíciója: ' + parentInfo + '</span>' +
|
|
'<span class="form-hint">Nincs fájlrendszer — formázásra kész</span>' +
|
|
'</div></div></div>';
|
|
});
|
|
}
|
|
|
|
availEl.innerHTML = html;
|
|
}
|
|
|
|
if (data.system && data.system.length > 0) {
|
|
var sysNames = data.system.map(function(d){ return d.Path + ' (' + (d.Size||'?') + ')'; }).join(', ');
|
|
sysEl.innerHTML = '<span class="form-hint">A rendszermeghajtó(k) nem választhatók: ' + sysNames + '</span>';
|
|
sysEl.style.display = 'block';
|
|
}
|
|
}
|
|
|
|
function selectDisk(el, path, needsPartition) {
|
|
// Deselect all
|
|
document.querySelectorAll('[data-path]').forEach(function(d) {
|
|
d.style.border = '2px solid transparent';
|
|
d.querySelector('.storage-path-label').textContent = d.querySelector('.storage-path-label').textContent.replace('● ', '○ ');
|
|
});
|
|
// Select this
|
|
el.style.border = '2px solid var(--accent-blue)';
|
|
el.querySelector('.storage-path-label').textContent = el.querySelector('.storage-path-label').textContent.replace('○ ', '● ');
|
|
|
|
selectedDevice = path;
|
|
document.getElementById('selected-device').value = path;
|
|
document.getElementById('create-partition').value = needsPartition ? 'true' : 'false';
|
|
document.getElementById('selected-device-display').textContent = path;
|
|
|
|
// Update warning text based on whole-disk vs partition-only operation
|
|
var warningEl = document.querySelector('#wizard-configure .alert-error');
|
|
if (needsPartition) {
|
|
warningEl.innerHTML = '<strong>⚠️ FIGYELEM:</strong> A meghajtó <strong>ÖSSZES</strong> adata törlődik!<br>' +
|
|
'Ez a művelet <strong>NEM vonható vissza.</strong>';
|
|
} else {
|
|
warningEl.innerHTML = '<strong>⚠️ FIGYELEM:</strong> A partíció formázva lesz, a rajta lévő adatok törlődnek!<br>' +
|
|
'A rendszermeghajtó többi partíciója <strong>NEM</strong> érintett.';
|
|
}
|
|
|
|
// Show/hide the partitioning progress step
|
|
var partStep = document.getElementById('pstep-partitioning');
|
|
if (partStep) partStep.style.display = needsPartition ? '' : 'none';
|
|
|
|
// Show configure step
|
|
document.getElementById('wizard-configure').style.display = 'block';
|
|
document.getElementById('wizard-configure').scrollIntoView({behavior:'smooth'});
|
|
}
|
|
|
|
function resetWizard() {
|
|
selectedDevice = null;
|
|
document.getElementById('wizard-configure').style.display = 'none';
|
|
document.getElementById('init-error').style.display = 'none';
|
|
document.getElementById('confirm-input').value = '';
|
|
document.getElementById('init-btn').disabled = true;
|
|
}
|
|
|
|
// Enable init button only when confirmation is correct
|
|
document.getElementById('confirm-input').addEventListener('input', function() {
|
|
document.getElementById('init-btn').disabled = (this.value !== 'FORMÁZÁS');
|
|
});
|
|
|
|
document.getElementById('init-form').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
if (document.getElementById('confirm-input').value !== 'FORMÁZÁS') {
|
|
return;
|
|
}
|
|
|
|
var mountName = document.getElementById('mount-name').value.trim();
|
|
var label = document.getElementById('storage-label').value.trim();
|
|
var setDefault = document.getElementById('set-default').checked;
|
|
|
|
if (!mountName) {
|
|
document.getElementById('init-error').textContent = 'A csatlakoztatási nevet meg kell adni.';
|
|
document.getElementById('init-error').style.display = 'block';
|
|
return;
|
|
}
|
|
|
|
document.getElementById('wizard-scan').style.display = 'none';
|
|
document.getElementById('wizard-configure').style.display = 'none';
|
|
document.getElementById('wizard-progress').style.display = 'block';
|
|
document.getElementById('wizard-progress').scrollIntoView({behavior:'smooth'});
|
|
|
|
var body = {
|
|
device_path: selectedDevice,
|
|
mount_name: mountName,
|
|
label: label,
|
|
create_partition: document.getElementById('create-partition').value === 'true',
|
|
set_default: setDefault,
|
|
confirm: 'FORMÁZÁS'
|
|
};
|
|
|
|
fetch('/api/storage/init', {
|
|
method: 'POST',
|
|
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
|
body: JSON.stringify(body)
|
|
}).then(function(r){ return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.ok) {
|
|
showProgressError(data.error || 'Ismeretlen hiba');
|
|
return;
|
|
}
|
|
// Start polling
|
|
pollTimer = setInterval(pollProgress, 1500);
|
|
})
|
|
.catch(function(e) {
|
|
showProgressError('Hálózati hiba: ' + e.message);
|
|
});
|
|
});
|
|
|
|
var stepOrder = ['validating','partitioning','formatting','mounting','permissions','done'];
|
|
|
|
function pollProgress() {
|
|
fetch('/api/storage/init/status')
|
|
.then(function(r){ return r.json(); })
|
|
.then(function(data) {
|
|
if (!data.ok) return;
|
|
updateProgressUI(data);
|
|
if (data.done) {
|
|
clearInterval(pollTimer);
|
|
if (data.step === 'done') {
|
|
showDone('/mnt/' + document.getElementById('mount-name').value.trim());
|
|
}
|
|
}
|
|
})
|
|
.catch(function(){});
|
|
}
|
|
|
|
function updateProgressUI(data) {
|
|
// Update step icons
|
|
var currentIdx = stepOrder.indexOf(data.step);
|
|
stepOrder.forEach(function(s, i) {
|
|
var el = document.getElementById('pstep-' + 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' ? '❌' : '⏳';
|
|
} else {
|
|
el.className = 'disk-step';
|
|
icon.textContent = '○';
|
|
}
|
|
});
|
|
|
|
// Progress bar
|
|
var pct = data.pct || 0;
|
|
document.getElementById('progress-fill').style.width = pct + '%';
|
|
document.getElementById('progress-percent').textContent = pct + '%';
|
|
document.getElementById('progress-msg').textContent = data.msg || '';
|
|
|
|
if (data.step === 'error' || data.error) {
|
|
showProgressError(data.error || data.msg || 'Ismeretlen hiba');
|
|
}
|
|
}
|
|
|
|
function showProgressError(msg) {
|
|
clearInterval(pollTimer);
|
|
document.getElementById('progress-error').textContent = 'Hiba: ' + msg;
|
|
document.getElementById('progress-error').style.display = 'block';
|
|
document.getElementById('wizard-progress').querySelector('h3').textContent = 'Inicializálás sikertelen';
|
|
}
|
|
|
|
function showDone(mountPath) {
|
|
document.getElementById('wizard-progress').style.display = 'none';
|
|
document.getElementById('wizard-done').style.display = 'block';
|
|
document.getElementById('done-path').textContent = mountPath;
|
|
document.getElementById('wizard-done').scrollIntoView({behavior:'smooth'});
|
|
}
|
|
</script>
|
|
|
|
{{template "layout_end" .}}
|
|
{{end}}
|