- InviteController: CRUD inviti (gen, list, show, revoke, rigenera, validate) - Token inv_* sha256-hashed, one-shot o multi-use, canali, scadenza - ServicesController::provision() accetta invite_token al posto di X-Provision-Secret Piano e durata forzati dall'invito, markUsed() chiaamto dopo provisioning riuscito - index.php: routing /api/invites/* aggiunto (controller + action map) - integrazioniext.html: nuovo tab "Inviti & Licenze" con flow completo, endpoints, esempi curl/php, guida lg231 aggiornata con sezione provisioning automatico Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
59 lines
2.6 KiB
SQL
59 lines
2.6 KiB
SQL
-- ============================================================
|
|
-- NIS2 Agile — Migration 012: Sistema Inviti / Licenze
|
|
-- Inviti a tempo per provisioning automatico da e-commerce,
|
|
-- lg231 e altri sistemi Agile partner.
|
|
-- ============================================================
|
|
|
|
USE nis2_agile_db;
|
|
|
|
CREATE TABLE IF NOT EXISTS invites (
|
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
|
|
-- Token (visibile al chiamante, formato: inv_XXXX...)
|
|
token_prefix VARCHAR(16) NOT NULL, -- es: inv_a1b2c3
|
|
token_hash VARCHAR(64) NOT NULL UNIQUE, -- SHA-256 del token completo
|
|
|
|
-- Configurazione licenza
|
|
plan ENUM('essentials','professional','enterprise') NOT NULL DEFAULT 'professional',
|
|
duration_months TINYINT UNSIGNED NOT NULL DEFAULT 12,
|
|
label VARCHAR(255) NULL, -- es: "lg231 Partner Q1 2026"
|
|
notes TEXT NULL,
|
|
|
|
-- Limiti utilizzo
|
|
max_uses TINYINT UNSIGNED NOT NULL DEFAULT 1, -- 1 = one-shot, N = multi-use
|
|
used_count TINYINT UNSIGNED NOT NULL DEFAULT 0,
|
|
|
|
-- Scadenza invito (indipendente dalla scadenza licenza)
|
|
expires_at DATETIME NOT NULL,
|
|
|
|
-- Canale di distribuzione
|
|
channel VARCHAR(64) NULL, -- 'lg231', 'ecommerce', 'direct', 'manual'
|
|
issued_to VARCHAR(255) NULL, -- email o nome destinatario
|
|
issued_by INT UNSIGNED NULL, -- user_id NIS2 admin che ha generato
|
|
|
|
-- Utilizzo
|
|
status ENUM('pending','used','expired','revoked') NOT NULL DEFAULT 'pending',
|
|
used_at DATETIME NULL,
|
|
used_by_org_id INT UNSIGNED NULL, -- org creata dall'invito
|
|
used_by_ip VARCHAR(45) NULL,
|
|
|
|
-- Restrizioni facoltative
|
|
restrict_vat VARCHAR(11) NULL, -- solo per questa P.IVA
|
|
restrict_email VARCHAR(255) NULL, -- solo per questo admin email
|
|
|
|
-- Metadata extra (JSON)
|
|
metadata JSON NULL, -- dati extra (es: lg231 order_id)
|
|
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
|
PRIMARY KEY (id),
|
|
INDEX idx_invites_token_hash (token_hash),
|
|
INDEX idx_invites_status (status),
|
|
INDEX idx_invites_channel (channel),
|
|
INDEX idx_invites_expires (expires_at),
|
|
INDEX idx_invites_org (used_by_org_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
|
|
SELECT 'Migration 012 invites completata.' AS stato;
|