diff --git a/.env_template b/.env_template index 73d9eda..e90a93a 100644 --- a/.env_template +++ b/.env_template @@ -26,4 +26,6 @@ SMTP_PASS= IMAGE_UPLOAD_SERVER= INVITATION_LINK_HOST= -INVITATION_LINK_TOKEN_KEY= \ No newline at end of file +INVITATION_LINK_TOKEN_KEY= + +SENTRY_DSN= \ No newline at end of file diff --git a/app/config.py b/app/config.py index 8eb1bac..8693abe 100644 --- a/app/config.py +++ b/app/config.py @@ -37,5 +37,7 @@ class Settings(BaseSettings): INVITATION_LINK_HOST: str INVITATION_LINK_TOKEN_KEY: bytes + SENTRY_DSN: str + settings = Settings() diff --git a/app/exceptions.py b/app/exceptions.py index 3e0fb93..8d29bc7 100644 --- a/app/exceptions.py +++ b/app/exceptions.py @@ -77,3 +77,8 @@ class WrongCodeException(BlackPhoenixException): class UserNotFoundException(BlackPhoenixException): status_code = status.HTTP_404_NOT_FOUND detail = 'Юзер не найден' + + +class UserAlreadyInChatException(BlackPhoenixException): + status_code = status.HTTP_409_CONFLICT + detail = "Юзер уже добавлен в чат" diff --git a/app/main.py b/app/main.py index bd07968..0233a78 100644 --- a/app/main.py +++ b/app/main.py @@ -2,17 +2,25 @@ from fastapi import FastAPI from sqladmin import Admin from starlette.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware +import sentry_sdk from app.admin.auth import authentication_backend from app.admin.views import UsersAdmin, ChatsAdmin, MessagesAdmin, UsersXChatsAdmin +from app.config import settings from app.database import engine from app.users.auth import check_verificated_user -from app.users.chat.router import router as chat_router from app.users.router import router as user_router from app.pages.router import router as pages_router from app.users.chat.websocket import router as websocket_router from app.images.router import router as image_router + +sentry_sdk.init( + dsn=settings.SENTRY_DSN, + traces_sample_rate=1.0, + profiles_sample_rate=1.0, +) + app = FastAPI( title="Чат BP", root_path="/api" diff --git a/app/users/chat/dao.py b/app/users/chat/dao.py index e0c1134..436b07b 100644 --- a/app/users/chat/dao.py +++ b/app/users/chat/dao.py @@ -2,6 +2,7 @@ from sqlalchemy import insert, select, update, and_ from app.dao.base import BaseDAO from app.database import async_session_maker +from app.exceptions import UserAlreadyInChatException from app.users.models import Users from app.users.chat.models import Chats, Messages, UsersXChats @@ -9,8 +10,8 @@ from app.users.chat.models import Chats, Messages, UsersXChats class ChatDAO(BaseDAO): model = Chats - @classmethod - async def create(cls, user_id: int, chat_name: str, created_by: int) -> int: + @staticmethod + async def create(user_id: int, chat_name: str, created_by: int) -> int: query = insert(Chats).values(chat_for=user_id, chat_name=chat_name, created_by=created_by).returning(Chats.id) async with async_session_maker() as session: result = await session.execute(query) @@ -18,16 +19,21 @@ class ChatDAO(BaseDAO): result = result.scalar() return result - @classmethod - async def add_user_to_chat(cls, user_id: int, chat_id: int): - query = insert(UsersXChats).values(user_id=user_id, chat_id=chat_id) + @staticmethod + async def add_user_to_chat(user_id: int, chat_id: int): + query = select(UsersXChats.user_id).where(UsersXChats.chat_id == chat_id) async with async_session_maker() as session: + result = await session.execute(query) + result = [res['user_id'] for res in result.mappings().all()] + if user_id in result: + raise UserAlreadyInChatException + query = insert(UsersXChats).values(user_id=user_id, chat_id=chat_id) await session.execute(query) await session.commit() return True - @classmethod - async def send_message(cls, user_id, chat_id: int, message: str, image_url: str | None = None): + @staticmethod + async def send_message(user_id, chat_id: int, message: str, image_url: str | None = None): query = (insert(Messages).values(chat_id=chat_id, user_id=user_id, message=message, image_url=image_url) .returning(Messages.id)) async with async_session_maker() as session: @@ -35,8 +41,8 @@ class ChatDAO(BaseDAO): await session.commit() return result.scalar() - @classmethod - async def get_message_by_id(cls, message_id: int): + @staticmethod + async def get_message_by_id(message_id: int): query = (select(Messages.message, Messages.image_url, Messages.chat_id, Messages.user_id, Messages.created_at, Users.avatar_image, Users.username).select_from(Messages) .join(Users, Users.id == Messages.user_id) @@ -52,20 +58,16 @@ class ChatDAO(BaseDAO): if result: return result[0] - @classmethod - async def delete_message(cls, message_id: int): + @staticmethod + async def delete_message(message_id: int): query = update(Messages).where(Messages.id == message_id).values(visibility=False) async with async_session_maker() as session: await session.execute(query) await session.commit() return True - @classmethod - async def delete_chat(cls, chat_id: int, user_id: int): - pass - - @classmethod - async def get_some_messages(cls, chat_id: int, message_number_from: int, messages_to_get: int): + @staticmethod + async def get_some_messages(chat_id: int, message_number_from: int, messages_to_get: int): """ WITH messages_with_users AS ( SELECT * @@ -101,3 +103,7 @@ class ChatDAO(BaseDAO): if result: result = [dict(res) for res in result] return result + + @staticmethod + async def delete_chat(chat_id: int, user_id: int): + pass diff --git a/app/users/chat/router.py b/app/users/chat/router.py index f0567f1..2ca8ade 100644 --- a/app/users/chat/router.py +++ b/app/users/chat/router.py @@ -107,7 +107,7 @@ async def create_invitation_link(chat_id: int, user: Users = Depends(get_current @router.get("/invite_to_chat/{invitation_token}") async def invite_to_chat(invitation_token: str, user: Users = Depends(get_current_user)): - invitation_token = bytes(invitation_token, 'utf-8') + invitation_token = invitation_token.encode() cipher_suite = Fernet(settings.INVITATION_LINK_TOKEN_KEY) chat_id = int(cipher_suite.decrypt(invitation_token)) chat = await ChatDAO.find_one_or_none(id=chat_id) @@ -115,3 +115,7 @@ async def invite_to_chat(invitation_token: str, user: Users = Depends(get_curren raise UserCanNotReadThisChatException return await ChatDAO.add_user_to_chat(chat_id=chat_id, user_id=user.id) + +@router.get("/delete_user_from_chat/{chat_id}") +async def delete_user_from_chat(chat_id: int, user_id: int, user: Users = Depends(get_current_user)): + pass diff --git a/app/users/dao.py b/app/users/dao.py index 30ae392..02a1c3d 100644 --- a/app/users/dao.py +++ b/app/users/dao.py @@ -12,8 +12,8 @@ from app.users.models import Users, UsersVerificationCodes class UserDAO(BaseDAO): model = Users - @classmethod - async def change_data(cls, user_id: int, **data_to_change): + @staticmethod + async def change_data(user_id: int, **data_to_change): query = update(Users).where(Users.id == user_id).values(**data_to_change) async with async_session_maker() as session: await session.execute(query) @@ -22,15 +22,15 @@ class UserDAO(BaseDAO): result = await session.execute(query) return result.scalar() - @classmethod - async def get_user_role(cls, user_id: int): + @staticmethod + async def get_user_role(user_id: int): query = select(Users.role).where(Users.id == user_id) async with async_session_maker() as session: result = await session.execute(query) return result.scalar() - @classmethod - async def get_user_allowed_chats(cls, user_id: int): + @staticmethod + async def get_user_allowed_chats(user_id: int): """ WITH chats_with_descriptions AS ( SELECT * @@ -81,8 +81,8 @@ class UserDAO(BaseDAO): result = result.mappings().all() return result - @classmethod - async def get_user_avatar(cls, user_id: int): + @staticmethod + async def get_user_avatar(user_id: int): query = select(Users.avatar_image).where(Users.id == user_id) async with async_session_maker() as session: result = await session.execute(query) @@ -102,8 +102,8 @@ class UserCodesDAO(BaseDAO): await session.commit() return result.scalar() - @classmethod - async def get_user_codes(cls, **filter_by) -> list[dict | None]: + @staticmethod + async def get_user_codes(**filter_by) -> list[dict | None]: """ SELECT usersverificationcodes.id,