Files
vidconf/backend/models/user.py
Max Ronzhin 4f82ebe17a
Some checks failed
CI / backend (push) Has been cancelled
CI / frontend (push) Has been cancelled
feat(auth): согласие на обработку персональных данных при регистрации
Отключаемый модуль (instance_settings.consent_policy): галочка + ссылка на
публичную страницу регламента на форме регистрации, редактируемый в админке
текст с типовым шаблоном по умолчанию (плейсхолдеры под организацию, не
проходил юридическую проверку), версия текста растёт при каждой правке.
Факт согласия хранится в users (consent_version, consent_given_at) — второй
эшелон проверки на сервере, как и для отключаемых модулей ранее. Дефолт
(выключено) сохраняет поведение существующих инсталляций, у уже
зарегистрированных пользователей согласие не запрашивается.
2026-08-04 22:20:00 +03:00

58 lines
2.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Модель User."""
import uuid
from datetime import datetime
from sqlalchemy import (
Boolean,
CheckConstraint,
DateTime,
ForeignKey,
Integer,
String,
Text,
func,
text,
)
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from models.base import Base
class User(Base):
"""Зарегистрированный пользователь приложения."""
__tablename__ = "users"
__table_args__ = (CheckConstraint("role IN ('admin', 'user')", name="ck_users_role"),)
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, server_default=text("gen_random_uuid()")
)
email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
name_user: Mapped[str] = mapped_column(String(255), nullable=False)
password_hash: Mapped[str] = mapped_column(Text, nullable=False)
role: Mapped[str] = mapped_column(String(16), nullable=False, server_default="user")
email_verified: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false")
# Блокировка администратором: проверяется в `api/deps.py::_user_from_token`,
# действует немедленно — до истечения уже выданного access-токена не ждём.
is_blocked: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="false")
# Привязка к команде (справочник `teams`):
# необязательная, при удалении команды обнуляется (ON DELETE SET NULL).
team_id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("teams.id", ondelete="SET NULL"), nullable=True
)
# Путь к загруженному аватару (относительно `MEDIA_ROOT`):
# `avatars/{user_id}.{ext}`; `NULL` — заглушка с инициалами на фронте.
avatar_path: Mapped[str | None] = mapped_column(String(512), nullable=True)
# Согласие на обработку персональных данных при регистрации: редакция
# регламента (`instance_settings.consent_policy.version` на момент
# согласия) и время. `NULL` у обоих — согласие не запрашивалось (модуль
# был выключен либо пользователь зарегистрирован до появления этой
# настройки); вход таким пользователям не блокируется.
consent_version: Mapped[int | None] = mapped_column(Integer, nullable=True)
consent_given_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), nullable=False, server_default=func.now()
)