from fastapi import Depends from faststream.rabbit.fastapi import RabbitRouter, RabbitMessage from src.infrastructure.config import settings from src.infrastructure.logger import logger from src.infrastructure.mail import TemplateRenderer, get_email_sender, get_renderer from src.infrastructure.mail.assets import EXA_LOGO_CID, get_exa_logo_png from src.infrastructure.mail.sender import EmailSender from datetime import datetime from typing import Literal, Optional from pydantic import BaseModel, EmailStr, Field from src.infrastructure.rabbit.broker import email_code_queue from src.infrastructure.metrics import observe_notify code_broker = RabbitRouter(settings.RABBIT_URL) class Metadata(BaseModel): trace_id: Optional[str] = None source: str timestamp: datetime message_id: str class Payload(BaseModel): email: EmailStr code: str = Field(min_length=1, max_length=64) ttl_seconds: int = Field(gt=0, lt=24 * 3600) class LoginCodeCreated(BaseModel): event: Literal['login', 'registration', 'bank_details_update', 'change_password', 'forgot_password'] payload: Payload metadata: Metadata @code_broker.subscriber(email_code_queue) async def consume_email_code( msg_body: LoginCodeCreated, message: RabbitMessage, sender: EmailSender = Depends(get_email_sender), renderer: TemplateRenderer = Depends(get_renderer), ): trace_id = ( (message.headers or {}).get("trace_id") or message.correlation_id or msg_body.metadata.trace_id ) logger.info( f"received event={msg_body.event} " f"email={msg_body.payload.email} " f"ttl={msg_body.payload.ttl_seconds} " f"trace_id={trace_id}" ) logo_png = get_exa_logo_png() logo_src = f'cid:{EXA_LOGO_CID}' if logo_png else '' html = renderer.render( 'email_code.html', subject='Экса — код подтверждения', code=msg_body.payload.code, ttl_minutes=msg_body.payload.ttl_seconds // 60, brand='Экса', logo_src=logo_src, trace_id=trace_id, year=datetime.now().year, ) text = renderer.render( 'email_code.txt', code=msg_body.payload.code, ttl_minutes=msg_body.payload.ttl_seconds // 60, brand='Экса', trace_id=trace_id, year=datetime.now().year, ) observe_notify('email', msg_body.event, 'received') try: await sender.send( to=msg_body.payload.email, subject='Экса — код подтверждения', body=html, plain=text, inline_png=(EXA_LOGO_CID, logo_png) if logo_png else None, ) except Exception: observe_notify('email', msg_body.event, 'error') raise observe_notify('email', msg_body.event, 'sent')