# 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`
