mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 17:46:06 +00:00
chore(init): scaffold OpenFrame — Next.js + Bun + shadcn
Initialize OpenFrame project with core scaffold and UI foundation. - Project scaffold: Next.js 16.1 (App Router) + Bun runtime - UI: shadcn/ui + TailwindCSS; landing, dashboard, project, and auth UIs - Video support: provider abstraction (YouTube first), video player page with timestamped comments and custom timeline - Auth & DB: NextAuth skeleton and Prisma schema (Postgres) included - UX: dark-mode toggle, full-width layouts, comments sidebar, video route moved to /watch/[videoId] - Dev: added YouTube iframe integration, custom controls, and TypeScript types - Next steps: DB migrations, API routes (CRUD), real data wiring, voice-recording & sharing
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user