// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ===== USERS & AUTHENTICATION ===== model User { id String @id @default(cuid()) clerkId String @unique // Clerk user ID username String @unique email String @unique displayName String? bio String? avatarUrl String? bannerUrl String? isCreator Boolean @default(false) verified Boolean @default(false) // Relations channels Channel[] followedUsers UserFollow[] @relation("following") followers UserFollow[] @relation("follower") chatMessages ChatMessage[] subscriptions Subscription[] donations Donation[] followers Follower[] clips Clip[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([username]) @@index([email]) @@index([clerkId]) } model UserFollowUser { followerId String followingId String follower User @relation("follower", fields: [followerId], references: [id], onDelete: Cascade) following User @relation("following", fields: [followingId], references: [id], onDelete: Cascade) followDate DateTime @default(now()) @@id([followerId, followingId]) @@index([followerId]) @@index([followingId]) } // ===== CHANNELS & STREAMING ===== model Channel { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) name String @unique slug String @unique description String? category String? // gaming, music, education, etc. language String? language String? isLive Boolean @default(false) totalViews BigInt @default(0) streams Stream[] followers Follower[] subscriptions Subscription[] donations Donation[] clips Clip[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId]) @@index([slug]) } model Stream { id String @id @default(cuid()) channelId String channel Channel @relation(fields: [channelId], references: [id], onDelete: Cascade) title String description String? status String @default("scheduled") // live, ended, scheduled streamKey String @unique hlsUrl String? thumbnailUrl String? viewerCount Int @default(0) startedAt DateTime? endedAt DateTime? durationSeconds Int? isArchived Boolean @default(true) archiveUrl String? chatMessages ChatMessage[] vod VOD? polls Poll[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([channelId]) @@index([status]) @@index([isLive]) } // ===== SOCIAL FEATURES ===== model Follower { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) channelId String channel Channel @relation(fields: [channelId], references: [id], onDelete: Cascade) notificationEnabled Boolean @default(true) followDate DateTime @default(now()) @@unique([userId, channelId]) @@index([channelId]) } // ===== CHAT & MODERATION ===== model ChatMessage { id String @id @default(cuid()) streamId String stream Stream @relation(fields: [streamId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) message String isDeleted Boolean @default(false) createdAt DateTime @default(now()) @@index([streamId]) @@index([createdAt]) } // ===== MONETIZATION ===== model Subscription { id String @id @default(cuid()) subscriberId String subscriber User @relation(fields: [subscriberId], references: [id], onDelete: Cascade) channelId String channel Channel @relation(fields: [channelId], references: [id], onDelete: Cascade) tier String // basic, premium, vip priceCents Int stripeSubscriptionId String? status String @default("active") // active, cancelled, expired autoRenew Boolean @default(true) startedAt DateTime @default(now()) endedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([subscriberId, channelId]) @@index([channelId]) @@index([status]) } model Donation { id String @id @default(cuid()) donorId String donor User @relation(fields: [donorId], references: [id], onDelete: Cascade) channelId String channel Channel @relation(fields: [channelId], references: [id], onDelete: Cascade) streamId String? // Optional: specific stream amountCents Int currency String @default("USD") message String? stripeChargeId String? status String @default("completed") // pending, completed, failed createdAt DateTime @default(now()) @@index([channelId]) @@index([status]) } // ===== CONTENT ===== model VOD { id String @id @default(cuid()) streamId String @unique stream Stream @relation(fields: [streamId], references: [id], onDelete: Cascade) title String? description String? thumbnailUrl String? durationSeconds Int? views Int @default(0) isPublic Boolean @default(true) processingStatus String @default("processing") // processing, ready, failed clips Clip[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([streamId]) } model Clip { id String @id @default(cuid()) vodId String? vod VOD? @relation(fields: [vodId], references: [id], onDelete: SetNull) channelId String channel Channel @relation(fields: [channelId], references: [id], onDelete: Cascade) creatorId String creator User @relation(fields: [creatorId], references: [id], onDelete: Cascade) title String slug String @unique description String? thumbnailUrl String? videoUrl String? startSeconds Int? durationSeconds Int? views Int @default(0) likes Int @default(0) isPublic Boolean @default(true) clipLikes ClipLike[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([channelId]) @@index([creatorId]) } model ClipLike { id String @id @default(cuid()) userId String clipId String clip Clip @relation(fields: [clipId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) @@unique([userId, clipId]) @@index([clipId]) } // ===== INTERACTIONS ===== model Poll { id String @id @default(cuid()) streamId String stream Stream @relation(fields: [streamId], references: [id], onDelete: Cascade) question String durationSeconds Int? status String @default("active") // active, ended options PollOption[] createdAt DateTime @default(now()) endedAt DateTime? @@index([streamId]) } model PollOption { id String @id @default(cuid()) pollId String poll Poll @relation(fields: [pollId], references: [id], onDelete: Cascade) option String voteCount Int @default(0) votes PollVote[] @@index([pollId]) } model PollVote { id String @id @default(cuid()) optionId String option PollOption @relation(fields: [optionId], references: [id], onDelete: Cascade) userId String createdAt DateTime @default(now()) @@unique([userId, optionId]) @@index([optionId]) }