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

158
backend/schemas/admin.py Normal file
View File

@@ -0,0 +1,158 @@
"""Pydantic-схемы админ-API (`/api/v1/admin/*`)."""
import uuid
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, ConfigDict, EmailStr, Field
from core.plugins.config import AiLevel, SummaryRecipientsMode
from schemas.conferences import ConferenceOut
from services.ai_levels import AiLevelStatus
class AdminConferenceOut(ConferenceOut):
"""Конференция в ответах админ-API — `ConferenceOut` + данные владельца.
`owner_name`/`owner_email` — `None` для конференции без владельца
(`conferences.owner_id IS NULL`, ADR-001) — колонка «Владелец» в таблице
админки (`frontend/src/api/admin.ts`).
"""
owner_name: str | None = None
owner_email: str | None = None
class AdminConferenceListOut(BaseModel):
"""Страница списка конференций (`GET /admin/conferences`)."""
items: list[AdminConferenceOut]
total: int
class AdminUserOut(BaseModel):
"""Пользователь в ответах админ-API (профиль + служебные поля модерации).
`avatar_url`/`team_name` заполняются явно роутером (не через
`from_attributes` — оба поля вычисляемые, не хранятся как атрибут ORM),
см. `api/admin.py::_to_admin_user_out`.
"""
model_config = ConfigDict(from_attributes=True)
id: uuid.UUID
email: str
name_user: str
role: str
is_blocked: bool
email_verified: bool
created_at: datetime
team_id: uuid.UUID | None = None
avatar_url: str | None = None
team_name: str | None = None
class AdminUserListOut(BaseModel):
"""Страница списка пользователей (`GET /admin/users`)."""
items: list[AdminUserOut]
total: int
class AdminUserCreateIn(BaseModel):
"""Тело создания пользователя администратором (`POST /admin/users`).
Та же политика пароля, что при самостоятельной регистрации
(`RegisterIn.password`, min 8 символов). `team_id` — опциональная
привязка к команде, в отличие от публичной регистрации не завязана на
настройку `registration_team_choice` (админ назначает команду всегда).
"""
name_user: str = Field(min_length=1, max_length=255)
email: EmailStr
password: str = Field(min_length=8)
team_id: uuid.UUID | None = None
class AdminUserUpdateIn(BaseModel):
"""Тело правки пользователя администратором — роль, блокировка, ФИО и/или команда.
`team_id` не различает «поле не передано» и «явный `null`» через
сравнение с `None` — роутер читает `model_fields_set`, чтобы явный сброс
команды (`{"team_id": null}`) отличался от отсутствия поля в запросе
(тот же паттерн, что `summary_recipients` в `schemas/conferences.py`).
`name_user` — та же правка, что доступна пользователю в своём профиле
(редактирование по тем же правилам).
"""
role: Literal["admin", "user"] | None = None
is_blocked: bool | None = None
team_id: uuid.UUID | None = None
name_user: str | None = Field(default=None, min_length=1, max_length=255)
class TeamOut(BaseModel):
"""Команда в ответах админ-API."""
model_config = ConfigDict(from_attributes=True)
id: uuid.UUID
name: str
created_at: datetime
class TeamListOut(BaseModel):
"""Список команд (`GET /admin/teams`), отсортирован по названию."""
items: list[TeamOut]
total: int
class TeamCreateIn(BaseModel):
"""Тело создания команды; `name` обрезается от пробелов до проверки длины."""
model_config = ConfigDict(str_strip_whitespace=True)
name: str = Field(min_length=1, max_length=255)
class TeamUpdateIn(BaseModel):
"""Тело переименования команды; `name` обрезается от пробелов до проверки длины."""
model_config = ConfigDict(str_strip_whitespace=True)
name: str = Field(min_length=1, max_length=255)
class InvitationsSendIn(BaseModel):
"""Тело ручной рассылки .ics-приглашений (`POST /admin/conferences/{id}/invitations`).
Пустой список получателей не отличается от отсутствующего поля — оба
трактуются как «получатели по умолчанию» (см. `workers/tasks/invitations.py`).
"""
emails: list[EmailStr] | None = None
class SettingsOut(BaseModel):
"""Эффективные настройки инстанса для отображения в админке.
`transcription_queue_served` — обслуживается ли очередь `transcription`
хотя бы одним воркером Celery
прямо сейчас (`services.pipeline_producer.transcription_queue_served`);
`False` при включённой транскрибации — сигнал админке показать
предупреждение рядом с чекбоксом AI («модуль включён, но задачи некому
обрабатывать»), не связано с доступностью уровня AI (`ai_levels`,
которая смотрит только на железо/скачанные модели).
"""
chat_enabled: bool
transcription_enabled: bool
ai_level: AiLevel
ai_levels: list[AiLevelStatus]
transcription_queue_served: bool
summary_recipients: SummaryRecipientsMode
display_timezone: str
registration_team_choice: bool
registration_email_domain_enabled: bool
registration_email_domain: str | None = None