mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
refactor: move all input note normalization into helper method, have assertPrivate mock a message object (with said normalization) before sending message
This commit is contained in:
@@ -40,6 +40,95 @@ const sanitizeConfig = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Mocks._normalize = async (object) => {
|
||||||
|
// Normalized incoming AP objects into expected types for easier mocking
|
||||||
|
let { attributedTo, url, image, content, source } = object;
|
||||||
|
|
||||||
|
switch (true) { // non-string attributedTo handling
|
||||||
|
case Array.isArray(attributedTo): {
|
||||||
|
attributedTo = attributedTo.reduce((valid, cur) => {
|
||||||
|
if (typeof cur === 'string') {
|
||||||
|
valid.push(cur);
|
||||||
|
} else if (typeof cur === 'object') {
|
||||||
|
if (cur.type === 'Person' && cur.id) {
|
||||||
|
valid.push(cur.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}, []);
|
||||||
|
attributedTo = attributedTo.shift(); // take first valid uid
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case typeof attributedTo === 'object' && attributedTo.hasOwnProperty('id'): {
|
||||||
|
attributedTo = attributedTo.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let sourceContent = source && source.mediaType === 'text/markdown' ? source.content : undefined;
|
||||||
|
if (sourceContent) {
|
||||||
|
content = null;
|
||||||
|
sourceContent = await activitypub.helpers.remoteAnchorToLocalProfile(sourceContent, true);
|
||||||
|
} else if (content && content.length) {
|
||||||
|
content = sanitize(content, sanitizeConfig);
|
||||||
|
content = await activitypub.helpers.remoteAnchorToLocalProfile(content);
|
||||||
|
} else {
|
||||||
|
content = '<em>This post did not contain any content.</em>';
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case image && image.hasOwnProperty('url') && !!image.url: {
|
||||||
|
image = image.url;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case image && typeof image === 'string': {
|
||||||
|
// no change
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
image = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (image) {
|
||||||
|
const parsed = new URL(image);
|
||||||
|
if (!mime.getType(parsed.pathname).startsWith('image/')) {
|
||||||
|
activitypub.helpers.log(`[activitypub/mocks.post] Received image not identified as image due to MIME type: ${image}`);
|
||||||
|
image = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url) { // Handle url array
|
||||||
|
if (Array.isArray(url)) {
|
||||||
|
url = url.reduce((valid, cur) => {
|
||||||
|
if (typeof cur === 'string') {
|
||||||
|
valid.push(cur);
|
||||||
|
} else if (typeof cur === 'object') {
|
||||||
|
if (cur.type === 'Link' && cur.href) {
|
||||||
|
if (!cur.mediaType || (cur.mediaType && cur.mediaType === 'text/html')) {
|
||||||
|
valid.push(cur.href);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}, []);
|
||||||
|
url = url.shift(); // take first valid url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...object,
|
||||||
|
attributedTo,
|
||||||
|
content,
|
||||||
|
sourceContent,
|
||||||
|
image,
|
||||||
|
url,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
Mocks.profile = async (actors, hostMap) => {
|
Mocks.profile = async (actors, hostMap) => {
|
||||||
// Should only ever be called by activitypub.actors.assert
|
// Should only ever be called by activitypub.actors.assert
|
||||||
const profiles = await Promise.all(actors.map(async (actor) => {
|
const profiles = await Promise.all(actors.map(async (actor) => {
|
||||||
@@ -154,6 +243,8 @@ Mocks.post = async (objects) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const posts = await Promise.all(objects.map(async (object) => {
|
const posts = await Promise.all(objects.map(async (object) => {
|
||||||
|
object = await Mocks._normalize(object);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!activitypub._constants.acceptedPostTypes.includes(object.type) ||
|
!activitypub._constants.acceptedPostTypes.includes(object.type) ||
|
||||||
!activitypub.helpers.isUri(object.id) // sanity-check the id
|
!activitypub.helpers.isUri(object.id) // sanity-check the id
|
||||||
@@ -166,31 +257,10 @@ Mocks.post = async (objects) => {
|
|||||||
url,
|
url,
|
||||||
attributedTo: uid,
|
attributedTo: uid,
|
||||||
inReplyTo: toPid,
|
inReplyTo: toPid,
|
||||||
published, updated, name, content, source,
|
published, updated, name, content, sourceContent,
|
||||||
type, to, cc, audience, attachment, tag, image,
|
type, to, cc, audience, attachment, tag, image,
|
||||||
} = object;
|
} = object;
|
||||||
|
|
||||||
switch (true) { // non-string attributedTo handling
|
|
||||||
case Array.isArray(uid): {
|
|
||||||
uid = uid.reduce((valid, cur) => {
|
|
||||||
if (typeof cur === 'string') {
|
|
||||||
valid.push(cur);
|
|
||||||
} else if (typeof cur === 'object') {
|
|
||||||
if (cur.type === 'Person' && cur.id) {
|
|
||||||
valid.push(cur.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid;
|
|
||||||
}, []);
|
|
||||||
uid = uid.shift(); // take first valid uid
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case typeof uid === 'object' && uid.hasOwnProperty('id'): {
|
|
||||||
uid = uid.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await activitypub.actors.assert(uid);
|
await activitypub.actors.assert(uid);
|
||||||
|
|
||||||
const resolved = await activitypub.helpers.resolveLocalId(toPid);
|
const resolved = await activitypub.helpers.resolveLocalId(toPid);
|
||||||
@@ -202,59 +272,6 @@ Mocks.post = async (objects) => {
|
|||||||
let edited = new Date(updated);
|
let edited = new Date(updated);
|
||||||
edited = Number.isNaN(edited.valueOf()) ? undefined : edited;
|
edited = Number.isNaN(edited.valueOf()) ? undefined : edited;
|
||||||
|
|
||||||
let sourceContent = source && source.mediaType === 'text/markdown' ? source.content : undefined;
|
|
||||||
if (sourceContent) {
|
|
||||||
content = null;
|
|
||||||
sourceContent = await activitypub.helpers.remoteAnchorToLocalProfile(sourceContent, true);
|
|
||||||
} else if (content && content.length) {
|
|
||||||
content = sanitize(content, sanitizeConfig);
|
|
||||||
content = await activitypub.helpers.remoteAnchorToLocalProfile(content);
|
|
||||||
} else {
|
|
||||||
content = '<em>This post did not contain any content.</em>';
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (true) {
|
|
||||||
case image && image.hasOwnProperty('url') && !!image.url: {
|
|
||||||
image = image.url;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case image && typeof image === 'string': {
|
|
||||||
// no change
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
image = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (image) {
|
|
||||||
const parsed = new URL(image);
|
|
||||||
if (!mime.getType(parsed.pathname).startsWith('image/')) {
|
|
||||||
activitypub.helpers.log(`[activitypub/mocks.post] Received image not identified as image due to MIME type: ${image}`);
|
|
||||||
image = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url) { // Handle url array
|
|
||||||
if (Array.isArray(url)) {
|
|
||||||
url = url.reduce((valid, cur) => {
|
|
||||||
if (typeof cur === 'string') {
|
|
||||||
valid.push(cur);
|
|
||||||
} else if (typeof cur === 'object') {
|
|
||||||
if (cur.type === 'Link' && cur.href) {
|
|
||||||
if (!cur.mediaType || (cur.mediaType && cur.mediaType === 'text/html')) {
|
|
||||||
valid.push(cur.href);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid;
|
|
||||||
}, []);
|
|
||||||
url = url.shift(); // take first valid url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'Video') {
|
if (type === 'Video') {
|
||||||
attachment = attachment || [];
|
attachment = attachment || [];
|
||||||
attachment.push({ url });
|
attachment.push({ url });
|
||||||
@@ -281,6 +298,19 @@ Mocks.post = async (objects) => {
|
|||||||
return single ? posts.pop() : posts;
|
return single ? posts.pop() : posts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Mocks.message = async (object) => {
|
||||||
|
object = await Mocks._normalize(object);
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
mid: object.id,
|
||||||
|
uid: object.attributedTo,
|
||||||
|
content: object.content,
|
||||||
|
// ip: caller.ip,
|
||||||
|
};
|
||||||
|
|
||||||
|
return message;
|
||||||
|
};
|
||||||
|
|
||||||
Mocks.actors = {};
|
Mocks.actors = {};
|
||||||
|
|
||||||
Mocks.actors.user = async (uid) => {
|
Mocks.actors.user = async (uid) => {
|
||||||
|
|||||||
@@ -286,31 +286,30 @@ Notes.assertPrivate = async (object) => {
|
|||||||
timestamp = Date.now();
|
timestamp = Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const payload = await activitypub.mocks.message(object);
|
||||||
|
|
||||||
if (!roomId) {
|
if (!roomId) {
|
||||||
roomId = await messaging.newRoom(object.attributedTo, { uids: [...recipients] });
|
roomId = await messaging.newRoom(payload.uid, { uids: [...recipients] });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add any new members to the chat
|
// Add any new members to the chat
|
||||||
const added = Array.from(recipients).filter(uid => !participantUids.includes(uid));
|
const added = Array.from(recipients).filter(uid => !participantUids.includes(uid));
|
||||||
const assertion = await activitypub.actors.assert(added);
|
const assertion = await activitypub.actors.assert(added);
|
||||||
if (assertion) {
|
if (assertion) {
|
||||||
await messaging.addUsersToRoom(object.attributedTo, added, roomId);
|
await messaging.addUsersToRoom(payload.uid, added, roomId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add message to room
|
// Add message to room
|
||||||
const message = await messaging.sendMessage({
|
const message = await messaging.sendMessage({
|
||||||
mid: object.id,
|
...payload,
|
||||||
uid: object.attributedTo,
|
|
||||||
roomId: roomId,
|
|
||||||
content: object.content,
|
|
||||||
toMid: toMid,
|
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
// ip: caller.ip,
|
roomId: roomId,
|
||||||
|
toMid: toMid,
|
||||||
});
|
});
|
||||||
messaging.notifyUsersInRoom(object.attributedTo, roomId, message);
|
messaging.notifyUsersInRoom(payload.uid, roomId, message);
|
||||||
|
|
||||||
// Set real timestamp back so that the message shows even though it predates room joining
|
// Set real timestamp back so that the message shows even though it predates room joining
|
||||||
await messaging.setMessageField(object.id, 'timestamp', timestamp);
|
await messaging.setMessageField(payload.mid, 'timestamp', timestamp);
|
||||||
|
|
||||||
return { roomId };
|
return { roomId };
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user