Compare commits
3 Commits
21cf44cebc
...
feature/1
| Author | SHA1 | Date | |
|---|---|---|---|
| d794d3f9c6 | |||
| d2e8eb9e4d | |||
| b6ffdd9553 |
@@ -7,6 +7,8 @@ from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
|||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||||
from starlette.middleware.cors import CORSMiddleware
|
from starlette.middleware.cors import CORSMiddleware
|
||||||
|
from starlette.exceptions import HTTPException
|
||||||
|
from fastapi.exceptions import RequestValidationError
|
||||||
from src.application.domain.exceptions import ApplicationException, UnauthorizedException
|
from src.application.domain.exceptions import ApplicationException, UnauthorizedException
|
||||||
from src.infrastructure.cache import create_redis_client
|
from src.infrastructure.cache import create_redis_client
|
||||||
from src.infrastructure.vault import JwtKeyStore, start_jwt_keys_scheduler
|
from src.infrastructure.vault import JwtKeyStore, start_jwt_keys_scheduler
|
||||||
@@ -15,7 +17,9 @@ from src.infrastructure.logger import logger
|
|||||||
from src.infrastructure.config import settings
|
from src.infrastructure.config import settings
|
||||||
from src.presentation.handlers import (
|
from src.presentation.handlers import (
|
||||||
application_exception_handler,
|
application_exception_handler,
|
||||||
|
http_exception_handler,
|
||||||
unhandled_exception_handler,
|
unhandled_exception_handler,
|
||||||
|
validation_exception_handler,
|
||||||
)
|
)
|
||||||
from src.presentation.middleware import TraceIDMiddleware, SecurityHeadersMiddleware
|
from src.presentation.middleware import TraceIDMiddleware, SecurityHeadersMiddleware
|
||||||
from src.presentation.routing import me_router
|
from src.presentation.routing import me_router
|
||||||
@@ -80,6 +84,8 @@ app: FastAPI = FastAPI(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app.add_exception_handler(RequestValidationError, validation_exception_handler)
|
||||||
|
app.add_exception_handler(HTTPException, http_exception_handler)
|
||||||
app.add_exception_handler(ApplicationException, application_exception_handler)
|
app.add_exception_handler(ApplicationException, application_exception_handler)
|
||||||
app.add_exception_handler(Exception, unhandled_exception_handler)
|
app.add_exception_handler(Exception, unhandled_exception_handler)
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
from src.presentation.handlers.unhandled_handler import unhandled_exception_handler
|
from src.presentation.handlers.unhandled_handler import unhandled_exception_handler
|
||||||
from src.presentation.handlers.application_handler import application_exception_handler
|
from src.presentation.handlers.application_handler import application_exception_handler
|
||||||
|
from src.presentation.handlers.http_exception_handler import http_exception_handler
|
||||||
|
from src.presentation.handlers.validation_handler import validation_exception_handler
|
||||||
|
|||||||
11
src/presentation/handlers/http_exception_handler.py
Normal file
11
src/presentation/handlers/http_exception_handler.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from fastapi import Request
|
||||||
|
from fastapi.responses import ORJSONResponse
|
||||||
|
from starlette.exceptions import HTTPException
|
||||||
|
|
||||||
|
|
||||||
|
async def http_exception_handler(_request: Request, exc: HTTPException) -> ORJSONResponse:
|
||||||
|
return ORJSONResponse(
|
||||||
|
status_code=exc.status_code,
|
||||||
|
content={'detail': exc.detail},
|
||||||
|
headers=dict(exc.headers) if exc.headers else None,
|
||||||
|
)
|
||||||
10
src/presentation/handlers/validation_handler.py
Normal file
10
src/presentation/handlers/validation_handler.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from fastapi import Request
|
||||||
|
from fastapi.exceptions import RequestValidationError
|
||||||
|
from fastapi.responses import ORJSONResponse
|
||||||
|
|
||||||
|
|
||||||
|
async def validation_exception_handler(_request: Request, exc: RequestValidationError) -> ORJSONResponse:
|
||||||
|
return ORJSONResponse(
|
||||||
|
status_code=422,
|
||||||
|
content={'detail': exc.errors()},
|
||||||
|
)
|
||||||
@@ -10,7 +10,6 @@ from src.presentation.dependencies.logger import get_logger
|
|||||||
from src.presentation.decorators import csrf_protect
|
from src.presentation.decorators import csrf_protect
|
||||||
from src.presentation.schemas.api_errors import ApiErrorPayload, ApiValidationErrorsPayload
|
from src.presentation.schemas.api_errors import ApiErrorPayload, ApiValidationErrorsPayload
|
||||||
from src.presentation.schemas.me_public import MeUserPublicResponse
|
from src.presentation.schemas.me_public import MeUserPublicResponse
|
||||||
from src.presentation.serializers import me_user_public
|
|
||||||
|
|
||||||
account_router = APIRouter()
|
account_router = APIRouter()
|
||||||
|
|
||||||
@@ -37,6 +36,10 @@ account_router = APIRouter()
|
|||||||
'description': 'Ошибка проверки CSRF (нет пары cookie/заголовка, несовпадение или просрочен токен).',
|
'description': 'Ошибка проверки CSRF (нет пары cookie/заголовка, несовпадение или просрочен токен).',
|
||||||
'model': ApiErrorPayload,
|
'model': ApiErrorPayload,
|
||||||
},
|
},
|
||||||
|
status.HTTP_404_NOT_FOUND: {
|
||||||
|
'description': 'Учётная запись не найдена.',
|
||||||
|
'model': ApiErrorPayload,
|
||||||
|
},
|
||||||
status.HTTP_422_UNPROCESSABLE_ENTITY: {
|
status.HTTP_422_UNPROCESSABLE_ENTITY: {
|
||||||
'description': 'Ошибка валидации входных данных (например, заголовков).',
|
'description': 'Ошибка валидации входных данных (например, заголовков).',
|
||||||
'model': ApiValidationErrorsPayload,
|
'model': ApiValidationErrorsPayload,
|
||||||
@@ -52,4 +55,22 @@ async def me(
|
|||||||
) -> MeUserPublicResponse:
|
) -> MeUserPublicResponse:
|
||||||
user = await command(user_id=auth.user_id)
|
user = await command(user_id=auth.user_id)
|
||||||
logger.info(f'Get user: {user.id}')
|
logger.info(f'Get user: {user.id}')
|
||||||
return me_user_public(user)
|
return MeUserPublicResponse(
|
||||||
|
id=user.id,
|
||||||
|
email=user.email,
|
||||||
|
first_name=user.first_name,
|
||||||
|
middle_name=user.middle_name,
|
||||||
|
last_name=user.last_name,
|
||||||
|
birth_date=user.birth_date,
|
||||||
|
encrypted_mnemonic=user.encrypted_mnemonic,
|
||||||
|
phone=user.phone,
|
||||||
|
passport_data=user.passport_data,
|
||||||
|
inn=user.inn,
|
||||||
|
erc20=user.erc20,
|
||||||
|
avatar_link=user.avatar_link,
|
||||||
|
kyc_verified=user.kyc_verified,
|
||||||
|
is_deleted=user.is_deleted,
|
||||||
|
created_at=user.created_at,
|
||||||
|
updated_at=user.updated_at,
|
||||||
|
kyc_verified_at=user.kyc_verified_at
|
||||||
|
)
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ from src.presentation.schemas import (
|
|||||||
)
|
)
|
||||||
from src.presentation.schemas.api_errors import ApiErrorPayload, ApiValidationErrorsPayload
|
from src.presentation.schemas.api_errors import ApiErrorPayload, ApiValidationErrorsPayload
|
||||||
from src.presentation.schemas.me_public import MeUserPublicResponse, SetAvatarPublicResponse
|
from src.presentation.schemas.me_public import MeUserPublicResponse, SetAvatarPublicResponse
|
||||||
from src.presentation.serializers import me_user_public
|
|
||||||
|
|
||||||
|
|
||||||
account_settings_router = APIRouter(prefix='/settings')
|
account_settings_router = APIRouter(prefix='/settings')
|
||||||
@@ -175,7 +174,25 @@ async def set_avatar(
|
|||||||
command: SetAvatarCommand = Depends(get_set_avatar_command),
|
command: SetAvatarCommand = Depends(get_set_avatar_command),
|
||||||
) -> SetAvatarPublicResponse:
|
) -> SetAvatarPublicResponse:
|
||||||
user, webp_size = await command(user_id=auth.user_id, image_bytes=body.decoded_bytes)
|
user, webp_size = await command(user_id=auth.user_id, image_bytes=body.decoded_bytes)
|
||||||
pub = me_user_public(user)
|
pub = MeUserPublicResponse(
|
||||||
|
id=user.id,
|
||||||
|
email=user.email,
|
||||||
|
first_name=user.first_name,
|
||||||
|
middle_name=user.middle_name,
|
||||||
|
last_name=user.last_name,
|
||||||
|
birth_date=user.birth_date,
|
||||||
|
encrypted_mnemonic=user.encrypted_mnemonic,
|
||||||
|
phone=user.phone,
|
||||||
|
passport_data=user.passport_data,
|
||||||
|
inn=user.inn,
|
||||||
|
erc20=user.erc20,
|
||||||
|
avatar_link=user.avatar_link,
|
||||||
|
kyc_verified=user.kyc_verified,
|
||||||
|
is_deleted=user.is_deleted,
|
||||||
|
created_at=user.created_at,
|
||||||
|
updated_at=user.updated_at,
|
||||||
|
kyc_verified_at=user.kyc_verified_at
|
||||||
|
)
|
||||||
return SetAvatarPublicResponse(**pub.model_dump(), webp_size_bytes=webp_size)
|
return SetAvatarPublicResponse(**pub.model_dump(), webp_size_bytes=webp_size)
|
||||||
|
|
||||||
|
|
||||||
@@ -197,7 +214,25 @@ async def delete_avatar(
|
|||||||
command: DeleteAvatarCommand = Depends(get_delete_avatar_command),
|
command: DeleteAvatarCommand = Depends(get_delete_avatar_command),
|
||||||
) -> MeUserPublicResponse:
|
) -> MeUserPublicResponse:
|
||||||
user = await command(user_id=auth.user_id)
|
user = await command(user_id=auth.user_id)
|
||||||
return me_user_public(user)
|
return MeUserPublicResponse(
|
||||||
|
id=user.id,
|
||||||
|
email=user.email,
|
||||||
|
first_name=user.first_name,
|
||||||
|
middle_name=user.middle_name,
|
||||||
|
last_name=user.last_name,
|
||||||
|
birth_date=user.birth_date,
|
||||||
|
encrypted_mnemonic=user.encrypted_mnemonic,
|
||||||
|
phone=user.phone,
|
||||||
|
passport_data=user.passport_data,
|
||||||
|
inn=user.inn,
|
||||||
|
erc20=user.erc20,
|
||||||
|
avatar_link=user.avatar_link,
|
||||||
|
kyc_verified=user.kyc_verified,
|
||||||
|
is_deleted=user.is_deleted,
|
||||||
|
created_at=user.created_at,
|
||||||
|
updated_at=user.updated_at,
|
||||||
|
kyc_verified_at=user.kyc_verified_at
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@account_settings_router.post(
|
@account_settings_router.post(
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
from src.presentation.serializers.me_user import me_user_payload, me_user_public
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from src.application.domain.entities import UserEntity
|
|
||||||
|
|
||||||
from src.presentation.schemas.me_public import MeUserPublicResponse
|
|
||||||
|
|
||||||
|
|
||||||
def me_user_public(user: UserEntity) -> MeUserPublicResponse:
|
|
||||||
return MeUserPublicResponse.from_user(user)
|
|
||||||
|
|
||||||
|
|
||||||
def me_user_payload(user: UserEntity) -> dict:
|
|
||||||
return me_user_public(user).model_dump(mode='json')
|
|
||||||
Reference in New Issue
Block a user