Benutzer mit Rolle, OIDC-Konten, Sitzungen, Passkeys samt kurzlebiger WebAuthn-Herausforderung, Bereiche mit Tageskapazität und Arbeitsfenster sowie ein unveränderlicher Audit-Eintrag je schreibender Aktion. Aufgabe und Zeitblock kommen mit M2 und M4, Kalenderkonto mit M3 — dann mit den Feldern, die der jeweilige Provider wirklich braucht. Prisma 7 nimmt die Verbindung nicht mehr aus dem Schema entgegen: die URL steht in prisma.config.ts, der Client bekommt den pg-Treiberadapter. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
171 lines
4.4 KiB
Plaintext
171 lines
4.4 KiB
Plaintext
// 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[]
|
|
sitzungen Sitzung[]
|
|
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")
|
|
}
|
|
|
|
model Sitzung {
|
|
id String @id @default(cuid())
|
|
sessionToken String @unique
|
|
benutzerId String
|
|
expires DateTime
|
|
|
|
benutzer Benutzer @relation(fields: [benutzerId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([benutzerId])
|
|
@@map("sitzung")
|
|
}
|
|
|
|
model VerifizierungsToken {
|
|
identifier String
|
|
token String @unique
|
|
expires DateTime
|
|
|
|
@@unique([identifier, token])
|
|
@@map("verifizierungs_token")
|
|
}
|
|
|
|
/// 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")
|
|
}
|