- README rewritten for the real Next.js + Payload + Postgres stack (old one described the dead static prototype) - Logo + 4 live screenshots (home, articles, post, subscription) - Arabic flow explanations with mermaid diagrams: visitor journey, content pipeline, request path - Architecture table, quick start, API reference, security summary, deploy commands - AGENT.md: orientation for future AI agents (data model, trust rules, rate limits, routes, env, deploy, house rules) - GLOSSARY.md and docs/adr committed (linked from README)
1.8 KiB
1.8 KiB
ADR 0003 — Cloudflare Email Sending REST, not SMTP
Date: 2026-10-03
Status
Accepted
Context
The site needs exactly one transactional email: the verification mail sent by POST /api/auth/resend-verification via sendVerificationEmail(to, token) in src/lib/mail.ts. The first implementation used nodemailer over generic SMTP. Cloudflare Email Service (the mail provider chosen for anwarpy.ai) has no SMTP interface — it exposes a REST endpoint only — so the nodemailer path could never be activated on the target host.
Decision
src/lib/mail.tssends viaPOST https://api.cloudflare.com/client/v4/accounts/{CLOUDFLARE_ACCOUNT_ID}/email/sending/sendusingfetch, authenticated withAuthorization: Bearer ${CLOUDFLARE_API_TOKEN}.- The
fromfield uses the REST shape{ address, name }(key isaddress, notemail); default addressnoreply@anwarpy.ai(override:MAIL_FROM). - Active only when
CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_IDare set; otherwise{ sent: false }(existing degrade-gracefully contract — the resend route answers 503 with a clear message). - Non-OK response or fetch throw:
console.errorthe error body, return{ sent: false }. No retry logic (single-shot; the resend route re-invokes). - nodemailer is uninstalled; the exported signature
sendVerificationEmail(to: string, token: string): Promise<{ sent: boolean }>is unchanged.
Consequences
- The API token needs the Email Sending permission; the sender domain must be onboarded in Cloudflare (SPF/DKIM records created via
wrangler email sending dns get). smtpConfigured()and allSMTP_*env vars are gone; anything expecting them must be updated when mail features grow.- Adding more email types later means extending this one module — it is the single mail boundary.