19 KiB
19 KiB
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) Что важно помнить по инфраструктуре
- Traefik удалять нельзя. Причина: динамические контейнеры создают labels во время работы, и именно Traefik маршрутизирует:
/s/<session_id>/.../svc/<slug>/.../w/<slot>/.../u/<slot>/...
- При Nginx Proxy Manager (NPM):
- внешний домен -> NPM -> внутренний Traefik.
- в
docker-compose.ymlTraefik опубликован так:0.0.0.0:2288 -> 4430.0.0.0:8288 -> 80
- в NPM обязательна опция
Websockets Support.
- Кнопка «Домой» в 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-образов:
docker compose --profile build-only build kiosk-image rdp-proxy-image universal-runtime-image
Поднять всё:
docker compose up -d --build
Перезапуск только API:
docker compose up -d api
Проверка состояния:
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
Базовый рабочий сценарий:
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:
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)
- UI/брендинг:
- Тексты в интерфейсе переведены на формулировку
инфрастуктурный полигон. - На главной панели приветствие в блоке
admin-intro:Добро пожаловать в инфрастуктурный полигон. - Кнопка выхода на дашборде:
Выход(вместоLogout).
- WEB runtime (браузерные сервисы):
- В панели управления runtime оставлены 2 кнопки:
НазадГлавная(ведет на главную панель портала/).
- Кнопка
Впередудалена. - Изменения применены в
kiosk/entrypoint.shиuniversal-runtime/entrypoint.sh.
- Логин и просроченные пользователи:
- Если пользователь найден и пароль верный, но аккаунт просрочен/неактивен, на экране входа показывается сообщение:
Доступ к сервису приостоновлен, обратитесь к вашему менеджеру. - Сообщение рендерится в шаблоне
app/templates/login.htmlчерезlogin_error.
- Категории сервисов:
- Добавлены сущности и связи:
categoriesservice_categories
- Категории можно создавать/удалять в админке.
- При создании/редактировании WEB/RDP сервиса можно выбрать категории.
- На главной панели добавлен стильный фильтр по категориям (chips) и бейджи категорий на карточке сервиса.
- Иконки сервисов:
- Иконки на главной панели увеличены примерно в 6 раз.
- Масштабирование иконок:
object-fit: contain, чтобы картинка полностью влезала в рамку. - В админке загрузка иконки стала автоматической при выборе файла (без кнопки Upload).
- Многоворкерный API и startup:
- API работает с
uvicorn --workers 4. - Чтобы убрать гонку DDL на старте (при нескольких воркерах), добавлен file-lock на bootstrap схемы:
- lock-файл:
/tmp/portal-schema.lock - сериализуется выполнение
Base.metadata.create_all(...)иensure_schema_compatibility().
- lock-файл:
- Операционные заметки по применению runtime-изменений:
- После изменения
kiosk/universal-runtimeнужно:- пересобрать runtime-образы,
- пересоздать
portal-webpool-*,portal-universal-*,portal-warm-*контейнеры, - перезапустить
api.
14) Обновление контекста (2026-04-21, вечер)
- Главная страница и 500:
- Был зафиксирован Internal Server Error на /.
- Причина: синтаксическая ошибка Jinja в app/templates/login.html (поврежденный endif).
- Статус: исправлено, API перезапущен, / отвечает 200.
- Фон и визуальные эффекты:
- Были тесты фонов main.jpg, main_general.jpg, 123.jpg и локального файла 71ba42f1d7d61e4313ad8fd086d3ed7f.jpg.
- Текущее состояние по запросу: эффекты отключены.
- Отключено: parallax, анимации облаков, hover-движения карточек/ссылок, blur карточек.
- Главная панель оставлена со статичным светлым фоном без motion-эффектов.
- Файлы, затронутые в этой волне:
- app/templates/dashboard.html: удален parallax/cloud слой из разметки.
- app/static/style.css: добавлен override-блок для отключения эффектов.
- app/templates/login.html: исправлена ошибка шаблона.
- Актуальный операционный контур:
- Сервер: ruslan@10.17.39.3
- Проект: /root/Stend_mont
- Контекст: /root/Stend_mont/docs/PROJECT_CONTEXT.md
- Применение UI-правок:
- ssh ruslan@10.17.39.3
- sudo -s
- cd /root/Stend_mont
- docker compose up -d --build api
- 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, таймаут и пулы)
- Таймаут простаивания сессии уменьшен:
- Было:
SESSION_IDLE_SECONDS=1800(~30 минут). - Стало:
SESSION_IDLE_SECONDS=300(~5 минут). - Источник значения:
.env:SESSION_IDLE_SECONDS=300docker-compose.yml:SESSION_IDLE_SECONDS: ${SESSION_IDLE_SECONDS:-300}- fallback в
app/main.py:300.
- Поведение при простое (heartbeat):
- В runtime-страницах (
kiosk,universal-runtime,rdp-proxy) heartbeat теперь проверяет HTTP-статусtouch. - Если
touchвозвращает не2xx(например,410 Session expired), клиент делает редирект на:/?session_closed=idle - На
/добавлено уведомление:Сессия была закрыта из-за простоя. Откройте сервис заново. - Уведомление показывается и на login-page, и на dashboard.
- Изменение API для touch:
POST /api/sessions/{id}/touch:404если сессия не найдена/не принадлежит пользователю;410если сессия найдена, но уже неACTIVE.
16) Обновления (2026-04-21, ночь)
- Ограничение активных сервисов пользователя:
- Лимит оставлен
MAX_ACTIVE_SERVICES_PER_USER=4. - Поведение изменено на FIFO-ротацию:
- при открытии 5-го сервиса автоматически закрывается самый старый активный;
- при открытии 6-го — следующий по старшинству и т.д.
- Жесткий редирект с ошибкой теперь используется только как аварийный fallback.
- Время простоя:
- Для обычного простоя подтверждено
SESSION_IDLE_SECONDS=300(5 минут). - Значения синхронизированы в
.env,docker-compose.yml,app/main.py.
- Runtime-навигация в сервисах:
- Кнопки оставлены символьные:
←(назад)⌂(главная)
- Позиция обновлена: слева вверху, но чуть ниже прежнего:
kiosk:top:34pxuniversal-runtime:top:64px(ниже статусного блока)
- UI карточек на главной:
- В описании карточки добавлена прокрутка (
max-height+overflow:auto), если текст не влезает. - Поддержаны переносы строк.
- Поддержано отображение жирного текста из:
**markdown**- простых HTML-тегов (
<b>,<strong>,<i>,<em>,<u>,<br>), с безопасным экранированием остального.
- Авторизация:
- При неверном логине/пароле теперь отображается явное сообщение на странице входа:
Неверный логин или пароль(вместо немого 401 без человекочитаемого текста).
- Производительность API:
- Увеличено число воркеров Uvicorn:
- было:
--workers 4 - стало:
--workers 6
- было:
- Изменение внесено в
docker-compose.yml.
- WEB pool (устойчивость при пике):
- Добавлен recovery на конфликты Docker имен/удаления (
already in use,marked for removal). - Для
ensure_web_poolдобавлены повторные попытки и принудительное удаление конфликтного контейнера перед повтором. - Это закрывает сценарий, когда буфер (
WEB_POOL_BUFFER) должен расширять пул, но упирается в конфликт имени контейнера.
- 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.
- Важный операционный урок:
- При работе с
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)
- Причина закрытия сессии (
idlevslimit):
- Добавлен статус сессии
ROTATEDв API; - Для
POST /api/sessions/{id}/touchпри закрытой сессии возвращается410с JSON:ok: falsereason: idle|limitstatus: <SessionStatus>
- В рантаймах (
kiosk,universal-runtime) редирект на главную теперь учитывает причину:/?session_closed=idle/?session_closed=limit
- На главной странице добавлено отдельное сообщение о закрытии из-за лимита активных сервисов.
- API воркеры:
- Значение в
docker-compose.ymlувеличено доuvicorn --workers 18.
- Логирование API усилено (structured logging):
- Добавлены структурированные JSON-события с
eventиreq_id; - Расширен middleware логирования запросов: метод, путь, query, статус, длительность, client_ip, user_agent;
- Добавлен порог медленных запросов через
LOG_SLOW_REQUEST_MS(по умолчанию2000мс); - Добавлены ключевые события жизненного цикла сессий:
session_open_requestedsession_createdsession_rotatedsession_closedsession_touch_rejectedsession_closed_by_user
- Операционная польза:
- Быстрее диагностируются причины
504/обрывов/закрытий; - Проще фильтровать инциденты по
req_idиsession_idвdocker compose logs api.