Files
ForbiddenStarsApp/backend/app/services/user_service.py
T

95 lines
3.2 KiB
Python

"""Пользователи: создание из внешней личности, ник, активная группа."""
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