Skip to content

feat(email): multi-provider con fallback chain (v2.0.0) - #25

Merged
agentecopilotoai-code merged 1 commit into
mainfrom
feat/v2.0.0-email-providers-multi
May 28, 2026
Merged

feat(email): multi-provider con fallback chain (v2.0.0)#25
agentecopilotoai-code merged 1 commit into
mainfrom
feat/v2.0.0-email-providers-multi

Conversation

@agentecopilotoai-code

Copy link
Copy Markdown
Owner

Summary

  • Reemplaza el wrapper ResendProvider hardcodeado por un subsistema multi-provider donde el platform_owner configura uno o más providers (Resend, SendGrid, Mailgun, SMTP) desde la admin SPA en /admin/platform/email-providers.
  • Dispatcher con fallback chain por prioridad ascendente + audit por intento en app.email_dispatch_log.
  • Schema nuevo app.email_providers + migration 30-email-providers.sql (idempotente, RLS platform-owner-only).
  • 4 adapters: ResendProvider, SendGridProvider, MailgunProvider, SMTPProvider. Cada uno valida su config_jsonb con Pydantic al instanciar y mapea errores HTTP/SDK a excepciones tipadas que el dispatcher inspecciona.
  • API legacy (copiloto_core.services.email) sigue funcionando: get_email_provider() ahora devuelve un wrapper sobre el nuevo EmailDispatcher. Si no hay providers configurados, cae a NoopProvider (mismo comportamiento legacy).

BREAKING CHANGES

  • RESEND_API_KEY / RESEND_API_KEY_FILE env vars ya no se leen.
  • Settings.resend_api_key y Settings.resend_api_key_file removidos.
  • Para enviar emails reales: el operador debe entrar a /admin/platform/email-providers y crear al menos un provider con su API key. Si la tabla está vacía → NoopProvider (loguea pero no envía), mismo comportamiento legacy.
  • Si la migration 30-email-providers.sql no se aplicó, el shim logea warning y cae a Noop sin crashear.

Upgrade path

  1. python -m copiloto_core bootstrap aplica 30-email-providers.sql idempotentemente (incluido en _PLATFORM_SQL_FILES).
  2. Login al admin como platform_owner con MFA.
  3. Navegar a /admin/platform/email-providers, click "Añadir provider".
  4. Pegar la API key de Resend (o el provider preferido) y guardar.
  5. (Opcional) Agregar un segundo provider con priority mayor como fallback.

Test plan

  • 1279 backend tests pass (+37 nuevos cubriendo los 4 adapters + dispatcher + factory + CRUD routes).
  • Admin SPA build: nuevo chunk EmailProvidersContainer-*.js (~12 kB gz) copiado a copiloto_core/admin/static/dist/.
  • Bootstrap idempotency tests actualizados para el nuevo SQL.
  • Smoke manual: crear un Resend provider, enviar test email, verificar audit en app.email_dispatch_log.

Files

Categoría Path
Schema copiloto_core/platform_schema/30-email-providers.sql
Bootstrap copiloto_core/bootstrap.py (extiende _PLATFORM_SQL_FILES)
Adapters copiloto_core/email/providers/{base,resend,sendgrid,mailgun,smtp,factory}.py
Dispatcher copiloto_core/email/dispatcher.py
CRUD endpoints copiloto_core/platform_admin/email_provider_routes.py
Legacy shim copiloto_core/services/email.py (refactor a wrapper sobre EmailDispatcher)
UI admin-panel/src/features/platform/email-providers/
Docs docs/EMAIL.md (nuevo) + README + CONSUMER_ROUTES
Tests tests/test_unit_email_*.py (7 archivos, 37 tests)

🤖 Generated with Claude Code

Reemplaza el `ResendProvider` hardcodeado que leía `RESEND_API_KEY` por
un subsistema multi-provider donde el platform_owner configura uno o
varios providers (Resend, SendGrid, Mailgun, SMTP) desde la admin SPA
en `/admin/platform/email-providers`. El dispatcher recorre los
providers activos por prioridad ascendente y hace fallback al siguiente
cuando el actual falla con un error retryable
(`ProviderUnavailable` o `ProviderRateLimited`).

Cambios principales:

- Nuevo schema `app.email_providers` + `app.email_dispatch_log`
  (`copiloto_core/platform_schema/30-email-providers.sql`). RLS
  platform-owner-only.
- Nuevo paquete `copiloto_core.email` con `EmailDispatcher`,
  `EmailMessage`, factory y 4 adapters concretos. Excepciones tipadas
  (`ProviderUnavailable`/`RateLimited`/`InvalidConfig`/`Rejected`) que
  el dispatcher inspecciona para decidir fallback.
- CRUD endpoints `/v1/platform/email-providers/*` + `POST .../test`
  para smoke test desde el admin.
- UI nueva `admin-panel/src/features/platform/email-providers/`
  (rebuild del SPA incluido en `copiloto_core/admin/static/dist/`).
- `copiloto_core/services/email.py` mantiene la API legacy
  (`get_email_provider`, `EmailMessage`, `EmailSendResult`,
  `EmailSendError`) como shim que delega al nuevo dispatcher — los
  callers (`services/invitations.py`) no requieren cambios.
- Docs nueva `docs/EMAIL.md` con setup paso a paso por provider +
  troubleshooting + upgrade path.

BREAKING CHANGES:

- `RESEND_API_KEY` y `RESEND_API_KEY_FILE` ya NO se leen del env.
- `Settings.resend_api_key` y `Settings.resend_api_key_file`
  eliminados.
- Nuevo provider `app.email_providers` debe tener al menos una fila
  activa para enviar emails reales — si está vacía, el shim cae a
  NoopProvider (mismo comportamiento legacy que sin
  `RESEND_API_KEY`).

Upgrade path:

1. Correr `python -m copiloto_core bootstrap` (aplica
   `30-email-providers.sql` idempotentemente).
2. Login al admin como platform_owner → `/admin/platform/email-providers`.
3. Crear el provider Resend (o el que prefieras) con la API key.
4. (Opcional) Agregar un segundo provider de respaldo con menor
   prioridad para fallback automático.

Suite: 1279 passed, 3 skipped (baseline era 1242 — +37 tests nuevos
cubren los 4 adapters + dispatcher + factory + CRUD).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@agentecopilotoai-code
agentecopilotoai-code merged commit bd42251 into main May 28, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants