- Add Prisma schema with all database models - Creator channels, streams, VOD, clips, monetization - Real-time chat, polls, interactions - Subscriptions, donations, payouts - Social graph (followers), clips, leaderboards - Complete Platform Architecture documentation - API structure and routes documentation - Implementation roadmap (8 phases) - Updated README with platform vision - Install core dependencies: Prisma, Clerk, Stripe, Socket.io Ready for Phase 1 development with database setup.
323 lines
7.8 KiB
Text
323 lines
7.8 KiB
Text
// 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])
|
|
}
|