Files
Proxmox-AIS-Server/provisioner/static/host-forms.js
T
BartelLuis 7b979e6243
CI / javascript-check (push) Successful in 14s
CI / container-policy (push) Successful in 4s
CI / python-tests (push) Successful in 1m48s
CI / container-verify (push) Skipped
CI / container-publish (push) Successful in 49s
feat(ui): replace configuration editors with graphical forms
2026-09-14 21:52:39 +02:00

56 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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);
}