Backend (5→7 files): - api_test.py: 622→479, terraform functions → operations/terraform.py (142) - api_scenario.py: 241→split into run (153) + defs (108) with _validate_steps() - db/scenario_defs.py: +client_id, stand, is_seed in list_definitions Frontend (1→10 files): - app.js: 554→16 (loader), split into: utils.js (45), instances.js (89), operations.js (249), history.js (35), scenario-list.js (90) - New CRUD: scenario-form.js (128), create (25), edit (12), delete (13) Max file size: JS 249, PY 479
46 lines
1.7 KiB
JavaScript
46 lines
1.7 KiB
JavaScript
// utils.js — общие хелперы: _esc, busy lock, validateJson, лог-панель
|
|
|
|
function _esc(s){
|
|
/* HTML-экранирование: " → " & → & < → < */
|
|
return String(s||'').replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<');
|
|
}
|
|
|
|
function validateJson(el,quiet){
|
|
/* Проверить что значение в поле — валидный JSON.
|
|
quiet=true — не менять внешний вид (для batch-проверки перед отправкой). */
|
|
const errEl=el.parentElement.querySelector('.json-err');
|
|
const v=el.value.trim();
|
|
if(!v) return true;
|
|
try{ JSON.parse(v); }
|
|
catch(e){
|
|
if(!quiet&&errEl){ errEl.style.display='inline'; errEl.textContent='Ошибка JSON: '+e.message; }
|
|
el.style.borderColor='var(--destructive)';
|
|
return false;
|
|
}
|
|
if(!quiet&&errEl) errEl.style.display='none';
|
|
el.style.borderColor='';
|
|
return true;
|
|
}
|
|
|
|
// === Панель логов ===
|
|
let logPollTimer=null;
|
|
function startLogPoll(){
|
|
if(logPollTimer) return;
|
|
logPollTimer=setInterval(async()=>{
|
|
const el=document.getElementById('log-panel');
|
|
if(!el||el.style.display==='none') return;
|
|
try{
|
|
const r=await fetch('/api/log');
|
|
const lines=await r.json();
|
|
el.innerHTML=lines.map(l=>`<div>${_esc(l)}</div>`).join('');
|
|
el.scrollTop=el.scrollHeight;
|
|
}catch(e){}
|
|
},2000);
|
|
}
|
|
function toggleLog(){
|
|
const el=document.getElementById('log-panel');
|
|
if(!el) return;
|
|
if(el.style.display==='none'){ el.style.display='block'; startLogPoll(); }
|
|
else el.style.display='none';
|
|
}
|