from fastapi import Depends, APIRouter, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, delete from app.db import models from app.core.security import get_current_user from app.api import schemas from fastapi.encoders import jsonable_encoder from typing import Optional, List from datetime import datetime, timezone from pydantic import BaseModel async def get_db(): async with models.AsyncSessionLocal() as db: try: yield db finally: await db.close() messagesRouter = APIRouter( prefix="/messages", tags=[], ) @messagesRouter.get("/history/{contact_id}") async def get_chat_history( contact_id: int, current_user: models.User = Depends(get_current_user), db: AsyncSession = Depends(get_db), limit: int = 50, offset: int = 0, v2_pagination: bool = False, anchor_id: int = None, limit_before: int = 20, limit_after: int = 20 ): chat_filter = ( ((models.Message.sender_id == current_user.id) & (models.Message.receiver_id == contact_id)) | ((models.Message.sender_id == contact_id) & (models.Message.receiver_id == current_user.id)) ) if not v2_pagination: res = await db.execute( select(models.Message) .where(chat_filter) .order_by(models.Message.id.desc()) .offset(offset) .limit(limit) ) messages = res.scalars().all() print(f"DEBUG history (Legacy): user={current_user.id}, contact={contact_id}, count={len(messages)}") return jsonable_encoder(messages) if anchor_id is None: unread_res = await db.execute( select(models.Message) .where(chat_filter, models.Message.sender_id == contact_id, models.Message.read_at == None) .order_by(models.Message.id.asc()) ) first_unread = unread_res.scalars().first() if first_unread: anchor_id = first_unread.id print(f"DEBUG server-anchor: Found unread message ID {anchor_id}") else: res = await db.execute( select(models.Message) .where(chat_filter) .order_by(models.Message.id.desc()) .limit(limit_before) ) messages = res.scalars().all() return { "anchor_id": None, "messages": jsonable_encoder(messages) } older_res = await db.execute( select(models.Message) .where(chat_filter, models.Message.id < anchor_id) .order_by(models.Message.id.desc()) .limit(limit_before) ) older_messages = older_res.scalars().all() anchor_res = await db.execute( select(models.Message) .where(chat_filter, models.Message.id == anchor_id) ) anchor_message = anchor_res.scalars().first() newer_res = await db.execute( select(models.Message) .where(chat_filter, models.Message.id > anchor_id) .order_by(models.Message.id.asc()) .limit(limit_after) ) newer_messages = newer_res.scalars().all() combined_messages = older_messages + ([anchor_message] if anchor_message else []) + newer_messages combined_messages.sort(key=lambda msg: msg.id, reverse=True) return { "anchor_id": anchor_id, "messages": jsonable_encoder(combined_messages) } @messagesRouter.get("/last") async def get_last_messages( contact_id: int, current_user: models.User = Depends(get_current_user), db: AsyncSession = Depends(get_db), limit: int = 2 ): res = await db.execute( select(models.Message) .where( ((models.Message.sender_id == current_user.id) & (models.Message.receiver_id == contact_id)) | ((models.Message.sender_id == contact_id) & (models.Message.receiver_id == current_user.id)) ) .order_by(models.Message.timestamp.desc()) .limit(limit) ) messages = res.scalars().all() return jsonable_encoder(messages) @messagesRouter.delete("/all") async def delete_all_messages( current_user: models.User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): # 1. Находим всех пользователей, с которыми у current_user были диалоги res = await db.execute( select(models.Message.sender_id, models.Message.receiver_id) .where( (models.Message.sender_id == current_user.id) | (models.Message.receiver_id == current_user.id) ) ) rows = res.all() contact_ids = set() for row in rows: if row[0] != current_user.id: contact_ids.add(row[0]) if row[1] != current_user.id: contact_ids.add(row[1]) # 2. Удаляем все сообщения await db.execute( delete(models.Message) .where( (models.Message.sender_id == current_user.id) | (models.Message.receiver_id == current_user.id) ) ) await db.commit() # 3. Оповещаем остальных через WebSocket from app.websocket.connection_manager import manager for contact_id in contact_ids: await manager.send_personal_message({ "type": "chat_deleted", "contact_id": current_user.id }, str(contact_id)) return {"status": "ok", "detail": "Все сообщения удалены"}