@oneuptime/common 10.0.66 → 10.0.68

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.
Files changed (132) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +370 -0
  2. package/Models/DatabaseModels/Alert.ts +2 -0
  3. package/Models/DatabaseModels/ApiKey.ts +2 -0
  4. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +3 -0
  5. package/Models/DatabaseModels/GlobalConfig.ts +56 -0
  6. package/Models/DatabaseModels/Incident.ts +2 -0
  7. package/Models/DatabaseModels/Index.ts +4 -0
  8. package/Models/DatabaseModels/Label.ts +2 -0
  9. package/Models/DatabaseModels/Monitor.ts +2 -0
  10. package/Models/DatabaseModels/OnCallDutyPolicy.ts +2 -0
  11. package/Models/DatabaseModels/Project.ts +110 -0
  12. package/Models/DatabaseModels/ScheduledMaintenance.ts +2 -0
  13. package/Models/DatabaseModels/StatusPage.ts +2 -0
  14. package/Models/DatabaseModels/Team.ts +2 -0
  15. package/Models/DatabaseModels/TelegramLog.ts +1025 -0
  16. package/Models/DatabaseModels/UserNotificationRule.ts +49 -0
  17. package/Models/DatabaseModels/UserNotificationSetting.ts +17 -0
  18. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +48 -0
  19. package/Models/DatabaseModels/UserTelegram.ts +312 -0
  20. package/Server/API/UserTelegramAPI.ts +167 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1776761171349-MigrationName.ts +325 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1776801030808-MigrationName.ts +35 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  24. package/Server/Services/AuditLogService.ts +574 -0
  25. package/Server/Services/DatabaseService.ts +71 -0
  26. package/Server/Services/Index.ts +8 -0
  27. package/Server/Services/TelegramLogService.ts +15 -0
  28. package/Server/Services/TelegramService.ts +139 -0
  29. package/Server/Services/UserNotificationRuleService.ts +350 -1
  30. package/Server/Services/UserNotificationSettingService.ts +114 -0
  31. package/Server/Services/UserTelegramService.ts +140 -0
  32. package/Server/Utils/Monitor/MonitorResource.ts +29 -15
  33. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +29 -16
  34. package/Tests/Types/Date.test.ts +158 -0
  35. package/Types/AnalyticsDatabase/AnalyticsTableName.ts +1 -0
  36. package/Types/AuditLog/AuditLogAction.ts +7 -0
  37. package/Types/BaseDatabase/EnableAuditLogOn.ts +5 -0
  38. package/Types/Database/EnableAuditLog.ts +18 -0
  39. package/Types/Date.ts +12 -3
  40. package/Types/Icon/IconProp.ts +1 -0
  41. package/Types/Permission.ts +24 -0
  42. package/Types/Telegram/TelegramMessage.ts +9 -0
  43. package/Types/TelegramStatus.ts +14 -0
  44. package/UI/Components/Icon/Icon.tsx +15 -0
  45. package/build/dist/Models/AnalyticsModels/AuditLog.js +337 -0
  46. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -0
  47. package/build/dist/Models/DatabaseModels/Alert.js +2 -0
  48. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  49. package/build/dist/Models/DatabaseModels/ApiKey.js +2 -0
  50. package/build/dist/Models/DatabaseModels/ApiKey.js.map +1 -1
  51. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  52. package/build/dist/Models/DatabaseModels/GlobalConfig.js +59 -0
  53. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  54. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  55. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  56. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  57. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  58. package/build/dist/Models/DatabaseModels/Label.js +2 -0
  59. package/build/dist/Models/DatabaseModels/Label.js.map +1 -1
  60. package/build/dist/Models/DatabaseModels/Monitor.js +2 -0
  61. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  62. package/build/dist/Models/DatabaseModels/OnCallDutyPolicy.js +2 -0
  63. package/build/dist/Models/DatabaseModels/OnCallDutyPolicy.js.map +1 -1
  64. package/build/dist/Models/DatabaseModels/Project.js +114 -0
  65. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  66. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +2 -0
  67. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  68. package/build/dist/Models/DatabaseModels/StatusPage.js +2 -0
  69. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  70. package/build/dist/Models/DatabaseModels/Team.js +2 -0
  71. package/build/dist/Models/DatabaseModels/Team.js.map +1 -1
  72. package/build/dist/Models/DatabaseModels/TelegramLog.js +1056 -0
  73. package/build/dist/Models/DatabaseModels/TelegramLog.js.map +1 -0
  74. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +49 -0
  75. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  76. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +19 -0
  77. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
  78. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +48 -0
  79. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  80. package/build/dist/Models/DatabaseModels/UserTelegram.js +331 -0
  81. package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -0
  82. package/build/dist/Server/API/UserTelegramAPI.js +99 -0
  83. package/build/dist/Server/API/UserTelegramAPI.js.map +1 -0
  84. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776761171349-MigrationName.js +116 -0
  85. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776761171349-MigrationName.js.map +1 -0
  86. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776801030808-MigrationName.js +18 -0
  87. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776801030808-MigrationName.js.map +1 -0
  88. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  89. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  90. package/build/dist/Server/Services/AuditLogService.js +402 -0
  91. package/build/dist/Server/Services/AuditLogService.js.map +1 -0
  92. package/build/dist/Server/Services/DatabaseService.js +68 -8
  93. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  94. package/build/dist/Server/Services/Index.js +8 -0
  95. package/build/dist/Server/Services/Index.js.map +1 -1
  96. package/build/dist/Server/Services/TelegramLogService.js +13 -0
  97. package/build/dist/Server/Services/TelegramLogService.js.map +1 -0
  98. package/build/dist/Server/Services/TelegramService.js +100 -0
  99. package/build/dist/Server/Services/TelegramService.js.map +1 -0
  100. package/build/dist/Server/Services/UserNotificationRuleService.js +272 -21
  101. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  102. package/build/dist/Server/Services/UserNotificationSettingService.js +94 -0
  103. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  104. package/build/dist/Server/Services/UserTelegramService.js +133 -0
  105. package/build/dist/Server/Services/UserTelegramService.js.map +1 -0
  106. package/build/dist/Server/Utils/Monitor/MonitorResource.js +25 -12
  107. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  108. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +24 -12
  109. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  110. package/build/dist/Tests/Types/Date.test.js +96 -0
  111. package/build/dist/Tests/Types/Date.test.js.map +1 -1
  112. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +1 -0
  113. package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -1
  114. package/build/dist/Types/AuditLog/AuditLogAction.js +8 -0
  115. package/build/dist/Types/AuditLog/AuditLogAction.js.map +1 -0
  116. package/build/dist/Types/BaseDatabase/EnableAuditLogOn.js +2 -0
  117. package/build/dist/Types/BaseDatabase/EnableAuditLogOn.js.map +1 -0
  118. package/build/dist/Types/Database/EnableAuditLog.js +15 -0
  119. package/build/dist/Types/Database/EnableAuditLog.js.map +1 -0
  120. package/build/dist/Types/Date.js +9 -3
  121. package/build/dist/Types/Date.js.map +1 -1
  122. package/build/dist/Types/Icon/IconProp.js +1 -0
  123. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  124. package/build/dist/Types/Permission.js +21 -0
  125. package/build/dist/Types/Permission.js.map +1 -1
  126. package/build/dist/Types/Telegram/TelegramMessage.js +2 -0
  127. package/build/dist/Types/Telegram/TelegramMessage.js.map +1 -0
  128. package/build/dist/Types/TelegramStatus.js +15 -0
  129. package/build/dist/Types/TelegramStatus.js.map +1 -0
  130. package/build/dist/UI/Components/Icon/Icon.js +5 -0
  131. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  132. package/package.json +2 -2
@@ -6,10 +6,12 @@ import DatabaseService from "./DatabaseService";
6
6
  import MailService from "./MailService";
7
7
  import SmsService from "./SmsService";
8
8
  import TeamMemberService from "./TeamMemberService";
9
+ import TelegramService from "./TelegramService";
9
10
  import UserCallService from "./UserCallService";
10
11
  import UserEmailService from "./UserEmailService";
11
12
  import UserSmsService from "./UserSmsService";
12
13
  import PushNotificationService from "./PushNotificationService";
14
+ import UserTelegramService from "./UserTelegramService";
13
15
  import UserWhatsAppService from "./UserWhatsAppService";
14
16
  import WhatsAppService from "./WhatsAppService";
15
17
  import { CallRequestMessage } from "../../Types/Call/CallRequest";
@@ -21,6 +23,9 @@ import ObjectID from "../../Types/ObjectID";
21
23
  import PositiveNumber from "../../Types/PositiveNumber";
22
24
  import { SMSMessage } from "../../Types/SMS/SMS";
23
25
  import PushNotificationMessage from "../../Types/PushNotification/PushNotificationMessage";
26
+ import TelegramMessage, {
27
+ TelegramMessagePayload,
28
+ } from "../../Types/Telegram/TelegramMessage";
24
29
  import WhatsAppMessage, {
25
30
  WhatsAppMessagePayload,
26
31
  } from "../../Types/WhatsApp/WhatsAppMessage";
@@ -28,6 +33,7 @@ import UserCall from "../../Models/DatabaseModels/UserCall";
28
33
  import UserEmail from "../../Models/DatabaseModels/UserEmail";
29
34
  import UserNotificationSetting from "../../Models/DatabaseModels/UserNotificationSetting";
30
35
  import UserSMS from "../../Models/DatabaseModels/UserSMS";
36
+ import UserTelegram from "../../Models/DatabaseModels/UserTelegram";
31
37
  import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
32
38
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
33
39
  import { appendRecipientToWhatsAppMessage } from "../Utils/WhatsAppTemplateUtil";
@@ -47,6 +53,7 @@ export class Service extends DatabaseService<UserNotificationSetting> {
47
53
  callRequestMessage: CallRequestMessage;
48
54
  pushNotificationMessage: PushNotificationMessage;
49
55
  whatsAppMessage: WhatsAppMessagePayload;
56
+ telegramMessage?: TelegramMessagePayload | undefined;
50
57
  incidentId?: ObjectID | undefined;
51
58
  alertId?: ObjectID | undefined;
52
59
  alertEpisodeId?: ObjectID | undefined;
@@ -79,6 +86,7 @@ export class Service extends DatabaseService<UserNotificationSetting> {
79
86
  alertByEmail: true,
80
87
  alertBySMS: true,
81
88
  alertByWhatsApp: true,
89
+ alertByTelegram: true,
82
90
  alertByCall: true,
83
91
  alertByPush: true,
84
92
  },
@@ -239,6 +247,112 @@ export class Service extends DatabaseService<UserNotificationSetting> {
239
247
  }
240
248
  }
241
249
 
250
+ if (notificationSettings.alertByTelegram) {
251
+ const userTelegrams: Array<UserTelegram> =
252
+ await UserTelegramService.findBy({
253
+ query: {
254
+ userId: data.userId,
255
+ projectId: data.projectId,
256
+ isVerified: true,
257
+ },
258
+ select: {
259
+ telegramChatId: true,
260
+ },
261
+ limit: LIMIT_PER_PROJECT,
262
+ skip: 0,
263
+ props: {
264
+ isRoot: true,
265
+ },
266
+ });
267
+
268
+ /*
269
+ * When the caller did not provide a Telegram-specific message we build a
270
+ * nicely-formatted HTML body from the email subject + SMS body + optional
271
+ * URL from the email envelope, with a 🔔 prefix. If they did provide one,
272
+ * we respect their body/parseMode verbatim.
273
+ */
274
+ const callerProvidedTelegramBody: boolean = Boolean(
275
+ data.telegramMessage?.body,
276
+ );
277
+
278
+ const escapeHtml: (value: string) => string = (
279
+ value: string,
280
+ ): string => {
281
+ return value
282
+ .replace(/&/g, "&amp;")
283
+ .replace(/</g, "&lt;")
284
+ .replace(/>/g, "&gt;");
285
+ };
286
+
287
+ let telegramBody: string = "";
288
+ let telegramParseMode: TelegramMessage["parseMode"] | undefined =
289
+ undefined;
290
+
291
+ if (callerProvidedTelegramBody) {
292
+ telegramBody = data.telegramMessage!.body;
293
+ telegramParseMode = data.telegramMessage!.parseMode;
294
+ } else {
295
+ const subject: string = data.emailEnvelope.subject || "";
296
+ const smsBody: string = data.smsMessage.message || "";
297
+
298
+ if (subject || smsBody) {
299
+ const lines: Array<string> = [];
300
+ if (subject) {
301
+ lines.push(`🔔 <b>${escapeHtml(subject)}</b>`);
302
+ } else {
303
+ lines.push("🔔 <b>OneUptime notification</b>");
304
+ }
305
+ if (smsBody) {
306
+ lines.push("");
307
+ lines.push(escapeHtml(smsBody));
308
+ }
309
+ telegramBody = lines.join("\n");
310
+ telegramParseMode = "HTML";
311
+ }
312
+ }
313
+
314
+ if (!telegramBody) {
315
+ logger.warn(
316
+ "Skipping Telegram notification because message body is empty.",
317
+ );
318
+ } else {
319
+ for (const userTelegram of userTelegrams) {
320
+ if (!userTelegram.telegramChatId) {
321
+ continue;
322
+ }
323
+
324
+ const telegramMessage: TelegramMessage = {
325
+ to: userTelegram.telegramChatId,
326
+ body: telegramBody,
327
+ parseMode: telegramParseMode,
328
+ disableWebPagePreview:
329
+ data.telegramMessage?.disableWebPagePreview ?? true,
330
+ };
331
+
332
+ TelegramService.sendTelegramMessage(telegramMessage, {
333
+ projectId: data.projectId,
334
+ incidentId: data.incidentId,
335
+ alertId: data.alertId,
336
+ alertEpisodeId: data.alertEpisodeId,
337
+ incidentEpisodeId: data.incidentEpisodeId,
338
+ monitorId: data.monitorId,
339
+ scheduledMaintenanceId: data.scheduledMaintenanceId,
340
+ statusPageId: data.statusPageId,
341
+ statusPageAnnouncementId: data.statusPageAnnouncementId,
342
+ userId: data.userId,
343
+ teamId: data.teamId,
344
+ onCallPolicyId: data.onCallPolicyId,
345
+ onCallPolicyEscalationRuleId: data.onCallPolicyEscalationRuleId,
346
+ onCallDutyPolicyExecutionLogTimelineId:
347
+ data.onCallDutyPolicyExecutionLogTimelineId,
348
+ onCallScheduleId: data.onCallScheduleId,
349
+ }).catch((err: Error) => {
350
+ logger.error(err);
351
+ });
352
+ }
353
+ }
354
+ }
355
+
242
356
  if (notificationSettings.alertByCall) {
243
357
  const userCalls: Array<UserCall> = await UserCallService.findBy({
244
358
  query: {
@@ -0,0 +1,140 @@
1
+ import { IsBillingEnabled } from "../EnvironmentConfig";
2
+ import CreateBy from "../Types/Database/CreateBy";
3
+ import DeleteBy from "../Types/Database/DeleteBy";
4
+ import { OnCreate, OnDelete } from "../Types/Database/Hooks";
5
+ import DatabaseService from "./DatabaseService";
6
+ import ProjectService from "./ProjectService";
7
+ import UserNotificationRuleService from "./UserNotificationRuleService";
8
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
9
+ import BadDataException from "../../Types/Exception/BadDataException";
10
+ import ObjectID from "../../Types/ObjectID";
11
+ import Text from "../../Types/Text";
12
+ import Project from "../../Models/DatabaseModels/Project";
13
+ import Model from "../../Models/DatabaseModels/UserTelegram";
14
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
15
+
16
+ export class Service extends DatabaseService<Model> {
17
+ public constructor() {
18
+ super(Model);
19
+ }
20
+
21
+ @CaptureSpan()
22
+ protected override async onBeforeDelete(
23
+ deleteBy: DeleteBy<Model>,
24
+ ): Promise<OnDelete<Model>> {
25
+ const itemsToDelete: Array<Model> = await this.findBy({
26
+ query: deleteBy.query,
27
+ select: {
28
+ _id: true,
29
+ projectId: true,
30
+ },
31
+ skip: 0,
32
+ limit: LIMIT_MAX,
33
+ props: {
34
+ isRoot: true,
35
+ },
36
+ });
37
+
38
+ for (const item of itemsToDelete) {
39
+ await UserNotificationRuleService.deleteBy({
40
+ query: {
41
+ userTelegramId: item.id!,
42
+ projectId: item.projectId!,
43
+ },
44
+ limit: LIMIT_MAX,
45
+ skip: 0,
46
+ props: {
47
+ isRoot: true,
48
+ },
49
+ });
50
+ }
51
+
52
+ return {
53
+ deleteBy,
54
+ carryForward: null,
55
+ };
56
+ }
57
+
58
+ @CaptureSpan()
59
+ protected override async onBeforeCreate(
60
+ createBy: CreateBy<Model>,
61
+ ): Promise<OnCreate<Model>> {
62
+ if (!createBy.props.isRoot && createBy.data.isVerified) {
63
+ throw new BadDataException("isVerified cannot be set to true");
64
+ }
65
+
66
+ if (!createBy.props.isRoot && createBy.data.telegramChatId) {
67
+ throw new BadDataException("telegramChatId cannot be set directly");
68
+ }
69
+
70
+ const project: Project | null = await ProjectService.findOneById({
71
+ id: createBy.data.projectId!,
72
+ props: {
73
+ isRoot: true,
74
+ },
75
+ select: {
76
+ enableTelegramNotifications: true,
77
+ smsOrCallCurrentBalanceInUSDCents: true,
78
+ },
79
+ });
80
+
81
+ if (!project) {
82
+ throw new BadDataException("Project not found");
83
+ }
84
+
85
+ if (!project.enableTelegramNotifications) {
86
+ throw new BadDataException(
87
+ "Telegram notifications are disabled for this project. Please enable them in Project Settings > Notification Settings.",
88
+ );
89
+ }
90
+
91
+ if (
92
+ (project.smsOrCallCurrentBalanceInUSDCents as number) <= 100 &&
93
+ IsBillingEnabled
94
+ ) {
95
+ throw new BadDataException(
96
+ "Your notification balance is low. Please recharge your balance in Project Settings > Notification Settings.",
97
+ );
98
+ }
99
+
100
+ return {
101
+ createBy,
102
+ carryForward: null,
103
+ };
104
+ }
105
+
106
+ @CaptureSpan()
107
+ public async regenerateVerificationCode(itemId: ObjectID): Promise<void> {
108
+ const item: Model | null = await this.findOneById({
109
+ id: itemId,
110
+ props: {
111
+ isRoot: true,
112
+ },
113
+ select: {
114
+ isVerified: true,
115
+ },
116
+ });
117
+
118
+ if (!item) {
119
+ throw new BadDataException(
120
+ "Item with ID " + itemId.toString() + " not found",
121
+ );
122
+ }
123
+
124
+ if (item.isVerified) {
125
+ throw new BadDataException("Telegram account already verified");
126
+ }
127
+
128
+ await this.updateOneById({
129
+ id: itemId,
130
+ props: {
131
+ isRoot: true,
132
+ },
133
+ data: {
134
+ verificationCode: Text.generateRandomNumber(6),
135
+ },
136
+ });
137
+ }
138
+ }
139
+
140
+ export default new Service();
@@ -280,22 +280,36 @@ export default class MonitorResourceUtil {
280
280
 
281
281
  logger.debug(dataToProcess);
282
282
 
283
- await MonitorService.updateOneById({
284
- id: monitor.id!,
285
- data: {
286
- serverMonitorRequestReceivedAt:
287
- serverMonitorResponse.requestReceivedAt!,
288
- serverMonitorResponse,
289
- },
290
- props: {
291
- isRoot: true,
292
- ignoreHooks: true,
293
- },
294
- });
283
+ /*
284
+ * Skip persistence when this evaluation originated from the
285
+ * CheckOnlineStatus cron (onlyCheckRequestReceivedAt=true). The cron
286
+ * re-evaluates using the already-stale value read from the DB and has
287
+ * no new heartbeat data to persist — writing it back would race with
288
+ * (and overwrite) the ingest path's fresh heartbeat update, causing
289
+ * the monitor to flap between Online and Offline every minute.
290
+ */
291
+ if (!serverMonitorResponse.onlyCheckRequestReceivedAt) {
292
+ await MonitorService.updateOneById({
293
+ id: monitor.id!,
294
+ data: {
295
+ serverMonitorRequestReceivedAt:
296
+ serverMonitorResponse.requestReceivedAt!,
297
+ serverMonitorResponse,
298
+ },
299
+ props: {
300
+ isRoot: true,
301
+ ignoreHooks: true,
302
+ },
303
+ });
295
304
 
296
- logger.debug(
297
- `${dataToProcess.monitorId.toString()} - Monitor Server Response Updated`,
298
- );
305
+ logger.debug(
306
+ `${dataToProcess.monitorId.toString()} - Monitor Server Response Updated`,
307
+ );
308
+ } else {
309
+ logger.debug(
310
+ `${dataToProcess.monitorId.toString()} - Skipping Monitor Server Response persist (cron re-evaluation).`,
311
+ );
312
+ }
299
313
  }
300
314
 
301
315
  if (incomingMonitorRequest) {
@@ -183,16 +183,10 @@ export default class MonitorTemplateUtil {
183
183
  }
184
184
  }
185
185
 
186
- if (
187
- data.monitorType === MonitorType.SyntheticMonitor ||
188
- data.monitorType === MonitorType.CustomJavaScriptCode
189
- ) {
186
+ if (data.monitorType === MonitorType.CustomJavaScriptCode) {
190
187
  const customCodeResponse: CustomCodeMonitorResponse | undefined = (
191
188
  data.dataToProcess as ProbeMonitorResponse
192
189
  ).customCodeMonitorResponse;
193
- const syntheticResponse: SyntheticMonitorResponse[] | undefined = (
194
- data.dataToProcess as ProbeMonitorResponse
195
- ).syntheticMonitorResponse;
196
190
 
197
191
  storageMap = {
198
192
  executionTimeInMs: customCodeResponse?.executionTimeInMS,
@@ -202,16 +196,35 @@ export default class MonitorTemplateUtil {
202
196
  failureCause: (data.dataToProcess as ProbeMonitorResponse)
203
197
  .failureCause,
204
198
  } as JSONObject;
199
+ }
205
200
 
206
- // Add synthetic monitor specific fields if available
207
- if (syntheticResponse && syntheticResponse.length > 0) {
208
- const firstResponse: SyntheticMonitorResponse = syntheticResponse[0]!;
209
- if (firstResponse) {
210
- storageMap["screenshots"] = firstResponse.screenshots;
211
- storageMap["browserType"] = firstResponse.browserType;
212
- storageMap["screenSizeType"] = firstResponse.screenSizeType;
213
- }
214
- }
201
+ if (data.monitorType === MonitorType.SyntheticMonitor) {
202
+ const syntheticResponse: SyntheticMonitorResponse[] | undefined = (
203
+ data.dataToProcess as ProbeMonitorResponse
204
+ ).syntheticMonitorResponse;
205
+
206
+ /*
207
+ * Synthetic monitors run across multiple browser / screen-size combinations.
208
+ * Each run is exposed through the syntheticResponses array — use
209
+ * {{syntheticResponses[i].*}} or {{#each syntheticResponses}} in templates.
210
+ */
211
+ storageMap = {
212
+ syntheticResponses: (syntheticResponse || []).map(
213
+ (response: SyntheticMonitorResponse) => {
214
+ return {
215
+ executionTimeInMs: response.executionTimeInMS,
216
+ result: response.result,
217
+ scriptError: response.scriptError,
218
+ logMessages: response.logMessages || [],
219
+ screenshots: response.screenshots,
220
+ browserType: response.browserType,
221
+ screenSizeType: response.screenSizeType,
222
+ };
223
+ },
224
+ ),
225
+ failureCause: (data.dataToProcess as ProbeMonitorResponse)
226
+ .failureCause,
227
+ } as JSONObject;
215
228
  }
216
229
 
217
230
  if (data.monitorType === MonitorType.SNMP) {
@@ -1,5 +1,6 @@
1
1
  import OneUptimeDate from "../../Types/Date";
2
2
  import PositiveNumber from "../../Types/PositiveNumber";
3
+ import Timezone from "../../Types/Timezone";
3
4
  import moment, { isMoment } from "moment";
4
5
 
5
6
  describe("class OneUptimeDate", () => {
@@ -92,4 +93,161 @@ describe("class OneUptimeDate", () => {
92
93
  OneUptimeDate.fromString("2026-04-01 14:45:31.414000000").toISOString(),
93
94
  ).toBe("2026-04-01T14:45:31.414Z");
94
95
  });
96
+
97
+ describe("getZoneAbbrByTimezone (DST awareness)", () => {
98
+ test("returns EST for America/New_York on a winter date", () => {
99
+ const winterDate: Date = new Date("2026-01-15T12:00:00Z");
100
+ expect(
101
+ OneUptimeDate.getZoneAbbrByTimezone(
102
+ Timezone.AmericaNew_York,
103
+ winterDate,
104
+ ),
105
+ ).toBe("EST");
106
+ });
107
+
108
+ test("returns EDT for America/New_York on a summer date", () => {
109
+ const summerDate: Date = new Date("2026-07-15T12:00:00Z");
110
+ expect(
111
+ OneUptimeDate.getZoneAbbrByTimezone(
112
+ Timezone.AmericaNew_York,
113
+ summerDate,
114
+ ),
115
+ ).toBe("EDT");
116
+ });
117
+
118
+ test("returns PST for America/Los_Angeles on a winter date", () => {
119
+ const winterDate: Date = new Date("2026-01-15T12:00:00Z");
120
+ expect(
121
+ OneUptimeDate.getZoneAbbrByTimezone(
122
+ Timezone.AmericaLos_Angeles,
123
+ winterDate,
124
+ ),
125
+ ).toBe("PST");
126
+ });
127
+
128
+ test("returns PDT for America/Los_Angeles on a summer date", () => {
129
+ const summerDate: Date = new Date("2026-07-15T12:00:00Z");
130
+ expect(
131
+ OneUptimeDate.getZoneAbbrByTimezone(
132
+ Timezone.AmericaLos_Angeles,
133
+ summerDate,
134
+ ),
135
+ ).toBe("PDT");
136
+ });
137
+
138
+ test("returns UTC for UTC regardless of date", () => {
139
+ const winterDate: Date = new Date("2026-01-15T12:00:00Z");
140
+ const summerDate: Date = new Date("2026-07-15T12:00:00Z");
141
+ expect(
142
+ OneUptimeDate.getZoneAbbrByTimezone(Timezone.UTC, winterDate),
143
+ ).toBe("UTC");
144
+ expect(
145
+ OneUptimeDate.getZoneAbbrByTimezone(Timezone.UTC, summerDate),
146
+ ).toBe("UTC");
147
+ });
148
+
149
+ test("returns IST for Asia/Kolkata year-round (no DST)", () => {
150
+ const winterDate: Date = new Date("2026-01-15T12:00:00Z");
151
+ const summerDate: Date = new Date("2026-07-15T12:00:00Z");
152
+ expect(
153
+ OneUptimeDate.getZoneAbbrByTimezone(Timezone.AsiaKolkata, winterDate),
154
+ ).toBe("IST");
155
+ expect(
156
+ OneUptimeDate.getZoneAbbrByTimezone(Timezone.AsiaKolkata, summerDate),
157
+ ).toBe("IST");
158
+ });
159
+
160
+ test("returns correct abbreviation right after US DST spring-forward", () => {
161
+ // US DST begins 2026-03-08 at 02:00 local -> springs to 03:00 EDT.
162
+ const beforeDst: Date = new Date("2026-03-08T06:00:00Z"); // 01:00 EST
163
+ const afterDst: Date = new Date("2026-03-08T08:00:00Z"); // 04:00 EDT
164
+ expect(
165
+ OneUptimeDate.getZoneAbbrByTimezone(
166
+ Timezone.AmericaNew_York,
167
+ beforeDst,
168
+ ),
169
+ ).toBe("EST");
170
+ expect(
171
+ OneUptimeDate.getZoneAbbrByTimezone(Timezone.AmericaNew_York, afterDst),
172
+ ).toBe("EDT");
173
+ });
174
+
175
+ test("accepts an ISO date string", () => {
176
+ expect(
177
+ OneUptimeDate.getZoneAbbrByTimezone(
178
+ Timezone.AmericaNew_York,
179
+ "2026-01-15T12:00:00Z",
180
+ ),
181
+ ).toBe("EST");
182
+ expect(
183
+ OneUptimeDate.getZoneAbbrByTimezone(
184
+ Timezone.AmericaNew_York,
185
+ "2026-07-15T12:00:00Z",
186
+ ),
187
+ ).toBe("EDT");
188
+ });
189
+
190
+ test("falls back to current date when no date is passed (backward compat)", () => {
191
+ const abbr: string = OneUptimeDate.getZoneAbbrByTimezone(Timezone.UTC);
192
+ expect(abbr).toBe("UTC");
193
+ });
194
+ });
195
+
196
+ describe("getDateAsFormattedArrayInMultipleTimezones (DST awareness)", () => {
197
+ test("shows EST for a winter event in America/New_York", () => {
198
+ const result: Array<string> =
199
+ OneUptimeDate.getDateAsFormattedArrayInMultipleTimezones({
200
+ date: new Date("2026-01-15T17:00:00Z"),
201
+ timezones: [Timezone.AmericaNew_York],
202
+ use12HourFormat: true,
203
+ });
204
+ expect(result).toHaveLength(1);
205
+ expect(result[0]).toContain("EST");
206
+ expect(result[0]).not.toContain("EDT");
207
+ // 17:00 UTC in winter EST (UTC-5) = 12:00 PM
208
+ expect(result[0]).toContain("12:00 PM");
209
+ });
210
+
211
+ test("shows EDT for a summer event in America/New_York", () => {
212
+ const result: Array<string> =
213
+ OneUptimeDate.getDateAsFormattedArrayInMultipleTimezones({
214
+ date: new Date("2026-07-15T17:00:00Z"),
215
+ timezones: [Timezone.AmericaNew_York],
216
+ use12HourFormat: true,
217
+ });
218
+ expect(result).toHaveLength(1);
219
+ expect(result[0]).toContain("EDT");
220
+ expect(result[0]).not.toContain("EST");
221
+ // 17:00 UTC in summer EDT (UTC-4) = 01:00 PM
222
+ expect(result[0]).toContain("01:00 PM");
223
+ });
224
+
225
+ test("picks the correct abbreviation per-timezone for the same event", () => {
226
+ // A winter event in multiple zones: NY should say EST, LA should say PST, UTC stays UTC.
227
+ const result: Array<string> =
228
+ OneUptimeDate.getDateAsFormattedArrayInMultipleTimezones({
229
+ date: new Date("2026-01-15T17:00:00Z"),
230
+ timezones: [
231
+ Timezone.UTC,
232
+ Timezone.AmericaNew_York,
233
+ Timezone.AmericaLos_Angeles,
234
+ ],
235
+ use12HourFormat: true,
236
+ });
237
+ expect(result[0]).toContain("UTC");
238
+ expect(result[1]).toContain("EST");
239
+ expect(result[2]).toContain("PST");
240
+ });
241
+
242
+ test("omits the abbreviation when onlyShowDate is true", () => {
243
+ const result: Array<string> =
244
+ OneUptimeDate.getDateAsFormattedArrayInMultipleTimezones({
245
+ date: new Date("2026-07-15T17:00:00Z"),
246
+ timezones: [Timezone.AmericaNew_York],
247
+ onlyShowDate: true,
248
+ });
249
+ expect(result[0]).not.toContain("EDT");
250
+ expect(result[0]).not.toContain("EST");
251
+ });
252
+ });
95
253
  });
@@ -6,6 +6,7 @@ enum AnalyticsTableName {
6
6
  MonitorLog = "MonitorLogV2",
7
7
  Profile = "ProfileItemV2",
8
8
  ProfileSample = "ProfileSampleItemV2",
9
+ AuditLog = "AuditLogV1",
9
10
  }
10
11
 
11
12
  export default AnalyticsTableName;
@@ -0,0 +1,7 @@
1
+ enum AuditLogAction {
2
+ Create = "Create",
3
+ Update = "Update",
4
+ Delete = "Delete",
5
+ }
6
+
7
+ export default AuditLogAction;
@@ -0,0 +1,5 @@
1
+ export default interface EnableAuditLogOn {
2
+ create?: boolean | undefined;
3
+ update?: boolean | undefined;
4
+ delete?: boolean | undefined;
5
+ }
@@ -0,0 +1,18 @@
1
+ import EnableAuditLogOn from "../BaseDatabase/EnableAuditLogOn";
2
+ import GenericFunction from "../GenericFunction";
3
+
4
+ export default (
5
+ enableAuditLogOn: EnableAuditLogOn = {
6
+ create: true,
7
+ update: true,
8
+ delete: true,
9
+ },
10
+ ) => {
11
+ return (ctr: GenericFunction) => {
12
+ ctr.prototype.enableAuditLogOn = {
13
+ create: enableAuditLogOn.create ?? true,
14
+ update: enableAuditLogOn.update ?? true,
15
+ delete: enableAuditLogOn.delete ?? true,
16
+ };
17
+ };
18
+ };
package/Types/Date.ts CHANGED
@@ -1406,7 +1406,7 @@ export default class OneUptimeDate {
1406
1406
  " " +
1407
1407
  (onlyShowDate
1408
1408
  ? ""
1409
- : this.getZoneAbbrByTimezone(timezones[i] as Timezone)),
1409
+ : this.getZoneAbbrByTimezone(timezones[i] as Timezone, date)),
1410
1410
  );
1411
1411
  }
1412
1412
 
@@ -1497,8 +1497,17 @@ export default class OneUptimeDate {
1497
1497
  return this.getZoneAbbrByTimezone(this.getCurrentTimezone());
1498
1498
  }
1499
1499
 
1500
- public static getZoneAbbrByTimezone(timezone: Timezone): string {
1501
- let zoneAbbr: string = moment.tz(timezone).zoneAbbr();
1500
+ public static getZoneAbbrByTimezone(
1501
+ timezone: Timezone,
1502
+ date?: string | Date | undefined,
1503
+ ): string {
1504
+ /*
1505
+ * Pass the reference date into moment so the abbreviation reflects DST
1506
+ * at that date (e.g. EST in winter, EDT in summer) rather than "now".
1507
+ */
1508
+ let zoneAbbr: string = date
1509
+ ? moment(this.fromString(date)).tz(timezone).zoneAbbr()
1510
+ : moment.tz(timezone).zoneAbbr();
1502
1511
 
1503
1512
  if (zoneAbbr.startsWith("+") || zoneAbbr.startsWith("-")) {
1504
1513
  zoneAbbr = "GMT" + zoneAbbr;
@@ -141,6 +141,7 @@ enum IconProp {
141
141
  SquareStack3D = "SquareStack3D",
142
142
  ExclaimationCircle = "ExclaimationCircle",
143
143
  WhatsApp = "WhatsApp",
144
+ Telegram = "Telegram",
144
145
  Brain = "Brain",
145
146
  FlowDiagram = "FlowDiagram",
146
147
  GitHub = "GitHub",