8.5 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 mandateType (system/personal/company), isSystem, deletedAt
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 Personal-Mandate, wenn User keine Admin-Mandate hat - 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 (44x _-Prefix-Filter für System-Felder) |
@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: _saveRecord setzt automatisch _createdAt, _createdBy, _modifiedAt, _modifiedBy |
@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: Aktiv/Archiv, Rename/Delete/Archive Icons |
@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 |
System-Felder (DB-Metadaten) — OFFENER PUNKT
Die DB-Schicht (connectorDbPostgre.py) pflegt automatisch 4 System-Felder für jede Tabelle:
_createdAt(float, UTC timestamp)_createdBy(str, userId)_modifiedAt(float, UTC timestamp)_modifiedBy(str, userId)
Diese werden aktuell in interfaceDbApp.py an 44 Stellen via {k: v for k, v in record.items() if not k.startswith("_")} herausgefiltert, bevor Records an die API/Frontend gehen. Es gibt KEINE gemeinsame Base-Klasse — alle 30+ Pydantic-Models erben direkt von BaseModel.
Offene Architektur-Entscheidung: Diese 4 System-Felder sollen im UI als read-only sichtbar sein (z.B. in Tabellen/Formularen). Optionen:
- (a)
PowerOnModel(BaseModel)als Base-Klasse mit den 4 Feldern (mitalias="_createdAt"etc.) einführen,_-Filter entfernen - (b) Einige Models (
FeatureDataSource,DataSource,FileFolder,Invitation,Messaging) haben bereits eigenecreatedAt/createdByals Business-Felder — Namenskonflikte zu klären! - Der
FormGeneratorunterstützt bereitsreadonly-Attribute korrekt
Weitere offene Themen aus dem vorherigen Chat
- CommCoach UDB-Integration: UDB in CommCoach einbinden (ohne Chats-Tab), gleicher AI-Service wie Workspace, CommCoach behält eigene SSE-Chat-Komponente
- Zentrale AI-Neutralisierung: RAG mandate-weit verfügbar, Neutralisierungs-Pipeline weiter konsolidieren
- Settings-Seite: 5 Tabs vorhanden (profile, appearance, voice, neutralization, privacy) — Vollständigkeit prüfen
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)