docs(state): Coolify configurato + mailer verificato; deploy fermo sul record DKIM

- RESEND_API_KEY e RESEND_FROM create su Coolify (production + preview)
- sendEmail() verificato con il template OTP reale: {ok:true}
- unico blocco residuo: TXT resend._domainkey.iamcavalli.net ha una chiave
  vecchia, dominio Resend status=failed, invio dal dominio rifiutato 403
- valore DKIM corretto e sequenza di ripresa annotati in STATE.md
- note API Coolify: envs non accetta is_build_time (422)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 23:20:47 +02:00
parent 8158038145
commit c9b5cd7451
+34 -13
View File
@@ -3,9 +3,9 @@ gsd_state_version: 1.0
milestone: v2.3 milestone: v2.3
milestone_name: Email & Accesso milestone_name: Email & Accesso
status: executing status: executing
stopped_at: "v2.3 implementata e testata in locale; deploy BLOCCATO su RESEND_API_KEY + whitelist clienti" stopped_at: "v2.3 pronta e Coolify configurato; deploy BLOCCATO sul record DKIM di iamcavalli.net (chiave vecchia)"
last_updated: "2026-07-28T20:45:00.000Z" last_updated: "2026-07-29T15:45:00.000Z"
last_activity: 2026-07-28 -- Phases 23/24/25 (OTP gate) implementate e verificate E2E; non ancora pushate last_activity: 2026-07-29 -- Coolify configurato + mailer verificato; deploy fermo sul record DKIM (azione utente)
progress: progress:
total_phases: 3 total_phases: 3
completed_phases: 3 completed_phases: 3
@@ -31,13 +31,32 @@ Plan: `~/.claude/plans/si-ma-abbiamo-un-jaunty-micali.md`
Status: **Bloccata sul deploy**, non sul codice. Vedi "Blocchi al deploy" qui sotto. Status: **Bloccata sul deploy**, non sul codice. Vedi "Blocchi al deploy" qui sotto.
Last activity: 2026-07-28 — gate OTP completo, 9/9 test E2E passati contro il DB di produzione Last activity: 2026-07-28 — gate OTP completo, 9/9 test E2E passati contro il DB di produzione
### ⛔ Blocchi al deploy (2026-07-28) ### ⛔ Unico blocco residuo al deploy: record DKIM (2026-07-29)
Pushare così com'è **chiude fuori i clienti reali dal loro portale**. Prima del push servono, in quest'ordine: **Il record DKIM di `iamcavalli.net` contiene una chiave vecchia**, diversa da quella che Resend si aspetta → dominio `status: failed`, invio da `no-reply@iamcavalli.net` rifiutato con 403. SPF (TXT + MX su `send`) sono verificati, solo il DKIM no.
1. **`RESEND_API_KEY` + `RESEND_FROM` su Coolify** (production). Senza, nessuno riceve il codice e il portale è inaccessibile a tutti. Serve un account Resend con dominio verificato. In `.env.local` i due nomi ci sono ma il valore della key è vuoto. Da sostituire — TXT, nome `resend._domainkey`:
2. **Whitelist popolata per i 3 clienti reali.** La migration ha seedato `client_emails` da `clients.email`, ma solo 1 cliente su 4 aveva quel campo valorizzato (`mario@test.it`, cliente di test "Rossi Inc"). George Vlad / Protocollo Estetico, Gian Luca Caruso / Caruso Speaker e Gianfranco Barban / Teckell hanno **whitelist vuota** → gate senza via d'uscita. Da fare da `/admin/clients/<id>` → sezione "Accessi al portale".
3. Solo dopo: push su `main` → auto-deploy Coolify → riverifica su `hub.iamcavalli.net`. ```
p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFHKhBLdT31CDeh5R0MWQM1XRmxwQrXS6TJXNa1Id9KP+nDPree4EWvTal7o440MUp4vwbW07AONoNjuIT8gMaC3a+e4396kQ9ewUpApa26azNf4k1092N6kSfNhfmU+oM5c/VYFEuPqp0yf1ar3M8w8WA4BELL4VBq29xk4/m4QIDAQAB
```
Finché non è sistemato, deployare renderebbe il portale inaccessibile a **tutti** i clienti (nessuno riceve il codice). Decisione dell'utente il 2026-07-29: **non pushare, si aspetta il DNS.** I token Hetzner/Cloudflare in `~/.coolify.env` sono vuoti → il DNS lo cambia l'utente a mano.
Sequenza alla ripresa:
1. Verificare propagazione: `dig +short TXT resend._domainkey.iamcavalli.net @8.8.8.8` deve restituire la chiave che inizia per `p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFHKhB…`
2. Ri-triggerare la verifica su Resend e controllare `status: verified` (`GET /domains/a2a80798-6f3a-42fe-8946-2c7f40f32c03`).
3. Test d'invio dal dominio reale verso un indirizzo qualsiasi.
4. Push su `main` → auto-deploy Coolify.
5. Popolare la whitelist dei 3 clienti reali da `/admin/clients/<id>` → "Accessi al portale". L'utente ha detto che li re-invita, quindi non è bloccante: la migration ha seedato solo `mario@test.it` (cliente di test "Rossi Inc"), mentre Protocollo Estetico, Caruso Speaker e Teckell partono con whitelist vuota.
6. Riverifica su `hub.iamcavalli.net`.
**Già fatto (2026-07-29):**
- `RESEND_API_KEY` + `RESEND_FROM` **configurate su Coolify** (production e preview) via API — verificate presenti.
- **Mailer verificato funzionante**: `sendEmail()` con il template OTP reale ha restituito `{ok:true, id:…}` e Resend ha accettato l'email (consegna confermata verso `info@iamcavalli.net` usando `onboarding@resend.dev`, l'unica coppia mittente/destinatario permessa senza dominio verificato).
- Verificato che quando Resend rifiuta, la route risponde comunque con il messaggio neutro e logga l'errore lato server — il comportamento no-enumeration tiene anche a provider guasto.
### Cosa è stato consegnato in v2.3 (codice locale, buildato e testato) ### Cosa è stato consegnato in v2.3 (codice locale, buildato e testato)
@@ -119,8 +138,10 @@ None yet.
### Blockers/Concerns ### Blockers/Concerns
- **[BLOCCANTE] `RESEND_API_KEY` + `RESEND_FROM` su Coolify** — senza, il gate OTP deployato rende il portale inaccessibile a tutti i clienti. Stessa procedura di `ANTHROPIC_API_KEY` (2026-06-20). Serve un account Resend con dominio verificato. - **[BLOCCANTE] Record DKIM di `iamcavalli.net` con chiave vecchia** — vedi "Unico blocco residuo al deploy". Finché non è corretto, nessun invio dal dominio è possibile e deployare il gate chiuderebbe fuori tutti i clienti.
- **[BLOCCANTE] Whitelist vuota per 3 clienti su 4** — vedi "Blocchi al deploy". Da popolare da `/admin/clients/<id>` prima del push. - ~~`RESEND_API_KEY` + `RESEND_FROM` su Coolify~~ — **fatto 2026-07-29**, production e preview.
- **Whitelist vuota per 3 clienti su 4** (non bloccante: l'utente li re-invita) — da popolare da `/admin/clients/<id>` → "Accessi al portale".
- **Coolify API**: credenziali in `~/.coolify.env` (`export COOLIFY_URL/COOLIFY_TOKEN`, va sorgentato con `set -a; . ~/.coolify.env`). App ClientHub uuid `xsksow44g4kcoo8wocsgkscc`. Il POST su `/api/v1/applications/<uuid>/envs` **non accetta** il campo `is_build_time` (422): mandare solo `key`, `value`, `is_preview`. I token Hetzner/Cloudflare nel file sono **vuoti** → il DNS non è modificabile via API.
- **Migrations (sempre valido)**: ogni fase con schema DEVE avere la migration applicata a prod PRIMA di pushare il codice dipendente. `drizzle-kit generate` rotto → SQL a mano. La 0015 è già applicata. Due strade: `cat migration.sql | ssh root@178.104.27.55 "docker exec -i xwkk0040w0kk0gsgcgog8owk psql -U clienthub -d clienthub -v ON_ERROR_STOP=1 --single-transaction"` (autoritativa, nessun tunnel), oppure tunnel `ssh -f -N -L 54321:localhost:54321 root@178.104.27.55` con `DATABASE_URL` riscritto a `127.0.0.1:54321` se serve puntarci il tooling locale. - **Migrations (sempre valido)**: ogni fase con schema DEVE avere la migration applicata a prod PRIMA di pushare il codice dipendente. `drizzle-kit generate` rotto → SQL a mano. La 0015 è già applicata. Due strade: `cat migration.sql | ssh root@178.104.27.55 "docker exec -i xwkk0040w0kk0gsgcgog8owk psql -U clienthub -d clienthub -v ON_ERROR_STOP=1 --single-transaction"` (autoritativa, nessun tunnel), oppure tunnel `ssh -f -N -L 54321:localhost:54321 root@178.104.27.55` con `DATABASE_URL` riscritto a `127.0.0.1:54321` se serve puntarci il tooling locale.
- **`.env.local` punta al DB di PRODUZIONE** (178.104.27.55:54321, richiede il tunnel). Non esiste un DB di sviluppo separato: qualsiasi test in locale scrive su dati reali. Verificare sempre i conteggi delle tabelle protette prima e dopo. - **`.env.local` punta al DB di PRODUZIONE** (178.104.27.55:54321, richiede il tunnel). Non esiste un DB di sviluppo separato: qualsiasi test in locale scrive su dati reali. Verificare sempre i conteggi delle tabelle protette prima e dopo.
- **Debito tecnico (non bloccante)**: tabelle legacy `service_catalog`/`offer_services`/`offer_micro_services` restano come deadweight; `createService`/`serviceSchema` dead code in `catalog/actions.ts`. - **Debito tecnico (non bloccante)**: tabelle legacy `service_catalog`/`offer_services`/`offer_micro_services` restano come deadweight; `createService`/`serviceSchema` dead code in `catalog/actions.ts`.
@@ -141,7 +162,7 @@ Items acknowledged and carried forward from previous milestone close:
## Session Continuity ## Session Continuity
Last session: 2026-07-28T20:45:00.000Z Last session: 2026-07-29T15:45:00.000Z
Stopped at: v2.3 implementata e verificata E2E in locale (9/9). **Non pushata**: mancano `RESEND_API_KEY` su Coolify e la whitelist dei 3 clienti reali. Stopped at: v2.3 implementata, verificata E2E (9/9) e mailer confermato funzionante. Coolify configurato. **Non pushata**: manca solo la correzione del record DKIM, che deve fare l'utente a mano.
Next: configurare Resend → popolare le whitelist → push su `main` → riverifica su `hub.iamcavalli.net`. Next: `dig +short TXT resend._domainkey.iamcavalli.net @8.8.8.8` → verifica Resend → push su `main` → whitelist dei 3 clienti → riverifica su `hub.iamcavalli.net`.
Resume file: .planning/STATE.md Resume file: .planning/STATE.md