Ajoute la base organisable par carnet (schéma, champs partagés, valeurs par note) avec activation guidée, tableau éditable, kanban et suppression de colonnes. Corrige le multiselect en vue tableau et enrichit sidebar, grille et i18n FR/EN. Inclut aussi les améliorations flashcards SM-2, l'audit consentement IA et la robustesse du serveur MCP (config, validation, rate-limit, métriques). Co-authored-by: Cursor <cursoragent@cursor.com>
945 lines
31 KiB
Plaintext
945 lines
31 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
binaryTargets = ["debian-openssl-3.0.x", "native"]
|
|
previewFeatures = ["postgresqlExtensions"]
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
extensions = [vector]
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
name String?
|
|
email String @unique
|
|
emailVerified DateTime?
|
|
password String?
|
|
role String @default("USER")
|
|
sessionVersion Int @default(0)
|
|
image String?
|
|
theme String @default("light")
|
|
resetToken String? @unique
|
|
resetTokenExpiry DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
cardSizeMode String @default("variable")
|
|
accentColor String @default("#A47148")
|
|
accounts Account[]
|
|
agents Agent[]
|
|
aiFeedback AiFeedback[]
|
|
brainstormActivities BrainstormActivity[]
|
|
brainstormParticipant BrainstormParticipant[]
|
|
brainstormSessions BrainstormSession[]
|
|
canvases Canvas[]
|
|
conversations Conversation[]
|
|
labels Label[]
|
|
memoryEchoInsights MemoryEchoInsight[]
|
|
notes Note[]
|
|
noteHistories NoteHistory[]
|
|
sentShares NoteShare[] @relation("SentShares")
|
|
receivedShares NoteShare[] @relation("ReceivedShares")
|
|
sentBrainstormShares BrainstormShare[] @relation("SentBrainstormShares")
|
|
receivedBrainstormShares BrainstormShare[] @relation("ReceivedBrainstormShares")
|
|
notebooks Notebook[]
|
|
notifications Notification[]
|
|
sessions Session[]
|
|
aiSettings UserAISettings?
|
|
workflows Workflow[]
|
|
subscription Subscription?
|
|
usageLogs UsageLog[]
|
|
apiKeys UserAPIKey[]
|
|
aiConsentLogs AiConsentLog[]
|
|
noteClusters NoteCluster[]
|
|
bridgeNotes BridgeNote[]
|
|
bridgeSuggestions BridgeSuggestion[]
|
|
flashcardDecks FlashcardDeck[]
|
|
}
|
|
|
|
model Account {
|
|
userId String
|
|
type String
|
|
provider String
|
|
providerAccountId String
|
|
refresh_token String?
|
|
access_token String?
|
|
expires_at Int?
|
|
token_type String?
|
|
scope String?
|
|
id_token String?
|
|
session_state String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([provider, providerAccountId])
|
|
}
|
|
|
|
model Session {
|
|
sessionToken String @unique
|
|
userId String
|
|
expires DateTime
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model VerificationToken {
|
|
identifier String
|
|
token String
|
|
expires DateTime
|
|
|
|
@@id([identifier, token])
|
|
}
|
|
|
|
model Notebook {
|
|
id String @id @default(cuid())
|
|
name String
|
|
icon String?
|
|
color String?
|
|
order Int
|
|
userId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
parentId String?
|
|
trashedAt DateTime?
|
|
agents Agent[]
|
|
conversations Conversation[]
|
|
labels Label[]
|
|
notes Note[]
|
|
parent Notebook? @relation("NotebookTree", fields: [parentId], references: [id], onDelete: Cascade)
|
|
children Notebook[] @relation("NotebookTree")
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
workflows Workflow[]
|
|
flashcardDeck FlashcardDeck?
|
|
schema NotebookSchema?
|
|
|
|
@@index([userId, order])
|
|
@@index([userId])
|
|
@@index([parentId])
|
|
@@index([trashedAt])
|
|
}
|
|
|
|
model Label {
|
|
id String @id @default(cuid())
|
|
name String
|
|
color String @default("gray")
|
|
notebookId String?
|
|
userId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
type String @default("user")
|
|
notebook Notebook? @relation(fields: [notebookId], references: [id], onDelete: Cascade)
|
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
notes Note[] @relation("LabelToNote")
|
|
|
|
@@unique([notebookId, name])
|
|
@@index([notebookId])
|
|
@@index([userId])
|
|
}
|
|
|
|
model Note {
|
|
id String @id @default(cuid())
|
|
title String?
|
|
content String
|
|
color String @default("default")
|
|
isPinned Boolean @default(false)
|
|
isArchived Boolean @default(false)
|
|
type String @default("richtext")
|
|
dismissedFromRecent Boolean @default(false)
|
|
checkItems String?
|
|
labels String?
|
|
images String?
|
|
links String?
|
|
reminder DateTime?
|
|
isReminderDone Boolean @default(false)
|
|
reminderRecurrence String?
|
|
reminderLocation String?
|
|
isMarkdown Boolean @default(false)
|
|
size String @default("small")
|
|
sharedWith String?
|
|
userId String?
|
|
order Int @default(0)
|
|
notebookId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
contentUpdatedAt DateTime @default(now())
|
|
autoGenerated Boolean?
|
|
aiProvider String?
|
|
aiConfidence Int?
|
|
language String?
|
|
languageConfidence Float?
|
|
lastAiAnalysis DateTime?
|
|
trashedAt DateTime?
|
|
historyEnabled Boolean @default(false)
|
|
/// URL d'origine pour les clips web (Web Clipper)
|
|
sourceUrl String?
|
|
/// Illustration SVG (sanitized) for editorial feed thumbnail — optional, peut être généré par IA
|
|
illustrationSvg String?
|
|
tsv Unsupported("tsvector")?
|
|
aiFeedback AiFeedback[]
|
|
convertedBrainstormIdeas BrainstormIdea[] @relation("ConvertedBrainstormIdea")
|
|
exportedBrainstormSessions BrainstormSession[] @relation("ExportedBrainstorm")
|
|
sourceBrainstormSessions BrainstormSession[] @relation("SourceBrainstorm")
|
|
memoryEchoAsNote1 MemoryEchoInsight[] @relation("EchoNote1")
|
|
memoryEchoAsNote2 MemoryEchoInsight[] @relation("EchoNote2")
|
|
notebook Notebook? @relation(fields: [notebookId], references: [id])
|
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
noteEmbedding NoteEmbedding?
|
|
historyEntries NoteHistory[]
|
|
shares NoteShare[]
|
|
labelRelations Label[] @relation("LabelToNote")
|
|
attachments NoteAttachment[]
|
|
brainstormNoteRefs BrainstormNoteRef[]
|
|
outgoingLinks NoteLink[] @relation("SourceLinks")
|
|
incomingLinks NoteLink[] @relation("TargetLinks")
|
|
clusterMemberships ClusterMember[]
|
|
bridgeNote BridgeNote?
|
|
sourceLiveBlocks LiveBlockRef[] @relation("SourceLiveBlocks")
|
|
targetLiveBlocks LiveBlockRef[] @relation("TargetLiveBlocks")
|
|
flashcards Flashcard[]
|
|
properties NoteProperty[]
|
|
|
|
@@index([isPinned])
|
|
@@index([isArchived])
|
|
@@index([trashedAt])
|
|
@@index([order])
|
|
@@index([reminder])
|
|
@@index([userId])
|
|
@@index([userId, notebookId])
|
|
}
|
|
|
|
model LiveBlockRef {
|
|
id String @id @default(cuid())
|
|
sourceNoteId String
|
|
blockId String
|
|
targetNoteId String
|
|
createdAt DateTime @default(now())
|
|
sourceNote Note @relation("SourceLiveBlocks", fields: [sourceNoteId], references: [id], onDelete: Cascade)
|
|
targetNote Note @relation("TargetLiveBlocks", fields: [targetNoteId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([sourceNoteId, blockId])
|
|
@@index([targetNoteId])
|
|
}
|
|
|
|
model NoteHistory {
|
|
id String @id @default(cuid())
|
|
noteId String
|
|
userId String
|
|
version Int
|
|
reason String?
|
|
title String?
|
|
content String
|
|
color String
|
|
isPinned Boolean
|
|
isArchived Boolean
|
|
type String
|
|
checkItems String?
|
|
labels String?
|
|
images String?
|
|
links String?
|
|
isMarkdown Boolean
|
|
size String
|
|
notebookId String?
|
|
createdAt DateTime @default(now())
|
|
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([noteId, version])
|
|
@@index([noteId, createdAt(sort: Desc)])
|
|
@@index([userId, noteId, createdAt(sort: Desc)])
|
|
}
|
|
|
|
model NoteShare {
|
|
id String @id @default(cuid())
|
|
noteId String
|
|
userId String
|
|
sharedBy String
|
|
status String @default("pending")
|
|
permission String @default("view")
|
|
notifiedAt DateTime?
|
|
respondedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
|
|
sharer User @relation("SentShares", fields: [sharedBy], references: [id], onDelete: Cascade)
|
|
user User @relation("ReceivedShares", fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([noteId, userId])
|
|
@@index([userId])
|
|
@@index([status])
|
|
@@index([sharedBy])
|
|
}
|
|
|
|
model SystemConfig {
|
|
key String @id
|
|
value String
|
|
}
|
|
|
|
model AiFeedback {
|
|
id String @id @default(cuid())
|
|
noteId String
|
|
userId String?
|
|
feedbackType String
|
|
feature String
|
|
originalContent String
|
|
correctedContent String?
|
|
metadata String?
|
|
createdAt DateTime @default(now())
|
|
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
|
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([noteId])
|
|
@@index([userId])
|
|
@@index([feature])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model MemoryEchoInsight {
|
|
id String @id @default(cuid())
|
|
userId String?
|
|
note1Id String
|
|
note2Id String
|
|
similarityScore Float
|
|
insight String
|
|
insightDate DateTime @default(now())
|
|
viewed Boolean @default(false)
|
|
feedback String?
|
|
dismissed Boolean @default(false)
|
|
note1 Note @relation("EchoNote1", fields: [note1Id], references: [id], onDelete: Cascade)
|
|
note2 Note @relation("EchoNote2", fields: [note2Id], references: [id], onDelete: Cascade)
|
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, insightDate])
|
|
@@index([userId, insightDate])
|
|
@@index([userId, dismissed])
|
|
}
|
|
|
|
model UserAISettings {
|
|
userId String @id
|
|
titleSuggestions Boolean @default(true)
|
|
semanticSearch Boolean @default(true)
|
|
paragraphRefactor Boolean @default(true)
|
|
memoryEcho Boolean @default(true)
|
|
memoryEchoFrequency String @default("daily")
|
|
aiProvider String @default("auto")
|
|
preferredLanguage String @default("auto")
|
|
fontSize String @default("medium")
|
|
demoMode Boolean @default(false)
|
|
showRecentNotes Boolean @default(true)
|
|
emailNotifications Boolean @default(false)
|
|
desktopNotifications Boolean @default(false)
|
|
anonymousAnalytics Boolean @default(false)
|
|
autoLabeling Boolean @default(true)
|
|
fontFamily String @default("inter")
|
|
languageDetection Boolean @default(true)
|
|
noteHistory Boolean @default(false)
|
|
noteHistoryMode String @default("manual")
|
|
autoSave Boolean @default(true)
|
|
aiProcessingConsent Boolean @default(false)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([memoryEcho])
|
|
@@index([aiProvider])
|
|
@@index([memoryEchoFrequency])
|
|
@@index([preferredLanguage])
|
|
}
|
|
|
|
model AiConsentLog {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
consent Boolean @default(true)
|
|
ipAddress String?
|
|
userAgent String?
|
|
createdAt DateTime @default(now())
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model NoteEmbedding {
|
|
id String @id @default(cuid())
|
|
noteId String @unique
|
|
embedding Unsupported("vector(1536)")?
|
|
createdAt DateTime @default(now())
|
|
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([noteId])
|
|
}
|
|
|
|
model NoteLink {
|
|
id String @id @default(cuid())
|
|
sourceNoteId String
|
|
targetNoteId String
|
|
contextSnippet String?
|
|
createdAt DateTime @default(now())
|
|
sourceNote Note @relation("SourceLinks", fields: [sourceNoteId], references: [id], onDelete: Cascade)
|
|
targetNote Note @relation("TargetLinks", fields: [targetNoteId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([sourceNoteId, targetNoteId])
|
|
@@index([sourceNoteId])
|
|
@@index([targetNoteId])
|
|
}
|
|
|
|
model Agent {
|
|
id String @id @default(cuid())
|
|
name String
|
|
description String?
|
|
type String? @default("scraper")
|
|
role String
|
|
sourceUrls String?
|
|
frequency String @default("manual")
|
|
lastRun DateTime?
|
|
nextRun DateTime?
|
|
isEnabled Boolean @default(true)
|
|
targetNotebookId String?
|
|
sourceNotebookId String?
|
|
tools String? @default("[]")
|
|
maxSteps Int @default(10)
|
|
notifyEmail Boolean @default(false)
|
|
includeImages Boolean @default(false)
|
|
userId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
scheduledTime String? @default("08:00")
|
|
scheduledDay Int?
|
|
timezone String?
|
|
sourceNoteIds String?
|
|
slideStyle String?
|
|
slideTheme String?
|
|
notebook Notebook? @relation(fields: [targetNotebookId], references: [id])
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
actions AgentAction[]
|
|
|
|
@@index([userId])
|
|
@@index([isEnabled])
|
|
}
|
|
|
|
model AgentAction {
|
|
id String @id @default(cuid())
|
|
agentId String
|
|
status String @default("pending")
|
|
result String?
|
|
log String?
|
|
input String?
|
|
toolLog String?
|
|
tokensUsed Int?
|
|
createdAt DateTime @default(now())
|
|
agent Agent @relation(fields: [agentId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([agentId])
|
|
}
|
|
|
|
model Conversation {
|
|
id String @id @default(cuid())
|
|
title String?
|
|
userId String
|
|
notebookId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
messages ChatMessage[]
|
|
notebook Notebook? @relation(fields: [notebookId], references: [id])
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([notebookId])
|
|
}
|
|
|
|
model ChatMessage {
|
|
id String @id @default(cuid())
|
|
conversationId String
|
|
role String
|
|
content String
|
|
createdAt DateTime @default(now())
|
|
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([conversationId])
|
|
}
|
|
|
|
model Canvas {
|
|
id String @id @default(cuid())
|
|
name String
|
|
data String
|
|
userId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
model Workflow {
|
|
id String @id @default(cuid())
|
|
name String
|
|
description String?
|
|
graph String @default("{\"nodes\":[],\"edges\":[]}")
|
|
isEnabled Boolean @default(true)
|
|
userId String
|
|
notebookId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
notebook Notebook? @relation(fields: [notebookId], references: [id])
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
runs WorkflowRun[]
|
|
|
|
@@index([userId])
|
|
@@index([isEnabled])
|
|
}
|
|
|
|
model WorkflowRun {
|
|
id String @id @default(cuid())
|
|
workflowId String
|
|
status String @default("running")
|
|
log String?
|
|
createdAt DateTime @default(now())
|
|
workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([workflowId])
|
|
@@index([status])
|
|
}
|
|
|
|
model Notification {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
type String
|
|
title String
|
|
message String?
|
|
read Boolean @default(false)
|
|
actionUrl String?
|
|
relatedId String?
|
|
createdAt DateTime @default(now())
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId, read])
|
|
@@index([userId, createdAt])
|
|
}
|
|
|
|
model BrainstormSession {
|
|
id String @id @default(cuid())
|
|
seedIdea String
|
|
sourceNoteId String?
|
|
contextNoteIds String?
|
|
exportedNoteId String?
|
|
userId String
|
|
inviteToken String? @unique
|
|
inviteExpiry DateTime?
|
|
liveblocksRoomId String?
|
|
isPublic Boolean @default(false)
|
|
guestCanEdit Boolean @default(false)
|
|
status String @default("active")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
ideas BrainstormIdea[]
|
|
participants BrainstormParticipant[]
|
|
activities BrainstormActivity[]
|
|
shares BrainstormShare[]
|
|
snapshots BrainstormSnapshot[]
|
|
sourceNote Note? @relation("SourceBrainstorm", fields: [sourceNoteId], references: [id])
|
|
exportedNote Note? @relation("ExportedBrainstorm", fields: [exportedNoteId], references: [id])
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([userId, createdAt])
|
|
@@index([inviteToken])
|
|
@@index([isPublic])
|
|
}
|
|
|
|
model BrainstormIdea {
|
|
id String @id @default(cuid())
|
|
sessionId String
|
|
waveNumber Int
|
|
title String
|
|
description String
|
|
connectionToSeed String?
|
|
noveltyScore Int?
|
|
parentIdeaId String?
|
|
convertedToNoteId String?
|
|
relatedNoteIds String?
|
|
status String @default("active")
|
|
positionX Float?
|
|
positionY Float?
|
|
isStarred Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
createdBy String?
|
|
createdByType String? @default("ai")
|
|
convertedNote Note? @relation("ConvertedBrainstormIdea", fields: [convertedToNoteId], references: [id])
|
|
parentIdea BrainstormIdea? @relation("IdeaTree", fields: [parentIdeaId], references: [id])
|
|
children BrainstormIdea[] @relation("IdeaTree")
|
|
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
noteRefs BrainstormNoteRef[]
|
|
|
|
@@index([sessionId])
|
|
@@index([waveNumber])
|
|
@@index([status])
|
|
@@index([parentIdeaId])
|
|
@@index([sessionId, status])
|
|
@@index([sessionId, waveNumber, createdAt])
|
|
}
|
|
|
|
model BrainstormNoteRef {
|
|
id String @id @default(cuid())
|
|
ideaId String
|
|
noteId String?
|
|
relation String
|
|
explanation String
|
|
verdict String @default("unresolved")
|
|
createdAt DateTime @default(now())
|
|
visibility String @default("participants")
|
|
idea BrainstormIdea @relation(fields: [ideaId], references: [id], onDelete: Cascade)
|
|
note Note? @relation(fields: [noteId], references: [id])
|
|
|
|
@@index([ideaId])
|
|
@@index([noteId])
|
|
@@index([noteId, relation])
|
|
@@index([visibility])
|
|
}
|
|
|
|
model BrainstormParticipant {
|
|
id String @id @default(cuid())
|
|
sessionId String
|
|
userId String
|
|
role String @default("viewer")
|
|
joinedAt DateTime @default(now())
|
|
lastSeenAt DateTime @default(now())
|
|
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([sessionId, userId])
|
|
@@index([sessionId])
|
|
@@index([userId])
|
|
@@index([sessionId, userId, role])
|
|
}
|
|
|
|
model BrainstormActivity {
|
|
id String @id @default(cuid())
|
|
sessionId String
|
|
userId String?
|
|
action String
|
|
details String?
|
|
createdAt DateTime @default(now())
|
|
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([sessionId, createdAt], map: "BrainstormActivity_sessionId_createdAt_asc_idx")
|
|
@@index([sessionId, createdAt(sort: Desc)], map: "BrainstormActivity_sessionId_createdAt_desc_idx")
|
|
@@index([sessionId, createdAt])
|
|
}
|
|
|
|
model BrainstormShare {
|
|
id String @id @default(cuid())
|
|
sessionId String
|
|
userId String
|
|
sharedBy String
|
|
status String @default("pending")
|
|
permission String @default("editor")
|
|
notifiedAt DateTime?
|
|
respondedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
sharer User @relation("SentBrainstormShares", fields: [sharedBy], references: [id], onDelete: Cascade)
|
|
user User @relation("ReceivedBrainstormShares", fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([sessionId, userId])
|
|
@@index([userId])
|
|
@@index([status])
|
|
}
|
|
|
|
model BrainstormSnapshot {
|
|
id String @id @default(cuid())
|
|
sessionId String
|
|
activityId String?
|
|
step Int
|
|
label String?
|
|
ideaGraph String
|
|
createdAt DateTime @default(now())
|
|
session BrainstormSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([sessionId, step])
|
|
@@index([sessionId, createdAt])
|
|
@@index([activityId])
|
|
}
|
|
|
|
model NoteAttachment {
|
|
id String @id @default(cuid())
|
|
noteId String
|
|
fileName String
|
|
fileType String
|
|
fileSize Int
|
|
filePath String
|
|
mimeType String
|
|
status String @default("pending")
|
|
pageCount Int?
|
|
error String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
|
|
chunks DocumentChunk[]
|
|
|
|
@@index([noteId])
|
|
@@index([status])
|
|
}
|
|
|
|
model DocumentChunk {
|
|
id String @id @default(cuid())
|
|
attachmentId String
|
|
content String
|
|
chunkIndex Int
|
|
pageNumber Int?
|
|
startChar Int?
|
|
endChar Int?
|
|
metadata String?
|
|
embedding Unsupported("vector(1536)")?
|
|
createdAt DateTime @default(now())
|
|
attachment NoteAttachment @relation(fields: [attachmentId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([attachmentId])
|
|
@@index([attachmentId, chunkIndex])
|
|
}
|
|
|
|
// ===== BYOK (Story 3.5) =====
|
|
|
|
model UserAPIKey {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
provider String
|
|
alias String @default("")
|
|
encryptedKey String
|
|
keyHash String
|
|
model String?
|
|
isActive Boolean @default(true)
|
|
lastUsedAt DateTime?
|
|
lastUsedFor String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, provider])
|
|
@@index([userId])
|
|
@@index([keyHash])
|
|
}
|
|
|
|
// ===== SUBSCRIPTION MODELS =====
|
|
|
|
enum SubscriptionTier {
|
|
BASIC
|
|
PRO
|
|
BUSINESS
|
|
ENTERPRISE
|
|
}
|
|
|
|
enum SubscriptionStatus {
|
|
ACTIVE
|
|
PAST_DUE
|
|
CANCELED
|
|
TRIALING
|
|
INACTIVE
|
|
}
|
|
|
|
model Subscription {
|
|
id String @id @default(cuid())
|
|
userId String @unique
|
|
tier SubscriptionTier @default(BASIC)
|
|
status SubscriptionStatus @default(ACTIVE)
|
|
|
|
stripeCustomerId String? @unique
|
|
stripeSubscriptionId String? @unique
|
|
stripePriceId String?
|
|
|
|
trialEndsAt DateTime?
|
|
currentPeriodStart DateTime
|
|
currentPeriodEnd DateTime
|
|
|
|
canceledAt DateTime?
|
|
cancelAtPeriodEnd Boolean @default(false)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model UsageLog {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
feature String
|
|
periodStart DateTime
|
|
periodEnd DateTime
|
|
requestsCount Int @default(0)
|
|
tokensUsed Int @default(0)
|
|
|
|
syncedAt DateTime?
|
|
metadata String?
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, feature, periodStart])
|
|
@@index([userId, periodStart])
|
|
@@index([periodStart])
|
|
}
|
|
|
|
model FeatureFlag {
|
|
id String @id @default(cuid())
|
|
key String @unique
|
|
enabled Boolean @default(false)
|
|
tiers String[]
|
|
metadata String?
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
// ===== CLUSTERING & BRIDGE NOTES =====
|
|
|
|
model NoteCluster {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
clusterId Int // Cluster number for this user (0, 1, 2, ...)
|
|
name String? // Auto-generated cluster name
|
|
centroid Unsupported("vector(1536)")? // Optional: centroid embedding for cluster
|
|
noteCount Int @default(0)
|
|
lastCalculated DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([userId, clusterId])
|
|
@@unique([userId, clusterId])
|
|
}
|
|
|
|
model ClusterMember {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
noteId String
|
|
clusterId Int // Cluster number
|
|
membershipScore Float @default(0.0) // How strongly this note belongs to the cluster
|
|
isCentral Boolean @default(false) // Is this a central note in the cluster?
|
|
createdAt DateTime @default(now())
|
|
|
|
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId, clusterId])
|
|
@@index([noteId])
|
|
@@unique([noteId, clusterId])
|
|
}
|
|
|
|
model BridgeNote {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
noteId String @unique
|
|
bridgeScore Float // len(clusters_touched) / max_clusters
|
|
clustersConnected String // JSON array: ["0", "2", "5"]
|
|
lastCalculated DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([bridgeScore])
|
|
}
|
|
|
|
model BridgeSuggestion {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
clusterAId Int
|
|
clusterBId Int
|
|
clusterAName String
|
|
clusterBName String
|
|
suggestedTitle String
|
|
suggestedContent String // @default(dbgenerated)
|
|
justification String // Why this connection makes sense
|
|
isDismissed Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([userId, isDismissed])
|
|
@@index([clusterAId, clusterBId])
|
|
}
|
|
|
|
model NotebookSchema {
|
|
id String @id @default(cuid())
|
|
notebookId String @unique
|
|
viewSettings String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
notebook Notebook @relation(fields: [notebookId], references: [id], onDelete: Cascade)
|
|
properties NotebookProperty[]
|
|
}
|
|
|
|
model NotebookProperty {
|
|
id String @id @default(cuid())
|
|
schemaId String
|
|
name String
|
|
type String
|
|
options String?
|
|
position Int
|
|
schema NotebookSchema @relation(fields: [schemaId], references: [id], onDelete: Cascade)
|
|
noteValues NoteProperty[]
|
|
|
|
@@index([schemaId, position])
|
|
}
|
|
|
|
model NoteProperty {
|
|
id String @id @default(cuid())
|
|
noteId String
|
|
propertyId String
|
|
value String?
|
|
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
|
|
property NotebookProperty @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([noteId, propertyId])
|
|
@@index([noteId])
|
|
@@index([propertyId])
|
|
}
|
|
|
|
model FlashcardDeck {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
notebookId String? @unique
|
|
name String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
flashcards Flashcard[]
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
notebook Notebook? @relation(fields: [notebookId], references: [id], onDelete: SetNull)
|
|
|
|
@@index([userId])
|
|
}
|
|
|
|
model Flashcard {
|
|
id String @id @default(cuid())
|
|
deckId String
|
|
noteId String?
|
|
front String
|
|
back String
|
|
type String @default("qa")
|
|
interval Int @default(1)
|
|
easinessFactor Float @default(2.5)
|
|
nextReviewAt DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
deck FlashcardDeck @relation(fields: [deckId], references: [id], onDelete: Cascade)
|
|
note Note? @relation(fields: [noteId], references: [id], onDelete: SetNull)
|
|
reviews FlashcardReview[]
|
|
|
|
@@index([deckId])
|
|
@@index([noteId])
|
|
@@index([nextReviewAt])
|
|
}
|
|
|
|
model FlashcardReview {
|
|
id String @id @default(cuid())
|
|
cardId String
|
|
grade Int
|
|
reviewedAt DateTime @default(now())
|
|
card Flashcard @relation(fields: [cardId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([cardId])
|
|
@@index([reviewedAt])
|
|
}
|