[CONTEXT+MKTG] Contesto sessione + HTML migliorati per comunicazione terze parti

- CONTEXT_LAST_SESSION.md: documento completo di tutto lo sprint B2B
- mktg-api-doc.html: Quick Start box con chiave attiva, flusso visivo 4 step, curl pronti
- integrazioniext.html tab Inviti: tabella "Chi fa cosa" per ruolo, sezione mktg-agile
  con chiave API + 3 curl pronti, Quick Start aggiornato con tabella risorse per destinatario

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile 2026-03-07 16:37:14 +01:00
parent 13eb8ae8a8
commit f7347ccd8c
3 changed files with 277 additions and 7 deletions

View File

@ -0,0 +1,126 @@
# Contesto Ultima Sessione — NIS2 Agile
**Data**: 2026-03-07
**Dominio produzione**: https://nis2.agile.software/
---
## Cosa è stato fatto in questa sessione
### Sprint B2B / Integrazioni / Licenze
#### 1. Migrazione dominio
- DNS A record `nis2.agile.software → 135.181.149.254` via Cloudflare API
- SSL Let's Encrypt certbot (`/etc/letsencrypt/live/nis2.agile.software/`)
- Apache vhost HTTPS abilitato
- Redirect 301 da `nis2.certisource.it → nis2.agile.software`
#### 2. Test runner
- `public/test-runner.php` — token `Nis2Test2026`, SSE streaming, dark terminal UI
- Bottone `Reset + Simula + Testa Tutto` (dark green, top Test tab)
- `docs/sql/reset-demo.sql` aggiornato: mantiene sempre `cristiano.benassati@gmail.com` (super_admin, `Silvia1978!@`)
#### 3. Integrazioni B2B (ServicesController)
- `POST /api/services/token` — API Key → JWT 15min
- `POST /api/services/sso` — SSO federato con responsabilità utente, JWT 2h
- `POST /api/services/provision` — provisioning automatico org+utente+api_key da invito
- `logExternalCall()` in BaseController per audit trail chiamate esterne
- `BaseController::base64UrlEncode/Decode` cambiati da private a protected
#### 4. Sistema Inviti / Licenze
- **`application/controllers/InviteController.php`** — CRUD inviti
- Auth: `requireLicenseAuth()` accetta X-API-Key (`admin:licenses`) O JWT super_admin
- `POST /api/invites/create` — genera token `inv_xxx`, SHA-256 in DB
- `GET /api/invites/list` — lista con filtri status/channel
- `GET /api/invites/{id}` — dettaglio
- `DELETE /api/invites/{id}` — revoca
- `POST /api/invites/{id}/regenerate` — nuovo token
- `GET /api/invites/validate?token=` — pubblica, nessuna auth
- `static resolveInvite()` / `static markUsed()` — usati da provision
- **`ServicesController::provision()`** aggiornato:
- Accetta `invite_token` nel body (B2B flow) O `X-Provision-Secret` (admin diretto)
- Piano e durata forzati dall'invito
- Chiama `InviteController::markUsed()` dopo provisioning riuscito
- Salva `license_max_users` nell'org
- **`public/index.php`** aggiornato: route `/api/invites/*`
#### 5. DB Migrations applicate in produzione
- **011_provisioning.sql**: `organizations` +provisioned_by, +license_plan, +license_expires_at, +lg231_company_id; `users` +must_change_password, +phone, +job_title; `api_keys` +created_by
- **012_invites.sql**: tabella `invites` (token_hash, plan, duration_months, max_uses, expires_at, channel, restrict_vat/email, status, used_by_org_id)
- **013_license_ext.sql**: `invites` +max_users_per_org, +price_eur, +reseller_name; `organizations` +license_max_users
#### 6. Pagine HTML nuove
- **`public/integrazioniext.html`** — documentazione partner tecnici (lg231, e-commerce)
- Tab: Services API, Inviti & Licenze, Guida lg231, Webhook, Quick Start
- **`public/licenseExt.html`** — pannello marketing per gestione licenze
- Login JWT super_admin, stats strip, genera licenze, lista/filtri, revoca/rigenera, export CSV
- **`public/mktg-api-doc.html`** — risponde alle 6 domande marketing
- Quick start, endpoint, auth, response format, lista, revoca, spiegazione pagine
- **`public/nis2-license-api.postman.json`** — Postman collection scaricabile
- **`docs/integration/nis2-license-api.postman.json`** — copia in repo
#### 7. API Key mktg-agile (generata in produzione)
- **Chiave**: `nis2_mktg_8c8bd38e78fccb9faa749d8601051f42`
- **Scope**: `admin:licenses`
- **DB api_keys.id**: 1
- **Scade**: 2028-03-07
#### 8. Documentazione lg231
- `docs/integration/lg231-nis2-integration.md` — spec completa per agente Claude lg231
- `public/settings.html` — aggiunto scope `admin:licenses`, `admin:org`, `sso:login` al form API Keys
---
## File creati o modificati
| File | Azione |
|------|--------|
| `application/controllers/InviteController.php` | NUOVO |
| `application/controllers/ServicesController.php` | MODIFICATO (provision + sso + token) |
| `application/controllers/BaseController.php` | MODIFICATO (base64Url protected) |
| `application/config/config.php` | MODIFICATO (PROVISION_SECRET) |
| `public/index.php` | MODIFICATO (route invites) |
| `public/integrazioniext.html` | NUOVO |
| `public/licenseExt.html` | NUOVO |
| `public/mktg-api-doc.html` | NUOVO |
| `public/nis2-license-api.postman.json` | NUOVO |
| `public/settings.html` | MODIFICATO (scopes) |
| `public/js/common.js` | MODIFICATO (sidebar link integrazioniext) |
| `docs/sql/011_provisioning.sql` | NUOVO |
| `docs/sql/012_invites.sql` | NUOVO |
| `docs/sql/013_license_ext.sql` | NUOVO |
| `docs/sql/reset-demo.sql` | MODIFICATO (super_admin persistente) |
| `docs/integration/lg231-nis2-integration.md` | NUOVO |
| `docs/integration/nis2-license-api.postman.json` | NUOVO |
---
## File deployati su Hetzner
Tutti i file sopra tramite `git push + git pull`. Migrations applicate via SSH/mysql.
---
## URL attivi in produzione
| Risorsa | URL |
|---------|-----|
| App | https://nis2.agile.software/ |
| Test runner | https://nis2.agile.software/test-runner.php?t=Nis2Test2026 |
| Doc partner (lg231) | https://nis2.agile.software/integrazioniext.html |
| Pannello licenze (mktg) | https://nis2.agile.software/licenseExt.html |
| Doc API marketing | https://nis2.agile.software/mktg-api-doc.html |
| Postman collection | https://nis2.agile.software/nis2-license-api.postman.json |
---
## Prossimi passi consigliati
1. **mktg-agile**: recepire API Key + integrare `POST /api/invites/create` nel flusso di acquisto
2. **lg231**: leggere `integrazioniext.html` + `docs/integration/lg231-nis2-integration.md` e implementare Nis2Client + provisioning
3. **E-commerce**: usare `GET /api/invites/validate?token=` per mostrare anteprima piano prima dell'attivazione
4. **Controllo utenti per org**: aggiungere check `license_max_users` in `OrganizationController` quando si invitano nuovi utenti
5. **Rinnovo licenze**: endpoint `POST /api/invites/{id}/renew` che estende `license_expires_at` sull'org
---
## Credenziali importanti (non committare)
- Super admin: `cristiano.benassati@gmail.com` / `Silvia1978!@`
- API Key mktg: `nis2_mktg_8c8bd38e78fccb9faa749d8601051f42` (scope: admin:licenses, scade 2028)
- SSH Hetzner: `ssh -i docs/credentials/hetzner_key root@135.181.149.254`

View File

@ -324,9 +324,46 @@
<strong>Sistema Inviti / Licenze B2B:</strong> NIS2 Agile genera token di invito che abilitano <strong>Sistema Inviti / Licenze B2B:</strong> NIS2 Agile genera token di invito che abilitano
il provisioning automatico di organizzazioni e utenti. L'e-commerce o il partner riceve l'invito, il provisioning automatico di organizzazioni e utenti. L'e-commerce o il partner riceve l'invito,
lo consegna al cliente finale (es. lg231), che lo usa per attivarsi automaticamente. lo consegna al cliente finale (es. lg231), che lo usa per attivarsi automaticamente.
L'accesso si interrompe alla scadenza dell'invito. L'accesso si interrompe alla scadenza della licenza.
</div> </div>
<div class="section-title">Chi fa cosa — ruoli nell'ecosistema</div>
<table class="api-table" style="margin-bottom:1.5rem">
<thead><tr><th>Sistema</th><th>Ruolo</th><th>API usata</th><th>Auth</th></tr></thead>
<tbody>
<tr>
<td><strong>NIS2 Admin</strong><br><span style="font-size:.72rem;color:var(--text-secondary)">o mktg-agile</span></td>
<td>Genera i token licenza, fissa piano/durata/utenti/prezzo</td>
<td><code>POST /api/invites/create</code></td>
<td><span class="badge-status s-pending" style="font-size:.65rem">API Key admin:licenses</span></td>
</tr>
<tr>
<td><strong>E-commerce</strong></td>
<td>Riceve token all'acquisto, lo consegna al cliente con l'ordine</td>
<td>— (semplice consegna token)</td>
<td><span class="badge-status s-pending" style="font-size:.65rem">API Key admin:licenses</span></td>
</tr>
<tr>
<td><strong>lg231 / partner</strong></td>
<td>Valida il token, fa provisioning automatico per il cliente</td>
<td><code>GET /api/invites/validate</code><br><code>POST /api/services/provision</code></td>
<td><span class="badge-status s-used" style="font-size:.65rem">invite_token nel body</span></td>
</tr>
<tr>
<td><strong>Cliente finale</strong></td>
<td>Apre invite_url e si registra in autonomia via wizard</td>
<td><code>onboarding.html?invite=inv_...</code></td>
<td><span class="badge-status" style="font-size:.65rem;background:rgba(156,163,175,.1);color:#9ca3af">nessuna auth</span></td>
</tr>
<tr>
<td><strong>NIS2 Admin</strong></td>
<td>Monitora uso licenze, revoca, rigenera</td>
<td><code>GET /api/invites/list</code><br><code>DELETE /api/invites/{id}</code></td>
<td><span class="badge-status s-pending" style="font-size:.65rem">API Key o JWT super_admin</span></td>
</tr>
</tbody>
</table>
<div class="section-title">Flusso completo</div> <div class="section-title">Flusso completo</div>
<div class="code-block" style="font-family:monospace;line-height:1.8;font-size:.82rem"> <div class="code-block" style="font-family:monospace;line-height:1.8;font-size:.82rem">
<span style="color:#22c55e">1. GENERAZIONE</span> NIS2 Admin (super_admin) <span style="color:#22c55e">1. GENERAZIONE</span> NIS2 Admin (super_admin)
@ -458,6 +495,31 @@ curl "https://nis2.agile.software/api/invites/validate?token=inv_a1b2c3..."
</tbody> </tbody>
</table> </table>
<div class="section-title">Per mktg-agile / E-commerce — Quick Start</div>
<div class="callout">
Chiave API già attiva per mktg-agile: <code style="color:#4ade80">nis2_mktg_8c8bd38e78fccb9faa749d8601051f42</code>
· Scope: <code>admin:licenses</code> · Scade: 2028-03-07
· Doc completa: <a href="/mktg-api-doc.html" style="color:var(--primary)">mktg-api-doc.html</a>
· Pannello web: <a href="/licenseExt.html" style="color:var(--primary)">licenseExt.html</a>
</div>
<div class="code-block"><span class="cmt"># 1. Crea licenza (mktg-agile → NIS2)</span>
curl -X POST https://nis2.agile.software/api/invites/create \
-H <span class="str">"X-API-Key: nis2_mktg_8c8bd38e78fccb9faa749d8601051f42"</span> \
-H <span class="str">"Content-Type: application/json"</span> \
-d <span class="str">'{"plan":"professional","duration_months":12,"max_users_per_org":10,
"label":"Ordine MKT-001","channel":"ecommerce","price_eur":990}'</span>
<span class="cmt"># → risposta contiene token (inv_xxx) e invite_url</span>
<span class="cmt"># → consegna invite_url al cliente nel messaggio di conferma ordine</span>
<span class="cmt"># 2. Verifica stato licenza in qualsiasi momento</span>
curl -H <span class="str">"X-API-Key: nis2_mktg_8c8bd38e78fccb9faa749d8601051f42"</span> \
"https://nis2.agile.software/api/invites/42"
<span class="cmt"># 3. Lista licenze attive per canale ecommerce</span>
curl -H <span class="str">"X-API-Key: nis2_mktg_8c8bd38e78fccb9faa749d8601051f42"</span> \
"https://nis2.agile.software/api/invites/list?channel=ecommerce&amp;status=pending"</div>
<div class="callout"> <div class="callout">
<strong>Sicurezza token:</strong> il token <code>inv_</code> viene restituito in chiaro UNA SOLA VOLTA <strong>Sicurezza token:</strong> il token <code>inv_</code> viene restituito in chiaro UNA SOLA VOLTA
alla creazione. Nel DB è conservato solo il hash SHA-256. Il <code>token_prefix</code> (es: <code>inv_a1b2c3...</code>) alla creazione. Nel DB è conservato solo il hash SHA-256. Il <code>token_prefix</code> (es: <code>inv_a1b2c3...</code>)
@ -729,14 +791,45 @@ curl -H <span class="str">"X-API-Key: nis2_TUA_CHIAVE"</span> \
curl -H <span class="str">"X-API-Key: nis2_TUA_CHIAVE"</span> \ curl -H <span class="str">"X-API-Key: nis2_TUA_CHIAVE"</span> \
"https://nis2.agile.software/api/services/risks/feed?level=high,critical"</div> "https://nis2.agile.software/api/services/risks/feed?level=high,critical"</div>
<div class="section-title">Contatti e riferimenti</div> <div class="section-title">Risorse per ogni tipo di integratore</div>
<table class="api-table"> <table class="api-table">
<thead><tr><th>Destinatario</th><th>Risorsa</th><th>URL</th></tr></thead>
<tbody> <tbody>
<tr><td>App NIS2 Agile</td><td><a href="https://nis2.agile.software" style="color:var(--primary)">https://nis2.agile.software</a></td></tr> <tr>
<tr><td>Specifiche OpenAPI</td><td><code>GET /api/services/openapi.json</code></td></tr> <td><strong>mktg-agile / E-commerce</strong></td>
<tr><td>Test Runner</td><td><a href="/test-runner.php?t=Nis2Test2026" style="color:var(--primary)">test-runner.php</a></td></tr> <td>Documentazione API licenze + Quick Start</td>
<tr><td>Amministratore</td><td>cristiano.benassati@gmail.com</td></tr> <td><a href="/mktg-api-doc.html" style="color:var(--primary)">mktg-api-doc.html</a></td>
<tr><td>Documento tecnico lg231</td><td><code>docs/integration/lg231-nis2-integration.md</code></td></tr> </tr>
<tr>
<td><strong>mktg-agile / E-commerce</strong></td>
<td>Pannello web gestione licenze</td>
<td><a href="/licenseExt.html" style="color:var(--primary)">licenseExt.html</a></td>
</tr>
<tr>
<td><strong>mktg-agile / E-commerce</strong></td>
<td>Postman Collection (import diretto)</td>
<td><a href="/nis2-license-api.postman.json" download style="color:var(--primary)">nis2-license-api.postman.json</a></td>
</tr>
<tr>
<td><strong>lg231 / Partner tecnici</strong></td>
<td>Questa pagina — tab Guida lg231</td>
<td><a href="/integrazioniext.html" style="color:var(--primary)">integrazioniext.html</a></td>
</tr>
<tr>
<td><strong>lg231 (agente Claude)</strong></td>
<td>Spec tecnica completa per implementazione</td>
<td><code>docs/integration/lg231-nis2-integration.md</code></td>
</tr>
<tr>
<td><strong>Tutti</strong></td>
<td>Health check (no auth)</td>
<td><code>GET https://nis2.agile.software/api/services/status</code></td>
</tr>
<tr>
<td><strong>Tutti</strong></td>
<td>App NIS2 Agile</td>
<td><a href="https://nis2.agile.software" style="color:var(--primary)">https://nis2.agile.software</a></td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -103,6 +103,57 @@ table.resp code { background: rgba(255,255,255,.06); padding: .1rem .3rem; borde
</div> </div>
</div> </div>
<!-- ── QUICK START ── -->
<div style="background:rgba(6,182,212,.06);border:1px solid rgba(6,182,212,.25);border-radius:12px;padding:1.5rem;margin-bottom:2rem">
<div style="font-size:.75rem;font-weight:700;color:var(--primary);letter-spacing:.06em;margin-bottom:1rem">QUICK START — OPERATIVO IN 2 MINUTI</div>
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:1rem;margin-bottom:1.25rem">
<div style="background:rgba(0,0,0,.25);border-radius:8px;padding:1rem">
<div style="font-size:.7rem;color:var(--text-secondary);margin-bottom:.4rem">CHIAVE API (già attiva)</div>
<code style="font-size:.75rem;color:#4ade80;word-break:break-all">nis2_mktg_8c8bd38e78fccb9faa749d8601051f42</code>
<div style="font-size:.68rem;color:var(--text-secondary);margin-top:.3rem">Scope: admin:licenses · Scade: 2028-03-07</div>
</div>
<div style="background:rgba(0,0,0,.25);border-radius:8px;padding:1rem">
<div style="font-size:.7rem;color:var(--text-secondary);margin-bottom:.4rem">BASE URL</div>
<code style="font-size:.8rem;color:var(--text-primary)">https://nis2.agile.software/api</code>
<div style="font-size:.68rem;color:var(--text-secondary);margin-top:.3rem">Tutti gli endpoint sotto questo path</div>
</div>
<div style="background:rgba(0,0,0,.25);border-radius:8px;padding:1rem">
<div style="font-size:.7rem;color:var(--text-secondary);margin-bottom:.4rem">TEST IMMEDIATO</div>
<code style="font-size:.75rem;color:#fbbf24">curl nis2.agile.software/api/services/status</code>
<div style="font-size:.68rem;color:var(--text-secondary);margin-top:.3rem">Nessuna auth — deve rispondere 200</div>
</div>
</div>
<div style="font-size:.78rem;color:var(--text-secondary);margin-bottom:.5rem;font-weight:600">FLUSSO COMPLETO — dalla vendita all'attivazione cliente</div>
<div style="display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;font-size:.8rem">
<div style="background:rgba(168,85,247,.15);border:1px solid rgba(168,85,247,.3);border-radius:6px;padding:.3rem .7rem;color:#a855f7;white-space:nowrap">
<strong>1. mktg-agile</strong><br><span style="font-size:.7rem">POST /invites/create</span>
</div>
<div style="color:var(--text-secondary)">→ token inv_xxx</div>
<div style="background:rgba(249,115,22,.12);border:1px solid rgba(249,115,22,.25);border-radius:6px;padding:.3rem .7rem;color:#f97316;white-space:nowrap">
<strong>2. e-commerce</strong><br><span style="font-size:.7rem">consegna token al cliente</span>
</div>
<div style="color:var(--text-secondary)">→ inv_xxx</div>
<div style="background:rgba(6,182,212,.12);border:1px solid rgba(6,182,212,.25);border-radius:6px;padding:.3rem .7rem;color:#06b6d4;white-space:nowrap">
<strong>3. lg231 / cliente</strong><br><span style="font-size:.7rem">POST /services/provision</span>
</div>
<div style="color:var(--text-secondary)">→ org + api_key</div>
<div style="background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.25);border-radius:6px;padding:.3rem .7rem;color:#22c55e;white-space:nowrap">
<strong>4. NIS2 attivo</strong><br><span style="font-size:.7rem">fino a license_expires_at</span>
</div>
</div>
<pre style="margin-top:1rem;margin-bottom:0;font-size:.78rem"><span class="cm"># Crea licenza in 1 chiamata:</span>
curl -X POST https://nis2.agile.software/api/invites/create \
-H <span class="sv">"X-API-Key: nis2_mktg_8c8bd38e78fccb9faa749d8601051f42"</span> \
-H <span class="sv">"Content-Type: application/json"</span> \
-d <span class="sv">'{"plan":"professional","duration_months":12,"label":"Ordine #123","channel":"ecommerce","max_users_per_org":10,"price_eur":990}'</span>
<span class="cm"># Risposta → prendi invites[0].token (inv_xxx) e invites[0].invite_url</span>
<span class="cm"># Consegna invite_url al cliente — lui clicca e si attiva in autonomia</span></pre>
</div>
<!-- ── Q1 ── --> <!-- ── Q1 ── -->
<h2><span class="num">1</span> API di creazione licenza</h2> <h2><span class="num">1</span> API di creazione licenza</h2>
<div class="answer-box highlight"> <div class="answer-box highlight">