@oneuptime/common 9.3.22 → 9.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/IncomingCallLog.ts +521 -0
- package/Models/DatabaseModels/IncomingCallLogItem.ts +463 -0
- package/Models/DatabaseModels/IncomingCallPolicy.ts +811 -0
- package/Models/DatabaseModels/IncomingCallPolicyEscalationRule.ts +597 -0
- package/Models/DatabaseModels/Index.ts +18 -0
- package/Models/DatabaseModels/ProjectSCIMLog.ts +422 -0
- package/Models/DatabaseModels/StatusPageSCIMLog.ts +455 -0
- package/Models/DatabaseModels/User.ts +0 -15
- package/Models/DatabaseModels/UserIncomingCallNumber.ts +296 -0
- package/Server/API/UserIncomingCallNumberAPI.ts +128 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1768583966447-MigrationName.ts +121 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1768647802022-RemoveAlertPhoneNumberFromUser.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1768649699509-MigrationName.ts +787 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1768682071562-MigrationName.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Services/IncomingCallLogItemService.ts +10 -0
- package/Server/Services/IncomingCallLogService.ts +10 -0
- package/Server/Services/IncomingCallPolicyEscalationRuleService.ts +267 -0
- package/Server/Services/IncomingCallPolicyService.ts +10 -0
- package/Server/Services/Index.ts +7 -0
- package/Server/Services/ProjectSCIMLogService.ts +11 -0
- package/Server/Services/StatusPageSCIMLogService.ts +11 -0
- package/Server/Services/UserCallService.ts +31 -0
- package/Server/Services/UserIncomingCallNumberService.ts +258 -0
- package/Server/Services/UserSmsService.ts +31 -0
- package/Server/Utils/StartServer.ts +5 -0
- package/Types/Call/CallProvider.ts +99 -0
- package/Types/Call/CallProviderType.ts +6 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/IncomingCall/IncomingCallStatus.ts +13 -0
- package/Types/Permission.ts +126 -0
- package/Types/Phone.ts +53 -4
- package/Types/SCIM/SCIMLogStatus.ts +7 -0
- package/UI/Components/Diagram/ConceptCards.tsx +74 -0
- package/UI/Components/Diagram/HorizontalStepChain.tsx +92 -0
- package/UI/Components/Diagram/Index.ts +11 -0
- package/UI/Components/Diagram/NumberedSteps.tsx +77 -0
- package/UI/Components/Diagram/VerticalFlowSteps.tsx +59 -0
- package/UI/Components/Icon/Icon.tsx +10 -0
- package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +86 -2
- package/build/dist/Models/DatabaseModels/IncomingCallLog.js +565 -0
- package/build/dist/Models/DatabaseModels/IncomingCallLog.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncomingCallLogItem.js +497 -0
- package/build/dist/Models/DatabaseModels/IncomingCallLogItem.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js +840 -0
- package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyEscalationRule.js +619 -0
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyEscalationRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +16 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectSCIMLog.js +455 -0
- package/build/dist/Models/DatabaseModels/ProjectSCIMLog.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageSCIMLog.js +486 -0
- package/build/dist/Models/DatabaseModels/StatusPageSCIMLog.js.map +1 -0
- package/build/dist/Models/DatabaseModels/User.js +0 -16
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +315 -0
- package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -0
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js +72 -0
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768583966447-MigrationName.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768583966447-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768647802022-RemoveAlertPhoneNumberFromUser.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768647802022-RemoveAlertPhoneNumberFromUser.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768649699509-MigrationName.js +270 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768649699509-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768682071562-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1768682071562-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallLogItemService.js +9 -0
- package/build/dist/Server/Services/IncomingCallLogItemService.js.map +1 -0
- package/build/dist/Server/Services/IncomingCallLogService.js +9 -0
- package/build/dist/Server/Services/IncomingCallLogService.js.map +1 -0
- package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js +197 -0
- package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js.map +1 -0
- package/build/dist/Server/Services/IncomingCallPolicyService.js +9 -0
- package/build/dist/Server/Services/IncomingCallPolicyService.js.map +1 -0
- package/build/dist/Server/Services/Index.js +6 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/ProjectSCIMLogService.js +10 -0
- package/build/dist/Server/Services/ProjectSCIMLogService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageSCIMLogService.js +10 -0
- package/build/dist/Server/Services/StatusPageSCIMLogService.js.map +1 -0
- package/build/dist/Server/Services/UserCallService.js +21 -0
- package/build/dist/Server/Services/UserCallService.js.map +1 -1
- package/build/dist/Server/Services/UserIncomingCallNumberService.js +225 -0
- package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -0
- package/build/dist/Server/Services/UserSmsService.js +21 -0
- package/build/dist/Server/Services/UserSmsService.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +5 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Types/Call/CallProvider.js +2 -0
- package/build/dist/Types/Call/CallProvider.js.map +1 -0
- package/build/dist/Types/Call/CallProviderType.js +7 -0
- package/build/dist/Types/Call/CallProviderType.js.map +1 -0
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/IncomingCall/IncomingCallStatus.js +14 -0
- package/build/dist/Types/IncomingCall/IncomingCallStatus.js.map +1 -0
- package/build/dist/Types/Permission.js +104 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Phone.js +47 -3
- package/build/dist/Types/Phone.js.map +1 -1
- package/build/dist/Types/SCIM/SCIMLogStatus.js +8 -0
- package/build/dist/Types/SCIM/SCIMLogStatus.js.map +1 -0
- package/build/dist/UI/Components/Diagram/ConceptCards.js +30 -0
- package/build/dist/UI/Components/Diagram/ConceptCards.js.map +1 -0
- package/build/dist/UI/Components/Diagram/HorizontalStepChain.js +30 -0
- package/build/dist/UI/Components/Diagram/HorizontalStepChain.js.map +1 -0
- package/build/dist/UI/Components/Diagram/Index.js +5 -0
- package/build/dist/UI/Components/Diagram/Index.js.map +1 -0
- package/build/dist/UI/Components/Diagram/NumberedSteps.js +18 -0
- package/build/dist/UI/Components/Diagram/NumberedSteps.js.map +1 -0
- package/build/dist/UI/Components/Diagram/VerticalFlowSteps.js +16 -0
- package/build/dist/UI/Components/Diagram/VerticalFlowSteps.js.map +1 -0
- package/build/dist/UI/Components/Icon/Icon.js +4 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +30 -1
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
2
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
3
|
+
import { OnCreate } from "../Types/Database/Hooks";
|
|
4
|
+
import logger from "../Utils/Logger";
|
|
5
|
+
import DatabaseService from "./DatabaseService";
|
|
6
|
+
import ProjectService from "./ProjectService";
|
|
7
|
+
import SmsService from "./SmsService";
|
|
8
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
9
|
+
import ObjectID from "../../Types/ObjectID";
|
|
10
|
+
import Text from "../../Types/Text";
|
|
11
|
+
import Project from "../../Models/DatabaseModels/Project";
|
|
12
|
+
import Model from "../../Models/DatabaseModels/UserIncomingCallNumber";
|
|
13
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
14
|
+
|
|
15
|
+
export class Service extends DatabaseService<Model> {
|
|
16
|
+
public constructor() {
|
|
17
|
+
super(Model);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@CaptureSpan()
|
|
21
|
+
protected override async onBeforeCreate(
|
|
22
|
+
createBy: CreateBy<Model>,
|
|
23
|
+
): Promise<OnCreate<Model>> {
|
|
24
|
+
// Check if user is trying to set isVerified to true
|
|
25
|
+
if (!createBy.props.isRoot && createBy.data.isVerified) {
|
|
26
|
+
throw new BadDataException("isVerified cannot be set to true");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check if SMS notifications are enabled for this project
|
|
30
|
+
const project: Project | null = await ProjectService.findOneById({
|
|
31
|
+
id: createBy.data.projectId!,
|
|
32
|
+
props: {
|
|
33
|
+
isRoot: true,
|
|
34
|
+
},
|
|
35
|
+
select: {
|
|
36
|
+
enableSmsNotifications: true,
|
|
37
|
+
smsOrCallCurrentBalanceInUSDCents: true,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!project) {
|
|
42
|
+
throw new BadDataException("Project not found");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!project.enableSmsNotifications) {
|
|
46
|
+
throw new BadDataException(
|
|
47
|
+
"SMS notifications are disabled for this project. Please enable them in Project Settings > Notification Settings.",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
(project.smsOrCallCurrentBalanceInUSDCents as number) <= 100 &&
|
|
53
|
+
IsBillingEnabled
|
|
54
|
+
) {
|
|
55
|
+
throw new BadDataException(
|
|
56
|
+
"Your SMS balance is low. Please recharge your SMS balance in Project Settings > Notification Settings.",
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check if user already has a verified phone number for this project
|
|
61
|
+
const existingVerifiedNumber: Model | null = await this.findOneBy({
|
|
62
|
+
query: {
|
|
63
|
+
userId: createBy.data.userId!,
|
|
64
|
+
projectId: createBy.data.projectId!,
|
|
65
|
+
isVerified: true,
|
|
66
|
+
},
|
|
67
|
+
select: {
|
|
68
|
+
_id: true,
|
|
69
|
+
},
|
|
70
|
+
props: {
|
|
71
|
+
isRoot: true,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (existingVerifiedNumber) {
|
|
76
|
+
throw new BadDataException(
|
|
77
|
+
"You already have a verified phone number for this project. Please delete the existing one before adding a new one.",
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { carryForward: null, createBy };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@CaptureSpan()
|
|
85
|
+
protected override async onCreateSuccess(
|
|
86
|
+
_onCreate: OnCreate<Model>,
|
|
87
|
+
createdItem: Model,
|
|
88
|
+
): Promise<Model> {
|
|
89
|
+
if (!createdItem.isVerified) {
|
|
90
|
+
this.sendVerificationCode(createdItem);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return createdItem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@CaptureSpan()
|
|
97
|
+
public async resendVerificationCode(itemId: ObjectID): Promise<void> {
|
|
98
|
+
const item: Model | null = await this.findOneById({
|
|
99
|
+
id: itemId,
|
|
100
|
+
props: {
|
|
101
|
+
isRoot: true,
|
|
102
|
+
},
|
|
103
|
+
select: {
|
|
104
|
+
phone: true,
|
|
105
|
+
verificationCode: true,
|
|
106
|
+
isVerified: true,
|
|
107
|
+
projectId: true,
|
|
108
|
+
userId: true,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
if (!item) {
|
|
113
|
+
throw new BadDataException(
|
|
114
|
+
"Item with ID " + itemId.toString() + " not found",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (item.isVerified) {
|
|
119
|
+
throw new BadDataException("Phone Number already verified");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Check if SMS notifications are enabled for this project
|
|
123
|
+
const project: Project | null = await ProjectService.findOneById({
|
|
124
|
+
id: item.projectId!,
|
|
125
|
+
props: {
|
|
126
|
+
isRoot: true,
|
|
127
|
+
},
|
|
128
|
+
select: {
|
|
129
|
+
enableSmsNotifications: true,
|
|
130
|
+
smsOrCallCurrentBalanceInUSDCents: true,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (!project) {
|
|
135
|
+
throw new BadDataException("Project not found");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!project.enableSmsNotifications) {
|
|
139
|
+
throw new BadDataException(
|
|
140
|
+
"SMS notifications are disabled for this project. Please enable them in Project Settings > Notification Settings.",
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (
|
|
145
|
+
(project.smsOrCallCurrentBalanceInUSDCents as number) <= 100 &&
|
|
146
|
+
IsBillingEnabled
|
|
147
|
+
) {
|
|
148
|
+
throw new BadDataException(
|
|
149
|
+
"Your SMS balance is low. Please recharge your SMS balance in Project Settings > Notification Settings.",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Generate new verification code
|
|
154
|
+
item.verificationCode = Text.generateRandomNumber(6);
|
|
155
|
+
|
|
156
|
+
await this.updateOneById({
|
|
157
|
+
id: item.id!,
|
|
158
|
+
props: {
|
|
159
|
+
isRoot: true,
|
|
160
|
+
},
|
|
161
|
+
data: {
|
|
162
|
+
verificationCode: item.verificationCode,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
this.sendVerificationCode(item);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public sendVerificationCode(item: Model): void {
|
|
170
|
+
// Send verification SMS
|
|
171
|
+
SmsService.sendSms(
|
|
172
|
+
{
|
|
173
|
+
to: item.phone!,
|
|
174
|
+
message:
|
|
175
|
+
"This message is from OneUptime. Your verification code for incoming call routing is " +
|
|
176
|
+
item.verificationCode,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
projectId: item.projectId,
|
|
180
|
+
isSensitive: true,
|
|
181
|
+
userId: item.userId!,
|
|
182
|
+
},
|
|
183
|
+
).catch((err: Error) => {
|
|
184
|
+
logger.error(err);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@CaptureSpan()
|
|
189
|
+
public async verifyPhoneNumber(
|
|
190
|
+
itemId: ObjectID,
|
|
191
|
+
userId: ObjectID,
|
|
192
|
+
code: string,
|
|
193
|
+
): Promise<void> {
|
|
194
|
+
const item: Model | null = await this.findOneById({
|
|
195
|
+
id: itemId,
|
|
196
|
+
props: {
|
|
197
|
+
isRoot: true,
|
|
198
|
+
},
|
|
199
|
+
select: {
|
|
200
|
+
userId: true,
|
|
201
|
+
verificationCode: true,
|
|
202
|
+
isVerified: true,
|
|
203
|
+
projectId: true,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (!item) {
|
|
208
|
+
throw new BadDataException("Item not found");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Check user ID
|
|
212
|
+
if (item.userId?.toString() !== userId.toString()) {
|
|
213
|
+
throw new BadDataException("Invalid user ID");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (item.isVerified) {
|
|
217
|
+
throw new BadDataException("Phone number is already verified");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (item.verificationCode !== code) {
|
|
221
|
+
throw new BadDataException("Invalid verification code");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Check if user already has a verified number for this project
|
|
225
|
+
const existingVerifiedNumber: Model | null = await this.findOneBy({
|
|
226
|
+
query: {
|
|
227
|
+
userId: item.userId!,
|
|
228
|
+
projectId: item.projectId!,
|
|
229
|
+
isVerified: true,
|
|
230
|
+
},
|
|
231
|
+
select: {
|
|
232
|
+
_id: true,
|
|
233
|
+
},
|
|
234
|
+
props: {
|
|
235
|
+
isRoot: true,
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (existingVerifiedNumber) {
|
|
240
|
+
throw new BadDataException(
|
|
241
|
+
"You already have a verified phone number for this project. Please delete the existing one before verifying a new one.",
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Mark as verified
|
|
246
|
+
await this.updateOneById({
|
|
247
|
+
id: itemId,
|
|
248
|
+
props: {
|
|
249
|
+
isRoot: true,
|
|
250
|
+
},
|
|
251
|
+
data: {
|
|
252
|
+
isVerified: true,
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export default new Service();
|
|
@@ -137,6 +137,37 @@ export class Service extends DatabaseService<Model> {
|
|
|
137
137
|
throw new BadDataException("Phone Number already verified");
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// Check if SMS notifications are enabled for this project
|
|
141
|
+
const project: Project | null = await ProjectService.findOneById({
|
|
142
|
+
id: item.projectId!,
|
|
143
|
+
props: {
|
|
144
|
+
isRoot: true,
|
|
145
|
+
},
|
|
146
|
+
select: {
|
|
147
|
+
enableSmsNotifications: true,
|
|
148
|
+
smsOrCallCurrentBalanceInUSDCents: true,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (!project) {
|
|
153
|
+
throw new BadDataException("Project not found");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!project.enableSmsNotifications) {
|
|
157
|
+
throw new BadDataException(
|
|
158
|
+
"SMS notifications are disabled for this project. Please enable them in Project Settings > Notification Settings.",
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (
|
|
163
|
+
(project.smsOrCallCurrentBalanceInUSDCents as number) <= 100 &&
|
|
164
|
+
IsBillingEnabled
|
|
165
|
+
) {
|
|
166
|
+
throw new BadDataException(
|
|
167
|
+
"Your SMS balance is low. Please recharge your SMS balance in Project Settings > Notification Settings.",
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
140
171
|
// generate new verification code
|
|
141
172
|
item.verificationCode = Text.generateRandomNumber(6);
|
|
142
173
|
|
|
@@ -45,6 +45,11 @@ const app: ExpressApplication = Express.getExpressApp();
|
|
|
45
45
|
app.disable("x-powered-by");
|
|
46
46
|
app.set("port", process.env["PORT"]);
|
|
47
47
|
app.set("view engine", "ejs");
|
|
48
|
+
/*
|
|
49
|
+
* Enable trust proxy to correctly interpret X-Forwarded-* headers from reverse proxies
|
|
50
|
+
* This is needed for req.protocol, req.ip to be correct when behind nginx/load balancers
|
|
51
|
+
*/
|
|
52
|
+
app.set("trust proxy", true);
|
|
48
53
|
app.use(CookieParser());
|
|
49
54
|
|
|
50
55
|
const jsonBodyParserMiddleware: RequestHandler = ExpressJson({
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { JSONObject } from "../JSON";
|
|
2
|
+
|
|
3
|
+
// Phone number from provider search
|
|
4
|
+
export interface AvailablePhoneNumber {
|
|
5
|
+
phoneNumber: string; // "+14155550123"
|
|
6
|
+
friendlyName: string; // "(415) 555-0123"
|
|
7
|
+
locality?: string; // "San Francisco"
|
|
8
|
+
region?: string; // "CA"
|
|
9
|
+
country: string; // "US"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Purchased phone number details
|
|
13
|
+
export interface PurchasedPhoneNumber {
|
|
14
|
+
phoneNumberId: string; // Provider's ID (e.g., Twilio SID)
|
|
15
|
+
phoneNumber: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Owned phone number (already purchased in Twilio account)
|
|
19
|
+
export interface OwnedPhoneNumber {
|
|
20
|
+
phoneNumberId: string; // Provider's ID (e.g., Twilio SID)
|
|
21
|
+
phoneNumber: string; // "+14155550123"
|
|
22
|
+
friendlyName: string; // "(415) 555-0123"
|
|
23
|
+
voiceUrl?: string | undefined; // Current webhook URL configured
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Search options for available numbers
|
|
27
|
+
export interface SearchNumberOptions {
|
|
28
|
+
countryCode: string;
|
|
29
|
+
areaCode?: string;
|
|
30
|
+
contains?: string;
|
|
31
|
+
limit?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Options for dial operation
|
|
35
|
+
export interface DialOptions {
|
|
36
|
+
toPhoneNumber: string;
|
|
37
|
+
fromPhoneNumber: string;
|
|
38
|
+
timeoutSeconds: number;
|
|
39
|
+
statusCallbackUrl: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Parsed incoming call data from webhook
|
|
43
|
+
export interface IncomingCallData {
|
|
44
|
+
callId: string; // Provider's call ID
|
|
45
|
+
callerPhoneNumber: string;
|
|
46
|
+
calledPhoneNumber: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Parsed dial status data from webhook
|
|
50
|
+
export interface DialStatusData {
|
|
51
|
+
callId: string;
|
|
52
|
+
dialStatus: "completed" | "busy" | "no-answer" | "failed" | "canceled";
|
|
53
|
+
dialDurationSeconds?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Express Request type for webhook parsing
|
|
57
|
+
export interface WebhookRequest {
|
|
58
|
+
body: JSONObject;
|
|
59
|
+
headers: { [key: string]: string | string[] | undefined };
|
|
60
|
+
originalUrl: string;
|
|
61
|
+
url: string;
|
|
62
|
+
protocol: string;
|
|
63
|
+
get: (name: string) => string | undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Call provider interface - all providers must implement this
|
|
67
|
+
export interface ICallProvider {
|
|
68
|
+
// Phone number management
|
|
69
|
+
searchAvailableNumbers(
|
|
70
|
+
options: SearchNumberOptions,
|
|
71
|
+
): Promise<AvailablePhoneNumber[]>;
|
|
72
|
+
listOwnedNumbers(): Promise<OwnedPhoneNumber[]>;
|
|
73
|
+
purchaseNumber(
|
|
74
|
+
phoneNumber: string,
|
|
75
|
+
webhookUrl: string,
|
|
76
|
+
): Promise<PurchasedPhoneNumber>;
|
|
77
|
+
assignExistingNumber(
|
|
78
|
+
phoneNumberId: string,
|
|
79
|
+
webhookUrl: string,
|
|
80
|
+
): Promise<PurchasedPhoneNumber>;
|
|
81
|
+
releaseNumber(phoneNumberId: string): Promise<void>;
|
|
82
|
+
updateWebhookUrl(phoneNumberId: string, webhookUrl: string): Promise<void>;
|
|
83
|
+
|
|
84
|
+
// Voice response generation (provider-specific markup)
|
|
85
|
+
generateGreetingResponse(message: string): string;
|
|
86
|
+
generateDialResponse(options: DialOptions): string;
|
|
87
|
+
generateHangupResponse(message?: string): string;
|
|
88
|
+
generateEscalationResponse(
|
|
89
|
+
message: string,
|
|
90
|
+
nextDialOptions: DialOptions,
|
|
91
|
+
): string;
|
|
92
|
+
|
|
93
|
+
// Webhook parsing
|
|
94
|
+
parseIncomingCallWebhook(request: WebhookRequest): IncomingCallData;
|
|
95
|
+
parseDialStatusWebhook(request: WebhookRequest): DialStatusData;
|
|
96
|
+
|
|
97
|
+
// Webhook signature validation
|
|
98
|
+
validateWebhookSignature(request: WebhookRequest, signature: string): boolean;
|
|
99
|
+
}
|
package/Types/Icon/IconProp.ts
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
enum IncomingCallStatus {
|
|
2
|
+
Initiated = "Initiated",
|
|
3
|
+
Ringing = "Ringing",
|
|
4
|
+
Connected = "Connected",
|
|
5
|
+
Escalated = "Escalated",
|
|
6
|
+
NoAnswer = "NoAnswer",
|
|
7
|
+
Failed = "Failed",
|
|
8
|
+
Completed = "Completed",
|
|
9
|
+
CallerHungUp = "CallerHungUp",
|
|
10
|
+
Busy = "Busy",
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default IncomingCallStatus;
|
package/Types/Permission.ts
CHANGED
|
@@ -164,6 +164,9 @@ enum Permission {
|
|
|
164
164
|
ReadWorkspaceNotificationLog = "ReadWorkspaceNotificationLog",
|
|
165
165
|
ReadLlmLog = "ReadLlmLog",
|
|
166
166
|
|
|
167
|
+
ReadProjectSCIMLog = "ReadProjectSCIMLog",
|
|
168
|
+
ReadStatusPageSCIMLog = "ReadStatusPageSCIMLog",
|
|
169
|
+
|
|
167
170
|
CreateIncidentOwnerTeam = "CreateIncidentOwnerTeam",
|
|
168
171
|
DeleteIncidentOwnerTeam = "DeleteIncidentOwnerTeam",
|
|
169
172
|
EditIncidentOwnerTeam = "EditIncidentOwnerTeam",
|
|
@@ -598,6 +601,24 @@ enum Permission {
|
|
|
598
601
|
DeleteProjectOnCallDutyPolicyEscalationRuleTeam = "DeleteProjectOnCallDutyPolicyEscalationRuleTeam",
|
|
599
602
|
ReadProjectOnCallDutyPolicyEscalationRuleTeam = "ReadProjectOnCallDutyPolicyEscalationRuleTeam",
|
|
600
603
|
|
|
604
|
+
// Incoming Call Policy Permissions
|
|
605
|
+
CreateProjectIncomingCallPolicy = "CreateProjectIncomingCallPolicy",
|
|
606
|
+
EditProjectIncomingCallPolicy = "EditProjectIncomingCallPolicy",
|
|
607
|
+
DeleteProjectIncomingCallPolicy = "DeleteProjectIncomingCallPolicy",
|
|
608
|
+
ReadProjectIncomingCallPolicy = "ReadProjectIncomingCallPolicy",
|
|
609
|
+
|
|
610
|
+
// Incoming Call Policy Escalation Rule Permissions
|
|
611
|
+
CreateProjectIncomingCallPolicyEscalationRule = "CreateProjectIncomingCallPolicyEscalationRule",
|
|
612
|
+
EditProjectIncomingCallPolicyEscalationRule = "EditProjectIncomingCallPolicyEscalationRule",
|
|
613
|
+
DeleteProjectIncomingCallPolicyEscalationRule = "DeleteProjectIncomingCallPolicyEscalationRule",
|
|
614
|
+
ReadProjectIncomingCallPolicyEscalationRule = "ReadProjectIncomingCallPolicyEscalationRule",
|
|
615
|
+
|
|
616
|
+
// Incoming Call Log Permissions
|
|
617
|
+
ReadProjectIncomingCallLog = "ReadProjectIncomingCallLog",
|
|
618
|
+
|
|
619
|
+
// Incoming Call Log Item Permissions
|
|
620
|
+
ReadProjectIncomingCallLogItem = "ReadProjectIncomingCallLogItem",
|
|
621
|
+
|
|
601
622
|
// Project SMTP Config (Team Permission)
|
|
602
623
|
CreateProjectSMTPConfig = "CreateProjectSMTPConfig",
|
|
603
624
|
EditProjectSMTPConfig = "EditProjectSMTPConfig",
|
|
@@ -1373,6 +1394,23 @@ export class PermissionHelper {
|
|
|
1373
1394
|
isAccessControlPermission: false,
|
|
1374
1395
|
},
|
|
1375
1396
|
|
|
1397
|
+
{
|
|
1398
|
+
permission: Permission.ReadProjectSCIMLog,
|
|
1399
|
+
title: "Read Project SCIM Log",
|
|
1400
|
+
description:
|
|
1401
|
+
"This permission can read SCIM provisioning logs of the project.",
|
|
1402
|
+
isAssignableToTenant: true,
|
|
1403
|
+
isAccessControlPermission: false,
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
permission: Permission.ReadStatusPageSCIMLog,
|
|
1407
|
+
title: "Read Status Page SCIM Log",
|
|
1408
|
+
description:
|
|
1409
|
+
"This permission can read SCIM provisioning logs of status pages in the project.",
|
|
1410
|
+
isAssignableToTenant: true,
|
|
1411
|
+
isAccessControlPermission: false,
|
|
1412
|
+
},
|
|
1413
|
+
|
|
1376
1414
|
{
|
|
1377
1415
|
permission: Permission.CreateProjectMonitorStatus,
|
|
1378
1416
|
title: "Create Monitor Status",
|
|
@@ -2475,6 +2513,94 @@ export class PermissionHelper {
|
|
|
2475
2513
|
isAccessControlPermission: false,
|
|
2476
2514
|
},
|
|
2477
2515
|
|
|
2516
|
+
// Incoming Call Policy Permissions
|
|
2517
|
+
{
|
|
2518
|
+
permission: Permission.CreateProjectIncomingCallPolicy,
|
|
2519
|
+
title: "Create Incoming Call Policy",
|
|
2520
|
+
description:
|
|
2521
|
+
"This permission can create incoming call policies for this project.",
|
|
2522
|
+
isAssignableToTenant: true,
|
|
2523
|
+
isAccessControlPermission: false,
|
|
2524
|
+
},
|
|
2525
|
+
{
|
|
2526
|
+
permission: Permission.DeleteProjectIncomingCallPolicy,
|
|
2527
|
+
title: "Delete Incoming Call Policy",
|
|
2528
|
+
description:
|
|
2529
|
+
"This permission can delete incoming call policies of this project.",
|
|
2530
|
+
isAssignableToTenant: true,
|
|
2531
|
+
isAccessControlPermission: false,
|
|
2532
|
+
},
|
|
2533
|
+
{
|
|
2534
|
+
permission: Permission.EditProjectIncomingCallPolicy,
|
|
2535
|
+
title: "Edit Incoming Call Policy",
|
|
2536
|
+
description:
|
|
2537
|
+
"This permission can edit incoming call policies of this project.",
|
|
2538
|
+
isAssignableToTenant: true,
|
|
2539
|
+
isAccessControlPermission: false,
|
|
2540
|
+
},
|
|
2541
|
+
{
|
|
2542
|
+
permission: Permission.ReadProjectIncomingCallPolicy,
|
|
2543
|
+
title: "Read Incoming Call Policy",
|
|
2544
|
+
description:
|
|
2545
|
+
"This permission can read incoming call policies of this project.",
|
|
2546
|
+
isAssignableToTenant: true,
|
|
2547
|
+
isAccessControlPermission: false,
|
|
2548
|
+
},
|
|
2549
|
+
|
|
2550
|
+
// Incoming Call Policy Escalation Rule Permissions
|
|
2551
|
+
{
|
|
2552
|
+
permission: Permission.CreateProjectIncomingCallPolicyEscalationRule,
|
|
2553
|
+
title: "Create Incoming Call Policy Escalation Rule",
|
|
2554
|
+
description:
|
|
2555
|
+
"This permission can create incoming call policy escalation rules for this project.",
|
|
2556
|
+
isAssignableToTenant: true,
|
|
2557
|
+
isAccessControlPermission: false,
|
|
2558
|
+
},
|
|
2559
|
+
{
|
|
2560
|
+
permission: Permission.DeleteProjectIncomingCallPolicyEscalationRule,
|
|
2561
|
+
title: "Delete Incoming Call Policy Escalation Rule",
|
|
2562
|
+
description:
|
|
2563
|
+
"This permission can delete incoming call policy escalation rules of this project.",
|
|
2564
|
+
isAssignableToTenant: true,
|
|
2565
|
+
isAccessControlPermission: false,
|
|
2566
|
+
},
|
|
2567
|
+
{
|
|
2568
|
+
permission: Permission.EditProjectIncomingCallPolicyEscalationRule,
|
|
2569
|
+
title: "Edit Incoming Call Policy Escalation Rule",
|
|
2570
|
+
description:
|
|
2571
|
+
"This permission can edit incoming call policy escalation rules of this project.",
|
|
2572
|
+
isAssignableToTenant: true,
|
|
2573
|
+
isAccessControlPermission: false,
|
|
2574
|
+
},
|
|
2575
|
+
{
|
|
2576
|
+
permission: Permission.ReadProjectIncomingCallPolicyEscalationRule,
|
|
2577
|
+
title: "Read Incoming Call Policy Escalation Rule",
|
|
2578
|
+
description:
|
|
2579
|
+
"This permission can read incoming call policy escalation rules of this project.",
|
|
2580
|
+
isAssignableToTenant: true,
|
|
2581
|
+
isAccessControlPermission: false,
|
|
2582
|
+
},
|
|
2583
|
+
|
|
2584
|
+
// Incoming Call Log Permissions
|
|
2585
|
+
{
|
|
2586
|
+
permission: Permission.ReadProjectIncomingCallLog,
|
|
2587
|
+
title: "Read Incoming Call Log",
|
|
2588
|
+
description:
|
|
2589
|
+
"This permission can read incoming call logs of this project.",
|
|
2590
|
+
isAssignableToTenant: true,
|
|
2591
|
+
isAccessControlPermission: false,
|
|
2592
|
+
},
|
|
2593
|
+
|
|
2594
|
+
// Incoming Call Log Item Permissions
|
|
2595
|
+
{
|
|
2596
|
+
permission: Permission.ReadProjectIncomingCallLogItem,
|
|
2597
|
+
title: "Read Incoming Call Log Item",
|
|
2598
|
+
description:
|
|
2599
|
+
"This permission can read incoming call log items of this project.",
|
|
2600
|
+
isAssignableToTenant: true,
|
|
2601
|
+
isAccessControlPermission: false,
|
|
2602
|
+
},
|
|
2603
|
+
|
|
2478
2604
|
{
|
|
2479
2605
|
permission:
|
|
2480
2606
|
Permission.CreateProjectOnCallDutyPolicyEscalationRuleSchedule,
|
package/Types/Phone.ts
CHANGED
|
@@ -13,7 +13,7 @@ export default class Phone extends DatabaseProperty {
|
|
|
13
13
|
public static pickPhoneNumberToSendSMSOrCallFrom(data: {
|
|
14
14
|
to: Phone | string;
|
|
15
15
|
primaryPhoneNumberToPickFrom: Phone | string;
|
|
16
|
-
|
|
16
|
+
secondaryPhoneNumbersToPickFrom: Phone[] | string[];
|
|
17
17
|
}): Phone {
|
|
18
18
|
/*
|
|
19
19
|
* convert all to string, so that we can compare them
|
|
@@ -28,8 +28,8 @@ export default class Phone extends DatabaseProperty {
|
|
|
28
28
|
? data.primaryPhoneNumberToPickFrom
|
|
29
29
|
: data.primaryPhoneNumberToPickFrom.toString();
|
|
30
30
|
|
|
31
|
-
const
|
|
32
|
-
data.
|
|
31
|
+
const secondaryPhoneNumbersToPickFrom: string[] =
|
|
32
|
+
data.secondaryPhoneNumbersToPickFrom.map((phone: Phone | string) => {
|
|
33
33
|
return typeof phone === "string" ? phone : phone.toString();
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -51,7 +51,7 @@ export default class Phone extends DatabaseProperty {
|
|
|
51
51
|
return new Phone(primaryPhoneNumberToPickFrom);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
for (const secondaryPhoneNumber of
|
|
54
|
+
for (const secondaryPhoneNumber of secondaryPhoneNumbersToPickFrom) {
|
|
55
55
|
const secondaryPhoneNumberCountryCode: string = normalizePhoneNumber(
|
|
56
56
|
secondaryPhoneNumber,
|
|
57
57
|
).substring(0, 2);
|
|
@@ -148,4 +148,53 @@ export default class Phone extends DatabaseProperty {
|
|
|
148
148
|
example: { _type: ObjectType.Phone, value: "+1-555-123-4567" },
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
|
+
|
|
152
|
+
// Map of calling code prefixes to ISO country codes
|
|
153
|
+
private static readonly CALLING_CODE_TO_COUNTRY_MAP: Record<string, string> =
|
|
154
|
+
{
|
|
155
|
+
"+1": "US", // US and Canada
|
|
156
|
+
"+44": "GB", // United Kingdom
|
|
157
|
+
"+61": "AU", // Australia
|
|
158
|
+
"+49": "DE", // Germany
|
|
159
|
+
"+33": "FR", // France
|
|
160
|
+
"+91": "IN", // India
|
|
161
|
+
"+81": "JP", // Japan
|
|
162
|
+
"+86": "CN", // China
|
|
163
|
+
"+55": "BR", // Brazil
|
|
164
|
+
"+52": "MX", // Mexico
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Extract ISO country code from a phone number
|
|
169
|
+
* @param phoneNumber - The phone number (e.g., "+15551234567")
|
|
170
|
+
* @returns ISO country code (e.g., "US") or "US" as default
|
|
171
|
+
*/
|
|
172
|
+
public static getCountryCodeFromPhoneNumber(phoneNumber: string): string {
|
|
173
|
+
for (const [prefix, countryCode] of Object.entries(
|
|
174
|
+
Phone.CALLING_CODE_TO_COUNTRY_MAP,
|
|
175
|
+
)) {
|
|
176
|
+
if (phoneNumber.startsWith(prefix)) {
|
|
177
|
+
return countryCode;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return "US"; // Default to US if unknown
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Extract area code from a phone number
|
|
185
|
+
* Currently only supports US/Canada (+1) numbers
|
|
186
|
+
* @param phoneNumber - The phone number (e.g., "+15551234567")
|
|
187
|
+
* @returns Area code (e.g., "555") or empty string for non-US/CA numbers
|
|
188
|
+
*/
|
|
189
|
+
public static getAreaCodeFromPhoneNumber(phoneNumber: string): string {
|
|
190
|
+
// For US/Canada numbers (+1), extract the next 3 digits
|
|
191
|
+
if (phoneNumber.startsWith("+1")) {
|
|
192
|
+
const number: string = phoneNumber.substring(2);
|
|
193
|
+
if (number.length >= 3) {
|
|
194
|
+
return number.substring(0, 3);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// For other countries, area code extraction is more complex
|
|
198
|
+
return "";
|
|
199
|
+
}
|
|
151
200
|
}
|