feat: rename keep-notes to memento-note, migrate to PostgreSQL, fix MCP bugs

- Rename directory keep-notes -> memento-note with all code references
- Prisma: SQLite -> PostgreSQL (both app and MCP server schemas)
- Sync MCP schema with main app (add missing fields, relations, indexes)
- Delete 17 SQLite migrations (clean slate for PostgreSQL)
- Remove SQLite dependencies (@libsql/client, better-sqlite3, etc.)
- Fix MCP server: hardcoded Windows DB paths -> DATABASE_URL env var
- Fix MCP server: .dockerignore excluded index-sse.js (SSE mode broken)
- MCP Dockerfile: node:20 -> node:22
- Docker Compose: add postgres service, remove SQLite volume
- Generate favicon.ico, icon-192.png, icon-512.png, apple-icon.png
- Update layout.tsx icons and manifest.json for PNG icons
- Update all .env files for PostgreSQL
- Rewrite README.md with updated sections
- Remove mcp-server/node_modules and prisma/client-generated from git tracking

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Sepehr Ramezani
2026-04-20 20:58:04 +02:00
parent e2ddfa53d4
commit aa6a214f37
3548 changed files with 440 additions and 516800 deletions

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

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,20 @@
-- CreateTable
CREATE TABLE "Note" (
"id" TEXT NOT NULL PRIMARY KEY,
"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',
"checkItems" JSONB,
"labels" JSONB,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- CreateIndex
CREATE INDEX "Note_isPinned_idx" ON "Note"("isPinned");
-- CreateIndex
CREATE INDEX "Note_isArchived_idx" ON "Note"("isArchived");

View File

@@ -0,0 +1,23 @@
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Note" (
"id" TEXT NOT NULL PRIMARY KEY,
"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',
"checkItems" TEXT,
"labels" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
INSERT INTO "new_Note" ("checkItems", "color", "content", "createdAt", "id", "isArchived", "isPinned", "labels", "title", "type", "updatedAt") SELECT "checkItems", "color", "content", "createdAt", "id", "isArchived", "isPinned", "labels", "title", "type", "updatedAt" FROM "Note";
DROP TABLE "Note";
ALTER TABLE "new_Note" RENAME TO "Note";
CREATE INDEX "Note_isPinned_idx" ON "Note"("isPinned");
CREATE INDEX "Note_isArchived_idx" ON "Note"("isArchived");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;

View File

@@ -0,0 +1,25 @@
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Note" (
"id" TEXT NOT NULL PRIMARY KEY,
"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',
"checkItems" TEXT,
"labels" TEXT,
"order" INTEGER NOT NULL DEFAULT 0,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
INSERT INTO "new_Note" ("checkItems", "color", "content", "createdAt", "id", "isArchived", "isPinned", "labels", "title", "type", "updatedAt") SELECT "checkItems", "color", "content", "createdAt", "id", "isArchived", "isPinned", "labels", "title", "type", "updatedAt" FROM "Note";
DROP TABLE "Note";
ALTER TABLE "new_Note" RENAME TO "Note";
CREATE INDEX "Note_isPinned_idx" ON "Note"("isPinned");
CREATE INDEX "Note_isArchived_idx" ON "Note"("isArchived");
CREATE INDEX "Note_order_idx" ON "Note"("order");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Note" ADD COLUMN "images" TEXT;

View File

@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "Note" ADD COLUMN "reminder" DATETIME;
-- CreateIndex
CREATE INDEX "Note_reminder_idx" ON "Note"("reminder");

View File

@@ -0,0 +1,29 @@
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Note" (
"id" TEXT NOT NULL PRIMARY KEY,
"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',
"checkItems" TEXT,
"labels" TEXT,
"images" TEXT,
"reminder" DATETIME,
"isMarkdown" BOOLEAN NOT NULL DEFAULT false,
"order" INTEGER NOT NULL DEFAULT 0,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
INSERT INTO "new_Note" ("checkItems", "color", "content", "createdAt", "id", "images", "isArchived", "isPinned", "labels", "order", "reminder", "title", "type", "updatedAt") SELECT "checkItems", "color", "content", "createdAt", "id", "images", "isArchived", "isPinned", "labels", "order", "reminder", "title", "type", "updatedAt" FROM "Note";
DROP TABLE "Note";
ALTER TABLE "new_Note" RENAME TO "Note";
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");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;

View File

@@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Note" ADD COLUMN "reminderLocation" TEXT;
ALTER TABLE "Note" ADD COLUMN "reminderRecurrence" TEXT;

View File

@@ -0,0 +1,90 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT,
"email" TEXT NOT NULL,
"emailVerified" DATETIME,
"image" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL
);
-- 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" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
PRIMARY KEY ("provider", "providerAccountId"),
CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "Session" (
"sessionToken" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"expires" DATETIME NOT NULL,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateTable
CREATE TABLE "VerificationToken" (
"identifier" TEXT NOT NULL,
"token" TEXT NOT NULL,
"expires" DATETIME NOT NULL,
PRIMARY KEY ("identifier", "token")
);
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Note" (
"id" TEXT NOT NULL PRIMARY KEY,
"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',
"checkItems" TEXT,
"labels" TEXT,
"images" TEXT,
"reminder" DATETIME,
"reminderRecurrence" TEXT,
"reminderLocation" TEXT,
"isMarkdown" BOOLEAN NOT NULL DEFAULT false,
"userId" TEXT,
"order" INTEGER NOT NULL DEFAULT 0,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Note_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_Note" ("checkItems", "color", "content", "createdAt", "id", "images", "isArchived", "isMarkdown", "isPinned", "labels", "order", "reminder", "reminderLocation", "reminderRecurrence", "title", "type", "updatedAt") SELECT "checkItems", "color", "content", "createdAt", "id", "images", "isArchived", "isMarkdown", "isPinned", "labels", "order", "reminder", "reminderLocation", "reminderRecurrence", "title", "type", "updatedAt" FROM "Note";
DROP TABLE "Note";
ALTER TABLE "new_Note" RENAME TO "Note";
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");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");

View File

@@ -0,0 +1,16 @@
-- CreateTable
CREATE TABLE "Label" (
"id" TEXT NOT NULL PRIMARY KEY,
"name" TEXT NOT NULL,
"color" TEXT NOT NULL DEFAULT 'gray',
"userId" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Label_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "Label_name_key" ON "Label"("name");
-- CreateIndex
CREATE INDEX "Label_userId_idx" ON "Label"("userId");

View File

@@ -0,0 +1,35 @@
-- RedefineTables
PRAGMA defer_foreign_keys=ON;
PRAGMA foreign_keys=OFF;
CREATE TABLE "new_Note" (
"id" TEXT NOT NULL PRIMARY KEY,
"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',
"checkItems" TEXT,
"labels" TEXT,
"images" TEXT,
"reminder" DATETIME,
"isReminderDone" BOOLEAN NOT NULL DEFAULT false,
"reminderRecurrence" TEXT,
"reminderLocation" TEXT,
"isMarkdown" BOOLEAN NOT NULL DEFAULT false,
"userId" TEXT,
"order" INTEGER NOT NULL DEFAULT 0,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
CONSTRAINT "Note_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
);
INSERT INTO "new_Note" ("checkItems", "color", "content", "createdAt", "id", "images", "isArchived", "isMarkdown", "isPinned", "labels", "order", "reminder", "reminderLocation", "reminderRecurrence", "title", "type", "updatedAt", "userId") SELECT "checkItems", "color", "content", "createdAt", "id", "images", "isArchived", "isMarkdown", "isPinned", "labels", "order", "reminder", "reminderLocation", "reminderRecurrence", "title", "type", "updatedAt", "userId" FROM "Note";
DROP TABLE "Note";
ALTER TABLE "new_Note" RENAME TO "Note";
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");
PRAGMA foreign_keys=ON;
PRAGMA defer_foreign_keys=OFF;

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Note" ADD COLUMN "links" TEXT;

View File

@@ -0,0 +1,14 @@
/*
Warnings:
- A unique constraint covering the columns `[name,userId]` on the table `Label` will be added. If there are existing duplicate values, this will fail.
*/
-- DropIndex
DROP INDEX "Label_name_key";
-- AlterTable
ALTER TABLE "User" ADD COLUMN "password" TEXT;
-- CreateIndex
CREATE UNIQUE INDEX "Label_name_userId_key" ON "Label"("name", "userId");

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Note" ADD COLUMN "embedding" TEXT;

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "UserAISettings" ADD COLUMN "showRecentNotes" BOOLEAN NOT NULL DEFAULT false;

View File

@@ -0,0 +1,7 @@
-- AlterTable
ALTER TABLE "Note" ADD COLUMN "autoGenerated" BOOLEAN;
ALTER TABLE "Note" ADD COLUMN "aiProvider" TEXT;
ALTER TABLE "Note" ADD COLUMN "aiConfidence" INTEGER;
ALTER TABLE "Note" ADD COLUMN "language" TEXT;
ALTER TABLE "Note" ADD COLUMN "languageConfidence" REAL;
ALTER TABLE "Note" ADD COLUMN "lastAiAnalysis" DATETIME;

View File

@@ -0,0 +1,26 @@
-- CreateTable
CREATE TABLE "AiFeedback" (
"id" TEXT NOT NULL PRIMARY KEY,
"noteId" TEXT NOT NULL,
"userId" TEXT,
"feedbackType" TEXT NOT NULL,
"feature" TEXT NOT NULL,
"originalContent" TEXT NOT NULL,
"correctedContent" TEXT,
"metadata" TEXT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY ("noteId") REFERENCES "Note"("id") ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE
);
-- CreateIndex
CREATE INDEX "AiFeedback_noteId_idx" ON "AiFeedback"("noteId");
-- CreateIndex
CREATE INDEX "AiFeedback_userId_idx" ON "AiFeedback"("userId");
-- CreateIndex
CREATE INDEX "AiFeedback_feature_idx" ON "AiFeedback"("feature");
-- CreateIndex
CREATE INDEX "AiFeedback_createdAt_idx" ON "AiFeedback"("createdAt");

View File

@@ -0,0 +1,4 @@
-- AlterTable
ALTER TABLE "UserAISettings" ADD COLUMN "emailNotifications" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "UserAISettings" ADD COLUMN "desktopNotifications" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "UserAISettings" ADD COLUMN "anonymousAnalytics" BOOLEAN NOT NULL DEFAULT false;

View File

@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"

View File

@@ -0,0 +1,240 @@
generator client {
provider = "prisma-client-js"
output = "./client-generated"
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
accounts Account[]
aiFeedback AiFeedback[]
labels Label[]
memoryEchoInsights MemoryEchoInsight[]
notes Note[]
sentShares NoteShare[] @relation("SentShares")
receivedShares NoteShare[] @relation("ReceivedShares")
notebooks Notebook[]
sessions Session[]
aiSettings UserAISettings?
}
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[]
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)
type String @default("text")
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")
embedding String?
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")
@@index([isPinned])
@@index([isArchived])
@@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(false)
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])
}