// Prisma-Schema fuer das Kalender- und Reservierungstool. // Kernregeln aus plan.md: // USER -> genau 1 Kalender (ueber calendar_members, unique auf user_id) // ADMIN -> 0..n Kalender (owner_id) // Invite-Token wird nur gehasht gespeichert. generator client { provider = "prisma-client-js" // linux-musl-openssl-3.0.x: Alpine-Container (node:20-alpine) nutzen // OpenSSL 3; ohne dieses Target waehlt Prisma die 1.1-Engine, die // dort zur Laufzeit nicht ladbar ist. binaryTargets = ["native", "linux-musl-openssl-3.0.x"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") } /// Rollen nach plan.md Abschnitt 10: nur ADMIN und USER zum Start. enum Role { ADMIN USER } /// Status einer Reservierung. enum ReservationStatus { CONFIRMED CANCELLED } model User { id String @id @default(cuid()) email String @unique /// Frei waehlbarer Anzeigename; null, wenn noch nicht gesetzt. /// Im Profil-Badge wird username bevorzugt, sonst email angezeigt. username String? @unique passwordHash String @map("password_hash") role Role @default(USER) /// Stabile User-ID der MultiToolApp-Plattform (SSO via Hub-Token). /// null = rein lokaler Account (klassisches Login). Beim ersten /// Hub-Login wird der Account an die Hub-ID gekoppelt (JIT-Provisioning). hubId String? @unique @map("hub_id") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") /// Kalender, deren Mitglied dieser User ist. /// Durch @unique ist sichergestellt: Ein User gehoert GENAU EINEM Kalender an. memberships CalendarMember[] /// Kalender, die dieser User besitzt (nur fuer ADMIN relevant). ownedCalendars Calendar[] @relation("CalendarOwner") reservations Reservation[] @@map("users") } model Calendar { id String @id @default(cuid()) ownerId String @map("owner_id") title String description String? timezone String @default("Europe/Berlin") isActive Boolean @default(true) @map("is_active") /// Maximale Reservierungsdauer in Minuten (15..1440). 0 = unbegrenzt. maxReservationMinutes Int @default(240) @map("max_reservation_minutes") /// Duerven Nicht-Admins die Titel fremder Reservierungen sehen? showTitleToMembers Boolean @default(true) @map("show_title_to_members") /// Duerven Nicht-Admins die Notizen fremder Reservierungen sehen? showNotesToMembers Boolean @default(false) @map("show_notes_to_members") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") owner User @relation("CalendarOwner", fields: [ownerId], references: [id], onDelete: Cascade) members CalendarMember[] invites Invite[] reservations Reservation[] @@index([ownerId]) @@map("calendars") } /// Zuordnung User <-> Kalender. /// Das unique auf user_id erzwingt die Kernregel: ein User, genau ein Kalender. model CalendarMember { id String @id @default(cuid()) calendarId String @map("calendar_id") userId String @unique @map("user_id") createdAt DateTime @default(now()) @map("created_at") calendar Calendar @relation(fields: [calendarId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([calendarId, userId]) @@index([calendarId]) @@map("calendar_members") } model Reservation { id String @id @default(cuid()) calendarId String @map("calendar_id") userId String @map("user_id") title String startAt DateTime @map("start_at") endAt DateTime @map("end_at") status ReservationStatus @default(CONFIRMED) notes String? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") calendar Calendar @relation(fields: [calendarId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([calendarId, startAt]) @@index([userId]) @@map("reservations") } model Invite { id String @id @default(cuid()) calendarId String @map("calendar_id") tokenHash String @unique @map("token_hash") expiresAt DateTime? @map("expires_at") usedAt DateTime? @map("used_at") createdAt DateTime @default(now()) @map("created_at") calendar Calendar @relation(fields: [calendarId], references: [id], onDelete: Cascade) @@index([calendarId]) @@map("invites") }