Первоначальная версия 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

97
backend/main.py Normal file
View File

@@ -0,0 +1,97 @@
"""Точка входа FastAPI приложения."""
import logging
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from api.admin import router as admin_router
from api.auth import router as auth_router
from api.chat import router as chat_router
from api.conferences import router as conferences_router
from api.health import router as health_router
from api.livekit_webhook import router as livekit_webhook_router
from api.metrics import prometheus_latency_middleware
from api.metrics import router as metrics_router
from api.teams import router as teams_router
from api.users import router as users_router
from core.config import get_settings
from core.db import async_session_maker, engine
from core.redis import redis_client
from services.instance_settings import InstanceSettingsService, bootstrap_overrides_from_settings
def _configure_logging() -> None:
"""Настроить базовый вывод логов приложения (INFO) в stdout.
Без этого root-логгер под uvicorn остаётся без хендлера/на уровне
WARNING, и INFO-логи наших модулей (например, `services.email` —
ссылка подтверждения email в dev-режиме) никуда не попадают, включая
`docker logs`. `logging.basicConfig` идемпотентен: если хендлер на
root уже есть (в т.ч. при повторных вызовах `create_app()` в тестах),
он не добавляется повторно — дублирования строк не будет.
"""
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
logging.getLogger().setLevel(logging.INFO)
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
"""Бутстрап настроек инстанса из `plugins.yaml` + освобождение движка БД и Redis при завершении.
Бутстрап (`InstanceSettingsService.ensure_bootstrapped`) идемпотентен
(`INSERT ... ON CONFLICT DO NOTHING`) — безопасен при каждом рестарте
backend, уже сделанные администратором правки настроек не перетираются.
`overrides` — матрица «пресет → настройки» инсталлятора
(`BOOTSTRAP_*` в `.env`): влияет только на чистую БД
(первый запуск), см. докстринг `ensure_bootstrapped`.
"""
settings = get_settings()
overrides = bootstrap_overrides_from_settings(settings)
async with async_session_maker() as session:
await InstanceSettingsService(session).ensure_bootstrapped(
settings.plugins_config_path, overrides
)
try:
yield
finally:
await engine.dispose()
await redis_client.aclose()
def create_app() -> FastAPI:
"""Построить и настроить FastAPI приложение."""
_configure_logging()
app = FastAPI(title="VidConf API", lifespan=lifespan)
# Латентность HTTP по маршрутам — оборачивает
# ВСЕ запросы, включая сам /metrics (шаблон пути резолвится по
# `request.app.routes` в момент запроса, а не при регистрации — порядок
# относительно `include_router` ниже значения не имеет).
app.middleware("http")(prometheus_latency_middleware)
app.include_router(health_router)
app.include_router(metrics_router)
app.include_router(auth_router)
app.include_router(users_router)
app.include_router(teams_router)
app.include_router(conferences_router)
app.include_router(chat_router)
app.include_router(admin_router)
app.include_router(livekit_webhook_router)
# Статика загруженных файлов (аватары) — в проде эту же
# директорию раздаёт nginx (`location /media/`, `deploy/nginx/nginx.conf`),
# здесь — dev-режим/fallback. Каталог создаётся заранее: `StaticFiles`
# падает при монтировании несуществующей директории.
media_root = Path(get_settings().media_root)
media_root.mkdir(parents=True, exist_ok=True)
app.mount("/media", StaticFiles(directory=media_root), name="media")
return app
app = create_app()