feat: admin email hub — sent log, inbox, compose, audiences, delivery tracking

Add full email management at /admin/emails with:
- email_log table tracking all outgoing emails with resend_id + delivery events
- inbound_emails table for Resend webhook-received messages
- Resend webhook handler (/webhooks/resend) updating delivery status in real-time
- send_email() returns resend_id (str|None) instead of bool; all 9 worker
  handlers pass email_type= for per-type filtering
- Admin UI: sent log with HTMX filters, email detail with API-enriched HTML
  preview, inbox with unread badges + reply, compose with branded wrapping,
  audience management with contact list/remove
- Sidebar Email section with unread badge via blueprint context processor

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Deeman
2026-02-23 12:15:34 +01:00
parent cb1f00baf0
commit 454b362c88
20 changed files with 1049 additions and 21 deletions

View File

@@ -829,7 +829,7 @@ class TestResendLive:
@pytest.mark.asyncio
async def test_bounce_handled_gracefully(self):
"""Sending to bounced@resend.dev should not raise — send_email returns False."""
"""Sending to bounced@resend.dev should not raise — send_email returns str|None."""
result = await core.send_email(
to="bounced@resend.dev",
subject="Bounce test",
@@ -838,4 +838,5 @@ class TestResendLive:
)
# Resend may return success (delivery fails async) or error;
# either way the handler must not crash.
assert isinstance(result, bool)
# send_email now returns resend_id (str) on success, None on failure.
assert result is None or isinstance(result, str)