# app/api/v1/vendor/email_settings.py """ Vendor email settings API endpoints. Allows vendors to configure their email sending settings: - SMTP configuration (all tiers) - Advanced providers: SendGrid, Mailgun, SES (Business+ tier) - Sender identity (from_email, from_name, reply_to) - Signature/footer customization - Configuration verification via test email Vendor Context: Uses token_vendor_id from JWT token (authenticated vendor API pattern). """ import logging from fastapi import APIRouter, Depends from pydantic import BaseModel, EmailStr, Field from sqlalchemy.orm import Session from app.api.deps import get_current_vendor_api from app.core.database import get_db from app.services.vendor_email_settings_service import VendorEmailSettingsService from app.services.subscription_service import subscription_service from models.database.user import User router = APIRouter(prefix="/email-settings") logger = logging.getLogger(__name__) # ============================================================================= # SCHEMAS # ============================================================================= class EmailSettingsUpdate(BaseModel): """Schema for creating/updating email settings.""" # Sender Identity (Required) from_email: EmailStr = Field(..., description="Sender email address") from_name: str = Field(..., min_length=1, max_length=100, description="Sender name") reply_to_email: EmailStr | None = Field(None, description="Reply-to email address") # Signature (Optional) signature_text: str | None = Field(None, description="Plain text signature") signature_html: str | None = Field(None, description="HTML signature/footer") # Provider provider: str = Field("smtp", description="Email provider: smtp, sendgrid, mailgun, ses") # SMTP Settings smtp_host: str | None = Field(None, description="SMTP server hostname") smtp_port: int | None = Field(587, ge=1, le=65535, description="SMTP server port") smtp_username: str | None = Field(None, description="SMTP username") smtp_password: str | None = Field(None, description="SMTP password") smtp_use_tls: bool = Field(True, description="Use STARTTLS") smtp_use_ssl: bool = Field(False, description="Use SSL/TLS (port 465)") # SendGrid sendgrid_api_key: str | None = Field(None, description="SendGrid API key") # Mailgun mailgun_api_key: str | None = Field(None, description="Mailgun API key") mailgun_domain: str | None = Field(None, description="Mailgun sending domain") # SES ses_access_key_id: str | None = Field(None, description="AWS access key ID") ses_secret_access_key: str | None = Field(None, description="AWS secret access key") ses_region: str | None = Field("eu-west-1", description="AWS region") class VerifyEmailRequest(BaseModel): """Schema for verifying email settings.""" test_email: EmailStr = Field(..., description="Email address to send test email to") # Response models for API-001 compliance class EmailSettingsResponse(BaseModel): """Response for email settings.""" configured: bool verified: bool | None = None settings: dict | None = None message: str | None = None class EmailStatusResponse(BaseModel): """Response for email status check.""" is_configured: bool is_verified: bool class ProvidersResponse(BaseModel): """Response for available providers.""" providers: list[dict] current_tier: str | None class EmailUpdateResponse(BaseModel): """Response for email settings update.""" success: bool message: str settings: dict class EmailVerifyResponse(BaseModel): """Response for email verification.""" success: bool message: str class EmailDeleteResponse(BaseModel): """Response for email settings deletion.""" success: bool message: str # ============================================================================= # ENDPOINTS # ============================================================================= @router.get("", response_model=EmailSettingsResponse) def get_email_settings( current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ) -> EmailSettingsResponse: """ Get current email settings for the vendor. Returns settings with sensitive fields masked. """ vendor_id = current_user.token_vendor_id service = VendorEmailSettingsService(db) settings = service.get_settings(vendor_id) if not settings: return EmailSettingsResponse( configured=False, settings=None, message="Email settings not configured. Configure SMTP to send emails to customers.", ) return EmailSettingsResponse( configured=settings.is_configured, verified=settings.is_verified, settings=settings.to_dict(), ) @router.get("/status", response_model=EmailStatusResponse) def get_email_status( current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ) -> EmailStatusResponse: """ Get email configuration status. Used by frontend to show warning banner if not configured. """ vendor_id = current_user.token_vendor_id service = VendorEmailSettingsService(db) status = service.get_status(vendor_id) return EmailStatusResponse(**status) @router.get("/providers", response_model=ProvidersResponse) def get_available_providers( current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ) -> ProvidersResponse: """ Get available email providers for current tier. Returns list of providers with availability status. """ vendor_id = current_user.token_vendor_id service = VendorEmailSettingsService(db) # Get vendor's current tier tier = subscription_service.get_current_tier(db, vendor_id) return ProvidersResponse( providers=service.get_available_providers(tier), current_tier=tier.value if tier else None, ) @router.put("", response_model=EmailUpdateResponse) def update_email_settings( data: EmailSettingsUpdate, current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ) -> EmailUpdateResponse: """ Create or update email settings. Premium providers (SendGrid, Mailgun, SES) require Business+ tier. Raises AuthorizationException if tier is insufficient. Raises ValidationException if data is invalid. """ vendor_id = current_user.token_vendor_id service = VendorEmailSettingsService(db) # Get vendor's current tier for validation tier = subscription_service.get_current_tier(db, vendor_id) # Service raises appropriate exceptions (API-003 compliance) settings = service.create_or_update( vendor_id=vendor_id, data=data.model_dump(exclude_unset=True), current_tier=tier, ) db.commit() return EmailUpdateResponse( success=True, message="Email settings updated successfully", settings=settings.to_dict(), ) @router.post("/verify", response_model=EmailVerifyResponse) def verify_email_settings( data: VerifyEmailRequest, current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ) -> EmailVerifyResponse: """ Verify email settings by sending a test email. Sends a test email to the provided address and updates verification status. Raises ResourceNotFoundException if settings not configured. Raises ValidationException if verification fails. """ vendor_id = current_user.token_vendor_id service = VendorEmailSettingsService(db) # Service raises appropriate exceptions (API-003 compliance) result = service.verify_settings(vendor_id, data.test_email) db.commit() return EmailVerifyResponse( success=result["success"], message=result["message"], ) @router.delete("", response_model=EmailDeleteResponse) def delete_email_settings( current_user: User = Depends(get_current_vendor_api), db: Session = Depends(get_db), ) -> EmailDeleteResponse: """ Delete email settings. Warning: This will disable email sending for the vendor. Raises ResourceNotFoundException if settings not found. """ vendor_id = current_user.token_vendor_id service = VendorEmailSettingsService(db) # Service raises ResourceNotFoundException if not found (API-003 compliance) service.delete(vendor_id) db.commit() return EmailDeleteResponse( success=True, message="Email settings deleted", )