Первоначальная версия VidConf
This commit is contained in:
158
backend/schemas/admin.py
Normal file
158
backend/schemas/admin.py
Normal 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
|
||||
Reference in New Issue
Block a user