collabrix/backend/app/routers/direct_messages.py
DGSoft 93b98cfb5c Initial commit: Team Chat System with Code Snippet Library
- Complete chat application similar to Microsoft Teams
- Code snippet library with syntax highlighting
- Real-time messaging with WebSockets
- File upload with Office integration
- Department-based permissions
- Dark/Light theme support
- Production deployment with SSL/Reverse Proxy
- Docker containerization
- PostgreSQL database with SQLModel ORM
2025-12-09 22:25:03 +01:00

189 lines
6.7 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status
from sqlmodel import Session, select, or_, and_
from typing import List
from app.database import get_session
from app.models import DirectMessage, User
from app.schemas import DirectMessageCreate, DirectMessageResponse
from app.auth import get_current_user
from app.websocket import manager
router = APIRouter(prefix="/direct-messages", tags=["Direct Messages"])
@router.post("/", response_model=DirectMessageResponse, status_code=status.HTTP_201_CREATED)
async def create_direct_message(
message_data: DirectMessageCreate,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
"""Create a new direct message"""
# Check if receiver exists
receiver = session.get(User, message_data.receiver_id)
if not receiver:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Receiver not found"
)
# Can't send message to yourself
if message_data.receiver_id == current_user.id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Cannot send message to yourself"
)
new_message = DirectMessage(
content=message_data.content,
sender_id=current_user.id,
receiver_id=message_data.receiver_id,
snippet_id=message_data.snippet_id
)
session.add(new_message)
session.commit()
session.refresh(new_message)
# Build response
response = DirectMessageResponse.model_validate(new_message)
response.sender_username = current_user.username
response.receiver_username = receiver.username
response.sender_full_name = current_user.full_name
response.sender_profile_picture = current_user.profile_picture
# Broadcast via WebSocket to receiver (using negative user ID as "channel")
response_data = {
"id": new_message.id,
"content": new_message.content,
"sender_id": new_message.sender_id,
"receiver_id": new_message.receiver_id,
"sender_username": current_user.username,
"receiver_username": receiver.username,
"sender_full_name": current_user.full_name,
"sender_profile_picture": current_user.profile_picture,
"created_at": new_message.created_at.isoformat(),
"is_read": new_message.is_read,
"snippet_id": new_message.snippet_id,
"snippet": None
}
# Broadcast to both sender and receiver using their user IDs as "channel"
await manager.broadcast_to_channel(
{"type": "direct_message", "message": response_data},
-message_data.receiver_id # Negative to distinguish from channel IDs
)
await manager.broadcast_to_channel(
{"type": "direct_message", "message": response_data},
-current_user.id
)
return response
@router.get("/conversation/{user_id}", response_model=List[DirectMessageResponse])
def get_conversation(
user_id: int,
limit: int = 50,
offset: int = 0,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
"""Get direct messages between current user and another user"""
# Check if other user exists
other_user = session.get(User, user_id)
if not other_user:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
# Get messages where current_user is sender and user_id is receiver OR vice versa
statement = (
select(DirectMessage)
.where(
or_(
and_(DirectMessage.sender_id == current_user.id, DirectMessage.receiver_id == user_id),
and_(DirectMessage.sender_id == user_id, DirectMessage.receiver_id == current_user.id)
)
)
.order_by(DirectMessage.created_at.desc())
.offset(offset)
.limit(limit)
)
messages = session.exec(statement).all()
# Mark messages as read if they were sent to current user
for msg in messages:
if msg.receiver_id == current_user.id and not msg.is_read:
msg.is_read = True
session.add(msg)
session.commit()
# Build responses
responses = []
for msg in messages:
msg_response = DirectMessageResponse.model_validate(msg)
sender = session.get(User, msg.sender_id)
receiver = session.get(User, msg.receiver_id)
msg_response.sender_username = sender.username if sender else "Unknown"
msg_response.receiver_username = receiver.username if receiver else "Unknown"
msg_response.sender_full_name = sender.full_name if sender else None
msg_response.sender_profile_picture = sender.profile_picture if sender else None
responses.append(msg_response)
# Reverse to show oldest first
return list(reversed(responses))
@router.get("/conversations", response_model=List[dict])
def get_conversations(
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
"""Get list of users with whom current user has conversations"""
# Get all direct messages involving current user
statement = select(DirectMessage).where(
or_(
DirectMessage.sender_id == current_user.id,
DirectMessage.receiver_id == current_user.id
)
)
messages = session.exec(statement).all()
# Extract unique user IDs
user_ids = set()
for msg in messages:
if msg.sender_id != current_user.id:
user_ids.add(msg.sender_id)
if msg.receiver_id != current_user.id:
user_ids.add(msg.receiver_id)
# Get user details
conversations = []
for user_id in user_ids:
user = session.get(User, user_id)
if user:
# Get last message with this user
last_msg_stmt = (
select(DirectMessage)
.where(
or_(
and_(DirectMessage.sender_id == current_user.id, DirectMessage.receiver_id == user_id),
and_(DirectMessage.sender_id == user_id, DirectMessage.receiver_id == current_user.id)
)
)
.order_by(DirectMessage.created_at.desc())
.limit(1)
)
last_msg = session.exec(last_msg_stmt).first()
conversations.append({
"user_id": user.id,
"username": user.username,
"full_name": user.full_name,
"email": user.email,
"last_message": last_msg.content if last_msg else None,
"last_message_at": last_msg.created_at.isoformat() if last_msg else None
})
return conversations