From 6fc6cc33cd2ca524f277c1a3faafd37ef55e8396 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 10 Apr 2024 22:01:44 -0400 Subject: [PATCH] feat: note attachments via link preview plugin --- src/activitypub/mocks.js | 10 ++++++-- src/activitypub/notes.js | 37 ++------------------------- src/posts/attachments.js | 54 ++++++++++++++++++++++++++++++++++++++++ src/posts/index.js | 2 ++ 4 files changed, 66 insertions(+), 37 deletions(-) create mode 100644 src/posts/attachments.js diff --git a/src/activitypub/mocks.js b/src/activitypub/mocks.js index aa0e826045..f24c995f53 100644 --- a/src/activitypub/mocks.js +++ b/src/activitypub/mocks.js @@ -213,7 +213,6 @@ Mocks.note = async (post) => { const id = `${nconf.get('url')}/post/${post.pid}`; const published = new Date(parseInt(post.timestamp, 10)).toISOString(); - // todo: post visibility const to = new Set([activitypub._constants.publicAddress]); const cc = new Set([`${nconf.get('url')}/uid/${post.user.uid}/followers`]); @@ -281,6 +280,13 @@ Mocks.note = async (post) => { } } + let attachment = await posts.attachments.get(post.pid) || []; + attachment = attachment.map(({ mediaType, url }) => ({ + type: 'Document', + mediaType, + url, + })); + const object = { '@context': 'https://www.w3.org/ns/activitystreams', id, @@ -298,7 +304,7 @@ Mocks.note = async (post) => { content: post.content, source, tag, - attachment: [], // todo... requires refactoring of link preview plugin + attachment, // replies: {} todo... }; diff --git a/src/activitypub/notes.js b/src/activitypub/notes.js index 0a7af2e4f3..b083811644 100644 --- a/src/activitypub/notes.js +++ b/src/activitypub/notes.js @@ -1,7 +1,6 @@ 'use strict'; const winston = require('winston'); -const crypto = require('crypto'); const nconf = require('nconf'); const db = require('../database'); @@ -135,7 +134,7 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => { _activitypub: mainPost._activitypub, }), Notes.updateLocalRecipients(mainPid, { to, cc }), - Notes.saveAttachments(mainPid, attachment), + posts.attachments.update(mainPid, attachment), ]); unprocessed.pop(); } @@ -148,7 +147,7 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => { await Promise.all([ topics.reply(post), Notes.updateLocalRecipients(post.pid, { to, cc }), - Notes.saveAttachments(post.pid, attachment), + posts.attachments.update(post.pid, attachment), ]); // Category announce @@ -228,38 +227,6 @@ Notes.updateLocalRecipients = async (id, { to, cc }) => { } }; -Notes.saveAttachments = async (id, attachments) => { - if (!attachments) { - return; - } - - const bulkOps = { - hash: [], - zset: { - score: [], - value: [], - }, - }; - - attachments.filter(Boolean).forEach(({ mediaType, url, name, width, height }, idx) => { - if (!url) { // only required property - return; - } - - const hash = crypto.createHash('sha256').update(url).digest('hex'); - const key = `attachment:${hash}`; - - bulkOps.hash.push([key, { mediaType, url, name, width, height }]); - bulkOps.zset.score.push(idx); - bulkOps.zset.value.push(hash); - }); - - await Promise.all([ - db.setObjectBulk(bulkOps.hash), - db.sortedSetAdd(`post:${id}:attachments`, bulkOps.zset.score, bulkOps.zset.value), - ]); -}; - Notes.getParentChain = async (uid, input) => { // Traverse upwards via `inReplyTo` until you find the root-level Note const id = activitypub.helpers.isUri(input) ? input : input.id; diff --git a/src/posts/attachments.js b/src/posts/attachments.js new file mode 100644 index 0000000000..b94590109d --- /dev/null +++ b/src/posts/attachments.js @@ -0,0 +1,54 @@ +'use strict'; + +const crypto = require('crypto'); + +const db = require('../database'); + +const Attachments = module.exports; + +Attachments.get = async (pid) => { + const hashes = await db.getSortedSetMembers(`post:${pid}:attachments`); + const keys = hashes.map(hash => `attachment:${hash}`); + const attachments = (await db.getObjects(keys)).filter(Boolean); + + return attachments; +}; + +Attachments.update = async (pid, attachments) => { + if (!attachments) { + return; + } + + const bulkOps = { + hash: [], + zset: { + score: [], + value: [], + }, + }; + + attachments.filter(Boolean).forEach(({ _type, mediaType, url, name, width, height }, idx) => { + if (!url) { // only required property + return; + } + + const hash = crypto.createHash('sha256').update(url).digest('hex'); + const key = `attachment:${hash}`; + + if (_type) { + _type = 'attachment'; + } + + bulkOps.hash.push([key, { _type, mediaType, url, name, width, height }]); + bulkOps.zset.score.push(idx); + bulkOps.zset.value.push(hash); + }); + + await Promise.all([ + db.setObjectBulk(bulkOps.hash), + db.sortedSetAdd(`post:${pid}:attachments`, bulkOps.zset.score, bulkOps.zset.value), + ]); +}; + +// todo +// Attachments.remove = async (pid) => { ... } diff --git a/src/posts/index.js b/src/posts/index.js index d2bb6cb360..00b82922b1 100644 --- a/src/posts/index.js +++ b/src/posts/index.js @@ -27,6 +27,8 @@ require('./queue')(Posts); require('./diffs')(Posts); require('./uploads')(Posts); +Posts.attachments = require('./attachments'); + Posts.exists = async function (pids) { return await db.exists( Array.isArray(pids) ? pids.map(pid => `post:${pid}`) : `post:${pids}`