Files
vidconf/CHANGELOG.md

59 KiB
Raw Blame History

Изменения

Формат основан на Keep a Changelog, проект придерживается семантического версионирования.

[0.0.28] — 2026-08-04

Очередь поднятых рук видна всем участникам + отключаемый модуль.

Добавлено

  • Очередь поднятых рук больше не спрятана от обычных участников — её видит любой, кто в конференции, не только организатор. Опустить чужую руку по-прежнему может только организатор: сервер это уже проверял (api/chat.py, задача B1), правки — только на фронте, кнопка «Опустить» показывается у записи, если это своя рука либо пользователь сам организатор.
  • «Поднятие руки» (кнопка «Рука» + очередь целиком) — отключаемый модуль в админке (instance_settings.hand_queue, дефолт enabled=true — поведение существующих инсталляций не меняется). Настройка едет участнику в JoinOut (как chat_enabled) до входа в комнату. Выключенный модуль гасит кнопки на фронте и отклоняет raise_hand/lower_hand на сервере кодом hand_queue_disabled — вторая линия защиты для клиента со старым JoinOut на руках. Переключение применяется со следующего входа в комнату (та же застылость на время жизни соединения, что и у chat_enabled).

[0.0.27] — 2026-08-04

Разрыв связи больше не выбрасывает участника из конференции.

Исправлено

  • Телефон с погасшим экраном выпадал из конференции, а при возвращении мог оказаться в лобби вместо комнаты. Причина установлена по логам прода: Chrome срезает ресурсы фоновой вкладке, телефон перестаёт отвечать по ICE, и LiveKit закрывает участника через 5 с после потери соединения (замерено: 37 с после блокировки экрана, 23 с после сворачивания браузера — то есть достаточно просто убрать вкладку в фон, гасить экран не обязательно). Восстановить сессию после этого нельзя — участника на сервере уже нет; нужен полный повторный вход, и livekit-client пытается сделать его сам, но его бюджет повторов (10 попыток за ~44 с) в фоновой вкладке успевает сгореть. Тогда приходило событие Disconnected, и страница комнаты уводила пользователя в лобби.

    Теперь непреднамеренный разрыв не уводит со страницы: сбрасывается состояние входа, и работает уже имевшийся путь авто-перезахода — резолв конференции, свежий токен, вход заново. Намеренный выход отличается по нажатию кнопки «Выйти», а не по коду причины: причину CLIENT_INITIATED присылает и кнопка, и сам livekit-client, который при заморозке вкладки (событие freeze) вызывает disconnect() самостоятельно. Разрывы, после которых возвращаться нельзя (участника выгнал организатор, конференция закрыта, вход той же личностью с другого устройства), уводят в лобби как раньше. От цикла «вошёл — сразу выбросило» защищает лимит в 5 перезаходов подряд; соединение, прожившее дольше 30 с, счётчик обнуляет.

[0.0.26] — 2026-08-04

Мобильная комната: свайп шторки настроек и полноэкранный режим.

Исправлено

  • Шторка «Настройки» на мобильном не закрывалась свайпом вниз почти никогда: обработчики висели только на ручке-волоске (.room-sheet-handle, 40×4px) — палец в неё практически невозможно попасть, и палец почти всегда приземлялся на панель, где обработчиков не было вовсе. Теперь свайп закрывает шторку при жесте по любому месту панели, но только когда её содержимое проскроллено в самый верх (scrollTop === 0 на начало жеста) — иначе свайп вниз листает список устройств, как и должен стандартный bottom sheet.

Добавлено

  • Полноэкранный режим комнаты — на мобильном кнопка «Экран» в тулбаре (как на десктопе), при активации топбар и тулбар уходят из потока и лежат оверлеем поверх сцены: показываются по тапу/клику по сцене вне элементов управления и сами прячутся через несколько секунд бездействия. На десктопе — та же логика (топбар/тулбар тоже прячутся в полноэкранном режиме), дополнительный способ вернуть их — навести мышь в нижнюю полосу экрана.

Изменено

  • Кнопка настроек устройств в тулбаре на мобильном подписана «Настройки» вместо «Устройства».
  • Раскладка кнопок мобильного тулбара, когда они не помещаются в один ряд (7 и больше — с полноэкранным режимом и «Очередью» у организатора это обычный случай), стала равномерной сеткой на 4 колонки (7 → 4+3, 8 → 4+4) вместо переноса «как получится» через flex-wrap.
  • deploy/render-templates.sh умеет читать значения из файла, заданного переменной ENV_FILE, а не только из корневого .env (см. коммит 3847476, вошёл в этот релиз) — для локальных стендов, где корневой .env указывает на боевые адреса.

[0.0.25] — 2026-08-03

Два дефекта мини-окна конференции (Document PiP), оба видны только в Chrome.

Исправлено

  • В Chrome пропадал звук других участников после возврата сцены из мини-окна в основное. RoomAudioRenderer жил внутри RoomStage и рендерился в обеих её ветках, поэтому скрытые <audio> с чужими аудиотреками физически переезжали в документ PiP-окна и обратно. После такого переезда Chrome теряет аудиовыход у remote-трека: пакеты продолжают приходить (packetsReceived растёт), а totalSamplesDuration и totalAudioEnergy замирают, и трек молчит даже в свежесозданном <audio> со свежим MediaStream. Тот же цикл detach/attach в пределах одного документа безвреден — дело именно в переезде между документами. Рендерер вынесен в RoomPage: один экземпляр, всегда в основном документе, в PiP-окне аудиоэлементов нет вовсе. В Safari бага не было — там Document PiP не используется (video-фолбэк), сцена из основного окна не уезжает.
  • В мини-окне не действовало закрепление участника, а демонстрация экрана слетала на говорящего от любой чужой реплики. Мини-плеер намеренно ходил с упрощёнными правилами выбора фокуса (без holdScreenShare, без приоритета говорящего с камерой, без антидребезга) и с собственным локальным состоянием закрепления. Теперь pickStageFocus получает одинаковые правила в обоих вариантах сцены, а закрепление переезжает между окнами тем же мостиком через RoomPage, что и фокус. Сознательно оставлено одно отличие — фолбэк «показать себя» (localKey) только у мини-плеера: это защита от дефекта 0.0.11, когда мини-окно открывалось на самом пользователе.
  • Закрепление сбрасывалось при каждом монтировании сцены: на первом рендере useTracks отдаёт пустой набор треков, и правило «закреплённый вышел из комнаты» принимало это за уход участника.

Изменено

  • Кнопка-булавка на плитке подписана «Закрепить» вместо «Закрепить в основном окне» — закрепление больше не ограничено основным окном.

[0.0.24] — 2026-08-03

Три артефакта вёрстки, вылезающие за границы блоков (эмодзи-поповер чата, заголовок брендовой панели, чат в Firefox) + аудит похожих мест.

Исправлено

  • Заголовок «Ваша инфраструктура» на странице входа вылезал за край брендовой панели на широком окне с узкой панелью (.layout — flex 42/58) — старый фикс уменьшал кегль только по ширине ОКНА (@media), а не панели. Кегль .brand-headline теперь считается через container query (container-type: inline-size + cqw) — тот же приём, что у аватара участника в комнате. Тем же механизмом обрезались плашки статистики («AI-саммари») — .brand-stats теперь переносит их на мобильном и десктопе одинаково.
  • Эмодзи-поповер в чате комнаты: последняя (5-я) колонка вылезала за правый край поповера. Причина — гонка CSS-специфичности: правило круглой кнопки «Отправить» (.chat-input-row button, 42×42px) продолжало красить размер и кнопкам эмодзи внутри поповера (та же гонка чинилась для цвета в 0.0.19, но не для размера). Добавлены явные width/height: auto нужной специфичности + max-width на попап как общая страховка.
  • Чат комнаты вылезал за границы панели в Firefox: .chat-messages (flex-колонка) без min-height: 0 не сжималась в Firefox, textarea поля ввода без min-width: 0 упиралась в автоматическую минимальную ширину (Firefox считает её от атрибута cols, жёстче Chrome/Safari).

[0.0.23] — 2026-08-02

Документация по сайзингу под медиа-нагрузку + видимость TURN-аллокаций в логах.

Добавлено

  • docs/deploy/hardware-sizing.md — таблица «профиль нагрузки → CPU/RAM/ полоса» для медиа (видеоконференции), на реальных боевых замерах 28.07 и 31.07.2026, с формулой для расчёта под свой сценарий.

Исправлено

  • coturn: включено verbose-логирование — дефолтный уровень не писал построчно ALLOCATE/CreatePermission/Refresh даже при рабочем relay-соединении (найдено на релизе 0.0.22 — звонок через TURN работал, а grep -ci allocate по логам coturn показывал 0).

[0.0.22] — 2026-08-02

TURN over TLS (5349) — для клиентов из сетей, где наружу открыт только 443.

Добавлено

  • coturn слушает TLS на 5349 (сертификат Let's Encrypt, обновляется автоматически). Включается одним ключом TURN_TLS_HOST в .env (домен сертификата, НЕ IP) — пусто оставляет прежнее поведение без единого следа в рендеренных конфигах.
  • LiveKit объявляет клиентам запись protocol: tls в rtc.turn_servers (последней в списке, как самый дорогой фолбэк) — без анонса включённый TLS был бы бесполезен: именно так уже случалось с обычным TURN до 0.0.14 (сервис работал healthy, но не обслужил ни одной аллокации).
  • Новый init-контейнер coturn-certs-init: копирует fullchain/privkey из /etc/letsencrypt в отдельный volume под правами 644 — coturn (nobody:nogroup, без root-фазы в entrypoint) не может прочитать оригинальный ключ (root, 0600), а права на хосте ослаблять нельзя.
  • Deploy-hook certbot дополнительно перекопирует сертификат и перезапускает coturn при продлении — без этого TLS-TURN тихо остановился бы обслуживать новые TLS-хендшейки примерно через 60 дней.

Не сделано

  • TURN over 443 — недостижимо без SNI-мультиплексора: порт уже занят nginx (Docker port-publish), а coturn слушает в network_mode: host и не может разделить с ним один и тот же сокет.

[0.0.21] — 2026-08-02

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

Добавлено

  • Настройка инстанса «Потолок качества публикации видео» (без ограничения / 720p / 360p / 180p) — режет битрейт исходящего видео публикующего через publishDefaults LiveKit, снижает нагрузку на его канал и устройство. Дефолт — без ограничения, поведение существующих инсталляций не меняется.
  • Настройка инстанса «Максимум плиток на экране» (25 / 16 / 9 / 4) — участники сверх лимита уходят на следующую страницу сетки вместо подписки на их видеотреки, меньше одновременных видеопотоков на канал и экран участника. Дефолт — 25 (текущий максимум сетки 5×5), без изменений.
  • Обе настройки доступны в новой карточке «Нагрузка» вкладки «Настройки» админки и отдаются участнику вместе с токеном входа в конференцию — ещё до подключения к комнате, чтобы применяться до публикации трека и не вызывать переподключение уже вошедших участников при смене настройки.

[0.0.20] — 2026-08-02

Сеть LiveKit: один UDP-порт вместо диапазона на 101 порт.

Исправлено

  • Весь медиа-трафик конференций шёл через userland-прокси Docker: диапазон 54000-54100/udp заставлял поднимать по отдельному процессу docker-proxy на каждый порт. LiveKit переведён на rtc.udp_port (один порт, мультиплексирование ICE-сессий по ufrag внутри самого сервера) — проброс портов схлопнут до одного, TURN не затронут. Проверено локально синтетической нагрузкой (lk load-test, 2 видео + 2 аудио publisher'а, 2 subscriber'а) — 0% потерь пакетов, ICE во всех сессиях выбирает новый единственный порт.

[0.0.19] — 2026-08-02

Правки по замечаниям к части B (комната конференции).

Исправлено

  • Очередь поднятых рук открывалась боковой панелью во весь экран на мобильном — теперь компактный поповер над кнопкой (как «Вид»), размер подстраивается под число записей, после ~10 строк список скроллится.
  • Кнопки нижнего тулбара при сужении окна вылезали за края блока (задачи B1/B2 добавили «Рука»/«Очередь», в тулбаре стало до 11 кнопок вместо восьми) — теперь плавно уменьшаются на диапазоне 1200600px вместо жёсткого скачка на мобильный вид.
  • Подпись «Мини-окно» на промежуточных ширинах переносилась на 2 строки и делала эту кнопку выше соседних — ниже 1200px показывается короткое «Мини».
  • Эмодзи-поповер в чате красил все свои кнопки в зелёный цвет кнопки «Отправить» (гонка специфичности CSS-селекторов) — исправлено; заодно сетка приведена к ровным 5×6 без неполной строки, добавлены 🦾 🚀 🦞 💯 🤷‍♂️.

[0.0.18] — 2026-08-01

Ограничение частоты запросов больше не блокирует вход целой конференции.

Исправлено

  • Лимит на резолв конференции и гостевой вход считался по адресу контейнера nginx, а не клиента, — то есть «10 запросов в минуту» действовали на весь сервер разом. Одиннадцатый человек, открывший ссылку в течение минуты, получал отказ и видел «Не удалось найти конференцию» для существующей и активной конференции. Теперь адрес берётся из заголовка, который nginx уже передаёт.
  • Счётчик считает только неудачные попытки — конференция не найдена или пароль неверен. Именно так выглядит перебор номера, от которого защищает ограничение; массовый вход по рабочей ссылке к нему отношения не имеет. На общий поток с адреса оставлен потолок в 300 запросов в минуту.

[0.0.17] — 2026-08-01

Массовый вход в систему и в конференцию перестаёт упираться в проверку пароля.

Исправлено

  • Проверка пароля больше не останавливает весь backend. Хэширование argon2 — это десятки миллисекунд счёта, и выполнялось оно синхронно внутри асинхронного обработчика: пока считался один пароль, процесс не обслуживал ничего другого. На нагрузочном тесте 31.07 с примерно семью десятками одновременных входов это дало p95 логина 7.28 секунды, p95 входа в конференцию 7.06 секунды и 33 соединения к базе, висящих в открытой транзакции при одном активном запросе. Страдали и посторонние запросы — вход в конференцию отказывал, хотя пароль там не проверялся вовсе. Теперь хэширование считается в пуле потоков.
  • Параметры argon2id приведены к рекомендации OWASP (t=2, m=19 МБ, p=1) вместо дефолтов библиотеки (t=3, m=64 МБ, p=4): 95 мс против 42 мс на проверку. Прежнее значение parallelism=4 вдобавок занимало все четыре ядра сервера — те же, на которых работает медиа-сервер.
  • Пароли, сохранённые со старыми параметрами, продолжают работать и перевыпускаются автоматически при первом успешном входе.

[0.0.16] — 2026-08-01

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

Добавлено

  • Роль организатора прокинута в комнату — метаданные LiveKit-токена несут подсказку is_organizer для UI (владелец конференции/администратор); любое серверное действие организатора перепроверяется по владельцу конференции в БД, метаданным токена для авторизации не доверяем.
  • Поднятие руки: кнопка «Рука» в тулбаре (у всех участников, с общим счётчиком), бейдж на плитке говорящего, видимый всем, и панель «Очередь» для организатора — участники в порядке поднятия руки, с возможностью опустить любую руку. Состояние — в Redis (не в БД): очередь существует ровно во время звонка. Участник, вышедший из конференции, автоматически исчезает из очереди; переподключение место не теряет.
  • Принудительный мьют: организатор может выключить микрофон или камеру любого участника — кнопки на его плитке. Участник получает уведомление, что его выключил организатор, и может включить себя обратно сразу тем же тулбаром.
  • Эмодзи скачущего коня 🐎 в наборе смайликов чата.

Изменено

  • Транспорт чата (WS /conferences/{id}/chat) расширен: очередь поднятых рук и уведомления о мьюте едут по тому же аутентифицированному соединению, что и сообщения чата — без нового эндпоинта.

Миграций БД нет — новое состояние (очередь поднятых рук) хранится в Redis, не в Postgres.

[0.0.15] — 2026-07-30

Шесть доработок UI комнаты конференции.

Добавлено

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

Изменено

  • Участник заходит в конференцию с выключенными микрофоном и камерой, включает их сам уже в комнате.
  • В топбаре комнаты вместо технического room_name LiveKit (вида hC-Dmos9KEM) показывается название конференции; при его отсутствии — «Конференция без названия». Номер и ссылка-приглашение не задеты.

[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-сервиса в деплое нет) этот вызов висел 2025 секунд, всё это время удерживая соединение с БД. На нагрузочном тесте с 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 reloadrestart): без этого после автопродления сертификата nginx продолжал бы отдавать старый.
  • TURN-раздача клиентам (rtc.turn_servers + TURN/TLS на 443) остаётся опциональной — кросс-сетевое медиа проверено вживую и работает без неё; нужна только для экстремального NAT (CGNAT/жёсткий корпоративный firewall).

0.0.1

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