Files
dashboard/doc/CHAT_CONTEXT_FULL.md

35 KiB
Raw Permalink Blame History

Dashboard Service - Full Chat Context For Next Agent

Updated: 2026-04-16 Owner: naeel

1. Что это за сервис

Cloud Dashboard - отдельный веб-сервис (backend + frontend), который показывает инстансы облака, фильтрацию, детали, историю операций и граф зависимостей.

Текущая реализация:

  • Backend: Python + FastAPI
  • Frontend: статический SPA (HTML/CSS/vanilla JS)
  • Граф: Cytoscape
  • Runtime: контейнер в Kubernetes

2. Бизнес-цель

Сделать рабочий дашборд для повседневной эксплуатации:

  • быстрый вход по API token
  • фильтрация по статусам
  • понятная таблица и карточки статистики
  • просмотр параметров/зависимостей инстанса
  • визуализация зависимостей
  • стабильная работа в PROD

3. Ключевые сделанные фичи

3.1 Аутентификация и сессия

  • Логин по API token.
  • Токен сохраняется в localStorage.
  • Logout очищает токен.

3.2 Выбор стенда (ВАЖНО)

Сделано переключение окружений на экране логина:

Как работает:

  • выбранный стенд сохраняется в localStorage как deck_env
  • frontend отправляет в backend заголовок X-Deck-Env
  • backend выбирает base URL по whitelist
  • если значение некорректное, fallback на test

3.3 Таблица и UX

  • Фильтры статусов с чекбоксами (увеличены).
  • Sticky header и правильный скролл области таблицы.
  • Сортировки по колонкам.
  • Статистика по статусам.
  • Detail row с вкладками: параметры, история, зависимости, граф.

3.4 Зависимости и графы

Добавлены два режима:

  • граф для конкретного инстанса
  • общий граф для running

Оптимизации:

  • lazy-load графа (не считать сразу)
  • кэш графа на backend с TTL
  • клиентская подвыборка subgraph
  • индикаторы загрузки
  • open large graph in modal/fullscreen behavior при большом числе узлов

UI графа:

  • прямоугольные узлы
  • более крупный и читаемый текст
  • улучшенные стрелки/контраст
  • упрощенный layout (grid-подход)

3.5 Визуал и брендинг

  • day/night переключатель темы с сохранением в localStorage
  • favicon/logo из docs assets
  • удалены лишние декоративные элементы

4. Техническая архитектура

Browser

  • SPA рендерит UI
  • вызывает backend по префиксу /dashboard/api
  • хранит deck_token и deck_env в localStorage

Backend (FastAPI)

Главные маршруты:

  • GET /api/instances
  • GET /api/instances/{uid}
  • GET /api/graph

Транспорт:

  • принимает X-Deck-Token
  • принимает X-Deck-Env
  • проксирует запросы в Deck API выбранного окружения

Граф:

  • собирается из списка инстансов и деталей
  • edges из dependencies/dependentInstances
  • кэш in-memory по ключу token+env+statuses

Kubernetes

  • deployment/service/ingress в namespace terra
  • публичный доступ через /dashboard

5. Эксплуатационные детали

  • Сервис не использует БД.
  • Основная интеграция backend - только с Deck API.
  • Узкое место в производительности обычно внешний API, не сам UI.
  • В проекте уже обсужден путь: сначала стабилизировать логику на Python, потом при необходимости переписать backend на Go с сохранением API контрактов.

6. История решений (сжатая)

  1. Сначала починили критичные JS ошибки и нестабильный логин.
  2. Исправили фильтры статусов и прокинули корректный parsing на backend.
  3. Переделали скролл + sticky элементы.
  4. Добавили графы зависимостей, затем несколько итераций UX/производительности.
  5. Сделали lazy загрузку и кэш.
  6. Подчистили визуал (читаемость, стрелки, тема, favicon/logo).
  7. Реализовали выбор стенда PROD/DEV/TEST на логине с end-to-end маршрутизацией.
  8. Вынесли сервис в новую репу-папку dashboard и добавили правила агента + gitignore.

7. Что уже перенесено в новую репу dashboard

Из старого пути cloud-dashboard в новый dashboard перенесены:

  • Dockerfile
  • main.py
  • requirements.txt
  • static/
  • k8s/

Дополнительно создано:

  • .github/copilot-instructions.md (скопированы правила агента)
  • .gitignore (Python/кэши/локальные артефакты)

8. Что важно для нового агента

  1. Не ломать контракт frontend/backend без явного запроса.
  2. Заголовки X-Deck-Token и X-Deck-Env обязательны.
  3. Поддерживать 3 стенда через whitelist.
  4. Не тащить секреты/токены в git.
  5. Для графа сохранять lazy-load и кэш (иначе будет тяжелая загрузка).

9. Рекомендованный план ближайших шагов

  1. Сделать первичный commit в новой репе dashboard.
  2. Настроить CI:
    • lint
    • build image
    • push image
    • deploy (опционально)
  3. Добавить smoke tests:
    • /api/instances с разными env
    • /api/graph с фильтрами
  4. Подготовить migration checklist для возможного будущего Go backend.

10. Definition of Done (текущее состояние)

Сервис:

  • работает в k8s
  • логин и фильтры стабильны
  • графы и тема работают
  • выбор стенда реализован и сохраняется
  • код перенесен в новую папку/repo baseline

11. Текущие задачи (v9.7 — в работе)

Все 4 задачи согласованы с оператором. Реализовывать в таком порядке:

Задача 1 — Стриминговая загрузка инстансов

  • Backend /api/instances/stream уже есть (NDJSON, main.py строка ~297)
  • Фронт ещё использует старый /api/instances (index.html строка ~1004)
  • Нужно: переключить фронт на /api/instances/stream, строки добавлять в таблицу по одной по мере прихода
  • Все загруженные инстансы держать в памяти (allInstances). К API обращаться ТОЛЬКО по кнопке "Обновить"

Задача 2 — Метка "Актуально на ЧЧ:ММ:СС"

  • Разместить слева от кнопки "Обновить"
  • Показывать время завершения последней загрузки
  • Обновлять при каждом refresh

Задача 3 — Фильтрация зависимостей по чекбоксам

  • В блоке "Зависимости" инстанса показывать ТОЛЬКО те инстансы, статус которых выбран чекбоксами (running/suspended/etc)
  • При изменении чекбокса — сразу перерисовывать список зависимостей (без обращения к API, из памяти)

Задача 4 — Переход по клику + кнопка "Назад"

  • Клик на инстанс в блоке "Зависимости" → открыть его строку/детали в таблице
  • Добавить кнопку "Назад" для возврата к исходному инстансу

Правила разработки (напоминание):

  • ЧАСТО коммитить: git add -A && git commit на ВМ в ~/terra/dashboard
  • Всё через SSH: ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no naeel@5.172.178.213
  • Не ломать существующий рабочий код
  • Новая логика = новые функции в конце файла

12. Операционная дисциплина (КРИТИЧЕСКОЕ)

Для нового агента это обязательное правило:

  • Все команды по проекту выполнять ТОЛЬКО через SSH на ВМ.

SSH-шаблон: ssh -i ~/.ssh/naeel_vm_id_ed25519 -o StrictHostKeyChecking=no -o ConnectTimeout=10 naeel@5.172.178.213 КОМАНДА

Локально ЗАПРЕЩЕНО выполнять:

  • git-команды проекта
  • docker-команды
  • kubectl-команды
  • curl к API проекта
  • bash/python/go/тесты проекта

Почему:

  • так исключаются конфликты сред и ложные результаты;
  • единая точка выполнения (ВМ) дает предсказуемость и воспроизводимость.

13. Дисциплина загрузки таблицы (актуально)

Обязательная модель работы таблицы:

  • В начале загрузки читаем данные инстансов и их параметры только из API.
  • Строка таблицы появляется сразу после чтения параметров конкретного инстанса.
  • Параллельные обращения к внешнему API запрещены: только последовательная обработка.
  • После завершения загрузки повторных обращений к API нет.
  • Новые обращения к API выполняются только по кнопке Обновить.

Требования к UX загрузки:

  • В верхней строке перед кнопкой Обновить показывать прогресс в формате Загрузка X/Y.
  • После завершения загрузки показывать Актуально на ЧЧ:ММ:СС.
  • Рядом с меткой актуальности показывать время загрузки последнего обновления.

Требования к таблице (операторский режим):

  • После чтения параметров конкретного инстанса из API сразу добавлять его строку в таблицу.
  • Добавить узкий столбец удаляемости сразу после статуса.
  • Зеленый индикатор показывать только если инстанс в suspended и прошло 14+ дней с входа в suspended (по данным API).
  • В раскрытой строке suspended-инстанса показывать пояснение по удаляемости.
  • Блоки Input/Output параметров не растягивать на всю ширину; сделать компактный читаемый layout.
  • Порог удаляемости, паттерны операций и пользовательские тексты хранить в конфиге фронта (без хардкода в вычислениях).

14. Журнал решений и хронология (без скрытых рассуждений)

Шаг A — Режим загрузки таблицы

  • Цель: сделать поведение предсказуемым для оператора и убрать лишние запросы.
  • Входные ограничения: читать данные только из API; после загрузки работать из памяти; новые запросы только по Обновить.
  • Варианты:
    • A1: догружать детали по клику строки.
    • A2: читать детали сразу в стриме и больше не дергать API.
  • Выбранный вариант: A2.
  • Причина выбора: минимальная фрагментация данных, прозрачный UX, отсутствие неожиданной сетевой активности.
  • Проверка: поток отдает progress и done-событие; во фронте удалена догрузка деталей по клику.

Шаг B — Последовательные вызовы API

  • Цель: исключить параллельные обращения к API.
  • Входные ограничения: запрет на параллельные запросы.
  • Варианты:
    • B1: batch-параллелизм с ограничением concurrency.
    • B2: строго последовательный обход uid.
  • Выбранный вариант: B2.
  • Причина выбора: полное соответствие ограничению и упрощение отладки.
  • Проверка: убраны gather/create_task/Semaphore для стрим-логики.

Шаг C — Удаляемость suspended > 14 дней

  • Цель: добавить операторский индикатор удаления без лишней визуальной нагрузки.
  • Входные ограничения: считать только по API-данным.
  • Варианты:
    • C1: считать от dtState всегда.
    • C2: сначала искать suspend-операции, затем fallback на поля detail.
  • Выбранный вариант: C2.
  • Причина выбора: меньше ложных срабатываний при изменениях, не связанных с suspend.
  • Проверка: индикатор и пояснение показываются только для suspended; порог 14+ дней.

Шаг D — Немонолитная, но умеренная структура

  • Цель: не превращать файл в монолит и одновременно не перегрузить архитектуру.
  • Входные ограничения: без чрезмерного рефакторинга.
  • Варианты:
    • D1: полный вынос JS в модули/файлы.
    • D2: локальная модульность внутри файла через конфиг и мелкие функции.
  • Выбранный вариант: D2.
  • Причина выбора: безопасно для текущей стадии, быстро настраивается, меньше риск поломок.
  • Реализация:
    • Конфиг политики/текстов вынесен в DASHBOARD_CFG.
    • Общие шаблоны строк через fmtText.
    • Рендер таблицы разделен на renderDataRow и renderDetailRow.
    • Константа TABLE_COLSPAN и состояние TABLE_STATE исключают магические значения.

Шаг E — Backend как источник вычисляемых метаданных

  • Цель: убрать дублирующую бизнес-логику из фронта и сделать поведение единообразным.
  • Входные ограничения: без избыточного рефакторинга, только практичный шаг.
  • Варианты:
    • E1: оставить расчеты удаляемости только во фронте.
    • E2: считать удаляемость на backend и отдавать в stream-строке как мета-поля.
  • Выбранный вариант: E2.
  • Причина выбора: policy централизована на сервере, фронт становится проще и легче переконфигурируется.
  • Реализация:
    • В backend добавлен конфиг DELETE_POLICY и функции вычисления suspend-возраста.
    • В stream добавлено поле _meta.deleteCandidate.
    • Фронт сначала применяет backend _meta; клиентский fallback использовался временно и отключен на шаге G.

Шаг F — Локальный snapshot таблицы в браузере

  • Цель: после перезагрузки страницы не обращаться к API до явного нажатия Обновить.
  • Входные ограничения: решение должно быть легким и не монолитным.
  • Варианты:
    • F1: только RAM (теряется при reload).
    • F2: локальный snapshot per user+env в browser storage.
  • Выбранный вариант: F2.
  • Причина выбора: быстрый старт UI после reload и автономная работа без нового API-вызова.
  • Реализация:
    • Добавлен snapshot storage key на основе user+env.
    • После успешного полного refresh snapshot сохраняется.
    • На init сначала пробуется восстановление snapshot; API вызывается только если snapshot отсутствует.
    • На logout snapshot очищается.

Шаг G — Backend-only deleteability + snapshot schema v2

  • Цель: исключить расхождение бизнес-правил между клиентом и сервером и безопасно версионировать локальный кэш.
  • Входные ограничения: без тяжелого рефакторинга.
  • Реализация:
    • Клиентский fallback вычисления удаляемости отключен в stream-обработке.
    • Поля _canDelete/_suspendDays инициализируются из backend _meta.deleteCandidate (или остаются нулевыми по умолчанию).
    • Формат snapshot переведен на version=2 и schema=instance-stream-v2.
    • Ключ snapshot обновлен до dashboard_snapshot_v2, чтобы отделить новый формат от старого.

Шаг H — Релиз v9.22 (build/push/deploy + верификация)

  • Цель: доставить изменения шага G в кластер и подтвердить фактическую версию рантайма.

  • Реализация:

    • Собран образ naeel/cloud-dashboard:v9.22.
    • Выполнен push в Docker Hub.
    • Применен k8s/deployment.yaml в namespace terra.
    • Rollout завершен успешно.
    • Проверено соответствие версии в deployment и pod.
  • Проверка:

    • spec.image: naeel/cloud-dashboard:v9.22.
    • pod.image: naeel/cloud-dashboard:v9.22.
    • pod.imageID: docker.io/naeel/cloud-dashboard@sha256:18e87ff592a333cc280fd2af13390f47b111616b8da31f2e32d7ac24e3a3a7ea.
    • pod статус: Running, Ready=true, Restarts=0.
    • Внутренний smoke-check: GET http://127.0.0.1:8080/ -> HTTP 200.
    • Внешний smoke-check (ingress terra.k8c.ru): HTTPS / -> 200, HTTP / -> 308.

    Шаг I — Добавлен столбец UUID после Имени (v9.29)

    • Цель: сделать идентификацию инстанса в таблице мгновенной без раскрытия строки.
    • Реализация:
      • В таблицу добавлен новый sortable-столбец UUID сразу после Имя.
      • В рендере строк добавлена ячейка instanceUid (моноширинный стиль).
      • TABLE_COLSPAN увеличен с 8 до 9.
      • Обновлены мобильные индексы скрываемых колонок (nth-child) после сдвига структуры.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.29`.

    Шаг J — Фикс renderDataRow is not defined (v9.30)

    • Цель: убрать runtime-ошибку в таблице после добавления UUID-колонки.
    • Реализация:
      • renderDataRow и renderDetailRow вынесены из toggle() в общий scope.
      • Рендер таблицы (render) снова вызывает доступные функции без ReferenceError.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.30`.

    Шаг K — Перенос светофора в колонку Изменён (v9.31)

    • Цель: убрать отдельный столбец индикатора и показывать только полезный сигнал.
    • Реализация:
      • Удален отдельный столбец светофора из таблицы.
      • Зеленая точка теперь рисуется после времени в колонке Изменён только для eligible (suspended > 14 дней, по backend-метаданным).
      • Серые точки полностью убраны.
      • TABLE_COLSPAN обновлен с 9 до 8.
      • Обновлены мобильные индексы скрываемых колонок.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.31`.

    Шаг L — Возврат потокового вывода строк (v9.32)

    • Цель: показывать строки таблицы по мере загрузки stream, без ожидания _done.
    • Реализация:
      • Исправлен scheduleRender(): вместо perpetual-debounce применяется throttle (если таймер уже запущен, новый не ставится).
      • UI теперь обновляется регулярно в ходе потока, а не откладывается до конца непрерывного чтения.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.32`.

    Шаг M — Левое выравнивание 4 колонок в Параметрах (v9.33)

    • Цель: сделать Input/Output читабельным в табличном стиле без центрирования.
    • Реализация:
      • params-grid переведен на max-content колонки и выравнивание влево.
      • p-item переведен на grid из 2 колонок (key + value) с увеличенным фиксированным зазором между ними.
      • p-key и p-val выровнены влево.
      • Ширина колонок определяется содержимым; при реальном переполнении включается перенос (overflow-wrap: anywhere).
      • Выполнен version bump: .naeel/cloud-dashboard:v9.33`.

    Шаг N — Ширина колонки имен: max + 10ch (v9.34)

    • Цель: сделать значения читаемыми по единой вертикали и увеличить отступ от имен.
    • Реализация:
      • Для каждой секции (Input/Output) вычисляется максимальная длина имени параметра.
      • Ширина колонки имен задается формулой: maxKeyLen * 1ch + 10ch.
      • Значения начинаются строго от единой левой границы value-колонки в каждой секции.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.34`.

    Шаг O — Output +20 и copy-иконка параметров (v9.35)

    • Цель: повысить читаемость Input/Output и ускорить копирование значений.
    • Реализация:
      • Увеличен межсекционный отступ между Input и Output на +20.
      • Каждая строка параметров переведена в 3 зоны: key | value | copy.
      • Иконка копирования фиксирована в конце строки (правая зона), со вспышкой после успешного копирования.
      • Добавлена функция copyParamValue с Clipboard API и fallback через execCommand.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.35`.

    Шаг P — Скрытие copy для пустых + фикс дневного режима (v9.36)

    • Цель: убрать визуальный шум и исправить неполное переключение в light theme.
    • Реализация:
      • renderCopyBtnHtml не рисует кнопку копирования для пустых значений и .
      • Добавлены body.light стили для .stat-card и .stat-card .lbl, чтобы карточки статистики реально становились светлыми.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.36`.

    Шаг Q — Light-theme для status-badge (v9.37)

    • Цель: убрать темные/кислотные бейджи статусов в дневном режиме.
    • Реализация:
      • Добавлены body.light переопределения для .s-running, .s-suspended, .s-deleting, .s-deleted, .s-pending, .s-creating, .s-default.
      • Для светлой темы введены мягкие светлые фоны и контрастный текст, плюс тонкая рамка бейджа.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.37`.

    Шаг R — Контрастный фон блока деталей/параметров (v9.38)

    • Цель: убрать визуальное слияние раскрытой области деталей с основной таблицей.
    • Реализация:
      • Усилен контейнер .detail-box: отдельный фон, рамка, скругление, внутренний отступ от таблицы.
      • Добавлен фон для .detail-row td и контрастная подложка вкладок .tabs.
      • Увеличен контраст панелей .panel-params, .panel-history, .panel-deps в dark/light режимах.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.38`.

    Шаг S — Полосатые строки (zebra) для читаемости (v9.39)

    • Цель: улучшить восприятие строк, чтобы данные не сливались по горизонтали.
    • Реализация:
      • Добавлена мягкая zebra-разметка в основной таблице (tbody tr.data-row) с разным тоном соседних строк.
      • Добавлена zebra-разметка в таблице истории (.hist-table tbody tr).
      • Добавлена лёгкая zebra-подложка для строк параметров (.p-item).
      • Отдельно настроены оттенки для dark и light темы.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.39`.

    Шаг T — Полная деактивация графа + явная зебра основной таблицы (v9.40)

    • Цель: полностью убрать интеракции графа и сделать полосатость в основной таблице заметной.
    • Реализация:
      • Из header удалена кнопка Граф зависимостей.
      • Из деталей удалена вкладка Граф и панель графа.
      • Удалены обработчики клика по глобальному графу/модалке, чтобы граф не открывался вообще.
      • Полосатость основной таблицы переведена на классы row-base/row-alt, назначаемые в renderDataRow по индексу данных (не зависит от detail-row).
      • Усилен контраст тонов зебры в light/dark.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.40`.

    Шаг U — Copy для UUID + жирные имена инстансов (v9.41)

    • Цель: ускорить работу оператора в таблице и усилить визуальный приоритет названий.
    • Реализация:
      • В колонке UUID добавлена иконка копирования для каждой строки.
      • Кнопка UUID-копирования использует существующую логику copyParamValue и не триггерит раскрытие строки (event.stopPropagation()).
      • Имена инстансов в колонке Имя переведены в жирное начертание (.inst-name).
      • Для uuid-copy добавлены отдельные hover/ok стили в dark/light теме.
      • Выполнен version bump: .naeel/cloud-dashboard:v9.41`.

    Шаг V — Правильная логика иконки day/night (v9.42)

    • Цель: сделать поведение кнопки темы интуитивным (иконка показывает целевое переключение, а не текущий режим).
    • Реализация:
      • В applyTheme изменена логика кнопки:
        • при активной дневной теме показывается 🌙 (переключение на ночь),
        • при активной ночной теме показывается (переключение на день).
      • Обновлены title-подсказки на действие: Переключить на ... тему.
      • Выполнен version bump: naeel/cloud-dashboard:v9.42.

15. HANDOFF для нового чата (что делать дальше без доп. подсказок)

Текущее состояние (baseline):

  • Таблица загружается через /instances/stream один раз на refresh, далее работает из памяти.
  • Повторных API-вызовов между refresh нет.
  • После reload данные восстанавливаются из browser snapshot (version=2, schema=instance-stream-v2).
  • Удаляемость берется только из backend _meta.deleteCandidate.
  • В кластере задеплоен образ naeel/cloud-dashboard:v9.43.

Нельзя нарушать (инварианты):

  • Не добавлять параллельные обращения к внешнему API.
  • Не делать догрузку detail по клику строки.
  • Не возвращать клиентский fallback вычисления удаляемости.
  • Все проектные команды выполнять только через SSH на ВМ.

При старте нового чата сделать в таком порядке:

  1. Проверить, что в k8s/deployment.yaml увеличен image tag относительно предыдущего.
  2. Проверить соответствие stream-контракта: _progress, _detail, _meta, _done.
  3. Проверить, что restoreSnapshot() вызывается до loadAllDetails().
  4. Прогнать smoke-flow вручную: login -> refresh -> reload -> refresh.
  5. Зафиксировать результат в этот файл отдельным шагом (H, I, ...).

Примечание по sync-проверке путей:

  • Путь /home/naeel/remote_dev/dashboard может отсутствовать на ВМ, поэтому прямое сравнение хешей с ~/terra/dashboard не всегда выполнимо на стороне ВМ.
  • Если путь отсутствует, источником истины считать ~/terra/dashboard, а для альтернативной проверки синхронизации запросить у оператора допустимый способ сравнения.

Чеклист smoke-flow (обязательный):

  • После login/refresh видно Загрузка X/Y.
  • После завершения видно Актуально на ... (загрузка N c).
  • После reload видно Актуально на ... (из локального snapshot).
  • До кнопки Обновить новых обращений к API нет.
  • Индикатор удаляемости совпадает с backend _meta.deleteCandidate.

Definition of Done для следующего изменения:

  • Выполнен version bump (минимум patch) в image tag.
  • Изменение не ломает table-first режим и snapshot-режим.
  • Документация обновлена новым шагом в разделе 14 или отдельной секцией.

Шаг W — Реальный фикс theme-toggle после валидации pod (v9.43)

  • Цель: гарантировать, что в рантайме действительно применяется инвертированная UX-логика иконки day/night.
  • Причина: после релиза v9.42 в pod обнаружилась старая логика applyTheme.
  • Реализация:
    • На VM подтвержденно обновлен static/index.html:
      • light -> 🌙 (переключение на ночь),
      • dark -> ☀ (переключение на день),
      • title показывает действие переключения.
    • Выполнен повторный version bump: naeel/cloud-dashboard:v9.43.