# app/modules/messaging/routes/api/admin_email_templates.py """ Admin email template management endpoints. Allows platform administrators to: - View all email templates - Edit template content for all languages - Preview templates with sample data - Send test emails - View email logs """ import logging from typing import Any from fastapi import APIRouter, Depends from pydantic import BaseModel, EmailStr, Field from sqlalchemy.orm import Session from app.api.deps import get_current_admin_api from app.core.database import get_db from app.modules.messaging.services.email_service import EmailService from app.modules.messaging.services.email_template_service import EmailTemplateService from models.schema.auth import UserContext admin_email_templates_router = APIRouter(prefix="/email-templates") logger = logging.getLogger(__name__) # ============================================================================= # SCHEMAS # ============================================================================= class TemplateUpdate(BaseModel): """Schema for updating a platform template.""" subject: str = Field(..., min_length=1, max_length=500) body_html: str = Field(..., min_length=1) body_text: str | None = None class PreviewRequest(BaseModel): """Schema for previewing a template.""" template_code: str language: str = "en" variables: dict[str, Any] = {} class TestEmailRequest(BaseModel): """Schema for sending a test email.""" template_code: str language: str = "en" to_email: EmailStr variables: dict[str, Any] = {} class TemplateListItem(BaseModel): """Schema for a template in the list.""" code: str name: str description: str | None = None category: str languages: list[str] # Matches service output field name is_platform_only: bool = False variables: list[str] = [] class Config: from_attributes = True class TemplateListResponse(BaseModel): """Response schema for listing templates.""" templates: list[TemplateListItem] class CategoriesResponse(BaseModel): """Response schema for template categories.""" categories: list[str] # ============================================================================= # ENDPOINTS # ============================================================================= @admin_email_templates_router.get("", response_model=TemplateListResponse) def list_templates( current_user: UserContext = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """ List all platform email templates. Returns templates grouped by code with available languages. """ service = EmailTemplateService(db) return TemplateListResponse(templates=service.list_platform_templates()) @admin_email_templates_router.get("/categories", response_model=CategoriesResponse) def get_categories( current_user: UserContext = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """Get list of email template categories.""" service = EmailTemplateService(db) return CategoriesResponse(categories=service.get_template_categories()) @admin_email_templates_router.get("/{code}") def get_template( code: str, current_user: UserContext = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """ Get a specific template with all language versions. Returns template metadata and content for all available languages. """ service = EmailTemplateService(db) return service.get_platform_template(code) @admin_email_templates_router.get("/{code}/{language}") def get_template_language( code: str, language: str, current_user: UserContext = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """ Get a specific template for a specific language. Returns template content with variables information. """ service = EmailTemplateService(db) template = service.get_platform_template_language(code, language) return { "code": template.code, "language": template.language, "name": template.name, "description": template.description, "category": template.category, "subject": template.subject, "body_html": template.body_html, "body_text": template.body_text, "variables": template.variables, "required_variables": template.required_variables, "is_platform_only": template.is_platform_only, } @admin_email_templates_router.put("/{code}/{language}") def update_template( code: str, language: str, template_data: TemplateUpdate, current_user: UserContext = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """ Update a platform email template. Updates the template content for a specific language. """ service = EmailTemplateService(db) service.update_platform_template( code=code, language=language, subject=template_data.subject, body_html=template_data.body_html, body_text=template_data.body_text, ) db.commit() return {"message": "Template updated successfully"} @admin_email_templates_router.post("/{code}/preview") def preview_template( code: str, preview_data: PreviewRequest, current_user: UserContext = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """ Preview a template with sample variables. Renders the template with provided variables and returns the result. """ service = EmailTemplateService(db) # Merge with sample variables if not provided variables = { **_get_sample_variables(code), **preview_data.variables, } return service.preview_template(code, preview_data.language, variables) @admin_email_templates_router.post("/{code}/test") def send_test_email( code: str, test_data: TestEmailRequest, current_user: UserContext = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """ Send a test email using the template. Sends the template to the specified email address with sample data. """ # Merge with sample variables variables = { **_get_sample_variables(code), **test_data.variables, } try: email_svc = EmailService(db) email_log = email_svc.send_template( template_code=code, to_email=test_data.to_email, variables=variables, language=test_data.language, ) if email_log.status == "sent": return { "success": True, "message": f"Test email sent to {test_data.to_email}", } return { "success": False, "message": email_log.error_message or "Failed to send email", } except Exception as e: logger.exception(f"Failed to send test email: {e}") return { "success": False, "message": str(e), } @admin_email_templates_router.get("/{code}/logs") def get_template_logs( code: str, limit: int = 50, offset: int = 0, current_user: UserContext = Depends(get_current_admin_api), db: Session = Depends(get_db), ): """ Get email logs for a specific template. Returns recent email send attempts for the template. """ service = EmailTemplateService(db) logs, total = service.get_template_logs(code, limit, offset) return { "logs": logs, "total": total, "limit": limit, "offset": offset, } # ============================================================================= # HELPERS # ============================================================================= def _get_sample_variables(template_code: str) -> dict[str, Any]: """Get sample variables for testing templates.""" samples = { "signup_welcome": { "first_name": "John", "merchant_name": "Acme Corp", "email": "john@example.com", "store_code": "acme", "login_url": "https://example.com/login", "trial_days": "14", "tier_name": "Business", "platform_name": "Orion", }, "order_confirmation": { "customer_name": "Jane Doe", "order_number": "ORD-12345", "order_total": "€99.99", "order_items_count": "3", "order_date": "2024-01-15", "shipping_address": "123 Main St, Luxembourg City, L-1234", "platform_name": "Orion", }, "password_reset": { "customer_name": "John Doe", "reset_link": "https://example.com/reset?token=abc123", "expiry_hours": "1", "platform_name": "Orion", }, "team_invite": { "invitee_name": "Jane", "inviter_name": "John", "store_name": "Acme Corp", "role": "Admin", "accept_url": "https://example.com/accept", "expires_in_days": "7", "platform_name": "Orion", }, "subscription_welcome": { "store_name": "Acme Corp", "tier_name": "Business", "billing_cycle": "Monthly", "amount": "€49.99", "next_billing_date": "2024-02-15", "dashboard_url": "https://example.com/dashboard", "platform_name": "Orion", }, "payment_failed": { "store_name": "Acme Corp", "tier_name": "Business", "amount": "€49.99", "retry_date": "2024-01-18", "update_payment_url": "https://example.com/billing", "support_email": "support@orion.lu", "platform_name": "Orion", }, "subscription_cancelled": { "store_name": "Acme Corp", "tier_name": "Business", "end_date": "2024-02-15", "reactivate_url": "https://example.com/billing", "platform_name": "Orion", }, "trial_ending": { "store_name": "Acme Corp", "tier_name": "Business", "days_remaining": "3", "trial_end_date": "2024-01-18", "upgrade_url": "https://example.com/upgrade", "features_list": "Unlimited products, API access, Priority support", "platform_name": "Orion", }, } return samples.get(template_code, {"platform_name": "Orion"})