Hop til hovedindhold

Systemarkitektur

Canopy er en webapplikation bygget på fire hovedkomponenter. Denne side beskriver stakken, forklarer designvalgene og dækker de valgfrie moduler, du kan tilføje for at udvide platformen.

Den firekomponent-stak

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)

Hele brugergrænsefladen er en enkeltsidesapplikation bygget med React 18, MUI 6 og React Router 7. Vite håndterer byggeprocessen; alle sider på ruteniveau bruger lazy()-imports til kodeopdeling, så kun koden til den aktuelle side indlæses. SPA'en kommunikerer udelukkende med FastAPI-backenden via /api/-præfikset — der er ingen direkte databaseadgang fra frontend.

Centrale frontend-biblioteker: AG Grid (inventartabel), Recharts (diagrammer), bpmn-js (BPMN-redigering), TipTap (formateret tekst), @dnd-kit (træk-og-slip), React Flow (arkitekturdiagrammer).

FastAPI-backend

Backenden håndterer al forretningslogik, dataadgang, håndhævelse af tilladelser og realtidshændelser. Den er struktureret som en samling routere (én pr. domæne), der alle monteres under /api/v1/. Enhver routehandler er async og bruger SQLAlchemy's asynkrone session med asyncpg-driveren.

Autentifikation bruger JWT-tokens (HS256, gemt i browserens sessionStorage). Bcrypt håndterer adgangskode-hashing. Følsomme værdier gemt i databasen (SSO-hemmeligheder, SMTP-adgangskoder) krypteres med Fernet symmetrisk kryptering før skrivning og dekrypteres ved læsning.

PostgreSQL

Alle applikationsdata ligger i PostgreSQL. Skemaet administreres af Alembic; applikationen kører alembic upgrade head ved hver opstart, så skemamigreringe anvendes automatisk ved opdatering. Tabeller bruger UUID som primærnøgler overalt.

Der er ingen Redis, ingen meddelelses­kø og ingen ekstern cache. Realtidsopdateringer bruger Server-Sent Events (SSE) over en lang-polling HTTP-forbindelse direkte fra backenden.

Edge Nginx

En slank Nginx-container sidder foran React- og FastAPI-containerne. Den leverer den kompilerede React SPA, proxier /api/* til backenden (med SSE-egnede headere), leverer den selvhostede DrawIO på /drawio/* og anvender sikkerhedsheadere (CSP, HSTS, X-Frame-Options osv.). I produktion er dette den eneste container, der er eksponeret for netværket.

Valgfrie moduler

Ollama (AI-forslag)

Tilføj --profile ai til Docker Compose for at starte en medfølgende Ollama-container ved siden af hoveds­takken. Backendenes AI-forslagspipeline kalder Ollama HTTP API'et for at generere kortbeskrivelser ved hjælp af en lokalt kørende LLM. Modellen kører udelukkende inden for din infrastruktur.

Du kan også pege Canopy mod en ekstern udbyder, der er kompatibel med Ollama (OpenAI, Anthropic via en proxy osv.), ved at indstille AI_PROVIDER_URL.

MCP-server

Tilføj --profile mcp for at starte Model Context Protocol-serveren. Dette eksponerer Canopys data for AI-assistenter (Claude Desktop, VS Code Copilot osv.) som et sæt værktøjer — som standard kun til læsning, med opt-in skriveværktøjer beskyttet af størrelsesgrænser pr. kald og et bekræftelsesflow.

MCP-serveren autentificerer via OAuth 2.1 delegeret til Canopys SSO-udbyder, så brugere ser de samme tilladelser via MCP-værktøjer som i webgrænsefladen.

Dataflow: gemning af et kort

For at gøre arkitekturen konkret er her, hvad der sker, når en bruger gemmer et kort:

  1. React kalder PATCH /api/v1/cards/{id} med den opdaterede nyttelast
  2. Nginx proxier anmodningen til FastAPI-backenden
  3. Routehandleren validerer JWT, kontrollerer brugerens inventory.edit-tilladelse og validerer anmodningskroppen med et Pydantic-skema
  4. SQLAlchemy skriver den opdaterede række til PostgreSQL
  5. Beregnings­motoren kører eventuelle aktive formler for denne korttype
  6. Datakvalitetsscoren genberegnes ud fra felternes vægte
  7. En hændelse publiceres til den hukommelsesbaserede SSE-bus
  8. Alle tilsluttede browsere modtager hændelsen og opdaterer brugergrænsefladen i realtid

Porte og netværk

TjenesteIntern portEksponeret for vært
Nginx (edge)80HOST_PORT (standard 8920)
FastAPI8000Nej — kun intern
React (kun dev)5173Ja i dev, leveret via Nginx i prod
PostgreSQL5432Nej — kun intern
Ollama (valgfri)11434Nej — kun intern
MCP-server (valgfri)8001Via Nginx /mcp/-præfiks

Se også