""" Celery tasks for email dispatch in DocuVault. Tasks follow the same pattern as document_tasks.py: - Plain sync def (Celery workers have no asyncio event loop by default) - Async body via asyncio.run() - All imports deferred inside _run_* functions to avoid circular imports (see celery_app.py comment — do NOT import config at module level) Tasks registered here: send_reset_email — dispatches a password-reset email to the user send_security_alert_email — dispatches a security alert on refresh token reuse (AUTH-07) """ import asyncio from celery_app import celery_app @celery_app.task(name="tasks.email_tasks.send_reset_email") def send_reset_email(to_address: str, reset_link: str) -> dict: """Synchronous Celery entry-point — send a password reset email. Called as: send_reset_email.delay(to_address, reset_link) Delegates to the async body via asyncio.run(). """ return asyncio.run(_run_send_reset(to_address, reset_link)) async def _run_send_reset(to_address: str, reset_link: str) -> dict: """Async body of send_reset_email. Deferred imports to avoid circular deps.""" from services.email import send_password_reset_email # noqa: PLC0415 try: send_password_reset_email(to_address, reset_link) return {"status": "sent", "to": to_address} except Exception as exc: return {"status": "failed", "error": str(exc)} @celery_app.task(name="tasks.email_tasks.send_security_alert_email") def send_security_alert_email(user_id: str) -> dict: """Synchronous Celery entry-point — send a security alert on token reuse. Called as: send_security_alert_email.delay(user_id) Fetches the user's email from the DB inside the task using asyncio.run(). On SMTP not configured: logs a WARNING per D-02 convention. """ return asyncio.run(_run_send_security_alert(user_id)) async def _run_send_security_alert(user_id: str) -> dict: """Async body of send_security_alert_email. Deferred imports to avoid circular deps.""" import uuid as _uuid # noqa: PLC0415 from db.session import AsyncSessionLocal # noqa: PLC0415 from db.models import User # noqa: PLC0415 from services.email import send_security_alert_email_sync # noqa: PLC0415 try: user_uuid = _uuid.UUID(user_id) except ValueError: return {"status": "failed", "error": f"Invalid user_id: {user_id}"} try: async with AsyncSessionLocal() as session: user = await session.get(User, user_uuid) if user is None: return {"status": "failed", "error": f"User {user_id} not found"} to_address = user.email send_security_alert_email_sync(to_address, user_id) return {"status": "sent", "user_id": user_id} except Exception as exc: return {"status": "failed", "error": str(exc)}