- i18n.js: sistema traduzioni IT/EN con ~150 chiavi, localStorage, data-i18n - help.js: help contestuale per 10 pagine con riferimenti NIS2 - architecture.html: descrizione architettura sistema completa - common.js: language toggle sidebar (IT/EN), link Architettura, icone - Integrato i18n + help in tutte le 14 pagine app + 3 admin Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1089 lines
48 KiB
HTML
1089 lines
48 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="it">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Architettura - NIS2 Agile</title>
|
|
<link rel="stylesheet" href="css/style.css">
|
|
<style>
|
|
/* ── Architecture Page Styles ────────────────────────────────── */
|
|
|
|
.arch-section {
|
|
margin-bottom: 32px;
|
|
}
|
|
|
|
.arch-section:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.arch-section .card-header h3 {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.arch-section .card-header h3 .section-number {
|
|
width: 28px;
|
|
height: 28px;
|
|
border-radius: 50%;
|
|
background: var(--primary);
|
|
color: #fff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.75rem;
|
|
font-weight: 700;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Overview text */
|
|
.arch-overview p {
|
|
font-size: 0.9375rem;
|
|
line-height: 1.75;
|
|
color: var(--gray-600);
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.arch-overview p:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.arch-highlight {
|
|
display: inline;
|
|
font-weight: 600;
|
|
color: var(--gray-800);
|
|
}
|
|
|
|
/* Tech Stack Grid */
|
|
.tech-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
gap: 16px;
|
|
}
|
|
|
|
.tech-item {
|
|
background: var(--gray-50);
|
|
border: 1px solid var(--gray-200);
|
|
border-radius: var(--border-radius);
|
|
padding: 20px;
|
|
text-align: center;
|
|
transition: all var(--transition-fast);
|
|
}
|
|
|
|
.tech-item:hover {
|
|
border-color: var(--primary);
|
|
background: var(--primary-bg);
|
|
transform: translateY(-2px);
|
|
box-shadow: var(--card-shadow);
|
|
}
|
|
|
|
.tech-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: var(--border-radius);
|
|
margin: 0 auto 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.25rem;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.tech-icon.frontend { background: #fef3c7; color: #d97706; }
|
|
.tech-icon.backend { background: #e0e7ff; color: #4338ca; }
|
|
.tech-icon.database { background: #dbeafe; color: #1d4ed8; }
|
|
.tech-icon.server { background: #d1fae5; color: #059669; }
|
|
.tech-icon.container { background: #e0f2fe; color: #0284c7; }
|
|
.tech-icon.ai { background: #fce7f3; color: #be185d; }
|
|
.tech-icon.auth { background: #fef9c3; color: #a16207; }
|
|
|
|
.tech-item h4 {
|
|
font-size: 0.8125rem;
|
|
font-weight: 700;
|
|
color: var(--gray-800);
|
|
margin-bottom: 6px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.03em;
|
|
}
|
|
|
|
.tech-item p {
|
|
font-size: 0.8125rem;
|
|
color: var(--gray-500);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* Architecture Diagram */
|
|
.arch-diagram {
|
|
padding: 32px 16px;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.arch-flow {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0;
|
|
min-width: 700px;
|
|
flex-wrap: nowrap;
|
|
}
|
|
|
|
.arch-box {
|
|
padding: 16px 20px;
|
|
border-radius: var(--border-radius);
|
|
text-align: center;
|
|
font-size: 0.8125rem;
|
|
font-weight: 600;
|
|
min-width: 120px;
|
|
position: relative;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.arch-box .arch-box-sub {
|
|
font-size: 0.7rem;
|
|
font-weight: 400;
|
|
margin-top: 4px;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.arch-box.client {
|
|
background: #dbeafe;
|
|
border: 2px solid #3b82f6;
|
|
color: #1e40af;
|
|
}
|
|
|
|
.arch-box.nginx {
|
|
background: #d1fae5;
|
|
border: 2px solid #10b981;
|
|
color: #065f46;
|
|
}
|
|
|
|
.arch-box.php {
|
|
background: #e0e7ff;
|
|
border: 2px solid #6366f1;
|
|
color: #3730a3;
|
|
}
|
|
|
|
.arch-box.controller {
|
|
background: #fef3c7;
|
|
border: 2px solid #f59e0b;
|
|
color: #92400e;
|
|
}
|
|
|
|
.arch-box.database {
|
|
background: #fce7f3;
|
|
border: 2px solid #ec4899;
|
|
color: #9d174d;
|
|
}
|
|
|
|
.arch-arrow {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
padding: 0 4px;
|
|
}
|
|
|
|
.arch-arrow svg {
|
|
width: 32px;
|
|
height: 20px;
|
|
fill: var(--gray-400);
|
|
}
|
|
|
|
/* Routing sub-flow */
|
|
.arch-routing {
|
|
margin-top: 24px;
|
|
padding: 20px 24px;
|
|
background: var(--gray-50);
|
|
border: 1px solid var(--gray-200);
|
|
border-radius: var(--border-radius);
|
|
}
|
|
|
|
.arch-routing h4 {
|
|
font-size: 0.8125rem;
|
|
font-weight: 700;
|
|
color: var(--gray-700);
|
|
margin-bottom: 12px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.03em;
|
|
}
|
|
|
|
.routing-flow {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.routing-step {
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--gray-300);
|
|
border-radius: var(--border-radius-sm);
|
|
padding: 8px 16px;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
color: var(--gray-700);
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
.routing-arrow {
|
|
padding: 0 8px;
|
|
color: var(--gray-400);
|
|
font-size: 1rem;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Directory Tree */
|
|
.dir-tree {
|
|
background: var(--gray-900);
|
|
color: #e2e8f0;
|
|
border-radius: var(--border-radius);
|
|
padding: 24px 28px;
|
|
font-family: var(--font-mono);
|
|
font-size: 0.8125rem;
|
|
line-height: 1.8;
|
|
overflow-x: auto;
|
|
white-space: pre;
|
|
}
|
|
|
|
.dir-tree .dir-name {
|
|
color: #60a5fa;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.dir-tree .dir-comment {
|
|
color: #6b7280;
|
|
}
|
|
|
|
.dir-tree .dir-file {
|
|
color: #a5b4fc;
|
|
}
|
|
|
|
/* Data Model */
|
|
.data-model {
|
|
overflow-x: auto;
|
|
padding: 24px 0;
|
|
}
|
|
|
|
.data-model-grid {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 24px;
|
|
min-width: 600px;
|
|
}
|
|
|
|
.data-model-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 12px;
|
|
flex-wrap: nowrap;
|
|
}
|
|
|
|
.data-entity {
|
|
background: var(--card-bg);
|
|
border: 2px solid var(--gray-300);
|
|
border-radius: var(--border-radius);
|
|
padding: 12px 18px;
|
|
text-align: center;
|
|
min-width: 130px;
|
|
flex-shrink: 0;
|
|
transition: all var(--transition-fast);
|
|
}
|
|
|
|
.data-entity:hover {
|
|
border-color: var(--primary);
|
|
box-shadow: var(--card-shadow);
|
|
}
|
|
|
|
.data-entity.primary-entity {
|
|
border-color: var(--primary);
|
|
background: var(--primary-bg);
|
|
}
|
|
|
|
.data-entity h5 {
|
|
font-size: 0.8rem;
|
|
font-weight: 700;
|
|
color: var(--gray-800);
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
.data-entity p {
|
|
font-size: 0.7rem;
|
|
color: var(--gray-500);
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.data-rel {
|
|
color: var(--gray-400);
|
|
font-size: 0.875rem;
|
|
flex-shrink: 0;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.data-children {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
justify-content: center;
|
|
padding: 0 16px;
|
|
}
|
|
|
|
.data-child {
|
|
background: var(--gray-50);
|
|
border: 1px solid var(--gray-200);
|
|
border-radius: var(--border-radius-sm);
|
|
padding: 8px 14px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
color: var(--gray-600);
|
|
font-family: var(--font-mono);
|
|
transition: all var(--transition-fast);
|
|
}
|
|
|
|
.data-child:hover {
|
|
border-color: var(--primary);
|
|
color: var(--primary);
|
|
background: var(--primary-bg);
|
|
}
|
|
|
|
.data-arrow-down {
|
|
display: flex;
|
|
justify-content: center;
|
|
padding: 4px 0;
|
|
color: var(--gray-400);
|
|
}
|
|
|
|
.data-note {
|
|
margin-top: 16px;
|
|
padding: 12px 16px;
|
|
background: var(--info-bg);
|
|
border-radius: var(--border-radius-sm);
|
|
border-left: 3px solid var(--primary);
|
|
font-size: 0.8125rem;
|
|
color: var(--gray-600);
|
|
}
|
|
|
|
.data-note code {
|
|
background: rgba(26, 115, 232, 0.12);
|
|
padding: 2px 6px;
|
|
border-radius: 3px;
|
|
font-family: var(--font-mono);
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
color: var(--primary);
|
|
}
|
|
|
|
/* Security List */
|
|
.security-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
gap: 16px;
|
|
}
|
|
|
|
.security-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
padding: 16px;
|
|
background: var(--gray-50);
|
|
border-radius: var(--border-radius);
|
|
border: 1px solid var(--gray-200);
|
|
}
|
|
|
|
.security-icon {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
background: var(--secondary-bg);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.security-icon svg {
|
|
width: 18px;
|
|
height: 18px;
|
|
fill: #15803d;
|
|
}
|
|
|
|
.security-item h4 {
|
|
font-size: 0.8125rem;
|
|
font-weight: 700;
|
|
color: var(--gray-800);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.security-item p {
|
|
font-size: 0.8rem;
|
|
color: var(--gray-500);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* Module cards */
|
|
.module-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
gap: 16px;
|
|
}
|
|
|
|
.module-card {
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--gray-200);
|
|
border-radius: var(--border-radius);
|
|
padding: 20px;
|
|
transition: all var(--transition-fast);
|
|
border-top: 3px solid var(--primary);
|
|
}
|
|
|
|
.module-card:hover {
|
|
box-shadow: var(--card-shadow-hover);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.module-card:nth-child(2) { border-top-color: #f59e0b; }
|
|
.module-card:nth-child(3) { border-top-color: var(--danger); }
|
|
.module-card:nth-child(4) { border-top-color: #8b5cf6; }
|
|
.module-card:nth-child(5) { border-top-color: #06b6d4; }
|
|
.module-card:nth-child(6) { border-top-color: #10b981; }
|
|
.module-card:nth-child(7) { border-top-color: #ec4899; }
|
|
.module-card:nth-child(8) { border-top-color: #f97316; }
|
|
.module-card:nth-child(9) { border-top-color: var(--gray-600); }
|
|
|
|
.module-card h4 {
|
|
font-size: 0.9375rem;
|
|
font-weight: 700;
|
|
color: var(--gray-800);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.module-card .module-ref {
|
|
font-size: 0.7rem;
|
|
font-weight: 600;
|
|
color: var(--primary);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.03em;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.module-card p {
|
|
font-size: 0.8125rem;
|
|
color: var(--gray-500);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* Integration items */
|
|
.integration-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
}
|
|
|
|
.integration-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 16px;
|
|
padding: 20px;
|
|
background: var(--gray-50);
|
|
border-radius: var(--border-radius);
|
|
border: 1px solid var(--gray-200);
|
|
}
|
|
|
|
.integration-icon {
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: var(--border-radius);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
font-size: 1.125rem;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.integration-icon.ext { background: #e0e7ff; color: #4338ca; }
|
|
.integration-icon.visura { background: #fef3c7; color: #d97706; }
|
|
.integration-icon.claude { background: #fce7f3; color: #be185d; }
|
|
|
|
.integration-item h4 {
|
|
font-size: 0.9375rem;
|
|
font-weight: 700;
|
|
color: var(--gray-800);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.integration-item p {
|
|
font-size: 0.8125rem;
|
|
color: var(--gray-500);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* Deploy items */
|
|
.deploy-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
|
gap: 16px;
|
|
}
|
|
|
|
.deploy-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 14px;
|
|
padding: 20px;
|
|
background: var(--gray-50);
|
|
border-radius: var(--border-radius);
|
|
border: 1px solid var(--gray-200);
|
|
}
|
|
|
|
.deploy-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: var(--border-radius);
|
|
background: var(--gray-800);
|
|
color: #fff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.deploy-icon svg {
|
|
width: 20px;
|
|
height: 20px;
|
|
fill: currentColor;
|
|
}
|
|
|
|
.deploy-item h4 {
|
|
font-size: 0.875rem;
|
|
font-weight: 700;
|
|
color: var(--gray-800);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.deploy-item p {
|
|
font-size: 0.8125rem;
|
|
color: var(--gray-500);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* RBAC role badges */
|
|
.role-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.role-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 5px 12px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
border-radius: 100px;
|
|
font-family: var(--font-mono);
|
|
}
|
|
|
|
.role-badge.sa { background: var(--danger-bg); color: var(--danger); }
|
|
.role-badge.oa { background: #fef3c7; color: #92400e; }
|
|
.role-badge.cm { background: var(--primary-bg); color: var(--primary); }
|
|
.role-badge.bm { background: #e0e7ff; color: #4338ca; }
|
|
.role-badge.au { background: var(--secondary-bg); color: #15803d; }
|
|
.role-badge.em { background: var(--gray-100); color: var(--gray-600); }
|
|
|
|
/* Responsive overrides */
|
|
@media (max-width: 768px) {
|
|
.tech-grid {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
.module-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
.security-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
.deploy-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
.arch-flow {
|
|
min-width: 580px;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.tech-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="app-layout">
|
|
<!-- Sidebar -->
|
|
<aside class="sidebar" id="sidebar"></aside>
|
|
|
|
<!-- Main Content -->
|
|
<main class="main-content">
|
|
<header class="content-header">
|
|
<h2>Architettura del Sistema</h2>
|
|
<div class="content-header-actions">
|
|
<span class="badge badge-neutral">v1.0</span>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="content-body">
|
|
|
|
<!-- ═══ 1. Panoramica ═══════════════════════════════════ -->
|
|
<div class="arch-section">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><span class="section-number">1</span> Panoramica</h3>
|
|
</div>
|
|
<div class="card-body arch-overview">
|
|
<p>
|
|
<span class="arch-highlight">NIS2 Agile</span> e' una piattaforma di gestione della conformita' progettata
|
|
per la <span class="arch-highlight">Direttiva NIS2 dell'Unione Europea (2022/2555)</span>. La piattaforma
|
|
guida le organizzazioni italiane attraverso tutti i requisiti normativi, fornendo strumenti strutturati per
|
|
la valutazione del rischio, la gestione degli incidenti, la generazione di policy e il monitoraggio della conformita'.
|
|
</p>
|
|
<p>
|
|
L'applicazione e' costruita specificamente per le organizzazioni italiane classificate come
|
|
<span class="badge badge-danger">Soggetti Essenziali</span> e
|
|
<span class="badge badge-warning">Soggetti Importanti</span>
|
|
ai sensi della direttiva, con supporto completo per i requisiti degli Articoli 20-25.
|
|
</p>
|
|
<p>
|
|
L'architettura adotta un modello <span class="arch-highlight">multi-tenant SaaS</span> con controllo degli
|
|
accessi basato sui ruoli (RBAC), consentendo a piu' organizzazioni di operare in modo sicuro e isolato
|
|
sulla stessa istanza della piattaforma.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ 2. Stack Tecnologico ════════════════════════════ -->
|
|
<div class="arch-section">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><span class="section-number">2</span> Stack Tecnologico</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="tech-grid">
|
|
<div class="tech-item">
|
|
<div class="tech-icon frontend">FE</div>
|
|
<h4>Frontend</h4>
|
|
<p>HTML5, CSS3 (Custom Design System), Vanilla JavaScript (no framework)</p>
|
|
</div>
|
|
<div class="tech-item">
|
|
<div class="tech-icon backend">BE</div>
|
|
<h4>Backend</h4>
|
|
<p>PHP 8.4 (vanilla, no framework), Front Controller pattern</p>
|
|
</div>
|
|
<div class="tech-item">
|
|
<div class="tech-icon database">DB</div>
|
|
<h4>Database</h4>
|
|
<p>MySQL 8.0 con PDO (prepared statements)</p>
|
|
</div>
|
|
<div class="tech-item">
|
|
<div class="tech-icon server">WS</div>
|
|
<h4>Web Server</h4>
|
|
<p>Nginx 1.27 Alpine + PHP-FPM 8.4</p>
|
|
</div>
|
|
<div class="tech-item">
|
|
<div class="tech-icon container">DC</div>
|
|
<h4>Containerizzazione</h4>
|
|
<p>Docker Compose (multi-container orchestration)</p>
|
|
</div>
|
|
<div class="tech-item">
|
|
<div class="tech-icon ai">AI</div>
|
|
<h4>Intelligenza Artificiale</h4>
|
|
<p>Claude API (Anthropic) per generazione policy e analisi</p>
|
|
</div>
|
|
<div class="tech-item">
|
|
<div class="tech-icon auth">JW</div>
|
|
<h4>Autenticazione</h4>
|
|
<p>JWT HS256 (access token 2h + refresh token 7d)</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ 3. Architettura dell'Applicazione ═══════════════ -->
|
|
<div class="arch-section">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><span class="section-number">3</span> Architettura dell'Applicazione</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="arch-diagram">
|
|
<div class="arch-flow">
|
|
<div class="arch-box client">
|
|
Client Browser
|
|
<div class="arch-box-sub">HTML/CSS/JS</div>
|
|
</div>
|
|
<div class="arch-arrow">
|
|
<svg viewBox="0 0 32 20"><path d="M0 10h26l-6-6 1.5-1.5L30 10l-8.5 7.5L20 16l6-6H0z"/></svg>
|
|
</div>
|
|
<div class="arch-box nginx">
|
|
Nginx
|
|
<div class="arch-box-sub">Reverse Proxy</div>
|
|
</div>
|
|
<div class="arch-arrow">
|
|
<svg viewBox="0 0 32 20"><path d="M0 10h26l-6-6 1.5-1.5L30 10l-8.5 7.5L20 16l6-6H0z"/></svg>
|
|
</div>
|
|
<div class="arch-box php">
|
|
PHP-FPM
|
|
<div class="arch-box-sub">Front Controller</div>
|
|
</div>
|
|
<div class="arch-arrow">
|
|
<svg viewBox="0 0 32 20"><path d="M0 10h26l-6-6 1.5-1.5L30 10l-8.5 7.5L20 16l6-6H0z"/></svg>
|
|
</div>
|
|
<div class="arch-box controller">
|
|
Controllers
|
|
<div class="arch-box-sub">Business Logic</div>
|
|
</div>
|
|
<div class="arch-arrow">
|
|
<svg viewBox="0 0 32 20"><path d="M0 10h26l-6-6 1.5-1.5L30 10l-8.5 7.5L20 16l6-6H0z"/></svg>
|
|
</div>
|
|
<div class="arch-box database">
|
|
MySQL 8.0
|
|
<div class="arch-box-sub">Database</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="arch-routing">
|
|
<h4>Flusso di Routing</h4>
|
|
<div class="routing-flow">
|
|
<div class="routing-step">index.php</div>
|
|
<div class="routing-arrow">→</div>
|
|
<div class="routing-step">Router</div>
|
|
<div class="routing-arrow">→</div>
|
|
<div class="routing-step">Controller</div>
|
|
<div class="routing-arrow">→</div>
|
|
<div class="routing-step">Service</div>
|
|
<div class="routing-arrow">→</div>
|
|
<div class="routing-step">JSON Response</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ 4. Struttura del Progetto ═══════════════════════ -->
|
|
<div class="arch-section">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><span class="section-number">4</span> Struttura del Progetto</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="dir-tree"><span class="dir-name">nis2-agile/</span>
|
|
<span class="dir-name">├── application/</span>
|
|
<span class="dir-name">│ ├── controllers/</span> <span class="dir-comment"># Controller PHP (BaseController, Auth, Assessment, etc.)</span>
|
|
<span class="dir-name">│ ├── services/</span> <span class="dir-comment"># Servizi business logic (VisuraService, AI)</span>
|
|
<span class="dir-name">│ ├── models/</span> <span class="dir-comment"># Modelli dati</span>
|
|
│ └── <span class="dir-name">config/</span> <span class="dir-comment"># Configurazione e Database class</span>
|
|
<span class="dir-name">├── public/</span> <span class="dir-comment"># Web root (document root Nginx)</span>
|
|
│ ├── <span class="dir-file">index.php</span> <span class="dir-comment"># Front Controller + Router</span>
|
|
│ ├── <span class="dir-name">css/</span> <span class="dir-comment"># Foglio di stile principale</span>
|
|
│ ├── <span class="dir-name">js/</span> <span class="dir-comment"># API client, Common utilities, i18n, Help</span>
|
|
│ └── <span class="dir-file">*.html</span> <span class="dir-comment"># Pagine frontend (SPA-like)</span>
|
|
<span class="dir-name">├── docker/</span> <span class="dir-comment"># Docker Compose, Dockerfile, Nginx config</span>
|
|
└── <span class="dir-name">docs/</span>
|
|
├── <span class="dir-name">sql/</span> <span class="dir-comment"># Migrazioni database (001-004)</span>
|
|
└── <span class="dir-name">questionnaire/</span> <span class="dir-comment"># Questionario NIS2 (JSON)</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ 5. Modello Dati ═════════════════════════════════ -->
|
|
<div class="arch-section">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><span class="section-number">5</span> Modello Dati</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="data-model">
|
|
<div class="data-model-grid">
|
|
<!-- Core relationship: users <-> user_organizations <-> organizations -->
|
|
<div class="data-model-row">
|
|
<div class="data-entity primary-entity">
|
|
<h5>users</h5>
|
|
<p>Utenti piattaforma</p>
|
|
</div>
|
|
<div class="data-rel">↔</div>
|
|
<div class="data-entity">
|
|
<h5>user_organizations</h5>
|
|
<p>Associazione N:M</p>
|
|
</div>
|
|
<div class="data-rel">↔</div>
|
|
<div class="data-entity primary-entity">
|
|
<h5>organizations</h5>
|
|
<p>Organizzazioni tenant</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Arrow down from organizations -->
|
|
<div class="data-arrow-down">
|
|
<svg width="24" height="32" viewBox="0 0 24 32" fill="var(--gray-400)">
|
|
<path d="M12 0v26l-6-6-1.5 1.5L12 30l7.5-8.5L18 20l-6 6V0z"/>
|
|
</svg>
|
|
</div>
|
|
|
|
<!-- Children tables -->
|
|
<div class="data-children">
|
|
<div class="data-child">assessments</div>
|
|
<div class="data-child">assessment_answers</div>
|
|
<div class="data-child">risks</div>
|
|
<div class="data-child">incidents</div>
|
|
<div class="data-child">policies</div>
|
|
<div class="data-child">suppliers</div>
|
|
<div class="data-child">training_courses</div>
|
|
<div class="data-child">assets</div>
|
|
<div class="data-child">compliance_controls</div>
|
|
<div class="data-child">non_conformities</div>
|
|
<div class="data-child">capa_actions</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="data-note">
|
|
<strong>Multi-tenancy:</strong> Tutte le tabelle operative contengono la chiave esterna
|
|
<code>organization_id</code> che garantisce l'isolamento completo dei dati tra le diverse
|
|
organizzazioni all'interno della stessa istanza.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ 6. Sicurezza ════════════════════════════════════ -->
|
|
<div class="arch-section">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><span class="section-number">6</span> Sicurezza</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="security-grid">
|
|
<div class="security-item">
|
|
<div class="security-icon">
|
|
<svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"/></svg>
|
|
</div>
|
|
<div>
|
|
<h4>Autenticazione JWT</h4>
|
|
<p>Token di accesso HS256 (2h) con refresh token (7d) per sessioni sicure e scalabili.</p>
|
|
</div>
|
|
</div>
|
|
<div class="security-item">
|
|
<div class="security-icon">
|
|
<svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M4.083 9h1.946c.089-1.546.383-2.97.837-4.118A6.004 6.004 0 004.083 9zM10 2a8 8 0 100 16 8 8 0 000-16zm0 2c-.076 0-.232.032-.465.262-.238.234-.497.623-.737 1.182-.389.907-.673 2.142-.766 3.556h3.936c-.093-1.414-.377-2.649-.766-3.556-.24-.56-.5-.948-.737-1.182C10.232 4.032 10.076 4 10 4zm3.971 5c-.089-1.546-.383-2.97-.837-4.118A6.004 6.004 0 0115.917 9h-1.946zm-2.003 2H8.032c.093 1.414.377 2.649.766 3.556.24.56.5.948.737 1.182.233.23.389.262.465.262.076 0 .232-.032.465-.262.238-.234.497-.623.737-1.182.389-.907.673-2.142.766-3.556zm1.166 4.118c.454-1.147.748-2.572.837-4.118h1.946a6.004 6.004 0 01-2.783 4.118zm-6.268 0C6.412 13.97 6.118 12.546 6.03 11H4.083a6.004 6.004 0 002.783 4.118z" clip-rule="evenodd"/></svg>
|
|
</div>
|
|
<div>
|
|
<h4>Configurazione CORS</h4>
|
|
<p>Header CORS configurati per controllare le origini autorizzate delle richieste cross-origin.</p>
|
|
</div>
|
|
</div>
|
|
<div class="security-item">
|
|
<div class="security-icon">
|
|
<svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M2.166 4.999A11.954 11.954 0 0010 1.944 11.954 11.954 0 0017.834 5c.11.65.166 1.32.166 2.001 0 5.225-3.34 9.67-8 11.317C5.34 16.67 2 12.225 2 7c0-.682.057-1.35.166-2.001zm11.541 3.708a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>
|
|
</div>
|
|
<div>
|
|
<h4>SQL Injection Prevention</h4>
|
|
<p>Validazione input e prevenzione SQL injection tramite prepared statements PDO su tutte le query.</p>
|
|
</div>
|
|
</div>
|
|
<div class="security-item">
|
|
<div class="security-icon">
|
|
<svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M12.316 3.051a1 1 0 01.633 1.265l-4 12a1 1 0 11-1.898-.632l4-12a1 1 0 011.265-.633zM5.707 6.293a1 1 0 010 1.414L3.414 10l2.293 2.293a1 1 0 11-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0zm8.586 0a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 11-1.414-1.414L16.586 10l-2.293-2.293a1 1 0 010-1.414z" clip-rule="evenodd"/></svg>
|
|
</div>
|
|
<div>
|
|
<h4>Prevenzione XSS</h4>
|
|
<p>Escape sistematico di tutti gli output HTML tramite funzione <code>escapeHtml()</code> lato client.</p>
|
|
</div>
|
|
</div>
|
|
<div class="security-item">
|
|
<div class="security-icon">
|
|
<svg viewBox="0 0 20 20"><path d="M9 6a3 3 0 11-6 0 3 3 0 016 0zM17 6a3 3 0 11-6 0 3 3 0 016 0zM12.93 17c.046-.327.07-.66.07-1a6.97 6.97 0 00-1.5-4.33A5 5 0 0119 16v1h-6.07zM6 11a5 5 0 015 5v1H1v-1a5 5 0 015-5z"/></svg>
|
|
</div>
|
|
<div>
|
|
<h4>RBAC (Role-Based Access Control)</h4>
|
|
<p>Sei ruoli con permessi granulari per il controllo degli accessi alle risorse.</p>
|
|
<div class="role-list">
|
|
<span class="role-badge sa">super_admin</span>
|
|
<span class="role-badge oa">org_admin</span>
|
|
<span class="role-badge cm">compliance_manager</span>
|
|
<span class="role-badge bm">board_member</span>
|
|
<span class="role-badge au">auditor</span>
|
|
<span class="role-badge em">employee</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="security-item">
|
|
<div class="security-icon">
|
|
<svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/></svg>
|
|
</div>
|
|
<div>
|
|
<h4>Audit Logging</h4>
|
|
<p>Registrazione completa di tutte le operazioni sensibili con tracciamento utente, azione e timestamp.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ 7. Moduli Funzionali ════════════════════════════ -->
|
|
<div class="arch-section">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><span class="section-number">7</span> Moduli Funzionali</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="module-grid">
|
|
<div class="module-card">
|
|
<div class="module-ref">Art. 21 - Direttiva NIS2</div>
|
|
<h4>Gap Analysis</h4>
|
|
<p>Assessment di conformita' NIS2 su 10 categorie con punteggio automatico e analisi AI delle lacune.</p>
|
|
</div>
|
|
<div class="module-card">
|
|
<div class="module-ref">Art. 21.2.a</div>
|
|
<h4>Gestione Rischi</h4>
|
|
<p>Matrice del rischio, piani di trattamento, monitoraggio continuo e classificazione per impatto e probabilita'.</p>
|
|
</div>
|
|
<div class="module-card">
|
|
<div class="module-ref">Art. 23</div>
|
|
<h4>Gestione Incidenti</h4>
|
|
<p>Timeline di notifica NIS2 completa: pre-allarme 24h, notifica 72h, relazione finale 30 giorni.</p>
|
|
</div>
|
|
<div class="module-card">
|
|
<div class="module-ref">Art. 21</div>
|
|
<h4>Policy Management</h4>
|
|
<p>Generazione assistita da AI di policy di sicurezza con workflow di approvazione e versionamento.</p>
|
|
</div>
|
|
<div class="module-card">
|
|
<div class="module-ref">Art. 21.2.d</div>
|
|
<h4>Supply Chain</h4>
|
|
<p>Valutazione del rischio dei fornitori, monitoraggio della catena di approvvigionamento e classificazione.</p>
|
|
</div>
|
|
<div class="module-card">
|
|
<div class="module-ref">Art. 20.2</div>
|
|
<h4>Formazione</h4>
|
|
<p>Tracciamento della formazione obbligatoria per il management, corsi, completamento e scadenze.</p>
|
|
</div>
|
|
<div class="module-card">
|
|
<div class="module-ref">Art. 21.2.i</div>
|
|
<h4>Asset Management</h4>
|
|
<p>Inventario degli asset IT/OT, mappatura delle dipendenze e classificazione per criticita'.</p>
|
|
</div>
|
|
<div class="module-card">
|
|
<div class="module-ref">NCR / CAPA</div>
|
|
<h4>Non Conformita' e Azioni Correttive</h4>
|
|
<p>Tracciamento delle non conformita' con azioni correttive e preventive strutturate.</p>
|
|
</div>
|
|
<div class="module-card">
|
|
<div class="module-ref">Compliance</div>
|
|
<h4>Audit e Report</h4>
|
|
<p>Generazione di evidenze di conformita', report per l'autorita' competente e dashboard di monitoraggio.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ 8. Integrazione Esterna ═════════════════════════ -->
|
|
<div class="arch-section">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><span class="section-number">8</span> Integrazione Esterna</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="integration-list">
|
|
<div class="integration-item">
|
|
<div class="integration-icon ext">SG</div>
|
|
<div>
|
|
<h4>SistemiG.agile</h4>
|
|
<p>Predisposizione per la sincronizzazione delle non conformita' (NCR) tramite webhook e API REST.
|
|
Permette l'integrazione bidirezionale con il sistema di gestione qualita' SistemiG.agile per una
|
|
governance unificata.</p>
|
|
</div>
|
|
</div>
|
|
<div class="integration-item">
|
|
<div class="integration-icon visura">VC</div>
|
|
<div>
|
|
<h4>Visura Camerale</h4>
|
|
<p>Analisi automatizzata delle visure camerali tramite upload PDF. Estrazione intelligente dei dati
|
|
aziendali (ragione sociale, codice ATECO, sede, organi di amministrazione) mediante AI per la
|
|
classificazione NIS2 dell'organizzazione.</p>
|
|
</div>
|
|
</div>
|
|
<div class="integration-item">
|
|
<div class="integration-icon claude">AI</div>
|
|
<div>
|
|
<h4>Claude AI (Anthropic)</h4>
|
|
<p>Integrazione con l'API Claude di Anthropic per la generazione automatica di policy di sicurezza
|
|
personalizzate, l'analisi dei gap di conformita' e raccomandazioni contestuali basate sul profilo
|
|
dell'organizzazione.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ═══ 9. Deploy ═══════════════════════════════════════ -->
|
|
<div class="arch-section">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3><span class="section-number">9</span> Deploy</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="deploy-grid">
|
|
<div class="deploy-item">
|
|
<div class="deploy-icon">
|
|
<svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M2 5a2 2 0 012-2h12a2 2 0 012 2v2a2 2 0 01-2 2H4a2 2 0 01-2-2V5zm14 1a1 1 0 11-2 0 1 1 0 012 0zM2 13a2 2 0 012-2h12a2 2 0 012 2v2a2 2 0 01-2 2H4a2 2 0 01-2-2v-2zm14 1a1 1 0 11-2 0 1 1 0 012 0z" clip-rule="evenodd"/></svg>
|
|
</div>
|
|
<div>
|
|
<h4>Docker Compose</h4>
|
|
<p>Orchestrazione con 3 container: app (PHP-FPM), web (Nginx), db (MySQL).</p>
|
|
</div>
|
|
</div>
|
|
<div class="deploy-item">
|
|
<div class="deploy-icon">
|
|
<svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/></svg>
|
|
</div>
|
|
<div>
|
|
<h4>Produzione</h4>
|
|
<p>Hosting su Hetzner Cloud con infrastruttura europea per conformita' GDPR.</p>
|
|
</div>
|
|
</div>
|
|
<div class="deploy-item">
|
|
<div class="deploy-icon">
|
|
<svg viewBox="0 0 20 20"><path fill-rule="evenodd" d="M12.316 3.051a1 1 0 01.633 1.265l-4 12a1 1 0 11-1.898-.632l4-12a1 1 0 011.265-.633zM5.707 6.293a1 1 0 010 1.414L3.414 10l2.293 2.293a1 1 0 11-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0zm8.586 0a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 11-1.414-1.414L16.586 10l-2.293-2.293a1 1 0 010-1.414z" clip-rule="evenodd"/></svg>
|
|
</div>
|
|
<div>
|
|
<h4>Git Deployment</h4>
|
|
<p>Deploy basato su Git tramite Gitea con workflow di build e rilascio.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<script src="js/api.js"></script>
|
|
<script src="js/common.js"></script>
|
|
<script src="js/i18n.js"></script>
|
|
<script src="js/help.js"></script>
|
|
<script>
|
|
if (!checkAuth()) throw new Error('Not authenticated');
|
|
loadSidebar();
|
|
I18n.init();
|
|
</script>
|
|
</body>
|
|
</html>
|