Saltar al contenido principal

Arquitectura del sistema

Canopy es una aplicación web construida a partir de cuatro componentes principales. Esta página describe la pila tecnológica, explica las decisiones de diseño y cubre los módulos opcionales que puede añadir para ampliar la plataforma.

La pila de cuatro componentes

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)

Toda la interfaz de usuario es una aplicación de una sola página construida con React 18, MUI 6 y React Router 7. Vite gestiona la compilación; todas las páginas a nivel de ruta usan importaciones lazy() para la división de código, de modo que solo se carga el código de la página actual. La SPA se comunica exclusivamente con el backend FastAPI a través del prefijo /api/ — no hay acceso directo a la base de datos desde el frontend.

Bibliotecas frontend principales: AG Grid (tabla de inventario), Recharts (gráficos), bpmn-js (edición de BPMN), TipTap (texto enriquecido), @dnd-kit (arrastrar y soltar), React Flow (diagramas de arquitectura).

Backend FastAPI

El backend gestiona toda la lógica de negocio, el acceso a datos, la aplicación de permisos y los eventos en tiempo real. Está estructurado como una colección de routers (uno por dominio) que se montan bajo /api/v1/. Cada manejador de ruta es async, usando la sesión asíncrona de SQLAlchemy con el controlador asyncpg.

La autenticación usa tokens JWT (HS256, almacenados en el sessionStorage del navegador). Bcrypt gestiona el hash de contraseñas. Los valores sensibles almacenados en la base de datos (secretos SSO, contraseñas SMTP) se cifran con cifrado simétrico Fernet antes de escribirse y se descifran al leer.

PostgreSQL

Todos los datos de la aplicación residen en PostgreSQL. El esquema está gestionado por Alembic; la aplicación ejecuta alembic upgrade head en cada inicio, por lo que las migraciones de esquema se aplican automáticamente al actualizar. Las tablas usan claves primarias UUID en todo momento.

No hay Redis, ni cola de mensajes, ni caché externa. Las actualizaciones en tiempo real usan Server-Sent Events (SSE) sobre una conexión HTTP de larga duración directamente desde el backend.

Nginx de borde

Un contenedor Nginx ligero se sitúa frente a los contenedores de React y FastAPI. Sirve la React SPA compilada, hace de proxy para /api/* hacia el backend (con cabeceras apropiadas para SSE), sirve el DrawIO autoalojado en /drawio/* y aplica cabeceras de seguridad (CSP, HSTS, X-Frame-Options, etc.). En producción, este es el único contenedor expuesto a la red.

Módulos opcionales

Ollama (sugerencias de IA)

Añada --profile ai a Docker Compose para iniciar un contenedor Ollama incluido junto con la pila principal. El canal de sugerencias de IA del backend llama a la API HTTP de Ollama para generar descripciones de tarjetas usando un LLM que se ejecuta localmente. El modelo se ejecuta íntegramente dentro de su infraestructura.

También puede apuntar Canopy hacia cualquier proveedor externo compatible con Ollama (OpenAI, Anthropic a través de un proxy, etc.) estableciendo AI_PROVIDER_URL.

Servidor MCP

Añada --profile mcp para iniciar el servidor del Protocolo de Contexto de Modelo. Esto expone los datos de Canopy a los asistentes de IA (Claude Desktop, VS Code Copilot, etc.) como un conjunto de herramientas — consultas de solo lectura por defecto, con herramientas de escritura opcionales protegidas por límites de tamaño por llamada y un flujo de confirmación.

El servidor MCP se autentica mediante OAuth 2.1 delegado al proveedor SSO de Canopy, de modo que los usuarios ven los mismos permisos a través de las herramientas MCP que en la interfaz web.

Flujo de datos: guardar una tarjeta

Para hacer la arquitectura más concreta, esto es lo que ocurre cuando un usuario guarda una tarjeta:

  1. React llama a PATCH /api/v1/cards/{id} con el contenido actualizado
  2. Nginx hace de proxy de la solicitud hacia el backend FastAPI
  3. El manejador de ruta valida el JWT, comprueba el permiso inventory.edit del usuario y valida el cuerpo de la solicitud con un esquema Pydantic
  4. SQLAlchemy escribe la fila actualizada en PostgreSQL
  5. El motor de cálculo ejecuta cualquier fórmula activa para este tipo de tarjeta
  6. La puntuación de calidad de datos se recalcula a partir de los pesos de los campos
  7. Se publica un evento en el bus SSE en memoria
  8. Cualquier navegador conectado recibe el evento y actualiza la interfaz en tiempo real

Puertos y red

ServicioPuerto internoExpuesto al host
Nginx (borde)80HOST_PORT (por defecto 8920)
FastAPI8000No — solo interno
React (solo desarrollo)5173Sí en desarrollo, servido vía Nginx en producción
PostgreSQL5432No — solo interno
Ollama (opcional)11434No — solo interno
Servidor MCP (opcional)8001A través del prefijo /mcp/ de Nginx

Véase también