Spaces:
Sleeping
Sleeping
| from fastapi import APIRouter, Depends, HTTPException, Query, Path | |
| from sqlalchemy.orm import Session | |
| from motor.motor_asyncio import AsyncIOMotorClient | |
| from typing import Union, Optional | |
| import datetime | |
| from app.db.session import get_db, get_mongo_db | |
| from app.security.dependencies import get_current_user_from_cookie | |
| from app.models import User, Admin, Group, GroupMember | |
| from app.schemas.message import MessageHistory | |
| router = APIRouter() | |
| async def get_message_history( | |
| conversation_type: str = Path(..., description="Type of conversation: 'private' or 'group'"), | |
| partner_id: int = Path(..., description="ID of the user, admin, or group"), | |
| partner_role: Optional[str] = Query(None, description="Role of the partner if private: 'user' or 'admin'"), | |
| before: Optional[str] = Query(None, description="ISO timestamp cursor for pagination"), | |
| limit: int = Query(50, gt=0, le=100), | |
| current_entity: Union[User, Admin] = Depends(get_current_user_from_cookie), | |
| db: Session = Depends(get_db), | |
| mongo_db: AsyncIOMotorClient = Depends(get_mongo_db) | |
| ): | |
| """ | |
| Fetch the message history for a specific conversation with pagination. | |
| """ | |
| messages_collection = mongo_db["messages"] | |
| entity_id = current_entity.id | |
| entity_role = "admin" if isinstance(current_entity, Admin) else "user" | |
| query = {"type": conversation_type} | |
| # --- Authorization and Query Building --- | |
| if conversation_type == "private": | |
| if not partner_role: | |
| raise HTTPException(status_code=400, detail="Partner role is required for private chats.") | |
| # Build a query that finds messages between the two parties, regardless of who is sender/receiver | |
| query["$or"] = [ | |
| {"sender.id": entity_id, "sender.role": entity_role, "receiver.id": partner_id, "receiver.role": partner_role}, | |
| {"sender.id": partner_id, "sender.role": partner_role, "receiver.id": entity_id, "receiver.role": entity_role} | |
| ] | |
| elif conversation_type == "group": | |
| # Verify the current entity is a member of the group | |
| group = db.query(Group).filter(Group.id == partner_id).first() | |
| if not group: raise HTTPException(status_code=404, detail="Group not found.") | |
| is_member = False | |
| if isinstance(current_entity, Admin) and current_entity.id == group.admin_id: | |
| is_member = True | |
| elif isinstance(current_entity, User): | |
| membership = db.query(GroupMember).filter(GroupMember.group_id == partner_id, GroupMember.user_id == entity_id).first() | |
| if membership: is_member = True | |
| if not is_member: | |
| raise HTTPException(status_code=403, detail="You are not a member of this group.") | |
| query["group.id"] = partner_id | |
| else: | |
| raise HTTPException(status_code=400, detail="Invalid conversation type.") | |
| # --- Pagination --- | |
| if before: | |
| try: | |
| # Using timestamp for cursor-based pagination | |
| cursor_time = datetime.datetime.fromisoformat(before.replace("Z", "+00:00")) | |
| query["timestamp"] = {"$lt": cursor_time} | |
| except ValueError: | |
| raise HTTPException(status_code=400, detail="Invalid 'before' timestamp format.") | |
| # --- Fetching Messages --- | |
| messages_cursor = messages_collection.find(query).sort("timestamp", -1).limit(limit) | |
| messages = await messages_cursor.to_list(length=limit) | |
| # Convert MongoDB's _id to a string for Pydantic | |
| for msg in messages: | |
| msg["_id"] = str(msg["_id"]) | |
| # --- Determine the next cursor --- | |
| next_cursor = None | |
| if len(messages) == limit: | |
| # The timestamp of the last message fetched is the cursor for the next page | |
| next_cursor = messages[-1]['timestamp'].isoformat() + "Z" | |
| return {"messages": messages, "next_cursor": next_cursor} | |