feat: new ap mocks, now publishing user outboxes

This commit is contained in:
Julian Lam
2026-03-11 12:34:57 -04:00
parent b835eef91f
commit f848393e6e
3 changed files with 143 additions and 41 deletions

View File

@@ -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,
});
};