aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc/routers/bookmarks.ts
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2026-01-18 21:38:21 +0000
committerGitHub <noreply@github.com>2026-01-18 21:38:21 +0000
commite09061bd37c6496685ea0fdabe1d4d01f1b659ad (patch)
tree2896ccceb54e7af8dc006d4bdc89b18af42ca078 /packages/trpc/routers/bookmarks.ts
parentedf3f681a77ce6739ca0773af6e5a645e2c91ee9 (diff)
downloadkarakeep-e09061bd37c6496685ea0fdabe1d4d01f1b659ad.tar.zst
feat: Add attachedBy field to update tags endpoint (#2281)
* feat: Add attachedBy field to updateTags endpoint This change allows callers to specify the attachedBy field when updating tags on a bookmark. The field defaults to "human" if not provided, maintaining backward compatibility with existing code. Changes: - Added attachedBy field to zManipulatedTagSchema with default "human" - Updated updateTags endpoint to use the specified attachedBy value - Created mapping logic to correctly assign attachedBy to each tag * fix(cli): migrate bookmark source in migration command * fix * reduce queries --------- Co-authored-by: Claude <noreply@anthropic.com>
Diffstat (limited to 'packages/trpc/routers/bookmarks.ts')
-rw-r--r--packages/trpc/routers/bookmarks.ts54
1 files changed, 42 insertions, 12 deletions
diff --git a/packages/trpc/routers/bookmarks.ts b/packages/trpc/routers/bookmarks.ts
index bf960748..37497bcf 100644
--- a/packages/trpc/routers/bookmarks.ts
+++ b/packages/trpc/routers/bookmarks.ts
@@ -714,10 +714,10 @@ export const bookmarksAppRouter = router({
)
.use(ensureBookmarkOwnership)
.mutation(async ({ input, ctx }) => {
- // Helper function to fetch tag IDs from a list of tag identifiers
- const fetchTagIds = async (
+ // Helper function to fetch tag IDs and their names from a list of tag identifiers
+ const fetchTagIdsWithNames = async (
tagIdentifiers: { tagId?: string; tagName?: string }[],
- ): Promise<string[]> => {
+ ): Promise<{ id: string; name: string }[]> => {
const tagIds = tagIdentifiers.flatMap((t) =>
t.tagId ? [t.tagId] : [],
);
@@ -729,7 +729,7 @@ export const bookmarksAppRouter = router({
const [byIds, byNames] = await Promise.all([
tagIds.length > 0
? ctx.db
- .select({ id: bookmarkTags.id })
+ .select({ id: bookmarkTags.id, name: bookmarkTags.name })
.from(bookmarkTags)
.where(
and(
@@ -740,7 +740,7 @@ export const bookmarksAppRouter = router({
: Promise.resolve([]),
tagNames.length > 0
? ctx.db
- .select({ id: bookmarkTags.id })
+ .select({ id: bookmarkTags.id, name: bookmarkTags.name })
.from(bookmarkTags)
.where(
and(
@@ -751,15 +751,25 @@ export const bookmarksAppRouter = router({
: Promise.resolve([]),
]);
- // Union results and deduplicate tag IDs
- const results = [...byIds, ...byNames];
- return [...new Set(results.map((t) => t.id))];
+ // Union results and deduplicate by tag ID
+ const seen = new Set<string>();
+ const results: { id: string; name: string }[] = [];
+
+ for (const tag of [...byIds, ...byNames]) {
+ if (!seen.has(tag.id)) {
+ seen.add(tag.id);
+ results.push({ id: tag.id, name: tag.name });
+ }
+ }
+
+ return results;
};
// Normalize tag names and create new tags outside transaction to reduce transaction duration
const normalizedAttachTags = input.attach.map((tag) => ({
tagId: tag.tagId,
tagName: tag.tagName ? normalizeTagName(tag.tagName) : undefined,
+ attachedBy: tag.attachedBy,
}));
{
@@ -779,11 +789,31 @@ export const bookmarksAppRouter = router({
}
// Fetch tag IDs for attachment/detachment now that we know that they all exist
- const [allIdsToAttach, idsToRemove] = await Promise.all([
- fetchTagIds(normalizedAttachTags),
- fetchTagIds(input.detach),
+ const [attachTagsWithNames, detachTagsWithNames] = await Promise.all([
+ fetchTagIdsWithNames(normalizedAttachTags),
+ fetchTagIdsWithNames(input.detach),
]);
+ // Build the attachedBy map from the fetched results
+ const tagIdToAttachedBy = new Map<string, "ai" | "human">();
+
+ for (const fetchedTag of attachTagsWithNames) {
+ // Find the corresponding input tag
+ const inputTag = normalizedAttachTags.find(
+ (t) =>
+ (t.tagId && t.tagId === fetchedTag.id) ||
+ (t.tagName && t.tagName === fetchedTag.name),
+ );
+
+ if (inputTag) {
+ tagIdToAttachedBy.set(fetchedTag.id, inputTag.attachedBy);
+ }
+ }
+
+ // Extract just the IDs for the transaction
+ const allIdsToAttach = attachTagsWithNames.map((t) => t.id);
+ const idsToRemove = detachTagsWithNames.map((t) => t.id);
+
const res = await ctx.db.transaction(async (tx) => {
// Detaches
if (idsToRemove.length > 0) {
@@ -805,7 +835,7 @@ export const bookmarksAppRouter = router({
allIdsToAttach.map((i) => ({
tagId: i,
bookmarkId: input.bookmarkId,
- attachedBy: "human" as const,
+ attachedBy: tagIdToAttachedBy.get(i) ?? "human",
})),
)
.onConflictDoNothing();