from fastapi import APIRouter, Depends, Query from sqlalchemy import and_, or_, select from sqlalchemy.ext.asyncio import AsyncSession from ..cursors import decode, decode_datetime, encode from ..db import get_session from ..models import NotificationOutbox from ..schemas import NotificationOutboxItem, OutboxPage router = APIRouter() @router.get("/notifiers/outbox", response_model=OutboxPage) async def list_outbox( state: str | None = Query(default=None), cursor: str | None = Query(default=None, max_length=512), limit: int = Query(default=100, ge=1, le=500), session: AsyncSession = Depends(get_session), ) -> OutboxPage: stmt = select(NotificationOutbox).order_by( NotificationOutbox.created_at.desc(), NotificationOutbox.id.desc() ) if state is not None: stmt = stmt.where(NotificationOutbox.state == state) if cursor is not None: ts_str, last_id = decode(cursor, 2) ts = decode_datetime(ts_str) stmt = stmt.where( or_( NotificationOutbox.created_at < ts, and_( NotificationOutbox.created_at == ts, NotificationOutbox.id < last_id, ), ) ) rows = (await session.execute(stmt.limit(limit + 1))).scalars().all() next_cursor = None if len(rows) > limit: rows = rows[:limit] next_cursor = encode(rows[-1].created_at.isoformat(), rows[-1].id) return OutboxPage( items=[ NotificationOutboxItem( id=str(r.id), notifier=r.notifier, state=r.state, incident_id=str(r.incident_id), event_type=r.event_type, attempts=r.attempts, next_attempt_at=r.next_attempt_at, last_error=r.last_error, updated_at=r.updated_at, ) for r in rows ], next_cursor=next_cursor, )