3504ec97cc
Move skipper and crew master data to the user profile pool, replace the logbook crew tab with selection from that pool, inherit crew on new travel days, and sync via new PersonPayload and LogbookCrewSelection models. Includes migration from legacy crew records, tour/demo updates, and i18n. Co-authored-by: Cursor <cursoragent@cursor.com>
229 lines
6.5 KiB
Plaintext
229 lines
6.5 KiB
Plaintext
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[]
|
|
}
|
|
|
|
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?
|
|
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 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])
|
|
}
|