From 857ff4c780319fd235b8c84cdf68819ad71720e6 Mon Sep 17 00:00:00 2001 From: urec56 Date: Wed, 17 Jul 2024 16:52:00 +0400 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=B8=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/chat_service.py | 31 ++++++++++++++-- app/services/redis_service.py | 32 +++++++++++----- app/services/user_service.py | 70 ++++++++++++++++++++++++++--------- 3 files changed, 103 insertions(+), 30 deletions(-) diff --git a/app/services/chat_service.py b/app/services/chat_service.py index 668c6ce..9a45d80 100644 --- a/app/services/chat_service.py +++ b/app/services/chat_service.py @@ -1,8 +1,12 @@ +from datetime import timedelta from urllib.parse import urljoin +from pydantic import ValidationError + from app.chat.exceptions import UserDontHavePermissionException, UserCanNotReadThisChatException 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.utils.unit_of_work import UnitOfWork from app.users.schemas import SInvitationData @@ -18,9 +22,20 @@ class ChatService: @staticmethod async def get_all_chats(uow: UnitOfWork, user_id: int) -> SAllowedChats: - async with uow: - allowed_chats = await uow.user.get_user_allowed_chats(user_id=user_id) - return allowed_chats + 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: + 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 @staticmethod async def create_chat(uow: UnitOfWork, user_id: int, user_to_exclude_id: int, chat_name: str) -> int: @@ -36,6 +51,8 @@ class ChatService: ) await uow.chat.add_user_to_chat(user_id, chat_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 @@ -48,6 +65,8 @@ class ChatService: 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 @@ -65,6 +84,8 @@ class ChatService: 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 @@ -74,6 +95,8 @@ class ChatService: 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 @@ -83,6 +106,8 @@ class ChatService: 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 diff --git a/app/services/redis_service.py b/app/services/redis_service.py index ed5c58d..c290b80 100644 --- a/app/services/redis_service.py +++ b/app/services/redis_service.py @@ -1,8 +1,11 @@ from datetime import timedelta +from pydantic import BaseModel from redis.asyncio.client import Redis +from redis.exceptions import RedisError from app.config import settings +from app.exceptions import BlackPhoenixException def get_redis_session() -> Redis: @@ -10,17 +13,26 @@ def get_redis_session() -> Redis: class RedisService: - @staticmethod - async def set_verification_code(redis: Redis, user_id: int, verification_code: str) -> None: - await redis.setex(f"user_verification_code: {user_id}", timedelta(minutes=5), verification_code) + def __init__(self, is_raise=None): + self.redis_session_factory = get_redis_session + self.is_raise = is_raise - @staticmethod - async def get_verification_code(redis: Redis, user_id: int) -> str: - verification_code = await redis.get(f"user_verification_code: {user_id}") - return verification_code.decode() + async def __aenter__(self): + self.redis_session = self.redis_session_factory() + return self - @staticmethod - async def delete_verification_code(redis: Redis, user_id: int) -> None: - await redis.delete(f"user_verification_code: {user_id}") + async def __aexit__(self, exc_type, exc, tb): + if isinstance(exc, RedisError): + if self.is_raise: + raise BlackPhoenixException + return True + async def set_key(self, key: str, expire_time: timedelta, value: str) -> None: + await self.redis_session.setex(key, expire_time, value) + async def get_value[T: type[BaseModel]](self, key: str, model: T | None = None) -> T | str: + value = await self.redis_session.get(key) + return model.model_validate_json(value) if model else value.decode() + + async def delete_key(self, key: str) -> None: + await self.redis_session.delete(key) diff --git a/app/services/user_service.py b/app/services/user_service.py index e3fb865..557465c 100644 --- a/app/services/user_service.py +++ b/app/services/user_service.py @@ -1,5 +1,9 @@ +from datetime import timedelta + +from pydantic import ValidationError + from app.config import settings -from app.services.redis_service import get_redis_session, RedisService +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 from app.users.exceptions import PasswordsMismatchException, WrongCodeException @@ -10,15 +14,33 @@ from app.services.auth_service import AuthService class UserService: @staticmethod async def find_user(uow: UnitOfWork, **find_by) -> SUser: - async with uow: - user = await uow.user.find_one(**find_by) - return user + try: + async with RedisService() as redis: + user = await redis.get_value(key=f"user: {find_by}", model=SUser) + return user + except ValidationError: + 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(hours=1), value=user.model_dump_json() + ) + return user @staticmethod async def find_all(uow: UnitOfWork, username: str) -> SUsers: - async with uow: - users = await uow.user.find_all(username=username) - return users + try: + async with RedisService() as redis: + users = await redis.get_value(key=f"users: {username}", model=SUsers) + return users + except ValidationError: + async with uow: + users = await uow.user.find_all(username=username) + async with RedisService() as redis: + await redis.set_key( + key=f"users: {username}", expire_time=timedelta(hours=1), value=users.model_dump_json() + ) + return users @staticmethod async def add_user(uow: UnitOfWork, user_data: SUserRegister) -> SUser: @@ -39,10 +61,10 @@ class UserService: @staticmethod async def send_confirmation_email(user: SUser, mail_type: str, email: str | None = None) -> None: - redis_session = get_redis_session() user_code = generate_confirmation_code() - await RedisService.delete_verification_code(redis=redis_session, user_id=user.id) - await RedisService.set_verification_code(redis=redis_session, user_id=user.id, verification_code=user_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( { @@ -57,13 +79,20 @@ class UserService: @staticmethod async def verificate_user(uow: UnitOfWork, user: SUser, confirmation_code: str) -> None: - redis_session = get_redis_session() - verification_code = await RedisService.get_verification_code(redis=redis_session, user_id=user.id) - + 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 redis.set_key( + key=f"user: {dict(id=user.id)}", expire_time=timedelta(hours=1), value=user.model_dump_json() + ) await uow.commit() @staticmethod @@ -74,11 +103,16 @@ class UserService: @staticmethod async def change_data(uow: UnitOfWork, user: SUser, user_data: SUserChangeData) -> None: - redis_session = get_redis_session() - verification_code = await RedisService.get_verification_code(redis=redis_session, user_id=user.id) + 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 + 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, @@ -90,5 +124,7 @@ class UserService: (await uow.user.add_user_avatar(user_id=user.id, avatar=str(user_data.avatar_url))) if user_data.avatar_url else None await uow.commit() + async with RedisService() as redis: + await redis.delete_key(key=f"user: {dict(id=user.id)}") + send_data_change_email.delay(user_data.username, user_data.email) - await RedisService.delete_verification_code(redis=redis_session, user_id=user.id)