""" E-Mail-Template Routes — Benachrichtigungsvorlagen fuer DSGVO-Compliance. Verwaltet Templates fuer DSR, Consent, Breach, Vendor und Training E-Mails. Inklusive Versionierung, Approval-Workflow, Vorschau und Send-Logging. Phase 1 Step 4 refactor: handlers delegate to EmailTemplateService (templates/settings/logs/stats/initialize) and EmailTemplateVersionService (version workflow + preview + test-send). Template types catalog is re-exported for any legacy callers. """ from typing import Any, Optional from fastapi import APIRouter, Depends, Header, Query from sqlalchemy.orm import Session from classroom_engine.database import get_db from compliance.api._http_errors import translate_domain_errors from compliance.schemas.email_template import ( PreviewRequest, SendTestRequest, SettingsUpdate, TemplateCreate, VersionCreate, VersionUpdate, ) from compliance.services.email_template_service import ( TEMPLATE_TYPES, VALID_CATEGORIES, VALID_STATUSES, EmailTemplateService, ) from compliance.services.email_template_version_service import ( EmailTemplateVersionService, ) router = APIRouter(prefix="/email-templates", tags=["compliance-email-templates"]) DEFAULT_TENANT = "9282a473-5c95-4b3a-bf78-0ecc0ec71d3e" def _get_tenant(x_tenant_id: Optional[str] = Header(None, alias="X-Tenant-ID")) -> str: return x_tenant_id or DEFAULT_TENANT def get_template_service(db: Session = Depends(get_db)) -> EmailTemplateService: return EmailTemplateService(db) def get_version_service(db: Session = Depends(get_db)) -> EmailTemplateVersionService: return EmailTemplateVersionService(db) # ============================================================================= # Template Type Info (MUST be before parameterized routes) # ============================================================================= @router.get("/types") async def get_template_types() -> list[dict[str, Any]]: """Gibt alle verfuegbaren Template-Typen mit Variablen zurueck.""" return EmailTemplateService.list_types() @router.get("/stats") async def get_stats( tenant_id: str = Depends(_get_tenant), service: EmailTemplateService = Depends(get_template_service), ) -> dict[str, Any]: """Statistiken ueber E-Mail-Templates.""" with translate_domain_errors(): return service.stats(tenant_id) @router.get("/settings") async def get_settings( tenant_id: str = Depends(_get_tenant), service: EmailTemplateService = Depends(get_template_service), ) -> dict[str, Any]: """Globale E-Mail-Einstellungen laden.""" with translate_domain_errors(): return service.get_settings(tenant_id) @router.put("/settings") async def update_settings( body: SettingsUpdate, tenant_id: str = Depends(_get_tenant), service: EmailTemplateService = Depends(get_template_service), ) -> dict[str, Any]: """Globale E-Mail-Einstellungen speichern.""" with translate_domain_errors(): return service.update_settings(tenant_id, body) @router.get("/logs") async def get_send_logs( limit: int = Query(20, ge=1, le=100), offset: int = Query(0, ge=0), template_type: Optional[str] = Query(None), tenant_id: str = Depends(_get_tenant), service: EmailTemplateService = Depends(get_template_service), ) -> dict[str, Any]: """Send-Logs (paginiert).""" with translate_domain_errors(): return service.send_logs(tenant_id, limit, offset, template_type) @router.post("/initialize") async def initialize_defaults( tenant_id: str = Depends(_get_tenant), service: EmailTemplateService = Depends(get_template_service), ) -> dict[str, Any]: """Default-Templates fuer einen Tenant initialisieren.""" with translate_domain_errors(): return service.initialize_defaults(tenant_id) @router.get("/default/{template_type}") async def get_default_content(template_type: str) -> dict[str, Any]: """Default-Content fuer einen Template-Typ.""" with translate_domain_errors(): return EmailTemplateService.default_content(template_type) # ============================================================================= # Template CRUD # ============================================================================= @router.get("") async def list_templates( category: Optional[str] = Query(None), tenant_id: str = Depends(_get_tenant), service: EmailTemplateService = Depends(get_template_service), ) -> list[dict[str, Any]]: """Alle Templates mit letzter publizierter Version.""" with translate_domain_errors(): return service.list_templates(tenant_id, category) @router.post("") async def create_template( body: TemplateCreate, tenant_id: str = Depends(_get_tenant), service: EmailTemplateService = Depends(get_template_service), ) -> dict[str, Any]: """Template erstellen.""" with translate_domain_errors(): return service.create_template(tenant_id, body) # ============================================================================= # Version Management (static path before parameterized) # ============================================================================= @router.post("/versions") async def create_version( body: VersionCreate, template_id: str = Query(..., alias="template_id"), tenant_id: str = Depends(_get_tenant), service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Neue Version erstellen (via query param template_id).""" with translate_domain_errors(): return service.create_version(tenant_id, template_id, body) # ============================================================================= # Single Template (parameterized — after all static paths) # ============================================================================= @router.get("/{template_id}") async def get_template( template_id: str, tenant_id: str = Depends(_get_tenant), service: EmailTemplateService = Depends(get_template_service), ) -> dict[str, Any]: """Template-Detail.""" with translate_domain_errors(): return service.get_template(tenant_id, template_id) @router.get("/{template_id}/versions") async def get_versions( template_id: str, tenant_id: str = Depends(_get_tenant), service: EmailTemplateVersionService = Depends(get_version_service), ) -> list[dict[str, Any]]: """Versionen eines Templates.""" with translate_domain_errors(): return service.list_versions(tenant_id, template_id) @router.post("/{template_id}/versions") async def create_version_for_template( template_id: str, body: VersionCreate, tenant_id: str = Depends(_get_tenant), service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Neue Version fuer ein Template erstellen.""" with translate_domain_errors(): return service.create_version(tenant_id, template_id, body) # ============================================================================= # Version Workflow (parameterized by version_id) # ============================================================================= @router.get("/versions/{version_id}") async def get_version( version_id: str, service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Version-Detail.""" with translate_domain_errors(): return service.get_version(version_id) @router.put("/versions/{version_id}") async def update_version( version_id: str, body: VersionUpdate, service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Draft aktualisieren.""" with translate_domain_errors(): return service.update_version(version_id, body) @router.post("/versions/{version_id}/submit") async def submit_version( version_id: str, service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Zur Pruefung einreichen.""" with translate_domain_errors(): return service.submit(version_id) @router.post("/versions/{version_id}/approve") async def approve_version( version_id: str, comment: Optional[str] = None, service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Genehmigen.""" with translate_domain_errors(): return service.approve(version_id, comment) @router.post("/versions/{version_id}/reject") async def reject_version( version_id: str, comment: Optional[str] = None, service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Ablehnen.""" with translate_domain_errors(): return service.reject(version_id, comment) @router.post("/versions/{version_id}/publish") async def publish_version( version_id: str, service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Publizieren.""" with translate_domain_errors(): return service.publish(version_id) @router.post("/versions/{version_id}/preview") async def preview_version( version_id: str, body: PreviewRequest, service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Vorschau mit Test-Variablen.""" with translate_domain_errors(): return service.preview(version_id, body) @router.post("/versions/{version_id}/send-test") async def send_test_email( version_id: str, body: SendTestRequest, tenant_id: str = Depends(_get_tenant), service: EmailTemplateVersionService = Depends(get_version_service), ) -> dict[str, Any]: """Test-E-Mail senden (Simulation — loggt nur).""" with translate_domain_errors(): return service.send_test(tenant_id, version_id, body) # Legacy re-exports __all__ = [ "router", "TEMPLATE_TYPES", "VALID_CATEGORIES", "VALID_STATUSES", ]