Passa al contenuto principale

Architettura del sistema

Canopy è un'applicazione web costruita da quattro componenti principali. Questa pagina descrive la pila tecnologica, spiega le scelte di design e illustra i moduli opzionali che è possibile aggiungere per estendere la piattaforma.

La pila a quattro componenti

Browser (React SPA)

│ /api/* (HTTP proxy)

Edge Nginx ──────────────────────────► /drawio/* (self-hosted DrawIO)

│ proxy_pass :8000

FastAPI Backend (Python 3.12, uvicorn)
├── SQLAlchemy 2 (async, asyncpg)
├── Alembic migrations
├── JWT auth (HS256) + bcrypt
├── SSE event stream
└── Rate limiting (slowapi)


PostgreSQL 18

React SPA (frontend)

L'intera interfaccia utente è un'applicazione a pagina singola costruita con React 18, MUI 6 e React Router 7. Vite gestisce la compilazione; tutte le pagine a livello di rotta utilizzano importazioni lazy() per la divisione del codice, in modo che venga caricato solo il codice della pagina corrente. La SPA comunica esclusivamente con il backend FastAPI tramite il prefisso /api/ — non vi è accesso diretto al database dal frontend.

Librerie frontend principali: AG Grid (tabella inventario), Recharts (grafici), bpmn-js (editing BPMN), TipTap (testo ricco), @dnd-kit (drag-and-drop), React Flow (diagrammi architetturali).

Backend FastAPI

Il backend gestisce tutta la logica di business, l'accesso ai dati, l'applicazione dei permessi e gli eventi in tempo reale. È strutturato come una raccolta di router (uno per dominio) che si montano tutti sotto /api/v1/. Ogni gestore di rotta è async, utilizzando la sessione asincrona di SQLAlchemy con il driver asyncpg.

L'autenticazione utilizza token JWT (HS256, memorizzati nel sessionStorage del browser). Bcrypt gestisce l'hashing delle password. I valori sensibili memorizzati nel database (segreti SSO, password SMTP) vengono cifrati con cifratura simmetrica Fernet prima della scrittura e decifrati in lettura.

PostgreSQL

Tutti i dati dell'applicazione risiedono in PostgreSQL. Lo schema è gestito da Alembic; l'applicazione esegue alembic upgrade head ad ogni avvio, quindi le migrazioni dello schema vengono applicate automaticamente all'aggiornamento. Le tabelle utilizzano chiavi primarie UUID in tutto il sistema.

Non c'è Redis, nessuna coda messaggi e nessuna cache esterna. Gli aggiornamenti in tempo reale utilizzano Server-Sent Events (SSE) tramite una connessione HTTP a lunga durata direttamente dal backend.

Nginx di bordo

Un contenitore Nginx leggero si trova davanti ai contenitori React e FastAPI. Serve la React SPA compilata, fa da proxy per /api/* verso il backend (con intestazioni appropriate per SSE), serve il DrawIO self-hosted su /drawio/* e applica intestazioni di sicurezza (CSP, HSTS, X-Frame-Options, ecc.). In produzione questo è l'unico contenitore esposto alla rete.

Moduli opzionali

Ollama (suggerimenti AI)

Aggiungere --profile ai a Docker Compose per avviare un contenitore Ollama incluso insieme alla pila principale. La pipeline di suggerimenti AI del backend chiama l'API HTTP di Ollama per generare descrizioni delle schede utilizzando un LLM in esecuzione locale. Il modello viene eseguito interamente all'interno della propria infrastruttura.

È anche possibile puntare Canopy verso qualsiasi provider esterno compatibile con Ollama (OpenAI, Anthropic tramite un proxy, ecc.) impostando AI_PROVIDER_URL.

Server MCP

Aggiungere --profile mcp per avviare il server del Protocollo di Contesto del Modello. Questo espone i dati di Canopy agli assistenti AI (Claude Desktop, VS Code Copilot, ecc.) come un insieme di strumenti — query di sola lettura per impostazione predefinita, con strumenti di scrittura opzionali protetti da limiti di dimensione per chiamata e da un flusso di conferma.

Il server MCP si autentica tramite OAuth 2.1 delegato al provider SSO di Canopy, quindi gli utenti vedono gli stessi permessi tramite gli strumenti MCP come nell'interfaccia web.

Flusso di dati: il salvataggio di una scheda

Per rendere concreta l'architettura, ecco cosa accade quando un utente salva una scheda:

  1. React chiama PATCH /api/v1/cards/{id} con il contenuto aggiornato
  2. Nginx fa da proxy della richiesta verso il backend FastAPI
  3. Il gestore della rotta valida il JWT, verifica il permesso inventory.edit dell'utente e valida il corpo della richiesta con uno schema Pydantic
  4. SQLAlchemy scrive la riga aggiornata in PostgreSQL
  5. Il motore di calcolo esegue le formule attive per questo tipo di scheda
  6. Il punteggio di qualità dei dati viene ricalcolato dai pesi dei campi
  7. Un evento viene pubblicato sul bus SSE in memoria
  8. Qualsiasi browser connesso riceve l'evento e aggiorna l'interfaccia utente in tempo reale

Porte e rete

ServizioPorta internaEsposto all'host
Nginx (bordo)80HOST_PORT (predefinito 8920)
FastAPI8000No — solo interno
React (solo sviluppo)5173Sì in sviluppo, servito via Nginx in produzione
PostgreSQL5432No — solo interno
Ollama (opzionale)11434No — solo interno
Server MCP (opzionale)8001Tramite il prefisso /mcp/ di Nginx

Vedere anche