// instances.js — список инстансов, выбор сервиса, toggle операций // // Отвечает за: // - Загрузку инстансов выбранного сервиса (GET /api/operations/{svcId}) // - Отображение списка инстансов с фильтром autotest- // - Показ доступных операций для выбранного инстанса // - Кнопки операций (modify, delete, suspend, resume, redeploy) // // Глобальные переменные (используются в operations.js и других модулях): // svcInstances[] — текущий список инстансов // selectedInst — UUID выбранного инстанса (или null) // selectedOp — {opId, opName, svcId} текущая выбранная операция // currentSvcId — ID текущего сервиса // currentSvcName — имя текущего сервиса // currentSvcShort — короткое имя (для генерации displayName) // busy — флаг блокировки UI (пока идёт операция) // AUTOTEST_PREFIX — "autotest-" (фильтр "наших" инстансов) let svcInstances=[], selectedInst=null, selectedOp=null; let currentSvcId=(window.APP&&window.APP.firstServiceId)||1; let currentSvcName=''; let currentSvcShort=''; let busy=false; const AUTOTEST_PREFIX='autotest-'; /** * Выбрать сервис — загрузить его инстансы и показать в списке. * Вызывается при клике на сервис в левой панели. * * @param {number} svcId — ID сервиса */ async function selectService(svcId){ if(busy) return; // блокировка — идёт операция // Сброс предыдущего состояния currentSvcId=svcId; selectedInst=null; selectedOp=null; stopPoll(); // остановить поллинг предыдущей операции // Скрыть панель параметров и этапы document.getElementById('params-area').style.display='none'; document.querySelectorAll('.stages-box').forEach(e=>e.remove()); // Подсветка активного сервиса в списке document.querySelectorAll('.svc-item').forEach(e=>e.classList.remove('active')); const svcEl=document.querySelector(`.svc-item[onclick*="${svcId}"]`); if(svcEl) svcEl.classList.add('active'); try{ // GET /api/operations/{svcId} — возвращает операции + autotest-инстансы const r=await fetch('/api/operations/'+svcId); const d=await r.json(); svcInstances=d.instances||[]; currentSvcName=d.svc||''; currentSvcShort=d.svcShort||''; // Обновить текст кнопки «Создать» const btnSpan=document.querySelector('#create-btn-area span'); if(btnSpan) btnSpan.textContent='+ Создать новый инстанс '+(currentSvcName||'сервиса'); renderInstances(); // отрисовать список }catch(e){ document.getElementById('inst-list').innerHTML= 'Ошибка загрузки'; } } /** * Отрисовать список инстансов в #inst-list. * Каждый инстанс — строка: имя + сервис + статус + created + modified. */ function renderInstances(){ let html=''; svcInstances.forEach(i=>{ // Статус: из нашего поля status (cloud-first) или explainedStatus const status=i.status||i.explainedStatus||'?'; const sc=status==='running'?'badge-success':''; // Строка инстанса — кликабельна html+=`
${_esc(i.displayName)} ${_esc(i.svc||'')} ${_esc(status)} ${relativeTime(i.instanceConfigDtCreated)} ${relativeTime(i.instanceConfigDtModified)}
`; // Контейнер для кнопок операций (изначально скрыт) html+=`
`; }); document.getElementById('inst-list').innerHTML=html; } /** * Обновить список инстансов (без смены сервиса). * Вызывается после завершения операции. */ async function refreshInstances(){ try{ const r=await fetch('/api/operations/'+currentSvcId); const d=await r.json(); svcInstances=d.instances||[]; renderInstances(); }catch(e){} } /** * Раскрыть/скрыть кнопки операций для инстанса. * Загружает доступные операции через GET /api/operations/{svcId}. * * @param {string} iuid — instanceUid */ async function toggleInstance(iuid){ if(busy) return; selectedInst=iuid; stopPoll(); // Подсветка выбранного инстанса document.querySelectorAll('[data-iuid]').forEach(e=> e.classList.toggle('active',e.dataset.iuid===iuid)); // Скрыть панель параметров (если была открыта для другого инстанса) document.getElementById('params-area').style.display='none'; const opsEl=document.getElementById('ops-'+iuid); // Если уже открыт — закрыть (toggle) if(opsEl.classList.contains('open')){opsEl.classList.remove('open');return;} // Закрыть ВСЕ другие открытые панели операций document.querySelectorAll('.inst-ops').forEach(e=>e.classList.remove('open')); // Найти инстанс в списке (может быть из другого сервиса) const inst=svcInstances.find(x=>x.instanceUid===iuid); const svcId=inst?inst.serviceId:currentSvcId; try{ const r=await fetch('/api/operations/'+svcId); const d=await r.json(); // Фильтруем операции: убираем reconcile (техническая) и create (уже создан) let ops=(d.operations||[]).filter(o=>o.operation!=='reconcile'&&o.operation!=='create'); // Для not created инстансов — только delete if(inst&&(inst.status||inst.explainedStatus)==='not created'){ ops=ops.filter(o=>o.operation==='delete'); } // Кнопки операций с цветовой маркировкой opsEl.innerHTML=ops.map(o=> `` ).join(''); opsEl.classList.add('open'); }catch(e){ opsEl.innerHTML='Ошибка загрузки'; opsEl.classList.add('open'); } } /** * CSS-класс для кнопки операции (цветовая маркировка). * * @param {string} opName — "modify"/"delete"/"suspend"/... * @returns {string} CSS-класс */ function opClass(opName){ if(opName==='modify') return 'op-btn-modify'; // синий if(opName==='suspend') return 'op-btn-suspend'; // оранжевый if(opName==='resume') return 'op-btn-resume'; // зелёный if(opName==='delete') return 'op-btn-delete'; // красный if(opName==='redeploy') return 'op-btn-redeploy'; // фиолетовый return ''; }