From 53c81bbf7fb3ad6cf520ce3bd97aa5ee1a5a6bf5 Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Mon, 23 Dec 2024 10:50:54 -0300 Subject: [PATCH 1/7] chore: rename API method unauthorized method to forbidden (#32083) --- apps/meteor/app/api/server/api.ts | 188 +++++++++++------- apps/meteor/app/api/server/definition.ts | 19 +- apps/meteor/app/api/server/v1/channels.ts | 14 +- apps/meteor/app/api/server/v1/cloud.ts | 2 +- apps/meteor/app/api/server/v1/commands.ts | 6 +- apps/meteor/app/api/server/v1/e2e.ts | 2 +- apps/meteor/app/api/server/v1/groups.ts | 10 +- apps/meteor/app/api/server/v1/im.ts | 18 +- apps/meteor/app/api/server/v1/rooms.ts | 10 +- apps/meteor/app/api/server/v1/settings.ts | 2 +- apps/meteor/app/api/server/v1/teams.ts | 22 +- apps/meteor/app/api/server/v1/users.ts | 12 +- .../app/api/server/v1/videoConference.ts | 2 +- apps/meteor/app/api/server/v1/voip/events.ts | 2 +- .../app/api/server/v1/voip/extensions.ts | 2 +- apps/meteor/app/api/server/v1/voip/rooms.ts | 2 +- .../app/livechat/imports/server/rest/rooms.ts | 2 +- .../livechat/imports/server/rest/upload.ts | 6 +- .../app/livechat/imports/server/rest/users.ts | 4 +- .../app/livechat/server/api/v1/agent.ts | 2 +- .../meteor/app/livechat/server/api/v1/room.ts | 2 +- apps/meteor/ee/server/api/sessions.ts | 12 +- .../tests/end-to-end/api/livechat/00-rooms.ts | 2 +- 23 files changed, 199 insertions(+), 144 deletions(-) diff --git a/apps/meteor/app/api/server/api.ts b/apps/meteor/app/api/server/api.ts index 51930f1e9e50..5025cdb6946a 100644 --- a/apps/meteor/app/api/server/api.ts +++ b/apps/meteor/app/api/server/api.ts @@ -12,12 +12,14 @@ import type { RateLimiterOptionsToCheck } from 'meteor/rate-limit'; import { RateLimiter } from 'meteor/rate-limit'; import type { Request, Response } from 'meteor/rocketchat:restivus'; import { Restivus } from 'meteor/rocketchat:restivus'; +import semver from 'semver'; import _ from 'underscore'; import type { PermissionsPayload } from './api.helpers'; import { checkPermissionsForInvocation, checkPermissions, parseDeprecation } from './api.helpers'; import type { FailureResult, + ForbiddenResult, InternalError, NotFoundResult, Operations, @@ -36,10 +38,16 @@ import { hasPermissionAsync } from '../../authorization/server/functions/hasPerm import { notifyOnUserChangeAsync } from '../../lib/server/lib/notifyListener'; import { metrics } from '../../metrics/server'; import { settings } from '../../settings/server'; +import { Info } from '../../utils/rocketchat.info'; import { getDefaultUserFields } from '../../utils/server/functions/getDefaultUserFields'; const logger = new Logger('API'); +// We have some breaking changes planned to the API. +// To avoid conflicts or missing something during the period we are adopting a 'feature flag approach' +// TODO: MAJOR check if this is still needed +const applyBreakingChanges = semver.gte(Info.version, '8.0.0'); + interface IAPIProperties { useDefaultAuth: boolean; prettyJson: boolean; @@ -298,14 +306,14 @@ export class APIClass extends Restivus { statusCode: 500, body: { success: false, - error: msg || 'Internal error occured', + error: msg || 'Internal server error', }, }; } public unauthorized(msg?: T): UnauthorizedResult { return { - statusCode: 403, + statusCode: 401, body: { success: false, error: msg || 'unauthorized', @@ -313,6 +321,19 @@ export class APIClass extends Restivus { }; } + public forbidden(msg?: T): ForbiddenResult { + return { + statusCode: 403, + body: { + success: false, + // TODO: MAJOR remove 'unauthorized' in favor of 'forbidden' + // because of reasons beyond my control we were used to send `unauthorized` to 403 cases, to avoid a breaking change we just adapted here + // but thanks to the semver check tests should break as soon we bump to a new version + error: msg || (applyBreakingChanges ? 'forbidden' : 'unauthorized'), + }, + }; + } + public tooManyRequests(msg?: string): { statusCode: number; body: Record & { success?: boolean } } { return { statusCode: 429, @@ -577,13 +598,16 @@ export class APIClass extends Restivus { } if (!this.user && !settings.get('Accounts_AllowAnonymousRead')) { - return { - statusCode: 401, - body: { + const result = api.unauthorized('You must be logged in to do this.'); + // compatibility with the old API + // TODO: MAJOR + if (!applyBreakingChanges) { + Object.assign(result.body, { status: 'error', message: 'You must be logged in to do this.', - }, - }; + }); + } + return result; } } @@ -612,18 +636,29 @@ export class APIClass extends Restivus { throw new Meteor.Error('invalid-params', validatorFunc.errors?.map((error: any) => error.message).join('\n ')); } } - if ( - shouldVerifyPermissions && - (!this.userId || + if (shouldVerifyPermissions) { + if (!this.userId) { + if (applyBreakingChanges) { + throw new Meteor.Error('error-unauthorized', 'You must be logged in to do this'); + } + throw new Meteor.Error('error-unauthorized', 'User does not have the permissions required for this action'); + } + if ( !(await checkPermissionsForInvocation( this.userId, _options.permissionsRequired as PermissionsPayload, this.request.method, - ))) - ) { - throw new Meteor.Error('error-unauthorized', 'User does not have the permissions required for this action', { - permissions: _options.permissionsRequired, - }); + )) + ) { + if (applyBreakingChanges) { + throw new Meteor.Error('error-forbidden', 'User does not have the permissions required for this action', { + permissions: _options.permissionsRequired, + }); + } + throw new Meteor.Error('error-unauthorized', 'User does not have the permissions required for this action', { + permissions: _options.permissionsRequired, + }); + } } const invocation = new DDPCommon.MethodInvocation({ @@ -678,18 +713,26 @@ export class APIClass extends Restivus { responseTime: Date.now() - startTime, }); } catch (e: any) { - const apiMethod: string = - { - 'error-too-many-requests': 'tooManyRequests', - 'error-unauthorized': 'unauthorized', - }[e.error as string] || 'failure'; - - result = (API.v1 as Record)[apiMethod]( - typeof e === 'string' ? e : e.message, - e.error, - process.env.TEST_MODE ? e.stack : undefined, - e, - ); + result = ((e: any) => { + switch (e.error) { + case 'error-too-many-requests': + return API.v1.tooManyRequests(typeof e === 'string' ? e : e.message); + case 'unauthorized': + case 'error-unauthorized': + if (applyBreakingChanges) { + return API.v1.unauthorized(typeof e === 'string' ? e : e.message); + } + return API.v1.forbidden(typeof e === 'string' ? e : e.message); + case 'forbidden': + case 'error-forbidden': + if (applyBreakingChanges) { + return API.v1.forbidden(typeof e === 'string' ? e : e.message); + } + return API.v1.failure(typeof e === 'string' ? e : e.message, e.error, process.env.TEST_MODE ? e.stack : undefined, e); + default: + return API.v1.failure(typeof e === 'string' ? e : e.message, e.error, process.env.TEST_MODE ? e.stack : undefined, e); + } + })(e); log.http({ err: e, @@ -791,8 +834,8 @@ export class APIClass extends Restivus { // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; - (this as APIClass<'/v1'>).addRoute<'/v1/login', { authRequired: false }>( - 'login' as any, + (this as APIClass<'/v1'>).addRoute( + 'login', { authRequired: false }, { async post() { @@ -803,58 +846,55 @@ export class APIClass extends Restivus { connection: generateConnection(getRequestIP(request) || '', this.request.headers), }); - let auth; try { - auth = await DDP._CurrentInvocation.withValue(invocation as any, async () => Meteor.callAsync('login', args)); - } catch (error: any) { - let e = error; - if (error.reason === 'User not found') { - e = { - error: 'Unauthorized', - reason: 'Unauthorized', - }; - } - - return { - statusCode: 401, - body: { - status: 'error', - error: e.error, - details: e.details, - message: e.reason || e.message, + const auth = await DDP._CurrentInvocation.withValue(invocation as any, async () => Meteor.callAsync('login', args)); + this.user = await Users.findOne( + { + _id: auth.id, }, - } as unknown as SuccessResult>; - } + { + projection: getDefaultUserFields(), + }, + ); - this.user = await Users.findOne( - { - _id: auth.id, - }, - { - projection: getDefaultUserFields(), - }, - ); - - this.userId = (this.user as unknown as IUser)?._id; - - const response = { - status: 'success', - data: { - userId: this.userId, - authToken: auth.token, - me: await getUserInfo(this.user || ({} as IUser)), - }, - }; + if (!this.user) { + return self.unauthorized(); + } - const extraData = self._config.onLoggedIn?.call(this); + this.userId = this.user._id; - if (extraData != null) { - _.extend(response.data, { - extra: extraData, + const extraData = self._config.onLoggedIn?.call(this); + + return self.success({ + status: 'success', + data: { + userId: this.userId, + authToken: auth.token, + me: await getUserInfo(this.user || ({} as IUser)), + ...(extraData && { extra: extraData }), + }, }); - } + } catch (error) { + if (!(error instanceof Meteor.Error)) { + return self.internalError(); + } - return response as unknown as SuccessResult>; + const result = self.unauthorized(); + + if (!applyBreakingChanges) { + Object.assign(result.body, { + status: 'error', + error: error.error, + details: error.details, + message: error.reason || error.message, + ...(error.reason === 'User not found' && { + error: 'Unauthorized', + message: 'Unauthorized', + }), + }); + } + return result; + } }, }, ); diff --git a/apps/meteor/app/api/server/definition.ts b/apps/meteor/app/api/server/definition.ts index 84083ec8014e..40e4c3216a43 100644 --- a/apps/meteor/app/api/server/definition.ts +++ b/apps/meteor/app/api/server/definition.ts @@ -27,14 +27,29 @@ export type FailureResult = { - statusCode: 403; + statusCode: 401; body: { success: false; error: T | 'unauthorized'; }; }; -export type InternalError = { statusCode: 500; body: { error: T | 'Internal error occured'; success: false } }; +export type ForbiddenResult = { + statusCode: 403; + body: { + success: false; + // TODO: MAJOR remove 'unauthorized' + error: T | 'forbidden' | 'unauthorized'; + }; +}; + +export type InternalError = { + statusCode: 500; + body: { + error: T | 'Internal server error'; + success: false; + }; +}; export type NotFoundResult = { statusCode: 404; diff --git a/apps/meteor/app/api/server/v1/channels.ts b/apps/meteor/app/api/server/v1/channels.ts index 91c7b63c2098..a17729c47af2 100644 --- a/apps/meteor/app/api/server/v1/channels.ts +++ b/apps/meteor/app/api/server/v1/channels.ts @@ -174,7 +174,7 @@ API.v1.addRoute( }); if (!result) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } return API.v1.success(result); @@ -304,7 +304,7 @@ API.v1.addRoute( (await hasPermissionAsync(this.userId, 'view-joined-room')) && !(await Subscriptions.findOneByRoomIdAndUserId(findResult._id, this.userId, { projection: { _id: 1 } })) ) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { cursor, totalCount } = Messages.findPaginated(ourQuery, { @@ -499,7 +499,7 @@ API.v1.addRoute( } if (channelId && !(await hasPermissionAsync(this.userId, 'edit-room', channelId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const room = await findChannelByIdOrName({ @@ -613,7 +613,7 @@ API.v1.addRoute( if (userId) { if (!access) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } user = userId; } @@ -751,7 +751,7 @@ API.v1.addRoute( }); } catch (e: any) { if (e.message === 'unauthorized') { - error = API.v1.unauthorized(); + error = API.v1.forbidden(); } else { error = API.v1.failure(e.message); } @@ -801,7 +801,7 @@ API.v1.addRoute( }); if (!(await canAccessRoomAsync(findResult, { _id: this.userId }))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { offset, count } = await getPaginationItems(this.queryParams); @@ -1056,7 +1056,7 @@ API.v1.addRoute( }); if (findResult.broadcast && !(await hasPermissionAsync(this.userId, 'view-broadcast-member-list', findResult._id))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { offset: skip, count: limit } = await getPaginationItems(this.queryParams); diff --git a/apps/meteor/app/api/server/v1/cloud.ts b/apps/meteor/app/api/server/v1/cloud.ts index 17000832897f..369169987c10 100644 --- a/apps/meteor/app/api/server/v1/cloud.ts +++ b/apps/meteor/app/api/server/v1/cloud.ts @@ -89,7 +89,7 @@ API.v1.addRoute( { async get() { if (!(await hasRoleAsync(this.userId, 'admin'))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const registrationStatus = await retrieveRegistrationStatus(); diff --git a/apps/meteor/app/api/server/v1/commands.ts b/apps/meteor/app/api/server/v1/commands.ts index c4719386864b..9c7e581ece9f 100644 --- a/apps/meteor/app/api/server/v1/commands.ts +++ b/apps/meteor/app/api/server/v1/commands.ts @@ -198,7 +198,7 @@ API.v1.addRoute( } if (!(await canAccessRoomIdAsync(body.roomId, this.userId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const params = body.params ? body.params : ''; @@ -252,7 +252,7 @@ API.v1.addRoute( } if (!(await canAccessRoomIdAsync(query.roomId, user?._id))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const params = query.params ? query.params : ''; @@ -304,7 +304,7 @@ API.v1.addRoute( } if (!(await canAccessRoomIdAsync(body.roomId, this.userId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { params = '' } = body; diff --git a/apps/meteor/app/api/server/v1/e2e.ts b/apps/meteor/app/api/server/v1/e2e.ts index b5f0c2ded06d..09e6517e4e8d 100644 --- a/apps/meteor/app/api/server/v1/e2e.ts +++ b/apps/meteor/app/api/server/v1/e2e.ts @@ -302,7 +302,7 @@ API.v1.addRoute( async post() { const { rid, e2eKey, e2eKeyId } = this.bodyParams; if (!(await hasPermissionAsync(this.userId, 'toggle-room-e2e-encryption', rid))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } if (LockMap.has(rid)) { throw new Error('error-e2e-key-reset-in-progress'); diff --git a/apps/meteor/app/api/server/v1/groups.ts b/apps/meteor/app/api/server/v1/groups.ts index 1a8069fff205..479ebdc9a42e 100644 --- a/apps/meteor/app/api/server/v1/groups.ts +++ b/apps/meteor/app/api/server/v1/groups.ts @@ -270,7 +270,7 @@ API.v1.addRoute( if (params.userId) { if (!access) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } user = params.userId; } @@ -347,7 +347,7 @@ API.v1.addRoute( }); } catch (error: unknown) { if (isMeteorError(error) && error.reason === 'error-not-allowed') { - return API.v1.unauthorized(); + return API.v1.forbidden(); } throw error; } @@ -516,7 +516,7 @@ API.v1.addRoute( }); if (!result) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } return API.v1.success(result); @@ -707,7 +707,7 @@ API.v1.addRoute( }); if (findResult.broadcast && !(await hasPermissionAsync(this.userId, 'view-broadcast-member-list', findResult.rid))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { offset: skip, count: limit } = await getPaginationItems(this.queryParams); @@ -1252,7 +1252,7 @@ API.v1.addRoute( } if (!(await hasAllPermissionAsync(this.userId, ['create-team', 'edit-room'], room.rid))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const subscriptions = await Subscriptions.findByRoomId(room.rid, { diff --git a/apps/meteor/app/api/server/v1/im.ts b/apps/meteor/app/api/server/v1/im.ts index d74d3decfbab..765e3c896f75 100644 --- a/apps/meteor/app/api/server/v1/im.ts +++ b/apps/meteor/app/api/server/v1/im.ts @@ -135,7 +135,7 @@ API.v1.addRoute( } else { const canAccess = await canAccessRoomIdAsync(roomId, this.userId); if (!canAccess) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { subscription: subs } = await findDirectMessageRoom({ roomId }, this.userId); @@ -181,14 +181,14 @@ API.v1.addRoute( if (ruserId) { if (!access) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } user = ruserId; } const canAccess = await canAccessRoomIdAsync(roomId, user); if (!canAccess) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { room, subscription } = await findDirectMessageRoom({ roomId }, user); @@ -238,7 +238,7 @@ API.v1.addRoute( const canAccess = await canAccessRoomIdAsync(room._id, this.userId); if (!canAccess) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const ourQuery = query ? { rid: room._id, ...query } : { rid: room._id }; @@ -289,7 +289,7 @@ API.v1.addRoute( const result = await Meteor.callAsync('getChannelHistory', objectParams); if (!result) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } return API.v1.success(result); @@ -309,7 +309,7 @@ API.v1.addRoute( const canAccess = await canAccessRoomIdAsync(room._id, this.userId); if (!canAccess) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { offset, count } = await getPaginationItems(this.queryParams); @@ -384,7 +384,7 @@ API.v1.addRoute( const canAccess = await canAccessRoomIdAsync(room._id, this.userId); if (!canAccess) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { offset, count } = await getPaginationItems(this.queryParams); @@ -547,7 +547,7 @@ API.v1.addRoute( } const canAccess = await canAccessRoomIdAsync(roomId, this.userId); if (!canAccess) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { room, subscription } = await findDirectMessageRoom({ roomId }, this.userId); @@ -574,7 +574,7 @@ API.v1.addRoute( const canAccess = await canAccessRoomIdAsync(roomId, this.userId); if (!canAccess) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { room } = await findDirectMessageRoom({ roomId }, this.userId); diff --git a/apps/meteor/app/api/server/v1/rooms.ts b/apps/meteor/app/api/server/v1/rooms.ts index a8ebb6b9c717..8b545b0b8e2a 100644 --- a/apps/meteor/app/api/server/v1/rooms.ts +++ b/apps/meteor/app/api/server/v1/rooms.ts @@ -164,7 +164,7 @@ API.v1.addRoute( { async post() { if (!(await canAccessRoomIdAsync(this.urlParams.rid, this.userId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const file = await getUploadFormData( @@ -225,7 +225,7 @@ API.v1.addRoute( { async post() { if (!(await canAccessRoomIdAsync(this.urlParams.rid, this.userId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const file = await getUploadFormData( @@ -296,7 +296,7 @@ API.v1.addRoute( { async post() { if (!(await canAccessRoomIdAsync(this.urlParams.rid, this.userId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const file = await Uploads.findOneById(this.urlParams.fileId); @@ -556,7 +556,7 @@ API.v1.addRoute( }); if (!room || !(await canAccessRoomAsync(room, { _id: this.userId }))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } let initialImage: IUpload | null = null; @@ -852,7 +852,7 @@ API.v1.addRoute( isMember: (await Subscriptions.countByRoomIdAndUserId(room._id, user._id)) > 0, }); } - return API.v1.unauthorized(); + return API.v1.forbidden(); }, }, ); diff --git a/apps/meteor/app/api/server/v1/settings.ts b/apps/meteor/app/api/server/v1/settings.ts index e9183cb9e38e..1c09e8c72937 100644 --- a/apps/meteor/app/api/server/v1/settings.ts +++ b/apps/meteor/app/api/server/v1/settings.ts @@ -185,7 +185,7 @@ API.v1.addRoute( // Disable custom scripts in cloud trials to prevent phishing campaigns if (disableCustomScripts() && /^Custom_Script_/.test(this.urlParams._id)) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } // allow special handling of particular setting types diff --git a/apps/meteor/app/api/server/v1/teams.ts b/apps/meteor/app/api/server/v1/teams.ts index f5a9b0c52d5e..4b54cbffb722 100644 --- a/apps/meteor/app/api/server/v1/teams.ts +++ b/apps/meteor/app/api/server/v1/teams.ts @@ -129,7 +129,7 @@ API.v1.addRoute( } if (!(await hasPermissionAsync(this.userId, 'convert-team', team.roomId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const rooms = await Team.getMatchingTeamRooms(team._id, roomsToRemove); @@ -174,7 +174,7 @@ API.v1.addRoute( } if (!(await hasPermissionAsync(this.userId, 'move-room-to-team', team.roomId))) { - return API.v1.unauthorized('error-no-permission-team-channel'); + return API.v1.forbidden('error-no-permission-team-channel'); } const { rooms } = this.bodyParams; @@ -200,7 +200,7 @@ API.v1.addRoute( } if (!(await hasPermissionAsync(this.userId, 'remove-team-channel', team.roomId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const canRemoveAny = !!(await hasPermissionAsync(this.userId, 'view-all-team-channels', team.roomId)); @@ -235,7 +235,7 @@ API.v1.addRoute( } if (!(await hasPermissionAsync(this.userId, 'edit-team-channel', team.roomId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const canUpdateAny = !!(await hasPermissionAsync(this.userId, 'view-all-team-channels', team.roomId)); @@ -346,7 +346,7 @@ API.v1.addRoute( const { userId, canUserDelete } = this.queryParams; if (!(this.userId === userId || (await hasPermissionAsync(this.userId, 'view-all-team-channels', team.roomId)))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const booleanCanUserDelete = canUserDelete === 'true'; @@ -475,7 +475,7 @@ API.v1.addRoute( } if (!(await hasAtLeastOnePermissionAsync(this.userId, ['add-team-member', 'edit-team-member'], team.roomId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } await Team.addMembers(this.userId, team._id, members); @@ -502,7 +502,7 @@ API.v1.addRoute( } if (!(await hasAtLeastOnePermissionAsync(this.userId, ['edit-team-member'], team.roomId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } await Team.updateMember(team._id, member); @@ -529,7 +529,7 @@ API.v1.addRoute( } if (!(await hasAtLeastOnePermissionAsync(this.userId, ['edit-team-member'], team.roomId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const user = await Users.findOneActiveById(userId, {}); @@ -620,7 +620,7 @@ API.v1.addRoute( (await canAccessRoomAsync(room, { _id: this.userId })) || (await hasPermissionAsync(this.userId, 'view-all-teams')); if (!canViewInfo) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } return API.v1.success({ teamInfo }); @@ -644,7 +644,7 @@ API.v1.addRoute( } if (!(await hasPermissionAsync(this.userId, 'delete-team', team.roomId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const rooms: string[] = await Team.getMatchingTeamRooms(team._id, roomsToRemove); @@ -710,7 +710,7 @@ API.v1.addRoute( } if (!(await hasPermissionAsync(this.userId, 'edit-team', team.roomId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } await Team.update(this.userId, team._id, data); diff --git a/apps/meteor/app/api/server/v1/users.ts b/apps/meteor/app/api/server/v1/users.ts index 1dcbd3ab3c78..b5bd18ae9806 100644 --- a/apps/meteor/app/api/server/v1/users.ts +++ b/apps/meteor/app/api/server/v1/users.ts @@ -235,7 +235,7 @@ API.v1.addRoute( })(); if (!user) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } if (this.bodyParams.avatarUrl) { @@ -466,7 +466,7 @@ API.v1.addRoute( settings.get('API_Apply_permission_view-outside-room_on_users-list') && !(await hasPermissionAsync(this.userId, 'view-outside-room')) ) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { offset, count } = await getPaginationItems(this.queryParams); @@ -575,7 +575,7 @@ API.v1.addRoute( settings.get('API_Apply_permission_view-outside-room_on_users-list') && !(await hasPermissionAsync(this.userId, 'view-outside-room')) ) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { offset, count } = await getPaginationItems(this.queryParams); @@ -712,7 +712,7 @@ API.v1.addRoute( const data = await generateAccessToken(this.userId, user._id); - return data ? API.v1.success({ data }) : API.v1.unauthorized(); + return data ? API.v1.success({ data }) : API.v1.forbidden(); }, }, ); @@ -1201,7 +1201,7 @@ API.v1.addRoute( const userId = this.bodyParams.userId || this.userId; if (userId !== this.userId && !(await hasPermissionAsync(this.userId, 'logout-other-user'))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } // this method logs the user out automatically, if successful returns 1, otherwise 0 @@ -1278,7 +1278,7 @@ API.v1.addRoute( })(); if (!user) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } // TODO refactor to not update the user twice (one inside of `setStatusText` and then later just the status + statusDefault) diff --git a/apps/meteor/app/api/server/v1/videoConference.ts b/apps/meteor/app/api/server/v1/videoConference.ts index d5e54cbfdf7b..cf0a3a58b53a 100644 --- a/apps/meteor/app/api/server/v1/videoConference.ts +++ b/apps/meteor/app/api/server/v1/videoConference.ts @@ -27,7 +27,7 @@ API.v1.addRoute( } if (!(await hasPermissionAsync(userId, 'call-management', roomId))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } try { diff --git a/apps/meteor/app/api/server/v1/voip/events.ts b/apps/meteor/app/api/server/v1/voip/events.ts index 49234480b756..7a2c9288b4c4 100644 --- a/apps/meteor/app/api/server/v1/voip/events.ts +++ b/apps/meteor/app/api/server/v1/voip/events.ts @@ -26,7 +26,7 @@ API.v1.addRoute( return API.v1.notFound(); } if (!(await canAccessRoomAsync(room, this.user))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } return API.v1.success(await LivechatVoip.handleEvent(event, room, this.user, comment)); diff --git a/apps/meteor/app/api/server/v1/voip/extensions.ts b/apps/meteor/app/api/server/v1/voip/extensions.ts index 91b0466626b6..fd2181d97f0d 100644 --- a/apps/meteor/app/api/server/v1/voip/extensions.ts +++ b/apps/meteor/app/api/server/v1/voip/extensions.ts @@ -95,7 +95,7 @@ API.v1.addRoute( const { id } = this.queryParams; if (id !== this.userId) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { extension } = diff --git a/apps/meteor/app/api/server/v1/voip/rooms.ts b/apps/meteor/app/api/server/v1/voip/rooms.ts index 402a002dd4e2..2e4c7cea7c1b 100644 --- a/apps/meteor/app/api/server/v1/voip/rooms.ts +++ b/apps/meteor/app/api/server/v1/voip/rooms.ts @@ -171,7 +171,7 @@ API.v1.addRoute( const hasAgentAccess = (await hasPermissionAsync(this.userId, 'view-l-room')) && agents?.includes(this.userId) && agents?.length === 1; if (!hasAdminAccess && !hasAgentAccess) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const createdAt = parseAndValidate('createdAt', createdAtParam); diff --git a/apps/meteor/app/livechat/imports/server/rest/rooms.ts b/apps/meteor/app/livechat/imports/server/rest/rooms.ts index f80ed61a131e..0882bae844f6 100644 --- a/apps/meteor/app/livechat/imports/server/rest/rooms.ts +++ b/apps/meteor/app/livechat/imports/server/rest/rooms.ts @@ -40,7 +40,7 @@ API.v1.addRoute( const hasAgentAccess = (await hasPermissionAsync(this.userId, 'view-l-room')) && agents?.includes(this.userId) && agents?.length === 1; if (!hasAdminAccess && !hasAgentAccess) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } let parsedCf: { [key: string]: string } | undefined = undefined; diff --git a/apps/meteor/app/livechat/imports/server/rest/upload.ts b/apps/meteor/app/livechat/imports/server/rest/upload.ts index 14db8f20afcf..86c815cce72c 100644 --- a/apps/meteor/app/livechat/imports/server/rest/upload.ts +++ b/apps/meteor/app/livechat/imports/server/rest/upload.ts @@ -11,7 +11,7 @@ import { sendFileLivechatMessage } from '../../../server/methods/sendFileLivecha API.v1.addRoute('livechat/upload/:rid', { async post() { if (!this.request.headers['x-visitor-token']) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const canUpload = settings.get('Livechat_fileupload_enabled') && settings.get('FileUpload_Enabled'); @@ -26,12 +26,12 @@ API.v1.addRoute('livechat/upload/:rid', { const visitor = await LivechatVisitors.getVisitorByToken(visitorToken as string, {}); if (!visitor) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const room = await LivechatRooms.findOneOpenByRoomIdAndVisitorToken(this.urlParams.rid, visitorToken as string); if (!room) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const maxFileSize = settings.get('FileUpload_MaxFileSize') || 104857600; diff --git a/apps/meteor/app/livechat/imports/server/rest/users.ts b/apps/meteor/app/livechat/imports/server/rest/users.ts index 0ed8bf187cd9..85ceb5103687 100644 --- a/apps/meteor/app/livechat/imports/server/rest/users.ts +++ b/apps/meteor/app/livechat/imports/server/rest/users.ts @@ -35,7 +35,7 @@ API.v1.addRoute( if (this.urlParams.type === 'agent') { if (!(await hasAtLeastOnePermissionAsync(this.userId, ['transfer-livechat-guest', 'edit-omnichannel-contact']))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { onlyAvailable, excludeId, showIdleAgents } = this.queryParams; @@ -55,7 +55,7 @@ API.v1.addRoute( } if (this.urlParams.type === 'manager') { if (!(await hasAtLeastOnePermissionAsync(this.userId, ['view-livechat-manager']))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } return API.v1.success( diff --git a/apps/meteor/app/livechat/server/api/v1/agent.ts b/apps/meteor/app/livechat/server/api/v1/agent.ts index 6c583e1b24db..0cd3139bf6a5 100644 --- a/apps/meteor/app/livechat/server/api/v1/agent.ts +++ b/apps/meteor/app/livechat/server/api/v1/agent.ts @@ -100,7 +100,7 @@ API.v1.addRoute( if (agentId !== this.userId) { if (!(await hasPermissionAsync(this.userId, 'manage-livechat-agents'))) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } // Silent fail for admins when BH is closed diff --git a/apps/meteor/app/livechat/server/api/v1/room.ts b/apps/meteor/app/livechat/server/api/v1/room.ts index b46a8c3e0663..63a7c20784c2 100644 --- a/apps/meteor/app/livechat/server/api/v1/room.ts +++ b/apps/meteor/app/livechat/server/api/v1/room.ts @@ -386,7 +386,7 @@ API.v1.addRoute( (!room.servedBy || room.servedBy._id !== this.userId) && !(await hasPermissionAsync(this.userId, 'save-others-livechat-room-info')) ) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } if (room.sms) { diff --git a/apps/meteor/ee/server/api/sessions.ts b/apps/meteor/ee/server/api/sessions.ts index f1ae8b2ccd46..f4ab45be6462 100644 --- a/apps/meteor/ee/server/api/sessions.ts +++ b/apps/meteor/ee/server/api/sessions.ts @@ -86,7 +86,7 @@ API.v1.addRoute( { async get() { if (!License.hasModule('device-management')) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { offset, count } = await getPaginationItems(this.queryParams); @@ -109,7 +109,7 @@ API.v1.addRoute( { async get() { if (!License.hasModule('device-management')) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { sessionId } = this.queryParams; @@ -128,7 +128,7 @@ API.v1.addRoute( { async post() { if (!License.hasModule('device-management')) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { sessionId } = this.bodyParams; @@ -154,7 +154,7 @@ API.v1.addRoute( { async get() { if (!License.hasModule('device-management')) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { offset, count } = await getPaginationItems(this.queryParams); @@ -194,7 +194,7 @@ API.v1.addRoute( { async get() { if (!License.hasModule('device-management')) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const sessionId = this.queryParams?.sessionId as string; @@ -213,7 +213,7 @@ API.v1.addRoute( { async post() { if (!License.hasModule('device-management')) { - return API.v1.unauthorized(); + return API.v1.forbidden(); } const { sessionId } = this.bodyParams; diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index a2ea92add96e..947d3dfde7c0 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -154,7 +154,7 @@ describe('LIVECHAT - rooms', () => { .expect(403) .expect((res: Response) => { expect(res.body).to.have.property('success', false); - expect(res.body.error).to.be.equal('unauthorized'); + expect(res.body).to.have.property('error', 'unauthorized'); }); await restorePermissionToRoles('view-livechat-rooms'); From b4ce5797b7fc52e851aa4afc54c4617fc12cbf72 Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Mon, 23 Dec 2024 12:28:19 -0300 Subject: [PATCH 2/7] fix: New livechat conversations are not assigned to contact manager (#34210) --- .changeset/popular-cameras-grin.md | 6 ++ .../app/livechat/server/lib/LivechatTyped.ts | 7 +- .../hooks/handleNextAgentPreferredEvents.ts | 44 ++++++----- apps/meteor/lib/callbacks.ts | 6 +- apps/meteor/server/models/raw/Users.js | 4 +- .../end-to-end/api/livechat/24-routing.ts | 75 +++++++++++++++++-- .../model-typings/src/models/IUsersModel.ts | 6 +- 7 files changed, 118 insertions(+), 30 deletions(-) create mode 100644 .changeset/popular-cameras-grin.md diff --git a/.changeset/popular-cameras-grin.md b/.changeset/popular-cameras-grin.md new file mode 100644 index 000000000000..4ad9f6239f33 --- /dev/null +++ b/.changeset/popular-cameras-grin.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +Fixes livechat conversations not being assigned to the contact manager even when the "Assign new conversations to the contact manager" setting is enabled diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index b94b070537ea..6b3a1370c7b9 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -357,7 +357,12 @@ class LivechatClass { throw new Error('error-contact-channel-blocked'); } - const defaultAgent = await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, visitor); + const defaultAgent = + agent ?? + (await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, { + visitorId: visitor._id, + source: roomInfo.source, + })); // if no department selected verify if there is at least one active and pick the first if (!defaultAgent && !visitor.department) { const department = await getRequiredDepartment(); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts index 7146b8df4e4c..5dc13cfce1e0 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -1,29 +1,33 @@ import type { IUser, SelectedAgent } from '@rocket.chat/core-typings'; -import { LivechatVisitors, LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models'; +import { LivechatVisitors, LivechatContacts, LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models'; import { notifyOnLivechatInquiryChanged } from '../../../../../app/lib/server/lib/notifyListener'; import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager'; +import { migrateVisitorIfMissingContact } from '../../../../../app/livechat/server/lib/contacts/migrateVisitorIfMissingContact'; import { settings } from '../../../../../app/settings/server'; import { callbacks } from '../../../../../lib/callbacks'; let contactManagerPreferred = false; let lastChattedAgentPreferred = false; -const normalizeDefaultAgent = (agent?: Pick | null): SelectedAgent | null => { +const normalizeDefaultAgent = (agent?: Pick | null): SelectedAgent | undefined => { if (!agent) { - return null; + return undefined; } const { _id: agentId, username } = agent; return { agentId, username }; }; -const getDefaultAgent = async (username?: string): Promise => { - if (!username) { - return null; +const getDefaultAgent = async ({ username, id }: { username?: string; id?: string }): Promise => { + if (!username && !id) { + return undefined; } - return normalizeDefaultAgent(await Users.findOneOnlineAgentByUserList(username, { projection: { _id: 1, username: 1 } })); + if (id) { + return normalizeDefaultAgent(await Users.findOneOnlineAgentById(id, undefined, { projection: { _id: 1, username: 1 } })); + } + return normalizeDefaultAgent(await Users.findOneOnlineAgentByUserList(username || [], { projection: { _id: 1, username: 1 } })); }; settings.watch('Livechat_last_chatted_agent_routing', (value) => { @@ -88,30 +92,32 @@ settings.watch('Omnichannel_contact_manager_routing', (value) => { callbacks.add( 'livechat.checkDefaultAgentOnNewRoom', - async (defaultAgent, defaultGuest) => { - if (defaultAgent || !defaultGuest) { + async (defaultAgent, { visitorId, source } = {}) => { + if (defaultAgent || !visitorId || !source) { return defaultAgent; } - const { _id: guestId } = defaultGuest; - const guest = await LivechatVisitors.findOneEnabledById(guestId, { + const guest = await LivechatVisitors.findOneEnabledById(visitorId, { projection: { lastAgent: 1, token: 1, contactManager: 1 }, }); if (!guest) { - return defaultAgent; + return undefined; } - const { lastAgent, token, contactManager } = guest; - const guestManager = contactManager?.username && contactManagerPreferred && getDefaultAgent(contactManager?.username); + const contactId = await migrateVisitorIfMissingContact(visitorId, source); + const contact = contactId ? await LivechatContacts.findOneById(contactId, { projection: { contactManager: 1 } }) : undefined; + + const guestManager = contactManagerPreferred && (await getDefaultAgent({ id: contact?.contactManager })); if (guestManager) { return guestManager; } if (!lastChattedAgentPreferred) { - return defaultAgent; + return undefined; } - const guestAgent = lastAgent?.username && getDefaultAgent(lastAgent?.username); + const { lastAgent, token } = guest; + const guestAgent = await getDefaultAgent({ username: lastAgent?.username }); if (guestAgent) { return guestAgent; } @@ -120,19 +126,19 @@ callbacks.add( projection: { servedBy: 1 }, }); if (!room?.servedBy) { - return defaultAgent; + return undefined; } const { servedBy: { username: usernameByRoom }, } = room; if (!usernameByRoom) { - return defaultAgent; + return undefined; } const lastRoomAgent = normalizeDefaultAgent( await Users.findOneOnlineAgentByUserList(usernameByRoom, { projection: { _id: 1, username: 1 } }), ); - return lastRoomAgent ?? defaultAgent; + return lastRoomAgent; }, callbacks.priority.MEDIUM, 'livechat-check-default-agent-new-room', diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 901c8101e034..746c56401b91 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -24,6 +24,7 @@ import type { IOmnichannelRoomInfo, IOmnichannelInquiryExtraData, IOmnichannelRoomExtraData, + IOmnichannelSource, } from '@rocket.chat/core-typings'; import type { Updater } from '@rocket.chat/models'; import type { FilterOperators } from 'mongodb'; @@ -118,7 +119,10 @@ type ChainedCallbackSignatures = { ) => Promise; 'livechat.beforeRouteChat': (inquiry: ILivechatInquiryRecord, agent?: { agentId: string; username: string }) => ILivechatInquiryRecord; - 'livechat.checkDefaultAgentOnNewRoom': (agent: SelectedAgent, visitor?: ILivechatVisitor) => SelectedAgent | null; + 'livechat.checkDefaultAgentOnNewRoom': ( + defaultAgent?: SelectedAgent, + params?: { visitorId?: string; source?: IOmnichannelSource }, + ) => SelectedAgent | undefined; 'livechat.onLoadForwardDepartmentRestrictions': (params: { departmentId: string }) => Record; diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 04df04af5939..aa8d481c86e0 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -1657,11 +1657,11 @@ export class UsersRaw extends BaseRaw { return this.findOne(query); } - findOneOnlineAgentById(_id, isLivechatEnabledWhenAgentIdle) { + findOneOnlineAgentById(_id, isLivechatEnabledWhenAgentIdle, options) { // TODO: Create class Agent const query = queryStatusAgentOnline({ _id }, isLivechatEnabledWhenAgentIdle); - return this.findOne(query); + return this.findOne(query, options); } findAgents() { diff --git a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts index fb1301341069..582d9c096b77 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts @@ -1,9 +1,11 @@ +import { faker } from '@faker-js/faker'; import type { Credentials } from '@rocket.chat/api-client'; -import { UserStatus, type ILivechatDepartment, type IUser } from '@rocket.chat/core-typings'; +import { UserStatus } from '@rocket.chat/core-typings'; +import type { ILivechatDepartment, IUser } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { after, before, describe, it } from 'mocha'; -import { getCredentials, request, api } from '../../../data/api-data'; +import { getCredentials, request, api, credentials } from '../../../data/api-data'; import { createAgent, makeAgentAvailable, @@ -33,7 +35,9 @@ import { IS_EE } from '../../../e2e/config/constants'; let testUser: { user: IUser; credentials: Credentials }; let testUser2: { user: IUser; credentials: Credentials }; + let testUser3: { user: IUser; credentials: Credentials }; let testDepartment: ILivechatDepartment; + let visitorEmail: string; before(async () => { const user = await createUser(); @@ -60,14 +64,43 @@ import { IS_EE } from '../../../e2e/config/constants'; }); before(async () => { - testDepartment = await createDepartment([{ agentId: testUser.user._id }]); + const user = await createUser(); + await createAgent(user.username); + const credentials3 = await login(user.username, password); + await makeAgentAvailable(credentials3); + + testUser3 = { + user, + credentials: credentials3, + }; }); - after(async () => { - await deleteUser(testUser.user); - await deleteUser(testUser2.user); + before(async () => { + testDepartment = await createDepartment([{ agentId: testUser.user._id }, { agentId: testUser3.user._id }]); + await updateSetting('Livechat_assign_new_conversation_to_bot', true); + + const visitorName = faker.person.fullName(); + visitorEmail = faker.internet.email().toLowerCase(); + await request + .post(api('omnichannel/contacts')) + .set(credentials) + .send({ + name: visitorName, + emails: [visitorEmail], + phones: [], + contactManager: testUser3.user._id, + }); }); + after(async () => + Promise.all([ + deleteUser(testUser.user), + deleteUser(testUser2.user), + deleteUser(testUser3.user), + updateSetting('Livechat_assign_new_conversation_to_bot', false), + ]), + ); + it('should route a room to an available agent', async () => { const visitor = await createVisitor(testDepartment._id); const room = await createLivechatRoom(visitor.token); @@ -91,9 +124,24 @@ import { IS_EE } from '../../../e2e/config/constants'; expect(roomInfo.servedBy).to.be.an('object'); expect(roomInfo.servedBy?._id).to.not.be.equal(testUser2.user._id); }); + (IS_EE ? it : it.skip)( + 'should route to contact manager if it is online and Livechat_assign_new_conversation_to_bot is enabled', + async () => { + const visitor = await createVisitor(testDepartment._id, faker.person.fullName(), visitorEmail); + const room = await createLivechatRoom(visitor.token); + + await sleep(5000); + + const roomInfo = await getLivechatRoomInfo(room._id); + + expect(roomInfo.servedBy).to.be.an('object'); + expect(roomInfo.servedBy?._id).to.be.equal(testUser3.user._id); + }, + ); it('should fail to start a conversation if there is noone available and Livechat_accept_chats_with_no_agents is false', async () => { await updateSetting('Livechat_accept_chats_with_no_agents', false); await makeAgentUnavailable(testUser.credentials); + await makeAgentUnavailable(testUser3.credentials); const visitor = await createVisitor(testDepartment._id); const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }).expect(400); @@ -147,6 +195,21 @@ import { IS_EE } from '../../../e2e/config/constants'; const roomInfo = await getLivechatRoomInfo(room._id); expect(roomInfo.servedBy).to.be.undefined; }); + (IS_EE ? it : it.skip)( + 'should route to another available agent if contact manager is unavailable and Livechat_assign_new_conversation_to_bot is enabled', + async () => { + await makeAgentAvailable(testUser.credentials); + const visitor = await createVisitor(testDepartment._id, faker.person.fullName(), visitorEmail); + const room = await createLivechatRoom(visitor.token); + + await sleep(5000); + + const roomInfo = await getLivechatRoomInfo(room._id); + + expect(roomInfo.servedBy).to.be.an('object'); + expect(roomInfo.servedBy?._id).to.be.equal(testUser.user._id); + }, + ); }); describe('Load Balancing', () => { before(async () => { diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index 407006596ba6..e7a6acd94180 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -263,7 +263,11 @@ export interface IUsersModel extends IBaseModel { findOnlineAgents(agentId?: string): FindCursor; countOnlineAgents(agentId: string): Promise; findOneBotAgent(): Promise; - findOneOnlineAgentById(agentId: string, isLivechatEnabledWhenAgentIdle?: boolean): Promise; + findOneOnlineAgentById( + agentId: string, + isLivechatEnabledWhenAgentIdle?: boolean, + options?: FindOptions, + ): Promise; findAgents(): FindCursor; countAgents(): Promise; getNextAgent(ignoreAgentId?: string, extraQuery?: Filter): Promise<{ agentId: string; username: string } | null>; From b62b0a665b3c572786f65e9007fc846fb1a59f4b Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Mon, 23 Dec 2024 13:58:07 -0600 Subject: [PATCH 3/7] chore: Update translation of `view-livechat-rooms` (#34275) --- packages/i18n/src/locales/af.i18n.json | 1 - packages/i18n/src/locales/ar.i18n.json | 1 - packages/i18n/src/locales/az.i18n.json | 1 - packages/i18n/src/locales/be-BY.i18n.json | 1 - packages/i18n/src/locales/bg.i18n.json | 1 - packages/i18n/src/locales/bs.i18n.json | 1 - packages/i18n/src/locales/ca.i18n.json | 1 - packages/i18n/src/locales/cs.i18n.json | 1 - packages/i18n/src/locales/cy.i18n.json | 1 - packages/i18n/src/locales/da.i18n.json | 1 - packages/i18n/src/locales/de-AT.i18n.json | 1 - packages/i18n/src/locales/de-IN.i18n.json | 1 - packages/i18n/src/locales/de.i18n.json | 1 - packages/i18n/src/locales/el.i18n.json | 1 - packages/i18n/src/locales/en.i18n.json | 2 +- packages/i18n/src/locales/eo.i18n.json | 1 - packages/i18n/src/locales/es.i18n.json | 2 +- packages/i18n/src/locales/fa.i18n.json | 1 - packages/i18n/src/locales/fi.i18n.json | 1 - packages/i18n/src/locales/fr.i18n.json | 1 - packages/i18n/src/locales/hi-IN.i18n.json | 1 - packages/i18n/src/locales/hr.i18n.json | 1 - packages/i18n/src/locales/hu.i18n.json | 1 - packages/i18n/src/locales/id.i18n.json | 1 - packages/i18n/src/locales/it.i18n.json | 1 - packages/i18n/src/locales/ja.i18n.json | 1 - packages/i18n/src/locales/ka-GE.i18n.json | 1 - packages/i18n/src/locales/km.i18n.json | 1 - packages/i18n/src/locales/ko.i18n.json | 1 - packages/i18n/src/locales/ku.i18n.json | 1 - packages/i18n/src/locales/lo.i18n.json | 1 - packages/i18n/src/locales/lt.i18n.json | 1 - packages/i18n/src/locales/lv.i18n.json | 1 - packages/i18n/src/locales/mn.i18n.json | 1 - packages/i18n/src/locales/ms-MY.i18n.json | 1 - packages/i18n/src/locales/nl.i18n.json | 1 - packages/i18n/src/locales/nn.i18n.json | 1 - packages/i18n/src/locales/no.i18n.json | 1 - packages/i18n/src/locales/pl.i18n.json | 1 - packages/i18n/src/locales/pt-BR.i18n.json | 2 +- packages/i18n/src/locales/pt.i18n.json | 1 - packages/i18n/src/locales/ro.i18n.json | 1 - packages/i18n/src/locales/ru.i18n.json | 1 - packages/i18n/src/locales/se.i18n.json | 1 - packages/i18n/src/locales/sk-SK.i18n.json | 1 - packages/i18n/src/locales/sl-SI.i18n.json | 1 - packages/i18n/src/locales/sq.i18n.json | 1 - packages/i18n/src/locales/sr.i18n.json | 1 - packages/i18n/src/locales/sv.i18n.json | 1 - packages/i18n/src/locales/ta-IN.i18n.json | 1 - packages/i18n/src/locales/th-TH.i18n.json | 1 - packages/i18n/src/locales/uk.i18n.json | 1 - packages/i18n/src/locales/vi-VN.i18n.json | 1 - packages/i18n/src/locales/zh-HK.i18n.json | 1 - packages/i18n/src/locales/zh-TW.i18n.json | 1 - packages/i18n/src/locales/zh.i18n.json | 1 - 56 files changed, 3 insertions(+), 56 deletions(-) diff --git a/packages/i18n/src/locales/af.i18n.json b/packages/i18n/src/locales/af.i18n.json index 20c862010cb6..f5557eda0c5c 100644 --- a/packages/i18n/src/locales/af.i18n.json +++ b/packages/i18n/src/locales/af.i18n.json @@ -2629,7 +2629,6 @@ "view-l-room_description": "Toestemming om livechat kanale te sien", "view-livechat-manager": "Bekyk Livechat Manager", "view-livechat-manager_description": "Toestemming om ander livechat bestuurders te bekyk", - "view-livechat-rooms": "Kyk Livechat Rooms", "view-livechat-rooms_description": "Toestemming om ander livechat kanale te sien", "view-logs": "Bekyk logs", "view-logs_description": "Toestemming om die bediener logs te besigtig", diff --git a/packages/i18n/src/locales/ar.i18n.json b/packages/i18n/src/locales/ar.i18n.json index 7af11923ae03..823124a1c87f 100644 --- a/packages/i18n/src/locales/ar.i18n.json +++ b/packages/i18n/src/locales/ar.i18n.json @@ -4571,7 +4571,6 @@ "view-livechat-room-closed-same-department_description": "إذن لعرض غرف الدردشة المباشرة المغلقة من قِبل وكيل آخر في نفس القسم", "view-livechat-room-customfields": "عرض الحقول المخصصة لـ Room القناة متعددة الاتجاهات", "view-livechat-room-customfields_description": "إذن لعرض الحقول المخصصة لغرفة الدردشة المباشرة", - "view-livechat-rooms": "عرض Rooms القناة متعددة الاتجاهات", "view-livechat-rooms_description": "إذن لعرض غرف القناة متعددة الاتجاهات الأخرى", "view-livechat-triggers": "عرض مشغلات القناة متعددة الاتجاهات", "view-livechat-triggers_description": "إذن لعرض مشغلات الدردشة المباشرة", diff --git a/packages/i18n/src/locales/az.i18n.json b/packages/i18n/src/locales/az.i18n.json index 1fa9ee0cea70..d08a533c19b7 100644 --- a/packages/i18n/src/locales/az.i18n.json +++ b/packages/i18n/src/locales/az.i18n.json @@ -2629,7 +2629,6 @@ "view-l-room_description": "Livechat kanallarını keçirmək üçün icazə", "view-livechat-manager": "Liveçat menecerinə baxın", "view-livechat-manager_description": "Digər livechat menecerlərini görmək icazəsi", - "view-livechat-rooms": "Livechat otaqlarına baxın", "view-livechat-rooms_description": "Digər livechat kanallarını görmək icazəsi", "view-logs": "Günlükləri bax", "view-logs_description": "Server jurnallarını görmək icazəsi", diff --git a/packages/i18n/src/locales/be-BY.i18n.json b/packages/i18n/src/locales/be-BY.i18n.json index 2d777c73cc5f..8f19c7acdada 100644 --- a/packages/i18n/src/locales/be-BY.i18n.json +++ b/packages/i18n/src/locales/be-BY.i18n.json @@ -2647,7 +2647,6 @@ "view-l-room_description": "Дазвол для прагляду Livechat каналаў", "view-livechat-manager": "Прагледзець Livechat Менеджэр", "view-livechat-manager_description": "Дазвол для прагляду іншых мэнэджараў Livechat", - "view-livechat-rooms": "Паглядзець Livechat Пакоі", "view-livechat-rooms_description": "Дазвол для прагляду іншых каналаў Livechat", "view-logs": "прагляд часопісаў", "view-logs_description": "Дазвол для прагляду логаваў сервера", diff --git a/packages/i18n/src/locales/bg.i18n.json b/packages/i18n/src/locales/bg.i18n.json index 30158cc9114c..f7c887a99cbe 100644 --- a/packages/i18n/src/locales/bg.i18n.json +++ b/packages/i18n/src/locales/bg.i18n.json @@ -2625,7 +2625,6 @@ "view-l-room_description": "Разрешение за гледане на livechat канали", "view-livechat-manager": "Преглед на мениджъра на Livechat", "view-livechat-manager_description": "Разрешение за гледане на други мениджъри на живо", - "view-livechat-rooms": "Преглед на стаите на Livechat", "view-livechat-rooms_description": "Разрешение за гледане на други канали livechat", "view-logs": "Преглед на регистрационните файлове", "view-logs_description": "Разрешение за преглед на сървърните дневници", diff --git a/packages/i18n/src/locales/bs.i18n.json b/packages/i18n/src/locales/bs.i18n.json index 0bf165f464c9..465634477d1d 100644 --- a/packages/i18n/src/locales/bs.i18n.json +++ b/packages/i18n/src/locales/bs.i18n.json @@ -2622,7 +2622,6 @@ "view-l-room_description": "Dopuštenje za prikaz livechat kanala", "view-livechat-manager": "Pogledajte Livechat Manager", "view-livechat-manager_description": "Dopuštenje za pregled ostalih upravitelja uživo", - "view-livechat-rooms": "Pogledajte Livechat sobe", "view-livechat-rooms_description": "Dopuštenje za prikaz ostalih kanala uživo", "view-logs": "Pogledaj zapise", "view-logs_description": "Dozvola za pregledavanje zapisnika poslužitelja", diff --git a/packages/i18n/src/locales/ca.i18n.json b/packages/i18n/src/locales/ca.i18n.json index de5327ab75f5..7d5cd2b4716f 100644 --- a/packages/i18n/src/locales/ca.i18n.json +++ b/packages/i18n/src/locales/ca.i18n.json @@ -4481,7 +4481,6 @@ "view-livechat-room-closed-same-department_description": "Permís per veure sales de Livechat tancades per un altre agent de el mateix departament", "view-livechat-room-customfields": "Veure camps personalitzats de Room livechat", "view-livechat-room-customfields_description": "Permís per veure els camps personalitzats de la sala d'livechat", - "view-livechat-rooms": "Veure les sales de LiveChat", "view-livechat-rooms_description": "Permís per veure altres canals de LiveChat", "view-livechat-triggers": "Veure activadors Livechat", "view-livechat-triggers_description": "Permís per veure els activadors de livechat", diff --git a/packages/i18n/src/locales/cs.i18n.json b/packages/i18n/src/locales/cs.i18n.json index 2bce5a03c8ca..5512adf1dcbb 100644 --- a/packages/i18n/src/locales/cs.i18n.json +++ b/packages/i18n/src/locales/cs.i18n.json @@ -3792,7 +3792,6 @@ "view-livechat-queue": "Zobrazit Omnichannel frontu", "view-livechat-room-closed-by-another-agent": "Zobrazit Omnichannel místnosti zavřené jiným operátorem", "view-livechat-room-closed-same-department": "Zobrazit Omnichannel místnosti zavřené jiným operátorem ze stejného oddělení", - "view-livechat-rooms": "Zobrazit ostatní Omnichannel místnosti", "view-livechat-rooms_description": "Právo zobrazit ostatní Omnichannel místnosti", "view-livechat-unit": "Zobrazit Livechat skupiny", "view-logs": "Zobrazit logy", diff --git a/packages/i18n/src/locales/cy.i18n.json b/packages/i18n/src/locales/cy.i18n.json index 4684ca63e300..18a3b32233d8 100644 --- a/packages/i18n/src/locales/cy.i18n.json +++ b/packages/i18n/src/locales/cy.i18n.json @@ -2623,7 +2623,6 @@ "view-l-room_description": "Caniatâd i weld sianeli byw byw", "view-livechat-manager": "Gweld Rheolwr Livechat", "view-livechat-manager_description": "Caniatâd i weld rheolwyr byw byw eraill", - "view-livechat-rooms": "Gweld Ystafelloedd Livechat", "view-livechat-rooms_description": "Caniatâd i weld sianeli byw byw eraill", "view-logs": "Gweld Logiau", "view-logs_description": "Caniatâd i weld y logiau gweinydd", diff --git a/packages/i18n/src/locales/da.i18n.json b/packages/i18n/src/locales/da.i18n.json index cc9db86ea536..65a699933d7b 100644 --- a/packages/i18n/src/locales/da.i18n.json +++ b/packages/i18n/src/locales/da.i18n.json @@ -3898,7 +3898,6 @@ "view-livechat-queue": "Se omnikanal-kø", "view-livechat-room-closed-by-another-agent": "Se omnikanal-rum lukket af en anden agent", "view-livechat-room-closed-same-department": "Se omnikanal-rum er lukket af en anden agent i samme afdeling", - "view-livechat-rooms": "Se omnikanal-rum", "view-livechat-rooms_description": "Tilladelse til at se andre omnikanal-rum", "view-livechat-unit": "Se Livechat-enheder", "view-logs": "Se logfiler", diff --git a/packages/i18n/src/locales/de-AT.i18n.json b/packages/i18n/src/locales/de-AT.i18n.json index 640c9878e85e..665435e53eb1 100644 --- a/packages/i18n/src/locales/de-AT.i18n.json +++ b/packages/i18n/src/locales/de-AT.i18n.json @@ -2631,7 +2631,6 @@ "view-l-room_description": "Erlaubnis, Livechat-Kanäle anzusehen", "view-livechat-manager": "Zeigen Sie den Livechat-Manager an", "view-livechat-manager_description": "Erlaubnis, andere Livechat-Manager zu sehen", - "view-livechat-rooms": "Livechat-Räume anzeigen", "view-livechat-rooms_description": "Erlaubnis, andere Livechat-Kanäle zu sehen", "view-logs": "Protokolle anzeigen", "view-logs_description": "Berechtigung zum Anzeigen der Serverprotokolle", diff --git a/packages/i18n/src/locales/de-IN.i18n.json b/packages/i18n/src/locales/de-IN.i18n.json index bfacfea754ed..d406aee14c50 100644 --- a/packages/i18n/src/locales/de-IN.i18n.json +++ b/packages/i18n/src/locales/de-IN.i18n.json @@ -2963,7 +2963,6 @@ "view-livechat-analytics": "Livechat-Analytics anzeigen", "view-livechat-manager": "Zeige Livechat-Manager", "view-livechat-manager_description": "Berechtigung, andere Livechat-Manager zu sehen", - "view-livechat-rooms": "Zeige Livechat-Räume", "view-livechat-rooms_description": "Berechtigung, andere Livechat-Räume einzusehen", "view-logs": "Logs anzeigen", "view-logs_description": "Berechtigung, die Server-Logs anzuzeigen", diff --git a/packages/i18n/src/locales/de.i18n.json b/packages/i18n/src/locales/de.i18n.json index d3c5cb1eae9d..caaca7d212bb 100644 --- a/packages/i18n/src/locales/de.i18n.json +++ b/packages/i18n/src/locales/de.i18n.json @@ -5141,7 +5141,6 @@ "view-livechat-room-closed-same-department_description": "Berechtigung, Livechat-Räume anzuzeigen, die von einem anderen Agenten derselben Abteilung geschlossen wurden", "view-livechat-room-customfields": "Benutzerdefinierte Felder von Omnichannel-Räumen anzeigen", "view-livechat-room-customfields_description": "Berechtigung, benutzerdefinierte Felder von Livechat-Räumen anzuzeigen", - "view-livechat-rooms": "Omnichannel-Rooms anzeigen", "view-livechat-rooms_description": "Berechtigung, andere Omnichannel-Räume anzuzeigen", "view-livechat-triggers": "Omnichannel-Trigger anzeigen", "view-livechat-triggers_description": "Berechtigung, Livechat-Trigger anzuzeigen", diff --git a/packages/i18n/src/locales/el.i18n.json b/packages/i18n/src/locales/el.i18n.json index 5fc038165aa8..a9977e458f5b 100644 --- a/packages/i18n/src/locales/el.i18n.json +++ b/packages/i18n/src/locales/el.i18n.json @@ -2636,7 +2636,6 @@ "view-l-room_description": "Άδεια προβολής ζωντανών καναλιών", "view-livechat-manager": "Προβολή διαχειριστή Livechat", "view-livechat-manager_description": "Άδεια προβολής άλλων διαχειριστών livechat", - "view-livechat-rooms": "Προβολή δωματίων Livechat", "view-livechat-rooms_description": "Άδεια προβολής άλλων καναλιών livechat", "view-logs": "Προβολή αρχείων καταγραφής", "view-logs_description": "Άδεια για την προβολή των αρχείων καταγραφής διακομιστών", diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 2b955fc6c327..134bfbbee6fe 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -6026,7 +6026,7 @@ "view-livechat-room-closed-same-department_description": "Permission to view live chat rooms closed by another agent in the same department", "view-livechat-room-customfields": "View Omnichannel Room Custom Fields", "view-livechat-room-customfields_description": "Permission to view live chat room custom fields", - "view-livechat-rooms": "View Omnichannel Rooms", + "view-livechat-rooms": "View all omnichannel rooms", "view-livechat-rooms_description": "Permission to view other Omnichannel rooms", "view-livechat-triggers": "View Omnichannel Triggers", "view-livechat-triggers_description": "Permission to view live chat triggers", diff --git a/packages/i18n/src/locales/eo.i18n.json b/packages/i18n/src/locales/eo.i18n.json index 43707e5b8c6b..921a8471e7a1 100644 --- a/packages/i18n/src/locales/eo.i18n.json +++ b/packages/i18n/src/locales/eo.i18n.json @@ -2629,7 +2629,6 @@ "view-l-room_description": "Permeso por vidi livechat kanaloj", "view-livechat-manager": "Rigardu Livechat-Manager", "view-livechat-manager_description": "Permeso por vidi aliajn aktivulojn", - "view-livechat-rooms": "Vidi Salutojn de Livechat", "view-livechat-rooms_description": "Permeso por vidi aliajn aktivajn kanalojn", "view-logs": "Vidi Registrojn", "view-logs_description": "Permeso por vidi la servilojn", diff --git a/packages/i18n/src/locales/es.i18n.json b/packages/i18n/src/locales/es.i18n.json index 31b3b41aefb1..4f72a6fdd7d4 100644 --- a/packages/i18n/src/locales/es.i18n.json +++ b/packages/i18n/src/locales/es.i18n.json @@ -4551,7 +4551,7 @@ "view-livechat-room-closed-same-department_description": "Permiso para ver salas de Livechat cerradas por otro agente del mismo departamento", "view-livechat-room-customfields": "Ver campos personalizados de Room de Omnichannel", "view-livechat-room-customfields_description": "Permiso para ver los campos personalizados de la sala de Livechat", - "view-livechat-rooms": "Ver Rooms de Omnichannel", + "view-livechat-rooms": "Ver todas las salas de Omnichannel", "view-livechat-rooms_description": "Permiso para ver otras salas de Omnichannel", "view-livechat-triggers": "Ver activadores de Omnichannel", "view-livechat-triggers_description": "Permiso para ver los activadores de Livechat", diff --git a/packages/i18n/src/locales/fa.i18n.json b/packages/i18n/src/locales/fa.i18n.json index b66441d5e9a5..dbec5f2f7a03 100644 --- a/packages/i18n/src/locales/fa.i18n.json +++ b/packages/i18n/src/locales/fa.i18n.json @@ -2973,7 +2973,6 @@ "view-livechat-room-closed-by-another-agent": "مشاهده اتاق‌های کانال همه‌کاره که دیگر پشتیبان‌ها بسته اند", "view-livechat-room-closed-same-department": "مشاهده اتاق‌های کانال همه‌کاره که دیگران پشتیبان ها در این بخش بسته اند", "view-livechat-room-customfields": "مشاهده گزینه های سفارشی اتاق کانال همه‌کاره", - "view-livechat-rooms": "مشاهده اتاق‌های کانال همه‌کاره", "view-livechat-rooms_description": "مجوز مشاهده اتاق های کانال همه‌کاره دیگران", "view-livechat-triggers": "مشاهده محرک کانال همه‌کاره", "view-livechat-webhooks": "مشاهده وب‌قلاب‌های کانال همه‌کاره", diff --git a/packages/i18n/src/locales/fi.i18n.json b/packages/i18n/src/locales/fi.i18n.json index b1435822c864..c14ae7579614 100644 --- a/packages/i18n/src/locales/fi.i18n.json +++ b/packages/i18n/src/locales/fi.i18n.json @@ -5254,7 +5254,6 @@ "view-livechat-room-closed-same-department_description": "Lupa tarkastella saman osaston toisen agentin sulkemia live-chattihuoneita samalla osastolla", "view-livechat-room-customfields": "Näytä Omnichannel-huoneen Room mukautetut kentät", "view-livechat-room-customfields_description": "Lupa tarkastella live-chattihuoneen mukautettuja kenttiä", - "view-livechat-rooms": "Näytä Omnichannel-huoneet Room", "view-livechat-rooms_description": "Lupa katsella muita Omnichannel-huoneita", "view-livechat-triggers": "Näytä Omnichannel-triggerit", "view-livechat-triggers_description": "Lupa tarkastella live-chatin triggereitä", diff --git a/packages/i18n/src/locales/fr.i18n.json b/packages/i18n/src/locales/fr.i18n.json index e4208c564aaf..f87679350cac 100644 --- a/packages/i18n/src/locales/fr.i18n.json +++ b/packages/i18n/src/locales/fr.i18n.json @@ -4569,7 +4569,6 @@ "view-livechat-room-closed-same-department_description": "Autorisation d'afficher les salons Livechat fermés par un autre agent du même département", "view-livechat-room-customfields": "Afficher les champs personnalisés du salon omnicanal", "view-livechat-room-customfields_description": "Autorisation d'afficher les champs personnalisés du salon Livechat", - "view-livechat-rooms": "Voir les salons omnicanaux", "view-livechat-rooms_description": "Autorisation d'afficher d'autres salons omnicanaux", "view-livechat-triggers": "Afficher les déclencheurs omnicanaux", "view-livechat-triggers_description": "Autorisation d'afficher les déclencheurs Livechat", diff --git a/packages/i18n/src/locales/hi-IN.i18n.json b/packages/i18n/src/locales/hi-IN.i18n.json index 35dd69ec28ac..51c14ebf42c3 100644 --- a/packages/i18n/src/locales/hi-IN.i18n.json +++ b/packages/i18n/src/locales/hi-IN.i18n.json @@ -5533,7 +5533,6 @@ "view-livechat-room-closed-same-department_description": "उसी विभाग में किसी अन्य एजेंट द्वारा बंद किए गए लाइव चैट रूम देखने की अनुमति", "view-livechat-room-customfields": "ओमनीचैनल कक्ष कस्टम फ़ील्ड देखें", "view-livechat-room-customfields_description": "लाइव चैट रूम कस्टम फ़ील्ड देखने की अनुमति", - "view-livechat-rooms": "ओमनीचैनल कमरे देखें", "view-livechat-rooms_description": "अन्य ओमनीचैनल कमरे देखने की अनुमति", "view-livechat-triggers": "ओमनीचैनल ट्रिगर देखें", "view-livechat-triggers_description": "लाइव चैट ट्रिगर देखने की अनुमति", diff --git a/packages/i18n/src/locales/hr.i18n.json b/packages/i18n/src/locales/hr.i18n.json index b0c457211515..6d36f66fa205 100644 --- a/packages/i18n/src/locales/hr.i18n.json +++ b/packages/i18n/src/locales/hr.i18n.json @@ -2762,7 +2762,6 @@ "view-l-room_description": "Dopuštenje za prikaz livechat kanala", "view-livechat-manager": "Pogledajte Livechat Manager", "view-livechat-manager_description": "Dopuštenje za pregled ostalih upravitelja uživo", - "view-livechat-rooms": "Pogledajte Livechat sobe", "view-livechat-rooms_description": "Dopuštenje za prikaz ostalih kanala uživo", "view-logs": "Pogledaj zapise", "view-logs_description": "Dozvola za pregledavanje zapisnika poslužitelja", diff --git a/packages/i18n/src/locales/hu.i18n.json b/packages/i18n/src/locales/hu.i18n.json index 9f2ed6f082aa..632ae3629a39 100644 --- a/packages/i18n/src/locales/hu.i18n.json +++ b/packages/i18n/src/locales/hu.i18n.json @@ -5060,7 +5060,6 @@ "view-livechat-room-closed-same-department_description": "Jogosultság ugyanazon a részlegen lévő más ügyintéző által lezárt élő csevegési szobák megtekintéséhez", "view-livechat-room-customfields": "Összcsatornás szoba egyéni mezőinek megtekintése", "view-livechat-room-customfields_description": "Jogosultság az élő csevegési szoba egyéni mezőinek megtekintéséhez", - "view-livechat-rooms": "Összcsatornás szobák megtekintése", "view-livechat-rooms_description": "Jogosultság más összcsatornás szobák megtekintéséhez", "view-livechat-triggers": "Összcsatornás aktiválók megtekintése", "view-livechat-triggers_description": "Jogosultság az élő csevegés aktiválóinak megtekintéséhez", diff --git a/packages/i18n/src/locales/id.i18n.json b/packages/i18n/src/locales/id.i18n.json index ffe250cfba91..7474e94d8b4c 100644 --- a/packages/i18n/src/locales/id.i18n.json +++ b/packages/i18n/src/locales/id.i18n.json @@ -2637,7 +2637,6 @@ "view-l-room_description": "Izin untuk melihat saluran livechat", "view-livechat-manager": "Lihat Manajer Livechat", "view-livechat-manager_description": "Izin untuk melihat manajer livechat lainnya", - "view-livechat-rooms": "Lihat Kamar Livechat", "view-livechat-rooms_description": "Izin untuk melihat saluran livechat lainnya", "view-logs": "Lihat Log", "view-logs_description": "Izin untuk melihat log server", diff --git a/packages/i18n/src/locales/it.i18n.json b/packages/i18n/src/locales/it.i18n.json index e83bc48163e3..857015aa9f4c 100644 --- a/packages/i18n/src/locales/it.i18n.json +++ b/packages/i18n/src/locales/it.i18n.json @@ -3214,7 +3214,6 @@ "view-l-room_description": "Autorizzazione a vedere i canali di livechat", "view-livechat-manager": "Visualizza Livechat Manager", "view-livechat-manager_description": "Autorizzazione a visualizzare altri gestori di livechat", - "view-livechat-rooms": "Visualizza Livechat Rooms", "view-livechat-rooms_description": "Autorizzazione a vedere altri canali di livechat", "view-logs": "Visualizza i registri", "view-logs_description": "Autorizzazione a visualizzare i registri del server", diff --git a/packages/i18n/src/locales/ja.i18n.json b/packages/i18n/src/locales/ja.i18n.json index f25cd0eff21f..ff6d86c07db6 100644 --- a/packages/i18n/src/locales/ja.i18n.json +++ b/packages/i18n/src/locales/ja.i18n.json @@ -4512,7 +4512,6 @@ "view-livechat-room-closed-same-department_description": "同じ部署の別のエージェントによって閉じられたライブチャットルームを表示する権限", "view-livechat-room-customfields": "オムニチャネルRoomのカスタムフィールドの表示", "view-livechat-room-customfields_description": "ライブチャットルームのカスタムフィールドを表示する権限", - "view-livechat-rooms": "オムニチャネルRoomの表示", "view-livechat-rooms_description": "他のオムニチャネルルームを表示する権限", "view-livechat-triggers": "オムニチャネルのトリガーの表示", "view-livechat-triggers_description": "ライブチャットのトリガーを表示する権限", diff --git a/packages/i18n/src/locales/ka-GE.i18n.json b/packages/i18n/src/locales/ka-GE.i18n.json index 2d966c7aa3c4..f61ccf6dc352 100644 --- a/packages/i18n/src/locales/ka-GE.i18n.json +++ b/packages/i18n/src/locales/ka-GE.i18n.json @@ -3519,7 +3519,6 @@ "view-livechat-queue": "იხილეთ Omnichannel-ის რიგი", "view-livechat-room-closed-by-another-agent": "იხილეთ სხვა აგენტის მიერ დახურული Omnichannel ოთახები ", "view-livechat-room-closed-same-department": "იხილეთ იმავე განყოფილებაში სხვა აგენტის მიერ დახურული Omnichannel ოთახები ", - "view-livechat-rooms": "Omnichannel ოთახების ნახვა", "view-livechat-rooms_description": "სხვა Omnichannel ოთახების ნახვის უფლება", "view-livechat-unit": "იხილეთ Livechat იუნიტები", "view-logs": "ლოგების ნახვა", diff --git a/packages/i18n/src/locales/km.i18n.json b/packages/i18n/src/locales/km.i18n.json index d867a8ba6f79..e5b867b27b06 100644 --- a/packages/i18n/src/locales/km.i18n.json +++ b/packages/i18n/src/locales/km.i18n.json @@ -2978,7 +2978,6 @@ "view-livechat-analytics": "មើល LiveChat វិភាគ", "view-livechat-manager": "មើលកម្មវិធីគ្រប់គ្រង Livechat", "view-livechat-manager_description": "ការអនុញ្ញាតមើលអ្នកគ្រប់គ្រង livechat ផ្សេងទៀត", - "view-livechat-rooms": "មើលបន្ទប់ Livechat", "view-livechat-rooms_description": "ការអនុញ្ញាតឱ្យមើលឆានែល livechat ផ្សេងទៀត", "view-logs": "មើលកំណត់ហេតុ", "view-logs_description": "ការអនុញ្ញាតមើលកំណត់ត្រាម៉ាស៊ីនមេ", diff --git a/packages/i18n/src/locales/ko.i18n.json b/packages/i18n/src/locales/ko.i18n.json index 44f57d2b30a5..4b2178cb1080 100644 --- a/packages/i18n/src/locales/ko.i18n.json +++ b/packages/i18n/src/locales/ko.i18n.json @@ -3849,7 +3849,6 @@ "view-livechat-queue": "실시간상담대기열보기", "view-livechat-room-closed-by-another-agent": "다른 상담사가 종료한 실시간상담 Room보기", "view-livechat-room-closed-same-department": "같은 부서의 다른 상담사가 종료한 실시간상담 Room보기", - "view-livechat-rooms": "실시간상담 대화방 보기", "view-livechat-rooms_description": "다른 실시간상담 대화방을 볼 수 있는 권한", "view-livechat-unit": "Livechat 유닛 보기", "view-logs": "로그 보기", diff --git a/packages/i18n/src/locales/ku.i18n.json b/packages/i18n/src/locales/ku.i18n.json index 7409ecd08d0c..ba39af95227e 100644 --- a/packages/i18n/src/locales/ku.i18n.json +++ b/packages/i18n/src/locales/ku.i18n.json @@ -2622,7 +2622,6 @@ "view-l-room_description": "Destûra ku kanalên livechat bibînin", "view-livechat-manager": "View Livechat Manager", "view-livechat-manager_description": "Destûra birêveberên din ên bijîşk ên dîtar", - "view-livechat-rooms": "View Livechat Rooms", "view-livechat-rooms_description": "Destûrkirina kanalên din ên livechat binêre", "view-logs": "View Logs", "view-logs_description": "Destûra sazkirina têketinê serverê", diff --git a/packages/i18n/src/locales/lo.i18n.json b/packages/i18n/src/locales/lo.i18n.json index 6f424d51917d..e25ff7f355e0 100644 --- a/packages/i18n/src/locales/lo.i18n.json +++ b/packages/i18n/src/locales/lo.i18n.json @@ -2666,7 +2666,6 @@ "view-l-room_description": "ການອະນຸຍາດໃຫ້ເບິ່ງຊ່ອງ livechat", "view-livechat-manager": "ເບິ່ງ Livechat Manager", "view-livechat-manager_description": "ການອະນຸຍາດໃຫ້ເບິ່ງຜູ້ຈັດການ livechat ອື່ນໆ", - "view-livechat-rooms": "ເບິ່ງຫ້ອງ Livechat", "view-livechat-rooms_description": "ການອະນຸຍາດໃຫ້ເບິ່ງຊ່ອງອື່ນໆທີ່ມີ Livechat", "view-logs": "ເບິ່ງບັນທຶກ", "view-logs_description": "ການອະນຸຍາດໃຫ້ເບິ່ງບັນທຶກຂອງເຄື່ອງແມ່ຂ່າຍ", diff --git a/packages/i18n/src/locales/lt.i18n.json b/packages/i18n/src/locales/lt.i18n.json index 9afd8e50612a..ac3e88478209 100644 --- a/packages/i18n/src/locales/lt.i18n.json +++ b/packages/i18n/src/locales/lt.i18n.json @@ -2684,7 +2684,6 @@ "view-l-room_description": "Leidimas peržiūrėti livechat kanalus", "view-livechat-manager": "Peržiūrėkite \"Livechat Manager\"", "view-livechat-manager_description": "Leidimas peržiūrėti kitus livechat vadovus", - "view-livechat-rooms": "Peržiūrėti \"Livechat\" kambarius", "view-livechat-rooms_description": "Leidimas peržiūrėti kitus livechat kanalus", "view-logs": "Žiūrėti žurnalus", "view-logs_description": "Leidimas peržiūrėti serverio žurnalus", diff --git a/packages/i18n/src/locales/lv.i18n.json b/packages/i18n/src/locales/lv.i18n.json index a02e61a2547b..6cd5136bc393 100644 --- a/packages/i18n/src/locales/lv.i18n.json +++ b/packages/i18n/src/locales/lv.i18n.json @@ -2627,7 +2627,6 @@ "view-l-room_description": "Atļauja skatīties livechat kanālus", "view-livechat-manager": "Skatīt Livechat pārvaldnieku", "view-livechat-manager_description": "Atļauja skatīt citus livechat vadītājus", - "view-livechat-rooms": "skatīt Livechat istabas", "view-livechat-rooms_description": "Atļauja skatīt citus livechat kanālus", "view-logs": "Skatīt žurnālus", "view-logs_description": "Atļauja skatīt servera žurnālus", diff --git a/packages/i18n/src/locales/mn.i18n.json b/packages/i18n/src/locales/mn.i18n.json index ead45a7ed408..ab4cd9b568e8 100644 --- a/packages/i18n/src/locales/mn.i18n.json +++ b/packages/i18n/src/locales/mn.i18n.json @@ -2619,7 +2619,6 @@ "view-l-room_description": "Livechat суваг харах зөвшөөрөл", "view-livechat-manager": "Livechat менежерийг харах", "view-livechat-manager_description": "Бусад livechat менежерүүдийг харах зөвшөөрөл", - "view-livechat-rooms": "Livechat өрөө харах", "view-livechat-rooms_description": "Бусад livechat сувгийг харах зөвшөөрөл", "view-logs": "Бүртгэлийг харах", "view-logs_description": "Серверийн бүртгэлийг үзэх зөвшөөрөл", diff --git a/packages/i18n/src/locales/ms-MY.i18n.json b/packages/i18n/src/locales/ms-MY.i18n.json index 7970c7121244..28d58124f53a 100644 --- a/packages/i18n/src/locales/ms-MY.i18n.json +++ b/packages/i18n/src/locales/ms-MY.i18n.json @@ -2633,7 +2633,6 @@ "view-l-room_description": "Kebenaran untuk melihat saluran livechat", "view-livechat-manager": "Lihat Pengurus Livechat", "view-livechat-manager_description": "Kebenaran untuk melihat pengurus livechat lain", - "view-livechat-rooms": "Lihat bilik Livechat", "view-livechat-rooms_description": "Kebenaran untuk melihat saluran livechat lain", "view-logs": "Lihat Log", "view-logs_description": "Kebenaran untuk melihat log pelayan", diff --git a/packages/i18n/src/locales/nl.i18n.json b/packages/i18n/src/locales/nl.i18n.json index 0dba59dc7418..d5ac9dba83c5 100644 --- a/packages/i18n/src/locales/nl.i18n.json +++ b/packages/i18n/src/locales/nl.i18n.json @@ -4554,7 +4554,6 @@ "view-livechat-room-closed-same-department_description": "Toestemming om livechatkamers te bekijken die door een andere agent op de zelfde afdeling gesloten zijn", "view-livechat-room-customfields": "Bekijk aangepaste velden van omnichannel-kamers", "view-livechat-room-customfields_description": "Toestemming om aangepaste velden voor livechatkamers te bekijken", - "view-livechat-rooms": "Bekijk omnichannel-kamers", "view-livechat-rooms_description": "Toestemming om andere omnichannel-kamers te bekijken", "view-livechat-triggers": "Bekijk omnichannel-triggers", "view-livechat-triggers_description": "Toestemming om livechat-triggers te bekijken", diff --git a/packages/i18n/src/locales/nn.i18n.json b/packages/i18n/src/locales/nn.i18n.json index 6637364b8265..61c45461f9dc 100644 --- a/packages/i18n/src/locales/nn.i18n.json +++ b/packages/i18n/src/locales/nn.i18n.json @@ -4325,7 +4325,6 @@ "onboarding.page.awaitingConfirmation.subtitle": "Vi har sendt deg en e-post til {{emailAddress}} med en bekreftelseslenke. Vennligst bekreft at sikkerhetskoden nedenfor samsvarer med den i e-posten.", "view-livechat-manager": "Se Livechat Manager", "view-livechat-manager_description": "Tillatelse til å vise andre livechat-ledere", - "view-livechat-rooms": "Se Livechat-rom", "view-livechat-rooms_description": "Tillatelse til å vise andre livechat kanaler", "view-logs": "Se logger", "view-logs_description": "Tillatelse til å vise serverloggene", diff --git a/packages/i18n/src/locales/no.i18n.json b/packages/i18n/src/locales/no.i18n.json index 39a50fbad692..3eb0a8d29d5e 100644 --- a/packages/i18n/src/locales/no.i18n.json +++ b/packages/i18n/src/locales/no.i18n.json @@ -4325,7 +4325,6 @@ "onboarding.page.awaitingConfirmation.subtitle": "Vi har sendt deg en e-post til {{emailAddress}} med en bekreftelseslenke. Vennligst bekreft at sikkerhetskoden nedenfor samsvarer med den i e-posten.", "view-livechat-manager": "Se Livechat Manager", "view-livechat-manager_description": "Tillatelse til å vise andre livechat-ledere", - "view-livechat-rooms": "Se Livechat-rom", "view-livechat-rooms_description": "Tillatelse til å vise andre livechat kanaler", "view-logs": "Se logger", "view-logs_description": "Tillatelse til å vise serverloggene", diff --git a/packages/i18n/src/locales/pl.i18n.json b/packages/i18n/src/locales/pl.i18n.json index f43c37775fa3..309da935057a 100644 --- a/packages/i18n/src/locales/pl.i18n.json +++ b/packages/i18n/src/locales/pl.i18n.json @@ -5049,7 +5049,6 @@ "view-livechat-room-closed-same-department_description": "Uprawnienie do przeglądania pokojów livechat zamkniętych przez innego agenta z tego samego departamentu", "view-livechat-room-customfields": "Wyświetl pola niestandardowe dla kanału omnichannel", "view-livechat-room-customfields_description": "Uprawnienie do wyświetlania pól niestandardowych pokoju livechat", - "view-livechat-rooms": "Wyświetl pokoje Livechat", "view-livechat-rooms_description": "Zezwolenie na oglądanie innych kanałów na żywo", "view-livechat-triggers": "Zobacz wyzwalacze omnichannel", "view-livechat-triggers_description": "Uprawnienie do wyświetlania wyzwalaczy livechat", diff --git a/packages/i18n/src/locales/pt-BR.i18n.json b/packages/i18n/src/locales/pt-BR.i18n.json index 700115f75f30..f77743f7f3f8 100644 --- a/packages/i18n/src/locales/pt-BR.i18n.json +++ b/packages/i18n/src/locales/pt-BR.i18n.json @@ -4736,7 +4736,7 @@ "view-livechat-room-closed-same-department_description": "Permissão para ver salas de livechat encerradas por outro agente no mesmo departamento", "view-livechat-room-customfields": "Ver campos personalizados da sala do omnichannel", "view-livechat-room-customfields_description": "Permissão para ver campos personalizados do livechat", - "view-livechat-rooms": "Ver salas do omnichannel", + "view-livechat-rooms": "Ver todas as salas do omnichannel", "view-livechat-rooms_description": "Permissão para ver outras salas do omnichannel", "view-livechat-triggers": "Ver gatilhos do omnichannel", "view-livechat-triggers_description": "Permissão para ver gatilhos do livechat", diff --git a/packages/i18n/src/locales/pt.i18n.json b/packages/i18n/src/locales/pt.i18n.json index 342f1ca8291c..81c7f112ded8 100644 --- a/packages/i18n/src/locales/pt.i18n.json +++ b/packages/i18n/src/locales/pt.i18n.json @@ -3032,7 +3032,6 @@ "view-livechat-analytics": "Ver o Livechat Analytics", "view-livechat-manager": "Ver o administrador de conferência", "view-livechat-manager_description": "Permissão para ver outros administradores de conferência", - "view-livechat-rooms": "Ver salas de conferência", "view-livechat-rooms_description": "Permissão para ver outros canais de conferência", "view-logs": "Ver registos", "view-logs_description": "Permissão para visualizar os registos do servidor", diff --git a/packages/i18n/src/locales/ro.i18n.json b/packages/i18n/src/locales/ro.i18n.json index 418d92307063..8510de00334e 100644 --- a/packages/i18n/src/locales/ro.i18n.json +++ b/packages/i18n/src/locales/ro.i18n.json @@ -2626,7 +2626,6 @@ "view-l-room_description": "Permisiune pentru vizualizarea canalelor livechat", "view-livechat-manager": "Vedeți managerul Livechat", "view-livechat-manager_description": "Permisiune pentru a vedea alți manageri livechat", - "view-livechat-rooms": "Vedeți camerele Livechat", "view-livechat-rooms_description": "Permisiune pentru a vedea alte canale livechat", "view-logs": "Vizualizați jurnalele", "view-logs_description": "Permisiunea de a vizualiza jurnalele serverului", diff --git a/packages/i18n/src/locales/ru.i18n.json b/packages/i18n/src/locales/ru.i18n.json index dd8b95fdb143..29865211631a 100644 --- a/packages/i18n/src/locales/ru.i18n.json +++ b/packages/i18n/src/locales/ru.i18n.json @@ -4755,7 +4755,6 @@ "view-livechat-room-closed-same-department_description": "Разрешение на просмотр чатов, закрытых другим агентом в том же отделе", "view-livechat-room-customfields": "Просмотр пользовательских полей чатов Omnichannel", "view-livechat-room-customfields_description": "Разрешение на просмотр пользовательских полей Livechat", - "view-livechat-rooms": "Просматривать LiveChat комнаты", "view-livechat-rooms_description": "Разрешение на просмотр других каналов LiveChat", "view-livechat-triggers": "Просмотр триггеров Omnichannel", "view-livechat-triggers_description": "Разрешение на просмотр триггеров Livechat", diff --git a/packages/i18n/src/locales/se.i18n.json b/packages/i18n/src/locales/se.i18n.json index 2c2512c9aba6..1d0359279977 100644 --- a/packages/i18n/src/locales/se.i18n.json +++ b/packages/i18n/src/locales/se.i18n.json @@ -5890,7 +5890,6 @@ "view-livechat-room-closed-same-department_description": "Permission to view live chat rooms closed by another agent in the same department", "view-livechat-room-customfields": "View Omnichannel Room Custom Fields", "view-livechat-room-customfields_description": "Permission to view live chat room custom fields", - "view-livechat-rooms": "View Omnichannel Rooms", "view-livechat-rooms_description": "Permission to view other Omnichannel rooms", "view-livechat-triggers": "View Omnichannel Triggers", "view-livechat-triggers_description": "Permission to view live chat triggers", diff --git a/packages/i18n/src/locales/sk-SK.i18n.json b/packages/i18n/src/locales/sk-SK.i18n.json index 83d2e8f0991e..a4fdc5d9f8ff 100644 --- a/packages/i18n/src/locales/sk-SK.i18n.json +++ b/packages/i18n/src/locales/sk-SK.i18n.json @@ -2637,7 +2637,6 @@ "view-l-room_description": "Povolenie na zobrazenie kanálov livechat", "view-livechat-manager": "Zobraziť Správcu Livechat", "view-livechat-manager_description": "Povolenie na zobrazenie iných manažérov livechat", - "view-livechat-rooms": "Zobrazenie miestností služby Livechat", "view-livechat-rooms_description": "Povolenie na zobrazenie iných kanálov livechat", "view-logs": "Zobraziť denníky", "view-logs_description": "Povolenie na zobrazenie protokolov servera", diff --git a/packages/i18n/src/locales/sl-SI.i18n.json b/packages/i18n/src/locales/sl-SI.i18n.json index 96ebd4c74c5d..4152f3cd5789 100644 --- a/packages/i18n/src/locales/sl-SI.i18n.json +++ b/packages/i18n/src/locales/sl-SI.i18n.json @@ -2617,7 +2617,6 @@ "view-l-room_description": "Dovoljenje za ogled Livechat kanalov", "view-livechat-manager": "Oglejte si upravitelja klepeta v živo", "view-livechat-manager_description": "Dovoljenje za ogled drugih upraviteljev klepeta v živo", - "view-livechat-rooms": "Oglejte si sobe Livechat", "view-livechat-rooms_description": "Dovoljenje za ogled drugih kanalov s klepetom v živo", "view-logs": "Oglejte si dnevnike", "view-logs_description": "Dovoljenje za ogled strežniških dnevnikov", diff --git a/packages/i18n/src/locales/sq.i18n.json b/packages/i18n/src/locales/sq.i18n.json index 13eee087d88a..5fd31bf8b243 100644 --- a/packages/i18n/src/locales/sq.i18n.json +++ b/packages/i18n/src/locales/sq.i18n.json @@ -2627,7 +2627,6 @@ "view-l-room_description": "Leja për të parë kanalet livechat", "view-livechat-manager": "Shikoni Livechat Manager", "view-livechat-manager_description": "Leja për të parë menaxherët e tjerë të drejtpërdrejtë", - "view-livechat-rooms": "Shiko Dhomat Livechat", "view-livechat-rooms_description": "Leje për të parë kanalet e tjera livechat", "view-logs": "Shiko Ditaret", "view-logs_description": "Leja për të parë shkrimet e serverit", diff --git a/packages/i18n/src/locales/sr.i18n.json b/packages/i18n/src/locales/sr.i18n.json index 79ee67501920..abc26ba1f7fe 100644 --- a/packages/i18n/src/locales/sr.i18n.json +++ b/packages/i18n/src/locales/sr.i18n.json @@ -2422,7 +2422,6 @@ "view-l-room_description": "Дозвола за преглед ливецхат канала", "view-livechat-manager": "Погледајте Ливецхат менаџера", "view-livechat-manager_description": "Дозвола за преглед других менаџера ливецхат", - "view-livechat-rooms": "Погледајте Ливецхат собе", "view-livechat-rooms_description": "Дозвола за преглед других ливецхат канала", "view-logs": "Виев Логс", "view-logs_description": "Дозвола за преглед дневника сервера", diff --git a/packages/i18n/src/locales/sv.i18n.json b/packages/i18n/src/locales/sv.i18n.json index d097e915b2e4..4009e1799cb3 100644 --- a/packages/i18n/src/locales/sv.i18n.json +++ b/packages/i18n/src/locales/sv.i18n.json @@ -5262,7 +5262,6 @@ "view-livechat-room-closed-same-department_description": "Behörighet att visa livechattrum som stängts av en annan agent inom samma avdelning", "view-livechat-room-customfields": "Visa anpassade fält för Omnichannel-rum", "view-livechat-room-customfields_description": "Behörighet att visa anpassade fält för livechattrum", - "view-livechat-rooms": "Visa Omnichannel-rum", "view-livechat-rooms_description": "Tillstånd att se andra Omnichannel-rum", "view-livechat-triggers": "Visa utlösare för Omnichannel", "view-livechat-triggers_description": "Behörighet att visa utlösare för livechatt", diff --git a/packages/i18n/src/locales/ta-IN.i18n.json b/packages/i18n/src/locales/ta-IN.i18n.json index 3cae91078be8..23d967468ead 100644 --- a/packages/i18n/src/locales/ta-IN.i18n.json +++ b/packages/i18n/src/locales/ta-IN.i18n.json @@ -2628,7 +2628,6 @@ "view-l-room_description": "Livechat சேனல்களைக் காண அனுமதி", "view-livechat-manager": "Livechat மேலாளரைக் காண்க", "view-livechat-manager_description": "மற்ற livechat மேலாளர்களைக் காண அனுமதி", - "view-livechat-rooms": "லைவ் ஸ்காட் மனைகளைக் காண்க", "view-livechat-rooms_description": "மற்ற livechat சேனல்களை காண அனுமதி", "view-logs": "பதிவுகள் காண்க", "view-logs_description": "சர்வர் பதிவுகளைப் பார்க்க அனுமதி", diff --git a/packages/i18n/src/locales/th-TH.i18n.json b/packages/i18n/src/locales/th-TH.i18n.json index db864b2a0ebd..6f05da3fada6 100644 --- a/packages/i18n/src/locales/th-TH.i18n.json +++ b/packages/i18n/src/locales/th-TH.i18n.json @@ -2615,7 +2615,6 @@ "view-l-room_description": "อนุญาตให้ดูแชแนล livechat", "view-livechat-manager": "ดูผู้จัดการ Livechat", "view-livechat-manager_description": "อนุญาตให้ดูผู้จัดการผู้เล่น livechat คนอื่น ๆ", - "view-livechat-rooms": "ดูห้องพัก Livechat", "view-livechat-rooms_description": "อนุญาตให้ดูแชแนล livechat อื่น ๆ", "view-logs": "ดูบันทึก", "view-logs_description": "อนุญาตให้ดูบันทึกของเซิร์ฟเวอร์", diff --git a/packages/i18n/src/locales/uk.i18n.json b/packages/i18n/src/locales/uk.i18n.json index 601d1299f02c..4c221ad883d1 100644 --- a/packages/i18n/src/locales/uk.i18n.json +++ b/packages/i18n/src/locales/uk.i18n.json @@ -3203,7 +3203,6 @@ "view-livechat-analytics": "Переглянути аналітику Livechat", "view-livechat-manager": "Переглянути менеджер Livechat", "view-livechat-manager_description": "Дозвіл на перегляд інших менеджерів livechat", - "view-livechat-rooms": "Переглянути номери \"Livechat\"", "view-livechat-rooms_description": "Дозвіл на перегляд інших каналів livechat", "view-logs": "Переглянути журнали", "view-logs_description": "Дозвіл на перегляд журналів сервера", diff --git a/packages/i18n/src/locales/vi-VN.i18n.json b/packages/i18n/src/locales/vi-VN.i18n.json index f9c8848aa328..06df1045587d 100644 --- a/packages/i18n/src/locales/vi-VN.i18n.json +++ b/packages/i18n/src/locales/vi-VN.i18n.json @@ -2725,7 +2725,6 @@ "view-l-room_description": "Cho phép xem kênh livechat", "view-livechat-manager": "Xem trình quản lý Livechat", "view-livechat-manager_description": "Cho phép xem các nhà quản lý livechat khác", - "view-livechat-rooms": "Xem các phòng Livechat", "view-livechat-rooms_description": "Cho phép xem kênh livechat khác", "view-logs": "Xem các bản ghi", "view-logs_description": "Cho phép xem nhật ký máy chủ", diff --git a/packages/i18n/src/locales/zh-HK.i18n.json b/packages/i18n/src/locales/zh-HK.i18n.json index af7e8495f868..199711204cb1 100644 --- a/packages/i18n/src/locales/zh-HK.i18n.json +++ b/packages/i18n/src/locales/zh-HK.i18n.json @@ -2649,7 +2649,6 @@ "view-l-room_description": "允许查看实时聊天频道", "view-livechat-manager": "查看Livechat经理", "view-livechat-manager_description": "允许查看其他Livechat经理", - "view-livechat-rooms": "查看即时聊天室", "view-livechat-rooms_description": "允许查看其他实时聊天频道", "view-logs": "查看日志", "view-logs_description": "查看服务器日志的权限", diff --git a/packages/i18n/src/locales/zh-TW.i18n.json b/packages/i18n/src/locales/zh-TW.i18n.json index 37864a815f40..52cc3bf04b2f 100644 --- a/packages/i18n/src/locales/zh-TW.i18n.json +++ b/packages/i18n/src/locales/zh-TW.i18n.json @@ -4292,7 +4292,6 @@ "view-livechat-queue": "檢視即時聊天佇列", "view-livechat-room-closed-by-another-agent": "查看被另一個代理關閉的Omnichannel Room", "view-livechat-room-closed-same-department": "查看由同一部門中的另一個代理關閉的Omnichannel Room", - "view-livechat-rooms": "檢視即時聊天 Room", "view-livechat-rooms_description": "允許檢視其他即時聊天頻道", "view-livechat-unit": "檢視即時聊天單位", "view-logs": "檢視日誌", diff --git a/packages/i18n/src/locales/zh.i18n.json b/packages/i18n/src/locales/zh.i18n.json index 530ab418cdeb..435a8032cf40 100644 --- a/packages/i18n/src/locales/zh.i18n.json +++ b/packages/i18n/src/locales/zh.i18n.json @@ -3948,7 +3948,6 @@ "view-livechat-queue": "查看 Omnichannel 队列", "view-livechat-room-closed-by-another-agent": "查看其他客服关闭的 Omnichannel 聊天室", "view-livechat-room-closed-same-department": "查看其他同部门客服关闭的 Omnichannel 聊天室", - "view-livechat-rooms": "查看 Omnichannel 聊天室", "view-livechat-rooms_description": "查看其他 Omnichannel 聊天室的权限", "view-livechat-unit": "查看 Livechat 单位", "view-logs": "查看日志", From d37433b9589efec51603a1ee79f067bceb459759 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 23 Dec 2024 17:04:12 -0300 Subject: [PATCH 4/7] refactor: Move add and remove role code out of Roles model (#34488) --- apps/meteor/app/api/server/v1/roles.ts | 3 +- .../server/methods/addUserToRole.ts | 3 +- .../server/methods/removeUserFromRole.ts | 3 +- .../incoming/addIncomingIntegration.ts | 5 +- .../incoming/updateIncomingIntegration.ts | 5 +- .../functions/relinquishRoomOwnerships.ts | 5 +- apps/meteor/server/lib/roles/addUserRoles.ts | 37 ++++++++++-- .../server/lib/roles/removeUserFromRoles.ts | 30 ++++++++-- .../meteor/server/methods/afterVerifyEmail.ts | 9 ++- apps/meteor/server/models/raw/Roles.ts | 56 ------------------- .../model-typings/src/models/IRolesModel.ts | 2 - 11 files changed, 78 insertions(+), 80 deletions(-) diff --git a/apps/meteor/app/api/server/v1/roles.ts b/apps/meteor/app/api/server/v1/roles.ts index 20f6e38f3567..b52e20e12920 100644 --- a/apps/meteor/app/api/server/v1/roles.ts +++ b/apps/meteor/app/api/server/v1/roles.ts @@ -5,6 +5,7 @@ import { isRoleAddUserToRoleProps, isRoleDeleteProps, isRoleRemoveUserFromRolePr import { check, Match } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; +import { removeUserFromRolesAsync } from '../../../../server/lib/roles/removeUserFromRoles'; import { getUsersInRolePaginated } from '../../../authorization/server/functions/getUsersInRole'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { hasRoleAsync, hasAnyRoleAsync } from '../../../authorization/server/functions/hasRole'; @@ -221,7 +222,7 @@ API.v1.addRoute( } } - await Roles.removeUserRoles(user._id, [role._id], scope); + await removeUserFromRolesAsync(user._id, [role._id], scope); if (settings.get('UI_DisplayRoles')) { void api.broadcast('user.roleUpdate', { diff --git a/apps/meteor/app/authorization/server/methods/addUserToRole.ts b/apps/meteor/app/authorization/server/methods/addUserToRole.ts index 81582dd7e9fb..6f26c4a61461 100644 --- a/apps/meteor/app/authorization/server/methods/addUserToRole.ts +++ b/apps/meteor/app/authorization/server/methods/addUserToRole.ts @@ -4,6 +4,7 @@ import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Roles, Users } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; +import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../functions/hasPermission'; @@ -75,7 +76,7 @@ Meteor.methods({ }); } - const add = await Roles.addUserRoles(user._id, [role._id], scope); + const add = await addUserRolesAsync(user._id, [role._id], scope); if (settings.get('UI_DisplayRoles')) { void api.broadcast('user.roleUpdate', { diff --git a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts index 56f0c2e307ab..77f8c54e2f49 100644 --- a/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts +++ b/apps/meteor/app/authorization/server/methods/removeUserFromRole.ts @@ -4,6 +4,7 @@ import type { ServerMethods } from '@rocket.chat/ddp-client'; import { Roles, Users } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; +import { removeUserFromRolesAsync } from '../../../../server/lib/roles/removeUserFromRoles'; import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; import { settings } from '../../../settings/server'; import { hasPermissionAsync } from '../functions/hasPermission'; @@ -79,7 +80,7 @@ Meteor.methods({ } } - const remove = await Roles.removeUserRoles(user._id, [role._id], scope); + const remove = await removeUserFromRolesAsync(user._id, [role._id], scope); const event = { type: 'removed', _id: role._id, diff --git a/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts index 8f1f1b5dd576..eaa3b98fd629 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/addIncomingIntegration.ts @@ -1,12 +1,13 @@ import type { INewIncomingIntegration, IIncomingIntegration } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ddp-client'; -import { Integrations, Roles, Subscriptions, Users, Rooms } from '@rocket.chat/models'; +import { Integrations, Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { Random } from '@rocket.chat/random'; import { Babel } from 'meteor/babel-compiler'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; +import { addUserRolesAsync } from '../../../../../server/lib/roles/addUserRoles'; import { hasPermissionAsync, hasAllPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { notifyOnIntegrationChanged } from '../../../../lib/server/lib/notifyListener'; import { validateScriptEngine, isScriptEngineFrozen } from '../../lib/validateScriptEngine'; @@ -154,7 +155,7 @@ export const addIncomingIntegration = async (userId: string, integration: INewIn } } - await Roles.addUserRoles(user._id, ['bot']); + await addUserRolesAsync(user._id, ['bot']); const { insertedId } = await Integrations.insertOne(integrationData); diff --git a/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts b/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts index b4ef5b9bf973..cdf0d4465309 100644 --- a/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts +++ b/apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts @@ -1,11 +1,12 @@ import type { IIntegration, INewIncomingIntegration, IUpdateIncomingIntegration } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ddp-client'; -import { Integrations, Roles, Subscriptions, Users, Rooms } from '@rocket.chat/models'; +import { Integrations, Subscriptions, Users, Rooms } from '@rocket.chat/models'; import { wrapExceptions } from '@rocket.chat/tools'; import { Babel } from 'meteor/babel-compiler'; import { Meteor } from 'meteor/meteor'; import _ from 'underscore'; +import { addUserRolesAsync } from '../../../../../server/lib/roles/addUserRoles'; import { hasAllPermissionAsync, hasPermissionAsync } from '../../../../authorization/server/functions/hasPermission'; import { notifyOnIntegrationChanged } from '../../../../lib/server/lib/notifyListener'; import { isScriptEngineFrozen, validateScriptEngine } from '../../lib/validateScriptEngine'; @@ -164,7 +165,7 @@ Meteor.methods({ }); } - await Roles.addUserRoles(user._id, ['bot']); + await addUserRolesAsync(user._id, ['bot']); const updatedIntegration = await Integrations.findOneAndUpdate( { _id: integrationId }, diff --git a/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts b/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts index 2d53b9b55c7c..a62f893319a9 100644 --- a/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts +++ b/apps/meteor/app/lib/server/functions/relinquishRoomOwnerships.ts @@ -1,6 +1,7 @@ -import { Messages, Roles, Rooms, Subscriptions, ReadReceipts } from '@rocket.chat/models'; +import { Messages, Rooms, Subscriptions, ReadReceipts } from '@rocket.chat/models'; import type { SubscribedRoomsForUserWithDetails } from './getRoomsWithSingleOwner'; +import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles'; import { FileUpload } from '../../../file-upload/server'; import { notifyOnSubscriptionChanged } from '../lib/notifyListener'; @@ -29,7 +30,7 @@ export const relinquishRoomOwnerships = async function ( const changeOwner = subscribedRooms.filter(({ shouldChangeOwner }) => shouldChangeOwner); for await (const { newOwner, rid } of changeOwner) { - newOwner && (await Roles.addUserRoles(newOwner, ['owner'], rid)); + newOwner && (await addUserRolesAsync(newOwner, ['owner'], rid)); } const roomIdsToRemove: string[] = subscribedRooms.filter(({ shouldBeRemoved }) => shouldBeRemoved).map(({ rid }) => rid); diff --git a/apps/meteor/server/lib/roles/addUserRoles.ts b/apps/meteor/server/lib/roles/addUserRoles.ts index a064553f5cb4..1333f0af6237 100644 --- a/apps/meteor/server/lib/roles/addUserRoles.ts +++ b/apps/meteor/server/lib/roles/addUserRoles.ts @@ -1,18 +1,45 @@ import { MeteorError } from '@rocket.chat/core-services'; import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings'; -import { Roles } from '@rocket.chat/models'; +import { Roles, Subscriptions, Users } from '@rocket.chat/models'; import { validateRoleList } from './validateRoleList'; +import { notifyOnSubscriptionChangedByRoomIdAndUserId } from '../../../app/lib/server/lib/notifyListener'; -export const addUserRolesAsync = async (userId: IUser['_id'], roleIds: IRole['_id'][], scope?: IRoom['_id']): Promise => { - if (!userId || !roleIds?.length) { +export const addUserRolesAsync = async (userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise => { + if (!userId || !roles?.length) { return false; } - if (!(await validateRoleList(roleIds))) { + if (!(await validateRoleList(roles))) { throw new MeteorError('error-invalid-role', 'Invalid role'); } - await Roles.addUserRoles(userId, roleIds, scope); + if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { + throw new Error('Roles.addUserRoles method received a role scope instead of a scope value.'); + } + + if (!Array.isArray(roles)) { + roles = [roles]; + process.env.NODE_ENV === 'development' && console.warn('[WARN] RolesRaw.addUserRoles: roles should be an array'); + } + + for await (const roleId of roles) { + const role = await Roles.findOneById>(roleId, { projection: { scope: 1 } }); + + if (!role) { + process.env.NODE_ENV === 'development' && console.warn(`[WARN] RolesRaw.addUserRoles: role: ${roleId} not found`); + continue; + } + + if (role.scope === 'Subscriptions' && scope) { + const addRolesResponse = await Subscriptions.addRolesByUserId(userId, [role._id], scope); + if (addRolesResponse.modifiedCount) { + void notifyOnSubscriptionChangedByRoomIdAndUserId(scope, userId); + } + } else { + await Users.addRolesByUserId(userId, [role._id]); + } + } + return true; }; diff --git a/apps/meteor/server/lib/roles/removeUserFromRoles.ts b/apps/meteor/server/lib/roles/removeUserFromRoles.ts index 2145fd9aee52..de12ec91e66a 100644 --- a/apps/meteor/server/lib/roles/removeUserFromRoles.ts +++ b/apps/meteor/server/lib/roles/removeUserFromRoles.ts @@ -1,11 +1,12 @@ import { MeteorError } from '@rocket.chat/core-services'; import type { IRole, IUser, IRoom } from '@rocket.chat/core-typings'; -import { Users, Roles } from '@rocket.chat/models'; +import { Users, Subscriptions, Roles } from '@rocket.chat/models'; import { validateRoleList } from './validateRoleList'; +import { notifyOnSubscriptionChangedByRoomIdAndUserId } from '../../../app/lib/server/lib/notifyListener'; -export const removeUserFromRolesAsync = async (userId: IUser['_id'], roleIds: IRole['_id'][], scope?: IRoom['_id']): Promise => { - if (!userId || !roleIds) { +export const removeUserFromRolesAsync = async (userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise => { + if (!userId || !roles) { return false; } @@ -14,10 +15,29 @@ export const removeUserFromRolesAsync = async (userId: IUser['_id'], roleIds: IR throw new MeteorError('error-invalid-user', 'Invalid user'); } - if (!(await validateRoleList(roleIds))) { + if (!(await validateRoleList(roles))) { throw new MeteorError('error-invalid-role', 'Invalid role'); } - await Roles.removeUserRoles(userId, roleIds, scope); + if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { + throw new Error('Roles.removeUserRoles method received a role scope instead of a scope value.'); + } + + for await (const roleId of roles) { + const role = await Roles.findOneById>(roleId, { projection: { scope: 1 } }); + if (!role) { + continue; + } + + if (role.scope === 'Subscriptions' && scope) { + const removeRolesResponse = await Subscriptions.removeRolesByUserId(userId, [roleId], scope); + if (removeRolesResponse.modifiedCount) { + void notifyOnSubscriptionChangedByRoomIdAndUserId(scope, userId); + } + } else { + await Users.removeRolesByUserId(userId, [roleId]); + } + } + return true; }; diff --git a/apps/meteor/server/methods/afterVerifyEmail.ts b/apps/meteor/server/methods/afterVerifyEmail.ts index 06bf389b0a03..9e1589c1895a 100644 --- a/apps/meteor/server/methods/afterVerifyEmail.ts +++ b/apps/meteor/server/methods/afterVerifyEmail.ts @@ -1,8 +1,11 @@ import type { IRole } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ddp-client'; -import { Roles, Users } from '@rocket.chat/models'; +import { Users } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; +import { addUserRolesAsync } from '../lib/roles/addUserRoles'; +import { removeUserFromRolesAsync } from '../lib/roles/removeUserFromRoles'; + const rolesToChangeTo: Map = new Map([['anonymous', ['user']]]); declare module '@rocket.chat/ddp-client' { @@ -33,9 +36,9 @@ Meteor.methods({ rolesThatNeedChanges.map(async (role) => { const rolesToAdd = rolesToChangeTo.get(role); if (rolesToAdd) { - await Roles.addUserRoles(userId, rolesToAdd); + await addUserRolesAsync(userId, rolesToAdd); } - await Roles.removeUserRoles(user._id, [role]); + await removeUserFromRolesAsync(user._id, [role]); }), ); } diff --git a/apps/meteor/server/models/raw/Roles.ts b/apps/meteor/server/models/raw/Roles.ts index 36fc7674416f..fad0312bc52f 100644 --- a/apps/meteor/server/models/raw/Roles.ts +++ b/apps/meteor/server/models/raw/Roles.ts @@ -4,7 +4,6 @@ import { Subscriptions, Users } from '@rocket.chat/models'; import type { Collection, FindCursor, Db, Filter, FindOptions, Document, CountDocumentsOptions } from 'mongodb'; import { BaseRaw } from './BaseRaw'; -import { notifyOnSubscriptionChangedByRoomIdAndUserId } from '../../../app/lib/server/lib/notifyListener'; export class RolesRaw extends BaseRaw implements IRolesModel { constructor(db: Db, trash?: Collection>) { @@ -19,37 +18,6 @@ export class RolesRaw extends BaseRaw implements IRolesModel { return options ? this.find(query, options) : this.find(query); } - async addUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise { - if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { - throw new Error('Roles.addUserRoles method received a role scope instead of a scope value.'); - } - - if (!Array.isArray(roles)) { - roles = [roles]; - process.env.NODE_ENV === 'development' && console.warn('[WARN] RolesRaw.addUserRoles: roles should be an array'); - } - - for await (const roleId of roles) { - const role = await this.findOneById>(roleId, { projection: { scope: 1 } }); - - if (!role) { - process.env.NODE_ENV === 'development' && console.warn(`[WARN] RolesRaw.addUserRoles: role: ${roleId} not found`); - continue; - } - - if (role.scope === 'Subscriptions' && scope) { - // TODO remove dependency from other models - this logic should be inside a function/service - const addRolesResponse = await Subscriptions.addRolesByUserId(userId, [role._id], scope); - if (addRolesResponse.modifiedCount) { - void notifyOnSubscriptionChangedByRoomIdAndUserId(scope, userId); - } - } else { - await Users.addRolesByUserId(userId, [role._id]); - } - } - return true; - } - async isUserInRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise { if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { throw new Error('Roles.isUserInRoles method received a role scope instead of a scope value.'); @@ -78,30 +46,6 @@ export class RolesRaw extends BaseRaw implements IRolesModel { return false; } - async removeUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise { - if (process.env.NODE_ENV === 'development' && (scope === 'Users' || scope === 'Subscriptions')) { - throw new Error('Roles.removeUserRoles method received a role scope instead of a scope value.'); - } - - for await (const roleId of roles) { - const role = await this.findOneById>(roleId, { projection: { scope: 1 } }); - - if (!role) { - continue; - } - - if (role.scope === 'Subscriptions' && scope) { - const removeRolesResponse = await Subscriptions.removeRolesByUserId(userId, [roleId], scope); - if (removeRolesResponse.modifiedCount) { - void notifyOnSubscriptionChangedByRoomIdAndUserId(scope, userId); - } - } else { - await Users.removeRolesByUserId(userId, [roleId]); - } - } - return true; - } - async findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options?: undefined): Promise; async findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options: FindOptions): Promise; diff --git a/packages/model-typings/src/models/IRolesModel.ts b/packages/model-typings/src/models/IRolesModel.ts index 84d49ef6baee..dabff59952a3 100644 --- a/packages/model-typings/src/models/IRolesModel.ts +++ b/packages/model-typings/src/models/IRolesModel.ts @@ -5,9 +5,7 @@ import type { IBaseModel } from './IBaseModel'; export interface IRolesModel extends IBaseModel { findByUpdatedDate(updatedAfterDate: Date, options?: FindOptions): FindCursor; - addUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise; isUserInRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise; - removeUserRoles(userId: IUser['_id'], roles: IRole['_id'][], scope?: IRoom['_id']): Promise; findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options?: undefined): Promise; findOneByIdOrName(_idOrName: IRole['_id'] | IRole['name'], options: FindOptions): Promise; From e5600bbe2743c964c5a12ebf156af438308a4f3f Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 23 Dec 2024 23:56:31 -0300 Subject: [PATCH 5/7] refactor: remove getOmniChatSortQuery dependency from livechat inquiry model (#34491) Co-authored-by: Guilherme Gazzo --- .../app/livechat/server/lib/QueueManager.ts | 3 ++- .../livechat-enterprise/server/lib/Helper.ts | 5 +++-- .../server/models/raw/LivechatInquiry.ts | 20 ++++++++----------- .../server/services/omnichannel/queue.ts | 3 ++- .../src/models/ILivechatInquiryModel.ts | 11 +++------- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/QueueManager.ts b/apps/meteor/app/livechat/server/lib/QueueManager.ts index fb1469bcde8b..2e846bc24d9e 100644 --- a/apps/meteor/app/livechat/server/lib/QueueManager.ts +++ b/apps/meteor/app/livechat/server/lib/QueueManager.ts @@ -33,6 +33,7 @@ import { } from '../../../lib/server/lib/notifyListener'; import { settings } from '../../../settings/server'; import { i18n } from '../../../utils/lib/i18n'; +import { getOmniChatSortQuery } from '../../lib/inquiries'; const logger = new Logger('QueueManager'); @@ -336,7 +337,7 @@ export class QueueManager { const [inq] = await LivechatInquiry.getCurrentSortedQueueAsync({ inquiryId: inquiry._id, department: room.departmentId, - queueSortBy: getInquirySortMechanismSetting(), + queueSortBy: getOmniChatSortQuery(getInquirySortMechanismSetting()), }); if (inq) { diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.ts b/apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.ts index da78d351250b..cb8503d82e76 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.ts @@ -16,6 +16,7 @@ import { OmnichannelQueueInactivityMonitor } from './QueueInactivityMonitor'; import { updateInquiryQueueSla } from './SlaHelper'; import { memoizeDebounce } from './debounceByParams'; import { logger } from './logger'; +import { getOmniChatSortQuery } from '../../../../../app/livechat/lib/inquiries'; import { getInquirySortMechanismSetting } from '../../../../../app/livechat/server/lib/settings'; import { settings } from '../../../../../app/settings/server'; import { callbacks } from '../../../../../lib/callbacks'; @@ -123,7 +124,7 @@ const dispatchWaitingQueueStatus = async (department?: string) => { const queue = await LivechatInquiry.getCurrentSortedQueueAsync({ department, - queueSortBy: getInquirySortMechanismSetting(), + queueSortBy: getOmniChatSortQuery(getInquirySortMechanismSetting()), }); if (!queue.length) { @@ -262,7 +263,7 @@ export const getLivechatQueueInfo = async (room?: IOmnichannelRoom) => { const [inq] = await LivechatInquiry.getCurrentSortedQueueAsync({ inquiryId: _id, department, - queueSortBy: getInquirySortMechanismSetting(), + queueSortBy: getOmniChatSortQuery(getInquirySortMechanismSetting()), }); if (!inq) { diff --git a/apps/meteor/server/models/raw/LivechatInquiry.ts b/apps/meteor/server/models/raw/LivechatInquiry.ts index ec5ad430b160..de5ebc069949 100644 --- a/apps/meteor/server/models/raw/LivechatInquiry.ts +++ b/apps/meteor/server/models/raw/LivechatInquiry.ts @@ -1,10 +1,4 @@ -import type { - ILivechatInquiryRecord, - IMessage, - RocketChatRecordDeleted, - OmnichannelSortingMechanismSettingType, - ILivechatPriority, -} from '@rocket.chat/core-typings'; +import type { ILivechatInquiryRecord, IMessage, RocketChatRecordDeleted, ILivechatPriority } from '@rocket.chat/core-typings'; import { LivechatInquiryStatus } from '@rocket.chat/core-typings'; import type { ILivechatInquiryModel } from '@rocket.chat/model-typings'; import type { @@ -24,7 +18,6 @@ import type { } from 'mongodb'; import { BaseRaw } from './BaseRaw'; -import { getOmniChatSortQuery } from '../../../app/livechat/lib/inquiries'; import { readSecondaryPreferred } from '../../database/readSecondaryPreferred'; export class LivechatInquiryRaw extends BaseRaw implements ILivechatInquiryModel { @@ -154,7 +147,10 @@ export class LivechatInquiryRaw extends BaseRaw implemen return updated?.value; } - async findNextAndLock(queueSortBy: OmnichannelSortingMechanismSettingType, department?: string): Promise { + async findNextAndLock( + queueSortBy: FindOptions['sort'], + department?: string, + ): Promise { const date = new Date(); const result = await this.findOneAndUpdate( { @@ -180,7 +176,7 @@ export class LivechatInquiryRaw extends BaseRaw implemen }, }, { - sort: getOmniChatSortQuery(queueSortBy), + sort: queueSortBy, }, ); @@ -212,7 +208,7 @@ export class LivechatInquiryRaw extends BaseRaw implemen }: { inquiryId?: string; department?: string; - queueSortBy: OmnichannelSortingMechanismSettingType; + queueSortBy: FindOptions['sort']; }): Promise<(Pick & { position: number })[]> { const filter: Filter[] = [ { @@ -221,7 +217,7 @@ export class LivechatInquiryRaw extends BaseRaw implemen ...(department && { department }), }, }, - { $sort: getOmniChatSortQuery(queueSortBy) }, + { $sort: queueSortBy }, { $group: { _id: 1, diff --git a/apps/meteor/server/services/omnichannel/queue.ts b/apps/meteor/server/services/omnichannel/queue.ts index 29d48b9f1f6b..ed7420e78d99 100644 --- a/apps/meteor/server/services/omnichannel/queue.ts +++ b/apps/meteor/server/services/omnichannel/queue.ts @@ -4,6 +4,7 @@ import { License } from '@rocket.chat/license'; import { LivechatInquiry, LivechatRooms } from '@rocket.chat/models'; import { queueLogger } from './logger'; +import { getOmniChatSortQuery } from '../../../app/livechat/lib/inquiries'; import { dispatchAgentDelegated } from '../../../app/livechat/server/lib/Helper'; import { RoutingManager } from '../../../app/livechat/server/lib/RoutingManager'; import { getInquirySortMechanismSetting } from '../../../app/livechat/server/lib/settings'; @@ -112,7 +113,7 @@ export class OmnichannelQueue implements IOmnichannelQueue { private async checkQueue(queue: string | undefined) { queueLogger.debug(`Processing items for queue ${queue || 'Public'}`); try { - const nextInquiry = await LivechatInquiry.findNextAndLock(getInquirySortMechanismSetting(), queue); + const nextInquiry = await LivechatInquiry.findNextAndLock(getOmniChatSortQuery(getInquirySortMechanismSetting()), queue); if (!nextInquiry) { queueLogger.debug(`No more items for queue ${queue || 'Public'}`); return; diff --git a/packages/model-typings/src/models/ILivechatInquiryModel.ts b/packages/model-typings/src/models/ILivechatInquiryModel.ts index e44ff37b6ed4..b81c37afb95a 100644 --- a/packages/model-typings/src/models/ILivechatInquiryModel.ts +++ b/packages/model-typings/src/models/ILivechatInquiryModel.ts @@ -1,9 +1,4 @@ -import type { - IMessage, - ILivechatInquiryRecord, - LivechatInquiryStatus, - OmnichannelSortingMechanismSettingType, -} from '@rocket.chat/core-typings'; +import type { IMessage, ILivechatInquiryRecord, LivechatInquiryStatus } from '@rocket.chat/core-typings'; import type { FindOptions, DistinctOptions, Document, UpdateResult, DeleteResult, FindCursor, DeleteOptions } from 'mongodb'; import type { IBaseModel } from './IBaseModel'; @@ -21,14 +16,14 @@ export interface ILivechatInquiryModel extends IBaseModel; setDepartmentByInquiryId(inquiryId: string, department: string): Promise; setLastMessageByRoomId(rid: ILivechatInquiryRecord['rid'], message: IMessage): Promise; - findNextAndLock(queueSortBy: OmnichannelSortingMechanismSettingType, department?: string): Promise; + findNextAndLock(queueSortBy: FindOptions['sort'], department?: string): Promise; unlock(inquiryId: string): Promise; unlockAndQueue(inquiryId: string): Promise; unlockAll(): Promise; getCurrentSortedQueueAsync(props: { inquiryId?: string; department?: string; - queueSortBy: OmnichannelSortingMechanismSettingType; + queueSortBy: FindOptions['sort']; }): Promise<(Pick & { position: number })[]>; removeByRoomId(rid: string, options?: DeleteOptions): Promise; getQueuedInquiries(options?: FindOptions): FindCursor; From d1d096e900404a97ecf85e37852cfa12c8c40dd3 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Mon, 23 Dec 2024 23:57:28 -0300 Subject: [PATCH 6/7] refactor: remove getDefaultSubscriptionPref dep from Subscriptions model (#34490) --- apps/meteor/app/lib/server/functions/createRoom.ts | 1 + apps/meteor/server/models/raw/Subscriptions.ts | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/meteor/app/lib/server/functions/createRoom.ts b/apps/meteor/app/lib/server/functions/createRoom.ts index bb099af8cee7..c9d5b0e31075 100644 --- a/apps/meteor/app/lib/server/functions/createRoom.ts +++ b/apps/meteor/app/lib/server/functions/createRoom.ts @@ -92,6 +92,7 @@ async function createUsersSubscriptions({ extraData: { ...extra, ...autoTranslateConfig, + ...getDefaultSubscriptionPref(member), }, }); } diff --git a/apps/meteor/server/models/raw/Subscriptions.ts b/apps/meteor/server/models/raw/Subscriptions.ts index 7b8de27693b5..2ba63aba86cf 100644 --- a/apps/meteor/server/models/raw/Subscriptions.ts +++ b/apps/meteor/server/models/raw/Subscriptions.ts @@ -34,7 +34,6 @@ import type { } from 'mongodb'; import { BaseRaw } from './BaseRaw'; -import { getDefaultSubscriptionPref } from '../../../app/utils/lib/getDefaultSubscriptionPref'; export class SubscriptionsRaw extends BaseRaw implements ISubscriptionsModel { constructor(db: Db, trash?: Collection>) { @@ -1856,7 +1855,6 @@ export class SubscriptionsRaw extends BaseRaw implements ISubscri name: user.name, }, ...(room.prid && { prid: room.prid }), - ...getDefaultSubscriptionPref(user), ...extraData, }; @@ -1894,7 +1892,6 @@ export class SubscriptionsRaw extends BaseRaw implements ISubscri name: user.name, }, ...(room.prid && { prid: room.prid }), - ...getDefaultSubscriptionPref(user), ...extraData, })); From b31e7f9d32f101b23c04d6b07440eb1971e140e4 Mon Sep 17 00:00:00 2001 From: Martin Schoeler Date: Tue, 24 Dec 2024 10:01:12 -0300 Subject: [PATCH 7/7] fix: wrong dates on last `x` date on omnichannel reports (#34113) --- .changeset/quick-rivers-ring.md | 10 ++++++++++ .../client/components/dashboards/periods.ts | 17 +++++++++++------ .../omnichannel/reports/components/constants.ts | 2 +- .../e2e/omnichannel/omnichannel-reports.spec.ts | 8 ++++---- packages/i18n/src/locales/en.i18n.json | 2 +- packages/i18n/src/locales/hi-IN.i18n.json | 1 - packages/i18n/src/locales/nn.i18n.json | 1 - packages/i18n/src/locales/no.i18n.json | 1 - packages/i18n/src/locales/pt-BR.i18n.json | 2 +- packages/i18n/src/locales/se.i18n.json | 1 - 10 files changed, 28 insertions(+), 17 deletions(-) create mode 100644 .changeset/quick-rivers-ring.md diff --git a/.changeset/quick-rivers-ring.md b/.changeset/quick-rivers-ring.md new file mode 100644 index 000000000000..d628f1b29eba --- /dev/null +++ b/.changeset/quick-rivers-ring.md @@ -0,0 +1,10 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes dates being incorrect when fetching omnichannel reports with the following time periods: + +last week +last month +last 6 months +last year diff --git a/apps/meteor/client/components/dashboards/periods.ts b/apps/meteor/client/components/dashboards/periods.ts index 6da6ab9e9bd0..824064b99d6c 100644 --- a/apps/meteor/client/components/dashboards/periods.ts +++ b/apps/meteor/client/components/dashboards/periods.ts @@ -23,6 +23,11 @@ const lastNDays = return { start, end }; }; +const getLast6Months = () => { + const last6Months = moment().subtract(6, 'months'); + return moment().diff(last6Months, 'days'); +}; + const periods = [ { key: 'today', @@ -32,7 +37,7 @@ const periods = [ { key: 'this week', label: label('This_week'), - range: lastNDays(7), + range: lastNDays(moment().day()), }, { key: 'last 7 days', @@ -47,7 +52,7 @@ const periods = [ { key: 'this month', label: label('This_month'), - range: lastNDays(30), + range: lastNDays(moment().date()), }, { key: 'last 30 days', @@ -62,12 +67,12 @@ const periods = [ { key: 'last 6 months', label: label('Last_6_months'), - range: lastNDays(180), + range: lastNDays(getLast6Months()), }, { - key: 'last year', - label: label('Last_year'), - range: lastNDays(365), + key: 'this year', + label: label('This_year'), + range: lastNDays(moment().dayOfYear()), }, ] as const; diff --git a/apps/meteor/client/omnichannel/reports/components/constants.ts b/apps/meteor/client/omnichannel/reports/components/constants.ts index c69fae35f374..a00dbe73403f 100644 --- a/apps/meteor/client/omnichannel/reports/components/constants.ts +++ b/apps/meteor/client/omnichannel/reports/components/constants.ts @@ -41,4 +41,4 @@ export const COLORS = { warning2: Palette.statusColor['status-font-on-warning-2'].toString(), }; -export const PERIOD_OPTIONS: Period['key'][] = ['today', 'this week', 'last 15 days', 'this month', 'last 6 months', 'last year']; +export const PERIOD_OPTIONS: Period['key'][] = ['today', 'this week', 'last 15 days', 'this month', 'last 6 months', 'this year']; diff --git a/apps/meteor/tests/e2e/omnichannel/omnichannel-reports.spec.ts b/apps/meteor/tests/e2e/omnichannel/omnichannel-reports.spec.ts index f336bf7eab62..2fcc6cff2ad0 100644 --- a/apps/meteor/tests/e2e/omnichannel/omnichannel-reports.spec.ts +++ b/apps/meteor/tests/e2e/omnichannel/omnichannel-reports.spec.ts @@ -90,12 +90,12 @@ test.describe.serial('Omnichannel Reports', () => { await route.fulfill({ response, json: mock }); }); - await poReports.statusSection.selectPeriod('last year'); + await poReports.statusSection.selectPeriod('this year'); await expect(poReports.statusSection.legendItem(`Closed 25 (25%)`)).toBeVisible(); await expect(poReports.statusSection.legendItem(`Open 25 (25%)`)).toBeVisible(); await expect(poReports.statusSection.legendItem(`Queued 25 (25%)`)).toBeVisible(); await expect(poReports.statusSection.legendItem(`On hold 25 (25%)`)).toBeVisible(); - await expect(poReports.statusSection.txtSummary).toHaveText('100 conversations, last year'); + await expect(poReports.statusSection.txtSummary).toHaveText('100 conversations, this year'); }); }); @@ -145,10 +145,10 @@ test.describe.serial('Omnichannel Reports', () => { await route.fulfill({ response, json: mock }); }); - await poReports.channelsSection.selectPeriod('last year'); + await poReports.channelsSection.selectPeriod('this year'); await expect(poReports.channelsSection.legendItem(`Channel 1 50 (50%)`)).toBeVisible(); await expect(poReports.channelsSection.legendItem(`Channel 2 50 (50%)`)).toBeVisible(); - await expect(poReports.channelsSection.txtSummary).toHaveText('100 conversations, last year'); + await expect(poReports.channelsSection.txtSummary).toHaveText('100 conversations, this year'); }); await test.step('More than 5 channels', async () => { diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 134bfbbee6fe..93ea90a09a3c 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -3052,7 +3052,6 @@ "Last_30_days": "Last 30 Days", "Last_90_days": "Last 90 Days", "Last_6_months": "Last 6 months", - "Last_year": "Last year", "Last_active": "Last active", "Last_Call": "Last Call", "Last_Chat": "Last Chat", @@ -4510,6 +4509,7 @@ "Readability": "Readability", "This_room_is_read_only": "This room is read only", "This_page_will_be_deprecated_soon": "This page will be deprecated soon", + "This_year": "This Year", "Only_people_with_permission_can_send_messages_here": "Only people with permission can send messages here", "Read_only_changed_successfully": "Read only changed successfully", "Read_only_channel": "Read Only Channel", diff --git a/packages/i18n/src/locales/hi-IN.i18n.json b/packages/i18n/src/locales/hi-IN.i18n.json index 51c14ebf42c3..2a88e325716d 100644 --- a/packages/i18n/src/locales/hi-IN.i18n.json +++ b/packages/i18n/src/locales/hi-IN.i18n.json @@ -2797,7 +2797,6 @@ "Last_30_days": "पिछले 30 दिनों में", "Last_90_days": "पिछले 90 दिन", "Last_6_months": "पिछले 6 महीने", - "Last_year": "पिछले साल", "Last_active": "अंतिम सक्रिय", "Last_Call": "आखिरी कॉल", "Last_Chat": "आखिरी चैट", diff --git a/packages/i18n/src/locales/nn.i18n.json b/packages/i18n/src/locales/nn.i18n.json index 61c45461f9dc..b1b7759f3d1c 100644 --- a/packages/i18n/src/locales/nn.i18n.json +++ b/packages/i18n/src/locales/nn.i18n.json @@ -2474,7 +2474,6 @@ "Last_30_days": "Siste 30 dager", "Last_90_days": "Siste 90 dager", "Last_6_months": "Siste 6 måneder", - "Last_year": "I fjor", "Last_active": "Sist aktiv", "Last_Call": "Siste samtale", "Last_login": "Siste innlogging", diff --git a/packages/i18n/src/locales/no.i18n.json b/packages/i18n/src/locales/no.i18n.json index 3eb0a8d29d5e..605893a6bfb9 100644 --- a/packages/i18n/src/locales/no.i18n.json +++ b/packages/i18n/src/locales/no.i18n.json @@ -2474,7 +2474,6 @@ "Last_30_days": "Siste 30 dager", "Last_90_days": "Siste 90 dager", "Last_6_months": "Siste 6 måneder", - "Last_year": "I fjor", "Last_active": "Sist aktiv", "Last_Call": "Siste samtale", "Last_login": "Siste innlogging", diff --git a/packages/i18n/src/locales/pt-BR.i18n.json b/packages/i18n/src/locales/pt-BR.i18n.json index f77743f7f3f8..5ec5f61650df 100644 --- a/packages/i18n/src/locales/pt-BR.i18n.json +++ b/packages/i18n/src/locales/pt-BR.i18n.json @@ -2465,7 +2465,6 @@ "Last_30_days": "Últimos 30 dias", "Last_90_days": "Últimos 90 dias", "Last_6_months": "Últimos 6 meses", - "Last_year": "Último ano", "Last_active": "Ativo pela última vez", "Last_Call": "Última chamada", "Last_Chat": "Última conversa", @@ -3533,6 +3532,7 @@ "Read_by": "Lido por", "Read_only": "Somente leitura", "This_room_is_read_only": "Esta sala é somente leitura", + "This_year": "Este ano", "Read_only_changed_successfully": "Somente leitura foi alterado com sucesso", "Read_only_channel": "Canal somente leitura", "Read_only_group": "Grupo somente de leitura", diff --git a/packages/i18n/src/locales/se.i18n.json b/packages/i18n/src/locales/se.i18n.json index 1d0359279977..f86cfc3b9472 100644 --- a/packages/i18n/src/locales/se.i18n.json +++ b/packages/i18n/src/locales/se.i18n.json @@ -2963,7 +2963,6 @@ "Last_30_days": "Last 30 Days", "Last_90_days": "Last 90 Days", "Last_6_months": "Last 6 months", - "Last_year": "Last year", "Last_active": "Last active", "Last_Call": "Last Call", "Last_Chat": "Last Chat",