chore: clean up repo for public release

- Remove BMAD framework, IDE configs, dev screenshots, test files,
  internal docs, and backup files
- Rename keep-notes/ to memento-note/
- Update all references from keep-notes to memento-note
- Add Apache 2.0 license with Commons Clause (non-commercial restriction)
- Add clean .gitignore and .env.docker.example
This commit is contained in:
Sepehr Ramezani
2026-04-20 22:48:06 +02:00
parent 402e88b788
commit e4d4e23dc7
3981 changed files with 407 additions and 530622 deletions

BIN
memento-note/prisma/dev.db Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,256 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"name" TEXT,
"email" TEXT NOT NULL,
"emailVerified" TIMESTAMP(3),
"password" TEXT,
"role" TEXT NOT NULL DEFAULT 'USER',
"image" TEXT,
"theme" TEXT NOT NULL DEFAULT 'light',
"resetToken" TEXT,
"resetTokenExpiry" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Account" (
"userId" TEXT NOT NULL,
"type" TEXT NOT NULL,
"provider" TEXT NOT NULL,
"providerAccountId" TEXT NOT NULL,
"refresh_token" TEXT,
"access_token" TEXT,
"expires_at" INTEGER,
"token_type" TEXT,
"scope" TEXT,
"id_token" TEXT,
"session_state" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Account_pkey" PRIMARY KEY ("provider","providerAccountId")
);
-- CreateTable
CREATE TABLE "Session" (
"sessionToken" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"expires" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Session_pkey" PRIMARY KEY ("sessionToken")
);
-- CreateTable
CREATE TABLE "VerificationToken" (
"identifier" TEXT NOT NULL,
"token" TEXT NOT NULL,
"expires" TIMESTAMP(3) NOT NULL,
CONSTRAINT "VerificationToken_pkey" PRIMARY KEY ("identifier","token")
);
-- CreateTable
CREATE TABLE "Notebook" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"icon" TEXT,
"color" TEXT,
"order" INTEGER NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Notebook_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Label" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"color" TEXT NOT NULL DEFAULT 'gray',
"notebookId" TEXT,
"userId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Label_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Note" (
"id" TEXT NOT NULL,
"title" TEXT,
"content" TEXT NOT NULL,
"color" TEXT NOT NULL DEFAULT 'default',
"isPinned" BOOLEAN NOT NULL DEFAULT false,
"isArchived" BOOLEAN NOT NULL DEFAULT false,
"type" TEXT NOT NULL DEFAULT 'text',
"dismissedFromRecent" BOOLEAN NOT NULL DEFAULT false,
"checkItems" JSONB,
"labels" JSONB,
"images" JSONB,
"links" JSONB,
"reminder" TIMESTAMP(3),
"isReminderDone" BOOLEAN NOT NULL DEFAULT false,
"reminderRecurrence" TEXT,
"reminderLocation" TEXT,
"isMarkdown" BOOLEAN NOT NULL DEFAULT false,
"size" TEXT NOT NULL DEFAULT 'small',
"embedding" JSONB,
"sharedWith" JSONB,
"userId" TEXT,
"order" INTEGER NOT NULL DEFAULT 0,
"notebookId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"contentUpdatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"autoGenerated" BOOLEAN,
"aiProvider" TEXT,
"aiConfidence" INTEGER,
"language" TEXT,
"languageConfidence" DOUBLE PRECISION,
"lastAiAnalysis" TIMESTAMP(3),
CONSTRAINT "Note_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "NoteShare" (
"id" TEXT NOT NULL,
"noteId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"sharedBy" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'pending',
"permission" TEXT NOT NULL DEFAULT 'view',
"notifiedAt" TIMESTAMP(3),
"respondedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "NoteShare_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "SystemConfig" (
"key" TEXT NOT NULL,
"value" TEXT NOT NULL,
CONSTRAINT "SystemConfig_pkey" PRIMARY KEY ("key")
);
-- CreateTable
CREATE TABLE "AiFeedback" (
"id" TEXT NOT NULL,
"noteId" TEXT NOT NULL,
"userId" TEXT,
"feedbackType" TEXT NOT NULL,
"feature" TEXT NOT NULL,
"originalContent" TEXT NOT NULL,
"correctedContent" TEXT,
"metadata" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AiFeedback_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "MemoryEchoInsight" (
"id" TEXT NOT NULL,
"userId" TEXT,
"note1Id" TEXT NOT NULL,
"note2Id" TEXT NOT NULL,
"similarityScore" DOUBLE PRECISION NOT NULL,
"insight" TEXT NOT NULL,
"insightDate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"viewed" BOOLEAN NOT NULL DEFAULT false,
"feedback" TEXT,
"dismissed" BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT "MemoryEchoInsight_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "UserAISettings" (
"userId" TEXT NOT NULL,
"titleSuggestions" BOOLEAN NOT NULL DEFAULT true,
"semanticSearch" BOOLEAN NOT NULL DEFAULT true,
"paragraphRefactor" BOOLEAN NOT NULL DEFAULT true,
"memoryEcho" BOOLEAN NOT NULL DEFAULT true,
"memoryEchoFrequency" TEXT NOT NULL DEFAULT 'daily',
"aiProvider" TEXT NOT NULL DEFAULT 'auto',
"preferredLanguage" TEXT NOT NULL DEFAULT 'auto',
"fontSize" TEXT NOT NULL DEFAULT 'medium',
"demoMode" BOOLEAN NOT NULL DEFAULT false,
"showRecentNotes" BOOLEAN NOT NULL DEFAULT true,
"notesViewMode" TEXT NOT NULL DEFAULT 'masonry',
"emailNotifications" BOOLEAN NOT NULL DEFAULT false,
"desktopNotifications" BOOLEAN NOT NULL DEFAULT false,
"anonymousAnalytics" BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT "UserAISettings_pkey" PRIMARY KEY ("userId")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
CREATE UNIQUE INDEX "User_resetToken_key" ON "User"("resetToken");
CREATE UNIQUE INDEX "Label_notebookId_name_key" ON "Label"("notebookId", "name");
CREATE UNIQUE INDEX "NoteShare_noteId_userId_key" ON "NoteShare"("noteId", "userId");
CREATE UNIQUE INDEX "MemoryEchoInsight_userId_insightDate_key" ON "MemoryEchoInsight"("userId", "insightDate");
-- CreateIndex (performance)
CREATE INDEX "Notebook_userId_order_idx" ON "Notebook"("userId", "order");
CREATE INDEX "Notebook_userId_idx" ON "Notebook"("userId");
CREATE INDEX "Label_notebookId_idx" ON "Label"("notebookId");
CREATE INDEX "Label_userId_idx" ON "Label"("userId");
CREATE INDEX "Note_isPinned_idx" ON "Note"("isPinned");
CREATE INDEX "Note_isArchived_idx" ON "Note"("isArchived");
CREATE INDEX "Note_order_idx" ON "Note"("order");
CREATE INDEX "Note_reminder_idx" ON "Note"("reminder");
CREATE INDEX "Note_userId_idx" ON "Note"("userId");
CREATE INDEX "Note_userId_notebookId_idx" ON "Note"("userId", "notebookId");
CREATE INDEX "NoteShare_userId_idx" ON "NoteShare"("userId");
CREATE INDEX "NoteShare_status_idx" ON "NoteShare"("status");
CREATE INDEX "NoteShare_sharedBy_idx" ON "NoteShare"("sharedBy");
CREATE INDEX "AiFeedback_noteId_idx" ON "AiFeedback"("noteId");
CREATE INDEX "AiFeedback_userId_idx" ON "AiFeedback"("userId");
CREATE INDEX "AiFeedback_feature_idx" ON "AiFeedback"("feature");
CREATE INDEX "MemoryEchoInsight_userId_insightDate_idx" ON "MemoryEchoInsight"("userId", "insightDate");
CREATE INDEX "MemoryEchoInsight_userId_dismissed_idx" ON "MemoryEchoInsight"("userId", "dismissed");
CREATE INDEX "UserAISettings_memoryEcho_idx" ON "UserAISettings"("memoryEcho");
CREATE INDEX "UserAISettings_aiProvider_idx" ON "UserAISettings"("aiProvider");
CREATE INDEX "UserAISettings_memoryEchoFrequency_idx" ON "UserAISettings"("memoryEchoFrequency");
CREATE INDEX "UserAISettings_preferredLanguage_idx" ON "UserAISettings"("preferredLanguage");
-- _LabelToNote (many-to-many between Note and Label)
CREATE TABLE "_LabelToNote" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_LabelToNote_A_fkey" FOREIGN KEY ("A") REFERENCES "Label"("id") ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT "_LabelToNote_B_fkey" FOREIGN KEY ("B") REFERENCES "Note"("id") ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE UNIQUE INDEX "_LabelToNote_AB_unique" ON "_LabelToNote"("A", "B");
CREATE INDEX "_LabelToNote_B_index" ON "_LabelToNote"("B");
-- Foreign Keys
ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Notebook" ADD CONSTRAINT "Notebook_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Label" ADD CONSTRAINT "Label_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Label" ADD CONSTRAINT "Label_notebookId_fkey" FOREIGN KEY ("notebookId") REFERENCES "Notebook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Note" ADD CONSTRAINT "Note_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "Note" ADD CONSTRAINT "Note_notebookId_fkey" FOREIGN KEY ("notebookId") REFERENCES "Notebook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "NoteShare" ADD CONSTRAINT "NoteShare_noteId_fkey" FOREIGN KEY ("noteId") REFERENCES "Note"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "NoteShare" ADD CONSTRAINT "NoteShare_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "NoteShare" ADD CONSTRAINT "NoteShare_sharedBy_fkey" FOREIGN KEY ("sharedBy") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "AiFeedback" ADD CONSTRAINT "AiFeedback_noteId_fkey" FOREIGN KEY ("noteId") REFERENCES "Note"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "AiFeedback" ADD CONSTRAINT "AiFeedback_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MemoryEchoInsight" ADD CONSTRAINT "MemoryEchoInsight_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MemoryEchoInsight" ADD CONSTRAINT "MemoryEchoInsight_note1Id_fkey" FOREIGN KEY ("note1Id") REFERENCES "Note"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "MemoryEchoInsight" ADD CONSTRAINT "MemoryEchoInsight_note2Id_fkey" FOREIGN KEY ("note2Id") REFERENCES "Note"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "UserAISettings" ADD CONSTRAINT "UserAISettings_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1 @@
provider = "postgresql"

View File

@@ -0,0 +1,381 @@
generator client {
provider = "prisma-client-js"
binaryTargets = ["debian-openssl-1.1.x", "native"]
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
name String?
email String @unique
emailVerified DateTime?
password String?
role String @default("USER")
image String?
theme String @default("light")
cardSizeMode String @default("variable")
resetToken String? @unique
resetTokenExpiry DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
accounts Account[]
aiFeedback AiFeedback[]
labels Label[]
memoryEchoInsights MemoryEchoInsight[]
notes Note[]
sentShares NoteShare[] @relation("SentShares")
receivedShares NoteShare[] @relation("ReceivedShares")
notebooks Notebook[]
sessions Session[]
aiSettings UserAISettings?
agents Agent[]
workflows Workflow[]
conversations Conversation[]
canvases Canvas[]
}
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
labels Label[]
notes Note[]
agents Agent[]
workflows Workflow[]
conversations Conversation[]
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, order])
@@index([userId])
}
model Label {
id String @id @default(cuid())
name String
color String @default("gray")
notebookId String?
userId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
notebook Notebook? @relation(fields: [notebookId], 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)
trashedAt DateTime?
type String @default("text")
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?
aiFeedback AiFeedback[]
memoryEchoAsNote2 MemoryEchoInsight[] @relation("EchoNote2")
memoryEchoAsNote1 MemoryEchoInsight[] @relation("EchoNote1")
notebook Notebook? @relation(fields: [notebookId], references: [id])
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
shares NoteShare[]
labelRelations Label[] @relation("LabelToNote")
noteEmbedding NoteEmbedding?
@@index([isPinned])
@@index([isArchived])
@@index([trashedAt])
@@index([order])
@@index([reminder])
@@index([userId])
@@index([userId, notebookId])
}
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
sharer User @relation("SentShares", fields: [sharedBy], references: [id], onDelete: Cascade)
user User @relation("ReceivedShares", fields: [userId], references: [id], onDelete: Cascade)
note Note @relation(fields: [noteId], 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())
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
@@index([noteId])
@@index([userId])
@@index([feature])
}
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)
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
note2 Note @relation("EchoNote2", fields: [note2Id], references: [id], onDelete: Cascade)
note1 Note @relation("EchoNote1", fields: [note1Id], 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)
/// "masonry" = grille cartes Muuri ; "tabs" = onglets + panneau (type OneNote). Ancienne valeur "list" migrée vers "tabs" en lecture.
notesViewMode String @default("masonry")
emailNotifications Boolean @default(false)
desktopNotifications Boolean @default(false)
anonymousAnalytics Boolean @default(false)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([memoryEcho])
@@index([aiProvider])
@@index([memoryEchoFrequency])
@@index([preferredLanguage])
}
model NoteEmbedding {
id String @id @default(cuid())
noteId String @unique
embedding String
createdAt DateTime @default(now())
note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
@@index([noteId])
}
// ============================================================================
// NEW MODELS FOR AGENTIC SYSTEM & LAB
// ============================================================================
model Agent {
id String @id @default(cuid())
name String
description String?
type String? @default("scraper") // scraper, researcher, monitor, custom
role String // System prompt / Persona
sourceUrls String? // JSON list of URLs to scrape
frequency String @default("manual") // manual, hourly, daily, weekly, monthly
lastRun DateTime?
nextRun DateTime?
isEnabled Boolean @default(true)
targetNotebookId String?
sourceNotebookId String? // For monitor type: notebook to watch
tools String? @default("[]") // JSON array: ["web_search", "note_search", ...]
maxSteps Int @default(10) // Max tool-use iterations
notifyEmail Boolean @default(false) // Send email notification after execution
includeImages Boolean @default(false) // Extract images from scraped pages
userId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
notebook Notebook? @relation(fields: [targetNotebookId], references: [id])
actions AgentAction[]
@@index([userId])
@@index([isEnabled])
}
model AgentAction {
id String @id @default(cuid())
agentId String
status String @default("pending") // pending, running, success, failure
result String? // ID of the created note or summary message
log String? // Error message or execution log
input String? // JSON: prompt initial + config
toolLog String? // JSON: trace [{step, toolCalls, toolResults}]
tokensUsed Int? // Token consumption
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? // Optional context
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
notebook Notebook? @relation(fields: [notebookId], references: [id])
messages ChatMessage[]
@@index([userId])
@@index([notebookId])
}
model ChatMessage {
id String @id @default(cuid())
conversationId String
role String // user, assistant, system
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 // Large JSON blob for tldraw state
userId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
}
// ============================================================================
// VISUAL WORKFLOW BUILDER
// ============================================================================
model Workflow {
id String @id @default(cuid())
name String
description String?
graph String @default("{\"nodes\":[],\"edges\":[]}") // JSON: React Flow nodes[] + edges[]
isEnabled Boolean @default(true)
userId String
notebookId String? // default target notebook for Create Note nodes
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
notebook Notebook? @relation(fields: [notebookId], references: [id])
runs WorkflowRun[]
@@index([userId])
@@index([isEnabled])
}
model WorkflowRun {
id String @id @default(cuid())
workflowId String
status String @default("running") // running, success, failure
log String? // JSON: per-node status + timing
createdAt DateTime @default(now())
workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade)
@@index([workflowId])
@@index([status])
}