This commit is contained in:
ValueOn AG 2026-01-23 21:05:15 +01:00
parent b1e63d758e
commit 8fa4c870c7
7 changed files with 2581 additions and 847 deletions

View file

@ -0,0 +1,852 @@
# Refactor Nyla UI - Analyse und Implementation Plan
## Datum: 23. Januar 2026
---
## Executive Summary
Die Seiten **Chat Playground**, **Workflows**, **Automations**, **Prompts**, **Files** und **Connections** im neuen UI (`frontend_nyla`) sind inkomplett implementiert. Sie nutzen:
- Eigene, vereinfachte Table-Komponenten statt des etablierten `FormGenerator`-Systems
- Fehlende Dashboard- und Chat-Elemente (PlaygroundPage)
- Keine RBAC-Berechtigungsprüfung
- Keine Backend-Pagination/Sorting/Filtering
- Keine standardisierten CRUD-Modals
### Design-Prinzipien für die Implementation
> **WICHTIG:** Das bestehende Look & Feel von Nyla MUSS beibehalten werden!
>
> - Keine neuen Styles oder Design-Patterns einführen
> - Bestehende CSS-Module und Komponenten-Styles wiederverwenden
> - Nur **fehlende Logik ergänzen**, nicht das visuelle Design ändern
> - Neue Komponenten müssen sich nahtlos in das bestehende Design einfügen
---
## 1. IST-Analyse
### 1.1 Chat Playground (PlaygroundPage.tsx)
**Aktueller Zustand:**
Die PlaygroundPage ist extrem vereinfacht und hat fast alle Funktionen aus dem alten UI (`frontend_agents`) verloren.
**Was FEHLT (Vergleich zu `frontend_agents/public/htmlparts/part_workflow.html`):**
| Element | Alt (frontend_agents) | Neu (frontend_nyla) | Ziel |
|---------|----------------------|---------------------|------|
| **Layout** | 70/30 Spalten (Chat/Dashboard) | Single-Column, kein Dashboard | **Resizable Spalten mit Drag-Divider** |
| **Dashboard** | Hierarchisches Dashboard mit Rounds → Operations → Logs | ❌ Komplett fehlt | Logik portieren, Nyla-Styles |
| **Progress-Tracking** | Progress-Bars pro Operation | ❌ Komplett fehlt | Logik portieren, Nyla-Styles |
| **Collapse/Expand** | Für Rounds, Operations, Logs | ❌ Komplett fehlt | Logik portieren |
| **Prompt-Auswahl** | Dropdown mit Prompts aus API | ❌ Fehlt | Bestehende DropdownSelect nutzen |
| **Workflow-Modus** | Dropdown mit Enum aus Backend | ❌ Fehlt | Bestehende DropdownSelect nutzen |
| **Datei-Upload** | Button + Drag & Drop, mehrere Dateien | ❌ Fehlt | Bestehende DragDropOverlay nutzen |
| **Voice Recording** | Mikrofon-Button mit STT | ❌ Fehlt | Logik portieren, Nyla Button-Styles |
| **Stop/Reset Buttons** | Stop, Reset, Refresh Tokens | Nur Läuft... Button | Bestehende Button-Komponenten |
| **Data Statistics** | Sent/Received/Time/Tokens | ❌ Fehlt | Logik portieren, Nyla-Styles |
| **File Preview Modal** | Vorschau, Download, Copy, TTS | ❌ Fehlt | Bestehende Popup-Komponente nutzen |
| **Unified Content Area** | Kombinierte Logs + Messages chronologisch | Getrennte Listen | Bestehende Log/Messages nutzen |
| **Auto-Scroll** | Automatisches Scrollen | ❌ Fehlt | Bestehende AutoScroll nutzen |
| **Empty State** | Mit Link zur Einführung | Einfacher Text | Nyla-Styles |
**Vorhandene Hooks im `frontend_nyla/src/hooks/playground/`:**
```
- useDashboardInputForm.ts
- useDashboardLogTree.ts
- useWorkflowLifecycle.ts
- useWorkflowOperations.ts
- useWorkflowPolling.ts
- useWorkflows.ts
- playgroundUtils.ts
```
**Problem:** Die Hooks sind vorhanden, aber die UI-Komponenten nutzen sie nicht vollständig!
### 1.2 Workflows, Automations, Prompts, Files, Connections
**Aktueller Zustand:**
Diese Seiten haben eigene, einfache Table-Implementierungen statt `FormGeneratorTable`.
**Vergleich:**
| Feature | AdminUsersPage (korrekt) | WorkflowsPage (falsch) |
|---------|--------------------------|------------------------|
| `FormGeneratorTable` | ✅ Verwendet | ❌ Eigene `<table>` |
| `FormGeneratorForm` | ✅ Für Create/Edit Modals | ❌ Keine Modals |
| `attributes` vom Backend | ✅ Über Hook | ❌ Hardcoded Columns |
| `columns` aus attributes | ✅ Dynamisch generiert | ❌ Hardcoded |
| Backend-Pagination | ✅ Via hookData | ❌ Client-seitig |
| Backend-Sorting | ✅ Via hookData | ❌ Client-seitig |
| Backend-Filtering | ✅ Via hookData | ❌ Client-seitig |
| Inline-Editing | ✅ Für Boolean-Felder | ❌ Nicht unterstützt |
| RBAC Permissions | ✅ `permissions?.create !== 'n'` | ❌ Keine Prüfung |
| Action Buttons | ✅ Standard + Custom | ❌ Nur Delete |
| Loading State | ✅ Overlay | ❌ Einfacher Text |
| Empty State | ✅ Mit Icon und CTA | ❌ Einfacher Text |
| Error State | ✅ Mit Retry-Button | ❌ Einfacher Text |
---
## 2. Vorhandene Ressourcen im frontend_nyla
### 2.1 FormGenerator-System (vollständig implementiert)
**Komponenten:**
- `FormGeneratorTable` - Tabelle mit Pagination, Sorting, Filtering
- `FormGeneratorForm` - Backend-driven Formulare
- `FormGeneratorList` - Listenansicht
- `FormGeneratorControls` - Such- und Filter-Controls
- Action Buttons: Edit, Delete, View, Copy, Custom
**Features:**
- FK-Resolution (Foreign Keys anzeigen als Labels)
- TextMultilingual-Support
- Inline-Editing für Boolean-Felder
- Column-Resizing mit LocalStorage-Persistenz
- Multi-Column-Sorting
### 2.2 Vorhandene Hooks
**Workflows:**
- `useWorkflows.ts` - `useUserWorkflows()`, `useWorkflowOperations()`
**Automations:**
- `useAutomations.ts` - `useAutomations()`, `useAutomationOperations()`
**Prompts:**
- `usePrompts.ts` - `usePrompts()`, `usePromptOperations()`
**Files:**
- `useFiles.ts` - `useUserFiles()`, `useFileOperations()`
**Connections:**
- `useConnections.ts` - `useConnections()` (vollständig)
**Playground-spezifisch:**
- `hooks/playground/useDashboardInputForm.ts`
- `hooks/playground/useDashboardLogTree.ts`
- `hooks/playground/useWorkflowLifecycle.ts`
- `hooks/playground/useWorkflowOperations.ts`
- `hooks/playground/useWorkflowPolling.ts`
### 2.3 Bestehende Nyla UI-Komponenten (WIEDERVERWENDEN!)
> **WICHTIG:** Diese Komponenten definieren das Nyla Look & Feel und sollten
> bei der Implementation wiederverwendet werden!
**UiComponents (in `components/UiComponents/`):**
- `Button/` - Standard-Buttons, CreateButton, UploadButton
- `TextField/` - Input-Felder
- `DropdownSelect/` - Select-Komponente
- `DragDropOverlay/` - Für File-Upload mit Drag & Drop
- `Log/` - Log-Darstellung mit LogMessage
- `Messages/` - Chat-Nachrichten
- `Popup/` - Modal-Dialoge
- `Toast/` - Notifications
- `Tabs/` - Tab-Navigation
- `AutoScroll/` - Auto-Scroll-Funktionalität
**Diese Komponenten-Styles übernehmen:**
- Farben (CSS-Variablen)
- Typografie
- Abstände/Padding
- Border-Radien
- Hover-/Focus-States
---
## 3. Implementation Plan
### Phase 1: Chat Playground (Priorität HOCH)
#### 3.1.1 Layout-Refactoring
**Datei:** `frontend_nyla/src/pages/workflows/PlaygroundPage.tsx`
**Design-Prinzip:** Bestehendes Nyla Look & Feel beibehalten, nur fehlende Logik ergänzen!
1. **Resizable Spalten-Layout mit Drag-Divider:**
Statt eines fixen 70/30-Layouts wird ein **dynamisch anpassbares Layout** implementiert:
- Default: 70% Chat / 30% Dashboard
- Benutzer kann die Trennlinie mit der Maus verschieben
- Min/Max-Grenzen: Chat min 40%, Dashboard min 20%
- Spaltenbreite wird in LocalStorage persistiert
```tsx
import { useResizablePanels } from '../../hooks/useResizablePanels';
const PlaygroundPage: React.FC = () => {
// Hook für resizable panels mit LocalStorage-Persistenz
const {
leftWidth, // in Prozent (default 70)
isDragging,
handleMouseDown, // Auf Divider anwenden
} = useResizablePanels({
storageKey: 'playground-panel-width',
defaultLeftWidth: 70,
minLeftWidth: 40,
maxLeftWidth: 80,
});
return (
<div className={styles.playgroundContainer}>
<div className={styles.chatSection}>
<div className={styles.chatColumns}>
{/* Links - Chat Messages (dynamische Breite) */}
<div
className={styles.chatLeft}
style={{ width: `${leftWidth}%` }}
>
<UnifiedContentArea />
</div>
{/* Resizable Divider */}
<div
className={`${styles.resizeDivider} ${isDragging ? styles.dragging : ''}`}
onMouseDown={handleMouseDown}
>
<div className={styles.dividerHandle} />
</div>
{/* Rechts - Dashboard (dynamische Breite) */}
<div
className={styles.chatRight}
style={{ width: `${100 - leftWidth}%` }}
>
<WorkflowDashboard />
</div>
</div>
</div>
<div className={styles.workflowFooter}>
<UserInputArea />
<DataStatistics />
</div>
</div>
);
};
```
#### 3.1.1.1 Hook: useResizablePanels
**Zu erstellen:** `frontend_nyla/src/hooks/useResizablePanels.ts`
```typescript
import { useState, useCallback, useEffect, useRef } from 'react';
interface UseResizablePanelsOptions {
storageKey: string;
defaultLeftWidth: number; // in %
minLeftWidth: number; // in %
maxLeftWidth: number; // in %
}
export const useResizablePanels = ({
storageKey,
defaultLeftWidth,
minLeftWidth,
maxLeftWidth,
}: UseResizablePanelsOptions) => {
// Initialer Wert aus LocalStorage oder Default
const [leftWidth, setLeftWidth] = useState<number>(() => {
const stored = localStorage.getItem(storageKey);
if (stored) {
const parsed = parseFloat(stored);
if (!isNaN(parsed) && parsed >= minLeftWidth && parsed <= maxLeftWidth) {
return parsed;
}
}
return defaultLeftWidth;
});
const [isDragging, setIsDragging] = useState(false);
const containerRef = useRef<HTMLElement | null>(null);
// Mouse-Event-Handler
const handleMouseDown = useCallback((e: React.MouseEvent) => {
e.preventDefault();
setIsDragging(true);
containerRef.current = (e.target as HTMLElement).closest('.chatColumns');
}, []);
useEffect(() => {
if (!isDragging) return;
const handleMouseMove = (e: MouseEvent) => {
if (!containerRef.current) return;
const containerRect = containerRef.current.getBoundingClientRect();
const newLeftWidth = ((e.clientX - containerRect.left) / containerRect.width) * 100;
// Clamp zwischen min und max
const clampedWidth = Math.max(minLeftWidth, Math.min(maxLeftWidth, newLeftWidth));
setLeftWidth(clampedWidth);
};
const handleMouseUp = () => {
setIsDragging(false);
// In LocalStorage speichern
localStorage.setItem(storageKey, leftWidth.toString());
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
return () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
}, [isDragging, leftWidth, storageKey, minLeftWidth, maxLeftWidth]);
return {
leftWidth,
isDragging,
handleMouseDown,
setLeftWidth,
};
};
```
#### 3.1.1.2 CSS für Resizable Divider
**In `Playground.module.css` hinzufügen:**
```css
/* Resizable Divider - passend zum Nyla Design */
.resizeDivider {
width: 6px;
cursor: col-resize;
background-color: transparent;
position: relative;
flex-shrink: 0;
z-index: 10;
transition: background-color 0.15s ease;
}
.resizeDivider:hover,
.resizeDivider.dragging {
background-color: var(--color-border-hover, rgba(255, 255, 255, 0.1));
}
.dividerHandle {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 4px;
height: 40px;
border-radius: 2px;
background-color: var(--color-text-muted, rgba(255, 255, 255, 0.3));
opacity: 0;
transition: opacity 0.15s ease;
}
.resizeDivider:hover .dividerHandle,
.resizeDivider.dragging .dividerHandle {
opacity: 1;
}
/* Verhindert Text-Selektion während Drag */
.chatColumns.dragging {
user-select: none;
}
```
#### 3.1.2 Neue Komponenten erstellen
> **Design-Prinzip:** Alle neuen Komponenten müssen das bestehende Nyla Look & Feel nutzen!
> - Bestehende CSS-Variablen verwenden (aus `:root` oder Theme)
> - Vorhandene Button-/Input-Styles wiederverwenden
> - Keine neuen Farbschemata oder Typografie einführen
> - Komponenten-Styles in Module-CSS kapseln
**Zu erstellen in `frontend_nyla/src/components/Playground/`:**
| Komponente | Beschreibung | Logik basiert auf | Styles |
|------------|--------------|-------------------|--------|
| `WorkflowDashboard.tsx` | Hierarchisches Dashboard | `workflowUiRendererDashboard.js` | Nyla-Styles |
| `UnifiedContentArea.tsx` | Kombinierte Logs + Messages | `workflowUiRenderer.js` | Nyla-Styles |
| `UserInputArea.tsx` | Prompt-Auswahl, Input, Buttons | `part_workflow.html` | Bestehende Nyla Input-Styles |
| `DataStatistics.tsx` | Sent/Received/Time/Tokens | `part_workflow.html` | Nyla-Styles |
| `FilePreviewModal.tsx` | Datei-Vorschau | Existiert teilweise | Bestehende Modal-Styles |
| `WorkflowControls.tsx` | Stop, Reset, Start Buttons | `workflowUiControls.js` | Bestehende Button-Styles |
**Wichtig:** Diese Komponenten sollen die **Logik** aus dem alten `frontend_agents` übernehmen,
aber das **visuelle Design** von Nyla beibehalten. Das heisst:
- Hooks und State-Management aus `frontend_agents` portieren
- CSS-Styles aus den bestehenden Nyla-Komponenten verwenden/erweitern
#### 3.1.3 Dashboard Implementation
**State-Struktur:**
```typescript
interface DashboardLogTree {
rounds: Map<number, {
operations: Map<string, Operation>;
rootOperations: string[];
expanded: boolean;
isCompleted: boolean;
}>;
operations: Map<string, {
logs: Map<string, LogEntry>;
parentId: string | null;
expanded: boolean;
latestProgress: number | null;
latestStatus: string | null;
roundNumber: number;
}>;
logExpandedStates: Map<string, boolean>;
currentRound: number | null;
}
```
**Verwendung von existierendem Hook:**
```typescript
import { useDashboardLogTree } from '../../hooks/playground/useDashboardLogTree';
const {
dashboardLogTree,
processDashboardLogs,
toggleRoundExpanded,
toggleOperationExpanded,
clearDashboard,
} = useDashboardLogTree();
```
#### 3.1.4 Input-Bereich Implementation
**Neue Elemente:**
- Prompt-Dropdown (aus `usePrompts()`)
- Workflow-Modus-Dropdown (aus `/api/options/workflow.mode`)
- File-Upload mit Drag & Drop
- Voice-Recording-Button
- Stop/Reset/Start-Buttons
### Phase 2-6: Data-Seiten (Workflows, Automations, Prompts, Files, Connections)
> **Design-Prinzip für alle Data-Seiten:**
> - `FormGeneratorTable` und `FormGeneratorForm` verwenden (bereits Nyla-konform)
> - Bestehende Page-Header-Styles aus Admin-Seiten übernehmen
> - Keine neuen Layouts/Designs einführen
> - Nur fehlende Hooks/Logik ergänzen
### Phase 2: Workflows Page (Priorität HOCH)
#### 3.2.1 Hook erweitern
**Datei:** `frontend_nyla/src/hooks/useWorkflows.ts`
Erweitern um:
```typescript
export const useWorkflowsAdmin = () => {
const { data, loading, error, refetch } = useApi<WorkflowResponse>('/api/workflows/');
// Attributes vom Backend
const { data: attributesResponse } = useApi<AttributesResponse>('/api/attributes/ChatWorkflow');
return {
data: data?.items || [],
attributes: attributesResponse?.attributes || [],
columns: generateColumns(attributesResponse?.attributes),
permissions: data?.permissions,
pagination: data?.pagination,
loading,
error,
refetch,
// Operations
handleDelete: async (id: string) => { /* ... */ },
handleInlineUpdate: async (id: string, data: Partial<Workflow>) => { /* ... */ },
};
};
```
#### 3.2.2 Page umschreiben
**Datei:** `frontend_nyla/src/pages/workflows/WorkflowsPage.tsx`
Nach Pattern von `AdminUsersPage.tsx`:
```tsx
import { FormGeneratorTable } from '../../components/FormGenerator';
import { useWorkflowsAdmin } from '../../hooks/useWorkflows';
export const WorkflowsPage: React.FC = () => {
const {
data: workflows,
attributes,
columns,
permissions,
pagination,
loading,
error,
refetch,
handleDelete,
handleInlineUpdate,
} = useWorkflowsAdmin();
return (
<div className={styles.page}>
<PageHeader title="Workflows" onRefresh={refetch} />
<FormGeneratorTable
data={workflows}
columns={columns}
loading={loading}
pagination={true}
actionButtons={[
{ type: 'edit', onAction: handleContinue },
{ type: 'delete' },
]}
customActions={[
{
id: 'continue',
icon: <FaPlay />,
onClick: handleContinue,
title: 'Workflow fortsetzen',
}
]}
onDelete={handleDelete}
hookData={{
refetch,
permissions,
pagination,
handleDelete,
handleInlineUpdate,
}}
/>
</div>
);
};
```
### Phase 3: Automations Page
**Analog zu Workflows Page:**
1. Hook erweitern: `useAutomationsAdmin()`
2. Page umschreiben mit `FormGeneratorTable`
3. Custom Actions: Execute, Toggle Active
### Phase 4: Prompts Page
**Spezifische Anpassungen:**
1. Hook erweitern: `usePromptsAdmin()`
2. Page umschreiben mit `FormGeneratorTable`
3. Create/Edit Modal mit `FormGeneratorForm`
4. Content-Feld als Textarea
### Phase 5: Files Page
**Spezifische Anpassungen:**
1. Hook erweitern: `useFilesAdmin()`
2. Page umschreiben mit `FormGeneratorTable`
3. Custom Actions: Download, Preview
4. Upload-Button mit `UploadButton`-Komponente
### Phase 6: Connections Page
**Spezifische Anpassungen:**
1. Hook erweitern: `useConnectionsAdmin()`
2. Page umschreiben mit `FormGeneratorTable`
3. Custom Actions: Connect, Refresh Token
4. Provider-spezifische Buttons (Google, Microsoft)
---
## 4. Detaillierte Änderungsliste
### 4.1 Zu erstellende Dateien
```
frontend_nyla/src/
├── hooks/
│ └── useResizablePanels.ts # NEU: Hook für resizable panels
├── components/
│ └── Playground/
│ ├── index.ts
│ ├── Playground.module.css # NEU: Layout-Styles inkl. Divider
│ ├── WorkflowDashboard/
│ │ ├── index.ts
│ │ ├── WorkflowDashboard.tsx # Logik aus workflowUiRendererDashboard.js
│ │ ├── WorkflowDashboard.module.css
│ │ ├── DashboardRound.tsx
│ │ ├── DashboardOperation.tsx
│ │ └── DashboardProgressBar.tsx
│ ├── UnifiedContentArea/
│ │ ├── index.ts
│ │ ├── UnifiedContentArea.tsx # Logik aus workflowUiRenderer.js
│ │ ├── UnifiedContentArea.module.css
│ │ ├── MessageItem.tsx
│ │ └── LogItem.tsx
│ ├── UserInputArea/
│ │ ├── index.ts
│ │ ├── UserInputArea.tsx # Logik aus workflow.js + part_workflow.html
│ │ ├── UserInputArea.module.css
│ │ ├── PromptSelector.tsx
│ │ ├── WorkflowModeSelector.tsx
│ │ └── VoiceRecordButton.tsx
│ ├── WorkflowControls/
│ │ ├── index.ts
│ │ ├── WorkflowControls.tsx # Logik aus workflowUiControls.js
│ │ └── WorkflowControls.module.css
│ └── DataStatistics/
│ ├── index.ts
│ ├── DataStatistics.tsx
│ └── DataStatistics.module.css
```
> **Hinweis:** Alle `.module.css` Dateien sollen bestehende Nyla CSS-Variablen nutzen
> und sich nahtlos ins bestehende Design einfügen!
### 4.2 Zu ändernde Dateien
| Datei | Änderung |
|-------|----------|
| `pages/workflows/PlaygroundPage.tsx` | Kompletter Rewrite mit neuen Komponenten |
| `pages/workflows/WorkflowsPage.tsx` | Umschreiben auf FormGeneratorTable |
| `pages/workflows/AutomationsPage.tsx` | Umschreiben auf FormGeneratorTable |
| `pages/basedata/PromptsPage.tsx` | Umschreiben auf FormGeneratorTable |
| `pages/basedata/FilesPage.tsx` | Umschreiben auf FormGeneratorTable |
| `pages/basedata/ConnectionsPage.tsx` | Umschreiben auf FormGeneratorTable |
| `hooks/useWorkflows.ts` | Admin-Hook hinzufügen |
| `hooks/useAutomations.ts` | Admin-Hook hinzufügen |
| `hooks/usePrompts.ts` | Admin-Hook hinzufügen |
| `hooks/useFiles.ts` | Admin-Hook hinzufügen |
| `hooks/useConnections.ts` | Admin-Hook hinzufügen |
### 4.3 CSS-Anpassungen
**Neue CSS-Module:**
- `Playground.module.css` - Hauptlayout
- `WorkflowDashboard.module.css` - Dashboard-Styles
- `UnifiedContentArea.module.css` - Content-Styles
**Bestehende zu aktualisieren:**
- `WorkflowPages.module.css` - Für alle Workflow-Seiten
---
## 5. API-Endpunkte (bereits vorhanden)
| Endpunkt | Verwendung |
|----------|------------|
| `/api/workflows/` | Liste, CRUD |
| `/api/workflows/{id}` | Einzelner Workflow |
| `/api/workflows/{id}/chat-data` | Unified Chat-Daten |
| `/api/automations/` | Liste, CRUD |
| `/api/prompts/` | Liste, CRUD |
| `/api/files/` | Liste, CRUD |
| `/api/connections/` | Liste, CRUD |
| `/api/attributes/{EntityType}` | Attribute-Definitionen |
| `/api/options/workflow.mode` | Workflow-Modus-Enum |
---
## 6. Migrations-Checkliste
### Chat Playground
- [ ] `useResizablePanels` Hook erstellen
- [ ] Resizable Spalten-Layout implementieren (mit Drag-Divider)
- [ ] WorkflowDashboard-Komponente erstellen (Logik aus frontend_agents, Nyla-Styles)
- [ ] UnifiedContentArea-Komponente erstellen (Logik aus frontend_agents, Nyla-Styles)
- [ ] UserInputArea mit allen Features erstellen (Nyla Input-Styles)
- [ ] DataStatistics-Komponente erstellen (Nyla-Styles)
- [ ] Polling integrieren (bestehende Hooks nutzen)
- [ ] File-Upload mit Drag & Drop (bestehende DragDropOverlay nutzen)
- [ ] Voice-Recording integrieren
- [ ] LocalStorage-Persistenz für Panel-Breite
### Workflows Page
- [ ] Hook `useWorkflowsAdmin` erstellen
- [ ] Page auf FormGeneratorTable umstellen
- [ ] Action Buttons konfigurieren
- [ ] RBAC-Permissions implementieren
### Automations Page
- [ ] Hook `useAutomationsAdmin` erstellen
- [ ] Page auf FormGeneratorTable umstellen
- [ ] Toggle Active implementieren
- [ ] Execute-Action implementieren
### Prompts Page
- [ ] Hook `usePromptsAdmin` erstellen
- [ ] Page auf FormGeneratorTable umstellen
- [ ] Create/Edit Modals mit FormGeneratorForm
- [ ] Content-Textarea korrekt anzeigen
### Files Page
- [ ] Hook `useFilesAdmin` erstellen
- [ ] Page auf FormGeneratorTable umstellen
- [ ] Download/Preview Actions
- [ ] Upload-Button integrieren
### Connections Page
- [ ] Hook `useConnectionsAdmin` erstellen
- [ ] Page auf FormGeneratorTable umstellen
- [ ] Connect/Refresh Actions
- [ ] Provider-spezifische Buttons
---
## 7. Schätzung Aufwand
| Phase | Aufwand | Priorität |
|-------|---------|-----------|
| Phase 1: Chat Playground | Hoch | KRITISCH |
| Phase 2: Workflows Page | Mittel | Hoch |
| Phase 3: Automations Page | Mittel | Hoch |
| Phase 4: Prompts Page | Niedrig | Mittel |
| Phase 5: Files Page | Niedrig | Mittel |
| Phase 6: Connections Page | Niedrig | Mittel |
---
## 8. Anhang: Pattern-Referenz
### Standard-Page-Struktur
```tsx
import React, { useState, useMemo } from 'react';
import { FormGeneratorTable, FormGeneratorForm } from '../../components/FormGenerator';
import { useEntityAdmin, useEntityOperations } from '../../hooks/useEntity';
import styles from './Page.module.css';
interface Entity {
id: string;
[key: string]: any;
}
export const EntityPage: React.FC = () => {
// Hooks
const {
data,
attributes,
columns,
permissions,
pagination,
loading,
error,
refetch,
fetchById,
handleInlineUpdate,
updateOptimistically,
} = useEntityAdmin();
const {
handleCreate,
handleUpdate,
handleDelete,
} = useEntityOperations();
// State
const [showCreateModal, setShowCreateModal] = useState(false);
const [editingItem, setEditingItem] = useState<Entity | null>(null);
// Permissions
const canCreate = permissions?.create !== 'n';
const canUpdate = permissions?.update !== 'n';
const canDelete = permissions?.delete !== 'n';
// Form Attributes
const formAttributes = useMemo(() => {
return (attributes || []).filter(attr => !['id'].includes(attr.name));
}, [attributes]);
// Handlers
const handleEditClick = async (item: Entity) => {
const fullItem = await fetchById(item.id);
if (fullItem) setEditingItem(fullItem);
};
const handleCreateSubmit = async (data: Partial<Entity>) => {
const result = await handleCreate(data);
if (result.success) {
setShowCreateModal(false);
refetch();
}
};
const handleEditSubmit = async (data: Partial<Entity>) => {
if (!editingItem) return;
const result = await handleUpdate(editingItem.id, data);
if (result.success) {
setEditingItem(null);
refetch();
}
};
// Render
return (
<div className={styles.page}>
<PageHeader
title="Entities"
onRefresh={refetch}
onCreate={canCreate ? () => setShowCreateModal(true) : undefined}
/>
<FormGeneratorTable
data={data}
columns={columns}
loading={loading}
pagination={true}
actionButtons={[
...(canUpdate ? [{ type: 'edit', onAction: handleEditClick }] : []),
...(canDelete ? [{ type: 'delete' }] : []),
]}
onDelete={handleDelete}
hookData={{
refetch,
permissions,
pagination,
handleDelete,
handleInlineUpdate,
updateOptimistically,
}}
/>
{/* Create Modal */}
{showCreateModal && (
<Modal onClose={() => setShowCreateModal(false)}>
<FormGeneratorForm
attributes={formAttributes}
mode="create"
onSubmit={handleCreateSubmit}
onCancel={() => setShowCreateModal(false)}
/>
</Modal>
)}
{/* Edit Modal */}
{editingItem && (
<Modal onClose={() => setEditingItem(null)}>
<FormGeneratorForm
attributes={formAttributes}
data={editingItem}
mode="edit"
onSubmit={handleEditSubmit}
onCancel={() => setEditingItem(null)}
/>
</Modal>
)}
</div>
);
};
```
---
## 9. Design-Konsistenz-Checklist
Bei jeder Implementation prüfen:
- [ ] **CSS-Variablen:** Werden bestehende Nyla CSS-Variablen verwendet? (Farben, Abstände, etc.)
- [ ] **Komponenten:** Werden bestehende UiComponents wiederverwendet wo möglich?
- [ ] **Styles:** Sind neue Styles konsistent mit bestehendem Nyla-Design?
- [ ] **Keine Neuerungen:** Wurden keine neuen Design-Patterns eingeführt?
- [ ] **Dark/Light Mode:** Funktioniert die Komponente in beiden Modi?
- [ ] **Responsive:** Funktioniert die Komponente auf verschiedenen Bildschirmgrössen?
---
*Dokumentation erstellt am 23. Januar 2026*
*Aktualisiert: Resizable Panels und Design-Prinzipien hinzugefügt*

View file

@ -1,367 +0,0 @@
# Backend Feature-Struktur Analyse
## 1. Übersicht Aktuelle Architektur
### 1.1 Feature-Hierarchie
```
Mandate (Mandant)
└── FeatureInstance (Feature-Instanz)
└── Feature-Datenbank-Tabellen
└── Datensätze mit mandateId + featureInstanceId
```
### 1.2 Datenbank-Struktur
| Datenbank | Zweck | Feature |
|-----------|-------|---------|
| `poweron_app` | Zentrale App-Daten | System (User, Mandate, Roles, Features) |
| `poweron_trustee` | Treuhand-Feature | Trustee |
| `poweron_chat` | Chat-Feature | Chatbot/Workflow |
| `poweron_realestate` | Immobilien-Feature | RealEstate |
---
## 2. Feature-Definitionen
### 2.1 Datenmodelle (`datamodels/`)
| Feature | Datenmodell-Datei | Tabellen |
|---------|-------------------|----------|
| **Trustee** | `datamodelTrustee.py` | TrusteeOrganisation, TrusteeRole, TrusteeAccess, TrusteeContract, TrusteeDocument, TrusteePosition, TrusteePositionDocument |
| **RealEstate** | `datamodelRealEstate.py` | RealEstateProperty, RealEstateUnit, etc. |
| **Chat/Workflow** | `datamodelChat.py`, `datamodelWorkflow.py` | ChatSession, ChatMessage, WorkflowExecution, etc. |
| **Files** | `datamodelFiles.py` | StoredFile, FileMetadata |
| **System** | `datamodelUam.py`, `datamodelMembership.py` | User, Mandate, FeatureAccess |
| **RBAC** | `datamodelRbac.py` | Role, AccessRule |
| **Features** | `datamodelFeatures.py` | Feature, FeatureInstance |
### 2.2 Zentrale Feature-Modelle (`datamodelFeatures.py`)
```python
class Feature:
code: str # PK: "trustee", "chatbot", "realestate"
label: dict # I18n: {"de": "Treuhand", "en": "Trustee"}
icon: str # "mdi-account-group"
class FeatureInstance:
id: str # UUID
featureCode: str # FK → Feature.code
mandateId: str # FK → Mandate.id (CASCADE DELETE)
label: str # "Buchhaltung 2025"
enabled: bool # True/False
```
---
## 3. Interface-Schicht (`interfaces/`)
### 3.1 Feature-spezifische Interfaces
| Interface-Datei | Feature | Datenbank |
|-----------------|---------|-----------|
| `interfaceDbTrusteeObjects.py` | Trustee | poweron_trustee |
| `interfaceDbChatObjects.py` | Chatbot | poweron_chat |
| `interfaceDbRealEstateObjects.py` | RealEstate | poweron_realestate |
| `interfaceDbAppObjects.py` | System | poweron_app |
| `interfaceFeatures.py` | Feature-Verwaltung | poweron_app |
### 3.2 Trustee-Interface Beispiel
```python
class TrusteeObjects:
def __init__(self, currentUser, mandateId, featureInstanceId):
# Verbindet sich mit poweron_trustee Datenbank
self.mandateId = mandateId
self.featureInstanceId = featureInstanceId
# Initialisiert RBAC
self.rbac = RbacClass(...)
# CRUD mit RBAC + Feature-Level Filterung
def createOrganisation(self, data):
data["mandateId"] = self.mandateId
data["featureInstanceId"] = self.featureInstanceId
...
```
### 3.3 Multi-Tenant Isolation
Jedes Interface:
1. Erhält `mandateId` und `featureInstanceId` aus dem Request-Context
2. Setzt diese Werte automatisch bei CREATE
3. Filtert bei READ/UPDATE/DELETE nach diesen Werten
4. Prüft RBAC-Berechtigungen (System + Feature-Level)
---
## 4. Route-Schicht (`routes/`)
### 4.1 Feature-Routes
| Route-Datei | Prefix | Feature |
|-------------|--------|---------|
| `routeFeatures.py` | `/api/features` | Feature-Verwaltung |
| `routeFeatureTrustee.py` | `/api/trustee` | Trustee-CRUD |
| `routeFeatureChatbot.py` | `/api/chatbot` | Chatbot |
| `routeFeatureAutomation.py` | `/api/automation` | Workflow |
| `routeFeatureRealEstate.py` | `/api/realestate` | Immobilien |
### 4.2 Trustee-Routen (mit instanceId im Pfad) ✅
```
/api/trustee/{instanceId}/organisations GET, POST
/api/trustee/{instanceId}/organisations/{id} GET, PUT, DELETE
/api/trustee/{instanceId}/roles GET, POST
/api/trustee/{instanceId}/roles/{id} GET, PUT, DELETE
/api/trustee/{instanceId}/access GET, POST
/api/trustee/{instanceId}/access/{id} GET, PUT, DELETE
/api/trustee/{instanceId}/contracts GET, POST
/api/trustee/{instanceId}/contracts/{id} GET, PUT, DELETE
/api/trustee/{instanceId}/documents GET, POST
/api/trustee/{instanceId}/documents/{id} GET, PUT, DELETE
/api/trustee/{instanceId}/positions GET, POST
/api/trustee/{instanceId}/positions/{id} GET, PUT, DELETE
/api/trustee/{instanceId}/position-documents GET, POST, DELETE
```
**Validierung:** Jeder Request validiert automatisch:
1. Existenz der FeatureInstance
2. Zugehörigkeit zum Feature "trustee"
3. User-Zugriff auf die Instance (via FeatureAccess)
### 4.3 Feature-Verwaltungs-Routen
```
/api/features/ GET, POST (SysAdmin)
/api/features/{code} GET
/api/features/instances GET, POST
/api/features/instances/{id} GET, DELETE
/api/features/instances/{id}/sync-roles POST
/api/features/my GET (User's Instanzen)
/api/features/templates/roles GET, POST (SysAdmin)
```
---
## 5. Status & Verbesserungsbedarf
### 5.1 Erledigte Verbesserungen ✅
| Verbesserung | Status | Beschreibung |
|--------------|--------|--------------|
| **URL mit instanceId** | ✅ Erledigt | Trustee Routes haben jetzt `/{instanceId}/` Prefix |
| **Instance-Validierung** | ✅ Erledigt | Automatische Prüfung von Existenz, Feature-Typ und User-Zugriff |
| **Frontend API angepasst** | ✅ Erledigt | `trusteeApi.ts` nutzt neue URL-Struktur |
### 5.2 Offene Punkte
| Problem | Beschreibung | Priorität |
|---------|--------------|-----------|
| **Andere Features** | Chatbot, Workflow, RealEstate noch ohne instanceId | Mittel |
| **Keine Feature-Registry** | Feature-Views sind hardcoded | Niedrig |
### 5.3 Aktuelle URL-Struktur (Trustee)
```
# MIT instanceId im Pfad (implementiert):
/api/trustee/{instanceId}/organisations
/api/trustee/{instanceId}/contracts
/api/trustee/{instanceId}/documents
/api/trustee/{instanceId}/positions
...
```
---
## 6. Empfohlene Feature-Struktur
### 6.1 Neue Route-Struktur pro Feature
```
/api/{featureCode}/{instanceId}/{entity}
```
Beispiel Trustee:
```
/api/trustee/{instanceId}/organisations
/api/trustee/{instanceId}/contracts
/api/trustee/{instanceId}/documents
/api/trustee/{instanceId}/positions
```
Beispiel Chatbot:
```
/api/chatbot/{instanceId}/sessions
/api/chatbot/{instanceId}/messages
```
### 6.2 Feature-Definition in DB (poweron_app.Feature)
```sql
INSERT INTO "Feature" (code, label, icon) VALUES
('trustee', '{"de": "Treuhand", "en": "Trustee"}', 'mdi-account-group'),
('chatbot', '{"de": "Chatbot", "en": "Chatbot"}', 'mdi-robot'),
('workflow', '{"de": "Workflow", "en": "Workflow"}', 'mdi-sitemap'),
('realestate', '{"de": "Immobilien", "en": "Real Estate"}', 'mdi-home-city');
```
### 6.3 Feature-Datenbank-Zuordnung
```python
FEATURE_DATABASES = {
"trustee": "poweron_trustee",
"chatbot": "poweron_chat",
"workflow": "poweron_chat", # Gleiche DB wie chatbot
"realestate": "poweron_realestate",
}
```
---
## 7. Bestehende Feature-Tabellen
### 7.1 Trustee (poweron_trustee)
| Tabelle | Beschreibung | Felder |
|---------|--------------|--------|
| `TrusteeOrganisation` | Firmen | id, label, enabled, mandateId, featureInstanceId |
| `TrusteeRole` | Rollen | id, desc, mandateId, featureInstanceId |
| `TrusteeAccess` | Zugriffe | id, organisationId, roleId, userId, contractId, mandateId, featureInstanceId |
| `TrusteeContract` | Verträge | id, organisationId, label, enabled, mandateId, featureInstanceId |
| `TrusteeDocument` | Dokumente | id, organisationId, contractId, documentName, documentData, mandateId, featureInstanceId |
| `TrusteePosition` | Positionen | id, organisationId, contractId, valuta, bookingAmount, ..., mandateId, featureInstanceId |
| `TrusteePositionDocument` | Verknüpfung | id, documentId, positionId, mandateId, featureInstanceId |
### 7.2 Chatbot/Workflow (poweron_chat)
| Tabelle | Beschreibung |
|---------|--------------|
| `ChatSession` | Chat-Sitzungen |
| `ChatMessage` | Nachrichten |
| `WorkflowExecution` | Workflow-Ausführungen |
| `WorkflowStep` | Workflow-Schritte |
### 7.3 RealEstate (poweron_realestate)
| Tabelle | Beschreibung |
|---------|--------------|
| `RealEstateProperty` | Liegenschaften |
| `RealEstateUnit` | Einheiten |
| `RealEstateTenant` | Mieter |
---
## 8. RBAC-Struktur
### 8.1 System-Level RBAC (poweron_app.Role)
```python
class Role:
id: str
roleLabel: str # "admin", "viewer"
featureCode: str # "trustee", None für System-Rollen
mandateId: str # None für globale Templates
featureInstanceId: str # None für Mandate-Level Rollen
isSystemRole: bool # True für system-weite Rollen
```
### 8.2 Access Rules
```python
class AccessRule:
roleId: str
context: str # "DATA", "UI", "RESOURCE"
item: str # "TrusteeContract", "trustee-contracts"
view: bool
read: str # "a" (all), "g" (group), "o" (own), "n" (none)
create: str
update: str
delete: str
```
### 8.3 Feature-Level RBAC (Trustee-spezifisch)
```python
# TrusteeAccess definiert Zugriff auf Organisationen
# Rollen: "admin", "operate", "userreport"
TrusteeAccess:
userId → User
organisationId → TrusteeOrganisation
roleId → TrusteeRole ("admin", "operate", "userreport")
contractId → TrusteeContract (optional, für Vertrags-Scope)
```
---
## 9. Migration / Nächste Schritte
### 9.1 Backend-Änderungen
1. **Routes mit instanceId**
- [x] `routeFeatureTrustee.py` erweitern: `/api/trustee/{instanceId}/...`
- [ ] Andere Features entsprechend anpassen (Chatbot, Workflow, RealEstate)
2. **Feature-Registry**
- [ ] `FEATURE_REGISTRY` in Backend implementieren
- [ ] Views und Tabellen pro Feature definieren
3. **Datenbank-Isolation**
- [x] Trustee-Tabellen haben `featureInstanceId`
- [ ] Prüfen, dass alle anderen Feature-Tabellen `featureInstanceId` haben
### 9.2 Frontend-Anpassungen (bereits implementiert)
- [x] URL-Struktur: `/mandates/{mandateId}/{featureCode}/{instanceId}/...`
- [x] `useCurrentInstance()` Hook
- [x] `FeatureStore` mit `/api/features/my`
- [x] Permission-Hooks für Tabellen und Views
- [x] `trusteeApi.ts` mit instanceId im URL-Pfad ✅
---
## 10. Zusammenfassung
### Aktuelle Struktur (IST)
```
Feature → hat eigene Datenbank
Feature → hat eigene Datenmodelle (datamodel*.py)
Feature → hat eigene Interface-Klasse (interface*.py)
Feature → hat eigene Routes (routeFeature*.py)
FeatureInstance → gehört zu Mandate
Datensätze → haben mandateId + featureInstanceId
```
### Empfohlene Struktur (SOLL)
```
1. Routes mit instanceId im Pfad
2. Feature-Registry im Backend
3. Konsistente RBAC-Prüfung in allen Interfaces
4. View-Definitionen pro Feature
5. Einheitliche Datenbank-Isolation
```
### Vorhandene Features
| Feature | Datenbank | Routes | Status |
|---------|-----------|--------|--------|
| Trustee | poweron_trustee | `/api/trustee/` | Produktiv |
| Chatbot | poweron_chat | `/api/chatbot/` | Produktiv |
| Workflow | poweron_chat | `/api/automation/` | Produktiv |
| RealEstate | poweron_realestate | `/api/realestate/` | In Entwicklung |
---
*Erstellt: 2026-01-17*
*Aktualisiert: 2026-01-19*
*Analyse-Basis: poweron/gateway Backend-Code*
## Änderungshistorie
| Datum | Änderung |
|-------|----------|
| 2026-01-19 | Trustee Routes auf instanceId im URL-Pfad umgestellt |
| 2026-01-19 | Instance-Validierung mit Zugriffsprüfung implementiert |
| 2026-01-17 | Initiale Analyse erstellt |

View file

@ -1,122 +0,0 @@
# Feature Naming Convention - Analyse & Umsetzung
## 1. Naming Convention Standard
Jedes Feature folgt dieser Namenskonvention:
| Komponente | Dateiname | Beispiel (Feature: Trustee) |
|------------|-----------|----------------------------|
| Datenmodell | `datamodelXxx.py` | `datamodelTrustee.py` |
| Interface | `interfaceDbXxx.py` | `interfaceDbTrustee.py` |
| Route | `routeFeatureXxx.py` | `routeFeatureTrustee.py` |
---
## 2. Aktuelle Struktur (IST)
### 2.1 Trustee Feature ✅
| Komponente | Dateiname | Status |
|------------|-----------|--------|
| Datenmodell | `datamodelTrustee.py` | ✅ |
| Interface | `interfaceDbTrustee.py` | ✅ |
| Route | `routeFeatureTrustee.py` | ✅ |
### 2.2 Chat-basierte Features (Chatbot, ChatDynamic, Workflow) ✅
| Komponente | Dateiname | Beschreibung |
|------------|-----------|--------------|
| **Core-Datenmodell** | `datamodelChat.py` | Shared Model für alle Chat-Features |
| Interface | `interfaceDbChatbot.py` | DB-Interface für Chat-Daten |
| Route Chatbot | `routeFeatureChatbot.py` | Chatbot-Endpoints |
| Route ChatDynamic | `routeFeatureChatDynamic.py` | Dynamic Chat-Endpoints |
| Route Workflow | `routeFeatureWorkflow.py` | Workflow-Endpoints |
### 2.3 RealEstate Feature ✅
| Komponente | Dateiname | Status |
|------------|-----------|--------|
| Datenmodell | `datamodelRealEstate.py` | ✅ |
| Interface | `interfaceDbRealEstate.py` | ✅ |
| Route | `routeFeatureRealEstate.py` | ✅ |
### 2.4 Workflow Feature ✅
| Komponente | Dateiname | Status |
|------------|-----------|--------|
| Datenmodell | `datamodelWorkflow.py` | ✅ |
| Interface | (Teil von Chatbot DB) | ✅ |
| Route | `routeFeatureWorkflow.py` | ✅ |
---
## 3. Zusammenfassung der Änderungen
### 3.1 Umbenennungen ✅ ERLEDIGT
| Von | Nach | Status |
|-----|------|--------|
| `interfaceDbTrusteeObjects.py` | `interfaceDbTrustee.py` | ✅ |
| `interfaceDbChatObjects.py` | `interfaceDbChatbot.py` | ✅ |
| `interfaceDbRealEstateObjects.py` | `interfaceDbRealEstate.py` | ✅ |
| `routeFeatureAutomation.py` | `routeFeatureWorkflow.py` | ✅ |
> **Hinweis:** `datamodelChat.py` wurde **NICHT** umbenannt, da es ein Core-Datenmodell ist,
> das von mehreren Features verwendet wird (Chatbot, ChatDynamic, Workflow).
### 3.2 Betroffene Import-Stellen
Nach jeder Umbenennung müssen Imports aktualisiert werden in:
- `app.py` (Router-Registrierung)
- Andere Route-Dateien (Cross-Imports)
- Interface-Dateien (Model-Imports)
- Feature-Module (Interface-Imports)
---
## 4. Feature-Übersicht nach Umbenennung
| Feature | Datenmodell | Interface | Route | Datenbank |
|---------|-------------|-----------|-------|-----------|
| **Trustee** | `datamodelTrustee.py` | `interfaceDbTrustee.py` | `routeFeatureTrustee.py` | `poweron_trustee` |
| **Chatbot** | `datamodelChat.py` (shared) | `interfaceDbChatbot.py` | `routeFeatureChatbot.py` | `poweron_chat` |
| **ChatDynamic** | `datamodelChat.py` (shared) | `interfaceDbChatbot.py` | `routeFeatureChatDynamic.py` | `poweron_chat` |
| **Workflow** | `datamodelChat.py` + `datamodelWorkflow.py` | `interfaceDbChatbot.py` | `routeFeatureWorkflow.py` | `poweron_chat` |
| **RealEstate** | `datamodelRealEstate.py` | `interfaceDbRealEstate.py` | `routeFeatureRealEstate.py` | `poweron_realestate` |
---
## 5. Nicht-Feature Dateien (bleiben unverändert)
Diese Dateien sind **keine Feature-Dateien** und folgen anderen Konventionen:
### Datenmodelle (System/Shared)
- `datamodelUam.py` - User Access Management
- `datamodelRbac.py` - Role-Based Access Control
- `datamodelFeatures.py` - Feature/FeatureInstance Definitionen
- `datamodelMembership.py` - FeatureAccess
- `datamodelPagination.py` - Pagination Utilities
- `datamodelAudit.py` - Audit Logging
- `datamodelFiles.py` - File Storage
- `datamodelMessaging.py` - Email/SMS
- `datamodelInvitation.py` - User Invitations
### Interfaces (System/Shared)
- `interfaceDbAppObjects.py` - Zentrale App-Datenbank
- `interfaceFeatures.py` - Feature-Verwaltung
- `interfaceRbac.py` - RBAC-Operationen
- `interfaceMessaging.py` - Messaging-Service
- `interfaceBootstrap.py` - System-Bootstrap
### Routes (System/Admin)
- `routeDataUsers.py` - User CRUD
- `routeDataMandates.py` - Mandate CRUD
- `routeRbac.py` - RBAC-Verwaltung
- `routeFeatures.py` - Feature-Verwaltung
- `routeAdmin.py` - Admin-Funktionen
- `routeSecurity*.py` - Auth-Endpoints
---
*Erstellt: 2026-01-19*
*Status: ✅ Alle Umbenennungen abgeschlossen*

View file

@ -1,358 +0,0 @@
# Multi-Tenant Gateway - Implementation Progress Tracking
**Konzept:** [mandate_implementation_gateway.md](./mandate_implementation_gateway.md)
**Letzte Aktualisierung:** 2026-01-18
---
## Gesamtfortschritt
| Phase | Status | Fortschritt |
|-------|--------|-------------|
| Phase 1: Foundation | ✅ Abgeschlossen | 7/7 |
| Phase 2: RBAC Core | ✅ Abgeschlossen | 4/4 |
| Phase 3: Auth & Context | ✅ Abgeschlossen | 6/6 |
| Phase 4: Routes Migration | ✅ Abgeschlossen | 11/11 |
| Phase 5: Interfaces Migration | ✅ Abgeschlossen | 5/5 |
| Phase 6: Features Migration | ✅ Abgeschlossen | 7/7 |
| Phase 7: Cleanup | ✅ Abgeschlossen | 3/3 |
| Phase 8: Neue Features | ✅ Abgeschlossen | 5/5 |
**Alle Phasen abgeschlossen!**
---
## Phase 1: Foundation (Datenmodelle & DB) ✅
**Ziel:** Neue Datenstrukturen ohne Breaking Changes am bestehenden Code.
| Schritt | Datei | Aktion | Status |
|---------|-------|--------|--------|
| 1.1 | `datamodelFeatures.py` | NEU: `Feature`, `FeatureInstance` | ✅ |
| 1.2 | `datamodelMembership.py` | NEU: `UserMandate`, `FeatureAccess`, `UserMandateRole`, `FeatureAccessRole` | ✅ |
| 1.3 | `datamodelInvitation.py` | NEU: `Invitation` | ✅ |
| 1.4 | `datamodelRbac.py` | ADD: `Role.mandateId`, `Role.featureInstanceId`, `Role.featureCode`, `AccessRule.roleId` | ✅ |
| 1.5 | `datamodelUam.py` | ADD: `User.isSysAdmin`, Mandate ohne `language` | ✅ |
| 1.6 | `datamodelSecurity.py` | Token ohne `mandateId` | ✅ |
| 1.7 | `interfaceBootstrap.py` | roleId-basiert, erstellt UserMandate + UserMandateRole | ✅ |
**Notizen:**
- Bootstrap erstellt jetzt Root-Mandate, System-Rollen (sysadmin, admin, user) und verknüpft Initial-User via Junction Tables
- `isSysAdmin` Feld hat Validator für None → False Konvertierung
- App startet erfolgreich mit neuer Struktur
---
## Phase 2: RBAC Core (Kern-Logik) ✅
**Ziel:** Neues RBAC-System parallel zum alten lauffähig.
| Schritt | Datei | Aktion | Status |
|---------|-------|--------|--------|
| 2.1 | `security/rbac.py` | Neue `getRulesForUserBulk()` mit Junction Tables | ✅ |
| 2.2 | `interfaceRbac.py` | `buildRbacWhereClause()` mit explizitem `mandateId` | ✅ |
| 2.3 | `interfaceDbAppObjects.py` | Neue Membership-Methoden (parallel zu alten) | ✅ |
| 2.4 | `interfaceFeatures.py` | NEU: Feature-Instanz-Management | ✅ |
**Notizen:**
- `getRulesForUserBulk()` implementiert mit optimierten SQL JOINs (3 Queries statt N+1)
- Membership-Methoden hinzugefügt: `getUserMandate`, `createUserMandate`, `deleteUserMandate`, `getRoleIdsForUserMandate`, etc.
- Feature-Methoden hinzugefügt: `getFeatureAccess`, `createFeatureAccess`, `getRoleIdsForFeatureAccess`
- `interfaceFeatures.py` erstellt mit Template-Kopierung und Synchronisation
---
## Phase 3: Auth & Context (Authentifizierung) ✅
**Ziel:** Request-Context-System einführen.
| Schritt | Datei | Aktion | Status |
|---------|-------|--------|--------|
| 3.1 | `auth/authentication.py` | `RequestContext` + `getRequestContext()` + `requireSysAdmin()` | ✅ |
| 3.2 | `routeSecurityLocal.py` | Login mit neuem Token (ohne `mandateId`) | ✅ |
| 3.3 | `routeSecurityMsft.py` | OAuth anpassen (Token ohne `mandateId`) | ✅ |
| 3.4 | `routeSecurityGoogle.py` | OAuth anpassen (Token ohne `mandateId`) | ✅ |
| 3.5 | `datamodelSecurity.py` | Token ohne `mandateId` | ✅ (Phase 1) |
| 3.6 | `auth/tokenRefreshService.py` | Bereits korrekt (OAuth-Token, nicht JWT) | ✅ |
**Notizen:**
- `RequestContext` Klasse implementiert: User + mandateId + featureInstanceId + roleIds
- `getRequestContext()` FastAPI Dependency: Liest `X-Mandate-Id` und `X-Instance-Id` Header
- `requireSysAdmin()` FastAPI Dependency: Prüft `isSysAdmin` Flag mit Audit-Logging
- JWT-Token enthalten KEIN `mandateId` mehr - Mandant-Kontext kommt per Request-Header
- Token-Validierung in `_getUserBase()` prüft nur noch `userId`, nicht mehr `mandateId`
- Audit-Logging für System-Funktionen (Login/Logout) verwendet `mandateId="system"` (kein User-Kontext)
---
## Phase 4: Routes Migration ✅
**Ziel:** Alle Routes auf neues Context-System migrieren.
### 4.A - Admin Routes ✅
| Schritt | Datei | Status |
|---------|-------|--------|
| 4.A.1 | `routeSecurityAdmin.py` | ✅ |
| 4.A.2 | `routeAdminRbacRoles.py` | ✅ |
| 4.A.3 | `routeDataUsers.py` | ✅ |
| 4.A.4 | `routeDataMandates.py` | ✅ |
| 4.A.5 | `routeRbac.py` | ✅ |
**Notizen Phase 4.A:**
- Alle Admin-Routes verwenden jetzt `requireSysAdmin()` Dependency
- `roleLabels`-basierte Prüfungen durch `isSysAdmin` ersetzt
- `currentUser.mandateId` durch `RequestContext` ersetzt
- User-Filterung via `UserMandate` Junction Table
- Audit-Logging verwendet `context.mandateId` oder "system"
### 4.B - Feature Routes ✅
| Schritt | Datei | Status |
|---------|-------|--------|
| 4.B.1 | `routeFeatureTrustee.py` (← routeDataTrustee.py) | ✅ |
| 4.B.2 | `routeFeatureChatbot.py` (← routeChatbot.py) | ✅ |
| 4.B.3 | `routeFeatureRealEstate.py` (← routeRealEstate.py) | ✅ |
| 4.B.4 | `routeFeatureNeutralization.py` (← routeDataNeutralization.py) | ✅ |
| 4.B.5 | `routeFeatureAutomation.py` (← routeAdminAutomationEvents.py) | ✅ |
| 4.B.6 | `routeFeatureChatDynamic.py` (← routeChatPlayground.py) | ✅ |
**Notizen Phase 4.B:**
- Alle Feature-Routes umbenannt mit `routeFeature...` Prefix
- `getCurrentUser` durch `getRequestContext` ersetzt
- `currentUser.mandateId` durch `context.mandateId` ersetzt
- `currentUser.roleLabels` durch `context.roleIds` ersetzt
- `requireSysAdmin()` für Sysadmin-Endpoints (z.B. Automation Events)
- Alte Route-Dateien gelöscht, Imports in `app.py` aktualisiert
---
## Phase 5: Interfaces Migration ✅
**Ziel:** Alle Interfaces auf Context-System migrieren.
| Schritt | Datei | Status |
|---------|-------|--------|
| 5.1 | `interfaceDbTrusteeObjects.py` | ✅ |
| 5.2 | `interfaceDbChatObjects.py` | ✅ |
| 5.3 | `interfaceDbRealEstateObjects.py` | ✅ |
| 5.4 | `interfaceDbComponentObjects.py` | ✅ |
| 5.5 | `interfaceVoiceObjects.py` | ✅ |
**Notizen Phase 5:**
- `getInterface(currentUser)``getInterface(currentUser, mandateId=...)`
- `setUserContext(currentUser)``setUserContext(currentUser, mandateId=...)`
- `self.mandateId = currentUser.mandateId``self.mandateId = mandateId` (aus Parameter)
- SysAdmins können ohne `mandateId` arbeiten (cross-mandate Operationen)
- Singleton-Cache-Keys verwenden jetzt `userId_mandateId` statt `userId_user.mandateId`
- Feature-Routes aktualisiert: `getInterface(context.user, mandateId=context.mandateId)`
---
## Phase 6: Features Migration ✅
**Ziel:** Feature-Module auf Context-System migrieren.
| Schritt | Datei | Status |
|---------|-------|--------|
| 6.1 | `features/chatbot/mainChatbot.py` | ✅ |
| 6.2 | `features/realEstate/mainRealEstate.py` | ✅ |
| 6.3 | `features/dynamicOptions/mainDynamicOptions.py` | ✅ |
| 6.4 | `features/neutralizePlayground/mainNeutralizePlayground.py` | ✅ |
| 6.5 | `workflows/workflowManager.py` | ✅ |
| 6.6 | `workflows/methods/methodBase.py` | ✅ (keine Änderung nötig) |
| 6.7 | `services/serviceNeutralization/mainServiceNeutralization.py` | ✅ (verwendet interfaceDbApp.mandateId) |
**Notizen Phase 6:**
- `Services` Klasse aktualisiert: `mandateId` als expliziter Parameter
- `getInterface(user, workflow, mandateId)` für alle Services
- Feature-Funktionen aktualisiert: `mandateId` als Parameter statt `currentUser.mandateId`
- Route-Aufrufe aktualisiert: `str(context.mandateId)` wird übergeben
- `workflowManager.py`: Verwendet `self.services.mandateId` für neue Workflows
- `mainDynamicOptions.py`: Verwendet `services.mandateId` für Benutzer-Lookup
---
## Phase 7: Cleanup & Breaking Changes ✅
**Ziel:** Alte Felder entfernen (erst wenn alles migriert ist!)
| Schritt | Datei | Aktion | Status |
|---------|-------|--------|--------|
| 7.1 | `datamodelUam.py` | ENTFERNE `User.mandateId`, `User.roleLabels` | ✅ |
| 7.2 | `interfaceDbAppObjects.py` | Alte Methoden entfernen | ✅ |
| 7.3 | `interfaceBootstrap.py` | Alte roleLabels-Logik entfernen | ✅ |
**Änderungen Phase 7:**
- `datamodelUam.py`: `User.mandateId` und `User.roleLabels` Felder entfernt
- `interfaceDbAppObjects.py`:
- `createUser()`: `roleLabels` Parameter entfernt, nur noch `isSysAdmin`
- `updateUser()`: `mandateId` Handling entfernt
- `deleteRole()`: Prüft jetzt `UserMandateRole` statt `user.roleLabels`
- `getAccessRules()`: Neuer `roleId` Parameter hinzugefügt
- `countRoleAssignments()`: Neue Methode für Role-Zählung via `UserMandateRole`
- `interfaceBootstrap.py`: `mandateId` und `roleLabels` aus Admin/Event User Erstellung entfernt
- `security/rbac.py`: `user.roleLabels` Fallback entfernt
- `routeDataUsers.py`: Verwendet `createUserMandate()` für Role-Assignment
- `routeSecurityLocal.py`: `roleLabels` aus User-Registrierung entfernt
- `routeRbac.py`: Verwendet `context.roleIds` statt `user.roleLabels`
- `routeAdminRbacRoles.py`: Komplett auf `UserMandateRole` umgestellt mit Hilfsfunktionen
---
## Phase 8: Neue Features ✅
**Ziel:** Neue Funktionalität hinzufügen.
| Schritt | Datei | Status |
|---------|-------|--------|
| 8.1 | `routeFeatures.py` (NEU) | ✅ |
| 8.2 | `routeInvitations.py` (NEU) | ✅ |
| 8.3 | `routeRbacExport.py` (NEU) | ✅ |
| 8.4 | `routeGdpr.py` (NEU) | ✅ |
| 8.5 | `app.py` (Routes registriert) | ✅ |
**Notizen Phase 8:**
- `routeFeatures.py`: Feature/FeatureInstance CRUD, Template-Rollen-Sync, `/api/features/my` Endpoint
- `routeInvitations.py`: Token-basierte Einladungen, öffentliche Validierung, Accept-Flow
- `routeRbacExport.py`: Global und Mandate-scoped Export/Import von RBAC-Regeln
- `routeGdpr.py`: DSGVO Art. 15 (Datenexport), Art. 17 (Löschrecht), Art. 20 (Datenportabilität)
- Alle neuen Routes in `app.py` registriert
---
## Zusätzliche Änderungen (Nicht in Phasen)
| Datei | Änderung | Status |
|-------|----------|--------|
| `connectorDbPostgre.py` | Entfernt Warning bei leerem userId (Bootstrap) | ✅ |
| `routeSecurityAdmin.py` | DB-Funktionen generisch gemacht (dynamische DB-Discovery) | ✅ |
| `env_*.env` | Konsolidierte DB-Konfiguration (DB_HOST, DB_USER, etc.) | ✅ |
| `interfaceDb*Objects.py` | Hardcoded DB-Namen (poweron_app, poweron_chat, etc.) | ✅ |
| `rootAccess.py` | Bootstrap-Aufruf wenn kein Initial-User | ✅ |
---
## Bekannte Probleme / TODOs
- [x] DB-Indizes für Junction Tables erstellen (Performance) → `sql/001_multi_tenant_indexes_triggers_fk.sql`
- [x] IMMUTABLE Triggers für Role/AccessRule auf DB-Ebene → `sql/001_multi_tenant_indexes_triggers_fk.sql`
- [x] Foreign Key Constraints für CASCADE DELETE → `sql/001_multi_tenant_indexes_triggers_fk.sql`
- [x] Add/Remove User from Mandate Endpoints → `routeDataMandates.py`
- [x] Combined Register + Accept Invitation Endpoint → `routeInvitations.py`
---
## Changelog
### 2026-01-17 (Phase 8 - Erweiterungen)
- **Fehlende Endpoints nachgeliefert:**
- `routeDataMandates.py`: User-Management innerhalb Mandaten
- `GET /api/mandates/{mandateId}/users` - Benutzer im Mandant auflisten
- `POST /api/mandates/{mandateId}/users` - Benutzer zu Mandant hinzufügen
- `DELETE /api/mandates/{mandateId}/users/{targetUserId}` - Benutzer aus Mandant entfernen
- `PUT /api/mandates/{mandateId}/users/{targetUserId}/roles` - Benutzer-Rollen aktualisieren
- SysAdmin Self-Eskalation Prevention: SysAdmin kann sich nicht selbst hinzufügen
- Orphan Prevention: Letzter Admin kann nicht entfernt werden
- `routeInvitations.py`: Combined Register + Accept
- `POST /api/invitations/register-and-accept` - Registrierung + Einladung in einem Schritt (öffentlich)
- **DB Migration Script erstellt:**
- `sql/001_multi_tenant_indexes_triggers_fk.sql`
- Indexes für alle Junction Tables (UserMandate, UserMandateRole, FeatureAccess, etc.)
- IMMUTABLE Triggers für Role und AccessRule (mandateId/featureInstanceId/roleId)
- Foreign Key Constraints mit CASCADE DELETE
### 2026-01-17 (Phase 8)
- **Phase 8 abgeschlossen:**
- `routeFeatures.py` NEU: Feature-Definitionen (SysAdmin), FeatureInstance CRUD (Mandate-Admin)
- `GET /api/features/` - Liste aller Features
- `GET /api/features/{featureCode}` - Feature-Details
- `POST /api/features/` - Feature erstellen (SysAdmin)
- `GET /api/features/instances` - Feature-Instanzen im Mandant
- `POST /api/features/instances` - Feature-Instanz erstellen
- `DELETE /api/features/instances/{id}` - Feature-Instanz löschen
- `POST /api/features/instances/{id}/sync-roles` - Rollen synchronisieren
- `GET /api/features/my` - Eigene Feature-Instanzen (kein Mandant-Kontext nötig)
- `GET /api/features/templates/roles` - Template-Rollen (SysAdmin)
- `POST /api/features/templates/roles` - Template-Rolle erstellen (SysAdmin)
- `routeInvitations.py` NEU: Token-basierte Einladungen für Self-Service Onboarding
- `POST /api/invitations/` - Einladung erstellen (Mandate-Admin)
- `GET /api/invitations/` - Einladungen auflisten
- `DELETE /api/invitations/{id}` - Einladung widerrufen
- `GET /api/invitations/validate/{token}` - Einladung validieren (öffentlich)
- `POST /api/invitations/accept/{token}` - Einladung annehmen (authentifiziert)
- `routeRbacExport.py` NEU: RBAC Export/Import
- `GET /api/rbac/export/global` - Globale Templates exportieren (SysAdmin)
- `POST /api/rbac/import/global` - Globale Templates importieren (SysAdmin)
- `GET /api/rbac/export/mandate` - Mandant-RBAC exportieren (Mandate-Admin)
- `POST /api/rbac/import/mandate` - Mandant-RBAC importieren (Mandate-Admin)
- `routeGdpr.py` NEU: DSGVO-Compliance Endpoints
- `GET /api/user/me/data-export` - Datenexport (Art. 15)
- `GET /api/user/me/data-portability` - Datenportabilität JSON-LD (Art. 20)
- `DELETE /api/user/me` - Account löschen (Art. 17)
- `GET /api/user/me/consent-info` - Datenschutz-Informationen
- `app.py`: Alle neuen Routes registriert
### 2026-01-18
- **Phase 6 abgeschlossen:**
- `services/__init__.py`: `Services` Klasse akzeptiert `mandateId` Parameter
- `getInterface(user, workflow, mandateId)` übergibt `mandateId` an alle Interface-Aufrufe
- `mainChatbot.py`: `chatProcess(user, mandateId, ...)` - `mandateId` für Workflow-Erstellung
- `mainRealEstate.py`: `processNaturalLanguageCommand(user, mandateId, ...)` + alle CREATE-Operationen
- `mainDynamicOptions.py`: `services.mandateId` statt `currentUser.mandateId`
- `mainNeutralizePlayground.py`: `NeutralizationPlayground(user, mandateId)` Konstruktor
- `workflowManager.py`: `self.services.mandateId` für neue Workflows
- Route-Aufrufe aktualisiert: `str(context.mandateId)` wird übergeben
- `methodBase.py`: Keine Änderung nötig (verwendet Services)
- `mainServiceNeutralization.py`: Keine Änderung nötig (verwendet `interfaceDbApp.mandateId`)
- **Phase 5 abgeschlossen:**
- `interfaceDbTrusteeObjects.py`: `getInterface(user, mandateId)` + `setUserContext(user, mandateId)`
- `interfaceDbChatObjects.py`: `getInterface(user, mandateId)` + `setUserContext(user, mandateId)`
- `interfaceDbRealEstateObjects.py`: `getInterface(user, mandateId)` + `setUserContext(user, mandateId)`
- `interfaceDbComponentObjects.py`: `getInterface(user, mandateId)` + `setUserContext(user, mandateId)`
- `interfaceVoiceObjects.py`: `getVoiceInterface(user, mandateId)` + `setUserContext(user, mandateId)`
- Feature-Routes aktualisiert: `getInterface(context.user, mandateId=context.mandateId)`
- **Phase 4.B abgeschlossen:**
- `routeFeatureTrustee.py`: Alle Endpoints auf `getRequestContext()` umgestellt
- `routeFeatureChatbot.py`: Streaming + RBAC mit `context.user`
- `routeFeatureRealEstate.py`: `context.mandateId` für Projekt-/Parzellen-Filterung
- `routeFeatureNeutralization.py`: Config + Processing mit `context.user`
- `routeFeatureAutomation.py`: `requireSysAdmin()` für alle Endpoints
- `routeFeatureChatDynamic.py`: Workflow-Start/Stop mit `context.user`
- Alte Route-Dateien gelöscht (6 Dateien)
- `app.py` Imports aktualisiert
- **Phase 5 - GREENFIELD Cleanup:**
- ALLE Fallbacks auf `currentUser.mandateId` entfernt (GREENFIELD = keine Backwards Compatibility)
- `interfaceDbAppObjects.py`: `self.mandateId` kommt nur aus Parameter, nicht aus User
- `interfaceDbTrusteeObjects.py`: Fallback-Logik entfernt
- `interfaceDbChatObjects.py`: `self.mandateId` statt `self.currentUser.mandateId`
- `interfaceDbRealEstateObjects.py`: Fallback-Logik entfernt
- `interfaceDbComponentObjects.py`: `self.mandateId` für alle Record-Erstellungen
- `interfaceVoiceObjects.py`: Fallback-Logik entfernt
- `interfaceRbac.py`: Keine Fallbacks mehr, `mandateId` ist explizit erforderlich
- Modul-Header aktualisiert (keine "Backwards Compatibility" Referenzen mehr)
### 2026-01-17
- **Phase 3 abgeschlossen:**
- `RequestContext` Klasse in `auth/authentication.py` implementiert
- `getRequestContext()` FastAPI Dependency mit `X-Mandate-Id` und `X-Instance-Id` Header-Handling
- `requireSysAdmin()` FastAPI Dependency für System-Level-Operationen
- JWT-Token enthalten kein `mandateId` mehr (Local, Microsoft, Google)
- Token-Validierung prüft nur noch `userId`
- `auth/__init__.py` exportiert neue Funktionen
- **Phase 4.A abgeschlossen:**
- `routeSecurityAdmin.py`: Alle Endpoints auf `requireSysAdmin()` umgestellt
- `routeAdminRbacRoles.py`: `requireSysAdmin()` + `getRootInterface()`
- `routeDataUsers.py`: `getRequestContext()` für Mandate-Scope, UserMandate-Filterung
- `routeDataMandates.py`: `requireSysAdmin()` (Mandates sind System-Ressourcen)
- `routeRbac.py`: Permissions mit `getRequestContext()`, Rules/Roles mit `requireSysAdmin()`
### 2026-01-16
- Phase 1 abgeschlossen
- Startup-Fehler behoben (isSysAdmin Validator, Bootstrap-Aufruf)
- DB-Konfiguration konsolidiert
- DB-Admin-Funktionen generisch gemacht
- **Phase 2 abgeschlossen:**
- `getRulesForUserBulk()` in `security/rbac.py` implementiert (optimierte Bulk-Queries)
- Membership-Methoden in `interfaceDbAppObjects.py` hinzugefügt
- `interfaceFeatures.py` NEU erstellt (Feature-Instanz-Management mit Template-Kopierung)

View file

@ -0,0 +1,579 @@
# RBAC Zugriffskonzept (Katalog + Seiten + Endpoints)
## Kontext aus dem Codebestand (aktueller Stand)
- RBAC-Regeln und Rollen existieren bereits mit immutable Kontextfeldern und AccessRule-Unterstuetzung fuer DATA/UI/RESOURCE. Siehe `AccessRuleContext`, `Role`, `AccessRule` in:
```
21:116:gateway/modules/datamodels/datamodelRbac.py
class AccessRuleContext(str, Enum):
DATA = "DATA"
UI = "UI"
RESOURCE = "RESOURCE"
...
class AccessRule(BaseModel):
roleId: str
context: AccessRuleContext
item: Optional[str]
view: bool
read: Optional[AccessLevel]
create: Optional[AccessLevel]
update: Optional[AccessLevel]
delete: Optional[AccessLevel]
```
- RBAC-Endpunkte fuer Permissions und Access Rules existieren bereits (Access Rules sind SysAdmin-only):
```
35:214:gateway/modules/routes/routeRbac.py
@router.get("/permissions")
@router.get("/permissions/all")
@router.get("/rules")
```
- Mandantenzuordnung von Benutzern/Rollen laeuft ueber `/api/mandates/{mandateId}/users` und die Junction-Tabellen `UserMandateRole` (Mandanten-Kontext) und `FeatureAccessRole` (Feature-Instanz-Kontext):
```
15:149:gateway/modules/datamodels/datamodelMembership.py
class UserMandateRole(BaseModel):
userMandateId: str
roleId: str
...
class FeatureAccessRole(BaseModel):
featureAccessId: str
roleId: str
```
- Im Frontend existieren Admin-Seiten fuer Benutzer-Mandant-Mitgliedschaften und Mandanten-Rollen:
```
1:120:frontend_nyla/src/pages/admin/AdminUserMandatesPage.tsx
1:185:frontend_nyla/src/pages/admin/AdminMandateRolesPage.tsx
```
Damit ist klar:
- Zugriffregeln existieren bereits (view + CRUD mit none/my/group/all).
- Fehlend ist ein **Katalog + Editor-UI** fuer Objektregeln (DATA/UI/RESOURCE).
## Entscheidungen / Antworten zu Inputs
1) **Shared Katalog-Location (Gateway):**
- Es gibt aktuell keinen gemeinsamen RBAC-Objektkatalog in datamodels. Datamodels definieren nur Schema.
- Ziel-Location: `gateway/modules/security/rbacCatalog/` (Service-Layer), nicht `datamodels`.
- DATA-Objekte koennen on-the-fly aus Modellen/Attributen abgeleitet werden, ohne Persistenz.
- UI- und RESOURCE-Objekte brauchen explizite Registry-Eintraege, persistent in einer Katalog-Tabelle, dann ueber Gateway-Endpunkte abrufbar.
- Feature-Initialisierung im Gateway registriert die UI/RESOURCE-Keys. Die Admin-UI zeigt alle persistierten Katalogobjekte (keine Filterung nach Registrierungsstatus).
- Persistenz ist Pflicht, um Regeln nicht zu verlieren, wenn Features entfernt oder deaktiviert werden.
2) **Admin-Seite fuer Mandantenverwaltung: gleich wie Rollen-Zuordnung?**
- Mandanten-CRUD getrennt von Rollen-Zuordnung.
- Heute: Mandanten-CRUD ist SysAdmin-only (`/api/mandates`), Rollen-Zuordnung ist Mandanten-Admin (`/api/mandates/{mandateId}/users`).
- Ziel: separate **AdminMandatesPage** (SysAdmin), **AdminUserMandatesPage** fuer Rollen-Zuordnung.
- Zusaetzlich: **AdminRbacRulesPage** (Feature-Instanz-Admin) fuer Objektregeln.
3) **Registry-Eigentum und Quellen:**
- Feature-Initialisierung im Gateway definiert alle UI/RESOURCE-Objekte der Features.
- Frontend registriert keine Feature-UI-Objekte.
- System-UI-Objekte werden direkt im Backend definiert (keine UI-Abhaengigkeit).
4) **UI-Permissions:**
- UI-Kontext nutzt nur `view`.
- Kein create/update/delete fuer UI-Kontext.
5) **Katalog-Scope:**
- Der Katalog enthaelt alle Objekte (global + mandate + feature instance).
6) **Bootstrap-Scope (interfaceBootstrap.py):**
- Bootstrap laeuft nur, wenn die Datenbank leer ist.
- Bootstrap erzeugt nur System-RBAC (Root-Mandant, Admin/Event-User, globale Rollen, Basisregeln).
- Bootstrap definiert keine Feature-Rollen oder Feature-Zugriffsregeln.
- System-UI-Objekte sind im Backend definiert und koennen im Bootstrap registriert werden.
## Zielarchitektur
### A) RBAC Objektkatalog (shared im Gateway)
**Ziel:** Ein einziger Objektbaum fuer DATA / UI / RESOURCE, mit Mandant + Feature-Instanz-Kontext.
**Quelle je Objekttyp:**
- **DATA**: abgeleitet aus Model-Metadaten (Tabellen + Felder).
- **UI**: Registry aus Feature-Initialisierung plus backend-definierte System-UI-Objekte.
- **RESOURCE**: Registry aus Feature-Initialisierung.
**Persistenz:**
- Kleine Katalog-Tabelle `RbacObject` (oder aehnlich) fuer UI/RESOURCE.
- DATA-Objekte nicht speichern; on-demand generieren.
- Nicht registrierte Objekte bleiben im Katalog und bleiben selektierbar; keine Filterung nach Registrierung.
**Model (gateway/modules/datamodels/datamodelRbacCatalog.py):**
- `id`
- `objectKey` (unique, z.B. `ui.feature.trustee.contracts.tab.documents`)
- `context` (DATA | UI | RESOURCE)
- `featureCode` (z.B. `trustee`)
- `featureInstanceId` (nullable)
- `mandateId` (nullable)
- `label` (multilingual, optional)
- `meta` (JSON, optional, z.B. Endpoint-Info oder UI-Hints)
**Registry-Logik (gateway/modules/security/rbacCatalog/catalogService.py):**
- `getDataCatalog(mandateId, featureInstanceId)` -> aus Modellen ableiten:
- `data.table.<table>`
- `data.table.<table>.<field>`
- `getUiCatalog(mandateId, featureInstanceId, featureCode)` -> aus `RbacObject` (UI-Kontext)
- `getResourceCatalog(mandateId, featureInstanceId, featureCode)` -> aus `RbacObject` (RESOURCE-Kontext)
- `getCatalog(...)` -> zusammenfuehren; alle Objekte (global + mandate + feature instance)
**ObjectKey-Konvention:**
- DATA:
- `data.table.<table>`
- `data.table.<table>.<field>`
- UI:
- `ui.feature.<featureCode>.<area>.<element>`
- Beispiel: `ui.feature.trustee.contracts.tab.documents`
- RESOURCE:
- `resource.feature.<featureCode>.<endpoint>`
- Beispiel: `resource.feature.trustee.contracts.create`
### B) RBAC Rules Editor (neue Admin-Seite)
**Seitenname:** `AdminRbacRulesPage`
**Zielgruppe:** Feature-Instanz-Admin
**Route:** `/admin/feature-instances/:featureInstanceId/rbac`
**Navigation:** Feature-Instanz-Seiten “Rollen” + “Zugriffe” ersetzen durch:
- “Feature-Benutzer” (bestehende User/Rollen-Zuordnung)
- “Zugriffe (RBAC)” (neue Seite fuer Objektregeln)
**Layout:**
- Links: Objektbaum (DATA / UI / RESOURCE)
- Oben: Rollen-Auswahl (Feature-Instanz-Rollen)
- Rechts: Regel-Grid (view/read/create/update/delete mit Scope)
- UI: nur `view`
- RESOURCE: view + CRUD mit none/my/group/all
- DATA: CRUD mit none/my/group/all
**Verhalten:**
- Auswahl eines Objekts zeigt Regeln je Rolle.
- Aenderungen ueber create/update/delete von AccessRule.
- Nur Rollen der Feature-Instanz sind auswaehlbar.
### C) Backend-Endpunkte (Vorschlag)
#### Katalog-Endpunkte
```
GET /api/rbac/catalog?featureInstanceId=...&featureCode=...&include=data,ui,resource
```
Response:
```
{
"data": [{ "objectKey": "data.table.TrusteeContract", "label": "...", "children": [...] }],
"ui": [{ "objectKey": "ui.feature.trustee.contracts", "label": "...", "children": [...] }],
"resource": [{ "objectKey": "resource.feature.trustee.contracts.create", "label": "..." }]
}
```
```
POST /api/rbac/catalog/ui
POST /api/rbac/catalog/resource
```
Payload:
```
{
"featureCode": "trustee",
"featureInstanceId": "optional",
"mandateId": "optional",
"objects": [
{ "objectKey": "ui.feature.trustee.contracts", "label": { "en": "...", "de": "..." }, "meta": {} }
]
}
```
Notes:
- Endpunkte registrieren/aktualisieren UI/RESOURCE-Katalogeintraege.
- DATA-Objekte werden nie gepostet.
- Admin-UI darf alle persistenten Objekte selektieren.
#### AccessRule-Endpunkte (bestehend, aber erweitert)
```
GET /api/rbac/rules?roleId=...&context=UI&item=ui.feature.trustee.contracts
POST /api/rbac/rules
PUT /api/rbac/rules/{ruleId}
DELETE /api/rbac/rules/{ruleId}
```
Policy:
- Nur SysAdmin darf Regeln verwalten.
#### Optionale Options-Endpunkte fuer Admin-UI
```
GET /api/options/mandate.roles?mandateId=...
GET /api/options/featureInstance.roles?featureInstanceId=...
GET /api/options/featureInstances?mandateId=...
```
### D) Enforcement
- **DATA:** ueber RBAC in der Daten-Schicht.
- **UI:** Frontend nutzt `getAllPermissions` und blendet aus via `view`.
- **RESOURCE:** Gateway prueft am Endpoint-Entry.
## Implementierungsschritte (high level)
1) Katalog-Model + Service im Gateway.
2) Katalog-Endpunkte in `routeRbacCatalog.py`.
3) Registry-Producer:
- Feature-Initialisierung registriert UI/RESOURCE.
- System-UI-Objekte werden im Backend registriert.
4) Neue Admin-Seite `AdminRbacRulesPage`.
5) Feature-Instanz-Navigation anpassen.
## Bootstrap-Abgleich (aktuell vs Ziel)
**Aktuell (interfaceBootstrap.py):**
- Root-Mandant, Admin-User, Event-User.
- Globale Template-Rollen: `admin`, `user`, `viewer`.
- AccessRules (nur wenn keine existieren):
- Default DATA rules (item=None).
- Table-spezifische DATA rules (Mandate, UserInDB, Standard-Tabellen, AuthEvent).
- UI rules (context=UI, item=None, view=True).
- RESOURCE rules (context=RESOURCE, item=None, view=True).
- Feature-Initialisierung und Feature-Template-Rollen.
- AccessRules fuer Feature-Template-Rollen.
**Ziel:**
- Bootstrap nur System-RBAC, nur bei leerer DB.
- Feature-Definitionen/Template-Rollen/Feature-Regeln entfernen.
- Feature-Initialisierung verwaltet Katalog + Feature-Regeln.
### Konkrete Aenderungen in `interfaceBootstrap.py`
**Aus `initBootstrap()` entfernen:**
- `initFeatures(db)`
- `_initFeatureTemplateRoleAccessRules(db)`
**Feature-Template-Rollen entfernen:**
- `_initFeatureTemplateRoles(db)` und alle Aufrufe
**Feature-Regeln entfernen:**
- `_initFeatureTemplateRoleAccessRules(db)` und alle Aufrufe
**System-RBAC behalten:**
- `initRoles()` fuer globale Rollen `admin`, `user`, `viewer`
- `initRbacRules()` fuer:
- Default DATA rules
- Table-spezifische DATA rules
- UI rules (view)
- RESOURCE rules (view)
**Bootstrap-Guard:**
- Nur bei leerer DB.
- System-UI-Objekte im Backend registrieren (Bootstrap oder Startup).
## Offene Punkte
- Alle Regeln bleiben persistent, bis sie manuell geloescht werden.
## Feature-Containerisierung
### Ziel
Jedes Feature in einem eigenen Ordner, damit Features durch Ordner-Praesenz aktiv sind. Jeder Container enthaelt:
- `datamodelFeatureXxx.py`
- `interfaceFeatureXxx.py`
- `routeFeatureXxx.py`
- main module fuer RBAC-Objekte
- optionale Helpers
### Aktuelle Feature-Dateien
**Routes (gateway/modules/routes):**
- `routeFeatureTrustee.py`
- `routeFeatureChatbot.py`
- `routeFeatureChatDynamic.py` (chatworkflow)
- `routeFeatureNeutralization.py`
- `routeFeatureRealEstate.py`
- `routeFeatureWorkflow.py` (automation events)
- `routeFeatures.py` (Feature/Instance-Management, kein Feature)
**Interfaces (gateway/modules/interfaces):**
- `interfaceDbTrustee.py`
- `interfaceDbChatbot.py` (shared mit workflow)
- `interfaceDbRealEstate.py`
- `interfaceFeatures.py` (Feature/Instance-Management, kein Feature)
**Datamodels (gateway/modules/datamodels):**
- `datamodelTrustee.py`
- `datamodelChat.py`
- `datamodelWorkflow.py`
- `datamodelRealEstate.py`
- `datamodelNeutralizer.py`
- `datamodelFeatures.py` (Feature/Instance-Management, kein Feature)
**Feature-Logik (gateway/modules/features):**
- `chatbot/` (mainChatbot.py, eventManager.py, chatbotConstants.py)
- `realEstate/` (mainRealEstate.py)
- `neutralizePlayground/` (mainNeutralizePlayground.py)
**Nicht-Feature Module (aus features heraus):**
- `gateway/modules/features/featuresLifecycle.py` -> `gateway/modules/workflows/automation/` (Event Scheduler Handling)
- `gateway/modules/features/workflow/` -> `gateway/modules/workflows/automation/` (zentrale Workflow Execution Engine)
- `gateway/modules/features/dynamicOptions/` -> entfernen, dynamische Optionen ueber API-Routen
### Zielstruktur pro Feature
**trustee** (neu):
- Move:
- `modules/routes/routeFeatureTrustee.py`
- `modules/interfaces/interfaceDbTrustee.py`
- `modules/datamodels/datamodelTrustee.py`
- Add:
- `modules/features/trustee/mainTrustee.py` (RBAC UI/RESOURCE-Katalog)
- Add main module fuer RBAC-Katalog (falls noch nicht vorhanden)
**chatbot**:
- Move:
- `modules/routes/routeFeatureChatbot.py`
- `modules/interfaces/interfaceDbChatbot.py`
- modules/datamodels/datamodelChatbot.py
- Keep:
- `modules/features/chatbot/mainChatbot.py`, Helpers
- Add main module fuer RBAC-Katalog (falls noch nicht vorhanden)
**aichat** (chatworkflow):
- Move:
- `modules/routes/routeFeatureChatDynamic.py`
- `modules/interfaces/interfaceDbChat.py`
- modules/datamodels/datamodelChat.py
- Add main module fuer RBAC-Katalog (falls noch nicht vorhanden)
**neutralizer** (Rename von neutralizePlayground):
- Move:
- `modules/routes/routeFeatureNeutralization.py`
- `modules/datamodels/datamodelNeutralizer.py`
- Keep:
- `modules/features/neutralizePlayground/mainNeutralizePlayground.py` (Rename Ordner zu `neutralizer`)
- Add main module fuer RBAC-Katalog (falls noch nicht vorhanden)
**realestate**:
- Move:
- `modules/routes/routeFeatureRealEstate.py`
- `modules/interfaces/interfaceDbRealEstate.py`
- `modules/datamodels/datamodelRealEstate.py`
- Keep:
- `modules/features/realEstate/mainRealEstate.py`
- Add main module fuer RBAC-Katalog (falls noch nicht vorhanden)
**automation** (neu):
- Move:
- `modules/routes/routeDataAutomation.py`
- `modules/interfaces/interfaceDbAutomation.py` <<- new, to put all automation related interfaces here>>
- `modules/datamodels/datamodelAutomation.py` <<- new, to put class AutomationDefinition here>>
- Add main module fuer RBAC-Katalog (falls noch nicht vorhanden)
- Neue datamodel/interface/route/main Module
### App-Routing als Plug-and-Play
`gateway/app.py` importiert `routeFeature*.py` aus Feature-Containern.
Pattern:
- `from modules.features.<featureCode>.routeFeature<FeatureCode> import router as <featureRouter>`
- Nur existierende Ordner werden eingebunden.
- Feature-Ordner loeschen = Feature deaktivieren.
### Notwendige Referenz-Updates
- Imports in `app.py` auf neue Pfade.
- Alle direkten Imports der alten `modules/routes/routeFeature*.py`.
- `featuresLifecycle.py` und `workflow` nach `gateway/modules/workflows/automation/` verschieben.
- `dynamicOptions` entfernen und UI/API auf Options-Routen umstellen.
- Referenzen auf `modules/interfaces/interfaceDb*.py` und `modules/datamodels/datamodel*.py` anpassen.
### Machbarkeit und Bedenken
- Shared Interfaces/Models (chatbot/workflow) brauchen klare Ownership oder Shared-Modul.
- Feature-Lifecycle sollte Feature-Registry dynamisch laden.
- Feature-Registry (`routeFeatures.py`, `interfaceFeatures.py`, `datamodelFeatures.py`) bleibt global.
### Fragen / Klaerungen
- Bleiben `routeFeatures.py` und `interfaceFeatures.py` global? -> JA
- Shared chatbot/chat: Shared-Modul oder Duplikation? --> Bereits dupliziert, somit separiert
- Feature-Discovery dynamisch (scan) oder explizite Imports in `app.py`? -> dynamisch
### Propositionen
- `routeFeatures.py`, `interfaceFeatures.py`, `datamodelFeatures.py` global behalten (System-Feature-Registry).
- Explizite Imports in `app.py`, aber zentral ueber Feature-Index. --> Die einzelnen Features benötigen keine zentrale komponente mehr. Echt plug & play
- Event Scheduler + Workflow Engine in `gateway/modules/workflows/automation/` (kein Feature).
- `dynamicOptions` entfernen, dynamische Daten per spezialisierte API-Routen (RBAC-Admin Stil).
### Bedenken
- Feature-Removal muss Cron/Jobs/Hook-Registrierungen entfernen. --> bereits implementiert. die werden dann nicht mehr gefunden
- Migration erzeugt viele Import-Aenderungen und Test-Anpassungen. --> korrekt. daher schritt für schritt plan machen
- `dynamicOptions`-Ablösung erfordert UI/API-Umstellung auf neue Options-Routen.
## Services in Feature-Container integrieren
### Ziel
Services, die eindeutig zu Features gehoeren, sollen in den jeweiligen Feature-Container verschoben werden und plug&play geladen werden (analog zu Routes).
### Zuordnung
- **aichat**:
- `gateway/modules/aicore`
- `gateway/modules/services/serviceAi`
- `gateway/modules/services/serviceExtraction`
- `gateway/modules/services/serviceGeneration`
- `gateway/modules/services/serviceWeb`
- **neutralizer**:
- `gateway/modules/services/serviceNeutralization`
- **Generisch (bleibt shared):**
- alle anderen Services bleiben in `gateway/modules/services`
### Plug&Play fuer Services (analog Routes)
- Feature-Container exportiert Service-Registrierung (z.B. `registerServices()`).
- Zentrale Service-Registry laedt nur vorhandene Feature-Container.
- Entfernt man den Feature-Ordner, werden auch Services nicht mehr geladen.
### Umsetzungsschritte (Ergaenzung)
**Schritt S1: aichat-Services verschieben**
- Move:
- `gateway/modules/aicore/` -> `gateway/modules/features/aichat/aicore/`
- Move:
- `gateway/modules/services/serviceAi/` -> `gateway/modules/features/aichat/services/serviceAi/`
- `gateway/modules/services/serviceExtraction/` -> `gateway/modules/features/aichat/services/serviceExtraction/`
- `gateway/modules/services/serviceGeneration/` -> `gateway/modules/features/aichat/services/serviceGeneration/`
- `gateway/modules/services/serviceWeb/` -> `gateway/modules/features/aichat/services/serviceWeb/`
- Referenzen anpassen:
- alle Imports dieser Services auf die neuen Pfade.
- Service-Registry in aichat registriert diese Module.
**Schritt S2: neutralizer-Services verschieben**
- Move:
- `gateway/modules/services/serviceNeutralization/` -> `gateway/modules/features/neutralizer/services/serviceNeutralization/`
- Referenzen anpassen:
- alle Imports auf neuen Pfad.
- Service-Registry in neutralizer registriert dieses Modul.
**Schritt S3: Service-Discovery**
- Zentraler Loader (shared) laedt Services pro Feature-Container dynamisch.
- Shared Services bleiben unveraendert in `gateway/modules/services`.
## Umsetzungskonzept (schrittweise)
### Schritt 0: Bestandsaufnahme und Freeze
- **Ziel:** saubere Basis, keine parallelen Feature-Refactors.
- **Aktion:** Branch erstellen, keine Feature-Folder loeschen vor Abschluss.
### Schritt 1: Bootstrap auf System-RBAC reduzieren
- **Datei:** `gateway/modules/interfaces/interfaceBootstrap.py`
- **Aenderung:**
- In `initBootstrap()` entfernen:
- `initFeatures(db)`
- `_initFeatureTemplateRoleAccessRules(db)`
- Entfernen:
- `_initFeatureTemplateRoles(db)` (Funktion + Call-Sites)
- `_initFeatureTemplateRoleAccessRules(db)` (Funktion + Call-Sites)
- **Referenzen:** alle Verweise auf Feature-Template-Rollen und Feature-AccessRules loeschen.
- **Ergebnis:** Bootstrap erstellt nur System-RBAC bei leerer DB.
### Schritt 2: Feature-Container vorbereiten (Ordnerstruktur)
- **Ziel:** pro Feature ein Container mit klarer Struktur.
- **Neuer Ordner pro Feature:**
- `gateway/modules/features/<featureCode>/`
- Pflicht-Module:
- `datamodelFeatureXxx.py`
- `interfaceFeatureXxx.py`
- `routeFeatureXxx.py`
- `main<Feature>.py` (registriert UI/RESOURCE Katalog)
- **Ergebnis:** leere Container fuer `trustee`, `chatbot`, `aichat`, `neutralizer`, `realestate`, `automation`.
### Schritt 3: Trustee in Container verschieben
- **Move:**
- `gateway/modules/routes/routeFeatureTrustee.py` -> `gateway/modules/features/trustee/routeFeatureTrustee.py`
- `gateway/modules/interfaces/interfaceDbTrustee.py` -> `gateway/modules/features/trustee/interfaceFeatureTrustee.py`
- `gateway/modules/datamodels/datamodelTrustee.py` -> `gateway/modules/features/trustee/datamodelFeatureTrustee.py`
- **Neu:**
- `gateway/modules/features/trustee/mainTrustee.py` (RBAC UI/RESOURCE Katalogregistrierung)
- **Referenzen anpassen:**
- Alle Imports von `routeFeatureTrustee.py`, `interfaceDbTrustee.py`, `datamodelTrustee.py`
- `app.py` Router-Import und Registrierung
- Tests, falls vorhanden
### Schritt 4: Chatbot in Container verschieben
- **Move:**
- `gateway/modules/routes/routeFeatureChatbot.py` -> `gateway/modules/features/chatbot/routeFeatureChatbot.py`
- `gateway/modules/interfaces/interfaceDbChatbot.py` -> `gateway/modules/features/chatbot/interfaceFeatureChatbot.py`
- `gateway/modules/datamodels/datamodelChatbot.py` -> `gateway/modules/features/chatbot/datamodelFeatureChatbot.py`
- **Keep:**
- `gateway/modules/features/chatbot/mainChatbot.py` + Helpers
- **Neu:**
- `gateway/modules/features/chatbot/mainChatbotCatalog.py` (oder in `mainChatbot.py`)
- **Referenzen anpassen:**
- Alle Imports von `routeFeatureChatbot.py`, `interfaceDbChatbot.py`, `datamodelChatbot.py`
- `app.py` Router-Import und Registrierung
### Schritt 5: AIChat (chatworkflow) in Container verschieben
- **Move:**
- `gateway/modules/routes/routeFeatureChatDynamic.py` -> `gateway/modules/features/aichat/routeFeatureAiChat.py`
- `gateway/modules/interfaces/interfaceDbChat.py` -> `gateway/modules/features/aichat/interfaceFeatureAiChat.py`
- `gateway/modules/datamodels/datamodelChat.py` -> `gateway/modules/features/aichat/datamodelFeatureAiChat.py`
- **Keep:**
- `gateway/modules/features/workflow/mainWorkflow.py` (falls nur Workflow-Logik)
- **Neu:**
- `gateway/modules/features/aichat/mainAiChat.py` (RBAC UI/RESOURCE Katalog)
- **Referenzen anpassen:**
- Alle Imports von `routeFeatureChatDynamic.py`, `interfaceDbChat.py`, `datamodelChat.py`
- `app.py` Router-Import und Registrierung
### Schritt 6: Neutralizer in Container verschieben und Ordner umbenennen
- **Rename:**
- `gateway/modules/features/neutralizePlayground/` -> `gateway/modules/features/neutralizer/`
- **Move:**
- `gateway/modules/routes/routeFeatureNeutralization.py` -> `gateway/modules/features/neutralizer/routeFeatureNeutralizer.py`
- `gateway/modules/datamodels/datamodelNeutralizer.py` -> `gateway/modules/features/neutralizer/datamodelFeatureNeutralizer.py`
- **Neu:**
- `gateway/modules/features/neutralizer/mainNeutralizer.py` (RBAC UI/RESOURCE Katalog)
- **Referenzen anpassen:**
- Alle Imports auf `neutralizePlayground`
- `app.py` Router-Import und Registrierung
### Schritt 7: RealEstate in Container verschieben
- **Move:**
- `gateway/modules/routes/routeFeatureRealEstate.py` -> `gateway/modules/features/realestate/routeFeatureRealEstate.py`
- `gateway/modules/interfaces/interfaceDbRealEstate.py` -> `gateway/modules/features/realestate/interfaceFeatureRealEstate.py`
- `gateway/modules/datamodels/datamodelRealEstate.py` -> `gateway/modules/features/realestate/datamodelFeatureRealEstate.py`
- **Keep:**
- `gateway/modules/features/realEstate/mainRealEstate.py`
- **Neu:**
- `gateway/modules/features/realestate/mainRealEstateCatalog.py` (oder in mainRealEstate)
- **Referenzen anpassen:**
- Alle Imports der alten Pfade
- `app.py` Router-Import und Registrierung
### Schritt 8: Automation als Feature-Container
- **Move:**
- `gateway/modules/routes/routeDataAutomation.py` -> `gateway/modules/features/automation/routeFeatureAutomation.py`
- **Neu:**
- `gateway/modules/features/automation/interfaceFeatureAutomation.py`
- `gateway/modules/features/automation/datamodelFeatureAutomation.py` (enthaelt `AutomationDefinition`)
- `gateway/modules/features/automation/mainAutomation.py` (RBAC UI/RESOURCE Katalog)
- **Referenzen anpassen:**
- Alle Imports von `routeDataAutomation.py`
- `app.py` Router-Import und Registrierung
### Schritt 9: App-Routing dynamisch
- **Datei:** `gateway/app.py`
- **Aenderung:**
- Entferne direkte Imports aus `modules/routes/`.
- Importiere Router aus Feature-Containern dynamisch (Feature-Discovery).
- **Referenzen:** alle Router-Registrierungen auf neue Pfade/Discovery umstellen.
### Schritt 10: featuresLifecycle anpassen
- **Datei:** `gateway/modules/features/featuresLifecycle.py`
- **Aenderung:**
- Feature-Registry dynamisch laden (Folder-Discovery).
- Keine direkten Imports aus alten Pfaden.
- **Referenzen:** Aufrufe in `app.py` bleiben, aber Lifecycle nutzt Feature-Liste.
### Schritt 11: Referenzen global bereinigen
- **Ziel:** Import-Pfade konsistent.
- **Aenderungen:**
- Alle Imports von `modules/routes/routeFeature*.py` entfernen.
- Alle Imports von `modules/interfaces/interfaceDb*.py` auf `interfaceFeature*.py` umstellen.
- Alle Imports von `modules/datamodels/datamodel*.py` auf `datamodelFeature*.py` umstellen.
### Schritt 12: RBAC Katalog Registrierungen pro Feature
- **Ziel:** jede Feature-Instanz registriert UI/RESOURCE Keys.
- **Dateien:** `main<Feature>.py` je Feature-Container
- **Aenderung:** einheitliche Registrierungs-API verwenden (im Katalog-Service).
### Schritt 13: Tests und Verifikation
- **Smoke Tests:**
- Start Gateway, pruefen dass alle Feature-Routes registriert sind.
- Entferne Feature-Ordner testweise -> Route nicht mehr vorhanden.
- RBAC Katalog liefert Objekte fuer alle Features.
- Bootstrap erzeugt nur System-RBAC bei leerer DB.

View file

@ -0,0 +1,463 @@
# Umsetzungskonzept RBAC Access (separat, schrittweise)
## Ziel
Das RBAC-Konzept aus `concept_rbac_access_pending.md` technisch umsetzen, inkl.:
- Feature-Containerisierung (plug&play)
- Bootstrap auf System-RBAC reduzieren
- Feature-Services in Container integrieren
- aicore in aichat integrieren
- dynamisches Laden von Routes und Services
- striktes Import-Regelwerk ohne Abhaengigkeiten zwischen Feature-Containern
## Import-Regelwerk (Zielzustand)
- **Kein Import zwischen Feature-Containern.**
- **Feature-Container duerfen zentrale Module importieren (`modules/interfaces`, `modules/datamodels`, `modules/services`, `modules/aicore`, `modules/shared`, `modules/connectors`, `modules/security`, `modules/auth`).**
- **Umgekehrt darf zentraler Code keine Feature-Container importieren.**
## Wichtige konzeptionelle und architektonische Hinweise
- Jeder Feature-Container ist vollstaendig eigenstaendig (Code, Datamodelle, Interfaces, Routes, Services), darf aber zentrale Module nutzen.
- Zentrale Pfade bleiben Plattform-Schnittstellen und duerfen von Features genutzt werden; umgekehrt keine Feature-Imports aus zentralem Code.
- System-RBAC und Feature-RBAC sind strikt getrennt: Bootstrap initialisiert nur System-RBAC, Feature-RBAC entsteht durch Feature-Container.
- Der RBAC-Katalog fuer UI/RESOURCE liegt in `gateway/modules/security/rbacCatalog/` und ist persistent; Feature-Container muessen nur registrieren, nicht speichern.
- UI/RESOURCE-Objekte werden rein im Backend definiert und persistiert, keine UI-Abhaengigkeit.
- Datenobjekte werden dynamisch abgeleitet, sind aber nicht auf Features beschraenkt (System- und Feature-Tabellen sind sichtbar).
- Strikte Importregel erzeugt harte Grenzen: Shared-Module muessen in den Feature-Container verschoben oder repliziert werden.
- Zentraler Workflow (geplant unter `gateway/modules/workflows/automation/`) darf aus Features nicht direkt importiert werden; Zugriff nur ueber feature-interne Adapter/Entry-Points oder durch Verlagerung der benoetigten Teile in die Feature-Container.
## Schrittfolge mit Dateiaenderungen und Referenz-Checkliste
### Schritt 1: Bootstrap auf System-RBAC reduzieren
**Datei:** `gateway/modules/interfaces/interfaceBootstrap.py`
- Entfernen in `initBootstrap()`:
- `initFeatures(db)`
- `_initFeatureTemplateRoleAccessRules(db)`
- Entfernen:
- `_initFeatureTemplateRoles(db)` (Funktion + Call-Sites)
- `_initFeatureTemplateRoleAccessRules(db)` (Funktion + Call-Sites)
- **Beibehalten:** `initRoles(db)`, `initRbacRules(db)`
- **Checkliste Referenzen:**
- Keine Feature-Initialisierung in Bootstrap
- Keine Feature-Template-Rollen
- Keine Feature-Template-AccessRules
### Schritt 2: Feature-Container-Struktur anlegen
**Neue Ordner:**
- `gateway/modules/features/trustee/`
- `gateway/modules/features/chatbot/` (existiert)
- `gateway/modules/features/aichat/` (neu)
- `gateway/modules/features/neutralizer/` (rename)
- `gateway/modules/features/realestate/` (neu)
- `gateway/modules/features/automation/` (neu)
**Pflichtdateien je Feature:**
- `datamodelFeatureXxx.py`
- `interfaceFeatureXxx.py`
- `routeFeatureXxx.py`
- `mainXxx.py` (RBAC UI/RESOURCE Registry)
**Struktur-Vorgaben (Soll):**
- Jeder Container enthaelt alle benoetigten Submodule, inkl. Services und Helfer.
- Zentrale Module duerfen genutzt werden; keine Feature-zu-Feature-Imports.
- Service-Subfolder bleiben erhalten, liegen aber direkt im Container (`serviceXxx/`).
**Calling-Referenzen (allgemein):**
- `gateway/app.py` registriert alle Feature-Router dynamisch.
- Feature-Container registrieren ihre RBAC-Objekte im Katalog (z.B. via `mainXxx.py` als Entry-Point).
- Systemcode greift nicht auf Feature-Implementierung zu, sondern nur auf deren registrierte Router/Services.
### Schritt 3: Trustee verschieben
**Move:**
- `gateway/modules/routes/routeFeatureTrustee.py`
-> `gateway/modules/features/trustee/routeFeatureTrustee.py`
- `gateway/modules/interfaces/interfaceDbTrustee.py`
-> `gateway/modules/features/trustee/interfaceFeatureTrustee.py`
- `gateway/modules/datamodels/datamodelTrustee.py`
-> `gateway/modules/features/trustee/datamodelFeatureTrustee.py`
**Add:**
- `gateway/modules/features/trustee/mainTrustee.py` (RBAC Katalog)
**Referenzen anpassen (Checkliste):**
- Alle Imports auf `routeFeatureTrustee.py` neu
- Alle Imports auf `interfaceDbTrustee.py` neu
- Alle Imports auf `datamodelTrustee.py` neu
- `app.py` Router-Registrierung auf neuen Pfad
**Calling-Referenzen (Suche):**
- `rg -n "routeFeatureTrustee|interfaceDbTrustee|datamodelTrustee" gateway/`
### Schritt 4: Chatbot verschieben
**Move:**
- `gateway/modules/routes/routeFeatureChatbot.py`
-> `gateway/modules/features/chatbot/routeFeatureChatbot.py`
- `gateway/modules/interfaces/interfaceDbChatbot.py`
-> `gateway/modules/features/chatbot/interfaceFeatureChatbot.py`
- `gateway/modules/datamodels/datamodelChatbot.py`
-> `gateway/modules/features/chatbot/datamodelFeatureChatbot.py`
**Keep:**
- `gateway/modules/features/chatbot/mainChatbot.py` + Helpers
**Add:**
- `gateway/modules/features/chatbot/mainChatbotCatalog.py` (oder in mainChatbot)
**Referenzen anpassen:**
- Imports fuer `interfaceDbChatbot`/`datamodelChatbot`/`routeFeatureChatbot`
- `app.py` Router-Registrierung
**Calling-Referenzen (Suche):**
- `rg -n "routeFeatureChatbot|interfaceDbChatbot|datamodelChatbot" gateway/`
### Schritt 5: AIChat (chatworkflow) verschieben
**Move:**
- `gateway/modules/routes/routeFeatureChatDynamic.py`
-> `gateway/modules/features/aichat/routeFeatureAiChat.py`
- `gateway/modules/interfaces/interfaceDbChat.py`
-> `gateway/modules/features/aichat/interfaceFeatureAiChat.py`
- `gateway/modules/datamodels/datamodelChat.py`
-> `gateway/modules/features/aichat/datamodelFeatureAiChat.py`
**Add:**
- `gateway/modules/features/aichat/mainAiChat.py` (RBAC Katalog)
**Keep:**
- `gateway/modules/features/workflow/mainWorkflow.py` (falls reine Workflow-Logik)
**Referenzen anpassen:**
- Alle Imports auf `routeFeatureChatDynamic.py`
- Alle Imports auf `interfaceDbChat.py`
- Alle Imports auf `datamodelChat.py`
- `app.py` Router-Registrierung
**Calling-Referenzen (Suche):**
- `rg -n "routeFeatureChatDynamic|interfaceDbChat|datamodelChat" gateway/`
### Schritt 6: Neutralizer verschieben und Ordner umbenennen
**Rename:**
- `gateway/modules/features/neutralizePlayground/`
-> `gateway/modules/features/neutralizer/`
**Move:**
- `gateway/modules/routes/routeFeatureNeutralization.py`
-> `gateway/modules/features/neutralizer/routeFeatureNeutralizer.py`
- `gateway/modules/datamodels/datamodelNeutralizer.py`
-> `gateway/modules/features/neutralizer/datamodelFeatureNeutralizer.py`
**Add:**
- `gateway/modules/features/neutralizer/mainNeutralizer.py` (RBAC Katalog)
**Referenzen anpassen:**
- Alle Imports auf `neutralizePlayground`
- Alle Imports auf `routeFeatureNeutralization.py`
- `app.py` Router-Registrierung
**Calling-Referenzen (Suche):**
- `rg -n "neutralizePlayground|routeFeatureNeutralization|datamodelNeutralizer" gateway/`
### Schritt 7: RealEstate verschieben
**Move:**
- `gateway/modules/routes/routeFeatureRealEstate.py`
-> `gateway/modules/features/realestate/routeFeatureRealEstate.py`
- `gateway/modules/interfaces/interfaceDbRealestate.py`
-> `gateway/modules/features/realestate/interfaceFeatureRealEstate.py`
- `gateway/modules/datamodels/datamodelRealEstate.py`
-> `gateway/modules/features/realestate/datamodelFeatureRealEstate.py`
**Keep:**
- `gateway/modules/features/realEstate/mainRealEstate.py`
**Add:**
- `gateway/modules/features/realestate/mainRealEstateCatalog.py` (oder in mainRealEstate)
**Referenzen anpassen:**
- Imports auf `interfaceDbRealestate`, `datamodelRealEstate`, `routeFeatureRealEstate`
- `app.py` Router-Registrierung
**Calling-Referenzen (Suche):**
- `rg -n "routeFeatureRealEstate|interfaceDbRealestate|datamodelRealEstate" gateway/`
### Schritt 8: Automation Feature-Container
**Move:**
- `gateway/modules/routes/routeDataAutomation.py`
-> `gateway/modules/features/automation/routeFeatureAutomation.py`
**Add:**
- `gateway/modules/features/automation/interfaceFeatureAutomation.py`
- `gateway/modules/features/automation/datamodelFeatureAutomation.py`
- enthaelt `AutomationDefinition`
- `gateway/modules/features/automation/mainAutomation.py` (RBAC Katalog)
**Referenzen anpassen:**
- Alle Imports von `routeDataAutomation.py`
- Alle `AutomationDefinition`-Imports auf neuen Pfad
- `app.py` Router-Registrierung
**Calling-Referenzen (Suche):**
- `rg -n "routeDataAutomation|AutomationDefinition" gateway/`
### Schritt 9: Services in Feature-Container
**aichat:**
- Move:
- `gateway/modules/aicore/` -> `gateway/modules/features/aichat/aicore/`
- `gateway/modules/services/serviceAi/` -> `gateway/modules/features/aichat/serviceAi/`
- `gateway/modules/services/serviceExtraction/` -> `gateway/modules/features/aichat/serviceExtraction/`
- `gateway/modules/services/serviceGeneration/` -> `gateway/modules/features/aichat/serviceGeneration/`
- `gateway/modules/services/serviceWeb/` -> `gateway/modules/features/aichat/serviceWeb/`
**neutralizer:**
- Move:
- `gateway/modules/services/serviceNeutralization/` -> `gateway/modules/features/neutralizer/serviceNeutralization/`
**Shared Services:**
- bleiben in `gateway/modules/services`
**Referenzen anpassen:**
- Alle Imports der o.g. Services auf neue Pfade
- AI-Services duerfen keine Imports ausserhalb des aichat-Containers haben
- In Feature-Folders kein Subfolder `services`; Services liegen direkt als `serviceXxx/`
**Calling-Referenzen (Suche):**
- `rg -n "modules\\.services\\.service(Ai|Extraction|Generation|Web)|modules\\.aicore" gateway/`
- `rg -n "serviceNeutralization" gateway/`
**Code-Struktur (Soll):**
- Feature-Services importieren nur Feature-Datamodelle innerhalb des Containers.
- `aicore` wird komplett unter `features/aichat/` verschoben und ist nur dort nutzbar.
### Schritt 10: Plug&Play Router Loading
**Datei:** `gateway/app.py`
- Ersetze statische Imports aus `modules/routes` durch dynamische Feature-Discovery.
- Nur existierende Feature-Folder werden registriert.
**Referenzen anpassen:**
- Alle Router-Registrierungen neu
- Keine Route-Imports aus `modules/routes`
**Konzept Router-Discovery (Soll):**
- Scanne `gateway/modules/features/*/` nach `routeFeature*.py`.
- Pro Datei wird ein `router`-Objekt erwartet und registriert.
- Fehlende Router in einem Feature duerfen den Start nicht abbrechen (log + skip).
- Feature-Status markieren: `active`, `degraded` (z.B. Router ok, Service-Load fehlschlaegt).
### Schritt 11: Plug&Play Service Loading
**Zentraler Loader (shared):**
- Ein Service-Registry-Modul laedt pro Feature-Container definierte Services.
- Nur existierende Feature-Folder werden registriert.
**Referenzen anpassen:**
- Alle Service-Center-Aufrufe auf neuen Loader
- Kein direkter Import aus `modules/services/serviceAi` etc.
**Konzept Service-Discovery (Soll):**
- Pro Feature-Container werden vorhandene `service*`-Ordner als Services erkannt.
- Der Loader scannt `gateway/modules/features/<feature>/service*/` und registriert diese Services zentral.
- Services werden nur geladen, wenn der Feature-Container vorhanden ist.
- Registrierungs-Contract analog `modules/services/__init__.py`:
- In jedem `service*`-Ordner existiert `mainService*.py` mit Service-Klasse.
- Loader instanziert Service und exponiert ihn via `PublicService(...)`.
### Schritt 12: featuresLifecycle anpassen
**Datei:** `gateway/modules/features/featuresLifecycle.py`
- Import der Workflow-Logik aus neuem zentralen Pfad (`gateway/modules/workflows/automation/`)
- Keine Imports von Feature-Containern
**Bemerkung:**
- Kein Feature-Discovery notwendig: Modul ist zentraler Scheduler fuer Workflow/Automation.
- Update der Imports: `modules.features.workflow` -> `modules.workflows.automation` (nach Auslagerung).
- Workflow-API ist zentral und stabil: Features duerfen nur explizite Entry-Points nutzen (z.B. `syncAutomationEvents`), keine direkten Imports in interne Workflow-Module.
### Schritt 13: Globale Referenzen bereinigen
**Checkliste:**
- Keine Imports von `modules/routes/routeFeature*.py` mehr
- `interfaceDb*.py` und `datamodel*.py` bleiben fuer System-Module bestehen
- `modules/services/service*` bleiben fuer System-Services bestehen
- `modules/aicore` wird verschoben (Feature `aichat`), keine zentralen Imports mehr
**Calling-Referenzen (Suche):**
- `rg -n "modules\\.routes\\.routeFeature" gateway/`
### Schritt 14: Tests
- Gateway starten, Router-Registrierung pruefen
- Feature-Folder umbenennen mit z.b. prefix: Route/Service nicht mehr vorhanden
- RBAC Katalog liefert Feature-Objekte
- Bootstrap erzeugt nur System-RBAC
- RBAC Regeln bleiben erhalten, wenn Feature-Ordner entfernt/umbenannt ist (Persistenztest).
- Service-Discovery registriert Services korrekt und markiert `degraded` bei Teilfehlern.
## Referenz-Checkliste (gesamt)
- `gateway/app.py` Router
- `gateway/modules/features/featuresLifecycle.py`
- alle `routeFeature*.py`
- alle `interfaceDb*.py`
- alle `datamodel*.py`
- alle `main*.py` in Feature-Containern
- Service-Center/Service-Loader
- Tests/Fixtures
- RBAC-Katalog Modul in `gateway/modules/security/rbacCatalog/`
- `gateway/modules/interfaces/interfaceBootstrap.py`
## Import-Analyse (Istzustand, zu verschiebende Module)
### Routes (aktueller Stand)
- `routeFeatureRealEstate.py`:
- imports: `modules.auth`, `modules.datamodels.*`, `modules.interfaces.interfaceDbRealestate`, `modules.features.realEstate.mainRealEstate`, `modules.connectors`, `modules.shared`
- `routeFeatureTrustee.py`:
- imports: `modules.auth`, `modules.interfaces.interfaceDbTrustee`, `modules.interfaces.interfaceDbApp`, `modules.interfaces.interfaceFeatures`, `modules.datamodels.*`
- `routeFeatureChatDynamic.py`:
- imports: `modules.auth`, `modules.interfaces.interfaceDbChat`, `modules.datamodels.datamodelChat`, `modules.features.workflow`
- `routeFeatureChatbot.py`:
- imports: `modules.auth`, `modules.interfaces.interfaceDbChat`, `modules.interfaces.interfaceRbac`, `modules.datamodels.*`, `modules.features.chatbot`, `modules.features.workflow`, `modules.shared`
- `routeFeatureNeutralization.py`:
- imports: `modules.auth`, `modules.datamodels.datamodelNeutralizer`, `modules.features.neutralizePlayground`
- `routeDataAutomation.py`:
- imports: `modules.interfaces.interfaceDbChat`, `modules.auth`, `modules.datamodels.datamodelChat`, `modules.features.workflow`, `modules.shared`
### Interfaces (aktueller Stand)
- `interfaceDbTrustee.py`:
- imports: `modules.connectors`, `modules.shared`, `modules.interfaces.interfaceRbac`, `modules.security`, `modules.datamodels.*`
- `interfaceDbRealestate.py`:
- imports: `modules.datamodels.*`, `modules.connectors`, `modules.shared`, `modules.security`, `modules.interfaces.interfaceRbac`
- `interfaceDbChat.py` / `interfaceDbChatbot.py`:
- imports: `modules.security`, `modules.datamodels.*`, `modules.connectors`, `modules.shared`, `modules.interfaces.interfaceRbac`, `modules.interfaces.interfaceDbManagement`
### Feature-Logik (aktueller Stand)
- `modules/features/chatbot/mainChatbot.py`:
- imports: `modules.datamodels.*`, `modules.shared`, `modules.services`, `modules.workflows`, `modules.connectors`
- `modules/features/workflow/mainWorkflow.py`:
- imports: `modules.datamodels.*`, `modules.shared`, `modules.services`, `modules.workflows`
- `modules/features/realEstate/mainRealEstate.py`:
- imports: `modules.datamodels.*`, `modules.services`, `modules.interfaces.interfaceDbRealestate`, `modules.connectors`, externe libs
- `modules/features/neutralizePlayground/mainNeutralizePlayground.py`:
- imports: `modules.datamodels.*`, `modules.services`
### Services / aicore (aktueller Stand)
- `modules/services/serviceAi/mainServiceAi.py`:
- imports: `modules.datamodels.*`, `modules.services.serviceExtraction`, `modules.interfaces.interfaceAiObjects`, `modules.shared`
- `modules/services/serviceNeutralization/mainServiceNeutralization.py`:
- imports: `modules.datamodels.datamodelNeutralizer`, eigene subProcess-* Module
- `modules/aicore/*`:
- imports: `modules.datamodels.*`, `modules.security`, `modules.connectors`
### Services Submodule-Analyse (Detail)
**serviceAi/**
- `mainServiceAi.py`
- imports: `modules.datamodels.datamodelChat`, `modules.datamodels.datamodelAi`,
`modules.datamodels.datamodelExtraction`, `modules.datamodels.datamodelWorkflow`,
`modules.datamodels.datamodelDocument`, `modules.interfaces.interfaceAiObjects`,
`modules.services.serviceExtraction`, `modules.shared`
- `subStructureGeneration.py`
- imports: `modules.datamodels.datamodelExtraction`, `modules.datamodels.datamodelAi`,
`modules.workflows.processing.shared.stateTools`
- `subStructureFilling.py`
- imports: `modules.datamodels.datamodelExtraction`, `modules.datamodels.datamodelAi`,
`modules.workflows.processing.shared.stateTools`
- `subResponseParsing.py`
- imports: `modules.shared.jsonUtils`, `modules.services.serviceAi.subJsonResponseHandling`,
`modules.datamodels.datamodelAi`
- `subJsonResponseHandling.py`
- imports: `modules.shared.jsonUtils`, `modules.datamodels.datamodelAi`
- `subJsonMerger.py`
- imports: `modules.shared.jsonUtils`
- `subDocumentIntents.py`
- imports: `modules.datamodels.datamodelChat`, `modules.datamodels.datamodelExtraction`,
`modules.workflows.processing.shared.stateTools`
- `subContentExtraction.py`
- imports: `modules.datamodels.datamodelChat`, `modules.datamodels.datamodelExtraction`,
`modules.workflows.processing.shared.stateTools`
- `subAiCallLooping.py`
- imports: `modules.datamodels.datamodelAi`, `modules.datamodels.datamodelExtraction`,
`modules.services.serviceAi.subJsonResponseHandling`,
`modules.services.serviceAi.subLoopingUseCases`,
`modules.workflows.processing.shared.stateTools`,
`modules.shared.jsonContinuation`, `modules.shared.jsonUtils`
- `subLoopingUseCases.py`, `subJsonResponseHandling.py`, `subJsonMerger.py` (shared utils)
- imports: `modules.shared.*` (je nach Datei)
**serviceExtraction/**
- `mainServiceExtraction.py`
- imports: `modules.datamodels.datamodelExtraction`, `modules.datamodels.datamodelChat`,
`modules.datamodels.datamodelAi`, `modules.aicore.aicoreModelRegistry`,
`modules.aicore.aicoreModelSelector`, `modules.shared.jsonUtils`
- `merging/*` (mergerText/mergerTable/mergerDefault)
- imports: `modules.datamodels.datamodelExtraction`
- `extractors/*` (Xml/Xlsx/Text/Sql/Pptx/Html/Image/Json/Pdf/Csv/Docx/Binary)
- imports: `modules.datamodels.datamodelExtraction` (teilweise ContentExtracted)
- `chunking/*` (chunkerText/ChunkerTable/ChunkerStructure/ChunkerImage)
- imports: `modules.datamodels.datamodelExtraction`
- `subRegistry.py`
- imports: `modules.datamodels.datamodelExtraction`
- `subPromptBuilderExtraction.py`
- imports: `modules.datamodels.datamodelAi`
- `subPipeline.py`
- imports: `modules.datamodels.datamodelExtraction`
- `subMerger.py`
- imports: `modules.datamodels.datamodelExtraction`
**serviceGeneration/**
- `mainServiceGeneration.py`
- imports: `modules.datamodels.datamodelDocument`, `modules.datamodels.datamodelChat`,
`modules.services.serviceGeneration.subDocumentUtility`
- `renderers/*`
- imports: `modules.datamodels.datamodelDocument` (alle Renderer)
- `documentRendererBaseTemplate.py` imports zusaetzlich:
`modules.datamodels.datamodelJson`, `modules.datamodels.datamodelAi`
- `paths/*`
- `imagePath.py`: `modules.datamodels.datamodelWorkflow`, `modules.datamodels.datamodelAi`
- `documentPath.py`: `modules.datamodels.datamodelWorkflow`, `modules.datamodels.datamodelExtraction`,
`modules.datamodels.datamodelAi`, `modules.datamodels.datamodelDocument`,
`modules.workflows.processing.shared.stateTools`
- `codePath.py`: `modules.datamodels.datamodelWorkflow`, `modules.datamodels.datamodelExtraction`,
`modules.datamodels.datamodelAi`, `modules.shared.jsonUtils`
- `subStructureGenerator.py` / `subPromptBuilderGeneration.py`
- imports: `modules.datamodels.datamodelJson`
- `subContentGenerator.py`
- imports: `modules.services.serviceGeneration.subContentIntegrator`,
`modules.workflows.processing.shared.stateTools`
**serviceWeb/**
- `mainServiceWeb.py`
- imports: `modules.datamodels.datamodelAi` (AiCallOptions, OperationTypeEnum, AiCallPrompt*)
**serviceNeutralization/**
- `mainServiceNeutralization.py`
- imports: `modules.datamodels.datamodelNeutralizer`,
`modules.services.serviceNeutralization.subProcess*`
- `subProcessText.py`
- imports: `modules.services.serviceNeutralization.subParseString`
- `subProcessList.py`
- imports: `modules.services.serviceNeutralization.subParseString`,
`modules.services.serviceNeutralization.subPatterns`
- `subParseString.py`
- imports: `modules.services.serviceNeutralization.subPatterns`
## Compliance-Check gegen Import-Regelwerk (Zielzustand)
**Zielkriterium:**
- Keine Imports zwischen Feature-Containern.
- Feature-Container duerfen zentrale Module importieren.
- Zentrale Module duerfen keine Feature-Container importieren.
**Erwarteter Zustand nach Migration:**
- Alle `interfaceFeatureXxx.py`, `routeFeatureXxx.py`, `datamodelFeatureXxx.py`, `mainXxx.py` liegen im jeweiligen Feature-Container.
- Interne Feature-Imports (z.B. `chatbot` -> `chatbot.eventManager`) sind erlaubt.
- Keine Feature-zu-Feature-Imports mehr (z.B. `chatbot` -> `workflow` entfaellt durch Auslagerung).
**Hinweis zur Validierung:**
- Compliance-Check erfolgt nach Abschluss der Verschiebungen und Import-Anpassungen.
- Der Istzustand dient nur als Analysegrundlage und ist nicht compliance-faehig.
## Import-Check (Zielzustand, Soll)
**Erlaubte Imports innerhalb eines Feature-Containers:**
- Relative Imports innerhalb des Containers (z.B. `.eventManager`, `.subModule`).
- Absolute Imports innerhalb des eigenen Containers (z.B. `modules.features.chatbot.*` im Container `chatbot`).
- Standardbibliothek und externe Libraries.
**Nicht erlaubt (muss im Zielzustand eliminiert werden):**
- Imports aus anderen Feature-Containern (`modules.features.<anderesFeature>.*`).
- Zentrale Module importieren Feature-Container (`modules.features.*`).
**Soll-Checkliste je Feature-Container:**
- Keine `modules.features.<anderesFeature>.*` Imports.
- Keine Feature-zu-Feature-Abhaengigkeiten.
**Pruefroutine (rg-Patterns, Zielzustand):**
- Finde direkte Cross-Feature-Imports:
- `rg -n "modules\\.features\\.(?!<feature>\\.)" gateway/modules/features/<feature>/`
- Finde verbotene Feature-Imports aus zentralem Code:
- `rg -n "modules\\.features\\." gateway/modules/(routes|interfaces|datamodels|services|aicore|shared|connectors|security|auth)/`
## Istzustand (nur Analyse, vor Migration)
**Konflikte (heute):**
- Alle Routen, Interfaces, Feature-Logiken und Services importieren Module ausserhalb eines Feature-Containers.
- Es bestehen Imports zwischen Features (z.B. `routeFeatureChatbot` -> `modules.features.workflow`, im Zielzustand entfaellt da `workflow` kein Feature mehr ist).
**Liste der Imports zwischen Features (Istzustand, Analyse):**
- `gateway/modules/interfaces/interfaceDbChatbot.py` -> `modules.features.chatbot.eventManager`
## Offene Punkte fuer Umsetzung
- Dynamische Feature-Discovery in `app.py` und Service-Loader designen
- Schrittweise Migration je Feature mit Tests

View file

@ -0,0 +1,687 @@
# Trustee Feature - RBAC Architektur & Gateway-Readiness
## Übersicht
Dieses Dokument beschreibt die **Zwei-Stufen-RBAC-Architektur** für das Trustee Feature und beantwortet die Frage, ob das Gateway bereit für die UI-Entwicklung ist.
**Erstellungsdatum**: 2026-01-23
**Status**: Review
---
## 1. Gateway-Status
### 1.1 Ist das Gateway bereit für UI-Arbeit?
**Ja, das Gateway ist vollständig bereit.**
| Komponente | Datei | Status |
|------------|-------|--------|
| **Routes** | `routeFeatureTrustee.py` | ✅ Komplett |
| **Interface** | `interfaceFeatureTrustee.py` | ✅ Komplett |
| **Datamodel** | `datamodelFeatureTrustee.py` | ✅ Komplett |
| **Feature-RBAC** | Via `TrusteeAccess` Tabelle | ✅ Implementiert |
### 1.2 Verfügbare API-Endpoints
Alle Endpoints verwenden das URL-Pattern: `/api/trustee/{instanceId}/...`
| Endpoint-Gruppe | Basis-Route | CRUD | Options |
|-----------------|-------------|------|---------|
| Organisations | `/api/trustee/{instanceId}/organisations` | ✅ | ✅ |
| Roles | `/api/trustee/{instanceId}/roles` | ✅ | ✅ |
| **Access** | `/api/trustee/{instanceId}/access` | ✅ | - |
| Contracts | `/api/trustee/{instanceId}/contracts` | ✅ | ✅ |
| Documents | `/api/trustee/{instanceId}/documents` | ✅ | ✅ |
| Positions | `/api/trustee/{instanceId}/positions` | ✅ | ✅ |
| Position-Documents | `/api/trustee/{instanceId}/position-documents` | ✅ | - |
| **User Options** | `/api/users/options` | - | ✅ |
### 1.3 Wichtige Änderung vs. UI-Spezifikation
Die ursprüngliche UI-Spezifikation (`doc_trustee_feature_ui_specification.md`) verwendet URLs wie:
```
/api/trustee/organisations/
```
Die **aktuelle Implementierung** verwendet:
```
/api/trustee/{instanceId}/organisations/
```
**Grund**: Multi-Tenancy - mehrere Treuhandbüros (Feature-Instanzen) pro Mandate möglich.
---
## 2. Architektur: Zwei-Stufen-RBAC für Features
### 2.1 Das Problem
Ein Treuhandbüro (Feature-Instanz) verwaltet mehrere Kunden (Organisationen). Jeder Mitarbeiter soll nur die Kunden sehen, für die er zuständig ist:
- **User A** → Kunde 1, Kunde 2
- **User B** → Kunde 2, Kunde 3
- **User C** → Alle Kunden (Admin)
Dies erfordert eine **Feature-interne Isolation**, die über das System-RBAC hinausgeht.
### 2.2 Lösung: Zwei-Stufen-RBAC
```
┌─────────────────────────────────────────────────────────────────────────┐
│ STUFE 1: SYSTEM-RBAC │
│ (Mandate + Feature Instance Zugang) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User ──► UserMandate ──► Mandate │
│ │ │
│ └──► FeatureAccess ──► FeatureInstance (Treuhandbüro) │
│ │ │
│ └── Rollen: feature-admin, feature-user │
│ │
│ Frage: Hat der User überhaupt Zugang zu diesem Feature? │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ STUFE 2: FEATURE-SPEZIFISCHES RBAC │
│ (Isolation innerhalb des Features) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ TrusteeAccess │
│ ├── userId → Welcher User │
│ ├── organisationId → Welcher Kunde (Isolationsobjekt) │
│ ├── roleId → Welche Feature-Rolle (admin, operate, userreport)│
│ └── contractId → Optional: Einschränkung auf bestimmten Vertrag │
│ │
│ Frage: Welche Kunden darf der User sehen? Mit welchen Rechten? │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.3 Vergleich: System-RBAC vs. Feature-RBAC
| Aspekt | System-RBAC | Feature-RBAC (Trustee) |
|--------|-------------|------------------------|
| **Tabelle** | `AccessRule` | `TrusteeAccess` |
| **Scope** | Global / Mandate | Feature-Instanz |
| **Objekte** | Tables, UI-Items, API-Resources | Organisationen, Contracts |
| **Verwaltung** | SysAdmin | Feature-Admin |
| **Zweck** | "Was darf der User technisch?" | "Welche Daten darf der User sehen?" |
### 2.4 Feature-spezifische Rollen
Das Trustee-Feature definiert drei Rollen:
| Rolle | Beschreibung | Berechtigungen |
|-------|--------------|----------------|
| **admin** | Feature-Administrator | Voller CRUD auf Organisation + alle Contracts |
| **operate** | Sachbearbeiter | CRUD für Contracts, Documents, Positions |
| **userreport** | Endbenutzer | Nur eigene erstellte Records (Documents, Positions) |
### 2.5 Contract-Level Isolation (optional)
`TrusteeAccess.contractId` ermöglicht feinere Isolation:
- **Leer/NULL**: Zugriff auf alle Contracts der Organisation
- **Gesetzt**: Zugriff nur auf diesen spezifischen Contract
```
Beispiel:
- User A hat Zugriff auf Organisation "Kunde-X" mit contractId=NULL
→ Sieht alle Contracts von Kunde-X
- User B hat Zugriff auf Organisation "Kunde-X" mit contractId="contract-123"
→ Sieht nur Contract "contract-123" von Kunde-X
```
---
## 3. Implementierung im Gateway
### 3.1 Datenmodell: TrusteeAccess
```python
class TrusteeAccess(BaseModel):
id: str # UUID
organisationId: str # FK zu TrusteeOrganisation
roleId: str # FK zu TrusteeRole (admin, operate, userreport)
userId: str # FK zu User
contractId: Optional[str] # Optional: FK zu TrusteeContract
mandateId: Optional[str] # System-Kontext
featureInstanceId: Optional[str] # Feature-Instanz
```
### 3.2 Kombinierte Berechtigungsprüfung
Die Methode `checkCombinedPermission()` in `interfaceFeatureTrustee.py` kombiniert beide Stufen:
```python
def checkCombinedPermission(self, modelClass, operation, organisationId, contractId, recordCreatedBy):
"""
Kombiniert System-RBAC + Feature-Level RBAC.
1. Prüft System-RBAC (AccessLevel: ALL, GROUP, MY, NONE)
2. Bei ALL: Bypass Feature-Level (SysAdmin)
3. Sonst: Prüft TrusteeAccess für User + Organisation + Contract
"""
# Stufe 1: System-RBAC
accessLevel = self.getRbacAccessLevel(modelClass, operation)
if accessLevel == AccessLevel.NONE:
return False
if accessLevel == AccessLevel.ALL:
return True # SysAdmin bypass
# Stufe 2: Feature-RBAC via TrusteeAccess
roles = self.getUserTrusteeRoles(self.userId, organisationId, contractId)
if "admin" in roles:
return True
if "operate" in roles and modelClass in (TrusteeContract, TrusteeDocument, ...):
return True
if "userreport" in roles and recordCreatedBy == self.userId:
return True
return False
```
### 3.3 Automatische Filterung
Die Methode `filterRecordsByTrusteeAccess()` filtert Ergebnisse basierend auf User-Zugriff:
```python
def filterRecordsByTrusteeAccess(self, records, modelClass):
"""
Filtert Records basierend auf TrusteeAccess des Users.
- admin: Sieht alle Records der Organisation
- operate: Sieht alle Records der Organisation
- userreport: Sieht nur eigene Records (_createdBy = userId)
"""
```
---
## 4. Generisches Muster für andere Features
### 4.1 Feature-Isolation-Pattern
Jedes Feature kann sein eigenes Isolationsmodell definieren:
```
{Feature}Access: User → {Isolationsobjekt} → Role [→ Unter-Objekt]
```
### 4.2 Beispiele für andere Features
| Feature | Access-Tabelle | Isolationsobjekt | Unter-Objekt |
|---------|----------------|------------------|--------------|
| **Trustee** | `TrusteeAccess` | Organisation (Kunde) | Contract |
| **RealEstate** | `RealEstateAccess` | Property (Liegenschaft) | Unit (Wohnung) |
| **ProjectMgmt** | `ProjectAccess` | Project | - |
| **Accounting** | `AccountingAccess` | Company | FiscalYear |
### 4.3 Implementierungsvorlage
Für ein neues Feature mit Isolation:
1. **Datamodel**: `{Feature}Access` mit `userId`, `{isolationObject}Id`, `roleId`
2. **Interface**: `checkCombinedPermission()` und `filterRecordsByAccess()`
3. **Routes**: CRUD für `{Feature}Access`
4. **Roles**: Feature-spezifische Rollen definieren
---
## 5. API-Referenz für UI-Entwicklung
### 5.1 Access-Verwaltung (User-Zuweisung zu Kunden)
```
# Alle Access-Records abrufen
GET /api/trustee/{instanceId}/access
# Access für bestimmte Organisation
GET /api/trustee/{instanceId}/access/organisation/{orgId}
# Access für bestimmten User
GET /api/trustee/{instanceId}/access/user/{userId}
# Neuen Access erstellen (User zu Kunde zuweisen)
POST /api/trustee/{instanceId}/access
Body: {
"userId": "user-uuid",
"organisationId": "kunde-id",
"roleId": "operate",
"contractId": null // Optional
}
# Access aktualisieren
PUT /api/trustee/{instanceId}/access/{accessId}
# Access löschen
DELETE /api/trustee/{instanceId}/access/{accessId}
```
### 5.2 Options-Endpoints für Dropdowns
```
# Organisationen für Dropdown
GET /api/trustee/{instanceId}/organisations/options
→ [{ "value": "org-id", "label": "Kunde AG" }, ...]
# Rollen für Dropdown
GET /api/trustee/{instanceId}/roles/options
→ [{ "value": "admin", "label": "Administrator" }, ...]
# Contracts für Dropdown (dynamisch nach Organisation)
GET /api/trustee/{instanceId}/contracts/options
→ [{ "value": "contract-uuid", "label": "Vertrag 2026" }, ...]
# Users für Dropdown (aus aktuellem Mandate)
GET /api/users/options
→ [{ "value": "user-uuid", "label": "Max Muster" }, ...]
```
---
## 6. UI-Anforderungen
### 6.1 Access-View (User-Verwaltung)
Eine View zur Verwaltung der User-Zuweisungen:
| Spalte | Typ | Quelle |
|--------|-----|--------|
| User | Select | `/api/users/options` |
| Organisation | Select | `/api/trustee/{instanceId}/organisations/options` |
| Rolle | Select | `/api/trustee/{instanceId}/roles/options` |
| Contract | Select (optional) | `/api/trustee/{instanceId}/contracts/options` |
**Hinweis**: Das `contractId` Dropdown sollte dynamisch nach Auswahl der Organisation gefiltert werden.
### 6.2 Berechtigungen für Access-View
- **Sichtbar für**: Feature-Admins (`admin` Rolle in mindestens einer Organisation)
- **Create/Update/Delete**: Nur für Organisationen, in denen User Admin ist
---
## 7. Offene Punkte / Entscheidungen
### 7.1 Geklärt
- [x] Ist das Gateway bereit? → **Ja**
- [x] Sind Feature-spezifische Rollen gleich wie System-Rollen? → **Nein, bewusst getrennt**
- [x] Ist zweistufiges RBAC nötig? → **Ja, bereits implementiert**
- [x] Existiert der Users-Options-Endpoint? → **Ja, `/api/users/options`**
### 7.2 Offen für UI-Review
- [ ] Soll die Access-View eine eigene Seite sein oder Tab in Organisation?
- [ ] Wie wird Contract-Dropdown bei organisationId-Änderung aktualisiert?
- [ ] Braucht es eine "Bulk-Zuweisung" (User zu mehreren Orgs gleichzeitig)?
---
## 8. Zusammenfassung
### Das Gateway bietet:
1. **Vollständige CRUD-Routes** für alle Trustee-Entities
2. **Zwei-Stufen-RBAC** (System + Feature-Level)
3. **Automatische Filterung** basierend auf User-Access
4. **Options-Endpoints** für alle Dropdowns
### Das UI muss implementieren:
1. **Access-View** zur Verwaltung von User-Zuweisungen
2. **Dynamische Dropdowns** mit Abhängigkeiten (Contract nach Organisation)
3. **instanceId** in allen API-Calls verwenden
### Architektur-Prinzip:
```
System-RBAC → Zugang zum Feature
Feature-RBAC → Isolation innerhalb des Features
```
---
## 9. Implementierungsplan: Fehlende Elemente
### 9.1 Aktueller Stand (Analyse)
| Komponente | Status | Details |
|------------|--------|---------|
| **Gateway (Backend)** | ✅ Komplett | Routes, Interface, Datamodel, RBAC |
| **API Hooks** (`useTrustee.ts`) | ✅ Komplett | CRUD-Hooks für alle Entities |
| **API Client** (`trusteeApi.ts`) | ✅ Komplett | Alle Endpoints implementiert |
| **Views (Grundgerüst)** | ⚠️ Teilweise | Tabellen existieren, aber ohne Forms |
| **Create/Edit Formulare** | ❌ Fehlt | Buttons vorhanden, aber ohne Funktion |
| **Label-Auflösung** | ❌ Fehlt | Zeigt IDs statt Labels (userId, orgId) |
| **Dynamische Dropdowns** | ❌ Fehlt | Contract abhängig von Organisation |
### 9.2 Fehlende Implementierungen
#### P1: Create/Edit Formulare für alle Views
**Problem:** Die "Neu erstellen" und "Bearbeiten" Buttons existieren, aber öffnen kein Formular.
**Betroffene Views:**
- `TrusteeOrganisationsView.tsx`
- `TrusteeRolesView.tsx`
- `TrusteeAccessView.tsx`
- `TrusteeContractsView.tsx`
- `TrusteeDocumentsView.tsx`
- `TrusteePositionsView.tsx`
**Lösung:** Pro View ein Modal mit EditForm implementieren, das die automatisch generierten Felder aus `generateEditFieldsFromAttributes()` verwendet.
**Beispiel-Pattern:**
```tsx
// In TrusteeOrganisationsView.tsx
const [isModalOpen, setIsModalOpen] = useState(false);
const [editingItem, setEditingItem] = useState<TrusteeOrganisation | null>(null);
const { generateCreateFieldsFromAttributes, generateEditFieldsFromAttributes } = useTrusteeOrganisations();
const { handleCreate, handleUpdate } = useTrusteeOrganisationOperations();
// Create-Button
<button onClick={() => { setEditingItem(null); setIsModalOpen(true); }}>
+ Neue Organisation
</button>
// Edit-Button
<button onClick={() => { setEditingItem(org); setIsModalOpen(true); }}>
✏️
</button>
// Modal mit EditForm
<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<EditForm
fields={editingItem ? generateEditFieldsFromAttributes() : generateCreateFieldsFromAttributes()}
initialData={editingItem || {}}
onSave={async (data) => {
if (editingItem) {
await handleUpdate(editingItem.id, data);
} else {
await handleCreate(data);
}
setIsModalOpen(false);
refetch();
}}
/>
</Modal>
```
---
#### P2: Label-Auflösung für Foreign Keys
**Problem:** `TrusteeAccessView` zeigt:
- `userId: "uuid-123"` statt `"Max Muster"`
- `organisationId: "org-1"` statt `"Kunde AG"`
- `roleId: "admin"` statt `"Administrator"`
**Lösung:** Options aus den entsprechenden Endpoints laden und als Lookup-Map verwenden.
**Implementierung:**
```tsx
// In TrusteeAccessView.tsx
const [userOptions, setUserOptions] = useState<{value: string, label: string}[]>([]);
const [orgOptions, setOrgOptions] = useState<{value: string, label: string}[]>([]);
const [roleOptions, setRoleOptions] = useState<{value: string, label: string}[]>([]);
useEffect(() => {
const loadOptions = async () => {
const [users, orgs, roles] = await Promise.all([
api.get('/api/users/options'),
api.get(`/api/trustee/${instanceId}/organisations/options`),
api.get(`/api/trustee/${instanceId}/roles/options`)
]);
setUserOptions(users.data);
setOrgOptions(orgs.data);
setRoleOptions(roles.data);
};
loadOptions();
}, [instanceId]);
// Lookup-Funktion
const getLabel = (options: {value: string, label: string}[], value: string) =>
options.find(o => o.value === value)?.label || value;
// In der Tabelle
<td>{getLabel(userOptions, access.userId)}</td>
<td>{getLabel(orgOptions, access.organisationId)}</td>
<td>{getLabel(roleOptions, access.roleId)}</td>
```
---
#### P3: Dynamische Dropdown-Filterung
**Problem:** Im AccessView/ContractView soll das Contract-Dropdown nur Contracts der ausgewählten Organisation zeigen.
**Lösung:** `dependsOn`-Attribut im Datamodel nutzen + dynamisches Nachladen.
**Implementierung im EditForm:**
```tsx
// Wenn organisationId geändert wird, Contracts neu laden
const handleFieldChange = async (fieldKey: string, value: any) => {
setFormData(prev => ({ ...prev, [fieldKey]: value }));
// Dynamische Abhängigkeiten prüfen
const dependentFields = fields.filter(f => f.dependsOn === fieldKey);
for (const depField of dependentFields) {
if (depField.optionsReference?.includes('contracts')) {
// Contracts für diese Organisation laden
const contracts = await api.get(
`/api/trustee/${instanceId}/contracts/options?organisationId=${value}`
);
// Options aktualisieren...
}
}
};
```
**Backend-Erweiterung nötig:** Der `/contracts/options` Endpoint muss einen `organisationId` Query-Parameter unterstützen.
---
#### P4: Generischer Options-Hook
**Problem:** Options-Laden ist in jeder View dupliziert.
**Lösung:** Zentraler Hook für Trustee-Options.
```tsx
// hooks/useTrusteeOptions.ts
export function useTrusteeOptions() {
const instanceId = useInstanceId();
const [options, setOptions] = useState<TrusteeOptionsMap>({});
const loadOptions = useCallback(async (entity: 'organisations' | 'roles' | 'contracts' | 'users') => {
if (entity === 'users') {
const res = await api.get('/api/users/options');
setOptions(prev => ({ ...prev, users: res.data }));
} else {
const res = await api.get(`/api/trustee/${instanceId}/${entity}/options`);
setOptions(prev => ({ ...prev, [entity]: res.data }));
}
}, [instanceId]);
const getLabel = useCallback((entity: string, value: string) => {
return options[entity]?.find(o => o.value === value)?.label || value;
}, [options]);
return { options, loadOptions, getLabel };
}
```
---
### 9.3 Implementierungsreihenfolge
| Phase | Aufgabe | Aufwand | Priorität |
|-------|---------|---------|-----------|
| **1** | `useTrusteeOptions` Hook erstellen | 2h | Hoch |
| **2** | Label-Auflösung in allen Views | 3h | Hoch |
| **3** | Create/Edit Modal in `TrusteeOrganisationsView` | 3h | Hoch |
| **4** | Create/Edit Modal für alle anderen Views kopieren | 4h | Mittel |
| **5** | Dynamische Contract-Filterung | 2h | Mittel |
| **6** | Backend: `/contracts/options?organisationId=` | 1h | Mittel |
| **7** | Position-Document Verknüpfungs-UI | 4h | Niedrig |
| **8** | Testing & Bugfixes | 4h | Hoch |
**Geschätzter Gesamtaufwand:** ~23h (3-4 Arbeitstage)
---
### 9.4 Detailplan Phase 1-3 (MVP)
#### Schritt 1: `useTrusteeOptions` Hook
**Datei:** `frontend_nyla/src/hooks/useTrusteeOptions.ts`
```typescript
/**
* Hook für Trustee-Options (Dropdowns, Label-Auflösung)
*/
import { useState, useCallback, useEffect } from 'react';
import api from '../api';
import { useInstanceId } from './useCurrentInstance';
interface Option {
value: string;
label: string;
}
interface TrusteeOptions {
users: Option[];
organisations: Option[];
roles: Option[];
contracts: Option[];
documents: Option[];
positions: Option[];
}
export function useTrusteeOptions(autoLoad: (keyof TrusteeOptions)[] = []) {
const instanceId = useInstanceId();
const [options, setOptions] = useState<Partial<TrusteeOptions>>({});
const [loading, setLoading] = useState(false);
const loadOptions = useCallback(async (
entities: (keyof TrusteeOptions)[],
filters?: { organisationId?: string }
) => {
if (!instanceId) return;
setLoading(true);
try {
const promises = entities.map(async (entity) => {
let url: string;
if (entity === 'users') {
url = '/api/users/options';
} else {
url = `/api/trustee/${instanceId}/${entity}/options`;
if (filters?.organisationId && entity === 'contracts') {
url += `?organisationId=${filters.organisationId}`;
}
}
const res = await api.get(url);
return { entity, data: res.data };
});
const results = await Promise.all(promises);
const newOptions: Partial<TrusteeOptions> = {};
results.forEach(({ entity, data }) => {
newOptions[entity] = data;
});
setOptions(prev => ({ ...prev, ...newOptions }));
} finally {
setLoading(false);
}
}, [instanceId]);
const getLabel = useCallback((entity: keyof TrusteeOptions, value: string): string => {
return options[entity]?.find(o => o.value === value)?.label || value;
}, [options]);
// Auto-Load bei Mount
useEffect(() => {
if (autoLoad.length > 0 && instanceId) {
loadOptions(autoLoad);
}
}, [instanceId, autoLoad.join(',')]);
return { options, loadOptions, getLabel, loading };
}
```
#### Schritt 2: TrusteeAccessView mit Labels
**Datei:** `frontend_nyla/src/pages/views/trustee/TrusteeAccessView.tsx`
- Import `useTrusteeOptions`
- Auto-Load: `['users', 'organisations', 'roles']`
- Tabelle: `getLabel()` für userId, organisationId, roleId
#### Schritt 3: Create/Edit Modal
**Ansatz:** Bestehende `EditPopup`-Komponente aus anderen Views (z.B. Prompts) wiederverwenden.
**Dateien:**
- `TrusteeOrganisationsView.tsx` - Modal hinzufügen
- Nutzt `generateCreateFieldsFromAttributes()` für Feld-Definitionen
- `handleCreate()` / `handleUpdate()` für API-Calls
---
### 9.5 Backend-Erweiterung (optional)
Für dynamische Contract-Filterung nach Organisation:
**Datei:** `gateway/modules/features/trustee/routeFeatureTrustee.py`
```python
@router.get("/{instanceId}/contracts/options", response_model=List[Dict[str, Any]])
async def getContractOptions(
request: Request,
instanceId: str = Path(...),
organisationId: Optional[str] = Query(None), # NEU
context: RequestContext = Depends(getRequestContext)
) -> List[Dict[str, Any]]:
"""Get contract options, optionally filtered by organisation."""
mandateId = await _validateInstanceAccess(instanceId, context)
interface = getInterface(context.user, mandateId=mandateId, featureInstanceId=instanceId)
if organisationId:
contracts = interface.getContractsByOrganisation(organisationId)
else:
result = interface.getAllContracts(None)
contracts = result.items if hasattr(result, 'items') else result
return [{"value": c.id, "label": c.label or c.name or c.id} for c in contracts]
```
---
### 9.6 Offene Entscheidungen für Review
| # | Frage | Optionen |
|---|-------|----------|
| 1 | **Modal vs. Inline-Edit** | A) Modal-Dialog / B) Inline in Tabelle / C) Separate Seite |
| 2 | **Bulk-Operationen** | A) Ja, Multi-Select + Batch-Aktionen / B) Nein, nur einzeln |
| 3 | **Position-Document UI** | A) Separate View / B) Inline in Position-View / C) Beides |
| 4 | **Validierung** | A) Client-only / B) Server-only / C) Beides |
---
**Dokumentversion**: 1.1
**Letzte Aktualisierung**: 2026-01-23
**Autor**: Claude (AI-Assistent)
**Status**: Zur Überprüfung