// operations.js — запуск операций: create, modify, delete, poll, stages let pollTimer=null; function makeCreateDisplayName(){ const rand = (Math.random() * 46656 | 0).toString(36).padStart(3, '0'); return currentSvcShort ? currentSvcShort+'-'+rand : rand; } function findInstName(){ const i=svcInstances.find(x=>x.instanceUid===selectedInst); return i?i.displayName:selectedInst; } function stopPoll(){ if(pollTimer){clearInterval(pollTimer);pollTimer=null;} } async function startCreate(){ if(busy) return; selectedInst=null; stopPoll(); document.querySelectorAll('[data-iuid]').forEach(e=>e.classList.remove('active')); document.querySelectorAll('.inst-ops').forEach(e=>e.classList.remove('open')); try{ const r=await fetch('/api/operations/'+currentSvcId); const d=await r.json(); const createOp=d.operations.find(o=>o.operation==='create'); const opId=createOp?createOp.svcOperationId:18; showParams(opId,'create'); }catch(e){ showParams(18,'create'); } } function runOp(opName,opId){ if(busy) return; stopPoll(); showParams(opId,opName); } function showParams(opId,opName){ selectedOp={opId,opName,svcId:currentSvcId}; document.getElementById('params-area').style.display='block'; document.querySelectorAll('.stages-box').forEach(e=>e.remove()); document.getElementById('test-status').textContent=''; const isCreate=opName==='create'; const qs=isCreate?'' : `?instanceUid=${selectedInst}`; fetch('/api/params/'+opId+qs).then(r=>r.json()).then(async data=>{ if(data.error){document.getElementById('params-form').innerHTML=`Ошибка: ${_esc(data.error)}`;return;} const params=data.params||data; if(!Array.isArray(params)){document.getElementById('params-form').innerHTML=`Неверный формат параметров`;return;} let allInst=[]; const needRefs=params.some(p=>p.refSvcId); if(needRefs){ try{const r=await fetch('/api/instances/list');const list=await r.json();allInst=list||[];}catch(e){} } const form=document.getElementById('params-form'); const displayNameValue=isCreate?makeCreateDisplayName():''; const createHeader=isCreate?`
Создание: ${_esc(currentSvcName)}
`:''; const opHeader=!isCreate?`
${_esc(opName)} → ${_esc(findInstName())}
`:''; form.innerHTML=createHeader+opHeader+(opName==='create'?`
${AUTOTEST_PREFIX}
`:'') + params.map(p=>renderParamRow(p,allInst)).join(''); const btn=document.getElementById('btn-test'); btn.style.display='inline-flex'; btn.textContent='Запустить'; btn.onclick=()=>{ let bad=[]; document.querySelectorAll('#params-form [data-type="map"]').forEach(el=>{if(!validateJson(el,true)) bad.push(el);}); if(bad.length>0){ document.getElementById('test-status').innerHTML=`Ошибка ${bad.length} полей с невалидным JSON — поправьте и нажмите Запустить снова`; return; } const pp=collectParams(); executeOp(pp); }; }).catch(e=>{ document.getElementById('params-form').innerHTML=`Ошибка загрузки: ${_esc(e.message)}`; }); } // renderParamRow, renderMapFixedRow, collectParams — теперь в params-render.js (общий модуль) function setFinishedState(statusText, statusClass, statusError, statusDuration, instanceName){ busy=false; document.querySelectorAll('.inst-ops.open').forEach(e=>e.classList.remove('open')); const btn=document.getElementById('btn-test'); btn.disabled=false; btn.textContent='Готово'; btn.onclick=null; const opName=selectedOp?.opName||'операция'; const durationText=statusDuration!==undefined&&statusDuration!==null?`${statusDuration}s`:'0s'; const extra=_esc(statusError||''); const instName=_esc(instanceName||findInstName()||selectedInst||''); document.getElementById('test-status').innerHTML=` ${statusText} ${opName} ${durationText} ${instName} ${extra}`; } function setRunningState(opName, displayName){ const btn=document.getElementById('btn-test'); btn.textContent=opName+'...'; document.getElementById('test-status').textContent=' ⏳ '+ (displayName||opName) +' выполняется...'; } async function executeOp(params){ busy=true; stopPoll(); const isCreate=selectedOp?.opName==='create'; const displayNameInput=document.getElementById('param-displayname'); const displayNameSuffix=(displayNameInput?.value||'').trim(); const displayName=isCreate ? `${AUTOTEST_PREFIX}${displayNameSuffix || makeCreateDisplayName()}` : findInstName(); document.getElementById('params-area').style.display='block'; document.getElementById('params-form').innerHTML=''; const btn=document.getElementById('btn-test'); btn.style.display='inline-flex'; btn.textContent=selectedOp.opName+'...'; btn.disabled=true; setRunningState(selectedOp.opName, displayName); const instId=selectedInst||''; const afterEl=instId?document.getElementById('ops-'+instId):null; document.querySelectorAll('.stages-box').forEach(e=>e.remove()); const stagesBox=document.createElement('div'); stagesBox.className='stages-box'; stagesBox.style.cssText='max-height:200px;overflow-y:auto;padding:4px 8px;margin-left:16px;background:var(--brand-grey-light);border-radius:4px;'; if(afterEl){afterEl.after(stagesBox);} else{document.getElementById('create-btn-area').after(stagesBox);} try{ const r=await fetch('/api/test',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ serviceId:currentSvcId, operation:selectedOp.opName, svcOperationId:selectedOp.opId, params, instanceUid:selectedInst||'', displayName:isCreate?displayName:'' })}); const d=await r.json(); if(d.status==='FAIL'){busy=false;document.getElementById('test-status').innerHTML=` FAIL ${_esc(d.error||'')}`;btn.disabled=false;return;} if(d.status==='OK'&&(d.opUid||'').startsWith('cmdb-')){ busy=false; setFinishedState('OK','badge-success','',0,d.displayName); try{await refreshInstances();}catch(e){} if(historyOpen) try{await loadHistory();}catch(e){} return; } const opUid=d.opUid; const instanceName=d.displayName||displayName; let _pollErrors=0; pollTimer=setInterval(async()=>{ try{ const sr=await fetch('/api/test/status/'+opUid); const sd=await sr.json(); _pollErrors=0; showStages(sd.stages||[]); if(sd.status!=='RUNNING'){ stopPoll(); setFinishedState(sd.status, sd.status==='OK'?'badge-success':'badge', sd.error||sd.errorLog, sd.duration, sd.displayName||instanceName); await refreshInstances(); if(historyOpen) await loadHistory(); } }catch(e){ _pollErrors++; if(_pollErrors>=5){stopPoll();setFinishedState('TIMEOUT','','Связь потеряна',0,instanceName);} } },2000); }catch(e){ busy=false; document.getElementById('test-status').textContent=' Ошибка: '+e.message; btn.disabled=false; } } function showStages(stages){ if(!stages||!stages.length) return; let html='
Этапы
'; stages.forEach(s=>{ const done=!!s.dtFinish; const icon=done?(s.isSuccessful?'✅':'❌'):'⏳'; html+=`
${icon} ${_esc(s.stage)} — ${(s.duration||0).toFixed(1)}s
`; }); const boxes=document.querySelectorAll('.stages-box'); if(boxes.length) boxes[boxes.length-1].innerHTML=html; }