refactor: remove Users from fibers 13 (#28772)

pull/28781/head
Marcos Spessatto Defendi 3 years ago committed by GitHub
parent e19f4a7d76
commit 80bd6f07a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      apps/meteor/app/livechat/server/lib/LivechatTyped.ts
  2. 4
      apps/meteor/app/meteor-accounts-saml/server/lib/SAML.ts
  3. 4
      apps/meteor/client/views/room/modals/ReadReceiptsModal/ReadReceiptRow.tsx
  4. 8
      apps/meteor/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts
  5. 9
      apps/meteor/ee/app/canned-responses/server/methods/saveCannedResponse.ts
  6. 6
      apps/meteor/ee/app/license/server/getSeatsRequestLink.ts
  7. 8
      apps/meteor/ee/app/license/server/license.ts
  8. 6
      apps/meteor/ee/app/livechat-enterprise/server/business-hour/Helper.ts
  9. 18
      apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts
  10. 12
      apps/meteor/ee/app/livechat-enterprise/server/lib/AutoTransferChatScheduler.ts
  11. 4
      apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.js
  12. 4
      apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.js
  13. 12
      apps/meteor/ee/app/livechat-enterprise/server/lib/QueueInactivityMonitor.ts
  14. 8
      apps/meteor/ee/app/livechat-enterprise/server/methods/resumeOnHold.ts
  15. 7
      apps/meteor/ee/server/api/licenses.ts
  16. 9
      apps/meteor/ee/server/apps/communication/rest.js
  17. 7
      apps/meteor/ee/server/configuration/saml.ts
  18. 13
      apps/meteor/ee/server/lib/audit/methods.ts
  19. 5
      apps/meteor/ee/server/lib/message-read-receipt/ReadReceipt.js
  20. 2
      apps/meteor/ee/server/lib/syncUserRoles.ts
  21. 10
      apps/meteor/ee/server/startup/seatsCap.ts
  22. 6
      apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts
  23. 2
      apps/meteor/imports/personal-access-tokens/server/api/methods/regenerateToken.ts
  24. 6
      apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts
  25. 20
      apps/meteor/server/features/EmailInbox/EmailInbox_Outgoing.ts
  26. 11
      apps/meteor/server/lib/dataExport/sendViaEmail.ts
  27. 4
      apps/meteor/server/lib/dataExport/uploadZipFile.ts
  28. 14
      apps/meteor/server/lib/resetUserE2EKey.ts
  29. 15
      apps/meteor/server/lib/roles/getRoomRoles.ts
  30. 6
      apps/meteor/server/models/raw/Users.js
  31. 3
      packages/core-typings/src/ReadReceipt.ts
  32. 21
      packages/model-typings/src/models/IUsersModel.ts

@ -41,7 +41,7 @@ type GenericCloseRoomParams = {
};
export type CloseRoomParamsByUser = {
user: IUser;
user: IUser | null;
} & GenericCloseRoomParams;
export type CloseRoomParamsByVisitor = {
@ -90,11 +90,11 @@ class LivechatClass {
let chatCloser: any;
if (isRoomClosedByUserParams(params)) {
const { user } = params;
this.logger.debug(`Closing by user ${user._id}`);
this.logger.debug(`Closing by user ${user?._id}`);
closeData.closer = 'user';
closeData.closedBy = {
_id: user._id,
username: user.username,
_id: user?._id || '',
username: user?.username,
};
chatCloser = user;
} else if (isRoomClosedByVisitorParams(params)) {

@ -5,7 +5,7 @@ import { Random } from '@rocket.chat/random';
import { Accounts } from 'meteor/accounts-base';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { escapeRegExp, escapeHTML } from '@rocket.chat/string-helpers';
import type { IUser, IIncomingMessage, ILoginToken } from '@rocket.chat/core-typings';
import type { IUser, IIncomingMessage, IPersonalAccessToken } from '@rocket.chat/core-typings';
import { CredentialTokens, Rooms, Users } from '@rocket.chat/models';
import { settings } from '../../../settings/server';
@ -175,7 +175,7 @@ export class SAML {
const stampedToken = Accounts._generateStampedLoginToken();
await Users.addPersonalAccessTokenToUser({
userId: user._id,
loginTokenObject: stampedToken as unknown as ILoginToken,
loginTokenObject: stampedToken as unknown as IPersonalAccessToken,
});
const updateData: Record<string, any> = {

@ -15,7 +15,7 @@ const hoverStyle = css`
`;
const ReadReceiptRow = ({ user, ts }: ReadReceipt): ReactElement => {
const displayName = useUserDisplayName(user);
const displayName = useUserDisplayName(user || {});
const formatDateAndTime = useFormatDateAndTime({ withSeconds: true });
return (
@ -30,7 +30,7 @@ const ReadReceiptRow = ({ user, ts }: ReadReceipt): ReactElement => {
className={hoverStyle}
>
<Box>
<UserAvatar username={user.username || ''} size='x24' />
<UserAvatar username={user?.username || ''} size='x24' />
<Box is='span' mis='x8'>
{displayName}
</Box>

@ -1,11 +1,10 @@
import get from 'lodash.get';
import type { IMessage, IOmnichannelRoom } from '@rocket.chat/core-typings';
import { isOmnichannelRoom } from '@rocket.chat/core-typings';
import { LivechatVisitors, Rooms } from '@rocket.chat/models';
import { LivechatVisitors, Rooms, Users } from '@rocket.chat/models';
import { settings } from '../../../../../app/settings/server';
import { callbacks } from '../../../../../lib/callbacks';
import { Users } from '../../../../../app/models/server';
const placeholderFields = {
'contact.name': {
@ -44,8 +43,11 @@ const handleBeforeSaveMessage = async (message: IMessage, room?: IOmnichannelRoo
let messageText = message.msg;
const agentId = room?.servedBy?._id;
if (!agentId) {
return message;
}
const visitorId = room?.v?._id;
const agent = Users.findOneById(agentId, { fields: { name: 1, _id: 1, emails: 1 } }) || {};
const agent = (await Users.findOneById(agentId, { projection: { name: 1, _id: 1, emails: 1 } })) || {};
const visitor = visitorId && ((await LivechatVisitors.findOneById(visitorId, {})) || {});
Object.keys(placeholderFields).map((field) => {

@ -1,11 +1,10 @@
import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { LivechatDepartment, CannedResponse } from '@rocket.chat/models';
import { LivechatDepartment, CannedResponse, Users } from '@rocket.chat/models';
import type { IOmnichannelCannedResponse } from '@rocket.chat/core-typings';
import { hasPermissionAsync } from '../../../../../app/authorization/server/functions/hasPermission';
import { Users } from '../../../../../app/models/server';
import notifications from '../../../../../app/notifications/server/lib/Notifications';
declare module '@rocket.chat/ui-contexts' {
@ -95,12 +94,12 @@ Meteor.methods<ServerMethods>({
result = await CannedResponse.updateCannedResponse(_id, { ...responseData, createdBy: cannedResponse.createdBy });
} else {
const user = Users.findOneById(Meteor.userId());
const user = await Users.findOneById(userId);
const data = {
...responseData,
...(responseData.scope === 'user' && { userId: user._id }),
createdBy: { _id: user._id, username: user.username },
...(responseData.scope === 'user' && { userId: user?._id }),
createdBy: { _id: user?._id || '', username: user?.username || '' },
_createdAt: new Date(),
};

@ -1,7 +1,5 @@
import type { ISetting } from '@rocket.chat/core-typings';
import { Settings } from '@rocket.chat/models';
import { Users } from '../../../../app/models/server';
import { Settings, Users } from '@rocket.chat/models';
type WizardSettings = Array<ISetting>;
@ -9,7 +7,7 @@ const url = 'https://go.rocket.chat/i/seats-cap-upgrade';
export const getSeatsRequestLink = async (): Promise<string> => {
const workspaceId = await Settings.findOneById('Cloud_Workspace_Id');
const activeUsers = Users.getActiveLocalUserCount();
const activeUsers = await Users.getActiveLocalUserCount();
const wizardSettings: WizardSettings = await Settings.findSetupWizardSettings().toArray();
const newUrl = new URL(url);

@ -2,8 +2,8 @@ import { EventEmitter } from 'events';
import { Apps } from '@rocket.chat/core-services';
import type { IAppStorageItem } from '@rocket.chat/apps-engine/server/storage';
import { Users } from '@rocket.chat/models';
import { Users } from '../../../../app/models/server';
import type { BundleFeature } from './bundles';
import { getBundleModules, isBundle, getBundleFromModule } from './bundles';
import decrypt from './decrypt';
@ -211,12 +211,12 @@ class LicenseClass {
this.showLicenses();
}
canAddNewUser(): boolean {
async canAddNewUser(): Promise<boolean> {
if (!maxActiveUsers) {
return true;
}
return maxActiveUsers > Users.getActiveLocalUserCount();
return maxActiveUsers > (await Users.getActiveLocalUserCount());
}
async canEnableApp(source: LicenseAppSources): Promise<boolean> {
@ -330,7 +330,7 @@ export function getAppsConfig(): NonNullable<ILicense['apps']> {
return License.getAppsConfig();
}
export function canAddNewUser(): boolean {
export async function canAddNewUser(): Promise<boolean> {
return License.canAddNewUser();
}

@ -39,13 +39,13 @@ const getAgentIdsToHandle = async (businessHour: Record<string, any>): Promise<s
export const openBusinessHour = async (businessHour: Record<string, any>): Promise<void> => {
const agentIds: string[] = await getAgentIdsToHandle(businessHour);
await Users.addBusinessHourByAgentIds(agentIds, businessHour._id);
Users.updateLivechatStatusBasedOnBusinessHours();
await Users.updateLivechatStatusBasedOnBusinessHours();
};
export const closeBusinessHour = async (businessHour: Record<string, any>): Promise<void> => {
const agentIds: string[] = await getAgentIdsToHandle(businessHour);
await Users.removeBusinessHourByAgentIds(agentIds, businessHour._id);
Users.updateLivechatStatusBasedOnBusinessHours();
await Users.updateLivechatStatusBasedOnBusinessHours();
};
export const removeBusinessHourByAgentIds = async (agentIds: string[], businessHourId: string): Promise<void> => {
@ -53,7 +53,7 @@ export const removeBusinessHourByAgentIds = async (agentIds: string[], businessH
return;
}
await Users.removeBusinessHourByAgentIds(agentIds, businessHourId);
Users.updateLivechatStatusBasedOnBusinessHours();
await Users.updateLivechatStatusBasedOnBusinessHours();
};
export const resetDefaultBusinessHourIfNeeded = async (): Promise<void> => {

@ -1,14 +1,14 @@
import { LivechatVisitors, LivechatInquiry, LivechatRooms } from '@rocket.chat/models';
import { LivechatVisitors, LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models';
import type { IUser } from '@rocket.chat/core-typings';
import { callbacks } from '../../../../../lib/callbacks';
import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager';
import { settings } from '../../../../../app/settings/server';
import { Users } from '../../../../../app/models/server';
let contactManagerPreferred = false;
let lastChattedAgentPreferred = false;
const normalizeDefaultAgent = (agent: { _id: string; username: string }) => {
const normalizeDefaultAgent = (agent?: Pick<IUser, '_id' | 'username'> | null) => {
if (!agent) {
return;
}
@ -17,8 +17,9 @@ const normalizeDefaultAgent = (agent: { _id: string; username: string }) => {
return { agentId, username };
};
const getDefaultAgent = async (username: string) =>
username && normalizeDefaultAgent(await Users.findOneOnlineAgentByUserList(username, { fields: { _id: 1, username: 1 } }));
const getDefaultAgent = async (username?: string) =>
username !== undefined &&
normalizeDefaultAgent(await Users.findOneOnlineAgentByUserList(username, { projection: { _id: 1, username: 1 } }));
settings.watch<boolean>('Livechat_last_chatted_agent_routing', function (value) {
lastChattedAgentPreferred = value;
@ -117,7 +118,12 @@ callbacks.add(
const {
servedBy: { username: usernameByRoom },
} = room;
const lastRoomAgent = normalizeDefaultAgent(Users.findOneOnlineAgentByUserList(usernameByRoom, { fields: { _id: 1, username: 1 } }));
if (!usernameByRoom) {
return defaultAgent;
}
const lastRoomAgent = normalizeDefaultAgent(
await Users.findOneOnlineAgentByUserList(usernameByRoom, { projection: { _id: 1, username: 1 } }),
);
return lastRoomAgent ?? defaultAgent;
},
callbacks.priority.MEDIUM,

@ -1,16 +1,14 @@
import { Agenda } from '@rocket.chat/agenda';
import { MongoInternals } from 'meteor/mongo';
import { Meteor } from 'meteor/meteor';
import { LivechatRooms } from '@rocket.chat/models';
import { LivechatRooms, Users } from '@rocket.chat/models';
import type { IUser } from '@rocket.chat/core-typings';
import { Users } from '../../../../../app/models/server';
import { Livechat } from '../../../../../app/livechat/server';
import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager';
import { forwardRoomToAgent } from '../../../../../app/livechat/server/lib/Helper';
import { settings } from '../../../../../app/settings/server';
const schedulerUser = Users.findOneById('rocket.cat');
const SCHEDULER_NAME = 'omnichannel_scheduler';
class AutoTransferChatSchedulerClass {
@ -35,6 +33,10 @@ class AutoTransferChatSchedulerClass {
this.running = true;
}
private async getSchedulerUser(): Promise<IUser | null> {
return Users.findOneById('rocket.cat');
}
public async scheduleRoom(roomId: string, timeout: number): Promise<void> {
await this.unscheduleRoom(roomId);
@ -77,7 +79,7 @@ class AutoTransferChatSchedulerClass {
return Livechat.returnRoomAsInquiry(room._id, departmentId, {
scope: 'autoTransferUnansweredChatsToQueue',
comment: timeoutDuration,
transferredBy: schedulerUser,
transferredBy: await this.getSchedulerUser(),
});
}
@ -85,7 +87,7 @@ class AutoTransferChatSchedulerClass {
if (agent) {
return forwardRoomToAgent(room, {
userId: agent.agentId,
transferredBy: schedulerUser,
transferredBy: await this.getSchedulerUser(),
transferredTo: agent,
scope: 'autoTransferUnansweredChatsToAgent',
comment: timeoutDuration,

@ -6,11 +6,11 @@ import {
LivechatDepartment as LivechatDepartmentRaw,
LivechatCustomField,
LivechatInquiry,
Users,
} from '@rocket.chat/models';
import { api } from '@rocket.chat/core-services';
import { memoizeDebounce } from './debounceByParams';
import { Users } from '../../../../../app/models/server';
import { settings } from '../../../../../app/settings/server';
import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager';
import { dispatchAgentDelegated } from '../../../../../app/livechat/server/lib/Helper';
@ -29,7 +29,7 @@ export const getMaxNumberSimultaneousChat = async ({ agentId, departmentId }) =>
}
if (agentId) {
const user = Users.getAgentInfo(agentId);
const user = await Users.getAgentInfo(agentId);
const { livechat: { maxNumberSimultaneousChat } = {} } = user || {};
if (maxNumberSimultaneousChat > 0) {
return maxNumberSimultaneousChat;

@ -29,7 +29,7 @@ export const LivechatEnterprise = {
async addMonitor(username) {
check(username, String);
const user = await Users.findOneByUsername(username, { fields: { _id: 1, username: 1 } });
const user = await Users.findOneByUsername(username, { projection: { _id: 1, username: 1 } });
if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
@ -47,7 +47,7 @@ export const LivechatEnterprise = {
async removeMonitor(username) {
check(username, String);
const user = await Users.findOneByUsername(username, { fields: { _id: 1 } });
const user = await Users.findOneByUsername(username, { projection: { _id: 1 } });
if (!user) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {

@ -4,11 +4,10 @@ import { MongoInternals } from 'meteor/mongo';
import { Meteor } from 'meteor/meteor';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import type { IUser, IOmnichannelRoom } from '@rocket.chat/core-typings';
import { LivechatRooms, LivechatInquiry as LivechatInquiryRaw } from '@rocket.chat/models';
import { LivechatRooms, LivechatInquiry as LivechatInquiryRaw, Users } from '@rocket.chat/models';
import { settings } from '../../../../../app/settings/server';
import { Logger } from '../../../../../app/logger/server';
import { Users } from '../../../../../app/models/server';
import { Livechat } from '../../../../../app/livechat/server/lib/LivechatTyped';
const SCHEDULER_NAME = 'omnichannel_queue_inactivity_monitor';
@ -41,12 +40,15 @@ class OmnichannelQueueInactivityMonitorClass {
defaultConcurrency: 1,
});
this.createIndex();
this.user = Users.findOneById('rocket.cat');
const language = settings.get<string>('Language') || 'en';
this.message = TAPi18n.__('Closed_automatically_chat_queued_too_long', { lng: language });
this.bindedCloseRoom = Meteor.bindEnvironment(this.closeRoom.bind(this));
}
private async getRocketCatUser(): Promise<IUser | null> {
return Users.findOneById('rocket.cat');
}
getName(inquiryId: string): string {
return `${this._name}-${inquiryId}`;
}
@ -93,12 +95,12 @@ class OmnichannelQueueInactivityMonitorClass {
await this.scheduler.cancel({ name });
}
closeRoomAction(room: IOmnichannelRoom): Promise<void> {
async closeRoomAction(room: IOmnichannelRoom): Promise<void> {
const comment = this.message;
return Livechat.closeRoom({
comment,
room,
user: this.user,
user: await this.getRocketCatUser(),
});
}

@ -1,11 +1,10 @@
import { Meteor } from 'meteor/meteor';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import type { ILivechatVisitor } from '@rocket.chat/core-typings';
import { LivechatVisitors, LivechatInquiry, LivechatRooms } from '@rocket.chat/models';
import { LivechatVisitors, LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models';
import { Message } from '@rocket.chat/core-services';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { Users } from '../../../../../app/models/server';
import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager';
import { callbacks } from '../../../../../lib/callbacks';
@ -62,7 +61,10 @@ Meteor.methods<ServerMethods>({
const { servedBy: { _id: agentId, username } = {} } = room;
await RoutingManager.takeInquiry(inquiry, { agentId, username }, options);
const onHoldChatResumedBy = options.clientAction ? await Meteor.userAsync() : Users.findOneById('rocket.cat');
const onHoldChatResumedBy = options.clientAction ? await Meteor.userAsync() : await Users.findOneById('rocket.cat');
if (!onHoldChatResumedBy) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'livechat:resumeOnHold' });
}
const comment = await resolveOnHoldCommentInfo(options, room, onHoldChatResumedBy);

@ -1,8 +1,7 @@
import { check } from 'meteor/check';
import { Settings } from '@rocket.chat/models';
import { Settings, Users } from '@rocket.chat/models';
import { getLicenses, validateFormat, flatModules, getMaxActiveUsers, isEnterprise } from '../../app/license/server/license';
import { Users } from '../../../app/models/server';
import { API } from '../../../app/api/server/api';
import { hasPermissionAsync } from '../../../app/authorization/server/functions/hasPermission';
import type { ILicense } from '../../app/license/definition/ILicense';
@ -61,9 +60,9 @@ API.v1.addRoute(
'licenses.maxActiveUsers',
{ authRequired: true },
{
get() {
async get() {
const maxActiveUsers = getMaxActiveUsers() || null;
const activeUsers = Users.getActiveLocalUserCount();
const activeUsers = await Users.getActiveLocalUserCount();
return API.v1.success({ maxActiveUsers, activeUsers });
},

@ -1,6 +1,6 @@
import { Meteor } from 'meteor/meteor';
import { HTTP } from 'meteor/http';
import { Settings, Users as UsersRaw } from '@rocket.chat/models';
import { Settings, Users } from '@rocket.chat/models';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { AppStatus, AppStatusUtils } from '@rocket.chat/apps-engine/definition/AppStatus';
@ -9,7 +9,6 @@ import { getUploadFormData } from '../../../../app/api/server/lib/getUploadFormD
import { getWorkspaceAccessToken, getWorkspaceAccessTokenWithScope } from '../../../../app/cloud/server';
import { settings } from '../../../../app/settings/server';
import { Info } from '../../../../app/utils/server';
import { Users } from '../../../../app/models/server';
import { Apps } from '../orchestrator';
import { formatAppInstanceForRest } from '../../../lib/misc/formatAppInstanceForRest';
import { actionButtonsHandler } from './endpoints/actionButtonsHandler';
@ -183,7 +182,7 @@ export class AppsRestApi {
const subscribeRoute = this.queryParams.details === 'true' ? 'subscribe/details' : 'subscribe';
const seats = Users.getActiveLocalUserCount();
const seats = await Users.getActiveLocalUserCount();
return API.v1.success({
url: `${baseUrl}/apps/${this.queryParams.appId}/${
@ -287,7 +286,7 @@ export class AppsRestApi {
const subscribeRoute = this.queryParams.details === 'true' ? 'subscribe/details' : 'subscribe';
const seats = Users.getActiveLocalUserCount();
const seats = await Users.getActiveLocalUserCount();
return API.v1.success({
url: `${baseUrl}/apps/${this.queryParams.appId}/${
@ -442,7 +441,7 @@ export class AppsRestApi {
let admins = [];
try {
const adminsRaw = await UsersRaw.findUsersInRoles('admin', undefined, {
const adminsRaw = await Users.findUsersInRoles('admin', undefined, {
projection: {
username: 1,
name: 1,

@ -1,11 +1,10 @@
import { Roles } from '@rocket.chat/models';
import { Roles, Users } from '@rocket.chat/models';
import { onLicense } from '../../app/license/server';
import type { ISAMLUser } from '../../../app/meteor-accounts-saml/server/definition/ISAMLUser';
import { SAMLUtils } from '../../../app/meteor-accounts-saml/server/lib/Utils';
import { settings } from '../../../app/settings/server';
import { addSettings } from '../settings/saml';
import { Users } from '../../../app/models/server';
import { ensureArray } from '../../../lib/utils/arrayUtils';
onLicense('saml-enterprise', () => {
@ -45,7 +44,7 @@ onLicense('saml-enterprise', () => {
});
});
SAMLUtils.events.on('updateCustomFields', (loginResult: Record<string, any>, updatedUser: { userId: string; token: string }) => {
SAMLUtils.events.on('updateCustomFields', async (loginResult: Record<string, any>, updatedUser: { userId: string; token: string }) => {
const userDataCustomFieldMap = settings.get('SAML_Custom_Default_user_data_custom_fieldmap') as string;
const customMap: Record<string, any> = JSON.parse(userDataCustomFieldMap);
@ -63,7 +62,7 @@ onLicense('saml-enterprise', () => {
customFieldsList[customAttribute] = value;
}
Users.updateCustomFieldsById(updatedUser.userId, customFieldsList);
await Users.updateCustomFieldsById(updatedUser.userId, customFieldsList);
});
});

@ -6,19 +6,20 @@ import { escapeRegExp } from '@rocket.chat/string-helpers';
import type { ILivechatAgent, ILivechatVisitor, IMessage, IRoom, IUser } from '@rocket.chat/core-typings';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import type { Filter } from 'mongodb';
import { LivechatRooms, Messages, Rooms } from '@rocket.chat/models';
import { LivechatRooms, Messages, Rooms, Users } from '@rocket.chat/models';
import AuditLog from './AuditLog';
import { Users } from '../../../../app/models/server';
import { hasPermissionAsync } from '../../../../app/authorization/server/functions/hasPermission';
import { updateCounter } from '../../../../app/statistics/server';
import type { IAuditLog } from '../../../definition/IAuditLog';
import { isTruthy } from '../../../../lib/isTruthy';
const getValue = (room: IRoom | null) => room && { rids: [room._id], name: room.name };
const getUsersIdFromUserName = (usernames: IUser['username'][]) => {
const user: IUser[] = usernames ? Users.findByUsername({ $in: usernames }) : undefined;
return user.map((userId) => userId._id);
const getUsersIdFromUserName = async (usernames: IUser['username'][]) => {
const users = usernames ? await Users.findByUsernames(usernames.filter(isTruthy)).toArray() : undefined;
return users?.filter(isTruthy).map((userId) => userId._id);
};
const getRoomInfoByAuditParams = async ({
@ -138,7 +139,7 @@ Meteor.methods<ServerMethods>({
};
if (type === 'u') {
const usersId = getUsersIdFromUserName(usernames);
const usersId = await getUsersIdFromUserName(usernames);
query['u._id'] = { $in: usersId };
} else {
const roomInfo = await getRoomInfoByAuditParams({ type, roomId: rid, users: usernames, visitor, agent });

@ -1,7 +1,6 @@
import { Random } from '@rocket.chat/random';
import { LivechatVisitors, ReadReceipts, Messages, Rooms, Subscriptions } from '@rocket.chat/models';
import { LivechatVisitors, ReadReceipts, Messages, Rooms, Subscriptions, Users } from '@rocket.chat/models';
import { Users } from '../../../../app/models/server';
import { settings } from '../../../../app/settings/server';
import { SystemLogger } from '../../../../server/lib/logger/system';
import { roomCoordinator } from '../../../../server/lib/rooms/roomCoordinator';
@ -116,7 +115,7 @@ export const ReadReceipt = {
...receipt,
user: receipt.token
? await LivechatVisitors.getVisitorByToken(receipt.token, { projection: { username: 1, name: 1 } })
: Users.findOneById(receipt.userId, { fields: { username: 1, name: 1 } }),
: await Users.findOneById(receipt.userId, { projection: { username: 1, name: 1 } }),
})),
);
},

@ -68,7 +68,7 @@ export async function syncUserRoles(
}
const wasGuest = existingRoles.length === 1 && existingRoles[0] === 'guest';
if (wasGuest && !canAddNewUser()) {
if (wasGuest && !(await canAddNewUser())) {
throw new Error('error-license-user-limit-reached');
}

@ -16,12 +16,12 @@ import { validateUserRoles } from '../../app/authorization/server/validateUserRo
callbacks.add(
'onCreateUser',
({ isGuest }: { isGuest: boolean }) => {
async ({ isGuest }: { isGuest: boolean }) => {
if (isGuest) {
return;
}
if (!canAddNewUser()) {
if (!(await canAddNewUser())) {
throw new Meteor.Error('error-license-user-limit-reached', TAPi18n.__('error-license-user-limit-reached'));
}
},
@ -31,7 +31,7 @@ callbacks.add(
callbacks.add(
'beforeActivateUser',
(user: IUser) => {
async (user: IUser) => {
if (user.roles.length === 1 && user.roles.includes('guest')) {
return;
}
@ -40,7 +40,7 @@ callbacks.add(
return;
}
if (!canAddNewUser()) {
if (!(await canAddNewUser())) {
throw new Meteor.Error('error-license-user-limit-reached', TAPi18n.__('error-license-user-limit-reached'));
}
},
@ -71,7 +71,7 @@ callbacks.add(
return;
}
if (!canAddNewUser()) {
if (!(await canAddNewUser())) {
throw new Meteor.Error('error-license-user-limit-reached', TAPi18n.__('error-license-user-limit-reached'));
}
},

@ -2,9 +2,9 @@ import { Meteor } from 'meteor/meteor';
import { Random } from '@rocket.chat/random';
import { Accounts } from 'meteor/accounts-base';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { Users } from '@rocket.chat/models';
import { hasPermissionAsync } from '../../../../../app/authorization/server/functions/hasPermission';
import { Users } from '../../../../../app/models/server';
import { twoFactorRequired } from '../../../../../app/2fa/server/twoFactorRequired';
declare module '@rocket.chat/ui-contexts' {
@ -29,7 +29,7 @@ Meteor.methods<ServerMethods>({
}
const token = Random.secret();
const tokenExist = Users.findPersonalAccessTokenByTokenNameAndUserId({
const tokenExist = await Users.findPersonalAccessTokenByTokenNameAndUserId({
userId: uid,
tokenName,
});
@ -39,7 +39,7 @@ Meteor.methods<ServerMethods>({
});
}
Users.addPersonalAccessTokenToUser({
await Users.addPersonalAccessTokenToUser({
userId: uid,
loginTokenObject: {
hashedToken: Accounts._hashLoginToken(token),

@ -26,7 +26,7 @@ Meteor.methods<ServerMethods>({
});
}
const tokenExist = Users.findPersonalAccessTokenByTokenNameAndUserId({
const tokenExist = await Users.findPersonalAccessTokenByTokenNameAndUserId({
userId: uid,
tokenName,
});

@ -1,8 +1,8 @@
import { Meteor } from 'meteor/meteor';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { Users } from '@rocket.chat/models';
import { hasPermissionAsync } from '../../../../../app/authorization/server/functions/hasPermission';
import { Users } from '../../../../../app/models/server';
import { twoFactorRequired } from '../../../../../app/2fa/server/twoFactorRequired';
declare module '@rocket.chat/ui-contexts' {
@ -25,7 +25,7 @@ Meteor.methods<ServerMethods>({
method: 'personalAccessTokens:removeToken',
});
}
const tokenExist = Users.findPersonalAccessTokenByTokenNameAndUserId({
const tokenExist = await Users.findPersonalAccessTokenByTokenNameAndUserId({
userId: uid,
tokenName,
});
@ -34,7 +34,7 @@ Meteor.methods<ServerMethods>({
method: 'personalAccessTokens:removeToken',
});
}
Users.removePersonalAccessTokenOfUser({
await Users.removePersonalAccessTokenOfUser({
userId: uid,
loginTokenObject: {
type: 'personalAccessToken',

@ -3,12 +3,11 @@ import { Match } from 'meteor/check';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import { isIMessageInbox } from '@rocket.chat/core-typings';
import type { IEmailInbox, IUser, IMessage, IOmnichannelRoom } from '@rocket.chat/core-typings';
import { Messages, Uploads, LivechatRooms, Rooms } from '@rocket.chat/models';
import { Messages, Uploads, LivechatRooms, Rooms, Users } from '@rocket.chat/models';
import { callbacks } from '../../../lib/callbacks';
import { FileUpload } from '../../../app/file-upload/server';
import { slashCommands } from '../../../app/utils/server';
import { Users } from '../../../app/models/server';
import type { Inbox } from './EmailInbox';
import { inboxes } from './EmailInbox';
import { sendMessage } from '../../../app/lib/server/functions/sendMessage';
@ -17,7 +16,7 @@ import { logger } from './logger';
const livechatQuoteRegExp = /^\[\s\]\(https?:\/\/.+\/live\/.+\?msg=(?<id>.+?)\)\s(?<text>.+)/s;
const user: IUser = Users.findOneById('rocket.cat');
const getRocketCatUser = async (): Promise<IUser | null> => Users.findOneById('rocket.cat');
const language = settings.get<string>('Language') || 'en';
const t = (s: string): string => TAPi18n.__(s, { lng: language });
@ -36,6 +35,11 @@ const sendErrorReplyMessage = async (error: string, options: any) => {
ts: new Date(),
};
const user = await getRocketCatUser();
if (!user) {
return;
}
return sendMessage(user, message, { _id: options.rid });
};
@ -51,6 +55,11 @@ const sendSuccessReplyMessage = async (options: any) => {
ts: new Date(),
};
const user = await getRocketCatUser();
if (!user) {
return;
}
return sendMessage(user, message, { _id: options.rid });
};
@ -184,6 +193,11 @@ callbacks.add(
return message;
}
const user = await getRocketCatUser();
if (!user) {
return message;
}
if (message.files?.length && message.u.username !== 'rocket.cat') {
await sendMessage(
user,

@ -1,9 +1,8 @@
import moment from 'moment';
import type { IMessage, IUser } from '@rocket.chat/core-typings';
import { Messages } from '@rocket.chat/models';
import { Messages, Users } from '@rocket.chat/models';
import * as Mailer from '../../../app/mailer/server/api';
import { Users } from '../../../app/models/server';
import { settings } from '../../../app/settings/server';
import { Message } from '../../../app/ui-utils/server';
import { getMomentLocale } from '../getMomentLocale';
@ -25,9 +24,11 @@ export async function sendViaEmail(
const missing = [...data.toUsers].filter(Boolean);
Users.findUsersByUsernames(data.toUsers, {
fields: { 'username': 1, 'emails.address': 1 },
}).forEach((user: IUser) => {
(
await Users.findUsersByUsernames(data.toUsers, {
projection: { 'username': 1, 'emails.address': 1 },
}).toArray()
).forEach((user: IUser) => {
const emailAddress = user.emails?.[0].address;
if (!emailAddress) {

@ -2,15 +2,15 @@ import { createReadStream } from 'fs';
import { open, stat } from 'fs/promises';
import type { IUser } from '@rocket.chat/core-typings';
import { Users } from '@rocket.chat/models';
import { Users } from '../../../app/models/server';
import { FileUpload } from '../../../app/file-upload/server';
export const uploadZipFile = async (filePath: string, userId: IUser['_id'], exportType: 'json' | 'html'): Promise<any> => {
const contentType = 'application/zip';
const { size } = await stat(filePath);
const user = Users.findOneById(userId);
const user = await Users.findOneById(userId);
let userDisplayName = userId;
if (user) {
userDisplayName = user.name || user.username || userId;

@ -1,15 +1,13 @@
import { Meteor } from 'meteor/meteor';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import type { IUser } from '@rocket.chat/core-typings';
import { Subscriptions, Users as UsersRaw } from '@rocket.chat/models';
import { Subscriptions, Users } from '@rocket.chat/models';
import { Users } from '../../app/models/server';
import { settings } from '../../app/settings/server';
import * as Mailer from '../../app/mailer/server/api';
import { isUserIdFederated } from './isUserIdFederated';
const sendResetNotification = function (uid: string): void {
const user: IUser = Users.findOneById(uid, {});
const sendResetNotification = async function (uid: string): Promise<void> {
const user = await Users.findOneById(uid, {});
if (!user) {
throw new Meteor.Error('invalid-user');
}
@ -60,7 +58,7 @@ const sendResetNotification = function (uid: string): void {
export async function resetUserE2EEncriptionKey(uid: string, notifyUser: boolean): Promise<boolean> {
if (notifyUser) {
sendResetNotification(uid);
await sendResetNotification(uid);
}
const isUserFederated = await isUserIdFederated(uid);
@ -68,11 +66,11 @@ export async function resetUserE2EEncriptionKey(uid: string, notifyUser: boolean
throw new Meteor.Error('error-not-allowed', 'Federated Users cant have TOTP', { function: 'resetTOTP' });
}
Users.resetE2EKey(uid);
await Users.resetE2EKey(uid);
await Subscriptions.resetUserE2EKey(uid);
// Force the user to logout, so that the keys can be generated again
await UsersRaw.unsetLoginTokens(uid);
await Users.unsetLoginTokens(uid);
return true;
}

@ -1,9 +1,8 @@
import _ from 'underscore';
import type { IRoom, ISubscription } from '@rocket.chat/core-typings';
import { Roles, Subscriptions } from '@rocket.chat/models';
import { Roles, Subscriptions, Users } from '@rocket.chat/models';
import { settings } from '../../../app/settings/server';
import { Users } from '../../../app/models/server';
export async function getRoomRoles(rid: IRoom['_id']): Promise<ISubscription[]> {
const options = {
@ -25,9 +24,11 @@ export async function getRoomRoles(rid: IRoom['_id']): Promise<ISubscription[]>
if (!useRealName) {
return subscriptions;
}
return subscriptions.map((subscription) => {
const user = Users.findOneById(subscription.u._id);
subscription.u.name = user?.name;
return subscription;
});
return Promise.all(
subscriptions.map(async (subscription) => {
const user = await Users.findOneById(subscription.u._id);
subscription.u.name = user?.name;
return subscription;
}),
);
}

@ -2000,6 +2000,12 @@ export class UsersRaw extends BaseRaw {
return this.find(query, options);
}
findByUsernames(usernames, options) {
const query = { $in: usernames };
return this.find(query, options);
}
findByUsernamesIgnoringCase(usernames, options) {
const query = {
username: {

@ -1,3 +1,4 @@
import type { ILivechatVisitor } from './ILivechatVisitor';
import type { IMessage } from './IMessage/IMessage';
import type { IRoom } from './IRoom';
import type { IUser } from './IUser';
@ -6,7 +7,7 @@ export type ReadReceipt = {
messageId: IMessage['_id'];
roomId: IRoom['_id'];
ts: Date;
user: Pick<IUser, '_id' | 'name' | 'username'>;
user: Pick<IUser, '_id' | 'name' | 'username'> | ILivechatVisitor | null;
userId: IUser['_id'];
_id: string;
};

@ -1,5 +1,14 @@
import type { Document, UpdateResult, FindCursor, FindOptions, Filter, InsertOneResult, DeleteResult } from 'mongodb';
import type { IUser, IRole, IRoom, ILivechatAgent, UserStatus, ILoginToken } from '@rocket.chat/core-typings';
import type {
IUser,
IRole,
IRoom,
ILivechatAgent,
UserStatus,
ILoginToken,
IPersonalAccessToken,
AtLeast,
} from '@rocket.chat/core-typings';
import type { FindPaginated, IBaseModel } from './IBaseModel';
@ -213,7 +222,7 @@ export interface IUsersModel extends IBaseModel<IUser> {
}[]
>;
findOneOnlineAgentByUserList(
userList: string[],
userList: string[] | string,
options?: FindOptions<IUser>,
isLivechatEnabledWhenAgentIdle?: boolean,
): Promise<IUser | null>;
@ -224,8 +233,11 @@ export interface IUsersModel extends IBaseModel<IUser> {
addRoomByUserId(userId: string, rid: string): Promise<UpdateResult>;
removeRoomByRoomIds(rids: string[]): Promise<UpdateResult | Document>;
getLoginTokensByUserId(userId: string): FindCursor<ILoginToken>;
addPersonalAccessTokenToUser(data: { userId: string; loginTokenObject: ILoginToken }): Promise<UpdateResult>;
removePersonalAccessTokenOfUser(data: { userId: string; loginTokenObject: ILoginToken }): Promise<UpdateResult>;
addPersonalAccessTokenToUser(data: { userId: string; loginTokenObject: IPersonalAccessToken }): Promise<UpdateResult>;
removePersonalAccessTokenOfUser(data: {
userId: string;
loginTokenObject: AtLeast<IPersonalAccessToken, 'type' | 'name'>;
}): Promise<UpdateResult>;
findPersonalAccessTokenByTokenNameAndUserId(data: { userId: string; tokenName: string }): Promise<IUser | null>;
setOperator(userId: string, operator: boolean): Promise<UpdateResult>;
checkOnlineAgents(agentId: string): Promise<boolean>;
@ -286,6 +298,7 @@ export interface IUsersModel extends IBaseModel<IUser> {
findNotIdUpdatedFrom(userId: string, updatedFrom: Date, options?: FindOptions<IUser>): FindCursor<IUser>;
findByRoomId(roomId: string, options?: FindOptions<IUser>): FindCursor<IUser>;
findByUsername(username: string, options?: FindOptions<IUser>): FindCursor<IUser>;
findByUsernames(usernames: string[], options?: FindOptions<IUser>): FindCursor<IUser>;
findByUsernamesIgnoringCase(usernames: string[], options?: FindOptions<IUser>): FindCursor<IUser>;
findActiveByUserIds(userIds: string[], options?: FindOptions<IUser>): FindCursor<IUser>;
findActiveLocalGuests(idsExceptions: string[], options?: FindOptions<IUser>): FindCursor<IUser>;

Loading…
Cancel
Save