10 KiB
10 KiB
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:
UserMandateRoleJunction Table - Logik: Zuordnung User → Mandant erfolgt via
createUserMandate()ininterfaceDbApp.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:
FeatureAccessRoleJunction Table - Logik: Zuordnung User → Feature-Instanz erfolgt via
createFeatureAccess()ininterfaceDbApp.py, welche IMMER mindestens eine Instanz-Rolle verlangt (ValueError wenn leer) - Jedes Feature-Modul definiert seine
TEMPLATE_ROLESinmainXxx.py
Strikte Regeln:
- NIE eine Mandantenrolle (
admin) einerFeatureAccessRolezuweisen - NIE eine Feature-Rolle (
workspace-admin) einerUserMandateRolezuweisen - Admin-Lookup für Feature-Instanzen:
roleLabel.endswith("-admin"), NICHT"admin" in roleLabel _copyTemplateRolessucht:featureCode=X, mandateId=NULL, featureInstanceId=NULL— nieisSystemRole=True- Alle Pfade, die
FeatureAccesserstellen, MÜSSEN mindestens eine instanz-scoped roleId mitgeben
Datenmodelle:
Role:@poweron/gateway/modules/datamodels/datamodelRbac.py— Felder: id, roleLabel, description, mandateId, featureInstanceId, featureCode, isSystemRoleUserMandate:@poweron/gateway/modules/datamodels/datamodelMembership.py— User-Mitgliedschaft in MandantFeatureAccess:@poweron/gateway/modules/datamodels/datamodelMembership.py— User-Zugriff auf Feature-InstanzFeatureInstance:@poweron/gateway/modules/datamodels/datamodelFeatures.py— Instanz eines Features in einem MandantenMandate:@poweron/gateway/modules/datamodels/datamodelUam.py— Mandantenmodell mit isSystem (Root-Schutz), deletedAt.mandateTypewurde 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
FeatureInstancemitcopyTemplateRoles=True→ findet Admin-Rolle (endswith("-admin")) → weist User viacreateFeatureAccess(userId, instanceId, roleIds=[adminRoleId])zu - Auto-Provisioning:
GET /api/store/mandateserstellt 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()undsyncRolesFromTemplate() - 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 vonPowerOnModelindatamodelBase.py. - Legacy: Frühere
_createdAt-Spalten werden bei Bootstrap durchmigrateLegacyUnderscoreSysColumns/migrateLegacyUnderscoreSysColumnsAllPoweronDatabasesin diesys*-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)
- CommCoach UDB + Voice: UDB in
CommcoachDossierView. Voice-Controller bezieht STT-Sprache dynamisch aus zentralen Preferences. - Neutralisierung End-to-End:
requireNeutralizationvom UI-Toggle bisAiCallRequestdurchgereicht via ServiceCenterContext. Mandanten-RAG in CommCoach viasearchSessionsByTopicRag. - Settings (5 Tabs): Profil, Darstellung, Stimme, Datenneutralisierung, Datenschutz.
- Billing: nur PREPAY_MANDATE:
PREPAY_USERkomplett entfernt.budgetAiCHFin Subscriptions (Trial: 5 CHF, Standard: 10 CHF/Monat). Auto-Recharge-Felder in BillingSettings. - mandateType entfernt: Enum, Feld, Validator in 13+ Dateien gelöscht.
isSystemals einziger Root-Schutz. - Home-Mandant: "Home {username}" bei erstem Login (Register/OAuth/Invitation) via
_ensureHomeMandate(). - UDB ChatsTab: Server-seitige Volltext-Suche (
searchWorkflowsByContent), Flat-Mode nachlastMessageAt, zweistufige Baumansicht (Feature-Code-Sektionen > Feature-Instanzen). - Chat Drag & Drop + RAG: Chat-Items aus ChatsTab auf Workspace droppbar. Backend
/resolve-ragliefert Zusammenfassung. - Datenvolumen:
assertCapacity("dataVolumeMB")prüft RAG-Index-Grösse (FileContentIndex.totalSize). API/api/subscription/data-volume/{mandateId}liefertragIndexMB,filesMB,percentUsed,warning(>=80%). - 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)