diff options
| author | MohamedBassem <me@mbassem.com> | 2024-03-05 13:11:06 +0000 |
|---|---|---|
| committer | MohamedBassem <me@mbassem.com> | 2024-03-05 13:11:06 +0000 |
| commit | 8a46ecb7373d6c5e7300861169ea51a7917cd2b4 (patch) | |
| tree | 4ad318c3b5fc8b7a74cba6d0e37b6ade24db829a /packages/trpc/routers/lists.ts | |
| parent | 224aa38d5976523f213e2860b6addc7630d472ba (diff) | |
| download | karakeep-8a46ecb7373d6c5e7300861169ea51a7917cd2b4.tar.zst | |
refactor: Extract trpc logic into its package
Diffstat (limited to 'packages/trpc/routers/lists.ts')
| -rw-r--r-- | packages/trpc/routers/lists.ts | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/packages/trpc/routers/lists.ts b/packages/trpc/routers/lists.ts new file mode 100644 index 00000000..fa97929d --- /dev/null +++ b/packages/trpc/routers/lists.ts @@ -0,0 +1,173 @@ +import { Context, authedProcedure, router } from "../index"; +import { SqliteError } from "@hoarder/db"; +import { z } from "zod"; +import { TRPCError, experimental_trpcMiddleware } from "@trpc/server"; +import { bookmarkLists, bookmarksInLists } from "@hoarder/db/schema"; +import { and, eq } from "drizzle-orm"; +import { zBookmarkListSchema } from "../types/lists"; + +const ensureListOwnership = experimental_trpcMiddleware<{ + ctx: Context; + input: { listId: string }; +}>().create(async (opts) => { + const list = await opts.ctx.db.query.bookmarkLists.findFirst({ + where: eq(bookmarkLists.id, opts.input.listId), + columns: { + userId: true, + }, + }); + if (!opts.ctx.user) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "User is not authorized", + }); + } + if (!list) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "List not found", + }); + } + if (list.userId != opts.ctx.user.id) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "User is not allowed to access resource", + }); + } + + return opts.next(); +}); + +export const listsAppRouter = router({ + create: authedProcedure + .input( + z.object({ + name: z.string().min(1).max(20), + icon: z.string(), + }), + ) + .output(zBookmarkListSchema) + .mutation(async ({ input, ctx }) => { + try { + const result = await ctx.db + .insert(bookmarkLists) + .values({ + name: input.name, + icon: input.icon, + userId: ctx.user.id, + }) + .returning(); + return result[0]; + } catch (e) { + if (e instanceof SqliteError) { + if (e.code == "SQLITE_CONSTRAINT_UNIQUE") { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "List already exists", + }); + } + } + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Something went wrong", + }); + } + }), + delete: authedProcedure + .input( + z.object({ + listId: z.string(), + }), + ) + .use(ensureListOwnership) + .mutation(async ({ input, ctx }) => { + const res = await ctx.db + .delete(bookmarkLists) + .where( + and( + eq(bookmarkLists.id, input.listId), + eq(bookmarkLists.userId, ctx.user.id), + ), + ); + if (res.changes == 0) { + throw new TRPCError({ code: "NOT_FOUND" }); + } + }), + addToList: authedProcedure + .input( + z.object({ + listId: z.string(), + bookmarkId: z.string(), + }), + ) + .use(ensureListOwnership) + .mutation(async ({ input, ctx }) => { + try { + await ctx.db.insert(bookmarksInLists).values({ + listId: input.listId, + bookmarkId: input.bookmarkId, + }); + } catch (e) { + if (e instanceof SqliteError) { + if (e.code == "SQLITE_CONSTRAINT_PRIMARYKEY") { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Bookmark already in the list", + }); + } + } + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Something went wrong", + }); + } + }), + get: authedProcedure + .input( + z.object({ + listId: z.string(), + }), + ) + .output( + zBookmarkListSchema.merge( + z.object({ + bookmarks: z.array(z.string()), + }), + ), + ) + .use(ensureListOwnership) + .query(async ({ input, ctx }) => { + const res = await ctx.db.query.bookmarkLists.findFirst({ + where: and( + eq(bookmarkLists.id, input.listId), + eq(bookmarkLists.userId, ctx.user.id), + ), + with: { + bookmarksInLists: true, + }, + }); + if (!res) { + throw new TRPCError({ code: "NOT_FOUND" }); + } + + return { + id: res.id, + name: res.name, + icon: res.icon, + bookmarks: res.bookmarksInLists.map((b) => b.bookmarkId), + }; + }), + list: authedProcedure + .output( + z.object({ + lists: z.array(zBookmarkListSchema), + }), + ) + .query(async ({ ctx }) => { + const lists = await ctx.db.query.bookmarkLists.findMany({ + where: and(eq(bookmarkLists.userId, ctx.user.id)), + }); + + return { lists }; + }), +}); |
