"""Email bot management API routes.""" import logging from fastapi import APIRouter, Depends, HTTPException, Query, status from pydantic import BaseModel from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession from ..auth.dependencies import get_current_user from ..database.engine import get_session from ..database.models import EmailBot, User from .helpers import get_owned_entity _LOGGER = logging.getLogger(__name__) router = APIRouter(prefix="/api/email-bots", tags=["email-bots"]) class EmailBotCreate(BaseModel): name: str icon: str = "" email: str smtp_host: str smtp_port: int = 587 smtp_username: str = "" smtp_password: str = "" smtp_use_tls: bool = True class EmailBotUpdate(BaseModel): name: str | None = None icon: str | None = None email: str | None = None smtp_host: str | None = None smtp_port: int | None = None smtp_username: str | None = None smtp_password: str | None = None smtp_use_tls: bool | None = None @router.get("") async def list_email_bots( user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): result = await session.exec( select(EmailBot).where(EmailBot.user_id == user.id) ) return [_response(b) for b in result.all()] @router.post("", status_code=status.HTTP_201_CREATED) async def create_email_bot( body: EmailBotCreate, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): # Verify SMTP connection before saving from notify_bridge_core.notifications.email.client import EmailClient, SmtpConfig client = EmailClient(SmtpConfig( host=body.smtp_host, port=body.smtp_port, username=body.smtp_username, password=body.smtp_password, from_address=body.email, from_name=body.name, use_tls=body.smtp_use_tls, )) result = await client.verify_connection() if not result.get("success"): raise HTTPException(status_code=400, detail=f"SMTP connection failed: {result.get('error', 'Unknown error')}") bot = EmailBot(user_id=user.id, **body.model_dump()) session.add(bot) await session.commit() await session.refresh(bot) return _response(bot) @router.get("/{bot_id}") async def get_email_bot( bot_id: int, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): bot = await _get_user_bot(session, bot_id, user.id) return _response(bot) @router.put("/{bot_id}") async def update_email_bot( bot_id: int, body: EmailBotUpdate, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): bot = await _get_user_bot(session, bot_id, user.id) updates = body.model_dump(exclude_unset=True) # Reject the masked value the GET response returns so the stored password # is preserved if the user saves without retyping it. if "smtp_password" in updates: pw = updates["smtp_password"] if isinstance(pw, str) and pw.startswith("***"): updates.pop("smtp_password") for field, value in updates.items(): setattr(bot, field, value) session.add(bot) await session.commit() await session.refresh(bot) return _response(bot) @router.delete("/{bot_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_email_bot( bot_id: int, user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): from .delete_protection import check_email_bot, raise_if_used bot = await _get_user_bot(session, bot_id, user.id) raise_if_used(await check_email_bot(session, bot.id), bot.name) await session.delete(bot) await session.commit() @router.post("/{bot_id}/test") async def test_email_bot( bot_id: int, locale: str = Query("en"), user: User = Depends(get_current_user), session: AsyncSession = Depends(get_session), ): """Send a test email to the bot's own address to verify SMTP connection.""" bot = await _get_user_bot(session, bot_id, user.id) from ..services.notifier import _get_test_message msg = _get_test_message(locale, "email") from notify_bridge_core.notifications.email.client import EmailClient, SmtpConfig client = EmailClient(SmtpConfig( host=bot.smtp_host, port=bot.smtp_port, username=bot.smtp_username, password=bot.smtp_password, from_address=bot.email, from_name=bot.name, use_tls=bot.smtp_use_tls, )) result = await client.send( to_email=bot.email, subject=f"Notify Bridge — {msg}", body_text=msg, ) return result def _response(bot: EmailBot) -> dict: return { "id": bot.id, "name": bot.name, "icon": bot.icon, "email": bot.email, "smtp_host": bot.smtp_host, "smtp_port": bot.smtp_port, "smtp_username": bot.smtp_username, "smtp_password": "***" if bot.smtp_password else "", "smtp_use_tls": bot.smtp_use_tls, "created_at": bot.created_at.isoformat(), } async def _get_user_bot(session: AsyncSession, bot_id: int, user_id: int) -> EmailBot: return await get_owned_entity( session, EmailBot, bot_id, user_id, not_found_msg="Email bot not found", )