v0.1 - макет интерфейса, аутентификация через логин, аккаунт админа, создание партии в 2 этапа, базовые настройки профиля и группы, переключение между группами, статистика
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"""Пользователи: создание из внешней личности, ник, активная группа."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.auth.provider import ExternalIdentity
|
||||
from app.core.errors import NicknameTakenError, NotFoundError, ValidationError
|
||||
from app.models import AuthIdentity, GroupMember, User
|
||||
|
||||
_NICK_RE = re.compile(r"^[\w .\-]{2,64}$", re.UNICODE)
|
||||
|
||||
|
||||
def get_user(session: Session, user_id: int) -> User:
|
||||
user = session.get(User, user_id)
|
||||
if user is None:
|
||||
raise NotFoundError("Пользователь не найден.")
|
||||
return user
|
||||
|
||||
|
||||
def nickname_available(session: Session, nickname: str, exclude_user_id: int | None = None) -> bool:
|
||||
stmt = select(User).where(User.nickname == nickname)
|
||||
existing = session.exec(stmt).first()
|
||||
return existing is None or existing.id == exclude_user_id
|
||||
|
||||
|
||||
def _unique_nickname(session: Session, base: str) -> str:
|
||||
base = (base or "Игрок").strip()[:60] or "Игрок"
|
||||
candidate = base
|
||||
suffix = 1
|
||||
while session.exec(select(User).where(User.nickname == candidate)).first() is not None:
|
||||
suffix += 1
|
||||
candidate = f"{base} {suffix}"
|
||||
return candidate
|
||||
|
||||
|
||||
def get_or_create_from_identity(session: Session, identity: ExternalIdentity) -> User:
|
||||
link = session.exec(
|
||||
select(AuthIdentity).where(
|
||||
AuthIdentity.provider == identity.provider,
|
||||
AuthIdentity.external_id == identity.external_id,
|
||||
)
|
||||
).first()
|
||||
if link is not None:
|
||||
return get_user(session, link.user_id)
|
||||
|
||||
user = User(
|
||||
nickname=_unique_nickname(session, identity.suggested_nickname or identity.external_id),
|
||||
role="player",
|
||||
auth_provider=identity.provider,
|
||||
telegram_id=identity.telegram_id,
|
||||
)
|
||||
session.add(user)
|
||||
session.flush()
|
||||
session.add(
|
||||
AuthIdentity(
|
||||
user_id=user.id, # type: ignore[arg-type]
|
||||
provider=identity.provider,
|
||||
external_id=identity.external_id,
|
||||
)
|
||||
)
|
||||
session.commit()
|
||||
session.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
def update_nickname(session: Session, user: User, new_nickname: str) -> User:
|
||||
new_nickname = (new_nickname or "").strip()
|
||||
if not _NICK_RE.match(new_nickname):
|
||||
raise ValidationError("Ник: 2–64 символа, буквы/цифры/пробел/.-_")
|
||||
if not nickname_available(session, new_nickname, exclude_user_id=user.id):
|
||||
raise NicknameTakenError()
|
||||
user.nickname = new_nickname
|
||||
session.add(user)
|
||||
session.commit()
|
||||
session.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
def set_active_group(session: Session, user: User, group_id: int | None) -> User:
|
||||
if group_id is not None:
|
||||
member = session.exec(
|
||||
select(GroupMember).where(
|
||||
GroupMember.group_id == group_id, GroupMember.user_id == user.id
|
||||
)
|
||||
).first()
|
||||
if member is None:
|
||||
raise ValidationError("Нельзя сделать активной группу, в которой вы не состоите.")
|
||||
user.active_group_id = group_id
|
||||
session.add(user)
|
||||
session.commit()
|
||||
session.refresh(user)
|
||||
return user
|
||||
Reference in New Issue
Block a user