aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc/routers
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-12-22 14:04:53 +0000
committerMohamed Bassem <me@mbassem.com>2025-12-22 14:04:53 +0000
commitca4bfa4c88287aba23c104a93e4fb6be7b2776da (patch)
treefa15813651d6e96a565464acb71f2c5a40a8d526 /packages/trpc/routers
parente18dc4c93eedc925bed6cc0754b200c58bcdf7f8 (diff)
downloadkarakeep-ca4bfa4c88287aba23c104a93e4fb6be7b2776da.tar.zst
fix: check quota usage instead bookmark transaction
Diffstat (limited to 'packages/trpc/routers')
-rw-r--r--packages/trpc/routers/bookmarks.ts254
1 files changed, 129 insertions, 125 deletions
diff --git a/packages/trpc/routers/bookmarks.ts b/packages/trpc/routers/bookmarks.ts
index 4f18e11b..65d401e2 100644
--- a/packages/trpc/routers/bookmarks.ts
+++ b/packages/trpc/routers/bookmarks.ts
@@ -131,149 +131,153 @@ export const bookmarksAppRouter = router({
}
}
- // Check user quota
- const quotaResult = await QuotaService.canCreateBookmark(
- ctx.db,
- ctx.user.id,
- );
- if (!quotaResult.result) {
- throw new TRPCError({
- code: "FORBIDDEN",
- message: quotaResult.error,
- });
- }
-
- const bookmark = await ctx.db.transaction(async (tx) => {
- const bookmark = (
- await tx
- .insert(bookmarks)
- .values({
- userId: ctx.user.id,
- title: input.title,
- type: input.type,
- archived: input.archived,
- favourited: input.favourited,
- note: input.note,
- summary: input.summary,
- createdAt: input.createdAt,
- source: input.source,
- // Only links currently support summarization. Let's set the status to null for other types for now.
- summarizationStatus:
- input.type === BookmarkTypes.LINK ? "pending" : null,
- })
- .returning()
- )[0];
-
- let content: ZBookmarkContent;
-
- switch (input.type) {
- case BookmarkTypes.LINK: {
- const link = (
- await tx
- .insert(bookmarkLinks)
+ const bookmark = await ctx.db.transaction(
+ async (tx) => {
+ // Check user quota
+ const quotaResult = await QuotaService.canCreateBookmark(
+ tx,
+ ctx.user.id,
+ );
+ if (!quotaResult.result) {
+ throw new TRPCError({
+ code: "FORBIDDEN",
+ message: quotaResult.error,
+ });
+ }
+ const bookmark = (
+ await tx
+ .insert(bookmarks)
+ .values({
+ userId: ctx.user.id,
+ title: input.title,
+ type: input.type,
+ archived: input.archived,
+ favourited: input.favourited,
+ note: input.note,
+ summary: input.summary,
+ createdAt: input.createdAt,
+ source: input.source,
+ // Only links currently support summarization. Let's set the status to null for other types for now.
+ summarizationStatus:
+ input.type === BookmarkTypes.LINK ? "pending" : null,
+ })
+ .returning()
+ )[0];
+
+ let content: ZBookmarkContent;
+
+ switch (input.type) {
+ case BookmarkTypes.LINK: {
+ const link = (
+ await tx
+ .insert(bookmarkLinks)
+ .values({
+ id: bookmark.id,
+ url: input.url.trim(),
+ })
+ .returning()
+ )[0];
+ if (input.precrawledArchiveId) {
+ await ensureAssetOwnership({
+ ctx,
+ assetId: input.precrawledArchiveId,
+ });
+ await tx
+ .update(assets)
+ .set({
+ bookmarkId: bookmark.id,
+ assetType: AssetTypes.LINK_PRECRAWLED_ARCHIVE,
+ })
+ .where(
+ and(
+ eq(assets.id, input.precrawledArchiveId),
+ eq(assets.userId, ctx.user.id),
+ ),
+ );
+ }
+ content = {
+ type: BookmarkTypes.LINK,
+ ...link,
+ };
+ break;
+ }
+ case BookmarkTypes.TEXT: {
+ const text = (
+ await tx
+ .insert(bookmarkTexts)
+ .values({
+ id: bookmark.id,
+ text: input.text,
+ sourceUrl: input.sourceUrl,
+ })
+ .returning()
+ )[0];
+ content = {
+ type: BookmarkTypes.TEXT,
+ text: text.text ?? "",
+ sourceUrl: text.sourceUrl,
+ };
+ break;
+ }
+ case BookmarkTypes.ASSET: {
+ const [asset] = await tx
+ .insert(bookmarkAssets)
.values({
id: bookmark.id,
- url: input.url.trim(),
+ assetType: input.assetType,
+ assetId: input.assetId,
+ content: null,
+ metadata: null,
+ fileName: input.fileName ?? null,
+ sourceUrl: null,
})
- .returning()
- )[0];
- if (input.precrawledArchiveId) {
- await ensureAssetOwnership({
+ .returning();
+ const uploadedAsset = await ensureAssetOwnership({
ctx,
- assetId: input.precrawledArchiveId,
+ assetId: input.assetId,
});
+ if (
+ !uploadedAsset.contentType ||
+ !SUPPORTED_BOOKMARK_ASSET_TYPES.has(uploadedAsset.contentType)
+ ) {
+ throw new TRPCError({
+ code: "BAD_REQUEST",
+ message: "Unsupported asset type",
+ });
+ }
await tx
.update(assets)
.set({
bookmarkId: bookmark.id,
- assetType: AssetTypes.LINK_PRECRAWLED_ARCHIVE,
+ assetType: AssetTypes.BOOKMARK_ASSET,
})
.where(
and(
- eq(assets.id, input.precrawledArchiveId),
+ eq(assets.id, input.assetId),
eq(assets.userId, ctx.user.id),
),
);
+ content = {
+ type: BookmarkTypes.ASSET,
+ assetType: asset.assetType,
+ assetId: asset.assetId,
+ };
+ break;
}
- content = {
- type: BookmarkTypes.LINK,
- ...link,
- };
- break;
- }
- case BookmarkTypes.TEXT: {
- const text = (
- await tx
- .insert(bookmarkTexts)
- .values({
- id: bookmark.id,
- text: input.text,
- sourceUrl: input.sourceUrl,
- })
- .returning()
- )[0];
- content = {
- type: BookmarkTypes.TEXT,
- text: text.text ?? "",
- sourceUrl: text.sourceUrl,
- };
- break;
- }
- case BookmarkTypes.ASSET: {
- const [asset] = await tx
- .insert(bookmarkAssets)
- .values({
- id: bookmark.id,
- assetType: input.assetType,
- assetId: input.assetId,
- content: null,
- metadata: null,
- fileName: input.fileName ?? null,
- sourceUrl: null,
- })
- .returning();
- const uploadedAsset = await ensureAssetOwnership({
- ctx,
- assetId: input.assetId,
- });
- if (
- !uploadedAsset.contentType ||
- !SUPPORTED_BOOKMARK_ASSET_TYPES.has(uploadedAsset.contentType)
- ) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "Unsupported asset type",
- });
- }
- await tx
- .update(assets)
- .set({
- bookmarkId: bookmark.id,
- assetType: AssetTypes.BOOKMARK_ASSET,
- })
- .where(
- and(
- eq(assets.id, input.assetId),
- eq(assets.userId, ctx.user.id),
- ),
- );
- content = {
- type: BookmarkTypes.ASSET,
- assetType: asset.assetType,
- assetId: asset.assetId,
- };
- break;
}
- }
- return {
- alreadyExists: false,
- tags: [] as ZBookmarkTags[],
- assets: [],
- content,
- ...bookmark,
- };
- });
+ return {
+ alreadyExists: false,
+ tags: [] as ZBookmarkTags[],
+ assets: [],
+ content,
+ ...bookmark,
+ };
+ },
+ {
+ behavior: "immediate",
+ },
+ );
if (input.importSessionId) {
const session = await ImportSession.fromId(ctx, input.importSessionId);