Files
zeekayandHanzo Dev fa7c528ae4 feat(db): migrate Prisma datasource postgres -> sqlite (native, file-backed)
Drops the shared sql-0 postgres dependency in favor of an embedded SQLite
file (DATABASE_URL=file:/data/<db>), WAL-replicated to SeaweedFS by the
operator persistence sidecar — same pattern as esign (Documenso).

Schema changes for the sqlite connector:
- datasource provider postgresql -> sqlite; drop directUrl/shadowDatabaseUrl
- 18 enums -> String (values preserved as string defaults)
- scalar-list fields (String[]/Int[]) -> Json @default("[]") (arrays
  round-trip through Prisma Json at runtime; next.config ignoreBuildErrors
  keeps the type-only churn out of the build)
- strip pg-native @db.* attributes (@db.Text/@db.Timestamp)
- startup: prisma migrate deploy -> prisma db push (pg migration history is
  postgres-specific; db push reconciles the sqlite schema idempotently)

CI: amd64-only (DOKS has no arm64); deploy:false — rollout is operator-managed
via the universe Service CR, never `kubectl set image`.

Verified: `prisma validate` + `prisma db push` materialize all 62 tables on a
fresh sqlite file. Source DB is empty (0 app rows), so cutover is data-safe.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-05 00:33:13 -07:00

207 lines
6.8 KiB
Plaintext

model Conversation {
id String @id @default(cuid())
title String? // Optional title for the conversation
isEnabled Boolean @default(true)
// Visibility control
visibilityMode String @default("PRIVATE")
// Core relationships
dataroomId String
dataroom Dataroom @relation(fields: [dataroomId], references: [id], onDelete: Cascade)
// Optional attachments
dataroomDocumentId String?
dataroomDocument DataroomDocument? @relation(fields: [dataroomDocumentId], references: [id], onDelete: SetNull)
documentVersionNumber Int? // Optional document version number reference
documentPageNumber Int? // Optional document page number reference
// Optional link relationship
linkId String?
link Link? @relation(fields: [linkId], references: [id], onDelete: SetNull)
// Optional viewer group relationship
viewerGroupId String?
viewerGroup ViewerGroup? @relation(fields: [viewerGroupId], references: [id], onDelete: SetNull)
// Original view that initiated the conversation (for reference)
initialViewId String?
initialView View? @relation(name: "initialView", fields: [initialViewId], references: [id], onDelete: SetNull)
teamId String
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
// Track all views that accessed this conversation
views ConversationView[]
// Track participants including the owner
participants ConversationParticipant[]
// Track conversations
messages Message[]
lastMessageAt DateTime? // Last message timestamp
// Published FAQs created from this conversation
faqItems DataroomFaqItem[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([dataroomId])
@@index([dataroomDocumentId])
@@index([linkId])
@@index([teamId])
@@index([viewerGroupId])
@@index([initialViewId])
}
// Define conversation visibility options
// Define participant roles
// Track participants in a conversation (including the owner)
model ConversationParticipant {
id String @id @default(cuid())
conversationId String
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
// Role in the conversation
role String @default("PARTICIPANT")
// Participant can be either a viewer or a user
viewerId String?
viewer Viewer? @relation(fields: [viewerId], references: [id], onDelete: SetNull)
userId String?
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
// Notification preferences
receiveNotifications Boolean @default(false)
createdAt DateTime @default(now())
@@unique([conversationId, viewerId])
@@unique([conversationId, userId])
@@index([conversationId])
@@index([viewerId])
@@index([userId])
}
model Message {
id String @id @default(cuid())
content String // The actual message content
// Conversation relationship
conversationId String
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
// Sender information
userId String? // Optional - for team members
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
viewerId String? // Optional - for viewers
viewer Viewer? @relation(fields: [viewerId], references: [id], onDelete: SetNull)
// The specific view when this message was sent (for tracking)
viewId String?
view View? @relation(fields: [viewId], references: [id], onDelete: SetNull)
// Tracking
isRead Boolean @default(false)
// FAQ relationships (reverse relations)
faqAsQuestion DataroomFaqItem[] @relation("FAQQuestionMessage")
faqAsAnswer DataroomFaqItem[] @relation("FAQAnswerMessage")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([conversationId])
@@index([userId])
@@index([viewerId])
@@index([viewId])
}
// Join table to track all views that accessed a conversation
model ConversationView {
id String @id @default(cuid())
conversationId String
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
viewId String
view View @relation(fields: [viewId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@unique([conversationId, viewId])
@@index([conversationId])
@@index([viewId])
}
// Published FAQ items for datarooms
model DataroomFaqItem {
id String @id @default(cuid())
title String? // Optional title for the FAQ
editedQuestion String // Admin-edited version of the question
originalQuestion String? // Original question from visitor (for reference)
answer String // The answer content
description String? // Optional context or description
// Relationships
dataroomId String
dataroom Dataroom @relation(fields: [dataroomId], references: [id], onDelete: Cascade)
linkId String? // Optional: specific link visibility
link Link? @relation(fields: [linkId], references: [id], onDelete: SetNull)
dataroomDocumentId String? // Optional: document-specific FAQ
dataroomDocument DataroomDocument? @relation(fields: [dataroomDocumentId], references: [id], onDelete: SetNull)
// Source conversation and messages (for reference and editing)
sourceConversationId String? // Original conversation
sourceConversation Conversation? @relation(fields: [sourceConversationId], references: [id], onDelete: SetNull)
questionMessageId String? // Reference to original question message
questionMessage Message? @relation(name: "FAQQuestionMessage", fields: [questionMessageId], references: [id], onDelete: SetNull)
answerMessageId String? // Reference to answer message
answerMessage Message? @relation(name: "FAQAnswerMessage", fields: [answerMessageId], references: [id], onDelete: SetNull)
// Publishing details
teamId String
team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)
publishedByUserId String
publishedByUser User @relation(fields: [publishedByUserId], references: [id], onDelete: Cascade)
// Visibility and status
visibilityMode String @default("PUBLIC_DATAROOM")
status String @default("PUBLISHED")
isAnonymized Boolean @default(true)
// Analytics
viewCount Int @default(0)
// Optional categorization
tags Json @default("[]")
// Metadata
documentPageNumber Int? // Optional: specific page reference
documentVersionNumber Int? // Optional: specific version reference
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([dataroomId])
@@index([linkId])
@@index([dataroomDocumentId])
@@index([sourceConversationId])
@@index([teamId])
@@index([publishedByUserId])
@@index([status])
@@index([visibilityMode])
@@index([createdAt])
}
// Define FAQ visibility options
// Define FAQ status