generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ─── Auth ──────────────────────────────────────────────── model User { id String @id @default(cuid()) did String? @unique username String? @unique name String? email String? @unique emailVerified DateTime? image String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt memberships SpaceMember[] notebooks Notebook[] comments Comment[] suggestions Suggestion[] reactions Reaction[] } // ─── Multi-tenant Spaces ───────────────────────────────── enum SpaceRole { ADMIN MODERATOR MEMBER VIEWER } model Space { id String @id @default(cuid()) slug String @unique name String description String @default("") icon String @default("") visibility String @default("public_read") ownerDid String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt members SpaceMember[] notebooks Notebook[] } model SpaceMember { id String @id @default(cuid()) userId String spaceId String role SpaceRole @default(MEMBER) joinedAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade) @@unique([userId, spaceId]) } // ─── Notebooks & Notes ─────────────────────────────────── model Notebook { id String @id @default(cuid()) spaceId String title String description String @default("") icon String @default("📓") createdBy String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade) creator User @relation(fields: [createdBy], references: [id]) notes Note[] @@index([spaceId]) } model Note { id String @id @default(cuid()) notebookId String title String @default("Untitled") yjsDocId String @unique @default(cuid()) sortOrder Int @default(0) createdBy String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt notebook Notebook @relation(fields: [notebookId], references: [id], onDelete: Cascade) comments Comment[] suggestions Suggestion[] @@index([notebookId]) } // ─── Suggestions (Track Changes) ───────────────────────── enum SuggestionStatus { PENDING ACCEPTED REJECTED } enum SuggestionType { INSERT DELETE FORMAT REPLACE } model Suggestion { id String @id @default(cuid()) noteId String authorId String type SuggestionType status SuggestionStatus @default(PENDING) fromPos Int toPos Int content String? oldContent String? attrs Json? resolvedBy String? resolvedAt DateTime? createdAt DateTime @default(now()) note Note @relation(fields: [noteId], references: [id], onDelete: Cascade) author User @relation(fields: [authorId], references: [id]) @@index([noteId, status]) } // ─── Comments & Threads ────────────────────────────────── model Comment { id String @id @default(cuid()) noteId String authorId String parentId String? body String fromPos Int toPos Int resolved Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt note Note @relation(fields: [noteId], references: [id], onDelete: Cascade) author User @relation(fields: [authorId], references: [id]) parent Comment? @relation("CommentThread", fields: [parentId], references: [id]) replies Comment[] @relation("CommentThread") reactions Reaction[] @@index([noteId]) } // ─── Emoji Reactions ───────────────────────────────────── model Reaction { id String @id @default(cuid()) commentId String userId String emoji String createdAt DateTime @default(now()) comment Comment @relation(fields: [commentId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id]) @@unique([commentId, userId, emoji]) }