## 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`](../../config/mail.php) e chave em [`config/services.php`](../../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 `r2` com variáveis explícitas (`R2_*`) e URL pública via `R2_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://.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.example` and README; config test asserts disk definition exists. - **[Custom domain not configured]** → broken public image URLs; `R2_URL` documented 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`; run `composer security-audit` in CI. ## Migration Plan 1. Merge change; run `composer install` in production image build. 2. Create R2 bucket, API token, and custom domain in Cloudflare dashboard (manual). 3. Create Resend API key and verify sending domain. 4. Set production env: `MAIL_MAILER=resend`, `RESEND_API_KEY`, `FILESYSTEM_DISK=r2`, `R2_*`, `R2_URL`, `MAIL_FROM_*`. 5. Deploy; upload test image via Filament; verify public URL resolves. 6. **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)_