Simplify: общие хелперы в роутерах

- IP клиента для аудита брался инлайном в 19 местах шести модулей; теперь
  security.client_ip — за привратником адрес придётся читать из
  X-Forwarded-For, и одна точка правки для этого обязательна.
- Чтение загруженной картинки (лимит размера + sniff формата) было скопировано
  в четыре обработчика; вынесено в user_service.read_capped_image.
- Лимит размера вложения жил двумя одинаковыми константами в игроцком и
  админском роутере — перенесён к самим вложениям.
- update_nickname и set_active_group переиспользуют nickname_format_ok и
  group_service.get_membership вместо собственных копий проверки.
- Убраны осиротевшие импорты и комментарий-заготовка о вложениях, которые
  давно реализованы (MatchAttachment).

#8

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BoiJK9ux8peeyjLb8TYjFf
This commit is contained in:
2026-09-09 18:48:29 +03:00
co-authored by Claude Opus 5
parent 8ac2757cd6
commit 94e2c09952
10 changed files with 71 additions and 63 deletions
+7 -9
View File
@@ -6,7 +6,8 @@ from fastapi.responses import FileResponse
from sqlmodel import Session
from app.auth.deps import get_current_user
from app.core.errors import NotFoundError, ValidationError
from app.core.security import client_ip
from app.core.errors import NotFoundError
from app.db.session import get_session
from app.models import User
from app.schemas import api as s
@@ -61,7 +62,7 @@ def update_me(
entity_type="user",
entity_id=user.id,
payload={"nickname": user.nickname},
ip=request.client.host if request.client else None,
ip=client_ip(request),
user_agent=request.headers.get("user-agent"),
)
session.commit()
@@ -108,7 +109,7 @@ def update_my_profile(
entity_type="user",
entity_id=user.id,
payload={key: True for key in changed},
ip=request.client.host if request.client else None,
ip=client_ip(request),
)
session.commit()
return build_me(session, user)
@@ -120,12 +121,9 @@ def upload_my_avatar(
session: Session = Depends(get_session),
user: User = Depends(get_current_user),
) -> s.MeRead:
content = file.file.read(_AVATAR_MAX_BYTES + 1)
if len(content) > _AVATAR_MAX_BYTES:
raise ValidationError("Файл слишком большой (макс. 2 МБ).")
ext = user_service.sniff_image_ext(content)
if ext is None:
raise ValidationError("Поддерживаются только изображения PNG, JPEG или WebP.")
content, ext = user_service.read_capped_image(
file, _AVATAR_MAX_BYTES, "Файл слишком большой (макс. 2 МБ)."
)
user_service.set_avatar(session, user, content, ext)
return build_me(session, user)