Files
ContentGen_BE/prisma/schema.prisma
Harun CAN 85c35c73e8
Some checks failed
Backend Deploy 🚀 / build-and-deploy (push) Has been cancelled
main
2026-03-29 12:43:49 +03:00

640 lines
17 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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")
}
// ============================================
// Core Models
// ============================================
model User {
id String @id @default(uuid())
email String @unique
password String
firstName String?
lastName String?
isActive Boolean @default(true)
// Core Relations
roles UserRole[]
refreshTokens RefreshToken[]
// Video SaaS Relations
projects Project[]
subscriptions Subscription[]
creditTransactions CreditTransaction[]
templateUsages TemplateUsage[]
notifications Notification[]
preferences UserPreference?
// Multi-tenancy (optional)
tenantId String?
tenant Tenant? @relation(fields: [tenantId], references: [id])
// Timestamps & Soft Delete
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@index([email])
@@index([tenantId])
}
model Role {
id String @id @default(uuid())
name String @unique
description String?
isSystem Boolean @default(false)
// Relations
users UserRole[]
permissions RolePermission[]
// Timestamps & Soft Delete
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@index([name])
}
model Permission {
id String @id @default(uuid())
name String @unique
description String?
resource String // e.g., "users", "posts"
action String // e.g., "create", "read", "update", "delete"
// Relations
roles RolePermission[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([resource, action])
@@index([resource])
}
// Many-to-many: User <-> Role
model UserRole {
id String @id @default(uuid())
userId String
roleId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@unique([userId, roleId])
@@index([userId])
@@index([roleId])
}
// Many-to-many: Role <-> Permission
model RolePermission {
id String @id @default(uuid())
roleId String
permissionId String
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())
@@unique([roleId, permissionId])
@@index([roleId])
@@index([permissionId])
}
// ============================================
// Authentication
// ============================================
model RefreshToken {
id String @id @default(uuid())
token String @unique
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
expiresAt DateTime
createdAt DateTime @default(now())
@@index([token])
@@index([userId])
}
// ============================================
// Multi-tenancy (Optional)
// ============================================
model Tenant {
id String @id @default(uuid())
name String
slug String @unique
isActive Boolean @default(true)
// Relations
users User[]
// Timestamps & Soft Delete
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@index([slug])
}
// ============================================
// i18n / Translations (Optional - DB driven)
// ============================================
model Translation {
id String @id @default(uuid())
key String
locale String // e.g., "en", "tr", "de"
value String
namespace String @default("common") // e.g., "common", "errors", "validation"
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([key, locale, namespace])
@@index([key])
@@index([locale])
@@index([namespace])
}
// ============================================
// Video SaaS — Enums
// ============================================
enum ProjectStatus {
DRAFT
GENERATING_SCRIPT
PENDING
GENERATING_MEDIA
RENDERING
COMPLETED
FAILED
}
enum AspectRatio {
PORTRAIT_9_16
SQUARE_1_1
LANDSCAPE_16_9
}
enum VideoStyle {
CINEMATIC
DOCUMENTARY
EDUCATIONAL
STORYTELLING
NEWS
PROMOTIONAL
ARTISTIC
MINIMALIST
}
enum MediaType {
VIDEO_CLIP
AUDIO_NARRATION
AUDIO_MUSIC
SUBTITLE
THUMBNAIL
FINAL_VIDEO
}
enum TransitionType {
CUT
FADE
DISSOLVE
SLIDE_LEFT
SLIDE_RIGHT
SLIDE_UP
ZOOM_IN
ZOOM_OUT
WIPE
}
enum RenderJobStatus {
QUEUED
PROCESSING
COMPLETED
FAILED
CANCELLED
}
enum RenderStage {
VIDEO_GENERATION
TTS_GENERATION
MUSIC_GENERATION
AMBIENT_GENERATION
MEDIA_MERGE
SUBTITLE_OVERLAY
FINALIZATION
UPLOAD
}
enum SourceType {
MANUAL
X_TWEET
YOUTUBE
}
// ============================================
// Video SaaS — Project & Scenes
// ============================================
model Project {
id String @id @default(uuid())
title String @db.VarChar(200)
description String? @db.VarChar(1000)
prompt String @db.VarChar(2000)
// AI Generated Script
scriptJson Json? // Gemini API raw JSON output
scriptVersion Int @default(0)
// Configuration
language String @default("tr") @db.VarChar(5) // ISO 639-1
aspectRatio AspectRatio @default(PORTRAIT_9_16)
videoStyle VideoStyle @default(CINEMATIC)
targetDuration Int @default(60) // saniye
// SEO & Social Content (skill-enhanced)
seoKeywords String[] // Hedeflenen SEO anahtar kelimeler
seoTitle String? @db.VarChar(200)
seoDescription String? @db.VarChar(500)
seoSchemaJson Json? // VideoObject structured data
socialContent Json? // { youtubeTitle, tiktokCaption, instagramCaption, twitterText }
referenceUrl String? @db.VarChar(500)
// İçerik Kaynağı
sourceType SourceType @default(MANUAL) // MANUAL, X_TWEET, YOUTUBE
sourceTweetData Json? // X/Twitter tweet verisi (id, author, metrics, media)
// Processing
status ProjectStatus @default(DRAFT)
progress Int @default(0) // 0-100
errorMessage String?
// Output
finalVideoUrl String?
thumbnailUrl String?
// Stats
creditsUsed Int @default(0)
viewCount Int @default(0)
// Template Support
isTemplate Boolean @default(false)
templateId String? // Hangi şablondan klonlandı?
template Template? @relation("ClonedFrom", fields: [templateId], references: [id])
// Relations
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
scenes Scene[]
mediaAssets MediaAsset[]
renderJobs RenderJob[]
templateEntry Template? @relation("SourceProject")
// Timestamps & Soft Delete
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
completedAt DateTime?
deletedAt DateTime?
@@index([userId])
@@index([status])
@@index([sourceType])
@@index([isTemplate])
@@index([createdAt])
}
model Scene {
id String @id @default(uuid())
order Int // Sahne sırası (1, 2, 3...)
title String? @db.VarChar(200)
// Content
narrationText String @db.Text // Hedef dildeki anlatım metni
visualPrompt String @db.Text // İngilizce — Higgsfield AI prompt
subtitleText String? @db.Text // Ekranda görünecek altyazı
// Timing
duration Float @default(5.0) // saniye
transitionType TransitionType @default(CUT)
// Relations
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
mediaAssets MediaAsset[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([projectId])
@@index([order])
}
// ============================================
// Video SaaS — Media & Render
// ============================================
model MediaAsset {
id String @id @default(uuid())
type MediaType
// Storage
s3Key String? // Cloudflare R2 / S3 object key
s3Bucket String? @db.VarChar(100)
url String? // Public CDN URL
// Metadata
fileName String? @db.VarChar(255)
mimeType String? @db.VarChar(100)
sizeBytes BigInt?
durationMs Int? // Medya süresi (video/audio için)
// AI Provider Info
aiProvider String? @db.VarChar(50) // higgsfield, elevenlabs, suno
aiJobId String? // Dış API job ID'si
// Relations
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
sceneId String? // null = proje genelinde (müzik, final vb.)
scene Scene? @relation(fields: [sceneId], references: [id], onDelete: SetNull)
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([projectId])
@@index([sceneId])
@@index([type])
}
model RenderJob {
id String @id @default(uuid())
status RenderJobStatus @default(QUEUED)
currentStage RenderStage?
// Queue Info
queueName String @default("video-generation") @db.VarChar(100)
bullJobId String? @db.VarChar(100) // BullMQ job ID
// Retry
attemptNumber Int @default(1)
maxAttempts Int @default(3)
// Processing
workerHostname String? @db.VarChar(100)
processingTimeMs Int? // Toplam render süresi
errorMessage String?
// Output
finalVideoUrl String?
finalVideoS3Key String?
// Relations
projectId String
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
logs RenderLog[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
startedAt DateTime?
completedAt DateTime?
@@index([projectId])
@@index([status])
@@index([bullJobId])
}
model RenderLog {
id String @id @default(uuid())
stage RenderStage
level String @default("info") @db.VarChar(10) // info, warn, error
message String @db.Text
durationMs Int? // Bu aşamanın süresi
metadata Json? // Ek JSON veri
// Relations
renderJobId String
renderJob RenderJob @relation(fields: [renderJobId], references: [id], onDelete: Cascade)
// Timestamps
createdAt DateTime @default(now())
@@index([renderJobId])
@@index([stage])
}
// ============================================
// Video SaaS — Template Marketplace
// ============================================
model Template {
id String @id @default(uuid())
// Display
title String @db.VarChar(200)
description String? @db.VarChar(500)
thumbnailUrl String?
previewVideoUrl String?
// Categorization
category String @default("general") @db.VarChar(50)
tags String[] // PostgreSQL array
language String @default("tr") @db.VarChar(5)
// Source
originalProjectId String @unique
originalProject Project @relation("SourceProject", fields: [originalProjectId], references: [id])
// Stats
usageCount Int @default(0)
rating Float @default(0)
ratingCount Int @default(0)
isFeatured Boolean @default(false)
isPublished Boolean @default(true)
// Relations
clonedProjects Project[] @relation("ClonedFrom")
usages TemplateUsage[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([category])
@@index([language])
@@index([isFeatured])
@@index([usageCount])
}
model TemplateUsage {
id String @id @default(uuid())
templateId String
template Template @relation(fields: [templateId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
clonedProjectId String? // Oluşturulan projenin ID'si
createdAt DateTime @default(now())
@@index([templateId])
@@index([userId])
}
// ============================================
// Video SaaS — Billing & Credits
// ============================================
model Plan {
id String @id @default(uuid())
name String @unique @db.VarChar(50) // free, pro, business
displayName String @db.VarChar(100)
description String? @db.VarChar(500)
// Pricing
monthlyPrice Int @default(0) // cent cinsinden (1900 = $19)
yearlyPrice Int? // Yıllık indirimli fiyat
currency String @default("usd") @db.VarChar(3)
// Limits
monthlyCredits Int @default(3)
maxDuration Int @default(30) // saniye
maxResolution String @default("720p") @db.VarChar(10)
maxProjects Int @default(5)
// Stripe
stripePriceId String? @db.VarChar(100)
stripeYearlyPriceId String? @db.VarChar(100)
// Features
features Json? // { "templates": true, "priorityQueue": false, ... }
isActive Boolean @default(true)
sortOrder Int @default(0)
// Relations
subscriptions Subscription[]
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([name])
@@index([isActive])
}
model Subscription {
id String @id @default(uuid())
status String @default("active") @db.VarChar(20) // active, canceled, past_due, trialing
// Stripe
stripeSubscriptionId String? @unique @db.VarChar(100)
stripeCustomerId String? @db.VarChar(100)
// Billing Cycle
currentPeriodStart DateTime?
currentPeriodEnd DateTime?
cancelAtPeriodEnd Boolean @default(false)
// Relations
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
planId String
plan Plan @relation(fields: [planId], references: [id])
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
canceledAt DateTime?
@@index([userId])
@@index([planId])
@@index([stripeSubscriptionId])
@@index([status])
}
model CreditTransaction {
id String @id @default(uuid())
amount Int // Pozitif: ekleme, Negatif: harcama
type String @db.VarChar(30) // grant, usage, refund, bonus
description String? @db.VarChar(200)
// Relations
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
projectId String? // Hangi projede harcandı
// Balance Snapshot
balanceAfter Int @default(0)
// Timestamps
createdAt DateTime @default(now())
@@index([userId])
@@index([type])
@@index([createdAt])
}
// ============================================
// Video SaaS — User Preferences & Notifications
// ============================================
model UserPreference {
id String @id @default(uuid())
// Defaults
defaultLanguage String @default("tr") @db.VarChar(5)
defaultVideoStyle VideoStyle @default(CINEMATIC)
defaultDuration Int @default(60)
// UI
theme String @default("dark") @db.VarChar(10)
emailNotifications Boolean @default(true)
pushNotifications Boolean @default(true)
// Relations
userId String @unique
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Timestamps
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
}
model Notification {
id String @id @default(uuid())
type String @db.VarChar(30) // render_complete, render_failed, credit_low, system
title String @db.VarChar(200)
message String? @db.Text
isRead Boolean @default(false)
metadata Json? // { projectId, renderJobId, ... }
// Relations
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Timestamps
createdAt DateTime @default(now())
readAt DateTime?
@@index([userId])
@@index([isRead])
@@index([createdAt])
}