mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-05-07 09:56:16 +02: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 };
|
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 => ({
|
Mocks.tombstone = async properties => ({
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
type: 'Tombstone',
|
type: 'Tombstone',
|
||||||
|
|||||||
@@ -254,12 +254,7 @@ Out.delete.note = enabledCheck(async (uid, pid) => {
|
|||||||
Out.like = {};
|
Out.like = {};
|
||||||
|
|
||||||
Out.like.note = enabledCheck(async (uid, pid) => {
|
Out.like.note = enabledCheck(async (uid, pid) => {
|
||||||
const payload = {
|
const payload = activitypub.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,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!activitypub.helpers.isUri(pid)) { // only 1b12 announce for local likes
|
if (!activitypub.helpers.isUri(pid)) { // only 1b12 announce for local likes
|
||||||
await activitypub.feps.announce(pid, payload);
|
await activitypub.feps.announce(pid, payload);
|
||||||
@@ -280,12 +275,7 @@ Out.like.note = enabledCheck(async (uid, pid) => {
|
|||||||
Out.dislike = {};
|
Out.dislike = {};
|
||||||
|
|
||||||
Out.dislike.note = enabledCheck(async (uid, pid) => {
|
Out.dislike.note = enabledCheck(async (uid, pid) => {
|
||||||
const payload = {
|
const payload = activitypub.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,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!activitypub.helpers.isUri(pid)) { // only 1b12 announce for local likes
|
if (!activitypub.helpers.isUri(pid)) { // only 1b12 announce for local likes
|
||||||
await activitypub.feps.announce(pid, payload);
|
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);
|
const allowed = await privileges.posts.can('topics:read', pid, activitypub._constants.uid);
|
||||||
if (!allowed) {
|
if (!allowed) {
|
||||||
activitypub.helpers.log(`[activitypub/api] Not federating announce of pid ${pid} to the fediverse due to privileges.`);
|
activitypub.helpers.log(`[activitypub/api] Not federating announce of pid ${pid} to the fediverse due to privileges.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { to, cc, targets } = await activitypub.buildRecipients({
|
const { activity, targets } = await activitypub.mocks.activities.announce(tid, uid);
|
||||||
id: pid,
|
await activitypub.send(uid ? 'uid' : 'cid', uid || cid, Array.from(targets), activity);
|
||||||
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,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Out.flag = enabledCheck(async (uid, flag) => {
|
Out.flag = enabledCheck(async (uid, flag) => {
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
const nconf = require('nconf');
|
const nconf = require('nconf');
|
||||||
const winston = require('winston');
|
const winston = require('winston');
|
||||||
|
|
||||||
|
const db = require('../../database');
|
||||||
const meta = require('../../meta');
|
const meta = require('../../meta');
|
||||||
|
const posts = require('../../posts');
|
||||||
const user = require('../../user');
|
const user = require('../../user');
|
||||||
const activitypub = require('../../activitypub');
|
const activitypub = require('../../activitypub');
|
||||||
const utils = require('../../utils');
|
const utils = require('../../utils');
|
||||||
@@ -116,12 +118,98 @@ Controller.getFollowers = async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Controller.getOutbox = 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({
|
res.status(200).json({
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
type: 'OrderedCollection',
|
type: paginate ? 'OrderedCollectionPage' : 'OrderedCollection',
|
||||||
totalItems: 0,
|
totalItems,
|
||||||
orderedItems: [],
|
...(prev && { prev }),
|
||||||
|
...(next && { next }),
|
||||||
|
...(first && { first }),
|
||||||
|
...(last && { last }),
|
||||||
|
...(partOf && { partOf }),
|
||||||
|
orderedItems: activities,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user