aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc/routers
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-12-24 13:58:37 +0200
committerGitHub <noreply@github.com>2025-12-24 11:58:37 +0000
commit013ca67c151b51575151424084f6358522b83579 (patch)
treec7c57c518b6c57d6cbab9d0620cc027d51fa06e0 /packages/trpc/routers
parent314c363e5ca69a50626650ade8968feec583e5ce (diff)
downloadkarakeep-013ca67c151b51575151424084f6358522b83579.tar.zst
refactor: move assets to their own model (#2301)
* refactor: move assets to their own model * move asset privacy checks to the model
Diffstat (limited to 'packages/trpc/routers')
-rw-r--r--packages/trpc/routers/assets.ts160
-rw-r--r--packages/trpc/routers/bookmarks.ts19
2 files changed, 18 insertions, 161 deletions
diff --git a/packages/trpc/routers/assets.ts b/packages/trpc/routers/assets.ts
index 7be85446..c75f1e2e 100644
--- a/packages/trpc/routers/assets.ts
+++ b/packages/trpc/routers/assets.ts
@@ -1,57 +1,20 @@
-import { TRPCError } from "@trpc/server";
-import { and, desc, eq, sql } from "drizzle-orm";
import { z } from "zod";
-import { assets, bookmarks } from "@karakeep/db/schema";
-import { deleteAsset } from "@karakeep/shared/assetdb";
import {
zAssetSchema,
zAssetTypesSchema,
} from "@karakeep/shared/types/bookmarks";
-import { authedProcedure, Context, router } from "../index";
-import {
- isAllowedToAttachAsset,
- isAllowedToDetachAsset,
- mapDBAssetTypeToUserType,
- mapSchemaAssetTypeToDB,
-} from "../lib/attachments";
+import { authedProcedure, router } from "../index";
+import { Asset } from "../models/assets";
import { ensureBookmarkOwnership } from "./bookmarks";
-export const ensureAssetOwnership = async (opts: {
- ctx: Context;
- assetId: string;
-}) => {
- const asset = await opts.ctx.db.query.assets.findFirst({
- where: eq(bookmarks.id, opts.assetId),
- });
- if (!opts.ctx.user) {
- throw new TRPCError({
- code: "UNAUTHORIZED",
- message: "User is not authorized",
- });
- }
- if (!asset) {
- throw new TRPCError({
- code: "NOT_FOUND",
- message: "Asset not found",
- });
- }
- if (asset.userId != opts.ctx.user.id) {
- throw new TRPCError({
- code: "FORBIDDEN",
- message: "User is not allowed to access resource",
- });
- }
- return asset;
-};
-
export const assetsAppRouter = router({
list: authedProcedure
.input(
z.object({
limit: z.number().min(1).max(100).default(20),
- cursor: z.number().nullish(), // page number
+ cursor: z.number().nullish(),
}),
)
.output(
@@ -71,29 +34,10 @@ export const assetsAppRouter = router({
}),
)
.query(async ({ input, ctx }) => {
- const page = input.cursor ?? 1;
- const [results, totalCount] = await Promise.all([
- ctx.db
- .select()
- .from(assets)
- .where(eq(assets.userId, ctx.user.id))
- .orderBy(desc(assets.size))
- .limit(input.limit)
- .offset((page - 1) * input.limit),
- ctx.db
- .select({ count: sql<number>`count(*)` })
- .from(assets)
- .where(eq(assets.userId, ctx.user.id)),
- ]);
-
- return {
- assets: results.map((a) => ({
- ...a,
- assetType: mapDBAssetTypeToUserType(a.assetType),
- })),
- nextCursor: page * input.limit < totalCount[0].count ? page + 1 : null,
- totalCount: totalCount[0].count,
- };
+ return await Asset.list(ctx, {
+ limit: input.limit,
+ cursor: input.cursor ?? null,
+ });
}),
attachAsset: authedProcedure
.input(
@@ -108,29 +52,7 @@ export const assetsAppRouter = router({
.output(zAssetSchema)
.use(ensureBookmarkOwnership)
.mutation(async ({ input, ctx }) => {
- await ensureAssetOwnership({ ctx, assetId: input.asset.id });
- if (!isAllowedToAttachAsset(input.asset.assetType)) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "You can't attach this type of asset",
- });
- }
- const [updatedAsset] = await ctx.db
- .update(assets)
- .set({
- assetType: mapSchemaAssetTypeToDB(input.asset.assetType),
- bookmarkId: input.bookmarkId,
- })
- .where(
- and(eq(assets.id, input.asset.id), eq(assets.userId, ctx.user.id)),
- )
- .returning();
-
- return {
- id: updatedAsset.id,
- assetType: mapDBAssetTypeToUserType(updatedAsset.assetType),
- fileName: updatedAsset.fileName,
- };
+ return await Asset.attachAsset(ctx, input);
}),
replaceAsset: authedProcedure
.input(
@@ -143,41 +65,7 @@ export const assetsAppRouter = router({
.output(z.void())
.use(ensureBookmarkOwnership)
.mutation(async ({ input, ctx }) => {
- await Promise.all([
- ensureAssetOwnership({ ctx, assetId: input.oldAssetId }),
- ensureAssetOwnership({ ctx, assetId: input.newAssetId }),
- ]);
- const [oldAsset] = await ctx.db
- .select()
- .from(assets)
- .where(
- and(eq(assets.id, input.oldAssetId), eq(assets.userId, ctx.user.id)),
- )
- .limit(1);
- if (
- !isAllowedToAttachAsset(mapDBAssetTypeToUserType(oldAsset.assetType))
- ) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "You can't attach this type of asset",
- });
- }
-
- await ctx.db.transaction(async (tx) => {
- await tx.delete(assets).where(eq(assets.id, input.oldAssetId));
- await tx
- .update(assets)
- .set({
- bookmarkId: input.bookmarkId,
- assetType: oldAsset.assetType,
- })
- .where(eq(assets.id, input.newAssetId));
- });
-
- await deleteAsset({
- userId: ctx.user.id,
- assetId: input.oldAssetId,
- }).catch(() => ({}));
+ await Asset.replaceAsset(ctx, input);
}),
detachAsset: authedProcedure
.input(
@@ -189,34 +77,6 @@ export const assetsAppRouter = router({
.output(z.void())
.use(ensureBookmarkOwnership)
.mutation(async ({ input, ctx }) => {
- await ensureAssetOwnership({ ctx, assetId: input.assetId });
- const [oldAsset] = await ctx.db
- .select()
- .from(assets)
- .where(
- and(eq(assets.id, input.assetId), eq(assets.userId, ctx.user.id)),
- );
- if (
- !isAllowedToDetachAsset(mapDBAssetTypeToUserType(oldAsset.assetType))
- ) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "You can't deattach this type of asset",
- });
- }
- const result = await ctx.db
- .delete(assets)
- .where(
- and(
- eq(assets.id, input.assetId),
- eq(assets.bookmarkId, input.bookmarkId),
- ),
- );
- if (result.changes == 0) {
- throw new TRPCError({ code: "NOT_FOUND" });
- }
- await deleteAsset({ userId: ctx.user.id, assetId: input.assetId }).catch(
- () => ({}),
- );
+ await Asset.detachAsset(ctx, input);
}),
});
diff --git a/packages/trpc/routers/bookmarks.ts b/packages/trpc/routers/bookmarks.ts
index 65d401e2..a9d0df38 100644
--- a/packages/trpc/routers/bookmarks.ts
+++ b/packages/trpc/routers/bookmarks.ts
@@ -48,9 +48,9 @@ import { normalizeTagName } from "@karakeep/shared/utils/tag";
import type { AuthedContext } from "../index";
import { authedProcedure, createRateLimitMiddleware, router } from "../index";
import { getBookmarkIdsFromMatcher } from "../lib/search";
+import { Asset } from "../models/assets";
import { BareBookmark, Bookmark } from "../models/bookmarks";
import { ImportSession } from "../models/importSessions";
-import { ensureAssetOwnership } from "./assets";
export const ensureBookmarkOwnership = experimental_trpcMiddleware<{
ctx: AuthedContext;
@@ -178,10 +178,7 @@ export const bookmarksAppRouter = router({
.returning()
)[0];
if (input.precrawledArchiveId) {
- await ensureAssetOwnership({
- ctx,
- assetId: input.precrawledArchiveId,
- });
+ await Asset.ensureOwnership(ctx, input.precrawledArchiveId);
await tx
.update(assets)
.set({
@@ -232,13 +229,13 @@ export const bookmarksAppRouter = router({
sourceUrl: null,
})
.returning();
- const uploadedAsset = await ensureAssetOwnership({
- ctx,
- assetId: input.assetId,
- });
+ const uploadedAsset = await Asset.fromId(ctx, input.assetId);
+ uploadedAsset.ensureOwnership();
if (
- !uploadedAsset.contentType ||
- !SUPPORTED_BOOKMARK_ASSET_TYPES.has(uploadedAsset.contentType)
+ !uploadedAsset.asset.contentType ||
+ !SUPPORTED_BOOKMARK_ASSET_TYPES.has(
+ uploadedAsset.asset.contentType,
+ )
) {
throw new TRPCError({
code: "BAD_REQUEST",