/* init.js — инициализация namespace, утилиты таймера и статуса */ var _initPolling = false; var _initTickBusy = false; var _initStartedAt = 0; function initLog(msg) { var el = document.getElementById('init-log'); var ts = new Date().toLocaleTimeString('ru-RU'); el.textContent += '[' + ts + '] ' + msg + '\n'; el.scrollTop = el.scrollHeight; } function formatSeconds(totalSeconds) { totalSeconds = Math.max(0, Number(totalSeconds) || 0); var minutes = Math.floor(totalSeconds / 60); var seconds = totalSeconds % 60; if (minutes <= 0) return seconds + 'с'; return minutes + 'м ' + seconds + 'с'; } function setInitElapsed(seconds) { document.getElementById('init-elapsed').textContent = formatSeconds(seconds); } function setInitCurrentStage(text) { document.getElementById('init-current-stage').textContent = text || 'Ожидание старта'; } function setBox(id, text) { var el = document.getElementById(id); if (!el) return; if (!text) { el.style.display = 'none'; el.textContent = ''; return; } el.style.display = 'block'; el.textContent = text; } function missingKeys(obj) { return Object.keys(obj || {}).filter(function (key) { return !obj[key]; }); } function firstPendingStage(stages) { var list = stages || []; for (var i = 0; i < list.length; i++) { if (!list[i].done) return list[i].name; } return list.length ? list[list.length - 1].name : 'Ожидание старта'; } function describeInitReason(debug) { if (!debug) return 'Собираем диагностические данные по namespace и control-plane...'; if (!debug.exists) return 'Ещё не создан Kubernetes namespace пользователя.'; if (!debug.active) return 'Namespace создан, но Kubernetes ещё не перевёл его в состояние Active.'; var preparation = debug.preparation || {}; if (!preparation.managedByConsole) return 'Namespace существует, но ещё не помечен как managed-by=fission-console.'; if (!preparation.watcherLabel) return 'Namespace ещё не помечен label fission.io/managed=true для NSWatcher.'; var missingSAs = missingKeys(preparation.serviceAccounts); if (missingSAs.length) return 'Не готовы bootstrap service accounts: ' + missingSAs.join(', ') + '.'; var missingRBs = missingKeys(preparation.roleBindings); if (missingRBs.length) return 'Не готовы bootstrap role bindings: ' + missingRBs.join(', ') + '.'; var control = debug.controlPlane || {}; if (!control.executor || !control.executor.ready) { return 'Executor ещё не ready. Возможна задержка из-за cold start, image pull или specialization.'; } if (!control.router || !control.router.ready) { return 'Router ещё не ready. HTTP маршрутизация пока не активна.'; } return 'Ожидаем финальное подтверждение готовности control-plane.'; } function summarizeInit(status, debug) { var done = status && status.stages ? status.stages.filter(function (s) { return s.done; }).length : 0; var namespace = debug && debug.namespace ? debug.namespace : 'не определён'; return 'Namespace: ' + namespace + '. Завершено шагов: ' + done + '/3.'; } function explainDelay(seconds) { if (seconds < 5) return 'Обычная операция без заметной задержки.'; if (seconds < 10) return 'Вероятен cold start: прогрев пула, image pull или ожидание specialization.'; return 'Задержка выше нормы: возможны cold start, specialization или ожидание готовности control-plane.'; } function startTimedStatus(startText, waitingPrefix, waitingHint) { var startedAt = Date.now(); showStatus(startText, ''); var timer = setInterval(function () { var elapsed = Math.max(1, Math.floor((Date.now() - startedAt) / 1000)); var text = waitingPrefix + ' ' + formatSeconds(elapsed); if (waitingHint) text += ' · ' + waitingHint(elapsed); showStatus(text, ''); }, 1000); return { stop: function (message, kind) { clearInterval(timer); var elapsed = Math.max(0, Math.round((Date.now() - startedAt) / 1000)); var text = message; if (message) text += ' за ' + formatSeconds(elapsed); showStatus(text, kind || ''); } }; } function renderInitStages(stages) { var el = document.getElementById('init-stages'); el.innerHTML = stages.map(function (s) { var icon = s.done ? '✅' : '⏳'; var color = s.done ? '#4caf50' : '#8bc7ff'; return '
' + icon + ' ' + s.name + '
'; }).join(''); } async function pollNSStatus() { if (_initPolling) return; _initPolling = true; _initTickBusy = false; _initStartedAt = Date.now(); var overlay = document.getElementById('init-overlay'); overlay.style.display = 'flex'; document.getElementById('init-log').textContent = ''; setInitElapsed(0); setInitCurrentStage('Подготовка запуска'); setBox('init-summary', 'Проверяем состояние namespace и control-plane...'); setBox('init-reason', 'Если пользователь новый, возможны cold start и создание bootstrap ресурсов.'); setBox('init-error', ''); initLog('Запуск инициализации...'); var attempt = 0; var maxAttempts = 60; // 5 минут var interval = setInterval(async function () { if (_initTickBusy) return; _initTickBusy = true; attempt++; try { var elapsed = Math.floor((Date.now() - _initStartedAt) / 1000); setInitElapsed(elapsed); var statusResp = await fetch(API_BASE + '/ns/status', { headers: authHeaders() }); var debugResp = await fetch(API_BASE + '/ns/debug', { headers: authHeaders() }); var d = await statusResp.json(); var dbg = await debugResp.json().catch(function () { return null; }); if (d.stages) renderInitStages(d.stages); setInitCurrentStage(firstPendingStage(d.stages)); setBox('init-summary', summarizeInit(d, dbg)); setBox('init-reason', describeInitReason(dbg)); setBox('init-error', ''); var done = d.stages ? d.stages.filter(function (s) { return s.done; }).length : 0; initLog('Шагов завершено: ' + done + '/3'); if (d.ready) { initLog('✅ Готово! Загружаем консоль...'); clearInterval(interval); _initPolling = false; _initTickBusy = false; overlay.style.display = 'none'; reloadAll(); return; } } catch (e) { setBox('init-error', 'Последняя ошибка: ' + e.message); initLog('Ошибка опроса: ' + e.message); } finally { _initTickBusy = false; } if (attempt >= maxAttempts) { clearInterval(interval); _initPolling = false; _initTickBusy = false; setBox('init-error', 'Превышено время ожидания. Чаще всего это bootstrap RBAC, control-plane readiness или холодный старт.'); initLog('⚠️ Превышено время ожидания. Попробуйте обновить страницу.'); } }, 5000); }