From 52b23313a3abc139a049f0fdf43d4fbb9929d9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 24 Feb 2025 11:04:19 -0500 Subject: [PATCH 1/8] fix: missing db --- src/emailer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/emailer.js b/src/emailer.js index 48b463759c..6c57d6b44a 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -12,6 +12,7 @@ const fs = require('fs'); const _ = require('lodash'); const jwt = require('jsonwebtoken'); +const db = require('./database'); const User = require('./user'); const Plugins = require('./plugins'); const meta = require('./meta'); From 07957e82436e9af6ae9be5163a308239dbd12665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Mon, 24 Feb 2025 12:40:25 -0500 Subject: [PATCH 2/8] fix: don't send validation email for pending emails --- src/user/profile.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/user/profile.js b/src/user/profile.js index c150675d6a..3009d0a3d5 100644 --- a/src/user/profile.js +++ b/src/user/profile.js @@ -282,6 +282,9 @@ module.exports = function (User) { if (oldEmail === newEmail) { return; } + if (await User.email.isValidationPending(uid, newEmail)) { + return; + } // 👉 Looking for email change logic? src/user/email.js (UserEmail.confirmByUid) if (newEmail) { From 53a2be9defea7cd144694473910bd81d20ed4538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 25 Feb 2025 10:27:38 -0500 Subject: [PATCH 3/8] refactor: don't make db request if there is no code --- src/user/email.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/user/email.js b/src/user/email.js index aec9379f41..161a3b1b0f 100644 --- a/src/user/email.js +++ b/src/user/email.js @@ -53,7 +53,7 @@ UserEmail.getEmailForValidation = async (uid) => { let email = ''; // check email from confirmObj const code = await db.get(`confirm:byUid:${uid}`); - const confirmObj = await db.getObject(`confirm:${code}`); + const confirmObj = code ? await db.getObject(`confirm:${code}`) : null; if (confirmObj && confirmObj.email && parseInt(uid, 10) === parseInt(confirmObj.uid, 10)) { email = confirmObj.email; } From 349084d8f90f56b4ac1873b99673d8cfe102fc53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Tue, 25 Feb 2025 10:33:06 -0500 Subject: [PATCH 4/8] refactor: use sortedSetRemoveBulk --- src/user/email.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/user/email.js b/src/user/email.js index 161a3b1b0f..c14c9c93fc 100644 --- a/src/user/email.js +++ b/src/user/email.js @@ -36,8 +36,10 @@ UserEmail.remove = async function (uid, sessionId) { email: '', 'email:confirmed': 0, }), - db.sortedSetRemove('email:uid', email.toLowerCase()), - db.sortedSetRemove('email:sorted', `${email.toLowerCase()}:${uid}`), + db.sortedSetRemoveBulk([ + ['email:uid', email.toLowerCase()], + ['email:sorted', `${email.toLowerCase()}:${uid}`], + ]), user.email.expireValidation(uid), sessionId ? user.auth.revokeAllSessions(uid, sessionId) : Promise.resolve(), events.log({ From d91b80d293de38b7aac31abc498e1602f1203962 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 25 Feb 2025 13:32:40 -0500 Subject: [PATCH 5/8] fix: handle multiple types in remote actor payload --- src/activitypub/actors.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/activitypub/actors.js b/src/activitypub/actors.js index bda513661c..0fa91f49c2 100644 --- a/src/activitypub/actors.js +++ b/src/activitypub/actors.js @@ -100,8 +100,16 @@ Actors.assert = async (ids, options = {}) => { try { activitypub.helpers.log(`[activitypub/actors] Processing ${id}`); const actor = (typeof id === 'object' && id.hasOwnProperty('id')) ? id : await activitypub.get('uid', 0, id, { cache: process.env.CI === 'true' }); + + let typeOk = false; + if (Array.isArray(actor.type)) { + typeOk = actor.type.some(type => activitypub._constants.acceptableActorTypes.has(type)); + } else { + typeOk = activitypub._constants.acceptableActorTypes.has(actor.type); + } + if ( - !activitypub._constants.acceptableActorTypes.has(actor.type) || + !typeOk || !activitypub._constants.requiredActorProps.every(prop => actor.hasOwnProperty(prop)) ) { return null; From 64267f7de01d0b7ec53dceab24bcd7bab67a9d58 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 25 Feb 2025 14:24:56 -0500 Subject: [PATCH 6/8] test: remove extra .only, add basic tests for public note assertion --- test/activitypub.js | 2 +- test/activitypub/notes.js | 58 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/test/activitypub.js b/test/activitypub.js index d2c2334140..d05f50e4e6 100644 --- a/test/activitypub.js +++ b/test/activitypub.js @@ -350,7 +350,7 @@ describe('ActivityPub integration', () => { }); }); - describe.only('Category Actor endpoint', () => { + describe('Category Actor endpoint', () => { let cid; let slug; let description; diff --git a/test/activitypub/notes.js b/test/activitypub/notes.js index 482b706d14..803c345c4b 100644 --- a/test/activitypub/notes.js +++ b/test/activitypub/notes.js @@ -3,6 +3,8 @@ const assert = require('assert'); const db = require('../../src/database'); +const meta = require('../../src/meta'); +const install = require('../../src/install'); const user = require('../../src/user'); const categories = require('../../src/categories'); const topics = require('../../src/topics'); @@ -10,6 +12,62 @@ const activitypub = require('../../src/activitypub'); const utils = require('../../src/utils'); describe('Notes', () => { + describe('Assertion', () => { + const baseUrl = 'https://example.org'; + + before(async () => { + meta.config.activitypubEnabled = 1; + await install.giveWorldPrivileges(); + }); + + it('should pull a remote root-level object by its id and create a new topic', async () => { + const uuid = utils.generateUUID(); + const id = `${baseUrl}/resource/${uuid}`; + activitypub._cache.set(`0;${id}`, { + '@context': 'https://www.w3.org/ns/activitystreams', + id, + url: id, + type: 'Note', + to: ['https://www.w3.org/ns/activitystreams#Public'], + cc: ['https://example.org/user/foobar/followers'], + inReplyTo: null, + attributedTo: 'https://example.org/user/foobar', + name: 'Foo Bar', + content: 'Baz quux', + published: new Date().toISOString(), + }); + + const { tid, count } = await activitypub.notes.assert(0, id, { skipChecks: true }); + assert.strictEqual(count, 1); + + const exists = await topics.exists(tid); + assert(exists); + }); + + it('should assert if the cc property is missing', async () => { + const uuid = utils.generateUUID(); + const id = `${baseUrl}/resource/${uuid}`; + activitypub._cache.set(`0;${id}`, { + '@context': 'https://www.w3.org/ns/activitystreams', + id, + url: id, + type: 'Note', + to: ['https://www.w3.org/ns/activitystreams#Public'], + inReplyTo: null, + attributedTo: 'https://example.org/user/foobar', + name: 'Foo Bar', + content: 'Baz quux', + published: new Date().toISOString(), + }); + + const { tid, count } = await activitypub.notes.assert(0, id, { skipChecks: true }); + assert.strictEqual(count, 1); + + const exists = await topics.exists(tid); + assert(exists); + }); + }); + describe('Inbox Synchronization', () => { let cid; let uid; From 13a13e1d1365ce553fda5466dd443e0371993700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Feb 2025 11:39:59 -0500 Subject: [PATCH 7/8] fix: closes #13207, add localComments --- src/routes/well-known.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/routes/well-known.js b/src/routes/well-known.js index becd1370f8..399668b679 100644 --- a/src/routes/well-known.js +++ b/src/routes/well-known.js @@ -39,8 +39,8 @@ module.exports = function (app, middleware, controllers) { const oneMonthAgo = addMonths(new Date(), -1); const sixMonthsAgo = addMonths(new Date(), -6); - const [{ postCount, userCount }, activeMonth, activeHalfyear] = await Promise.all([ - db.getObjectFields('global', ['postCount', 'userCount']), + const [{ postCount, topicCount, userCount }, activeMonth, activeHalfyear] = await Promise.all([ + db.getObjectFields('global', ['postCount', 'topicCount', 'userCount']), db.sortedSetCount('users:online', oneMonthAgo.getTime(), '+inf'), db.sortedSetCount('users:online', sixMonthsAgo.getTime(), '+inf'), ]); @@ -64,7 +64,8 @@ module.exports = function (app, middleware, controllers) { activeMonth: activeMonth, activeHalfyear: activeHalfyear, }, - localPosts: postCount, + localPosts: topicCount, + localComments: postCount - topicCount, }, openRegistrations: meta.config.registrationType === 'normal', metadata: { From 51872d5435b2a6349874fc74c90a03f5c430749d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 26 Feb 2025 11:51:35 -0500 Subject: [PATCH 8/8] fix: closes #13206, truncate long usernames --- src/views/partials/chats/parent.tpl | 2 +- src/views/partials/topic/post-parent.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/partials/chats/parent.tpl b/src/views/partials/chats/parent.tpl index 2d2e66bf3b..68f91414c5 100644 --- a/src/views/partials/chats/parent.tpl +++ b/src/views/partials/chats/parent.tpl @@ -4,7 +4,7 @@
diff --git a/src/views/partials/topic/post-parent.tpl b/src/views/partials/topic/post-parent.tpl index cabc6bb36c..38f22da616 100644 --- a/src/views/partials/topic/post-parent.tpl +++ b/src/views/partials/topic/post-parent.tpl @@ -3,7 +3,7 @@