datasource db { provider = "postgresql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" } model User { id String @id @default(uuid()) username String @unique createdAt DateTime @default(now()) encryptedMasterKeyPrf String? // Encrypted using PRF-derived key encryptedMasterKeyPrfIv String? encryptedMasterKeyPrfTag String? encryptedMasterKeyRec String // Encrypted using 12-word recovery phrase encryptedMasterKeyRecIv String encryptedMasterKeyRecTag String credentials Credential[] logbooks Logbook[] collaborations Collaboration[] pushSubscriptions PushSubscription[] notificationPrefs UserNotificationPrefs? appearancePrefs UserAppearancePrefs? personPool PersonPayload[] vesselPool VesselPayload[] } model PushSubscription { id String @id @default(uuid()) userId String endpoint String @unique p256dh String auth String userAgent String? locale String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) } model UserNotificationPrefs { userId String @id collaboratorChangesEnabled Boolean @default(false) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model UserAppearancePrefs { userId String @id theme String @default("auto") colorScheme String @default("auto") updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model Credential { id String @id @default(uuid()) userId String credentialId String @unique label String? publicKey Bytes counter BigInt transports String[] // WebAuthn transports list user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) } model Logbook { id String @id @default(uuid()) userId String encryptedTitle String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) // E2E Encrypted key for the owner (encrypted with owner's master key) encryptedKey String? iv String? tag String? yachts YachtPayload[] crews CrewPayload[] logbookCrewSelection LogbookCrewSelectionPayload? logbookVesselSelection LogbookVesselSelectionPayload? deviations DeviationPayload[] entries EntryPayload[] photos PhotoPayload[] gpsTracks GpsTrackPayload[] collaborators Collaboration[] invitations Invitation[] @@index([userId]) } model Collaboration { id String @id @default(uuid()) logbookId String userId String role String // "READ" | "WRITE" // The Logbook Key encrypted with this collaborator's master key encryptedLogbookKey String iv String tag String createdAt DateTime @default(now()) logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([logbookId, userId]) } model Invitation { token String @id @default(uuid()) logbookId String role String // "READ" | "WRITE" createdAt DateTime @default(now()) expiresAt DateTime logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) } model YachtPayload { id String @id @default(uuid()) logbookId String @unique encryptedData String iv String tag String updatedAt DateTime @updatedAt logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) } model CrewPayload { id String @id @default(uuid()) logbookId String payloadId String encryptedData String iv String tag String updatedAt DateTime @updatedAt logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) @@unique([logbookId, payloadId]) } model PersonPayload { id String @id @default(uuid()) userId String payloadId String encryptedData String iv String tag String updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([userId, payloadId]) @@index([userId]) } model LogbookCrewSelectionPayload { id String @id @default(uuid()) logbookId String @unique encryptedData String iv String tag String updatedAt DateTime @updatedAt logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) } model VesselPayload { id String @id @default(uuid()) userId String payloadId String encryptedData String iv String tag String updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([userId, payloadId]) @@index([userId]) } model LogbookVesselSelectionPayload { id String @id @default(uuid()) logbookId String @unique encryptedData String iv String tag String updatedAt DateTime @updatedAt logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) } model DeviationPayload { id String @id @default(uuid()) logbookId String @unique encryptedData String iv String tag String updatedAt DateTime @updatedAt logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) } model EntryPayload { id String @id @default(uuid()) logbookId String payloadId String encryptedData String iv String tag String updatedAt DateTime @updatedAt logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) @@unique([logbookId, payloadId]) @@index([logbookId]) } model PhotoPayload { id String @id @default(uuid()) logbookId String payloadId String entryId String encryptedData String iv String tag String updatedAt DateTime @updatedAt logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) @@unique([logbookId, payloadId]) @@index([logbookId]) @@index([entryId]) } model GpsTrackPayload { id String @id @default(uuid()) logbookId String entryId String @unique encryptedData String iv String tag String updatedAt DateTime @updatedAt logbook Logbook @relation(fields: [logbookId], references: [id], onDelete: Cascade) @@index([logbookId]) } model AiSummaryUsage { id String @id @default(uuid()) logbookId String entryId String count Int @default(0) updatedAt DateTime @updatedAt @@unique([logbookId, entryId]) @@index([logbookId]) }