# Project Context: Portal Stand Access Этот файл нужен как быстрый технический контекст для нового разработчика/оператора. ## 1) Что это за проект Веб-портал для выдачи пользователям доступа к стендам/сервисам через браузер. Ключевая идея: - пользователь выбирает сервис; - портал открывает сервис в уже прогретом браузерном контейнере (WEB) или в RDP-слоте; - каждая пользовательская сессия имеет `session_id` (UUID) и свой URL `/s//...`. ## 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//...`). ## 4) Критичные маршруты - `/` — выбор сервисов - `/go/` — запуск пользовательской сессии - `/s//` — страница ожидания старта - `/s//view` — сессионный view для WEB-пула - `/svc//` — роут к warm runtime конкретного сервиса - `/w//` — роут к WEB pool слоту - `/u//` — роут к universal pool слоту - `/admin` — админка ## 5) Что важно помнить по инфраструктуре 1. Traefik удалять нельзя. Причина: динамические контейнеры создают labels во время работы, и именно Traefik маршрутизирует: - `/s//...` - `/svc//...` - `/w//...` - `/u//...` 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//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-тегов (``, ``, ``, ``, ``, `
`), с безопасным экранированием остального. 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: ` - В рантаймах (`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`.