mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-10-31 19:15:58 +01:00 
			
		
		
		
	fix: issues related to adding new reply chains to an existing topic, resolveId method in notes module
This commit is contained in:
		| @@ -8,24 +8,32 @@ const posts = require('../posts'); | |||||||
| const activitypub = module.parent.exports; | const activitypub = module.parent.exports; | ||||||
| const Notes = module.exports; | const Notes = module.exports; | ||||||
|  |  | ||||||
|  | Notes.resolveId = async (uid, id) => { | ||||||
|  | 	({ id } = await activitypub.get(uid, id)); | ||||||
|  | 	return id; | ||||||
|  | }; | ||||||
|  |  | ||||||
| // todo: when asserted, notes aren't added to a global sorted set | // todo: when asserted, notes aren't added to a global sorted set | ||||||
| // also, db.exists call is probably expensive | // also, db.exists call is probably expensive | ||||||
| Notes.assert = async (uid, input) => { | Notes.assert = async (uid, input) => { | ||||||
| 	// Ensures that each note has been saved to the database | 	// Ensures that each note has been saved to the database | ||||||
| 	await Promise.all(input.map(async (item) => { | 	await Promise.all(input.map(async (item) => { | ||||||
| 		const id = activitypub.helpers.isUri(item) ? item : item.id; | 		const id = activitypub.helpers.isUri(item) ? item : item.pid; | ||||||
| 		const key = `post:${id}`; | 		const key = `post:${id}`; | ||||||
| 		const exists = await db.exists(key); | 		const exists = await db.exists(key); | ||||||
| 		winston.verbose(`[activitypub/notes.assert] Asserting note id ${id}`); | 		winston.verbose(`[activitypub/notes.assert] Asserting note id ${id}`); | ||||||
|  |  | ||||||
| 		let postData; |  | ||||||
| 		if (!exists) { | 		if (!exists) { | ||||||
|  | 			let postData; | ||||||
| 			winston.verbose(`[activitypub/notes.assert] Not found, saving note to database`); | 			winston.verbose(`[activitypub/notes.assert] Not found, saving note to database`); | ||||||
| 			const object = activitypub.helpers.isUri(item) ? await activitypub.get(uid, item) : item; | 			if (activitypub.helpers.isUri(item)) { | ||||||
|  | 				const object = await activitypub.get(uid, item); | ||||||
| 				postData = await activitypub.mocks.post(object); | 				postData = await activitypub.mocks.post(object); | ||||||
| 			if (postData) { | 			} else { | ||||||
| 				await db.setObject(key, postData); | 				postData = item; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | 			await db.setObject(key, postData); | ||||||
| 		} | 		} | ||||||
| 	})); | 	})); | ||||||
| }; | }; | ||||||
| @@ -59,21 +67,41 @@ Notes.getParentChain = async (uid, input) => { | |||||||
| 	return chain; | 	return chain; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | Notes.assertParentChain = async (chain) => { | ||||||
|  | 	const data = []; | ||||||
|  | 	chain.reduce((child, parent) => { | ||||||
|  | 		data.push([`pid:${parent.pid}:replies`, child.timestamp, child.pid]); | ||||||
|  | 		return parent; | ||||||
|  | 	}); | ||||||
|  |  | ||||||
|  | 	await db.sortedSetAddBulk(data); | ||||||
|  | }; | ||||||
|  |  | ||||||
| Notes.assertTopic = async (uid, id) => { | Notes.assertTopic = async (uid, id) => { | ||||||
| 	// Given the id of any post, traverses up (and soon, down) to cache the entire threaded context | 	/** | ||||||
|  | 	 * Given the id of any post, traverses up to cache the entire threaded context | ||||||
|  | 	 * | ||||||
|  | 	 * Unfortunately, due to limitations and fragmentation of the existing ActivityPub landscape, | ||||||
|  | 	 * retrieving the entire reply tree is not possible at this time. | ||||||
|  | 	 */ | ||||||
|  |  | ||||||
| 	const chain = Array.from(await Notes.getParentChain(uid, id)); | 	const chain = Array.from(await Notes.getParentChain(uid, id)); | ||||||
| 	const tid = chain[chain.length - 1].pid; | 	const tid = chain[chain.length - 1].pid; | ||||||
|  |  | ||||||
| 	const sorted = chain.sort((a, b) => a.timestamp - b.timestamp); | 	const members = await db.isSortedSetMembers(`tidRemote:${tid}:posts`, chain.map(p => p.pid)); | ||||||
| 	const [ids, timestamps] = [ | 	if (members.every(Boolean)) { | ||||||
| 		sorted.map(n => n.id), | 		// All cached, return early. | ||||||
| 		sorted.map(n => n.timestamp), | 		winston.info('[notes/assertTopic] No new notes to process.'); | ||||||
| 	]; | 		return tid; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	const postercount = chain.reduce((set, cur) => { | 	const unprocessed = chain.filter((p, idx) => !members[idx]); | ||||||
| 		set.add(cur.uid); | 	winston.info(`[notes/assertTopic] ${unprocessed.length} new notes found.`); | ||||||
| 		return set; |  | ||||||
| 	}, new Set()); | 	const [ids, timestamps] = [ | ||||||
|  | 		unprocessed.map(n => n.pid), | ||||||
|  | 		unprocessed.map(n => n.timestamp), | ||||||
|  | 	]; | ||||||
|  |  | ||||||
| 	await Promise.all([ | 	await Promise.all([ | ||||||
| 		db.setObject(`topicRemote:${tid}`, { | 		db.setObject(`topicRemote:${tid}`, { | ||||||
| @@ -83,16 +111,32 @@ Notes.assertTopic = async (uid, id) => { | |||||||
| 			mainPid: tid, | 			mainPid: tid, | ||||||
| 			title: 'TBD', | 			title: 'TBD', | ||||||
| 			slug: `remote?resource=${encodeURIComponent(tid)}`, | 			slug: `remote?resource=${encodeURIComponent(tid)}`, | ||||||
| 			postcount: sorted.length, |  | ||||||
| 			postercount, |  | ||||||
| 		}), | 		}), | ||||||
| 		db.sortedSetAdd(`tidRemote:${tid}:posts`, timestamps, ids), | 		db.sortedSetAdd(`tidRemote:${tid}:posts`, timestamps, ids), | ||||||
| 		Notes.assert(uid, chain), | 		Notes.assert(uid, unprocessed), | ||||||
|  | 	]); | ||||||
|  | 	await Promise.all([ // must be done after .assert() | ||||||
|  | 		Notes.assertParentChain(chain), | ||||||
|  | 		Notes.updateTopicCounts(tid), | ||||||
| 	]); | 	]); | ||||||
|  |  | ||||||
| 	return tid; | 	return tid; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | Notes.updateTopicCounts = async function (tid) { | ||||||
|  | 	const pids = await db.getSortedSetMembers(`tidRemote:${tid}:posts`); | ||||||
|  | 	let uids = await db.getObjectsFields(pids.map(p => `post:${p}`), ['uid']); | ||||||
|  | 	uids = uids.reduce((set, { uid }) => { | ||||||
|  | 		set.add(uid); | ||||||
|  | 		return set; | ||||||
|  | 	}, new Set()); | ||||||
|  |  | ||||||
|  | 	db.setObject(`topicRemote:${tid}`, { | ||||||
|  | 		postercount: uids.size, | ||||||
|  | 		postcount: pids.length, | ||||||
|  | 	}); | ||||||
|  | }; | ||||||
|  |  | ||||||
| Notes.getTopicPosts = async (tid, uid, start, stop) => { | Notes.getTopicPosts = async (tid, uid, start, stop) => { | ||||||
| 	const pids = await db.getSortedSetRange(`tidRemote:${tid}:posts`, start, stop); | 	const pids = await db.getSortedSetRange(`tidRemote:${tid}:posts`, start, stop); | ||||||
| 	return await posts.getPostsByPids(pids, uid); | 	return await posts.getPostsByPids(pids, uid); | ||||||
|   | |||||||
| @@ -9,10 +9,16 @@ const topics = require('../../topics'); | |||||||
| const { notes } = require('../../activitypub'); | const { notes } = require('../../activitypub'); | ||||||
| // const helpers = require('../helpers'); | // const helpers = require('../helpers'); | ||||||
| const pagination = require('../../pagination'); | const pagination = require('../../pagination'); | ||||||
|  | const helpers = require('../helpers'); | ||||||
|  |  | ||||||
| const controller = module.exports; | const controller = module.exports; | ||||||
|  |  | ||||||
| controller.get = async function (req, res, next) { | controller.get = async function (req, res, next) { | ||||||
|  | 	const pid = await notes.resolveId(req.uid, req.query.resource); | ||||||
|  | 	if (pid !== req.query.resource) { | ||||||
|  | 		return helpers.redirect(res, `/topic/remote?resource=${pid}`, true); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	const tid = await notes.assertTopic(req.uid, req.query.resource); | 	const tid = await notes.assertTopic(req.uid, req.query.resource); | ||||||
|  |  | ||||||
| 	let postIndex = await db.sortedSetRank(`tidRemote:${tid}:posts`, req.query.resource); | 	let postIndex = await db.sortedSetRank(`tidRemote:${tid}:posts`, req.query.resource); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user