collabrix/backend/app/routers/last_seen.py

104 lines
3.9 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, status
from sqlmodel import Session, select
from datetime import datetime
from typing import Optional
from app.database import get_session
from app.auth import get_current_user
from app.models import User, Channel, LastSeen
from app.websocket import manager
router = APIRouter(prefix="/me/last-seen", tags=["LastSeen"])
@router.post("/")
async def set_last_seen(
channel_id: Optional[int] = None,
dm_user_id: Optional[int] = None,
last_seen: Optional[str] = None,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user),
):
if not channel_id and not dm_user_id:
raise HTTPException(status_code=400, detail="channel_id or dm_user_id required")
# Validate target exists
if channel_id:
channel = session.get(Channel, channel_id)
if not channel:
raise HTTPException(status_code=404, detail="Channel not found")
# Basic access check: user must belong to the channel's department
user_dept_ids = [d.id for d in current_user.departments] if current_user.departments else []
if channel.department_id not in user_dept_ids:
raise HTTPException(status_code=403, detail="No access to channel")
if dm_user_id:
other = session.get(User, dm_user_id)
if not other:
raise HTTPException(status_code=404, detail="Target user not found")
ts = datetime.utcnow() if not last_seen else datetime.fromisoformat(last_seen)
# Upsert last seen
if channel_id:
statement = select(LastSeen).where(LastSeen.user_id == current_user.id, LastSeen.channel_id == channel_id)
else:
statement = select(LastSeen).where(LastSeen.user_id == current_user.id, LastSeen.dm_user_id == dm_user_id)
exists = session.exec(statement).first()
if exists:
exists.last_seen = ts
session.add(exists)
session.commit()
session.refresh(exists)
result = exists
else:
payload = LastSeen(user_id=current_user.id, channel_id=channel_id, dm_user_id=dm_user_id, last_seen=ts)
session.add(payload)
session.commit()
session.refresh(payload)
result = payload
# Broadcast read_marker to relevant channel or DM partner
try:
message = {
"type": "read_marker",
"user_id": current_user.id,
"last_seen": result.last_seen.isoformat(),
}
if channel_id:
message["channel_id"] = channel_id
await manager.broadcast_to_channel(message, channel_id)
elif dm_user_id:
message["dm_user_id"] = dm_user_id
# partner listens on channel id = -their_user_id
await manager.broadcast_to_channel(message, -dm_user_id)
# also broadcast to presence channel 0
await manager.broadcast_to_channel({**message, "type": "read_marker"}, 0)
except Exception:
pass
return {"id": result.id, "user_id": result.user_id, "channel_id": result.channel_id, "dm_user_id": result.dm_user_id, "last_seen": result.last_seen.isoformat()}
@router.get("/")
def get_last_seen(
channel_id: Optional[int] = None,
dm_user_id: Optional[int] = None,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user),
):
if not channel_id and not dm_user_id:
raise HTTPException(status_code=400, detail="channel_id or dm_user_id required")
if channel_id:
stmt = select(LastSeen).where(LastSeen.user_id == current_user.id, LastSeen.channel_id == channel_id)
else:
stmt = select(LastSeen).where(LastSeen.user_id == current_user.id, LastSeen.dm_user_id == dm_user_id)
found = session.exec(stmt).first()
if not found:
raise HTTPException(status_code=404, detail="LastSeen not found")
return {"id": found.id, "user_id": found.user_id, "channel_id": found.channel_id, "dm_user_id": found.dm_user_id, "last_seen": found.last_seen.isoformat()}