diff --git a/docker-compose.yml b/docker-compose.yml index 382058e..c185652 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,15 @@ services: - # backend: - # build: - # context: . - # args: - # GIT_COMMIT: ${GIT_COMMIT:-unknown} - # env_file: - # - .env - # ports: - # - "8000:8000" - # depends_on: - # - postgres + backend: + build: + context: . + args: + GIT_COMMIT: ${GIT_COMMIT:-unknown} + env_file: + - .env + ports: + - "8000:8000" + depends_on: + - postgres postgres: image: postgres:17-alpine diff --git a/src/controller/http/__init__.py b/src/controller/http/__init__.py index fb3740a..7b15d47 100644 --- a/src/controller/http/__init__.py +++ b/src/controller/http/__init__.py @@ -9,11 +9,9 @@ from src.controller.http.views import views_router api_router = APIRouter() -# Public auth endpoints (no prefix) -api_router.include_router(auth_router) - # API v1 endpoints api_v1_router = APIRouter(prefix='/api/v1') +api_v1_router.include_router(auth_router) api_v1_router.include_router(target_channels_router) api_v1_router.include_router(external_channels_router) api_v1_router.include_router(creatives_router) diff --git a/src/controller/http/auth.py b/src/controller/http/auth.py index d8d523a..b94de23 100644 --- a/src/controller/http/auth.py +++ b/src/controller/http/auth.py @@ -1,17 +1,25 @@ +from typing import Annotated + from fastapi import Depends from fastapi.routing import APIRouter -from src import dto -from src.dependencies import get_usecase -from src.usecase import Usecase +from src import dependencies, dto +from src.adapter.jwt import JWTPayload auth_router = APIRouter(prefix='/auth', tags=['auth']) @auth_router.get('/complete') -async def complete_auth(token: str, usecase: Usecase = Depends(get_usecase)) -> dto.ValidateLoginTokenOutput: - return await usecase.validate_login_token( +async def complete_auth(token: str) -> dto.ValidateLoginTokenOutput: + return await dependencies.get_usecase().validate_login_token( input=dto.ValidateLoginTokenInput( token=token, ) ) + + +@auth_router.get('/me') +async def get_me( + current_user: Annotated[JWTPayload, Depends(dependencies.get_current_user)], +) -> dto.UserOutput: + return await dependencies.get_usecase().get_me(user_id=current_user.user_id) diff --git a/src/dto/__init__.py b/src/dto/__init__.py index a98e930..eeefe90 100644 --- a/src/dto/__init__.py +++ b/src/dto/__init__.py @@ -37,6 +37,7 @@ __all__ = ( 'FetchViewsInputManually', 'FetchViewsManuallyOutput', 'UpdateViewsManuallyInput', + 'UserOutput', ) from .creative import ( @@ -78,6 +79,7 @@ from .target_channel import ( ) from .telegram_login import TelegramLoginInput from .validate_login_token import ValidateLoginTokenInput, ValidateLoginTokenOutput +from .user import UserOutput from .views import ( FetchViewsInputManually, FetchViewsManuallyOutput, diff --git a/src/dto/user.py b/src/dto/user.py new file mode 100644 index 0000000..2fed355 --- /dev/null +++ b/src/dto/user.py @@ -0,0 +1,10 @@ +import uuid + +import pydantic + + +class UserOutput(pydantic.BaseModel): + id: uuid.UUID + telegram_id: int + username: str | None + diff --git a/src/main.py b/src/main.py index 1374d8f..ac0c72a 100644 --- a/src/main.py +++ b/src/main.py @@ -2,6 +2,7 @@ from collections.abc import AsyncGenerator from contextlib import asynccontextmanager from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware from shared import logger from src import dependencies @@ -48,4 +49,16 @@ dependencies.set_usecase(usecase) views_worker = FetchViewsWorker(config=settings.fetch_views_worker) app = FastAPI(lifespan=lifespan, version=settings.logger.APP_VERSION) + +app.add_middleware( + CORSMiddleware, + allow_origins=[ + "https://app.tgexchange.ru", + "http://app.tgexchange.ru", + ], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + app.include_router(api_router) diff --git a/src/usecase/__init__.py b/src/usecase/__init__.py index d7ddc0d..950c05b 100644 --- a/src/usecase/__init__.py +++ b/src/usecase/__init__.py @@ -10,6 +10,7 @@ from .creative.delete_creative import delete_creative from .creative.get_creative import get_creative from .creative.get_creatives import get_creatives from .creative.update_creative import update_creative +from .get_me import get_me from .external_channel.create_external_channel import create_external_channel from .external_channel.delete_external_channel import delete_external_channel from .external_channel.get_external_channels import get_external_channels @@ -171,6 +172,7 @@ class Usecase: validate_login_token = validate_login_token telegram_login = telegram_login + get_me = get_me connect_target_chan = connect_target_chan get_user_target_chans = get_user_target_chans disconnect_target_chan = disconnect_target_chan diff --git a/src/usecase/get_me.py b/src/usecase/get_me.py new file mode 100644 index 0000000..86701f1 --- /dev/null +++ b/src/usecase/get_me.py @@ -0,0 +1,22 @@ +import typing +import uuid + +from src import domain, dto + +if typing.TYPE_CHECKING: + from . import Usecase + + +async def get_me(self: 'Usecase', user_id: uuid.UUID) -> dto.UserOutput: + async with self.database.transaction(): + user = await self.database.get_user(user_id=user_id) + + if not user: + raise domain.UserNotFound(user_id) + + return dto.UserOutput( + id=user.id, + telegram_id=user.telegram_id, + username=user.username, + ) +