@oneuptime/common 9.5.12 → 10.0.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/GlobalConfig.ts +19 -0
- package/Server/API/UserCallAPI.ts +19 -0
- package/Server/API/UserEmailAPI.ts +19 -0
- package/Server/API/UserPushAPI.ts +66 -22
- package/Server/API/UserSmsAPI.ts +19 -0
- package/Server/API/UserWhatsAppAPI.ts +19 -0
- package/Server/DatabaseConfig.ts +7 -0
- package/Server/EnvironmentConfig.ts +15 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770834237091-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/ProjectService.ts +14 -0
- package/Server/Services/PushNotificationService.ts +136 -12
- package/Server/Services/UserNotificationRuleService.ts +153 -111
- package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +142 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +13 -0
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +21 -0
- package/Server/Utils/VM/VMRunner.ts +214 -37
- package/Types/Monitor/CriteriaFilter.ts +9 -1
- package/Types/Monitor/DomainMonitor/DomainMonitorResponse.ts +15 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +67 -0
- package/Types/Monitor/MonitorStep.ts +32 -0
- package/Types/Monitor/MonitorStepDomainMonitor.ts +33 -0
- package/Types/Monitor/MonitorType.ts +66 -2
- package/Types/Probe/ProbeMonitorResponse.ts +2 -0
- package/UI/Components/CardSelect/CardSelect.tsx +133 -67
- package/UI/Components/Forms/Types/Field.ts +7 -2
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +21 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Server/API/UserCallAPI.js +17 -0
- package/build/dist/Server/API/UserCallAPI.js.map +1 -1
- package/build/dist/Server/API/UserEmailAPI.js +17 -0
- package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
- package/build/dist/Server/API/UserPushAPI.js +50 -12
- package/build/dist/Server/API/UserPushAPI.js.map +1 -1
- package/build/dist/Server/API/UserSmsAPI.js +17 -0
- package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
- package/build/dist/Server/API/UserWhatsAppAPI.js +17 -0
- package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
- package/build/dist/Server/DatabaseConfig.js +9 -0
- package/build/dist/Server/DatabaseConfig.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +5 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237091-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770834237091-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +12 -2
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +74 -12
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +96 -109
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +113 -0
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +10 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +16 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +182 -20
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +8 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/DomainMonitor/DomainMonitorResponse.js +2 -0
- package/build/dist/Types/Monitor/DomainMonitor/DomainMonitorResponse.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +62 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +22 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +24 -0
- package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorType.js +58 -2
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/UI/Components/CardSelect/CardSelect.js +55 -20
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
- package/package.json +2 -1
|
@@ -10,9 +10,16 @@ import {
|
|
|
10
10
|
VapidPublicKey,
|
|
11
11
|
VapidPrivateKey,
|
|
12
12
|
VapidSubject,
|
|
13
|
+
ExpoAccessToken,
|
|
14
|
+
PushNotificationRelayUrl,
|
|
13
15
|
} from "../EnvironmentConfig";
|
|
14
16
|
import webpush from "web-push";
|
|
15
17
|
import { Expo, ExpoPushMessage, ExpoPushTicket } from "expo-server-sdk";
|
|
18
|
+
import API from "../../Utils/API";
|
|
19
|
+
import URL from "../../Types/API/URL";
|
|
20
|
+
import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
|
|
21
|
+
import HTTPResponse from "../../Types/API/HTTPResponse";
|
|
22
|
+
import { JSONObject } from "../../Types/JSON";
|
|
16
23
|
import PushNotificationUtil from "../Utils/PushNotificationUtil";
|
|
17
24
|
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
18
25
|
import UserPush from "../../Models/DatabaseModels/UserPush";
|
|
@@ -43,7 +50,9 @@ export interface PushNotificationOptions {
|
|
|
43
50
|
|
|
44
51
|
export default class PushNotificationService {
|
|
45
52
|
public static isWebPushInitialized = false;
|
|
46
|
-
private static expoClient: Expo = new Expo(
|
|
53
|
+
private static expoClient: Expo = new Expo(
|
|
54
|
+
ExpoAccessToken ? { accessToken: ExpoAccessToken } : undefined,
|
|
55
|
+
);
|
|
47
56
|
|
|
48
57
|
public static initializeWebPush(): void {
|
|
49
58
|
if (this.isWebPushInitialized) {
|
|
@@ -340,20 +349,33 @@ export default class PushNotificationService {
|
|
|
340
349
|
);
|
|
341
350
|
}
|
|
342
351
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
dataPayload[key] = String(message.data[key]);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (message.url || message.clickAction) {
|
|
351
|
-
dataPayload["url"] = message.url || message.clickAction || "";
|
|
352
|
+
const dataPayload: { [key: string]: string } = {};
|
|
353
|
+
if (message.data) {
|
|
354
|
+
for (const key of Object.keys(message.data)) {
|
|
355
|
+
dataPayload[key] = String(message.data[key]);
|
|
352
356
|
}
|
|
357
|
+
}
|
|
358
|
+
if (message.url || message.clickAction) {
|
|
359
|
+
dataPayload["url"] = message.url || message.clickAction || "";
|
|
360
|
+
}
|
|
353
361
|
|
|
354
|
-
|
|
355
|
-
|
|
362
|
+
const channelId: string =
|
|
363
|
+
deviceType === PushDeviceType.Android ? "oncall_high" : "default";
|
|
364
|
+
|
|
365
|
+
// If EXPO_ACCESS_TOKEN is not set, relay through the push notification gateway
|
|
366
|
+
if (!ExpoAccessToken) {
|
|
367
|
+
await this.sendViaRelay(
|
|
368
|
+
expoPushToken,
|
|
369
|
+
message,
|
|
370
|
+
dataPayload,
|
|
371
|
+
channelId,
|
|
372
|
+
deviceType,
|
|
373
|
+
);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
356
376
|
|
|
377
|
+
// Send directly via Expo SDK
|
|
378
|
+
try {
|
|
357
379
|
const expoPushMessage: ExpoPushMessage = {
|
|
358
380
|
to: expoPushToken,
|
|
359
381
|
title: message.title,
|
|
@@ -403,6 +425,108 @@ export default class PushNotificationService {
|
|
|
403
425
|
}
|
|
404
426
|
}
|
|
405
427
|
|
|
428
|
+
private static async sendViaRelay(
|
|
429
|
+
expoPushToken: string,
|
|
430
|
+
message: PushNotificationMessage,
|
|
431
|
+
dataPayload: { [key: string]: string },
|
|
432
|
+
channelId: string,
|
|
433
|
+
deviceType: PushDeviceType,
|
|
434
|
+
): Promise<void> {
|
|
435
|
+
logger.info(
|
|
436
|
+
`Sending ${deviceType} push notification via relay: ${PushNotificationRelayUrl}`,
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
try {
|
|
440
|
+
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
441
|
+
await API.post<JSONObject>({
|
|
442
|
+
url: URL.fromString(PushNotificationRelayUrl),
|
|
443
|
+
data: {
|
|
444
|
+
to: expoPushToken,
|
|
445
|
+
title: message.title || "",
|
|
446
|
+
body: message.body || "",
|
|
447
|
+
data: dataPayload,
|
|
448
|
+
sound: "default",
|
|
449
|
+
priority: "high",
|
|
450
|
+
channelId: channelId,
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
if (response instanceof HTTPErrorResponse) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
`Push relay error: ${JSON.stringify(response.jsonData)}`,
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
logger.info(
|
|
461
|
+
`Push notification sent via relay successfully to ${deviceType} device`,
|
|
462
|
+
);
|
|
463
|
+
} catch (error: any) {
|
|
464
|
+
logger.error(
|
|
465
|
+
`Failed to send push notification via relay to ${deviceType} device: ${error.message}`,
|
|
466
|
+
);
|
|
467
|
+
throw error;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
public static isValidExpoPushToken(token: string): boolean {
|
|
472
|
+
return Expo.isExpoPushToken(token);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
public static hasExpoAccessToken(): boolean {
|
|
476
|
+
return Boolean(ExpoAccessToken);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
public static async sendRelayPushNotification(data: {
|
|
480
|
+
to: string;
|
|
481
|
+
title?: string;
|
|
482
|
+
body?: string;
|
|
483
|
+
data?: { [key: string]: string };
|
|
484
|
+
sound?: string;
|
|
485
|
+
priority?: string;
|
|
486
|
+
channelId?: string;
|
|
487
|
+
}): Promise<void> {
|
|
488
|
+
if (!ExpoAccessToken) {
|
|
489
|
+
throw new Error(
|
|
490
|
+
"Push relay is not configured. EXPO_ACCESS_TOKEN is not set on this server.",
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const expoPushMessage: ExpoPushMessage = {
|
|
495
|
+
to: data.to,
|
|
496
|
+
title: data.title || "",
|
|
497
|
+
body: data.body || "",
|
|
498
|
+
data: data.data || {},
|
|
499
|
+
sound: (data.sound as "default" | null) || "default",
|
|
500
|
+
priority: (data.priority as "default" | "normal" | "high") || "high",
|
|
501
|
+
channelId: data.channelId || "default",
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const tickets: ExpoPushTicket[] =
|
|
505
|
+
await this.expoClient.sendPushNotificationsAsync([expoPushMessage]);
|
|
506
|
+
|
|
507
|
+
const ticket: ExpoPushTicket | undefined = tickets[0];
|
|
508
|
+
|
|
509
|
+
if (ticket && ticket.status === "error") {
|
|
510
|
+
const errorTicket: ExpoPushTicket & {
|
|
511
|
+
message?: string;
|
|
512
|
+
details?: { error?: string };
|
|
513
|
+
} = ticket as ExpoPushTicket & {
|
|
514
|
+
message?: string;
|
|
515
|
+
details?: { error?: string };
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
logger.error(
|
|
519
|
+
`Push relay: Expo push notification error: ${errorTicket.message}`,
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
throw new Error(
|
|
523
|
+
`Failed to send push notification: ${errorTicket.message}`,
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
logger.info(`Push relay: notification sent successfully to ${data.to}`);
|
|
528
|
+
}
|
|
529
|
+
|
|
406
530
|
public static async sendPushNotificationToUser(
|
|
407
531
|
userId: ObjectID,
|
|
408
532
|
projectId: ObjectID,
|
|
@@ -69,6 +69,14 @@ import PushNotificationMessage from "../../Types/PushNotification/PushNotificati
|
|
|
69
69
|
import logger from "../Utils/Logger";
|
|
70
70
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
71
71
|
|
|
72
|
+
export interface NotificationMethodDescriptor {
|
|
73
|
+
userEmailId?: ObjectID;
|
|
74
|
+
userSmsId?: ObjectID;
|
|
75
|
+
userCallId?: ObjectID;
|
|
76
|
+
userWhatsAppId?: ObjectID;
|
|
77
|
+
userPushId?: ObjectID;
|
|
78
|
+
}
|
|
79
|
+
|
|
72
80
|
export class Service extends DatabaseService<Model> {
|
|
73
81
|
public constructor() {
|
|
74
82
|
super(Model);
|
|
@@ -2207,13 +2215,89 @@ export class Service extends DatabaseService<Model> {
|
|
|
2207
2215
|
}
|
|
2208
2216
|
|
|
2209
2217
|
@CaptureSpan()
|
|
2210
|
-
public async
|
|
2218
|
+
public async addDefaultNotificationRulesForVerifiedMethod(data: {
|
|
2211
2219
|
projectId: ObjectID;
|
|
2212
2220
|
userId: ObjectID;
|
|
2213
|
-
|
|
2221
|
+
notificationMethod: NotificationMethodDescriptor;
|
|
2214
2222
|
}): Promise<void> {
|
|
2215
|
-
const { projectId, userId,
|
|
2223
|
+
const { projectId, userId, notificationMethod } = data;
|
|
2224
|
+
|
|
2225
|
+
await this.createIncidentOnCallRules(projectId, userId, notificationMethod);
|
|
2226
|
+
await this.createAlertOnCallRules(projectId, userId, notificationMethod);
|
|
2227
|
+
await this.createSingleRule(
|
|
2228
|
+
projectId,
|
|
2229
|
+
userId,
|
|
2230
|
+
notificationMethod,
|
|
2231
|
+
NotificationRuleType.ON_CALL_EXECUTED_ALERT_EPISODE,
|
|
2232
|
+
);
|
|
2233
|
+
await this.createSingleRule(
|
|
2234
|
+
projectId,
|
|
2235
|
+
userId,
|
|
2236
|
+
notificationMethod,
|
|
2237
|
+
NotificationRuleType.ON_CALL_EXECUTED_INCIDENT_EPISODE,
|
|
2238
|
+
);
|
|
2239
|
+
await this.createSingleRule(
|
|
2240
|
+
projectId,
|
|
2241
|
+
userId,
|
|
2242
|
+
notificationMethod,
|
|
2243
|
+
NotificationRuleType.WHEN_USER_GOES_ON_CALL,
|
|
2244
|
+
);
|
|
2245
|
+
await this.createSingleRule(
|
|
2246
|
+
projectId,
|
|
2247
|
+
userId,
|
|
2248
|
+
notificationMethod,
|
|
2249
|
+
NotificationRuleType.WHEN_USER_GOES_OFF_CALL,
|
|
2250
|
+
);
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
private applyNotificationMethod(
|
|
2254
|
+
rule: Model,
|
|
2255
|
+
descriptor: NotificationMethodDescriptor,
|
|
2256
|
+
): void {
|
|
2257
|
+
if (descriptor.userEmailId) {
|
|
2258
|
+
rule.userEmailId = descriptor.userEmailId;
|
|
2259
|
+
}
|
|
2260
|
+
if (descriptor.userSmsId) {
|
|
2261
|
+
rule.userSmsId = descriptor.userSmsId;
|
|
2262
|
+
}
|
|
2263
|
+
if (descriptor.userCallId) {
|
|
2264
|
+
rule.userCallId = descriptor.userCallId;
|
|
2265
|
+
}
|
|
2266
|
+
if (descriptor.userWhatsAppId) {
|
|
2267
|
+
rule.userWhatsAppId = descriptor.userWhatsAppId;
|
|
2268
|
+
}
|
|
2269
|
+
if (descriptor.userPushId) {
|
|
2270
|
+
rule.userPushId = descriptor.userPushId;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
private getNotificationMethodQuery(
|
|
2275
|
+
descriptor: NotificationMethodDescriptor,
|
|
2276
|
+
): Record<string, ObjectID> {
|
|
2277
|
+
const query: Record<string, ObjectID> = {};
|
|
2278
|
+
if (descriptor.userEmailId) {
|
|
2279
|
+
query["userEmailId"] = descriptor.userEmailId;
|
|
2280
|
+
}
|
|
2281
|
+
if (descriptor.userSmsId) {
|
|
2282
|
+
query["userSmsId"] = descriptor.userSmsId;
|
|
2283
|
+
}
|
|
2284
|
+
if (descriptor.userCallId) {
|
|
2285
|
+
query["userCallId"] = descriptor.userCallId;
|
|
2286
|
+
}
|
|
2287
|
+
if (descriptor.userWhatsAppId) {
|
|
2288
|
+
query["userWhatsAppId"] = descriptor.userWhatsAppId;
|
|
2289
|
+
}
|
|
2290
|
+
if (descriptor.userPushId) {
|
|
2291
|
+
query["userPushId"] = descriptor.userPushId;
|
|
2292
|
+
}
|
|
2293
|
+
return query;
|
|
2294
|
+
}
|
|
2216
2295
|
|
|
2296
|
+
private async createIncidentOnCallRules(
|
|
2297
|
+
projectId: ObjectID,
|
|
2298
|
+
userId: ObjectID,
|
|
2299
|
+
notificationMethod: NotificationMethodDescriptor,
|
|
2300
|
+
): Promise<void> {
|
|
2217
2301
|
const incidentSeverities: Array<IncidentSeverity> =
|
|
2218
2302
|
await IncidentSeverityService.findBy({
|
|
2219
2303
|
query: {
|
|
@@ -2229,38 +2313,34 @@ export class Service extends DatabaseService<Model> {
|
|
|
2229
2313
|
},
|
|
2230
2314
|
});
|
|
2231
2315
|
|
|
2232
|
-
// create for incident severities.
|
|
2233
2316
|
for (const incidentSeverity of incidentSeverities) {
|
|
2234
|
-
//check if this rule already exists.
|
|
2235
2317
|
const existingRule: Model | null = await this.findOneBy({
|
|
2236
2318
|
query: {
|
|
2237
2319
|
projectId,
|
|
2238
2320
|
userId,
|
|
2239
|
-
|
|
2321
|
+
...this.getNotificationMethodQuery(notificationMethod),
|
|
2240
2322
|
incidentSeverityId: incidentSeverity.id!,
|
|
2241
2323
|
ruleType: NotificationRuleType.ON_CALL_EXECUTED_INCIDENT,
|
|
2242
|
-
},
|
|
2324
|
+
} as any,
|
|
2243
2325
|
props: {
|
|
2244
2326
|
isRoot: true,
|
|
2245
2327
|
},
|
|
2246
2328
|
});
|
|
2247
2329
|
|
|
2248
2330
|
if (existingRule) {
|
|
2249
|
-
continue;
|
|
2331
|
+
continue;
|
|
2250
2332
|
}
|
|
2251
2333
|
|
|
2252
|
-
const
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
notificationRule.ruleType =
|
|
2260
|
-
NotificationRuleType.ON_CALL_EXECUTED_INCIDENT;
|
|
2334
|
+
const rule: Model = new Model();
|
|
2335
|
+
rule.projectId = projectId;
|
|
2336
|
+
rule.userId = userId;
|
|
2337
|
+
this.applyNotificationMethod(rule, notificationMethod);
|
|
2338
|
+
rule.incidentSeverityId = incidentSeverity.id!;
|
|
2339
|
+
rule.notifyAfterMinutes = 0;
|
|
2340
|
+
rule.ruleType = NotificationRuleType.ON_CALL_EXECUTED_INCIDENT;
|
|
2261
2341
|
|
|
2262
2342
|
await this.create({
|
|
2263
|
-
data:
|
|
2343
|
+
data: rule,
|
|
2264
2344
|
props: {
|
|
2265
2345
|
isRoot: true,
|
|
2266
2346
|
},
|
|
@@ -2268,14 +2348,11 @@ export class Service extends DatabaseService<Model> {
|
|
|
2268
2348
|
}
|
|
2269
2349
|
}
|
|
2270
2350
|
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
}): Promise<void> {
|
|
2277
|
-
const { projectId, userId, userEmail } = data;
|
|
2278
|
-
|
|
2351
|
+
private async createAlertOnCallRules(
|
|
2352
|
+
projectId: ObjectID,
|
|
2353
|
+
userId: ObjectID,
|
|
2354
|
+
notificationMethod: NotificationMethodDescriptor,
|
|
2355
|
+
): Promise<void> {
|
|
2279
2356
|
const alertSeverities: Array<AlertSeverity> =
|
|
2280
2357
|
await AlertSeverityService.findBy({
|
|
2281
2358
|
query: {
|
|
@@ -2291,37 +2368,34 @@ export class Service extends DatabaseService<Model> {
|
|
|
2291
2368
|
},
|
|
2292
2369
|
});
|
|
2293
2370
|
|
|
2294
|
-
// create for Alert severities.
|
|
2295
2371
|
for (const alertSeverity of alertSeverities) {
|
|
2296
|
-
//check if this rule already exists.
|
|
2297
2372
|
const existingRule: Model | null = await this.findOneBy({
|
|
2298
2373
|
query: {
|
|
2299
2374
|
projectId,
|
|
2300
2375
|
userId,
|
|
2301
|
-
|
|
2376
|
+
...this.getNotificationMethodQuery(notificationMethod),
|
|
2302
2377
|
alertSeverityId: alertSeverity.id!,
|
|
2303
2378
|
ruleType: NotificationRuleType.ON_CALL_EXECUTED_ALERT,
|
|
2304
|
-
},
|
|
2379
|
+
} as any,
|
|
2305
2380
|
props: {
|
|
2306
2381
|
isRoot: true,
|
|
2307
2382
|
},
|
|
2308
2383
|
});
|
|
2309
2384
|
|
|
2310
2385
|
if (existingRule) {
|
|
2311
|
-
continue;
|
|
2386
|
+
continue;
|
|
2312
2387
|
}
|
|
2313
2388
|
|
|
2314
|
-
const
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
notificationRule.ruleType = NotificationRuleType.ON_CALL_EXECUTED_ALERT;
|
|
2389
|
+
const rule: Model = new Model();
|
|
2390
|
+
rule.projectId = projectId;
|
|
2391
|
+
rule.userId = userId;
|
|
2392
|
+
this.applyNotificationMethod(rule, notificationMethod);
|
|
2393
|
+
rule.alertSeverityId = alertSeverity.id!;
|
|
2394
|
+
rule.notifyAfterMinutes = 0;
|
|
2395
|
+
rule.ruleType = NotificationRuleType.ON_CALL_EXECUTED_ALERT;
|
|
2322
2396
|
|
|
2323
2397
|
await this.create({
|
|
2324
|
-
data:
|
|
2398
|
+
data: rule,
|
|
2325
2399
|
props: {
|
|
2326
2400
|
isRoot: true,
|
|
2327
2401
|
},
|
|
@@ -2329,6 +2403,43 @@ export class Service extends DatabaseService<Model> {
|
|
|
2329
2403
|
}
|
|
2330
2404
|
}
|
|
2331
2405
|
|
|
2406
|
+
private async createSingleRule(
|
|
2407
|
+
projectId: ObjectID,
|
|
2408
|
+
userId: ObjectID,
|
|
2409
|
+
notificationMethod: NotificationMethodDescriptor,
|
|
2410
|
+
ruleType: NotificationRuleType,
|
|
2411
|
+
): Promise<void> {
|
|
2412
|
+
const existingRule: Model | null = await this.findOneBy({
|
|
2413
|
+
query: {
|
|
2414
|
+
projectId,
|
|
2415
|
+
userId,
|
|
2416
|
+
...this.getNotificationMethodQuery(notificationMethod),
|
|
2417
|
+
ruleType,
|
|
2418
|
+
} as any,
|
|
2419
|
+
props: {
|
|
2420
|
+
isRoot: true,
|
|
2421
|
+
},
|
|
2422
|
+
});
|
|
2423
|
+
|
|
2424
|
+
if (existingRule) {
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
const rule: Model = new Model();
|
|
2429
|
+
rule.projectId = projectId;
|
|
2430
|
+
rule.userId = userId;
|
|
2431
|
+
this.applyNotificationMethod(rule, notificationMethod);
|
|
2432
|
+
rule.notifyAfterMinutes = 0;
|
|
2433
|
+
rule.ruleType = ruleType;
|
|
2434
|
+
|
|
2435
|
+
await this.create({
|
|
2436
|
+
data: rule,
|
|
2437
|
+
props: {
|
|
2438
|
+
isRoot: true,
|
|
2439
|
+
},
|
|
2440
|
+
});
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2332
2443
|
@CaptureSpan()
|
|
2333
2444
|
public async addDefaultNotificationRuleForUser(
|
|
2334
2445
|
projectId: ObjectID,
|
|
@@ -2361,82 +2472,13 @@ export class Service extends DatabaseService<Model> {
|
|
|
2361
2472
|
});
|
|
2362
2473
|
}
|
|
2363
2474
|
|
|
2364
|
-
|
|
2365
|
-
await this.addDefaultIncidentNotificationRuleForUser({
|
|
2475
|
+
await this.addDefaultNotificationRulesForVerifiedMethod({
|
|
2366
2476
|
projectId,
|
|
2367
2477
|
userId,
|
|
2368
|
-
|
|
2369
|
-
});
|
|
2370
|
-
|
|
2371
|
-
// add default alert rules for user, just like the incident
|
|
2372
|
-
|
|
2373
|
-
await this.addDefaultAlertNotificationRulesForUser({
|
|
2374
|
-
projectId,
|
|
2375
|
-
userId,
|
|
2376
|
-
userEmail,
|
|
2377
|
-
});
|
|
2378
|
-
|
|
2379
|
-
//check if this rule already exists.
|
|
2380
|
-
const existingRuleOnCall: Model | null = await this.findOneBy({
|
|
2381
|
-
query: {
|
|
2382
|
-
projectId,
|
|
2383
|
-
userId,
|
|
2384
|
-
userEmailId: userEmail.id!,
|
|
2385
|
-
ruleType: NotificationRuleType.WHEN_USER_GOES_ON_CALL,
|
|
2386
|
-
},
|
|
2387
|
-
props: {
|
|
2388
|
-
isRoot: true,
|
|
2389
|
-
},
|
|
2390
|
-
});
|
|
2391
|
-
|
|
2392
|
-
if (!existingRuleOnCall) {
|
|
2393
|
-
// on and off call.
|
|
2394
|
-
const onCallRule: Model = new Model();
|
|
2395
|
-
|
|
2396
|
-
onCallRule.projectId = projectId;
|
|
2397
|
-
onCallRule.userId = userId;
|
|
2398
|
-
onCallRule.userEmailId = userEmail.id!;
|
|
2399
|
-
onCallRule.notifyAfterMinutes = 0;
|
|
2400
|
-
onCallRule.ruleType = NotificationRuleType.WHEN_USER_GOES_ON_CALL;
|
|
2401
|
-
|
|
2402
|
-
await this.create({
|
|
2403
|
-
data: onCallRule,
|
|
2404
|
-
props: {
|
|
2405
|
-
isRoot: true,
|
|
2406
|
-
},
|
|
2407
|
-
});
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
|
-
//check if this rule already exists.
|
|
2411
|
-
const existingRuleOffCall: Model | null = await this.findOneBy({
|
|
2412
|
-
query: {
|
|
2413
|
-
projectId,
|
|
2414
|
-
userId,
|
|
2478
|
+
notificationMethod: {
|
|
2415
2479
|
userEmailId: userEmail.id!,
|
|
2416
|
-
ruleType: NotificationRuleType.WHEN_USER_GOES_OFF_CALL,
|
|
2417
|
-
},
|
|
2418
|
-
props: {
|
|
2419
|
-
isRoot: true,
|
|
2420
2480
|
},
|
|
2421
2481
|
});
|
|
2422
|
-
|
|
2423
|
-
if (!existingRuleOffCall) {
|
|
2424
|
-
// on and off call.
|
|
2425
|
-
const offCallRule: Model = new Model();
|
|
2426
|
-
|
|
2427
|
-
offCallRule.projectId = projectId;
|
|
2428
|
-
offCallRule.userId = userId;
|
|
2429
|
-
offCallRule.userEmailId = userEmail.id!;
|
|
2430
|
-
offCallRule.notifyAfterMinutes = 0;
|
|
2431
|
-
offCallRule.ruleType = NotificationRuleType.WHEN_USER_GOES_OFF_CALL;
|
|
2432
|
-
|
|
2433
|
-
await this.create({
|
|
2434
|
-
data: offCallRule,
|
|
2435
|
-
props: {
|
|
2436
|
-
isRoot: true,
|
|
2437
|
-
},
|
|
2438
|
-
});
|
|
2439
|
-
}
|
|
2440
2482
|
}
|
|
2441
2483
|
}
|
|
2442
2484
|
export default new Service();
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import DataToProcess from "../DataToProcess";
|
|
2
|
+
import CompareCriteria from "./CompareCriteria";
|
|
3
|
+
import {
|
|
4
|
+
CheckOn,
|
|
5
|
+
CriteriaFilter,
|
|
6
|
+
FilterType,
|
|
7
|
+
} from "../../../../Types/Monitor/CriteriaFilter";
|
|
8
|
+
import DomainMonitorResponse from "../../../../Types/Monitor/DomainMonitor/DomainMonitorResponse";
|
|
9
|
+
import ProbeMonitorResponse from "../../../../Types/Probe/ProbeMonitorResponse";
|
|
10
|
+
import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
11
|
+
|
|
12
|
+
export default class DomainMonitorCriteria {
|
|
13
|
+
@CaptureSpan()
|
|
14
|
+
public static async isMonitorInstanceCriteriaFilterMet(input: {
|
|
15
|
+
dataToProcess: DataToProcess;
|
|
16
|
+
criteriaFilter: CriteriaFilter;
|
|
17
|
+
}): Promise<string | null> {
|
|
18
|
+
let threshold: number | string | undefined | null =
|
|
19
|
+
input.criteriaFilter.value;
|
|
20
|
+
|
|
21
|
+
const dataToProcess: ProbeMonitorResponse =
|
|
22
|
+
input.dataToProcess as ProbeMonitorResponse;
|
|
23
|
+
|
|
24
|
+
const domainResponse: DomainMonitorResponse | undefined =
|
|
25
|
+
dataToProcess.domainResponse;
|
|
26
|
+
|
|
27
|
+
// Check domain expires in days
|
|
28
|
+
if (input.criteriaFilter.checkOn === CheckOn.DomainExpiresDaysIn) {
|
|
29
|
+
threshold = CompareCriteria.convertToNumber(threshold);
|
|
30
|
+
|
|
31
|
+
if (threshold === null || threshold === undefined) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!domainResponse?.expiresDate) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const expiresDate: Date = new Date(domainResponse.expiresDate);
|
|
40
|
+
const now: Date = new Date();
|
|
41
|
+
const diffMs: number = expiresDate.getTime() - now.getTime();
|
|
42
|
+
const diffDays: number = Math.ceil(diffMs / (1000 * 60 * 60 * 24));
|
|
43
|
+
|
|
44
|
+
return CompareCriteria.compareCriteriaNumbers({
|
|
45
|
+
value: diffDays,
|
|
46
|
+
threshold: threshold as number,
|
|
47
|
+
criteriaFilter: input.criteriaFilter,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Check domain registrar
|
|
52
|
+
if (input.criteriaFilter.checkOn === CheckOn.DomainRegistrar) {
|
|
53
|
+
if (!domainResponse?.registrar) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return CompareCriteria.compareCriteriaStrings({
|
|
58
|
+
value: domainResponse.registrar,
|
|
59
|
+
threshold: String(threshold),
|
|
60
|
+
criteriaFilter: input.criteriaFilter,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check domain name server
|
|
65
|
+
if (input.criteriaFilter.checkOn === CheckOn.DomainNameServer) {
|
|
66
|
+
if (
|
|
67
|
+
!domainResponse?.nameServers ||
|
|
68
|
+
domainResponse.nameServers.length === 0
|
|
69
|
+
) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check if any name server matches the criteria
|
|
74
|
+
for (const nameServer of domainResponse.nameServers) {
|
|
75
|
+
const result: string | null = CompareCriteria.compareCriteriaStrings({
|
|
76
|
+
value: nameServer,
|
|
77
|
+
threshold: String(threshold),
|
|
78
|
+
criteriaFilter: input.criteriaFilter,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
if (result) {
|
|
82
|
+
return `Domain name server: ${result}`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Check domain status code
|
|
90
|
+
if (input.criteriaFilter.checkOn === CheckOn.DomainStatusCode) {
|
|
91
|
+
if (
|
|
92
|
+
!domainResponse?.domainStatus ||
|
|
93
|
+
domainResponse.domainStatus.length === 0
|
|
94
|
+
) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check if any status matches the criteria
|
|
99
|
+
for (const status of domainResponse.domainStatus) {
|
|
100
|
+
const result: string | null = CompareCriteria.compareCriteriaStrings({
|
|
101
|
+
value: status,
|
|
102
|
+
threshold: String(threshold),
|
|
103
|
+
criteriaFilter: input.criteriaFilter,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (result) {
|
|
107
|
+
return `Domain status: ${result}`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check if domain is expired
|
|
115
|
+
if (input.criteriaFilter.checkOn === CheckOn.DomainIsExpired) {
|
|
116
|
+
const isTrue: boolean =
|
|
117
|
+
input.criteriaFilter.filterType === FilterType.True;
|
|
118
|
+
const isFalse: boolean =
|
|
119
|
+
input.criteriaFilter.filterType === FilterType.False;
|
|
120
|
+
|
|
121
|
+
if (!domainResponse?.expiresDate) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const expiresDate: Date = new Date(domainResponse.expiresDate);
|
|
126
|
+
const now: Date = new Date();
|
|
127
|
+
const isExpired: boolean = expiresDate.getTime() < now.getTime();
|
|
128
|
+
|
|
129
|
+
if (isExpired && isTrue) {
|
|
130
|
+
return `Domain is expired (expired on ${domainResponse.expiresDate}).`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (!isExpired && isFalse) {
|
|
134
|
+
return `Domain is not expired (expires on ${domainResponse.expiresDate}).`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -13,6 +13,7 @@ import TraceMonitorCriteria from "./Criteria/TraceMonitorCriteria";
|
|
|
13
13
|
import ExceptionMonitorCriteria from "./Criteria/ExceptionMonitorCriteria";
|
|
14
14
|
import SnmpMonitorCriteria from "./Criteria/SnmpMonitorCriteria";
|
|
15
15
|
import DnsMonitorCriteria from "./Criteria/DnsMonitorCriteria";
|
|
16
|
+
import DomainMonitorCriteria from "./Criteria/DomainMonitorCriteria";
|
|
16
17
|
import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
|
|
17
18
|
import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
|
|
18
19
|
import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
|
|
@@ -506,6 +507,18 @@ ${contextBlock}
|
|
|
506
507
|
}
|
|
507
508
|
}
|
|
508
509
|
|
|
510
|
+
if (input.monitor.monitorType === MonitorType.Domain) {
|
|
511
|
+
const domainMonitorResult: string | null =
|
|
512
|
+
await DomainMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
|
|
513
|
+
dataToProcess: input.dataToProcess,
|
|
514
|
+
criteriaFilter: input.criteriaFilter,
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
if (domainMonitorResult) {
|
|
518
|
+
return domainMonitorResult;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
509
522
|
return null;
|
|
510
523
|
}
|
|
511
524
|
|