';
- if (!rows) {
+ var fnList = fns || [];
+ if (fnList.length === 0) {
+ progress.stop('Загружено: нет функций', '');
document.getElementById('fn-rows').innerHTML = '
Нет функций
';
+ } else {
+ // Рендерим первую строку сразу — убираем placeholder
+ var tbody = document.getElementById('fn-rows');
+ tbody.innerHTML = makeFnRow(fnList[0]);
+ // Остальные добавляем по одной с небольшой задержкой
+ var i = 1;
+ function appendNext() {
+ if (i >= fnList.length) {
+ progress.stop('Загружено ' + fnList.length + ' функций', 'ok');
+ return;
+ }
+ var tr = document.createElement('tbody');
+ tr.innerHTML = makeFnRow(fnList[i]);
+ tbody.appendChild(tr.firstChild);
+ i++;
+ setTimeout(appendNext, 40);
+ }
+ setTimeout(appendNext, 40);
}
} catch (e) {
document.getElementById('fn-rows').innerHTML = '
Load error: ' + e.message + '
';
- showStatus('Ошибка загрузки: ' + e.message, 'err');
+ progress.stop('Ошибка загрузки: ' + e.message, 'err');
}
}
diff --git a/doc/descriptions/page-load-flow.md b/doc/descriptions/page-load-flow.md
new file mode 100644
index 0000000..866c931
--- /dev/null
+++ b/doc/descriptions/page-load-flow.md
@@ -0,0 +1,66 @@
+# Загрузка страницы — flow (май 2026)
+
+## Последовательность при открытии/перезагрузке
+
+```
+1. HTML загружен, JS инициализирован
+ │
+ ▼
+2. checkAuth() (auth.js)
+ ├── localStorage["auth_token"] отсутствует → showLoginOverlay() → СТОП
+ └── токен есть → setUserAvatar() → hideLoginOverlay() → reloadAll()
+ │
+ ▼
+3. reloadAll() (app.js)
+ ├── показывает в статусе "Загрузка... Nс" (startTimedStatus, тикает каждую секунду)
+ ├── показывает в таблице "⏳ Загружаем функции..."
+ ├── запускает 5 параллельных запросов (Promise.all):
+ │ GET /console/api/environments
+ │ GET /console/api/packages
+ │ GET /console/api/functions ← обычно самый долгий
+ │ GET /console/api/httptriggers
+ │ GET /console/api/timetriggers
+ └── (ждём ответа... может занять 3–10с при cold start)
+ │
+ ▼
+4. Данные получены
+ ├── обновляем счётчики (envs, pkgs, fns, http, cron)
+ ├── рендерим ПЕРВУЮ строку функций немедленно
+ ├── остальные строки добавляем по одной каждые 40мс (setTimeout)
+ └── по завершении статус: "Загружено N функций за Xс"
+```
+
+## Путь через doLogin() (при ручном входе)
+
+После успешного POST /console/api/auth:
+1. Сохраняем токен в localStorage
+2. Проверяем GET /console/api/ns/status
+ - `ready: false` → `pollNSStatus()` → показывается init-overlay с прогрессом по стадиям
+ - `ready: true` → сразу `reloadAll()`
+
+## Почему долго
+
+При первой загрузке после простоя:
+- Fission `poolmgr` может не иметь прогретых пулов → холодный старт на first request к каждому language env
+- Kubernetes API (~k8s list всех функций, пакетов) — несколько сотен мс при большом числе объектов
+- `Promise.all` ждёт самый долгий запрос
+
+## Что показывается пользователю (v1.3.34+)
+
+| Момент | Что видит |
+|---|---|
+| Сразу при запуске | Таблица: "⏳ Загружаем функции..." |
+| Каждую секунду | Статус: "Загрузка данных Nс" |
+| Первая функция пришла | Первая строка таблицы появляется |
+| +40мс × N | Строки добавляются одна за другой |
+| Конец | Статус: "Загружено N функций за Xс" |
+
+## Файлы
+
+| Файл | Роль |
+|---|---|
+| `ui/js/auth.js` → `checkAuth()` | Точка входа при загрузке страницы |
+| `ui/js/auth.js` → `doLogin()` | Ручной вход, проверка NS status |
+| `ui/js/app.js` → `reloadAll()` | Загрузка данных, построчный рендер |
+| `ui/js/init.js` → `pollNSStatus()` | Ожидание готовности namespace (init overlay) |
+| `ui/js/init.js` → `startTimedStatus()` | Таймер в статус-баре |