'use strict'; async function graphicalProfileForm(kind,existing=null) { const p=existing||{}; const install=kind==='installation'; const [secrets,modules]=await Promise.all([api('/secrets'),install?Promise.resolve([]):api('/modules')]); const fields=field('name','Profilname',p.name,{required:true,full:true,placeholder:install?'PVE · Einzelplatte':'PVE · Basiskonfiguration',hint:'Beim Speichern entsteht eine neue Profilversion.'})+ dataEditor('profile-builds',arr(p.target_builds),{label:'Unterstützte Zielbuilds',schema:{type:'array',minItems:1,items:{type:'string',minLength:1}}})+ (install?installationEditor(p.values||{},{prefix:'installation',secrets:arr(secrets)}):postinstallEditor(p,arr(modules).filter(m=>m.status==='published'),arr(secrets)))+ field('reason','Änderungsgrund','',{full:true,placeholder:'Grund für diesen Profilstand'}); showModal(existing?'Neue Profilversion':'Profil erstellen',form(fields,'Entwurf speichern'),async data=>{ const builds=readDataEditor(modal,'profile-builds'); if(!builds.length||builds.some(build=>typeof build!=='string'||!build.trim()))throw new Error('Bitte mindestens einen tatsächlich geprüften Zielbuild angeben.'); const configuration=install?{values:readInstallationEditor(modal,'installation',p.values||{}),steps:[]}:readPostinstallEditor(modal,p); const body={name:data.get('name'),kind,target_builds:builds.map(build=>build.trim()),...configuration,reason:data.get('reason')||''}; if(Object.hasOwn(p,'locked_fields'))body.locked_fields=p.locked_fields; await api('/profiles',{method:'POST',body});closeModal();toast('Profilentwurf gespeichert.');await refresh(); },install?'INSTALLATIONSPROFIL':'POSTINSTALLATIONSPROFIL'); wireDataEditors(modal); if(install)wireInstallationEditor(modal,'installation');else wirePostinstallEditor(modal,arr(modules).filter(m=>m.status==='published'),arr(secrets)); } async function graphicalModuleForm(existing=null) { const m=existing||{}; const [catalog,modules]=await Promise.all([api('/modules/builtin'),api('/modules')]); const templates=arr(catalog); let source=m.source||''; let schema=structuredClone(m.parameters_schema||{type:'object',properties:{},additionalProperties:false}); let previousTemplateName=''; const currentTemplate=templates.find(item=>item.id===m.id||item.source===m.source); const fields=field('name','Modulname',m.name,{required:true,full:true,placeholder:'PVE-Dienste prüfen'})+ `

Modul auswählen

${field('module_template','Vorlage',currentTemplate?.id||'',{type:'select',full:true,options:[{value:'',label:source?'Vorhandenes Skript beibehalten':'Vorlage auswählen oder Skriptdatei laden'},...templates.map(item=>({value:item.id,label:item.name}))]})}

${source?'Skript übernommen.':'Noch kein Skript ausgewählt.'}

Skript ansehen
${esc(source)}
`+ dataEditor('module-builds',arr(m.target_builds),{label:'Unterstützte Zielbuilds',schema:{type:'array',items:{type:'string'}}})+ field('timeout_seconds','Timeout in Sekunden',m.timeout_seconds??300,{type:'number',required:true,min:1,max:7200,full:true})+ `
${moduleSchemaEditor(schema)}
`+ `
${dataEditor('module-dependencies',arr(m.dependencies),{label:'Vorher benötigte Module',schema:{type:'array',items:{type:'string',enum:[...new Set([...arr(modules).map(item=>item.name),...templates.map(item=>item.name),...arr(m.dependencies)])]}}})}
`+ field('retry_safe','Apply darf nach Zustandsprüfung wiederholt werden.',m.retry_safe??false,{type:'checkbox',full:true,hint:'Nur aktivieren, wenn die Wiederholbarkeit nachgewiesen wurde.'})+ field('reason','Änderungsgrund','',{full:true}); showModal(existing?'Neue Modulversion':'Skriptmodul erstellen',form(fields,'Entwurf speichern'),async data=>{ if(!source)throw new Error('Bitte eine Vorlage oder Skriptdatei auswählen.'); const builds=readDataEditor(modal,'module-builds'); if(!builds.length||builds.some(build=>typeof build!=='string'||!build.trim()))throw new Error('Bitte mindestens einen geprüften Zielbuild angeben.'); await api('/modules',{method:'POST',body:{name:data.get('name'),source,parameters_schema:readModuleSchemaEditor(modal,schema),dependencies:readDataEditor(modal,'module-dependencies'),target_builds:builds.map(build=>build.trim()),timeout_seconds:Number(data.get('timeout_seconds')),retry_safe:data.get('retry_safe')==='on',reason:data.get('reason')||''}});closeModal();toast('Modulentwurf gespeichert.');await refresh(); },'VERSIONIERTE SKRIPTMODULE'); wireDataEditors(modal); wireModuleSchemaEditor(modal); const updateSource=label=>{modal.querySelector('[data-source-status]').textContent=label;modal.querySelector('[data-source-preview]').textContent=source;}; modal.querySelector('[name="module_template"]').addEventListener('change',event=>{ const template=templates.find(item=>item.id===event.target.value); if(!template)return; source=template.source; const nameInput=modal.querySelector('[name="name"]'); if(!nameInput.value||nameInput.value===previousTemplateName)nameInput.value=template.name; previousTemplateName=template.name; schema=structuredClone(template.parameters_schema); modal.querySelector('[data-schema-container]').innerHTML=moduleSchemaEditor(schema); modal.querySelector('[data-dependencies-container]').innerHTML=dataEditor('module-dependencies',template.dependencies,{label:'Vorher benötigte Module',schema:{type:'array',items:{type:'string',enum:[...new Set([...arr(modules).map(item=>item.name),...templates.map(item=>item.name)])]}}}); modal.querySelector('[name="timeout_seconds"]').value=template.timeout_seconds; modal.querySelector('[name="retry_safe"]').checked=template.retry_safe; wireModuleSchemaEditor(modal); updateSource(`${template.name}: Vorlage übernommen.`); }); modal.querySelector('[data-module-file]').addEventListener('change',async event=>{ const file=event.target.files[0]; if(!file)return; if(file.size>262144){toast('Die Skriptdatei darf höchstens 256 KiB groß sein.',true);event.target.value='';return;} try{source=await file.text();modal.querySelector('[name="module_template"]').value='';updateSource(`${file.name}: Skript geladen.`);}catch{toast('Die Skriptdatei konnte nicht gelesen werden.',true);} }); }