""" In-app varslingssenter (FEATURE_BACKLOG.md "Varsler"-runden, 2026-07-25). Eid av BRUKER (mottaker), ikke organisasjon -- plain_connection(), samme mønster som personlig profil/HCP-historikk/venner. `create_notification()` er den eneste skrivevegen inn -- kalt fra andre routere (i dag kun friends.py) ved konkrete hendelser, ikke noe generisk event-system. """ from __future__ import annotations from typing import Literal from fastapi import APIRouter, Depends from pydantic import BaseModel from ..auth import CurrentUser, get_current_user from ..db import plain_connection router = APIRouter(tags=["notifications"]) NotificationType = Literal["friend", "tournament", "round", "result"] async def create_notification(conn, *, user_id: str, type: NotificationType, message: str, link_path: str) -> None: """Kalles fra andre routere sin egen `plain_connection()`/transaksjon -- tar en allerede-åpen `conn`, åpner ikke en egen.""" await conn.execute( "INSERT INTO notification (user_id, type, message, link_path) VALUES ($1, $2, $3, $4)", user_id, type, message, link_path, ) class NotificationOut(BaseModel): id: str type: str message: str link_path: str created_at: str read_at: str | None @router.get("/notifications", response_model=list[NotificationOut]) async def list_notifications(user: CurrentUser = Depends(get_current_user)) -> list[NotificationOut]: async with plain_connection() as conn: rows = await conn.fetch( """ SELECT id::text AS id, type, message, link_path, created_at, read_at FROM notification WHERE user_id = $1 ORDER BY created_at DESC LIMIT 100 """, user.user_id, ) return [ NotificationOut( id=r["id"], type=r["type"], message=r["message"], link_path=r["link_path"], created_at=r["created_at"].isoformat(), read_at=r["read_at"].isoformat() if r["read_at"] else None, ) for r in rows ] @router.get("/notifications/unread-count") async def unread_count(user: CurrentUser = Depends(get_current_user)) -> dict[str, int]: async with plain_connection() as conn: count = await conn.fetchval( "SELECT COUNT(*) FROM notification WHERE user_id = $1 AND read_at IS NULL", user.user_id, ) return {"count": count} @router.post("/notifications/{notification_id}/read") async def mark_read(notification_id: str, user: CurrentUser = Depends(get_current_user)) -> dict[str, bool]: async with plain_connection() as conn: await conn.execute( "UPDATE notification SET read_at = now() WHERE id = $1 AND user_id = $2 AND read_at IS NULL", notification_id, user.user_id, ) return {"ok": True} @router.post("/notifications/read-all") async def mark_all_read(user: CurrentUser = Depends(get_current_user)) -> dict[str, bool]: async with plain_connection() as conn: await conn.execute( "UPDATE notification SET read_at = now() WHERE user_id = $1 AND read_at IS NULL", user.user_id, ) return {"ok": True}