diff options
Diffstat (limited to 'packages/trpc')
| -rw-r--r-- | packages/trpc/lib/__tests__/search.test.ts | 14 | ||||
| -rw-r--r-- | packages/trpc/routers/bookmarks.ts | 30 | ||||
| -rw-r--r-- | packages/trpc/routers/lists.ts | 59 |
3 files changed, 79 insertions, 24 deletions
diff --git a/packages/trpc/lib/__tests__/search.test.ts b/packages/trpc/lib/__tests__/search.test.ts index bf32bcb1..468aef83 100644 --- a/packages/trpc/lib/__tests__/search.test.ts +++ b/packages/trpc/lib/__tests__/search.test.ts @@ -130,10 +130,16 @@ beforeEach(async () => { ]); await db.insert(bookmarkLists).values([ - { id: "l1", userId: testUserId, name: "list1", icon: "🚀" }, - { id: "l2", userId: testUserId, name: "list2", icon: "🚀" }, - { id: "l3", userId: testUserId, name: "favorites", icon: "⭐" }, - { id: "l4", userId: testUserId, name: "work", icon: "💼" }, + { id: "l1", userId: testUserId, name: "list1", icon: "🚀", type: "manual" }, + { id: "l2", userId: testUserId, name: "list2", icon: "🚀", type: "manual" }, + { + id: "l3", + userId: testUserId, + name: "favorites", + icon: "⭐", + type: "manual", + }, + { id: "l4", userId: testUserId, name: "work", icon: "💼", type: "manual" }, ]); await db.insert(bookmarksInLists).values([ diff --git a/packages/trpc/routers/bookmarks.ts b/packages/trpc/routers/bookmarks.ts index 3320b3b9..47ba623b 100644 --- a/packages/trpc/routers/bookmarks.ts +++ b/packages/trpc/routers/bookmarks.ts @@ -14,6 +14,7 @@ import { AssetTypes, bookmarkAssets, bookmarkLinks, + bookmarkLists, bookmarks, bookmarksInLists, bookmarkTags, @@ -33,6 +34,7 @@ import { triggerSearchReindex, } from "@hoarder/shared/queues"; import { getSearchIdxClient } from "@hoarder/shared/search"; +import { parseSearchQuery } from "@hoarder/shared/searchQueryParser"; import { BookmarkTypes, DEFAULT_NUM_BOOKMARKS_PER_PAGE, @@ -625,6 +627,34 @@ export const bookmarksAppRouter = router({ if (!input.limit) { input.limit = DEFAULT_NUM_BOOKMARKS_PER_PAGE; } + if (input.listId) { + const list = await ctx.db.query.bookmarkLists.findFirst({ + where: and( + eq(bookmarkLists.id, input.listId), + eq(bookmarkLists.userId, ctx.user.id), + ), + }); + if (!list) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "List not found", + }); + } + if (list.type === "smart") { + invariant(list.query); + const query = parseSearchQuery(list.query); + if (query.result !== "full") { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Found an invalid smart list query", + }); + } + if (query.matcher) { + input.ids = await getBookmarkIdsFromMatcher(ctx, query.matcher); + delete input.listId; + } + } + } const sq = ctx.db.$with("bookmarksSq").as( ctx.db diff --git a/packages/trpc/routers/lists.ts b/packages/trpc/routers/lists.ts index 0cc937b1..ec7cb10f 100644 --- a/packages/trpc/routers/lists.ts +++ b/packages/trpc/routers/lists.ts @@ -1,12 +1,14 @@ import assert from "node:assert"; import { experimental_trpcMiddleware, TRPCError } from "@trpc/server"; import { and, eq } from "drizzle-orm"; +import invariant from "tiny-invariant"; import { z } from "zod"; import { SqliteError } from "@hoarder/db"; import { bookmarkLists, bookmarksInLists } from "@hoarder/db/schema"; import { zBookmarkListSchema, + zEditBookmarkListSchemaWithValidation, zNewBookmarkListSchema, } from "@hoarder/shared/types/lists"; @@ -58,28 +60,40 @@ export const listsAppRouter = router({ icon: input.icon, userId: ctx.user.id, parentId: input.parentId, + type: input.type, + query: input.query, }) .returning(); return result; }), edit: authedProcedure - .input( - zNewBookmarkListSchema - .partial() - .merge(z.object({ listId: z.string() })) - .refine((val) => val.parentId != val.listId, { - message: "List can't be its own parent", - path: ["parentId"], - }), - ) + .input(zEditBookmarkListSchemaWithValidation) .output(zBookmarkListSchema) + .use(ensureListOwnership) .mutation(async ({ input, ctx }) => { + if (input.query) { + const list = await ctx.db.query.bookmarkLists.findFirst({ + where: and( + eq(bookmarkLists.id, input.listId), + eq(bookmarkLists.userId, ctx.user.id), + ), + }); + // List must exist given that we passed the ownership check + invariant(list); + if (list.type !== "smart") { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Manual lists cannot have a query", + }); + } + } const result = await ctx.db .update(bookmarkLists) .set({ name: input.name, icon: input.icon, parentId: input.parentId, + query: input.query, }) .where( and( @@ -123,6 +137,19 @@ export const listsAppRouter = router({ .use(ensureListOwnership) .use(ensureBookmarkOwnership) .mutation(async ({ input, ctx }) => { + const list = await ctx.db.query.bookmarkLists.findFirst({ + where: and( + eq(bookmarkLists.id, input.listId), + eq(bookmarkLists.userId, ctx.user.id), + ), + }); + invariant(list); + if (list.type === "smart") { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Smart lists cannot be added to", + }); + } try { await ctx.db.insert(bookmarksInLists).values({ listId: input.listId, @@ -174,13 +201,7 @@ export const listsAppRouter = router({ listId: z.string(), }), ) - .output( - zBookmarkListSchema.merge( - z.object({ - bookmarks: z.array(z.string()), - }), - ), - ) + .output(zBookmarkListSchema) .use(ensureListOwnership) .query(async ({ input, ctx }) => { const res = await ctx.db.query.bookmarkLists.findFirst({ @@ -188,9 +209,6 @@ export const listsAppRouter = router({ eq(bookmarkLists.id, input.listId), eq(bookmarkLists.userId, ctx.user.id), ), - with: { - bookmarksInLists: true, - }, }); if (!res) { throw new TRPCError({ code: "NOT_FOUND" }); @@ -201,7 +219,8 @@ export const listsAppRouter = router({ name: res.name, icon: res.icon, parentId: res.parentId, - bookmarks: res.bookmarksInLists.map((b) => b.bookmarkId), + type: res.type, + query: res.query, }; }), list: authedProcedure |
