Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions apps/meteor/app/livechat/imports/server/rest/departments.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { ILivechatDepartment } from '@rocket.chat/core-typings';
import { LivechatDepartment, LivechatDepartmentAgents } from '@rocket.chat/models';
import { isGETLivechatDepartmentProps, isPOSTLivechatDepartmentProps } from '@rocket.chat/rest-typings';
import {
isLivechatDepartmentDepartmentIdAgentsGETProps,
isLivechatDepartmentDepartmentIdAgentsPOSTProps,
} from '@rocket.chat/rest-typings/src/v1/omnichannel';
import { Match, check } from 'meteor/check';

import { API } from '../../../../api/server';
Expand Down Expand Up @@ -270,12 +274,10 @@ API.v1.addRoute(
GET: { permissions: ['view-livechat-departments', 'view-l-room'], operation: 'hasAny' },
POST: { permissions: ['manage-livechat-departments', 'add-livechat-department-agents'], operation: 'hasAny' },
},
validateParams: { GET: isLivechatDepartmentDepartmentIdAgentsGETProps, POST: isLivechatDepartmentDepartmentIdAgentsPOSTProps },
},
{
async get() {
check(this.urlParams, {
_id: String,
});
const { offset, count } = await getPaginationItems(this.queryParams);
const { sort } = await this.parseJsonQuery();

Expand All @@ -292,13 +294,6 @@ API.v1.addRoute(
return API.v1.success(agents);
},
async post() {
check(
this.bodyParams,
Match.ObjectIncluding({
upsert: Array,
remove: Array,
}),
);
await saveDepartmentAgents(this.urlParams._id, this.bodyParams);

return API.v1.success();
Expand Down
60 changes: 35 additions & 25 deletions apps/meteor/app/livechat/server/api/lib/livechat.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ILivechatAgent, ILivechatDepartment, ILivechatTrigger, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings';
import { License } from '@rocket.chat/license';
import { EmojiCustom, LivechatTrigger, LivechatVisitors, LivechatRooms, LivechatDepartment } from '@rocket.chat/models';
import { makeFunction } from '@rocket.chat/patch-injection';
import { Meteor } from 'meteor/meteor';

import { callbacks } from '../../../../../lib/callbacks';
Expand All @@ -26,18 +27,15 @@ async function findTriggers(): Promise<Pick<ILivechatTrigger, '_id' | 'actions'
async function findDepartments(
businessUnit?: string,
): Promise<Pick<ILivechatDepartment, '_id' | 'name' | 'showOnRegistration' | 'showOnOfflineForm' | 'departmentsAllowedToForward'>[]> {
// TODO: check this function usage
return (
await LivechatDepartment.findEnabledWithAgentsAndBusinessUnit<
Pick<ILivechatDepartment, '_id' | 'name' | 'showOnRegistration' | 'showOnOfflineForm' | 'departmentsAllowedToForward'>
>(businessUnit, {
_id: 1,
name: 1,
showOnRegistration: 1,
showOnOfflineForm: 1,
departmentsAllowedToForward: 1,
})
).toArray();
return LivechatDepartment.findEnabledWithAgentsAndBusinessUnit<
Pick<ILivechatDepartment, '_id' | 'name' | 'showOnRegistration' | 'showOnOfflineForm' | 'departmentsAllowedToForward'>
>(businessUnit, {
_id: 1,
name: 1,
showOnRegistration: 1,
showOnOfflineForm: 1,
departmentsAllowedToForward: 1,
}).toArray();
}

export function findGuest(token: string): Promise<ILivechatVisitor | null> {
Expand Down Expand Up @@ -96,12 +94,13 @@ export function normalizeHttpHeaderData(headers: Headers = new Headers()): {
}

export async function settings({ businessUnit = '' }: { businessUnit?: string } = {}): Promise<Record<string, string | number | any>> {
// Putting this ugly conversion while we type the livechat service
const initSettings = await getInitSettings();
const triggers = await findTriggers();
const departments = await findDepartments(businessUnit);
const [initSettings, triggers, departments, emojis] = await Promise.all([
getInitSettings(),
findTriggers(),
findDepartments(businessUnit),
EmojiCustom.find().toArray(),
]);
const sound = `${Meteor.absoluteUrl()}sounds/chime.mp3`;
const emojis = await EmojiCustom.find().toArray();
return {
enabled: initSettings.Livechat_enabled,
settings: {
Expand Down Expand Up @@ -178,11 +177,22 @@ export async function settings({ businessUnit = '' }: { businessUnit?: string }
};
}

export async function getExtraConfigInfo(room?: IOmnichannelRoom): Promise<any> {
return callbacks.run('livechat.onLoadConfigApi', { room });
}

// TODO: please forgive me for this. Still finding the good types for these callbacks
export function onCheckRoomParams(params: any): Promise<unknown> {
return callbacks.run('livechat.onCheckRoomApiParams', params);
}
export const getExtraConfigInfo = makeFunction(
async (options: {
room?: IOmnichannelRoom;
}): Promise<{
queueInfo?: unknown;
customFields?: {
options?: string[] | undefined;
_id: string;
label: string;
regexp: string | undefined;
required: boolean;
type: string | undefined;
defaultValue: string | null;
}[];
room?: IOmnichannelRoom;
}> => options,
);

export const onCheckRoomParams = makeFunction((params: any) => params);
18 changes: 10 additions & 8 deletions apps/meteor/app/livechat/server/api/v1/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,20 @@ API.v1.addRoute(
}

const { token, department, businessUnit } = this.queryParams;

const config = await cachedSettings({ businessUnit });

const status = await online(department);
const guest = token ? await findGuestWithoutActivity(token) : null;
const [config, status, guest] = await Promise.all([
cachedSettings({ businessUnit }),
online(department),
token ? findGuestWithoutActivity(token) : null,
]);

const room = guest ? await findOpenRoom(guest.token) : undefined;
const agent = guest && room && room.servedBy && (await findAgent(room.servedBy._id));
const agentPromise = room?.servedBy ? findAgent(room.servedBy._id) : null;
const extraInfoPromise = getExtraConfigInfo({ room });

const [agent, extraInfo] = await Promise.all([agentPromise, extraInfoPromise]);

const extra = await getExtraConfigInfo(room);
return API.v1.success({
config: { ...config, online: status, ...extra, ...(guest && { guest }), ...(room && { room }), ...(agent && { agent }) },
config: { ...config, online: status, ...extraInfo, ...(guest && { guest }), ...(room && { room }), ...(agent && { agent }) },
});
},
},
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/api/v1/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ API.v1.addRoute(
{
async get() {
// I'll temporary use check for validation, as validateParams doesnt support what's being done here
const extraCheckParams = await onCheckRoomParams({
const extraCheckParams = onCheckRoomParams({
token: String,
rid: Match.Maybe(String),
agentId: Match.Maybe(String),
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ const parseFromIntOrStr = (value: string | number) => {
export const updateDepartmentAgents = async (
departmentId: string,
agents: {
upsert?: Pick<ILivechatDepartmentAgents, 'agentId' | 'count' | 'order'>[];
upsert?: (Pick<ILivechatDepartmentAgents, 'agentId'> & { count?: number; order?: number })[];
remove?: Pick<ILivechatDepartmentAgents, 'agentId'>[];
},
departmentEnabled: boolean,
Expand Down
30 changes: 7 additions & 23 deletions apps/meteor/app/livechat/server/lib/departmentsLib.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AppEvents, Apps } from '@rocket.chat/apps';
import type { LivechatDepartmentDTO, ILivechatDepartment, ILivechatDepartmentAgents, ILivechatAgent } from '@rocket.chat/core-typings';
import { LivechatDepartment, LivechatDepartmentAgents, LivechatVisitors, LivechatRooms, Users } from '@rocket.chat/models';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';

import { updateDepartmentAgents } from './Helper';
Expand All @@ -25,7 +26,7 @@ export async function saveDepartment(
departmentData: LivechatDepartmentDTO,
departmentAgents?: {
upsert?: { agentId: string; count?: number; order?: number }[];
remove?: { agentId: string; count?: number; order?: number };
remove?: { agentId: string; count?: number; order?: number }[];
},
departmentUnit?: { _id?: string },
) {
Expand Down Expand Up @@ -182,30 +183,13 @@ export async function unarchiveDepartment(_id: string) {
export async function saveDepartmentAgents(
_id: string,
departmentAgents: {
upsert?: Pick<ILivechatDepartmentAgents, 'agentId' | 'count' | 'order' | 'username'>[];
remove?: Pick<ILivechatDepartmentAgents, 'agentId'>[];
upsert?: (Pick<ILivechatDepartmentAgents, 'agentId' | 'username'> & {
count?: number;
sort?: number;
})[];
remove?: Pick<ILivechatDepartmentAgents, 'agentId' | 'username'>[];
},
) {
check(_id, String);
check(departmentAgents, {
upsert: Match.Maybe([
Match.ObjectIncluding({
agentId: String,
username: String,
count: Match.Maybe(Match.Integer),
order: Match.Maybe(Match.Integer),
}),
]),
remove: Match.Maybe([
Match.ObjectIncluding({
agentId: String,
username: Match.Maybe(String),
count: Match.Maybe(Match.Integer),
order: Match.Maybe(Match.Integer),
}),
]),
});

const department = await LivechatDepartment.findOneById<Pick<ILivechatDepartment, 'enabled'>>(_id, { projection: { enabled: 1 } });
if (!department) {
throw new Meteor.Error('error-department-not-found', 'Department not found');
Expand Down
11 changes: 0 additions & 11 deletions apps/meteor/app/livechat/server/lib/omni-users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { afterAgentAdded, afterRemoveAgent } from './hooks';
import { callbacks } from '../../../../lib/callbacks';
import { addUserRolesAsync } from '../../../../server/lib/roles/addUserRoles';
import { removeUserFromRolesAsync } from '../../../../server/lib/roles/removeUserFromRoles';
import { hasRoleAsync } from '../../../authorization/server/functions/hasRole';
import { settings } from '../../../settings/server';

export async function notifyAgentStatusChanged(userId: string, status?: UserStatus) {
Expand Down Expand Up @@ -83,16 +82,6 @@ export async function removeManager(id: IUser['_id']) {
}

export async function saveAgentInfo(_id: string, agentData: any, agentDepartments: string[]) {
// TODO: check if these 'check' functions are necessary
check(_id, String);
check(agentData, Object);
check(agentDepartments, [String]);

const user = await Users.findOneById(_id);
if (!user || !(await hasRoleAsync(_id, 'livechat-agent'))) {
throw new Error('error-user-is-not-agent');
}

await Users.setLivechatData(_id, removeEmpty(agentData));

const currentDepartmentsForAgent = await LivechatDepartmentAgents.findByAgentId(_id).toArray();
Expand Down
12 changes: 7 additions & 5 deletions apps/meteor/app/livechat/server/methods/saveAgentInfo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { ServerMethods } from '@rocket.chat/ddp-client';
import { Users } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';

import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
Expand All @@ -15,15 +14,18 @@ declare module '@rocket.chat/ddp-client' {

Meteor.methods<ServerMethods>({
async 'livechat:saveAgentInfo'(_id, agentData, agentDepartments) {
const uid = Meteor.userId();
if (!uid || !(await hasPermissionAsync(uid, 'manage-livechat-agents'))) {
check(_id, String);
check(agentData, Object);
check(agentDepartments, [String]);

const user = await Meteor.userAsync();
if (!user || !(await hasPermissionAsync(user._id, 'manage-livechat-agents'))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', {
method: 'livechat:saveAgentInfo',
});
}

const user = await Users.findOneById(_id);
if (!user || !(await hasRoleAsync(_id, 'livechat-agent'))) {
if (!(await hasRoleAsync(_id, 'livechat-agent'))) {
throw new Meteor.Error('error-user-is-not-agent', 'User is not a livechat agent', {
method: 'livechat:saveAgentInfo',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import './beforeNewInquiry';
import './beforeNewRoom';
import './beforeRoutingChat';
import './checkAgentBeforeTakeInquiry';
import './onCheckRoomParamsApi';
import './onLoadConfigApi';
import './onCloseLivechat';
import './onSaveVisitorInfo';
import './onBusinessHourStart';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { Match } from 'meteor/check';

import { callbacks } from '../../../../../lib/callbacks';
import { onCheckRoomParams } from '../../../../../app/livechat/server/api/lib/livechat';

callbacks.add(
'livechat.onCheckRoomApiParams',
(params) => ({ ...params, sla: Match.Maybe(String), priority: Match.Maybe(String) }),
callbacks.priority.MEDIUM,
'livechat-on-check-room-params-api',
);
onCheckRoomParams.patch((_: any, params) => ({ ...params, sla: Match.Maybe(String), priority: Match.Maybe(String) }));
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import { callbacks } from '../../../../../lib/callbacks';
import type { IOmnichannelRoom } from '@rocket.chat/core-typings';

import { getExtraConfigInfo } from '../../../../../app/livechat/server/api/lib/livechat';
import { getLivechatQueueInfo, getLivechatCustomFields } from '../lib/Helper';

callbacks.add(
'livechat.onLoadConfigApi',
async (options) => {
getExtraConfigInfo.patch(
async (
_: any,
options: {
room?: IOmnichannelRoom;
},
): Promise<{
queueInfo?: unknown;
customFields?: {
options?: string[] | undefined;
_id: string;
label: string;
regexp: string | undefined;
required: boolean;
type: string | undefined;
defaultValue: string | null;
}[];
room?: IOmnichannelRoom;
}> => {
const { room } = options;

const [queueInfo, customFields] = await Promise.all([getLivechatQueueInfo(room), getLivechatCustomFields()]);
Expand All @@ -13,6 +31,4 @@ callbacks.add(
...options,
};
},
callbacks.priority.MEDIUM,
'livechat-on-load-config-api',
);
Loading
Loading