56 lines
7.4 KiB
JavaScript
56 lines
7.4 KiB
JavaScript
'use strict';
|
||
|
||
function identityRow(identity={kind:'serial',value:''}) {
|
||
return `<div class="identity-row" data-identity-row>${field('identity_kind','Identität',identity.kind,{type:'select',options:[{value:'serial',label:'Seriennummer'},{value:'uuid',label:'System-UUID'},{value:'mac',label:'MAC-Adresse'}]})}${field('identity_value','Wert',identity.value,{required:true})}<button type="button" class="icon-button" data-host-action="remove-identity" aria-label="Identität entfernen">×</button></div>`;
|
||
}
|
||
function identitiesEditor(identities=[]) {
|
||
return `<section class="form-section full" data-identities><h3>Hardware erkennen</h3><p class="field-help">Mindestens eine Kennung ordnet diesen Server beim Start zu.</p><div data-identity-rows>${(identities.length?identities:[{kind:'serial',value:''}]).map(identityRow).join('')}</div><button type="button" class="button small" data-host-action="add-identity">${svg('plus')}Kennung hinzufügen</button></section>`;
|
||
}
|
||
function readIdentities(container) {
|
||
const identities=[...container.querySelectorAll('[data-identity-row]')].map(row=>({kind:row.querySelector('[name="identity_kind"]').value,value:row.querySelector('[name="identity_value"]').value.trim()}));
|
||
if(!identities.length||identities.some(item=>!item.value))throw new Error('Bitte mindestens eine vollständige Hardware-Kennung eintragen.');
|
||
return identities;
|
||
}
|
||
function wireHostRows(container) {
|
||
if(container.dataset.hostRowsBound)return;
|
||
container.dataset.hostRowsBound='true';
|
||
container.addEventListener('click',event=>{
|
||
const button=event.target.closest('[data-host-action]');
|
||
if(!button)return;
|
||
event.preventDefault();
|
||
if(button.dataset.hostAction==='remove-identity')button.closest('[data-identity-row]').remove();
|
||
if(button.dataset.hostAction==='add-identity')button.closest('[data-identities]').querySelector('[data-identity-rows]').insertAdjacentHTML('beforeend',identityRow());
|
||
if(button.dataset.hostAction==='remove-host')button.closest('[data-import-host]').remove();
|
||
if(button.dataset.hostAction==='add-host')container.querySelector('[data-import-hosts]').insertAdjacentHTML('beforeend',importHostRow());
|
||
});
|
||
}
|
||
async function graphicalHostForm(existing=null,discovery=null) {
|
||
const [profiles,isos,secrets]=await Promise.all([api('/profiles'),api('/iso-records'),api('/secrets')]);
|
||
const h=existing||discovery||{};
|
||
const withMissing=(items,id)=>id&&!items.some(item=>item.id===id)?[...items,{id,name:'Bestehende Zuordnung (nicht mehr verfügbar)'}]:items;
|
||
const fields=`<section class="form-section full"><h3>Server</h3><div class="form-grid">${field('fqdn','Vollständiger Hostname (FQDN)',h.fqdn,{required:true,placeholder:'pve-01.example.net'})}${field('site','Standort',h.site,{required:true,placeholder:'Rechenzentrum Berlin'})}${field('management_ip','Management-IP mit Präfix',h.management_ip,{placeholder:'192.0.2.10/24',hint:'Zum Erfassen optional; vor der Installation erforderlich.',full:true})}${dataEditor('host-tags',arr(h.tags),{label:'Tags',schema:{type:'array',items:{type:'string'}}})}</div></section>${identitiesEditor(arr(h.identities))}<section class="form-section full"><h3>Installation zuweisen</h3><div class="form-grid">${field('installation_profile_id','Installationsprofil',h.installation_profile_id,{type:'select',options:selectObjects(withMissing(arr(profiles).filter(p=>p.kind==='installation'&&p.status==='published'),h.installation_profile_id),'Noch nicht zuweisen')})}${field('postinstall_profile_id','Postinstallationsprofil',h.postinstall_profile_id,{type:'select',options:selectObjects(withMissing(arr(profiles).filter(p=>p.kind==='postinstall'&&p.status==='published'),h.postinstall_profile_id),'Noch nicht zuweisen')})}${field('iso_id','Installationsmedium',h.iso_id,{type:'select',full:true,options:selectObjects(withMissing(arr(isos),h.iso_id),'Noch nicht zuweisen')})}</div></section><details class="form-section full"><summary>Abweichende Einstellungen für diesen Server</summary><p class="field-help">Ohne Abweichung gelten die Werte des zugewiesenen Profils.</p>${installationEditor(h.overrides||{},{prefix:'host-override',override:true,secrets:arr(secrets)})}</details>`;
|
||
showModal(existing?'Server bearbeiten':'Server hinzufügen',form(fields,existing?'Änderungen speichern':'Server anlegen'),async data=>{
|
||
const body={fqdn:data.get('fqdn'),site:data.get('site'),management_ip:data.get('management_ip')||null,tags:readDataEditor(modal,'host-tags'),identities:readIdentities(modal),installation_profile_id:data.get('installation_profile_id')||null,postinstall_profile_id:data.get('postinstall_profile_id')||null,iso_id:data.get('iso_id')||null,overrides:readInstallationEditor(modal,'host-override',h.overrides||{})};
|
||
if(existing){for(const key of Object.keys(body))if(JSON.stringify(body[key])===JSON.stringify(existing[key]??null))delete body[key];if(!Object.keys(body).length){closeModal();toast('Keine Änderungen vorhanden.');return;}body.expected_version=existing.version;}
|
||
await api(existing?`/hosts/${encodeURIComponent(existing.id)}`:'/hosts',{method:existing?'PATCH':'POST',body});closeModal();toast(existing?'Server aktualisiert.':'Server wurde angelegt.');await refresh();
|
||
},'INVENTAR');
|
||
wireDataEditors(modal);
|
||
wireHostRows(modal);
|
||
wireInstallationEditor(modal,'host-override');
|
||
}
|
||
function importHostRow() {
|
||
return `<section class="form-section" data-import-host><div class="section-heading"><h3>Server</h3><button type="button" class="icon-button" data-host-action="remove-host" aria-label="Server entfernen">×</button></div><div class="form-grid">${field('import_fqdn','Vollständiger Hostname','',{required:true,placeholder:'pve-01.example.net'})}${field('import_ip','Management-IP mit Präfix','',{placeholder:'192.0.2.10/24'})}${identitiesEditor()}</div></section>`;
|
||
}
|
||
async function graphicalHostImport() {
|
||
const [profiles,isos]=await Promise.all([api('/profiles'),api('/iso-records')]);
|
||
const fields=field('site','Gemeinsamer Standort','',{required:true,full:true})+field('installation_profile_id','Installationsprofil','',{type:'select',options:selectObjects(arr(profiles).filter(p=>p.kind==='installation'&&p.status==='published'),'Noch nicht zuweisen')})+field('postinstall_profile_id','Postinstallationsprofil','',{type:'select',options:selectObjects(arr(profiles).filter(p=>p.kind==='postinstall'&&p.status==='published'),'Noch nicht zuweisen')})+field('iso_id','Installationsmedium','',{type:'select',full:true,options:selectObjects(arr(isos),'Noch nicht zuweisen')})+`<div class="full stack" data-import-hosts>${importHostRow()}</div><div class="full"><button type="button" class="button" data-host-action="add-host">${svg('plus')}Weiteren Server hinzufügen</button></div>`;
|
||
showModal('Mehrere Server erfassen',form(fields,'Server anlegen'),async data=>{
|
||
const rows=[...modal.querySelectorAll('[data-import-host]')];
|
||
if(!rows.length||rows.length>100)throw new Error('Bitte zwischen einem und 100 Servern erfassen.');
|
||
const common={site:data.get('site'),installation_profile_id:data.get('installation_profile_id')||null,postinstall_profile_id:data.get('postinstall_profile_id')||null,iso_id:data.get('iso_id')||null};
|
||
const hosts=rows.map(row=>({...common,fqdn:row.querySelector('[name="import_fqdn"]').value,management_ip:row.querySelector('[name="import_ip"]').value||null,identities:readIdentities(row)}));
|
||
await api('/hosts/import',{method:'POST',body:hosts});closeModal();toast(`${hosts.length} Server angelegt.`);await refresh();
|
||
},'SERVERINVENTAR');
|
||
wireHostRows(modal);
|
||
}
|