mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
feat: changes to how a topic is presented via ActivityPub; conformance with upcoming changes to 7888
This commit is contained in:
@@ -12,6 +12,7 @@ const acceptableTypes = ['Collection', 'CollectionPage', 'OrderedCollection', 'O
|
|||||||
Contexts.get = async (uid, id) => {
|
Contexts.get = async (uid, id) => {
|
||||||
let context;
|
let context;
|
||||||
let type;
|
let type;
|
||||||
|
let collection;
|
||||||
|
|
||||||
// Generate digest for If-None-Match if locally cached
|
// Generate digest for If-None-Match if locally cached
|
||||||
const tid = await posts.getPostField(id, 'tid');
|
const tid = await posts.getPostField(id, 'tid');
|
||||||
@@ -27,12 +28,15 @@ Contexts.get = async (uid, id) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
({ context } = await activitypub.get('uid', uid, id, { headers }));
|
({ id, type, context, posts: collection } = await activitypub.get('uid', uid, id, { headers }));
|
||||||
if (!context) {
|
if (type === 'Conversation' && collection) {
|
||||||
|
activitypub.helpers.log(`[activitypub/context] ${id} is the context.`);
|
||||||
|
return { context: id, collection };
|
||||||
|
} else if (!context) {
|
||||||
activitypub.helpers.log(`[activitypub/context] ${id} contains no context.`);
|
activitypub.helpers.log(`[activitypub/context] ${id} contains no context.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
({ type } = await activitypub.get('uid', uid, context));
|
({ type, posts: collection } = await activitypub.get('uid', uid, context));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code === 'ap_get_304') {
|
if (e.code === 'ap_get_304') {
|
||||||
activitypub.helpers.log(`[activitypub/context] ${id} context unchanged.`);
|
activitypub.helpers.log(`[activitypub/context] ${id} context unchanged.`);
|
||||||
@@ -43,8 +47,8 @@ Contexts.get = async (uid, id) => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (acceptableTypes.includes(type)) {
|
if (type === 'Conversation') {
|
||||||
return { context };
|
return { context, collection };
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -98,9 +102,13 @@ Contexts.getItems = async (uid, id, options) => {
|
|||||||
const inputId = activitypub.helpers.isUri(options.input) ? options.input : options.input.id;
|
const inputId = activitypub.helpers.isUri(options.input) ? options.input : options.input.id;
|
||||||
const inCollection = Array.from(chain).map(p => p.pid).includes(inputId);
|
const inCollection = Array.from(chain).map(p => p.pid).includes(inputId);
|
||||||
if (!inCollection) {
|
if (!inCollection) {
|
||||||
chain.add(activitypub.helpers.isUri(options.input) ?
|
const item = activitypub.helpers.isUri(options.input) ?
|
||||||
await parseString(uid, options.input) :
|
await parseString(uid, options.input) :
|
||||||
await parseItem(uid, options.input));
|
await parseItem(uid, options.input);
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
chain.add(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return chain;
|
return chain;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ Notes.assert = async (uid, input, options = { skipChecks: false }) => {
|
|||||||
const { tid } = context;
|
const { tid } = context;
|
||||||
return { tid, count: 0 };
|
return { tid, count: 0 };
|
||||||
} else if (context.context) {
|
} else if (context.context) {
|
||||||
chain = Array.from(await activitypub.contexts.getItems(uid, context.context, { input }));
|
chain = Array.from(await activitypub.contexts.getItems(uid, context.collection, { input }));
|
||||||
if (chain && chain.length) {
|
if (chain && chain.length) {
|
||||||
// Context resolves, use in later topic creation
|
// Context resolves, use in later topic creation
|
||||||
context = context.context;
|
context = context.context;
|
||||||
|
|||||||
@@ -115,12 +115,52 @@ Actors.replies = async function (req, res) {
|
|||||||
Actors.topic = async function (req, res, next) {
|
Actors.topic = async function (req, res, next) {
|
||||||
const allowed = await privileges.topics.can('topics:read', req.params.tid, activitypub._constants.uid);
|
const allowed = await privileges.topics.can('topics:read', req.params.tid, activitypub._constants.uid);
|
||||||
if (!allowed) {
|
if (!allowed) {
|
||||||
return res.sendStatus(404);
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
const { cid, titleRaw: name, mainPid, slug } = await topics.getTopicFields(req.params.tid, ['cid', 'title', 'mainPid', 'slug']);
|
||||||
|
let pids = await db.getSortedSetMembers(`tid:${req.params.tid}:posts`);
|
||||||
|
pids.push(mainPid);
|
||||||
|
pids = pids.map(pid => (utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid));
|
||||||
|
|
||||||
|
// Generate digest for ETag
|
||||||
|
const digest = activitypub.helpers.generateDigest(new Set(pids));
|
||||||
|
const ifNoneMatch = (req.get('If-None-Match') || '').split(',').map((tag) => {
|
||||||
|
tag = tag.trim();
|
||||||
|
if (tag.startsWith('"') && tag.endsWith('"')) {
|
||||||
|
return tag.slice(1, tag.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
});
|
||||||
|
if (ifNoneMatch.includes(digest)) {
|
||||||
|
return res.sendStatus(304);
|
||||||
|
}
|
||||||
|
res.set('ETag', digest);
|
||||||
|
|
||||||
|
const object = {
|
||||||
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
|
id: `${nconf.get('url')}/topic/${req.params.tid}`,
|
||||||
|
url: `${nconf.get('url')}/topic/${slug}`,
|
||||||
|
type: 'Conversation',
|
||||||
|
name,
|
||||||
|
attributedTo: `${nconf.get('url')}/category/${cid}`,
|
||||||
|
// audience: cid !== -1 ? `${nconf.get('url')}/category/${cid}` : undefined,
|
||||||
|
posts: `${nconf.get('url')}/topic/${req.params.tid}/posts`,
|
||||||
|
};
|
||||||
|
|
||||||
|
res.status(200).json(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
Actors.topicPosts = async function (req, res, next) {
|
||||||
|
const allowed = await privileges.topics.can('topics:read', req.params.tid, activitypub._constants.uid);
|
||||||
|
if (!allowed) {
|
||||||
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
const page = parseInt(req.query.page, 10) || undefined;
|
const page = parseInt(req.query.page, 10) || undefined;
|
||||||
const perPage = meta.config.postsPerPage;
|
const perPage = meta.config.postsPerPage;
|
||||||
const { cid, titleRaw: name, mainPid, slug } = await topics.getTopicFields(req.params.tid, ['cid', 'title', 'mainPid', 'slug']);
|
const mainPid = await topics.getTopicField(req.params.tid, 'mainPid');
|
||||||
try {
|
try {
|
||||||
let [collection, pids] = await Promise.all([
|
let [collection, pids] = await Promise.all([
|
||||||
activitypub.helpers.generateCollection({
|
activitypub.helpers.generateCollection({
|
||||||
@@ -136,21 +176,6 @@ Actors.topic = async function (req, res, next) {
|
|||||||
pids = pids.map(pid => (utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid));
|
pids = pids.map(pid => (utils.isNumber(pid) ? `${nconf.get('url')}/post/${pid}` : pid));
|
||||||
collection.totalItems += 1; // account for mainPid
|
collection.totalItems += 1; // account for mainPid
|
||||||
|
|
||||||
// Generate digest for ETag
|
|
||||||
const digest = activitypub.helpers.generateDigest(new Set(pids));
|
|
||||||
const ifNoneMatch = (req.get('If-None-Match') || '').split(',').map((tag) => {
|
|
||||||
tag = tag.trim();
|
|
||||||
if (tag.startsWith('"') && tag.endsWith('"')) {
|
|
||||||
return tag.slice(1, tag.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
});
|
|
||||||
if (ifNoneMatch.includes(digest)) {
|
|
||||||
return res.sendStatus(304);
|
|
||||||
}
|
|
||||||
res.set('ETag', digest);
|
|
||||||
|
|
||||||
// Convert pids to urls
|
// Convert pids to urls
|
||||||
if (page || collection.totalItems < meta.config.postsPerPage) {
|
if (page || collection.totalItems < meta.config.postsPerPage) {
|
||||||
collection.orderedItems = collection.orderedItems || [];
|
collection.orderedItems = collection.orderedItems || [];
|
||||||
@@ -162,11 +187,7 @@ Actors.topic = async function (req, res, next) {
|
|||||||
|
|
||||||
const object = {
|
const object = {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
id: `${nconf.get('url')}/topic/${req.params.tid}${collection.orderedItems && page ? `?page=${page}` : ''}`,
|
id: `${nconf.get('url')}/topic/${req.params.tid}/posts${collection.orderedItems && page ? `?page=${page}` : ''}`,
|
||||||
url: `${nconf.get('url')}/topic/${slug}`,
|
|
||||||
name,
|
|
||||||
attributedTo: `${nconf.get('url')}/category/${cid}`,
|
|
||||||
audience: cid !== -1 ? `${nconf.get('url')}/category/${cid}` : undefined,
|
|
||||||
...collection,
|
...collection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -40,13 +40,14 @@ module.exports = function (app, middleware, controllers) {
|
|||||||
app.get('/post/:pid', [...middlewares, middleware.assert.post], controllers.activitypub.actors.note);
|
app.get('/post/:pid', [...middlewares, middleware.assert.post], controllers.activitypub.actors.note);
|
||||||
app.get('/post/:pid/replies', [...middlewares, middleware.assert.post], controllers.activitypub.actors.replies);
|
app.get('/post/:pid/replies', [...middlewares, middleware.assert.post], controllers.activitypub.actors.replies);
|
||||||
|
|
||||||
|
app.get('/topic/:tid/posts', [...middlewares, middleware.assert.topic], controllers.activitypub.actors.topicPosts);
|
||||||
app.get('/topic/:tid/:slug?', [...middlewares, middleware.assert.topic], controllers.activitypub.actors.topic);
|
app.get('/topic/:tid/:slug?', [...middlewares, middleware.assert.topic], controllers.activitypub.actors.topic);
|
||||||
|
|
||||||
app.get('/category/:cid/:slug?', [...middlewares, middleware.assert.category], controllers.activitypub.actors.category);
|
|
||||||
app.get('/category/:cid/inbox', [...middlewares, middleware.assert.category], controllers.activitypub.getInbox);
|
app.get('/category/:cid/inbox', [...middlewares, middleware.assert.category], controllers.activitypub.getInbox);
|
||||||
app.post('/category/:cid/inbox', [...inboxMiddlewares, middleware.assert.category, ...inboxMiddlewares], controllers.activitypub.postInbox);
|
app.post('/category/:cid/inbox', [...inboxMiddlewares, middleware.assert.category, ...inboxMiddlewares], controllers.activitypub.postInbox);
|
||||||
app.get('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.getCategoryOutbox);
|
app.get('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.getCategoryOutbox);
|
||||||
app.post('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.postOutbox);
|
app.post('/category/:cid/outbox', [...middlewares, middleware.assert.category], controllers.activitypub.postOutbox);
|
||||||
|
app.get('/category/:cid/:slug?', [...middlewares, middleware.assert.category], controllers.activitypub.actors.category);
|
||||||
|
|
||||||
app.get('/message/:mid', [...middlewares, middleware.assert.message], controllers.activitypub.actors.message);
|
app.get('/message/:mid', [...middlewares, middleware.assert.message], controllers.activitypub.actors.message);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user