@oneuptime/common 9.5.6 → 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/AlertEpisode.ts +25 -0
- package/Models/DatabaseModels/AlertGroupingRule.ts +2 -2
- package/Models/DatabaseModels/IncidentEpisode.ts +25 -0
- package/Models/DatabaseModels/IncidentGroupingRule.ts +2 -2
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/OpenSourceDeployment.ts +140 -0
- package/Models/DatabaseModels/UserPush.ts +2 -1
- package/Server/API/OpenSourceDeploymentAPI.ts +73 -0
- package/Server/API/UserPushAPI.ts +51 -4
- package/Server/EnvironmentConfig.ts +3 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770237245070-MigrationName.ts +7 -7
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770668054908-MigrationName.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770728946893-MigrationName.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1770732721195-MigrationName.ts +27 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/UserAuthorization.ts +14 -9
- package/Server/Services/AlertEpisodeMemberService.ts +10 -20
- package/Server/Services/AlertEpisodeService.ts +3 -2
- package/Server/Services/AlertGroupingEngineService.ts +1 -0
- package/Server/Services/IncidentEpisodeMemberService.ts +12 -20
- package/Server/Services/IncidentEpisodeService.ts +65 -3
- package/Server/Services/IncidentGroupingEngineService.ts +1 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/OpenSourceDeploymentService.ts +10 -0
- 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/Forms/BasicForm.tsx +8 -1
- package/UI/Components/Forms/Types/Field.ts +3 -0
- 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/AlertEpisode.js +26 -0
- package/build/dist/Models/DatabaseModels/AlertEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertGroupingRule.js +2 -2
- package/build/dist/Models/DatabaseModels/AlertGroupingRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +26 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js +2 -2
- package/build/dist/Models/DatabaseModels/IncidentGroupingRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OpenSourceDeployment.js +167 -0
- package/build/dist/Models/DatabaseModels/OpenSourceDeployment.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserPush.js +2 -1
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Server/API/OpenSourceDeploymentAPI.js +55 -0
- package/build/dist/Server/API/OpenSourceDeploymentAPI.js.map +1 -0
- package/build/dist/Server/API/UserPushAPI.js +34 -3
- package/build/dist/Server/API/UserPushAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +1 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245070-MigrationName.js +7 -7
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770237245070-MigrationName.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770668054908-MigrationName.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770668054908-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770728946893-MigrationName.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770728946893-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770732721195-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1770732721195-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.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/AlertEpisodeMemberService.js +7 -13
- package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeService.js +3 -2
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/AlertGroupingEngineService.js +1 -0
- package/build/dist/Server/Services/AlertGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +7 -13
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +43 -3
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +1 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/OpenSourceDeploymentService.js +9 -0
- package/build/dist/Server/Services/OpenSourceDeploymentService.js.map +1 -0
- 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/Forms/BasicForm.js +3 -1
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- 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
|
@@ -94,26 +94,16 @@ export class Service extends DatabaseService<Model> {
|
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
// Update episode's alertCount and lastAlertAddedAt
|
|
97
|
-
|
|
98
|
-
.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
logger.error(
|
|
108
|
-
`Error updating episode counts in AlertEpisodeMemberService.onCreateSuccess: ${error}`,
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
.catch((error: Error) => {
|
|
113
|
-
logger.error(
|
|
114
|
-
`Critical error in AlertEpisodeMemberService.onCreateSuccess: ${error}`,
|
|
115
|
-
);
|
|
116
|
-
});
|
|
97
|
+
try {
|
|
98
|
+
await AlertEpisodeService.updateAlertCount(createdItem.alertEpisodeId!);
|
|
99
|
+
await AlertEpisodeService.updateLastAlertAddedAt(
|
|
100
|
+
createdItem.alertEpisodeId!,
|
|
101
|
+
);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
logger.error(
|
|
104
|
+
`Error updating episode counts in AlertEpisodeMemberService.onCreateSuccess: ${error}`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
117
107
|
|
|
118
108
|
// Get alert details for feed
|
|
119
109
|
const alert: Alert | null = await AlertService.findOneById({
|
|
@@ -815,11 +815,12 @@ export class Service extends DatabaseService<Model> {
|
|
|
815
815
|
},
|
|
816
816
|
});
|
|
817
817
|
|
|
818
|
-
// Clear resolved timestamp
|
|
818
|
+
// Clear resolved timestamp and allAlertsResolvedAt
|
|
819
819
|
await this.updateOneById({
|
|
820
820
|
id: episodeId,
|
|
821
821
|
data: {
|
|
822
|
-
resolvedAt:
|
|
822
|
+
resolvedAt: null,
|
|
823
|
+
allAlertsResolvedAt: null,
|
|
823
824
|
},
|
|
824
825
|
props: {
|
|
825
826
|
isRoot: true,
|
|
@@ -688,6 +688,7 @@ class AlertGroupingEngineServiceClass {
|
|
|
688
688
|
newEpisode.alertGroupingRuleId = rule.id!;
|
|
689
689
|
newEpisode.groupingKey = groupingKey;
|
|
690
690
|
newEpisode.isManuallyCreated = false;
|
|
691
|
+
newEpisode.lastAlertAddedAt = OneUptimeDate.getCurrentDate();
|
|
691
692
|
|
|
692
693
|
// Set severity from alert
|
|
693
694
|
if (alert.alertSeverityId) {
|
|
@@ -96,26 +96,18 @@ export class Service extends DatabaseService<Model> {
|
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
// Update episode's incidentCount and lastIncidentAddedAt
|
|
99
|
-
|
|
100
|
-
.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
.catch((error: Error) => {
|
|
115
|
-
logger.error(
|
|
116
|
-
`Critical error in IncidentEpisodeMemberService.onCreateSuccess: ${error}`,
|
|
117
|
-
);
|
|
118
|
-
});
|
|
99
|
+
try {
|
|
100
|
+
await IncidentEpisodeService.updateIncidentCount(
|
|
101
|
+
createdItem.incidentEpisodeId!,
|
|
102
|
+
);
|
|
103
|
+
await IncidentEpisodeService.updateLastIncidentAddedAt(
|
|
104
|
+
createdItem.incidentEpisodeId!,
|
|
105
|
+
);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logger.error(
|
|
108
|
+
`Error updating episode counts in IncidentEpisodeMemberService.onCreateSuccess: ${error}`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
119
111
|
|
|
120
112
|
// Get incident details for feed
|
|
121
113
|
const incident: Incident | null = await IncidentService.findOneById({
|
|
@@ -609,6 +609,18 @@ export class Service extends DatabaseService<Model> {
|
|
|
609
609
|
},
|
|
610
610
|
cascadeToIncidents: cascadeToIncidents,
|
|
611
611
|
});
|
|
612
|
+
|
|
613
|
+
// Clear resolved timestamp and allIncidentsResolvedAt when episode is reopened
|
|
614
|
+
await this.updateOneById({
|
|
615
|
+
id: episodeId,
|
|
616
|
+
data: {
|
|
617
|
+
resolvedAt: null,
|
|
618
|
+
allIncidentsResolvedAt: null,
|
|
619
|
+
},
|
|
620
|
+
props: {
|
|
621
|
+
isRoot: true,
|
|
622
|
+
},
|
|
623
|
+
});
|
|
612
624
|
}
|
|
613
625
|
|
|
614
626
|
@CaptureSpan()
|
|
@@ -712,15 +724,65 @@ export class Service extends DatabaseService<Model> {
|
|
|
712
724
|
},
|
|
713
725
|
});
|
|
714
726
|
|
|
715
|
-
|
|
727
|
+
const incidentCount: number = count.toNumber();
|
|
728
|
+
|
|
729
|
+
// Get the episode to check for templates
|
|
730
|
+
const episode: Model | null = await this.findOneById({
|
|
716
731
|
id: episodeId,
|
|
717
|
-
|
|
718
|
-
|
|
732
|
+
select: {
|
|
733
|
+
titleTemplate: true,
|
|
734
|
+
descriptionTemplate: true,
|
|
735
|
+
title: true,
|
|
736
|
+
description: true,
|
|
719
737
|
},
|
|
720
738
|
props: {
|
|
721
739
|
isRoot: true,
|
|
722
740
|
},
|
|
723
741
|
});
|
|
742
|
+
|
|
743
|
+
const updateData: {
|
|
744
|
+
incidentCount: number;
|
|
745
|
+
title?: string;
|
|
746
|
+
description?: string;
|
|
747
|
+
} = {
|
|
748
|
+
incidentCount: incidentCount,
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
// Update title with dynamic variables if template exists
|
|
752
|
+
if (episode?.titleTemplate) {
|
|
753
|
+
updateData.title = this.renderTemplateWithDynamicValues(
|
|
754
|
+
episode.titleTemplate,
|
|
755
|
+
incidentCount,
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Update description with dynamic variables if template exists
|
|
760
|
+
if (episode?.descriptionTemplate) {
|
|
761
|
+
updateData.description = this.renderTemplateWithDynamicValues(
|
|
762
|
+
episode.descriptionTemplate,
|
|
763
|
+
incidentCount,
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
await this.updateOneById({
|
|
768
|
+
id: episodeId,
|
|
769
|
+
data: updateData,
|
|
770
|
+
props: {
|
|
771
|
+
isRoot: true,
|
|
772
|
+
},
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
private renderTemplateWithDynamicValues(
|
|
777
|
+
template: string,
|
|
778
|
+
incidentCount: number,
|
|
779
|
+
): string {
|
|
780
|
+
let result: string = template;
|
|
781
|
+
|
|
782
|
+
// Replace dynamic variables
|
|
783
|
+
result = result.replace(/\{\{incidentCount\}\}/g, incidentCount.toString());
|
|
784
|
+
|
|
785
|
+
return result;
|
|
724
786
|
}
|
|
725
787
|
|
|
726
788
|
@CaptureSpan()
|
|
@@ -757,6 +757,7 @@ class IncidentGroupingEngineServiceClass {
|
|
|
757
757
|
newEpisode.incidentGroupingRuleId = rule.id!;
|
|
758
758
|
newEpisode.groupingKey = groupingKey;
|
|
759
759
|
newEpisode.isManuallyCreated = false;
|
|
760
|
+
newEpisode.lastIncidentAddedAt = OneUptimeDate.getCurrentDate();
|
|
760
761
|
|
|
761
762
|
// Set severity from incident
|
|
762
763
|
if (incident.incidentSeverityId) {
|
package/Server/Services/Index.ts
CHANGED
|
@@ -79,6 +79,7 @@ import ProjectSmtpConfigService from "./ProjectSmtpConfigService";
|
|
|
79
79
|
import ProjectSsoService from "./ProjectSsoService";
|
|
80
80
|
import PromoCodeService from "./PromoCodeService";
|
|
81
81
|
import EnterpriseLicenseService from "./EnterpriseLicenseService";
|
|
82
|
+
import OpenSourceDeploymentService from "./OpenSourceDeploymentService";
|
|
82
83
|
import ResellerPlanService from "./ResellerPlanService";
|
|
83
84
|
import ResellerService from "./ResellerService";
|
|
84
85
|
import ScheduledMaintenanceCustomFieldService from "./ScheduledMaintenanceCustomFieldService";
|
|
@@ -210,6 +211,7 @@ const services: Array<BaseService> = [
|
|
|
210
211
|
AcmeCertificateService,
|
|
211
212
|
PromoCodeService,
|
|
212
213
|
EnterpriseLicenseService,
|
|
214
|
+
OpenSourceDeploymentService,
|
|
213
215
|
|
|
214
216
|
ResellerService,
|
|
215
217
|
ResellerPlanService,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import DatabaseService from "./DatabaseService";
|
|
2
|
+
import OpenSourceDeployment from "../../Models/DatabaseModels/OpenSourceDeployment";
|
|
3
|
+
|
|
4
|
+
export class Service extends DatabaseService<OpenSourceDeployment> {
|
|
5
|
+
public constructor() {
|
|
6
|
+
super(OpenSourceDeployment);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default new Service();
|
|
@@ -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(", "),
|