feat: add delete avatar
This commit is contained in:
@@ -2,6 +2,7 @@ from src.presentation.dependencies.commands import (
|
||||
get_get_me_command,
|
||||
get_set_phone_command,
|
||||
get_set_avatar_command,
|
||||
get_delete_avatar_command,
|
||||
get_set_encrypted_mnemonic_start_command,
|
||||
get_set_encrypted_mnemonic_complete_command,
|
||||
get_update_bank_details_start_command,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from fastapi import Depends
|
||||
from src.application.abstractions import IUnitOfWork
|
||||
from src.application.commands import GetMeCommand, SetPhoneCommand, SetAvatarCommand, SetEncryptedMnemonicStartCommand, SetEncryptedMnemonicCompleteCommand, UpdateBankDetailsStartCommand, UpdateBankDetailsCompleteCommand, ChangePasswordStartCommand, ChangePasswordCompleteCommand, ChangeEmailStartCommand, ChangeEmailConfirmOldCommand, ChangeEmailCompleteCommand
|
||||
from src.application.commands import GetMeCommand, SetPhoneCommand, SetAvatarCommand, DeleteAvatarCommand, SetEncryptedMnemonicStartCommand, SetEncryptedMnemonicCompleteCommand, UpdateBankDetailsStartCommand, UpdateBankDetailsCompleteCommand, ChangePasswordStartCommand, ChangePasswordCompleteCommand, ChangeEmailStartCommand, ChangeEmailConfirmOldCommand, ChangeEmailCompleteCommand
|
||||
from src.application.contracts import ILogger, ICache, IQueueMessanger, IHashService, IS3
|
||||
from src.presentation.dependencies.cache import get_cache
|
||||
from src.presentation.dependencies.logger import get_logger
|
||||
@@ -35,6 +35,15 @@ def get_set_avatar_command(
|
||||
return SetAvatarCommand(unit_of_work=unit_of_work, logger=logger, cache=cache, s3=s3)
|
||||
|
||||
|
||||
def get_delete_avatar_command(
|
||||
logger: ILogger = Depends(get_logger),
|
||||
unit_of_work: IUnitOfWork = Depends(get_unit_of_work),
|
||||
cache: ICache = Depends(get_cache),
|
||||
s3: IS3 = Depends(get_s3_storage),
|
||||
) -> DeleteAvatarCommand:
|
||||
return DeleteAvatarCommand(unit_of_work=unit_of_work, logger=logger, cache=cache, s3=s3)
|
||||
|
||||
|
||||
def get_set_encrypted_mnemonic_start_command(
|
||||
logger: ILogger = Depends(get_logger),
|
||||
unit_of_work: IUnitOfWork = Depends(get_unit_of_work),
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import ORJSONResponse
|
||||
from starlette import status
|
||||
from src.application.commands import SetPhoneCommand, SetAvatarCommand
|
||||
from src.application.commands import SetPhoneCommand, SetAvatarCommand, DeleteAvatarCommand
|
||||
from src.application.domain.dto import AuthContext
|
||||
from src.presentation.decorators import require_access_token, csrf_protect
|
||||
from src.presentation.dependencies import (
|
||||
get_delete_avatar_command,
|
||||
get_set_avatar_command,
|
||||
get_set_phone_command,
|
||||
)
|
||||
from src.presentation.schemas import SetAvatarRequest, SetPhoneRequest
|
||||
from src.presentation.schemas.api_errors import ApiErrorPayload, ApiValidationErrorsPayload
|
||||
from src.presentation.schemas.me_public import SetAvatarPublicResponse
|
||||
from src.presentation.schemas.me_public import MeUserPublicResponse, SetAvatarPublicResponse
|
||||
from src.presentation.serializers import me_user_public
|
||||
|
||||
|
||||
@@ -45,6 +46,26 @@ _SET_AVATAR_ERROR_RESPONSES: dict[int, dict[str, object]] = {
|
||||
}
|
||||
|
||||
|
||||
_DELETE_AVATAR_ERROR_RESPONSES: dict[int, dict[str, object]] = {
|
||||
status.HTTP_401_UNAUTHORIZED: {
|
||||
'description': 'Не передан или неверен access token.',
|
||||
'model': ApiErrorPayload,
|
||||
},
|
||||
status.HTTP_404_NOT_FOUND: {
|
||||
'description': 'Учётная запись не найдена.',
|
||||
'model': ApiErrorPayload,
|
||||
},
|
||||
status.HTTP_500_INTERNAL_SERVER_ERROR: {
|
||||
'description': 'Внутренняя ошибка сервера; клиенту отдаётся обобщённое сообщение.',
|
||||
'model': ApiErrorPayload,
|
||||
},
|
||||
status.HTTP_503_SERVICE_UNAVAILABLE: {
|
||||
'description': 'S3 не сконфигурирован или временная недоступность удаления объекта.',
|
||||
'model': ApiErrorPayload,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@account_settings_router.patch(path='/phone', response_class=ORJSONResponse, status_code=status.HTTP_200_OK)
|
||||
@csrf_protect()
|
||||
async def set_phone(
|
||||
@@ -64,7 +85,8 @@ async def set_phone(
|
||||
response_model=SetAvatarPublicResponse,
|
||||
summary='Обновить аватар',
|
||||
description=(
|
||||
'Принимает фото в Base64, сохраняет как WebP в объектном хранилище и записывает публичный URL в профиль.'
|
||||
'Принимает фото в Base64, сохраняет как WebP в объектном хранилище и записывает публичный URL в профиль. '
|
||||
'После успешной записи удаляется предыдущий объект в S3 (если ссылку удаётся сопоставить с ключом).'
|
||||
),
|
||||
response_description=(
|
||||
'Профиль пользователя в том же формате, что и GET /me, плюс размер сохранённого WebP.'
|
||||
@@ -82,6 +104,27 @@ async def set_avatar(
|
||||
pub = me_user_public(user)
|
||||
return SetAvatarPublicResponse(**pub.model_dump(), webp_size_bytes=webp_size)
|
||||
|
||||
|
||||
@account_settings_router.delete(
|
||||
path='/avatar',
|
||||
response_class=ORJSONResponse,
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=MeUserPublicResponse,
|
||||
summary='Удалить аватар',
|
||||
description=(
|
||||
'Удаляет файл в объектном хранилище при известном URL и обнуляет avatar_link в профиле.'
|
||||
),
|
||||
responses=_DELETE_AVATAR_ERROR_RESPONSES,
|
||||
)
|
||||
@csrf_protect()
|
||||
async def delete_avatar(
|
||||
request: Request,
|
||||
auth: AuthContext = Depends(require_access_token),
|
||||
command: DeleteAvatarCommand = Depends(get_delete_avatar_command),
|
||||
) -> MeUserPublicResponse:
|
||||
user = await command(user_id=auth.user_id)
|
||||
return me_user_public(user)
|
||||
|
||||
#
|
||||
# @account_settings_router.post(path='/encrypted-mnemonic/start', response_class=ORJSONResponse, status_code=status.HTTP_200_OK)
|
||||
# async def encrypted_mnemonic_start(
|
||||
|
||||
Reference in New Issue
Block a user