# ================================================================ # services/notification/app/api/public_contact.py # ================================================================ """ Public contact form API endpoints Handles contact, feedback, and prelaunch email submissions These endpoints are public (no authentication required) """ import structlog from fastapi import APIRouter, HTTPException, Request from pydantic import BaseModel, EmailStr, Field from typing import Optional, Literal from datetime import datetime from app.services.email_service import EmailService from app.core.config import settings logger = structlog.get_logger() router = APIRouter(prefix="/public", tags=["public-contact"]) # ================================================================ # PYDANTIC MODELS # ================================================================ class ContactFormRequest(BaseModel): """Contact form submission request""" name: str = Field(..., min_length=2, max_length=100) email: EmailStr phone: Optional[str] = Field(None, max_length=20) bakery_name: Optional[str] = Field(None, max_length=100) type: Literal["general", "technical", "sales", "feedback"] = "general" subject: str = Field(..., min_length=5, max_length=200) message: str = Field(..., min_length=10, max_length=5000) class FeedbackFormRequest(BaseModel): """Feedback form submission request""" name: str = Field(..., min_length=2, max_length=100) email: EmailStr category: Literal["suggestion", "bug", "feature", "praise", "complaint"] title: str = Field(..., min_length=5, max_length=200) description: str = Field(..., min_length=10, max_length=5000) rating: Optional[int] = Field(None, ge=1, le=5) class PrelaunchEmailRequest(BaseModel): """Prelaunch email subscription request""" email: EmailStr class ContactFormResponse(BaseModel): """Response for form submissions""" success: bool message: str # ================================================================ # EMAIL TEMPLATES # ================================================================ CONTACT_EMAIL_TEMPLATE_TEXT = """ Nuevo mensaje de contacto recibido ============================================== DATOS DEL CONTACTO ============================================== Nombre: {name} Email: {email} Teléfono: {phone} Panadería: {bakery_name} Tipo de consulta: {type_label} ============================================== ASUNTO ============================================== {subject} ============================================== MENSAJE ============================================== {message} ============================================== Recibido: {timestamp} """ CONTACT_EMAIL_TEMPLATE_HTML = """

Nuevo Mensaje de Contacto

Nombre: {name}
Email: {email}
Teléfono: {phone}
Panadería: {bakery_name}
Tipo: {type_label}

{subject}

{message}

""" FEEDBACK_EMAIL_TEMPLATE_TEXT = """ Nuevo feedback recibido ============================================== DATOS DEL USUARIO ============================================== Nombre: {name} Email: {email} Categoría: {category_label} Valoración: {rating_display} ============================================== TÍTULO ============================================== {title} ============================================== DESCRIPCIÓN ============================================== {description} ============================================== Recibido: {timestamp} """ FEEDBACK_EMAIL_TEMPLATE_HTML = """

Nuevo Feedback Recibido

Nombre: {name}
Email: {email}
Categoría: {category_label}
Valoración: {rating_display}

{title}

{description}

""" PRELAUNCH_EMAIL_TEMPLATE_TEXT = """ Nueva suscripción de pre-lanzamiento ============================================== NUEVO INTERESADO ============================================== Email: {email} El usuario ha mostrado interés en BakeWise y quiere ser notificado cuando se lance oficialmente. ============================================== Recibido: {timestamp} """ PRELAUNCH_EMAIL_TEMPLATE_HTML = """

Nueva Suscripción Pre-Lanzamiento

🚀

Un nuevo usuario quiere ser notificado del lanzamiento de BakeWise

Añade este email a la lista de espera para el lanzamiento oficial.

""" # ================================================================ # HELPER FUNCTIONS # ================================================================ def get_type_label(type_code: str) -> str: """Get human-readable label for contact type""" labels = { "general": "Consulta General", "technical": "Soporte Técnico", "sales": "Ventas", "feedback": "Feedback" } return labels.get(type_code, type_code) def get_category_label(category: str) -> str: """Get human-readable label for feedback category""" labels = { "suggestion": "Sugerencia", "bug": "Error/Bug", "feature": "Nueva Funcionalidad", "praise": "Elogio", "complaint": "Queja" } return labels.get(category, category) def get_rating_display(rating: Optional[int]) -> str: """Convert rating to star display""" if rating is None: return "N/A" return "★" * rating + "☆" * (5 - rating) # ================================================================ # API ENDPOINTS # ================================================================ @router.post("/contact", response_model=ContactFormResponse) async def submit_contact_form(request: ContactFormRequest): """ Submit a contact form message. Sends an email to contact@bakewise.ai with the form data. """ try: email_service = EmailService() timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S") type_label = get_type_label(request.type) # Format email content text_content = CONTACT_EMAIL_TEMPLATE_TEXT.format( name=request.name, email=request.email, phone=request.phone or "No proporcionado", bakery_name=request.bakery_name or "No proporcionado", type=request.type, type_label=type_label, subject=request.subject, message=request.message, timestamp=timestamp ) html_content = CONTACT_EMAIL_TEMPLATE_HTML.format( name=request.name, email=request.email, phone=request.phone or "No proporcionado", bakery_name=request.bakery_name or "No proporcionado", type=request.type, type_label=type_label, subject=request.subject, message=request.message, timestamp=timestamp ) # Send email to contact@bakewise.ai success = await email_service.send_email( to_email="contact@bakewise.ai", subject=f"[{type_label}] {request.subject}", text_content=text_content, html_content=html_content, reply_to=request.email ) if success: logger.info("Contact form submitted successfully", from_email=request.email, type=request.type, subject=request.subject) return ContactFormResponse( success=True, message="Mensaje enviado correctamente. Te responderemos pronto." ) else: logger.error("Failed to send contact form email", from_email=request.email) raise HTTPException( status_code=500, detail="Error al enviar el mensaje. Por favor, inténtalo de nuevo." ) except HTTPException: raise except Exception as e: logger.error("Contact form submission error", error=str(e)) raise HTTPException( status_code=500, detail="Error interno. Por favor, inténtalo más tarde." ) @router.post("/feedback", response_model=ContactFormResponse) async def submit_feedback_form(request: FeedbackFormRequest): """ Submit a feedback form. Sends an email to contact@bakewise.ai with the feedback data. """ try: email_service = EmailService() timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S") category_label = get_category_label(request.category) rating_display = get_rating_display(request.rating) # Format email content text_content = FEEDBACK_EMAIL_TEMPLATE_TEXT.format( name=request.name, email=request.email, category=request.category, category_label=category_label, rating_display=rating_display, title=request.title, description=request.description, timestamp=timestamp ) html_content = FEEDBACK_EMAIL_TEMPLATE_HTML.format( name=request.name, email=request.email, category=request.category, category_label=category_label, rating_display=rating_display, title=request.title, description=request.description, timestamp=timestamp ) # Send email to contact@bakewise.ai success = await email_service.send_email( to_email="contact@bakewise.ai", subject=f"[Feedback - {category_label}] {request.title}", text_content=text_content, html_content=html_content, reply_to=request.email ) if success: logger.info("Feedback form submitted successfully", from_email=request.email, category=request.category, title=request.title) return ContactFormResponse( success=True, message="Gracias por tu feedback. Lo revisaremos pronto." ) else: logger.error("Failed to send feedback form email", from_email=request.email) raise HTTPException( status_code=500, detail="Error al enviar el feedback. Por favor, inténtalo de nuevo." ) except HTTPException: raise except Exception as e: logger.error("Feedback form submission error", error=str(e)) raise HTTPException( status_code=500, detail="Error interno. Por favor, inténtalo más tarde." ) @router.post("/prelaunch-subscribe", response_model=ContactFormResponse) async def submit_prelaunch_email(request: PrelaunchEmailRequest): """ Submit a prelaunch email subscription. Sends a notification email to contact@bakewise.ai. """ try: email_service = EmailService() timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S") # Format email content text_content = PRELAUNCH_EMAIL_TEMPLATE_TEXT.format( email=request.email, timestamp=timestamp ) html_content = PRELAUNCH_EMAIL_TEMPLATE_HTML.format( email=request.email, timestamp=timestamp ) # Send email to contact@bakewise.ai success = await email_service.send_email( to_email="contact@bakewise.ai", subject=f"[Pre-Lanzamiento] Nueva suscripción: {request.email}", text_content=text_content, html_content=html_content, reply_to=request.email ) if success: logger.info("Prelaunch subscription submitted successfully", email=request.email) return ContactFormResponse( success=True, message="Te hemos apuntado a la lista de espera." ) else: logger.error("Failed to send prelaunch subscription email", email=request.email) raise HTTPException( status_code=500, detail="Error al registrar tu email. Por favor, inténtalo de nuevo." ) except HTTPException: raise except Exception as e: logger.error("Prelaunch subscription error", error=str(e)) raise HTTPException( status_code=500, detail="Error interno. Por favor, inténtalo más tarde." )