from datetime import timedelta from pydantic import ValidationError from app.config import settings 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 from app.users.schemas import SUser, SUsers, SUserRegister, SConfirmationData, SUserAvatars, SUserChangeData from app.services.auth_service import AuthService class UserService: @staticmethod async def find_user(uow: UnitOfWork, **find_by) -> SUser: 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(minutes=30), 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 @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() return user @staticmethod async def send_confirmation_email(user: SUser, mail_type: str, email: str | None = None) -> None: user_code = 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, } ) 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() 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)