Skip to content
23 changes: 21 additions & 2 deletions apps/meteor/app/apps/server/bridges/users.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Random } from 'meteor/random';
import { UserBridge } from '@rocket.chat/apps-engine/server/bridges/UserBridge';
import type { IUserCreationOptions, IUser } from '@rocket.chat/apps-engine/definition/users';
import type { IUserCreationOptions, IUser, UserType } from '@rocket.chat/apps-engine/definition/users';
import { Subscriptions, Users as UsersRaw } from '@rocket.chat/models';

import { setUserAvatar, checkUsernameAvailability, deleteUser } from '../../../lib/server/functions';
import { setUserAvatar, checkUsernameAvailability, deleteUser, getUserCreatedByApp } from '../../../lib/server/functions';
import { Users } from '../../../models/server';
import type { AppServerOrchestrator } from '../orchestrator';

Expand Down Expand Up @@ -33,6 +33,24 @@ export class AppUserBridge extends UserBridge {
return this.orch.getConverters()?.get('users').convertToApp(user);
}

/**
* Deletes all bot or app users created by the App.
* @param appId the App's ID.
* @param type the type of the user to be deleted.
* @returns true if any user was deleted, false otherwise.
*/
protected async deleteUsersCreatedByApp(appId: string, type: UserType.APP | UserType.BOT): Promise<boolean> {
this.orch.debugLog(`The App ${appId} is deleting all bot users`);

const appUsers = await getUserCreatedByApp(appId, type);
if (appUsers.length) {
this.orch.debugLog(`The App ${appId} is deleting ${appUsers.length} users`);
await Promise.all(appUsers.map((appUser) => deleteUser(appUser._id)));
return true;
}
return false;
}

protected async create(userDescriptor: Partial<IUser>, appId: string, options?: IUserCreationOptions): Promise<string> {
this.orch.debugLog(`The App ${appId} is requesting to create a new user.`);
const user = this.orch.getConverters()?.get('users').convertToRocketChat(userDescriptor);
Expand All @@ -46,6 +64,7 @@ export class AppUserBridge extends UserBridge {
}

switch (user.type) {
case 'bot':
case 'app':
if (!checkUsernameAvailability(user.username)) {
throw new Error(`The username "${user.username}" is already being used. Rename or remove the user using it to install this App`);
Expand Down
13 changes: 13 additions & 0 deletions apps/meteor/app/lib/server/functions/getUserCreatedByApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { FindOptions } from 'mongodb';
import type { IUser } from '@rocket.chat/core-typings';
import { Users } from '@rocket.chat/models';
import type { UserType } from '@rocket.chat/apps-engine/definition/users';

export async function getUserCreatedByApp(
appId: string,
type: UserType.BOT | UserType.APP,
options?: FindOptions<IUser>,
): Promise<Array<IUser>> {
const users = await Users.find({ appId, type }, options).toArray();
return users ?? [];
}
1 change: 1 addition & 0 deletions apps/meteor/app/lib/server/functions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export { deleteRoom } from './deleteRoom';
export { deleteUser } from './deleteUser';
export { getRoomByNameOrIdWithOptionToJoin } from './getRoomByNameOrIdWithOptionToJoin';
export { getUserSingleOwnedRooms } from './getUserSingleOwnedRooms';
export { getUserCreatedByApp } from './getUserCreatedByApp';
export { generateUsernameSuggestion } from './getUsernameSuggestion';
export { insertMessage } from './insertMessage';
export { isTheLastMessage } from './isTheLastMessage';
Expand Down