chat_back/app/services/user_service.py

121 lines
4.3 KiB
Python

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 redis.set_key(
key=f"user: {dict(id=user.id)}", expire_time=timedelta(hours=1), value=user.model_dump_json()
)
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)