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