From 9f07dae200a467543717483be60e83f26de323d4 Mon Sep 17 00:00:00 2001 From: Kibi Kelburton Date: Thu, 13 Aug 2026 18:04:44 +0200 Subject: [PATCH] fix comment visibility for private uploads --- src/inc/routes/comments.mjs | 56 ++++++++++++++++++-------------- src/inc/routes/notifications.mjs | 21 +++++++++--- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/src/inc/routes/comments.mjs b/src/inc/routes/comments.mjs index a7e10e8..0eed96d 100644 --- a/src/inc/routes/comments.mjs +++ b/src/inc/routes/comments.mjs @@ -615,30 +615,28 @@ export default (router, tpl) => { // 1. Thread live update db.notify('comments', JSON.stringify(livePayload)); - // 2. Sidebar activity update (only for public items) + // 2. Sidebar activity update const itemVisibility = itemQuery[0]?.visibility ?? 0; - if (itemVisibility === 0) { - const activityIsLong = content.length > 120 - || content.split('\n').length > 2 - || activityFiles.length > 0 - || /\[(video|audio|youtube|img)\]|!\[|https?:\/\//i.test(content); - db.notify('activity', JSON.stringify({ - user_id: req.session.id, - item_id: item_id, - type: 'comment', - body: notifyBody, - id: commentId, - item_rating_class: ratingClass, - item_rating_label: ratingLabel, - avatar: req.session.avatar, - avatar_file: req.session.avatar_file, - username: req.session.user, - username_color: req.session.username_color, - display_name: req.session.display_name || null, - files: activityFiles, - is_long: activityIsLong - })); - } + const activityIsLong = content.length > 120 + || content.split('\n').length > 2 + || activityFiles.length > 0 + || /\[(video|audio|youtube|img)\]|!\[|https?:\/\//i.test(content); + db.notify('activity', JSON.stringify({ + user_id: req.session.id, + item_id: item_id, + type: 'comment', + body: notifyBody, + id: commentId, + item_rating_class: ratingClass, + item_rating_label: ratingLabel, + avatar: req.session.avatar, + avatar_file: req.session.avatar_file, + username: req.session.user, + username_color: req.session.username_color, + display_name: req.session.display_name || null, + files: activityFiles, + is_long: activityIsLong + })); // Automatically subscribe user to the thread const subResult = await db` @@ -1006,6 +1004,16 @@ export default (router, tpl) => { const globalfilter = globalfilterTags.length ? globalfilterTags.map(n => `tag_id = ${n}`).join(' or ') : null; const excludedTags = req.session ? (req.session.excluded_tags || []) : []; + const isOwnerOrAdmin = req.session && (req.session.admin || req.session.is_moderator); + const sessionUser = req.session ? (req.session.user || '').toLowerCase() : null; + const sessionUserId = req.session ? req.session.id : null; + + const visibilityFilter = isOwnerOrAdmin + ? db`` + : (req.session + ? db`AND (COALESCE(i.visibility, 0) = 0 OR LOWER(i.username) = ${sessionUser} OR c.user_id = ${sessionUserId})` + : db`AND COALESCE(i.visibility, 0) = 0`); + const comments = await db` SELECT c.*, @@ -1028,7 +1036,7 @@ export default (router, tpl) => { WHERE c.is_deleted = false AND i.active = true AND i.is_deleted = false - AND COALESCE(i.visibility, 0) = 0 + ${visibilityFilter} AND ${db.unsafe(modequery)} ${!req.session && globalfilter ? db`and not exists (select 1 from tags_assign where item_id = i.id and (${db.unsafe(globalfilter)}))` : db``} ${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = i.id and tag_id = any(${excludedTags}::int[]))` : db``} diff --git a/src/inc/routes/notifications.mjs b/src/inc/routes/notifications.mjs index 4d47a98..2829262 100644 --- a/src/inc/routes/notifications.mjs +++ b/src/inc/routes/notifications.mjs @@ -123,7 +123,7 @@ db.listen('activity', async (payload) => { // We need the username, avatar, and item mime for the preview // trigger only gave us user_id and item_id const [details] = await db` - SELECT u.id as user_id, u.user as username, uo.avatar, uo.avatar_file, uo.username_color, uo.display_name, i.mime, i.slug as item_slug, COALESCE(i.visibility, 0) as visibility, + SELECT u.id as user_id, u.user as username, uo.avatar, uo.avatar_file, uo.username_color, uo.display_name, i.mime, i.slug as item_slug, COALESCE(i.visibility, 0) as visibility, LOWER(i.username) as item_username, (SELECT tag_id FROM tags_assign WHERE item_id = i.id AND tag_id IN (1, 2) LIMIT 1) as tag_id FROM "user" u LEFT JOIN user_options uo ON u.id = uo.user_id @@ -132,7 +132,6 @@ db.listen('activity', async (payload) => { `; if (details) { - if (details.visibility > 0) return; data.username = details.username; data.avatar = details.avatar; data.avatar_file = details.avatar_file; @@ -141,13 +140,25 @@ db.listen('activity', async (payload) => { data.display_name = details.display_name || null; data.tag_id = details.tag_id; data.item_slug = getEnableItemSlugs() ? details.item_slug : null; + data.visibility = details.visibility; + data.item_username = details.item_username; } else { data.username = 'System'; + data.visibility = 0; + data.item_username = null; } - // Broadcast to ALL connected clients + // Broadcast to clients permitted to view this activity for (const client of clients) { - client.send({ type: 'activity', data }); + const itemVisibility = data.visibility ?? 0; + const isPublic = itemVisibility === 0; + const isCommentAuthor = client.userId && client.userId === data.user_id; + const isItemOwner = client.username && data.item_username && client.username.toLowerCase() === data.item_username.toLowerCase(); + const isAdminOrMod = client.admin || client.is_moderator; + + if (isPublic || isCommentAuthor || isItemOwner || isAdminOrMod) { + client.send({ type: 'activity', data }); + } } } catch (e) { console.error('Activity broadcast error:', e); @@ -595,6 +606,8 @@ export default (router, tpl) => { avatar_file: req.session?.avatar_file || null, avatar: req.session?.avatar || null, username_color: req.session?.username_color || null, + admin: !!req.session?.admin, + is_moderator: !!req.session?.is_moderator, receive_system_notifications: req.session?.receive_system_notifications !== false, receive_user_notifications: req.session?.receive_user_notifications !== false, do_not_disturb: req.session?.do_not_disturb === true,