aethex.live/prisma/schema.prisma
MrPiglr 58d231e72f
Major: Add complete platform architecture with full data models
- 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.
2026-02-07 04:52:01 +00:00

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])
}