# Изменения

Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/),
проект придерживается [семантического версионирования](https://semver.org/lang/ru/).

## [0.0.14] — 2026-07-28

TURN-фолбэк для участников из сетей с жёстким NAT.

### Исправлено
- LiveKit теперь анонсирует клиентам внешний TURN-сервер (`rtc.turn_servers`
  в конфигурации, UDP и TCP на 3478). Раньше coturn поднимался и был healthy,
  но клиенты о нём не знали: внешний TURN в конфиге объявлен не был, встроенный
  выключен, а фронтенд `iceServers` не задаёт. За всё время работы в логах
  coturn не было ни одной аллокации — то есть relay не использовался никогда,
  и участники из сетей, где прямое UDP-соединение не проходит, теряли связь
  (`PEER_CONNECTION_DISCONNECTED`). На нагрузочном тесте 28.07 все такие
  разрывы пришлись на внешних участников и ни одного — на офисных.
- Credentials TURN генерируются по механизму TURN REST API из общего
  `TURN_STATIC_AUTH_SECRET`, то есть тот же секрет, что и у coturn.

### Изменено
- `deploy/render-templates.sh` подставляет `TURN_EXTERNAL_IP` и
  `TURN_STATIC_AUTH_SECRET` также в конфигурацию LiveKit.
- Раздел 8 руководства по развёртыванию переписан: TURN больше не «план на
  будущее», а рабочая конфигурация. Отдельно описано правило ufw для
  relay-диапазона `49160:49200/udp` — без него TURN отвечает на запросы, но
  релей не работает, причём в логах coturn при этом тишина.

TURN over TLS (5349/443) по-прежнему не настроен: требует монтирования
сертификата в coturn. Неработающий `turns:` намеренно не анонсируется, иначе
клиент ждал бы таймаута перед переходом к рабочему кандидату.

## [0.0.13] — 2026-07-28

Снижение нагрузки на сеть: клиент перестаёт получать полное качество всех
чужих камер независимо от того, какого размера плитка на экране.

### Изменено
- Включены `adaptiveStream` и `dynacast` в опциях комнаты. Оба флага в LiveKit
  выключены по умолчанию, из-за чего каждый участник был подписан на полное
  качество всех чужих треков, а каждый паблишер слал все слои симулкаста, даже
  когда их никто не смотрит. Теперь качество подписки выбирается по фактическому
  размеру плитки, а неотрисованные треки уходят в паузу. На локальном стенде
  (9 участников, паблишеры 720p) входящий поток одного клиента упал с
  11 110 до 778 кбит/с.

  Вместе с этим начинает экономить уже написанный код, который до сих пор не
  давал выигрыша: «скрыть остальных» не рендерит карусель (исходящий трафик
  LiveKit 0.76 → 0.03 Мбит/с), пагинация сетки участников рендерит только
  текущую страницу, а пауза чужого видео в свёрнутой вкладке работает лишь
  при включённом `adaptiveStream`.
- Контейнеры больше не пересобирают окружение Python при запуске: во все
  вызовы `uv run` в прод-путях (CMD образа, `command`/`entrypoint`/`healthcheck`
  сервисов, миграции и seed в `install.sh`) добавлен `--no-sync`. Раньше
  окружение, собранное на этапе build с `--no-dev`, при каждом старте
  синхронизировалось заново и подтягивало dev-группу: ~26 МБ загрузок,
  замедленный старт и ruff/mypy/pytest в рантайме. Healthcheck'и делали то же
  самое каждые 15 секунд всю жизнь контейнера. Размер `/app/.venv` после
  запуска: 574 → 456 МБ. Локальная разработка не затронута — там dev-группа
  нужна и ставится как раньше.

## [0.0.12] — 2026-07-28

Разблокировка backend под нагрузкой: вход в конференцию перестаёт отваливаться,
когда участники включают микрофоны.

### Исправлено
- Обработчик webhook `track_published` больше не запускает Track Egress внутри
  своей транзакции. Раньше каждый опубликованный микрофон уходил в сетевой
  вызов, а на инстансе без профиля `transcribe` (egress-сервиса в деплое нет)
  этот вызов висел 20–25 секунд, всё это время удерживая соединение с БД. На
  нагрузочном тесте с 19 участниками пул соединений выгребался за секунды, и
  вход в конференцию начинал отвечать 500. Теперь запуск уходит в фоновую
  задачу со своей сессией, а webhook отвечает сразу — LiveKit перестаёт копить
  очередь доставки и терять события.
- Track Egress не запускается вовсе, если транскрибация выключена в настройках
  инстанса — тот же guard, что уже был в обработчике `room_finished`.
- Запуск Track Egress ограничен таймаутом (по умолчанию 3 секунды) вместо
  ожидания собственного таймаута LiveKit.

### Изменено
- Пул соединений с БД задаётся явно (`DB_POOL_SIZE`, `DB_MAX_OVERFLOW`,
  `DB_POOL_TIMEOUT`) вместо дефолта SQLAlchemy 5 + 10. Считайте бюджет на весь
  инстанс: каждый воркер держит свой пул, и сумма должна оставаться заметно
  ниже `max_connections` у Postgres.
- Backend запускается с несколькими процессами uvicorn (`UVICORN_WORKERS`,
  по умолчанию 2). Одиночный процесс означал, что любой блокирующий вызов
  останавливает и параллельные запросы, и WS-чат всех участников. Дефолт 2, а
  не по числу ядер: на четырёхъядерном сервере ядра делятся с LiveKit.

## [0.0.11] — 2026-07-28

Выбор режима показа участников в конференции, скрытие остальных и круглая
иконка участника при выключенной камере.

### Добавлено
- Выбор режима показа участников на сцене вместо автоматической раскладки:
  «Стандарт» (крупная плитка плюс карусель остальных сбоку, как было),
  «Плитки» (все участники равными плитками) и «Живые плитки» (плитками —
  только те, у кого включена камера, остальные в карусели). Кто именно
  показан крупно в «Стандарте», по-прежнему решает прежняя логика фокуса
  (новая демонстрация → закреплённый → говорящий с видео → говорящий).
  Выбор сохраняется между заходами в комнату.
- Скрытие остальных участников — на сцене остаётся только основная плитка.
  Скрыть можно из меню «Вид», из шторки настроек на мобильном и кнопкой над
  колонкой миниатюр; вернуть — кнопкой «Показать остальных (N)», которая
  видна всё время, пока кто-то скрыт. В режиме «Плитки» скрывать нечего,
  переключатель там заблокирован.
- Переключатель вида: кнопка «Вид» с поповером в тулбаре на широком экране;
  на мобильном — секция «Вид» в шторке настроек, чтобы не добавлять шестую
  кнопку в уже ужатый тулбар.

### Изменено
- Демонстрация экрана временно перебивает выбранный режим: пока идёт шэр,
  сцена ведёт себя как «Стандарт», а по его завершении возвращается к
  выбранному виду.
- Равномерная сетка плиток стала пригодна для телефона: 2 колонки от 360px
  ширины и портретная раскладка 2×3 вместо двух плиток на страницу, которые
  давал набор раскладок библиотеки. Лишние участники уходят на следующую
  страницу — листается свайпом и стрелками.

### Исправлено
- Иконка участника при выключенной камере больше не превращается в эллипс:
  размер считается от узкой стороны плитки (container-запросы), а не как
  проценты от разных сторон. В крупной плитке иконка занимает примерно её
  половину, в миниатюрах карусели — всю узкую сторону с отступом 5px; на
  большой плитке фокуса иконка больше не упирается в прежний потолок 96px.
- Мини-плеер в Chrome открывался на самом пользователе вместо того, что он
  видел крупно: сцена в PiP-окне — отдельный экземпляр компонента и выбирала
  фокус с нуля, доходя до фолбэка «показать себя». Теперь ключ фокуса
  переживает переезд в мини-окно и обратно. В Safari (video-фолбэк вместо
  Document PiP) поведение не менялось.

## [0.0.10] — 2026-07-28

Разбивка метрик по контейнерам в Grafana и плашка с характеристиками сервера.

### Добавлено
- Метрики по контейнерам (CPU/память/сеть каждого отдельно) — замена
  `cAdvisor`, несовместимого с containerd-снапшоттером Docker Engine на
  сервере `1gb` (убран в `0.0.9`). Свой минимальный сервис
  `container-exporter` (`deploy/monitoring/container-exporter/`) опрашивает
  Docker Engine API параллельно в фоновой задаче, независимо от
  storage-driver'а. Доступ к докер-сокету изолирован через
  `docker-socket-proxy` — экспортеру разрешены только `GET`-запросы по
  контейнерам, любые изменяющие запросы блокируются на уровне прокси.
  Панели «Топ контейнеров по CPU/памяти» вернулись в дашборд
  «Хост и контейнеры».
- Плашка «Характеристики сервера» в том же дашборде: живые «Ядра CPU» /
  «Объём RAM» / «Диск (/)» из `node-exporter`, GPU — из новой info-метрики
  backend `vidconf_host_info` (данные `install.sh`, `HW_*` в `.env`).

## [0.0.9] — 2026-07-28

Метрики CPU/RAM/диска хоста в Grafana; дефолтные подвкладки конференций и
пользователей в админке.

### Добавлено
- Мониторинг железа: `node-exporter` (CPU/RAM/диск/сеть/load average хоста)
  — профиль compose `monitoring`, без публикации портов наружу. Новый
  дашборд Grafana «Хост и контейнеры». Три новых алерта Prometheus:
  `HostMemoryLow`, `HostDiskLow`, `HostCpuHigh` (пороги — под сервер `1gb`:
  8 ГБ RAM, 4 CPU, 50 ГБ диска). `cAdvisor` (разбивка по контейнерам)
  сознательно не включён — несовместим с containerd-снапшоттером Docker
  Engine на `1gb`, см. `docs/deploy/monitoring.md`.

### Изменено
- Админка, вкладка «Конференции»: вкладка «Все» перенесена в конец списка
  фильтров (после «Завершённые»), по умолчанию открываются «Запланированные».
- Админка, вкладка «Пользователи»: по умолчанию открываются «Активные»
  (было «Все»).

## [0.0.8] — 2026-07-27

Админка: несколько mail-доменов для регистрации, фильтр и поиск по команде в
списке пользователей, исправлен баг сохранения настроек.

### Исправлено
- Сохранение любой настройки инстанса блокировалось ошибкой «уровень AI
  недоступен», даже если AI-модуль (транскрибация/суммаризация) выключен и
  пользователь уровень не трогал — фронт отправлял в `PUT /admin/settings`
  весь набор полей целиком, включая уже сохранённый `ai_level`. Теперь
  доступность уровня проверяется только при его фактической смене; фронт
  отправляет в запросе лишь реально изменённые поля.

### Добавлено
- «Эталон mail-домена» в настройках инстанса поддерживает несколько
  доменов вместо одного — регистрация проходит с email с любого домена из
  списка. Старое значение из БД (один домен) читается без миграции и
  переписывается в новый формат при первом же сохранении.
- В списке пользователей админки — вкладки «Активные»/«Заблокированные»/
  «Все» перед полем поиска.
- Поиск по пользователям учитывает и название команды — пользователи без
  команды по-прежнему остаются в общей выдаче.

## [0.0.7] — 2026-07-27

Настройки устройств: выбор аудиовывода и компактная шторка на мобильном.

### Добавлено
- Выбор устройства аудиовывода (динамики/наушники/Bluetooth-гарнитура) в
  «Настройках устройств» рядом с микрофоном и камерой. Выбор переживает
  перезаход в комнату. Список показывается только там, где браузер умеет
  им управлять (Android Chrome и другие Chromium на десктопе/мобильном);
  в iOS Safari поддержки нет вовсе — вместо списка короткая подсказка, что
  переключение звука там на стороне системы.
- На мобильной ширине «Настройки устройств» открываются компактной шторкой
  снизу вместо модалки по центру экрана — закрывается свайпом вниз, тапом
  вне панели или Escape. На десктопе поведение не изменилось.

### Примечания по эксплуатации
- Управлять маршрутизацией звука (принудительно включить громкую связь или
  перекинуть звук на Bluetooth) из браузера нельзя в принципе — это делает
  ОС, веб-страница может только выбрать устройство вывода там, где браузер
  это позволяет (`setSinkId`).

## [0.0.6] — 2026-07-27

Управление фокусом основного окна конференции: участника можно закрепить, а
без закрепления окно само следует за говорящим. Плюс общий футер на всех
страницах сервиса.

### Добавлено
- Закрепление участника в основном окне — кнопка-булавка на плитке.
  Закреплённый участник держит фокус, даже когда говорят другие; повторное
  нажатие снимает закрепление, как и выход закреплённого из комнаты.
  Закреплённая плитка помечена рамкой и подсвеченной булавкой; на
  тач-устройствах булавка видна без наведения.
- Общий футер на всех страницах, кроме комнаты конференции: версия инстанса
  и копирайт со ссылкой на автора.

### Изменения
- Основное окно конференции следует за активным говорящим, а не удерживает
  выбранного один раз участника. Фокус не переключается на короткие реплики
  (удержание 1.2 с) и не уводится с идущей демонстрации экрана. Среди
  нескольких одновременно говорящих крупно показывается тот, у кого включена
  камера. Мини-плеер (Document PiP) работает как раньше.
- Демонстрация экрана по-прежнему главнее всего: она перебивает и
  закрепление, а после её завершения фокус возвращается на закреплённого
  участника.
- Футер админки заменён общим компонентом; в брендовой панели экранов входа
  строка копирайта с захардкоженным годом убрана — год теперь берётся
  текущий.

### Исправлено
- Календарь: «г.» после года убрана и из подписей дней в самой сетке (видны
  как всплывающая подсказка и в accessibility-дереве) — в недельном и
  месячном виде. В 0.0.4 это было исправлено только для подписи периода.

## [0.0.5] — 2026-07-27

Контактный адрес инстанса: настраиваемый адрес для ответов на письма
(уходят от `no-reply@`) и кнопка проверки почтовой конфигурации из
админки.

### Добавлено
- Настройка «Контактный адрес» в админке (вкладка «Настройки») — email
  подставляется в заголовок `Reply-To` писем подтверждения регистрации,
  приглашений на конференции и саммари; при выключенной настройке
  `Reply-To` не проставляется, поведение как раньше.
- Кнопка «Отправить тестовое письмо» — синхронная проверка почтовой
  конфигурации без регистрации фиктивного пользователя. Получатель по
  умолчанию — email текущего администратора, можно указать другой.
  Результат виден сразу: успех либо текст ошибки транспорта (сервер
  недоступен/получатель отклонён), без логина и пароля SMTP в ответе.

## [0.0.4] — 2026-07-26

Календарь конференций: перестроен в раскладку «календарь во всю ширину плюс
модальные окна» — карточки событий разгружены, подробности вынесены во
всплывающее окно.

### Изменения
- Форма создания и редактирования конференции больше не занимает постоянную
  колонку справа, а открывается в модальном окне — по кнопке «Новая
  конференция», по карандашику на карточке события или из информационного
  окна. Добавлен общий хук `useModalDismiss` (закрытие по Escape и возврат
  фокуса на элемент, из которого окно открыли).
- Блок календаря занимает всю ширину страницы.
- Карточка события в сетке содержит только название (обрезается многоточием и
  больше не вылезает за границы ячейки) и — у организатора — кнопку
  редактирования. Время, номер и статус переехали в информационное окно.
- Клик по карточке события всегда открывает информационное окно, в том числе
  для своих конференций: редактирование стало отдельным действием.
- Информационное окно вхождения дополнено статус-бейджем и кнопкой
  «Редактировать» для организатора.

### Исправления
- Подпись периода в недельном и месячном виде больше не содержит «г.»
  (форматирование через `Intl.DateTimeFormat` вместо локали FullCalendar).

## [0.0.3] — 2026-07-26

Мобильная версия: адаптивная вёрстка для узких экранов на ключевых экранах
сервиса, найденная и поправленная по итогам ручного прохода по мобильному
интерфейсу.

### Исправления
- Вход/регистрация (`AuthLayout`): заголовок «Ваша инфраструктура» вылезал за
  край брендовой панели на узких экранах — кегль теперь адаптивно уменьшается
  (`clamp()`) вместо обрезки.
- Комната конференции: топбар и тулбар не помещались на мобильной ширине
  (тулбар с 8 кнопками физически шире экрана, крайние кнопки были обрезаны без
  возможности до них докрутить). На мобильном тулбар сокращён до
  микрофона/камеры/устройств/чата/выхода (демонстрация экрана,
  полноэкранный режим и мини-окно скрыты — редко нужны с телефона), в топбаре
  скрыты название конференции и строка статуса, а чип «№ … / Пригласить»
  оставлен — с телефона он нужнее.
- Админка: заголовок и 4 вкладки раздела не помещались в ширину (вкладки
  переносятся в 2 ряда, поясняющий текст под заголовком скрыт на мобильном);
  таблицы «Конференции» и «Пользователи» обрезались вбок — на мобильном в них
  остаются только ключевые колонки, название конференции стало кнопкой,
  открывающей то же окно редактирования, что и раньше (туда же добавлена
  кнопка удаления — на мобильном столбец действий скрыт).
- Подключение к конференции (`/join`): кнопка «Ввести другую ссылку или
  номер» на экране завершённой конференции вылезала за карточку на узких
  экранах (единственная кнопка на этом флоу без формы-обёртки, задающей ей
  ширину, а `.btn` держит `white-space: nowrap`).

## [0.0.2] — 2026-07-26

Релиз воспроизводимого боевого развёртывания: конфигурация деплоя перенесена в
репозиторий как источник истины, устранён инцидент безопасности, добавлена
пошаговая инструкция для девопсов.

### Безопасность
- `postgres` и `redis` сняты с публикации на `0.0.0.0` и привязаны к `127.0.0.1`
  (docker писал DNAT в обход `ufw`, порты были открыты в интернет).
- Порты `backend`, `livekit`-signaling, `prometheus`, `grafana`, `llm` также
  переведены на loopback — наружу остаются только `80/443/7881` (+ UDP `54000-54100`).
- Для `redis` включена обязательная аутентификация (`--requirepass`); пароль
  проброшен во всех потребителей (backend, worker, exporter, livekit, egress).

### Развёртывание и инфраструктура
- `nginx`/`coturn`/`livekit`: TLS на 443, http→https редирект, ACME-webroot;
  конфиги вынесены в `*.template` и рендерятся через `deploy/render-templates.sh`
  (`envsubst`). Секреты — только из `.env`, обязательны (`${VAR:?}`), без
  небезопасных дефолтов.
- `--env-file .env` добавлен во все вызовы `docker compose` (без него compose не
  подхватывал корневой `.env` и брал dev-дефолты — корень бага «конференции не
  работают у других» и `WARN: LIVEKIT_API_KEY not set`).
- Мониторинг развязан от опционального LLM: `job=llm`/алерт `LlmDown`
  закомментированы для профилей без `llm`/`llm-gpu`.
- `install.sh`: fail-fast валидация — установка останавливается с понятной
  ошибкой, если на боевом домене остались dev-плейсхолдеры (`example.com`,
  `ws://localhost`, `devkey` и т.п.), вместо тихого подъёма нерабочего прода.
- `install.sh`: флаг `--monitoring` (Grafana/Prometheus раньше не поднимались
  ни в одном пресете).
- `.env.example` покрывает все параметры деплоя; дефолтные профили —
  `media,monitoring`.

### Исправления
- `nginx`: `/openapi.json` больше не перехватывается SPA-фолбэком, а отдаётся
  схемой backend.
- Предупреждение в `.env.example`: `LIVEKIT_PUBLIC_URL` в HTTPS-проде обязан
  быть `wss://`.

### Документация
- Добавлено `docs/deploy/DEPLOYMENT.md` — полное руководство от голой Ubuntu 24
  до рабочего `https://<домен>`: SSL, `.env`, профили, траблшутинг, доступ к
  мониторингу через ssh-туннель, описание панелей, runbook включения
  транскрибации/суммаризации. Ссылки добавлены в `README.md`.
- `docs/deploy/install.md`: задокументирован флаг `--monitoring`.

### Примечания по эксплуатации
- На боевом сервере исправлен certbot deploy-hook (`nginx -s reload` → `restart`):
  без этого после автопродления сертификата nginx продолжал бы отдавать старый.
- TURN-раздача клиентам (`rtc.turn_servers` + TURN/TLS на 443) остаётся
  опциональной — кросс-сетевое медиа проверено вживую и работает без неё;
  нужна только для экстремального NAT (CGNAT/жёсткий корпоративный firewall).

## [0.0.1]

Первоначальная версия VidConf.

[0.0.10]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.10
[0.0.9]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.9
[0.0.8]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.8
[0.0.7]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.7
[0.0.6]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.6
[0.0.5]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.5
[0.0.4]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.4
[0.0.3]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.3
[0.0.2]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.2
[0.0.1]: https://github.com/MaxRonzhin/vidconf_rel/releases/tag/v0.0.1
