mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
feat: Announce(Note) and Undo(Announce)
This commit is contained in:
@@ -5,6 +5,7 @@ const winston = require('winston');
|
|||||||
const db = require('../database');
|
const db = require('../database');
|
||||||
const user = require('../user');
|
const user = require('../user');
|
||||||
const posts = require('../posts');
|
const posts = require('../posts');
|
||||||
|
const topics = require('../topics');
|
||||||
const categories = require('../categories');
|
const categories = require('../categories');
|
||||||
const activitypub = require('.');
|
const activitypub = require('.');
|
||||||
|
|
||||||
@@ -65,6 +66,46 @@ inbox.like = async (req) => {
|
|||||||
await posts.upvote(id, actor);
|
await posts.upvote(id, actor);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inbox.announce = async (req) => {
|
||||||
|
const { actor, object, published } = req.body;
|
||||||
|
let timestamp = Date.now();
|
||||||
|
try {
|
||||||
|
timestamp = new Date(published).getTime();
|
||||||
|
} catch (e) {
|
||||||
|
// ok to fail
|
||||||
|
}
|
||||||
|
|
||||||
|
const { type, id } = await activitypub.helpers.resolveLocalId(object);
|
||||||
|
if (type !== 'post' || !(await posts.exists(id))) {
|
||||||
|
throw new Error('[[error:activitypub.invalid-id]]');
|
||||||
|
}
|
||||||
|
|
||||||
|
const assertion = await activitypub.actors.assert(actor);
|
||||||
|
if (!assertion) {
|
||||||
|
throw new Error('[[error:activitypub.invalid-id]]');
|
||||||
|
}
|
||||||
|
|
||||||
|
const tid = await posts.getPostField(id, 'tid');
|
||||||
|
|
||||||
|
// No double-announce allowed
|
||||||
|
const existing = await topics.events.find(tid, {
|
||||||
|
type: 'announce',
|
||||||
|
uid: actor,
|
||||||
|
pid: id,
|
||||||
|
});
|
||||||
|
if (existing.length) {
|
||||||
|
await topics.events.purge(tid, existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
await topics.events.log(tid, {
|
||||||
|
type: 'announce',
|
||||||
|
uid: actor,
|
||||||
|
href: `/post/${id}`,
|
||||||
|
pid: id,
|
||||||
|
timestamp,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
inbox.follow = async (req) => {
|
inbox.follow = async (req) => {
|
||||||
// Sanity checks
|
// Sanity checks
|
||||||
const { type, id } = await helpers.resolveLocalId(req.body.object);
|
const { type, id } = await helpers.resolveLocalId(req.body.object);
|
||||||
@@ -157,6 +198,10 @@ inbox.undo = async (req) => {
|
|||||||
const { actor, object } = req.body;
|
const { actor, object } = req.body;
|
||||||
const { type } = object;
|
const { type } = object;
|
||||||
|
|
||||||
|
if (actor !== object.actor) {
|
||||||
|
throw new Error('[[error:activitypub.actor-mismatch]]');
|
||||||
|
}
|
||||||
|
|
||||||
const assertion = await activitypub.actors.assert(actor);
|
const assertion = await activitypub.actors.assert(actor);
|
||||||
if (!assertion) {
|
if (!assertion) {
|
||||||
throw new Error('[[error:activitypub.invalid-id]]');
|
throw new Error('[[error:activitypub.invalid-id]]');
|
||||||
@@ -202,5 +247,23 @@ inbox.undo = async (req) => {
|
|||||||
await posts.unvote(id, actor);
|
await posts.unvote(id, actor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'Announce': {
|
||||||
|
const exists = await posts.exists(id);
|
||||||
|
if (localType !== 'post' || !exists) {
|
||||||
|
throw new Error('[[error:invalid-pid]]');
|
||||||
|
}
|
||||||
|
|
||||||
|
const tid = await posts.getPostField(id, 'tid');
|
||||||
|
const existing = await topics.events.find(tid, {
|
||||||
|
type: 'announce',
|
||||||
|
uid: actor,
|
||||||
|
pid: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existing.length) {
|
||||||
|
await topics.events.purge(tid, existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -237,3 +237,15 @@ ActivityPub.send = async (type, id, targets, payload) => {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
await ActivityPub.send('uid', 1, 'https://localhost/uid/1', {
|
||||||
|
// type: 'Undo',
|
||||||
|
// object: {
|
||||||
|
type: 'Announce',
|
||||||
|
actor: `https://localhost/uid/1`,
|
||||||
|
object: 'https://localhost/post/1',
|
||||||
|
published: new Date().toISOString(),
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
|||||||
@@ -112,42 +112,11 @@ Controller.getInbox = async (req, res) => {
|
|||||||
|
|
||||||
Controller.postInbox = async (req, res) => {
|
Controller.postInbox = async (req, res) => {
|
||||||
// Note: underlying methods are internal use only, hence no exposure via src/api
|
// Note: underlying methods are internal use only, hence no exposure via src/api
|
||||||
switch (req.body.type) {
|
const method = String(req.body.type).toLowerCase();
|
||||||
case 'Create': {
|
if (req.body.hasOwnProperty(method)) {
|
||||||
await activitypub.inbox.create(req);
|
return res.sendStatus(501);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'Update': {
|
|
||||||
await activitypub.inbox.update(req);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'Like': {
|
|
||||||
await activitypub.inbox.like(req);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'Follow': {
|
|
||||||
await activitypub.inbox.follow(req);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'Accept': {
|
|
||||||
await activitypub.inbox.accept(req);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'Undo': {
|
|
||||||
await activitypub.inbox.undo(req);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
res.sendStatus(501);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await activitypub.inbox[method](req);
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ Events._types = {
|
|||||||
icon: 'fa-code-fork',
|
icon: 'fa-code-fork',
|
||||||
translation: async (event, language) => translateEventArgs(event, language, 'topic:user-forked-topic', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)),
|
translation: async (event, language) => translateEventArgs(event, language, 'topic:user-forked-topic', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)),
|
||||||
},
|
},
|
||||||
|
announce: {
|
||||||
|
icon: 'fa-share-alt',
|
||||||
|
translation: async (event, language) => translateEventArgs(event, language, 'activitypub:topic-event-announce', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Events.init = async () => {
|
Events.init = async () => {
|
||||||
@@ -199,8 +203,9 @@ async function modifyEvent({ tid, uid, eventIds, timestamps, events }) {
|
|||||||
event.id = parseInt(eventIds[idx], 10);
|
event.id = parseInt(eventIds[idx], 10);
|
||||||
event.timestamp = timestamps[idx];
|
event.timestamp = timestamps[idx];
|
||||||
event.timestampISO = new Date(timestamps[idx]).toISOString();
|
event.timestampISO = new Date(timestamps[idx]).toISOString();
|
||||||
|
event.uid = utils.isNumber(event.uid) ? parseInt(event.uid, 10) : event.uid;
|
||||||
if (event.hasOwnProperty('uid')) {
|
if (event.hasOwnProperty('uid')) {
|
||||||
event.user = users.get(event.uid === 'system' ? 'system' : parseInt(event.uid, 10));
|
event.user = users.get(event.uid === 'system' ? 'system' : event.uid);
|
||||||
}
|
}
|
||||||
if (event.hasOwnProperty('fromCid')) {
|
if (event.hasOwnProperty('fromCid')) {
|
||||||
event.fromCategory = fromCategories[event.fromCid];
|
event.fromCategory = fromCategories[event.fromCid];
|
||||||
|
|||||||
Reference in New Issue
Block a user