generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(cuid()) email String @unique passwordHash String displayName String createdAt DateTime @default(now()) devices Device[] refreshTokens RefreshToken[] messages Message[] @relation("MessageSender") memberships ChatMember[] bots Bot[] } model Device { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) name String? createdAt DateTime @default(now()) lastSeenAt DateTime? } model RefreshToken { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) deviceId String? tokenHash String expiresAt DateTime createdAt DateTime @default(now()) } enum ChatTypeEnum { DIRECT GROUP CHANNEL } model Chat { id String @id @default(cuid()) type ChatTypeEnum title String? isBroadcast Boolean @default(false) isPublic Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt members ChatMember[] messages Message[] pins PinnedMessage[] calls CallSession[] botMembers BotMember[] } model ChatMember { chatId String userId String role String lastReadSeq Int @default(0) lastDeliveredSeq Int @default(0) chat Chat @relation(fields: [chatId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@id([chatId, userId]) } model Message { id String @id @default(cuid()) chatId String chat Chat @relation(fields: [chatId], references: [id], onDelete: Cascade) senderId String? sender User? @relation("MessageSender", fields: [senderId], references: [id]) kind String body String? replyToId String? seq Int createdAt DateTime @default(now()) editedAt DateTime? deletedAt DateTime? media MessageMedia[] pins PinnedMessage[] @@unique([chatId, seq]) @@index([chatId, seq]) } model MediaObject { id String @id @default(cuid()) userId String mimeType String s3Key String sizeBytes Int? thumbnailKey String? finalized Boolean @default(false) createdAt DateTime @default(now()) messageLinks MessageMedia[] } model MessageMedia { messageId String mediaId String message Message @relation(fields: [messageId], references: [id], onDelete: Cascade) media MediaObject @relation(fields: [mediaId], references: [id]) @@id([messageId, mediaId]) } model PinnedMessage { chatId String messageId String chat Chat @relation(fields: [chatId], references: [id], onDelete: Cascade) message Message @relation(fields: [messageId], references: [id], onDelete: Cascade) @@id([chatId, messageId]) } model CallSession { id String @id @default(cuid()) chatId String chat Chat @relation(fields: [chatId], references: [id], onDelete: Cascade) createdBy String status String createdAt DateTime @default(now()) } model Bot { id String @id @default(cuid()) ownerUserId String owner User @relation(fields: [ownerUserId], references: [id], onDelete: Cascade) displayName String tokenHash String webhookUrl String? createdAt DateTime @default(now()) memberships BotMember[] } model BotMember { botId String chatId String bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) chat Chat @relation(fields: [chatId], references: [id], onDelete: Cascade) @@id([botId, chatId]) }