diff --git a/app/api/server/helpers/getUserFromParams.js b/app/api/server/helpers/getUserFromParams.js index c97d6f34e1e..0b562a87b99 100644 --- a/app/api/server/helpers/getUserFromParams.js +++ b/app/api/server/helpers/getUserFromParams.js @@ -25,3 +25,28 @@ API.helperMethods.set('getUserFromParams', function _getUserFromParams() { return user; }); + +API.helperMethods.set('getUserListFromParams', function _getUserListFromParams() { + let users; + const params = this.requestParams(); + // if params.userId is provided, include it as well + const soleUser = params.userId || params.username || params.user; + let userListParam = params.userIds || params.usernames || []; + userListParam.push(soleUser); + userListParam = userListParam.filter(Boolean); + + // deduplicate to avoid errors + userListParam = [...new Set(userListParam)]; + + if (!userListParam.length) { + throw new Meteor.Error('error-users-params-not-provided', 'Please provide "userId" or "username" or "userIds" or "usernames" as param'); + } + + if (params.userIds || params.userId) { + users = Users.findByIds(userListParam); + } else { + users = Users.findByUsernamesIgnoringCase(userListParam); + } + + return users.fetch(); +}); diff --git a/app/api/server/v1/channels.js b/app/api/server/v1/channels.js index 0c287890b8f..5677d810fb9 100644 --- a/app/api/server/v1/channels.js +++ b/app/api/server/v1/channels.js @@ -422,10 +422,14 @@ API.v1.addRoute('channels.invite', { authRequired: true }, { post() { const findResult = findChannelByIdOrName({ params: this.requestParams() }); - const user = this.getUserFromParams(); + const users = this.getUserListFromParams(); + + if (!users.length) { + return API.v1.failure('invalid-user-invite-list', 'Cannot invite if no users are provided'); + } Meteor.runAsUser(this.userId, () => { - Meteor.call('addUserToRoom', { rid: findResult._id, username: user.username }); + Meteor.call('addUsersToRoom', { rid: findResult._id, users: users.map((u) => u.username) }); }); return API.v1.success({ diff --git a/app/api/server/v1/groups.js b/app/api/server/v1/groups.js index c2e67462651..5b357e809f8 100644 --- a/app/api/server/v1/groups.js +++ b/app/api/server/v1/groups.js @@ -393,9 +393,13 @@ API.v1.addRoute('groups.invite', { authRequired: true }, { throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "roomName" param provided does not match any group'); } - const { username } = this.getUserFromParams(); + const users = this.getUserListFromParams(); - Meteor.runAsUser(this.userId, () => Meteor.call('addUserToRoom', { rid, username })); + if (!users.length) { + throw new Meteor.Error('error-empty-invite-list', 'Cannot invite if no valid users are provided'); + } + + Meteor.runAsUser(this.userId, () => Meteor.call('addUsersToRoom', { rid, users: users.map((u) => u.username) })); return API.v1.success({ group: this.composeRoomWithLastMessage(Rooms.findOneById(rid, { fields: API.v1.defaultFieldsToExclude }), this.userId), diff --git a/app/models/server/models/Users.js b/app/models/server/models/Users.js index ad9929c6b26..e6e60cd6a03 100644 --- a/app/models/server/models/Users.js +++ b/app/models/server/models/Users.js @@ -717,6 +717,16 @@ export class Users extends Base { return this.find(query, options); } + findByUsernamesIgnoringCase(usernames, options) { + const query = { + username: { + $in: usernames.filter(Boolean).map((u) => new RegExp(`^${ escapeRegExp(u) }$`, 'i')), + }, + }; + + return this.find(query, options); + } + findActive(options = {}) { return this.find({ active: true,