INSIGHT-MVP/packages/core-service/prisma/core.schema.prisma
Thomas Reitz 10f291cdda feat: implement Sprint 1 Alpha - full stack with Docker, NestJS, React
Docker Infrastructure:
- docker-compose.yml with Traefik 3, PostgreSQL 16, PgBouncer, Redis 7, step-ca
- docker-compose.observability.yml with Prometheus, Grafana, Loki, Tempo, Promtail
- Traefik dynamic config (TLS, security headers, CORS, compression)
- PostgreSQL init script (uuid-ossp, pgcrypto, pg_trgm extensions)
- Grafana auto-provisioned datasources (Prometheus, Loki, Tempo)

NestJS Core-Service:
- Auth module: Login (email/password), TOTP 2FA, JWT RS256, token refresh/revocation
- Users module: CRUD, bcrypt cost 12, pagination, role-based access
- Tenants module: CRUD, member management, slug validation
- Prisma schemas: core (Users, AuthProviders, Tenants, Modules, AuditLog)
                  tenant (Contacts, Activities - CRM reference for Sprint 2)
- TenantPrismaService: Dynamic per-tenant DB connections with caching
- RedisService: Token blocklist, refresh token families, generic cache
- Global JwtAuthGuard with @Public() decorator, RolesGuard, GlobalExceptionFilter
- Health endpoint with DB + Redis status checks
- Swagger API documentation (dev only)
- Multi-stage Dockerfile (dev + production)

React Frontend:
- Vite 6 + React 18 + TypeScript strict
- AuthContext with silent refresh (access token in memory, NOT localStorage)
- Login page with TOTP 2FA support
- App shell with sidebar navigation
- Admin pages: Users + Tenants management tables
- API client with automatic token refresh interceptor
- Multi-stage Dockerfile (dev + nginx production)

CI/CD Pipelines:
- ci.yml: Lint, type-check, test, build on all branches
- deploy.yml: Docker build, push to Forgejo registry, SSH deploy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 15:33:36 +01:00

181 lines
6.2 KiB
Text

// ============================================================
// INSIGHT MVP - Core Schema (platform_core Datenbank)
// ============================================================
// Zentrale Plattform-Tabellen: Users, Tenants, Auth, Modules
// Kein raw SQL - nur 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)
role String @default("USER") @db.VarChar(50) // PLATFORM_ADMIN, TENANT_ADMIN, USER
isActive Boolean @default(true) @map("is_active")
// 2FA
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")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relationen
authProvider AuthProvider[]
tenantMemberships TenantMembership[]
auditLogs AuditLog[]
@@map("users")
}
// --------------------------------------------------------
// AuthProvider - Authentifizierungs-Provider pro User
// --------------------------------------------------------
// Unterstuetzt mehrere Auth-Methoden pro User:
// LOCAL (Passwort), MS_SSO (spaeter), M2M (Machine-to-Machine)
model AuthProvider {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
provider String @db.VarChar(50) // LOCAL, MS_SSO, M2M
providerId String? @map("provider_id") @db.VarChar(255) // Externe ID (z.B. MS Object ID)
passwordHash String? @map("password_hash") @db.VarChar(255)
totpSecret String? @map("totp_secret") @db.VarChar(255) // Verschluesselt
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relationen
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, provider])
@@map("auth_providers")
}
// --------------------------------------------------------
// Tenant - Mandant
// --------------------------------------------------------
model Tenant {
id String @id @default(uuid()) @db.Uuid
name String @db.VarChar(200)
slug String @unique @db.VarChar(50) // URL-freundlich, fuer DB-Name
isActive Boolean @default(true) @map("is_active")
// Mandant-Einstellungen (JSON)
settings Json @default("{}")
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relationen
members TenantMembership[]
modules TenantModule[]
@@map("tenants")
}
// --------------------------------------------------------
// TenantMembership - User-Tenant-Zuordnung (M:N)
// --------------------------------------------------------
model TenantMembership {
id String @id @default(uuid()) @db.Uuid
userId String @map("user_id") @db.Uuid
tenantId String @map("tenant_id") @db.Uuid
tenantRole String @default("MEMBER") @map("tenant_role") @db.VarChar(50) // ADMIN, MEMBER, VIEWER
isActive Boolean @default(true) @map("is_active")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relationen
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
@@unique([userId, tenantId])
@@map("tenant_memberships")
}
// --------------------------------------------------------
// Module - Verfuegbare Plattform-Module
// --------------------------------------------------------
model Module {
id String @id @default(uuid()) @db.Uuid
key String @unique @db.VarChar(50) // z.B. "crm", "project", "docs"
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")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
// Relationen
tenantModules TenantModule[]
@@map("modules")
}
// --------------------------------------------------------
// TenantModule - Welcher Tenant welche Module nutzt
// --------------------------------------------------------
model TenantModule {
id String @id @default(uuid()) @db.Uuid
tenantId String @map("tenant_id") @db.Uuid
moduleId String @map("module_id") @db.Uuid
isActive Boolean @default(true) @map("is_active")
// Modul-spezifische Konfiguration pro Tenant
config Json @default("{}")
activatedAt DateTime @default(now()) @map("activated_at")
// Relationen
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
module Module @relation(fields: [moduleId], references: [id], onDelete: Cascade)
@@unique([tenantId, moduleId])
@@map("tenant_modules")
}
// --------------------------------------------------------
// AuditLog - Plattform-weites Audit-Log
// --------------------------------------------------------
model AuditLog {
id String @id @default(uuid()) @db.Uuid
userId String? @map("user_id") @db.Uuid
action String @db.VarChar(100) // z.B. "user.login", "tenant.create"
entity String @db.VarChar(100) // z.B. "User", "Tenant"
entityId String? @map("entity_id") @db.VarChar(255)
details Json? // Zusaetzliche Informationen
ipAddress String? @map("ip_address") @db.VarChar(45)
userAgent String? @map("user_agent") @db.Text
createdAt DateTime @default(now()) @map("created_at")
// Relationen
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
@@index([userId])
@@index([action])
@@index([entity, entityId])
@@index([createdAt])
@@map("audit_logs")
}