Files
vidconf/backend/repositories/users.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

76 lines
3.0 KiB
Python
Raw 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.
"""Репозиторий доступа к таблице `users`."""
import uuid
from datetime import datetime
from sqlalchemy import or_, select
from sqlalchemy.ext.asyncio import AsyncSession
from models.user import User
class UserRepository:
"""Инкапсулирует SQL-запросы к пользователям."""
def __init__(self, session: AsyncSession) -> None:
self._session = session
async def get_by_email(self, email: str) -> User | None:
"""Найти пользователя по email (регистр значим, как задано в БД)."""
result = await self._session.execute(select(User).where(User.email == email))
return result.scalar_one_or_none()
async def get_by_id(self, user_id: uuid.UUID) -> User | None:
"""Найти пользователя по id."""
return await self._session.get(User, user_id)
async def create(
self,
*,
email: str,
name_user: str,
password_hash: str,
team_id: uuid.UUID | None = None,
consent_version: int | None = None,
consent_given_at: datetime | None = None,
) -> User:
"""Создать нового пользователя (role='user', email_verified=False по умолчанию).
`consent_version`/`consent_given_at` — редакция регламента обработки
персональных данных, с которой согласился пользователь, и время
согласия; `None` у обоих, если согласие не запрашивалось (модуль
выключен) — см. `services.auth.AuthService.register`.
"""
user = User(
email=email,
name_user=name_user,
password_hash=password_hash,
team_id=team_id,
consent_version=consent_version,
consent_given_at=consent_given_at,
)
self._session.add(user)
await self._session.flush()
return user
async def list_all(self) -> list[User]:
"""Список всех пользователей (для мультиселекта участников брони, без пагинации)."""
result = await self._session.execute(select(User).order_by(User.name_user))
return list(result.scalars().all())
async def search(self, *, q: str | None, limit: int) -> list[User]:
"""Пикер участников конференции: без `q` — полный список (как `list_all`);
с `q` — поиск по имени/email (`ILIKE`), ограниченный `limit`.
"""
if not q:
return await self.list_all()
like = f"%{q}%"
stmt = (
select(User)
.where(or_(User.name_user.ilike(like), User.email.ilike(like)))
.order_by(User.name_user)
.limit(limit)
)
result = await self._session.execute(stmt)
return list(result.scalars().all())