diff --git a/apps/meteor/app/apps/server/bridges/livechat.ts b/apps/meteor/app/apps/server/bridges/livechat.ts index 23a0e91411da..28f28a9ae4c9 100644 --- a/apps/meteor/app/apps/server/bridges/livechat.ts +++ b/apps/meteor/app/apps/server/bridges/livechat.ts @@ -11,10 +11,11 @@ import { IUser } from '@rocket.chat/apps-engine/definition/users'; import { IMessage } from '@rocket.chat/apps-engine/definition/messages'; import { IExtraRoomParams } from '@rocket.chat/apps-engine/definition/accessors/ILivechatCreator'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; import { getRoom } from '../../../livechat/server/api/lib/livechat'; import { Livechat } from '../../../livechat/server/lib/Livechat'; -import { Users, LivechatDepartment, LivechatVisitors, LivechatRooms } from '../../../models/server'; +import { Users, LivechatDepartment, LivechatRooms } from '../../../models/server'; import { AppServerOrchestrator } from '../orchestrator'; export class AppLivechatBridge extends LivechatBridge { @@ -216,9 +217,9 @@ export class AppLivechatBridge extends LivechatBridge { console.warn('The method AppLivechatBridge.findVisitors is deprecated. Please consider using its alternatives'); } - return LivechatVisitors.find(query) - .fetch() - .map((visitor: IVisitor) => this.orch.getConverters()?.get('visitors').convertVisitor(visitor)); + return (await LivechatVisitors.find(query).toArray()).map( + (visitor) => visitor && this.orch.getConverters()?.get('visitors').convertVisitor(visitor), + ); } protected async findVisitorById(id: string, appId: string): Promise { @@ -230,19 +231,28 @@ export class AppLivechatBridge extends LivechatBridge { protected async findVisitorByEmail(email: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is looking for livechat visitors.`); - return this.orch.getConverters()?.get('visitors').convertVisitor(LivechatVisitors.findOneGuestByEmailAddress(email)); + return this.orch + .getConverters() + ?.get('visitors') + .convertVisitor(await LivechatVisitors.findOneGuestByEmailAddress(email)); } protected async findVisitorByToken(token: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is looking for livechat visitors.`); - return this.orch.getConverters()?.get('visitors').convertVisitor(LivechatVisitors.getVisitorByToken(token, {})); + return this.orch + .getConverters() + ?.get('visitors') + .convertVisitor(await LivechatVisitors.getVisitorByToken(token, {})); } protected async findVisitorByPhoneNumber(phoneNumber: string, appId: string): Promise { this.orch.debugLog(`The App ${appId} is looking for livechat visitors.`); - return this.orch.getConverters()?.get('visitors').convertVisitor(LivechatVisitors.findOneVisitorByPhone(phoneNumber)); + return this.orch + .getConverters() + ?.get('visitors') + .convertVisitor(await LivechatVisitors.findOneVisitorByPhone(phoneNumber)); } protected async findDepartmentByIdOrName(value: string, appId: string): Promise { diff --git a/apps/meteor/app/apps/server/converters/rooms.js b/apps/meteor/app/apps/server/converters/rooms.js index 32214be6372f..4a9f6225af15 100644 --- a/apps/meteor/app/apps/server/converters/rooms.js +++ b/apps/meteor/app/apps/server/converters/rooms.js @@ -1,6 +1,7 @@ import { RoomType } from '@rocket.chat/apps-engine/definition/rooms'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { Rooms, Users, LivechatVisitors, LivechatDepartment } from '../../../models/server'; +import { Rooms, Users, LivechatDepartment } from '../../../models/server'; import { transformMappedData } from '../../lib/misc/transformMappedData'; export class AppRoomsConverter { @@ -36,7 +37,7 @@ export class AppRoomsConverter { let v; if (room.visitor) { - const visitor = LivechatVisitors.findOneById(room.visitor.id); + const visitor = Promise.await(LivechatVisitors.findOneById(room.visitor.id)); v = { _id: visitor._id, username: visitor.username, diff --git a/apps/meteor/app/apps/server/converters/visitors.js b/apps/meteor/app/apps/server/converters/visitors.js index 40c29e1c59a8..361aa3758c6a 100644 --- a/apps/meteor/app/apps/server/converters/visitors.js +++ b/apps/meteor/app/apps/server/converters/visitors.js @@ -1,19 +1,21 @@ -import LivechatVisitors from '../../../models/server/models/LivechatVisitors'; +import { LivechatVisitors } from '@rocket.chat/models'; + import { transformMappedData } from '../../lib/misc/transformMappedData'; +// TODO: check if functions from this converter can be async export class AppVisitorsConverter { constructor(orch) { this.orch = orch; } convertById(id) { - const visitor = LivechatVisitors.findOneById(id); + const visitor = Promise.await(LivechatVisitors.findOneById(id)); return this.convertVisitor(visitor); } convertByToken(token) { - const visitor = LivechatVisitors.getVisitorByToken(token); + const visitor = Promise.await(LivechatVisitors.getVisitorByToken(token)); return this.convertVisitor(visitor); } diff --git a/apps/meteor/app/livechat/imports/server/rest/facebook.js b/apps/meteor/app/livechat/imports/server/rest/facebook.js index 829e92d56930..db8f74ea264c 100644 --- a/apps/meteor/app/livechat/imports/server/rest/facebook.js +++ b/apps/meteor/app/livechat/imports/server/rest/facebook.js @@ -1,9 +1,10 @@ import crypto from 'crypto'; import { Random } from 'meteor/random'; +import { LivechatVisitors } from '@rocket.chat/models'; import { API } from '../../../../api/server'; -import { LivechatRooms, LivechatVisitors } from '../../../../models/server'; +import { LivechatRooms } from '../../../../models/server'; import { settings } from '../../../../settings/server'; import { Livechat } from '../../../server/lib/Livechat'; @@ -21,7 +22,7 @@ import { Livechat } from '../../../server/lib/Livechat'; * @apiParam {String} [attachments] Facebook message attachments */ API.v1.addRoute('livechat/facebook', { - post() { + async post() { if (!this.bodyParams.text && !this.bodyParams.attachments) { return { success: false, @@ -63,7 +64,7 @@ API.v1.addRoute('livechat/facebook', { }, }, }; - let visitor = LivechatVisitors.getVisitorByToken(this.bodyParams.token); + let visitor = await LivechatVisitors.getVisitorByToken(this.bodyParams.token); if (visitor) { const rooms = LivechatRooms.findOpenByVisitorToken(visitor.token).fetch(); if (rooms && rooms.length > 0) { @@ -76,12 +77,12 @@ API.v1.addRoute('livechat/facebook', { sendMessage.message.rid = Random.id(); sendMessage.message.token = this.bodyParams.token; - const userId = Livechat.registerGuest({ + const userId = await Livechat.registerGuest({ token: sendMessage.message.token, name: `${this.bodyParams.first_name} ${this.bodyParams.last_name}`, }); - visitor = LivechatVisitors.findOneById(userId); + visitor = await LivechatVisitors.findOneById(userId); } sendMessage.message.msg = this.bodyParams.text; diff --git a/apps/meteor/app/livechat/imports/server/rest/sms.js b/apps/meteor/app/livechat/imports/server/rest/sms.js index 3447870082b8..1756325757df 100644 --- a/apps/meteor/app/livechat/imports/server/rest/sms.js +++ b/apps/meteor/app/livechat/imports/server/rest/sms.js @@ -1,9 +1,10 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; import { FileUpload } from '../../../../file-upload/server'; -import { LivechatRooms, LivechatVisitors, LivechatDepartment } from '../../../../models/server'; +import { LivechatRooms, LivechatDepartment } from '../../../../models/server'; import { API } from '../../../../api/server'; import { fetch } from '../../../../../server/lib/http/fetch'; import { SMS } from '../../../../sms'; @@ -34,7 +35,7 @@ const defineDepartment = (idOrName) => { return department && department._id; }; -const defineVisitor = (smsNumber, targetDepartment) => { +const defineVisitor = async (smsNumber, targetDepartment) => { const visitor = LivechatVisitors.findOneVisitorByPhone(smsNumber); let data = { token: (visitor && visitor.token) || Random.id(), @@ -53,7 +54,7 @@ const defineVisitor = (smsNumber, targetDepartment) => { data.department = targetDepartment; } - const id = Livechat.registerGuest(data); + const id = await Livechat.registerGuest(data); return LivechatVisitors.findOneById(id); }; @@ -79,7 +80,7 @@ API.v1.addRoute('livechat/sms-incoming/:service', { targetDepartment = defineDepartment(SMS.department); } - const visitor = defineVisitor(sms.from, targetDepartment); + const visitor = await defineVisitor(sms.from, targetDepartment); const { token } = visitor; const room = LivechatRooms.findOneOpenByVisitorTokenAndDepartmentId(token, targetDepartment); const roomExists = !!room; diff --git a/apps/meteor/app/livechat/imports/server/rest/upload.js b/apps/meteor/app/livechat/imports/server/rest/upload.js index 7947ac2d9826..5057c5e7e006 100644 --- a/apps/meteor/app/livechat/imports/server/rest/upload.js +++ b/apps/meteor/app/livechat/imports/server/rest/upload.js @@ -1,8 +1,9 @@ import { Meteor } from 'meteor/meteor'; import filesize from 'filesize'; +import { LivechatVisitors } from '@rocket.chat/models'; import { settings } from '../../../../settings/server'; -import { Settings, LivechatRooms, LivechatVisitors } from '../../../../models/server'; +import { Settings, LivechatRooms } from '../../../../models/server'; import { fileUploadIsValidContentType } from '../../../../utils/server'; import { FileUpload } from '../../../../file-upload'; import { API } from '../../../../api/server'; @@ -19,13 +20,13 @@ settings.watch('FileUpload_MaxFileSize', function (value) { }); API.v1.addRoute('livechat/upload/:rid', { - post() { + async post() { if (!this.request.headers['x-visitor-token']) { return API.v1.unauthorized(); } const visitorToken = this.request.headers['x-visitor-token']; - const visitor = LivechatVisitors.getVisitorByToken(visitorToken); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken); if (!visitor) { return API.v1.unauthorized(); @@ -36,13 +37,11 @@ API.v1.addRoute('livechat/upload/:rid', { return API.v1.unauthorized(); } - const [file, fields] = Promise.await( - getUploadFormData( - { - request: this.request, - }, - { field: 'file' }, - ), + const [file, fields] = await getUploadFormData( + { + request: this.request, + }, + { field: 'file' }, ); if (!fileUploadIsValidContentType(file.mimetype)) { diff --git a/apps/meteor/app/livechat/server/api/lib/livechat.js b/apps/meteor/app/livechat/server/api/lib/livechat.js index b1d45ac58892..92de8a4ffa9b 100644 --- a/apps/meteor/app/livechat/server/api/lib/livechat.js +++ b/apps/meteor/app/livechat/server/api/lib/livechat.js @@ -1,9 +1,9 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import { EmojiCustom, LivechatTrigger } from '@rocket.chat/models'; +import { EmojiCustom, LivechatTrigger, LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, LivechatVisitors, LivechatDepartment } from '../../../../models/server'; +import { LivechatRooms, LivechatDepartment } from '../../../../models/server'; import { Livechat } from '../../lib/Livechat'; import { callbacks } from '../../../../../lib/callbacks'; import { normalizeAgent } from '../../lib/Helper'; @@ -40,7 +40,7 @@ export function findDepartments(businessUnit) { export function findGuest(token) { return LivechatVisitors.getVisitorByToken(token, { - fields: { + projection: { name: 1, username: 1, token: 1, diff --git a/apps/meteor/app/livechat/server/api/v1/agent.js b/apps/meteor/app/livechat/server/api/v1/agent.js index 7e2329058542..104f0bc012e3 100644 --- a/apps/meteor/app/livechat/server/api/v1/agent.js +++ b/apps/meteor/app/livechat/server/api/v1/agent.js @@ -6,14 +6,14 @@ import { findRoom, findGuest, findAgent, findOpenRoom } from '../lib/livechat'; import { Livechat } from '../../lib/Livechat'; API.v1.addRoute('livechat/agent.info/:rid/:token', { - get() { + async get() { try { check(this.urlParams, { rid: String, token: String, }); - const visitor = findGuest(this.urlParams.token); + const visitor = await findGuest(this.urlParams.token); if (!visitor) { throw new Meteor.Error('invalid-token'); } diff --git a/apps/meteor/app/livechat/server/api/v1/contact.js b/apps/meteor/app/livechat/server/api/v1/contact.js index be4e5670338d..af43442bdcf7 100644 --- a/apps/meteor/app/livechat/server/api/v1/contact.js +++ b/apps/meteor/app/livechat/server/api/v1/contact.js @@ -1,15 +1,15 @@ import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { LivechatVisitors } from '@rocket.chat/models'; import { API } from '../../../../api/server'; import { Contacts } from '../../lib/Contacts'; -import { LivechatVisitors } from '../../../../models/server'; API.v1.addRoute( 'omnichannel/contact', { authRequired: true }, { - post() { + async post() { try { check(this.bodyParams, { _id: Match.Maybe(String), @@ -23,19 +23,19 @@ API.v1.addRoute( }), }); - const contact = Contacts.registerContact(this.bodyParams); + const contact = await Contacts.registerContact(this.bodyParams); return API.v1.success({ contact }); } catch (e) { return API.v1.failure(e); } }, - get() { + async get() { check(this.queryParams, { contactId: String, }); - const contact = Promise.await(LivechatVisitors.findOneById(this.queryParams.contactId)); + const contact = await LivechatVisitors.findOneById(this.queryParams.contactId); return API.v1.success({ contact }); }, @@ -46,7 +46,7 @@ API.v1.addRoute( 'omnichannel/contact.search', { authRequired: true }, { - get() { + async get() { try { check(this.queryParams, { email: Match.Maybe(String), @@ -67,7 +67,7 @@ API.v1.addRoute( }, ); - const contact = Promise.await(LivechatVisitors.findOne(query)); + const contact = await LivechatVisitors.findOne(query); return API.v1.success({ contact }); } catch (e) { return API.v1.failure(e); diff --git a/apps/meteor/app/livechat/server/api/v1/customField.js b/apps/meteor/app/livechat/server/api/v1/customField.js index b15ded04a5d0..5d242f3d00ad 100644 --- a/apps/meteor/app/livechat/server/api/v1/customField.js +++ b/apps/meteor/app/livechat/server/api/v1/customField.js @@ -7,7 +7,7 @@ import { Livechat } from '../../lib/Livechat'; import { findLivechatCustomFields, findCustomFieldById } from '../lib/customFields'; API.v1.addRoute('livechat/custom.field', { - post() { + async post() { try { check(this.bodyParams, { token: String, @@ -18,12 +18,12 @@ API.v1.addRoute('livechat/custom.field', { const { token, key, value, overwrite } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } - if (!Livechat.setCustomFields({ token, key, value, overwrite })) { + if (!(await Livechat.setCustomFields({ token, key, value, overwrite }))) { return API.v1.failure(); } @@ -35,7 +35,7 @@ API.v1.addRoute('livechat/custom.field', { }); API.v1.addRoute('livechat/custom.fields', { - post() { + async post() { check(this.bodyParams, { token: String, customFields: [ @@ -48,19 +48,21 @@ API.v1.addRoute('livechat/custom.fields', { }); const { token } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } - const fields = this.bodyParams.customFields.map((customField) => { - const data = Object.assign({ token }, customField); - if (!Livechat.setCustomFields(data)) { - return API.v1.failure(); - } + const fields = await Promise.all( + this.bodyParams.customFields.map(async (customField) => { + const data = Object.assign({ token }, customField); + if (!(await Livechat.setCustomFields(data))) { + return API.v1.failure(); + } - return { Key: customField.key, value: customField.value, overwrite: customField.overwrite }; - }); + return { Key: customField.key, value: customField.value, overwrite: customField.overwrite }; + }), + ); return API.v1.success({ fields }); }, diff --git a/apps/meteor/app/livechat/server/api/v1/message.js b/apps/meteor/app/livechat/server/api/v1/message.js index 78fda9d95d60..2d57ca7f5724 100644 --- a/apps/meteor/app/livechat/server/api/v1/message.js +++ b/apps/meteor/app/livechat/server/api/v1/message.js @@ -2,8 +2,9 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { Random } from 'meteor/random'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { Messages, LivechatRooms, LivechatVisitors } from '../../../../models/server'; +import { Messages, LivechatRooms } from '../../../../models/server'; import { hasPermission } from '../../../../authorization'; import { API } from '../../../../api/server'; import { loadMessageHistory } from '../../../../lib'; @@ -13,7 +14,7 @@ import { normalizeMessageFileUpload } from '../../../../utils/server/functions/n import { settings } from '../../../../settings/server'; API.v1.addRoute('livechat/message', { - post() { + async post() { try { check(this.bodyParams, { _id: Match.Maybe(String), @@ -28,7 +29,7 @@ API.v1.addRoute('livechat/message', { const { token, rid, agent, msg } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -67,7 +68,7 @@ API.v1.addRoute('livechat/message', { }, }; - const result = Promise.await(Livechat.sendMessage(sendMessage)); + const result = await Livechat.sendMessage(sendMessage); if (result) { const message = Messages.findOneById(_id); return API.v1.success({ message }); @@ -81,7 +82,7 @@ API.v1.addRoute('livechat/message', { }); API.v1.addRoute('livechat/message/:_id', { - get() { + async get() { try { check(this.urlParams, { _id: String, @@ -95,7 +96,7 @@ API.v1.addRoute('livechat/message/:_id', { const { token, rid } = this.queryParams; const { _id } = this.urlParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -111,7 +112,7 @@ API.v1.addRoute('livechat/message/:_id', { } if (message.file) { - message = Promise.await(normalizeMessageFileUpload(message)); + message = await normalizeMessageFileUpload(message); } return API.v1.success({ message }); @@ -120,7 +121,7 @@ API.v1.addRoute('livechat/message/:_id', { } }, - put() { + async put() { try { check(this.urlParams, { _id: String, @@ -135,7 +136,7 @@ API.v1.addRoute('livechat/message/:_id', { const { token, rid } = this.bodyParams; const { _id } = this.urlParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -157,7 +158,7 @@ API.v1.addRoute('livechat/message/:_id', { if (result) { let message = Messages.findOneById(_id); if (message.file) { - message = Promise.await(normalizeMessageFileUpload(message)); + message = await normalizeMessageFileUpload(message); } return API.v1.success({ message }); @@ -168,7 +169,7 @@ API.v1.addRoute('livechat/message/:_id', { return API.v1.failure(e); } }, - delete() { + async delete() { try { check(this.urlParams, { _id: String, @@ -182,7 +183,7 @@ API.v1.addRoute('livechat/message/:_id', { const { token, rid } = this.bodyParams; const { _id } = this.urlParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -197,7 +198,7 @@ API.v1.addRoute('livechat/message/:_id', { throw new Meteor.Error('invalid-message'); } - const result = Promise.await(Livechat.deleteMessage({ guest, message })); + const result = await Livechat.deleteMessage({ guest, message }); if (result) { return API.v1.success({ message: { @@ -215,7 +216,7 @@ API.v1.addRoute('livechat/message/:_id', { }); API.v1.addRoute('livechat/messages.history/:rid', { - get() { + async get() { try { check(this.urlParams, { rid: String, @@ -230,7 +231,7 @@ API.v1.addRoute('livechat/messages.history/:rid', { throw new Meteor.Error('error-token-param-not-provided', 'The required "token" query param is missing.'); } - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -276,7 +277,7 @@ API.v1.addRoute( 'livechat/messages', { authRequired: true }, { - post() { + async post() { if (!hasPermission(this.userId, 'view-livechat-manager')) { return API.v1.unauthorized(); } @@ -299,7 +300,7 @@ API.v1.addRoute( const visitorToken = this.bodyParams.visitor.token; - let visitor = LivechatVisitors.getVisitorByToken(visitorToken); + let visitor = await LivechatVisitors.getVisitorByToken(visitorToken); let rid; if (visitor) { const rooms = LivechatRooms.findOpenByVisitorToken(visitorToken).fetch(); @@ -314,8 +315,8 @@ API.v1.addRoute( const guest = this.bodyParams.visitor; guest.connectionData = normalizeHttpHeaderData(this.request.headers); - const visitorId = Livechat.registerGuest(guest); - visitor = LivechatVisitors.findOneById(visitorId); + const visitorId = await Livechat.registerGuest(guest); + visitor = await LivechatVisitors.findOneById(visitorId); } const sentMessages = this.bodyParams.messages.map((message) => { diff --git a/apps/meteor/app/livechat/server/api/v1/room.js b/apps/meteor/app/livechat/server/api/v1/room.js index 81a6ebfa9a87..6a0f991795ae 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.js +++ b/apps/meteor/app/livechat/server/api/v1/room.js @@ -15,7 +15,7 @@ import { canAccessRoom } from '../../../../authorization/server'; import { addUserToRoom } from '../../../../lib/server/functions'; API.v1.addRoute('livechat/room', { - get() { + async get() { const defaultCheckParams = { token: String, rid: Match.Maybe(String), @@ -28,7 +28,7 @@ API.v1.addRoute('livechat/room', { const { token, rid: roomId, agentId, ...extraParams } = this.queryParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -54,7 +54,7 @@ API.v1.addRoute('livechat/room', { }, }; - room = Promise.await(getRoom({ guest, rid, agent, roomInfo, extraParams })); + room = await getRoom({ guest, rid, agent, roomInfo, extraParams }); return API.v1.success(room); } @@ -68,7 +68,7 @@ API.v1.addRoute('livechat/room', { }); API.v1.addRoute('livechat/room.close', { - post() { + async post() { try { check(this.bodyParams, { rid: String, @@ -77,7 +77,7 @@ API.v1.addRoute('livechat/room.close', { const { rid, token } = this.bodyParams; - const visitor = findGuest(token); + const visitor = await findGuest(token); if (!visitor) { throw new Meteor.Error('invalid-token'); } @@ -106,7 +106,7 @@ API.v1.addRoute('livechat/room.close', { }); API.v1.addRoute('livechat/room.transfer', { - post() { + async post() { try { check(this.bodyParams, { rid: String, @@ -116,7 +116,7 @@ API.v1.addRoute('livechat/room.transfer', { const { rid, token, department } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -132,7 +132,7 @@ API.v1.addRoute('livechat/room.transfer', { const { _id, username, name } = guest; const transferredBy = normalizeTransferredByData({ _id, username, name, userType: 'visitor' }, room); - if (!Promise.await(Livechat.transfer(room, guest, { roomId: rid, departmentId: department, transferredBy }))) { + if (!(await Livechat.transfer(room, guest, { roomId: rid, departmentId: department, transferredBy }))) { return API.v1.failure(); } @@ -145,7 +145,7 @@ API.v1.addRoute('livechat/room.transfer', { }); API.v1.addRoute('livechat/room.survey', { - post() { + async post() { try { check(this.bodyParams, { rid: String, @@ -160,7 +160,7 @@ API.v1.addRoute('livechat/room.survey', { const { rid, token, data } = this.bodyParams; - const visitor = findGuest(token); + const visitor = await findGuest(token); if (!visitor) { throw new Meteor.Error('invalid-token'); } @@ -170,7 +170,7 @@ API.v1.addRoute('livechat/room.survey', { throw new Meteor.Error('invalid-room'); } - const config = Promise.await(settings()); + const config = await settings(); if (!config.survey || !config.survey.items || !config.survey.values) { throw new Meteor.Error('invalid-livechat-config'); } diff --git a/apps/meteor/app/livechat/server/api/v1/transcript.js b/apps/meteor/app/livechat/server/api/v1/transcript.js index f8f3c923d25e..040bb51a0f65 100644 --- a/apps/meteor/app/livechat/server/api/v1/transcript.js +++ b/apps/meteor/app/livechat/server/api/v1/transcript.js @@ -5,7 +5,7 @@ import { API } from '../../../../api/server'; import { Livechat } from '../../lib/Livechat'; API.v1.addRoute('livechat/transcript', { - post() { + async post() { try { check(this.bodyParams, { token: String, @@ -14,7 +14,7 @@ API.v1.addRoute('livechat/transcript', { }); const { token, rid, email } = this.bodyParams; - if (!Livechat.sendTranscript({ token, rid, email })) { + if (!(await Livechat.sendTranscript({ token, rid, email }))) { return API.v1.failure({ message: TAPi18n.__('Error_sending_livechat_transcript') }); } diff --git a/apps/meteor/app/livechat/server/api/v1/videoCall.js b/apps/meteor/app/livechat/server/api/v1/videoCall.js index 74dce685b66d..efca6c1a06ff 100644 --- a/apps/meteor/app/livechat/server/api/v1/videoCall.js +++ b/apps/meteor/app/livechat/server/api/v1/videoCall.js @@ -15,7 +15,7 @@ import { Logger } from '../../../../logger'; const logger = new Logger('LivechatVideoCallApi'); API.v1.addRoute('livechat/video.call/:token', { - get() { + async get() { try { check(this.urlParams, { token: String, @@ -27,7 +27,7 @@ API.v1.addRoute('livechat/video.call/:token', { const { token } = this.urlParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -40,8 +40,8 @@ API.v1.addRoute('livechat/video.call/:token', { alias: 'video-call', }, }; - const { room } = getRoom({ guest, rid, roomInfo }); - const config = Promise.await(settings()); + const { room } = await getRoom({ guest, rid, roomInfo }); + const config = await settings(); if (!config.theme || !config.theme.actionLinks || !config.theme.actionLinks.jitsi) { throw new Meteor.Error('invalid-livechat-config'); } diff --git a/apps/meteor/app/livechat/server/api/v1/visitor.ts b/apps/meteor/app/livechat/server/api/v1/visitor.ts index ca21e3ee26d3..9226926f94a9 100644 --- a/apps/meteor/app/livechat/server/api/v1/visitor.ts +++ b/apps/meteor/app/livechat/server/api/v1/visitor.ts @@ -3,7 +3,7 @@ import { Match, check } from 'meteor/check'; import type { ILivechatVisitorDTO, IRoom } from '@rocket.chat/core-typings'; import { LivechatVisitors as VisitorsRaw } from '@rocket.chat/models'; -import { LivechatRooms, LivechatVisitors, LivechatCustomField } from '../../../../models/server'; +import { LivechatRooms, LivechatCustomField } from '../../../../models/server'; import { API } from '../../../../api/server'; import { findGuest, normalizeHttpHeaderData } from '../lib/livechat'; import { Livechat } from '../../lib/Livechat'; @@ -37,7 +37,7 @@ API.v1.addRoute('livechat/visitor', { } guest.connectionData = normalizeHttpHeaderData(this.request.headers); - const visitorId = Livechat.registerGuest(guest as any); // TODO: Rewrite Livechat to TS + const visitorId = await Livechat.registerGuest(guest as any); // TODO: Rewrite Livechat to TS let visitor = await VisitorsRaw.findOneById(visitorId, {}); // If it's updating an existing visitor, it must also update the roomInfo @@ -55,7 +55,8 @@ API.v1.addRoute('livechat/visitor', { return; } const { key, value, overwrite } = field; - if (customField.scope === 'visitor' && !LivechatVisitors.updateLivechatDataByToken(token, key, value, overwrite)) { + // TODO: refactor this to use normal await + if (customField.scope === 'visitor' && !Promise.await(VisitorsRaw.updateLivechatDataByToken(token, key, value, overwrite))) { return API.v1.failure(); } }); @@ -112,7 +113,7 @@ API.v1.addRoute('livechat/visitor/:token', { } const { _id } = visitor; - const result = Livechat.removeGuest(_id); + const result = await Livechat.removeGuest(_id); if (!result) { throw new Meteor.Error('error-removing-visitor', 'An error ocurred while deleting visitor'); } @@ -156,7 +157,7 @@ API.v1.addRoute('livechat/visitor.callStatus', { }); const { token, callStatus, rid, callId } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } @@ -174,7 +175,7 @@ API.v1.addRoute('livechat/visitor.status', { const { token, status } = this.bodyParams; - const guest = findGuest(token); + const guest = await findGuest(token); if (!guest) { throw new Meteor.Error('invalid-token'); } diff --git a/apps/meteor/app/livechat/server/hooks/leadCapture.js b/apps/meteor/app/livechat/server/hooks/leadCapture.js index 156edc32c6f2..6a1d3b5b67b9 100644 --- a/apps/meteor/app/livechat/server/hooks/leadCapture.js +++ b/apps/meteor/app/livechat/server/hooks/leadCapture.js @@ -1,6 +1,7 @@ +import { LivechatVisitors } from '@rocket.chat/models'; + import { callbacks } from '../../../../lib/callbacks'; import { settings } from '../../../settings/server'; -import { LivechatVisitors } from '../../../models/server'; function validateMessage(message, room) { // skips this callback if the message was edited @@ -40,7 +41,7 @@ callbacks.add( const msgEmails = message.msg.match(emailRegexp); if (msgEmails || msgPhones) { - LivechatVisitors.saveGuestEmailPhoneById(room.v._id, msgEmails, msgPhones); + Promise.await(LivechatVisitors.saveGuestEmailPhoneById(room.v._id, msgEmails, msgPhones)); callbacks.run('livechat.leadCapture', room); } diff --git a/apps/meteor/app/livechat/server/hooks/saveContactLastChat.js b/apps/meteor/app/livechat/server/hooks/saveContactLastChat.js index 0d87723b7079..9745b09930e3 100644 --- a/apps/meteor/app/livechat/server/hooks/saveContactLastChat.js +++ b/apps/meteor/app/livechat/server/hooks/saveContactLastChat.js @@ -13,7 +13,7 @@ callbacks.add( _id, ts: new Date(), }; - Livechat.updateLastChat(guestId, lastChat); + Promise.await(Livechat.updateLastChat(guestId, lastChat)); }, callbacks.priority.MEDIUM, 'livechat-save-last-chat', diff --git a/apps/meteor/app/livechat/server/hooks/sendToCRM.js b/apps/meteor/app/livechat/server/hooks/sendToCRM.js index 3a6ffce3a64c..c54b761717c9 100644 --- a/apps/meteor/app/livechat/server/hooks/sendToCRM.js +++ b/apps/meteor/app/livechat/server/hooks/sendToCRM.js @@ -41,7 +41,7 @@ function sendToCRM(type, room, includeMessages = true) { return room; } - const postData = Livechat.getLivechatRoomGuestInfo(room); + const postData = Promise.await(Livechat.getLivechatRoomGuestInfo(room)); postData.type = type; diff --git a/apps/meteor/app/livechat/server/hooks/sendTranscriptOnClose.js b/apps/meteor/app/livechat/server/hooks/sendTranscriptOnClose.js index cfa17e4ac039..597d656b2f0e 100644 --- a/apps/meteor/app/livechat/server/hooks/sendTranscriptOnClose.js +++ b/apps/meteor/app/livechat/server/hooks/sendTranscriptOnClose.js @@ -9,7 +9,8 @@ const sendTranscriptOnClose = (room) => { } const { email, subject, requestedBy: user } = transcriptRequest; - Livechat.sendTranscript({ token, rid, email, subject, user }); + // TODO: refactor this to use normal await + Promise.await(Livechat.sendTranscript({ token, rid, email, subject, user })); LivechatRooms.removeTranscriptRequestByRoomId(rid); diff --git a/apps/meteor/app/livechat/server/lib/Contacts.js b/apps/meteor/app/livechat/server/lib/Contacts.js index 27fc010786a0..df4b05a9904f 100644 --- a/apps/meteor/app/livechat/server/lib/Contacts.js +++ b/apps/meteor/app/livechat/server/lib/Contacts.js @@ -1,19 +1,19 @@ import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import s from 'underscore.string'; -import { Users } from '@rocket.chat/models'; +import { LivechatVisitors, Users } from '@rocket.chat/models'; -import { LivechatVisitors, LivechatCustomField, LivechatRooms, Rooms, LivechatInquiry, Subscriptions } from '../../../models/server'; +import { LivechatCustomField, LivechatRooms, Rooms, LivechatInquiry, Subscriptions } from '../../../models/server'; export const Contacts = { - registerContact({ token, name, email, phone, username, customFields = {}, contactManager = {} } = {}) { + async registerContact({ token, name, email, phone, username, customFields = {}, contactManager = {} } = {}) { check(token, String); const visitorEmail = s.trim(email).toLowerCase(); if (contactManager?.username) { // verify if the user exists with this username and has a livechat-agent role - const user = Promise.await(Users.findOneByUsername(contactManager.username, { projection: { roles: 1 } })); + const user = await Users.findOneByUsername(contactManager.username, { projection: { roles: 1 } }); if (!user) { throw new Meteor.Error('error-contact-manager-not-found', `No user found with username ${contactManager.username}`); } @@ -29,18 +29,18 @@ export const Contacts = { }, }; - const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + const user = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); if (user) { contactId = user._id; } else { if (!username) { - username = LivechatVisitors.getNextVisitorUsername(); + username = await LivechatVisitors.getNextVisitorUsername(); } let existingUser = null; - if (visitorEmail !== '' && (existingUser = LivechatVisitors.findOneGuestByEmailAddress(visitorEmail))) { + if (visitorEmail !== '' && (existingUser = await LivechatVisitors.findOneGuestByEmailAddress(visitorEmail))) { contactId = existingUser._id; } else { const userData = { @@ -48,7 +48,7 @@ export const Contacts = { ts: new Date(), }; - contactId = LivechatVisitors.insert(userData); + contactId = await LivechatVisitors.insertOne(userData); } } @@ -68,7 +68,7 @@ export const Contacts = { updateUser.$set.livechatData = livechatData; updateUser.$set.contactManager = (contactManager?.username && { username: contactManager.username }) || null; - LivechatVisitors.updateById(contactId, updateUser); + await LivechatVisitors.updateById(contactId, updateUser); const rooms = LivechatRooms.findByVisitorId(contactId).fetch(); diff --git a/apps/meteor/app/livechat/server/lib/Helper.js b/apps/meteor/app/livechat/server/lib/Helper.js index 2a050560c10b..3a51f2343d1e 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.js +++ b/apps/meteor/app/livechat/server/lib/Helper.js @@ -531,7 +531,7 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => { } const { token } = guest; - Livechat.setDepartmentForGuest({ token, department: departmentId }); + await Livechat.setDepartmentForGuest({ token, department: departmentId }); logger.debug(`Department for visitor with token ${token} was updated to ${departmentId}`); return true; diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index 93ffbffd5c2a..424847dddb70 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -9,7 +9,7 @@ import _ from 'underscore'; import s from 'underscore.string'; import moment from 'moment-timezone'; import UAParser from 'ua-parser-js'; -import { Users as UsersRaw, LivechatVisitors as LivechatVisitorsRaw } from '@rocket.chat/models'; +import { Users as UsersRaw, LivechatVisitors } from '@rocket.chat/models'; import { QueueManager } from './QueueManager'; import { RoutingManager } from './RoutingManager'; @@ -27,7 +27,6 @@ import { LivechatDepartmentAgents, LivechatDepartment, LivechatCustomField, - LivechatVisitors, LivechatInquiry, } from '../../../models/server'; import { Logger } from '../../../logger/server'; @@ -172,8 +171,8 @@ export const Livechat = { } if (guest.department && !LivechatDepartment.findOneById(guest.department)) { - LivechatVisitors.removeDepartmentById(guest._id); - guest = LivechatVisitors.findOneById(guest._id); + await LivechatVisitors.removeDepartmentById(guest._id); + guest = await LivechatVisitors.findOneById(guest._id); } if (room == null) { @@ -272,7 +271,7 @@ export const Livechat = { return true; }, - registerGuest({ id, token, name, email, department, phone, username, connectionData, status = 'online' } = {}) { + async registerGuest({ id, token, name, email, department, phone, username, connectionData, status = 'online' } = {}) { check(token, String); check(id, Match.Maybe(String)); @@ -307,24 +306,24 @@ export const Livechat = { updateUser.$set.department = dep._id; } - const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + const user = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); let existingUser = null; if (user) { Livechat.logger.debug('Found matching user by token'); userId = user._id; - } else if (phone?.number && (existingUser = LivechatVisitors.findOneVisitorByPhone(phone.number))) { + } else if (phone?.number && (existingUser = await LivechatVisitors.findOneVisitorByPhone(phone.number))) { Livechat.logger.debug('Found matching user by phone number'); userId = existingUser._id; // Don't change token when matching by phone number, use current visitor token updateUser.$set.token = existingUser.token; - } else if (email && (existingUser = LivechatVisitors.findOneGuestByEmailAddress(email))) { + } else if (email && (existingUser = await LivechatVisitors.findOneGuestByEmailAddress(email))) { Livechat.logger.debug('Found matching user by email'); userId = existingUser._id; } else { Livechat.logger.debug(`No matches found. Attempting to create new user with token ${token}`); if (!username) { - username = LivechatVisitors.getNextVisitorUsername(); + username = await LivechatVisitors.getNextVisitorUsername(); } const userData = { @@ -344,15 +343,15 @@ export const Livechat = { } } - userId = LivechatVisitors.insert(userData); + userId = (await LivechatVisitors.insertOne(userData)).insertedId; } - LivechatVisitors.updateById(userId, updateUser); + await LivechatVisitors.updateById(userId, updateUser); return userId; }, - setDepartmentForGuest({ token, department } = {}) { + async setDepartmentForGuest({ token, department } = {}) { check(token, String); check(department, String); @@ -371,14 +370,14 @@ export const Livechat = { }); } - const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + const user = await LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); if (user) { return LivechatVisitors.updateById(user._id, updateUser); } return false; }, - saveGuest({ _id, name, email, phone, livechatData = {} }, userId) { + async saveGuest({ _id, name, email, phone, livechatData = {} }, userId) { Livechat.logger.debug(`Saving data for visitor ${_id}`); const updateData = {}; @@ -411,7 +410,7 @@ export const Livechat = { }); updateData.livechatData = customFields; } - const ret = LivechatVisitors.saveGuestById(_id, updateData); + const ret = await LivechatVisitors.saveGuestById(_id, updateData); Meteor.defer(() => { Apps.triggerEvent(AppEvents.IPostLivechatGuestSaved, _id); @@ -506,7 +505,7 @@ export const Livechat = { return LivechatRooms.removeById(rid); }, - setCustomFields({ token, key, value, overwrite } = {}) { + async setCustomFields({ token, key, value, overwrite } = {}) { check(token, String); check(key, String); check(value, String); @@ -636,7 +635,8 @@ export const Livechat = { forwardOpenChats(userId) { Livechat.logger.debug(`Transferring open chats for user ${userId}`); LivechatRooms.findOpenByAgent(userId).forEach((room) => { - const guest = LivechatVisitors.findOneById(room.v._id); + // TODO: refactor to use normal await + const guest = Promise.await(LivechatVisitors.findOneById(room.v._id)); const user = Users.findOneById(userId); const { _id, username, name } = user; const transferredBy = normalizeTransferredByData({ _id, username, name }, room); @@ -811,8 +811,8 @@ export const Livechat = { } }, - getLivechatRoomGuestInfo(room) { - const visitor = LivechatVisitors.findOneById(room.v._id); + async getLivechatRoomGuestInfo(room) { + const visitor = await LivechatVisitors.findOneById(room.v._id); const agent = Users.findOneById(room.servedBy && room.servedBy._id); const ua = new UAParser(); @@ -925,7 +925,7 @@ export const Livechat = { Users.removeLivechatData(_id); this.setUserStatusLivechat(_id, 'not-available'); LivechatDepartmentAgents.removeByAgentId(_id); - Promise.await(LivechatVisitorsRaw.removeContactManagerByUsername(username)); + Promise.await(LivechatVisitors.removeContactManagerByUsername(username)); return true; } @@ -946,16 +946,16 @@ export const Livechat = { return removeUserFromRoles(user._id, ['livechat-manager']); }, - removeGuest(_id) { + async removeGuest(_id) { check(_id, String); - const guest = LivechatVisitors.findOneById(_id); + const guest = await LivechatVisitors.findOneById(_id, { projection: { _id: 1 } }); if (!guest) { throw new Meteor.Error('error-invalid-guest', 'Invalid guest', { method: 'livechat:removeGuest', }); } - this.cleanGuestHistory(_id); + await this.cleanGuestHistory(_id); return LivechatVisitors.removeById(_id); }, @@ -971,8 +971,8 @@ export const Livechat = { return user; }, - cleanGuestHistory(_id) { - const guest = LivechatVisitors.findOneById(_id); + async cleanGuestHistory(_id) { + const guest = await LivechatVisitors.findOneById(_id); if (!guest) { throw new Meteor.Error('error-invalid-guest', 'Invalid guest', { method: 'livechat:cleanGuestHistory', @@ -1144,15 +1144,15 @@ export const Livechat = { }); }, - sendTranscript({ token, rid, email, subject, user }) { + async sendTranscript({ token, rid, email, subject, user }) { check(rid, String); check(email, String); Livechat.logger.debug(`Sending conversation transcript of room ${rid} to user with token ${token}`); const room = LivechatRooms.findOneById(rid); - const visitor = LivechatVisitors.getVisitorByToken(token, { - fields: { _id: 1, token: 1, language: 1, username: 1, name: 1 }, + const visitor = await LivechatVisitors.getVisitorByToken(token, { + projection: { _id: 1, token: 1, language: 1, username: 1, name: 1 }, }); const userLanguage = (visitor && visitor.language) || settings.get('Language') || 'en'; const timezone = getTimezone(user); @@ -1402,13 +1402,13 @@ export const Livechat = { return LivechatRooms.findOneById(roomId); }, - updateLastChat(contactId, lastChat) { + async updateLastChat(contactId, lastChat) { const updateUser = { $set: { lastChat, }, }; - LivechatVisitors.updateById(contactId, updateUser); + await LivechatVisitors.updateById(contactId, updateUser); }, updateCallStatus(callId, rid, status, user) { Rooms.setCallStatus(rid, status); diff --git a/apps/meteor/app/livechat/server/methods/closeByVisitor.js b/apps/meteor/app/livechat/server/methods/closeByVisitor.js index 7f7bb952e38c..022e614bb7f0 100644 --- a/apps/meteor/app/livechat/server/methods/closeByVisitor.js +++ b/apps/meteor/app/livechat/server/methods/closeByVisitor.js @@ -1,13 +1,14 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { LivechatVisitors } from '@rocket.chat/models'; import { settings } from '../../../settings/server'; -import { LivechatRooms, LivechatVisitors } from '../../../models/server'; +import { LivechatRooms } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ - 'livechat:closeByVisitor'({ roomId, token }) { - const visitor = LivechatVisitors.getVisitorByToken(token); + async 'livechat:closeByVisitor'({ roomId, token }) { + const visitor = await LivechatVisitors.getVisitorByToken(token); const language = (visitor && visitor.language) || settings.get('Language') || 'en'; diff --git a/apps/meteor/app/livechat/server/methods/getAgentData.js b/apps/meteor/app/livechat/server/methods/getAgentData.js index cf1b393ecedd..1cc6993974c6 100644 --- a/apps/meteor/app/livechat/server/methods/getAgentData.js +++ b/apps/meteor/app/livechat/server/methods/getAgentData.js @@ -1,15 +1,16 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { Users, LivechatRooms, LivechatVisitors } from '../../../models/server'; +import { Users, LivechatRooms } from '../../../models/server'; Meteor.methods({ - 'livechat:getAgentData'({ roomId, token }) { + async 'livechat:getAgentData'({ roomId, token }) { check(roomId, String); check(token, String); const room = LivechatRooms.findOneById(roomId); - const visitor = LivechatVisitors.getVisitorByToken(token); + const visitor = await LivechatVisitors.getVisitorByToken(token); if (!room || room.t !== 'l' || !room.v || room.v.token !== visitor.token) { throw new Meteor.Error('error-invalid-room', 'Invalid room'); diff --git a/apps/meteor/app/livechat/server/methods/getInitialData.js b/apps/meteor/app/livechat/server/methods/getInitialData.js index 9d39298b634a..b6eab00f57bd 100644 --- a/apps/meteor/app/livechat/server/methods/getInitialData.js +++ b/apps/meteor/app/livechat/server/methods/getInitialData.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; -import { LivechatTrigger } from '@rocket.chat/models'; +import { LivechatTrigger, LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, Users, LivechatDepartment, LivechatVisitors } from '../../../models/server'; +import { LivechatRooms, Users, LivechatDepartment } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; import { deprecationWarning } from '../../../api/server/helpers/deprecationWarning'; @@ -53,7 +53,7 @@ Meteor.methods({ info.room = room[0]; } - const visitor = LivechatVisitors.getVisitorByToken(visitorToken, { + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken, { fields: { name: 1, username: 1, diff --git a/apps/meteor/app/livechat/server/methods/loadHistory.js b/apps/meteor/app/livechat/server/methods/loadHistory.js index 2b81226f304a..7e2fd97d5329 100644 --- a/apps/meteor/app/livechat/server/methods/loadHistory.js +++ b/apps/meteor/app/livechat/server/methods/loadHistory.js @@ -1,15 +1,16 @@ import { Meteor } from 'meteor/meteor'; +import { LivechatVisitors } from '@rocket.chat/models'; import { loadMessageHistory } from '../../../lib'; -import { LivechatVisitors, LivechatRooms } from '../../../models/server'; +import { LivechatRooms } from '../../../models/server'; Meteor.methods({ - 'livechat:loadHistory'({ token, rid, end, limit = 20, ls }) { + async 'livechat:loadHistory'({ token, rid, end, limit = 20, ls }) { if (!token || typeof token !== 'string') { return; } - const visitor = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + const visitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); if (!visitor) { throw new Meteor.Error('invalid-visitor', 'Invalid Visitor', { diff --git a/apps/meteor/app/livechat/server/methods/loginByToken.js b/apps/meteor/app/livechat/server/methods/loginByToken.js index 5b4c15d75ad8..4c2c7c658365 100644 --- a/apps/meteor/app/livechat/server/methods/loginByToken.js +++ b/apps/meteor/app/livechat/server/methods/loginByToken.js @@ -1,10 +1,9 @@ import { Meteor } from 'meteor/meteor'; - -import { LivechatVisitors } from '../../../models/server'; +import { LivechatVisitors } from '@rocket.chat/models'; Meteor.methods({ - 'livechat:loginByToken'(token) { - const visitor = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } }); + async 'livechat:loginByToken'(token) { + const visitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); if (!visitor) { return; diff --git a/apps/meteor/app/livechat/server/methods/registerGuest.js b/apps/meteor/app/livechat/server/methods/registerGuest.js index 364507de6893..b6c7b7f14f32 100644 --- a/apps/meteor/app/livechat/server/methods/registerGuest.js +++ b/apps/meteor/app/livechat/server/methods/registerGuest.js @@ -1,11 +1,12 @@ import { Meteor } from 'meteor/meteor'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { Messages, LivechatRooms, LivechatVisitors } from '../../../models/server'; +import { Messages, LivechatRooms } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ - 'livechat:registerGuest'({ token, name, email, department, customFields } = {}) { - const userId = Livechat.registerGuest.call(this, { + async 'livechat:registerGuest'({ token, name, email, department, customFields } = {}) { + const userId = await Livechat.registerGuest.call(this, { token, name, email, @@ -15,8 +16,8 @@ Meteor.methods({ // update visited page history to not expire Messages.keepHistoryForToken(token); - const visitor = LivechatVisitors.getVisitorByToken(token, { - fields: { + const visitor = await LivechatVisitors.getVisitorByToken(token, { + projection: { token: 1, name: 1, username: 1, @@ -32,6 +33,7 @@ Meteor.methods({ }); if (customFields && customFields instanceof Array) { + // TODO: refactor to use normal await customFields.forEach((customField) => { if (typeof customField !== 'object') { return; @@ -39,7 +41,7 @@ Meteor.methods({ if (!customField.scope || customField.scope !== 'room') { const { key, value, overwrite } = customField; - LivechatVisitors.updateLivechatDataByToken(token, key, value, overwrite); + Promise.await(LivechatVisitors.updateLivechatDataByToken(token, key, value, overwrite)); } }); } diff --git a/apps/meteor/app/livechat/server/methods/saveInfo.js b/apps/meteor/app/livechat/server/methods/saveInfo.js index 76fa8c28915e..c5b4a164d6ca 100644 --- a/apps/meteor/app/livechat/server/methods/saveInfo.js +++ b/apps/meteor/app/livechat/server/methods/saveInfo.js @@ -7,7 +7,7 @@ import { callbacks } from '../../../../lib/callbacks'; import { Livechat } from '../lib/Livechat'; Meteor.methods({ - 'livechat:saveInfo'(guestData, roomData) { + async 'livechat:saveInfo'(guestData, roomData) { const userId = Meteor.userId(); if (!userId || !hasPermission(userId, 'view-l-room')) { @@ -49,7 +49,7 @@ Meteor.methods({ delete guestData.phone; } - const ret = Livechat.saveGuest(guestData, userId) && Livechat.saveRoomInfo(roomData, guestData, userId); + const ret = (await Livechat.saveGuest(guestData, userId)) && Livechat.saveRoomInfo(roomData, guestData, userId); const user = Meteor.users.findOne({ _id: userId }, { fields: { _id: 1, username: 1 } }); diff --git a/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.js b/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.js index bcae8f2402ef..7d908cdc7a90 100644 --- a/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.js +++ b/apps/meteor/app/livechat/server/methods/saveSurveyFeedback.js @@ -1,17 +1,16 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import _ from 'underscore'; - -import { LivechatRooms, LivechatVisitors } from '../../../models/server'; +import { LivechatRooms, LivechatVisitors } from '@rocket.chat/models'; Meteor.methods({ - 'livechat:saveSurveyFeedback'(visitorToken, visitorRoom, formData) { + async 'livechat:saveSurveyFeedback'(visitorToken, visitorRoom, formData) { check(visitorToken, String); check(visitorRoom, String); check(formData, [Match.ObjectIncluding({ name: String, value: String })]); - const visitor = LivechatVisitors.getVisitorByToken(visitorToken); - const room = LivechatRooms.findOneById(visitorRoom); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken); + const room = await LivechatRooms.findOneById(visitorRoom); if (visitor !== undefined && room !== undefined && room.v !== undefined && room.v.token === visitor.token) { const updateData = {}; diff --git a/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.js b/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.js index 5d647815b439..3f2a3e7c98bb 100644 --- a/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.js +++ b/apps/meteor/app/livechat/server/methods/sendFileLivechatMessage.js @@ -1,13 +1,14 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { Random } from 'meteor/random'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, LivechatVisitors } from '../../../models/server'; +import { LivechatRooms } from '../../../models/server'; import { FileUpload } from '../../../file-upload/server'; Meteor.methods({ async sendFileLivechatMessage(roomId, visitorToken, file, msgData = {}) { - const visitor = LivechatVisitors.getVisitorByToken(visitorToken); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken); if (!visitor) { return false; diff --git a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.js b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.js index 51887e90c660..7b6d2d0608b9 100644 --- a/apps/meteor/app/livechat/server/methods/sendMessageLivechat.js +++ b/apps/meteor/app/livechat/server/methods/sendMessageLivechat.js @@ -1,13 +1,13 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatVisitors } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; import { settings } from '../../../settings/server'; Meteor.methods({ - sendMessageLivechat({ token, _id, rid, msg, file, attachments }, agent) { + async sendMessageLivechat({ token, _id, rid, msg, file, attachments }, agent) { check(token, String); check(_id, String); check(rid, String); @@ -21,8 +21,8 @@ Meteor.methods({ }), ); - const guest = LivechatVisitors.getVisitorByToken(token, { - fields: { + const guest = await LivechatVisitors.getVisitorByToken(token, { + projection: { name: 1, username: 1, department: 1, diff --git a/apps/meteor/app/livechat/server/methods/setCustomField.js b/apps/meteor/app/livechat/server/methods/setCustomField.js index 73d3efe71418..cd8576bcd0aa 100644 --- a/apps/meteor/app/livechat/server/methods/setCustomField.js +++ b/apps/meteor/app/livechat/server/methods/setCustomField.js @@ -1,9 +1,10 @@ import { Meteor } from 'meteor/meteor'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, LivechatVisitors, LivechatCustomField } from '../../../models/server'; +import { LivechatRooms, LivechatCustomField } from '../../../models/server'; Meteor.methods({ - 'livechat:setCustomField'(token, key, value, overwrite = true) { + async 'livechat:setCustomField'(token, key, value, overwrite = true) { const customField = LivechatCustomField.findOneById(key); if (customField) { if (customField.scope === 'room') { diff --git a/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.js b/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.js index 0b1cf0e3d081..ada1703b34c6 100644 --- a/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.js +++ b/apps/meteor/app/livechat/server/methods/setDepartmentForVisitor.js @@ -1,18 +1,19 @@ import { Meteor } from 'meteor/meteor'; import { check } from 'meteor/check'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, Messages, LivechatVisitors } from '../../../models/server'; +import { LivechatRooms, Messages } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; import { normalizeTransferredByData } from '../lib/Helper'; Meteor.methods({ - 'livechat:setDepartmentForVisitor'({ roomId, visitorToken, departmentId } = {}) { + async 'livechat:setDepartmentForVisitor'({ roomId, visitorToken, departmentId } = {}) { check(roomId, String); check(visitorToken, String); check(departmentId, String); const room = LivechatRooms.findOneById(roomId); - const visitor = LivechatVisitors.getVisitorByToken(visitorToken); + const visitor = await LivechatVisitors.getVisitorByToken(visitorToken); if (!room || room.t !== 'l' || !room.v || room.v.token !== visitor.token) { throw new Meteor.Error('error-invalid-room', 'Invalid room'); diff --git a/apps/meteor/app/livechat/server/methods/startFileUploadRoom.js b/apps/meteor/app/livechat/server/methods/startFileUploadRoom.js index fbbe2b6673f1..5f8f6a7c8889 100644 --- a/apps/meteor/app/livechat/server/methods/startFileUploadRoom.js +++ b/apps/meteor/app/livechat/server/methods/startFileUploadRoom.js @@ -1,15 +1,16 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatVisitors } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; +// TODO: check if this is still in use Meteor.methods({ - 'livechat:startFileUploadRoom'(roomId, token) { + async 'livechat:startFileUploadRoom'(roomId, token) { methodDeprecationLogger.warn('livechat:startFileUploadRoom will be deprecated in future versions of Rocket.Chat'); - const guest = LivechatVisitors.getVisitorByToken(token); + const guest = await LivechatVisitors.getVisitorByToken(token); const message = { _id: Random.id(), diff --git a/apps/meteor/app/livechat/server/methods/transfer.js b/apps/meteor/app/livechat/server/methods/transfer.js index f825d29327a4..52a189c97e5c 100644 --- a/apps/meteor/app/livechat/server/methods/transfer.js +++ b/apps/meteor/app/livechat/server/methods/transfer.js @@ -1,13 +1,14 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { hasPermission } from '../../../authorization'; -import { LivechatRooms, Subscriptions, LivechatVisitors, Users } from '../../../models/server'; +import { hasPermission } from '../../../authorization/server'; +import { LivechatRooms, Subscriptions, Users } from '../../../models/server'; import { Livechat } from '../lib/Livechat'; import { normalizeTransferredByData } from '../lib/Helper'; Meteor.methods({ - 'livechat:transfer'(transferData) { + async 'livechat:transfer'(transferData) { if (!Meteor.userId() || !hasPermission(Meteor.userId(), 'view-l-room')) { throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'livechat:transfer' }); } @@ -38,7 +39,7 @@ Meteor.methods({ }); } - const guest = LivechatVisitors.findOneById(room.v && room.v._id); + const guest = await LivechatVisitors.findOneById(room.v && room.v._id); transferData.transferredBy = normalizeTransferredByData(Meteor.user() || {}, room); if (transferData.userId) { const userToTransfer = Users.findOneById(transferData.userId); diff --git a/apps/meteor/app/livechat/server/sendMessageBySMS.js b/apps/meteor/app/livechat/server/sendMessageBySMS.js index 22ab45bc9866..587dd2663a29 100644 --- a/apps/meteor/app/livechat/server/sendMessageBySMS.js +++ b/apps/meteor/app/livechat/server/sendMessageBySMS.js @@ -1,7 +1,8 @@ +import { LivechatVisitors } from '@rocket.chat/models'; + import { callbacks } from '../../../lib/callbacks'; import { settings } from '../../settings/server'; import { SMS } from '../../sms'; -import { LivechatVisitors } from '../../models/server'; import { normalizeMessageFileUpload } from '../../utils/server/functions/normalizeMessageFileUpload'; callbacks.add( @@ -49,7 +50,7 @@ callbacks.add( return message; } - const visitor = LivechatVisitors.getVisitorByToken(room.v.token); + const visitor = Promise.await(LivechatVisitors.getVisitorByToken(room.v.token)); if (!visitor || !visitor.phone || visitor.phone.length === 0) { return message; diff --git a/apps/meteor/app/models/server/index.js b/apps/meteor/app/models/server/index.js index cb275dcff133..697bad8152da 100644 --- a/apps/meteor/app/models/server/index.js +++ b/apps/meteor/app/models/server/index.js @@ -10,7 +10,6 @@ import LivechatCustomField from './models/LivechatCustomField'; import LivechatDepartment from './models/LivechatDepartment'; import LivechatDepartmentAgents from './models/LivechatDepartmentAgents'; import LivechatRooms from './models/LivechatRooms'; -import LivechatVisitors from './models/LivechatVisitors'; import LivechatInquiry from './models/LivechatInquiry'; import OmnichannelQueue from './models/OmnichannelQueue'; import ImportData from './models/ImportData'; @@ -36,7 +35,6 @@ export { LivechatDepartment, LivechatDepartmentAgents, LivechatRooms, - LivechatVisitors, LivechatInquiry, OmnichannelQueue, ImportData, diff --git a/apps/meteor/app/models/server/models/LivechatVisitors.js b/apps/meteor/app/models/server/models/LivechatVisitors.js deleted file mode 100644 index ffffe1095fcd..000000000000 --- a/apps/meteor/app/models/server/models/LivechatVisitors.js +++ /dev/null @@ -1,253 +0,0 @@ -import _ from 'underscore'; -import s from 'underscore.string'; - -import { Base } from './_Base'; -import Settings from './Settings'; - -export class LivechatVisitors extends Base { - constructor() { - super('livechat_visitor'); - - this.tryEnsureIndex({ token: 1 }); - this.tryEnsureIndex({ 'phone.phoneNumber': 1 }, { sparse: true }); - this.tryEnsureIndex({ 'visitorEmails.address': 1 }, { sparse: true }); - this.tryEnsureIndex({ name: 1 }, { sparse: true }); - this.tryEnsureIndex({ username: 1 }); - this.tryEnsureIndex({ 'contactManager.username': 1 }, { sparse: true }); - } - - /** - * Gets visitor by token - * @param {string} token - Visitor token - */ - getVisitorByToken(token, options) { - const query = { - token, - }; - - return this.findOne(query, options); - } - - /** - * Find visitors by _id - * @param {string} token - Visitor token - */ - findById(_id, options) { - const query = { - _id, - }; - - return this.find(query, options); - } - - /** - * Find One visitor by _id - */ - findOneById(_id, options) { - const query = { - _id, - }; - - return this.findOne(query, options); - } - - /** - * Gets visitor by token - * @param {string} token - Visitor token - */ - findVisitorByToken(token) { - const query = { - token, - }; - - return this.find(query); - } - - updateLivechatDataByToken(token, key, value, overwrite = true) { - const query = { - token, - }; - - if (!overwrite) { - const user = this.findOne(query, { fields: { livechatData: 1 } }); - if (user.livechatData && typeof user.livechatData[key] !== 'undefined') { - return true; - } - } - - const update = { - $set: { - [`livechatData.${key}`]: value, - }, - }; - - return this.update(query, update); - } - - updateLastAgentByToken(token, lastAgent) { - const query = { - token, - }; - - const update = { - $set: { - lastAgent, - }, - }; - - return this.update(query, update); - } - - /** - * Find a visitor by their phone number - * @return {object} User from db - */ - findOneVisitorByPhone(phone) { - const query = { - 'phone.phoneNumber': phone, - }; - - return this.findOne(query); - } - - getVisitorsBetweenDate(date) { - const query = { - _updatedAt: { - $gte: date.gte, // ISO Date, ts >= date.gte - $lt: date.lt, // ISODate, ts < date.lt - }, - }; - - return this.find(query, { fields: { _id: 1 } }); - } - - /** - * Get the next visitor name - * @return {string} The next visitor name - */ - getNextVisitorUsername() { - const query = { - _id: 'Livechat_guest_count', - }; - - const update = { - $inc: { - value: 1, - }, - }; - - const livechatCount = Settings.findAndModify(query, null, update); - - return `guest-${livechatCount.value.value + 1}`; - } - - updateById(_id, update) { - return this.update({ _id }, update); - } - - saveGuestById(_id, data) { - const setData = {}; - const unsetData = {}; - - if (data.name) { - if (!_.isEmpty(s.trim(data.name))) { - setData.name = s.trim(data.name); - } else { - unsetData.name = 1; - } - } - - if (data.email) { - if (!_.isEmpty(s.trim(data.email))) { - setData.visitorEmails = [{ address: s.trim(data.email) }]; - } else { - unsetData.visitorEmails = 1; - } - } - - if (data.phone) { - if (!_.isEmpty(s.trim(data.phone))) { - setData.phone = [{ phoneNumber: s.trim(data.phone) }]; - } else { - unsetData.phone = 1; - } - } - - if (data.livechatData) { - Object.keys(data.livechatData).forEach((key) => { - const value = s.trim(data.livechatData[key]); - if (value) { - setData[`livechatData.${key}`] = value; - } else { - unsetData[`livechatData.${key}`] = 1; - } - }); - } - - const update = {}; - - if (!_.isEmpty(setData)) { - update.$set = setData; - } - - if (!_.isEmpty(unsetData)) { - update.$unset = unsetData; - } - - if (_.isEmpty(update)) { - return true; - } - - return this.update({ _id }, update); - } - - findOneGuestByEmailAddress(emailAddress) { - const query = { - 'visitorEmails.address': String(emailAddress).toLowerCase(), - }; - - return this.findOne(query); - } - - saveGuestEmailPhoneById(_id, emails, phones) { - const update = { - $addToSet: {}, - }; - - const saveEmail = [] - .concat(emails) - .filter((email) => email && email.trim()) - .map((email) => ({ address: email })); - - if (saveEmail.length > 0) { - update.$addToSet.visitorEmails = { $each: saveEmail }; - } - - const savePhone = [] - .concat(phones) - .filter((phone) => phone && phone.trim().replace(/[^\d]/g, '')) - .map((phone) => ({ phoneNumber: phone })); - - if (savePhone.length > 0) { - update.$addToSet.phone = { $each: savePhone }; - } - - if (!update.$addToSet.visitorEmails && !update.$addToSet.phone) { - return; - } - - return this.update({ _id }, update); - } - - // REMOVE - removeDepartmentById(_id) { - return this.update({ _id }, { $unset: { department: 1 } }); - } - - removeById(_id) { - const query = { _id }; - return this.remove(query); - } -} - -export default new LivechatVisitors(); diff --git a/apps/meteor/app/statistics/server/lib/statistics.ts b/apps/meteor/app/statistics/server/lib/statistics.ts index 0a89174d8c99..3efc88d46d0d 100644 --- a/apps/meteor/app/statistics/server/lib/statistics.ts +++ b/apps/meteor/app/statistics/server/lib/statistics.ts @@ -15,13 +15,14 @@ import { Invites, Uploads, LivechatDepartment, + LivechatVisitors, EmailInbox, LivechatBusinessHours, Messages as MessagesRaw, InstanceStatus, } from '@rocket.chat/models'; -import { Settings, Users, Rooms, Subscriptions, Messages, LivechatVisitors } from '../../../models/server'; +import { Settings, Users, Rooms, Subscriptions, Messages } from '../../../models/server'; import { settings } from '../../../settings/server'; import { Info, getMongoInfo } from '../../../utils/server'; import { getControl } from '../../../../server/lib/migrations'; @@ -112,7 +113,7 @@ export const statistics = { statistics.totalThreads = Messages.countThreads(); // livechat visitors - statistics.totalLivechatVisitors = LivechatVisitors.find().count(); + statistics.totalLivechatVisitors = await LivechatVisitors.find().count(); // livechat agents statistics.totalLivechatAgents = Users.findAgents().count(); diff --git a/apps/meteor/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts b/apps/meteor/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts index 94035c20d7cc..e732adbb6696 100644 --- a/apps/meteor/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts +++ b/apps/meteor/ee/app/canned-responses/server/hooks/onMessageSentParsePlaceholder.ts @@ -1,10 +1,11 @@ import get from 'lodash.get'; -import type { IMessage } from '@rocket.chat/core-typings'; -import { IOmnichannelRoom, isOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { IMessage, IOmnichannelRoom } from '@rocket.chat/core-typings'; +import { isOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; import { settings } from '../../../../../app/settings/server'; import { callbacks } from '../../../../../lib/callbacks'; -import { Users, LivechatVisitors, Rooms } from '../../../../../app/models/server'; +import { Users, Rooms } from '../../../../../app/models/server'; const placeholderFields = { 'contact.name': { @@ -45,7 +46,7 @@ const handleBeforeSaveMessage = (message: IMessage, room?: IOmnichannelRoom): IM const agentId = room?.servedBy?._id; const visitorId = room?.v?._id; const agent = Users.findOneById(agentId, { fields: { name: 1, _id: 1, emails: 1 } }) || {}; - const visitor = LivechatVisitors.findOneById(visitorId) || {}; + const visitor = visitorId && (Promise.await(LivechatVisitors.findOneById(visitorId, {})) || {}); Object.keys(placeholderFields).map((field) => { const templateKey = `{{${field}}}`; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js index 7d02964e79fb..6ffb215450af 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js @@ -1,7 +1,9 @@ +import { LivechatVisitors } from '@rocket.chat/models'; + import { callbacks } from '../../../../../lib/callbacks'; import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager'; import { settings } from '../../../../../app/settings/server'; -import { LivechatRooms, LivechatInquiry, LivechatVisitors, Users } from '../../../../../app/models/server'; +import { LivechatRooms, LivechatInquiry, Users } from '../../../../../app/models/server'; let contactManagerPreferred = false; let lastChattedAgentPreferred = false; @@ -24,9 +26,11 @@ const checkDefaultAgentOnNewRoom = (defaultAgent, defaultGuest) => { } const { _id: guestId } = defaultGuest; - const guest = LivechatVisitors.findOneById(guestId, { - fields: { lastAgent: 1, token: 1, contactManager: 1 }, - }); + const guest = Promise.await( + LivechatVisitors.findOneById(guestId, { + projection: { lastAgent: 1, token: 1, contactManager: 1 }, + }), + ); if (!guest) { return defaultAgent; } @@ -89,7 +93,7 @@ const afterTakeInquiry = (inquiry, agent) => { return inquiry; } - LivechatVisitors.updateLastAgentByToken(token, { ...agent, ts: new Date() }); + Promise.await(LivechatVisitors.updateLastAgentByToken(token, { ...agent, ts: new Date() })); return inquiry; }; diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.js b/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.js index c272c0f516eb..e4b229803133 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.js +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/VisitorInactivityMonitor.js @@ -1,9 +1,10 @@ import { SyncedCron } from 'meteor/littledata:synced-cron'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { Meteor } from 'meteor/meteor'; +import { LivechatVisitors } from '@rocket.chat/models'; import { settings } from '../../../../../app/settings/server'; -import { LivechatRooms, LivechatDepartment, Users, LivechatVisitors } from '../../../../../app/models/server'; +import { LivechatRooms, LivechatDepartment, Users } from '../../../../../app/models/server'; import { Livechat } from '../../../../../app/livechat/server/lib/Livechat'; import { LivechatEnterprise } from './LivechatEnterprise'; @@ -78,11 +79,11 @@ export class VisitorInactivityMonitor { }); } - placeRoomOnHold(room) { + async placeRoomOnHold(room) { const timeout = settings.get('Livechat_visitor_inactivity_timeout'); const { v: { _id: visitorId } = {} } = room; - const visitor = LivechatVisitors.findOneById(visitorId); + const visitor = await LivechatVisitors.findOneById(visitorId); if (!visitor) { throw new Meteor.Error('error-invalid_visitor', 'Visitor Not found'); } @@ -105,7 +106,7 @@ export class VisitorInactivityMonitor { break; } case 'on-hold': { - this.placeRoomOnHold(room); + Promise.await(this.placeRoomOnHold(room)); break; } } diff --git a/apps/meteor/ee/app/livechat-enterprise/server/methods/resumeOnHold.ts b/apps/meteor/ee/app/livechat-enterprise/server/methods/resumeOnHold.ts index caa2c1ea0d70..716a4babaf68 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/methods/resumeOnHold.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/methods/resumeOnHold.ts @@ -1,30 +1,32 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; +import { ILivechatVisitor } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, LivechatInquiry, Messages, Users, LivechatVisitors } from '../../../../../app/models/server'; +import { LivechatRooms, LivechatInquiry, Messages, Users } from '../../../../../app/models/server'; import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager'; import { callbacks } from '../../../../../lib/callbacks'; -const resolveOnHoldCommentInfo = (options: { clientAction: boolean }, room: any, onHoldChatResumedBy: any): string => { - let comment = ''; +async function resolveOnHoldCommentInfo(options: { clientAction: boolean }, room: any, onHoldChatResumedBy: any): Promise { if (options.clientAction) { - comment = TAPi18n.__('Omnichannel_on_hold_chat_manually', { + return TAPi18n.__('Omnichannel_on_hold_chat_manually', { user: onHoldChatResumedBy.name || onHoldChatResumedBy.username, }); - } else { - const { - v: { _id: visitorId }, - } = room; - const visitor = LivechatVisitors.findOneById(visitorId, { name: 1, username: 1 }); - if (!visitor) { - throw new Meteor.Error('error-invalid_visitor', 'Visitor Not found'); - } - const guest = visitor.name || visitor.username; - comment = TAPi18n.__('Omnichannel_on_hold_chat_automatically', { guest }); } + const { + v: { _id: visitorId }, + } = room; + const visitor = await LivechatVisitors.findOneById>(visitorId, { + projection: { name: 1, username: 1 }, + }); + if (!visitor) { + throw new Meteor.Error('error-invalid_visitor', 'Visitor Not found'); + } + + const guest = visitor.name || visitor.username; - return comment; -}; + return TAPi18n.__('Omnichannel_on_hold_chat_automatically', { guest }); +} Meteor.methods({ async 'livechat:resumeOnHold'(roomId, options = { clientAction: false }) { @@ -55,7 +57,7 @@ Meteor.methods({ const onHoldChatResumedBy = options.clientAction ? Meteor.user() : Users.findOneById('rocket.cat'); - const comment = resolveOnHoldCommentInfo(options, room, onHoldChatResumedBy); + const comment = await resolveOnHoldCommentInfo(options, room, onHoldChatResumedBy); (Messages as any).createOnHoldResumedHistoryWithRoomIdMessageAndUser(roomId, comment, onHoldChatResumedBy); const updatedRoom = LivechatRooms.findOneById(roomId); diff --git a/apps/meteor/imports/message-read-receipt/server/lib/ReadReceipt.js b/apps/meteor/imports/message-read-receipt/server/lib/ReadReceipt.js index 625ee3d88c94..158d3ddc3365 100644 --- a/apps/meteor/imports/message-read-receipt/server/lib/ReadReceipt.js +++ b/apps/meteor/imports/message-read-receipt/server/lib/ReadReceipt.js @@ -1,8 +1,8 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; -import { ReadReceipts } from '@rocket.chat/models'; +import { LivechatVisitors, ReadReceipts } from '@rocket.chat/models'; -import { Subscriptions, Messages, Rooms, Users, LivechatVisitors } from '../../../../app/models/server'; +import { Subscriptions, Messages, Rooms, 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'; @@ -98,11 +98,13 @@ export const ReadReceipt = { async getReceipts(message) { const receipts = await ReadReceipts.findByMessageId(message._id).toArray(); - return receipts.map((receipt) => ({ - ...receipt, - user: receipt.token - ? LivechatVisitors.getVisitorByToken(receipt.token, { fields: { username: 1, name: 1 } }) - : Users.findOneById(receipt.userId, { fields: { username: 1, name: 1 } }), - })); + return Promise.all( + receipts.map(async (receipt) => ({ + ...receipt, + user: receipt.token + ? await LivechatVisitors.getVisitorByToken(receipt.token, { projection: { username: 1, name: 1 } }) + : Users.findOneById(receipt.userId, { fields: { username: 1, name: 1 } }), + })), + ); }, }; diff --git a/apps/meteor/server/features/EmailInbox/EmailInbox_Incoming.ts b/apps/meteor/server/features/EmailInbox/EmailInbox_Incoming.ts index 6c5e498cb1ee..a767be02b7c2 100644 --- a/apps/meteor/server/features/EmailInbox/EmailInbox_Incoming.ts +++ b/apps/meteor/server/features/EmailInbox/EmailInbox_Incoming.ts @@ -3,10 +3,11 @@ import stripHtml from 'string-strip-html'; import { Random } from 'meteor/random'; import { ParsedMail, Attachment } from 'mailparser'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; -import { OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { ILivechatVisitor, OmnichannelSourceType } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; import { Livechat } from '../../../app/livechat/server/lib/Livechat'; -import { LivechatRooms, LivechatVisitors, Messages } from '../../../app/models/server'; +import { LivechatRooms, Messages } from '../../../app/models/server'; import { FileUpload } from '../../../app/file-upload/server'; import { QueueManager } from '../../../app/livechat/server/lib/QueueManager'; import { settings } from '../../../app/settings/server'; @@ -30,9 +31,9 @@ type FileAttachment = { const language = settings.get('Language') || 'en'; const t = (s: string): string => TAPi18n.__(s, { lng: language }); -function getGuestByEmail(email: string, name: string, department = ''): any { +async function getGuestByEmail(email: string, name: string, department = ''): Promise { logger.debug(`Attempt to register a guest for ${email} on department: ${department}`); - const guest = LivechatVisitors.findOneGuestByEmailAddress(email); + const guest = await LivechatVisitors.findOneGuestByEmailAddress(email); if (guest) { logger.debug(`Guest with email ${email} found with id ${guest._id}`); @@ -44,11 +45,11 @@ function getGuestByEmail(email: string, name: string, department = ''): any { newDepartment: department, }); if (!department) { - LivechatVisitors.removeDepartmentById(guest._id); + await LivechatVisitors.removeDepartmentById(guest._id); delete guest.department; return guest; } - Livechat.setDepartmentForGuest({ token: guest.token, department }); + await Livechat.setDepartmentForGuest({ token: guest.token, department }); return LivechatVisitors.findOneById(guest._id, {}); } return guest; @@ -58,7 +59,7 @@ function getGuestByEmail(email: string, name: string, department = ''): any { msg: 'Creating a new Omnichannel guest for visitor with email', email, }); - const userId = Livechat.registerGuest({ + const userId = await Livechat.registerGuest({ token: Random.id(), name: name || email, email, @@ -69,7 +70,7 @@ function getGuestByEmail(email: string, name: string, department = ''): any { id: undefined, }); - const newGuest = LivechatVisitors.findOneById(userId, {}); + const newGuest = await LivechatVisitors.findOneById(userId); logger.debug(`Guest ${userId} for visitor ${email} created`); if (newGuest) { return newGuest; @@ -136,7 +137,12 @@ export async function onEmailReceived(email: ParsedMail, inbox: string, departme const thread = references?.[0] ?? email.messageId; logger.debug(`Fetching guest for visitor ${email.from.value[0].address}`); - const guest = getGuestByEmail(email.from.value[0].address, email.from.value[0].name, department); + const guest = await getGuestByEmail(email.from.value[0].address, email.from.value[0].name, department); + + if (!guest) { + logger.debug(`No visitor found for ${email.from.value[0].address}`); + return; + } logger.debug(`Guest ${guest._id} obtained. Attempting to find or create a room on department ${department}`); diff --git a/apps/meteor/server/lib/rooms/roomTypes/livechat.ts b/apps/meteor/server/lib/rooms/roomTypes/livechat.ts index fe69aacc7217..1dadd91ee27d 100644 --- a/apps/meteor/server/lib/rooms/roomTypes/livechat.ts +++ b/apps/meteor/server/lib/rooms/roomTypes/livechat.ts @@ -1,6 +1,7 @@ import type { AtLeast, ValueOf } from '@rocket.chat/core-typings'; +import { LivechatVisitors } from '@rocket.chat/models'; -import { LivechatRooms, LivechatVisitors } from '../../../../app/models/server'; +import { LivechatRooms } from '../../../../app/models/server'; import { RoomSettingsEnum, RoomMemberActions } from '../../../../definition/IRoomTypeConfig'; import type { IRoomTypeServerDirectives } from '../../../../definition/IRoomTypeConfig'; import { getLivechatRoomType } from '../../../../lib/rooms/roomTypes/livechat'; @@ -38,7 +39,7 @@ roomCoordinator.add(LivechatRoomType, { }, getMsgSender(senderId) { - return LivechatVisitors.findOneById(senderId); + return Promise.await(LivechatVisitors.findOneById(senderId)); }, getReadReceiptsExtraData(message) { diff --git a/apps/meteor/server/models/raw/LivechatVisitors.ts b/apps/meteor/server/models/raw/LivechatVisitors.ts index dbf81fb94d38..ec6373d2bd13 100644 --- a/apps/meteor/server/models/raw/LivechatVisitors.ts +++ b/apps/meteor/server/models/raw/LivechatVisitors.ts @@ -1,4 +1,4 @@ -import type { ILivechatVisitor, RocketChatRecordDeleted } from '@rocket.chat/core-typings'; +import type { ILivechatVisitor, ISetting, RocketChatRecordDeleted } from '@rocket.chat/core-typings'; import type { ILivechatVisitorsModel } from '@rocket.chat/model-typings'; import type { AggregationCursor, @@ -8,9 +8,12 @@ import type { FilterQuery, FindOneOptions, UpdateWriteOpResult, - WithoutProjection, + IndexSpecification, + DeleteWriteOpResultObject, + UpdateQuery, + WriteOpResult, } from 'mongodb'; -import { getCollectionName } from '@rocket.chat/models'; +import { getCollectionName, Settings } from '@rocket.chat/models'; import { escapeRegExp } from '@rocket.chat/string-helpers'; import { BaseRaw } from './BaseRaw'; @@ -20,15 +23,54 @@ export class LivechatVisitorsRaw extends BaseRaw implements IL super(db, getCollectionName('livechat_visitor'), trash); } - findOneById(_id: string, options: WithoutProjection>): Promise { + protected modelIndexes(): IndexSpecification[] { + return [ + { key: { token: 1 } }, + { key: { 'phone.phoneNumber': 1 }, sparse: true }, + { key: { 'visitorEmails.address': 1 }, sparse: true }, + { key: { name: 1 }, sparse: true }, + { key: { username: 1 } }, + { key: { 'contactMananger.username': 1 }, sparse: true }, + ]; + } + + findOneVisitorByPhone(phone: string): Promise { + const query = { + 'phone.phoneNumber': phone, + }; + + return this.findOne(query); + } + + findOneGuestByEmailAddress(emailAddress: string): Promise { + const query = { + 'visitorEmails.address': String(emailAddress).toLowerCase(), + }; + + return this.findOne(query); + } + + /** + * Find visitors by _id + * @param {string} token - Visitor token + */ + findById(_id: string, options: FindOneOptions): Cursor { const query = { _id, }; - return this.findOne(query, options); + return this.find(query, options); + } + + findVisitorByToken(token: string): Cursor { + const query = { + token, + }; + + return this.find(query); } - getVisitorByToken(token: string, options: WithoutProjection>): Promise { + getVisitorByToken(token: string, options: FindOneOptions): Promise { const query = { token, }; @@ -48,6 +90,27 @@ export class LivechatVisitorsRaw extends BaseRaw implements IL return this.find(query, { projection: { _id: 1 } }); } + async getNextVisitorUsername(): Promise { + const query = { + _id: 'Livechat_guest_count', + }; + + const update: UpdateQuery = { + $inc: { + // @ts-expect-error looks like the typings of ISetting.value conflict with this type of update + value: 1, + }, + }; + + const livechatCount = await Settings.findOneAndUpdate(query, update, { returnDocument: 'after' }); + + if (!livechatCount.value) { + throw new Error("Can't find Livechat_guest_count setting"); + } + + return `guest-${livechatCount.value.value}`; + } + findByNameRegexWithExceptionsAndConditions

( searchTerm: string, exceptions: string[] = [], @@ -121,6 +184,137 @@ export class LivechatVisitorsRaw extends BaseRaw implements IL return this.find(query, options); } + async updateLivechatDataByToken(token: string, key: string, value: unknown, overwrite = true): Promise { + const query = { + token, + }; + + if (!overwrite) { + const user = await this.getVisitorByToken(token, { projection: { livechatData: 1 } }); + if (user?.livechatData && typeof user.livechatData[key] !== 'undefined') { + return true; + } + } + + const update = { + $set: { + [`livechatData.${key}`]: value, + }, + }; + + return this.update(query, update); + } + + updateLastAgentByToken(token: string, lastAgent: ILivechatVisitor['lastAgent']): Promise { + const query = { + token, + }; + + const update = { + $set: { + lastAgent, + }, + }; + + return this.update(query, update); + } + + updateById(_id: string, update: UpdateQuery): Promise { + return this.update({ _id }, update); + } + + saveGuestById( + _id: string, + data: { name?: string; username?: string; email?: string; phone?: string; livechatData: { [k: string]: any } }, + ): Promise { + const setData: DeepWriteable['$set']> = {}; + const unsetData: DeepWriteable['$unset']> = {}; + + if (data.name) { + if (data.name?.trim()) { + setData.name = data.name.trim(); + } else { + unsetData.name = 1; + } + } + + if (data.email) { + if (data.email?.trim()) { + setData.visitorEmails = [{ address: data.email.trim() }]; + } else { + unsetData.visitorEmails = 1; + } + } + + if (data.phone) { + if (data.phone?.trim()) { + setData.phone = [{ phoneNumber: data.phone.trim() }]; + } else { + unsetData.phone = 1; + } + } + + if (data.livechatData) { + Object.keys(data.livechatData).forEach((key) => { + const value = data.livechatData[key]?.trim(); + if (value) { + setData[`livechatData.${key}`] = value; + } else { + unsetData[`livechatData.${key}`] = 1; + } + }); + } + + const update: UpdateQuery = { + ...(Object.keys(setData).length && { $set: setData as UpdateQuery['$set'] }), + ...(Object.keys(unsetData).length && { $unset: unsetData as UpdateQuery['$unset'] }), + }; + + if (!Object.keys(update).length) { + return Promise.resolve(true); + } + + return this.update({ _id }, update); + } + + removeDepartmentById(_id: string): Promise { + return this.update({ _id }, { $unset: { department: 1 } }); + } + + removeById(_id: string): Promise { + return this.removeById(_id); + } + + saveGuestEmailPhoneById(_id: string, emails: string[], phones: string[]): Promise { + const update: DeepWriteable> = { + $addToSet: {}, + }; + + const saveEmail = ([] as string[]) + .concat(emails) + .filter((email) => email?.trim()) + .map((email) => ({ address: email })); + + if (update.$addToSet && saveEmail.length > 0) { + update.$addToSet.visitorEmails = { $each: saveEmail }; + } + + const savePhone = ([] as string[]) + .concat(phones) + .filter((phone) => phone?.trim().replace(/[^\d]/g, '')) + .map((phone) => ({ phoneNumber: phone })); + + if (update.$addToSet && savePhone.length > 0) { + update.$addToSet.phone = { $each: savePhone }; + } + + if (!Object.keys(update).length) { + return Promise.resolve(); + } + + return this.update({ _id }, update as UpdateQuery); + } + removeContactManagerByUsername(manager: string): Promise { return this.updateMany( { @@ -136,3 +330,5 @@ export class LivechatVisitorsRaw extends BaseRaw implements IL ); } } + +type DeepWriteable = { -readonly [P in keyof T]: DeepWriteable }; diff --git a/apps/meteor/server/startup/migrations/v260.ts b/apps/meteor/server/startup/migrations/v260.ts index ee2ad17beab9..b92e10bc7173 100644 --- a/apps/meteor/server/startup/migrations/v260.ts +++ b/apps/meteor/server/startup/migrations/v260.ts @@ -1,9 +1,9 @@ import { ILivechatVisitor } from '@rocket.chat/core-typings'; import { BulkWriteOperation, Cursor } from 'mongodb'; -import { LivechatVisitors as VisitorsRaw } from '@rocket.chat/models'; +import { LivechatVisitors } from '@rocket.chat/models'; import { addMigration } from '../../lib/migrations'; -import { LivechatVisitors, Users } from '../../../app/models/server'; +import { Users } from '../../../app/models/server'; const getNextPageCursor = (skip: number, limit: number): Cursor => { return LivechatVisitors.find({ 'visitorEmails.address': /[A-Z]/ }, { skip, limit, sort: { _id: 1 } }); @@ -12,9 +12,9 @@ const getNextPageCursor = (skip: number, limit: number): Cursor[] = []; - const count = LivechatVisitors.find({ 'visitorEmails.address': /[A-Z]/ }).count(); + const count = await LivechatVisitors.find({ 'visitorEmails.address': /[A-Z]/ }).count(); const limit = 5000; let skip = 0; @@ -32,7 +32,8 @@ addMigration({ }); if (updates.length) { - Promise.await(VisitorsRaw.col.bulkWrite(updates)); + // eslint-disable-next-line no-await-in-loop + await LivechatVisitors.col.bulkWrite(updates); } incrementSkip(limit); diff --git a/packages/core-typings/src/ILivechatVisitor.ts b/packages/core-typings/src/ILivechatVisitor.ts index d7e2772721e6..2759b96099c9 100644 --- a/packages/core-typings/src/ILivechatVisitor.ts +++ b/packages/core-typings/src/ILivechatVisitor.ts @@ -20,6 +20,10 @@ export interface IVisitorEmail { address: string; } +interface ILivechatData { + [k: string]: unknown; +} + export interface ILivechatVisitor extends IRocketChatRecord { username: string; ts: Date; @@ -32,6 +36,12 @@ export interface ILivechatVisitor extends IRocketChatRecord { ip?: string; host?: string; visitorEmails?: IVisitorEmail[]; + lastAgent?: { + username: string; + agentId: string; + ts: Date; + }; + livechatData?: ILivechatData; contactManager?: { username: string; }; diff --git a/packages/model-typings/src/models/ILivechatVisitorsModel.ts b/packages/model-typings/src/models/ILivechatVisitorsModel.ts index 988faba46790..a355ecc57a2d 100644 --- a/packages/model-typings/src/models/ILivechatVisitorsModel.ts +++ b/packages/model-typings/src/models/ILivechatVisitorsModel.ts @@ -1,10 +1,18 @@ -import type { AggregationCursor, Cursor, FilterQuery, FindOneOptions, WithoutProjection, UpdateWriteOpResult } from 'mongodb'; +import type { + AggregationCursor, + Cursor, + FilterQuery, + FindOneOptions, + WithoutProjection, + UpdateWriteOpResult, + WriteOpResult, +} from 'mongodb'; import type { ILivechatVisitor } from '@rocket.chat/core-typings'; import type { IBaseModel } from './IBaseModel'; export interface ILivechatVisitorsModel extends IBaseModel { - findOneById(_id: string, options: WithoutProjection>): Promise; + findById(_id: string, options: FindOneOptions): Cursor; getVisitorByToken(token: string, options: WithoutProjection>): Promise; getVisitorsBetweenDate({ start, end, department }: { start: Date; end: Date; department: string }): Cursor; findByNameRegexWithExceptionsAndConditions

( @@ -22,4 +30,12 @@ export interface ILivechatVisitorsModel extends IBaseModel { options: FindOneOptions, ): Cursor; removeContactManagerByUsername(manager: string): Promise; + + updateLivechatDataByToken(token: string, key: string, value: unknown, overwrite: boolean): Promise; + + findOneGuestByEmailAddress(emailAddress: string): Promise; + + findOneVisitorByPhone(phone: string): Promise; + + removeDepartmentById(_id: string): Promise; } diff --git a/yarn.lock b/yarn.lock index ea334bb70a38..e18b3ed257e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7805,7 +7805,7 @@ __metadata: human-interval: ~1.0.0 moment-timezone: ~0.5.27 mongodb: ~3.5.0 - checksum: acb4ebb7e7356f6e53e810d821eb6aa3d88bbfb9e85183e707517bee6d1eea1f189f38bdf0dd2b91360492ab7643134d510c320d2523d86596498ab98e59735b + checksum: f5f68008298f9482631f1f494e392cd6b8ba7971a3b0ece81ae2abe60f53d67973ff4476156fa5c9c41b8b58c4ccd284e95c545e0523996dfd05f9a80b843e07 languageName: node linkType: hard