@oneuptime/common 9.5.7 → 9.5.8
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/UserPush.ts +2 -1
- package/Server/API/UserPushAPI.ts +51 -4
- package/Server/Middleware/UserAuthorization.ts +14 -9
- package/Server/Services/PushNotificationService.ts +129 -27
- package/Server/Services/UserNotificationRuleService.ts +13 -3
- package/Server/Services/UserPushService.ts +2 -1
- package/Server/Utils/PushNotificationUtil.ts +56 -0
- package/Types/PushNotification/PushDeviceType.ts +7 -0
- package/Types/PushNotification/PushNotificationRequest.ts +3 -1
- package/UI/Components/ModelDelete/ModelDelete.tsx +4 -1
- package/UI/Components/ModelDetail/CardModelDetail.tsx +4 -0
- package/UI/Components/ModelDetail/ModelDetail.tsx +4 -1
- package/UI/Components/Page/ModelPage.tsx +4 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +2 -1
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Server/API/UserPushAPI.js +34 -3
- package/build/dist/Server/API/UserPushAPI.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +10 -4
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js +77 -21
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +12 -9
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +2 -1
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Utils/PushNotificationUtil.js +32 -8
- package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
- package/build/dist/Types/PushNotification/PushDeviceType.js +8 -0
- package/build/dist/Types/PushNotification/PushDeviceType.js.map +1 -0
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js +2 -1
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js +2 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
- package/build/dist/UI/Components/Page/ModelPage.js +2 -1
- package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
- package/package.json +2 -1
|
@@ -16,6 +16,7 @@ import TenantColumn from "../../Types/Database/TenantColumn";
|
|
|
16
16
|
import IconProp from "../../Types/Icon/IconProp";
|
|
17
17
|
import ObjectID from "../../Types/ObjectID";
|
|
18
18
|
import Permission from "../../Types/Permission";
|
|
19
|
+
import PushDeviceType from "../../Types/PushNotification/PushDeviceType";
|
|
19
20
|
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
20
21
|
|
|
21
22
|
@TenantColumn("projectId")
|
|
@@ -122,7 +123,7 @@ class UserPush extends BaseModel {
|
|
|
122
123
|
unique: false,
|
|
123
124
|
nullable: false,
|
|
124
125
|
})
|
|
125
|
-
public deviceType?:
|
|
126
|
+
public deviceType?: PushDeviceType = undefined;
|
|
126
127
|
|
|
127
128
|
@ColumnAccessControl({
|
|
128
129
|
create: [Permission.CurrentUser],
|
|
@@ -14,6 +14,7 @@ import Response from "../Utils/Response";
|
|
|
14
14
|
import BaseAPI from "./BaseAPI";
|
|
15
15
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
16
16
|
import ObjectID from "../../Types/ObjectID";
|
|
17
|
+
import PushDeviceType from "../../Types/PushNotification/PushDeviceType";
|
|
17
18
|
import UserPush from "../../Models/DatabaseModels/UserPush";
|
|
18
19
|
import PushNotificationMessage from "../../Types/PushNotification/PushNotificationMessage";
|
|
19
20
|
|
|
@@ -39,11 +40,17 @@ export default class UserPushAPI extends BaseAPI<
|
|
|
39
40
|
);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
const validDeviceTypes: string[] = Object.values(PushDeviceType);
|
|
44
|
+
if (
|
|
45
|
+
!req.body.deviceType ||
|
|
46
|
+
!validDeviceTypes.includes(req.body.deviceType)
|
|
47
|
+
) {
|
|
43
48
|
return Response.sendErrorResponse(
|
|
44
49
|
req,
|
|
45
50
|
res,
|
|
46
|
-
new BadDataException(
|
|
51
|
+
new BadDataException(
|
|
52
|
+
"Device type must be one of: " + validDeviceTypes.join(", "),
|
|
53
|
+
),
|
|
47
54
|
);
|
|
48
55
|
}
|
|
49
56
|
|
|
@@ -86,7 +93,7 @@ export default class UserPushAPI extends BaseAPI<
|
|
|
86
93
|
userPush.deviceToken = req.body.deviceToken;
|
|
87
94
|
userPush.deviceType = req.body.deviceType;
|
|
88
95
|
userPush.deviceName = req.body.deviceName || "Unknown Device";
|
|
89
|
-
userPush.isVerified = true; //
|
|
96
|
+
userPush.isVerified = true; // Web, iOS, and Android devices are verified immediately
|
|
90
97
|
|
|
91
98
|
const savedDevice: UserPush = await this.service.create({
|
|
92
99
|
data: userPush,
|
|
@@ -105,6 +112,46 @@ export default class UserPushAPI extends BaseAPI<
|
|
|
105
112
|
},
|
|
106
113
|
);
|
|
107
114
|
|
|
115
|
+
this.router.post(
|
|
116
|
+
`/user-push/unregister`,
|
|
117
|
+
UserMiddleware.getUserMiddleware,
|
|
118
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
119
|
+
try {
|
|
120
|
+
req = req as OneUptimeRequest;
|
|
121
|
+
|
|
122
|
+
if (!req.body.deviceToken) {
|
|
123
|
+
return Response.sendErrorResponse(
|
|
124
|
+
req,
|
|
125
|
+
res,
|
|
126
|
+
new BadDataException("Device token is required"),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const userId: ObjectID = (req as OneUptimeRequest).userAuthorization!
|
|
131
|
+
.userId!;
|
|
132
|
+
|
|
133
|
+
await this.service.deleteBy({
|
|
134
|
+
query: {
|
|
135
|
+
userId: userId,
|
|
136
|
+
deviceToken: req.body.deviceToken,
|
|
137
|
+
},
|
|
138
|
+
limit: 100,
|
|
139
|
+
skip: 0,
|
|
140
|
+
props: {
|
|
141
|
+
isRoot: true,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
return Response.sendJsonObjectResponse(req, res, {
|
|
146
|
+
success: true,
|
|
147
|
+
message: "Device unregistered successfully",
|
|
148
|
+
});
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return next(error);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
|
|
108
155
|
this.router.post(
|
|
109
156
|
`/user-push/:deviceId/test-notification`,
|
|
110
157
|
UserMiddleware.getUserMiddleware,
|
|
@@ -186,7 +233,7 @@ export default class UserPushAPI extends BaseAPI<
|
|
|
186
233
|
},
|
|
187
234
|
],
|
|
188
235
|
message: testMessage,
|
|
189
|
-
deviceType: device.deviceType
|
|
236
|
+
deviceType: device.deviceType! as PushDeviceType,
|
|
190
237
|
},
|
|
191
238
|
{
|
|
192
239
|
isSensitive: false,
|
|
@@ -64,18 +64,23 @@ export default class UserMiddleware {
|
|
|
64
64
|
public static getAccessTokenFromExpressRequest(
|
|
65
65
|
req: ExpressRequest,
|
|
66
66
|
): string | undefined {
|
|
67
|
-
|
|
67
|
+
// 1. Try cookie (existing web dashboard flow)
|
|
68
|
+
const cookieToken: string | undefined =
|
|
69
|
+
CookieUtil.getCookieFromExpressRequest(req, CookieUtil.getUserTokenKey());
|
|
68
70
|
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
) {
|
|
72
|
-
accessToken = CookieUtil.getCookieFromExpressRequest(
|
|
73
|
-
req,
|
|
74
|
-
CookieUtil.getUserTokenKey(),
|
|
75
|
-
);
|
|
71
|
+
if (cookieToken) {
|
|
72
|
+
return cookieToken;
|
|
76
73
|
}
|
|
77
74
|
|
|
78
|
-
|
|
75
|
+
// 2. Fallback: Check Authorization: Bearer <token> header (mobile app flow)
|
|
76
|
+
const authHeader: string | undefined = req.headers["authorization"] as
|
|
77
|
+
| string
|
|
78
|
+
| undefined;
|
|
79
|
+
if (authHeader && authHeader.startsWith("Bearer ")) {
|
|
80
|
+
return authHeader.substring(7);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return undefined;
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
@CaptureSpan()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import PushNotificationRequest from "../../Types/PushNotification/PushNotificationRequest";
|
|
2
2
|
import PushNotificationMessage from "../../Types/PushNotification/PushNotificationMessage";
|
|
3
|
+
import PushDeviceType from "../../Types/PushNotification/PushDeviceType";
|
|
3
4
|
import ObjectID from "../../Types/ObjectID";
|
|
4
5
|
import logger from "../Utils/Logger";
|
|
5
6
|
import UserPushService from "./UserPushService";
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
VapidSubject,
|
|
12
13
|
} from "../EnvironmentConfig";
|
|
13
14
|
import webpush from "web-push";
|
|
15
|
+
import { Expo, ExpoPushMessage, ExpoPushTicket } from "expo-server-sdk";
|
|
14
16
|
import PushNotificationUtil from "../Utils/PushNotificationUtil";
|
|
15
17
|
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
16
18
|
import UserPush from "../../Models/DatabaseModels/UserPush";
|
|
@@ -41,6 +43,7 @@ export interface PushNotificationOptions {
|
|
|
41
43
|
|
|
42
44
|
export default class PushNotificationService {
|
|
43
45
|
public static isWebPushInitialized = false;
|
|
46
|
+
private static expoClient: Expo = new Expo();
|
|
44
47
|
|
|
45
48
|
public static initializeWebPush(): void {
|
|
46
49
|
if (this.isWebPushInitialized) {
|
|
@@ -76,13 +79,8 @@ export default class PushNotificationService {
|
|
|
76
79
|
throw new Error("No devices provided");
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
if (request.deviceType !== "web") {
|
|
80
|
-
logger.error(`Unsupported device type: ${request.deviceType}`);
|
|
81
|
-
throw new Error("Only web push notifications are supported");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
82
|
logger.info(
|
|
85
|
-
`Sending
|
|
83
|
+
`Sending ${request.deviceType} push notifications to ${request.devices.length} devices`,
|
|
86
84
|
);
|
|
87
85
|
logger.info(`Notification message: ${JSON.stringify(request.message)}`);
|
|
88
86
|
|
|
@@ -98,9 +96,25 @@ export default class PushNotificationService {
|
|
|
98
96
|
const promises: Promise<void>[] = [];
|
|
99
97
|
|
|
100
98
|
for (const device of request.devices) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
99
|
+
if (request.deviceType === PushDeviceType.Web) {
|
|
100
|
+
promises.push(
|
|
101
|
+
this.sendWebPushNotification(device.token, request.message, options),
|
|
102
|
+
);
|
|
103
|
+
} else if (
|
|
104
|
+
request.deviceType === PushDeviceType.iOS ||
|
|
105
|
+
request.deviceType === PushDeviceType.Android
|
|
106
|
+
) {
|
|
107
|
+
promises.push(
|
|
108
|
+
this.sendExpoPushNotification(
|
|
109
|
+
device.token,
|
|
110
|
+
request.message,
|
|
111
|
+
request.deviceType,
|
|
112
|
+
options,
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
} else {
|
|
116
|
+
logger.error(`Unsupported device type: ${request.deviceType}`);
|
|
117
|
+
}
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
const results: Array<any> = await Promise.allSettled(promises);
|
|
@@ -314,6 +328,81 @@ export default class PushNotificationService {
|
|
|
314
328
|
}
|
|
315
329
|
}
|
|
316
330
|
|
|
331
|
+
private static async sendExpoPushNotification(
|
|
332
|
+
expoPushToken: string,
|
|
333
|
+
message: PushNotificationMessage,
|
|
334
|
+
deviceType: PushDeviceType,
|
|
335
|
+
_options: PushNotificationOptions,
|
|
336
|
+
): Promise<void> {
|
|
337
|
+
if (!Expo.isExpoPushToken(expoPushToken)) {
|
|
338
|
+
throw new Error(
|
|
339
|
+
`Invalid Expo push token for ${deviceType} device: ${expoPushToken}`,
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
const dataPayload: { [key: string]: string } = {};
|
|
345
|
+
if (message.data) {
|
|
346
|
+
for (const key of Object.keys(message.data)) {
|
|
347
|
+
dataPayload[key] = String(message.data[key]);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (message.url || message.clickAction) {
|
|
351
|
+
dataPayload["url"] = message.url || message.clickAction || "";
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const channelId: string =
|
|
355
|
+
deviceType === PushDeviceType.Android ? "oncall_high" : "default";
|
|
356
|
+
|
|
357
|
+
const expoPushMessage: ExpoPushMessage = {
|
|
358
|
+
to: expoPushToken,
|
|
359
|
+
title: message.title,
|
|
360
|
+
body: message.body,
|
|
361
|
+
data: dataPayload,
|
|
362
|
+
sound: "default",
|
|
363
|
+
priority: "high",
|
|
364
|
+
channelId: channelId,
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const tickets: ExpoPushTicket[] =
|
|
368
|
+
await this.expoClient.sendPushNotificationsAsync([expoPushMessage]);
|
|
369
|
+
|
|
370
|
+
const ticket: ExpoPushTicket | undefined = tickets[0];
|
|
371
|
+
|
|
372
|
+
if (ticket && ticket.status === "error") {
|
|
373
|
+
const errorTicket: ExpoPushTicket & {
|
|
374
|
+
message?: string;
|
|
375
|
+
details?: { error?: string };
|
|
376
|
+
} = ticket as ExpoPushTicket & {
|
|
377
|
+
message?: string;
|
|
378
|
+
details?: { error?: string };
|
|
379
|
+
};
|
|
380
|
+
logger.error(
|
|
381
|
+
`Expo push notification error for ${deviceType} device: ${errorTicket.message}`,
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
if (errorTicket.details?.error === "DeviceNotRegistered") {
|
|
385
|
+
logger.info(
|
|
386
|
+
"Expo push token is no longer valid (DeviceNotRegistered)",
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
throw new Error(
|
|
391
|
+
`Expo push notification failed: ${errorTicket.message}`,
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
logger.info(
|
|
396
|
+
`Expo push notification sent successfully to ${deviceType} device`,
|
|
397
|
+
);
|
|
398
|
+
} catch (error: any) {
|
|
399
|
+
logger.error(
|
|
400
|
+
`Failed to send Expo push notification to ${deviceType} device: ${error.message}`,
|
|
401
|
+
);
|
|
402
|
+
throw error;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
317
406
|
public static async sendPushNotificationToUser(
|
|
318
407
|
userId: ObjectID,
|
|
319
408
|
projectId: ObjectID,
|
|
@@ -342,33 +431,46 @@ export default class PushNotificationService {
|
|
|
342
431
|
|
|
343
432
|
if (userPushDevices.length === 0) {
|
|
344
433
|
logger.info(
|
|
345
|
-
`No verified
|
|
434
|
+
`No verified push devices found for user ${userId.toString()}`,
|
|
346
435
|
);
|
|
347
436
|
return;
|
|
348
437
|
}
|
|
349
438
|
|
|
350
|
-
//
|
|
351
|
-
const
|
|
439
|
+
// Group devices by type
|
|
440
|
+
const devicesByType: Map<
|
|
441
|
+
string,
|
|
442
|
+
Array<{ token: string; name?: string }>
|
|
443
|
+
> = new Map();
|
|
352
444
|
|
|
353
445
|
for (const device of userPushDevices) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
name: device.deviceName || "Unknown Device",
|
|
358
|
-
});
|
|
446
|
+
const type: string = device.deviceType || PushDeviceType.Web;
|
|
447
|
+
if (!devicesByType.has(type)) {
|
|
448
|
+
devicesByType.set(type, []);
|
|
359
449
|
}
|
|
450
|
+
devicesByType.get(type)!.push({
|
|
451
|
+
token: device.deviceToken!,
|
|
452
|
+
name: device.deviceName || "Unknown Device",
|
|
453
|
+
});
|
|
360
454
|
}
|
|
361
455
|
|
|
362
|
-
// Send notifications to
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
456
|
+
// Send notifications to each device type group
|
|
457
|
+
const sendPromises: Promise<void>[] = [];
|
|
458
|
+
|
|
459
|
+
for (const [deviceType, devices] of devicesByType.entries()) {
|
|
460
|
+
if (devices.length > 0) {
|
|
461
|
+
sendPromises.push(
|
|
462
|
+
this.sendPushNotification(
|
|
463
|
+
{
|
|
464
|
+
devices: devices,
|
|
465
|
+
message: message,
|
|
466
|
+
deviceType: deviceType as PushDeviceType,
|
|
467
|
+
},
|
|
468
|
+
options,
|
|
469
|
+
),
|
|
470
|
+
);
|
|
471
|
+
}
|
|
372
472
|
}
|
|
473
|
+
|
|
474
|
+
await Promise.allSettled(sendPromises);
|
|
373
475
|
}
|
|
374
476
|
}
|
|
@@ -29,6 +29,7 @@ import EmailTemplateType from "../../Types/Email/EmailTemplateType";
|
|
|
29
29
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
30
30
|
import NotificationRuleType from "../../Types/NotificationRule/NotificationRuleType";
|
|
31
31
|
import ObjectID from "../../Types/ObjectID";
|
|
32
|
+
import PushDeviceType from "../../Types/PushNotification/PushDeviceType";
|
|
32
33
|
import Phone from "../../Types/Phone";
|
|
33
34
|
import SMS from "../../Types/SMS/SMS";
|
|
34
35
|
import WhatsAppMessage from "../../Types/WhatsApp/WhatsAppMessage";
|
|
@@ -1101,6 +1102,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1101
1102
|
...(alert.alertNumber !== undefined && {
|
|
1102
1103
|
alertNumber: alert.alertNumber,
|
|
1103
1104
|
}),
|
|
1105
|
+
alertId: alert.id!.toString(),
|
|
1106
|
+
projectId: alert.projectId!.toString(),
|
|
1104
1107
|
});
|
|
1105
1108
|
|
|
1106
1109
|
// send push notification.
|
|
@@ -1115,7 +1118,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1115
1118
|
},
|
|
1116
1119
|
],
|
|
1117
1120
|
message: pushMessage,
|
|
1118
|
-
deviceType: notificationRuleItem.userPush
|
|
1121
|
+
deviceType: notificationRuleItem.userPush
|
|
1122
|
+
.deviceType! as PushDeviceType,
|
|
1119
1123
|
},
|
|
1120
1124
|
{
|
|
1121
1125
|
projectId: options.projectId,
|
|
@@ -1175,6 +1179,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1175
1179
|
...(incident.incidentNumber !== undefined && {
|
|
1176
1180
|
incidentNumber: incident.incidentNumber,
|
|
1177
1181
|
}),
|
|
1182
|
+
incidentId: incident.id!.toString(),
|
|
1183
|
+
projectId: incident.projectId!.toString(),
|
|
1178
1184
|
});
|
|
1179
1185
|
|
|
1180
1186
|
// send push notification.
|
|
@@ -1189,7 +1195,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1189
1195
|
},
|
|
1190
1196
|
],
|
|
1191
1197
|
message: pushMessage,
|
|
1192
|
-
deviceType: notificationRuleItem.userPush
|
|
1198
|
+
deviceType: notificationRuleItem.userPush
|
|
1199
|
+
.deviceType! as PushDeviceType,
|
|
1193
1200
|
},
|
|
1194
1201
|
{
|
|
1195
1202
|
projectId: options.projectId,
|
|
@@ -1251,6 +1258,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1251
1258
|
...(alertEpisode.episodeNumberWithPrefix && {
|
|
1252
1259
|
episodeNumberWithPrefix: alertEpisode.episodeNumberWithPrefix,
|
|
1253
1260
|
}),
|
|
1261
|
+
alertEpisodeId: alertEpisode.id!.toString(),
|
|
1262
|
+
projectId: alertEpisode.projectId!.toString(),
|
|
1254
1263
|
});
|
|
1255
1264
|
|
|
1256
1265
|
PushNotificationService.sendPushNotification(
|
|
@@ -1264,7 +1273,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
1264
1273
|
},
|
|
1265
1274
|
],
|
|
1266
1275
|
message: pushMessage,
|
|
1267
|
-
deviceType: notificationRuleItem.userPush
|
|
1276
|
+
deviceType: notificationRuleItem.userPush
|
|
1277
|
+
.deviceType! as PushDeviceType,
|
|
1268
1278
|
},
|
|
1269
1279
|
{
|
|
1270
1280
|
projectId: options.projectId,
|
|
@@ -4,6 +4,7 @@ import { OnCreate, OnDelete } from "../Types/Database/Hooks";
|
|
|
4
4
|
import DatabaseService from "./DatabaseService";
|
|
5
5
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
6
6
|
import PositiveNumber from "../../Types/PositiveNumber";
|
|
7
|
+
import PushDeviceType from "../../Types/PushNotification/PushDeviceType";
|
|
7
8
|
import UserPush from "../../Models/DatabaseModels/UserPush";
|
|
8
9
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
9
10
|
|
|
@@ -25,7 +26,7 @@ export class Service extends DatabaseService<UserPush> {
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
// Validate device type
|
|
28
|
-
const validDeviceTypes: string[] =
|
|
29
|
+
const validDeviceTypes: string[] = Object.values(PushDeviceType);
|
|
29
30
|
if (!validDeviceTypes.includes(createBy.data.deviceType)) {
|
|
30
31
|
throw new BadDataException(
|
|
31
32
|
"Device type must be one of: " + validDeviceTypes.join(", "),
|
|
@@ -21,6 +21,8 @@ export default class PushNotificationUtil {
|
|
|
21
21
|
incidentViewLink: string;
|
|
22
22
|
incidentNumber?: number;
|
|
23
23
|
incidentNumberWithPrefix?: string;
|
|
24
|
+
incidentId?: string;
|
|
25
|
+
projectId?: string;
|
|
24
26
|
}): PushNotificationMessage {
|
|
25
27
|
const {
|
|
26
28
|
incidentTitle,
|
|
@@ -28,6 +30,8 @@ export default class PushNotificationUtil {
|
|
|
28
30
|
incidentViewLink,
|
|
29
31
|
incidentNumber,
|
|
30
32
|
incidentNumberWithPrefix,
|
|
33
|
+
incidentId,
|
|
34
|
+
projectId,
|
|
31
35
|
} = params;
|
|
32
36
|
const displayNumber: string =
|
|
33
37
|
incidentNumberWithPrefix || (incidentNumber ? `#${incidentNumber}` : "");
|
|
@@ -43,6 +47,9 @@ export default class PushNotificationUtil {
|
|
|
43
47
|
requireInteraction: true,
|
|
44
48
|
data: {
|
|
45
49
|
type: "incident-created",
|
|
50
|
+
entityType: "incident",
|
|
51
|
+
entityId: incidentId,
|
|
52
|
+
projectId: projectId,
|
|
46
53
|
incidentTitle: incidentTitle,
|
|
47
54
|
projectName: projectName,
|
|
48
55
|
url: incidentViewLink,
|
|
@@ -58,6 +65,8 @@ export default class PushNotificationUtil {
|
|
|
58
65
|
incidentViewLink: string;
|
|
59
66
|
incidentNumber?: number;
|
|
60
67
|
incidentNumberWithPrefix?: string;
|
|
68
|
+
incidentId?: string;
|
|
69
|
+
projectId?: string;
|
|
61
70
|
}): PushNotificationMessage {
|
|
62
71
|
const {
|
|
63
72
|
incidentTitle,
|
|
@@ -67,6 +76,8 @@ export default class PushNotificationUtil {
|
|
|
67
76
|
incidentViewLink,
|
|
68
77
|
incidentNumber,
|
|
69
78
|
incidentNumberWithPrefix,
|
|
79
|
+
incidentId,
|
|
80
|
+
projectId,
|
|
70
81
|
} = params;
|
|
71
82
|
const displayNumber: string =
|
|
72
83
|
incidentNumberWithPrefix || (incidentNumber ? `#${incidentNumber}` : "");
|
|
@@ -85,6 +96,9 @@ export default class PushNotificationUtil {
|
|
|
85
96
|
requireInteraction: true,
|
|
86
97
|
data: {
|
|
87
98
|
type: "incident-state-changed",
|
|
99
|
+
entityType: "incident",
|
|
100
|
+
entityId: incidentId,
|
|
101
|
+
projectId: projectId,
|
|
88
102
|
incidentTitle: incidentTitle,
|
|
89
103
|
projectName: projectName,
|
|
90
104
|
newState: newState,
|
|
@@ -101,6 +115,8 @@ export default class PushNotificationUtil {
|
|
|
101
115
|
incidentViewLink: string;
|
|
102
116
|
incidentNumber?: number;
|
|
103
117
|
incidentNumberWithPrefix?: string;
|
|
118
|
+
incidentId?: string;
|
|
119
|
+
projectId?: string;
|
|
104
120
|
}): PushNotificationMessage {
|
|
105
121
|
const {
|
|
106
122
|
incidentTitle,
|
|
@@ -109,6 +125,8 @@ export default class PushNotificationUtil {
|
|
|
109
125
|
incidentViewLink,
|
|
110
126
|
incidentNumber,
|
|
111
127
|
incidentNumberWithPrefix,
|
|
128
|
+
incidentId,
|
|
129
|
+
projectId,
|
|
112
130
|
} = params;
|
|
113
131
|
const noteType: string = isPrivateNote ? "Private" : "Public";
|
|
114
132
|
const displayNumber: string =
|
|
@@ -125,6 +143,9 @@ export default class PushNotificationUtil {
|
|
|
125
143
|
requireInteraction: true,
|
|
126
144
|
data: {
|
|
127
145
|
type: "incident-note-posted",
|
|
146
|
+
entityType: "incident",
|
|
147
|
+
entityId: incidentId,
|
|
148
|
+
projectId: projectId,
|
|
128
149
|
incidentTitle: incidentTitle,
|
|
129
150
|
projectName: projectName,
|
|
130
151
|
isPrivateNote: isPrivateNote,
|
|
@@ -139,6 +160,8 @@ export default class PushNotificationUtil {
|
|
|
139
160
|
alertViewLink: string;
|
|
140
161
|
alertNumber?: number;
|
|
141
162
|
alertNumberWithPrefix?: string;
|
|
163
|
+
alertId?: string;
|
|
164
|
+
projectId?: string;
|
|
142
165
|
}): PushNotificationMessage {
|
|
143
166
|
const {
|
|
144
167
|
alertTitle,
|
|
@@ -146,6 +169,8 @@ export default class PushNotificationUtil {
|
|
|
146
169
|
alertViewLink,
|
|
147
170
|
alertNumber,
|
|
148
171
|
alertNumberWithPrefix,
|
|
172
|
+
alertId,
|
|
173
|
+
projectId,
|
|
149
174
|
} = params;
|
|
150
175
|
const displayNumber: string =
|
|
151
176
|
alertNumberWithPrefix || (alertNumber ? `#${alertNumber}` : "");
|
|
@@ -161,6 +186,9 @@ export default class PushNotificationUtil {
|
|
|
161
186
|
requireInteraction: true,
|
|
162
187
|
data: {
|
|
163
188
|
type: "alert-created",
|
|
189
|
+
entityType: "alert",
|
|
190
|
+
entityId: alertId,
|
|
191
|
+
projectId: projectId,
|
|
164
192
|
alertTitle: alertTitle,
|
|
165
193
|
projectName: projectName,
|
|
166
194
|
url: alertViewLink,
|
|
@@ -174,6 +202,8 @@ export default class PushNotificationUtil {
|
|
|
174
202
|
alertEpisodeViewLink: string;
|
|
175
203
|
episodeNumber?: number;
|
|
176
204
|
episodeNumberWithPrefix?: string;
|
|
205
|
+
alertEpisodeId?: string;
|
|
206
|
+
projectId?: string;
|
|
177
207
|
}): PushNotificationMessage {
|
|
178
208
|
const {
|
|
179
209
|
alertEpisodeTitle,
|
|
@@ -181,6 +211,8 @@ export default class PushNotificationUtil {
|
|
|
181
211
|
alertEpisodeViewLink,
|
|
182
212
|
episodeNumber,
|
|
183
213
|
episodeNumberWithPrefix,
|
|
214
|
+
alertEpisodeId,
|
|
215
|
+
projectId,
|
|
184
216
|
} = params;
|
|
185
217
|
const displayNumber: string =
|
|
186
218
|
episodeNumberWithPrefix || (episodeNumber ? `#${episodeNumber}` : "");
|
|
@@ -196,6 +228,9 @@ export default class PushNotificationUtil {
|
|
|
196
228
|
requireInteraction: true,
|
|
197
229
|
data: {
|
|
198
230
|
type: "alert-episode-created",
|
|
231
|
+
entityType: "alert-episode",
|
|
232
|
+
entityId: alertEpisodeId,
|
|
233
|
+
projectId: projectId,
|
|
199
234
|
alertEpisodeTitle: alertEpisodeTitle,
|
|
200
235
|
projectName: projectName,
|
|
201
236
|
url: alertEpisodeViewLink,
|
|
@@ -209,6 +244,8 @@ export default class PushNotificationUtil {
|
|
|
209
244
|
incidentEpisodeViewLink: string;
|
|
210
245
|
episodeNumber?: number;
|
|
211
246
|
episodeNumberWithPrefix?: string;
|
|
247
|
+
incidentEpisodeId?: string;
|
|
248
|
+
projectId?: string;
|
|
212
249
|
}): PushNotificationMessage {
|
|
213
250
|
const {
|
|
214
251
|
incidentEpisodeTitle,
|
|
@@ -216,6 +253,8 @@ export default class PushNotificationUtil {
|
|
|
216
253
|
incidentEpisodeViewLink,
|
|
217
254
|
episodeNumber,
|
|
218
255
|
episodeNumberWithPrefix,
|
|
256
|
+
incidentEpisodeId,
|
|
257
|
+
projectId,
|
|
219
258
|
} = params;
|
|
220
259
|
const displayNumber: string =
|
|
221
260
|
episodeNumberWithPrefix || (episodeNumber ? `#${episodeNumber}` : "");
|
|
@@ -231,6 +270,9 @@ export default class PushNotificationUtil {
|
|
|
231
270
|
requireInteraction: true,
|
|
232
271
|
data: {
|
|
233
272
|
type: "incident-episode-created",
|
|
273
|
+
entityType: "incident-episode",
|
|
274
|
+
entityId: incidentEpisodeId,
|
|
275
|
+
projectId: projectId,
|
|
234
276
|
incidentEpisodeTitle: incidentEpisodeTitle,
|
|
235
277
|
projectName: projectName,
|
|
236
278
|
url: incidentEpisodeViewLink,
|
|
@@ -244,6 +286,8 @@ export default class PushNotificationUtil {
|
|
|
244
286
|
newStatus: string;
|
|
245
287
|
previousStatus?: string;
|
|
246
288
|
monitorViewLink: string;
|
|
289
|
+
monitorId?: string;
|
|
290
|
+
projectId?: string;
|
|
247
291
|
}): PushNotificationMessage {
|
|
248
292
|
const {
|
|
249
293
|
monitorName,
|
|
@@ -251,6 +295,8 @@ export default class PushNotificationUtil {
|
|
|
251
295
|
newStatus,
|
|
252
296
|
previousStatus,
|
|
253
297
|
monitorViewLink,
|
|
298
|
+
monitorId,
|
|
299
|
+
projectId,
|
|
254
300
|
} = params;
|
|
255
301
|
const statusChangeText: string = previousStatus
|
|
256
302
|
? `Monitor status changed from ${previousStatus} to ${newStatus}`
|
|
@@ -264,6 +310,9 @@ export default class PushNotificationUtil {
|
|
|
264
310
|
requireInteraction: true,
|
|
265
311
|
data: {
|
|
266
312
|
type: "monitor-status-changed",
|
|
313
|
+
entityType: "monitor",
|
|
314
|
+
entityId: monitorId,
|
|
315
|
+
projectId: projectId,
|
|
267
316
|
monitorName: monitorName,
|
|
268
317
|
projectName: projectName,
|
|
269
318
|
newStatus: newStatus,
|
|
@@ -280,6 +329,8 @@ export default class PushNotificationUtil {
|
|
|
280
329
|
viewLink: string;
|
|
281
330
|
scheduledMaintenanceNumber?: number;
|
|
282
331
|
scheduledMaintenanceNumberWithPrefix?: string;
|
|
332
|
+
scheduledMaintenanceId?: string;
|
|
333
|
+
projectId?: string;
|
|
283
334
|
}): PushNotificationMessage {
|
|
284
335
|
const {
|
|
285
336
|
title,
|
|
@@ -288,6 +339,8 @@ export default class PushNotificationUtil {
|
|
|
288
339
|
viewLink,
|
|
289
340
|
scheduledMaintenanceNumber,
|
|
290
341
|
scheduledMaintenanceNumberWithPrefix,
|
|
342
|
+
scheduledMaintenanceId,
|
|
343
|
+
projectId,
|
|
291
344
|
} = params;
|
|
292
345
|
const displayNumber: string =
|
|
293
346
|
scheduledMaintenanceNumberWithPrefix ||
|
|
@@ -304,6 +357,9 @@ export default class PushNotificationUtil {
|
|
|
304
357
|
requireInteraction: false,
|
|
305
358
|
data: {
|
|
306
359
|
type: "scheduled-maintenance",
|
|
360
|
+
entityType: "scheduled-maintenance",
|
|
361
|
+
entityId: scheduledMaintenanceId,
|
|
362
|
+
projectId: projectId,
|
|
307
363
|
title: title,
|
|
308
364
|
projectName: projectName,
|
|
309
365
|
state: state,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import PushDeviceType from "./PushDeviceType";
|
|
2
|
+
|
|
1
3
|
interface PushNotificationRequest {
|
|
2
4
|
devices: Array<{
|
|
3
5
|
token: string;
|
|
@@ -19,7 +21,7 @@ interface PushNotificationRequest {
|
|
|
19
21
|
clickAction?: string;
|
|
20
22
|
url?: string;
|
|
21
23
|
};
|
|
22
|
-
deviceType:
|
|
24
|
+
deviceType: PushDeviceType;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export default PushNotificationRequest;
|