Добавил эндпоинты для работы с чатом
This commit is contained in:
parent
7342415ad0
commit
3124b35f67
10 changed files with 202 additions and 12 deletions
|
@ -54,3 +54,13 @@ class IncorrectLengthOfNicknameException(BlackPhoenixException):
|
|||
detail = "Ник должен быть не короче 2 и не длиннее 30 символов"
|
||||
|
||||
|
||||
class UDontHavePermissionException(BlackPhoenixException):
|
||||
status_code = status.HTTP_409_CONFLICT
|
||||
detail = "У вас нет прав для этого действия"
|
||||
|
||||
|
||||
class MessageNotFoundException(BlackPhoenixException):
|
||||
status_code = status.HTTP_404_NOT_FOUND
|
||||
detail = "Сообщение не найдено"
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ from logging.config import fileConfig
|
|||
from sqlalchemy import engine_from_config, pool
|
||||
from alembic import context
|
||||
|
||||
sys.path.insert(0, dirname(dirname(abspath(__file__))))
|
||||
|
||||
from app.database import DATABASE_URL, Base
|
||||
from app.users.models import Users # noqa
|
||||
from app.users.chat.models import Chats, Messages, UsersXChats # noqa
|
||||
|
@ -12,8 +14,6 @@ from app.users.chat.models import Chats, Messages, UsersXChats # noqa
|
|||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
|
||||
sys.path.insert(0, dirname(dirname(abspath(__file__))))
|
||||
|
||||
config = context.config
|
||||
|
||||
config.set_main_option('sqlalchemy.url', f'{DATABASE_URL}?async_fallback=True')
|
||||
|
@ -29,6 +29,7 @@ if config.config_file_name is not None:
|
|||
# target_metadata = mymodel.Base.metadata
|
||||
target_metadata = Base.metadata
|
||||
|
||||
|
||||
# other values from the config, defined by the needs of env.py,
|
||||
# can be acquired:
|
||||
# my_important_option = config.get_main_option("my_important_option")
|
||||
|
|
32
app/migrations/versions/37cf151ccb02_изменение_models_py.py
Normal file
32
app/migrations/versions/37cf151ccb02_изменение_models_py.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
"""Изменение models.py
|
||||
|
||||
Revision ID: 37cf151ccb02
|
||||
Revises: 5d84c98e0f22
|
||||
Create Date: 2024-01-31 20:52:41.569988
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '37cf151ccb02'
|
||||
down_revision: Union[str, None] = '5d84c98e0f22'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('messages', sa.Column('message', sa.String(), nullable=False))
|
||||
op.drop_column('messages', 'text')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('messages', sa.Column('text', sa.VARCHAR(), autoincrement=False, nullable=False))
|
||||
op.drop_column('messages', 'message')
|
||||
# ### end Alembic commands ###
|
32
app/migrations/versions/5d84c98e0f22_изменение_models_py.py
Normal file
32
app/migrations/versions/5d84c98e0f22_изменение_models_py.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
"""Изменение models.py
|
||||
|
||||
Revision ID: 5d84c98e0f22
|
||||
Revises: ad488d81e7b5
|
||||
Create Date: 2024-01-31 20:35:24.317899
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '5d84c98e0f22'
|
||||
down_revision: Union[str, None] = 'ad488d81e7b5'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('chats', sa.Column('chat_for', sa.Integer(), nullable=True))
|
||||
op.create_foreign_key(None, 'chats', 'users', ['chat_for'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'chats', type_='foreignkey')
|
||||
op.drop_column('chats', 'chat_for')
|
||||
# ### end Alembic commands ###
|
|
@ -1,7 +1,63 @@
|
|||
from sqlalchemy import insert, select, update, and_
|
||||
|
||||
from app.dao.base import BaseDAO
|
||||
from app.database import async_session_maker
|
||||
from app.users.models import Users
|
||||
from app.users.chat.models import Chats
|
||||
from app.users.chat.models import Chats, Messages
|
||||
|
||||
|
||||
class ChatDAO(BaseDAO):
|
||||
model = Chats
|
||||
|
||||
@classmethod
|
||||
async def create(cls, user_id):
|
||||
query = insert(Chats).values(chat_for=user_id)
|
||||
async with async_session_maker() as session:
|
||||
await session.execute(query)
|
||||
await session.commit()
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def send_message(cls, user_id, chat_id, message, image_url):
|
||||
query = insert(Messages).values(chat_id=chat_id, user_id=user_id, message=message, image_url=image_url)
|
||||
async with async_session_maker() as session:
|
||||
await session.execute(query)
|
||||
await session.commit()
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def get_message_by_id(cls, message_id):
|
||||
query = select(Messages.__table__.columns).where(
|
||||
and_(
|
||||
Messages.id == message_id,
|
||||
Messages.visibility == True
|
||||
)
|
||||
|
||||
)
|
||||
async with async_session_maker() as session:
|
||||
result = await session.execute(query)
|
||||
result = result.mappings().all()
|
||||
if result:
|
||||
return result[0]
|
||||
|
||||
@classmethod
|
||||
async def get_last_message(cls, chat_id):
|
||||
query = select(Messages.__table__.columns).where(
|
||||
and_(
|
||||
Messages.chat_id == chat_id,
|
||||
Messages.visibility == True
|
||||
)
|
||||
).order_by(Messages.created_at.desc()).limit(1)
|
||||
async with async_session_maker() as session:
|
||||
result = await session.execute(query)
|
||||
result = result.mappings().all()
|
||||
if result:
|
||||
return result[0]
|
||||
|
||||
@classmethod
|
||||
async def delete_message(cls, message_id):
|
||||
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
|
||||
|
|
|
@ -11,9 +11,11 @@ class Chats(Base):
|
|||
__tablename__ = "chats"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
chat_for = mapped_column(ForeignKey("users.id"))
|
||||
|
||||
message = relationship("Messages", back_populates="chat")
|
||||
usersxchats = relationship("UsersXChats", back_populates="chat")
|
||||
user_to_exclude = relationship("Users", back_populates="chat")
|
||||
|
||||
def __str__(self):
|
||||
return f"Чат #{self.id}."
|
||||
|
@ -25,7 +27,7 @@ class Messages(Base):
|
|||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
chat_id = mapped_column(ForeignKey("chats.id"))
|
||||
user_id = mapped_column(ForeignKey("users.id"))
|
||||
text: Mapped[str]
|
||||
message: Mapped[str]
|
||||
image_url: Mapped[Optional[str]]
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
visibility: Mapped[bool] = mapped_column(server_default='true')
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
from fastapi import APIRouter, WebSocket
|
||||
from fastapi import APIRouter, WebSocket, Depends
|
||||
from starlette.websockets import WebSocketDisconnect
|
||||
|
||||
from app.exceptions import UDontHavePermissionException, MessageNotFoundException
|
||||
from app.users.chat.dao import ChatDAO
|
||||
from app.users.chat.websocket import manager
|
||||
from app.users.dao import UserDAO
|
||||
from app.users.dependencies import get_current_user
|
||||
from app.users.models import Users
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/chat",
|
||||
|
@ -9,18 +14,63 @@ router = APIRouter(
|
|||
)
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def root():
|
||||
pass
|
||||
@router.post("")
|
||||
async def add_message_to_chat(
|
||||
chat_id: int,
|
||||
message: str,
|
||||
image_url: str = None,
|
||||
user: Users = Depends(get_current_user)
|
||||
):
|
||||
send_message_to_chat = await ChatDAO.send_message(
|
||||
user_id=user.id,
|
||||
chat_id=chat_id,
|
||||
message=message,
|
||||
image_url=image_url
|
||||
)
|
||||
return send_message_to_chat
|
||||
|
||||
|
||||
@router.websocket("/ws/{user_id}")
|
||||
async def websocket_endpoint(websocket: WebSocket, user_id: int):
|
||||
@router.delete("/delete_message")
|
||||
async def delete_message_from_chat(
|
||||
message_id: int,
|
||||
user: Users = Depends(get_current_user)
|
||||
):
|
||||
get_message_sender = await ChatDAO.get_message_by_id(message_id=message_id)
|
||||
if get_message_sender is None:
|
||||
raise MessageNotFoundException
|
||||
if get_message_sender["user_id"] != user.id:
|
||||
get_user_role = await UserDAO.get_user_role(user_id=user.id)
|
||||
if not get_user_role == 1:
|
||||
raise UDontHavePermissionException
|
||||
deleted_message = await ChatDAO.delete_message(message_id=message_id)
|
||||
return deleted_message
|
||||
|
||||
|
||||
@router.get("/get_last_message")
|
||||
async def get_last_message(chat_id: int, user: Users = Depends(get_current_user)):
|
||||
message = await ChatDAO.get_last_message(chat_id=chat_id)
|
||||
if message is not None:
|
||||
return message
|
||||
raise MessageNotFoundException
|
||||
|
||||
|
||||
@router.post("/create_chat")
|
||||
async def create_chat(
|
||||
user_to_exclude: int,
|
||||
user: Users = Depends(get_current_user)
|
||||
):
|
||||
created_chat = await ChatDAO.create(user_id=user_to_exclude)
|
||||
return created_chat
|
||||
|
||||
|
||||
@router.websocket("/ws")
|
||||
async def websocket_endpoint(websocket: WebSocket, user: Users = Depends(get_current_user)):
|
||||
await manager.connect(websocket)
|
||||
try:
|
||||
while True:
|
||||
data = await websocket.receive_text()
|
||||
await manager.broadcast(f"User {user_id}: {data}")
|
||||
|
||||
await manager.broadcast(f"User {user.id}: {data}")
|
||||
except WebSocketDisconnect:
|
||||
manager.disconnect(websocket)
|
||||
await manager.broadcast(f"User {user_id}: себался")
|
||||
await manager.broadcast(f"User {user.id}: себался")
|
||||
|
|
0
app/users/chat/shemas.py
Normal file
0
app/users/chat/shemas.py
Normal file
|
@ -19,3 +19,9 @@ class UserDAO(BaseDAO):
|
|||
result = await session.execute(query)
|
||||
return result.scalar()
|
||||
|
||||
@classmethod
|
||||
async def get_user_role(cls, 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()
|
||||
|
|
|
@ -20,6 +20,7 @@ class Users(Base):
|
|||
|
||||
message = relationship("Messages", back_populates="user")
|
||||
usersxchats = relationship("UsersXChats", back_populates="user")
|
||||
chat = relationship("Chats", back_populates="user_to_exclude")
|
||||
|
||||
def __str__(self):
|
||||
return f"Юзер {self.username}"
|
||||
|
|
Loading…
Add table
Reference in a new issue