Skip to content

Commit 3c3e7ca

Browse files
chore:
1 parent d70a7b7 commit 3c3e7ca

File tree

4 files changed

+168
-119
lines changed

4 files changed

+168
-119
lines changed
Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,38 @@
1+
import type { ISetting } from '@rocket.chat/core-typings';
2+
import { ajv, validateUnauthorizedErrorResponse } from '@rocket.chat/rest-typings';
3+
14
import { API } from '../../../../api/server';
5+
import type { ExtractRoutesFromAPI } from '../../../../api/server/ApiClass';
26
import { findIntegrationSettings } from '../../../server/api/lib/integrations';
37

4-
API.v1.addRoute(
8+
const livechatIntegrationsEndpoints = API.v1.get(
59
'livechat/integrations.settings',
6-
{ authRequired: true, permissionsRequired: ['view-livechat-manager'] },
710
{
8-
async get() {
9-
return API.v1.success(await findIntegrationSettings());
11+
authRequired: true,
12+
permissionsRequired: ['view-livechat-manager'],
13+
response: {
14+
401: validateUnauthorizedErrorResponse,
15+
200: ajv.compile<{ settings: ISetting[]; success: boolean }>({
16+
type: 'object',
17+
properties: {
18+
settings: {
19+
type: 'array',
20+
items: { type: 'object' },
21+
},
22+
success: { type: 'boolean', enum: [true] },
23+
},
24+
required: ['settings', 'success'],
25+
}),
1026
},
1127
},
28+
async function action() {
29+
return API.v1.success(await findIntegrationSettings());
30+
},
1231
);
32+
33+
type LivechatIntegrationsEndpoints = ExtractRoutesFromAPI<typeof livechatIntegrationsEndpoints>;
34+
35+
declare module '@rocket.chat/rest-typings' {
36+
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-object-type, @typescript-eslint/no-empty-interface
37+
interface Endpoints extends LivechatIntegrationsEndpoints {}
38+
}
Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
1-
import { GETLivechatConfigRouting, isGETLivechatConfigParams } from '@rocket.chat/rest-typings';
1+
import { ajv, GETLivechatConfigRouting, isGETLivechatConfigParams, validateUnauthorizedErrorResponse } from '@rocket.chat/rest-typings';
22
import mem from 'mem';
33

44
import { API } from '../../../../api/server';
55
import type { ExtractRoutesFromAPI } from '../../../../api/server/ApiClass';
6-
import { settings as serverSettings } from '../../../../settings/server';
6+
import { settings as serverSettings } from '../../../../settings/server/index';
77
import { RoutingManager } from '../../lib/RoutingManager';
88
import { online } from '../../lib/service-status';
99
import { settings, findOpenRoom, getExtraConfigInfo, findAgent, findGuestWithoutActivity } from '../lib/livechat';
1010

1111
const cachedSettings = mem(settings, { maxAge: process.env.TEST_MODE === 'true' ? 1 : 1000, cacheKey: JSON.stringify });
1212

13-
API.v1.addRoute(
14-
'livechat/config',
15-
{ validateParams: isGETLivechatConfigParams },
16-
{
17-
async get() {
13+
const livechatConfigEndpoints = API.v1
14+
.get(
15+
'livechat/config',
16+
{
17+
query: isGETLivechatConfigParams,
18+
response: {
19+
200: ajv.compile<{ config: Record<string, unknown>; success: boolean }>({
20+
type: 'object',
21+
properties: {
22+
config: { type: 'object' },
23+
success: { type: 'boolean', enum: [true] },
24+
},
25+
required: ['config', 'success'],
26+
}),
27+
},
28+
},
29+
async function action() {
1830
const enabled = serverSettings.get<boolean>('Livechat_enabled');
1931

2032
if (!enabled) {
@@ -38,27 +50,26 @@ API.v1.addRoute(
3850
config: { ...config, online: status, ...extraInfo, ...(guest && { guest }), ...(room && { room }), ...(agent && { agent }) },
3951
});
4052
},
41-
},
42-
);
43-
44-
const livechatConfigEndpoints = API.v1.get(
45-
'livechat/config/routing',
46-
{
47-
response: {
48-
200: GETLivechatConfigRouting,
53+
)
54+
.get(
55+
'livechat/config/routing',
56+
{
57+
response: {
58+
200: GETLivechatConfigRouting,
59+
401: validateUnauthorizedErrorResponse,
60+
},
61+
authRequired: true,
4962
},
50-
authRequired: true,
51-
},
52-
async function action() {
53-
const config = RoutingManager.getConfig();
63+
async function action() {
64+
const config = RoutingManager.getConfig();
5465

55-
return API.v1.success({ config });
56-
},
57-
);
66+
return API.v1.success({ config });
67+
},
68+
);
5869

5970
type LivechatConfigEndpoints = ExtractRoutesFromAPI<typeof livechatConfigEndpoints>;
6071

6172
declare module '@rocket.chat/rest-typings' {
62-
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
73+
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-object-type, @typescript-eslint/no-empty-interface
6374
interface Endpoints extends LivechatConfigEndpoints {}
6475
}
Lines changed: 91 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,115 @@
11
import { Logger } from '@rocket.chat/logger';
22
import type { ExtendedFetchOptions } from '@rocket.chat/server-fetch';
33
import { serverFetch as fetch } from '@rocket.chat/server-fetch';
4+
import { ajv, validateUnauthorizedErrorResponse } from '@rocket.chat/rest-typings';
45

56
import { API } from '../../../../api/server';
7+
import type { ExtractRoutesFromAPI } from '../../../../api/server/ApiClass';
68
import { settings } from '../../../../settings/server';
79

810
const logger = new Logger('WebhookTest');
911

10-
API.v1.addRoute(
12+
const livechatWebhookEndpoints = API.v1.post(
1113
'livechat/webhook.test',
12-
{ authRequired: true, permissionsRequired: ['view-livechat-webhooks'] },
1314
{
14-
async post() {
15-
const sampleData = {
16-
type: 'LivechatSession',
17-
_id: 'fasd6f5a4sd6f8a4sdf',
18-
label: 'title',
19-
topic: 'asiodojf',
20-
createdAt: new Date(),
21-
lastMessageAt: new Date(),
22-
tags: ['tag1', 'tag2', 'tag3'],
15+
authRequired: true,
16+
permissionsRequired: ['view-livechat-webhooks'],
17+
response: {
18+
401: validateUnauthorizedErrorResponse,
19+
200: ajv.compile<void>({
20+
type: 'object',
21+
properties: {
22+
success: { type: 'boolean', enum: [true] },
23+
},
24+
required: ['success'],
25+
}),
26+
},
27+
},
28+
async function action() {
29+
const sampleData = {
30+
type: 'LivechatSession',
31+
_id: 'fasd6f5a4sd6f8a4sdf',
32+
label: 'title',
33+
topic: 'asiodojf',
34+
createdAt: new Date(),
35+
lastMessageAt: new Date(),
36+
tags: ['tag1', 'tag2', 'tag3'],
37+
customFields: {
38+
productId: '123456',
39+
},
40+
visitor: {
41+
_id: '',
42+
name: 'visitor name',
43+
username: 'visitor-username',
44+
department: 'department',
45+
email: 'email@address.com',
46+
phone: '192873192873',
47+
ip: '123.456.7.89',
48+
browser: 'Chrome',
49+
os: 'Linux',
2350
customFields: {
24-
productId: '123456',
51+
customerId: '123456',
2552
},
26-
visitor: {
27-
_id: '',
28-
name: 'visitor name',
53+
},
54+
agent: {
55+
_id: 'asdf89as6df8',
56+
username: 'agent.username',
57+
name: 'Agent Name',
58+
email: 'agent@email.com',
59+
},
60+
messages: [
61+
{
2962
username: 'visitor-username',
30-
department: 'department',
31-
email: 'email@address.com',
32-
phone: '192873192873',
33-
ip: '123.456.7.89',
34-
browser: 'Chrome',
35-
os: 'Linux',
36-
customFields: {
37-
customerId: '123456',
38-
},
63+
msg: 'message content',
64+
ts: new Date(),
3965
},
40-
agent: {
41-
_id: 'asdf89as6df8',
66+
{
4267
username: 'agent.username',
43-
name: 'Agent Name',
44-
email: 'agent@email.com',
68+
agentId: 'asdf89as6df8',
69+
msg: 'message content from agent',
70+
ts: new Date(),
4571
},
46-
messages: [
47-
{
48-
username: 'visitor-username',
49-
msg: 'message content',
50-
ts: new Date(),
51-
},
52-
{
53-
username: 'agent.username',
54-
agentId: 'asdf89as6df8',
55-
msg: 'message content from agent',
56-
ts: new Date(),
57-
},
58-
],
59-
};
60-
const options = {
61-
method: 'POST',
62-
headers: {
63-
'X-RocketChat-Livechat-Token': settings.get<string>('Livechat_secret_token'),
64-
'Accept': 'application/json',
65-
},
66-
body: sampleData,
67-
// SECURITY: Webhooks can only be configured by users with enough privileges. It's ok to disable this check here.
68-
ignoreSsrfValidation: true,
69-
size: 10 * 1024 * 1024,
70-
} as ExtendedFetchOptions;
71-
72-
const webhookUrl = settings.get<string>('Livechat_webhookUrl');
72+
],
73+
};
74+
const options = {
75+
method: 'POST',
76+
headers: {
77+
'X-RocketChat-Livechat-Token': settings.get<string>('Livechat_secret_token'),
78+
'Accept': 'application/json',
79+
},
80+
body: sampleData,
81+
// SECURITY: Webhooks can only be configured by users with enough privileges. It's ok to disable this check here.
82+
ignoreSsrfValidation: true,
83+
size: 10 * 1024 * 1024,
84+
} as ExtendedFetchOptions;
7385

74-
if (!webhookUrl) {
75-
return API.v1.failure('Webhook_URL_not_set');
76-
}
86+
const webhookUrl = settings.get<string>('Livechat_webhookUrl');
7787

78-
try {
79-
logger.debug({ msg: 'Testing webhook', webhookUrl });
80-
const request = await fetch(webhookUrl, options);
81-
const response = await request.text();
88+
if (!webhookUrl) {
89+
return API.v1.failure('Webhook_URL_not_set');
90+
}
8291

83-
logger.debug({ msg: 'Webhook response', response });
84-
if (request.status === 200) {
85-
return API.v1.success();
86-
}
92+
try {
93+
logger.debug({ msg: 'Testing webhook', webhookUrl });
94+
const request = await fetch(webhookUrl, options);
95+
const response = await request.text();
8796

88-
throw new Error('Invalid status code');
89-
} catch (error) {
90-
logger.error({ msg: 'Error testing webhook', err: error });
91-
throw new Error('error-invalid-webhook-response');
97+
logger.debug({ msg: 'Webhook response', response });
98+
if (request.status === 200) {
99+
return API.v1.success();
92100
}
93-
},
101+
102+
throw new Error('Invalid status code');
103+
} catch (error) {
104+
logger.error({ msg: 'Error testing webhook', err: error });
105+
throw new Error('error-invalid-webhook-response');
106+
}
94107
},
95108
);
109+
110+
type LivechatWebhookEndpoints = ExtractRoutesFromAPI<typeof livechatWebhookEndpoints>;
111+
112+
declare module '@rocket.chat/rest-typings' {
113+
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-object-type, @typescript-eslint/no-empty-interface
114+
interface Endpoints extends LivechatWebhookEndpoints {}
115+
}

packages/rest-typings/src/v1/omnichannel.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,14 +2848,14 @@ type POSTLivechatRoomCloseByUserParams = {
28482848
generateTranscriptPdf?: boolean;
28492849
forceClose?: boolean;
28502850
transcriptEmail?:
2851-
| {
2852-
// Note: if sendToVisitor is false, then any previously requested transcripts (like via livechat:requestTranscript) will be also cancelled
2853-
sendToVisitor: false;
2854-
}
2855-
| {
2856-
sendToVisitor: true;
2857-
requestData: Pick<NonNullable<IOmnichannelRoom['transcriptRequest']>, 'email' | 'subject'>;
2858-
};
2851+
| {
2852+
// Note: if sendToVisitor is false, then any previously requested transcripts (like via livechat:requestTranscript) will be also cancelled
2853+
sendToVisitor: false;
2854+
}
2855+
| {
2856+
sendToVisitor: true;
2857+
requestData: Pick<NonNullable<IOmnichannelRoom['transcriptRequest']>, 'email' | 'subject'>;
2858+
};
28592859
};
28602860

28612861
const POSTLivechatRoomCloseByUserParamsSchema = {
@@ -4369,8 +4369,8 @@ export const isLivechatTriggerWebhookCallParams = ajv.compile<LivechatTriggerWeb
43694369

43704370
type POSTLivechatRoomsCloseAll =
43714371
| {
4372-
departmentIds?: string[];
4373-
}
4372+
departmentIds?: string[];
4373+
}
43744374
| undefined;
43754375

43764376
const POSTLivechatRoomsCloseAllSchema = {
@@ -4898,11 +4898,7 @@ export type OmnichannelEndpoints = {
48984898
'/v1/livechat/agent.next/:token': {
48994899
GET: (params: GETAgentNextToken) => { agent: ILivechatAgent | { hiddenInfo: true } } | void;
49004900
};
4901-
'/v1/livechat/config': {
4902-
GET: (params: GETLivechatConfigParams) => {
4903-
config: { [k: string]: string | boolean } & { room?: IOmnichannelRoom; agent?: ILivechatAgent };
4904-
};
4905-
};
4901+
49064902
'/v1/livechat/custom.field': {
49074903
POST: (params: POSTLivechatCustomFieldParams) => { field: { key: string; value: string; overwrite: boolean } };
49084904
};
@@ -5005,9 +5001,7 @@ export type OmnichannelEndpoints = {
50055001
}[];
50065002
}>;
50075003
};
5008-
'/v1/livechat/integrations.settings': {
5009-
GET: () => { settings: ISetting[]; success: boolean };
5010-
};
5004+
50115005
'/v1/livechat/upload/:rid': {
50125006
POST: (params: { file: File }) => IMessage & { newRoom: boolean; showConnecting: boolean };
50135007
};
@@ -5209,7 +5203,5 @@ export type OmnichannelEndpoints = {
52095203
'/v1/livechat/analytics/dashboards/conversations-by-agent': {
52105204
GET: (params: GETDashboardConversationsByType) => ReportWithUnmatchingElements;
52115205
};
5212-
'/v1/livechat/webhook.test': {
5213-
POST: () => void;
5214-
};
5206+
52155207
};

0 commit comments

Comments
 (0)