44 lines
4.2 KiB
JavaScript
44 lines
4.2 KiB
JavaScript
'use strict';
|
|
let operationState=null, operationSending=false;
|
|
function clearOperations(){operationState=null;['operation-status','operation-projects','operation-events'].forEach(id=>$(id).replaceChildren());$('operation-audit').hidden=true;}
|
|
async function changeOperation(data,button){
|
|
if(operationSending)return;const generation=session;operationSending=true;button.disabled=true;
|
|
try{const result=await api('/v1/operations',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)});
|
|
if(generation!==session)return;
|
|
error(result.status==='QUEUED'?'Fortsetzung vorgemerkt. Der Worker prüft die Freigabe vor der Ausführung.':'');
|
|
await refreshOperations();
|
|
}catch(e){if(generation===session)error(`Betriebsaktion fehlgeschlagen: ${e.message}`);}
|
|
finally{operationSending=false;if(generation===session)button.disabled=false;}
|
|
}
|
|
function addRetryControl(data,root){
|
|
if(!operationState?.operator||data.revision!==data.current_revision)return;
|
|
if(data.state==='BLOCKED'&&data.reason==='ENTITY_RESOLUTION_INVALID_RESPONSES'){
|
|
root.append(text('p','Nach einer technischen Korrektur kann ein zusätzlicher Antwortversuch für die Namensauflösung freigegeben werden. Vorhandene Ergebnisse und Untersuchungsbudgets bleiben erhalten. Der Folgeaufruf kann LLM-Kosten verursachen.','hint'));
|
|
const label=text('label','Durchgeführte Fehlerbehebung (keine Schlüssel oder Passwörter)');
|
|
const reason=document.createElement('textarea');reason.maxLength=1000;reason.rows=2;label.append(reason);root.append(label);
|
|
const button=text('button','Einen Reparaturversuch freigeben');button.type='button';
|
|
button.addEventListener('click',()=>{const note=reason.value.trim();if(!note){error('Bitte die durchgeführte Fehlerbehebung beschreiben.');return;}
|
|
changeOperation({action:'repair',key:data.key,package_generation:data.package_generation,reason:note},button);});
|
|
root.append(button);return;
|
|
}
|
|
const retry=data.state==='BLOCKED'?data.reason==='RESEARCH_UNAVAILABLE':!finished.has(data.state)&&['ERROR','ATTENTION','BUSY','DEFERRED','RETRY_PENDING'].includes(data.worker.status);
|
|
if(!retry)return;
|
|
const button=text('button','Fortsetzung nach Fehlerbehebung anfordern');button.type='button';
|
|
button.addEventListener('click',()=>changeOperation({action:'retry',key:data.key,package_generation:data.package_generation},button));root.append(button);
|
|
}
|
|
async function refreshOperations(){
|
|
const generation=session;const result=await api('/v1/operations');if(generation!==session)return;
|
|
const changed=operationState?.operator!==result.operator;operationState=result;
|
|
const status=$('operation-status');status.replaceChildren();
|
|
if(result.worker){const w=result.worker;status.append(text('p',`${w.live?'Worker erreichbar':'Kein lebender Worker bestätigt'} · Letzter Zustand: ${w.status||'Unbekannt'}${w.code?' · '+w.code:''}`));
|
|
if(w.updated_at)status.append(text('p',`Letzte Aktivität: ${new Date(w.updated_at).toLocaleString('de-DE')}`,'hint'));}
|
|
else status.append(text('p',result.operator?'Workerdetails erfordern Zugriff auf alle Projekte.':'Lesender Betriebszugriff. Betreiberaktionen sind nicht freigegeben.','hint'));
|
|
if(result.pending)status.append(text('p',`${result.pending} Fortsetzungsaufträge warten auf den Worker.`));
|
|
const projectsRoot=$('operation-projects');projectsRoot.replaceChildren();
|
|
result.projects.forEach(p=>{const row=text('div','','section-head');row.append(text('p',`${p.project_id} · ${p.paused?'Neue Fälle pausiert':'Neue Fälle freigegeben'}`));
|
|
if(result.operator){const button=text('button',p.paused?'Neue Fälle wieder zulassen':'Neue Fälle pausieren');button.type='button';button.addEventListener('click',()=>changeOperation({action:p.paused?'resume':'pause',project_id:p.project_id},button));row.append(button);}projectsRoot.append(row);});
|
|
$('operation-audit').hidden=!result.operator;const log=$('operation-events');log.replaceChildren();
|
|
result.events.slice().reverse().forEach(e=>log.append(text('p',`${new Date(e.at).toLocaleString('de-DE')} · ${e.actor} · ${e.project_id}${e.case_id?' · '+e.case_id:''} · ${e.action} · ${e.status}${e.code?' · '+e.code:''}`)));
|
|
if(changed&&selectedCase&&location.hash.startsWith('#case/'))renderDetail(selectedCase);
|
|
}
|