1504 lines
69 KiB
Markdown
1504 lines
69 KiB
Markdown
# Architecture Specification – web-cad
|
||
|
||
**Project:** web-cad – Web-based 2D-CAD for Event Seating Plans
|
||
**Phase:** 2 (Architecture)
|
||
**Date:** 2026-06-19
|
||
**Status:** Draft v1
|
||
**Based on:** Requirements Specification v5 (2026-06-19)
|
||
|
||
---
|
||
|
||
## 1. System Architecture Overview
|
||
|
||
### 1.1 High-Level Architecture Diagram
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────────────┐
|
||
│ Browser (Client) │
|
||
│ ┌───────────────────────────────────────────────────────────────────┐ │
|
||
│ │ Frontend Container (PWA – React + Vite) │ │
|
||
│ │ ┌─────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │ │
|
||
│ │ │ Canvas 2D │ │ React UI │ │ KI Copilot Panel │ │ │
|
||
│ │ │ Renderer │ │ (Ribbons, │ │ (Chat/Command Interface) │ │ │
|
||
│ │ │ + rbush │ │ Side Panel, │ │ → sends to Backend │ │ │
|
||
│ │ │ Spatial IDX │ │ Command Line)│ │ ← receives function │ │ │
|
||
│ │ │ │ │ │ │ calls & results │ │ │
|
||
│ │ └──────┬──────┘ └──────┬───────┘ └────────────┬─────────────┘ │ │
|
||
│ │ ┌──────┴──────────────────┴──────────────────────┴─────────────┐ │ │
|
||
│ │ │ Application State Layer │ │ │
|
||
│ │ │ ┌──────────┐ ┌────────────┐ ┌───────────┐ ┌───────────┐ │ │ │
|
||
│ │ │ │ Yjs Doc │ │ Undo/Redo │ │ Plugin │ │ IndexedDB │ │ │ │
|
||
│ │ │ │ (CRDT) │ │ History │ │ Runtime │ │ (Offline) │ │ │ │
|
||
│ │ │ └────┬─────┘ └────────────┘ └───────────┘ └───────────┘ │ │ │
|
||
│ │ └───────┼───────────────────────────────────────────────────────┘ │ │
|
||
│ └──────────┼─────────────────────────────────────────────────────────┘ │
|
||
└─────────────┼─────────────────────────────────────────────────────────────┘
|
||
│ WebSocket (y-websocket) │ REST API (HTTPS)
|
||
│ │
|
||
┌─────────────┼───────────────────────────────────┼─────────────────────────┐
|
||
│ ▼ ▼ │
|
||
│ ┌──────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ CAD-Backend Container (Node.js + Fastify) │ │
|
||
│ │ ┌──────────────┐ ┌──────────────┐ ┌───────────────────────────┐ │ │
|
||
│ │ │ WebSocket │ │ REST API │ │ KI Copilot Service │ │ │
|
||
│ │ │ Server │ │ (Fastify + │ │ (OpenAI-compatible │ │ │
|
||
│ │ │ (y-websocket │ │ OpenAPI/ │ │ endpoint proxy + │ │ │
|
||
│ │ │ + y-leveldb)│ │ Swagger) │ │ Function Calling) │ │ │
|
||
│ │ └──────┬───────┘ └──────┬───────┘ └────────────┬──────────────┘ │ │
|
||
│ │ ┌──────┴──────────────────┴──────────────────────┴───────────────┐ │ │
|
||
│ │ │ Backend Core Services │ │ │
|
||
│ │ │ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌──────────────┐ │ │ │
|
||
│ │ │ │ Auth │ │ Project │ │ Plugin │ │ CAD-Function │ │ │ │
|
||
│ │ │ │ Service │ │ Service │ │ Registry │ │ Registry │ │ │ │
|
||
│ │ │ │ (bcrypt/ │ │ (CRUD, │ │ (manifest │ │ (for LLM │ │ │ │
|
||
│ │ │ │ argon2) │ │ export) │ │ mgmt) │ │ tool use) │ │ │ │
|
||
│ │ │ └────┬─────┘ └────┬─────┘ └───────────┘ └──────────────┘ │ │ │
|
||
│ │ └───────┼─────────────┼─────────────────────────────────────────┘ │ │
|
||
│ │ ┌───────┴─────────────┴──────────────────────────────────────────┐ │ │
|
||
│ │ │ SQLite (better-sqlite3) + LevelDB (Yjs persistence) │ │ │
|
||
│ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │
|
||
│ │ │ │ projects | layers | elements | users | sessions | │ │ │ │
|
||
│ │ │ │ plugins | versions | settings | units_config │ │ │ │
|
||
│ │ │ └────────────────────────────────────────────────────────┘ │ │ │
|
||
│ │ └──────────────────────────────────────────────────────────────┘ │ │
|
||
│ └──────────────────────────────────────────────────────────────────────┘ │
|
||
│ ┌──────────────────────────────────────────────────────────────────────┐ │
|
||
│ │ External Services │ │
|
||
│ │ ┌──────────────────────┐ ┌──────────────────────────────────────┐ │ │
|
||
│ │ │ OpenAI-compatible │ │ Docker Volume (data persistence) │ │ │
|
||
│ │ │ LLM API (user-prov.) │ │ /data/db (SQLite) + /data/yjs │ │ │
|
||
│ │ └──────────────────────┘ └──────────────────────────────────────┘ │ │
|
||
│ └──────────────────────────────────────────────────────────────────────┘ │
|
||
└────────────────────────────────────────────────────────────────────────────┘
|
||
│
|
||
│ Deployed via Coolify on coolify-01
|
||
│ Domain: cad.media-on.de
|
||
│ SSL: Let's Encrypt via Traefik
|
||
```
|
||
|
||
### 1.2 Container Architecture
|
||
|
||
| Container | Technology | Port | Purpose |
|
||
|-----------|-----------|------|---------|
|
||
| **cad-frontend** | Nginx + React static build | 8080 | Serves PWA, static assets, Service Worker |
|
||
| **cad-backend** | Node.js + Fastify | 3001 | REST API, WebSocket server, KI proxy, SQLite |
|
||
|
||
Both containers deployed via Coolify on coolify-01 (46.225.91.159).
|
||
TLS terminated by Traefik (Let's Encrypt).
|
||
|
||
### 1.3 Data Flow Overview
|
||
|
||
```
|
||
User Action → Canvas Event → Yjs Doc Update → WebSocket → y-leveldb (persist)
|
||
↓
|
||
React UI re-render
|
||
rbush index rebuild (incremental)
|
||
Canvas 2D paint (requestAnimationFrame)
|
||
|
||
User Input (KI) → Frontend KI Panel → Backend KI Service → OpenAI-compat API
|
||
← Function Call response ←
|
||
→ CAD-Function-Registry executes → Canvas
|
||
← Result feedback ← User
|
||
|
||
External Client → REST API → Fastify route → Service layer → SQLite
|
||
↓
|
||
OpenAPI/Swagger docs
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Component Design
|
||
|
||
### 2.1 Frontend (React + Vite PWA)
|
||
|
||
**Technology:** React 19 + Vite 6 + TypeScript 5
|
||
**License:** MIT
|
||
**Role:** API client, rendering engine, user interaction, offline support
|
||
|
||
#### 2.1.1 Canvas Renderer
|
||
|
||
| Sub-component | Responsibility | Technology |
|
||
|---------------|---------------|------------|
|
||
| **RenderEngine** | Main render loop, requestAnimationFrame, double-buffering | Canvas 2D API |
|
||
| **SpatialIndex** | Bounding box queries, hit testing, viewport culling | rbush (MIT) |
|
||
| **LayerManager** | Layer rendering order, visibility, lock states | Internal |
|
||
| **ZoomPanController** | Affine transforms, zoom-to-fit, zoom-to-window | Canvas transforms |
|
||
| **SnapEngine** | Snap-to-grid, snap-to-endpoint, snap-to-midpoint, polar tracking | Internal |
|
||
| **SelectionEngine** | Single/window/crossing/fence selection, quick-select | Internal + rbush |
|
||
|
||
**Rendering Pipeline:**
|
||
```
|
||
1. Calculate viewport transform (pan offset, zoom factor)
|
||
2. Query rbush for elements intersecting viewport bbox
|
||
3. Sort by layer order (locked layers last for overlay)
|
||
4. For each visible layer:
|
||
a. Set layer alpha (if partially transparent)
|
||
b. For each element in layer (from rbush result):
|
||
- Apply element transform (position, rotation, scale)
|
||
- Stroke/fill based on element type (line, circle, polyline, etc.)
|
||
- Render selection highlight if selected
|
||
- Render handles/grips if in edit mode
|
||
5. Render overlays: snap markers, cursor crosshair, selection box, grid
|
||
6. requestAnimationFrame → repeat if dirty
|
||
```
|
||
|
||
**Performance Strategy for 50k+ elements:**
|
||
- rbush spatial index: only render elements within viewport bbox (viewport culling)
|
||
- Dirty rectangle tracking: only repaint changed regions when possible
|
||
- Layer-based culling: skip invisible/locked layers entirely
|
||
- Batch rendering: group elements by type for fewer state changes
|
||
- Off-screen canvas for static layers (grid, background image)
|
||
- Incremental rbush updates on element add/remove/modify
|
||
|
||
#### 2.1.2 React UI Layer
|
||
|
||
| Component | Description |
|
||
|-----------|-------------|
|
||
| **RibbonBar** | Top toolbar with CAD functions (Draw, Modify, Annotate, etc.) |
|
||
| **SidePanel** | Right panel with tabs: Blocks, Layers, Properties, KI Copilot |
|
||
| **CommandLine** | Bottom command input bar with autocomplete |
|
||
| **StatusBar** | Bottom status: coordinates, snap toggles, active layer, units |
|
||
| **CanvasArea** | Main canvas with zoom/pan controls |
|
||
| **ModalDialogs** | Settings, import/export, version history, permissions |
|
||
| **ContextMenu** | Right-click context menus |
|
||
| **BlockEditor** | In-place block editing within drawing context |
|
||
|
||
**WCAG 2.1 AA compliance:** All UI elements except Canvas are WCAG 2.1 AA compliant.
|
||
Canvas has inherent barriers (visual drawing); Command Line provides text alternative.
|
||
|
||
#### 2.1.3 Yjs CRDT Client
|
||
|
||
```
|
||
┌───────────────────────────────────────┐
|
||
│ Yjs Document (Y.Doc) │
|
||
│ ┌─────────┐ ┌─────────────────────┐ │
|
||
│ │ Y.Map │ │ Y.Array │ │
|
||
│ │ project │ │ elements │ │
|
||
│ │ meta │ │ (CRDT array of │ │
|
||
│ │ │ │ element objects) │ │
|
||
│ └─────────┘ └─────────────────────┘ │
|
||
│ ┌─────────┐ ┌─────────────────────┐ │
|
||
│ │ Y.Map │ │ Y.Map │ │
|
||
│ │ layers │ │ blocks │ │
|
||
│ └─────────┘ └─────────────────────┘ │
|
||
└───────────────┬───────────────────────┘
|
||
│ y-websocket provider
|
||
▼
|
||
WebSocket Server (backend)
|
||
```
|
||
|
||
**Yjs Document Structure:**
|
||
```typescript
|
||
interface CADYjsDoc {
|
||
projectMeta: Y.Map<{ // Project-level metadata
|
||
name: string,
|
||
units: 'mm' | 'cm' | 'm' | 'inches' | 'feet',
|
||
createdAt: string,
|
||
updatedAt: string,
|
||
}>
|
||
layers: Y.Map<string, Y.Map<{ // layerId -> layer data
|
||
name: string,
|
||
visible: boolean,
|
||
locked: boolean,
|
||
color: string,
|
||
lineType: string,
|
||
transparency: number,
|
||
}>>
|
||
elements: Y.Array<CADElement> // Ordered array of all elements
|
||
blocks: Y.Map<string, Y.Array<CADElement>> // blockId -> block elements
|
||
blocksInstances: Y.Array<BlockInstance> // Block placements in drawing
|
||
}
|
||
```
|
||
|
||
#### 2.1.4 Undo/Redo System
|
||
|
||
- **History stack:** Array of Yjs update bytes (using Y.encodeStateAsUpdate)
|
||
- **Stack size:** Configurable (default: 100)
|
||
- **Each undo/redo:** Apply/reverse Yjs update to Y.Doc
|
||
- **KI operations:** Appear as single entries in undo history (atomic)
|
||
- **Group operations:** Drag-and-drop produces single undo entry (debounced)
|
||
|
||
#### 2.1.5 PWA / Offline Support
|
||
|
||
| Feature | Implementation |
|
||
|---------|---------------|
|
||
| Service Worker | vite-plugin-pwa (Workbox-based, MIT) |
|
||
| Caching | App shell (cache-first), API data (stale-while-revalidate) |
|
||
| IndexedDB | idb library (MIT) for offline element cache |
|
||
| Offline sync | On reconnect: Yjs CRDT merge + WebSocket re-sync |
|
||
| Install prompt | manifest.json with icons, theme color, standalone display |
|
||
|
||
#### 2.1.6 i18n
|
||
|
||
- **Framework:** i18next (MIT) + react-i18next (MIT)
|
||
- **Initial languages:** German (de), English (en)
|
||
- **Translation files:** /src/locales/{de,en}/translation.json
|
||
- **Architecture separation:** Architecture strings are separate from UI strings
|
||
|
||
### 2.2 Backend (Node.js + Fastify)
|
||
|
||
**Technology:** Node.js 22 LTS + Fastify 5 + TypeScript 5
|
||
**License:** MIT (Fastify), MIT (Node.js)
|
||
**Role:** API server, WebSocket server, KI proxy, persistence, auth
|
||
|
||
#### 2.2.1 REST API Layer (Fastify)
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ Fastify Server (port 3001) │
|
||
│ ┌──────────┐ ┌───────────────────────┐ │
|
||
│ │ CORS │ │ Rate Limiting │ │
|
||
│ │ Plugin │ │ (@fastify/rate-limit) │ │
|
||
│ │ │ │ 100 req/min per user │ │
|
||
│ └──────────┘ └───────────────────────┘ │
|
||
│ ┌──────────┐ ┌───────────────────────┐ │
|
||
│ │ JWT/Auth │ │ OpenAPI/Swagger │ │
|
||
│ │ Plugin │ │ (@fastify/swagger) │ │
|
||
│ │ (cookie) │ │ Auto-generated docs │ │
|
||
│ └──────────┘ └───────────────────────┘ │
|
||
│ ┌──────────────────────────────────────┐│
|
||
│ │ Route Groups ││
|
||
│ │ /api/auth/* → AuthController ││
|
||
│ │ /api/projects/* → ProjectController││
|
||
│ │ /api/layers/* → LayerController ││
|
||
│ │ /api/elements/* → ElementController││
|
||
│ │ /api/blocks/* → BlockController ││
|
||
│ │ /api/import/* → ImportController ││
|
||
│ │ /api/export/* → ExportController ││
|
||
│ │ /api/plugins/* → PluginController ││
|
||
│ │ /api/ai/* → AIController ││
|
||
│ │ /api/users/* → UserController ││
|
||
│ │ /api/settings/* → SettingsController││
|
||
│ └──────────────────────────────────────┘│
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
**API-First Design Principles:**
|
||
- All functionality accessible via REST API
|
||
- OpenAPI/Swagger auto-generated from Fastify schemas
|
||
- UI is one API client among potentially many
|
||
- External integrations (Event-Management, CRM) use same API
|
||
- Webhooks for project events (export finished, project created, etc.)
|
||
|
||
#### 2.2.2 WebSocket Server (y-websocket)
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ WebSocket Server (port 3001/ws) │
|
||
│ ┌─────────────────────────────────────┐ │
|
||
│ │ y-websocket server │ │
|
||
│ │ ├── Room: project:{projectId} │ │
|
||
│ │ │ ├── User A (Planner) │ │
|
||
│ │ │ ├── User B (Planner) │ │
|
||
│ │ │ └── User C (Viewer) │ │
|
||
│ │ ├── Room: project:{projectId} │ │
|
||
│ │ │ └── User D (Planner) │ │
|
||
│ │ └── ... │ │
|
||
│ └─────────────────────────────────────┘ │
|
||
│ ┌─────────────────────────────────────┐ │
|
||
│ │ y-leveldb persistence │ │
|
||
│ │ /data/yjs/{projectId}.ldb │ │
|
||
│ │ Single source of truth for CRDT │ │
|
||
│ └─────────────────────────────────────┘ │
|
||
│ ┌─────────────────────────────────────┐ │
|
||
│ │ Awareness (cursor positions, │ │
|
||
│ │ active user list, selections) │ │
|
||
│ └─────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────┘
|
||
```
|
||
|
||
**WebSocket Protocol:**
|
||
- y-websocket protocol: sync-step1, sync-step2, awareness update
|
||
- Authentication: JWT token in WebSocket connection query param or header
|
||
- Room isolation: each project has its own Yjs document room
|
||
- Permission enforcement: Planner (CRUD), Betrachter (read-only), Admin (full), Gast (read-only)
|
||
|
||
#### 2.2.3 KI Copilot Service (Backend)
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────┐
|
||
│ KI Copilot Service (Backend) │
|
||
│ │
|
||
│ User Message (text/speech) │
|
||
│ ↓ │
|
||
│ ┌─────────────────────────────────────────────┐ │
|
||
│ │ Context Injection │ │
|
||
│ │ - Active selection (elements) │ │
|
||
│ │ - Current zoom/viewport │ │
|
||
│ │ - Active layer │ │
|
||
│ │ - Recent actions (from undo history) │ │
|
||
│ │ - Current CAD state summary │ │
|
||
│ └─────────────────────────────────────────────┘ │
|
||
│ ↓ │
|
||
│ ┌─────────────────────────────────────────────┐ │
|
||
│ │ System Prompt Construction │ │
|
||
│ │ - CAD domain instructions │ │
|
||
│ │ - Available CAD functions (from registry) │ │
|
||
│ │ - Guardrails (safety rules) │ │
|
||
│ └─────────────────────────────────────────────┘ │
|
||
│ ↓ │
|
||
│ ┌─────────────────────────────────────────────┐ │
|
||
│ │ OpenAI-compatible API Call │ │
|
||
│ │ POST {base_url}/chat/completions │ │
|
||
│ │ Body: { model, messages, tools, ... } │ │
|
||
│ └─────────────────────────────────────────────┘ │
|
||
│ ↓ │
|
||
│ ┌─────────────────────────────────────────────┐ │
|
||
│ │ Response Processing │ │
|
||
│ │ - If tool_calls: execute via CAD-Function │ │
|
||
│ │ Registry → return result to LLM → loop │ │
|
||
│ │ - If text: return to user │ │
|
||
│ └─────────────────────────────────────────────┘ │
|
||
│ ↓ │
|
||
│ ┌─────────────────────────────────────────────┐ │
|
||
│ │ Guardrail Validation │ │
|
||
│ │ - Check: no destructive ops without confirm │ │
|
||
│ │ - Check: element count limits │ │
|
||
│ │ - Check: valid parameters │ │
|
||
│ └─────────────────────────────────────────────┘ │
|
||
│ ↓ │
|
||
│ Function Execution → Canvas Update → User Feedback│
|
||
└──────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**CAD-Function-Registry (OpenAI Function Calling Schema):**
|
||
|
||
```typescript
|
||
// Example function definitions (OpenAI tool schema)
|
||
const cadFunctions = [
|
||
{
|
||
type: "function",
|
||
function: {
|
||
name: "draw_line",
|
||
description: "Draw a line from point A to point B",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
x1: { type: "number", description: "Start X coordinate" },
|
||
y1: { type: "number", description: "Start Y coordinate" },
|
||
x2: { type: "number", description: "End X coordinate" },
|
||
y2: { type: "number", description: "End Y coordinate" },
|
||
layer: { type: "string", description: "Target layer name" }
|
||
},
|
||
required: ["x1", "y1", "x2", "y2"]
|
||
}
|
||
}
|
||
},
|
||
{
|
||
type: "function",
|
||
function: {
|
||
name: "draw_circle",
|
||
description: "Draw a circle at center with radius",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
cx: { type: "number", description: "Center X" },
|
||
cy: { type: "number", description: "Center Y" },
|
||
radius: { type: "number", description: "Circle radius" },
|
||
layer: { type: "string", description: "Target layer name" }
|
||
},
|
||
required: ["cx", "cy", "radius"]
|
||
}
|
||
}
|
||
},
|
||
// ... draw_polyline, draw_polygon, draw_rect, draw_arc,
|
||
// draw_text, draw_dimension,
|
||
// move_elements, copy_elements, rotate_elements,
|
||
// scale_elements, mirror_elements, trim_elements,
|
||
// delete_elements, select_elements,
|
||
// create_layer, set_active_layer, toggle_layer,
|
||
// create_block, insert_block, explode_block,
|
||
// place_seating_row, place_seating_block,
|
||
// export_dxf, export_svg, export_pdf,
|
||
// import_dxf, import_svg,
|
||
// zoom_to, zoom_to_fit, set_snap,
|
||
// undo, redo, save
|
||
]
|
||
```
|
||
|
||
**Guardrails:**
|
||
- Destructive operations (delete all, clear) require user confirmation
|
||
- Element creation limit: max 10,000 elements per KI operation
|
||
- Parameter validation: coordinates within project bounds, radius > 0
|
||
- No direct DOM manipulation: KI operates through function registry only
|
||
- Rate limiting: max 20 function calls per KI conversation turn
|
||
- Error handling: if function fails, return error to LLM for self-correction
|
||
|
||
#### 2.2.4 Auth Service
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ Auth Service │
|
||
│ ┌──────────┐ ┌──────────────────────────┐ │
|
||
│ │ Register │ │ Login │ │
|
||
│ │ (email + │ │ (email + pass) │ │
|
||
│ │ pass) │ │ → bcrypt/argon2 │ │
|
||
│ │ │ │ → JWT (cookie) │ │
|
||
│ └──────────┘ └──────────────────────────┘ │
|
||
│ ┌──────────┐ ┌──────────────────────────┐ │
|
||
│ │ Reset │ │ Session │ │
|
||
│ │ (email │ │ (HTTP-only cookie, CSRF) │ │
|
||
│ │ link) │ │ │ │
|
||
│ └──────────┘ └──────────────────────────┘ │
|
||
│ ┌──────────┐ ┌──────────────────────────┐ │
|
||
│ │ User │ │ Role Mgmt │ │
|
||
│ │ Mgmt │ │ (Admin/Betrachter/ │ │
|
||
│ │ (Admin) │ │ Planner/Gast) │ │
|
||
│ └──────────┘ └──────────────────────────┘ │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.3 Plugin System Architecture
|
||
|
||
#### 2.3.1 Plugin Lifecycle
|
||
|
||
```
|
||
Plugin Discovery → Manifest Validation → Registration → Installation
|
||
↓
|
||
Activation ←─── Loading ←─────┘
|
||
↓ ↑
|
||
Execution Deactivation
|
||
↓ ↑
|
||
UI Integration Uninstall
|
||
```
|
||
|
||
#### 2.3.2 Plugin Manifest
|
||
|
||
```typescript
|
||
interface PluginManifest {
|
||
name: string; // Unique plugin identifier
|
||
version: string; // Semver version
|
||
displayName: string; // Human-readable name
|
||
description: string; // Plugin description
|
||
author: string; // Author name
|
||
license: string; // License identifier
|
||
main: string; // Entry point (JS module)
|
||
apiVersion: string; // Required CAD API version
|
||
permissions: PluginPermission[]; // Requested permissions
|
||
ui: PluginUIConfig; // UI integration config
|
||
hooks: PluginHooks; // API hook registrations
|
||
dependencies: string[]; // Other plugin dependencies
|
||
}
|
||
|
||
interface PluginPermission {
|
||
type: 'canvas' | 'api' | 'storage' | 'network' | 'clipboard';
|
||
access: 'read' | 'write' | 'read-write';
|
||
scope?: string; // Optional scope limitation
|
||
}
|
||
|
||
interface PluginUIConfig {
|
||
ribbonTab?: { label: string; icon: string; tools: ToolDef[] };
|
||
sidePanelTab?: { label: string; icon: string; component: string };
|
||
commandLineCommands?: { command: string; handler: string }[];
|
||
contextMenuItems?: { label: string; action: string }[];
|
||
}
|
||
|
||
interface PluginHooks {
|
||
onElementCreate?: string; // Hook function name
|
||
onElementModify?: string;
|
||
onElementDelete?: string;
|
||
onProjectOpen?: string;
|
||
onProjectSave?: string;
|
||
onExport?: string;
|
||
onImport?: string;
|
||
onSelectionChange?: string;
|
||
onLayerChange?: string;
|
||
}
|
||
```
|
||
|
||
#### 2.3.3 Plugin Isolation
|
||
|
||
```
|
||
┌───────────────────────────────────────────────┐
|
||
│ Plugin Sandbox (try-catch + API gating) │
|
||
│ ┌───────────────────────────────────────────┐ │
|
||
│ │ Plugin Code (runs in main thread) │ │
|
||
│ │ ├── Access only via Plugin API proxy │ │
|
||
│ │ ├── No direct access to React internals │ │
|
||
│ │ ├── No direct access to Yjs document │ │
|
||
│ │ └── No direct access to SQLite │ │
|
||
│ │ ┌─────────────────────────────────────┐ │ │
|
||
│ │ │ Plugin API (controlled interface) │ │ │
|
||
│ │ │ ├── canvas.addElement() │ │ │
|
||
│ │ │ ├── canvas.removeElement() │ │ │
|
||
│ │ │ ├── canvas.getSelection() │ │ │
|
||
│ │ │ ├── layers.create() / .toggle() │ │ │
|
||
│ │ │ ├── blocks.create() / .insert() │ │ │
|
||
│ │ │ ├── api.call(endpoint, params) │ │ │
|
||
│ │ │ ├── storage.get() / .set() │ │ │
|
||
│ │ │ ├── ui.registerTab() │ │ │
|
||
│ │ │ ├── ui.registerCommand() │ │ │
|
||
│ │ │ └── events.on() / .emit() │ │ │
|
||
│ │ └─────────────────────────────────────┘ │ │
|
||
│ └───────────────────────────────────────────┘ │
|
||
│ ┌───────────────────────────────────────────┐ │
|
||
│ │ Error Boundary (plugin crashes don't │ │
|
||
│ │ affect core or other plugins) │ │
|
||
│ └───────────────────────────────────────────┘ │
|
||
└───────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Isolation Strategy:**
|
||
- **No direct imports:** Plugins interact via a controlled Plugin API proxy
|
||
- **Error boundaries:** Each plugin wrapped in try-catch; failures logged, not fatal
|
||
- **Permission enforcement:** API proxy checks manifest permissions before each call
|
||
- **No shared state:** Plugins have their own state namespace
|
||
- **UI isolation:** Plugin UI components rendered in isolated containers
|
||
- **Registration:** Plugins registered via manifest; loaded lazily on activation
|
||
|
||
#### 2.3.4 Plugin API Hooks
|
||
|
||
| Hook | Trigger | Plugin Can |
|
||
|------|---------|------------|
|
||
| `onElementCreate` | Element added | Validate, modify, reject |
|
||
| `onElementModify` | Element changed | Validate, modify |
|
||
| `onElementDelete` | Element removed | Validate, reject |
|
||
| `onProjectOpen` | Project loaded | Initialize, add data |
|
||
| `onProjectSave` | Project saved | Pre-save hook |
|
||
| `onExport` | Export triggered | Transform export data |
|
||
| `onImport` | Import triggered | Transform import data |
|
||
| `onSelectionChange` | Selection changed | React to selection |
|
||
| `onLayerChange` | Layer toggled | React to layer state |
|
||
|
||
---
|
||
|
||
## 3. Data Model
|
||
|
||
### 3.1 SQLite Schema
|
||
|
||
```sql
|
||
-- Projects table
|
||
CREATE TABLE projects (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
name TEXT NOT NULL,
|
||
description TEXT,
|
||
owner_id TEXT NOT NULL REFERENCES users(id),
|
||
units TEXT NOT NULL DEFAULT 'mm',
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||
thumbnail_path TEXT
|
||
);
|
||
|
||
-- Users table
|
||
CREATE TABLE users (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
email TEXT UNIQUE NOT NULL,
|
||
password_hash TEXT NOT NULL,
|
||
display_name TEXT NOT NULL,
|
||
role TEXT NOT NULL DEFAULT 'planner',
|
||
is_active INTEGER NOT NULL DEFAULT 1,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Sessions table
|
||
CREATE TABLE sessions (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
user_id TEXT NOT NULL REFERENCES users(id),
|
||
token_hash TEXT UNIQUE NOT NULL,
|
||
expires_at TEXT NOT NULL,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||
ip_address TEXT,
|
||
user_agent TEXT
|
||
);
|
||
|
||
-- Project members (collaboration permissions)
|
||
CREATE TABLE project_members (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||
user_id TEXT NOT NULL REFERENCES users(id),
|
||
role TEXT NOT NULL DEFAULT 'betrachter',
|
||
invited_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||
accepted_at TEXT,
|
||
UNIQUE(project_id, user_id)
|
||
);
|
||
|
||
-- Layers (persisted as project configuration; live data in Yjs)
|
||
CREATE TABLE layers (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||
name TEXT NOT NULL,
|
||
visible INTEGER NOT NULL DEFAULT 1,
|
||
locked INTEGER NOT NULL DEFAULT 0,
|
||
color TEXT,
|
||
line_type TEXT,
|
||
transparency REAL NOT NULL DEFAULT 0.0,
|
||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Elements (persisted as snapshots; live data in Yjs CRDT)
|
||
CREATE TABLE elements (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||
layer_id TEXT REFERENCES layers(id),
|
||
element_type TEXT NOT NULL,
|
||
geometry TEXT NOT NULL,
|
||
style TEXT,
|
||
metadata TEXT,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Blocks (block definitions)
|
||
CREATE TABLE blocks (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||
name TEXT UNIQUE NOT NULL,
|
||
base_element_ids TEXT NOT NULL,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Block instances (placements in drawing)
|
||
CREATE TABLE block_instances (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||
block_id TEXT NOT NULL REFERENCES blocks(id) ON DELETE CASCADE,
|
||
layer_id TEXT REFERENCES layers(id),
|
||
position_x REAL NOT NULL,
|
||
position_y REAL NOT NULL,
|
||
rotation REAL NOT NULL DEFAULT 0.0,
|
||
scale_x REAL NOT NULL DEFAULT 1.0,
|
||
scale_y REAL NOT NULL DEFAULT 1.0,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Background images / floor plans
|
||
CREATE TABLE background_images (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||
file_path TEXT NOT NULL,
|
||
position_x REAL NOT NULL DEFAULT 0.0,
|
||
position_y REAL NOT NULL DEFAULT 0.0,
|
||
scale REAL NOT NULL DEFAULT 1.0,
|
||
rotation REAL NOT NULL DEFAULT 0.0,
|
||
opacity REAL NOT NULL DEFAULT 1.0,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Version history (snapshots)
|
||
CREATE TABLE versions (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||
label TEXT,
|
||
description TEXT,
|
||
snapshot_data BLOB NOT NULL,
|
||
created_by TEXT REFERENCES users(id),
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Plugins registry
|
||
CREATE TABLE plugins (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
name TEXT UNIQUE NOT NULL,
|
||
version TEXT NOT NULL,
|
||
display_name TEXT NOT NULL,
|
||
description TEXT,
|
||
manifest TEXT NOT NULL,
|
||
is_active INTEGER NOT NULL DEFAULT 0,
|
||
installed_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||
activated_at TEXT
|
||
);
|
||
|
||
-- Settings (per user and global)
|
||
CREATE TABLE settings (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
user_id TEXT REFERENCES users(id),
|
||
key TEXT NOT NULL,
|
||
value TEXT NOT NULL,
|
||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||
UNIQUE(user_id, key)
|
||
);
|
||
|
||
-- KI configuration
|
||
CREATE TABLE ai_config (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
scope TEXT NOT NULL,
|
||
project_id TEXT REFERENCES projects(id),
|
||
api_base_url TEXT NOT NULL,
|
||
api_key_encrypted TEXT NOT NULL,
|
||
model TEXT NOT NULL DEFAULT 'gpt-4o',
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Webhooks
|
||
CREATE TABLE webhooks (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
||
url TEXT NOT NULL,
|
||
event TEXT NOT NULL,
|
||
is_active INTEGER NOT NULL DEFAULT 1,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Audit log
|
||
CREATE TABLE audit_log (
|
||
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
||
user_id TEXT REFERENCES users(id),
|
||
project_id TEXT REFERENCES projects(id),
|
||
action TEXT NOT NULL,
|
||
details TEXT,
|
||
ip_address TEXT,
|
||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||
);
|
||
|
||
-- Indexes
|
||
CREATE INDEX idx_elements_project ON elements(project_id);
|
||
CREATE INDEX idx_elements_layer ON elements(layer_id);
|
||
CREATE INDEX idx_elements_type ON elements(project_id, element_type);
|
||
CREATE INDEX idx_layers_project ON layers(project_id);
|
||
CREATE INDEX idx_blocks_project ON blocks(project_id);
|
||
CREATE INDEX idx_versions_project ON versions(project_id);
|
||
CREATE INDEX idx_sessions_user ON sessions(user_id);
|
||
CREATE INDEX idx_sessions_token ON sessions(token_hash);
|
||
CREATE INDEX idx_project_members_project ON project_members(project_id);
|
||
CREATE INDEX idx_project_members_user ON project_members(user_id);
|
||
CREATE INDEX idx_settings_user_key ON settings(user_id, key);
|
||
CREATE INDEX idx_audit_log_user ON audit_log(user_id);
|
||
CREATE INDEX idx_audit_log_project ON audit_log(project_id);
|
||
```
|
||
|
||
### 3.2 Entity Relationship Diagram
|
||
|
||
```
|
||
users ────< project_members >──── projects
|
||
│ │
|
||
│ ├──< layers
|
||
│ ├──< elements
|
||
│ ├──< blocks ──< block_instances
|
||
│ ├──< background_images
|
||
│ ├──< versions
|
||
│ ├──< webhooks
|
||
│ └──< ai_config (project scope)
|
||
│
|
||
├──< sessions
|
||
├──< settings
|
||
└──< audit_log
|
||
|
||
plugins (standalone registry)
|
||
settings (user_id nullable for global)
|
||
ai_config (scope: global or project)
|
||
```
|
||
|
||
### 3.3 Database Abstraction Layer
|
||
|
||
- All database access through a DatabaseInterface abstract class
|
||
- Query builder uses standard SQL compatible with both SQLite and PostgreSQL
|
||
- No SQLite-specific extensions in core queries
|
||
- Migration scripts provided for PostgreSQL/MySQL transition
|
||
- SQLite Adapter (default) → PostgreSQL Adapter (future)
|
||
|
||
---
|
||
|
||
## 4. API Design
|
||
|
||
### 4.1 REST API Endpoints
|
||
|
||
#### Authentication
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| POST | /api/auth/register | Register new user | None |
|
||
| POST | /api/auth/login | Login, returns session cookie | None |
|
||
| POST | /api/auth/logout | Logout, invalidate session | Required |
|
||
| POST | /api/auth/reset-password | Request password reset email | None |
|
||
| POST | /api/auth/reset-password/confirm | Reset password with token | None |
|
||
| GET | /api/auth/me | Get current user info | Required |
|
||
|
||
#### Projects
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| GET | /api/projects | List user projects | Required |
|
||
| POST | /api/projects | Create new project | Required |
|
||
| GET | /api/projects/:id | Get project details | Required |
|
||
| PUT | /api/projects/:id | Update project metadata | Planner+ |
|
||
| DELETE | /api/projects/:id | Delete project | Admin |
|
||
| GET | /api/projects/:id/members | List project members | Member |
|
||
| POST | /api/projects/:id/members | Invite member | Admin |
|
||
| PUT | /api/projects/:id/members/:userId | Update member role | Admin |
|
||
| DELETE | /api/projects/:id/members/:userId | Remove member | Admin |
|
||
| GET | /api/projects/:id/versions | List versions | Member |
|
||
| POST | /api/projects/:id/versions | Create version snapshot | Planner+ |
|
||
| GET | /api/projects/:id/versions/:vId | Get version details | Member |
|
||
| POST | /api/projects/:id/versions/:vId/restore | Restore version | Planner+ |
|
||
|
||
#### Layers
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| GET | /api/projects/:id/layers | List layers | Member |
|
||
| POST | /api/projects/:id/layers | Create layer | Planner+ |
|
||
| PUT | /api/projects/:id/layers/:layerId | Update layer | Planner+ |
|
||
| DELETE | /api/projects/:id/layers/:layerId | Delete layer | Planner+ |
|
||
| PUT | /api/projects/:id/layers/reorder | Reorder layers | Planner+ |
|
||
|
||
#### Elements
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| GET | /api/projects/:id/elements | List elements (filtered) | Member |
|
||
| POST | /api/projects/:id/elements | Create element | Planner+ |
|
||
| PUT | /api/projects/:id/elements/:elemId | Update element | Planner+ |
|
||
| DELETE | /api/projects/:id/elements/:elemId | Delete element | Planner+ |
|
||
| POST | /api/projects/:id/elements/batch | Batch operations | Planner+ |
|
||
|
||
#### Blocks
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| GET | /api/projects/:id/blocks | List block definitions | Member |
|
||
| POST | /api/projects/:id/blocks | Create block definition | Planner+ |
|
||
| PUT | /api/projects/:id/blocks/:blockId | Update block definition | Planner+ |
|
||
| DELETE | /api/projects/:id/blocks/:blockId | Delete block definition | Planner+ |
|
||
| POST | /api/projects/:id/blocks/:blockId/insert | Insert block instance | Planner+ |
|
||
|
||
#### Import / Export
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| POST | /api/projects/:id/import/dxf | Import DXF file | Planner+ |
|
||
| POST | /api/projects/:id/import/svg | Import SVG file | Planner+ |
|
||
| GET | /api/projects/:id/export/dxf | Export as DXF | Member |
|
||
| GET | /api/projects/:id/export/svg | Export as SVG | Member |
|
||
| GET | /api/projects/:id/export/pdf | Export as PDF | Member |
|
||
| GET | /api/projects/:id/export/png | Export as PNG | Member |
|
||
| GET | /api/projects/:id/export/json | Export project as JSON | Member |
|
||
|
||
#### KI Copilot
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| POST | /api/ai/chat | Send message to KI copilot | Required |
|
||
| GET | /api/ai/functions | List available CAD functions | Required |
|
||
| POST | /api/ai/config | Set KI API config (admin) | Admin |
|
||
| GET | /api/ai/config | Get KI API config (admin) | Admin |
|
||
| GET | /api/ai/health | Check KI endpoint connectivity | Admin |
|
||
|
||
#### Plugins
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| GET | /api/plugins | List installed plugins | Required |
|
||
| POST | /api/plugins/install | Install plugin from package | Admin |
|
||
| PUT | /api/plugins/:id/activate | Activate plugin | Admin |
|
||
| PUT | /api/plugins/:id/deactivate | Deactivate plugin | Admin |
|
||
| DELETE | /api/plugins/:id | Uninstall plugin | Admin |
|
||
| GET | /api/plugins/:id/manifest | Get plugin manifest | Required |
|
||
|
||
#### Users (Admin only)
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| GET | /api/users | List all users | Admin |
|
||
| PUT | /api/users/:id | Update user role | Admin |
|
||
| DELETE | /api/users/:id | Delete user | Admin |
|
||
|
||
#### Settings
|
||
|
||
| Method | Path | Description | Auth |
|
||
|--------|------|-------------|------|
|
||
| GET | /api/settings | Get user settings | Required |
|
||
| PUT | /api/settings | Update user settings | Required |
|
||
| GET | /api/settings/global | Get global settings | Admin |
|
||
| PUT | /api/settings/global | Update global settings | Admin |
|
||
|
||
### 4.2 OpenAPI / Swagger
|
||
|
||
- Auto-generated via @fastify/swagger and @fastify/swagger-ui
|
||
- Available at /api/docs (Swagger UI) and /api/openapi.json (spec)
|
||
- All routes have TypeScript schema definitions (params, querystring, body, response)
|
||
- OpenAPI 3.0 specification
|
||
- External clients can auto-generate SDKs from the spec
|
||
|
||
### 4.3 WebSocket API
|
||
|
||
| Event | Direction | Description |
|
||
|-------|-----------|-------------|
|
||
| sync-step1 | Client to Server | Initial sync request |
|
||
| sync-step2 | Server to Client | Sync response with missing updates |
|
||
| update | Bi-directional | Yjs document update bytes |
|
||
| awareness | Bi-directional | Cursor position, user info |
|
||
| permission-denied | Server to Client | User lacks permission for action |
|
||
|
||
---
|
||
|
||
## 5. Rendering Pipeline
|
||
|
||
### 5.1 Canvas 2D + rbush Architecture
|
||
|
||
```
|
||
1. Viewport Calculation (panX, panY, zoom, canvasWidth x canvasHeight)
|
||
2. Spatial Query: rbush.search(viewportBBox) → only visible elements
|
||
3. Layer Sorting: sort by sortOrder, skip invisible layers
|
||
4. Canvas Transform: ctx.save(), ctx.translate(panX, panY), ctx.scale(zoom, zoom)
|
||
5. Background Rendering: grid + background image (offscreen canvas, cached)
|
||
6. Element Rendering: for each element in sorted results:
|
||
- Apply element transform (pos, rot, scale)
|
||
- Set stroke/fill from element style or layer
|
||
- Render by type: line, circle, polyline, polygon, rect, arc, text, dimension, block_instance
|
||
- Render selection highlight if selected
|
||
- Render edit handles if in edit mode
|
||
7. Overlay Rendering: snap markers, cursor crosshair, selection box, multi-user cursors
|
||
8. Cleanup: ctx.restore(), mark canvas clean
|
||
```
|
||
|
||
### 5.2 rbush Spatial Index
|
||
|
||
```typescript
|
||
import RBush from 'rbush';
|
||
|
||
interface CADItem {
|
||
minX: number; minY: number;
|
||
maxX: number; maxY: number;
|
||
elementId: string;
|
||
elementType: string;
|
||
layerId: string;
|
||
}
|
||
|
||
const spatialIndex = new RBush<CADItem>(9);
|
||
|
||
// Insert on element create
|
||
spatialIndex.insert({ minX, minY, maxX, maxY, elementId, elementType, layerId });
|
||
|
||
// Query viewport (render culling)
|
||
const visibleItems = spatialIndex.search({
|
||
minX: viewportLeft, minY: viewportTop,
|
||
maxX: viewportRight, maxY: viewportBottom
|
||
});
|
||
// O(log n + k) where k = results
|
||
|
||
// Query point (hit testing)
|
||
const hits = spatialIndex.search({ minX: x, minY: y, maxX: x, maxY: y });
|
||
```
|
||
|
||
### 5.3 Zoom / Pan
|
||
|
||
```typescript
|
||
// screen to world
|
||
function screenToWorld(screenX, screenY) {
|
||
return { x: (screenX - panX) / zoom, y: (screenY - panY) / zoom };
|
||
}
|
||
|
||
// world to screen
|
||
function worldToScreen(worldX, worldY) {
|
||
return { x: worldX * zoom + panX, y: worldY * zoom + panY };
|
||
}
|
||
|
||
// Zoom limits
|
||
const MIN_ZOOM = 0.001;
|
||
const MAX_ZOOM = 1000;
|
||
```
|
||
|
||
### 5.4 Performance Optimization
|
||
|
||
| Technique | Impact | Implementation |
|
||
|-----------|--------|----------------|
|
||
| Viewport culling | Only render visible elements | rbush search on viewport bbox |
|
||
| Layer culling | Skip invisible/locked layers | Filter before render |
|
||
| Off-screen canvas | Cache static layers | grid/background to OffscreenCanvas |
|
||
| Dirty rect tracking | Only repaint changed regions | Track changed bboxes, clip region |
|
||
| Incremental index updates | Avoid full rbush rebuild | Insert/remove on element change |
|
||
| Batch rendering | Fewer canvas state changes | Group by stroke/fill style |
|
||
| requestAnimationFrame | Sync with browser repaint | 60fps cap, skip if not dirty |
|
||
| Device pixel ratio | Crisp rendering on HiDPI | Scale canvas by devicePixelRatio |
|
||
|
||
---
|
||
|
||
## 6. Multi-User Architecture (Yjs CRDT)
|
||
|
||
### 6.1 Synchronization Flow
|
||
|
||
```
|
||
User A draws line → Yjs Doc update → Y.encodeStateAsUpdate()
|
||
→ WebSocket send (update bytes)
|
||
→ y-websocket Server: receives, broadcasts to B/C, persists to y-leveldb
|
||
→ User B receives update → Y.applyUpdate(Yjs Doc)
|
||
→ React re-render → rbush index update → Canvas repaint
|
||
```
|
||
|
||
### 6.2 Conflict Resolution (CRDT)
|
||
|
||
- Commutative: Updates can be applied in any order
|
||
- Associative: Grouping of updates does not matter
|
||
- Idempotent: Applying the same update twice has no additional effect
|
||
- No conflicts: CRDTs guarantee convergence without conflict resolution
|
||
- Element-level merge: concurrent changes to different properties of same element merge automatically
|
||
- Array order: Yjs Y.Array uses insertion-based CRDT; deterministic order based on client ID
|
||
|
||
### 6.3 Permission Enforcement
|
||
|
||
| Role | Yjs Access | API Access |
|
||
|------|-----------|------------|
|
||
| Planner | Read + Write (full CRDT) | All CRUD operations |
|
||
| Betrachter | Read-only (observe only) | GET only |
|
||
| Admin | Read + Write | All + user management |
|
||
| Gast | Read-only (temporary) | GET only, limited scope |
|
||
|
||
### 6.4 Offline Synchronization
|
||
|
||
1. User goes offline → Service Worker detects → UI shows Offline indicator → Yjs updates queued locally (IndexedDB)
|
||
2. User continues editing → changes applied to local Yjs Doc → IndexedDB persists → Canvas renders from local state
|
||
3. User reconnects → WebSocket re-establishes → y-websocket sync exchanges missing updates → CRDT merge converges → UI shows Online
|
||
|
||
### 6.5 Scalability (Future: Redis Pub/Sub)
|
||
|
||
Initial: single server with all WebSocket connections.
|
||
Future: multiple servers with Redis Pub/Sub for cross-server broadcast.
|
||
|
||
---
|
||
|
||
## 7. KI Copilot Architecture
|
||
|
||
### 7.1 Architecture Pattern
|
||
|
||
```
|
||
Frontend KI Panel → Backend KI Service → OpenAI-compatible API Endpoint
|
||
↓
|
||
Context Injection (selection, zoom, layer, recent actions)
|
||
↓
|
||
System Prompt + Function Definitions
|
||
↓
|
||
API Call (POST /chat/completions with tools)
|
||
↓
|
||
Response Processing:
|
||
- tool_calls → CAD-Function-Registry executes → Canvas update
|
||
- text → return to user
|
||
↓
|
||
Guardrail Validation (destructive ops, limits, parameter validation)
|
||
↓
|
||
Function Execution → Canvas Update → User Feedback
|
||
```
|
||
|
||
### 7.2 Context Injection
|
||
|
||
```typescript
|
||
interface KIContext {
|
||
activeLayer: { id: string; name: string };
|
||
currentZoom: number;
|
||
viewportBBox: { minX: number; minY: number; maxX: number; maxY: number };
|
||
selectedElements: Array<{ id: string; type: string; geometry: object; layer: string }>;
|
||
recentActions: Array<{ action: string; params: object }>;
|
||
projectName: string;
|
||
layerCount: number;
|
||
elementCount: number;
|
||
blockCount: number;
|
||
units: string;
|
||
model: string;
|
||
apiBaseUrl: string;
|
||
}
|
||
```
|
||
|
||
### 7.3 Guardrails
|
||
|
||
| Guardrail | Implementation |
|
||
|-----------|----------------|
|
||
| Destructive operation confirmation | Functions like delete_elements with all=true require confirm: true param |
|
||
| Element count limit | Max 10,000 elements per function call |
|
||
| Parameter validation | Coordinates within bounds, radius > 0, valid layer names |
|
||
| No direct DOM access | LLM can only interact through registered functions |
|
||
| Rate limiting | Max 20 function calls per conversation turn |
|
||
| Error recovery | Function errors returned to LLM for self-correction (max 3 retries) |
|
||
| Audit logging | All KI operations logged in audit_log table |
|
||
| Undo integration | All KI operations appear in undo history as atomic entries |
|
||
|
||
---
|
||
|
||
## 8. Plugin System Architecture (Detailed)
|
||
|
||
### 8.1 Plugin Loading Sequence
|
||
|
||
1. App Startup: Scan /plugins directory, read manifests, validate schema
|
||
2. Registration: Check API version, check dependencies, register in SQLite
|
||
3. Activation: Load entry point, create API proxy, verify permissions, init plugin, register UI/hooks
|
||
4. Execution: Hooks fire on events, UI components rendered, API calls through proxy, errors caught
|
||
5. Deactivation: Call deactivate(), remove UI, unregister hooks, clean state
|
||
6. Uninstall: Deactivate, remove from DB, delete files
|
||
|
||
### 8.2 Plugin API Surface
|
||
|
||
```typescript
|
||
interface PluginAPI {
|
||
canvas: {
|
||
addElement(element: CADElement): string;
|
||
removeElement(id: string): boolean;
|
||
modifyElement(id: string, changes: Partial<CADElement>): boolean;
|
||
getElement(id: string): CADElement | null;
|
||
getElements(filter: ElementFilter): CADElement[];
|
||
getSelection(): string[];
|
||
setSelection(ids: string[]): void;
|
||
getViewport(): ViewportState;
|
||
setViewport(zoom: number, panX: number, panY: number): void;
|
||
};
|
||
layers: {
|
||
create(name: string, options?: LayerOptions): string;
|
||
delete(id: string): boolean;
|
||
toggle(id: string): void;
|
||
setActive(id: string): void;
|
||
list(): Layer[];
|
||
};
|
||
blocks: {
|
||
create(name: string, elementIds: string[]): string;
|
||
insert(blockId: string, x: number, y: number, options?: InsertOptions): string;
|
||
explode(instanceId: string): boolean;
|
||
};
|
||
api: {
|
||
call(endpoint: string, method: string, body?: object): Promise<any>;
|
||
getProject(): ProjectInfo;
|
||
};
|
||
storage: {
|
||
get(key: string): any;
|
||
set(key: string, value: any): void;
|
||
remove(key: string): void;
|
||
};
|
||
ui: {
|
||
registerRibbonTab(tab: RibbonTabDef): void;
|
||
registerSidePanelTab(tab: SidePanelTabDef): void;
|
||
registerCommand(cmd: CommandDef): void;
|
||
registerContextMenuItem(item: ContextMenuItemDef): void;
|
||
showDialog(component: string, props?: object): void;
|
||
showToast(message: string, type?: string): void;
|
||
};
|
||
events: {
|
||
on(event: string, handler: Function): void;
|
||
off(event: string, handler: Function): void;
|
||
emit(event: string, data?: any): void;
|
||
};
|
||
ai: {
|
||
registerFunction(def: FunctionDefinition): void;
|
||
unregisterFunction(name: string): void;
|
||
};
|
||
}
|
||
```
|
||
|
||
### 8.3 Plugin UI Integration Points
|
||
|
||
- Ribbon Bar: [Core Tabs] [Plugin Tabs...]
|
||
- Side Panel: [Blocks] [Layers] [Properties] [KI] [Plugin Tabs...]
|
||
- Command Line: core commands + plugin commands
|
||
- Context Menu: core actions + plugin actions
|
||
|
||
---
|
||
|
||
## 9. Deployment Architecture
|
||
|
||
### 9.1 Docker Container Setup
|
||
|
||
```yaml
|
||
version: '3.8'
|
||
services:
|
||
cad-backend:
|
||
build: ./backend
|
||
container_name: cad-backend
|
||
restart: unless-stopped
|
||
ports:
|
||
- "3001:3001"
|
||
volumes:
|
||
- cad-data:/data
|
||
environment:
|
||
- NODE_ENV=production
|
||
- PORT=3001
|
||
- JWT_SECRET=${JWT_SECRET}
|
||
- AI_API_URL=${AI_API_URL}
|
||
- AI_API_KEY=${AI_API_KEY}
|
||
- AI_MODEL=${AI_MODEL}
|
||
- DATABASE_PATH=/data/db/cad.sqlite
|
||
- YJS_PERSISTENCE_PATH=/data/yjs
|
||
- CORS_ORIGIN=https://cad.media-on.de
|
||
- RATE_LIMIT_MAX=100
|
||
- RATE_LIMIT_WINDOW=60000
|
||
healthcheck:
|
||
test: ["CMD", "curl", "-f", "http://localhost:3001/api/health"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
|
||
cad-frontend:
|
||
build: ./frontend
|
||
container_name: cad-frontend
|
||
restart: unless-stopped
|
||
ports:
|
||
- "8080:80"
|
||
depends_on:
|
||
- cad-backend
|
||
environment:
|
||
- API_URL=https://cad.media-on.de/api
|
||
- WS_URL=wss://cad.media-on.de/ws
|
||
|
||
volumes:
|
||
cad-data:
|
||
```
|
||
|
||
### 9.2 Coolify Deployment
|
||
|
||
| Aspect | Configuration |
|
||
|--------|---------------|
|
||
| Server | coolify-01 (46.225.91.159) |
|
||
| Domain | cad.media-on.de (TBD) |
|
||
| SSL | Let's Encrypt via Traefik |
|
||
| Containers | 2 (cad-backend + cad-frontend) via docker-compose |
|
||
| Volume | Docker volume cad-data for SQLite + Yjs + uploads |
|
||
| Environment | Secrets via Coolify environment variables |
|
||
| KI API | External OpenAI-compatible endpoint via env vars |
|
||
| WebSocket | Traefik WebSocket proxy support |
|
||
| Scaling | Initial: single server. Future: Redis Pub/Sub |
|
||
|
||
### 9.3 Reverse Proxy (Traefik)
|
||
|
||
```
|
||
User Browser → Traefik (TLS) → /api/* → cad-backend:3001 (REST)
|
||
→ /ws → cad-backend:3001 (WebSocket upgrade)
|
||
→ /* → cad-frontend:8080 (static PWA)
|
||
```
|
||
|
||
### 9.4 Data Persistence
|
||
|
||
```
|
||
/data/
|
||
├── db/cad.sqlite # SQLite database
|
||
├── yjs/ # Yjs LevelDB persistence per project
|
||
├── uploads/backgrounds/ # Uploaded background images
|
||
├── uploads/imports/ # Imported DXF/SVG files
|
||
└── plugins/installed/ # Installed plugin packages
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Security Concept
|
||
|
||
### 10.1 Authentication
|
||
|
||
- Method: Email + Password
|
||
- Password hashing: argon2 (preferred) or bcrypt (fallback)
|
||
- Session: JWT in HTTP-only, Secure, SameSite=Strict cookie
|
||
- CSRF: Double-submit token via @fastify/csrf
|
||
- Session timeout: Configurable, default 24 hours
|
||
- Password reset: Time-limited token via email link
|
||
|
||
### 10.2 Authorization (RBAC)
|
||
|
||
| Role | Project Access | User Mgmt | KI Config | Plugin Mgmt |
|
||
|------|---------------|-----------|-----------|-------------|
|
||
| Admin | Full (all) | Full | Full | Full |
|
||
| Planner | CRUD (own + shared) | None | None | None |
|
||
| Betrachter | Read-only | None | None | None |
|
||
| Gast | Read-only (temp) | None | None | None |
|
||
|
||
### 10.3 API Rate Limiting
|
||
|
||
- Per user (authenticated): 100 req/min
|
||
- Per IP (unauthenticated): 20 req/min
|
||
- KI API calls: 20 req/min
|
||
- WebSocket messages: 100 updates/min
|
||
- On limit exceeded: HTTP 429 with Retry-After header
|
||
|
||
### 10.4 Data Security
|
||
|
||
- Transport: TLS 1.3 via Traefik
|
||
- Passwords: argon2/bcrypt hash (never plaintext)
|
||
- KI API key: AES-256 encrypted in database
|
||
- Input validation: Fastify schema validation on all routes
|
||
- Import validation: DXF/SVG/PDF schema validation, size limits, sanitize
|
||
- SQL injection: Parameterized queries only (better-sqlite3)
|
||
- XSS: React auto-escaping, CSP headers via @fastify/helmet
|
||
- CORS: Configured for specific origin (cad.media-on.de)
|
||
|
||
### 10.5 GDPR Compliance
|
||
|
||
- Data export: User can export all personal data (JSON)
|
||
- Data deletion: User can delete account + all associated data
|
||
- Audit log: All data access logged
|
||
- Data minimization: Only collect email + display name
|
||
- Consent: Registration requires explicit consent checkbox
|
||
- Retention: Audit logs 90 days, sessions purged on expiry
|
||
|
||
### 10.6 KI Safety
|
||
|
||
- Destructive ops: require confirm flag; prompt user if missing
|
||
- Element limits: Max 10,000 elements per KI operation
|
||
- Parameter bounds: Validate all function parameters before execution
|
||
- Function whitelist: Only registered CAD functions callable
|
||
- Rate limiting: Max 20 function calls per conversation turn
|
||
- Audit trail: All KI operations logged
|
||
|
||
---
|
||
|
||
## 11. Technology Stack
|
||
|
||
### 11.1 Stack Summary with Licenses
|
||
|
||
| Layer | Technology | License | Justification |
|
||
|-------|-----------|---------|---------------|
|
||
| Frontend Framework | React 19 + TypeScript 5 | MIT | Mature ecosystem, component model, hooks |
|
||
| Build Tool | Vite 6 | MIT | Fast HMR, optimized builds, PWA plugin |
|
||
| Rendering | Canvas 2D + rbush | MIT | Browser-native, 50k+ elements @ 60fps |
|
||
| WebGL (optional) | regl or Three.js | MIT | Reserved for very large scenes |
|
||
| CRDT | Yjs + y-websocket + y-leveldb | MIT | Conflict-free sync, WebSocket transport |
|
||
| Offline | y-indexeddb + vite-plugin-pwa | MIT | IndexedDB persistence, Workbox PWA |
|
||
| i18n | i18next + react-i18next | MIT | Industry standard, namespace support |
|
||
| Backend | Fastify 5 + TypeScript 5 | MIT | High performance, schema validation, OpenAPI |
|
||
| Runtime | Node.js 22 LTS | MIT | Long-term support, ecosystem |
|
||
| WebSocket | ws + y-websocket | MIT | Standard WebSocket, y-websocket protocol |
|
||
| Database | SQLite (better-sqlite3) | Public Domain/MIT | Embedded, zero-config, WAL mode |
|
||
| Auth | argon2 + @fastify/cookie + @fastify/jwt | MIT | Secure hashing, JWT sessions |
|
||
| API Docs | @fastify/swagger | MIT | Auto-generated OpenAPI 3.0 |
|
||
| Rate Limiting | @fastify/rate-limit | MIT | Configurable rate limiting |
|
||
| Security | @fastify/helmet + @fastify/cors | MIT | Security headers, CORS |
|
||
| DXF | dxf-parser + dxf-writer | MIT | Open-source DXF parsing/writing |
|
||
| DWG (optional) | libredwg | GPL | Optional DWG support |
|
||
| PDF | pdf-lib | MIT | Client/server PDF generation |
|
||
| SVG | Native browser API | MIT | SVG import/export |
|
||
| KI Client | OpenAI SDK or native fetch | MIT | OpenAI-compatible API client |
|
||
| Validation | Zod | MIT | TypeScript-first schema validation |
|
||
| Testing | Vitest + Playwright | MIT | Unit/integration + E2E testing |
|
||
| Container | Docker | Apache 2.0 | Standard container runtime |
|
||
| Deployment | Coolify | AGPL-3.0 | Self-hosted deployment platform |
|
||
| Reverse Proxy | Traefik | MIT | TLS, WebSocket proxy, Let's Encrypt |
|
||
|
||
### 11.2 ADRs (Architecture Decision Records)
|
||
|
||
#### ADR-001: Canvas 2D over WebGL as Primary Renderer
|
||
- Status: Accepted
|
||
- Decision: Canvas 2D with rbush spatial index as primary renderer
|
||
- Rationale: Browser-native, no GPU dependency, with rbush viewport culling only visible elements rendered. WebGL reserved as optional hybrid for extremely large scenes.
|
||
- Alternatives: WebGL only (higher complexity, GPU dependency), SVG (DOM overhead at >3000 elements)
|
||
|
||
#### ADR-002: Yjs CRDT over Operational Transform
|
||
- Status: Accepted
|
||
- Decision: Yjs CRDT with y-websocket transport
|
||
- Rationale: CRDTs guarantee convergence without central conflict resolution. MIT-licensed, well-maintained. Works offline and resyncs automatically.
|
||
- Alternatives: OT (requires central server), Custom sync (too risky)
|
||
|
||
#### ADR-003: React over Vue/Svelte for Frontend
|
||
- Status: Accepted
|
||
- Decision: React 19 + TypeScript
|
||
- Rationale: Largest ecosystem for complex UI, hooks model, excellent TypeScript support.
|
||
- Alternatives: Vue (smaller CAD ecosystem), Svelte (smaller ecosystem)
|
||
|
||
#### ADR-004: Node.js + Fastify over Python + FastAPI
|
||
- Status: Accepted
|
||
- Decision: Node.js 22 + Fastify 5
|
||
- Rationale: Yjs is JavaScript; Node.js backend shares types. y-websocket server is Node-native. Single language across stack. Fastify has built-in schema validation and OpenAPI generation.
|
||
- Alternatives: Python + FastAPI (Yjs requires Node.js anyway), Deno (less maturity)
|
||
|
||
#### ADR-005: SQLite over PostgreSQL for Initial Version
|
||
- Status: Accepted
|
||
- Decision: SQLite with better-sqlite3, abstracted via DatabaseInterface
|
||
- Rationale: Zero config, embedded, single-file. Perfect for initial single-server. Abstraction enables future migration.
|
||
- Alternatives: PostgreSQL (requires separate container), MySQL (similar overhead)
|
||
|
||
#### ADR-006: API-First Design
|
||
- Status: Accepted
|
||
- Decision: Backend is purely an API server; UI is one API client
|
||
- Rationale: All functionality via documented REST API. External integrations use same API. Webhooks for events.
|
||
|
||
#### ADR-007: Plugin System with API Proxy Isolation
|
||
- Status: Accepted
|
||
- Decision: Plugins run in main thread with API proxy isolation and error boundaries
|
||
- Rationale: Full sandbox (Web Workers) limits Canvas/DOM access. API proxy gives controlled access with permission enforcement.
|
||
- Alternatives: Web Worker (too restrictive), iframe (complex messaging)
|
||
|
||
#### ADR-008: OpenAI-compatible Endpoint for KI Copilot
|
||
- Status: Accepted
|
||
- Decision: Use OpenAI-compatible API endpoint (user-provided base_url + api_key)
|
||
- Rationale: Supports OpenAI, OpenRouter, Ollama, vLLM, llama.cpp. No lock-in. Function Calling is standard.
|
||
|
||
#### ADR-009: 2 Docker Containers (Backend + Frontend)
|
||
- Status: Accepted
|
||
- Decision: 2 containers: cad-backend (Node.js) + cad-frontend (Nginx static)
|
||
- Rationale: Separation of concerns, independent scaling, frontend CDN-cacheable.
|
||
|
||
---
|
||
|
||
## 12. Risk Assessment
|
||
|
||
| Risk | Likelihood | Impact | Mitigation |
|
||
|------|-----------|--------|------------|
|
||
| Canvas 2D performance at 50k+ elements | Medium | High | rbush spatial index, viewport culling, dirty rect tracking; benchmark early |
|
||
| Yjs CRDT complexity for complex CAD data | Medium | Medium | Use simple Y.Map/Y.Array structures; test concurrent editing |
|
||
| KI API endpoint reliability (external) | Medium | Medium | Timeout handling, fallback to manual, retry logic |
|
||
| KI function calling errors | Medium | Medium | Guardrails, parameter validation, error feedback, max 3 retries |
|
||
| Plugin system destabilizing core | Low | High | API proxy isolation, error boundaries, permission enforcement |
|
||
| DXF/DWG compatibility issues | Medium | Medium | dxf-parser (MIT), test with real files, DWG optional |
|
||
| Browser memory with large projects | Medium | Medium | Virtualized rendering, element pagination, IndexedDB offloading |
|
||
| Cross-browser Canvas rendering differences | Low | Low | Regression tests, visual tolerance checks |
|
||
| SQLite concurrent write contention | Low | Medium | WAL mode, write queue, batch operations |
|
||
| Offline merge complexity | Low | Low | Yjs CRDT guarantees convergence; merge report for transparency |
|
||
|
||
---
|
||
|
||
## 13. Non-Functional Requirements Mapping
|
||
|
||
| NFR | Architecture Response |
|
||
|-----|---------------------|
|
||
| NF-PERF-01 (50k+ @ 60fps) | Canvas 2D + rbush viewport culling + dirty rect tracking |
|
||
| NF-PERF-02 (Lighthouse >= 80) | Vite optimization, code splitting, lazy loading, PWA caching |
|
||
| NF-PERF-03 (<= 500MB memory) | Spatial index, virtualized rendering, IndexedDB offloading |
|
||
| NF-PERF-04 (< 1s collab latency) | WebSocket + Yjs binary updates, server-side broadcast |
|
||
| NF-PERF-05 (KI < 3s) | Streaming responses, context injection optimization |
|
||
| NF-SEC-01 (Auth) | argon2/bcrypt, HTTP-only cookies, CSRF protection |
|
||
| NF-SEC-02 (RBAC) | Server-side role enforcement on all routes and WebSocket |
|
||
| NF-SEC-03 (TLS) | Traefik + Let's Encrypt |
|
||
| NF-SEC-04 (Input validation) | Zod schemas + Fastify validation |
|
||
| NF-SEC-05 (KI safety) | Guardrails, function validation, audit logging |
|
||
| NF-SEC-06 (GDPR) | Data export, deletion, audit log, data minimization |
|
||
| NF-SEC-07 (Rate limiting) | @fastify/rate-limit, 100 req/min, HTTP 429 |
|
||
| NF-A11Y-01 (WCAG 2.1 AA) | All UI except Canvas; Command Line as alternative |
|
||
| NF-DEP-01 (Docker) | Dockerfile + docker-compose.yml |
|
||
| NF-DEP-02 (Coolify) | Coolify deployment on coolify-01 |
|
||
| NF-DEP-03 (Env vars) | All secrets via environment variables |
|
||
| NF-DEP-04 (SQLite) | SQLite default, DatabaseInterface for migration |
|
||
| NF-DEP-05 (KI external) | KI API via env vars, no local model |
|
||
| NF-PLAT-01 (Browser) | Chrome, Firefox, Edge, Safari (latest 2) |
|
||
| NF-PLAT-02 (WebAssembly) | Not required; native JS/Canvas sufficient |
|
||
| NF-PLAT-03 (Web Speech API) | Optional voice input, browser-native |
|
||
| NF-PLAT-04 (PWA) | vite-plugin-pwa, manifest.json, service worker |
|
||
| NF-OSS-01 (Open Source) | AGPL-3.0 or MIT for web-cad; all deps OSS |
|
||
| NF-OSS-02 (OSS components) | All libraries MIT/Apache/BSD/ISC/LGPL/AGPL |
|
||
| NF-OSS-03 (License compat) | All licenses compatible |
|
||
| NF-OSS-04 (DXF) | dxf-parser + dxf-writer (MIT) |
|
||
| NF-OSS-05 (DWG optional) | libredwg (GPL) if needed |
|
||
| NF-OSS-06 (KI offsite) | User-provided OpenAI-compatible endpoint |
|
||
|
||
---
|
||
|
||
## 14. Open Design Questions
|
||
|
||
1. Exact domain name: TBD (proposed: cad.media-on.de) - needs DNS confirmation
|
||
2. KI model default: Depends on user endpoint - no default model forced
|
||
3. Plugin distribution: Local file upload vs marketplace URL - initial: local upload
|
||
4. Email service for password reset: SMTP configuration needed
|
||
5. Backup strategy: SQLite backup schedule and Yjs LevelDB backup - needs ops plan
|
||
6. Monitoring/logging: Log aggregation strategy - initial: container logs
|
||
|
||
---
|
||
|
||
## 15. Handoff
|
||
|
||
### Design Status
|
||
- Architecture complete and documented
|
||
- All major components designed (Frontend, Backend, KI Copilot, Plugin System, Rendering)
|
||
- 9 ADRs with rationale and alternatives
|
||
- Data model with 14 tables defined
|
||
- REST API with 50+ endpoints documented
|
||
- Deployment architecture for 2 containers on Coolify
|
||
- Security concept with auth, RBAC, rate limiting, GDPR, KI safety
|
||
- Risk assessment with 10 identified risks and mitigations
|
||
|
||
### Task Count
|
||
- Estimated 25-30 implementation tasks (see task_graph.json)
|
||
- Tasks sequenced with dependencies
|
||
- Each task sized for one implementation block
|
||
|
||
### Risks
|
||
- Canvas 2D performance at 50k+ elements (mitigated by rbush, needs early benchmark)
|
||
- KI API reliability (external dependency, mitigated by timeout/retry/fallback)
|
||
- Plugin system stability (mitigated by isolation and error boundaries)
|
||
- DXF compatibility (mitigated by dxf-parser, needs real-file testing)
|
||
|
||
### Open Design Questions
|
||
- See Section 14 (6 items)
|
||
|
||
### Ready for Implementation: No - awaiting quality review and UI design phase
|