"""Notification target management API routes.""" from fastapi import APIRouter, Depends, HTTPException, status from pydantic import BaseModel from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession from typing import Any from ..auth.dependencies import get_current_user from ..database.engine import get_session from ..database.models import NotificationTarget, User router = APIRouter(prefix="/api/targets", tags=["targets"]) class TargetCreate(BaseModel): type: str # "telegram" or "webhook" name: str icon: str = "" config: dict[str, Any] = {} template_config_id: int | None = None class TargetUpdate(BaseModel): name: str | None = None icon: str | None = None config: dict[str, Any] | None = None template_config_id: int | None = None @router.get("") async def list_targets( user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """List all notification targets for the current user.""" result = await session.exec( select(NotificationTarget).where(NotificationTarget.user_id == user.id) ) return [ { "id": t.id, "type": t.type, "name": t.name, "icon": t.icon, "config": _safe_config(t), "template_config_id": t.template_config_id, "created_at": t.created_at.isoformat(), } for t in result.all() ] @router.post("", status_code=status.HTTP_201_CREATED) async def create_target( body: TargetCreate, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Create a new notification target.""" if body.type not in ("telegram", "webhook"): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Type must be 'telegram' or 'webhook'", ) target = NotificationTarget( user_id=user.id, type=body.type, name=body.name, icon=body.icon, config=body.config, template_config_id=body.template_config_id, ) session.add(target) await session.commit() await session.refresh(target) return {"id": target.id, "type": target.type, "name": target.name} @router.get("/{target_id}") async def get_target( target_id: int, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Get a specific notification target.""" target = await _get_user_target(session, target_id, user.id) return { "id": target.id, "type": target.type, "name": target.name, "icon": target.icon, "config": _safe_config(target), "template_config_id": target.template_config_id, } @router.put("/{target_id}") async def update_target( target_id: int, body: TargetUpdate, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Update a notification target.""" target = await _get_user_target(session, target_id, user.id) if body.name is not None: target.name = body.name if body.icon is not None: target.icon = body.icon if body.config is not None: target.config = body.config if body.template_config_id is not None: target.template_config_id = body.template_config_id session.add(target) await session.commit() await session.refresh(target) return {"id": target.id, "type": target.type, "name": target.name} @router.delete("/{target_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_target( target_id: int, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Delete a notification target.""" target = await _get_user_target(session, target_id, user.id) await session.delete(target) await session.commit() @router.post("/{target_id}/test") async def test_target( target_id: int, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Send a test notification to a target.""" target = await _get_user_target(session, target_id, user.id) from ..services.notifier import send_test_notification result = await send_test_notification(target) return result def _safe_config(target: NotificationTarget) -> dict: """Return config with sensitive fields masked.""" config = dict(target.config) if "bot_token" in config: token = config["bot_token"] config["bot_token"] = f"{token[:8]}...{token[-4:]}" if len(token) > 12 else "***" if "api_key" in config: config["api_key"] = "***" return config async def _get_user_target( session: AsyncSession, target_id: int, user_id: int ) -> NotificationTarget: target = await session.get(NotificationTarget, target_id) if not target or target.user_id != user_id: raise HTTPException(status_code=404, detail="Target not found") return target