Add safe database migration workflow and note history infrastructure.
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 5s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 5s
This introduces guarded migrations with automatic backups, fixes note creation after DB reset, and wires snapshot/restore history across notes surfaces.
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "NoteHistory" (
|
||||
"id" TEXT NOT NULL,
|
||||
"noteId" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"version" INTEGER NOT NULL,
|
||||
"reason" TEXT,
|
||||
"title" TEXT,
|
||||
"content" TEXT NOT NULL,
|
||||
"color" TEXT NOT NULL,
|
||||
"isPinned" BOOLEAN NOT NULL,
|
||||
"isArchived" BOOLEAN NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"checkItems" TEXT,
|
||||
"labels" TEXT,
|
||||
"images" TEXT,
|
||||
"links" TEXT,
|
||||
"isMarkdown" BOOLEAN NOT NULL,
|
||||
"size" TEXT NOT NULL,
|
||||
"notebookId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "NoteHistory_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "UserAISettings"
|
||||
ADD COLUMN "noteHistory" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "NoteHistory_noteId_version_key" ON "NoteHistory"("noteId", "version");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "NoteHistory_noteId_createdAt_idx" ON "NoteHistory"("noteId", "createdAt" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "NoteHistory_userId_noteId_createdAt_idx" ON "NoteHistory"("userId", "noteId", "createdAt" DESC);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "NoteHistory" ADD CONSTRAINT "NoteHistory_noteId_fkey" FOREIGN KEY ("noteId") REFERENCES "Note"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "NoteHistory" ADD CONSTRAINT "NoteHistory_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
409
Momento-main/momento/memento-note/prisma/schema.prisma
Normal file
409
Momento-main/momento/memento-note/prisma/schema.prisma
Normal file
@@ -0,0 +1,409 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
binaryTargets = ["debian-openssl-3.0.x", "native"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
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")
|
||||
resetToken String? @unique
|
||||
resetTokenExpiry DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
cardSizeMode String @default("variable")
|
||||
accounts Account[]
|
||||
agents Agent[]
|
||||
aiFeedback AiFeedback[]
|
||||
canvases Canvas[]
|
||||
conversations Conversation[]
|
||||
labels Label[]
|
||||
memoryEchoInsights MemoryEchoInsight[]
|
||||
notes Note[]
|
||||
noteHistories NoteHistory[]
|
||||
sentShares NoteShare[] @relation("SentShares")
|
||||
receivedShares NoteShare[] @relation("ReceivedShares")
|
||||
notebooks Notebook[]
|
||||
sessions Session[]
|
||||
aiSettings UserAISettings?
|
||||
workflows Workflow[]
|
||||
}
|
||||
|
||||
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
|
||||
agents Agent[]
|
||||
conversations Conversation[]
|
||||
labels Label[]
|
||||
notes Note[]
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
workflows Workflow[]
|
||||
|
||||
@@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
|
||||
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("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?
|
||||
trashedAt DateTime?
|
||||
aiFeedback AiFeedback[]
|
||||
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?
|
||||
shares NoteShare[]
|
||||
labelRelations Label[] @relation("LabelToNote")
|
||||
historyEntries NoteHistory[]
|
||||
|
||||
@@index([isPinned])
|
||||
@@index([isArchived])
|
||||
@@index([trashedAt])
|
||||
@@index([order])
|
||||
@@index([reminder])
|
||||
@@index([userId])
|
||||
@@index([userId, notebookId])
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
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)
|
||||
/// "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)
|
||||
autoLabeling Boolean @default(true)
|
||||
noteHistory Boolean @default(false)
|
||||
languageDetection Boolean @default(true)
|
||||
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])
|
||||
}
|
||||
|
||||
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?
|
||||
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])
|
||||
}
|
||||
Reference in New Issue
Block a user