// Prisma Schema for OpenFrame - Video Feedback Platform generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" } // ============================================ // AUTH MODELS (NextAuth.js compatible) // ============================================ model User { id String @id @default(cuid()) name String? email String? @unique emailVerified DateTime? image String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations accounts Account[] sessions Session[] projects Project[] comments Comment[] memberships ProjectMember[] @@map("users") } model Account { id String @id @default(cuid()) userId String type String provider String providerAccountId String refresh_token String? @db.Text access_token String? @db.Text expires_at Int? token_type String? scope String? id_token String? @db.Text session_state String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([provider, providerAccountId]) @@map("accounts") } model Session { id String @id @default(cuid()) sessionToken String @unique userId String expires DateTime user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("sessions") } model VerificationToken { identifier String token String @unique expires DateTime @@unique([identifier, token]) @@map("verification_tokens") } // ============================================ // APPLICATION MODELS // ============================================ model Project { id String @id @default(cuid()) name String description String? @db.Text slug String @unique // URL-friendly identifier // Visibility visibility ProjectVisibility @default(PRIVATE) // Ownership ownerId String owner User @relation(fields: [ownerId], references: [id], onDelete: Cascade) // Timestamps createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations videos Video[] members ProjectMember[] shareLinks ShareLink[] @@index([ownerId]) @@index([slug]) @@map("projects") } enum ProjectVisibility { PRIVATE // Only owner and members can access LINK_ONLY // Anyone with link can view PUBLIC // Listed publicly } model ProjectMember { id String @id @default(cuid()) role ProjectMemberRole @default(VIEWER) projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) createdAt DateTime @default(now()) @@unique([projectId, userId]) @@index([userId]) @@map("project_members") } enum ProjectMemberRole { VIEWER // Can view and comment EDITOR // Can add/edit videos ADMIN // Can manage members and settings } model Video { id String @id @default(cuid()) title String description String? @db.Text // Ordering within project position Int @default(0) // Project relation projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) // Timestamps createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // Relations versions VideoVersion[] @@index([projectId]) @@map("videos") } model VideoVersion { id String @id @default(cuid()) versionNumber Int // 1, 2, 3... versionLabel String? // Optional label like "Final Cut", "Review Draft" // Video source (future-proof for multiple providers) providerId String // 'youtube', 'vimeo', 'direct' videoId String // Provider-specific video ID originalUrl String // Original URL submitted by user // Cached metadata title String? thumbnailUrl String? duration Int? // Duration in seconds // Status isActive Boolean @default(true) // Currently displayed version // Parent video videoParentId String video Video @relation(fields: [videoParentId], references: [id], onDelete: Cascade) // Timestamps createdAt DateTime @default(now()) // Relations comments Comment[] @@unique([videoParentId, versionNumber]) @@index([videoParentId]) @@map("video_versions") } model Comment { id String @id @default(cuid()) // Comment content content String? @db.Text // Text content (null if voice-only) // Timestamp in video (in seconds, with decimal for precision) timestamp Float // e.g., 65.5 = 1:05.5 timestampEnd Float? // Optional end timestamp for range comments // Voice recording (optional) voiceUrl String? // URL to voice recording file voiceDuration Float? // Duration of voice recording in seconds // Threading parentId String? parent Comment? @relation("CommentReplies", fields: [parentId], references: [id], onDelete: Cascade) replies Comment[] @relation("CommentReplies") // Status isResolved Boolean @default(false) resolvedAt DateTime? // Author (optional for guest comments) authorId String? author User? @relation(fields: [authorId], references: [id], onDelete: SetNull) // Guest author info (when authorId is null) guestName String? guestEmail String? // Video version relation versionId String version VideoVersion @relation(fields: [versionId], references: [id], onDelete: Cascade) // Timestamps createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([versionId]) @@index([parentId]) @@index([authorId]) @@index([timestamp]) @@map("comments") } model ShareLink { id String @id @default(cuid()) token String @unique // Random token for URL // What is being shared projectId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) // Permissions permission SharePermission @default(VIEW) // Optional restrictions expiresAt DateTime? // Link expiration maxUses Int? // Maximum number of uses useCount Int @default(0) password String? // Optional password protection // Settings allowGuests Boolean @default(true) // Allow comments without account // Timestamps createdAt DateTime @default(now()) @@index([projectId]) @@index([token]) @@map("share_links") } enum SharePermission { VIEW // Can only view COMMENT // Can view and comment }