mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 19:46:01 +01:00
feat: category actors, stub outbox
This commit is contained in:
@@ -2,10 +2,13 @@
|
||||
|
||||
const nconf = require('nconf');
|
||||
const mime = require('mime');
|
||||
const path = require('path');
|
||||
|
||||
const user = require('../user');
|
||||
const categories = require('../categories');
|
||||
const posts = require('../posts');
|
||||
const topics = require('../topics');
|
||||
const utils = require('../utils');
|
||||
|
||||
const activitypub = module.parent.exports;
|
||||
const Mocks = module.exports;
|
||||
@@ -111,7 +114,9 @@ Mocks.post = async (objects) => {
|
||||
return single ? posts.pop() : posts;
|
||||
};
|
||||
|
||||
Mocks.actor = async (uid) => {
|
||||
Mocks.actors = {};
|
||||
|
||||
Mocks.actors.user = async (uid) => {
|
||||
let { username, userslug, displayname: name, aboutme, picture, 'cover:url': cover } = await user.getUserData(uid);
|
||||
const publicKey = await activitypub.getPublicKey(uid);
|
||||
|
||||
@@ -157,6 +162,36 @@ Mocks.actor = async (uid) => {
|
||||
};
|
||||
};
|
||||
|
||||
Mocks.actors.category = async (cid) => {
|
||||
let { name, slug, description: summary, backgroundImage } = await categories.getCategoryData(cid);
|
||||
|
||||
if (backgroundImage) {
|
||||
const filename = utils.decodeHTMLEntities(backgroundImage).split('/').pop();
|
||||
const imagePath = path.join(nconf.get('upload_path'), 'category', filename);
|
||||
backgroundImage = {
|
||||
type: 'Image',
|
||||
mediaType: mime.getType(imagePath),
|
||||
url: `${nconf.get('url')}${utils.decodeHTMLEntities(backgroundImage)}`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
id: `${nconf.get('url')}/category/${cid}`,
|
||||
url: `${nconf.get('url')}/category/${slug}`,
|
||||
// followers: ,
|
||||
// following: ,
|
||||
inbox: `${nconf.get('url')}/category/${cid}/inbox`,
|
||||
outbox: `${nconf.get('url')}/category/${cid}/outbox`,
|
||||
|
||||
type: 'Group',
|
||||
name,
|
||||
preferredUsername: name,
|
||||
summary,
|
||||
icon: backgroundImage,
|
||||
};
|
||||
};
|
||||
|
||||
Mocks.note = async (post) => {
|
||||
const id = `${nconf.get('url')}/post/${post.pid}`;
|
||||
const published = new Date(parseInt(post.timestamp, 10)).toISOString();
|
||||
|
||||
@@ -88,7 +88,7 @@ activitypubApi.update = {};
|
||||
|
||||
activitypubApi.update.profile = async (caller, { uid }) => {
|
||||
const [object, followers] = await Promise.all([
|
||||
activitypub.mocks.actor(uid),
|
||||
activitypub.mocks.actors.user(uid),
|
||||
db.getSortedSetMembers(`followersRemote:${caller.uid}`),
|
||||
]);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ const nconf = require('nconf');
|
||||
|
||||
const meta = require('../../meta');
|
||||
const posts = require('../../posts');
|
||||
const categories = require('../../categories');
|
||||
const activitypub = require('../../activitypub');
|
||||
|
||||
const Actors = module.exports;
|
||||
@@ -33,7 +34,7 @@ Actors.application = async function (req, res) {
|
||||
|
||||
Actors.user = async function (req, res) {
|
||||
// todo: view:users priv gate
|
||||
const payload = await activitypub.mocks.actor(req.params.uid);
|
||||
const payload = await activitypub.mocks.actors.user(req.params.uid);
|
||||
|
||||
res.status(200).json(payload);
|
||||
};
|
||||
@@ -56,3 +57,13 @@ Actors.note = async function (req, res, next) {
|
||||
const payload = await activitypub.mocks.note(post);
|
||||
res.status(200).json(payload);
|
||||
};
|
||||
|
||||
Actors.category = async function (req, res, next) {
|
||||
const exists = await categories.exists(req.params.cid);
|
||||
if (!exists) {
|
||||
return next('route');
|
||||
}
|
||||
|
||||
const payload = await activitypub.mocks.actors.category(req.params.cid);
|
||||
res.status(200).json(payload);
|
||||
};
|
||||
|
||||
@@ -90,6 +90,16 @@ Controller.getOutbox = async (req, res) => {
|
||||
});
|
||||
};
|
||||
|
||||
Controller.getCategoryOutbox = async (req, res) => {
|
||||
// stub
|
||||
res.status(200).json({
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
type: 'OrderedCollection',
|
||||
totalItems: 0,
|
||||
orderedItems: [],
|
||||
});
|
||||
};
|
||||
|
||||
Controller.postOutbox = async (req, res) => {
|
||||
// This is a client-to-server feature so it is deliberately not implemented at this time.
|
||||
res.sendStatus(405);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const nconf = require('nconf');
|
||||
|
||||
const user = require('../user');
|
||||
const categories = require('../categories');
|
||||
const privileges = require('../privileges');
|
||||
|
||||
const Controller = module.exports;
|
||||
@@ -15,53 +16,86 @@ Controller.webfinger = async (req, res) => {
|
||||
return res.sendStatus(400);
|
||||
}
|
||||
|
||||
const canView = await privileges.global.can('view:users', req.uid);
|
||||
if (!canView) {
|
||||
return res.sendStatus(403);
|
||||
}
|
||||
|
||||
// Get the slug
|
||||
const slug = resource.slice(5, resource.length - (host.length + 1));
|
||||
|
||||
let uid = await user.getUidByUserslug(slug);
|
||||
if (slug === hostname) {
|
||||
uid = 0;
|
||||
} else if (!uid) {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
const response = {
|
||||
const uid = await user.getUidByUserslug(slug);
|
||||
let response = {
|
||||
subject: `acct:${slug}@${host}`,
|
||||
};
|
||||
|
||||
if (uid) {
|
||||
response.aliases = [
|
||||
`${nconf.get('url')}/uid/${uid}`,
|
||||
`${nconf.get('url')}/user/${slug}`,
|
||||
];
|
||||
try {
|
||||
if (slug.startsWith('cid.')) {
|
||||
response = await category(req.uid, slug.slice(4), response);
|
||||
} else if (slug === hostname) {
|
||||
response = application(response);
|
||||
} else if (uid) {
|
||||
response = await profile(req.uid, uid, response);
|
||||
} else {
|
||||
return res.sendStatus(404);
|
||||
}
|
||||
|
||||
response.links = [
|
||||
{
|
||||
rel: 'self',
|
||||
type: 'application/activity+json',
|
||||
href: `${nconf.get('url')}/uid/${uid}`, // actor
|
||||
},
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
type: 'text/html',
|
||||
href: `${nconf.get('url')}/user/${slug}`,
|
||||
},
|
||||
];
|
||||
} else {
|
||||
response.aliases = [nconf.get('url')];
|
||||
response.links = [
|
||||
{
|
||||
rel: 'self',
|
||||
type: 'application/activity+json',
|
||||
href: `${nconf.get('url')}/actor`, // actor
|
||||
},
|
||||
];
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
res.sendStatus(400);
|
||||
}
|
||||
|
||||
res.status(200).json(response);
|
||||
};
|
||||
|
||||
function application(response) {
|
||||
response.aliases = [nconf.get('url')];
|
||||
response.links = [
|
||||
{
|
||||
rel: 'self',
|
||||
type: 'application/activity+json',
|
||||
href: `${nconf.get('url')}/actor`, // actor
|
||||
},
|
||||
];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function profile(callerUid, uid, response) {
|
||||
const canView = await privileges.global.can('view:users', callerUid);
|
||||
if (!canView) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
const slug = await user.getUserField(uid, 'userslug');
|
||||
|
||||
response.aliases = [
|
||||
`${nconf.get('url')}/uid/${uid}`,
|
||||
`${nconf.get('url')}/user/${slug}`,
|
||||
];
|
||||
|
||||
response.links = [
|
||||
{
|
||||
rel: 'self',
|
||||
type: 'application/activity+json',
|
||||
href: `${nconf.get('url')}/uid/${uid}`, // actor
|
||||
},
|
||||
{
|
||||
rel: 'http://webfinger.net/rel/profile-page',
|
||||
type: 'text/html',
|
||||
href: `${nconf.get('url')}/user/${slug}`,
|
||||
},
|
||||
];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function category(callerUid, cid, response) {
|
||||
const canFind = await privileges.categories.can('find', cid, callerUid);
|
||||
if (!canFind) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
const slug = await categories.getCategoryField(cid, 'slug');
|
||||
|
||||
response.aliases = [`${nconf.get('url')}/category/${slug}`];
|
||||
response.links = [
|
||||
{
|
||||
rel: 'self',
|
||||
type: 'application/activity+json',
|
||||
href: `${nconf.get('url')}/category/${cid}`, // actor
|
||||
},
|
||||
];
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -15,17 +15,21 @@ module.exports = function (app, middleware, controllers) {
|
||||
const middlewares = [middleware.activitypub.enabled, middleware.activitypub.assertS2S];
|
||||
|
||||
app.get('/actor', middlewares, controllers.activitypub.actors.application);
|
||||
|
||||
app.get('/uid/:uid', middlewares, controllers.activitypub.actors.user);
|
||||
app.get('/user/:userslug', [...middlewares, middleware.exposeUid], controllers.activitypub.actors.userBySlug);
|
||||
|
||||
app.get('/uid/:uid/inbox', middlewares, controllers.activitypub.getInbox);
|
||||
app.post('/uid/:uid/inbox', [...middlewares, middleware.activitypub.validate], controllers.activitypub.postInbox);
|
||||
|
||||
app.get('/uid/:uid/outbox', middlewares, controllers.activitypub.getOutbox);
|
||||
app.post('/uid/:uid/outbox', middlewares, controllers.activitypub.postOutbox);
|
||||
|
||||
app.get('/uid/:uid/following', middlewares, controllers.activitypub.getFollowing);
|
||||
app.get('/uid/:uid/followers', middlewares, controllers.activitypub.getFollowers);
|
||||
|
||||
app.get('/post/:pid', middlewares, controllers.activitypub.actors.note);
|
||||
|
||||
app.get('/category/:cid', middlewares, controllers.activitypub.actors.category);
|
||||
app.get('/category/:cid/inbox', middlewares, controllers.activitypub.getInbox);
|
||||
app.post('/category/:cid/inbox', [...middlewares, middleware.activitypub.validate], controllers.activitypub.postInbox);
|
||||
app.get('/category/:cid/outbox', middlewares, controllers.activitypub.getCategoryOutbox);
|
||||
app.post('/category/:cid/outbox', middlewares, controllers.activitypub.postOutbox);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user