generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ─── Users ────────────────────────────────────────────────────────── model User { id String @id @default(cuid()) did String @unique // EncryptID DID username String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt notebooks NotebookCollaborator[] notes Note[] files File[] sharedByMe SharedAccess[] @relation("SharedBy") } // ─── Notebooks ────────────────────────────────────────────────────── model Notebook { id String @id @default(cuid()) title String slug String @unique description String? @db.Text coverColor String @default("#f59e0b") canvasSlug String? canvasShapeId String? isPublic Boolean @default(false) sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt collaborators NotebookCollaborator[] notes Note[] sharedAccess SharedAccess[] @@index([slug]) } enum CollaboratorRole { OWNER EDITOR VIEWER } model NotebookCollaborator { id String @id @default(cuid()) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) notebookId String notebook Notebook @relation(fields: [notebookId], references: [id], onDelete: Cascade) role CollaboratorRole @default(VIEWER) joinedAt DateTime @default(now()) @@unique([userId, notebookId]) @@index([notebookId]) } // ─── Notes ────────────────────────────────────────────────────────── model Note { id String @id @default(cuid()) notebookId String? notebook Notebook? @relation(fields: [notebookId], references: [id], onDelete: SetNull) authorId String? author User? @relation(fields: [authorId], references: [id], onDelete: SetNull) title String content String @db.Text contentPlain String? @db.Text type NoteType @default(NOTE) url String? archiveUrl String? language String? mimeType String? fileUrl String? fileSize Int? duration Int? isPinned Boolean @default(false) canvasShapeId String? sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // ─── Memory Card fields ───────────────────────────────────────── parentId String? parent Note? @relation("NoteTree", fields: [parentId], references: [id], onDelete: SetNull) children Note[] @relation("NoteTree") bodyJson Json? // TipTap JSON (canonical format) bodyMarkdown String? @db.Text // portable markdown for search + Logseq bodyFormat String @default("html") // "html" | "markdown" | "blocks" cardType String @default("note") // note|link|file|task|person|idea|reference summary String? // auto or manual visibility String @default("private") // private|space|public position Float? // fractional ordering properties Json @default("{}") // Logseq-compatible key-value archivedAt DateTime? // soft-delete tags NoteTag[] attachments CardAttachment[] @@index([notebookId]) @@index([authorId]) @@index([type]) @@index([isPinned]) @@index([parentId]) @@index([cardType]) @@index([archivedAt]) @@index([position]) } enum NoteType { NOTE CLIP BOOKMARK CODE IMAGE FILE AUDIO } // ─── Files & Attachments ──────────────────────────────────────────── model File { id String @id @default(cuid()) storageKey String @unique // unique filename on disk filename String // original filename mimeType String sizeBytes Int checksum String? authorId String? author User? @relation(fields: [authorId], references: [id], onDelete: SetNull) createdAt DateTime @default(now()) attachments CardAttachment[] } model CardAttachment { id String @id @default(cuid()) noteId String note Note @relation(fields: [noteId], references: [id], onDelete: Cascade) fileId String file File @relation(fields: [fileId], references: [id], onDelete: Cascade) role String @default("supporting") // "primary"|"preview"|"supporting" caption String? position Float @default(0) createdAt DateTime @default(now()) @@unique([noteId, fileId]) @@index([noteId]) @@index([fileId]) } // ─── Tags ─────────────────────────────────────────────────────────── model Tag { id String @id @default(cuid()) name String color String? @default("#6b7280") spaceId String @default("") // "" = global, otherwise space-scoped schema Json? createdAt DateTime @default(now()) notes NoteTag[] @@unique([spaceId, name]) @@index([spaceId]) } model NoteTag { id String @id @default(cuid()) noteId String note Note @relation(fields: [noteId], references: [id], onDelete: Cascade) tagId String tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade) @@unique([noteId, tagId]) @@index([tagId]) @@index([noteId]) } // ─── Shared Access ────────────────────────────────────────────────── model SharedAccess { id String @id @default(cuid()) notebookId String notebook Notebook @relation(fields: [notebookId], references: [id], onDelete: Cascade) sharedById String sharedBy User @relation("SharedBy", fields: [sharedById], references: [id], onDelete: Cascade) targetDid String role CollaboratorRole @default(VIEWER) createdAt DateTime @default(now()) @@unique([notebookId, targetDid]) @@index([targetDid]) }