ocean-brain 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/index.js +73 -41
  2. package/dist/mcp.js +587 -26
  3. package/package.json +6 -1
  4. package/scripts/postinstall-prisma.mjs +41 -0
  5. package/server/client/dist/assets/Calendar-DRv2aEl8.js +28 -0
  6. package/server/client/dist/assets/Callout-B2kPcDrq.js +1 -0
  7. package/server/client/dist/assets/Graph-DotbTrZR.js +1 -0
  8. package/server/client/dist/assets/Image-B5R7gF2P.js +1 -0
  9. package/server/client/dist/assets/Image.es-Dld9r9MJ.js +1 -0
  10. package/server/client/dist/assets/ModalActionRow-CES9L_Gy.js +1 -0
  11. package/server/client/dist/assets/Note-SK4hDhEM.css +1 -0
  12. package/server/client/dist/assets/Note-cIbXJc1v.js +1 -0
  13. package/server/client/dist/assets/Reminders-4LunlZRb.js +1 -0
  14. package/server/client/dist/assets/Search-Cp17DGR6.js +1 -0
  15. package/server/client/dist/assets/SurfaceCard-yzVU4Qkx.js +1 -0
  16. package/server/client/dist/assets/Tag-CwmuMPmo.js +1 -0
  17. package/server/client/dist/assets/TagNotes-gGroWMtT.js +1 -0
  18. package/server/client/dist/assets/Trash.es-B3KWD8_C.js +1 -0
  19. package/server/client/dist/assets/color-wpu7wT_s.js +1 -0
  20. package/server/client/dist/assets/graph-vendor-CUxe67Lr.js +46 -0
  21. package/server/client/dist/assets/image.api-DJgOBt8S.js +17 -0
  22. package/server/client/dist/assets/index-40Y2UsVr.js +205 -0
  23. package/server/client/dist/assets/index-BnxpTwtL.css +1 -0
  24. package/server/client/dist/assets/index-DYUg2N-6.js +1 -0
  25. package/server/client/dist/assets/manage-image-ByY1mRZx.js +1 -0
  26. package/server/client/dist/assets/manage-image-detail-BVaDZHRu.js +1 -0
  27. package/server/client/dist/assets/mcp-Jj3061aI.js +1 -0
  28. package/server/client/dist/assets/{note-core-DTIu0anW.js → note-core-BCgMq5QA.js} +7 -7
  29. package/server/client/dist/assets/note-vendor-BofYbzmZ.js +54 -0
  30. package/server/client/dist/assets/placeholder-4P_GK7fV.js +34 -0
  31. package/server/client/dist/assets/trash-CPBwY07S.js +1 -0
  32. package/server/client/dist/assets/useReminderMutate-1YVndM2T.js +82 -0
  33. package/server/client/dist/index.html +5 -4
  34. package/server/dist/app.js +97 -9
  35. package/server/dist/app.js.map +1 -1
  36. package/server/dist/main.js +16 -2
  37. package/server/dist/main.js.map +1 -1
  38. package/server/dist/modules/auth-guard.js +73 -0
  39. package/server/dist/modules/auth-guard.js.map +1 -0
  40. package/server/dist/modules/auth-mode.js +51 -0
  41. package/server/dist/modules/auth-mode.js.map +1 -0
  42. package/server/dist/modules/auth.js +9 -0
  43. package/server/dist/modules/auth.js.map +1 -1
  44. package/server/dist/modules/blocknote.js +7 -1
  45. package/server/dist/modules/blocknote.js.map +1 -1
  46. package/server/dist/modules/error-handler.js +41 -0
  47. package/server/dist/modules/error-handler.js.map +1 -0
  48. package/server/dist/modules/image-upload.js +114 -0
  49. package/server/dist/modules/image-upload.js.map +1 -0
  50. package/server/dist/modules/mcp-admin.js +88 -0
  51. package/server/dist/modules/mcp-admin.js.map +1 -0
  52. package/server/dist/modules/mcp-auth.js +71 -0
  53. package/server/dist/modules/mcp-auth.js.map +1 -0
  54. package/server/dist/modules/mcp-token.js +28 -0
  55. package/server/dist/modules/mcp-token.js.map +1 -0
  56. package/server/dist/modules/note-authoring.js +140 -0
  57. package/server/dist/modules/note-authoring.js.map +1 -0
  58. package/server/dist/modules/note-cleanup.js +207 -0
  59. package/server/dist/modules/note-cleanup.js.map +1 -0
  60. package/server/dist/modules/note-snapshot.js +223 -0
  61. package/server/dist/modules/note-snapshot.js.map +1 -0
  62. package/server/dist/modules/note-trash.js +262 -0
  63. package/server/dist/modules/note-trash.js.map +1 -0
  64. package/server/dist/modules/prisma-runtime.js +46 -0
  65. package/server/dist/modules/prisma-runtime.js.map +1 -0
  66. package/server/dist/modules/recovery-retention.js +26 -0
  67. package/server/dist/modules/recovery-retention.js.map +1 -0
  68. package/server/dist/modules/remote-image.js +176 -0
  69. package/server/dist/modules/remote-image.js.map +1 -0
  70. package/server/dist/modules/tag-organization.js +66 -0
  71. package/server/dist/modules/tag-organization.js.map +1 -0
  72. package/server/dist/modules/use-async.js +2 -4
  73. package/server/dist/modules/use-async.js.map +1 -1
  74. package/server/dist/schema/note/index.js +150 -6
  75. package/server/dist/schema/note/index.js.map +1 -1
  76. package/server/dist/start.js +11 -0
  77. package/server/dist/start.js.map +1 -0
  78. package/server/dist/types/index.js.map +1 -1
  79. package/server/dist/urls.js +3 -2
  80. package/server/dist/urls.js.map +1 -1
  81. package/server/dist/views/auth.js +305 -0
  82. package/server/dist/views/auth.js.map +1 -0
  83. package/server/dist/views/image.js +24 -81
  84. package/server/dist/views/image.js.map +1 -1
  85. package/server/dist/views/index.js +4 -0
  86. package/server/dist/views/index.js.map +1 -1
  87. package/server/dist/views/mcp-admin.js +42 -0
  88. package/server/dist/views/mcp-admin.js.map +1 -0
  89. package/server/dist/views/note.js +124 -0
  90. package/server/dist/views/note.js.map +1 -0
  91. package/server/dist/views/tag.js +23 -0
  92. package/server/dist/views/tag.js.map +1 -0
  93. package/server/prisma/migrations/20260331183000_0010_note_snapshot/migration.sql +13 -0
  94. package/server/prisma/migrations/20260331195000_0011_note_trash_entities/migration.sql +35 -0
  95. package/server/prisma/migrations/20260331213000_0012_image_hash_unique/migration.sql +49 -0
  96. package/server/prisma/migrations/20260404000000_0013_mcp_admin_control/migration.sql +9 -0
  97. package/server/prisma/schema.prisma +66 -1
  98. package/server/client/dist/assets/index-BJgBFWwL.css +0 -1
  99. package/server/client/dist/assets/index-DvNRv9YC.js +0 -391
  100. package/server/client/dist/assets/note-vendor-CUVHa9LU.js +0 -54
@@ -0,0 +1,124 @@
1
+ import { createAppError } from "../modules/error-handler.js";
2
+ import {
3
+ createNoteFromMarkdown,
4
+ InvalidNoteAuthoringInputError,
5
+ updateNoteFromMarkdown
6
+ } from "../modules/note-authoring.js";
7
+ import { deleteNoteById } from "../modules/note-cleanup.js";
8
+ import { MCP_SNAPSHOT_META } from "../modules/note-snapshot.js";
9
+ const NOTE_LAYOUTS = /* @__PURE__ */ new Set(["narrow", "wide", "full"]);
10
+ const resolveNoteLayout = (value) => {
11
+ if (value === void 0) {
12
+ return void 0;
13
+ }
14
+ if (typeof value === "string" && NOTE_LAYOUTS.has(value)) {
15
+ return value;
16
+ }
17
+ return null;
18
+ };
19
+ const createMcpCreateNoteHandler = (createNote = createNoteFromMarkdown) => {
20
+ return async (req, res) => {
21
+ const { title, markdown, layout } = req.body ?? {};
22
+ const resolvedLayout = resolveNoteLayout(layout);
23
+ if (typeof title !== "string") {
24
+ throw createAppError(400, "INVALID_NOTE_TITLE", "A note title is required.");
25
+ }
26
+ if (markdown !== void 0 && typeof markdown !== "string") {
27
+ throw createAppError(400, "INVALID_NOTE_MARKDOWN", "Note markdown must be a string.");
28
+ }
29
+ if (layout !== void 0 && resolvedLayout === null) {
30
+ throw createAppError(400, "INVALID_NOTE_LAYOUT", "Note layout must be one of narrow, wide, or full.");
31
+ }
32
+ try {
33
+ const note = await createNote({
34
+ title,
35
+ ...markdown !== void 0 ? { markdown } : {},
36
+ ...resolvedLayout ? { layout: resolvedLayout } : {}
37
+ });
38
+ res.status(200).json({
39
+ created: true,
40
+ note
41
+ }).end();
42
+ } catch (error) {
43
+ if (error instanceof InvalidNoteAuthoringInputError) {
44
+ throw createAppError(400, "INVALID_NOTE_INPUT", error.message);
45
+ }
46
+ throw error;
47
+ }
48
+ };
49
+ };
50
+ const createMcpUpdateNoteHandler = (updateNote = updateNoteFromMarkdown) => {
51
+ return async (req, res) => {
52
+ const {
53
+ id,
54
+ title,
55
+ markdown,
56
+ layout,
57
+ editSessionId
58
+ } = req.body ?? {};
59
+ const noteId = Number(id);
60
+ const resolvedLayout = resolveNoteLayout(layout);
61
+ if (!Number.isInteger(noteId) || noteId <= 0) {
62
+ throw createAppError(400, "INVALID_NOTE_ID", "A valid note id is required.");
63
+ }
64
+ if (title !== void 0 && typeof title !== "string") {
65
+ throw createAppError(400, "INVALID_NOTE_TITLE", "Note title must be a string.");
66
+ }
67
+ if (markdown !== void 0 && typeof markdown !== "string") {
68
+ throw createAppError(400, "INVALID_NOTE_MARKDOWN", "Note markdown must be a string.");
69
+ }
70
+ if (layout !== void 0 && resolvedLayout === null) {
71
+ throw createAppError(400, "INVALID_NOTE_LAYOUT", "Note layout must be one of narrow, wide, or full.");
72
+ }
73
+ if (editSessionId !== void 0 && typeof editSessionId !== "string") {
74
+ throw createAppError(400, "INVALID_EDIT_SESSION_ID", "Edit session id must be a string.");
75
+ }
76
+ if (title === void 0 && markdown === void 0 && layout === void 0) {
77
+ throw createAppError(400, "INVALID_NOTE_INPUT", "At least one note field must be provided for update.");
78
+ }
79
+ try {
80
+ const note = await updateNote({
81
+ id: noteId,
82
+ ...title !== void 0 ? { title } : {},
83
+ ...markdown !== void 0 ? { markdown } : {},
84
+ ...resolvedLayout ? { layout: resolvedLayout } : {},
85
+ ...editSessionId !== void 0 ? { editSessionId } : {},
86
+ snapshotMeta: MCP_SNAPSHOT_META
87
+ });
88
+ if (!note) {
89
+ throw createAppError(404, "NOTE_NOT_FOUND", "The requested note was not found.");
90
+ }
91
+ res.status(200).json({
92
+ updated: true,
93
+ note
94
+ }).end();
95
+ } catch (error) {
96
+ if (error instanceof InvalidNoteAuthoringInputError) {
97
+ throw createAppError(400, "INVALID_NOTE_INPUT", error.message);
98
+ }
99
+ throw error;
100
+ }
101
+ };
102
+ };
103
+ const createMcpDeleteNoteHandler = (deleteNote = deleteNoteById) => {
104
+ return async (req, res) => {
105
+ const id = Number(req.body?.id);
106
+ if (!Number.isInteger(id) || id <= 0) {
107
+ throw createAppError(400, "INVALID_NOTE_ID", "A valid note id is required.");
108
+ }
109
+ const deletedNote = await deleteNote(id);
110
+ if (!deletedNote) {
111
+ throw createAppError(404, "NOTE_NOT_FOUND", "The requested note was not found.");
112
+ }
113
+ res.status(200).json({
114
+ deleted: true,
115
+ note: deletedNote
116
+ }).end();
117
+ };
118
+ };
119
+ export {
120
+ createMcpCreateNoteHandler,
121
+ createMcpDeleteNoteHandler,
122
+ createMcpUpdateNoteHandler
123
+ };
124
+ //# sourceMappingURL=note.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/views/note.ts"],"sourcesContent":["import type { Controller } from '~/types/index.js';\nimport { createAppError } from '~/modules/error-handler.js';\nimport {\n createNoteFromMarkdown,\n InvalidNoteAuthoringInputError,\n updateNoteFromMarkdown\n} from '~/modules/note-authoring.js';\nimport { deleteNoteById } from '~/modules/note-cleanup.js';\nimport { MCP_SNAPSHOT_META } from '~/modules/note-snapshot.js';\nimport type { NoteLayout } from '~/models.js';\n\nconst NOTE_LAYOUTS = new Set<NoteLayout>(['narrow', 'wide', 'full']);\n\nconst resolveNoteLayout = (value: unknown): NoteLayout | null | undefined => {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === 'string' && NOTE_LAYOUTS.has(value as NoteLayout)) {\n return value as NoteLayout;\n }\n\n return null;\n};\n\nexport const createMcpCreateNoteHandler = (\n createNote = createNoteFromMarkdown\n): Controller => {\n return async (req, res) => {\n const { title, markdown, layout } = req.body ?? {};\n const resolvedLayout = resolveNoteLayout(layout);\n\n if (typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'A note title is required.');\n }\n\n if (markdown !== undefined && typeof markdown !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_MARKDOWN', 'Note markdown must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n try {\n const note = await createNote({\n title,\n ...(markdown !== undefined ? { markdown } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {})\n });\n\n res.status(200).json({\n created: true,\n note\n }).end();\n } catch (error) {\n if (error instanceof InvalidNoteAuthoringInputError) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', error.message);\n }\n\n throw error;\n }\n };\n};\n\nexport const createMcpUpdateNoteHandler = (\n updateNote = updateNoteFromMarkdown\n): Controller => {\n return async (req, res) => {\n const {\n id, title, markdown, layout, editSessionId\n } = req.body ?? {};\n const noteId = Number(id);\n const resolvedLayout = resolveNoteLayout(layout);\n\n if (!Number.isInteger(noteId) || noteId <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n if (title !== undefined && typeof title !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_TITLE', 'Note title must be a string.');\n }\n\n if (markdown !== undefined && typeof markdown !== 'string') {\n throw createAppError(400, 'INVALID_NOTE_MARKDOWN', 'Note markdown must be a string.');\n }\n\n if (layout !== undefined && resolvedLayout === null) {\n throw createAppError(400, 'INVALID_NOTE_LAYOUT', 'Note layout must be one of narrow, wide, or full.');\n }\n\n if (editSessionId !== undefined && typeof editSessionId !== 'string') {\n throw createAppError(400, 'INVALID_EDIT_SESSION_ID', 'Edit session id must be a string.');\n }\n\n if (\n title === undefined &&\n markdown === undefined &&\n layout === undefined\n ) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', 'At least one note field must be provided for update.');\n }\n\n try {\n const note = await updateNote({\n id: noteId,\n ...(title !== undefined ? { title } : {}),\n ...(markdown !== undefined ? { markdown } : {}),\n ...(resolvedLayout ? { layout: resolvedLayout } : {}),\n ...(editSessionId !== undefined ? { editSessionId } : {}),\n snapshotMeta: MCP_SNAPSHOT_META\n });\n\n if (!note) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n res.status(200).json({\n updated: true,\n note\n }).end();\n } catch (error) {\n if (error instanceof InvalidNoteAuthoringInputError) {\n throw createAppError(400, 'INVALID_NOTE_INPUT', error.message);\n }\n\n throw error;\n }\n };\n};\n\nexport const createMcpDeleteNoteHandler = (\n deleteNote = deleteNoteById\n): Controller => {\n return async (req, res) => {\n const id = Number(req.body?.id);\n\n if (!Number.isInteger(id) || id <= 0) {\n throw createAppError(400, 'INVALID_NOTE_ID', 'A valid note id is required.');\n }\n\n const deletedNote = await deleteNote(id);\n\n if (!deletedNote) {\n throw createAppError(404, 'NOTE_NOT_FOUND', 'The requested note was not found.');\n }\n\n res.status(200).json({\n deleted: true,\n note: deletedNote\n }).end();\n };\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,23 @@
1
+ import { createAppError } from "../modules/error-handler.js";
2
+ import { InvalidTagNameError, ensureTagByName } from "../modules/tag-organization.js";
3
+ const createMcpCreateTagHandler = (ensureTag = ensureTagByName) => {
4
+ return async (req, res) => {
5
+ const name = req.body?.name;
6
+ if (typeof name !== "string") {
7
+ throw createAppError(400, "INVALID_TAG_NAME", "A tag name is required.");
8
+ }
9
+ try {
10
+ const result = await ensureTag(name);
11
+ res.status(200).json(result).end();
12
+ } catch (error) {
13
+ if (error instanceof InvalidTagNameError) {
14
+ throw createAppError(400, "INVALID_TAG_NAME", error.message);
15
+ }
16
+ throw error;
17
+ }
18
+ };
19
+ };
20
+ export {
21
+ createMcpCreateTagHandler
22
+ };
23
+ //# sourceMappingURL=tag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/views/tag.ts"],"sourcesContent":["import type { Controller } from '~/types/index.js';\nimport { createAppError } from '~/modules/error-handler.js';\nimport { InvalidTagNameError, ensureTagByName } from '~/modules/tag-organization.js';\n\nexport const createMcpCreateTagHandler = (\n ensureTag = ensureTagByName\n): Controller => {\n return async (req, res) => {\n const name = req.body?.name;\n\n if (typeof name !== 'string') {\n throw createAppError(400, 'INVALID_TAG_NAME', 'A tag name is required.');\n }\n\n try {\n const result = await ensureTag(name);\n res.status(200).json(result).end();\n } catch (error) {\n if (error instanceof InvalidTagNameError) {\n throw createAppError(400, 'INVALID_TAG_NAME', error.message);\n }\n\n throw error;\n }\n };\n};\n"],"mappings":"AACA,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB,uBAAuB;AAE9C,MAAM,4BAA4B,CACrC,YAAY,oBACC;AACb,SAAO,OAAO,KAAK,QAAQ;AACvB,UAAM,OAAO,IAAI,MAAM;AAEvB,QAAI,OAAO,SAAS,UAAU;AAC1B,YAAM,eAAe,KAAK,oBAAoB,yBAAyB;AAAA,IAC3E;AAEA,QAAI;AACA,YAAM,SAAS,MAAM,UAAU,IAAI;AACnC,UAAI,OAAO,GAAG,EAAE,KAAK,MAAM,EAAE,IAAI;AAAA,IACrC,SAAS,OAAO;AACZ,UAAI,iBAAiB,qBAAqB;AACtC,cAAM,eAAe,KAAK,oBAAoB,MAAM,OAAO;AAAA,MAC/D;AAEA,YAAM;AAAA,IACV;AAAA,EACJ;AACJ;","names":[]}
@@ -0,0 +1,13 @@
1
+ CREATE TABLE "NoteSnapshot" (
2
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
3
+ "noteId" INTEGER NOT NULL,
4
+ "title" TEXT NOT NULL,
5
+ "payload" TEXT NOT NULL,
6
+ "editSessionId" TEXT,
7
+ "meta" TEXT,
8
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
9
+ CONSTRAINT "NoteSnapshot_noteId_fkey" FOREIGN KEY ("noteId") REFERENCES "Note" ("id") ON DELETE CASCADE ON UPDATE CASCADE
10
+ );
11
+
12
+ CREATE INDEX "NoteSnapshot_noteId_createdAt_idx" ON "NoteSnapshot"("noteId", "createdAt");
13
+ CREATE UNIQUE INDEX "NoteSnapshot_noteId_editSessionId_key" ON "NoteSnapshot"("noteId", "editSessionId");
@@ -0,0 +1,35 @@
1
+ CREATE TABLE "DeletedNote" (
2
+ "id" INTEGER NOT NULL PRIMARY KEY,
3
+ "title" TEXT NOT NULL,
4
+ "content" TEXT NOT NULL,
5
+ "createdAt" DATETIME NOT NULL,
6
+ "updatedAt" DATETIME NOT NULL,
7
+ "deletedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
8
+ "pinned" BOOLEAN NOT NULL DEFAULT false,
9
+ "order" INTEGER NOT NULL DEFAULT 0,
10
+ "layout" TEXT NOT NULL DEFAULT 'wide'
11
+ );
12
+
13
+ CREATE TABLE "DeletedNoteTag" (
14
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
15
+ "deletedNoteId" INTEGER NOT NULL,
16
+ "name" TEXT NOT NULL,
17
+ CONSTRAINT "DeletedNoteTag_deletedNoteId_fkey" FOREIGN KEY ("deletedNoteId") REFERENCES "DeletedNote" ("id") ON DELETE CASCADE ON UPDATE CASCADE
18
+ );
19
+
20
+ CREATE TABLE "DeletedReminder" (
21
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
22
+ "deletedNoteId" INTEGER NOT NULL,
23
+ "originalId" INTEGER,
24
+ "reminderDate" DATETIME NOT NULL,
25
+ "completed" BOOLEAN NOT NULL DEFAULT false,
26
+ "priority" TEXT NOT NULL DEFAULT 'medium',
27
+ "content" TEXT,
28
+ "createdAt" DATETIME NOT NULL,
29
+ "updatedAt" DATETIME NOT NULL,
30
+ CONSTRAINT "DeletedReminder_deletedNoteId_fkey" FOREIGN KEY ("deletedNoteId") REFERENCES "DeletedNote" ("id") ON DELETE CASCADE ON UPDATE CASCADE
31
+ );
32
+
33
+ CREATE INDEX "DeletedNote_deletedAt_updatedAt_idx" ON "DeletedNote"("deletedAt", "updatedAt");
34
+ CREATE INDEX "DeletedNoteTag_deletedNoteId_idx" ON "DeletedNoteTag"("deletedNoteId");
35
+ CREATE INDEX "DeletedReminder_deletedNoteId_idx" ON "DeletedReminder"("deletedNoteId");
@@ -0,0 +1,49 @@
1
+ CREATE TEMP TABLE "_ImageDuplicateMap" AS
2
+ SELECT
3
+ duplicate."id" AS "duplicateId",
4
+ duplicate."url" AS "duplicateUrl",
5
+ canonical."id" AS "canonicalId",
6
+ canonical."url" AS "canonicalUrl",
7
+ ROW_NUMBER() OVER (ORDER BY duplicate."id") AS "position"
8
+ FROM "Image" AS duplicate
9
+ JOIN (
10
+ SELECT "hash", MIN("id") AS "canonicalId"
11
+ FROM "Image"
12
+ GROUP BY "hash"
13
+ ) AS grouped
14
+ ON grouped."hash" = duplicate."hash"
15
+ JOIN "Image" AS canonical
16
+ ON canonical."id" = grouped."canonicalId"
17
+ WHERE duplicate."id" <> grouped."canonicalId";
18
+
19
+ WITH RECURSIVE "rewritten"("noteId", "position", "content") AS (
20
+ SELECT "id", 0, "content"
21
+ FROM "Note"
22
+ UNION ALL
23
+ SELECT
24
+ "rewritten"."noteId",
25
+ "_ImageDuplicateMap"."position",
26
+ replace("rewritten"."content", "_ImageDuplicateMap"."duplicateUrl", "_ImageDuplicateMap"."canonicalUrl")
27
+ FROM "rewritten"
28
+ JOIN "_ImageDuplicateMap"
29
+ ON "_ImageDuplicateMap"."position" = "rewritten"."position" + 1
30
+ )
31
+ UPDATE "Note"
32
+ SET "content" = (
33
+ SELECT "rewritten"."content"
34
+ FROM "rewritten"
35
+ WHERE "rewritten"."noteId" = "Note"."id"
36
+ ORDER BY "rewritten"."position" DESC
37
+ LIMIT 1
38
+ )
39
+ WHERE EXISTS (SELECT 1 FROM "_ImageDuplicateMap");
40
+
41
+ DELETE FROM "Image"
42
+ WHERE "id" IN (
43
+ SELECT "duplicateId"
44
+ FROM "_ImageDuplicateMap"
45
+ );
46
+
47
+ CREATE UNIQUE INDEX "Image_hash_key" ON "Image"("hash");
48
+
49
+ DROP TABLE "_ImageDuplicateMap";
@@ -0,0 +1,9 @@
1
+ CREATE TABLE "McpToken" (
2
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
3
+ "tokenHash" TEXT NOT NULL,
4
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
5
+ "lastUsedAt" DATETIME,
6
+ "revokedAt" DATETIME
7
+ );
8
+
9
+ CREATE INDEX "McpToken_revokedAt_createdAt_idx" ON "McpToken"("revokedAt", "createdAt");
@@ -15,6 +15,16 @@ model Cache {
15
15
  updatedAt DateTime @updatedAt
16
16
  }
17
17
 
18
+ model McpToken {
19
+ id Int @id @default(autoincrement())
20
+ tokenHash String
21
+ createdAt DateTime @default(now())
22
+ lastUsedAt DateTime?
23
+ revokedAt DateTime?
24
+
25
+ @@index([revokedAt, createdAt])
26
+ }
27
+
18
28
  model Note {
19
29
  id Int @id @default(autoincrement())
20
30
  title String
@@ -26,12 +36,67 @@ model Note {
26
36
  layout NoteLayout @default(wide)
27
37
  tags Tag[] @relation("NoteToTag")
28
38
  reminders Reminder[] @relation("NoteToReminder")
39
+ snapshots NoteSnapshot[]
40
+ }
41
+
42
+ model NoteSnapshot {
43
+ id Int @id @default(autoincrement())
44
+ noteId Int
45
+ note Note @relation(fields: [noteId], references: [id], onDelete: Cascade)
46
+ title String
47
+ payload String
48
+ editSessionId String?
49
+ meta String?
50
+ createdAt DateTime @default(now())
51
+
52
+ @@index([noteId, createdAt])
53
+ @@unique([noteId, editSessionId])
54
+ }
55
+
56
+ model DeletedNote {
57
+ id Int @id
58
+ title String
59
+ content String
60
+ createdAt DateTime
61
+ updatedAt DateTime
62
+ deletedAt DateTime @default(now())
63
+ pinned Boolean @default(false)
64
+ order Int @default(0)
65
+ layout NoteLayout @default(wide)
66
+ tags DeletedNoteTag[]
67
+ reminders DeletedReminder[]
68
+
69
+ @@index([deletedAt, updatedAt])
70
+ }
71
+
72
+ model DeletedNoteTag {
73
+ id Int @id @default(autoincrement())
74
+ deletedNoteId Int
75
+ deletedNote DeletedNote @relation(fields: [deletedNoteId], references: [id], onDelete: Cascade)
76
+ name String
77
+
78
+ @@index([deletedNoteId])
79
+ }
80
+
81
+ model DeletedReminder {
82
+ id Int @id @default(autoincrement())
83
+ deletedNoteId Int
84
+ deletedNote DeletedNote @relation(fields: [deletedNoteId], references: [id], onDelete: Cascade)
85
+ originalId Int?
86
+ reminderDate DateTime
87
+ completed Boolean @default(false)
88
+ priority ReminderPriority @default(medium)
89
+ content String?
90
+ createdAt DateTime
91
+ updatedAt DateTime
92
+
93
+ @@index([deletedNoteId])
29
94
  }
30
95
 
31
96
  model Image {
32
97
  id Int @id @default(autoincrement())
33
98
  url String
34
- hash String
99
+ hash String @unique
35
100
  createdAt DateTime @default(now())
36
101
  updatedAt DateTime @updatedAt
37
102
  }