From aa983a4731d2e5fb063e3c75c0b2498b65edd164 Mon Sep 17 00:00:00 2001 From: juprodh Date: Sat, 22 Aug 2026 13:24:42 +0800 Subject: [PATCH] Key bookmarked wikis on at_uri so same-slug wikis stay apart Signed-off-by: juprodh --- src/server/db/queries/bookmark.ts | 20 +++------ src/server/db/queries/wiki.ts | 2 +- tests/server/db/queries/bookmark.test.ts | 55 ++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/src/server/db/queries/bookmark.ts b/src/server/db/queries/bookmark.ts index de54241..50124a1 100644 --- a/src/server/db/queries/bookmark.ts +++ b/src/server/db/queries/bookmark.ts @@ -1,6 +1,6 @@ import { getDb } from "../index.ts"; import { WIKI_NOT_MODERATED } from "./moderation.ts"; -import type { WikiWithNoteCount } from "./wiki.ts"; +import { WIKI_WITH_COUNTS, type WikiWithNoteCount } from "./wiki.ts"; export function upsertBookmark( did: string, @@ -54,18 +54,12 @@ export function getBookmarksForUser(did: string): WikiWithNoteCount[] { const db = getDb(); return db .query( - `SELECT w.*, COUNT(n.slug) AS note_count, - pc.handle AS owner_handle, - COALESCE(NULLIF(pc.handle, ''), w.did) AS owner_ref, - pc.avatar AS owner_avatar - FROM bookmarks b - JOIN wikis w ON w.at_uri = b.wiki_at_uri - LEFT JOIN notes n ON n.wiki_slug = w.slug - LEFT JOIN profile_cache pc ON pc.did = w.did - WHERE b.did = ? - AND ${WIKI_NOT_MODERATED} - GROUP BY w.slug - ORDER BY b.created_at DESC`, + `${WIKI_WITH_COUNTS} + JOIN bookmarks b ON b.wiki_at_uri = w.at_uri + WHERE b.did = ? + AND ${WIKI_NOT_MODERATED} + GROUP BY w.at_uri + ORDER BY b.created_at DESC`, ) .all(did) as WikiWithNoteCount[]; } diff --git a/src/server/db/queries/wiki.ts b/src/server/db/queries/wiki.ts index 0fdb517..bec5770 100644 --- a/src/server/db/queries/wiki.ts +++ b/src/server/db/queries/wiki.ts @@ -21,7 +21,7 @@ const OWNER_COLUMNS = `pc.handle AS owner_handle, COALESCE(NULLIF(pc.handle, ''), w.did) AS owner_ref, pc.avatar AS owner_avatar`; -const WIKI_WITH_COUNTS = ` +export const WIKI_WITH_COUNTS = ` SELECT w.*, COUNT(n.slug) as note_count, ${OWNER_COLUMNS} FROM wikis w diff --git a/tests/server/db/queries/bookmark.test.ts b/tests/server/db/queries/bookmark.test.ts index 0410d81..69e2c6d 100644 --- a/tests/server/db/queries/bookmark.test.ts +++ b/tests/server/db/queries/bookmark.test.ts @@ -79,6 +79,61 @@ describe("getBookmarksForUser", () => { test("returns empty for user with no bookmarks", () => { expect(getBookmarksForUser("did:plc:no-bookmarks")).toEqual([]); }); + + // A slug is unique per owner, not per service: "notes" and "wiki" are the + // common ones, so two bookmarks colliding on one is the normal case. + test("keeps bookmarks of same-slug wikis apart", () => { + const db = getDb(); + const COLLIDER = "did:plc:bookmark-collider"; + const shared = "notes"; + const owners = ["did:plc:bmk-alice", "did:plc:bmk-bob"]; + + for (const [i, owner] of owners.entries()) { + const wikiAtUri = `at://${owner}/wiki.lichen.wiki/${shared}`; + db.run( + `INSERT OR REPLACE INTO wikis (slug, did, name, visibility, language, at_uri, created_at, updated_at) + VALUES (?, ?, ?, 'public', 'en', ?, ?, ?)`, + [ + shared, + owner, + `Notes of ${owner}`, + wikiAtUri, + "2026-01-01", + "2026-01-01", + ], + ); + db.run( + `INSERT OR REPLACE INTO notes (wiki_at_uri, wiki_slug, slug, did, title, at_uri, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?)`, + [ + wikiAtUri, + shared, + `note-${i}`, + owner, + `Note ${i}`, + `at://${owner}/wiki.lichen.note/note-${i}`, + "2026-01-01", + ], + ); + upsertBookmark( + COLLIDER, + wikiAtUri, + `at://${COLLIDER}/community.lexicon.bookmarks.bookmark/bk-${i}`, + `2026-01-0${i + 1}T00:00:00.000Z`, + ); + } + + const bookmarks = getBookmarksForUser(COLLIDER); + expect(bookmarks.map((w) => w.did).sort()).toEqual(owners); + // One note each — a slug-keyed join counts both wikis' notes for both. + expect(bookmarks.map((w) => w.note_count)).toEqual([1, 1]); + + db.run("DELETE FROM bookmarks WHERE did = ?", [COLLIDER]); + for (const owner of owners) { + db.run("DELETE FROM notes WHERE did = ?", [owner]); + db.run("DELETE FROM wikis WHERE did = ?", [owner]); + } + }); }); describe("deleteBookmarkByUri", () => { -- 2.51.2