feat(email): pkg/email + Mailpit docker-compose service (ADR-0029 Phase A.1) #59

Merged
arcodange merged 1 commits from feat/email-infra-mailpit-phase-a1 into main 2026-05-05 10:47:04 +02:00
Owner

Foundation PR for the passwordless auth migration. Ships only the infrastructure: pkg/email.Sender interface + SMTPSender impl with Mailpit defaults, mailpit service in docker-compose, config keys + getter, EMAIL.md doc. No auth code yet — magic link handlers land in Phase A.2+. 6 unit tests cover validation, RFC5322 build (plain + multipart), header canonicalisation, defaults, ctx cancel. Race-clean. Cross-refs ADR-0028/0029/0030.

Foundation PR for the passwordless auth migration. Ships only the infrastructure: pkg/email.Sender interface + SMTPSender impl with Mailpit defaults, mailpit service in docker-compose, config keys + getter, EMAIL.md doc. No auth code yet — magic link handlers land in Phase A.2+. 6 unit tests cover validation, RFC5322 build (plain + multipart), header canonicalisation, defaults, ctx cancel. Race-clean. Cross-refs ADR-0028/0029/0030.
arcodange added 1 commit 2026-05-05 10:46:57 +02:00
Foundation for the passwordless auth migration (ADR-0028 Phase A) and
the BDD email-parallel strategy (ADR-0030). This PR ships only the
infrastructure — no auth code yet ; that lands in subsequent PRs.

Changes:
- docker-compose.yml: add mailpit service (axllent/mailpit:latest), SMTP
  on :1025, HTTP UI/API on :8025, MP_MAX_MESSAGES=5000
- pkg/email/sender.go: provider-agnostic Sender interface + Message struct
- pkg/email/smtp_sender.go: SMTPSender implementation (net/smtp), with
  Mailpit-friendly defaults (localhost:1025, no TLS, no AUTH), context-
  aware Send with timeout, supports plain text and multipart/alternative
- pkg/config: AuthConfig.Email field + EmailConfig struct + GetEmailConfig
  getter + 7 new env vars (DLC_AUTH_EMAIL_*) + defaults
- documentation/EMAIL.md: setup, inspection (UI + API), code examples,
  cross-refs to ADR-0028/0029/0030

Tests (pkg/email/smtp_sender_test.go):
- validateMessage rejects missing fields, accepts minimal
- buildRFC5322 plain-text path produces single-part text/plain with
  expected headers
- buildRFC5322 multipart path produces multipart/alternative with both
  parts and a closing boundary
- buildRFC5322 custom headers are canonicalised (lowercase keys → Title-Case)
- NewSMTPSender defaults are Mailpit-friendly
- Send respects context cancellation (no 10s wait when ctx cancelled)

Race detector clean. Build clean. Vet clean.

Out of scope for this PR (Phase A.2+):
- BDD email-steps helper package (pkg/bdd/mailpit/, pkg/bdd/steps/email_steps.go)
- magic_link_tokens table + repository
- magic-link/request and magic-link/consume HTTP handlers
- BDD scenarios for the magic-link flow
arcodange merged commit ef32e750ed into main 2026-05-05 10:47:04 +02:00
arcodange deleted branch feat/email-infra-mailpit-phase-a1 2026-05-05 10:47:04 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: arcodange/dance-lessons-coach#59