Переделал некоторые возвращаемые объекты
This commit is contained in:
parent
7759cdf64d
commit
8cbd025395
13 changed files with 165 additions and 138 deletions
|
@ -4,7 +4,17 @@ from fastapi import APIRouter, Depends, status
|
|||
|
||||
from app.config import settings
|
||||
from app.exceptions import UserDontHavePermissionException, MessageNotFoundException, UserCanNotReadThisChatException
|
||||
from app.chat.shemas import SMessage, SLastMessages, SPinnedChat, SDeletedUser, SChat, SDeletedChat
|
||||
from app.chat.shemas import (
|
||||
SMessage,
|
||||
SLastMessages,
|
||||
SPinnedChat,
|
||||
SDeletedUser,
|
||||
SDeletedChat,
|
||||
SAllowedChats,
|
||||
SMessageList,
|
||||
SPinnedChats,
|
||||
SPinnedMessages
|
||||
)
|
||||
from app.unit_of_work import UnitOfWork
|
||||
|
||||
from app.users.dependencies import check_verificated_user_with_exc
|
||||
|
@ -17,7 +27,7 @@ router = APIRouter(prefix="/chat", tags=["Чат"])
|
|||
@router.get(
|
||||
"",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SChat]
|
||||
response_model=SAllowedChats,
|
||||
)
|
||||
async def get_all_chats(user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
async with uow:
|
||||
|
@ -30,7 +40,12 @@ async def get_all_chats(user: SUser = Depends(check_verificated_user_with_exc),
|
|||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=None,
|
||||
)
|
||||
async def create_chat(user_to_exclude: int, chat_name: str, user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
async def create_chat(
|
||||
user_to_exclude: int,
|
||||
chat_name: str,
|
||||
user: SUser = Depends(check_verificated_user_with_exc),
|
||||
uow=Depends(UnitOfWork)
|
||||
):
|
||||
async with uow:
|
||||
if user.id == user_to_exclude:
|
||||
raise UserCanNotReadThisChatException
|
||||
|
@ -43,7 +58,7 @@ async def create_chat(user_to_exclude: int, chat_name: str, user: SUser = Depend
|
|||
@router.get(
|
||||
"/get_last_message/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SMessage]
|
||||
response_model=SMessageList,
|
||||
)
|
||||
async def get_last_message(chat_id: int, user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id)
|
||||
|
@ -59,10 +74,13 @@ async def get_last_message(chat_id: int, user: SUser = Depends(check_verificated
|
|||
@router.get(
|
||||
"/get_some_messages/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SMessage]
|
||||
response_model=SMessageList,
|
||||
)
|
||||
async def get_some_messages(
|
||||
chat_id: int, last_messages: SLastMessages = Depends(), user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)
|
||||
chat_id: int,
|
||||
last_messages: SLastMessages = Depends(),
|
||||
user: SUser = Depends(check_verificated_user_with_exc),
|
||||
uow=Depends(UnitOfWork)
|
||||
):
|
||||
await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id)
|
||||
async with uow:
|
||||
|
@ -81,9 +99,14 @@ async def get_some_messages(
|
|||
@router.get(
|
||||
"/message/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SMessage
|
||||
response_model=SMessage,
|
||||
)
|
||||
async def get_message_by_id(chat_id: int, message_id: int, user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
async def get_message_by_id(
|
||||
chat_id: int,
|
||||
message_id: int,
|
||||
user: SUser = Depends(check_verificated_user_with_exc),
|
||||
uow=Depends(UnitOfWork)
|
||||
):
|
||||
await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id)
|
||||
async with uow:
|
||||
message = await uow.chat.get_message_by_id(message_id=message_id)
|
||||
|
@ -99,7 +122,11 @@ async def get_message_by_id(chat_id: int, message_id: int, user: SUser = Depends
|
|||
status_code=status.HTTP_201_CREATED,
|
||||
response_model=SCreateInvitationLink,
|
||||
)
|
||||
async def create_invitation_link(chat_id: int, user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
async def create_invitation_link(
|
||||
chat_id: int,
|
||||
user: SUser = Depends(check_verificated_user_with_exc),
|
||||
uow=Depends(UnitOfWork)
|
||||
):
|
||||
await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id)
|
||||
cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
|
||||
invitation_token = cipher_suite.encrypt(str(chat_id).encode())
|
||||
|
@ -112,7 +139,11 @@ async def create_invitation_link(chat_id: int, user: SUser = Depends(check_verif
|
|||
status_code=status.HTTP_200_OK,
|
||||
response_model=SUserAddedToChat,
|
||||
)
|
||||
async def invite_to_chat(invitation_token: str, user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
async def invite_to_chat(
|
||||
invitation_token: str,
|
||||
user: SUser = Depends(check_verificated_user_with_exc),
|
||||
uow=Depends(UnitOfWork)
|
||||
):
|
||||
async with uow:
|
||||
invitation_token = invitation_token.encode()
|
||||
cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
|
||||
|
@ -139,9 +170,14 @@ async def delete_chat(chat_id: int, user: SUser = Depends(check_verificated_user
|
|||
@router.delete(
|
||||
"/delete_user_from_chat/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SDeletedUser
|
||||
response_model=SDeletedUser,
|
||||
)
|
||||
async def delete_user_from_chat(chat_id: int, user_id: int, user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
async def delete_user_from_chat(
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
user: SUser = Depends(check_verificated_user_with_exc),
|
||||
uow=Depends(UnitOfWork)
|
||||
):
|
||||
async with uow:
|
||||
chat = await uow.chat.find_one_or_none(id=chat_id)
|
||||
if user.id == chat.created_by:
|
||||
|
@ -152,7 +188,7 @@ async def delete_user_from_chat(chat_id: int, user_id: int, user: SUser = Depend
|
|||
@router.post(
|
||||
"/pin_chat",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SPinnedChat
|
||||
response_model=SPinnedChat,
|
||||
)
|
||||
async def pinn_chat(chat_id: int, user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id)
|
||||
|
@ -164,7 +200,7 @@ async def pinn_chat(chat_id: int, user: SUser = Depends(check_verificated_user_w
|
|||
@router.delete(
|
||||
"/unpin_chat",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=SPinnedChat
|
||||
response_model=SPinnedChat,
|
||||
)
|
||||
async def unpinn_chat(chat_id: int, user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id)
|
||||
|
@ -176,7 +212,7 @@ async def unpinn_chat(chat_id: int, user: SUser = Depends(check_verificated_user
|
|||
@router.get(
|
||||
"/get_pinned_chats",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SChat]
|
||||
response_model=SPinnedChats,
|
||||
)
|
||||
async def get_pinned_chats(user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
async with uow:
|
||||
|
@ -186,7 +222,7 @@ async def get_pinned_chats(user: SUser = Depends(check_verificated_user_with_exc
|
|||
@router.get(
|
||||
"/pinned_messages/{chat_id}",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SMessage] | None
|
||||
response_model=SPinnedMessages,
|
||||
)
|
||||
async def pinned_messages(chat_id: int, user: SUser = Depends(check_verificated_user_with_exc), uow=Depends(UnitOfWork)):
|
||||
await AuthService.validate_user_access_to_chat(uow=uow, chat_id=chat_id, user_id=user.id)
|
||||
|
|
|
@ -19,6 +19,10 @@ class SMessage(BaseModel):
|
|||
answer_id: int | None
|
||||
|
||||
|
||||
class SMessageList(BaseModel):
|
||||
messages: list[SMessage]
|
||||
|
||||
|
||||
class SLastMessages(BaseModel):
|
||||
messages_loaded: int
|
||||
messages_to_get: int
|
||||
|
@ -45,6 +49,18 @@ class SChat(BaseModel):
|
|||
avatar_hex: str
|
||||
|
||||
|
||||
class SAllowedChats(BaseModel):
|
||||
allowed_chats: list[SChat]
|
||||
|
||||
|
||||
class SPinnedChats(BaseModel):
|
||||
pinned_chat: list[SChat]
|
||||
|
||||
|
||||
class SPinnedMessages(BaseModel):
|
||||
pinned_messages: list[SMessage] | None
|
||||
|
||||
|
||||
class SSendMessage(BaseModel):
|
||||
flag: str
|
||||
message: str
|
||||
|
|
|
@ -117,7 +117,12 @@ manager = ConnectionManager()
|
|||
@router.websocket(
|
||||
"/ws/{chat_id}",
|
||||
)
|
||||
async def websocket_endpoint(chat_id: int, websocket: WebSocket, user: SUser = Depends(get_current_user_ws), uow=Depends(UnitOfWork)):
|
||||
async def websocket_endpoint(
|
||||
chat_id: int,
|
||||
websocket: WebSocket,
|
||||
user: SUser = Depends(get_current_user_ws),
|
||||
uow=Depends(UnitOfWork)
|
||||
):
|
||||
await AuthService.check_verificated_user_with_exc(uow=uow, user_id=user.id)
|
||||
await AuthService.validate_user_access_to_chat(uow=uow, user_id=user.id, chat_id=chat_id)
|
||||
await manager.connect(chat_id, websocket)
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from sqlalchemy import select, insert
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
|
||||
|
@ -8,17 +7,3 @@ class BaseDAO:
|
|||
def __init__(self, session: AsyncSession):
|
||||
self.session = session
|
||||
|
||||
async def add(self, **data): # Метод добавляет данные в БД
|
||||
stmt = insert(self.model).values(**data).returning(self.model.id)
|
||||
result = await self.session.execute(stmt)
|
||||
return result.scalar()
|
||||
|
||||
async def find_one_or_none(self, **filter_by): # Метод проверяет наличие строки с заданными параметрами
|
||||
query = select(self.model).filter_by(**filter_by)
|
||||
result = await self.session.execute(query)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
async def find_all(self, **filter_by): # Метод возвращает все строки таблицы или те, которые соответствуют отбору
|
||||
query = select(self.model.__table__.columns).filter_by(**filter_by)
|
||||
result = await self.session.execute(query)
|
||||
return result.mappings().all()
|
||||
|
|
|
@ -1,29 +1,42 @@
|
|||
from pydantic import HttpUrl
|
||||
from sqlalchemy import update, select, insert, func
|
||||
from sqlalchemy.exc import MultipleResultsFound, IntegrityError
|
||||
|
||||
from app.dao.base import BaseDAO
|
||||
from app.database import engine # noqa
|
||||
from app.exceptions import IncorrectDataException, UserAlreadyExistsException
|
||||
from app.models.chat import Chats
|
||||
from app.models.user_avatar import UserAvatar
|
||||
from app.models.users import Users
|
||||
from app.models.user_chat import UserChat
|
||||
from app.users.schemas import SUser, SUserAvatars
|
||||
from app.users.schemas import SUser, SUserAvatars, SUsers
|
||||
|
||||
|
||||
class UserDAO(BaseDAO):
|
||||
model = Users
|
||||
|
||||
async def find_one_or_none(self, **filter_by) -> SUser | None:
|
||||
query = select(Users).filter_by(**filter_by)
|
||||
result = await self.session.execute(query)
|
||||
result = result.scalar_one_or_none()
|
||||
if result:
|
||||
return SUser.model_validate(result, from_attributes=True)
|
||||
async def add(self, **data) -> int:
|
||||
try:
|
||||
stmt = insert(self.model).values(**data).returning(Users.id)
|
||||
result = await self.session.execute(stmt)
|
||||
return result.scalar()
|
||||
except IntegrityError:
|
||||
raise UserAlreadyExistsException
|
||||
|
||||
async def find_all(self, **filter_by):
|
||||
query = select(Users.__table__.columns).filter_by(**filter_by).where(Users.role != 100)
|
||||
async def find_one_or_none(self, **filter_by) -> SUser | None:
|
||||
try:
|
||||
query = select(Users.__table__.columns).filter_by(**filter_by)
|
||||
result = await self.session.execute(query)
|
||||
result = result.mappings().one()
|
||||
if result:
|
||||
return SUser.model_validate(result)
|
||||
except MultipleResultsFound:
|
||||
raise IncorrectDataException
|
||||
|
||||
async def find_all(self) -> SUsers:
|
||||
query = select(Users.__table__.columns).where(Users.role != 100)
|
||||
result = await self.session.execute(query)
|
||||
return result.mappings().all()
|
||||
return SUsers.model_validate({"users": result.mappings().all()})
|
||||
|
||||
async def change_data(self, user_id: int, **data_to_change) -> str:
|
||||
query = update(Users).where(Users.id == user_id).values(**data_to_change).returning(Users.username)
|
|
@ -14,21 +14,11 @@ class UserAlreadyExistsException(BlackPhoenixException):
|
|||
detail = "Пользователь с таким ником или почтой уже существует"
|
||||
|
||||
|
||||
class UsernameAlreadyInUseException(BlackPhoenixException):
|
||||
status_code = status.HTTP_409_CONFLICT
|
||||
detail = "Ник занят"
|
||||
|
||||
|
||||
class IncorrectAuthDataException(BlackPhoenixException):
|
||||
status_code = status.HTTP_401_UNAUTHORIZED
|
||||
detail = "Введены не верные данные"
|
||||
|
||||
|
||||
class IncorrectPasswordException(BlackPhoenixException):
|
||||
status_code = status.HTTP_409_CONFLICT
|
||||
detail = "Введён не верный пароль"
|
||||
|
||||
|
||||
class IncorrectTokenFormatException(BlackPhoenixException):
|
||||
status_code = status.HTTP_401_UNAUTHORIZED
|
||||
detail = "Некорректный формат токена"
|
||||
|
@ -93,11 +83,6 @@ class UserMustConfirmEmailException(BlackPhoenixException):
|
|||
detail = "Сначала подтвердите почту"
|
||||
|
||||
|
||||
class SomethingWentWrongException(BlackPhoenixException):
|
||||
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
||||
detail = "Что-то пошло не так"
|
||||
|
||||
|
||||
class IncorrectDataException(BlackPhoenixException):
|
||||
status_code = status.HTTP_409_CONFLICT
|
||||
detail = "Ты передал какую-то хуйню"
|
||||
|
|
|
@ -4,7 +4,9 @@ from app.unit_of_work import UnitOfWork
|
|||
|
||||
class MessageService:
|
||||
@staticmethod
|
||||
async def send_message(uow: UnitOfWork, user_id: int, chat_id: int, message: str, image_url: str | None = None) -> SMessage:
|
||||
async def send_message(
|
||||
uow: UnitOfWork, user_id: int, chat_id: int, message: str, image_url: str | None = None
|
||||
) -> SMessage:
|
||||
async with uow:
|
||||
new_message = await uow.chat.send_message(user_id=user_id, chat_id=chat_id, message=message, image_url=image_url)
|
||||
return new_message
|
||||
|
@ -24,7 +26,9 @@ class MessageService:
|
|||
@staticmethod
|
||||
async def edit_message(uow: UnitOfWork, message_id: int, new_message: str, new_image_url: str) -> bool:
|
||||
async with uow:
|
||||
new_message = await uow.chat.edit_message(message_id=message_id, new_message=new_message, new_image_url=new_image_url)
|
||||
new_message = await uow.chat.edit_message(
|
||||
message_id=message_id, new_message=new_message, new_image_url=new_image_url
|
||||
)
|
||||
return new_message
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import json
|
||||
import smtplib
|
||||
import random
|
||||
import string
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
from pydantic import EmailStr
|
||||
|
||||
|
||||
|
@ -14,38 +12,30 @@ from app.tasks.email_templates import (
|
|||
create_password_change_confirmation_template,
|
||||
create_password_recover_template,
|
||||
)
|
||||
from app.users.auth import encode_invitation_token
|
||||
from app.users.schemas import SInvitationData
|
||||
|
||||
|
||||
def generate_confirmation_code(length=6):
|
||||
def generate_confirmation_code(length=6) -> str:
|
||||
characters = string.ascii_letters + string.digits
|
||||
confirmation_code = "".join(random.choice(characters) for _ in range(length))
|
||||
return confirmation_code
|
||||
|
||||
|
||||
@celery.task
|
||||
def send_registration_confirmation_email(user_id: int, username: str, email_to: EmailStr, MODE: str):
|
||||
confirmation_code = generate_confirmation_code()
|
||||
def send_registration_confirmation_email(user_data: SInvitationData):
|
||||
invitation_token = encode_invitation_token(user_data)
|
||||
|
||||
if MODE == "TEST":
|
||||
return confirmation_code
|
||||
|
||||
load = {"user_id": user_id, "username": username, "email": email_to, "confirmation_code": confirmation_code}
|
||||
str_load = json.dumps(load)
|
||||
cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
|
||||
invitation_token = cipher_suite.encrypt(str_load.encode())
|
||||
|
||||
confirmation_link = settings.INVITATION_LINK_HOST + "/api/users/email_verification/" + invitation_token.decode()
|
||||
confirmation_link = settings.INVITATION_LINK_HOST + "/api/users/email_verification/" + invitation_token
|
||||
|
||||
msg_content = create_registration_confirmation_template(
|
||||
username=username, email_to=email_to, confirmation_link=confirmation_link
|
||||
username=user_data.username, email_to=user_data.email_to, confirmation_link=confirmation_link
|
||||
)
|
||||
|
||||
with smtplib.SMTP_SSL(settings.SMTP_HOST, settings.SMTP_PORT) as server:
|
||||
server.login(settings.SMTP_USER, settings.SMTP_PASS)
|
||||
server.send_message(msg_content)
|
||||
|
||||
return confirmation_code
|
||||
|
||||
|
||||
@celery.task
|
||||
def send_password_change_email(username: str, email_to: EmailStr, MODE: str):
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from app.chat.dao import ChatDAO
|
||||
from app.dao.chat import ChatDAO
|
||||
from app.database import async_session_maker
|
||||
from app.users.dao import UserDAO
|
||||
from app.dao.user import UserDAO
|
||||
|
||||
|
||||
class UnitOfWork:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from datetime import datetime, timedelta, UTC
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
from jose import jwt
|
||||
from passlib.context import CryptContext
|
||||
from pydantic import EmailStr
|
||||
|
@ -8,12 +9,11 @@ from app.config import settings
|
|||
from app.exceptions import (
|
||||
UserDontHavePermissionException,
|
||||
IncorrectAuthDataException,
|
||||
UserAlreadyExistsException,
|
||||
UserNotFoundException,
|
||||
UserMustConfirmEmailException,
|
||||
)
|
||||
from app.unit_of_work import UnitOfWork
|
||||
from app.users.schemas import SUserRegister, SUser
|
||||
from app.users.schemas import SUser, SInvitationData
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
|
@ -39,6 +39,19 @@ def create_access_token(data: dict[str, str | datetime]) -> str:
|
|||
return encoded_jwt
|
||||
|
||||
|
||||
def encode_invitation_token(user_data: SInvitationData) -> str:
|
||||
cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
|
||||
invitation_token = cipher_suite.encrypt(user_data.model_dump_json().encode())
|
||||
return invitation_token.decode()
|
||||
|
||||
|
||||
def decode_invitation_token(invitation_token: str) -> SInvitationData:
|
||||
user_code = invitation_token.encode()
|
||||
cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
|
||||
user_data = cipher_suite.decrypt(user_code)
|
||||
return SInvitationData.model_validate_json(user_data)
|
||||
|
||||
|
||||
class AuthService:
|
||||
@staticmethod
|
||||
async def authenticate_user_by_email(uow: UnitOfWork, email: EmailStr, password: str) -> SUser | None:
|
||||
|
@ -65,16 +78,6 @@ class AuthService:
|
|||
raise IncorrectAuthDataException
|
||||
return user
|
||||
|
||||
@staticmethod
|
||||
async def check_existing_user(uow: UnitOfWork, user_data: SUserRegister) -> None:
|
||||
async with uow:
|
||||
existing_user = await uow.user.find_one_or_none(email=user_data.email)
|
||||
if existing_user:
|
||||
raise UserAlreadyExistsException
|
||||
existing_user = await uow.user.find_one_or_none(username=user_data.username)
|
||||
if existing_user:
|
||||
raise UserAlreadyExistsException
|
||||
|
||||
@staticmethod
|
||||
async def check_verificated_user(uow: UnitOfWork, user_id: int) -> bool:
|
||||
async with uow:
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
import json
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
from fastapi import APIRouter, Depends, status
|
||||
|
||||
from app.config import settings
|
||||
|
@ -12,7 +9,8 @@ from app.exceptions import (
|
|||
)
|
||||
from app.services.redis_service import RedisService, get_redis_session
|
||||
from app.unit_of_work import UnitOfWork
|
||||
from app.users.auth import get_password_hash, create_access_token, VERIFICATED_USER, AuthService, verify_password
|
||||
from app.users.auth import get_password_hash, create_access_token, VERIFICATED_USER, AuthService, verify_password, \
|
||||
decode_invitation_token
|
||||
from app.users.dependencies import get_current_user
|
||||
from app.users.schemas import (
|
||||
SUserLogin,
|
||||
|
@ -20,16 +18,17 @@ from app.users.schemas import (
|
|||
SUserResponse,
|
||||
SEmailVerification,
|
||||
SUserAvatars,
|
||||
SUsername,
|
||||
SEmail,
|
||||
SUserFilter,
|
||||
SUser,
|
||||
SUserChangeData,
|
||||
SUserSendConfirmationCode,
|
||||
STokenLogin,
|
||||
SUsers,
|
||||
SInvitationData,
|
||||
)
|
||||
from app.tasks.tasks import (
|
||||
send_registration_confirmation_email,
|
||||
send_data_change_confirmation_email
|
||||
send_data_change_confirmation_email, generate_confirmation_code
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/users", tags=["Пользователи"])
|
||||
|
@ -38,7 +37,7 @@ router = APIRouter(prefix="/users", tags=["Пользователи"])
|
|||
@router.get(
|
||||
"",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=list[SUserResponse],
|
||||
response_model=SUsers,
|
||||
)
|
||||
async def get_all_users(uow=Depends(UnitOfWork)):
|
||||
async with uow:
|
||||
|
@ -47,25 +46,13 @@ async def get_all_users(uow=Depends(UnitOfWork)):
|
|||
|
||||
|
||||
@router.post(
|
||||
"/check_existing_username",
|
||||
"/check_existing_user",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=None,
|
||||
)
|
||||
async def check_existing_username(username: SUsername, uow=Depends(UnitOfWork)):
|
||||
async def check_existing_user(user_filter: SUserFilter, uow=Depends(UnitOfWork)):
|
||||
async with uow:
|
||||
user = await uow.user.find_one_or_none(username=username.username)
|
||||
if user:
|
||||
raise UserAlreadyExistsException
|
||||
|
||||
|
||||
@router.post(
|
||||
"/check_existing_email",
|
||||
status_code=status.HTTP_200_OK,
|
||||
response_model=None,
|
||||
)
|
||||
async def check_existing_email(email: SEmail, uow=Depends(UnitOfWork)):
|
||||
async with uow:
|
||||
user = await uow.user.find_one_or_none(email=email.email)
|
||||
user = await uow.user.find_one_or_none(**user_filter.model_dump(exclude_none=True))
|
||||
if user:
|
||||
raise UserAlreadyExistsException
|
||||
|
||||
|
@ -78,7 +65,6 @@ async def check_existing_email(email: SEmail, uow=Depends(UnitOfWork)):
|
|||
async def register_user(user_data: SUserRegister, uow=Depends(UnitOfWork)):
|
||||
if user_data.password != user_data.password2:
|
||||
raise PasswordsMismatchException
|
||||
await AuthService.check_existing_user(uow, user_data)
|
||||
|
||||
hashed_password = get_password_hash(user_data.password)
|
||||
async with uow:
|
||||
|
@ -90,14 +76,14 @@ async def register_user(user_data: SUserRegister, uow=Depends(UnitOfWork)):
|
|||
)
|
||||
await uow.commit()
|
||||
|
||||
result = send_registration_confirmation_email.delay(
|
||||
user_id=user_id, username=user_data.username, email_to=user_data.email, MODE=settings.MODE
|
||||
user_code = generate_confirmation_code()
|
||||
user_mail_data = SInvitationData.model_validate(
|
||||
{"user_id": user_id, "username": user_data.username, "email_to": user_data.email, "confirmation_code": user_code}
|
||||
)
|
||||
user_code = result.get()
|
||||
send_registration_confirmation_email.delay(user_mail_data)
|
||||
redis_session = get_redis_session()
|
||||
await RedisService.set_verification_code(redis=redis_session, user_id=user_id, verification_code=user_code)
|
||||
user = await AuthService.authenticate_user_by_email(uow, user_data.email, user_data.password)
|
||||
access_token = create_access_token({"sub": str(user.id)})
|
||||
access_token = create_access_token({"sub": str(user_id)})
|
||||
return {"authorization": f"Bearer {access_token}"}
|
||||
|
||||
|
||||
|
@ -107,17 +93,14 @@ async def register_user(user_data: SUserRegister, uow=Depends(UnitOfWork)):
|
|||
response_model=SEmailVerification,
|
||||
)
|
||||
async def email_verification(user_code: str, uow=Depends(UnitOfWork)):
|
||||
invitation_token = user_code.encode()
|
||||
cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY)
|
||||
user_data = cipher_suite.decrypt(invitation_token).decode("utf-8")
|
||||
user_data = json.loads(user_data)
|
||||
user_data = decode_invitation_token(user_code)
|
||||
redis_session = get_redis_session()
|
||||
async with uow:
|
||||
verification_code = await RedisService.get_verification_code(redis=redis_session, user_id=user_data["user_id"])
|
||||
if verification_code != user_data["confirmation_code"]:
|
||||
verification_code = await RedisService.get_verification_code(redis=redis_session, user_id=user_data.user_id)
|
||||
if verification_code != user_data.confirmation_code:
|
||||
raise WrongCodeException
|
||||
|
||||
await uow.user.change_data(user_id=user_data["user_id"], role=VERIFICATED_USER)
|
||||
await uow.user.change_data(user_id=user_data.user_id, role=VERIFICATED_USER)
|
||||
await uow.commit()
|
||||
return {"email_verification": True}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from datetime import date, timedelta, datetime, time
|
||||
from typing import Annotated
|
||||
|
||||
from pydantic_core import PydanticCustomError
|
||||
from pydantic import BaseModel, EmailStr, ConfigDict, field_validator, HttpUrl
|
||||
from pydantic import BaseModel, EmailStr, field_validator, HttpUrl, StringConstraints
|
||||
from fastapi import Query
|
||||
|
||||
|
||||
|
@ -17,25 +18,23 @@ class SUserRegister(BaseModel):
|
|||
password2: str = Query(None, min_length=8)
|
||||
date_of_birth: date
|
||||
|
||||
@field_validator("date_of_birth")
|
||||
@field_validator("date_of_birth") # noqa
|
||||
@classmethod
|
||||
def validate_date_of_birth(cls, input_date):
|
||||
if date.today() - input_date < timedelta(days=365 * 16):
|
||||
def validate_date_of_birth(cls, v):
|
||||
if date.today() - v < timedelta(days=365 * 16):
|
||||
date_of_birth = date.today() - timedelta(days=365 * 16)
|
||||
raise PydanticCustomError(
|
||||
"date_input_error", "date of birth might be earlier than {date_of_birth}", {"date_of_birth": date_of_birth}
|
||||
)
|
||||
elif datetime.combine(input_date, time.min).timestamp() < datetime(year=1924, month=1, day=1).timestamp():
|
||||
elif datetime.combine(v, time.min).timestamp() < datetime(year=1924, month=1, day=1).timestamp():
|
||||
date_of_birth = date(1924, 1, 1)
|
||||
raise PydanticCustomError(
|
||||
"date_input_error", "date of birth might be later than {date_of_birth}", {"date_of_birth": date_of_birth}
|
||||
)
|
||||
return input_date
|
||||
return v
|
||||
|
||||
|
||||
class SUserResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
email: EmailStr
|
||||
id: int
|
||||
username: str
|
||||
|
@ -46,6 +45,10 @@ class SUserResponse(BaseModel):
|
|||
date_of_registration: date
|
||||
|
||||
|
||||
class SUsers(BaseModel):
|
||||
users: list[SUserResponse]
|
||||
|
||||
|
||||
class SUser(BaseModel):
|
||||
id: int
|
||||
email: str
|
||||
|
@ -96,9 +99,13 @@ class SUserAvatars(BaseModel):
|
|||
user_avatars: list[SUserAvatarUrl] | None
|
||||
|
||||
|
||||
class SUsername(BaseModel):
|
||||
username: str = Query(None, min_length=2, max_length=30)
|
||||
class SUserFilter(BaseModel):
|
||||
username: Annotated[str, StringConstraints(min_length=2, max_length=30)] | None = None
|
||||
email: EmailStr | None = None
|
||||
|
||||
|
||||
class SEmail(BaseModel):
|
||||
email: EmailStr
|
||||
class SInvitationData(BaseModel):
|
||||
user_id: int
|
||||
username: str
|
||||
email_to: EmailStr
|
||||
confirmation_code: str
|
||||
|
|
Loading…
Add table
Reference in a new issue