generated from fahricansecer/boilerplate-be
2217 lines
55 KiB
Plaintext
2217 lines
55 KiB
Plaintext
// Content Hunter - Complete Database Schema v2.0
|
||
// Includes: Neuro Marketing, SEO, Source Accounts, Video/Thumbnail, Building Blocks
|
||
// Version: 2.0.0
|
||
|
||
generator client {
|
||
provider = "prisma-client-js"
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
}
|
||
|
||
// ============================================
|
||
// ENUMS
|
||
// ============================================
|
||
|
||
enum UserPlan {
|
||
FREE
|
||
STARTER
|
||
PRO
|
||
ULTIMATE
|
||
ENTERPRISE
|
||
}
|
||
|
||
enum ContentType {
|
||
BLOG
|
||
TWITTER
|
||
INSTAGRAM
|
||
LINKEDIN
|
||
FACEBOOK
|
||
TIKTOK
|
||
YOUTUBE
|
||
THREADS
|
||
PINTEREST
|
||
}
|
||
|
||
enum MasterContentType {
|
||
BLOG
|
||
NEWSLETTER
|
||
PODCAST_SCRIPT
|
||
VIDEO_SCRIPT
|
||
THREAD
|
||
}
|
||
|
||
enum ContentStatus {
|
||
DRAFT
|
||
REVIEW
|
||
APPROVED
|
||
SCHEDULED
|
||
PUBLISHED
|
||
FAILED
|
||
}
|
||
|
||
enum TrendSource {
|
||
GOOGLE_TRENDS
|
||
TWITTER
|
||
REDDIT
|
||
NEWSAPI
|
||
RSS
|
||
YOUTUBE
|
||
CUSTOM
|
||
}
|
||
|
||
enum TrendStatus {
|
||
NEW
|
||
REVIEWED
|
||
SELECTED
|
||
DISMISSED
|
||
EXPIRED
|
||
}
|
||
|
||
enum MediaType {
|
||
IMAGE
|
||
VIDEO
|
||
GIF
|
||
AUDIO
|
||
}
|
||
|
||
enum SocialPlatform {
|
||
TWITTER
|
||
INSTAGRAM
|
||
LINKEDIN
|
||
FACEBOOK
|
||
TIKTOK
|
||
YOUTUBE
|
||
THREADS
|
||
PINTEREST
|
||
}
|
||
|
||
enum AuthMethod {
|
||
OAUTH
|
||
CREDENTIALS
|
||
}
|
||
|
||
enum ScheduleStatus {
|
||
SCHEDULED
|
||
PUBLISHING
|
||
PUBLISHED
|
||
FAILED
|
||
CANCELLED
|
||
}
|
||
|
||
enum WorkspaceRole {
|
||
OWNER
|
||
ADMIN
|
||
EDITOR
|
||
VIEWER
|
||
}
|
||
|
||
enum ApprovalStatus {
|
||
PENDING
|
||
APPROVED
|
||
REJECTED
|
||
}
|
||
|
||
enum CreditTransactionType {
|
||
PURCHASE
|
||
SPEND
|
||
REFUND
|
||
BONUS
|
||
RESET
|
||
ADMIN_ADJUST
|
||
}
|
||
|
||
enum CreditCategory {
|
||
TREND_SCAN
|
||
DEEP_RESEARCH
|
||
MASTER_CONTENT
|
||
BUILDING_BLOCKS
|
||
PLATFORM_CONTENT
|
||
IMAGE_GENERATION
|
||
VIDEO_SCRIPT
|
||
THUMBNAIL
|
||
SEO_OPTIMIZATION
|
||
NEURO_ANALYSIS
|
||
SOURCE_ANALYSIS
|
||
AUTO_PUBLISH
|
||
}
|
||
|
||
enum BuildingBlockType {
|
||
HOOK
|
||
PAIN_POINT
|
||
PARADOX
|
||
QUOTE
|
||
STATISTIC
|
||
TRANSFORMATION_ARC
|
||
OBJECTION_HANDLER
|
||
CTA
|
||
METAPHOR
|
||
STORY
|
||
INSIGHT
|
||
}
|
||
|
||
enum WritingStyleType {
|
||
PATIENT_OBSERVER
|
||
HUSTLER_ACHIEVER
|
||
CONTRARIAN_THINKER
|
||
CUSTOM
|
||
}
|
||
|
||
enum PsychologyTriggerCategory {
|
||
CURIOSITY
|
||
SOCIAL_PROOF
|
||
SCARCITY
|
||
URGENCY
|
||
AUTHORITY
|
||
RECIPROCITY
|
||
LOSS_AVERSION
|
||
PATTERN_INTERRUPT
|
||
EMOTIONAL_RESONANCE
|
||
CONTROVERSY
|
||
}
|
||
|
||
enum ContentCategoryType {
|
||
PROVEN
|
||
EXPERIMENT
|
||
}
|
||
|
||
enum SourceType {
|
||
ARTICLE
|
||
STUDY
|
||
REPORT
|
||
SOCIAL_POST
|
||
VIDEO
|
||
PODCAST
|
||
BOOK
|
||
OFFICIAL_STATEMENT
|
||
RESEARCH_PAPER
|
||
NEWS
|
||
CUSTOM
|
||
}
|
||
|
||
enum VerificationLevel {
|
||
VERIFIED // 100% kaynak doğrulamalı
|
||
OPINION // Yazarın görüşü
|
||
INSPIRED // Kaynak içerikten ilham alınmış
|
||
}
|
||
|
||
enum InstagramFormat {
|
||
STATIC_IMAGE
|
||
CAROUSEL
|
||
REEL
|
||
STORY
|
||
}
|
||
|
||
// İçerik üretim dilleri (10 dil desteği)
|
||
enum ContentLanguage {
|
||
EN // English
|
||
TR // Turkish
|
||
ES // Spanish
|
||
FR // French
|
||
DE // German
|
||
ZH // Chinese (Mandarin)
|
||
PT // Portuguese
|
||
AR // Arabic
|
||
RU // Russian
|
||
JA // Japanese
|
||
}
|
||
|
||
// ============================================
|
||
// USER & AUTHENTICATION
|
||
// ============================================
|
||
|
||
model User {
|
||
id String @id @default(uuid())
|
||
email String @unique
|
||
password String
|
||
firstName String?
|
||
lastName String?
|
||
avatar String?
|
||
isActive Boolean @default(true)
|
||
emailVerified Boolean @default(false)
|
||
|
||
// Subscription & Credits
|
||
plan UserPlan @default(FREE)
|
||
credits Int @default(50)
|
||
creditsResetAt DateTime?
|
||
subscription Subscription?
|
||
stripeCustomerId String? @unique
|
||
|
||
// Settings
|
||
timezone String @default("UTC")
|
||
language String @default("en")
|
||
|
||
// Relations
|
||
roles UserRole[]
|
||
refreshTokens RefreshToken[]
|
||
brandVoice BrandVoice?
|
||
writingStyles WritingStyle[]
|
||
workspaces WorkspaceMember[]
|
||
ownedWorkspaces Workspace[] @relation("WorkspaceOwner")
|
||
niches Niche[]
|
||
masterContents MasterContent[]
|
||
contents Content[]
|
||
socialAccounts SocialAccount[]
|
||
sourceAccounts SourceAccount[]
|
||
templates Template[]
|
||
assets Asset[]
|
||
media Media[]
|
||
scheduledPosts ScheduledPost[]
|
||
apiKeys ApiKey[]
|
||
creditTransactions CreditTransaction[]
|
||
deepResearches DeepResearch[]
|
||
contentCalendars ContentCalendar[]
|
||
videoContents VideoContent[]
|
||
workflowTemplates WorkflowTemplate[]
|
||
contentSessions ContentSession[]
|
||
|
||
// Approval workflow
|
||
approvalRequests ContentApproval[] @relation("ApprovalRequests")
|
||
approvalReviews ContentApproval[] @relation("ApprovalReviews")
|
||
|
||
// Multi-tenancy
|
||
tenantId String?
|
||
tenant Tenant? @relation(fields: [tenantId], references: [id])
|
||
|
||
// Timestamps
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
deletedAt DateTime?
|
||
lastLoginAt DateTime?
|
||
|
||
@@index([email])
|
||
@@index([tenantId])
|
||
@@index([plan])
|
||
}
|
||
|
||
model Role {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
description String?
|
||
isSystem Boolean @default(false)
|
||
|
||
users UserRole[]
|
||
permissions RolePermission[]
|
||
|
||
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
|
||
action String
|
||
|
||
roles RolePermission[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([resource, action])
|
||
@@index([resource])
|
||
}
|
||
|
||
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])
|
||
}
|
||
|
||
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])
|
||
}
|
||
|
||
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
|
||
// ============================================
|
||
|
||
model Tenant {
|
||
id String @id @default(uuid())
|
||
name String
|
||
slug String @unique
|
||
domain String? @unique
|
||
logo String?
|
||
isActive Boolean @default(true)
|
||
|
||
brandColor String?
|
||
customCss String? @db.Text
|
||
|
||
users User[]
|
||
workspaces Workspace[]
|
||
translations Translation[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
deletedAt DateTime?
|
||
|
||
@@index([slug])
|
||
@@index([domain])
|
||
}
|
||
|
||
model Translation {
|
||
id String @id @default(uuid())
|
||
key String
|
||
locale String
|
||
value String
|
||
namespace String @default("common")
|
||
|
||
tenantId String?
|
||
tenant Tenant? @relation(fields: [tenantId], references: [id])
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([key, locale, namespace, tenantId])
|
||
@@index([key])
|
||
@@index([locale])
|
||
}
|
||
|
||
// ============================================
|
||
// CREDITS & BILLING
|
||
// ============================================
|
||
|
||
model SubscriptionPlan {
|
||
id String @id @default(uuid())
|
||
name UserPlan @unique
|
||
displayName String
|
||
description String?
|
||
|
||
monthlyPrice Decimal @default(0) @db.Decimal(10, 2)
|
||
yearlyPrice Decimal @default(0) @db.Decimal(10, 2)
|
||
currency String @default("USD")
|
||
|
||
stripeMonthlyPriceId String?
|
||
stripeYearlyPriceId String?
|
||
|
||
monthlyCredits Int @default(50)
|
||
maxWorkspaces Int @default(1)
|
||
maxTeamMembers Int @default(1)
|
||
maxNiches Int @default(1)
|
||
maxTemplates Int @default(5)
|
||
maxScheduledPosts Int @default(10)
|
||
maxSocialAccounts Int @default(2)
|
||
maxSourceAccounts Int @default(5)
|
||
maxStorageMb Int @default(100)
|
||
|
||
features Json @default("{}")
|
||
|
||
isActive Boolean @default(true)
|
||
sortOrder Int @default(0)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
subscriptions Subscription[]
|
||
}
|
||
|
||
model Subscription {
|
||
id String @id @default(uuid())
|
||
userId String @unique
|
||
user User @relation(fields: [userId], references: [id])
|
||
planId String
|
||
plan SubscriptionPlan @relation(fields: [planId], references: [id])
|
||
|
||
stripeSubscriptionId String? @unique
|
||
status String
|
||
|
||
currentPeriodStart DateTime
|
||
currentPeriodEnd DateTime
|
||
cancelAtPeriodEnd Boolean @default(false)
|
||
canceledAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([userId])
|
||
@@index([status])
|
||
@@index([stripeSubscriptionId])
|
||
}
|
||
|
||
model CreditTransaction {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
amount Int
|
||
balanceAfter Int
|
||
type CreditTransactionType
|
||
category CreditCategory?
|
||
|
||
description String?
|
||
referenceId String?
|
||
referenceType String?
|
||
|
||
adminUserId String?
|
||
adminNote String?
|
||
metadata Json?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([userId])
|
||
@@index([type])
|
||
@@index([category])
|
||
@@index([createdAt])
|
||
}
|
||
|
||
// ============================================
|
||
// BRAND VOICE & WRITING STYLES
|
||
// ============================================
|
||
|
||
model BrandVoice {
|
||
id String @id @default(uuid())
|
||
userId String @unique
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
name String?
|
||
description String? @db.Text
|
||
|
||
tone String[] @default([])
|
||
vocabulary String[] @default([])
|
||
avoidWords String[] @default([])
|
||
sampleContent String? @db.Text
|
||
|
||
aiProfile Json?
|
||
aiProfileVersion String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
model WritingStyle {
|
||
id String @id @default(uuid())
|
||
userId String?
|
||
user User? @relation(fields: [userId], references: [id])
|
||
|
||
type WritingStyleType
|
||
name String
|
||
|
||
traits String[] @default([])
|
||
tone String?
|
||
vocabulary String[] @default([])
|
||
avoidWords String[] @default([])
|
||
|
||
examples String? @db.Text
|
||
bestFor String[] @default([])
|
||
|
||
// Advanced style attributes
|
||
sentenceLength String?
|
||
emojiUsage String?
|
||
hashtagStyle String?
|
||
structurePreference String?
|
||
engagementStyle String?
|
||
signatureElements String[] @default([])
|
||
preferredPhrases String[] @default([])
|
||
|
||
isDefault Boolean @default(false)
|
||
isSystem Boolean @default(false)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
masterContents MasterContent[]
|
||
|
||
@@index([userId])
|
||
@@index([type])
|
||
}
|
||
|
||
// ============================================
|
||
// WORKSPACE & COLLABORATION
|
||
// ============================================
|
||
|
||
model Workspace {
|
||
id String @id @default(uuid())
|
||
name String
|
||
slug String @unique
|
||
description String?
|
||
logo String?
|
||
isActive Boolean @default(true)
|
||
|
||
tenantId String?
|
||
tenant Tenant? @relation(fields: [tenantId], references: [id])
|
||
|
||
ownerId String
|
||
owner User @relation("WorkspaceOwner", fields: [ownerId], references: [id])
|
||
|
||
settings Json @default("{}")
|
||
requireApproval Boolean @default(false)
|
||
|
||
members WorkspaceMember[]
|
||
niches Niche[]
|
||
masterContents MasterContent[]
|
||
contents Content[]
|
||
templates Template[]
|
||
socialAccounts SocialAccount[]
|
||
sourceAccounts SourceAccount[]
|
||
approvalWorkflows ApprovalWorkflow[]
|
||
calendars ContentCalendar[]
|
||
webhooks Webhook[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
deletedAt DateTime?
|
||
|
||
@@index([slug])
|
||
@@index([ownerId])
|
||
}
|
||
|
||
model WorkspaceMember {
|
||
id String @id @default(uuid())
|
||
workspaceId String
|
||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
role WorkspaceRole @default(VIEWER)
|
||
permissions Json?
|
||
|
||
joinedAt DateTime @default(now())
|
||
|
||
@@unique([workspaceId, userId])
|
||
@@index([workspaceId])
|
||
@@index([userId])
|
||
}
|
||
|
||
model ApprovalWorkflow {
|
||
id String @id @default(uuid())
|
||
workspaceId String
|
||
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
||
|
||
name String
|
||
description String?
|
||
isActive Boolean @default(true)
|
||
isDefault Boolean @default(false)
|
||
|
||
steps ApprovalStep[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([workspaceId])
|
||
}
|
||
|
||
model ApprovalStep {
|
||
id String @id @default(uuid())
|
||
workflowId String
|
||
workflow ApprovalWorkflow @relation(fields: [workflowId], references: [id], onDelete: Cascade)
|
||
|
||
order Int
|
||
name String?
|
||
approverRole WorkspaceRole?
|
||
approverUserId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([workflowId])
|
||
}
|
||
|
||
model ContentApproval {
|
||
id String @id @default(uuid())
|
||
contentId String
|
||
content Content @relation(fields: [contentId], references: [id], onDelete: Cascade)
|
||
|
||
// Workflow step
|
||
stepOrder Int @default(0)
|
||
status ApprovalStatus @default(PENDING)
|
||
|
||
// Requester
|
||
requestedById String
|
||
requestedBy User @relation("ApprovalRequests", fields: [requestedById], references: [id])
|
||
notes String? @db.Text
|
||
|
||
// Reviewer
|
||
reviewedById String?
|
||
reviewedBy User? @relation("ApprovalReviews", fields: [reviewedById], references: [id])
|
||
reviewedAt DateTime?
|
||
feedback String? @db.Text
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([contentId])
|
||
@@index([status])
|
||
@@index([requestedById])
|
||
}
|
||
|
||
// ============================================
|
||
// NICHE & TRENDS
|
||
// ============================================
|
||
|
||
model Niche {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
workspaceId String?
|
||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||
|
||
name String
|
||
description String? @db.Text
|
||
keywords String[] @default([])
|
||
|
||
sources NicheSource[]
|
||
|
||
scanFrequency String @default("daily")
|
||
autoScan Boolean @default(false)
|
||
autoScanCron String?
|
||
lastScannedAt DateTime?
|
||
|
||
trends Trend[]
|
||
masterContents MasterContent[]
|
||
contents Content[]
|
||
seoKeywords SeoKeyword[]
|
||
|
||
isActive Boolean @default(true)
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
deletedAt DateTime?
|
||
|
||
@@index([userId])
|
||
@@index([workspaceId])
|
||
}
|
||
|
||
model NicheSource {
|
||
id String @id @default(uuid())
|
||
nicheId String
|
||
niche Niche @relation(fields: [nicheId], references: [id], onDelete: Cascade)
|
||
|
||
type String
|
||
url String
|
||
name String?
|
||
|
||
isActive Boolean @default(true)
|
||
lastFetchedAt DateTime?
|
||
lastError String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([nicheId])
|
||
}
|
||
|
||
model Trend {
|
||
id String @id @default(uuid())
|
||
nicheId String
|
||
niche Niche @relation(fields: [nicheId], references: [id], onDelete: Cascade)
|
||
|
||
title String
|
||
description String? @db.Text
|
||
|
||
source TrendSource
|
||
sourceUrl String?
|
||
sourceData Json?
|
||
|
||
score Float @default(0)
|
||
velocity Float?
|
||
volume Int?
|
||
|
||
keywords String[] @default([])
|
||
relatedTopics String[] @default([])
|
||
sentiment String?
|
||
|
||
status TrendStatus @default(NEW)
|
||
|
||
masterContents MasterContent[]
|
||
contents Content[]
|
||
scanId String?
|
||
scan TrendScan? @relation(fields: [scanId], references: [id])
|
||
|
||
discoveredAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
expiresAt DateTime?
|
||
|
||
@@index([nicheId])
|
||
@@index([source])
|
||
@@index([score])
|
||
@@index([status])
|
||
@@index([discoveredAt])
|
||
@@unique([nicheId, title])
|
||
}
|
||
|
||
model TrendScan {
|
||
id String @id @default(uuid())
|
||
nicheId String
|
||
|
||
status String @default("pending")
|
||
|
||
trendsFound Int @default(0)
|
||
newTrends Int @default(0)
|
||
|
||
sources String[] @default([])
|
||
errors Json?
|
||
|
||
creditsUsed Int @default(0)
|
||
|
||
trends Trend[]
|
||
|
||
startedAt DateTime?
|
||
completedAt DateTime?
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([nicheId])
|
||
@@index([status])
|
||
@@index([createdAt])
|
||
}
|
||
|
||
// ============================================
|
||
// NEURO MARKETING & PSYCHOLOGY
|
||
// ============================================
|
||
|
||
model PsychologyTrigger {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
category PsychologyTriggerCategory
|
||
description String @db.Text
|
||
|
||
examples String[] @default([])
|
||
bestFor String[] @default([])
|
||
|
||
// Templates/phrases for this trigger
|
||
templates String[] @default([])
|
||
|
||
usageCount Int @default(0)
|
||
avgEngagement Float?
|
||
|
||
isSystem Boolean @default(true)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([category])
|
||
}
|
||
|
||
model EmotionalHook {
|
||
id String @id @default(uuid())
|
||
emotion String // curiosity, fear, excitement, anger, joy
|
||
hookType String // question, statement, story, statistic
|
||
template String @db.Text
|
||
|
||
examples String[] @default([])
|
||
|
||
usageCount Int @default(0)
|
||
avgEngagement Float?
|
||
|
||
isSystem Boolean @default(true)
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([emotion])
|
||
@@index([hookType])
|
||
}
|
||
|
||
model ContentPsychology {
|
||
id String @id @default(uuid())
|
||
contentId String @unique
|
||
content Content @relation(fields: [contentId], references: [id], onDelete: Cascade)
|
||
|
||
triggersUsed String[] @default([])
|
||
hookType String?
|
||
emotionalTone String?
|
||
|
||
// Scoring
|
||
engagementScore Float?
|
||
viralPotential Float?
|
||
controversyLevel Int? // 1-10
|
||
|
||
// AI analysis
|
||
aiAnalysis Json?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
// ============================================
|
||
// SEO INTELLIGENCE
|
||
// ============================================
|
||
|
||
model SeoKeyword {
|
||
id String @id @default(uuid())
|
||
keyword String
|
||
nicheId String?
|
||
niche Niche? @relation(fields: [nicheId], references: [id])
|
||
|
||
searchVolume Int?
|
||
difficulty Float?
|
||
cpc Float?
|
||
trend String?
|
||
|
||
relatedKeywords String[] @default([])
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([keyword, nicheId])
|
||
@@index([nicheId])
|
||
@@index([keyword])
|
||
}
|
||
|
||
model ContentSeo {
|
||
id String @id @default(uuid())
|
||
contentId String @unique
|
||
content Content @relation(fields: [contentId], references: [id], onDelete: Cascade)
|
||
|
||
primaryKeyword String?
|
||
secondaryKeywords String[] @default([])
|
||
keywordDensity Float?
|
||
|
||
metaTitle String?
|
||
metaDescription String?
|
||
slugSuggestion String?
|
||
|
||
seoScore Int?
|
||
readabilityScore Float?
|
||
|
||
improvements Json?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
}
|
||
|
||
// ============================================
|
||
// SOURCE ACCOUNTS & INSPIRATION
|
||
// ============================================
|
||
|
||
model SourceAccount {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
workspaceId String?
|
||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||
|
||
platform SocialPlatform
|
||
username String
|
||
displayName String?
|
||
profileUrl String
|
||
bio String? @db.Text
|
||
|
||
followersCount Int?
|
||
avgEngagement Float?
|
||
|
||
posts SourcePost[]
|
||
|
||
isActive Boolean @default(true)
|
||
lastFetchedAt DateTime?
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([userId, platform, username])
|
||
@@index([userId])
|
||
@@index([workspaceId])
|
||
@@index([platform])
|
||
}
|
||
|
||
model SourcePost {
|
||
id String @id @default(uuid())
|
||
accountId String
|
||
account SourceAccount @relation(fields: [accountId], references: [id], onDelete: Cascade)
|
||
|
||
platformPostId String
|
||
content String @db.Text
|
||
mediaUrls String[] @default([])
|
||
|
||
likes Int?
|
||
comments Int?
|
||
shares Int?
|
||
engagementRate Float?
|
||
|
||
analysis SourcePostAnalysis?
|
||
|
||
postedAt DateTime?
|
||
fetchedAt DateTime @default(now())
|
||
|
||
@@unique([accountId, platformPostId])
|
||
@@index([accountId])
|
||
@@index([engagementRate])
|
||
}
|
||
|
||
model SourcePostAnalysis {
|
||
id String @id @default(uuid())
|
||
postId String @unique
|
||
post SourcePost @relation(fields: [postId], references: [id], onDelete: Cascade)
|
||
|
||
// Extracted elements (Viral Post Breakdown)
|
||
hook String? @db.Text
|
||
pain String? @db.Text
|
||
payoff String? @db.Text
|
||
cta String? @db.Text
|
||
mainIdea String? @db.Text
|
||
|
||
psychologyTriggers String[] @default([])
|
||
structure Json?
|
||
|
||
// Reusable skeleton
|
||
structureSkeleton Json?
|
||
|
||
// Generated original content (plagiarism-free)
|
||
inspiredContent String? @db.Text
|
||
|
||
isProcessed Boolean @default(false)
|
||
processedAt DateTime?
|
||
|
||
createdAt DateTime @default(now())
|
||
}
|
||
|
||
model ViralPostAnalysis {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
|
||
originalPost String @db.Text
|
||
sourceUrl String?
|
||
platform SocialPlatform
|
||
engagementCount Int?
|
||
|
||
// Analysis results
|
||
hook String? @db.Text
|
||
pain String? @db.Text
|
||
payoff String? @db.Text
|
||
cta String? @db.Text
|
||
psychologyTriggers String[] @default([])
|
||
|
||
// Reusable template
|
||
structureSkeleton Json?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([userId])
|
||
@@index([platform])
|
||
}
|
||
|
||
// ============================================
|
||
// MASTER CONTENT & BUILDING BLOCKS
|
||
// ============================================
|
||
|
||
model MasterContent {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
workspaceId String?
|
||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||
nicheId String?
|
||
niche Niche? @relation(fields: [nicheId], references: [id])
|
||
trendId String?
|
||
trend Trend? @relation(fields: [trendId], references: [id])
|
||
|
||
type MasterContentType
|
||
title String
|
||
body String @db.Text
|
||
summary String? @db.Text
|
||
|
||
// Writing style used
|
||
writingStyleId String?
|
||
writingStyle WritingStyle? @relation(fields: [writingStyleId], references: [id])
|
||
|
||
// Research reference
|
||
researchNotes String? @db.Text
|
||
targetAudience String? @db.Text
|
||
|
||
outline String[] @default([])
|
||
status ContentStatus @default(DRAFT)
|
||
|
||
// Building blocks (denormalized for easy access)
|
||
hooks String[] @default([])
|
||
painPoints String[] @default([])
|
||
paradoxes String[] @default([])
|
||
quotes String[] @default([])
|
||
|
||
// Building blocks extracted
|
||
buildingBlocks BuildingBlock[]
|
||
|
||
// Derivatives generated
|
||
contents Content[]
|
||
|
||
// Video content if applicable
|
||
videoContent VideoContent?
|
||
|
||
// Research reference
|
||
researchId String?
|
||
research DeepResearch? @relation(fields: [researchId], references: [id])
|
||
|
||
// Credits
|
||
creditsUsed Int @default(0)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([userId])
|
||
@@index([workspaceId])
|
||
@@index([type])
|
||
}
|
||
|
||
model BuildingBlock {
|
||
id String @id @default(uuid())
|
||
masterContentId String
|
||
masterContent MasterContent @relation(fields: [masterContentId], references: [id], onDelete: Cascade)
|
||
|
||
type BuildingBlockType
|
||
content String @db.Text
|
||
|
||
// Scoring
|
||
engagementPotential Float?
|
||
|
||
// Usage tracking
|
||
isSelected Boolean @default(false)
|
||
usedInContentIds String[] @default([])
|
||
usageCount Int @default(0)
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([masterContentId])
|
||
@@index([type])
|
||
}
|
||
|
||
// ============================================
|
||
// TWO-PHASE CONTENT CREATION
|
||
// ============================================
|
||
|
||
model ContentSession {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
phase String @default("context_gathering") // context_gathering, content_generation
|
||
|
||
// Phase 1: Context Gathering
|
||
targetAudience String? @db.Text
|
||
keyTakeaway String? @db.Text
|
||
personalStories String? @db.Text
|
||
emotionToEvoke String?
|
||
beliefToChallenge String? @db.Text
|
||
actionToInspire String? @db.Text
|
||
|
||
// Additional context
|
||
additionalContext Json?
|
||
|
||
// Phase 2: Generated variations
|
||
variations ContentVariation[]
|
||
|
||
// Selected content
|
||
selectedVariationId String?
|
||
|
||
// Final content
|
||
contentId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([userId])
|
||
@@index([phase])
|
||
}
|
||
|
||
model ContentVariation {
|
||
id String @id @default(uuid())
|
||
sessionId String
|
||
session ContentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
||
|
||
order Int @default(0)
|
||
content String @db.Text
|
||
|
||
// Scoring
|
||
aiScore Float?
|
||
|
||
isSelected Boolean @default(false)
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([sessionId])
|
||
}
|
||
|
||
// ============================================
|
||
// CONTENT
|
||
// ============================================
|
||
|
||
model Content {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
workspaceId String?
|
||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||
nicheId String?
|
||
niche Niche? @relation(fields: [nicheId], references: [id])
|
||
trendId String?
|
||
trend Trend? @relation(fields: [trendId], references: [id])
|
||
masterContentId String?
|
||
masterContent MasterContent? @relation(fields: [masterContentId], references: [id])
|
||
|
||
type ContentType
|
||
|
||
title String?
|
||
body String @db.Text
|
||
summary String? @db.Text
|
||
|
||
htmlBody String? @db.Text
|
||
markdownBody String? @db.Text
|
||
|
||
hashtags String[] @default([])
|
||
keywords String[] @default([])
|
||
|
||
// Content category for growth formula
|
||
category ContentCategoryType @default(EXPERIMENT)
|
||
goldPostId String? // If spin-off, reference to gold post
|
||
|
||
// AI metadata
|
||
aiModel String?
|
||
aiPrompt String? @db.Text
|
||
creditsUsed Int @default(0)
|
||
|
||
// Çoklu dil desteği
|
||
sourceLanguage ContentLanguage? // Kaynak içeriğin dili (prompt veya referans)
|
||
targetLanguage ContentLanguage @default(EN) // Üretilen içeriğin dili
|
||
|
||
status ContentStatus @default(DRAFT)
|
||
|
||
// Relations
|
||
citations Citation[]
|
||
variants ContentVariant[]
|
||
media Media[]
|
||
scheduledPosts ScheduledPost[]
|
||
approvals ContentApproval[]
|
||
analytics ContentAnalytics[]
|
||
|
||
// SEO & Psychology
|
||
seoData ContentSeo?
|
||
psychology ContentPsychology?
|
||
|
||
// A/B Testing
|
||
abTestId String?
|
||
abTest ABTest? @relation(fields: [abTestId], references: [id])
|
||
|
||
// Research reference
|
||
researchId String?
|
||
research DeepResearch? @relation(fields: [researchId], references: [id])
|
||
|
||
// CORE RULE: Source Verification
|
||
isSourceVerified Boolean @default(false)
|
||
verificationLevel VerificationLevel @default(VERIFIED)
|
||
sources ContentSource[] // Mandatory sources for verified content
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
publishedAt DateTime?
|
||
scheduledAt DateTime?
|
||
publishedUrl String?
|
||
|
||
@@index([userId])
|
||
@@index([workspaceId])
|
||
@@index([nicheId])
|
||
@@index([trendId])
|
||
@@index([masterContentId])
|
||
@@index([type])
|
||
@@index([status])
|
||
@@index([category])
|
||
@@index([isSourceVerified])
|
||
}
|
||
|
||
model ContentVariant {
|
||
id String @id @default(uuid())
|
||
contentId String
|
||
content Content @relation(fields: [contentId], references: [id], onDelete: Cascade)
|
||
|
||
platform SocialPlatform
|
||
|
||
text String @db.Text
|
||
hashtags String[] @default([])
|
||
mentions String[] @default([])
|
||
|
||
characterCount Int?
|
||
|
||
// Metrics
|
||
impressions Int @default(0)
|
||
clicks Int @default(0)
|
||
engagements Int @default(0)
|
||
shares Int @default(0)
|
||
conversions Int @default(0)
|
||
|
||
// Status
|
||
name String? // Variant name for A/B testing
|
||
isActive Boolean @default(true)
|
||
isWinner Boolean @default(false)
|
||
|
||
media Media[]
|
||
scheduledPosts ScheduledPost[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([contentId])
|
||
@@index([platform])
|
||
}
|
||
|
||
model Citation {
|
||
id String @id @default(uuid())
|
||
contentId String
|
||
content Content @relation(fields: [contentId], references: [id], onDelete: Cascade)
|
||
|
||
title String
|
||
url String
|
||
author String?
|
||
source String?
|
||
publishedDate DateTime?
|
||
excerpt String? @db.Text
|
||
|
||
order Int @default(0)
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([contentId])
|
||
}
|
||
|
||
model DeepResearch {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
topic String
|
||
query String @db.Text
|
||
nicheId String?
|
||
trendId String?
|
||
|
||
sources Json?
|
||
summary String? @db.Text
|
||
keyFindings Json?
|
||
outline Json?
|
||
|
||
status String @default("pending")
|
||
error String?
|
||
|
||
creditsUsed Int @default(0)
|
||
|
||
masterContents MasterContent[]
|
||
contents Content[]
|
||
|
||
startedAt DateTime?
|
||
completedAt DateTime?
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([userId])
|
||
@@index([status])
|
||
}
|
||
|
||
// ============================================
|
||
// VIDEO & THUMBNAIL
|
||
// ============================================
|
||
|
||
model VideoContent {
|
||
id String @id @default(uuid())
|
||
masterContentId String? @unique
|
||
masterContent MasterContent? @relation(fields: [masterContentId], references: [id])
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
title String
|
||
description String? @db.Text
|
||
script String @db.Text
|
||
duration Int?
|
||
|
||
// SEO optimized
|
||
seoTitle String?
|
||
seoDescription String?
|
||
tags String[] @default([])
|
||
|
||
// Timestamps/chapters
|
||
timestamps Json?
|
||
|
||
// Thumbnails
|
||
thumbnails VideoThumbnail[]
|
||
|
||
creditsUsed Int @default(0)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([userId])
|
||
@@index([masterContentId])
|
||
}
|
||
|
||
model VideoThumbnail {
|
||
id String @id @default(uuid())
|
||
videoId String
|
||
video VideoContent @relation(fields: [videoId], references: [id], onDelete: Cascade)
|
||
|
||
headline String?
|
||
subheadline String?
|
||
style String?
|
||
|
||
// Neuro optimization
|
||
emotionalTrigger String?
|
||
colorScheme String?
|
||
faceExpression String?
|
||
|
||
// Generated image
|
||
imageUrl String?
|
||
prompt String? @db.Text
|
||
|
||
isSelected Boolean @default(false)
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([videoId])
|
||
}
|
||
|
||
// ============================================
|
||
// MEDIA & TEMPLATES
|
||
// ============================================
|
||
|
||
model Media {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
type MediaType
|
||
filename String
|
||
originalFilename String?
|
||
mimeType String
|
||
size Int
|
||
|
||
storagePath String
|
||
publicUrl String?
|
||
thumbnailUrl String?
|
||
|
||
width Int?
|
||
height Int?
|
||
duration Int?
|
||
|
||
isAiGenerated Boolean @default(false)
|
||
aiModel String?
|
||
aiPrompt String? @db.Text
|
||
creditsUsed Int @default(0)
|
||
|
||
contentId String?
|
||
content Content? @relation(fields: [contentId], references: [id])
|
||
variantId String?
|
||
variant ContentVariant? @relation(fields: [variantId], references: [id])
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([userId])
|
||
@@index([contentId])
|
||
@@index([type])
|
||
}
|
||
|
||
model Template {
|
||
id String @id @default(uuid())
|
||
userId String?
|
||
user User? @relation(fields: [userId], references: [id])
|
||
workspaceId String?
|
||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||
|
||
name String
|
||
description String?
|
||
platform SocialPlatform?
|
||
type String?
|
||
|
||
width Int
|
||
height Int
|
||
|
||
layers TemplateLayer[]
|
||
|
||
presetData Json?
|
||
|
||
thumbnailUrl String?
|
||
|
||
isPublic Boolean @default(false)
|
||
isSystem Boolean @default(false)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
deletedAt DateTime?
|
||
|
||
@@index([userId])
|
||
@@index([workspaceId])
|
||
@@index([platform])
|
||
@@index([isSystem])
|
||
}
|
||
|
||
model TemplateLayer {
|
||
id String @id @default(uuid())
|
||
templateId String
|
||
template Template @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
||
|
||
name String
|
||
type String
|
||
order Int
|
||
|
||
x Float @default(0)
|
||
y Float @default(0)
|
||
width Float
|
||
height Float
|
||
rotation Float @default(0)
|
||
|
||
content Json?
|
||
style Json?
|
||
|
||
assetId String?
|
||
asset Asset? @relation(fields: [assetId], references: [id])
|
||
|
||
isLocked Boolean @default(false)
|
||
isVisible Boolean @default(true)
|
||
|
||
@@index([templateId])
|
||
}
|
||
|
||
model Asset {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
name String
|
||
type String
|
||
|
||
filename String
|
||
mimeType String
|
||
size Int
|
||
storagePath String
|
||
publicUrl String?
|
||
|
||
width Int?
|
||
height Int?
|
||
|
||
layers TemplateLayer[]
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([userId])
|
||
@@index([type])
|
||
}
|
||
|
||
// ============================================
|
||
// SOCIAL MEDIA & PUBLISHING
|
||
// ============================================
|
||
|
||
model SocialAccount {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
workspaceId String?
|
||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||
|
||
platform SocialPlatform
|
||
|
||
platformUserId String?
|
||
username String?
|
||
displayName String?
|
||
profileImageUrl String?
|
||
|
||
authMethod AuthMethod @default(OAUTH)
|
||
|
||
accessToken String? @db.Text
|
||
refreshToken String? @db.Text
|
||
tokenExpiresAt DateTime?
|
||
tokenScope String?
|
||
|
||
encryptedCredentials String? @db.Text
|
||
|
||
isActive Boolean @default(true)
|
||
lastUsedAt DateTime?
|
||
lastError String?
|
||
errorCount Int @default(0)
|
||
|
||
optimalTimes OptimalPostTime[]
|
||
|
||
posts PublishedPost[]
|
||
scheduledPosts ScheduledPost[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([userId, platform, platformUserId])
|
||
@@index([userId])
|
||
@@index([workspaceId])
|
||
@@index([platform])
|
||
}
|
||
|
||
model ScheduledPost {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
contentId String?
|
||
content Content? @relation(fields: [contentId], references: [id])
|
||
variantId String?
|
||
variant ContentVariant? @relation(fields: [variantId], references: [id])
|
||
socialAccountId String
|
||
socialAccount SocialAccount @relation(fields: [socialAccountId], references: [id])
|
||
|
||
textSnapshot String @db.Text
|
||
mediaUrls String[] @default([])
|
||
|
||
scheduledFor DateTime
|
||
timezone String @default("UTC")
|
||
|
||
isRecurring Boolean @default(false)
|
||
recurrenceRule String?
|
||
parentPostId String?
|
||
|
||
aiSuggestedTime DateTime?
|
||
aiConfidence Float?
|
||
|
||
requiresApproval Boolean @default(true)
|
||
isApproved Boolean @default(false)
|
||
approvedBy String?
|
||
approvedAt DateTime?
|
||
|
||
status ScheduleStatus @default(SCHEDULED)
|
||
|
||
publishedPostId String?
|
||
publishedPost PublishedPost? @relation(fields: [publishedPostId], references: [id])
|
||
error String?
|
||
retryCount Int @default(0)
|
||
|
||
creditsUsed Int @default(0)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([userId])
|
||
@@index([socialAccountId])
|
||
@@index([scheduledFor])
|
||
@@index([status])
|
||
}
|
||
|
||
model PublishedPost {
|
||
id String @id @default(uuid())
|
||
socialAccountId String
|
||
socialAccount SocialAccount @relation(fields: [socialAccountId], references: [id])
|
||
|
||
platformPostId String
|
||
platformUrl String?
|
||
|
||
content String @db.Text
|
||
mediaUrls String[] @default([])
|
||
|
||
scheduledPosts ScheduledPost[]
|
||
|
||
analytics PostAnalytics[]
|
||
|
||
// Gold post detection
|
||
isGoldPost Boolean @default(false)
|
||
engagementMultiplier Float?
|
||
|
||
publishedAt DateTime @default(now())
|
||
|
||
@@index([socialAccountId])
|
||
@@index([platformPostId])
|
||
@@index([publishedAt])
|
||
@@index([isGoldPost])
|
||
}
|
||
|
||
// ============================================
|
||
// ANALYTICS & A/B TESTING
|
||
// ============================================
|
||
|
||
model ContentAnalytics {
|
||
id String @id @default(uuid())
|
||
contentId String
|
||
content Content @relation(fields: [contentId], references: [id], onDelete: Cascade)
|
||
|
||
platform SocialPlatform
|
||
|
||
views Int @default(0)
|
||
likes Int @default(0)
|
||
comments Int @default(0)
|
||
shares Int @default(0)
|
||
saves Int @default(0)
|
||
clicks Int @default(0)
|
||
|
||
impressions Int @default(0)
|
||
reach Int @default(0)
|
||
|
||
engagementRate Float?
|
||
|
||
predictedEngagement Float?
|
||
predictionAccuracy Float?
|
||
|
||
recordedAt DateTime @default(now())
|
||
|
||
@@index([contentId])
|
||
@@index([platform])
|
||
@@index([recordedAt])
|
||
}
|
||
|
||
model PostAnalytics {
|
||
id String @id @default(uuid())
|
||
publishedPostId String
|
||
publishedPost PublishedPost @relation(fields: [publishedPostId], references: [id], onDelete: Cascade)
|
||
|
||
views Int @default(0)
|
||
likes Int @default(0)
|
||
comments Int @default(0)
|
||
shares Int @default(0)
|
||
saves Int @default(0)
|
||
clicks Int @default(0)
|
||
impressions Int @default(0)
|
||
reach Int @default(0)
|
||
engagementRate Float?
|
||
|
||
snapshotAt DateTime @default(now())
|
||
|
||
@@index([publishedPostId])
|
||
@@index([snapshotAt])
|
||
}
|
||
|
||
model ABTest {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
workspaceId String?
|
||
|
||
name String
|
||
description String?
|
||
|
||
metric String
|
||
|
||
contents Content[]
|
||
|
||
winnerId String?
|
||
status String @default("running")
|
||
|
||
startedAt DateTime @default(now())
|
||
endedAt DateTime?
|
||
|
||
@@index([userId])
|
||
@@index([status])
|
||
}
|
||
|
||
model EngagementPrediction {
|
||
id String @id @default(uuid())
|
||
contentId String
|
||
|
||
platform SocialPlatform
|
||
|
||
predictedViews Int?
|
||
predictedLikes Int?
|
||
predictedComments Int?
|
||
predictedShares Int?
|
||
predictedEngRate Float?
|
||
|
||
confidence Float?
|
||
|
||
modelVersion String?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([contentId])
|
||
}
|
||
|
||
// ============================================
|
||
// CONTENT CALENDAR & WORKFLOW
|
||
// ============================================
|
||
|
||
model ContentCalendar {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id])
|
||
workspaceId String?
|
||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||
|
||
name String
|
||
description String?
|
||
|
||
isAiGenerated Boolean @default(false)
|
||
aiPrompt String? @db.Text
|
||
|
||
startDate DateTime
|
||
endDate DateTime
|
||
|
||
entries CalendarEntry[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([userId])
|
||
@@index([workspaceId])
|
||
}
|
||
|
||
model CalendarEntry {
|
||
id String @id @default(uuid())
|
||
calendarId String
|
||
calendar ContentCalendar @relation(fields: [calendarId], references: [id], onDelete: Cascade)
|
||
|
||
date DateTime
|
||
time String?
|
||
|
||
contentId String?
|
||
|
||
suggestedTopic String?
|
||
suggestedPlatform SocialPlatform?
|
||
suggestedType String?
|
||
|
||
// Growth formula tracking
|
||
contentCategory ContentCategoryType?
|
||
|
||
notes String? @db.Text
|
||
|
||
status String @default("planned")
|
||
|
||
@@index([calendarId])
|
||
@@index([date])
|
||
}
|
||
|
||
model OptimalPostTime {
|
||
id String @id @default(uuid())
|
||
socialAccountId String
|
||
account SocialAccount @relation(fields: [socialAccountId], references: [id], onDelete: Cascade)
|
||
|
||
platform SocialPlatform
|
||
|
||
dayOfWeek Int
|
||
hour Int
|
||
|
||
score Float
|
||
|
||
dataPoints Int @default(0)
|
||
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@unique([socialAccountId, platform, dayOfWeek, hour])
|
||
@@index([socialAccountId])
|
||
}
|
||
|
||
model WorkflowTemplate {
|
||
id String @id @default(uuid())
|
||
userId String?
|
||
user User? @relation(fields: [userId], references: [id])
|
||
|
||
name String
|
||
description String?
|
||
|
||
postsPerDay Int @default(3)
|
||
provenPercent Int @default(33)
|
||
experimentPercent Int @default(67)
|
||
|
||
days WorkflowDay[]
|
||
|
||
isSystem Boolean @default(false)
|
||
isActive Boolean @default(true)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([userId])
|
||
@@index([isSystem])
|
||
}
|
||
|
||
model WorkflowDay {
|
||
id String @id @default(uuid())
|
||
templateId String
|
||
template WorkflowTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
||
|
||
dayOfWeek Int
|
||
tasks WorkflowTask[]
|
||
|
||
@@index([templateId])
|
||
}
|
||
|
||
model WorkflowTask {
|
||
id String @id @default(uuid())
|
||
dayId String
|
||
day WorkflowDay @relation(fields: [dayId], references: [id], onDelete: Cascade)
|
||
|
||
order Int
|
||
type String
|
||
title String
|
||
description String?
|
||
duration Int?
|
||
|
||
@@index([dayId])
|
||
}
|
||
|
||
// ============================================
|
||
// YOUTUBE & EXTERNAL SOURCES
|
||
// ============================================
|
||
|
||
model YouTubeVideo {
|
||
id String @id @default(uuid())
|
||
videoId String @unique
|
||
url String
|
||
|
||
title String
|
||
description String? @db.Text
|
||
channelId String?
|
||
channelTitle String?
|
||
publishedAt DateTime?
|
||
duration Int?
|
||
thumbnailUrl String?
|
||
|
||
viewCount Int?
|
||
likeCount Int?
|
||
commentCount Int?
|
||
|
||
transcript String? @db.Text
|
||
transcriptLanguage String?
|
||
transcriptStatus String @default("pending")
|
||
|
||
lastFetchedAt DateTime @default(now())
|
||
|
||
@@index([videoId])
|
||
@@index([channelId])
|
||
}
|
||
|
||
model RssFeed {
|
||
id String @id @default(uuid())
|
||
url String @unique
|
||
title String?
|
||
description String?
|
||
siteUrl String?
|
||
|
||
fetchInterval Int @default(3600)
|
||
|
||
lastFetchedAt DateTime?
|
||
lastError String?
|
||
|
||
isActive Boolean @default(true)
|
||
|
||
items RssFeedItem[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([url])
|
||
}
|
||
|
||
model RssFeedItem {
|
||
id String @id @default(uuid())
|
||
feedId String
|
||
feed RssFeed @relation(fields: [feedId], references: [id], onDelete: Cascade)
|
||
|
||
guid String
|
||
title String
|
||
link String
|
||
description String? @db.Text
|
||
content String? @db.Text
|
||
author String?
|
||
categories String[] @default([])
|
||
publishedAt DateTime?
|
||
|
||
extractedContent String? @db.Text
|
||
isProcessed Boolean @default(false)
|
||
|
||
fetchedAt DateTime @default(now())
|
||
|
||
@@unique([feedId, guid])
|
||
@@index([feedId])
|
||
@@index([publishedAt])
|
||
}
|
||
|
||
// ============================================
|
||
// API & WEBHOOKS
|
||
// ============================================
|
||
|
||
model ApiKey {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||
|
||
name String
|
||
key String @unique
|
||
keyPrefix String
|
||
keyHash String
|
||
|
||
permissions String[] @default([])
|
||
|
||
rateLimit Int @default(1000)
|
||
|
||
isActive Boolean @default(true)
|
||
lastUsedAt DateTime?
|
||
usageCount Int @default(0)
|
||
|
||
expiresAt DateTime?
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([keyHash])
|
||
@@index([userId])
|
||
}
|
||
|
||
model Webhook {
|
||
id String @id @default(uuid())
|
||
userId String
|
||
workspaceId String?
|
||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||
|
||
name String
|
||
url String
|
||
secret String?
|
||
|
||
events String[] @default([])
|
||
|
||
isActive Boolean @default(true)
|
||
lastTriggeredAt DateTime?
|
||
consecutiveFailures Int @default(0)
|
||
|
||
logs WebhookLog[]
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([userId])
|
||
@@index([workspaceId])
|
||
}
|
||
|
||
model WebhookLog {
|
||
id String @id @default(uuid())
|
||
webhookId String
|
||
webhook Webhook @relation(fields: [webhookId], references: [id], onDelete: Cascade)
|
||
|
||
event String
|
||
payload Json
|
||
|
||
statusCode Int?
|
||
responseBody String? @db.Text
|
||
responseTime Int?
|
||
|
||
success Boolean
|
||
error String?
|
||
|
||
sentAt DateTime @default(now())
|
||
|
||
@@index([webhookId])
|
||
@@index([sentAt])
|
||
}
|
||
|
||
// ============================================
|
||
// SYSTEM & JOBS
|
||
// ============================================
|
||
|
||
model SystemSetting {
|
||
id String @id @default(uuid())
|
||
key String @unique
|
||
value Json
|
||
description String?
|
||
|
||
isPublic Boolean @default(false)
|
||
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([key])
|
||
}
|
||
|
||
model JobLog {
|
||
id String @id @default(uuid())
|
||
jobName String
|
||
jobId String?
|
||
|
||
status String
|
||
|
||
input Json?
|
||
output Json?
|
||
error String? @db.Text
|
||
|
||
duration Int?
|
||
|
||
startedAt DateTime @default(now())
|
||
completedAt DateTime?
|
||
|
||
@@index([jobName])
|
||
@@index([status])
|
||
@@index([startedAt])
|
||
}
|
||
|
||
// ============================================
|
||
// COPYWRITING FORMULAS
|
||
// ============================================
|
||
|
||
model CopywritingFormula {
|
||
id String @id @default(uuid())
|
||
name String @unique
|
||
acronym String? // AIDA, PAS, BAB, etc.
|
||
description String @db.Text
|
||
|
||
steps Json // Array of steps
|
||
examples String[] @default([])
|
||
bestFor String[] @default([])
|
||
|
||
isSystem Boolean @default(true)
|
||
|
||
createdAt DateTime @default(now())
|
||
}
|
||
|
||
model CtaTemplate {
|
||
id String @id @default(uuid())
|
||
category String // action, urgency, benefit, social_proof
|
||
template String
|
||
|
||
examples String[] @default([])
|
||
|
||
usageCount Int @default(0)
|
||
avgEngagement Float?
|
||
|
||
isSystem Boolean @default(true)
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([category])
|
||
}
|
||
|
||
// ============================================
|
||
// CORE RULE: SOURCE VERIFICATION
|
||
// ============================================
|
||
|
||
model ContentSource {
|
||
id String @id @default(uuid())
|
||
contentId String
|
||
content Content @relation(fields: [contentId], references: [id], onDelete: Cascade)
|
||
|
||
// Source info
|
||
sourceType SourceType
|
||
title String
|
||
url String?
|
||
author String?
|
||
publisher String?
|
||
publishDate DateTime?
|
||
|
||
// Verification
|
||
isVerified Boolean @default(false)
|
||
verifiedAt DateTime?
|
||
verifiedBy String? // AI or UserId
|
||
|
||
// Usage in content
|
||
excerpt String? @db.Text // Kullanılan kısım
|
||
claimMade String? @db.Text // Bu kaynaktan yapılan iddia
|
||
|
||
// Reliability
|
||
reliabilityScore Float? // 0-1 arası güvenilirlik
|
||
|
||
order Int @default(0)
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([contentId])
|
||
@@index([sourceType])
|
||
@@index([isVerified])
|
||
}
|
||
|
||
// ============================================
|
||
// VIRAL LEARNING DATABASE
|
||
// ============================================
|
||
|
||
model ViralLearning {
|
||
id String @id @default(uuid())
|
||
|
||
// Source post info
|
||
platform SocialPlatform
|
||
sourceUrl String?
|
||
originalContent String @db.Text
|
||
engagementCount Int?
|
||
|
||
// Extracted patterns
|
||
hookPattern String? @db.Text
|
||
painPattern String? @db.Text
|
||
payoffPattern String? @db.Text
|
||
ctaPattern String? @db.Text
|
||
|
||
// Psychology analysis
|
||
psychologyTriggers String[] @default([])
|
||
emotionalHooks String[] @default([])
|
||
|
||
// Structure template
|
||
structureTemplate Json?
|
||
|
||
// Learning metadata
|
||
successFactors String[] @default([])
|
||
targetAudience String?
|
||
contentType String?
|
||
|
||
// Usage tracking
|
||
timesUsed Int @default(0)
|
||
derivedContents String[] @default([]) // Content IDs
|
||
avgDerivedEngagement Float?
|
||
|
||
// Tags for retrieval
|
||
tags String[] @default([])
|
||
|
||
isActive Boolean @default(true)
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([platform])
|
||
@@index([tags])
|
||
@@index([timesUsed])
|
||
}
|
||
|
||
// ============================================
|
||
// INSTAGRAM SPECIFIC CONTENT
|
||
// ============================================
|
||
|
||
model InstagramContent {
|
||
id String @id @default(uuid())
|
||
contentId String
|
||
|
||
format InstagramFormat
|
||
|
||
// Caption
|
||
caption String @db.Text
|
||
hashtags String[] @default([])
|
||
mentions String[] @default([])
|
||
|
||
// Carousel specific
|
||
carouselSlides CarouselSlide[]
|
||
|
||
// Reel specific
|
||
reelDuration Int?
|
||
reelScript String? @db.Text
|
||
audioTrack String?
|
||
|
||
// Static image specific
|
||
imageAltText String?
|
||
|
||
// Cover image for all formats
|
||
coverImageUrl String?
|
||
|
||
createdAt DateTime @default(now())
|
||
updatedAt DateTime @updatedAt
|
||
|
||
@@index([contentId])
|
||
@@index([format])
|
||
}
|
||
|
||
model CarouselSlide {
|
||
id String @id @default(uuid())
|
||
instagramContentId String
|
||
instagramContent InstagramContent @relation(fields: [instagramContentId], references: [id], onDelete: Cascade)
|
||
|
||
order Int
|
||
|
||
// Slide content
|
||
imageUrl String?
|
||
text String? @db.Text
|
||
headline String?
|
||
|
||
// Template info
|
||
templateId String?
|
||
|
||
createdAt DateTime @default(now())
|
||
|
||
@@index([instagramContentId])
|
||
@@index([order])
|
||
}
|