Platform Email Settings (Admin): - Add GET/PUT/DELETE /admin/settings/email/* endpoints - Settings stored in admin_settings table override .env values - Support all providers: SMTP, SendGrid, Mailgun, Amazon SES - Edit mode UI with provider-specific configuration forms - Reset to .env defaults functionality - Test email to verify configuration Vendor Email Settings: - Add VendorEmailSettings model with one-to-one vendor relationship - Migration: v0a1b2c3d4e5_add_vendor_email_settings.py - Service: vendor_email_settings_service.py with tier validation - API endpoints: /vendor/email-settings/* (CRUD, status, verify) - Email tab in vendor settings page with full configuration - Warning banner until email is configured (like billing warnings) - Premium providers (SendGrid, Mailgun, SES) tier-gated to Business+ Email Service Updates: - get_platform_email_config(db) checks DB first, then .env - Configurable provider classes accept config dict - EmailService uses database-aware providers - Vendor emails use vendor's own SMTP (Wizamart doesn't pay) - "Powered by Wizamart" footer for Essential/Professional tiers - White-label (no footer) for Business/Enterprise tiers Other: - Add scripts/install.py for first-time platform setup - Add make install target - Update init-prod to include email template seeding 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
226 lines
7.2 KiB
Python
226 lines
7.2 KiB
Python
# 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, HTTPException
|
|
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.exceptions import NotFoundError, ValidationError, AuthorizationError
|
|
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")
|
|
|
|
|
|
# =============================================================================
|
|
# ENDPOINTS
|
|
# =============================================================================
|
|
|
|
|
|
@router.get("")
|
|
def get_email_settings(
|
|
current_user: User = Depends(get_current_vendor_api),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
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 {
|
|
"configured": False,
|
|
"settings": None,
|
|
"message": "Email settings not configured. Configure SMTP to send emails to customers.",
|
|
}
|
|
|
|
return {
|
|
"configured": settings.is_configured,
|
|
"verified": settings.is_verified,
|
|
"settings": settings.to_dict(),
|
|
}
|
|
|
|
|
|
@router.get("/status")
|
|
def get_email_status(
|
|
current_user: User = Depends(get_current_vendor_api),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
Get email configuration status.
|
|
|
|
Used by frontend to show warning banner if not configured.
|
|
"""
|
|
vendor_id = current_user.token_vendor_id
|
|
service = VendorEmailSettingsService(db)
|
|
return service.get_status(vendor_id)
|
|
|
|
|
|
@router.get("/providers")
|
|
def get_available_providers(
|
|
current_user: User = Depends(get_current_vendor_api),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
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 {
|
|
"providers": service.get_available_providers(tier),
|
|
"current_tier": tier.value if tier else None,
|
|
}
|
|
|
|
|
|
@router.put("")
|
|
def update_email_settings(
|
|
data: EmailSettingsUpdate,
|
|
current_user: User = Depends(get_current_vendor_api),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
Create or update email settings.
|
|
|
|
Premium providers (SendGrid, Mailgun, SES) require Business+ tier.
|
|
"""
|
|
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)
|
|
|
|
try:
|
|
settings = service.create_or_update(
|
|
vendor_id=vendor_id,
|
|
data=data.model_dump(exclude_unset=True),
|
|
current_tier=tier,
|
|
)
|
|
return {
|
|
"success": True,
|
|
"message": "Email settings updated successfully",
|
|
"settings": settings.to_dict(),
|
|
}
|
|
except AuthorizationError as e:
|
|
raise HTTPException(status_code=403, detail=str(e))
|
|
except ValidationError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
|
|
|
|
@router.post("/verify")
|
|
def verify_email_settings(
|
|
data: VerifyEmailRequest,
|
|
current_user: User = Depends(get_current_vendor_api),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
Verify email settings by sending a test email.
|
|
|
|
Sends a test email to the provided address and updates verification status.
|
|
"""
|
|
vendor_id = current_user.token_vendor_id
|
|
service = VendorEmailSettingsService(db)
|
|
|
|
try:
|
|
result = service.verify_settings(vendor_id, data.test_email)
|
|
if result["success"]:
|
|
return result
|
|
else:
|
|
raise HTTPException(status_code=400, detail=result["message"])
|
|
except NotFoundError as e:
|
|
raise HTTPException(status_code=404, detail=str(e))
|
|
except ValidationError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
|
|
|
|
@router.delete("")
|
|
def delete_email_settings(
|
|
current_user: User = Depends(get_current_vendor_api),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""
|
|
Delete email settings.
|
|
|
|
Warning: This will disable email sending for the vendor.
|
|
"""
|
|
vendor_id = current_user.token_vendor_id
|
|
service = VendorEmailSettingsService(db)
|
|
|
|
if service.delete(vendor_id):
|
|
return {"success": True, "message": "Email settings deleted"}
|
|
else:
|
|
raise HTTPException(status_code=404, detail="Email settings not found")
|