Add production provider deps/config so transactional email and CMS media can use Resend and a dedicated r2 disk with custom-domain URLs. Co-authored-by: Cursor <cursoragent@cursor.com>
4.5 KiB
Context
Fase 0–1 entregaram CMS com uploads de mídia via PublicImageUploadRules, que hoje seleciona disco public localmente e s3 quando FILESYSTEM_DISK=s3. Laravel 13 já inclui mailer resend em config/mail.php e chave em config/services.php, mas faltam dependências (resend/resend-php, league/flysystem-aws-s3-v3) e configuração explícita para Cloudflare R2.
Produção usará Resend para e-mail transacional e Cloudflare R2 para mídia pública via domínio customizado. Local e CI continuam com MAIL_MAILER=log/array e disco public.
Goals / Non-Goals
Goals:
- Resend como transporte nativo Laravel em produção (
MAIL_MAILER=resend,RESEND_API_KEY). - Disco dedicado
r2com variáveis explícitas (R2_*) e URL pública viaR2_URL(domínio customizado). - Seleção de disco de mídia alinhada a
FILESYSTEM_DISK=r2. - Documentação de env vars e testes automatizados de config/seleção (sem chamadas live).
Non-Goals:
- Templates de e-mail, filas de notificação de leads, ou fluxos CRM.
- Buckets privados, signed URLs, ou automação de DNS/CDN.
- Mudança de queue driver ou provisionamento Cloudflare via código.
Decisions
1. Resend via transporte nativo Laravel
Decisão: usar mailer resend já presente em config/mail.php + pacote resend/resend-php. Credencial em config/services.php → RESEND_API_KEY.
Alternativa rejeitada: SMTP genérico — menos idiomático; perde integração nativa Laravel 13.
2. Disco dedicado r2 (não reutilizar nome s3)
Decisão: adicionar disco r2 em config/filesystems.php com driver s3, endpoint R2 (https://<account_id>.r2.cloudflarestorage.com), use_path_style_endpoint=true, bucket e credenciais via R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET, R2_ENDPOINT, R2_URL (domínio público customizado).
Alternativa rejeitada: reutilizar disco s3 com vars AWS_* — funciona tecnicamente, mas obscurece provedor real e conflita com futuro uso de AWS S3.
3. FILESYSTEM_DISK=r2 em produção
Decisão: default disk de produção = r2. Local/test = local ou public. PublicImageUploadRules::disk() retorna r2 quando default é r2, s3 quando default é s3, senão public.
Alternativa rejeitada: mapear r2 para disco s3 internamente — confunde operadores e quebra intenção explícita do env.
4. URLs públicas via domínio customizado
Decisão: R2_URL aponta para domínio customizado (ex.: https://media.example.com) mapeado ao bucket via Cloudflare. Disco r2 define url => env('R2_URL') e visibility => public.
Alternativa rejeitada: URL r2.dev gerenciada — menos controle de marca e SEO; usuário escolheu custom domain.
5. Testes sem integração live
Decisão: testes usam Mail::fake, Storage::fake('r2'), e asserts de config (config('mail.default'), config('filesystems.disks.r2'), PublicImageUploadRules::disk()). Nenhuma chamada HTTP a Resend/R2 no CI.
6. Defaults seguros por ambiente
Decisão:
| Ambiente | MAIL_MAILER |
FILESYSTEM_DISK |
Disco de upload CMS |
|---|---|---|---|
| Local dev | log |
local |
public |
| Testes (phpunit) | array |
(unset → local) | public |
| Produção | resend |
r2 |
r2 |
Risks / Trade-offs
- [R2 credentials missing in prod] → app boot ok, upload fails at runtime; document required vars in
.env.exampleand README; config test asserts disk definition exists. - [Custom domain not configured] → broken public image URLs;
R2_URLdocumented as required for production media. - [Resend API key missing] → mail send fails; lead creation must not depend on mail (future phases per SPEC).
- [Package version drift] → pin compatible versions in
composer.json; runcomposer security-auditin CI.
Migration Plan
- Merge change; run
composer installin production image build. - Create R2 bucket, API token, and custom domain in Cloudflare dashboard (manual).
- Create Resend API key and verify sending domain.
- Set production env:
MAIL_MAILER=resend,RESEND_API_KEY,FILESYSTEM_DISK=r2,R2_*,R2_URL,MAIL_FROM_*. - Deploy; upload test image via Filament; verify public URL resolves.
- Rollback: revert env to
MAIL_MAILER=log,FILESYSTEM_DISK=public; existing DB paths remain valid for local disk until re-upload.
Open Questions
- (none — custom domain confirmed; Resend as sole production mail provider for this change)