mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-25 00:16:41 +02:00
760 lines
27 KiB
Markdown
760 lines
27 KiB
Markdown
# INSIGHT-Platform — Developer Briefing
|
|
|
|
> **Lies dieses Dokument vollständig bevor du Code schreibst.**
|
|
> Es ist dein vollständiger Einstieg in Phase 2.
|
|
|
|
---
|
|
|
|
## Deine Aufgabe
|
|
|
|
Du bist der **Platform-Entwickler** für INSIGHT. Dein Job in Phase 2:
|
|
|
|
> **NestJS Core-Service (Auth, User, RBAC, Settings) + React Shell (Login, Navigation, Admin) implementieren.**
|
|
|
|
Die Infrastruktur läuft bereits (Phase 1 ✅). Du hast Zugriff auf:
|
|
- PostgreSQL 16 auf `insight-dbs01.xinion.lan:5432` (Datenbank: `insight_core`)
|
|
- PgBouncer auf `insight-dbs01.xinion.lan:6432`
|
|
- Redis 8.6.1 auf `172.20.10.20:6379`
|
|
- Docker auf `insight-aps01.xinion.lan` für Deployments
|
|
- Nginx auf `insight-web01.xinion.lan` für das Frontend
|
|
|
|
---
|
|
|
|
## Was ist bereits fertig
|
|
|
|
- Infra: PostgreSQL, PgBouncer, Redis, Docker, Nginx — alles läuft ✅
|
|
- Datenbank `insight_core` existiert mit User `insight_app` ✅
|
|
- Repo-Struktur (`packages/core-service/`, `packages/frontend/`) ✅
|
|
- Deploy Key für dieses Repo: `.keys/deploy_platform_ed25519` ✅
|
|
- MVP-Referenzimplementierung unter `git@git.xinion.lan:gitadmin/INSIGHT-MVP.git` ✅
|
|
|
|
---
|
|
|
|
## Wichtiger Hinweis: MVP als Referenz
|
|
|
|
**Der MVP (`INSIGHT-MVP`) ist vollständig implementiert und dient als direkte Vorlage.**
|
|
|
|
Folgende Module kannst du 1:1 oder minimal angepasst übernehmen:
|
|
|
|
| MVP-Datei | Für INSIGHT nutzen |
|
|
|---|---|
|
|
| `packages/core-service/src/core/auth/auth.service.ts` | Auth-Logik (Login, TOTP, SSO, Refresh) |
|
|
| `packages/core-service/src/core/auth/auth.controller.ts` | Auth-Endpoints |
|
|
| `packages/core-service/src/core/auth/strategies/jwt.strategy.ts` | JWT RS256 Passport-Strategy |
|
|
| `packages/core-service/src/core/auth/totp.service.ts` | TOTP 2FA |
|
|
| `packages/core-service/src/core/auth/sso/entra-id.service.ts` | MS Entra ID SSO + M365 OAuth2 |
|
|
| `packages/core-service/src/core/auth/sso/sso.controller.ts` | SSO-Endpoints |
|
|
| `packages/core-service/src/core/users/users.service.ts` | User-CRUD |
|
|
| `packages/core-service/src/core/users/users.controller.ts` | User-Endpoints |
|
|
| `packages/core-service/src/core/settings/settings.controller.ts` | External Links, Branding, Company, SSL, AI-Config |
|
|
| `packages/core-service/src/redis/redis.service.ts` | Redis-Wrapper |
|
|
| `packages/core-service/src/common/guards/` | JwtAuthGuard, RolesGuard, Public-Decorator |
|
|
| `packages/frontend/src/auth/` | LoginPage, AuthContext, SsoCallbackPage |
|
|
| `packages/frontend/src/admin/AdminLayout.tsx` | Admin-Navigation-Shell |
|
|
| `packages/frontend/src/admin/Admin*.tsx` | Admin-Seiten (Users, SSO, Branding, etc.) |
|
|
| `packages/frontend/src/hooks/` | useAuth, TanStack Query Hooks |
|
|
| `packages/core-service/src/core/integrations/integrations.service.ts` | M365 Token speichern/laden/refreshen |
|
|
| `packages/core-service/src/core/integrations/integrations.controller.ts` | Integrationen-Endpoints |
|
|
| `packages/frontend/src/profile/ProfilePage.tsx` | Profil-Seite (nur Profil/Passwort/2FA/Integrationen Tabs) |
|
|
|
|
**Anpassungen notwendig:**
|
|
- **Kein Multi-Tenant**: INSIGHT ist single-tenant. Alle `tenant*`-Felder aus Prisma-Schema entfernen.
|
|
- **RBAC erweitern**: `moduleRoles` als JSON-Feld statt `tenantRole` (siehe Schema unten).
|
|
- **Neue Admin-Seiten**: User-Rollenmanagement (platformRole + moduleRoles), Modul-Registry.
|
|
- **Dashboard**: Modulare Widget-Area statt MVP-Dashboard (M365 Calendar/Email).
|
|
|
|
**Folgende MVP-Module werden NICHT übernommen (MVP-spezifisch):**
|
|
|
|
| MVP-Feature | Grund |
|
|
|---|---|
|
|
| `ProfileAccessGroup` (Gruppen-basierter Profilzugriff) | Wird durch RBAC `moduleRoles.expertprofile` ersetzt |
|
|
| `MasterData` (Departments, Locations, CostCenters, etc.) | MVP-spezifisch — kein Teil von INSIGHT |
|
|
| `AdminProfileAccessPage`, `AdminMasterDataPage` | MVP-spezifisch — kein Teil von INSIGHT |
|
|
|
|
**Folgende MVP-Features werden übernommen:**
|
|
|
|
| Feature | Details |
|
|
|---|---|
|
|
| **Profil-Seite** | Tabs: Profil \| Experten Profil \| Passwort ändern \| 2FA \| Integrationen |
|
|
| **"O365 übernehmen"** Button | Einmaliger manueller Import aller Felder aus M365 |
|
|
| **Profil-Sync beim Login** | Automatisch: Name, Jobtitel, Department, Company aus M365 |
|
|
| **Passwort ändern** | Eigenes Passwort mit Verifikation des aktuellen Passworts |
|
|
| **2FA Tab** | TOTP aktivieren/deaktivieren (nur für lokale Auth-User) |
|
|
| **Integrationen Tab** | Microsoft 365: verbinden, Status anzeigen, trennen |
|
|
| **Experten Profil Tab** (eigenes) | User bearbeitet sein eigenes Experten Profil |
|
|
| **ExpertProfile Datenmodell** | Skills, Sprachen, Erfahrung, Projekthistorie, Zertifikate, Anlagen |
|
|
| **PDF / Word Export** | Eigenes Profil exportieren |
|
|
|
|
> **Hintergrund Profil-Sync:** Wenn ein User sich via MS SSO einloggt, werden Name, JobTitle,
|
|
> Department, CompanyName, OfficeLocation automatisch aus dem M365-Token aktualisiert.
|
|
> Der "O365 übernehmen"-Button ermöglicht zusätzlich den manuellen Sync (Graph API `/me`).
|
|
|
|
---
|
|
|
|
## Experten Profile — Eigenständiger Menüpunkt
|
|
|
|
**"Experten Profile" ist ein eigener Menüpunkt in der Hauptnavigation** (nicht nur ein persönlicher Tab).
|
|
|
|
### Was er leistet:
|
|
|
|
| Aktion | Wer darf das |
|
|
|---|---|
|
|
| Eigenes Experten Profil bearbeiten | Jeder eingeloggte User (im Tab "Experten Profil" seiner Profil-Seite) |
|
|
| Alle Profile auflisten und **ansehen** | `moduleRoles.expertprofile: "VIEWER"` oder höher |
|
|
| Profile anderer User **bearbeiten** | `moduleRoles.expertprofile: "EDITOR"` oder höher |
|
|
| Profile **exportieren** (PDF/Word) | `moduleRoles.expertprofile: "EDITOR"` oder höher |
|
|
| Alles + Profil-Zuweisung verwalten | `moduleRoles.expertprofile: "ADMIN"` oder `platformRole: PLATFORM_ADMIN` |
|
|
|
|
### RBAC für Experten Profile:
|
|
|
|
```
|
|
moduleRoles.expertprofile:
|
|
"ADMIN" → vollständiger Zugang, kann alle Profile bearbeiten + exportieren
|
|
"EDITOR" → kann alle Profile bearbeiten + exportieren
|
|
"VIEWER" → kann alle Profile nur ansehen (kein Export, kein Bearbeiten)
|
|
(nicht gesetzt) → nur eigenes Profil über persönliche Profil-Seite
|
|
```
|
|
|
|
### UI-Konzept:
|
|
|
|
```
|
|
Navigation Sidebar:
|
|
└── 📋 Experten Profile (sichtbar für User mit expertprofile-Rolle)
|
|
├── Liste aller User mit Profilvorschau
|
|
├── Klick auf User → vollständiges Profil ansehen
|
|
├── [Bearbeiten]-Button (nur für EDITOR/ADMIN)
|
|
└── [PDF Export] / [Word Export] (nur für EDITOR/ADMIN)
|
|
```
|
|
|
|
### MVP-Referenzdateien für Experten Profile:
|
|
|
|
| MVP-Datei | Für INSIGHT nutzen |
|
|
|---|---|
|
|
| `packages/core-service/src/core/expert-profile/expert-profile.service.ts` | Experten Profil CRUD |
|
|
| `packages/core-service/src/core/expert-profile/expert-profile.controller.ts` | Endpoints |
|
|
| `packages/core-service/src/core/expert-profile/profile-export.service.ts` | PDF/Word Export |
|
|
| `packages/core-service/prisma/core.schema.prisma` (ExpertProfile-Modelle) | Datenmodell |
|
|
| `packages/frontend/src/profile/ProfilePage.tsx` | Eigenes Profil + Experten-Profil-Tab |
|
|
|
|
**Anpassung gegenüber MVP:** Im MVP war der Profilzugriff über `ProfileAccessGroup` geregelt
|
|
(Gruppen mit `canView`, `canEdit`, `canExport`). In INSIGHT wird das durch
|
|
`moduleRoles.expertprofile` ersetzt — einfacher, konsistent mit dem restlichen RBAC-System.
|
|
|
|
---
|
|
|
|
## Prisma Schema (insight_core)
|
|
|
|
**Datei:** `packages/core-service/prisma/schema.prisma`
|
|
|
|
```prisma
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
directUrl = env("DATABASE_URL_DIRECT")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// User - Plattform-Benutzer
|
|
// --------------------------------------------------------
|
|
model User {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
email String @unique @db.VarChar(255)
|
|
firstName String @map("first_name") @db.VarChar(100)
|
|
lastName String @map("last_name") @db.VarChar(100)
|
|
avatar String? @db.Text
|
|
|
|
// Kontaktdaten
|
|
phone String? @db.VarChar(30)
|
|
mobile String? @db.VarChar(30)
|
|
street String? @db.VarChar(200)
|
|
postalCode String? @map("postal_code") @db.VarChar(10)
|
|
city String? @db.VarChar(100)
|
|
|
|
// Organisation
|
|
jobTitle String? @map("job_title") @db.VarChar(100)
|
|
department String? @db.VarChar(100)
|
|
companyName String? @map("company_name") @db.VarChar(200)
|
|
officeLocation String? @map("office_location") @db.VarChar(200)
|
|
|
|
// Rollen
|
|
platformRole String @default("USER") @map("platform_role") @db.VarChar(50)
|
|
// "PLATFORM_ADMIN" oder "USER"
|
|
|
|
// Modul-Rollen als JSON: { "crm": "MANAGER", "project": "VIEWER" }
|
|
moduleRoles Json @default("{}") @map("module_roles")
|
|
|
|
isActive Boolean @default(true) @map("is_active")
|
|
|
|
// 2FA (nur für lokale Auth)
|
|
twoFactorEnabled Boolean @default(false) @map("two_factor_enabled")
|
|
|
|
// Login-Tracking
|
|
lastLogin DateTime? @map("last_login")
|
|
failedLoginAttempts Int @default(0) @map("failed_login_attempts")
|
|
lastFailedLogin DateTime? @map("last_failed_login")
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
authProvider AuthProvider[]
|
|
integrations UserIntegration[]
|
|
auditLogs AuditLog[]
|
|
expertProfile ExpertProfile?
|
|
|
|
@@map("users")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// AuthProvider - Lokale Auth, MS SSO
|
|
// --------------------------------------------------------
|
|
model AuthProvider {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @map("user_id") @db.Uuid
|
|
provider String @db.VarChar(50) // LOCAL, MS_SSO
|
|
providerId String? @map("provider_id") @db.VarChar(255)
|
|
passwordHash String? @map("password_hash") @db.VarChar(255)
|
|
totpSecret String? @map("totp_secret") @db.VarChar(255)
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, provider])
|
|
@@map("auth_providers")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// UserIntegration - Microsoft 365 OAuth2 Tokens pro User
|
|
// --------------------------------------------------------
|
|
model UserIntegration {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @map("user_id") @db.Uuid
|
|
provider String @db.VarChar(50) // MICROSOFT_365
|
|
accessToken String @map("access_token") @db.Text // AES-256-GCM verschlüsselt
|
|
refreshToken String @map("refresh_token") @db.Text
|
|
expiresAt DateTime @map("expires_at")
|
|
scopes String[] @default([])
|
|
msTenantId String? @map("ms_tenant_id") @db.VarChar(100)
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, provider])
|
|
@@map("user_integrations")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// Module - Registrierte INSIGHT-Module
|
|
// --------------------------------------------------------
|
|
model Module {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
key String @unique @db.VarChar(50) // "crm", "project"
|
|
name String @db.VarChar(100)
|
|
description String? @db.Text
|
|
version String @default("1.0.0") @db.VarChar(20)
|
|
isActive Boolean @default(true) @map("is_active")
|
|
// Verfügbare Rollen für dieses Modul (JSON Array): ["ADMIN","MANAGER","USER","VIEWER"]
|
|
availableRoles Json @default("[]") @map("available_roles")
|
|
config Json @default("{}")
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
@@map("modules")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// AuditLog
|
|
// --------------------------------------------------------
|
|
model AuditLog {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String? @map("user_id") @db.Uuid
|
|
action String @db.VarChar(100)
|
|
entity String @db.VarChar(100)
|
|
entityId String? @map("entity_id") @db.VarChar(255)
|
|
details Json?
|
|
ipAddress String? @map("ip_address") @db.VarChar(45)
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
|
|
|
@@index([userId])
|
|
@@index([action])
|
|
@@index([createdAt])
|
|
@@map("audit_logs")
|
|
}
|
|
|
|
// --------------------------------------------------------
|
|
// ExpertProfile - Experten-Profil (1:1 mit User)
|
|
// --------------------------------------------------------
|
|
model ExpertProfile {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @unique @map("user_id") @db.Uuid
|
|
|
|
skills String[] @default([])
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
experiences ExpertExperience[]
|
|
languages ExpertLanguage[]
|
|
projects ExpertProject[]
|
|
certifications ExpertCertification[]
|
|
attachments ExpertAttachment[]
|
|
|
|
@@map("expert_profiles")
|
|
}
|
|
|
|
model ExpertExperience {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
expertProfileId String @map("expert_profile_id") @db.Uuid
|
|
area String @db.VarChar(200)
|
|
years Int
|
|
level String? @db.VarChar(50)
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
@@map("expert_experiences")
|
|
}
|
|
|
|
model ExpertLanguage {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
expertProfileId String @map("expert_profile_id") @db.Uuid
|
|
language String @db.VarChar(100)
|
|
level String @db.VarChar(20) // Muttersprache, C2, C1, B2, B1, A2, A1
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
@@map("expert_languages")
|
|
}
|
|
|
|
model ExpertProject {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
expertProfileId String @map("expert_profile_id") @db.Uuid
|
|
fromMonth Int @map("from_month")
|
|
fromYear Int @map("from_year")
|
|
toMonth Int? @map("to_month")
|
|
toYear Int? @map("to_year")
|
|
isCurrent Boolean @default(false) @map("is_current")
|
|
role String @db.VarChar(200)
|
|
tasks String? @db.Text
|
|
company String? @db.VarChar(200)
|
|
companySize String? @map("company_size") @db.VarChar(20)
|
|
industry String? @db.VarChar(200)
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
@@index([expertProfileId, fromYear, fromMonth])
|
|
@@map("expert_projects")
|
|
}
|
|
|
|
model ExpertCertification {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
expertProfileId String @map("expert_profile_id") @db.Uuid
|
|
title String @db.VarChar(300)
|
|
issuingBody String @map("issuing_body") @db.VarChar(300)
|
|
website String? @db.VarChar(500)
|
|
issueYear Int @map("issue_year")
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
updatedAt DateTime @updatedAt @map("updated_at")
|
|
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
@@map("expert_certifications")
|
|
}
|
|
|
|
model ExpertAttachment {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
expertProfileId String @map("expert_profile_id") @db.Uuid
|
|
filename String @db.VarChar(255)
|
|
mimetype String @db.VarChar(100)
|
|
size Int
|
|
data String @db.Text // Base64
|
|
|
|
createdAt DateTime @default(now()) @map("created_at")
|
|
|
|
expertProfile ExpertProfile @relation(fields: [expertProfileId], references: [id], onDelete: Cascade)
|
|
@@map("expert_attachments")
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## RBAC-Konzept
|
|
|
|
```
|
|
User.platformRole:
|
|
PLATFORM_ADMIN → Zugang zum kompletten Admin-Bereich + alle Experten Profile
|
|
USER → Normaler Nutzer
|
|
|
|
User.moduleRoles (JSON-Objekt mit Rollen pro Modul):
|
|
|
|
Experten Profile:
|
|
{ "expertprofile": "ADMIN" } → Alle Profile ansehen + bearbeiten + exportieren
|
|
{ "expertprofile": "EDITOR" } → Alle Profile ansehen + bearbeiten + exportieren
|
|
{ "expertprofile": "VIEWER" } → Alle Profile nur ansehen (kein Export, kein Bearbeiten)
|
|
(nicht gesetzt) → Nur eigenes Profil über persönliche Profil-Seite
|
|
|
|
CRM (Phase 3):
|
|
{ "crm": "ADMIN" } → Admin im CRM-Modul
|
|
{ "crm": "MANAGER" } → Manager im CRM-Modul
|
|
{ "crm": "USER" } → Normaler CRM-Nutzer
|
|
{ "crm": "VIEWER" } → Nur lesen
|
|
(nicht gesetzt) → Kein Zugang zu CRM
|
|
|
|
Kombiniert möglich:
|
|
{ "expertprofile": "EDITOR", "crm": "MANAGER" }
|
|
```
|
|
|
|
**Im JWT-Token** werden beide Felder mitgegeben:
|
|
```json
|
|
{
|
|
"sub": "uuid",
|
|
"email": "user@firma.de",
|
|
"platformRole": "USER",
|
|
"moduleRoles": { "expertprofile": "EDITOR", "crm": "MANAGER" },
|
|
"jti": "uuid",
|
|
"iat": 1234567890,
|
|
"exp": 1234568790
|
|
}
|
|
```
|
|
|
|
**Guards:**
|
|
- `JwtAuthGuard` — globaler Default (alle Routen geschützt)
|
|
- `@Public()` — explizit für Login, SSO-Callback, Branding
|
|
- `@Roles('PLATFORM_ADMIN')` + `RolesGuard` — für Admin-Endpoints
|
|
- Module-Rollen: im jeweiligen Modul-Service prüfen (z.B. `user.moduleRoles.crm`)
|
|
|
|
---
|
|
|
|
## Implementierungsreihenfolge
|
|
|
|
### Backend (core-service)
|
|
|
|
**1. Projekt aufsetzen:**
|
|
```bash
|
|
cd packages/core-service
|
|
npm init -y
|
|
# NestJS CLI installieren und Projekt erstellen
|
|
npm install -g @nestjs/cli
|
|
nest new . --skip-git --package-manager npm
|
|
```
|
|
|
|
Abhängigkeiten (analog MVP):
|
|
```bash
|
|
npm install @nestjs/passport @nestjs/jwt passport passport-jwt
|
|
npm install @nestjs/config @nestjs/swagger
|
|
npm install @prisma/client prisma
|
|
npm install bcrypt uuid speakeasy qrcode
|
|
npm install @azure/msal-node
|
|
npm install ioredis cookie-parser
|
|
npm install class-validator class-transformer
|
|
npm install @types/bcrypt @types/uuid @types/passport-jwt @types/cookie-parser
|
|
```
|
|
|
|
**2. Prisma initialisieren:**
|
|
```bash
|
|
npx prisma init --datasource-provider postgresql
|
|
# schema.prisma aus diesem Briefing verwenden
|
|
npx prisma migrate dev --name init
|
|
```
|
|
|
|
**3. Implementierungsreihenfolge Module:**
|
|
1. `redis/` — RedisService (aus MVP kopieren)
|
|
2. `prisma/` — PrismaService (aus MVP kopieren)
|
|
3. `common/` — Guards, Decorators, Pipes (aus MVP kopieren, `tenantRole` → `platformRole`)
|
|
4. `core/auth/` — AuthService, AuthController, JwtStrategy, TotpService (aus MVP, tenant-Felder entfernen)
|
|
5. `core/auth/sso/` — EntraIdService, SsoController (aus MVP 1:1 übernehmen)
|
|
6. `core/users/` — UsersService, UsersController (aus MVP, `moduleRoles` statt `tenantRole`)
|
|
7. `core/settings/` — SettingsController (aus MVP, External Links + Branding + Company + SSL + AI-Config)
|
|
8. `core/integrations/` — IntegrationsService, IntegrationsController (aus MVP, M365 OAuth2 Tokens)
|
|
9. `core/expert-profile/` — ExpertProfileService, ExpertProfileController, ProfileExportService (aus MVP)
|
|
- Eigenes Profil bearbeiten: alle authentifizierten User
|
|
- Fremde Profile lesen: `moduleRoles.expertprofile` = VIEWER/EDITOR/ADMIN
|
|
- Fremde Profile bearbeiten: `moduleRoles.expertprofile` = EDITOR/ADMIN
|
|
- Export: `moduleRoles.expertprofile` = EDITOR/ADMIN
|
|
10. `core/modules/` — NEU: ModuleRegistryService (Module aus DB laden, User-Rollen-Zuweisung)
|
|
|
|
**4. JWT RS256 Keys generieren:**
|
|
```bash
|
|
mkdir -p keys
|
|
openssl genrsa -out keys/jwt-private.pem 4096
|
|
openssl rsa -in keys/jwt-private.pem -pubout -out keys/jwt-public.pem
|
|
```
|
|
Keys werden als Docker-Volume gemountet (nicht in Git!).
|
|
|
|
**5. Seed (erster Admin-User):**
|
|
```typescript
|
|
// prisma/seed.ts
|
|
const admin = await prisma.user.create({
|
|
data: {
|
|
email: 'admin@insight.local',
|
|
firstName: 'Platform',
|
|
lastName: 'Admin',
|
|
platformRole: 'PLATFORM_ADMIN',
|
|
moduleRoles: {},
|
|
isActive: true,
|
|
authProvider: {
|
|
create: {
|
|
provider: 'LOCAL',
|
|
passwordHash: await bcrypt.hash('ChangeMe123!', 12),
|
|
},
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
### Frontend (frontend)
|
|
|
|
**1. Projekt aufsetzen:**
|
|
```bash
|
|
cd packages/frontend
|
|
npm create vite@latest . -- --template react-ts
|
|
npm install react-router-dom @tanstack/react-query axios
|
|
npm install react-hot-toast @heroicons/react
|
|
```
|
|
|
|
**2. Implementierungsreihenfolge:**
|
|
1. `auth/AuthContext.tsx` — AuthContext + useAuth Hook + Silent Refresh (aus MVP)
|
|
2. `auth/LoginPage.tsx` — Login-Formular (lokal + SSO-Button) (aus MVP)
|
|
3. `auth/SsoCallbackPage.tsx` — SSO-Callback (aus MVP)
|
|
4. `shell/App.tsx` — Routing-Struktur (ohne CRM-Routen — die kommen Phase 3)
|
|
5. `shell/AppLayout.tsx` — Haupt-Layout (Sidebar + Topbar + Outlet)
|
|
6. `shell/DashboardPage.tsx` — Leeres Dashboard mit Widget-Platzhaltern
|
|
7. `profile/ProfilePage.tsx` — Eigenes Profil mit Tabs: Profil | Experten Profil | Passwort | 2FA | Integrationen (aus MVP)
|
|
8. `expert-profiles/ExpertProfilesPage.tsx` — **NEU**: Menüpunkt "Experten Profile" (Liste aller User-Profile, permission-gesteuert)
|
|
9. `expert-profiles/ExpertProfileDetailPage.tsx` — **NEU**: Einzelnes Profil ansehen + bearbeiten (EDITOR/ADMIN) + exportieren
|
|
10. `admin/AdminLayout.tsx` — Admin-Navigation (aus MVP)
|
|
11. `admin/AdminUsersPage.tsx` — User-Verwaltung + Rollen-Zuweisung (aus MVP erweitern)
|
|
12. `admin/AdminSsoPage.tsx` — SSO-Konfiguration (aus MVP 1:1)
|
|
13. `admin/AdminCustomizePage.tsx` — Branding (aus MVP 1:1)
|
|
14. `admin/AdminExternalLinksPage.tsx` — Externe Links (aus MVP 1:1)
|
|
15. `admin/AdminCompanyPage.tsx` — Firmendaten (aus MVP 1:1)
|
|
16. `admin/AdminSslPage.tsx` — SSL/Domain (aus MVP 1:1)
|
|
17. `admin/AdminAiSettingsPage.tsx` — KI-Einstellungen (aus MVP 1:1)
|
|
18. `admin/AdminModulesPage.tsx` — **NEU**: Modul-Registry (welche Module aktiv, Rollen-Konfiguration)
|
|
|
|
**Navigation Sidebar — Hauptmenü:**
|
|
```
|
|
🏠 Dashboard
|
|
📋 Experten Profile ← nur sichtbar wenn moduleRoles.expertprofile gesetzt
|
|
── Admin-Bereich ── ← nur sichtbar für PLATFORM_ADMIN
|
|
⚙️ Benutzer
|
|
⚙️ SSO / Sicherheit
|
|
⚙️ Erscheinungsbild
|
|
⚙️ Externe Links
|
|
⚙️ Firmendaten
|
|
⚙️ SSL / Domain
|
|
⚙️ KI-Einstellungen
|
|
⚙️ Module
|
|
```
|
|
|
|
**Wichtig für Admin-User-Management (NEU gegenüber MVP):**
|
|
- User-Liste zeigt: Name, E-Mail, platformRole, moduleRoles
|
|
- PLATFORM_ADMIN kann platformRole ändern
|
|
- PLATFORM_ADMIN kann moduleRoles pro Modul zuweisen:
|
|
- expertprofile: ADMIN / EDITOR / VIEWER / (kein Zugang)
|
|
- crm: ADMIN / MANAGER / USER / VIEWER / (kein Zugang) ← Phase 3, schon vorbereiten
|
|
|
|
---
|
|
|
|
## Deployment auf APS01
|
|
|
|
### Docker Compose (`docker-compose.yml` im Repo-Root)
|
|
|
|
```yaml
|
|
services:
|
|
core-service:
|
|
build: ./packages/core-service
|
|
restart: unless-stopped
|
|
environment:
|
|
DATABASE_URL: postgresql://insight_app:${DB_PASSWORD}@insight-dbs01.xinion.lan:6432/insight_core
|
|
DATABASE_URL_DIRECT: postgresql://insight_app:${DB_PASSWORD}@insight-dbs01.xinion.lan:5432/insight_core
|
|
REDIS_HOST: 172.20.10.20
|
|
REDIS_PORT: 6379
|
|
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
|
JWT_ISSUER: insight-platform
|
|
JWT_ACCESS_TOKEN_EXPIRY: 15m
|
|
JWT_REFRESH_TOKEN_EXPIRY: 7d
|
|
JWT_PRIVATE_KEY_PATH: /app/keys/jwt-private.pem
|
|
JWT_PUBLIC_KEY_PATH: /app/keys/jwt-public.pem
|
|
FRONTEND_URL: http://insight-web01.xinion.lan
|
|
NODE_ENV: production
|
|
volumes:
|
|
- ./keys:/app/keys:ro
|
|
- traefik_certs:/app/traefik-certs
|
|
- traefik_dynamic:/app/traefik-dynamic
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.core-api.rule=PathPrefix(`/api`)"
|
|
- "traefik.http.services.core-api.loadbalancer.server.port=3000"
|
|
networks:
|
|
- insight
|
|
|
|
frontend:
|
|
build: ./packages/frontend
|
|
restart: unless-stopped
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.frontend.rule=PathPrefix(`/`)"
|
|
- "traefik.http.services.frontend.loadbalancer.server.port=80"
|
|
networks:
|
|
- insight
|
|
|
|
traefik:
|
|
image: traefik:v3
|
|
restart: unless-stopped
|
|
command:
|
|
- "--api.insecure=true"
|
|
- "--providers.docker=true"
|
|
- "--entrypoints.web.address=:80"
|
|
ports:
|
|
- "80:80"
|
|
- "8080:8080"
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
networks:
|
|
- insight
|
|
|
|
networks:
|
|
insight:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
traefik_certs:
|
|
traefik_dynamic:
|
|
```
|
|
|
|
### Deployment-Kommando (von Mac aus):
|
|
```bash
|
|
ssh -i .keys/deploy_platform_ed25519 deploy@172.20.10.21 \
|
|
"cd ~/insight-platform && git pull origin develop && \
|
|
docker compose build && docker compose up -d"
|
|
```
|
|
|
|
---
|
|
|
|
## Sicherheitsregeln (nicht verhandelbar)
|
|
|
|
- Kein `any` in TypeScript — strict mode immer
|
|
- Kein `localStorage` für Tokens (Access: Memory, Refresh: HttpOnly Cookie)
|
|
- Globaler `ValidationPipe` (whitelist + forbidNonWhitelisted)
|
|
- `@Public()` explizit für öffentliche Routen
|
|
- Rollen immer via `@Roles()` + `RolesGuard`
|
|
- Kein raw SQL — ausschließlich Prisma
|
|
- BCRYPT_COST 12
|
|
- JWT RS256 (nicht HS256!)
|
|
- Tokens in Redis blocklistbar (JTI-basiert)
|
|
|
|
---
|
|
|
|
## Umgebungsvariablen (.env auf APS01)
|
|
|
|
```env
|
|
DATABASE_URL=postgresql://insight_app:PASSWORT@insight-dbs01.xinion.lan:6432/insight_core
|
|
DATABASE_URL_DIRECT=postgresql://insight_app:PASSWORT@insight-dbs01.xinion.lan:5432/insight_core
|
|
REDIS_HOST=172.20.10.20
|
|
REDIS_PORT=6379
|
|
REDIS_PASSWORD=PASSWORT
|
|
BCRYPT_COST=12
|
|
JWT_ISSUER=insight-platform
|
|
JWT_ACCESS_TOKEN_EXPIRY=15m
|
|
JWT_REFRESH_TOKEN_EXPIRY=7d
|
|
NODE_ENV=production
|
|
FRONTEND_URL=http://insight-web01.xinion.lan
|
|
```
|
|
|
|
Passwörter: siehe Confluence → [SSH Keys & Zugangsverwaltung](https://xinion.atlassian.net/wiki/x/UACVEw)
|
|
|
|
---
|
|
|
|
## INSIGHT-Shared — Kommunikations-Repo
|
|
|
|
**Wir nutzen `INSIGHT-Shared` als zentrales Kommunikations-Repo aller Entwickler.**
|
|
|
|
Nach jedem relevanten Fortschritt trägst du deinen Stand in `dev-status/platform.md` ein.
|
|
Dort siehst du auch was Infra- und Apps-Entwickler gerade machen.
|
|
|
|
```
|
|
git@git.xinion.lan:Projekt-INSIGHT/INSIGHT-Shared.git
|
|
```
|
|
|
|
**Format:**
|
|
```markdown
|
|
## [Datum] — [Kurzbeschreibung]
|
|
|
|
**Abgeschlossen:**
|
|
- Was ist fertig?
|
|
|
|
**Nächste Schritte:**
|
|
- Was machst du als nächstes?
|
|
|
|
**Blockiert / Brauche Input:**
|
|
- Was fehlt dir von anderen Streams?
|
|
|
|
**Bereit für andere Streams:**
|
|
- Was kann ein anderer Entwickler jetzt nutzen?
|
|
```
|
|
|
|
**Wichtig für Apps-Entwickler (Phase 3):**
|
|
Sobald folgendes fertig ist, sofort in `dev-status/platform.md` dokumentieren:
|
|
- `InsightModule`-Interface stabil (aus `INSIGHT-Shared/contracts/module-interface.md`)
|
|
- Auth-Guards und RBAC-Mechanismus beschrieben
|
|
- `useAuth()` Hook API definiert
|
|
- React Shell kann Module registrieren und rendern
|
|
|
|
---
|
|
|
|
## Confluence-Dokumentation
|
|
|
|
| Seite | Link |
|
|
|---|---|
|
|
| Phase 2 — Plattform | https://xinion.atlassian.net/wiki/x/oYKSEw |
|
|
| Berechtigungskonzept | https://xinion.atlassian.net/wiki/x/FQCVEw |
|
|
| Phase 1 Status (Infra fertig) | https://xinion.atlassian.net/wiki/x/PACVEw |
|
|
| Netzwerk & IPs | https://xinion.atlassian.net/wiki/x/FYCVEw |
|
|
| Projekt-Übersicht | https://xinion.atlassian.net/wiki/spaces/ProjektINS/overview |
|
|
|
|
---
|
|
|
|
## Branching
|
|
|
|
```
|
|
main — stabil, nur via PR
|
|
develop — Integration, hier arbeitest du
|
|
feature/* — für größere Änderungen
|
|
fix/* — Bugfixes
|
|
```
|
|
|
|
Commit-Format: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
|
|
|
---
|
|
|
|
## Server-Referenz
|
|
|
|
| Server | IP | DNS | Funktion |
|
|
|---|---|---|---|
|
|
| DBS01 | 172.20.10.20 | insight-dbs01.xinion.lan | PostgreSQL:5432, PgBouncer:6432, Redis:6379 |
|
|
| APS01 | 172.20.10.21 | insight-aps01.xinion.lan | Docker (core-service läuft hier) |
|
|
| WEB01 | 172.20.10.22 | insight-web01.xinion.lan | Nginx (Frontend) |
|
|
|
|
---
|
|
|
|
_Stand: 15.03.2026 | Phase 2 | Repo: git@git.xinion.lan:Projekt-INSIGHT/INSIGHT-Platform.git_
|