Add edit message

This commit is contained in:
2026-01-24 13:20:45 +03:00
parent a690116399
commit 8c0c063bac
17 changed files with 1237 additions and 112 deletions
+2 -2
View File
@@ -77,7 +77,7 @@ class MessageDAO(BaseDAO[MessageModel, MessageCreateDB, MessageUpdateDB]):
model = MessageModel
@classmethod
async def find_all_asc(
async def find_all_desc(
cls,
session: AsyncSession,
offset: Optional[int],
@@ -85,7 +85,7 @@ class MessageDAO(BaseDAO[MessageModel, MessageCreateDB, MessageUpdateDB]):
*filter,
**filter_by
) -> List[MessageModel]:
stmt = select(MessageModel).filter(*filter).filter_by(**filter_by).order_by(MessageModel.created_at.asc())
stmt = select(MessageModel).filter(*filter).filter_by(**filter_by).order_by(MessageModel.created_at.desc())
if offset is not None:
stmt = stmt.offset(offset)
+2 -1
View File
@@ -1,7 +1,7 @@
import uuid
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import ForeignKey, UUID, UniqueConstraint
from sqlalchemy import ForeignKey, UUID, UniqueConstraint, text
from app.core.database import Base
@@ -14,6 +14,7 @@ class MessageModel(Base):
chat_id: Mapped[uuid.UUID] = mapped_column(UUID, ForeignKey("chat.id", ondelete="CASCADE"), index=True)
content: Mapped[str] = mapped_column()
is_read: Mapped[bool] = mapped_column(default=False)
is_edited: Mapped[bool] = mapped_column(default=False, server_default=text("false"))
class ChatModel(Base):
+6 -2
View File
@@ -6,7 +6,7 @@ from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect
from app.chats.service import ChatService
from app.auth.dependencies import get_current_verified_user
from app.users.models import UserModel
from app.chats.schemas import Chat, MessageCreate, Message
from app.chats.schemas import Chat, MessageCreate, Message, MessageUpdate
router = APIRouter(prefix="/chats", tags=["chats"])
@@ -19,7 +19,7 @@ async def get_chats(
return await ChatService.get_chats(user, offset, limit)
@router.get("/{chat_id}")
async def get_chat(
async def get_messages(
chat_id: uuid.UUID,
offset: int = 0,
limit: int = 10,
@@ -31,6 +31,10 @@ async def get_chat(
async def send_message(message: MessageCreate, user: UserModel = Depends(get_current_verified_user)) -> Message:
return await ChatService.send_message(user, message)
@router.put("/message")
async def edit_message(message_update: MessageUpdate, user: UserModel = Depends(get_current_verified_user)) -> Message:
return await ChatService.update_message(user, message_update)
@router.websocket("/ws")
async def websocket_endpoint(ws: WebSocket, user: UserModel = Depends(get_current_verified_user)):
+7 -1
View File
@@ -2,7 +2,7 @@ from datetime import datetime
from typing import Optional
import uuid
from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict
class MessageCreate(BaseModel):
@@ -21,10 +21,13 @@ class MessageCreateDB(BaseModel):
chat_id: Optional[uuid.UUID]
content: Optional[str]
is_read: Optional[bool] = False
is_edited: Optional[bool] = False
class MessageUpdateDB(BaseModel):
content: Optional[str]
is_edited: Optional[bool] = False
class Message(BaseModel):
@@ -32,9 +35,12 @@ class Message(BaseModel):
sender_id: int
chat_id: uuid.UUID
content: str
is_edited: Optional[bool] = False
created_at: datetime
updated_at: datetime
model_config = ConfigDict(from_attributes=True)
class ChatBase(BaseModel):
is_group: Optional[bool] = False
+45 -11
View File
@@ -9,7 +9,7 @@ from sqlalchemy import and_
from app.core.database import async_session_maker
from app.chats.dao import ChatDAO, MessageDAO, ParticipantDAO
from app.chats.models import ChatModel, MessageModel, ParticipantModel
from app.chats.schemas import Chat, MessageCreate, MessageCreateDB, ChatCreateDB, ParticipantCreateDB, Message
from app.chats.schemas import Chat, MessageCreate, MessageCreateDB, ChatCreateDB, ParticipantCreateDB, Message, MessageUpdateDB, MessageUpdate
from app.users.models import UserModel
from app.core.redis import get_redis
@@ -92,14 +92,7 @@ class ChatService:
)
)
await cls._send_ws_message(members_ids, Message(
id=message_db.id,
sender_id=message_db.sender_id,
chat_id=message_db.chat_id,
content=message_db.content,
created_at=message_db.created_at,
updated_at=message_db.updated_at
))
await cls._send_ws_message(members_ids, Message.model_validate(message_db))
await ChatDAO.update(
session,
@@ -124,7 +117,7 @@ class ChatService:
log.warning("Access denied to chat", extra={"user_id": user.id, "chat_id": chat_id})
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Access denied")
messages = await MessageDAO.find_all_asc(
messages = await MessageDAO.find_all_desc(
session,
offset,
limit,
@@ -180,4 +173,45 @@ class ChatService:
"message": message.model_dump(mode='json')
}
await redis_client.publish("messenger_updates", json.dumps(payload))
log.debug(f"Published message for user_id: {user_id}")
log.debug(f"Published message for user_id: {user_id}")
@classmethod
async def update_message(cls, user: UserModel, message_update: MessageUpdate) -> Message:
async with async_session_maker() as session:
message_exist = await MessageDAO.find_one_or_none(
session,
and_(
MessageModel.id==message_update.id,
MessageModel.sender_id==user.id
)
)
if message_exist is None:
log.warning("Message not found", extra={"user_id": user.id, "message_id": message_update.id})
raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Message not found")
message_update_db = await MessageDAO.update(
session,
MessageModel.id==message_update.id,
obj_in=MessageUpdateDB(
content=message_update.content,
is_edited=True
)
)
members = await ParticipantDAO.find_all(
session,
None,
None,
ParticipantModel.chat_id==message_exist.chat_id
)
member_ids = [member.user_id for member in members]
await cls._send_ws_message(member_ids, Message.model_validate(message_update_db))
await session.commit()
log.info("Message update successfully", extra={"user_id": user.id, "message_id": message_update.id})
return message_update_db
@@ -0,0 +1,32 @@
"""Edit message table: adding is_edited column
Revision ID: 76159faa56c8
Revises: 0d3f7039ba77
Create Date: 2026-01-20 17:22:23.965106
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = '76159faa56c8'
down_revision: Union[str, Sequence[str], None] = '0d3f7039ba77'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('message', sa.Column('is_edited', sa.Boolean(), nullable=False, server_default=sa.text("false")))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('message', 'is_edited')
# ### end Alembic commands ###