mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-25 00:36:39 +02:00
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>
181 lines
6.2 KiB
Text
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")
|
|
}
|