diff --git a/apps/meteor/app/apps/server/converters/rooms.js b/apps/meteor/app/apps/server/converters/rooms.js index 111298213619..ae38feff5eff 100644 --- a/apps/meteor/app/apps/server/converters/rooms.js +++ b/apps/meteor/app/apps/server/converters/rooms.js @@ -53,7 +53,7 @@ export class AppRoomsConverter { let departmentId; if (room.department) { - const department = await LivechatDepartment.findOneById(room.department.id); + const department = await LivechatDepartment.findOneById(room.department.id, { projection: { _id: 1 } }); departmentId = department._id; } diff --git a/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts b/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts index 76942a681805..005df0bb2a7a 100644 --- a/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts +++ b/apps/meteor/app/e2e/server/methods/setRoomKeyID.ts @@ -31,7 +31,7 @@ Meteor.methods({ throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' }); } - const room = await Rooms.findOneById(rid, { fields: { e2eKeyId: 1 } }); + const room = await Rooms.findOneById>(rid, { projection: { e2eKeyId: 1 } }); if (!room) { throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'e2e.setRoomKeyID' }); diff --git a/apps/meteor/app/livechat/server/api/lib/departments.ts b/apps/meteor/app/livechat/server/api/lib/departments.ts index 215938fbb20b..049dbebaf7aa 100644 --- a/apps/meteor/app/livechat/server/api/lib/departments.ts +++ b/apps/meteor/app/livechat/server/api/lib/departments.ts @@ -129,7 +129,7 @@ export async function findDepartmentById({ department: await LivechatDepartment.findOne(query), ...(includeAgents && canViewLivechatDepartments && { - agents: await LivechatDepartmentAgents.find({ departmentId }).toArray(), + agents: await LivechatDepartmentAgents.findByDepartmentId(departmentId).toArray(), }), }; @@ -192,6 +192,6 @@ export async function findDepartmentsBetweenIds({ ids: string[]; fields: Record; }): Promise<{ departments: ILivechatDepartment[] }> { - const departments = await LivechatDepartment.findInIds(ids, fields).toArray(); + const departments = await LivechatDepartment.findInIds(ids, { projection: fields }).toArray(); return { departments }; } diff --git a/apps/meteor/app/livechat/server/api/lib/inquiries.ts b/apps/meteor/app/livechat/server/api/lib/inquiries.ts index 450e035af257..19cbfc21ede9 100644 --- a/apps/meteor/app/livechat/server/api/lib/inquiries.ts +++ b/apps/meteor/app/livechat/server/api/lib/inquiries.ts @@ -8,8 +8,10 @@ import { getOmniChatSortQuery } from '../../../lib/inquiries'; import { getInquirySortMechanismSetting } from '../../lib/settings'; const agentDepartments = async (userId: IUser['_id']): Promise => { - const agentDepartments = (await LivechatDepartmentAgents.findByAgentId(userId).toArray()).map(({ departmentId }) => departmentId); - return (await LivechatDepartment.find({ _id: { $in: agentDepartments }, enabled: true }).toArray()).map(({ _id }) => _id); + const agentDepartments = (await LivechatDepartmentAgents.findByAgentId(userId, { projection: { departmentId: 1 } }).toArray()).map( + ({ departmentId }) => departmentId, + ); + return (await LivechatDepartment.findEnabledInIds(agentDepartments, { projection: { _id: 1 } }).toArray()).map(({ _id }) => _id); }; const applyDepartmentRestrictions = async ( diff --git a/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.ts b/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.ts index b2c168ff9602..8a5a4c280670 100644 --- a/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.ts +++ b/apps/meteor/app/livechat/server/hooks/processRoomAbandonment.ts @@ -1,4 +1,4 @@ -import type { IOmnichannelRoom, IMessage, IBusinessHourWorkHour } from '@rocket.chat/core-typings'; +import type { IOmnichannelRoom, IMessage, IBusinessHourWorkHour, ILivechatDepartment } from '@rocket.chat/core-typings'; import { isOmnichannelRoom } from '@rocket.chat/core-typings'; import { LivechatBusinessHours, LivechatDepartment, Messages, LivechatRooms } from '@rocket.chat/models'; import moment from 'moment'; @@ -27,7 +27,11 @@ const getSecondsSinceLastAgentResponse = async (room: IOmnichannelRoom, agentLas return getSecondsWhenOfficeHoursIsDisabled(room, agentLastMessage); } let officeDays; - const department = room.departmentId ? await LivechatDepartment.findOneById(room.departmentId) : null; + const department = room.departmentId + ? await LivechatDepartment.findOneById>(room.departmentId, { + projection: { businessHourId: 1 }, + }) + : null; if (department?.businessHourId) { const businessHour = await LivechatBusinessHours.findOneById(department.businessHourId); if (!businessHour) { diff --git a/apps/meteor/app/livechat/server/lib/Departments.ts b/apps/meteor/app/livechat/server/lib/Departments.ts index 9743b1b65d3f..0dd48a328fd1 100644 --- a/apps/meteor/app/livechat/server/lib/Departments.ts +++ b/apps/meteor/app/livechat/server/lib/Departments.ts @@ -1,3 +1,4 @@ +import type { ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; import { LivechatDepartment, LivechatDepartmentAgents, LivechatRooms } from '@rocket.chat/models'; @@ -24,7 +25,10 @@ class DepartmentHelperClass { } this.logger.debug(`Department record removed: ${_id}`); - const agentsIds: string[] = await LivechatDepartmentAgents.findAgentsByDepartmentId(department._id) + const agentsIds: string[] = await LivechatDepartmentAgents.findAgentsByDepartmentId>( + department._id, + { projection: { agentId: 1 } }, + ) .cursor.map((agent) => agent.agentId) .toArray(); diff --git a/apps/meteor/app/livechat/server/lib/Helper.ts b/apps/meteor/app/livechat/server/lib/Helper.ts index 5f7fad7d2fe8..75722e709b17 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.ts +++ b/apps/meteor/app/livechat/server/lib/Helper.ts @@ -12,6 +12,7 @@ import type { ILivechatDepartmentAgents, TransferByData, ILivechatAgent, + ILivechatDepartment, } from '@rocket.chat/core-typings'; import { LivechatInquiryStatus, OmnichannelSourceType, DEFAULT_SLA_CONFIG, UserStatus } from '@rocket.chat/core-typings'; import { LivechatPriorityWeight } from '@rocket.chat/core-typings/src/ILivechatPriority'; @@ -519,7 +520,9 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi if (!user) { throw new Error('error-user-is-offline'); } - const isInDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(agentId, departmentId); + const isInDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(agentId, departmentId, { + projection: { _id: 1 }, + }); if (!isInDepartment) { throw new Error('error-user-not-belong-to-department'); } @@ -549,7 +552,11 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi const { servedBy, chatQueued } = roomTaken; if (!chatQueued && oldServedBy && servedBy && oldServedBy._id === servedBy._id) { - const department = departmentId ? await LivechatDepartment.findOneById(departmentId) : null; + const department = departmentId + ? await LivechatDepartment.findOneById>(departmentId, { + projection: { fallbackForwardDepartment: 1 }, + }) + : null; if (!department?.fallbackForwardDepartment?.length) { logger.debug(`Cannot forward room ${room._id}. Chat assigned to agent ${servedBy._id} (Previous was ${oldServedBy._id})`); throw new Error('error-no-agents-online-in-department'); diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index 21de9c332ee3..3963f6ce2411 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -709,7 +709,9 @@ export const Livechat = { }); } const ret = (await LivechatDepartmentRaw.removeById(_id)).deletedCount; - const agentsIds = (await LivechatDepartmentAgents.findByDepartmentId(_id).toArray()).map((agent) => agent.agentId); + const agentsIds = (await LivechatDepartmentAgents.findByDepartmentId(_id, { projection: { agentId: 1 } }).toArray()).map( + (agent) => agent.agentId, + ); await LivechatDepartmentAgents.removeByDepartmentId(_id); await LivechatDepartmentRaw.unsetFallbackDepartmentByDepartmentId(_id); if (ret) { diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 289ac0647545..1c60a257d319 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -9,6 +9,7 @@ import type { SelectedAgent, ILivechatAgent, IMessage, + ILivechatDepartment, } from '@rocket.chat/core-typings'; import { UserStatus, isOmnichannelRoom } from '@rocket.chat/core-typings'; import { Logger, type MainLogger } from '@rocket.chat/logger'; @@ -299,7 +300,10 @@ class LivechatClass { room = null; } - if (guest.department && !(await LivechatDepartment.findOneById(guest.department))) { + if ( + guest.department && + !(await LivechatDepartment.findOneById>(guest.department, { projection: { _id: 1 } })) + ) { await LivechatVisitors.removeDepartmentById(guest._id); const tmpGuest = await LivechatVisitors.findOneById(guest._id); if (tmpGuest) { @@ -357,7 +361,9 @@ class LivechatClass { return onlineForDep; } - const dep = await LivechatDepartment.findOneById(department); + const dep = await LivechatDepartment.findOneById>(department, { + projection: { fallbackForwardDepartment: 1 }, + }); if (!dep?.fallbackForwardDepartment) { return onlineForDep; } @@ -586,7 +592,7 @@ class LivechatClass { if (department) { Livechat.logger.debug(`Attempt to find a department with id/name ${department}`); - const dep = await LivechatDepartment.findOneByIdOrName(department); + const dep = await LivechatDepartment.findOneByIdOrName(department, { projection: { _id: 1 } }); if (!dep) { Livechat.logger.debug('Invalid department provided'); throw new Meteor.Error('error-invalid-department', 'The provided department is invalid'); @@ -673,7 +679,12 @@ class LivechatClass { }; } - const department = await LivechatDepartment.findOneById(departmentId); + const department = await LivechatDepartment.findOneById>( + departmentId, + { + projection: { requestTagBeforeClosingChat: 1, chatClosingTags: 1 }, + }, + ); if (!department) { return { updatedOptions: { diff --git a/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts b/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts index 8e598ee108d3..d5ef83272550 100644 --- a/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts +++ b/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts @@ -1,4 +1,4 @@ -import type { IUser, ILivechatDepartment, IOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { IUser, IOmnichannelRoom } from '@rocket.chat/core-typings'; import { LivechatDepartmentAgents, LivechatInquiry, LivechatRooms, LivechatDepartment } from '@rocket.chat/models'; import { hasPermissionAsync } from '../../authorization/server/functions/hasPermission'; @@ -47,10 +47,10 @@ export const validators: OmnichannelRoomAccessValidator[] = [ let departmentIds; if (!(await hasRoleAsync(user._id, 'livechat-manager'))) { - const departmentAgents = (await LivechatDepartmentAgents.findByAgentId(user._id).toArray()).map((d) => d.departmentId); - departmentIds = (await LivechatDepartment.find({ _id: { $in: departmentAgents }, enabled: true }).toArray()).map( - (d: ILivechatDepartment) => d._id, + const departmentAgents = (await LivechatDepartmentAgents.findByAgentId(user._id, { projection: { departmentId: 1 } }).toArray()).map( + (d) => d.departmentId, ); + departmentIds = (await LivechatDepartment.findEnabledInIds(departmentAgents, { projection: { _id: 1 } }).toArray()).map((d) => d._id); } const filter = { @@ -75,7 +75,9 @@ export const validators: OmnichannelRoomAccessValidator[] = [ if (!room.departmentId || room.open || !user?._id) { return; } - const agentOfDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(user._id, room.departmentId); + const agentOfDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(user._id, room.departmentId, { + projection: { _id: 1 }, + }); if (!agentOfDepartment) { return; } diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index d6af563638ba..8cfe45b42232 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -122,11 +122,11 @@ export const statistics = { statistics.totalThreads = await Messages.countThreads(); // livechat visitors - statistics.totalLivechatVisitors = await LivechatVisitors.col.estimatedDocumentCount(); + statistics.totalLivechatVisitors = await LivechatVisitors.estimatedDocumentCount(); // livechat agents statistics.totalLivechatAgents = await Users.countAgents(); - statistics.totalLivechatManagers = await Users.col.countDocuments({ roles: 'livechat-manager' }); + statistics.totalLivechatManagers = await Users.countDocuments({ roles: 'livechat-manager' }); // livechat enabled statistics.livechatEnabled = settings.get('Livechat_enabled'); @@ -147,14 +147,14 @@ export const statistics = { // Number of departments statsPms.push( - LivechatDepartment.col.count().then((count) => { + LivechatDepartment.estimatedDocumentCount().then((count) => { statistics.departments = count; }), ); // Number of archived departments statsPms.push( - LivechatDepartment.col.countDocuments({ archived: true }).then((count) => { + LivechatDepartment.countArchived().then((count) => { statistics.archivedDepartments = count; }), ); diff --git a/apps/meteor/ee/app/canned-responses/server/methods/saveCannedResponse.ts b/apps/meteor/ee/app/canned-responses/server/methods/saveCannedResponse.ts index 7875b92a6606..592ddaaf01da 100644 --- a/apps/meteor/ee/app/canned-responses/server/methods/saveCannedResponse.ts +++ b/apps/meteor/ee/app/canned-responses/server/methods/saveCannedResponse.ts @@ -1,4 +1,4 @@ -import type { IOmnichannelCannedResponse } from '@rocket.chat/core-typings'; +import type { IOmnichannelCannedResponse, ILivechatDepartment } from '@rocket.chat/core-typings'; import { LivechatDepartment, CannedResponse, Users } from '@rocket.chat/models'; import type { ServerMethods } from '@rocket.chat/ui-contexts'; import { Match, check } from 'meteor/check'; @@ -76,7 +76,10 @@ Meteor.methods({ }); } - if (responseData.departmentId && !(await LivechatDepartment.findOneById(responseData.departmentId))) { + if ( + responseData.departmentId && + !(await LivechatDepartment.findOneById>(responseData.departmentId, { projection: { _id: 1 } })) + ) { throw new Meteor.Error('error-invalid-department', 'Invalid department', { method: 'saveCannedResponse', }); diff --git a/apps/meteor/ee/app/license/server/getStatistics.ts b/apps/meteor/ee/app/license/server/getStatistics.ts index 0bbaeb957ec2..d7f81e416bfd 100644 --- a/apps/meteor/ee/app/license/server/getStatistics.ts +++ b/apps/meteor/ee/app/license/server/getStatistics.ts @@ -55,7 +55,7 @@ async function getEEStatistics(): Promise { // Number of livechat tags statsPms.push( - LivechatTag.col.count().then((count) => { + LivechatTag.estimatedDocumentCount().then((count) => { statistics.livechatTags = count; return true; }), @@ -63,7 +63,7 @@ async function getEEStatistics(): Promise { // Number of canned responses statsPms.push( - CannedResponse.col.estimatedDocumentCount().then((count) => { + CannedResponse.estimatedDocumentCount().then((count) => { statistics.cannedResponses = count; return true; }), @@ -71,21 +71,21 @@ async function getEEStatistics(): Promise { // Number of Service Level Agreements statsPms.push( - OmnichannelServiceLevelAgreements.col.count().then((count) => { + OmnichannelServiceLevelAgreements.estimatedDocumentCount().then((count) => { statistics.slas = count; return true; }), ); statsPms.push( - LivechatRooms.col.countDocuments({ priorityId: { $exists: true } }).then((count) => { + LivechatRooms.countPrioritizedRooms().then((count) => { statistics.omnichannelRoomsWithPriorities = count; return true; }), ); statsPms.push( - LivechatRooms.col.countDocuments({ slaId: { $exists: true } }).then((count) => { + LivechatRooms.countRoomsWithSla().then((count) => { statistics.omnichannelRoomsWithSlas = count; return true; }), @@ -101,7 +101,7 @@ async function getEEStatistics(): Promise { statsPms.push( // Total livechat monitors - Users.col.countDocuments({ roles: 'livechat-monitor' }).then((count) => { + Users.countByRole('livechat-monitor').then((count) => { statistics.livechatMonitors = count; return true; }), @@ -109,20 +109,16 @@ async function getEEStatistics(): Promise { // Number of PDF transcript requested statsPms.push( - LivechatRooms.find({ pdfTranscriptRequested: { $exists: true } }) - .count() - .then((count) => { - statistics.omnichannelPdfTranscriptRequested = count; - }), + LivechatRooms.countRoomsWithPdfTranscriptRequested().then((count) => { + statistics.omnichannelPdfTranscriptRequested = count; + }), ); // Number of PDF transcript that succeeded statsPms.push( - LivechatRooms.find({ pdfTranscriptFileId: { $exists: true } }) - .count() - .then((count) => { - statistics.omnichannelPdfTranscriptSucceeded = count; - }), + LivechatRooms.countRoomsWithTranscriptSent().then((count) => { + statistics.omnichannelPdfTranscriptSucceeded = count; + }), ); await Promise.all(statsPms).catch(log); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/api/lib/departments.ts b/apps/meteor/ee/app/livechat-enterprise/server/api/lib/departments.ts index dfd2ceeb3333..4e7ee3b0712c 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/api/lib/departments.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/api/lib/departments.ts @@ -24,7 +24,7 @@ export const getDepartmentsWhichUserCanAccess = async (userId: string, includeDi }; export const hasAccessToDepartment = async (userId: string, departmentId: string): Promise => { - const department = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(userId, departmentId); + const department = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(userId, departmentId, { projection: { _id: 1 } }); if (department) { helperLogger.debug(`User ${userId} has access to department ${departmentId} because they are an agent`); return true; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/applySimultaneousChatsRestrictions.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/applySimultaneousChatsRestrictions.ts index 4a28aee1b768..4dd7a032e717 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/applySimultaneousChatsRestrictions.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/applySimultaneousChatsRestrictions.ts @@ -1,3 +1,4 @@ +import type { ILivechatDepartment } from '@rocket.chat/core-typings'; import { LivechatDepartment } from '@rocket.chat/models'; import { settings } from '../../../../../app/settings/server'; @@ -8,7 +9,12 @@ callbacks.add( 'livechat.applySimultaneousChatRestrictions', async (_: any, { departmentId }: { departmentId?: string } = {}) => { if (departmentId) { - const departmentLimit = (await LivechatDepartment.findOneById(departmentId))?.maxNumberSimultaneousChat || 0; + const departmentLimit = + ( + await LivechatDepartment.findOneById>(departmentId, { + projection: { maxNumberSimultaneousChats: 1 }, + }) + )?.maxNumberSimultaneousChat || 0; if (departmentLimit > 0) { cbLogger.debug(`Applying department filters. Max chats per department ${departmentLimit}`); return { $match: { 'queueInfo.chats': { $gte: Number(departmentLimit) } } }; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeForwardRoomToDepartment.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeForwardRoomToDepartment.ts index cff2653ade65..2503411d46c4 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeForwardRoomToDepartment.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeForwardRoomToDepartment.ts @@ -1,3 +1,4 @@ +import type { ILivechatDepartment } from '@rocket.chat/core-typings'; import { LivechatDepartment } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; @@ -18,7 +19,9 @@ callbacks.add( return options; } const { department: departmentToTransfer } = transferData; - const currentDepartment = await LivechatDepartment.findOneById(departmentId); + const currentDepartment = await LivechatDepartment.findOneById>(departmentId, { + projection: { departmentsAllowedToForward: 1 }, + }); if (!currentDepartment) { cbLogger.debug('Skipping callback. Current department does not exists'); return options; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.ts index e5a8325c039e..6d2071406260 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.ts @@ -1,3 +1,4 @@ +import type { ILivechatDepartment } from '@rocket.chat/core-typings'; import { LivechatDepartment, LivechatInquiry, LivechatRooms } from '@rocket.chat/models'; import { online } from '../../../../../app/livechat/server/api/lib/livechat'; @@ -16,7 +17,12 @@ callbacks.add( // check here if department has fallback before queueing if (inquiry?.department && !(await online(inquiry.department, true, true))) { cbLogger.debug('No agents online on selected department. Inquiry will use fallback department'); - const department = await LivechatDepartment.findOneById(inquiry.department); + const department = await LivechatDepartment.findOneById>( + inquiry.department, + { + projection: { fallbackForwardDepartment: 1 }, + }, + ); if (!department) { cbLogger.debug('No department found. Skipping'); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts index 3df470d31376..8947ecf62081 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.ts @@ -1,5 +1,5 @@ import { OmnichannelEEService } from '@rocket.chat/core-services'; -import type { ILivechatVisitor, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings'; +import type { ILivechatVisitor, IOmnichannelRoom, IUser, ILivechatDepartment } from '@rocket.chat/core-typings'; import { cronJobs } from '@rocket.chat/cron'; import type { MainLogger } from '@rocket.chat/logger'; import { LivechatVisitors, LivechatRooms, LivechatDepartment, Users } from '@rocket.chat/models'; @@ -78,7 +78,10 @@ export class VisitorInactivityMonitor { this.logger.debug(`Using cached department abandoned custom message for department ${departmentId}`); return this.messageCache.get(departmentId); } - const department = await LivechatDepartment.findOneById(departmentId); + const department = await LivechatDepartment.findOneById>( + departmentId, + { projection: { _id: 1, abandonedRoomsCloseCustomMessage: 1 } }, + ); if (!department) { this.logger.debug(`Department ${departmentId} not found`); return; diff --git a/apps/meteor/ee/server/models/raw/LivechatDepartment.ts b/apps/meteor/ee/server/models/raw/LivechatDepartment.ts index 61ab66d0dc89..528352df94f6 100644 --- a/apps/meteor/ee/server/models/raw/LivechatDepartment.ts +++ b/apps/meteor/ee/server/models/raw/LivechatDepartment.ts @@ -21,6 +21,7 @@ declare module '@rocket.chat/model-typings' { businessUnit: string, projection: FindOptions['projection'], ): Promise>; + findByParentId(parentId: string, options?: FindOptions): FindCursor; } } @@ -75,4 +76,8 @@ export class LivechatDepartmentEE extends LivechatDepartmentRaw implements ILive return super.findActiveByUnitIds([businessUnit], { projection }); } + + findByParentId(parentId: string, options?: FindOptions): FindCursor { + return this.col.find({ parentId }, options); + } } diff --git a/apps/meteor/ee/server/models/raw/LivechatPriority.ts b/apps/meteor/ee/server/models/raw/LivechatPriority.ts index 9fbedfe10755..850a8580391b 100644 --- a/apps/meteor/ee/server/models/raw/LivechatPriority.ts +++ b/apps/meteor/ee/server/models/raw/LivechatPriority.ts @@ -1,7 +1,7 @@ import type { ILivechatPriority } from '@rocket.chat/core-typings'; import type { ILivechatPriorityModel } from '@rocket.chat/model-typings'; import { escapeRegExp } from '@rocket.chat/string-helpers'; -import type { Db, UpdateFilter, ModifyResult, IndexDescription, FindOptions } from 'mongodb'; +import type { Db, UpdateFilter, ModifyResult, IndexDescription } from 'mongodb'; import { BaseRaw } from '../../../../server/models/raw/BaseRaw'; @@ -75,10 +75,4 @@ export class LivechatPriorityRaw extends BaseRaw implements I returnDocument: 'after', }); } - - findOneById(_id: string, options?: FindOptions): Promise { - const query = { _id }; - - return this.findOne(query, options); - } } diff --git a/apps/meteor/ee/server/models/raw/LivechatRooms.ts b/apps/meteor/ee/server/models/raw/LivechatRooms.ts index 163e04192bb9..2e1956e640a8 100644 --- a/apps/meteor/ee/server/models/raw/LivechatRooms.ts +++ b/apps/meteor/ee/server/models/raw/LivechatRooms.ts @@ -37,6 +37,10 @@ declare module '@rocket.chat/model-typings' { ): FindCursor; setPriorityByRoomId(roomId: string, priority: Pick): Promise; unsetPriorityByRoomId(roomId: string): Promise; + countPrioritizedRooms(): Promise; + countRoomsWithSla(): Promise; + countRoomsWithPdfTranscriptRequested(): Promise; + countRoomsWithTranscriptSent(): Promise; } } @@ -45,6 +49,22 @@ export class LivechatRoomsRawEE extends LivechatRoomsRaw implements ILivechatRoo super(db, trash); } + countPrioritizedRooms(): Promise { + return this.col.countDocuments({ priorityId: { $exists: true } }); + } + + countRoomsWithSla(): Promise { + return this.col.countDocuments({ slaId: { $exists: true } }); + } + + countRoomsWithPdfTranscriptRequested(): Promise { + return this.col.countDocuments({ pdfTranscriptRequested: true }); + } + + countRoomsWithTranscriptSent(): Promise { + return this.col.countDocuments({ pdfTranscriptFileId: { $exists: true } }); + } + async unsetAllPredictedVisitorAbandonment(): Promise { return this.updateMany( { diff --git a/apps/meteor/ee/server/models/raw/LivechatUnit.ts b/apps/meteor/ee/server/models/raw/LivechatUnit.ts index 525af6e239fe..b49cbb959df1 100644 --- a/apps/meteor/ee/server/models/raw/LivechatUnit.ts +++ b/apps/meteor/ee/server/models/raw/LivechatUnit.ts @@ -116,7 +116,7 @@ export class LivechatUnitRaw extends BaseRaw implement }); } - const savedDepartments = (await LivechatDepartment.find({ parentId: _id }).toArray()).map(({ _id }) => _id); + const savedDepartments = (await LivechatDepartment.findByParentId(_id, { projection: { _id: 1 } }).toArray()).map(({ _id }) => _id); const departmentsToSave = departments.map(({ departmentId }) => departmentId); // remove other departments @@ -135,7 +135,7 @@ export class LivechatUnitRaw extends BaseRaw implement } for await (const departmentId of departmentsToSave) { - await LivechatDepartment.update( + await LivechatDepartment.updateOne( { _id: departmentId }, { $set: { diff --git a/apps/meteor/server/models/raw/BaseRaw.ts b/apps/meteor/server/models/raw/BaseRaw.ts index bc74844afa37..bfa5a81ddcf0 100644 --- a/apps/meteor/server/models/raw/BaseRaw.ts +++ b/apps/meteor/server/models/raw/BaseRaw.ts @@ -424,4 +424,12 @@ export abstract class BaseRaw< watch(pipeline?: object[]): ChangeStream { return this.col.watch(pipeline); } + + countDocuments(query: Filter): Promise { + return this.col.countDocuments(query); + } + + estimatedDocumentCount(): Promise { + return this.col.estimatedDocumentCount(); + } } diff --git a/apps/meteor/server/models/raw/LivechatDepartment.ts b/apps/meteor/server/models/raw/LivechatDepartment.ts index f43db9d72740..96a0dc5c9e0e 100644 --- a/apps/meteor/server/models/raw/LivechatDepartment.ts +++ b/apps/meteor/server/models/raw/LivechatDepartment.ts @@ -164,6 +164,11 @@ export class LivechatDepartmentRaw extends BaseRaw implemen return this.find(query, options); } + findEnabledInIds(departmentsIds: string[], options?: FindOptions): FindCursor { + const query = { _id: { $in: departmentsIds }, enabled: true }; + return this.find(query, options); + } + addBusinessHourToDepartmentsByIds(ids: string[] = [], businessHourId: string): Promise { const query = { _id: { $in: ids }, @@ -433,4 +438,12 @@ export class LivechatDepartmentRaw extends BaseRaw implemen return this.col.aggregate(aggregation).hasNext(); } + + countArchived(): Promise { + return this.col.countDocuments({ archived: true }); + } + + findByParentId(_parentId: string, _options?: FindOptions | undefined): FindCursor { + throw new Error('Method not implemented in CE'); + } } diff --git a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts index 478395de91ef..ec4e60948932 100644 --- a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts +++ b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts @@ -78,8 +78,8 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw { - return this.find({ agentId }); + findByAgentId(agentId: string, options?: FindOptions): FindCursor { + return this.find({ agentId }, options); } findAgentsByDepartmentId(departmentId: string): FindPaginated>; @@ -151,12 +151,16 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw { - return this.find({ departmentId }); + findByDepartmentId(departmentId: string, options?: FindOptions): FindCursor { + return this.find({ departmentId }, options); } - findOneByAgentIdAndDepartmentId(agentId: string, departmentId: string): Promise { - return this.findOne({ agentId, departmentId }); + findOneByAgentIdAndDepartmentId( + agentId: string, + departmentId: string, + options?: FindOptions, + ): Promise { + return this.findOne({ agentId, departmentId }, options); } saveAgent(agent: { diff --git a/apps/meteor/server/models/raw/LivechatRooms.ts b/apps/meteor/server/models/raw/LivechatRooms.ts index d7bc9a1633b5..97fee0efe112 100644 --- a/apps/meteor/server/models/raw/LivechatRooms.ts +++ b/apps/meteor/server/models/raw/LivechatRooms.ts @@ -2516,4 +2516,20 @@ export class LivechatRoomsRaw extends BaseRaw implements ILive async updateDepartmentAncestorsById(_rid: string, _departmentAncestors?: string[]): Promise { throw new Error('Method not implemented.'); } + + countPrioritizedRooms(): Promise { + throw new Error('Method not implemented.'); + } + + countRoomsWithSla(): Promise { + throw new Error('Method not implemented.'); + } + + countRoomsWithPdfTranscriptRequested(): Promise { + throw new Error('Method not implemented.'); + } + + countRoomsWithTranscriptSent(): Promise { + throw new Error('Method not implemented.'); + } } diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 8edf182e3890..03c3ab2769c9 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -2996,4 +2996,8 @@ export class UsersRaw extends BaseRaw { return this.updateOne({ _id }, update); } + + countByRole(role) { + return this.col.countDocuments({ roles: role }); + } } diff --git a/ee/packages/omnichannel-services/src/OmnichannelTranscript.ts b/ee/packages/omnichannel-services/src/OmnichannelTranscript.ts index 5438a95aa2de..802a6e15d0eb 100644 --- a/ee/packages/omnichannel-services/src/OmnichannelTranscript.ts +++ b/ee/packages/omnichannel-services/src/OmnichannelTranscript.ts @@ -40,7 +40,7 @@ type MessageData = Pick & { type WorkerData = { siteName: string; - visitor: ILivechatVisitor | null; + visitor: Pick | null; agent: ILivechatAgent | undefined; closedAt?: Date; messages: MessageData[]; diff --git a/packages/model-typings/src/models/IBaseModel.ts b/packages/model-typings/src/models/IBaseModel.ts index 250fbb9d9c92..e0ae05cba0c5 100644 --- a/packages/model-typings/src/models/IBaseModel.ts +++ b/packages/model-typings/src/models/IBaseModel.ts @@ -117,4 +117,6 @@ export interface IBaseModel< ): FindPaginated>>; watch(pipeline?: object[]): ChangeStream; + countDocuments(query: Filter): Promise; + estimatedDocumentCount(): Promise; } diff --git a/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts b/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts index 99929a2fbcc5..7d8f8eda0ef4 100644 --- a/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts +++ b/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts @@ -20,7 +20,7 @@ export interface ILivechatDepartmentAgentsModel extends IBaseModel | FindOptions

, ): FindCursor | FindCursor

; - findByAgentId(agentId: string): FindCursor; + findByAgentId(agentId: string, options?: FindOptions): FindCursor; findAgentsByDepartmentId(departmentId: string): FindPaginated>; @@ -60,7 +60,12 @@ export interface ILivechatDepartmentAgentsModel extends IBaseModel; setDepartmentEnabledByDepartmentId(departmentId: string, departmentEnabled: boolean): Promise; removeByDepartmentId(departmentId: string): Promise; - findByDepartmentId(departmentId: string): FindCursor; + findByDepartmentId(departmentId: string, options?: FindOptions): FindCursor; + findOneByAgentIdAndDepartmentId( + agentId: string, + departmentId: string, + options?: FindOptions, + ): Promise; findOneByAgentIdAndDepartmentId(agentId: string, departmentId: string): Promise; saveAgent(agent: { agentId: string; diff --git a/packages/model-typings/src/models/ILivechatDepartmentModel.ts b/packages/model-typings/src/models/ILivechatDepartmentModel.ts index 7733870d0253..a074d5c31126 100644 --- a/packages/model-typings/src/models/ILivechatDepartmentModel.ts +++ b/packages/model-typings/src/models/ILivechatDepartmentModel.ts @@ -69,4 +69,6 @@ export interface ILivechatDepartmentModel extends IBaseModel; checkIfMonitorIsMonitoringDepartmentById(monitorId: string, departmentId: string): Promise; + countArchived(): Promise; + findEnabledInIds(departmentsIds: string[], options?: FindOptions): FindCursor; } diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index 3289ddc2dda8..f8eda6a3639b 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -384,4 +384,5 @@ export interface IUsersModel extends IBaseModel { query: Filter, options: FindOptions, ): Promise<{ sortedResults: (T & { departments: string[] })[]; totalCount: { total: number }[] }[]>; + countByRole(roleName: string): Promise; }