// Datenmodell des Planers. // Fachbegriffe auf Deutsch, technische Felder auf Englisch. generator client { provider = "prisma-client" output = "../src/generated/prisma" moduleFormat = "esm" } datasource db { provider = "postgresql" } enum Rolle { ADMIN BENUTZER } /// Ein Mensch, der sich anmelden darf. model Benutzer { id String @id @default(cuid()) email String @unique name String? /// Nur gesetzt, wenn die Anmeldung per Passwort erlaubt ist. passwortHash String? rolle Rolle @default(BENUTZER) aktiv Boolean @default(true) bildUrl String? emailGeprueft DateTime? zuletztAktiv DateTime? erstelltAm DateTime @default(now()) aktualisiertAm DateTime @updatedAt konten Konto[] passkeys Passkey[] audit AuditEintrag[] @@index([aktiv]) @@map("benutzer") } /// Verknuepfung zu einem OIDC-Anbieter (Entra ID, authentik). model Konto { id String @id @default(cuid()) benutzerId String type String provider String providerAccountId String refresh_token String? access_token String? expires_at Int? token_type String? scope String? id_token String? session_state String? benutzer Benutzer @relation(fields: [benutzerId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) @@index([benutzerId]) @@map("konto") } /// Ein registrierter Passkey (WebAuthn) eines Benutzers. model Passkey { id String @id @default(cuid()) benutzerId String /// Base64URL der Credential-ID. credentialId String @unique publicKey Bytes counter BigInt @default(0) /// Kommagetrennt, z. B. "internal,hybrid". transports String? geraetetyp String? gesichert Boolean @default(false) bezeichnung String erstelltAm DateTime @default(now()) zuletztBenutzt DateTime? benutzer Benutzer @relation(fields: [benutzerId], references: [id], onDelete: Cascade) @@index([benutzerId]) @@map("passkey") } /// Laufende WebAuthn-Herausforderung; kurzlebig, wird nach Gebrauch geloescht. model WebauthnChallenge { id String @id @default(cuid()) /// E-Mail bei der Anmeldung, Benutzer-ID bei der Registrierung. kennung String challenge String zweck String gueltigBis DateTime @@index([kennung, zweck]) @@map("webauthn_challenge") } /// Lebensbereich mit Farbe, Tageskapazitaet und Arbeitsfenster. model Bereich { id String @id @default(cuid()) name String @unique /// Hex-Farbe inklusive Raute, z. B. "#2563eb". farbe String reihenfolge Int @default(0) aktiv Boolean @default(true) /// Tageskapazitaet in Minuten, Montag bis Sonntag. kapazitaetMo Int @default(0) kapazitaetDi Int @default(0) kapazitaetMi Int @default(0) kapazitaetDo Int @default(0) kapazitaetFr Int @default(0) kapazitaetSa Int @default(0) kapazitaetSo Int @default(0) /// Arbeitsfenster als Minuten seit Mitternacht, Europe/Berlin. arbeitsfensterVon Int @default(480) arbeitsfensterBis Int @default(1020) erstelltAm DateTime @default(now()) aktualisiertAm DateTime @updatedAt @@index([aktiv, reihenfolge]) @@map("bereich") } /// Unveraenderlicher Nachweis jeder schreibenden Aktion. model AuditEintrag { id String @id @default(cuid()) zeitpunkt DateTime @default(now()) benutzerId String? /// Bleibt erhalten, auch wenn der Benutzer spaeter geloescht wird. benutzerEmail String aktion String objektTyp String objektId String? /// Nur fachliche Felder, niemals Tokens, Mailinhalte oder Termin-Betreffs. details Json? instanz String benutzer Benutzer? @relation(fields: [benutzerId], references: [id], onDelete: SetNull) @@index([zeitpunkt]) @@index([objektTyp, objektId]) @@index([benutzerId]) @@map("audit_eintrag") }