feat: add message reliability foundation
All checks were successful
CI / test (push) Successful in 23s

- implement idempotent message creation via client_message_id

- add persistent delivered/read receipts

- expose /messages/status and wire websocket receipt events

- update web client to send client ids and auto-ack delivered/read
This commit is contained in:
2026-03-07 23:57:35 +03:00
parent ff6f409c5a
commit f6ad480973
13 changed files with 382 additions and 28 deletions

View File

@@ -3,8 +3,9 @@ from sqlalchemy.ext.asyncio import AsyncSession
from app.auth.service import get_current_user
from app.database.session import get_db
from app.messages.schemas import MessageCreateRequest, MessageRead, MessageUpdateRequest
from app.messages.schemas import MessageCreateRequest, MessageRead, MessageStatusUpdateRequest, MessageUpdateRequest
from app.messages.service import create_chat_message, delete_message, get_messages, update_message
from app.realtime.schemas import MessageStatusPayload
from app.realtime.service import realtime_gateway
from app.users.models import User
@@ -18,7 +19,11 @@ async def create_message(
current_user: User = Depends(get_current_user),
) -> MessageRead:
message = await create_chat_message(db, sender_id=current_user.id, payload=payload)
await realtime_gateway.publish_message_created(message=message, sender_id=current_user.id)
await realtime_gateway.publish_message_created(
message=message,
sender_id=current_user.id,
client_message_id=payload.client_message_id,
)
return message
@@ -50,3 +55,17 @@ async def remove_message(
current_user: User = Depends(get_current_user),
) -> None:
await delete_message(db, message_id=message_id, user_id=current_user.id)
@router.post("/status")
async def update_status(
payload: MessageStatusUpdateRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
) -> dict[str, int]:
return await realtime_gateway.handle_message_status(
db,
user_id=current_user.id,
payload=MessageStatusPayload(chat_id=payload.chat_id, message_id=payload.message_id),
event=payload.status,
)