Files
Stend_mont/docs/PROJECT_CONTEXT.md
T

358 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Project Context: Portal Stand Access
Этот файл нужен как быстрый технический контекст для нового разработчика/оператора.
## 1) Что это за проект
Веб-портал для выдачи пользователям доступа к стендам/сервисам через браузер.
Ключевая идея:
- пользователь выбирает сервис;
- портал открывает сервис в уже прогретом браузерном контейнере (WEB) или в RDP-слоте;
- каждая пользовательская сессия имеет `session_id` (UUID) и свой URL `/s/<session_id>/...`.
## 2) Текущий стек
- API: FastAPI (`app/main.py`)
- БД: PostgreSQL
- Edge/router: Traefik (обязателен для динамических маршрутов runtime-контейнеров)
- Runtime WEB: `portal-kiosk` (Chromium + x11vnc + websockify/noVNC)
- Runtime RDP: `portal-rdp-proxy` (xfreerdp + x11vnc + websockify/noVNC)
## 3) Принятые продуктовые решения
- Режим VNC как отдельный сервис больше не используется (deprecate).
- Основной сценарий для пользователей: WEB и RDP.
- Для WEB используется общий пул `portal-webpool-*` (и авторасширение при нагрузке).
- Для RDP используется универсальный пул слотов (`UNIVERSAL_POOL_SIZE`).
- Сессии пользователя имеют UUID-ссылки (`/s/<uuid>/...`).
## 4) Критичные маршруты
- `/` — выбор сервисов
- `/go/<slug>` — запуск пользовательской сессии
- `/s/<session_id>/` — страница ожидания старта
- `/s/<session_id>/view` — сессионный view для WEB-пула
- `/svc/<slug>/` — роут к warm runtime конкретного сервиса
- `/w/<slot>/` — роут к WEB pool слоту
- `/u/<slot>/` — роут к universal pool слоту
- `/admin` — админка
## 5) Что важно помнить по инфраструктуре
1. Traefik удалять нельзя.
Причина: динамические контейнеры создают labels во время работы, и именно Traefik маршрутизирует:
- `/s/<session_id>/...`
- `/svc/<slug>/...`
- `/w/<slot>/...`
- `/u/<slot>/...`
2. При Nginx Proxy Manager (NPM):
- внешний домен -> NPM -> внутренний Traefik.
- в `docker-compose.yml` Traefik опубликован так:
- `0.0.0.0:2288 -> 443`
- `0.0.0.0:8288 -> 80`
- в NPM обязательна опция `Websockets Support`.
3. Кнопка «Домой» в runtime UI:
- должна возвращать к выбору сервисов портала (`/`), а не вводить URL в удалённом сайте.
## 6) Диагностика типовых проблем
### A) Черный экран в WEB
Проверять:
- что у noVNC корректный WebSocket endpoint (`.../websockify`);
- что сессия active в БД;
- что контейнер WEB-пула running;
- что в NPM включен websocket proxy.
Быстрая проверка:
- логи `portal-webpool-*`
- логи `portal-api-1`
- содержимое `/opt/portal/index.html` внутри runtime-контейнера.
### B) "Соединение со слотом потеряно" в RDP
Обычно не проблема портала, а проблема соединения `xfreerdp` до целевого host:port/cred/sec.
Смотреть `/tmp/session-app.log`/`xfreerdp.log` в `portal-universal-*`.
### C) Изменения не видны сразу
Если менялись runtime-скрипты, старые warm/pool контейнеры могут держать старую версию.
Нужно пересобрать образ + пересоздать пул.
## 7) Где смотреть код
- Backend и orchestration: `app/main.py`
- Админка/UI: `app/templates/admin.html`, `app/static/style.css`
- Пользовательский дашборд: `app/templates/dashboard.html`
- WEB runtime: `kiosk/entrypoint.sh`, `kiosk/manager.py`
- RDP runtime: `rdp-proxy/entrypoint.sh`
- Universal runtime: `universal-runtime/entrypoint.sh`, `universal-runtime/manager.py`
- Оркестрация: `docker-compose.yml`, `traefik/traefik.yml`
## 8) Операционные команды
Сборка runtime-образов:
```bash
docker compose --profile build-only build kiosk-image rdp-proxy-image universal-runtime-image
```
Поднять всё:
```bash
docker compose up -d --build
```
Перезапуск только API:
```bash
docker compose up -d api
```
Проверка состояния:
```bash
docker compose ps
docker compose logs -f api traefik
```
## 9) Что еще можно улучшить
- вынести миграции в Alembic;
- добавить отдельный health dashboard с websocket/rdp метриками;
- централизованный сбор логов и алерты;
- e2e smoke-тесты на сценарии `/go -> /s/<uuid>/view`.
## 10) Сервер и рабочие пути
- SSH сервер: `ruslan@10.17.39.3`
- Пароль `sudo` на сервере: `utOgbZ09ruslanstand`
- Рабочий каталог проекта на сервере: `/root/Stend_mont`
- Файл контекста на сервере: `/root/Stend_mont/docs/PROJECT_CONTEXT.md`
Базовый рабочий сценарий:
```bash
ssh ruslan@10.17.39.3
sudo -s
cd /root/Stend_mont
```
## 11) Git доступ и публикация
Репозиторий:
- `https://git.ruslan.xyz/ruslan/Stend_mont`
Учетные данные HTTPS (текущие):
- login: `ruslan@ipcom.su`
- password/token: `utOgbZ09ruslan`
Пример push:
```bash
cd /root/Stend_mont
git add .
git commit -m "your message"
git push https://ruslan%40ipcom.su:utOgbZ09ruslan@git.ruslan.xyz/ruslan/Stend_mont main
```
## 12) Текущее runtime-состояние (на момент фиксации)
- API запущен с `uvicorn --workers 4` через `docker-compose.yml`.
- Для WEB используется `portal-webpool-*`.
- Для RDP используется `portal-universal-*`.
## 13) Последние изменения (2026-04-21)
1. UI/брендинг:
- Тексты в интерфейсе переведены на формулировку `инфрастуктурный полигон`.
- На главной панели приветствие в блоке `admin-intro`: `Добро пожаловать в инфрастуктурный полигон`.
- Кнопка выхода на дашборде: `Выход` (вместо `Logout`).
2. WEB runtime (браузерные сервисы):
- В панели управления runtime оставлены 2 кнопки:
- `Назад`
- `Главная` (ведет на главную панель портала `/`).
- Кнопка `Вперед` удалена.
- Изменения применены в `kiosk/entrypoint.sh` и `universal-runtime/entrypoint.sh`.
3. Логин и просроченные пользователи:
- Если пользователь найден и пароль верный, но аккаунт просрочен/неактивен, на экране входа показывается сообщение:
`Доступ к сервису приостоновлен, обратитесь к вашему менеджеру`.
- Сообщение рендерится в шаблоне `app/templates/login.html` через `login_error`.
4. Категории сервисов:
- Добавлены сущности и связи:
- `categories`
- `service_categories`
- Категории можно создавать/удалять в админке.
- При создании/редактировании WEB/RDP сервиса можно выбрать категории.
- На главной панели добавлен стильный фильтр по категориям (chips) и бейджи категорий на карточке сервиса.
5. Иконки сервисов:
- Иконки на главной панели увеличены примерно в 6 раз.
- Масштабирование иконок: `object-fit: contain`, чтобы картинка полностью влезала в рамку.
- В админке загрузка иконки стала автоматической при выборе файла (без кнопки Upload).
6. Многоворкерный API и startup:
- API работает с `uvicorn --workers 4`.
- Чтобы убрать гонку DDL на старте (при нескольких воркерах), добавлен file-lock на bootstrap схемы:
- lock-файл: `/tmp/portal-schema.lock`
- сериализуется выполнение `Base.metadata.create_all(...)` и `ensure_schema_compatibility()`.
7. Операционные заметки по применению runtime-изменений:
- После изменения `kiosk`/`universal-runtime` нужно:
1. пересобрать runtime-образы,
2. пересоздать `portal-webpool-*`, `portal-universal-*`, `portal-warm-*` контейнеры,
3. перезапустить `api`.
## 14) Обновление контекста (2026-04-21, вечер)
1. Главная страница и 500:
- Был зафиксирован Internal Server Error на /.
- Причина: синтаксическая ошибка Jinja в app/templates/login.html (поврежденный endif).
- Статус: исправлено, API перезапущен, / отвечает 200.
2. Фон и визуальные эффекты:
- Были тесты фонов main.jpg, main_general.jpg, 123.jpg и локального файла 71ba42f1d7d61e4313ad8fd086d3ed7f.jpg.
- Текущее состояние по запросу: эффекты отключены.
- Отключено: parallax, анимации облаков, hover-движения карточек/ссылок, blur карточек.
- Главная панель оставлена со статичным светлым фоном без motion-эффектов.
3. Файлы, затронутые в этой волне:
- app/templates/dashboard.html: удален parallax/cloud слой из разметки.
- app/static/style.css: добавлен override-блок для отключения эффектов.
- app/templates/login.html: исправлена ошибка шаблона.
4. Актуальный операционный контур:
- Сервер: ruslan@10.17.39.3
- Проект: /root/Stend_mont
- Контекст: /root/Stend_mont/docs/PROJECT_CONTEXT.md
- Применение UI-правок:
1) ssh ruslan@10.17.39.3
2) sudo -s
3) cd /root/Stend_mont
4) docker compose up -d --build api
5. Git публикация:
- origin: https://git.ruslan.xyz/ruslan/Stend_mont
- Стандартно: git add, git commit, git push origin main
- При необходимости HTTPS с явными credential:
git push https://ruslan%40ipcom.su:utOgbZ09ruslan@git.ruslan.xyz/ruslan/Stend_mont main
## 15) Обновления (2026-04-21, таймаут и пулы)
1. Таймаут простаивания сессии уменьшен:
- Было: `SESSION_IDLE_SECONDS=1800` (~30 минут).
- Стало: `SESSION_IDLE_SECONDS=300` (~5 минут).
- Источник значения:
- `.env`: `SESSION_IDLE_SECONDS=300`
- `docker-compose.yml`: `SESSION_IDLE_SECONDS: ${SESSION_IDLE_SECONDS:-300}`
- fallback в `app/main.py`: `300`.
2. Поведение при простое (heartbeat):
- В runtime-страницах (`kiosk`, `universal-runtime`, `rdp-proxy`) heartbeat теперь проверяет HTTP-статус `touch`.
- Если `touch` возвращает не `2xx` (например, `410 Session expired`), клиент делает редирект на:
`/?session_closed=idle`
- На `/` добавлено уведомление:
`Сессия была закрыта из-за простоя. Откройте сервис заново.`
- Уведомление показывается и на login-page, и на dashboard.
3. Изменение API для touch:
- `POST /api/sessions/{id}/touch`:
- `404` если сессия не найдена/не принадлежит пользователю;
- `410` если сессия найдена, но уже не `ACTIVE`.
## 16) Обновления (2026-04-21, ночь)
1. Ограничение активных сервисов пользователя:
- Лимит оставлен `MAX_ACTIVE_SERVICES_PER_USER=4`.
- Поведение изменено на FIFO-ротацию:
- при открытии 5-го сервиса автоматически закрывается самый старый активный;
- при открытии 6-го — следующий по старшинству и т.д.
- Жесткий редирект с ошибкой теперь используется только как аварийный fallback.
2. Время простоя:
- Для обычного простоя подтверждено `SESSION_IDLE_SECONDS=300` (5 минут).
- Значения синхронизированы в `.env`, `docker-compose.yml`, `app/main.py`.
3. Runtime-навигация в сервисах:
- Кнопки оставлены символьные:
- `←` (назад)
- `⌂` (главная)
- Позиция обновлена: слева вверху, но чуть ниже прежнего:
- `kiosk`: `top:34px`
- `universal-runtime`: `top:64px` (ниже статусного блока)
4. UI карточек на главной:
- В описании карточки добавлена прокрутка (`max-height` + `overflow:auto`), если текст не влезает.
- Поддержаны переносы строк.
- Поддержано отображение жирного текста из:
- `**markdown**`
- простых HTML-тегов (`<b>`, `<strong>`, `<i>`, `<em>`, `<u>`, `<br>`), с безопасным экранированием остального.
5. Авторизация:
- При неверном логине/пароле теперь отображается явное сообщение на странице входа:
`Неверный логин или пароль`
(вместо немого 401 без человекочитаемого текста).
6. Производительность API:
- Увеличено число воркеров Uvicorn:
- было: `--workers 4`
- стало: `--workers 6`
- Изменение внесено в `docker-compose.yml`.
4. WEB pool (устойчивость при пике):
- Добавлен recovery на конфликты Docker имен/удаления (`already in use`, `marked for removal`).
- Для `ensure_web_pool` добавлены повторные попытки и принудительное удаление конфликтного контейнера перед повтором.
- Это закрывает сценарий, когда буфер (`WEB_POOL_BUFFER`) должен расширять пул, но упирается в конфликт имени контейнера.
5. RDP режим приведен к on-demand модели:
- `UNIVERSAL_POOL_SIZE=0` в `.env`.
- default в `docker-compose.yml`: `${UNIVERSAL_POOL_SIZE:-0}`.
- Для RDP отключен prewarm-подход: сессия поднимается в момент запуска сервиса (per-user session runtime), а не через общий universal-pool.
- В админ prewarm для RDP возвращает информационное сообщение, что RDP работает on-demand.
6. Важный операционный урок:
- При работе с `docker compose` обязательно сохранять `.env` заполненным; пустой `.env` приводит к запуску со значениями по умолчанию (пустые креды/хост), что ломает подключение API к БД.
## 17) Версионность (введено 2026-04-22)
Принята базовая схема SemVer:
- `MAJOR` — несовместимые изменения API/поведения;
- `MINOR` — новая функциональность без поломки совместимости;
- `PATCH` — исправления багов и операционные правки.
Текущая версия проекта:
- `0.6.0` (см. файл `VERSION` в корне репозитория).
Правило релиза:
- при любом релизном изменении обновлять `VERSION` и добавлять краткую запись в `PROJECT_CONTEXT.md`.
## 18) Обновления (2026-04-22)
1. Причина закрытия сессии (`idle` vs `limit`):
- Добавлен статус сессии `ROTATED` в API;
- Для `POST /api/sessions/{id}/touch` при закрытой сессии возвращается `410` с JSON:
- `ok: false`
- `reason: idle|limit`
- `status: <SessionStatus>`
- В рантаймах (`kiosk`, `universal-runtime`) редирект на главную теперь учитывает причину:
- `/?session_closed=idle`
- `/?session_closed=limit`
- На главной странице добавлено отдельное сообщение о закрытии из-за лимита активных сервисов.
2. API воркеры:
- Значение в `docker-compose.yml` увеличено до `uvicorn --workers 18`.
3. Логирование API усилено (structured logging):
- Добавлены структурированные JSON-события с `event` и `req_id`;
- Расширен middleware логирования запросов: метод, путь, query, статус, длительность, client_ip, user_agent;
- Добавлен порог медленных запросов через `LOG_SLOW_REQUEST_MS` (по умолчанию `2000` мс);
- Добавлены ключевые события жизненного цикла сессий:
- `session_open_requested`
- `session_created`
- `session_rotated`
- `session_closed`
- `session_touch_rejected`
- `session_closed_by_user`
4. Операционная польза:
- Быстрее диагностируются причины `504`/обрывов/закрытий;
- Проще фильтровать инциденты по `req_id` и `session_id` в `docker compose logs api`.