Группы: приглашения вместо авто-добавления (согласиться/отказаться)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 20:07:20 +03:00
co-authored by Claude Opus 4.8
parent 66370c7df4
commit a5c54512a5
20 changed files with 742 additions and 98 deletions
+16 -7
View File
@@ -5,12 +5,14 @@ from fastapi import APIRouter, Depends, Query, Request
from sqlmodel import Session
from app.auth.deps import get_current_user
from app.core.timeutil import iso_utc
from app.db.session import get_session
from app.models import User
from app.schemas import api as s
from app.services import (
audit_service,
group_service,
invitation_service,
membership_service,
stats_service,
)
@@ -128,29 +130,36 @@ def list_members(
]
@router.post("/{group_id}/members", response_model=s.MemberRead)
def add_member(
@router.post("/{group_id}/invitations", response_model=s.InvitationRead)
def invite_member(
group_id: int,
body: s.MemberAdd,
request: Request,
session: Session = Depends(get_session),
user: User = Depends(get_current_user),
) -> s.MemberRead:
) -> s.InvitationRead:
# Приглашать вправе ЛЮБОЙ участник группы (не только владелец) — assert_member.
group_service.assert_member(session, group_id, user.id) # type: ignore[arg-type]
group = group_service.get_group(session, group_id)
member, added = membership_service.add_member_by_nickname(session, group, body.nickname)
inv, invited = invitation_service.create_invitation(session, group, user, body.nickname)
audit_service.record(
session,
actor_id=user.id,
action="create",
entity_type="group_member",
entity_type="group_invitation",
entity_id=group_id,
payload={"added_user_id": added.id, "nickname": added.nickname},
payload={"invited_user_id": invited.id, "nickname": invited.nickname},
ip=request.client.host if request.client else None,
user_agent=request.headers.get("user-agent"),
)
session.commit()
return s.MemberRead(user_id=added.id, nickname=added.nickname, role=member.role)
return s.InvitationRead(
id=inv.id, # type: ignore[arg-type]
group_id=group_id,
group_name=group.name,
invited_by_nickname=user.nickname,
created_at=iso_utc(inv.created_at),
)
@router.delete("/{group_id}/members/{user_id}", response_model=s.OkResponse)
+41
View File
@@ -0,0 +1,41 @@
"""Входящие приглашения игрока: список, принятие, отклонение."""
from __future__ import annotations
from fastapi import APIRouter, Depends
from sqlmodel import Session
from app.auth.deps import get_current_user
from app.db.session import get_session
from app.models import User
from app.schemas import api as s
from app.services import invitation_service
router = APIRouter(prefix="/invitations", tags=["invitations"])
@router.get("", response_model=list[s.InvitationRead])
def my_invitations(
session: Session = Depends(get_session),
user: User = Depends(get_current_user),
) -> list[dict]:
return invitation_service.list_for_user(session, user.id) # type: ignore[arg-type]
@router.post("/{invitation_id}/accept", response_model=s.OkResponse)
def accept_invitation(
invitation_id: int,
session: Session = Depends(get_session),
user: User = Depends(get_current_user),
) -> s.OkResponse:
invitation_service.accept(session, user, invitation_id)
return s.OkResponse()
@router.post("/{invitation_id}/decline", response_model=s.OkResponse)
def decline_invitation(
invitation_id: int,
session: Session = Depends(get_session),
user: User = Depends(get_current_user),
) -> s.OkResponse:
invitation_service.decline(session, user, invitation_id)
return s.OkResponse()