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

This commit is contained in:
2026-07-23 01:04:01 +03:00
commit 896455381a
335 changed files with 61527 additions and 0 deletions

120
deploy/nginx/nginx.conf Normal file
View File

@@ -0,0 +1,120 @@
# Dev reverse proxy: backend API + раздача фронтенд-SPA (статика в образе).
# Завершение TLS для production документировано в docs/deploy/dev-setup.md
# и должно добавляться отдельным server-блоком (443) с реальными сертификатами.
#
# ВАЖНО (stale DNS): статический `upstream { server backend:8000; }` nginx
# резолвит один раз при старте/reload и держит IP в памяти. После пересоздания
# контейнера backend (`docker compose up -d --force-recreate backend`) Docker
# выдаёт ему новый IP, а nginx продолжает стучаться по старому → 502, пока
# nginx не перезапустят. Чтобы резолвить имя заново на каждый запрос,
# используем embedded DNS Docker (127.0.0.11) через directive `resolver` и
# ПЕРЕМЕННУЮ в proxy_pass — переменные nginx не кэшируются на старте и
# резолвятся заново по истечении `valid=`.
resolver 127.0.0.11 valid=10s ipv6=off;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name _;
# --- Backend API ---
# Проброс через переменную: proxy_pass с переменной НЕ выполняет
# автоматическую подстановку URI (в отличие от статического
# `proxy_pass http://upstream/prefix/;`), поэтому URI-часть в proxy_pass
# не указываем — nginx передаёт исходный URI запроса как есть (path +
# query), что и сохраняет прежний маппинг /api/ -> backend:8000/api/
# (префикс совпадает 1-в-1, подмены не требовалось и раньше).
# Upgrade-заголовки нужны и обычным HTTP-запросам (без `Upgrade` в запросе
# `map $http_upgrade $connection_upgrade` выше подставляет `close`, что не
# ломает keep-alive обычных ответов), и WS-эндпоинту чата конференции
# (`WS /api/v1/conferences/{id}/chat`) — он живёт под тем же
# префиксом /api/, а не под отдельным /ws/.
location /api/ {
set $backend_upstream http://backend:8000;
proxy_pass $backend_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
# --- WebSocket upgrade для чата и LiveKit signaling через nginx ---
location /ws/ {
set $backend_upstream http://backend:8000;
proxy_pass $backend_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 3600s;
}
# --- LiveKit SFU signaling (WebSocket) ---
# Доступен только когда стек запущен с профилем `media`
# (см. docker-compose.yml) — без него resolver вернёт NXDOMAIN на первый
# запрос к этому location, что ожидаемо.
# Здесь префикс /livekit/ нужно СНЯТЬ (как раньше делал
# `proxy_pass http://livekit_upstream/;`). С переменной в proxy_pass
# автоматическая подмена недоступна, поэтому переписываем URI явно через
# rewrite ... break — nginx передаст на upstream уже переписанный $uri.
# ВАЖНО: `set` должен идти ДО `rewrite ... break` — break прерывает
# выполнение всех последующих директив модуля rewrite (включая set) в
# этом location, иначе переменная останется неинициализированной.
location /livekit/ {
set $livekit_upstream http://livekit:7880;
rewrite ^/livekit/(.*)$ /$1 break;
proxy_pass $livekit_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 3600s;
}
# --- Метрики backend: `GET /metrics` без авторизации
# внутри приложения (backend/api/metrics.py) — снаружи периметра нарочно
# закрываем явным запретом. Это ОБЯЗАТЕЛЬНО именно exact-match
# (`location = /metrics`): ниже есть catch-all `location /` (раздача
# фронтенд-SPA), который иначе отдал бы на /metrics файл фронта (или
# index.html через try_files). Exact-match в nginx приоритетнее любого
# prefix-location, поэтому 403 срабатывает раньше SPA-раздачи.
# Prometheus (профиль compose `monitoring`) ходит в backend НАПРЯМУЮ по
# внутренней docker-сети (`backend:8000/metrics`), минуя nginx.
location = /metrics {
return 403;
}
# --- Медиа (аватары): раздача напрямую из volume, в обход backend. ---
location /media/ {
alias /media/;
autoindex off;
}
# --- Frontend SPA (React + Vite): статика вкомпилирована в образ nginx
# (frontend/Dockerfile) в /usr/share/nginx/html. `try_files` с
# history-fallback на /index.html нужен для клиентского роутинга
# react-router (deep-ссылки вида /conferences/<id>, /admin/settings —
# при перезагрузке страницы отдаётся index.html, дальше роутит SPA).
# Более специфичные location выше (/api/, /media/, = /metrics, /livekit/,
# /ws/) матчатся раньше этого catch-all, поэтому API и метрики не
# перехватываются фронтом.
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /healthz {
return 200 "ok\n";
add_header Content-Type text/plain;
}
}