wiki/concepts/Kontext-20260328.md

129 lines
10 KiB
Markdown

Kontext: Wir arbeiten an einer Multi-Tenant-Applikation mit Feature-Store-Modell. Ich möchte UI und Gateway austesten und offene Themen weiterführen. Hier ist der vollständige architektonische Kontext:
---
## Architektur-Überblick
- **Backend (Gateway):** `@poweron/gateway` — FastAPI (Python), PostgreSQL
- **Frontend:** `@poweron/frontend_nyla` — React/TypeScript (Vite)
- **Konzeptdokument:** `@poweron/wiki/concepts/Multi-Mandate-Onboarding-und-Store-Konzept.md`
- **Umsetzungsbericht (103 Punkte OK):** `@poweron/wiki/concepts/Multi-Mandate-Umsetzungsbericht.md`
---
## RBAC-Rollenmodell (KRITISCH — nie verwechseln!)
Es gibt ZWEI vollständig getrennte Rollensysteme. Siehe auch: `@poweron/.cursor/rules/rbac-role-separation.mdc`
### 1. Mandantenrollen (Mandate Roles)
- Labels: `admin`, `user`, `viewer` (generisch)
- Gespeichert mit: `featureCode=NULL`, `featureInstanceId=NULL`, `mandateId=<id>`
- Templates: `isSystemRole=True`, `mandateId=NULL`, `featureCode=NULL`
- Verknüpfung: `UserMandateRole` Junction Table
- Logik: Zuordnung User → Mandant erfolgt via `createUserMandate()` in `interfaceDbApp.py`, welche IMMER mindestens eine Mandate-Rolle verlangt (ValueError wenn leer)
### 2. Feature-Instanz-Rollen (Feature Instance Roles)
- Labels: `workspace-admin`, `workspace-user`, `workspace-viewer`, `automation-admin`, etc. (Feature-Prefix!)
- Gespeichert mit: `featureCode=<code>`, `featureInstanceId=<id>`, `mandateId=<id>`
- Templates: `isSystemRole=False`, `mandateId=NULL`, `featureInstanceId=NULL`, `featureCode=<code>` (globale Vorlagen)
- Verknüpfung: `FeatureAccessRole` Junction Table
- Logik: Zuordnung User → Feature-Instanz erfolgt via `createFeatureAccess()` in `interfaceDbApp.py`, welche IMMER mindestens eine Instanz-Rolle verlangt (ValueError wenn leer)
- Jedes Feature-Modul definiert seine `TEMPLATE_ROLES` in `mainXxx.py`
### Strikte Regeln:
1. NIE eine Mandantenrolle (`admin`) einer `FeatureAccessRole` zuweisen
2. NIE eine Feature-Rolle (`workspace-admin`) einer `UserMandateRole` zuweisen
3. Admin-Lookup für Feature-Instanzen: `roleLabel.endswith("-admin")`, NICHT `"admin" in roleLabel`
4. `_copyTemplateRoles` sucht: `featureCode=X, mandateId=NULL, featureInstanceId=NULL` — nie `isSystemRole=True`
5. Alle Pfade, die `FeatureAccess` erstellen, MÜSSEN mindestens eine instanz-scoped roleId mitgeben
### Datenmodelle:
- `Role`: `@poweron/gateway/modules/datamodels/datamodelRbac.py` — Felder: id, roleLabel, description, mandateId, featureInstanceId, featureCode, isSystemRole
- `UserMandate`: `@poweron/gateway/modules/datamodels/datamodelMembership.py` — User-Mitgliedschaft in Mandant
- `FeatureAccess`: `@poweron/gateway/modules/datamodels/datamodelMembership.py` — User-Zugriff auf Feature-Instanz
- `FeatureInstance`: `@poweron/gateway/modules/datamodels/datamodelFeatures.py` — Instanz eines Features in einem Mandanten
- `Mandate`: `@poweron/gateway/modules/datamodels/datamodelUam.py` — Mandantenmodell mit isSystem (Root-Schutz), deletedAt. `mandateType` wurde entfernt (keine Geschäftslogik).
---
## Feature-Store-Konzept ("Own Instance Pattern")
- **UI:** `@poweron/frontend_nyla/src/pages/Store.tsx` — Pro Mandant ein "Aktivieren für [Mandant]" Button
- **Backend:** `@poweron/gateway/modules/routes/routeStore.py``POST /api/store/activate`
- **Flow:** User wählt Feature → wählt Mandant → Backend erstellt `FeatureInstance` mit `copyTemplateRoles=True` → findet Admin-Rolle (`endswith("-admin")`) → weist User via `createFeatureAccess(userId, instanceId, roleIds=[adminRoleId])` zu
- **Auto-Provisioning:** `GET /api/store/mandates` erstellt automatisch "Home {username}"-Mandate, wenn User keine Admin-Mandate hat
- **Home-Mandant:** Jeder User hat ab erstem Login ein Home-Mandat ("Home {username}"). Wird in Login, Register, OAuth-Onboarding und Store einheitlich sichergestellt.
- **Template-Rollen-Kopie:** `@poweron/gateway/modules/interfaces/interfaceFeatures.py``_copyTemplateRoles()` und `syncRolesFromTemplate()`
- **Orphan Control:** Letzer FeatureAccess entfernt → FeatureInstance wird gelöscht
---
## Schlüssel-Backend-Dateien
| Datei | Zweck |
|-------|-------|
| `@poweron/gateway/modules/interfaces/interfaceDbApp.py` | Kern-DB-Interface: createUserMandate, createFeatureAccess, _provisionMandateForUser; Domain-Models nutzen `PowerOnModel` / `sys*`-Metadaten |
| `@poweron/gateway/modules/interfaces/interfaceFeatures.py` | Feature-Instanz-Verwaltung: _copyTemplateRoles, syncRolesFromTemplate |
| `@poweron/gateway/modules/routes/routeStore.py` | Store-API: activate, mandates-list |
| `@poweron/gateway/modules/routes/routeAdminFeatures.py` | Admin-API: add_user_to_feature_instance (Rollen-Validierung: nur Instanz-Rollen!), rename instance |
| `@poweron/gateway/modules/routes/routeSystem.py` | `/api/navigation` — liefert isAdmin pro FeatureInstance |
| `@poweron/gateway/modules/routes/routeSecurityLocal.py` | Login, Register, _provisionMandateForUser |
| `@poweron/gateway/modules/shared/attributeUtils.py` | Generiert Attribute aus Pydantic-Models für FormGenerator |
| `@poweron/gateway/modules/connectors/connectorDbPostgre.py` | DB-Connector: `sysCreatedAt` / `sysCreatedBy` / `sysModifiedAt` / `sysModifiedBy`; Migration alter `_createdAt`-Spalten via `migrateLegacyUnderscoreSysColumns` (Bootstrap) |
| `@poweron/gateway/modules/migration/migrateRootUsers.py` | Root→Personal-Mandate-Migration |
---
## Schlüssel-Frontend-Dateien
| Datei | Zweck |
|-------|-------|
| `@poweron/frontend_nyla/src/pages/Store.tsx` | Feature-Store mit Mandate-Kontext |
| `@poweron/frontend_nyla/src/components/UnifiedDataBar/UnifiedDataBar.tsx` | UDB: Chats, Files, Sources Tabs |
| `@poweron/frontend_nyla/src/components/UnifiedDataBar/ChatsTab.tsx` | Chats: Volltext-Suche, Feature-Code-Gruppierung, Flat-Mode nach lastMessageAt |
| `@poweron/frontend_nyla/src/components/UnifiedDataBar/FilesTab.tsx` | Files: Scope/Neutralize Icons |
| `@poweron/frontend_nyla/src/components/UnifiedDataBar/SourcesTab.tsx` | Sources: Scope/Neutralize auf Active Sources |
| `@poweron/frontend_nyla/src/components/Navigation/MandateNavigation.tsx` | Nav-Tree mit Rename-Icon für Instanz-Admins |
| `@poweron/frontend_nyla/src/components/Navigation/TreeNavigation/TreeNavigation.tsx` | Generischer Tree mit actions-Slot |
| `@poweron/frontend_nyla/src/components/OnboardingAssistant.tsx` | Globaler Onboarding-Assistent (Dashboard, dismissible, reaktivierbar via UserSection) |
| `@poweron/frontend_nyla/src/components/Navigation/UserSection.tsx` | User-Kontextmenü: Guthaben, Einstellungen, Onboarding |
| `@poweron/frontend_nyla/src/pages/Settings.tsx` | User-Settings: profile, appearance, voice, neutralization, privacy |
| `@poweron/frontend_nyla/src/components/FormGenerator/` | Dynamische Formulare aus Backend-Attribut-Definitionen |
| `@poweron/frontend_nyla/src/hooks/useNavigation.ts` | Navigation-Hook mit FeatureInstance.isAdmin |
| `@poweron/frontend_nyla/src/pages/views/workspace/WorkspacePage.tsx` | Workspace-Feature-Seite mit Chat-Drag&Drop + RAG-Resolve |
| `@poweron/frontend_nyla/src/pages/views/commcoach/CommcoachDossierView.tsx` | CommCoach mit UDB (`hideTabs: chats`), gleicher `instanceId`/`mandateId`-Kontext wie Workspace |
| `@poweron/gateway/modules/datamodels/datamodelBase.py` | `PowerOnModel`: `sysCreatedAt`, `sysCreatedBy`, `sysModifiedAt`, `sysModifiedBy` (camelCase, UI-readonly vorbereitet) |
---
## System-Felder (DB-Metadaten)
- **Spalten / Modelle:** `sysCreatedAt`, `sysCreatedBy`, `sysModifiedAt`, `sysModifiedBy` — gepflegt durch den Connector (`connectorDbPostgre.py`); viele Domain-Models erben von `PowerOnModel` in `datamodelBase.py`.
- **Legacy:** Frühere `_createdAt`-Spalten werden bei Bootstrap durch `migrateLegacyUnderscoreSysColumns` / `migrateLegacyUnderscoreSysColumnsAllPoweronDatabases` in die `sys*`-Felder übernommen.
- **UI:** FormGenerator unterstützt `readonly`-Attribute; wo Metadaten in Listen/Forms explizit gezeigt werden sollen, Felder als readonly markieren (schrittweise je Screen).
---
## Produkt-Themen (Stand 2026-03-28)
1. **CommCoach UDB + Voice:** UDB in `CommcoachDossierView`. Voice-Controller bezieht STT-Sprache dynamisch aus zentralen Preferences.
2. **Neutralisierung End-to-End:** `requireNeutralization` vom UI-Toggle bis `AiCallRequest` durchgereicht via ServiceCenterContext. Mandanten-RAG in CommCoach via `searchSessionsByTopicRag`.
3. **Settings (5 Tabs):** Profil, Darstellung, Stimme, Datenneutralisierung, Datenschutz.
4. **Billing: nur PREPAY_MANDATE:** `PREPAY_USER` komplett entfernt. `budgetAiCHF` in Subscriptions (Trial: 5 CHF, Standard: 10 CHF/Monat). Auto-Recharge-Felder in BillingSettings.
5. **mandateType entfernt:** Enum, Feld, Validator in 13+ Dateien gelöscht. `isSystem` als einziger Root-Schutz.
6. **Home-Mandant:** "Home {username}" bei erstem Login (Register/OAuth/Invitation) via `_ensureHomeMandate()`.
7. **UDB ChatsTab:** Server-seitige Volltext-Suche (`searchWorkflowsByContent`), Flat-Mode nach `lastMessageAt`, zweistufige Baumansicht (Feature-Code-Sektionen > Feature-Instanzen).
8. **Chat Drag & Drop + RAG:** Chat-Items aus ChatsTab auf Workspace droppbar. Backend `/resolve-rag` liefert Zusammenfassung.
9. **Datenvolumen:** `assertCapacity("dataVolumeMB")` prüft RAG-Index-Grösse (`FileContentIndex.totalSize`). API `/api/subscription/data-volume/{mandateId}` liefert `ragIndexMB`, `filesMB`, `percentUsed`, `warning` (>=80%).
10. **Mandate-Cascade:** `deleteMandate(force=True)` löscht alle abhängigen Tabellen: ContentChunk, FileContentIndex, DataNeutralizerAttributes, DataSource, FeatureDataSource, FileItem, ChatMessage/Log/Workflow, FeatureAccessRole, FeatureAccess, FeatureInstance, UserMandateRole, UserMandate, Stripe-Subscriptions, BillingTransaction/Account/Settings, AccessRule, Role, Mandate. Frontend: Namensbestätigungs-Dialog.
---
## Coding-Konventionen
- Alle internen Funktionen beginnen mit `_` Prefix (z.B. `_copyTemplateRoles`)
- camelCase für alle Variablen und Funktionsnamen (kein snake_case)
- Keine unnötigen Fallbacks — Fehler müssen propagiert werden
- Keine Deprecations — alte Logik löschen statt deprecaten
- Keine Backwards-Compatibility-Workarounds
- Pydantic-Models sind die einzige Quelle für UI-Feld-Definitionen (via `attributeUtils.py`)