Skip to content

Commit 981c7e4

Browse files
feat: Pexip Integration
1 parent 8d173ac commit 981c7e4

32 files changed

Lines changed: 1358 additions & 21 deletions

apps/meteor/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@
143143
"@rocket.chat/password-policies": "workspace:^",
144144
"@rocket.chat/patch-injection": "workspace:^",
145145
"@rocket.chat/pdf-worker": "workspace:^",
146+
"@rocket.chat/pexip": "workspace:^",
146147
"@rocket.chat/poplib": "workspace:^",
147148
"@rocket.chat/presence": "workspace:^",
148149
"@rocket.chat/random": "workspace:^",

apps/meteor/server/api/api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export const API: {
4343
api: Router<'/api', any, APIActionHandler>;
4444
v1: APIClass<'/v1'>;
4545
default: APIClass;
46+
pexip: APIClass;
4647
ApiClass: typeof APIClass;
4748
channels?: {
4849
create: {
@@ -74,6 +75,9 @@ export const API: {
7475
useDefaultAuth: true,
7576
}),
7677
default: createApi({}),
78+
pexip: createApi({
79+
version: 'pexip',
80+
}),
7781
};
7882

7983
settings.watch<string>('Accounts_CustomFields', (value) => {
@@ -118,6 +122,7 @@ export const startRestAPI = () => {
118122
.use(cors(settings))
119123
.use(loggerMiddleware(logger))
120124
.use(API.v1.router)
125+
.use(API.pexip.router)
121126
.use(API.default.router).router,
122127
);
123128
};

apps/meteor/server/api/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ import './v1/moderation';
4848
import './v1/uploads';
4949
import './v1/twoFactorChallenges';
5050
import './v1/loginCode';
51+
import './pexip';
52+
5153
// This has to come last so all endpoints are registered before generating the OpenAPI documentation
5254
import './default/openApi';
5355

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { isEventSinkRequestProps, Pexip } from '@rocket.chat/pexip';
2+
import { ajv, validateNotFoundErrorResponse, validateUnauthorizedErrorResponse } from '@rocket.chat/rest-typings';
3+
4+
import { getPexipSettings } from '../../settings/pexip';
5+
import { API } from '../api';
6+
7+
const successResponseSchema = ajv.compile<void>({
8+
type: 'object',
9+
properties: { success: { type: 'boolean', enum: [true] } },
10+
required: ['success'],
11+
additionalProperties: false,
12+
});
13+
14+
API.pexip.post(
15+
'events',
16+
{
17+
response: {
18+
200: successResponseSchema,
19+
401: validateUnauthorizedErrorResponse,
20+
404: validateNotFoundErrorResponse,
21+
},
22+
body: isEventSinkRequestProps,
23+
authRequired: false,
24+
rateLimiterOptions: false,
25+
},
26+
async function action() {
27+
const {
28+
bodyParams: event,
29+
request: { headers },
30+
} = this;
31+
32+
const settings = getPexipSettings();
33+
const pexip = new Pexip(settings);
34+
35+
try {
36+
const authHeader = headers.get('authorization');
37+
38+
pexip.validateRequestCredentials(authHeader);
39+
} catch (err) {
40+
return API.pexip.unauthorized();
41+
}
42+
43+
void pexip.processEvent(event).catch(() => true);
44+
45+
return API.pexip.success();
46+
},
47+
);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import './eventSink';
2+
import './policyServer';
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import type { PolicyServerResponse } from '@rocket.chat/pexip';
2+
import { isServiceConfigurationRequestProps, isPolicyServerResponse, Pexip } from '@rocket.chat/pexip';
3+
import { validateNotFoundErrorResponse, validateUnauthorizedErrorResponse } from '@rocket.chat/rest-typings';
4+
5+
import { getPexipSettings } from '../../settings/pexip';
6+
import { API } from '../api';
7+
import type { SuccessResult } from '../definition';
8+
9+
API.pexip.get(
10+
'policy/v1/service/configuration',
11+
{
12+
response: {
13+
200: isPolicyServerResponse,
14+
401: validateUnauthorizedErrorResponse,
15+
404: validateNotFoundErrorResponse,
16+
},
17+
query: isServiceConfigurationRequestProps,
18+
authRequired: false,
19+
rateLimiterOptions: false,
20+
},
21+
async function action() {
22+
const {
23+
queryParams: serviceRequest,
24+
request: { headers },
25+
} = this;
26+
27+
const settings = getPexipSettings();
28+
const pexip = new Pexip(settings);
29+
30+
try {
31+
const authHeader = headers.get('authorization');
32+
33+
pexip.validateRequestCredentials(authHeader);
34+
} catch (err) {
35+
return API.pexip.unauthorized();
36+
}
37+
38+
const result = await pexip.getServiceConfiguration(serviceRequest);
39+
if (!result) {
40+
return API.pexip.notFound();
41+
}
42+
43+
const status = 'success' as const;
44+
const action = 'continue' as const;
45+
46+
const response: PolicyServerResponse = {
47+
status,
48+
action,
49+
result,
50+
};
51+
52+
// do not use API.pexip.success to prevent it from mutating the body
53+
return {
54+
statusCode: 200,
55+
body: response,
56+
} as SuccessResult<PolicyServerResponse>;
57+
},
58+
);

apps/meteor/server/lib/videoConfProviders.ts

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import type { VideoConferenceCapabilities } from '@rocket.chat/core-typings';
2+
import { Pexip, PexipVideoConfProvider } from '@rocket.chat/pexip';
23

34
import { settings } from '../settings';
5+
import { getPexipSettings } from '../settings/pexip';
46

57
const providers = new Map<string, { capabilities: VideoConferenceCapabilities; label: string; appId: string }>();
68

9+
type Provider = { key: string; label: string };
10+
711
export const videoConfProviders = {
812
registerProvider(providerName: string, capabilities: VideoConferenceCapabilities, appId: string): void {
913
providers.set(providerName.toLowerCase(), { capabilities, label: providerName, appId });
@@ -18,17 +22,20 @@ export const videoConfProviders = {
1822
},
1923

2024
getActiveProvider(): string | undefined {
21-
if (providers.size === 0) {
22-
return;
23-
}
24-
const defaultProvider = settings.get<string>('VideoConf_Default_Provider');
25+
if (providers.size !== 0) {
26+
const defaultProvider = settings.get<string>('VideoConf_Default_Provider');
27+
28+
if (defaultProvider) {
29+
if (this.isProviderAvailable(defaultProvider)) {
30+
return defaultProvider;
31+
}
2532

26-
if (defaultProvider) {
27-
if (providers.has(defaultProvider)) {
28-
return defaultProvider;
33+
return;
2934
}
35+
}
3036

31-
return;
37+
if (this.isProviderAvailable('core.pexip')) {
38+
return 'core.pexip';
3239
}
3340

3441
if (providers.size === 1) {
@@ -38,18 +45,56 @@ export const videoConfProviders = {
3845
},
3946

4047
hasAnyProvider(): boolean {
41-
return providers.size > 0;
48+
return providers.size > 0 || settings.get<boolean>('Pexip_Integration_Enabled');
4249
},
4350

44-
getProviderList(): { key: string; label: string }[] {
51+
getRegisteredProviders(): Provider[] {
4552
return [...providers.keys()].map((key) => ({ key, label: providers.get(key)?.label || key }));
4653
},
4754

55+
getInternalProviders(): Provider[] {
56+
const pexip = this.getPexipProvider();
57+
58+
if (pexip) {
59+
return [pexip];
60+
}
61+
62+
return [];
63+
},
64+
65+
getPexipProvider(): Provider | null {
66+
if (!settings.get<boolean>('Pexip_Integration_Enabled')) {
67+
return null;
68+
}
69+
70+
return { key: 'core.pexip', label: 'Pexip_Integration' };
71+
},
72+
73+
getAllProviders(): Provider[] {
74+
const registeredProviders = [...providers.keys()].map((key) => ({ key, label: providers.get(key)?.label || key }));
75+
const internalProviders = this.getInternalProviders();
76+
77+
return [...registeredProviders, ...internalProviders];
78+
},
79+
4880
isProviderAvailable(name: string): boolean {
81+
if (name === 'core.pexip') {
82+
return settings.get<boolean>('Pexip_Integration_Enabled');
83+
}
84+
4985
return providers.has(name);
5086
},
5187

5288
getProviderCapabilities(name: string): VideoConferenceCapabilities | undefined {
89+
if (name === 'core.pexip') {
90+
return {
91+
mic: false,
92+
cam: false,
93+
title: true,
94+
persistentChat: true,
95+
};
96+
}
97+
5398
const key = name.toLowerCase();
5499
if (!providers.has(key)) {
55100
return;
@@ -60,11 +105,29 @@ export const videoConfProviders = {
60105

61106
getProviderAppId(name: string): string | undefined {
62107
const key = name.toLowerCase();
108+
if (key === 'core.pexip') {
109+
return undefined;
110+
}
63111

64112
if (!providers.has(key)) {
65113
return;
66114
}
67115

68116
return providers.get(key)?.appId;
69117
},
118+
119+
getVideoConfProviderHandler(providerName: string): PexipVideoConfProvider | null {
120+
if (providerName === 'core.pexip') {
121+
return this.getPexipHandler();
122+
}
123+
124+
return null;
125+
},
126+
127+
getPexipHandler() {
128+
const pexipSettings = getPexipSettings();
129+
130+
const pexip = new Pexip(pexipSettings);
131+
return new PexipVideoConfProvider(pexip);
132+
},
70133
};

0 commit comments

Comments
 (0)