Files
youtube-studio-flow/backend/prisma/schema.prisma
T

493 lines
15 KiB
Plaintext

generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// ─── USERS ────────────────────────────────────────────────
model User {
id String @id @default(cuid())
email String @unique
name String?
googleId String @unique
isAppAdmin Boolean @default(false)
preferences Json?
teams TeamMember[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ─── TEAMS ────────────────────────────────────────────────
model Team {
id String @id @default(cuid())
name String
slug String @unique
dateFormat String?
timezone String @default("UTC")
publishingSchedule Json?
showCanvaLink Boolean @default(false)
disabledLintRules String[] @default([])
showDeletedVideos Boolean @default(false)
conflictDetectionEnabled Boolean @default(false)
conflictDetectionBatchSize Int @default(50)
conflictDetectionMinAgeDays Int @default(7)
members TeamMember[]
channels Channel[]
blocks DescriptionBlock[]
templates Template[]
collaborators Collaborator[]
bulkJobs BulkJob[]
savedViews SavedView[]
importJobs ImportJob[]
exportJobs ExportJob[]
campaigns Campaign[]
variables TeamVariable[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model TeamVariable {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
name String
value String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([teamId, name])
@@index([teamId])
}
model TeamMember {
userId String
teamId String
role TeamRole @default(EDITOR)
user User @relation(fields: [userId], references: [id])
team Team @relation(fields: [teamId], references: [id])
createdAt DateTime @default(now())
@@id([userId, teamId])
@@index([teamId])
}
enum TeamRole {
OWNER
ADMIN
EDITOR
REVIEWER
READONLY
}
// ─── CHANNELS ─────────────────────────────────────────────
model Channel {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
youtubeChannelId String @unique
name String
uploadsPlaylistId String?
supplementalVideoIds String[]
youtubeAccessToken String?
youtubeRefreshToken String?
youtubeTokenExpiry DateTime?
connectedBy String
videos Video[]
playlists Playlist[]
quotaLogs QuotaLog[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([teamId])
}
// ─── VIDEOS ───────────────────────────────────────────────
model Video {
id String @id @default(cuid())
youtubeVideoId String @unique
channelId String
channel Channel @relation(fields: [channelId], references: [id])
title String
youtubeDescription String?
renderedDescription String?
thumbnailUrl String?
tags String[]
categoryId String?
privacyStatus PrivacyStatus @default(PRIVATE)
publishedAt DateTime?
scheduledAt DateTime?
templateId String?
// Extended metadata
madeForKids Boolean @default(false)
selfDeclaredMadeForKids Boolean @default(false)
containsPaidPromotion Boolean @default(false)
ageRestricted Boolean @default(false)
embeddable Boolean @default(true)
license String @default("youtube")
defaultLanguage String?
defaultAudioLanguage String?
recordingDate DateTime?
gameTitle String?
collaboratorIds Json @default("[]")
youtubeSnapshot Json?
lintStatus LintStatus @default(OK)
lastSyncedAt DateTime?
lastSyncedHash String?
remoteConflict Boolean @default(false)
pendingRemoteSnapshot Json?
pendingRemoteDescription String?
youtubeDeletedAt DateTime?
template Template? @relation(fields: [templateId], references: [id])
config VideoConfig?
playlists VideoPlaylist[]
lintResults LintResult[]
bulkJobItems BulkJobItem[]
quotaLogs QuotaLog[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([channelId])
}
enum PrivacyStatus {
PUBLIC
PRIVATE
UNLISTED
}
enum LintStatus {
OK
WARNING
ERROR
}
// ─── VIDEO CONFIG ─────────────────────────────────────────
model VideoConfig {
id String @id @default(cuid())
videoId String @unique
video Video @relation(fields: [videoId], references: [id])
templateId String?
blockOrder Json
blockOverrides Json
variableValues Json
version Int @default(1)
renderHash String?
renderedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// ─── DESCRIPTION BLOCKS ───────────────────────────────────
model DescriptionBlock {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
name String
type BlockType
content String
language String @default("de")
campaignId String?
campaign Campaign? @relation(fields: [campaignId], references: [id])
version Int @default(1)
active Boolean @default(true)
compact Boolean @default(false)
tags String[]
variableDefinitions Json @default("[]")
condition Json?
versions BlockVersion[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([teamId])
}
enum BlockType {
STATIC
VARIABLE
CONDITIONAL
REPEATABLE
GLOBAL
CAMPAIGN
COLLABORATOR
}
model BlockVersion {
id String @id @default(cuid())
blockId String
block DescriptionBlock @relation(fields: [blockId], references: [id])
version Int
contentSnapshot Json
createdAt DateTime @default(now())
createdBy String?
}
// ─── TEMPLATES ────────────────────────────────────────────
model Template {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
name String
description String?
defaultBlocks Json
defaultOverrides Json @default("{}")
rules Json
variables Json
videoFields Json?
version Int @default(1)
active Boolean @default(true)
videos Video[]
versions TemplateVersion[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([teamId])
}
model TemplateVersion {
id String @id @default(cuid())
templateId String
template Template @relation(fields: [templateId], references: [id])
version Int
snapshot Json
createdAt DateTime @default(now())
}
// ─── COLLABORATORS ────────────────────────────────────────
model Collaborator {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
name String
youtubeLink String?
twitchLink String?
instagramLink String?
tiktokLink String?
twitterLink String?
blueskyLink String?
discordHandle String?
aliases String[]
active Boolean @default(true)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([teamId])
}
// ─── SAVED VIEWS ──────────────────────────────────────────
model SavedView {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
name String
description String?
isGlobal Boolean @default(false)
ownerId String?
queryJson Json
columnsJson Json
sortJson Json?
pinnedAsTab Boolean @default(false)
tabOrder Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([teamId])
}
// ─── LINT RESULTS ─────────────────────────────────────────
model LintResult {
id String @id @default(cuid())
videoId String
video Video @relation(fields: [videoId], references: [id])
ruleCode String
severity LintSeverity
targetField String?
message String
fixSuggestion String?
resolvedAt DateTime?
createdAt DateTime @default(now())
@@index([videoId, severity])
@@index([ruleCode])
}
enum LintSeverity {
INFO
WARNING
ERROR
}
// ─── BULK JOBS ────────────────────────────────────────────
model BulkJob {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
type String
initiatedBy String
filterSnapshot Json
targetIds String[]
status BulkJobStatus @default(PENDING)
dryRunResult Json?
rollbackData Json?
totalCount Int
successCount Int @default(0)
errorCount Int @default(0)
items BulkJobItem[]
createdAt DateTime @default(now())
completedAt DateTime?
@@index([teamId])
}
enum BulkJobStatus {
PENDING
DRY_RUN
CONFIRMED
RUNNING
DONE
FAILED
ROLLED_BACK
}
model BulkJobItem {
id String @id @default(cuid())
bulkJobId String
bulkJob BulkJob @relation(fields: [bulkJobId], references: [id])
videoId String
video Video @relation(fields: [videoId], references: [id])
beforeSnapshot Json?
afterSnapshot Json?
status String @default("pending")
errorMessage String?
createdAt DateTime @default(now())
}
// ─── CAMPAIGNS ────────────────────────────────────────────
model Campaign {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
name String
startAt DateTime
endAt DateTime?
status String @default("active")
notes String?
blocks DescriptionBlock[]
@@index([teamId])
}
// ─── PLAYLISTS ────────────────────────────────────────────
model Playlist {
id String @id @default(cuid())
channelId String
channel Channel @relation(fields: [channelId], references: [id])
youtubePlaylistId String @unique
title String
description String?
itemCount Int @default(0)
privacyStatus String @default("public")
videos VideoPlaylist[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([channelId])
}
model VideoPlaylist {
videoId String
playlistId String
position Int?
video Video @relation(fields: [videoId], references: [id])
playlist Playlist @relation(fields: [playlistId], references: [id])
@@id([videoId, playlistId])
}
// ─── IMPORT / EXPORT JOBS ─────────────────────────────────
model ImportJob {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
type String
sourceName String
mappingJson Json?
validationReport Json?
commitStatus String @default("pending")
createdBy String
createdAt DateTime @default(now())
committedAt DateTime?
@@index([teamId])
}
model ExportJob {
id String @id @default(cuid())
teamId String
team Team @relation(fields: [teamId], references: [id])
type String
scopeJson Json
fileReference String?
createdBy String
createdAt DateTime @default(now())
@@index([teamId])
}
// ─── YOUTUBE QUOTA LOG ────────────────────────────────────
model QuotaLog {
id String @id @default(cuid())
datePt DateTime
units Int
operation String
entityId String?
channelId String?
channel Channel? @relation(fields: [channelId], references: [id])
videoId String?
video Video? @relation(fields: [videoId], references: [id])
bulkJobId String?
actionId String?
actionType String?
createdAt DateTime @default(now())
@@index([datePt])
@@index([channelId])
@@index([actionId])
}
// ─── AUDIT LOG ────────────────────────────────────────────
model AuditLog {
id String @id @default(cuid())
actorId String
entityType String
entityId String
action String
beforeJson Json?
afterJson Json?
requestId String?
createdAt DateTime @default(now())
@@index([entityType, entityId])
@@index([createdAt])
}