from typing import Annotated, Any import asyncpg from dishka.integrations.fastapi import DishkaRoute, FromDishka from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel from utils.read import watches from utils.read.models import DEFAULT_LIMIT, AlertView, Page, WatchView router = APIRouter(prefix="/api", tags=["watches"], route_class=DishkaRoute) class WatchCreate(BaseModel): account_id: int kind: str params: dict[str, Any] = {} enabled: bool = True class WatchUpdate(BaseModel): params: dict[str, Any] = {} enabled: bool = True @router.get("/watches") async def list_watches( pool: FromDishka[asyncpg.Pool], account_id: Annotated[int, Query()] ) -> list[WatchView]: return await watches.list_watches(pool, account_id) @router.post("/watches", status_code=201) async def create_watch(pool: FromDishka[asyncpg.Pool], body: WatchCreate) -> WatchView: return await watches.create_watch( pool, body.account_id, body.kind, body.params, enabled=body.enabled ) @router.get("/watches/{watch_id}") async def get_watch(pool: FromDishka[asyncpg.Pool], watch_id: int) -> WatchView: watch = await watches.get_watch(pool, watch_id) if watch is None: raise HTTPException(status_code=404, detail="watch not found") return watch @router.put("/watches/{watch_id}") async def update_watch( pool: FromDishka[asyncpg.Pool], watch_id: int, body: WatchUpdate ) -> WatchView: watch = await watches.update_watch( pool, watch_id, body.params, enabled=body.enabled ) if watch is None: raise HTTPException(status_code=404, detail="watch not found") return watch @router.delete("/watches/{watch_id}", status_code=204) async def delete_watch(pool: FromDishka[asyncpg.Pool], watch_id: int) -> None: if not await watches.delete_watch(pool, watch_id): raise HTTPException(status_code=404, detail="watch not found") @router.get("/alerts") async def list_alerts( pool: FromDishka[asyncpg.Pool], account_id: Annotated[int, Query()], seen: Annotated[bool | None, Query()] = None, limit: Annotated[int, Query()] = DEFAULT_LIMIT, offset: Annotated[int, Query()] = 0, ) -> list[AlertView]: return await watches.list_alerts( pool, account_id, Page(limit=limit, offset=offset), seen=seen ) @router.post("/alerts/{alert_id}/seen", status_code=204) async def mark_alert_seen(pool: FromDishka[asyncpg.Pool], alert_id: int) -> None: if not await watches.mark_alert_seen(pool, alert_id): raise HTTPException(status_code=404, detail="alert not found")