Skip to content

Commit 63d28a5

Browse files
murtaza98KevLehman
andauthored
[NEW] Add new Endpoints to manage VoIP server configs (#23239)
* create voip server config collection in DB + modify interfaces * Add new endpoints to fetch management and server-config info * Add new endpoints to add or update voip server configs * Add translations + move serverName property to IVoipServerConfig interface * Remove VoipServerConfiguration DB model and rely on DB Raw module to create collection * archive server configs instead of completely deleting them upon update/insert * Add comment for future scope * Apply suggestions from code review Co-authored-by: Kevin Aleman <[email protected]> * Apply suggestions from code review * remove deactive logic from endpoint Co-authored-by: Kevin Aleman <[email protected]>
1 parent af534e4 commit 63d28a5

File tree

6 files changed

+160
-1
lines changed

6 files changed

+160
-1
lines changed

app/api/server/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ import './v1/instances';
4242
import './v1/banners';
4343
import './v1/email-inbox';
4444
import './v1/teams';
45+
import './v1/voip/server-config';
4546

4647
export { API, APIClass, defaultRateLimiterOptions } from './api';
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { Match, check } from 'meteor/check';
2+
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
3+
4+
import { API } from '../../api';
5+
import { hasPermission } from '../../../../authorization/server';
6+
import { Voip } from '../../../../../server/sdk';
7+
import { ICallServerConfigData, IManagementConfigData, ServerType } from '../../../../../definition/IVoipServerConfig';
8+
9+
// management api(s)
10+
API.v1.addRoute('voipServerConfig.management', { authRequired: true }, {
11+
get() {
12+
if (!hasPermission(this.userId, 'manage-voip-contact-center-settings')) {
13+
return API.v1.unauthorized(TAPi18n._('error-insufficient-permission', { permission: 'manage-voip-contact-center-settings' }));
14+
}
15+
16+
const config = Promise.await(Voip.getServerConfigData(ServerType.MANAGEMENT));
17+
18+
if (!config) {
19+
return API.v1.notFound();
20+
}
21+
22+
return API.v1.success({ ...config });
23+
},
24+
// NOTE: you can use this POST endpoint for both create and update operation
25+
post() {
26+
check(this.bodyParams, Match.ObjectIncluding({
27+
host: String,
28+
port: Number,
29+
serverName: String,
30+
username: String,
31+
password: String,
32+
}));
33+
34+
if (!hasPermission(this.userId, 'manage-voip-contact-center-settings')) {
35+
return API.v1.unauthorized(TAPi18n._('error-insufficient-permission', { permission: 'manage-voip-contact-center-settings' }));
36+
}
37+
38+
const { host, port, serverName, username, password } = this.bodyParams;
39+
40+
Promise.await(Voip.addServerConfigData({
41+
type: ServerType.MANAGEMENT,
42+
host,
43+
name: serverName,
44+
active: true,
45+
configData: {
46+
port,
47+
username,
48+
password,
49+
} as IManagementConfigData,
50+
}));
51+
52+
return API.v1.success();
53+
},
54+
});
55+
56+
// call-server api(s)
57+
API.v1.addRoute('voipServerConfig.callServer', { authRequired: true }, {
58+
get() {
59+
if (!hasPermission(this.userId, 'manage-voip-call-settings')) {
60+
return API.v1.unauthorized(TAPi18n._('error-insufficient-permission', { permission: 'manage-voip-call-settings' }));
61+
}
62+
63+
const config = Promise.await(Voip.getServerConfigData(ServerType.CALL_SERVER));
64+
if (!config) {
65+
return API.v1.notFound();
66+
}
67+
68+
return API.v1.success({ ...config });
69+
},
70+
// NOTE: you can use this POST endpoint for both create and update operation
71+
post() {
72+
check(this.bodyParams, Match.ObjectIncluding({
73+
host: String,
74+
serverName: String,
75+
websocketPort: Number,
76+
websocketPath: String,
77+
}));
78+
79+
if (!hasPermission(this.userId, 'manage-voip-call-settings')) {
80+
return API.v1.unauthorized(TAPi18n._('error-insufficient-permission', { permission: 'manage-voip-call-settings' }));
81+
}
82+
83+
const { host, websocketPort, websocketPath, serverName } = this.bodyParams;
84+
85+
Promise.await(Voip.addServerConfigData({
86+
type: ServerType.CALL_SERVER,
87+
host,
88+
name: serverName,
89+
active: true,
90+
configData: {
91+
websocketPort,
92+
websocketPath,
93+
} as ICallServerConfigData,
94+
}));
95+
96+
return API.v1.success();
97+
},
98+
});

definition/IVoipServerConfig.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,24 @@ import { IRocketChatRecord } from './IRocketChatRecord';
22

33
export enum ServerType {
44
MANAGEMENT = 'management',
5-
CALLS = 'calls',
5+
CALL_SERVER = 'call-server',
66
}
77

88
export interface IVoipServerConfig extends IRocketChatRecord {
99
type: ServerType;
1010
host: string;
11+
name: string;
12+
configData: IManagementConfigData | ICallServerConfigData;
13+
active: boolean;
14+
}
15+
16+
export interface IManagementConfigData {
1117
port: number;
18+
username: string;
19+
password: string;
20+
}
21+
22+
export interface ICallServerConfigData {
23+
websocketPort: number;
24+
websocketPath: string;
1225
}

packages/rocketchat-i18n/i18n/en.i18n.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,6 +1666,7 @@
16661666
"error-import-file-missing": "The file to be imported was not found on the specified path.",
16671667
"error-importer-not-defined": "The importer was not defined correctly, it is missing the Import class.",
16681668
"error-input-is-not-a-valid-field": "__input__ is not a valid __field__",
1669+
"error-insufficient-permission": "Error! You don't have ' __permission__ ' permission which is required to perform this operation",
16691670
"error-invalid-account": "Invalid Account",
16701671
"error-invalid-actionlink": "Invalid action link",
16711672
"error-invalid-arguments": "Invalid arguments",

server/sdk/types/IVoipService.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import { IVoipServerConfig, ServerType } from '../../../definition/IVoipServerConfig';
2+
13
export interface IVoipService {
24
getConfiguration(): any;
5+
getServerConfigData(serverType: ServerType): Promise<IVoipServerConfig | null>;
6+
addServerConfigData(config: Omit<IVoipServerConfig, '_id' | '_updatedAt'>): Promise<boolean>;
7+
updateServerConfigData(config: Omit<IVoipServerConfig, '_id' | '_updatedAt'>): Promise<boolean>;
8+
deactivateServerConfigDataIfAvailable(serverType: ServerType): Promise<boolean>;
39
}

server/services/voip/service.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Db } from 'mongodb';
33
import { IVoipService } from '../../sdk/types/IVoipService';
44
import { ServiceClass } from '../../sdk/types/ServiceClass';
55
import { VoipServerConfigurationRaw } from '../../../app/models/server/raw/VoipServerConfiguration';
6+
import { ServerType, IVoipServerConfig } from '../../../definition/IVoipServerConfig';
67

78
export class VoipService extends ServiceClass implements IVoipService {
89
protected name = 'voip';
@@ -17,6 +18,45 @@ export class VoipService extends ServiceClass implements IVoipService {
1718
this.VoipServerConfiguration = new VoipServerConfigurationRaw(db.collection('rocketchat_voip_server_configuration'));
1819
}
1920

21+
async addServerConfigData(config: Omit<IVoipServerConfig, '_id' | '_updatedAt'>): Promise<boolean> {
22+
const { type } = config;
23+
24+
Promise.await(this.deactivateServerConfigDataIfAvailable(type));
25+
26+
const existingConfig = await this.getServerConfigData(type);
27+
if (existingConfig) {
28+
throw new Error(`Error! There already exists an active record of type ${ type }`);
29+
}
30+
31+
return !!await this.VoipServerConfiguration.insertOne(config);
32+
}
33+
34+
async updateServerConfigData(config: Omit<IVoipServerConfig, '_id' | '_updatedAt'>): Promise<boolean> {
35+
const { type } = config;
36+
37+
Promise.await(this.deactivateServerConfigDataIfAvailable(type));
38+
39+
const existingConfig = await this.getServerConfigData(type);
40+
if (!existingConfig) {
41+
throw new Error(`Error! No active record exists of type ${ type }`);
42+
}
43+
44+
await this.VoipServerConfiguration.updateOne({ type, active: true }, config);
45+
46+
return true;
47+
}
48+
49+
// in-future, if we want to keep a track of duration during which a server config was active, then we'd need to modify the
50+
// IVoipServerConfig interface and add columns like "valid_from_ts" and "valid_to_ts"
51+
async deactivateServerConfigDataIfAvailable(type: ServerType): Promise<boolean> {
52+
await this.VoipServerConfiguration.updateMany({ type, active: true }, { $set: { active: false } });
53+
return true;
54+
}
55+
56+
async getServerConfigData(type: ServerType): Promise<IVoipServerConfig | null> {
57+
return this.VoipServerConfiguration.findOne({ type, active: true });
58+
}
59+
2060
// this is a dummy function to avoid having an empty IVoipService interface
2161
getConfiguration(): any {
2262
return {};

0 commit comments

Comments
 (0)