mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-03-18 10:30:49 +01:00
feat: new ap mocks, now publishing user outboxes
This commit is contained in:
@@ -986,6 +986,53 @@ Mocks.activities.create = async (pid, uid, post) => {
|
||||
return { activity, targets };
|
||||
};
|
||||
|
||||
Mocks.activities.like = (pid, uid) => ({
|
||||
id: `${nconf.get('url')}/uid/${uid}#activity/like/${encodeURIComponent(pid)}`,
|
||||
type: 'Like',
|
||||
actor: `${nconf.get('url')}/uid/${uid}`,
|
||||
object: utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid,
|
||||
});
|
||||
|
||||
Mocks.activities.dislike = (pid, uid) => ({
|
||||
id: `${nconf.get('url')}/uid/${uid}#activity/dislike/${encodeURIComponent(pid)}`,
|
||||
type: 'Dislike',
|
||||
actor: `${nconf.get('url')}/uid/${uid}`,
|
||||
object: utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid,
|
||||
});
|
||||
|
||||
Mocks.activities.announce = async (tid, uid) => {
|
||||
const { mainPid: pid, cid } = await topics.getTopicFields(tid, ['mainPid', 'cid']);
|
||||
const authorUid = await posts.getPostField(pid, 'uid'); // author
|
||||
const { to, cc, targets } = await activitypub.buildRecipients({
|
||||
id: pid,
|
||||
to: [activitypub._constants.publicAddress],
|
||||
}, uid ? { uid } : { cid });
|
||||
if (!utils.isNumber(authorUid)) {
|
||||
cc.push(authorUid);
|
||||
targets.add(authorUid);
|
||||
}
|
||||
|
||||
const payload = uid ? {
|
||||
id: `${nconf.get('url')}/post/${encodeURIComponent(pid)}#activity/announce/uid/${uid}`,
|
||||
type: 'Announce',
|
||||
actor: `${nconf.get('url')}/uid/${uid}`,
|
||||
} : {
|
||||
id: `${nconf.get('url')}/post/${encodeURIComponent(pid)}#activity/announce/cid/${cid}`,
|
||||
type: 'Announce',
|
||||
actor: `${nconf.get('url')}/category/${cid}`,
|
||||
};
|
||||
|
||||
return {
|
||||
activity: {
|
||||
...payload,
|
||||
to,
|
||||
cc,
|
||||
object: utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid,
|
||||
},
|
||||
targets,
|
||||
};
|
||||
};
|
||||
|
||||
Mocks.tombstone = async properties => ({
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
type: 'Tombstone',
|
||||
|
||||
@@ -254,12 +254,7 @@ Out.delete.note = enabledCheck(async (uid, pid) => {
|
||||
Out.like = {};
|
||||
|
||||
Out.like.note = enabledCheck(async (uid, pid) => {
|
||||
const payload = {
|
||||
id: `${nconf.get('url')}/uid/${uid}#activity/like/${encodeURIComponent(pid)}`,
|
||||
type: 'Like',
|
||||
actor: `${nconf.get('url')}/uid/${uid}`,
|
||||
object: utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid,
|
||||
};
|
||||
const payload = activitypub.mocks.activities.like(pid, uid);
|
||||
|
||||
if (!activitypub.helpers.isUri(pid)) { // only 1b12 announce for local likes
|
||||
await activitypub.feps.announce(pid, payload);
|
||||
@@ -280,12 +275,7 @@ Out.like.note = enabledCheck(async (uid, pid) => {
|
||||
Out.dislike = {};
|
||||
|
||||
Out.dislike.note = enabledCheck(async (uid, pid) => {
|
||||
const payload = {
|
||||
id: `${nconf.get('url')}/uid/${uid}#activity/dislike/${encodeURIComponent(pid)}`,
|
||||
type: 'Dislike',
|
||||
actor: `${nconf.get('url')}/uid/${uid}`,
|
||||
object: utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid,
|
||||
};
|
||||
const payload = activitypub.mocks.activities.dislike(pid, uid);
|
||||
|
||||
if (!activitypub.helpers.isUri(pid)) { // only 1b12 announce for local likes
|
||||
await activitypub.feps.announce(pid, payload);
|
||||
@@ -320,37 +310,14 @@ Out.announce.topic = enabledCheck(async (tid, uid) => {
|
||||
}
|
||||
}
|
||||
|
||||
const authorUid = await posts.getPostField(pid, 'uid'); // author
|
||||
const allowed = await privileges.posts.can('topics:read', pid, activitypub._constants.uid);
|
||||
if (!allowed) {
|
||||
activitypub.helpers.log(`[activitypub/api] Not federating announce of pid ${pid} to the fediverse due to privileges.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const { to, cc, targets } = await activitypub.buildRecipients({
|
||||
id: pid,
|
||||
to: [activitypub._constants.publicAddress],
|
||||
}, uid ? { uid } : { cid });
|
||||
if (!utils.isNumber(authorUid)) {
|
||||
cc.push(authorUid);
|
||||
targets.add(authorUid);
|
||||
}
|
||||
|
||||
const payload = uid ? {
|
||||
id: `${nconf.get('url')}/post/${encodeURIComponent(pid)}#activity/announce/uid/${uid}`,
|
||||
type: 'Announce',
|
||||
actor: `${nconf.get('url')}/uid/${uid}`,
|
||||
} : {
|
||||
id: `${nconf.get('url')}/post/${encodeURIComponent(pid)}#activity/announce/cid/${cid}`,
|
||||
type: 'Announce',
|
||||
actor: `${nconf.get('url')}/category/${cid}`,
|
||||
};
|
||||
await activitypub.send(uid ? 'uid' : 'cid', uid || cid, Array.from(targets), {
|
||||
...payload,
|
||||
to,
|
||||
cc,
|
||||
object: utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid,
|
||||
});
|
||||
const { activity, targets } = await activitypub.mocks.activities.announce(tid, uid);
|
||||
await activitypub.send(uid ? 'uid' : 'cid', uid || cid, Array.from(targets), activity);
|
||||
});
|
||||
|
||||
Out.flag = enabledCheck(async (uid, flag) => {
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
const nconf = require('nconf');
|
||||
const winston = require('winston');
|
||||
|
||||
const db = require('../../database');
|
||||
const meta = require('../../meta');
|
||||
const posts = require('../../posts');
|
||||
const user = require('../../user');
|
||||
const activitypub = require('../../activitypub');
|
||||
const utils = require('../../utils');
|
||||
@@ -116,12 +118,98 @@ Controller.getFollowers = async (req, res) => {
|
||||
};
|
||||
|
||||
Controller.getOutbox = async (req, res) => {
|
||||
// stub
|
||||
// Posts, shares, and votes
|
||||
const { uid } = req.params;
|
||||
let { after, before } = req.query;
|
||||
|
||||
let totalItems = await db.sortedSetsCard([`uid:${uid}:posts`, `uid:${uid}:upvote`, `uid:${uid}:downvote`, `uid:${uid}:shares`]);
|
||||
totalItems = totalItems.reduce((sum, count) => {
|
||||
sum += count;
|
||||
return sum;
|
||||
}, 0);
|
||||
|
||||
const perPage = 20;
|
||||
let paginate = true;
|
||||
if (totalItems <= perPage) {
|
||||
before = undefined;
|
||||
after = undefined;
|
||||
paginate = false;
|
||||
}
|
||||
|
||||
let prev;
|
||||
let next;
|
||||
const partOf = paginate && (after || before) && `${nconf.get('url')}/uid/${uid}/outbox`;
|
||||
const first = paginate && !after && !before && `${nconf.get('url')}/uid/${uid}/outbox?after=${Date.now()}`;
|
||||
const last = paginate && !after && !before && `${nconf.get('url')}/uid/${uid}/outbox?before=0`;
|
||||
let activities;
|
||||
|
||||
if (!paginate || after || before) {
|
||||
const limit = after ? parseInt(after, 10) - 1 : parseInt(before, 10) + 1;
|
||||
const method = after ? 'getSortedSetRevRangeByScoreWithScores' : 'getSortedSetRangeByScoreWithScores';
|
||||
|
||||
const [post, upvote, downvote, share] = await Promise.all([
|
||||
db[method](`uid:${uid}:posts`, 0, 20, limit, `${after ? '-' : '+'}inf`),
|
||||
db[method](`uid:${uid}:upvote`, 0, 20, limit, `${after ? '-' : '+'}inf`),
|
||||
db[method](`uid:${uid}:downvote`, 0, 20, limit, `${after ? '-' : '+'}inf`),
|
||||
db[method](`uid:${uid}:shares`, 0, 20, limit, `${after ? '-' : '+'}inf`),
|
||||
]);
|
||||
activities = [
|
||||
post.map(post => ({ ...post, type: 'post' })),
|
||||
upvote.map(upvote => ({ ...upvote, type: 'upvote' })),
|
||||
downvote.map(downvote => ({ ...downvote, type: 'downvote' })),
|
||||
share.map(share => ({ ...share, type: 'share' })),
|
||||
].flat().sort((a, b) => b.score - a.score);
|
||||
if (after) {
|
||||
activities = activities.slice(0, 20);
|
||||
} else {
|
||||
activities = activities.slice(-20);
|
||||
}
|
||||
|
||||
if (activities.length) {
|
||||
prev = `${nconf.get('url')}/uid/${uid}/outbox?before=${activities[0].score}`;
|
||||
next = `${nconf.get('url')}/uid/${uid}/outbox?after=${activities[19].score}`;
|
||||
|
||||
let postsData = activities.filter((({ type }) => type === 'post'));
|
||||
postsData = await posts.getPostSummaryByPids(postsData.map(({ value }) => value), 0, { stripTags: false });
|
||||
postsData = postsData.reduce((map, postData) => {
|
||||
map.set(postData.pid, postData);
|
||||
return map;
|
||||
}, new Map());
|
||||
|
||||
activities = await Promise.all(activities.map(async ({ type, value: id }) => {
|
||||
switch (type) {
|
||||
case 'post': {
|
||||
const { activity } = await activitypub.mocks.activities.create(id, 0, postsData.get(id));
|
||||
return activity;
|
||||
}
|
||||
|
||||
case 'upvote': {
|
||||
return activitypub.mocks.activities.like(id, uid);
|
||||
}
|
||||
|
||||
case 'downvote': {
|
||||
return activitypub.mocks.activities.dislike(id, uid);
|
||||
}
|
||||
|
||||
case 'share': {
|
||||
const { activity } = await activitypub.mocks.activities.announce(id, uid);
|
||||
return activity;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
type: 'OrderedCollection',
|
||||
totalItems: 0,
|
||||
orderedItems: [],
|
||||
type: paginate ? 'OrderedCollectionPage' : 'OrderedCollection',
|
||||
totalItems,
|
||||
...(prev && { prev }),
|
||||
...(next && { next }),
|
||||
...(first && { first }),
|
||||
...(last && { last }),
|
||||
...(partOf && { partOf }),
|
||||
orderedItems: activities,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user