diff --git a/app/chat/shemas.py b/app/chat/shemas.py index df53ebe..25aaec2 100644 --- a/app/chat/shemas.py +++ b/app/chat/shemas.py @@ -11,8 +11,8 @@ class SMessage(BaseModel): chat_id: int user_id: int username: str - created_at: datetime avatar_image: str + created_at: datetime answer_id: UUID | None answer_message: str | None answer_image_url: str | None diff --git a/app/chat/websocket.py b/app/chat/websocket.py index 4b04f65..f38c592 100644 --- a/app/chat/websocket.py +++ b/app/chat/websocket.py @@ -55,7 +55,10 @@ class ConnectionManager: message = SSendMessage.model_validate(message) new_message = await MessageService.send_message( - uow=uow, user_id=user_id, chat_id=chat_id, message=message, image_url=message.image_url + uow=uow, + user_id=user_id, + chat_id=chat_id, + message=message, ) new_message = new_message.model_dump() new_message["id"] = str(new_message["id"]) @@ -83,7 +86,10 @@ class ConnectionManager: raise UserDontHavePermissionException await MessageService.edit_message( - uow=uow, message_id=message.id, new_message=message.new_message, new_image_url=message.new_image_url + uow=uow, + message_id=message.id, + new_message=message.new_message, + new_image_url=message.new_image_url, ) new_message = { "flag": "edit", @@ -97,7 +103,10 @@ class ConnectionManager: async def _pin(uow: UnitOfWork, _: int, chat_id: int, message: dict) -> dict: message = SPinMessage.model_validate(message) pinned_message = await MessageService.pin_message( - uow=uow, chat_id=chat_id, user_id=message.user_id, message_id=message.id + uow=uow, + chat_id=chat_id, + user_id=message.user_id, + message_id=message.id, ) new_message = pinned_message.model_dump() new_message["id"] = str(new_message["id"]) diff --git a/app/dao/message.py b/app/dao/message.py index 98d5dce..04b7fa0 100644 --- a/app/dao/message.py +++ b/app/dao/message.py @@ -9,14 +9,14 @@ class MessageDAO: self.message = mongo_db.message async def send_message( - self, - user_id: int, - chat_id: int, - message: str | None, - image_url: str | None, - answer_id: UUID | None = None, - answer_message: str | None = None, - answer_image_url: str | None = None, + self, + user_id: int, + chat_id: int, + message: str | None, + image_url: str | None, + answer_id: UUID | None = None, + answer_message: str | None = None, + answer_image_url: str | None = None, ) -> UUID: message_id = uuid4() await self.message.insert_one( @@ -41,7 +41,7 @@ class MessageDAO: async def delete_message(self, message_id: UUID) -> None: await self.message.update_one({"id": str(message_id)}, {"$set": {"visibility": False}}) - await self.message.update_one( + await self.message.update_many( {"answer_id": str(message_id)}, {"$set": {"answer_message": None, "answer_image_url": None}} ) @@ -56,12 +56,12 @@ class MessageDAO: {"id": str(message_id)}, {"$set": {"message": new_message, "image_url": new_image_url}} ) - await self.message.update_one( + await self.message.update_many( {"answer_id": str(message_id)}, {"$set": {"answer_message": new_message, "answer_image_url": new_image_url}} ) - async def get_messages_from_ids(self, messages_ids: list[UUID]) -> SMessageRawList: + async def get_messages_by_ids(self, messages_ids: list[UUID]) -> SMessageRawList: cursor = self.message.find({"visibility": True, "id": {"$in": [str(message_id) for message_id in messages_ids]}}) return SMessageRawList.model_validate({"message_raw_list": await cursor.to_list(length=None) or None}) diff --git a/app/models/chat.py b/app/models/chat.py index 649898c..3771f97 100644 --- a/app/models/chat.py +++ b/app/models/chat.py @@ -9,7 +9,7 @@ class Chat(Base): id: Mapped[int] = mapped_column(primary_key=True) created_by: Mapped[int] = mapped_column(ForeignKey("users.id")) - chat_for = mapped_column(ForeignKey("users.id")) + chat_for: Mapped[int] = mapped_column(ForeignKey("users.id")) chat_name: Mapped[str] avatar_image: Mapped[str] visibility: Mapped[bool] = mapped_column(server_default="true") diff --git a/app/models/pinned_chat.py b/app/models/pinned_chat.py index dbc2062..e855798 100644 --- a/app/models/pinned_chat.py +++ b/app/models/pinned_chat.py @@ -1,5 +1,5 @@ from sqlalchemy import ForeignKey -from sqlalchemy.orm import mapped_column +from sqlalchemy.orm import mapped_column, Mapped from app.database import Base @@ -7,5 +7,5 @@ from app.database import Base class PinnedChat(Base): __tablename__ = "pinned_chat" - user_id = mapped_column(ForeignKey("users.id"), primary_key=True) - chat_id = mapped_column(ForeignKey("chat.id"), primary_key=True) + user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True) + chat_id: Mapped[int] = mapped_column(ForeignKey("chat.id"), primary_key=True) diff --git a/app/models/pinned_message.py b/app/models/pinned_message.py index eb9807c..59df228 100644 --- a/app/models/pinned_message.py +++ b/app/models/pinned_message.py @@ -9,6 +9,6 @@ from app.database import Base class PinnedMessage(Base): __tablename__ = "pinned_message" - chat_id = mapped_column(ForeignKey("chat.id")) + chat_id: Mapped[int] = mapped_column(ForeignKey("chat.id")) message_id: Mapped[UUID] = mapped_column(primary_key=True) - user_id = mapped_column(ForeignKey("users.id"), primary_key=True) + user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True) diff --git a/app/services/auth_service.py b/app/services/auth_service.py index 5a86655..a521232 100644 --- a/app/services/auth_service.py +++ b/app/services/auth_service.py @@ -15,19 +15,18 @@ 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") +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) + 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, hashed_password) + return pwd_context.verify(plain_password.encode("utf-8")[:72], hashed_password) @staticmethod def create_access_token(data: dict[str, str | datetime]) -> str: diff --git a/app/services/message_service.py b/app/services/message_service.py index 74f8273..56e8656 100644 --- a/app/services/message_service.py +++ b/app/services/message_service.py @@ -20,7 +20,7 @@ class MessageService: return user @classmethod - async def add_avatar_image_and_username_to_message(cls, uow: UnitOfWork, message: SMessageRaw) -> SMessage: + 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) @@ -28,13 +28,15 @@ class MessageService: return SMessage.model_validate(message) @classmethod - async def add_avatar_image_and_username_to_message_list( - cls, uow: UnitOfWork, messages: SMessageRawList + 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) + 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 } @@ -42,7 +44,11 @@ class MessageService: @classmethod async def send_message( - cls, uow: UnitOfWork, user_id: int, chat_id: int, message: SSendMessage, image_url: str | None = None + cls, + uow: UnitOfWork, + user_id: int, + chat_id: int, + message: SSendMessage, ) -> SMessage: async with uow: if message.answer: @@ -51,7 +57,7 @@ class MessageService: user_id=user_id, chat_id=chat_id, message=message.message, - image_url=image_url, + image_url=message.image_url, answer_id=answer_message.id, answer_message=answer_message.message, answer_image_url=answer_message.answer_image_url, @@ -61,10 +67,10 @@ class MessageService: user_id=user_id, chat_id=chat_id, message=message.message, - image_url=image_url, + image_url=message.image_url, ) 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) + new_message = await cls._add_avatar_image_and_username_to_message(uow=uow, message=raw_message) return new_message @@ -77,7 +83,9 @@ class MessageService: 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 + message_id=message_id, + new_message=new_message, + new_image_url=new_image_url, ) @classmethod @@ -87,7 +95,7 @@ class MessageService: 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) + pinned_message = await cls._add_avatar_image_and_username_to_message(uow=uow, message=raw_message) return pinned_message @@ -101,24 +109,30 @@ class MessageService: 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) + 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_from_ids(messages_ids=pinned_messages_ids) - pinned_messages = await cls.add_avatar_image_and_username_to_message_list(uow=uow, messages=raw_messages) + 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 + 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 + 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) + messages = await cls._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 a7c20bc..f7068d1 100644 --- a/app/services/user_service.py +++ b/app/services/user_service.py @@ -3,6 +3,7 @@ from datetime import timedelta 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.utils.unit_of_work import UnitOfWork @@ -15,15 +16,17 @@ class UserService: @staticmethod async def find_user(uow: UnitOfWork, **find_by) -> SUser: try: - async with RedisService() as redis: + async with RedisService(is_raise=True) as redis: user = await redis.get_value(key=f"user: {find_by}", model=SUser) return user - except ValidationError: + 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() + key=f"user: {find_by}", + expire_time=timedelta(minutes=30), + value=user.model_dump_json(), ) return user diff --git a/app/users/router.py b/app/users/router.py index 63586cc..7872743 100644 --- a/app/users/router.py +++ b/app/users/router.py @@ -194,7 +194,9 @@ 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( - uow=uow, email_or_username=user_data.email_or_username, password=user_data.password + uow=uow, + email_or_username=user_data.email_or_username, + password=user_data.password, ) access_token = AuthService.create_access_token({"sub": str(user.id)}) return {"authorization": f"Bearer {access_token}"} @@ -310,6 +312,8 @@ async def send_confirmation_code(user_data: SUserSendConfirmationCode, user: SUs }, ) async def change_user_data( - user_data: SUserChangeData, user: SUser = Depends(get_current_user), uow=Depends(UnitOfWork) + user_data: SUserChangeData, + user: SUser = Depends(get_current_user), + uow=Depends(UnitOfWork), ): await UserService.change_data(uow=uow, user=user, user_data=user_data) diff --git a/app/users/schemas.py b/app/users/schemas.py index 4b2861b..ad058cc 100644 --- a/app/users/schemas.py +++ b/app/users/schemas.py @@ -120,7 +120,7 @@ class SGetUsersFilter(BaseModel): @classmethod def validate_username(cls, v): if not v: - return "%" + return "%%" else: return f"%{v}%"