rcgit

/ karakeep

Commit dbe6c1de

SHA dbe6c1de20bc38e54c848983d75c861be288dfe1
Author Mohamed Bassem <me at mbassem dot com>
Author Date 2025-03-09 15:03 +0000
Committer Mohamed Bassem <me at mbassem dot com>
Commit Date 2025-03-09 21:38 +0000
Parent(s) d2c37c2e6ab2 (diff)
Tree 1e8b1ab6236d

patch snapshot

fix: Harden getBookmarks endpoint against inconsistent bookmarks. Fixes #1094
File + - Graph
M packages/shared/types/bookmarks.ts +2 -2
M packages/trpc/routers/bookmarks.ts +58 -64
2 file(s) changed, 60 insertions(+), 66 deletions(-)

packages/shared/types/bookmarks.ts

diff --git a/packages/shared/types/bookmarks.ts b/packages/shared/types/bookmarks.ts
index 9644095c..9e6e6f3f 100644
--- a/packages/shared/types/bookmarks.ts
+++ b/packages/shared/types/bookmarks.ts
@@ -3,7 +3,7 @@ import { z } from "zod";
 import { zCursorV2 } from "./pagination";
 import { zBookmarkTagSchema } from "./tags";
 
-const MAX_TITLE_LENGTH = 250;
+const MAX_TITLE_LENGTH = 1000;
 
 export const enum BookmarkTypes {
   LINK = "link",
@@ -78,7 +78,7 @@ export const zBareBookmarkSchema = z.object({
   id: z.string(),
   createdAt: z.date(),
   modifiedAt: z.date().nullable(),
-  title: z.string().max(MAX_TITLE_LENGTH).nullish(),
+  title: z.string().nullish(),
   archived: z.boolean(),
   favourited: z.boolean(),
   taggingStatus: z.enum(["success", "failure", "pending"]).nullable(),

packages/trpc/routers/bookmarks.ts

diff --git a/packages/trpc/routers/bookmarks.ts b/packages/trpc/routers/bookmarks.ts
index 7025c3da..1849f43c 100644
--- a/packages/trpc/routers/bookmarks.ts
+++ b/packages/trpc/routers/bookmarks.ts
@@ -191,44 +191,42 @@ function toZodSchema(bookmark: BookmarkQueryReturnType): ZBookmark {
   let content: ZBookmarkContent = {
     type: BookmarkTypes.UNKNOWN,
   };
-  switch (bookmark.type) {
-    case BookmarkTypes.LINK:
-      content = {
-        type: bookmark.type,
-        screenshotAssetId: assets.find(
-          (a) => a.assetType == AssetTypes.LINK_SCREENSHOT,
-        )?.id,
-        fullPageArchiveAssetId: assets.find(
-          (a) => a.assetType == AssetTypes.LINK_FULL_PAGE_ARCHIVE,
-        )?.id,
-        precrawledArchiveAssetId: assets.find(
-          (a) => a.assetType == AssetTypes.LINK_PRECRAWLED_ARCHIVE,
-        )?.id,
-        imageAssetId: assets.find(
-          (a) => a.assetType == AssetTypes.LINK_BANNER_IMAGE,
-        )?.id,
-        videoAssetId: assets.find((a) => a.assetType == AssetTypes.LINK_VIDEO)
-          ?.id,
-        ...link,
-      };
-      break;
-    case BookmarkTypes.TEXT:
-      content = {
-        type: bookmark.type,
-        text: text.text ?? "",
-        sourceUrl: text.sourceUrl,
-      };
-      break;
-    case BookmarkTypes.ASSET:
-      content = {
-        type: bookmark.type,
-        assetType: asset.assetType,
-        assetId: asset.assetId,
-        fileName: asset.fileName,
-        sourceUrl: asset.sourceUrl,
-        size: assets.find((a) => a.id == asset.assetId)?.size,
-      };
-      break;
+  if (bookmark.link) {
+    content = {
+      type: BookmarkTypes.LINK,
+      screenshotAssetId: assets.find(
+        (a) => a.assetType == AssetTypes.LINK_SCREENSHOT,
+      )?.id,
+      fullPageArchiveAssetId: assets.find(
+        (a) => a.assetType == AssetTypes.LINK_FULL_PAGE_ARCHIVE,
+      )?.id,
+      precrawledArchiveAssetId: assets.find(
+        (a) => a.assetType == AssetTypes.LINK_PRECRAWLED_ARCHIVE,
+      )?.id,
+      imageAssetId: assets.find(
+        (a) => a.assetType == AssetTypes.LINK_BANNER_IMAGE,
+      )?.id,
+      videoAssetId: assets.find((a) => a.assetType == AssetTypes.LINK_VIDEO)
+        ?.id,
+      ...link,
+    };
+  }
+  if (bookmark.text) {
+    content = {
+      type: BookmarkTypes.TEXT,
+      text: text.text ?? "",
+      sourceUrl: text.sourceUrl,
+    };
+  }
+  if (bookmark.asset) {
+    content = {
+      type: BookmarkTypes.ASSET,
+      assetType: asset.assetType,
+      assetId: asset.assetId,
+      fileName: asset.fileName,
+      sourceUrl: asset.sourceUrl,
+      size: assets.find((a) => a.id == asset.assetId)?.size,
+    };
   }
 
   return {
@@ -269,8 +267,8 @@ export const bookmarksAppRouter = router({
             .insert(bookmarks)
             .values({
               userId: ctx.user.id,
-              type: input.type,
               title: input.title,
+              type: input.type,
               archived: input.archived,
               favourited: input.favourited,
               note: input.note,
@@ -757,31 +755,27 @@ export const bookmarksAppRouter = router({
           const bookmarkId = row.bookmarksSq.id;
           if (!acc[bookmarkId]) {
             let content: ZBookmarkContent;
-            switch (row.bookmarksSq.type) {
-              case BookmarkTypes.LINK: {
-                content = { type: row.bookmarksSq.type, ...row.bookmarkLinks! };
-                break;
-              }
-              case BookmarkTypes.TEXT: {
-                content = {
-                  type: row.bookmarksSq.type,
-                  text: row.bookmarkTexts?.text ?? "",
-                  sourceUrl: row.bookmarkTexts?.sourceUrl ?? null,
-                };
-                break;
-              }
-              case BookmarkTypes.ASSET: {
-                const bookmarkAssets = row.bookmarkAssets!;
-                content = {
-                  type: row.bookmarksSq.type,
-                  assetId: bookmarkAssets.assetId,
-                  assetType: bookmarkAssets.assetType,
-                  fileName: bookmarkAssets.fileName,
-                  sourceUrl: bookmarkAssets.sourceUrl ?? null,
-                  size: null, // This will get filled in the asset loop
-                };
-                break;
-              }
+            if (row.bookmarkLinks) {
+              content = { type: BookmarkTypes.LINK, ...row.bookmarkLinks };
+            } else if (row.bookmarkTexts) {
+              content = {
+                type: BookmarkTypes.TEXT,
+                text: row.bookmarkTexts.text ?? "",
+                sourceUrl: row.bookmarkTexts.sourceUrl ?? null,
+              };
+            } else if (row.bookmarkAssets) {
+              content = {
+                type: BookmarkTypes.ASSET,
+                assetId: row.bookmarkAssets.assetId,
+                assetType: row.bookmarkAssets.assetType,
+                fileName: row.bookmarkAssets.fileName,
+                sourceUrl: row.bookmarkAssets.sourceUrl ?? null,
+                size: null, // This will get filled in the asset loop
+              };
+            } else {
+              content = {
+                type: BookmarkTypes.UNKNOWN,
+              };
             }
             acc[bookmarkId] = {
               ...row.bookmarksSq,