INSIGHT-MVP/repos/INSIGHT-Platform/BRIEFING.md

18 KiB

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

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).

Prisma Schema (insight_core)

Datei: packages/core-service/prisma/schema.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[]

  @@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")
}

RBAC-Konzept

User.platformRole:
  PLATFORM_ADMIN  → Zugang zum kompletten Admin-Bereich
  USER            → Normaler Nutzer

User.moduleRoles (JSON):
  { "crm": "ADMIN" }      → Admin im CRM-Modul
  { "crm": "MANAGER" }    → Manager im CRM-Modul
  { "crm": "USER" }       → Normaler CRM-Nutzer
  { "crm": "VIEWER" }     → Nur lesen
  {}                      → Kein Zugang zu CRM

Im JWT-Token werden beide Felder mitgegeben:

{
  "sub": "uuid",
  "email": "user@firma.de",
  "platformRole": "USER",
  "moduleRoles": { "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:

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):

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:

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, tenantRoleplatformRole)
  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/modules/ — NEU: ModuleRegistryService (Module aus DB laden, User-Rollen-Zuweisung)

4. JWT RS256 Keys generieren:

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):

// 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:

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 (aus MVP)
  8. admin/AdminLayout.tsx — Admin-Navigation (aus MVP, CRM-Settings entfernen)
  9. admin/AdminUsersPage.tsx — User-Verwaltung + Rollen-Zuweisung (aus MVP erweitern)
  10. admin/AdminSsoPage.tsx — SSO-Konfiguration (aus MVP 1:1)
  11. admin/AdminCustomizePage.tsx — Branding (aus MVP 1:1)
  12. admin/AdminExternalLinksPage.tsx — Externe Links (aus MVP 1:1)
  13. admin/AdminCompanyPage.tsx — Firmendaten (aus MVP 1:1)
  14. admin/AdminSslPage.tsx — SSL/Domain (aus MVP 1:1)
  15. admin/AdminAiSettingsPage.tsx — KI-Einstellungen (aus MVP 1:1)
  16. admin/AdminModulesPage.tsx — NEU: Modul-Registry (welche Module aktiv)

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 (Dropdown: ADMIN/MANAGER/USER/VIEWER/Kein Zugang)

Deployment auf APS01

Docker Compose (docker-compose.yml im Repo-Root)

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):

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)

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


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:

## [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