Files
web/backend/routes/messaging.py
T

131 lines
4.4 KiB
Python

from datetime import datetime
from email.policy import HTTP
from fastapi import APIRouter, Depends, HTTPException, WebSocket, WebSocketDisconnect
from sqlalchemy.orm import Session
from dependencies import get_current_user, get_db
from models import Message, SendMessageRequest
router = APIRouter()
def convert_message(msg: Message, current_user: dict) -> dict:
return {
"id": msg.id,
"content": msg.content,
"timestamp": msg.timestamp.isoformat(),
"is_author": msg.user_id == current_user["user_id"],
"is_read": msg.is_read,
"username": msg.author.username
}
async def get_messages_inner(current_user: dict, db: Session):
messages = db.query(Message).order_by(Message.timestamp.asc()).all()
messages_data = []
for msg in messages:
messages_data.append(convert_message(msg, current_user))
return {
"status": "success",
"messages": messages_data
}
async def send_message_inner(request: SendMessageRequest, current_user: dict, db: Session):
if not request.content.strip():
raise HTTPException(
status_code=400,
detail="No content provided"
)
new_message = Message(
content=request.content.strip(),
user_id=current_user["user_id"],
timestamp=datetime.now()
)
db.add(new_message)
db.commit()
db.refresh(new_message)
return {"status": "success", "message": convert_message(new_message, current_user)}
@router.post("/send_message")
async def send_message(
request: SendMessageRequest,
current_user: dict = Depends(get_current_user),
db: Session = Depends(get_db)
):
return await send_message_inner(request, current_user, db)
@router.get("/get_messages")
async def get_messages(
current_user: dict = Depends(get_current_user),
db: Session = Depends(get_db)
):
return await get_messages_inner(current_user, db)
class MessaggingSocketManager:
def __init__(self) -> None:
self.connections: list[WebSocket] = []
def get_dependencies(self):
return get_current_user(), next(get_db())
async def send_error(self, websocket: WebSocket, type: str, e: HTTPException):
await websocket.send_json({"type": type, "error": {"code": e.status_code, "detail": e.detail}})
async def handle_connection(self, websocket: WebSocket):
while True:
data = await websocket.receive_json()
if data.type == "ping":
await websocket.send_json({"type": "ping", "data": {"status": "success"}})
elif data.type == "getMessages":
try:
current_user, db = self.get_dependencies()
await websocket.send_json({"type": data.type, "data": await get_messages_inner(current_user, db)})
except HTTPException as e:
await self.send_error(websocket, data.type, e)
elif data.type == "sendMessage":
try:
current_user, db = self.get_dependencies()
request: SendMessageRequest = SendMessageRequest.model_validate(data.data)
response = await send_message_inner(request, current_user, db)
await self.broadcast({
"type": "newMessage",
"data": response.message
})
await websocket.send_json({"type": data.type, "data": response})
except HTTPException as e:
await self.send_error(websocket, data.type, e)
else:
await websocket.send_json({"type": data.type, "error": {"code": 400, "detail": "Invalid type"}})
async def disconnect(self, websocket: WebSocket, code: int = 1000, message: str | None = None):
try:
await websocket.close(code=code, reason=message)
finally:
self.connections.remove(websocket)
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.connections.append(websocket)
try:
await self.handle_connection(websocket)
finally:
self.connections.remove(websocket)
async def broadcast(self, message: dict):
for websocket in self.connections:
await websocket.send_json(message)
messagingManager = MessaggingSocketManager()
@router.websocket("/chat/ws")
async def messaging(websocket: WebSocket):
await messagingManager.connect(websocket)