diff --git a/src/posts/votes.js b/src/posts/votes.js index 599c4f92d0..d9487b1dc2 100644 --- a/src/posts/votes.js +++ b/src/posts/votes.js @@ -177,16 +177,18 @@ module.exports = function (Posts) { } const now = Date.now(); - if (type === 'upvote' && !unvote) { - await db.sortedSetAdd(`uid:${uid}:upvote`, now, pid); - } else { - await db.sortedSetRemove(`uid:${uid}:upvote`, pid); - } + if (utils.isNumber(uid)) { + if (type === 'upvote' && !unvote) { + await db.sortedSetAdd(`uid:${uid}:upvote`, now, pid); + } else { + await db.sortedSetRemove(`uid:${uid}:upvote`, pid); + } - if (type === 'upvote' || unvote) { - await db.sortedSetRemove(`uid:${uid}:downvote`, pid); - } else { - await db.sortedSetAdd(`uid:${uid}:downvote`, now, pid); + if (type === 'upvote' || unvote) { + await db.sortedSetRemove(`uid:${uid}:downvote`, pid); + } else { + await db.sortedSetAdd(`uid:${uid}:downvote`, now, pid); + } } const postData = await Posts.getPostFields(pid, ['pid', 'uid', 'tid']); diff --git a/src/topics/unread.js b/src/topics/unread.js index 4dd5a94435..ed93f19abf 100644 --- a/src/topics/unread.js +++ b/src/topics/unread.js @@ -290,7 +290,7 @@ module.exports = function (Topics) { }; Topics.markAsRead = async function (tids, uid) { - if (!Array.isArray(tids) || !tids.length) { + if (!Array.isArray(tids) || !tids.length || !utils.isNumber(uid)) { return false; } diff --git a/src/upgrades/4.7.1/remove_extraneous_ap_data.js b/src/upgrades/4.7.1/remove_extraneous_ap_data.js new file mode 100644 index 0000000000..99b5d9de4d --- /dev/null +++ b/src/upgrades/4.7.1/remove_extraneous_ap_data.js @@ -0,0 +1,24 @@ +'use strict'; + +const db = require('../../database'); +const batch = require('../../batch'); + +module.exports = { + name: 'Remove extraneous upvote and tids_read data for remote users', + timestamp: Date.UTC(2025, 11, 11), + method: async function () { + const { progress } = this; + await batch.processSortedSet('usersRemote:lastCrawled', async (uids) => { + const readKeys = uids.map(uid => `uid:${uid}:tids_read`); + const voteKeys = uids.map(uid => `uid:${uid}:upvote`); + + const combined = readKeys.concat(voteKeys); + + await db.deleteAll(combined); + }, { + batch: 500, + progress, + }); + + }, +}; diff --git a/test/activitypub/notes.js b/test/activitypub/notes.js index b43f2da49f..1eba189b92 100644 --- a/test/activitypub/notes.js +++ b/test/activitypub/notes.js @@ -432,6 +432,7 @@ describe('Notes', () => { describe('Create', () => { let uid; + let cid; before(async () => { uid = await user.create({ username: utils.generateUUID() }); @@ -451,6 +452,17 @@ describe('Notes', () => { assert.strictEqual(cid, -1); }); + it('should not append to the tids_read sorted set', async () => { + const { note, id } = helpers.mocks.note(); + const { activity } = helpers.mocks.create(note); + + await db.sortedSetAdd(`followersRemote:${note.attributedTo}`, Date.now(), uid); + await activitypub.inbox.create({ body: activity }); + + const exists = await db.exists(`uid:${note.attributedTo}:tids_read`); + assert(!exists); + }); + it('should create a new topic in a remote category if addressed (category same-origin)', async () => { const { id: remoteCid } = helpers.mocks.group(); const { note, id } = helpers.mocks.note({ @@ -467,40 +479,63 @@ describe('Notes', () => { }); it('should create a new topic in cid -1 if a non-same origin remote category is addressed', async function () { - this.timeout(30000); - const start = Date.now(); const { id: remoteCid } = helpers.mocks.group({ id: `https://example.com/${utils.generateUUID()}`, }); - console.log('1', Date.now() - start); const { note, id } = helpers.mocks.note({ audience: [remoteCid], }); - console.log('2', Date.now() - start); const { activity } = helpers.mocks.create(note); - console.log('3', Date.now() - start); try { await activitypub.inbox.create({ body: activity }); } catch (err) { - console.log('error in test', err.stack); assert(false); } - console.log('4', Date.now() - start); assert(await posts.exists(id)); - console.log('5', Date.now() - start); const cid = await posts.getCidByPid(id); - console.log('6', Date.now() - start); assert.strictEqual(cid, -1); }); }); + + describe('(Like)', () => { + let pid; + let voterUid; + + before(async () => { + ({ cid } = await categories.create({ name: utils.generateUUID() })); + const { postData } = await topics.post({ + uid, + cid, + title: utils.generateUUID(), + content: utils.generateUUID(), + }); + pid = postData.pid; + const object = await activitypub.mocks.notes.public(postData); + const { activity } = helpers.mocks.like({ object }); + voterUid = activity.actor; + await activitypub.inbox.like({ body: activity }); + }); + + it('should increment a like for the post', async () => { + const voted = await posts.hasVoted(pid, voterUid); + const count = await posts.getPostField(pid, 'upvotes'); + assert(voted); + assert.strictEqual(count, 1); + }); + + it('should not append to the uid upvotes zset', async () => { + const exists = await db.exists(`uid:${voterUid}:upvote`); + assert(!exists); + }); + }); }); describe('Announce', () => { let cid; before(async () => { - ({ cid } = await categories.create({ name: utils.generateUUID().slice(0, 8) })); + ({ cid } = await categories.create({ name: utils.generateUUID() })); }); describe('(Create)', () => {