R-280: attach list from mounted-but-unregistered filesystems; two-clicks promise made conditional
gates / gates (push) Successful in 17s
gates / gates (push) Successful in 17s
After a reinstall the data drive could not be re-attached through any dashboard route: both candidate lists came from the agent's unclaimed-disk scan, and the rebuilt box's drives are claimed. The restore page said it was two clicks while pointing at an empty picker. The attach list now also carries the controller's own mounted-but-unregistered filesystems. initialize is untouched, so the format wizard's system/backup protection is unchanged. The 'two clicks' sentence is conditional on the picker being non-empty, and says something true and actionable when it is not.
This commit is contained in:
@@ -23,7 +23,9 @@
|
||||
<label>Kiválasztott eszköz</label>
|
||||
<span class="settings-value mono" id="sel-device">—</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<!-- R-280: an already-mounted store has no mount name to choose — it is already at its path,
|
||||
and the action is to register that path. The group is hidden for those. -->
|
||||
<div class="form-group" id="mount-name-group">
|
||||
<label for="mount-name">Csatlakoztatási név <span class="required">*</span></label>
|
||||
<div style="display:flex;align-items:center;gap:.25rem">
|
||||
<span class="mono" style="opacity:.6">/mnt/</span>
|
||||
@@ -32,6 +34,10 @@
|
||||
</div>
|
||||
<span class="form-hint">A meghajtó a /mnt/<név> útvonalra kerül.</span>
|
||||
</div>
|
||||
<p class="form-hint" id="mounted-note" style="display:none">
|
||||
Ez a meghajtó már csatlakoztatva van, csak a gép nem tartja nyilván. A „Csatolás" a
|
||||
meglévő helyén veszi nyilvántartásba — <strong>semmi nem törlődik</strong>.
|
||||
</p>
|
||||
<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">
|
||||
@@ -49,7 +55,7 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var selDevice = "", selFSType = "";
|
||||
var selDevice = "", selFSType = "", selMounted = false;
|
||||
function esc(s){ return String(s==null?'':s).replace(/[&<>"]/g,function(c){return {'&':'&','<':'<','>':'>','"':'"'}[c];}); }
|
||||
function fmtSize(b){
|
||||
if(!b) return '';
|
||||
@@ -74,11 +80,15 @@ async function loadDisks(){
|
||||
if(d.fstype) parts.push(esc(d.fstype));
|
||||
var sub = parts.join(' · ');
|
||||
// Attach the FS-bearing node (mount_source, e.g. /dev/sdd1); the agent resolves its UUID.
|
||||
// R-280: for an already-mounted store mount_source is its MOUNTPOINT (/mnt/sys_drive) and the
|
||||
// action is register-in-place, so the card is labelled by the path the customer will see.
|
||||
var dev = d.mount_source || d.device;
|
||||
var mounted = !!d.already_mounted;
|
||||
if(mounted){ title = esc(d.mount_source); sub = [esc(d.device), esc(d.fstype)].filter(Boolean).join(' · '); }
|
||||
html+='<label class="drive-card role-user-data is-selectable" id="dc-'+i+'">'
|
||||
+'<div class="drive-card-top"><div class="drive-select"><input type="radio" name="disk" value="'+esc(dev)+'" data-fs="'+esc(d.fstype)+'" data-i="'+i+'" onchange="pickDisk(this)">'
|
||||
+'<div class="drive-card-top"><div class="drive-select"><input type="radio" name="disk" value="'+esc(dev)+'" data-fs="'+esc(d.fstype)+'" data-i="'+i+'" data-mounted="'+(mounted?'1':'')+'" onchange="pickDisk(this)">'
|
||||
+'<div class="drive-id"><span class="drive-name">'+title+'</span><span class="drive-sub">'+sub+'</span></div></div>'
|
||||
+'<div class="drive-badges"><span class="badge badge-ok">Fájlrendszer: '+esc(d.fstype)+'</span></div></div></label>';
|
||||
+'<div class="drive-badges">'+(mounted?'<span class="badge badge-ok">Már csatlakoztatva</span>':'<span class="badge badge-ok">Fájlrendszer: '+esc(d.fstype)+'</span>')+'</div></div></label>';
|
||||
});
|
||||
html+='</div>';
|
||||
document.getElementById('disk-list').innerHTML=html;
|
||||
@@ -87,7 +97,14 @@ async function loadDisks(){
|
||||
|
||||
function pickDisk(radio){
|
||||
selDevice=radio.value; selFSType=radio.getAttribute('data-fs')||"";
|
||||
selMounted=!!radio.getAttribute('data-mounted');
|
||||
document.getElementById('sel-device').textContent=selDevice;
|
||||
// An already-mounted store keeps its own path; there is no name to pick. Dropping `required` too,
|
||||
// or the hidden empty field blocks form submission with no visible cause.
|
||||
var mng=document.getElementById('mount-name-group'), mni=document.getElementById('mount-name');
|
||||
mng.style.display = selMounted ? 'none' : '';
|
||||
mni.required = !selMounted;
|
||||
document.getElementById('mounted-note').style.display = selMounted ? '' : 'none';
|
||||
document.querySelectorAll('.drive-card').forEach(function(c){c.classList.remove('is-picked');});
|
||||
var card=document.getElementById('dc-'+radio.getAttribute('data-i')); if(card) card.classList.add('is-picked');
|
||||
document.getElementById('cfg-card').style.display='block';
|
||||
@@ -99,12 +116,22 @@ async function submitAttach(ev){
|
||||
var btn=document.getElementById('attach-btn'); var out=document.getElementById('attach-result');
|
||||
btn.disabled=true; out.innerHTML='<p class="form-hint">Csatlakoztatás folyamatban…</p>';
|
||||
try{
|
||||
var body={device:selDevice, fstype:selFSType, mount_name:document.getElementById('mount-name').value,
|
||||
label:document.getElementById('storage-label').value, set_default:document.getElementById('set-default').checked};
|
||||
var r=await fetch('/api/storage/attach',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify(body)});
|
||||
// R-280: an already-mounted store is REGISTERED in place. Sending it to /api/storage/attach would
|
||||
// ask the agent to mount an in-guest path as if it were a raw device.
|
||||
var url, body;
|
||||
if(selMounted){
|
||||
url='/api/storage/register-mounted';
|
||||
body={path:selDevice, label:document.getElementById('storage-label').value,
|
||||
set_default:document.getElementById('set-default').checked};
|
||||
}else{
|
||||
url='/api/storage/attach';
|
||||
body={device:selDevice, fstype:selFSType, mount_name:document.getElementById('mount-name').value,
|
||||
label:document.getElementById('storage-label').value, set_default:document.getElementById('set-default').checked};
|
||||
}
|
||||
var r=await fetch(url,{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify(body)});
|
||||
var j=await r.json();
|
||||
if(!j.ok){ throw new Error(j.error||'Hiba'); }
|
||||
out.innerHTML='<div class="alert alert-success">A meghajtó sikeresen csatolva és regisztrálva: <strong class="mono">'+(j.data.where||'')+'</strong>. <a href="/settings">Vissza a Beállításokhoz →</a></div>';
|
||||
out.innerHTML='<div class="alert alert-success">A meghajtó sikeresen '+(selMounted?'nyilvántartásba véve':'csatolva és regisztrálva')+': <strong class="mono">'+(j.data.where||'')+'</strong>. <a href="/settings">Vissza a Beállításokhoz →</a></div>';
|
||||
}catch(e){ out.innerHTML='<div class="alert alert-error">Hiba: '+e.message+'</div>'; btn.disabled=false; }
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user