diff --git a/app/chat/router.py b/app/chat/router.py index ae61f67..3fb4a13 100644 --- a/app/chat/router.py +++ b/app/chat/router.py @@ -13,11 +13,9 @@ from app.chat.shemas import ( SChatId, Responses, ) -from app.services.chat_service import ChatService -from app.services.message_service import MessageService +from app.services import chat_service, message_service, auth_service from app.utils.unit_of_work import UnitOfWork from app.dependencies import get_verificated_user -from app.services.auth_service import AuthService from app.users.schemas import SCreateInvitationLink, SUser router = APIRouter(prefix="/chat", tags=["Чат"]) @@ -51,7 +49,7 @@ router = APIRouter(prefix="/chat", tags=["Чат"]) }, ) async def get_all_chats(user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork)): - allowed_chats = await ChatService.get_all_chats(uow=uow, user_id=user.id) + allowed_chats = await chat_service.get_all_chats(uow=uow, user_id=user.id) return allowed_chats @@ -88,7 +86,7 @@ async def create_chat( user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork), ): - chat_id = await ChatService.create_chat( + chat_id = await chat_service.create_chat( uow=uow, user_id=user.id, user_to_exclude_id=user_to_exclude, @@ -129,8 +127,8 @@ async def change_chat_data( user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork), ): - await AuthService.validate_user_access_to_chat(uow=uow, chat_id=user_data.chat_id, user_id=user.id) - await ChatService.change_chat_data(uow=uow, user_id=user.id, user_data=user_data) + await auth_service.validate_user_access_to_chat(uow=uow, chat_id=user_data.chat_id, user_id=user.id) + await chat_service.change_chat_data(uow=uow, user_id=user.id, user_data=user_data) @router.get( @@ -161,8 +159,8 @@ async def change_chat_data( }, ) async def get_last_message(chat_id: int, user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork)): - await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) - message = await MessageService.get_some_messages(uow=uow, chat_id=chat_id, message_number_from=0, messages_to_get=1) + await auth_service.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) + message = await message_service.get_some_messages(uow=uow, chat_id=chat_id, message_number_from=0, messages_to_get=1) return message @@ -199,8 +197,8 @@ async def get_some_messages( user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork), ): - await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) - messages = await MessageService.get_some_messages( + await auth_service.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) + messages = await message_service.get_some_messages( uow=uow, chat_id=chat_id, message_number_from=last_messages.messages_loaded, @@ -242,8 +240,8 @@ async def get_message_by_id( user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork), ): - await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) - message = await MessageService.get_message_by_id(uow=uow, message_id=message_id) + await auth_service.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) + message = await message_service.get_message_by_id(uow=uow, message_id=message_id) return message @@ -279,8 +277,8 @@ async def create_invitation_link( user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork), ): - await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) - invitation_link = ChatService.create_invitation_link(chat_id=chat_id) + await auth_service.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) + invitation_link = chat_service.create_invitation_link(chat_id=chat_id) return {"invitation_link": invitation_link} @@ -316,7 +314,7 @@ async def invite_to_chat( user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork), ): - await ChatService.invite_to_chat(uow=uow, user_id=user.id, invitation_token=invitation_token) + await chat_service.invite_to_chat(uow=uow, user_id=user.id, invitation_token=invitation_token) @router.delete( @@ -347,7 +345,7 @@ async def invite_to_chat( }, ) async def delete_chat(chat_id: int, user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork)): - await ChatService.delete_chat(uow=uow, user_id=user.id, chat_id=chat_id) + await chat_service.delete_chat(uow=uow, user_id=user.id, chat_id=chat_id) @router.delete( @@ -383,7 +381,7 @@ async def delete_user_from_chat( user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork), ): - await ChatService.delete_user_from_chat(uow=uow, user_id=user.id, chat_id=chat_id, user_id_for_delete=user_id) + await chat_service.delete_user_from_chat(uow=uow, user_id=user.id, chat_id=chat_id, user_id_for_delete=user_id) @router.post( @@ -414,8 +412,8 @@ async def delete_user_from_chat( }, ) async def pin_chat(chat_id: int, user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork)): - await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) - await ChatService.pin_chat(uow=uow, user_id=user.id, chat_id=chat_id) + await auth_service.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) + await chat_service.pin_chat(uow=uow, user_id=user.id, chat_id=chat_id) @router.delete( @@ -445,9 +443,9 @@ async def pin_chat(chat_id: int, user: SUser = Depends(get_verificated_user), uo }, }, ) -async def unpinn_chat(chat_id: int, user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork)): - await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) - await ChatService.unpin_chat(uow=uow, user_id=user.id, chat_id=chat_id) +async def unpin_chat(chat_id: int, user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork)): + await auth_service.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) + await chat_service.unpin_chat(uow=uow, user_id=user.id, chat_id=chat_id) @router.get( @@ -478,7 +476,7 @@ async def unpinn_chat(chat_id: int, user: SUser = Depends(get_verificated_user), }, ) async def get_pinned_chats(user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork)): - pinned_chats = await ChatService.get_pinned_chats(uow=uow, user_id=user.id) + pinned_chats = await chat_service.get_pinned_chats(uow=uow, user_id=user.id) return pinned_chats @@ -510,6 +508,6 @@ async def get_pinned_chats(user: SUser = Depends(get_verificated_user), uow=Depe }, ) async def get_pinned_messages(chat_id: int, user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork)): - await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) - pinned_messages = await MessageService.get_pinned_messages(uow=uow, chat_id=chat_id) + await auth_service.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id) + pinned_messages = await message_service.get_pinned_messages(uow=uow, chat_id=chat_id) return pinned_messages diff --git a/app/chat/websocket.py b/app/chat/websocket.py index f38c592..93088bd 100644 --- a/app/chat/websocket.py +++ b/app/chat/websocket.py @@ -8,9 +8,8 @@ from fastapi import WebSocket, WebSocketDisconnect, Depends, HTTPException, stat from app.chat.exceptions import UseWSException, MessageNotFoundException, MessageAlreadyPinnedException from app.exceptions import IncorrectDataException from app.chat.exceptions import UserDontHavePermissionException -from app.services.message_service import MessageService +from app.services import message_service, auth_service from app.utils.unit_of_work import UnitOfWork -from app.services.auth_service import AuthService from app.chat.router import router from app.chat.shemas import SSendMessage, SDeleteMessage, SEditMessage, SPinMessage, SUnpinMessage, Responses from app.dependencies import get_current_user_ws, get_token, get_subprotocol_ws, get_verificated_user @@ -33,7 +32,10 @@ class ConnectionManager: self.active_connections[chat_id].append(websocket) async def disconnect( - self, chat_id: int, websocket: WebSocket, code_and_reason: tuple[int, str] | None = None + self, + chat_id: int, + websocket: WebSocket, + code_and_reason: tuple[int, str] | None = None, ) -> None: self.active_connections[chat_id].remove(websocket) if code_and_reason: @@ -54,7 +56,7 @@ class ConnectionManager: async def _send(uow: UnitOfWork, user_id: int, chat_id: int, message: dict) -> dict: message = SSendMessage.model_validate(message) - new_message = await MessageService.send_message( + new_message = await message_service.send_message( uow=uow, user_id=user_id, chat_id=chat_id, @@ -74,7 +76,7 @@ class ConnectionManager: if message.user_id != user_id: raise UserDontHavePermissionException - await MessageService.delete_message(uow=uow, message_id=message.id) + await message_service.delete_message(uow=uow, message_id=message.id) new_message = {"id": str(message.id), "flag": "delete"} return new_message @@ -85,7 +87,7 @@ class ConnectionManager: if message.user_id != user_id: raise UserDontHavePermissionException - await MessageService.edit_message( + await message_service.edit_message( uow=uow, message_id=message.id, new_message=message.new_message, @@ -102,7 +104,7 @@ class ConnectionManager: @staticmethod async def _pin(uow: UnitOfWork, _: int, chat_id: int, message: dict) -> dict: message = SPinMessage.model_validate(message) - pinned_message = await MessageService.pin_message( + pinned_message = await message_service.pin_message( uow=uow, chat_id=chat_id, user_id=message.user_id, @@ -118,7 +120,7 @@ class ConnectionManager: @staticmethod async def _unpin(uow: UnitOfWork, _: int, chat_id: int, message: dict) -> dict: message = SUnpinMessage.model_validate(message) - await MessageService.unpin_message(uow=uow, chat_id=chat_id, message_id=message.id) + await message_service.unpin_message(uow=uow, chat_id=chat_id, message_id=message.id) new_message = {"flag": "unpin", "id": str(message.id)} return new_message @@ -137,8 +139,8 @@ async def websocket_endpoint( uow=Depends(UnitOfWork), ): try: - await AuthService.check_verificated_user(uow=uow, user_id=user.id) - await AuthService.validate_user_access_to_chat(uow=uow, user_id=user.id, chat_id=chat_id) + await auth_service.check_verificated_user(uow=uow, user_id=user.id) + await auth_service.validate_user_access_to_chat(uow=uow, user_id=user.id, chat_id=chat_id) await manager.connect(chat_id, websocket, subprotocol) while True: data = await websocket.receive_json() @@ -210,11 +212,11 @@ class PollingManager: raise HTTPException(status_code=status.HTTP_408_REQUEST_TIMEOUT, detail="Client disconnected") async def prepare( - self, - uow: UnitOfWork, - user_id: int, - chat_id: int, - message: SSendMessage | SDeleteMessage | SEditMessage | SPinMessage | SUnpinMessage, + self, + uow: UnitOfWork, + user_id: int, + chat_id: int, + message: SSendMessage | SDeleteMessage | SEditMessage | SPinMessage | SUnpinMessage, ) -> None: message = message.model_dump() await manager.broadcast(uow=uow, user_id=user_id, chat_id=chat_id, message=message) @@ -222,9 +224,9 @@ class PollingManager: await self.send(chat_id=chat_id, message=message) async def send( - self, - chat_id: int, - message: dict, + self, + chat_id: int, + message: dict, ) -> None: self.messages[chat_id].append(message) while self.waiters[chat_id]: @@ -266,7 +268,7 @@ polling_manager = PollingManager() }, ) async def poll(chat_id: int, user: SUser = Depends(get_verificated_user), uow=Depends(UnitOfWork)): - await AuthService.validate_user_access_to_chat(uow=uow, user_id=user.id, chat_id=chat_id) + await auth_service.validate_user_access_to_chat(uow=uow, user_id=user.id, chat_id=chat_id) return await polling_manager.poll(chat_id) @@ -298,10 +300,10 @@ async def poll(chat_id: int, user: SUser = Depends(get_verificated_user), uow=De }, ) async def send( - chat_id: int, - message: SSendMessage | SDeleteMessage | SEditMessage | SPinMessage | SUnpinMessage, - user: SUser = Depends(get_verificated_user), - uow=Depends(UnitOfWork), + chat_id: int, + message: SSendMessage | SDeleteMessage | SEditMessage | SPinMessage | SUnpinMessage, + user: SUser = Depends(get_verificated_user), + uow=Depends(UnitOfWork), ): - await AuthService.validate_user_access_to_chat(uow=uow, user_id=user.id, chat_id=chat_id) + await auth_service.validate_user_access_to_chat(uow=uow, user_id=user.id, chat_id=chat_id) await polling_manager.prepare(uow=uow, user_id=user.id, chat_id=chat_id, message=message) diff --git a/app/dependencies.py b/app/dependencies.py index d1c0d9b..25bbcbf 100644 --- a/app/dependencies.py +++ b/app/dependencies.py @@ -7,7 +7,7 @@ from jose import JWTError, jwt, ExpiredSignatureError from app.config import settings from app.exceptions import IncorrectTokenFormatException, TokenMissingException, TokenExpiredException -from app.services.user_service import UserService +from app.services import user_service from app.utils.unit_of_work import UnitOfWork from app.users.schemas import SUser from app.users.exceptions import UserNotFoundException, UserMustConfirmEmailException @@ -33,7 +33,7 @@ async def get_current_user(token: str = Depends(get_token), uow=Depends(UnitOfWo if not user_id: raise UserNotFoundException - user = await UserService.find_user(uow=uow, id=int(user_id)) + user = await user_service.find_user(uow=uow, id=int(user_id)) return user @@ -62,7 +62,7 @@ async def get_current_user_ws(token: str = Depends(get_token_ws), uow=Depends(Un if not user_id: raise UserNotFoundException - user = await UserService.find_user(uow=uow, id=int(user_id)) + user = await user_service.find_user(uow=uow, id=int(user_id)) return user diff --git a/app/images/router.py b/app/images/router.py index 401be93..b7f8776 100644 --- a/app/images/router.py +++ b/app/images/router.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, UploadFile, status, Depends from app.images.shemas import SImageUrl, Responses -from app.services.image_service import ImageService +from app.services import image_service from app.dependencies import get_verificated_user router = APIRouter(prefix="/images", tags=["Загрузка картинок"]) @@ -35,7 +35,7 @@ router = APIRouter(prefix="/images", tags=["Загрузка картинок"]) }, ) async def upload_avatar(file: UploadFile, _=Depends(get_verificated_user)): - image_url = await ImageService.upload_file_returning_str(file, "upload_avatar") + image_url = await image_service.upload_file_returning_str(file, "upload_avatar") return {"image_url": image_url} @@ -67,5 +67,5 @@ async def upload_avatar(file: UploadFile, _=Depends(get_verificated_user)): }, ) async def upload_image(file: UploadFile, _=Depends(get_verificated_user)): - image_url = await ImageService.upload_file_returning_str(file, "upload_image") + image_url = await image_service.upload_file_returning_str(file, "upload_image") return {"image_url": image_url} diff --git a/app/services/auth_service.py b/app/services/auth_service.py index a521232..c0ee066 100644 --- a/app/services/auth_service.py +++ b/app/services/auth_service.py @@ -15,82 +15,71 @@ from app.chat.exceptions import ChatNotFoundException, UserCanNotReadThisChatExc from app.utils.unit_of_work import UnitOfWork from app.users.schemas import SUser, SInvitationData -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # bcrypt не умеет больше 72 байт хешировать. Остальное обрезаем +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") +# bcrypt не умеет больше 72 байт хешировать. Остальное обрезаем cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY) -class AuthService: - @staticmethod - def get_password_hash(password: str) -> str: - return pwd_context.hash(password.encode("utf-8")[:72]) +def get_password_hash(password: str) -> str: + return pwd_context.hash(password.encode("utf-8")[:72]) - @staticmethod - def verify_password(plain_password: str, hashed_password: str) -> bool: - return pwd_context.verify(plain_password.encode("utf-8")[:72], hashed_password) +def verify_password(plain_password: str, hashed_password: str) -> bool: + return pwd_context.verify(plain_password.encode("utf-8")[:72], hashed_password) - @staticmethod - def create_access_token(data: dict[str, str | datetime]) -> str: - to_encode = data.copy() - expire = datetime.now(UTC) + timedelta(days=30) - to_encode.update({"exp": expire}) - encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, settings.ALGORITHM) - return encoded_jwt +def create_access_token(data: dict[str, str | datetime]) -> str: + to_encode = data.copy() + expire = datetime.now(UTC) + timedelta(days=30) + to_encode.update({"exp": expire}) + encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, settings.ALGORITHM) + return encoded_jwt - @staticmethod - def encode_invitation_token(user_data: SInvitationData) -> str: - invitation_token = cipher_suite.encrypt(user_data.model_dump_json().encode()) - return invitation_token.decode() +def encode_invitation_token(user_data: SInvitationData) -> str: + invitation_token = cipher_suite.encrypt(user_data.model_dump_json().encode()) + return invitation_token.decode() - @staticmethod - def decode_invitation_token(invitation_token: str) -> SInvitationData: - try: - user_code = invitation_token.encode() - user_data = cipher_suite.decrypt(user_code) - return SInvitationData.model_validate_json(user_data) - except InvalidToken: - raise WrongCodeException +def decode_invitation_token(invitation_token: str) -> SInvitationData: + try: + user_code = invitation_token.encode() + user_data = cipher_suite.decrypt(user_code) + return SInvitationData.model_validate_json(user_data) + except InvalidToken: + raise WrongCodeException - @staticmethod - def encode_confirmation_token(confirmation_code: str) -> str: - invitation_token = cipher_suite.encrypt(confirmation_code.encode()) - return invitation_token.decode() +def encode_confirmation_token(confirmation_code: str) -> str: + invitation_token = cipher_suite.encrypt(confirmation_code.encode()) + return invitation_token.decode() - @staticmethod - def decode_confirmation_token(invitation_token: str) -> str: - try: - user_code = invitation_token.encode() - confirmation_code = cipher_suite.decrypt(user_code).decode() - return confirmation_code - except InvalidToken: - raise WrongCodeException +def decode_confirmation_token(invitation_token: str) -> str: + try: + user_code = invitation_token.encode() + confirmation_code = cipher_suite.decrypt(user_code).decode() + return confirmation_code + except InvalidToken: + raise WrongCodeException - @classmethod - def get_confirmation_code(cls, user_code: str) -> str: - return user_code if len(user_code) == 6 else cls.decode_confirmation_token(user_code) +def get_confirmation_code(user_code: str) -> str: + return user_code if len(user_code) == 6 else decode_confirmation_token(user_code) - @classmethod - async def authenticate_user(cls, uow: UnitOfWork, email_or_username: str, password: str) -> SUser: - try: - async with uow: - user = await uow.user.find_one_by_username_or_email(username=email_or_username, email=email_or_username) - if not cls.verify_password(password, user.hashed_password): - raise IncorrectAuthDataException - return user - except UserNotFoundException: - raise IncorrectAuthDataException - - @classmethod - async def check_verificated_user(cls, uow: UnitOfWork, user_id: int) -> None: +async def authenticate_user(uow: UnitOfWork, email_or_username: str, password: str) -> SUser: + try: async with uow: - user = await uow.user.find_one(id=user_id) - if user.role < settings.VERIFICATED_USER: - raise UserMustConfirmEmailException + user = await uow.user.find_one_by_username_or_email(username=email_or_username, email=email_or_username) + if not verify_password(password, user.hashed_password): + raise IncorrectAuthDataException + return user + except UserNotFoundException: + raise IncorrectAuthDataException - @classmethod - async def validate_user_access_to_chat(cls, uow: UnitOfWork, user_id: int, chat_id: int) -> None: - try: - async with uow: - await uow.chat.find_chat(chat_id=chat_id, user_id=user_id) - except ChatNotFoundException: - raise UserCanNotReadThisChatException +async def check_verificated_user(uow: UnitOfWork, user_id: int) -> None: + async with uow: + user = await uow.user.find_one(id=user_id) + if user.role < settings.VERIFICATED_USER: + raise UserMustConfirmEmailException + +async def validate_user_access_to_chat(uow: UnitOfWork, user_id: int, chat_id: int) -> None: + try: + async with uow: + await uow.chat.find_chat(chat_id=chat_id, user_id=user_id) + except ChatNotFoundException: + raise UserCanNotReadThisChatException diff --git a/app/services/chat_service.py b/app/services/chat_service.py index d5ff98d..c796ef3 100644 --- a/app/services/chat_service.py +++ b/app/services/chat_service.py @@ -7,132 +7,118 @@ from app.chat.exceptions import UserDontHavePermissionException, UserCanNotReadT from app.chat.shemas import SAllowedChats, SChangeData, SPinnedChats, SChat from app.config import settings from app.services.redis_service import RedisService -from app.services.user_service import UserService +from app.services import user_service, auth_service from app.utils.unit_of_work import UnitOfWork from app.users.schemas import SInvitationData -from app.services.auth_service import AuthService -class ChatService: - @staticmethod - async def find_chat(uow: UnitOfWork, chat_id: int, user_id: int) -> SChat: +async def find_chat(uow: UnitOfWork, chat_id: int, user_id: int) -> SChat: + async with uow: + chat = await uow.chat.find_chat(chat_id=chat_id, user_id=user_id) + return chat + +async def find_one(uow: UnitOfWork, chat_id: int) -> SChat: + async with uow: + chat = await uow.chat.find_one(chat_id=chat_id) + return chat + +async def get_all_chats(uow: UnitOfWork, user_id: int) -> SAllowedChats: + try: + async with RedisService() as redis: + allowed_chats = await redis.get_value(key=f"user_allowed_chats: {user_id}", model=SAllowedChats) + return allowed_chats + except ValidationError: async with uow: - chat = await uow.chat.find_chat(chat_id=chat_id, user_id=user_id) - return chat - - @staticmethod - async def find_one(uow: UnitOfWork, chat_id: int) -> SChat: - async with uow: - chat = await uow.chat.find_one(chat_id=chat_id) - return chat - - @staticmethod - async def get_all_chats(uow: UnitOfWork, user_id: int) -> SAllowedChats: - try: + allowed_chats = await uow.user.get_user_allowed_chats(user_id=user_id) async with RedisService() as redis: - allowed_chats = await redis.get_value(key=f"user_allowed_chats: {user_id}", model=SAllowedChats) - return allowed_chats - except ValidationError: - async with uow: - allowed_chats = await uow.user.get_user_allowed_chats(user_id=user_id) - async with RedisService() as redis: - await redis.set_key( - key=f"user_allowed_chats: {user_id}", - expire_time=timedelta(minutes=30), - value=allowed_chats.model_dump_json(), - ) - return allowed_chats + await redis.set_key( + key=f"user_allowed_chats: {user_id}", + expire_time=timedelta(minutes=30), + value=allowed_chats.model_dump_json(), + ) + return allowed_chats - @staticmethod - async def create_chat(uow: UnitOfWork, user_id: int, user_to_exclude_id: int, chat_name: str) -> int: - if user_id == user_to_exclude_id: - raise UserCanNotReadThisChatException - user_chat_for = await UserService.find_user(uow=uow, id=user_to_exclude_id) - async with uow: - chat_id = await uow.chat.create_chat( - user_id=user_to_exclude_id, - chat_name=chat_name, - created_by=user_id, - avatar_image=user_chat_for.avatar_image, - ) - await uow.chat.add_user_to_chat(user_id, chat_id) - if user_id != settings.ADMIN_USER_ID: - await uow.chat.add_user_to_chat(settings.ADMIN_USER_ID, chat_id) - async with RedisService(is_raise=True) as redis: - await redis.delete_key(key=f"user_allowed_chats: {user_id}") - await uow.commit() - return chat_id +async def create_chat(uow: UnitOfWork, user_id: int, user_to_exclude_id: int, chat_name: str) -> int: + if user_id == user_to_exclude_id: + raise UserCanNotReadThisChatException + user_chat_for = await user_service.find_user(uow=uow, id=user_to_exclude_id) + async with uow: + chat_id = await uow.chat.create_chat( + user_id=user_to_exclude_id, + chat_name=chat_name, + created_by=user_id, + avatar_image=user_chat_for.avatar_image, + ) + await uow.chat.add_user_to_chat(user_id, chat_id) + if user_id != settings.ADMIN_USER_ID: + await uow.chat.add_user_to_chat(settings.ADMIN_USER_ID, chat_id) + async with RedisService(is_raise=True) as redis: + await redis.delete_key(key=f"user_allowed_chats: {user_id}") + await uow.commit() + return chat_id - @classmethod - async def change_chat_data(cls, uow: UnitOfWork, user_id: int, user_data: SChangeData) -> None: - chat = await cls.find_chat(uow=uow, chat_id=user_data.chat_id, user_id=user_id) - if chat.created_by != user_id: - raise UserDontHavePermissionException - async with uow: - await uow.chat.change_data( - chat_id=user_data.chat_id, - chat_name=user_data.chat_name, - avatar_image=user_data.avatar_image, - ) - async with RedisService(is_raise=True) as redis: - await redis.delete_key(key=f"user_allowed_chats: {user_id}") - await uow.commit() +async def change_chat_data(uow: UnitOfWork, user_id: int, user_data: SChangeData) -> None: + chat = await find_chat(uow=uow, chat_id=user_data.chat_id, user_id=user_id) + if chat.created_by != user_id: + raise UserDontHavePermissionException + async with uow: + await uow.chat.change_data( + chat_id=user_data.chat_id, + chat_name=user_data.chat_name, + avatar_image=user_data.avatar_image, + ) + async with RedisService(is_raise=True) as redis: + await redis.delete_key(key=f"user_allowed_chats: {user_id}") + await uow.commit() - @staticmethod - def create_invitation_link(chat_id: int) -> str: - invitation_data = SInvitationData.model_validate({"chat_id": chat_id}) - invitation_token = AuthService.encode_invitation_token(invitation_data) - invitation_link = urljoin(settings.INVITATION_LINK_HOST, f"/submitNewChat#code={invitation_token}") - return invitation_link +def create_invitation_link(chat_id: int) -> str: + invitation_data = SInvitationData.model_validate({"chat_id": chat_id}) + invitation_token = auth_service.encode_invitation_token(invitation_data) + invitation_link = urljoin(settings.INVITATION_LINK_HOST, f"/submitNewChat#code={invitation_token}") + return invitation_link - @classmethod - async def invite_to_chat(cls, uow: UnitOfWork, user_id: int, invitation_token: str) -> None: - invitation_data = AuthService.decode_invitation_token(invitation_token) - chat = await cls.find_one(uow=uow, chat_id=invitation_data.chat_id) - if user_id == chat.chat_for: - raise UserCanNotReadThisChatException - async with uow: - await uow.chat.add_user_to_chat(chat_id=invitation_data.chat_id, user_id=user_id) - async with RedisService(is_raise=True) as redis: - await redis.delete_key(key=f"user_allowed_chats: {user_id}") - await uow.commit() +async def invite_to_chat(uow: UnitOfWork, user_id: int, invitation_token: str) -> None: + invitation_data = auth_service.decode_invitation_token(invitation_token) + chat = await find_one(uow=uow, chat_id=invitation_data.chat_id) + if user_id == chat.chat_for: + raise UserCanNotReadThisChatException + async with uow: + await uow.chat.add_user_to_chat(chat_id=invitation_data.chat_id, user_id=user_id) + async with RedisService(is_raise=True) as redis: + await redis.delete_key(key=f"user_allowed_chats: {user_id}") + await uow.commit() - @classmethod - async def delete_chat(cls, uow: UnitOfWork, user_id: int, chat_id: int) -> None: - chat = await cls.find_chat(uow=uow, chat_id=chat_id, user_id=user_id) - if not user_id == chat.created_by: - raise UserDontHavePermissionException - async with uow: - await uow.chat.delete_chat(chat_id) - async with RedisService(is_raise=True) as redis: - await redis.delete_key(key=f"user_allowed_chats: {user_id}") - await uow.commit() +async def delete_chat(uow: UnitOfWork, user_id: int, chat_id: int) -> None: + chat = await find_chat(uow=uow, chat_id=chat_id, user_id=user_id) + if not user_id == chat.created_by: + raise UserDontHavePermissionException + async with uow: + await uow.chat.delete_chat(chat_id) + async with RedisService(is_raise=True) as redis: + await redis.delete_key(key=f"user_allowed_chats: {user_id}") + await uow.commit() - @classmethod - async def delete_user_from_chat(cls, uow: UnitOfWork, user_id: int, chat_id: int, user_id_for_delete: int) -> None: - chat = await cls.find_chat(uow=uow, chat_id=chat_id, user_id=user_id) - if not user_id == chat.created_by: - raise UserDontHavePermissionException - async with uow: - await uow.chat.delete_user_from_chat(chat_id=chat_id, user_id=user_id_for_delete) - async with RedisService(is_raise=True) as redis: - await redis.delete_key(key=f"user_allowed_chats: {user_id_for_delete}") - await uow.commit() +async def delete_user_from_chat(uow: UnitOfWork, user_id: int, chat_id: int, user_id_for_delete: int) -> None: + chat = await find_chat(uow=uow, chat_id=chat_id, user_id=user_id) + if not user_id == chat.created_by: + raise UserDontHavePermissionException + async with uow: + await uow.chat.delete_user_from_chat(chat_id=chat_id, user_id=user_id_for_delete) + async with RedisService(is_raise=True) as redis: + await redis.delete_key(key=f"user_allowed_chats: {user_id_for_delete}") + await uow.commit() - @staticmethod - async def pin_chat(uow: UnitOfWork, user_id: int, chat_id: int) -> None: - async with uow: - await uow.chat.pin_chat(chat_id=chat_id, user_id=user_id) - await uow.commit() +async def pin_chat(uow: UnitOfWork, user_id: int, chat_id: int) -> None: + async with uow: + await uow.chat.pin_chat(chat_id=chat_id, user_id=user_id) + await uow.commit() - @staticmethod - async def unpin_chat(uow: UnitOfWork, user_id: int, chat_id: int) -> None: - async with uow: - await uow.chat.unpin_chat(chat_id=chat_id, user_id=user_id) - await uow.commit() +async def unpin_chat(uow: UnitOfWork, user_id: int, chat_id: int) -> None: + async with uow: + await uow.chat.unpin_chat(chat_id=chat_id, user_id=user_id) + await uow.commit() - @staticmethod - async def get_pinned_chats(uow: UnitOfWork, user_id: int) -> SPinnedChats: - async with uow: - pinned_chats = await uow.chat.get_pinned_chats(user_id=user_id) - return pinned_chats +async def get_pinned_chats(uow: UnitOfWork, user_id: int) -> SPinnedChats: + async with uow: + pinned_chats = await uow.chat.get_pinned_chats(user_id=user_id) + return pinned_chats diff --git a/app/services/image_service.py b/app/services/image_service.py index 1c0644a..3be34ef 100644 --- a/app/services/image_service.py +++ b/app/services/image_service.py @@ -6,28 +6,25 @@ from httpx import AsyncClient, Response from app.config import settings -class ImageService: - @staticmethod - async def upload_file(file: UploadFile, remote_server_url: str) -> Response: - try: - file_content = await file.read() - async with AsyncClient() as ac: - response = await ac.post( - remote_server_url, - files={"file": file_content}, - timeout=20, - ) - if response.status_code != 200: - raise HTTPException(status_code=response.status_code, detail=response.json()["detail"]) - return response - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) +async def upload_file(file: UploadFile, remote_server_url: str) -> Response: + try: + file_content = await file.read() + async with AsyncClient() as ac: + response = await ac.post( + remote_server_url, + files={"file": file_content}, + timeout=20, + ) + if response.status_code != 200: + raise HTTPException(status_code=response.status_code, detail=response.json()["detail"]) + return response + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) - @classmethod - async def upload_file_returning_str(cls, file: UploadFile, sub_str: str) -> str: - remote_server_url = urljoin(settings.IMAGE_UPLOAD_SERVER, sub_str) - response = await cls.upload_file(file, remote_server_url) - image_url = response.json()["image_url"] - image_url = urljoin(settings.IMAGE_UPLOAD_SERVER, image_url) - return image_url +async def upload_file_returning_str(file: UploadFile, sub_str: str) -> str: + remote_server_url = urljoin(settings.IMAGE_UPLOAD_SERVER, sub_str) + response = await upload_file(file, remote_server_url) + image_url = response.json()["image_url"] + image_url = urljoin(settings.IMAGE_UPLOAD_SERVER, image_url) + return image_url diff --git a/app/services/message_service.py b/app/services/message_service.py index 56e8656..b62735f 100644 --- a/app/services/message_service.py +++ b/app/services/message_service.py @@ -2,137 +2,130 @@ from datetime import datetime, UTC, timedelta from uuid import UUID from app.chat.shemas import SMessage, SSendMessage, SPinnedMessages, SMessageList, SMessageRaw, SMessageRawList -from app.services.user_service import UserService +from app.services import user_service from app.users.schemas import SUser from app.utils.unit_of_work import UnitOfWork -class MessageService: - users: dict[int, tuple[SUser, datetime]] = {} +_users: dict[int, tuple[SUser, datetime]] = {} - @classmethod - async def _get_cached_user(cls, uow: UnitOfWork, user_id: int) -> SUser: - if user_id in cls.users and cls.users[user_id][1] > datetime.now(UTC): - return cls.users[user_id][0] +async def _get_cached_user(uow: UnitOfWork, user_id: int) -> SUser: + if user_id in _users and _users[user_id][1] > datetime.now(UTC): + return _users[user_id][0] - user = await UserService.find_user(uow=uow, id=user_id) - cls.users[user_id] = user, datetime.now(UTC) + timedelta(minutes=5) - return user + user = await user_service.find_user(uow=uow, id=user_id) + _users[user_id] = user, datetime.now(UTC) + timedelta(minutes=5) + return user - @classmethod - async def _add_avatar_image_and_username_to_message(cls, uow: UnitOfWork, message: SMessageRaw) -> SMessage: - user = await cls._get_cached_user(uow=uow, user_id=message.user_id) - message = message.model_dump() - message["avatar_image"] = str(user.avatar_image) - message["username"] = user.username - return SMessage.model_validate(message) - @classmethod - async def _add_avatar_image_and_username_to_message_list( - cls, - uow: UnitOfWork, - messages: SMessageRawList, - ) -> SMessageList: - return SMessageList.model_validate( - { - "messages": [ - await cls._add_avatar_image_and_username_to_message(uow=uow, message=message) - for message in messages.message_raw_list - ] if messages.message_raw_list else None - } +async def _add_avatar_image_and_username_to_message(uow: UnitOfWork, message: SMessageRaw) -> SMessage: + user = await _get_cached_user(uow=uow, user_id=message.user_id) + message = message.model_dump() + message["avatar_image"] = str(user.avatar_image) + message["username"] = user.username + return SMessage.model_validate(message) + + +async def _add_avatar_image_and_username_to_message_list( + uow: UnitOfWork, + messages: SMessageRawList, +) -> SMessageList: + return SMessageList.model_validate( + { + "messages": [ + await _add_avatar_image_and_username_to_message(uow=uow, message=message) + for message in messages.message_raw_list + ] if messages.message_raw_list else None + } + ) + +async def send_message( + uow: UnitOfWork, + user_id: int, + chat_id: int, + message: SSendMessage, +) -> SMessage: + async with uow: + if message.answer: + answer_message = await uow.message.get_message_by_id(message_id=message.answer) + message_id = await uow.message.send_message( + user_id=user_id, + chat_id=chat_id, + message=message.message, + image_url=message.image_url, + answer_id=answer_message.id, + answer_message=answer_message.message, + answer_image_url=answer_message.answer_image_url, + ) + else: + message_id = await uow.message.send_message( + user_id=user_id, + chat_id=chat_id, + message=message.message, + image_url=message.image_url, + ) + + new_message = await get_message_by_id(uow=uow, message_id=message_id) + + return new_message + +async def delete_message(uow: UnitOfWork, message_id: UUID) -> None: + async with uow: + await uow.message.delete_message(message_id=message_id) + +async def edit_message(uow: UnitOfWork, message_id: UUID, new_message: str, new_image_url: str) -> None: + async with uow: + await uow.message.edit_message( + message_id=message_id, + new_message=new_message, + new_image_url=new_image_url, ) - @classmethod - async def send_message( - cls, - uow: UnitOfWork, - user_id: int, - chat_id: int, - message: SSendMessage, - ) -> SMessage: - async with uow: - if message.answer: - answer_message = await uow.message.get_message_by_id(message_id=message.answer) - message_id = await uow.message.send_message( - user_id=user_id, - chat_id=chat_id, - message=message.message, - image_url=message.image_url, - answer_id=answer_message.id, - answer_message=answer_message.message, - answer_image_url=answer_message.answer_image_url, - ) - else: - message_id = await uow.message.send_message( - user_id=user_id, - chat_id=chat_id, - message=message.message, - image_url=message.image_url, - ) + +async def pin_message(uow: UnitOfWork, chat_id: int, user_id: int, message_id: UUID) -> SMessage: + async with uow: + await uow.chat.pin_message(chat_id=chat_id, message_id=message_id, user_id=user_id) + await uow.commit() + + pinned_message = await get_message_by_id(uow=uow, message_id=message_id) + + return pinned_message + + +async def unpin_message(uow: UnitOfWork, chat_id: int, message_id: UUID) -> None: + async with uow: + await uow.chat.unpin_message(chat_id=chat_id, message_id=message_id) + await uow.commit() + + +async def get_message_by_id(uow: UnitOfWork, message_id: UUID) -> SMessage: + async with uow: raw_message = await uow.message.get_message_by_id(message_id=message_id) - new_message = await cls._add_avatar_image_and_username_to_message(uow=uow, message=raw_message) + message = await _add_avatar_image_and_username_to_message(uow=uow, message=raw_message) + return message - return new_message - @staticmethod - async def delete_message(uow: UnitOfWork, message_id: UUID) -> None: - async with uow: - await uow.message.delete_message(message_id=message_id) +async def get_pinned_messages(uow: UnitOfWork, chat_id: int) -> SPinnedMessages: + async with uow: + pinned_messages_ids = await uow.chat.get_pinned_messages_ids(chat_id=chat_id) + raw_messages = await uow.message.get_messages_by_ids(messages_ids=pinned_messages_ids) - @staticmethod - async def edit_message(uow: UnitOfWork, message_id: UUID, new_message: str, new_image_url: str) -> None: - async with uow: - await uow.message.edit_message( - message_id=message_id, - new_message=new_message, - new_image_url=new_image_url, - ) + pinned_messages = await _add_avatar_image_and_username_to_message_list(uow=uow, messages=raw_messages) + return SPinnedMessages.model_validate({"pinned_messages": pinned_messages.messages}) - @classmethod - async def pin_message(cls, uow: UnitOfWork, chat_id: int, user_id: int, message_id: UUID) -> SMessage: - async with uow: - await uow.chat.pin_message(chat_id=chat_id, message_id=message_id, user_id=user_id) - await uow.commit() - raw_message = await uow.message.get_message_by_id(message_id=message_id) - pinned_message = await cls._add_avatar_image_and_username_to_message(uow=uow, message=raw_message) +async def get_some_messages( + uow: UnitOfWork, + chat_id: int, + message_number_from: int, + messages_to_get: int, +) -> SMessageList: + async with uow: + messages = await uow.message.get_some_messages( + chat_id=chat_id, + message_number_from=message_number_from, + messages_to_get=messages_to_get, + ) - return pinned_message - - @staticmethod - async def unpin_message(uow: UnitOfWork, chat_id: int, message_id: UUID) -> None: - async with uow: - await uow.chat.unpin_message(chat_id=chat_id, message_id=message_id) - await uow.commit() - - @classmethod - async def get_message_by_id(cls, uow: UnitOfWork, message_id: UUID) -> SMessage: - async with uow: - raw_message = await uow.message.get_message_by_id(message_id=message_id) - message = await cls._add_avatar_image_and_username_to_message(uow=uow, message=raw_message) - return message - - @classmethod - async def get_pinned_messages(cls, uow: UnitOfWork, chat_id: int) -> SPinnedMessages: - async with uow: - pinned_messages_ids = await uow.chat.get_pinned_messages_ids(chat_id=chat_id) - raw_messages = await uow.message.get_messages_by_ids(messages_ids=pinned_messages_ids) - pinned_messages = await cls._add_avatar_image_and_username_to_message_list(uow=uow, messages=raw_messages) - return SPinnedMessages.model_validate({"pinned_messages": pinned_messages.messages}) - - @classmethod - async def get_some_messages( - cls, - uow: UnitOfWork, - chat_id: int, - message_number_from: int, - messages_to_get: int, - ) -> SMessageList: - async with uow: - messages = await uow.message.get_some_messages( - chat_id=chat_id, - message_number_from=message_number_from, - messages_to_get=messages_to_get, - ) - messages = await cls._add_avatar_image_and_username_to_message_list(uow=uow, messages=messages) - return messages + messages = await _add_avatar_image_and_username_to_message_list(uow=uow, messages=messages) + return messages diff --git a/app/services/user_service.py b/app/services/user_service.py index f7068d1..7e6882f 100644 --- a/app/services/user_service.py +++ b/app/services/user_service.py @@ -4,118 +4,110 @@ from pydantic import ValidationError from app.config import settings from app.exceptions import BlackPhoenixException -from app.services.redis_service import RedisService -from app.tasks.tasks import generate_confirmation_code, send_confirmation_email, send_data_change_email +from app.tasks import tasks from app.utils.unit_of_work import UnitOfWork from app.users.exceptions import PasswordsMismatchException, WrongCodeException from app.users.schemas import SUser, SUsers, SUserRegister, SConfirmationData, SUserAvatars, SUserChangeData -from app.services.auth_service import AuthService +from app.services import auth_service +from app.services.redis_service import RedisService -class UserService: - @staticmethod - async def find_user(uow: UnitOfWork, **find_by) -> SUser: - try: - async with RedisService(is_raise=True) as redis: - user = await redis.get_value(key=f"user: {find_by}", model=SUser) - return user - except (ValidationError, BlackPhoenixException): - async with uow: - user = await uow.user.find_one(**find_by) - async with RedisService() as redis: - await redis.set_key( - key=f"user: {find_by}", - expire_time=timedelta(minutes=30), - value=user.model_dump_json(), - ) - return user - - @staticmethod - async def find_all(uow: UnitOfWork, username: str) -> SUsers: +async def find_user(uow: UnitOfWork, **find_by) -> SUser: + try: + async with RedisService(is_raise=True) as redis: + user = await redis.get_value(key=f"user: {find_by}", model=SUser) + return user + except (ValidationError, BlackPhoenixException): async with uow: - users = await uow.user.find_all(username=username) - return users - - @staticmethod - async def add_user(uow: UnitOfWork, user_data: SUserRegister) -> SUser: - if user_data.password != user_data.password2: - raise PasswordsMismatchException - - hashed_password = AuthService.get_password_hash(user_data.password) - async with uow: - user = await uow.user.add( - email=user_data.email, - hashed_password=hashed_password, - username=user_data.username, - date_of_birth=user_data.date_of_birth, - ) - await uow.user.add_user_avatar(user_id=user.id, avatar=str(user.avatar_image)) - await uow.commit() + user = await uow.user.find_one(**find_by) + async with RedisService() as redis: + await redis.set_key( + key=f"user: {find_by}", + expire_time=timedelta(minutes=30), + value=user.model_dump_json(), + ) return user - @staticmethod - async def send_confirmation_email(user: SUser, mail_type: str, email: str | None = None) -> None: - user_code = generate_confirmation_code() +async def find_all(uow: UnitOfWork, username: str) -> SUsers: + async with uow: + users = await uow.user.find_all(username=username) + return users + +async def add_user(uow: UnitOfWork, user_data: SUserRegister) -> SUser: + if user_data.password != user_data.password2: + raise PasswordsMismatchException + + hashed_password = auth_service.get_password_hash(user_data.password) + async with uow: + user = await uow.user.add( + email=user_data.email, + hashed_password=hashed_password, + username=user_data.username, + date_of_birth=user_data.date_of_birth, + ) + await uow.user.add_user_avatar(user_id=user.id, avatar=str(user.avatar_image)) + await uow.commit() + return user + +async def send_confirmation_email(user: SUser, mail_type: str, email: str | None = None) -> None: + user_code = tasks.generate_confirmation_code() + async with RedisService(is_raise=True) as redis: + await redis.delete_key(key=f"user_verification_code: {user.id}") + await redis.set_key(key=f"user_verification_code: {user.id}", expire_time=timedelta(minutes=5), value=user_code) + + user_mail_data = SConfirmationData.model_validate( + { + "user_id": user.id, + "username": user.username, + "email_to": email or user.email, + "confirmation_code": user_code, + "type": mail_type, + } + ) + tasks.send_confirmation_email.delay(user_mail_data.model_dump()) + +async def verificate_user(uow: UnitOfWork, user: SUser, confirmation_code: str) -> None: + try: async with RedisService(is_raise=True) as redis: - await redis.delete_key(key=f"user_verification_code: {user.id}") - await redis.set_key(key=f"user_verification_code: {user.id}", expire_time=timedelta(minutes=5), value=user_code) - - user_mail_data = SConfirmationData.model_validate( - { - "user_id": user.id, - "username": user.username, - "email_to": email or user.email, - "confirmation_code": user_code, - "type": mail_type, - } - ) - send_confirmation_email.delay(user_mail_data.model_dump()) - - @staticmethod - async def verificate_user(uow: UnitOfWork, user: SUser, confirmation_code: str) -> None: - try: - async with RedisService(is_raise=True) as redis: - verification_code = await redis.get_value(key=f"user_verification_code: {user.id}") - except AttributeError: - raise WrongCodeException - if verification_code != confirmation_code: - raise WrongCodeException - async with uow: - await uow.user.change_data(user_id=user.id, role=settings.VERIFICATED_USER) - async with RedisService() as redis: - await redis.delete_key(key=f"user: {dict(id=user.id)}") - await uow.commit() - - @staticmethod - async def get_user_avatars_history(uow: UnitOfWork, user_id: int) -> SUserAvatars: - async with uow: - user_avatars = await uow.user.get_user_avatars(user_id=user_id) - return user_avatars - - @staticmethod - async def change_data(uow: UnitOfWork, user: SUser, user_data: SUserChangeData) -> None: - try: - async with RedisService(is_raise=True) as redis: - verification_code = await redis.get_value(key=f"user_verification_code: {user.id}") - except AttributeError: - raise WrongCodeException - if verification_code != user_data.verification_code: - raise WrongCodeException - hashed_password = ( - AuthService.get_password_hash(user_data.new_password) if user_data.new_password else user.hashed_password - ) - async with uow: - await uow.user.change_data( - user_id=user.id, - email=user_data.email, - username=user_data.username, - avatar_image=str(user_data.avatar_url or user.avatar_image), - hashed_password=hashed_password, - ) - (await uow.user.add_user_avatar(user_id=user.id, avatar=str(user_data.avatar_url))) if user_data.avatar_url else None # noqa - await uow.commit() - + verification_code = await redis.get_value(key=f"user_verification_code: {user.id}") + except AttributeError: + raise WrongCodeException + if verification_code != confirmation_code: + raise WrongCodeException + async with uow: + await uow.user.change_data(user_id=user.id, role=settings.VERIFICATED_USER) async with RedisService() as redis: await redis.delete_key(key=f"user: {dict(id=user.id)}") + await uow.commit() - send_data_change_email.delay(user_data.username, user_data.email) +async def get_user_avatars_history(uow: UnitOfWork, user_id: int) -> SUserAvatars: + async with uow: + user_avatars = await uow.user.get_user_avatars(user_id=user_id) + return user_avatars + +async def change_data(uow: UnitOfWork, user: SUser, user_data: SUserChangeData) -> None: + try: + async with RedisService(is_raise=True) as redis: + verification_code = await redis.get_value(key=f"user_verification_code: {user.id}") + except AttributeError: + raise WrongCodeException + if verification_code != user_data.verification_code: + raise WrongCodeException + hashed_password = ( + auth_service.get_password_hash(user_data.new_password) if user_data.new_password else user.hashed_password + ) + async with uow: + await uow.user.change_data( + user_id=user.id, + email=user_data.email, + username=user_data.username, + avatar_image=str(user_data.avatar_url or user.avatar_image), + hashed_password=hashed_password, + ) + (await uow.user.add_user_avatar(user_id=user.id, avatar=str(user_data.avatar_url))) if user_data.avatar_url else None # noqa + await uow.commit() + + async with RedisService() as redis: + await redis.delete_key(key=f"user: {dict(id=user.id)}") + + tasks.send_data_change_email.delay(user_data.username, user_data.email) diff --git a/app/tasks/tasks.py b/app/tasks/tasks.py index e755185..f136b00 100644 --- a/app/tasks/tasks.py +++ b/app/tasks/tasks.py @@ -13,7 +13,7 @@ from app.tasks.email_templates import ( create_data_change_confirmation_email, create_data_change_email, ) -from app.services.auth_service import AuthService +from app.services import auth_service from app.users.schemas import SConfirmationData @@ -32,7 +32,7 @@ def generate_confirmation_code(length=6) -> str: @celery.task def send_confirmation_email(user_data: dict) -> None: user_data = SConfirmationData.model_validate(user_data) - invitation_token = AuthService.encode_confirmation_token(user_data.confirmation_code) + invitation_token = auth_service.encode_confirmation_token(user_data.confirmation_code) confirmation_link = urljoin(settings.INVITATION_LINK_HOST, f"/submitEmail#code={invitation_token}") diff --git a/app/users/router.py b/app/users/router.py index 7872743..0c1f103 100644 --- a/app/users/router.py +++ b/app/users/router.py @@ -2,10 +2,9 @@ from random import randrange from fastapi import APIRouter, Depends, status -from app.services.user_service import UserService +from app.services import user_service, auth_service from app.users.exceptions import UserAlreadyExistsException, PasswordAlreadyInUseException, UserNotFoundException from app.utils.unit_of_work import UnitOfWork -from app.services.auth_service import AuthService from app.dependencies import get_current_user from app.users.schemas import ( SUserLogin, @@ -39,7 +38,7 @@ router = APIRouter(prefix="/users", tags=["Пользователи"]) }, ) async def get_all_users(filter_by=Depends(SGetUsersFilter), uow=Depends(UnitOfWork)): - users = await UserService.find_all(uow=uow, username=filter_by.username) + users = await user_service.find_all(uow=uow, username=filter_by.username) return users @@ -60,7 +59,7 @@ async def get_all_users(filter_by=Depends(SGetUsersFilter), uow=Depends(UnitOfWo ) async def check_existing_user(user_filter: SUserFilter, uow=Depends(UnitOfWork)): try: - await UserService.find_user(uow=uow, **user_filter.model_dump(exclude_none=True)) + await user_service.find_user(uow=uow, **user_filter.model_dump(exclude_none=True)) raise UserAlreadyExistsException except UserNotFoundException: pass @@ -103,10 +102,10 @@ async def check_existing_password(_: SUserPassword): }, ) async def register_user(user_data: SUserRegister, uow=Depends(UnitOfWork)): - user = await UserService.add_user(uow=uow, user_data=user_data) - await UserService.send_confirmation_email(user=user, mail_type="registration") + user = await user_service.add_user(uow=uow, user_data=user_data) + await user_service.send_confirmation_email(user=user, mail_type="registration") - access_token = AuthService.create_access_token({"sub": str(user.id)}) + access_token = auth_service.create_access_token({"sub": str(user.id)}) return {"authorization": f"Bearer {access_token}"} @@ -138,7 +137,7 @@ async def register_user(user_data: SUserRegister, uow=Depends(UnitOfWork)): }, ) async def resend_email_verification(user: SUser = Depends(get_current_user)): - await UserService.send_confirmation_email(user=user, mail_type="registration") + await user_service.send_confirmation_email(user=user, mail_type="registration") @router.post( @@ -169,8 +168,8 @@ async def resend_email_verification(user: SUser = Depends(get_current_user)): }, ) async def email_verification(user_code: SUserCode, user: SUser = Depends(get_current_user), uow=Depends(UnitOfWork)): - confirmation_code = AuthService.get_confirmation_code(user_code.user_code) - await UserService.verificate_user(uow=uow, user=user, confirmation_code=confirmation_code) + confirmation_code = auth_service.get_confirmation_code(user_code.user_code) + await user_service.verificate_user(uow=uow, user=user, confirmation_code=confirmation_code) @router.post( @@ -193,12 +192,12 @@ async def email_verification(user_code: SUserCode, user: SUser = Depends(get_cur }, ) async def login_user(user_data: SUserLogin, uow=Depends(UnitOfWork)): - user = await AuthService.authenticate_user( + user = await auth_service.authenticate_user( uow=uow, email_or_username=user_data.email_or_username, password=user_data.password, ) - access_token = AuthService.create_access_token({"sub": str(user.id)}) + access_token = auth_service.create_access_token({"sub": str(user.id)}) return {"authorization": f"Bearer {access_token}"} @@ -253,7 +252,7 @@ async def get_user(user: SUser = Depends(get_current_user)): }, ) async def get_user_avatars_history(user=Depends(get_current_user), uow=Depends(UnitOfWork)): - user_avatars = await UserService.get_user_avatars_history(uow=uow, user_id=user.id) + user_avatars = await user_service.get_user_avatars_history(uow=uow, user_id=user.id) return user_avatars @@ -281,7 +280,7 @@ async def get_user_avatars_history(user=Depends(get_current_user), uow=Depends(U }, ) async def send_confirmation_code(user_data: SUserSendConfirmationCode, user: SUser = Depends(get_current_user)): - await UserService.send_confirmation_email(user=user, mail_type="data_change_confirmation", email=user_data.email) + await user_service.send_confirmation_email(user=user, mail_type="data_change_confirmation", email=user_data.email) @router.post( @@ -316,4 +315,4 @@ async def change_user_data( user: SUser = Depends(get_current_user), uow=Depends(UnitOfWork), ): - await UserService.change_data(uow=uow, user=user, user_data=user_data) + await user_service.change_data(uow=uow, user=user, user_data=user_data) diff --git a/ruff.toml b/ruff.toml index f186da1..43a25b3 100644 --- a/ruff.toml +++ b/ruff.toml @@ -42,7 +42,7 @@ target-version = "py312" [lint] # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. select = ["F", "E", "W", "C"] -ignore = ["W191", "W391", "C901"] +ignore = ["W191", "W391", "C901", "C408"] # Allow fix for all enabled rules (when `--fix`) is provided. fixable = ["ALL"]