From b389818600c513e139ad2d5070e5e7ae2abce2e3 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 28 Aug 2019 16:59:04 -0300 Subject: [PATCH] Revert "Federation improvements (#15234)" This reverts commit f5b0a610bf0a61bf6b060e8a0af28e9129e2183a. --- app/federation/client/admin/dashboard.js | 4 ++ app/federation/client/admin/visualizer.js | 2 + .../_client/callbacks/afterAddedToRoom.js | 20 +++--- .../callbacks/afterCreateDirectRoom.js | 11 ++-- .../_client/callbacks/afterCreateRoom.js | 19 +++--- .../_client/callbacks/afterDeleteMessage.js | 13 ++-- .../server/_client/callbacks/afterMuteUser.js | 13 ++-- .../_client/callbacks/afterRemoveFromRoom.js | 20 +++--- .../_client/callbacks/afterSaveMessage.js | 13 ++-- .../_client/callbacks/afterSetReaction.js | 15 ++--- .../_client/callbacks/afterUnmuteUser.js | 13 ++-- .../_client/callbacks/afterUnsetReaction.js | 15 ++--- .../_client/callbacks/beforeDeleteRoom.js | 17 ++--- .../callbacks/helpers/federatedResources.js | 38 ----------- app/federation/server/_client/client.js | 66 ++++++------------- app/federation/server/_server/index.js | 2 +- app/federation/server/_server/server.js | 5 ++ app/federation/server/errors.js | 10 +-- app/federation/server/federation.js | 23 +++---- .../server/methods/loadContextEvents.js | 21 +++--- .../normalizers/helpers/federatedResources.js | 2 - app/federation/server/normalizers/message.js | 21 +++--- app/federation/server/normalizers/room.js | 31 +++++---- .../server/normalizers/subscription.js | 17 +++-- app/federation/server/normalizers/user.js | 13 ++-- app/logger/server/server.js | 5 -- server/methods/browseChannels.js | 4 +- 27 files changed, 170 insertions(+), 263 deletions(-) delete mode 100644 app/federation/server/_client/callbacks/helpers/federatedResources.js delete mode 100644 app/federation/server/normalizers/helpers/federatedResources.js diff --git a/app/federation/client/admin/dashboard.js b/app/federation/client/admin/dashboard.js index e53325264d23..65f9de7d074b 100644 --- a/app/federation/client/admin/dashboard.js +++ b/app/federation/client/admin/dashboard.js @@ -18,6 +18,8 @@ let templateInstance; // current template instance/context const updateOverviewData = () => { Meteor.call('federation:getOverviewData', (error, result) => { if (error) { + console.log(error); + return; } @@ -30,6 +32,8 @@ const updateOverviewData = () => { const updateServers = () => { Meteor.call('federation:getServers', (error, result) => { if (error) { + console.log(error); + return; } diff --git a/app/federation/client/admin/visualizer.js b/app/federation/client/admin/visualizer.js index 68623f8f5546..f8e02b2efebb 100644 --- a/app/federation/client/admin/visualizer.js +++ b/app/federation/client/admin/visualizer.js @@ -29,6 +29,8 @@ let latestEventTimestamp = moment().startOf('week'); const loadContextEvents = () => { Meteor.call('federation:loadContextEvents', latestEventTimestamp.toISOString(), (error, result) => { if (error) { + console.log(error); + return; } diff --git a/app/federation/server/_client/callbacks/afterAddedToRoom.js b/app/federation/server/_client/callbacks/afterAddedToRoom.js index 605bd0a74cc5..d077dea7e2f7 100644 --- a/app/federation/server/_client/callbacks/afterAddedToRoom.js +++ b/app/federation/server/_client/callbacks/afterAddedToRoom.js @@ -1,5 +1,7 @@ +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; -import { isFederated, getFederatedRoomData } from './helpers/federatedResources'; +import getFederatedRoomData from './helpers/getFederatedRoomData'; +import getFederatedUserData from './helpers/getFederatedUserData'; import { FederationRoomEvents, Subscriptions } from '../../../../models/server'; import { Federation } from '../../federation'; import { normalizers } from '../../normalizers'; @@ -8,12 +10,12 @@ import { doAfterCreateRoom } from './afterCreateRoom'; async function afterAddedToRoom(involvedUsers, room) { const { user: addedUser } = involvedUsers; - if (!isFederated(room) && !isFederated(addedUser)) { return; } + // If there are not federated users on this room, ignore it + const { hasFederatedUser, users, subscriptions } = getFederatedRoomData(room); - logger.client.debug(() => `afterAddedToRoom => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } room=${ JSON.stringify(room, null, 2) }`); + if (!hasFederatedUser && !getFederatedUserData(addedUser).isFederated) { return; } - // If there are not federated users on this room, ignore it - const { users, subscriptions } = getFederatedRoomData(room); + logger.client.debug(`afterAddedToRoom => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } room=${ JSON.stringify(room, null, 2) }`); // Load the subscription const subscription = Promise.await(Subscriptions.findOneByRoomIdAndUserId(room._id, addedUser._id)); @@ -52,14 +54,10 @@ async function afterAddedToRoom(involvedUsers, room) { // Remove the user subscription from the room Promise.await(Subscriptions.remove({ _id: subscription._id })); - logger.client.error(() => `afterAddedToRoom => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } => Could not add user: ${ err }`); + logger.client.error(`afterAddedToRoom => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } => Could not add user: ${ err }`); } return involvedUsers; } -export const definition = { - hook: 'afterAddedToRoom', - callback: (roomOwner, room) => Promise.await(afterAddedToRoom(roomOwner, room)), - id: 'federation-after-added-to-room', -}; +callbacks.add('afterAddedToRoom', (roomOwner, room) => Promise.await(afterAddedToRoom(roomOwner, room)), callbacks.priority.LOW, 'federation-after-added-to-room'); diff --git a/app/federation/server/_client/callbacks/afterCreateDirectRoom.js b/app/federation/server/_client/callbacks/afterCreateDirectRoom.js index ddfb2890259c..e2c077fc95d1 100644 --- a/app/federation/server/_client/callbacks/afterCreateDirectRoom.js +++ b/app/federation/server/_client/callbacks/afterCreateDirectRoom.js @@ -1,3 +1,4 @@ +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; import { FederationRoomEvents, Subscriptions } from '../../../../models/server'; import { Federation } from '../../federation'; @@ -5,7 +6,7 @@ import { normalizers } from '../../normalizers'; import { deleteRoom } from '../../../../lib/server/functions'; async function afterCreateDirectRoom(room, extras) { - logger.client.debug(() => `afterCreateDirectRoom => room=${ JSON.stringify(room, null, 2) } extras=${ JSON.stringify(extras, null, 2) }`); + logger.client.debug(`afterCreateDirectRoom => room=${ JSON.stringify(room, null, 2) } extras=${ JSON.stringify(extras, null, 2) }`); // If the room is federated, ignore if (room.federation) { return; } @@ -60,14 +61,10 @@ async function afterCreateDirectRoom(room, extras) { } catch (err) { Promise.await(deleteRoom(room._id)); - logger.client.error(() => `afterCreateDirectRoom => room=${ JSON.stringify(room, null, 2) } => Could not create federated room: ${ err }`); + logger.client.error(`afterCreateDirectRoom => room=${ JSON.stringify(room, null, 2) } => Could not create federated room: ${ err }`); } return room; } -export const definition = { - hook: 'afterCreateDirectRoom', - callback: (room, extras) => Promise.await(afterCreateDirectRoom(room, extras)), - id: 'federation-after-create-direct-room', -}; +callbacks.add('afterCreateDirectRoom', (room, extras) => Promise.await(afterCreateDirectRoom(room, extras)), callbacks.priority.LOW, 'federation-after-create-direct-room'); diff --git a/app/federation/server/_client/callbacks/afterCreateRoom.js b/app/federation/server/_client/callbacks/afterCreateRoom.js index d48fb59df8d3..b8decebf2dbb 100644 --- a/app/federation/server/_client/callbacks/afterCreateRoom.js +++ b/app/federation/server/_client/callbacks/afterCreateRoom.js @@ -1,10 +1,11 @@ +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; import { FederationRoomEvents, Subscriptions, Users } from '../../../../models/server'; import { Federation } from '../../federation'; import { normalizers } from '../../normalizers'; import { deleteRoom } from '../../../../lib/server/functions'; -export async function doAfterCreateRoom(room, users, subscriptions) { +async function run(room, users, subscriptions) { // // Genesis // @@ -63,21 +64,19 @@ async function afterCreateRoom(roomOwner, room) { // If there are not federated users on this room, ignore it if (!hasFederatedUser) { return; } - logger.client.debug(() => `afterCreateRoom => roomOwner=${ JSON.stringify(roomOwner, null, 2) } room=${ JSON.stringify(room, null, 2) }`); + logger.client.debug(`afterCreateRoom => roomOwner=${ JSON.stringify(roomOwner, null, 2) } room=${ JSON.stringify(room, null, 2) }`); try { - await doAfterCreateRoom(room, users, subscriptions); + await run(room, users, subscriptions); } catch (err) { - deleteRoom(room._id); + Promise.await(deleteRoom(room._id)); - logger.client.error(() => `afterCreateRoom => room=${ JSON.stringify(room, null, 2) } => Could not create federated room: ${ err }`); + logger.client.error(`afterCreateRoom => room=${ JSON.stringify(room, null, 2) } => Could not create federated room: ${ err }`); } return room; } -export const definition = { - hook: 'afterCreateRoom', - callback: (roomOwner, room) => Promise.await(afterCreateRoom(roomOwner, room)), - id: 'federation-after-create-room', -}; +export const doAfterCreateRoom = run; + +callbacks.add('afterCreateRoom', (roomOwner, room) => Promise.await(afterCreateRoom(roomOwner, room)), callbacks.priority.LOW, 'federation-after-create-room'); diff --git a/app/federation/server/_client/callbacks/afterDeleteMessage.js b/app/federation/server/_client/callbacks/afterDeleteMessage.js index 8777ca11f748..921ba956115f 100644 --- a/app/federation/server/_client/callbacks/afterDeleteMessage.js +++ b/app/federation/server/_client/callbacks/afterDeleteMessage.js @@ -1,15 +1,16 @@ import { FederationRoomEvents, Rooms } from '../../../../models/server'; +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; import { Federation } from '../../federation'; -import { isFederated } from './helpers/federatedResources'; +import getFederatedRoomData from './helpers/getFederatedRoomData'; async function afterDeleteMessage(message) { const room = Rooms.findOneById(message.rid); // If there are not federated users on this room, ignore it - if (!isFederated(room)) { return; } + if (!getFederatedRoomData(room).hasFederatedUser) { return; } - logger.client.debug(() => `afterDeleteMessage => message=${ JSON.stringify(message, null, 2) } room=${ JSON.stringify(room, null, 2) }`); + logger.client.debug(`afterDeleteMessage => message=${ JSON.stringify(message, null, 2) } room=${ JSON.stringify(room, null, 2) }`); // Create the delete message event const event = await FederationRoomEvents.createDeleteMessageEvent(Federation.domain, room._id, message._id); @@ -20,8 +21,4 @@ async function afterDeleteMessage(message) { return message; } -export const definition = { - hook: 'afterDeleteMessage', - callback: (message) => Promise.await(afterDeleteMessage(message)), - id: 'federation-after-delete-message', -}; +callbacks.add('afterDeleteMessage', (message) => Promise.await(afterDeleteMessage(message)), callbacks.priority.LOW, 'federation-after-delete-message'); diff --git a/app/federation/server/_client/callbacks/afterMuteUser.js b/app/federation/server/_client/callbacks/afterMuteUser.js index 7141dc8b21d5..ea0f68c5835e 100644 --- a/app/federation/server/_client/callbacks/afterMuteUser.js +++ b/app/federation/server/_client/callbacks/afterMuteUser.js @@ -1,14 +1,15 @@ import { FederationRoomEvents } from '../../../../models/server'; +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; import { Federation } from '../../federation'; import { normalizers } from '../../normalizers'; -import { isFederated } from './helpers/federatedResources'; +import getFederatedRoomData from './helpers/getFederatedRoomData'; async function afterMuteUser(involvedUsers, room) { // If there are not federated users on this room, ignore it - if (!isFederated(room)) { return; } + if (!getFederatedRoomData(room).hasFederatedUser) { return; } - logger.client.debug(() => `afterMuteUser => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } room=${ JSON.stringify(room, null, 2) }`); + logger.client.debug(`afterMuteUser => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } room=${ JSON.stringify(room, null, 2) }`); const { mutedUser } = involvedUsers; @@ -21,8 +22,4 @@ async function afterMuteUser(involvedUsers, room) { return involvedUsers; } -export const definition = { - hook: 'afterMuteUser', - callback: (involvedUsers, room) => Promise.await(afterMuteUser(involvedUsers, room)), - id: 'federation-after-mute-user', -}; +callbacks.add('afterMuteUser', (involvedUsers, room) => Promise.await(afterMuteUser(involvedUsers, room)), callbacks.priority.LOW, 'federation-after-mute-user'); diff --git a/app/federation/server/_client/callbacks/afterRemoveFromRoom.js b/app/federation/server/_client/callbacks/afterRemoveFromRoom.js index 02997d378fff..2c31d203de92 100644 --- a/app/federation/server/_client/callbacks/afterRemoveFromRoom.js +++ b/app/federation/server/_client/callbacks/afterRemoveFromRoom.js @@ -1,5 +1,7 @@ +import { callbacks } from '../../../../callbacks'; import { FederationRoomEvents } from '../../../../models/server'; -import { isFederated, getFederatedRoomData } from './helpers/federatedResources'; +import getFederatedRoomData from './helpers/getFederatedRoomData'; +import getFederatedUserData from './helpers/getFederatedUserData'; import { logger } from '../../logger'; import { normalizers } from '../../normalizers'; import { Federation } from '../../federation'; @@ -7,12 +9,12 @@ import { Federation } from '../../federation'; async function afterRemoveFromRoom(involvedUsers, room) { const { removedUser } = involvedUsers; - // If there are not federated users on this room, ignore it - if (!isFederated(room) && !isFederated(removedUser)) { return; } + const { hasFederatedUser, users } = getFederatedRoomData(room); - logger.client.debug(() => `afterRemoveFromRoom => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } room=${ JSON.stringify(room, null, 2) }`); + // If there are not federated users on this room, ignore it + if (!hasFederatedUser && !getFederatedUserData(removedUser).isFederated) { return; } - const { users } = getFederatedRoomData(room); + logger.client.debug(`afterRemoveFromRoom => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } room=${ JSON.stringify(room, null, 2) }`); try { // Get the domains after removal @@ -37,7 +39,7 @@ async function afterRemoveFromRoom(involvedUsers, room) { // Dispatch the events Federation.client.dispatchEvent(domainsBeforeRemoval, removeUserEvent); } catch (err) { - logger.client.error(() => `afterRemoveFromRoom => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } => Could not add user: ${ err }`); + logger.client.error(`afterRemoveFromRoom => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } => Could not add user: ${ err }`); throw err; } @@ -45,8 +47,4 @@ async function afterRemoveFromRoom(involvedUsers, room) { return involvedUsers; } -export const definition = { - hook: 'afterRemoveFromRoom', - callback: (roomOwner, room) => Promise.await(afterRemoveFromRoom(roomOwner, room)), - id: 'federation-after-remove-from-room', -}; +callbacks.add('afterRemoveFromRoom', (roomOwner, room) => Promise.await(afterRemoveFromRoom(roomOwner, room)), callbacks.priority.LOW, 'federation-after-remove-from-room'); diff --git a/app/federation/server/_client/callbacks/afterSaveMessage.js b/app/federation/server/_client/callbacks/afterSaveMessage.js index 738a3fad25a2..bc99bb14b6bc 100644 --- a/app/federation/server/_client/callbacks/afterSaveMessage.js +++ b/app/federation/server/_client/callbacks/afterSaveMessage.js @@ -1,14 +1,15 @@ +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; import { FederationRoomEvents } from '../../../../models/server'; import { Federation } from '../../federation'; import { normalizers } from '../../normalizers'; -import { isFederated } from './helpers/federatedResources'; +import getFederatedRoomData from './helpers/getFederatedRoomData'; async function afterSaveMessage(message, room) { // If there are not federated users on this room, ignore it - if (!isFederated(room)) { return; } + if (!getFederatedRoomData(room).hasFederatedUser) { return; } - logger.client.debug(() => `afterSaveMessage => message=${ JSON.stringify(message, null, 2) } room=${ JSON.stringify(room, null, 2) }`); + logger.client.debug(`afterSaveMessage => message=${ JSON.stringify(message, null, 2) } room=${ JSON.stringify(room, null, 2) }`); let event; @@ -27,8 +28,4 @@ async function afterSaveMessage(message, room) { return message; } -export const definition = { - hook: 'afterSaveMessage', - callback: (message, room) => Promise.await(afterSaveMessage(message, room)), - id: 'federation-after-save-message', -}; +callbacks.add('afterSaveMessage', (message, room) => Promise.await(afterSaveMessage(message, room)), callbacks.priority.LOW, 'federation-after-save-message'); diff --git a/app/federation/server/_client/callbacks/afterSetReaction.js b/app/federation/server/_client/callbacks/afterSetReaction.js index 8dfb4dee4ec3..186bd86f14ed 100644 --- a/app/federation/server/_client/callbacks/afterSetReaction.js +++ b/app/federation/server/_client/callbacks/afterSetReaction.js @@ -1,17 +1,18 @@ import _ from 'underscore'; import { FederationRoomEvents, Rooms } from '../../../../models/server'; +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; import { Federation } from '../../federation'; -import { isFederated } from './helpers/federatedResources'; +import getFederatedRoomData from './helpers/getFederatedRoomData'; async function afterSetReaction(message, { user, reaction }) { - const room = Rooms.findOneById(message.rid, { fields: { _id: 1, federation: 1 } }); + const room = Rooms.findOneById(message.rid); // If there are not federated users on this room, ignore it - if (!isFederated(room)) { return; } + if (!getFederatedRoomData(room).hasFederatedUser) { return; } - logger.client.debug(() => `afterSetReaction => message=${ JSON.stringify(_.pick(message, '_id', 'msg'), null, 2) } room=${ JSON.stringify(_.pick(room, '_id'), null, 2) } user=${ JSON.stringify(_.pick(user, 'username'), null, 2) } reaction=${ reaction }`); + logger.client.debug(`afterSetReaction => message=${ JSON.stringify(_.pick(message, '_id', 'msg'), null, 2) } room=${ JSON.stringify(_.pick(room, '_id'), null, 2) } user=${ JSON.stringify(_.pick(user, 'username'), null, 2) } reaction=${ reaction }`); // Create the event const event = await FederationRoomEvents.createSetMessageReactionEvent(Federation.domain, room._id, message._id, user.username, reaction); @@ -22,8 +23,4 @@ async function afterSetReaction(message, { user, reaction }) { return message; } -export const definition = { - hook: 'afterSetReaction', - callback: (message, extras) => Promise.await(afterSetReaction(message, extras)), - id: 'federation-after-set-reaction', -}; +callbacks.add('afterSetReaction', (message, extras) => Promise.await(afterSetReaction(message, extras)), callbacks.priority.LOW, 'federation-after-set-reaction'); diff --git a/app/federation/server/_client/callbacks/afterUnmuteUser.js b/app/federation/server/_client/callbacks/afterUnmuteUser.js index d577e7d22d7a..273386186793 100644 --- a/app/federation/server/_client/callbacks/afterUnmuteUser.js +++ b/app/federation/server/_client/callbacks/afterUnmuteUser.js @@ -1,14 +1,15 @@ import { FederationRoomEvents } from '../../../../models/server'; +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; import { Federation } from '../../federation'; import { normalizers } from '../../normalizers'; -import { isFederated } from './helpers/federatedResources'; +import getFederatedRoomData from './helpers/getFederatedRoomData'; async function afterUnmuteUser(involvedUsers, room) { // If there are not federated users on this room, ignore it - if (!isFederated(room)) { return; } + if (!getFederatedRoomData(room).hasFederatedUser) { return; } - logger.client.debug(() => `afterUnmuteUser => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } room=${ JSON.stringify(room, null, 2) }`); + logger.client.debug(`afterUnmuteUser => involvedUsers=${ JSON.stringify(involvedUsers, null, 2) } room=${ JSON.stringify(room, null, 2) }`); const { unmutedUser } = involvedUsers; @@ -21,8 +22,4 @@ async function afterUnmuteUser(involvedUsers, room) { return involvedUsers; } -export const definition = { - hook: 'afterUnmuteUser', - callback: (involvedUsers, room) => Promise.await(afterUnmuteUser(involvedUsers, room)), - id: 'federation-after-unmute-user', -}; +callbacks.add('afterUnmuteUser', (involvedUsers, room) => Promise.await(afterUnmuteUser(involvedUsers, room)), callbacks.priority.LOW, 'federation-after-unmute-user'); diff --git a/app/federation/server/_client/callbacks/afterUnsetReaction.js b/app/federation/server/_client/callbacks/afterUnsetReaction.js index 3acbb0213752..b687f2ad7088 100644 --- a/app/federation/server/_client/callbacks/afterUnsetReaction.js +++ b/app/federation/server/_client/callbacks/afterUnsetReaction.js @@ -1,17 +1,18 @@ import _ from 'underscore'; import { FederationRoomEvents, Rooms } from '../../../../models/server'; +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; import { Federation } from '../../federation'; -import { isFederated } from './helpers/federatedResources'; +import getFederatedRoomData from './helpers/getFederatedRoomData'; async function afterUnsetReaction(message, { user, reaction }) { - const room = Rooms.findOneById(message.rid, { fields: { _id: 1, federation: 1 } }); + const room = Rooms.findOneById(message.rid); // If there are not federated users on this room, ignore it - if (!isFederated(room)) { return; } + if (!getFederatedRoomData(room).hasFederatedUser) { return; } - logger.client.debug(() => `afterUnsetReaction => message=${ JSON.stringify(_.pick(message, '_id', 'msg'), null, 2) } room=${ JSON.stringify(_.pick(room, '_id'), null, 2) } user=${ JSON.stringify(_.pick(user, 'username'), null, 2) } reaction=${ reaction }`); + logger.client.debug(`afterUnsetReaction => message=${ JSON.stringify(_.pick(message, '_id', 'msg'), null, 2) } room=${ JSON.stringify(_.pick(room, '_id'), null, 2) } user=${ JSON.stringify(_.pick(user, 'username'), null, 2) } reaction=${ reaction }`); // Create the event const event = await FederationRoomEvents.createUnsetMessageReactionEvent(Federation.domain, room._id, message._id, user.username, reaction); @@ -22,8 +23,4 @@ async function afterUnsetReaction(message, { user, reaction }) { return message; } -export const definition = { - hook: 'afterUnsetReaction', - callback: (message, extras) => Promise.await(afterUnsetReaction(message, extras)), - id: 'federation-after-unset-reaction', -}; +callbacks.add('afterUnsetReaction', (message, extras) => Promise.await(afterUnsetReaction(message, extras)), callbacks.priority.LOW, 'federation-after-unset-reaction'); diff --git a/app/federation/server/_client/callbacks/beforeDeleteRoom.js b/app/federation/server/_client/callbacks/beforeDeleteRoom.js index a0bf9668753d..b9de7778d8f4 100644 --- a/app/federation/server/_client/callbacks/beforeDeleteRoom.js +++ b/app/federation/server/_client/callbacks/beforeDeleteRoom.js @@ -1,18 +1,19 @@ +import { callbacks } from '../../../../callbacks'; import { logger } from '../../logger'; import { FederationRoomEvents, Rooms } from '../../../../models/server'; import { Federation } from '../../federation'; -import { isFederated } from './helpers/federatedResources'; +import getFederatedRoomData from './helpers/getFederatedRoomData'; async function beforeDeleteRoom(roomId) { - const room = Rooms.findOneById(roomId, { fields: { _id: 1, federation: 1 } }); + const room = Rooms.findOneById(roomId); // If room does not exist, skip if (!room) { return; } // If there are not federated users on this room, ignore it - if (!isFederated(room)) { return; } + if (!getFederatedRoomData(room).hasFederatedUser) { return; } - logger.client.debug(() => `beforeDeleteRoom => room=${ JSON.stringify(room, null, 2) }`); + logger.client.debug(`beforeDeleteRoom => room=${ JSON.stringify(room, null, 2) }`); try { // Create the message event @@ -21,7 +22,7 @@ async function beforeDeleteRoom(roomId) { // Dispatch event (async) Federation.client.dispatchEvent(room.federation.domains, event); } catch (err) { - logger.client.error(() => `beforeDeleteRoom => room=${ JSON.stringify(room, null, 2) } => Could not remove room: ${ err }`); + logger.client.error(`beforeDeleteRoom => room=${ JSON.stringify(room, null, 2) } => Could not remove room: ${ err }`); throw err; } @@ -29,8 +30,4 @@ async function beforeDeleteRoom(roomId) { return roomId; } -export const definition = { - hook: 'beforeDeleteRoom', - callback: (roomId) => Promise.await(beforeDeleteRoom(roomId)), - id: 'federation-before-delete-room', -}; +callbacks.add('beforeDeleteRoom', (roomId) => Promise.await(beforeDeleteRoom(roomId)), callbacks.priority.LOW, 'federation-before-delete-room'); diff --git a/app/federation/server/_client/callbacks/helpers/federatedResources.js b/app/federation/server/_client/callbacks/helpers/federatedResources.js deleted file mode 100644 index fec8f1e05ab1..000000000000 --- a/app/federation/server/_client/callbacks/helpers/federatedResources.js +++ /dev/null @@ -1,38 +0,0 @@ -import { Subscriptions, Users } from '../../../../../models/server'; - -export const isFederated = (resource) => !!resource.federation; - -export const getFederatedRoomData = (room) => { - let hasFederatedUser = false; - - let users = null; - let subscriptions = null; - - if (room.t === 'd') { - // Check if there is a federated user on this room - hasFederatedUser = room.usernames.find((u) => u.indexOf('@') !== -1); - } else { - // Find all subscriptions of this room - subscriptions = Subscriptions.findByRoomIdWhenUsernameExists(room._id).fetch(); - subscriptions = subscriptions.reduce((acc, s) => { - acc[s.u._id] = s; - - return acc; - }, {}); - - // Get all user ids - const userIds = Object.keys(subscriptions); - - // Load all the users - users = Users.findUsersWithUsernameByIds(userIds).fetch(); - - // Check if there is a federated user on this room - hasFederatedUser = users.find((u) => u.username.indexOf('@') !== -1); - } - - return { - hasFederatedUser, - users, - subscriptions, - }; -}; diff --git a/app/federation/server/_client/client.js b/app/federation/server/_client/client.js index cb105e2fde12..f8f5b744219d 100644 --- a/app/federation/server/_client/client.js +++ b/app/federation/server/_client/client.js @@ -2,45 +2,27 @@ import qs from 'querystring'; import { logger } from '../logger'; import { Federation } from '../federation'; + // Callbacks -import { definition as afterAddedToRoomDef } from './callbacks/afterAddedToRoom'; -import { definition as afterCreateDirectRoomDef } from './callbacks/afterCreateDirectRoom'; -import { definition as afterCreateRoomDef } from './callbacks/afterCreateRoom'; -import { definition as afterDeleteMessageDef } from './callbacks/afterDeleteMessage'; -import { definition as afterMuteUserDef } from './callbacks/afterMuteUser'; -import { definition as afterRemoveFromRoomDef } from './callbacks/afterRemoveFromRoom'; -import { definition as afterSaveMessageDef } from './callbacks/afterSaveMessage'; -import { definition as afterSetReactionDef } from './callbacks/afterSetReaction'; -import { definition as afterUnmuteUserDef } from './callbacks/afterUnmuteUser'; -import { definition as afterUnsetReactionDef } from './callbacks/afterUnsetReaction'; -import { definition as beforeDeleteRoomDef } from './callbacks/beforeDeleteRoom'; -import { callbacks } from '../../../callbacks'; +import './callbacks/afterAddedToRoom'; +import './callbacks/afterCreateDirectRoom'; +import './callbacks/afterCreateRoom'; +import './callbacks/afterDeleteMessage'; +import './callbacks/afterMuteUser'; +import './callbacks/afterSaveMessage'; +import './callbacks/afterSetReaction'; +import './callbacks/afterUnmuteUser'; +import './callbacks/afterUnsetReaction'; +import './callbacks/beforeDeleteRoom'; +import './callbacks/afterRemoveFromRoom'; class Client { - callbackDefinitions = []; - - register(callbackDefition) { - this.callbackDefinitions.push(callbackDefition); - } - - enableCallbacks() { - for (const definition of this.callbackDefinitions) { - callbacks.add(definition.hook, definition.callback, callbacks.priority.LOW, definition.id); - } - } - - disableCallbacks() { - for (const definition of this.callbackDefinitions) { - callbacks.remove(definition.hook, definition.id); - } - } - searchUsers(query) { if (!Federation.enabled) { throw Federation.errors.disabled('client.searchUsers'); } - logger.client.debug(() => `searchUsers => query=${ query }`); + logger.client.debug(`searchUsers => query=${ query }`); const [username, peerDomain] = query.split('@'); @@ -56,7 +38,7 @@ class Client { throw Federation.errors.disabled('client.searchUsers'); } - logger.client.debug(() => `getUserByUsername => query=${ query }`); + logger.client.debug(`getUserByUsername => query=${ query }`); const [username, peerDomain] = query.split('@'); @@ -80,12 +62,14 @@ class Client { throw Federation.errors.disabled('client.dispatchEvents'); } - logger.client.debug(() => `dispatchEvents => domains=${ domains.join(', ') } events=${ events.map((e) => JSON.stringify(e, null, 2)) }`); + logger.client.debug(`dispatchEvents => domains=${ domains.join(', ') } events=${ events.map((e) => JSON.stringify(e, null, 2)) }`); const uri = '/api/v1/federation.events.dispatch'; for (const domain of domains) { - Federation.http.requestToPeer('POST', domain, uri, { events }, { ignoreErrors: true }); + const { data } = Federation.http.requestToPeer('POST', domain, uri, { events }, { ignoreErrors: true }); + + console.log(data); } } @@ -94,7 +78,7 @@ class Client { throw Federation.errors.disabled('client.requestEventsFromLatest'); } - logger.client.debug(() => `requestEventsFromLatest => domain=${ domain } contextType=${ contextType } contextQuery=${ JSON.stringify(contextQuery, null, 2) } latestEventIds=${ latestEventIds.join(', ') }`); + logger.client.debug(`requestEventsFromLatest => domain=${ domain } contextType=${ contextType } contextQuery=${ JSON.stringify(contextQuery, null, 2) } latestEventIds=${ latestEventIds.join(', ') }`); const uri = '/api/v1/federation.events.requestFromLatest'; @@ -109,15 +93,3 @@ class Client { } export const client = new Client(); - -client.register(afterAddedToRoomDef); -client.register(afterCreateDirectRoomDef); -client.register(afterCreateRoomDef); -client.register(afterDeleteMessageDef); -client.register(afterMuteUserDef); -client.register(beforeDeleteRoomDef); -client.register(afterSaveMessageDef); -client.register(afterSetReactionDef); -client.register(afterUnmuteUserDef); -client.register(afterUnsetReactionDef); -client.register(afterRemoveFromRoomDef); diff --git a/app/federation/server/_server/index.js b/app/federation/server/_server/index.js index 1199af15d79f..4dde68525e6a 100644 --- a/app/federation/server/_server/index.js +++ b/app/federation/server/_server/index.js @@ -1 +1 @@ -import './server'; +export { server } from './server'; diff --git a/app/federation/server/_server/server.js b/app/federation/server/_server/server.js index 739145a446bd..963cab2d8146 100644 --- a/app/federation/server/_server/server.js +++ b/app/federation/server/_server/server.js @@ -1,3 +1,8 @@ import './endpoints/events'; import './endpoints/uploads'; import './endpoints/users'; + +class Server { +} + +export const server = new Server(); diff --git a/app/federation/server/errors.js b/app/federation/server/errors.js index 0b70b1f531a5..72289f7a2d1a 100644 --- a/app/federation/server/errors.js +++ b/app/federation/server/errors.js @@ -1,6 +1,8 @@ import { Meteor } from 'meteor/meteor'; -export const disabled = (method) => new Meteor.Error('federation-error-disabled', 'Federation disabled', { method }); -export const userNotFound = (query) => new Meteor.Error('federation-user-not-found', `Could not find federated users using "${ query }"`); -export const peerNotFoundUsingDNS = (method) => new Meteor.Error('federation-error-peer-no-found-using-dns', 'Could not find the peer using DNS or Hub', { method }); -export const peerCouldNotBeRegisteredWithHub = (method) => new Meteor.Error('federation-error-peer-could-not-register-with-hub', 'Could not register the peer using the Hub', { method }); +module.exports = { + disabled: (method) => new Meteor.Error('federation-error-disabled', 'Federation disabled', { method }), + userNotFound: (query) => new Meteor.Error('federation-user-not-found', `Could not find federated users using "${ query }"`), + peerNotFoundUsingDNS: (method) => new Meteor.Error('federation-error-peer-no-found-using-dns', 'Could not find the peer using DNS or Hub', { method }), + peerCouldNotBeRegisteredWithHub: (method) => new Meteor.Error('federation-error-peer-could-not-register-with-hub', 'Could not register the peer using the Hub', { method }), +}; diff --git a/app/federation/server/federation.js b/app/federation/server/federation.js index 7d4a135ac037..f316e63d7703 100644 --- a/app/federation/server/federation.js +++ b/app/federation/server/federation.js @@ -6,14 +6,15 @@ import { settings } from '../../settings'; import { logger } from './logger'; import * as errors from './errors'; import { addUser } from './methods/addUser'; +import { loadContextEvents } from './methods/loadContextEvents'; import { searchUsers } from './methods/searchUsers'; import { dns } from './dns'; import { http } from './http'; import { client } from './_client'; +import { server } from './_server'; import { crypt } from './crypt'; import { FederationKeys } from '../../models/server'; import { updateStatus, updateEnabled } from './settingsUpdater'; -import './_server'; import './methods/testSetup'; @@ -27,12 +28,14 @@ export const Federation = { client, dns, http, + server, crypt, }; // Add Federation methods Federation.methods = { addUser, + loadContextEvents, searchUsers, }; @@ -42,6 +45,14 @@ if (!FederationKeys.getPublicKey()) { } const updateSettings = _.debounce(Meteor.bindEnvironment(function() { + const _enabled = settings.get('FEDERATION_Enabled'); + + if (!_enabled) { + updateStatus('Disabled'); + + return; + } + Federation.domain = settings.get('FEDERATION_Domain').replace('@', ''); Federation.discoveryMethod = settings.get('FEDERATION_Discovery_Method'); @@ -69,16 +80,6 @@ function enableOrDisable() { logger.setup.info(`Federation is ${ Federation.enabled ? 'enabled' : 'disabled' }`); - if (Federation.enabled) { - updateSettings(); - - Federation.client.enableCallbacks(); - } else { - updateStatus('Disabled'); - - Federation.client.disableCallbacks(); - } - Federation.enabled && updateSettings(); } diff --git a/app/federation/server/methods/loadContextEvents.js b/app/federation/server/methods/loadContextEvents.js index c68700d3ab43..c5c8984c067a 100644 --- a/app/federation/server/methods/loadContextEvents.js +++ b/app/federation/server/methods/loadContextEvents.js @@ -1,18 +1,15 @@ import { Meteor } from 'meteor/meteor'; -import { hasRole } from '../../../authorization/server'; -import { FederationRoomEvents } from '../../../models/server'; +import { FederationRoomEvents } from '../../../models'; -Meteor.methods({ - 'federation:loadContextEvents': (latestEventTimestamp) => { - if (!Meteor.userId()) { - throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'loadContextEvents' }); - } +export function loadContextEvents(latestEventTimestamp) { + if (!Meteor.userId()) { + throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'loadContextEvents' }); + } - if (!hasRole(Meteor.userId(), 'admin')) { - throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'loadContextEvents' }); - } + return FederationRoomEvents.find({ timestamp: { $gt: new Date(latestEventTimestamp) } }, { sort: { timestamp: 1 } }).fetch(); +} - return FederationRoomEvents.find({ timestamp: { $gt: new Date(latestEventTimestamp) } }, { sort: { timestamp: 1 } }).fetch(); - }, +Meteor.methods({ + 'federation:loadContextEvents': loadContextEvents, }); diff --git a/app/federation/server/normalizers/helpers/federatedResources.js b/app/federation/server/normalizers/helpers/federatedResources.js deleted file mode 100644 index a364444c2599..000000000000 --- a/app/federation/server/normalizers/helpers/federatedResources.js +++ /dev/null @@ -1,2 +0,0 @@ -export const getNameAndDomain = (fullyQualifiedName) => fullyQualifiedName.split('@'); -export const isFullyQualified = (name) => name.indexOf('@') !== -1; diff --git a/app/federation/server/normalizers/message.js b/app/federation/server/normalizers/message.js index 83bb41c27795..cceea12c398b 100644 --- a/app/federation/server/normalizers/message.js +++ b/app/federation/server/normalizers/message.js @@ -1,10 +1,9 @@ import { Federation } from '../index'; -import { getNameAndDomain, isFullyQualified } from './helpers/federatedResources'; -const denormalizeMessage = (originalResource) => { - const resource = { ...originalResource }; +const denormalizeMessage = (resource) => { + resource = { ...resource }; - const [username, domain] = getNameAndDomain(resource.u.username); + const [username, domain] = resource.u.username.split('@'); // Denormalize username resource.u.username = domain === Federation.domain ? username : resource.u.username; @@ -14,7 +13,7 @@ const denormalizeMessage = (originalResource) => { // Ignore if we are dealing with all, here or rocket.cat if (['all', 'here', 'rocket.cat'].indexOf(mention.username) !== -1) { continue; } - const [username, domain] = getNameAndDomain(mention.username); + const [username, domain] = mention.username.split('@'); if (domain === Federation.domain) { const originalUsername = mention.username; @@ -30,7 +29,7 @@ const denormalizeMessage = (originalResource) => { // Ignore if we are dealing with all, here or rocket.cat if (['all', 'here', 'rocket.cat'].indexOf(channel.name) !== -1) { continue; } - const [username, domain] = getNameAndDomain(channel.name); + const [username, domain] = channel.name.split('@'); if (domain === Federation.domain) { const originalUsername = channel.name; @@ -46,10 +45,10 @@ const denormalizeMessage = (originalResource) => { const denormalizeAllMessages = (resources) => resources.map(denormalizeMessage); -const normalizeMessage = (originalResource) => { - const resource = { ...originalResource }; +const normalizeMessage = (resource) => { + resource = { ...resource }; - resource.u.username = !isFullyQualified(resource.u.username) ? `${ resource.u.username }@${ Federation.domain }` : resource.u.username; + resource.u.username = resource.u.username.indexOf('@') === -1 ? `${ resource.u.username }@${ Federation.domain }` : resource.u.username; // Federation resource.federation = resource.federation || { @@ -61,7 +60,7 @@ const normalizeMessage = (originalResource) => { // Ignore if we are dealing with all, here or rocket.cat if (['all', 'here', 'rocket.cat'].indexOf(mention.username) !== -1) { continue; } - if (!isFullyQualified(mention.username)) { + if (mention.username.indexOf('@') === -1) { const originalUsername = mention.username; mention.username = `${ mention.username }@${ Federation.domain }`; @@ -72,7 +71,7 @@ const normalizeMessage = (originalResource) => { // Normalize channels for (const channel of resource.channels) { - if (!isFullyQualified(channel.name)) { + if (channel.name.indexOf('@') === -1) { const originalUsername = channel.name; channel.name = `${ channel.name }@${ Federation.domain }`; diff --git a/app/federation/server/normalizers/room.js b/app/federation/server/normalizers/room.js index 6930f4f7e34a..3310ba1943b8 100644 --- a/app/federation/server/normalizers/room.js +++ b/app/federation/server/normalizers/room.js @@ -1,30 +1,29 @@ import { Federation } from '../index'; -import { getNameAndDomain, isFullyQualified } from './helpers/federatedResources'; -const denormalizeRoom = (originalResource) => { - const resource = { ...originalResource }; +const denormalizeRoom = (resource) => { + resource = { ...resource }; if (resource.t === 'd') { resource.usernames = resource.usernames.map((u) => { - const [username, domain] = getNameAndDomain(u); + const [username, domain] = u.split('@'); return domain === Federation.domain ? username : u; }); } else { // Denormalize room name - const [roomName, roomDomain] = getNameAndDomain(resource.name); + const [roomName, roomDomain] = resource.name.split('@'); resource.name = roomDomain === Federation.domain ? roomName : resource.name; // Denormalize room owner name - const [username, userDomain] = getNameAndDomain(resource.u.username); + const [username, userDomain] = resource.u.username.split('@'); resource.u.username = userDomain === Federation.domain ? username : resource.u.username; // Denormalize muted users if (resource.muted) { resource.muted = resource.muted.map((u) => { - const [username, domain] = getNameAndDomain(u); + const [username, domain] = u.split('@'); return domain === Federation.domain ? username : u; }); @@ -33,7 +32,7 @@ const denormalizeRoom = (originalResource) => { // Denormalize unmuted users if (resource.unmuted) { resource.unmuted = resource.unmuted.map((u) => { - const [username, domain] = getNameAndDomain(u); + const [username, domain] = u.split('@'); return domain === Federation.unmuted ? username : u; }); @@ -43,38 +42,38 @@ const denormalizeRoom = (originalResource) => { return resource; }; -const normalizeRoom = (originalResource, users) => { - const resource = { ...originalResource }; +const normalizeRoom = (resource, users) => { + resource = { ...resource }; let domains = ''; if (resource.t === 'd') { // Handle user names, adding the Federation domain to local users - resource.usernames = resource.usernames.map((u) => (!isFullyQualified(u) ? `${ u }@${ Federation.domain }` : u)); + resource.usernames = resource.usernames.map((u) => (u.indexOf('@') === -1 ? `${ u }@${ Federation.domain }` : u)); // Get the domains of the usernames - domains = resource.usernames.map((u) => getNameAndDomain(u)[1]); + domains = resource.usernames.map((u) => u.split('@')[1]); } else { // Ensure private resource.t = 'p'; // Normalize room name - resource.name = !isFullyQualified(resource.name) ? `${ resource.name }@${ Federation.domain }` : resource.name; + resource.name = resource.name.indexOf('@') === -1 ? `${ resource.name }@${ Federation.domain }` : resource.name; // Get the users domains domains = users.map((u) => u.federation.origin); // Normalize the username - resource.u.username = !isFullyQualified(resource.u.username) ? `${ resource.u.username }@${ Federation.domain }` : resource.u.username; + resource.u.username = resource.u.username.indexOf('@') === -1 ? `${ resource.u.username }@${ Federation.domain }` : resource.u.username; // Normalize the muted users if (resource.muted) { - resource.muted = resource.muted.map((u) => (!isFullyQualified(u) ? `${ u }@${ Federation.domain }` : u)); + resource.muted = resource.muted.map((u) => (u.indexOf('@') === -1 ? `${ u }@${ Federation.domain }` : u)); } // Normalize the unmuted users if (resource.unmuted) { - resource.unmuted = resource.unmuted.map((u) => (!isFullyQualified(u) ? `${ u }@${ Federation.domain }` : u)); + resource.unmuted = resource.unmuted.map((u) => (u.indexOf('@') === -1 ? `${ u }@${ Federation.domain }` : u)); } } diff --git a/app/federation/server/normalizers/subscription.js b/app/federation/server/normalizers/subscription.js index 130c29be1b37..191e726a88e4 100644 --- a/app/federation/server/normalizers/subscription.js +++ b/app/federation/server/normalizers/subscription.js @@ -1,14 +1,13 @@ import { Federation } from '../index'; -import { getNameAndDomain, isFullyQualified } from './helpers/federatedResources'; -const denormalizeSubscription = (originalResource) => { - const resource = { ...originalResource }; +const denormalizeSubscription = (resource) => { + resource = { ...resource }; - const [username, domain] = getNameAndDomain(resource.u.username); + const [username, domain] = resource.u.username.split('@'); resource.u.username = domain === Federation.domain ? username : resource.u.username; - const [nameUsername, nameDomain] = getNameAndDomain(resource.name); + const [nameUsername, nameDomain] = resource.name.split('@'); resource.name = nameDomain === Federation.domain ? nameUsername : resource.name; @@ -17,12 +16,12 @@ const denormalizeSubscription = (originalResource) => { const denormalizeAllSubscriptions = (resources) => resources.map(denormalizeSubscription); -const normalizeSubscription = (originalResource) => { - const resource = { ...originalResource }; +const normalizeSubscription = (resource) => { + resource = { ...resource }; - resource.u.username = !isFullyQualified(resource.u.username) ? `${ resource.u.username }@${ Federation.domain }` : resource.u.username; + resource.u.username = resource.u.username.indexOf('@') === -1 ? `${ resource.u.username }@${ Federation.domain }` : resource.u.username; - resource.name = !isFullyQualified(resource.name) ? `${ resource.name }@${ Federation.domain }` : resource.name; + resource.name = resource.name.indexOf('@') === -1 ? `${ resource.name }@${ Federation.domain }` : resource.name; // Federation resource.federation = resource.federation || { diff --git a/app/federation/server/normalizers/user.js b/app/federation/server/normalizers/user.js index 5c98feed32f4..537438410dfd 100644 --- a/app/federation/server/normalizers/user.js +++ b/app/federation/server/normalizers/user.js @@ -2,16 +2,15 @@ import _ from 'underscore'; import { Federation } from '../index'; import { Users } from '../../../models/server'; -import { getNameAndDomain, isFullyQualified } from './helpers/federatedResources'; -const denormalizeUser = (originalResource) => { - const resource = { ...originalResource }; +const denormalizeUser = (resource) => { + resource = { ...resource }; resource.emails = [{ address: resource.federation.originalInfo.email, }]; - const [username, domain] = getNameAndDomain(resource.username); + const [username, domain] = resource.username.split('@'); resource.username = domain === Federation.domain ? username : resource.username; @@ -20,9 +19,9 @@ const denormalizeUser = (originalResource) => { const denormalizeAllUsers = (resources) => resources.map(denormalizeUser); -const normalizeUser = (originalResource) => { +const normalizeUser = (resource) => { // Get only what we need, non-sensitive data - const resource = _.pick(originalResource, '_id', 'username', 'type', 'emails', 'name', 'federation', 'isRemote', 'createdAt', '_updatedAt'); + resource = _.pick(resource, '_id', 'username', 'type', 'emails', 'name', 'federation', 'isRemote', 'createdAt', '_updatedAt'); const email = resource.emails[0].address; @@ -33,7 +32,7 @@ const normalizeUser = (originalResource) => { resource.active = true; resource.roles = ['user']; resource.status = 'online'; - resource.username = !isFullyQualified(resource.username) ? `${ resource.username }@${ Federation.domain }` : resource.username; + resource.username = resource.username.indexOf('@') === -1 ? `${ resource.username }@${ Federation.domain }` : resource.username; // Federation resource.federation = resource.federation || { diff --git a/app/logger/server/server.js b/app/logger/server/server.js index 40c3c7dc8237..6d15ed22883a 100644 --- a/app/logger/server/server.js +++ b/app/logger/server/server.js @@ -289,11 +289,6 @@ class _Logger { return; } - // Deferred logging - if (typeof options.arguments[0] === 'function') { - options.arguments[0] = options.arguments[0](); - } - const prefix = this.getPrefix(options); if (options.box === true && _.isString(options.arguments[0])) { diff --git a/server/methods/browseChannels.js b/server/methods/browseChannels.js index 3aa357398477..4a077933f6a2 100644 --- a/server/methods/browseChannels.js +++ b/server/methods/browseChannels.js @@ -131,7 +131,9 @@ Meteor.methods({ const users = Federation.methods.searchUsers(text); for (const user of users) { - if (results.find((e) => e._id === user._id)) { continue; } + const exists = results.findIndex((e) => e._id === user._id) !== -1; + + if (exists) { continue; } // Add the federated user to the results results.unshift({