generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(cuid()) email String @unique name String avatarColor String @default("#00a82d") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model NotebookStack { id String @id @default(cuid()) userId String @default("default") name String sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt notebooks Notebook[] @@unique([userId, name]) @@index([userId]) } model Notebook { id String @id @default(cuid()) userId String @default("default") name String stackId String? stack NotebookStack? @relation(fields: [stackId], references: [id], onDelete: SetNull) isDefault Boolean @default(false) isShortcut Boolean @default(false) sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt notes Note[] autoSortRules AutoSortRule[] shares NotebookShare[] @@unique([userId, name]) @@index([userId]) @@index([stackId]) } model NotebookShare { id String @id @default(cuid()) notebookId String notebook Notebook @relation(fields: [notebookId], references: [id], onDelete: Cascade) userId String // Collaborator user ID role String @default("write") // "read" | "write" createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([notebookId, userId]) @@index([userId]) @@index([notebookId]) } model Note { id String @id @default(cuid()) userId String @default("default") title String @default("Untitled Note") contentHtml String @default("") contentText String @default("") excerpt String @default("") notebookId String notebook Notebook @relation(fields: [notebookId], references: [id], onDelete: Cascade) isPinned Boolean @default(false) isShortcut Boolean @default(false) isTrashed Boolean @default(false) trashedAt DateTime? thumbnailUrl String? sourceUrl String? author String? latitude Float? longitude Float? reminderAt DateTime? reminderDone Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) tags NoteTag[] attachments Attachment[] history NoteHistory[] @@index([userId, isTrashed, isPinned, updatedAt(sort: Desc)]) @@index([notebookId, isTrashed, updatedAt(sort: Desc)]) @@index([isTrashed, isPinned, updatedAt(sort: Desc)]) @@index([isShortcut, isTrashed]) @@index([createdAt(sort: Desc)]) @@index([title]) } model NoteHistory { id String @id @default(cuid()) noteId String note Note @relation(fields: [noteId], references: [id], onDelete: Cascade) title String contentHtml String contentText String excerpt String @default("") wordCount Int @default(0) createdBy String? createdAt DateTime @default(now()) @@index([noteId, createdAt(sort: Desc)]) } model Tag { id String @id @default(cuid()) userId String @default("default") name String color String @default("#00a82d") parentId String? parent Tag? @relation("TagHierarchy", fields: [parentId], references: [id], onDelete: SetNull) children Tag[] @relation("TagHierarchy") isShortcut Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt notes NoteTag[] @@unique([userId, name]) @@index([userId]) @@index([parentId]) } model NoteTag { noteId String tagId String note Note @relation(fields: [noteId], references: [id], onDelete: Cascade) tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade) assignedAt DateTime @default(now()) @@id([noteId, tagId]) @@index([tagId]) } model Attachment { id String @id @default(cuid()) noteId String note Note @relation(fields: [noteId], references: [id], onDelete: Cascade) filename String mimeType String sizeBytes Int md5Hash String storagePath String width Int? height Int? extractedText String? createdAt DateTime @default(now()) @@index([noteId]) @@index([md5Hash]) } model SavedSearch { id String @id @default(cuid()) userId String @default("default") name String query String isShortcut Boolean @default(false) sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([userId, name]) @@index([userId]) } model ApiKey { id String @id @default(cuid()) userId String @default("default") name String keyHash String @unique keyPrefix String lastUsedAt DateTime? createdAt DateTime @default(now()) } model ImportJob { id String @id @default(cuid()) userId String @default("default") sourceType String filename String status String @default("running") totalNotes Int @default(0) processedNotes Int @default(0) failedNotes Int @default(0) errorLog String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model EmailConfig { id String @id @default("default") userId String @default("default") enabled Boolean @default(false) imapHost String @default("imap.gmail.com") imapPort Int @default(993) imapSecure Boolean @default(true) imapUser String @default("") imapPassword String @default("") mailbox String @default("INBOX") subjectFilter String @default("") allowedSenders String @default("") defaultNotebook String @default("Inbox") defaultTags String @default("emailed") markSeen Boolean @default(true) lastCheckedAt DateTime? lastStatus String? emailsImported Int @default(0) updatedAt DateTime @updatedAt } model AutoSortRule { id String @id @default(cuid()) userId String @default("default") name String enabled Boolean @default(true) priority Int @default(0) conditionField String @default("title") // title | content | sourceUrl | author | attachmentType | hasTodo conditionOperator String @default("contains") // contains | not_contains | equals | startsWith | endsWith | matchesRegex conditionValue String @default("") targetNotebookId String? targetNotebook Notebook? @relation(fields: [targetNotebookId], references: [id], onDelete: SetNull) addTags String @default("") // comma-separated tag names isPinned Boolean? isShortcut Boolean? stopOnMatch Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId, enabled, priority]) @@index([targetNotebookId]) }