@oneuptime/common 10.0.66 → 10.0.67

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 (79) hide show
  1. package/Models/DatabaseModels/GlobalConfig.ts +56 -0
  2. package/Models/DatabaseModels/Index.ts +4 -0
  3. package/Models/DatabaseModels/Project.ts +30 -0
  4. package/Models/DatabaseModels/TelegramLog.ts +1025 -0
  5. package/Models/DatabaseModels/UserNotificationRule.ts +49 -0
  6. package/Models/DatabaseModels/UserNotificationSetting.ts +17 -0
  7. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +48 -0
  8. package/Models/DatabaseModels/UserTelegram.ts +312 -0
  9. package/Server/API/UserTelegramAPI.ts +167 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1776761171349-MigrationName.ts +325 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  12. package/Server/Services/Index.ts +6 -0
  13. package/Server/Services/TelegramLogService.ts +15 -0
  14. package/Server/Services/TelegramService.ts +139 -0
  15. package/Server/Services/UserNotificationRuleService.ts +350 -1
  16. package/Server/Services/UserNotificationSettingService.ts +114 -0
  17. package/Server/Services/UserTelegramService.ts +140 -0
  18. package/Server/Utils/Monitor/MonitorResource.ts +29 -15
  19. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +29 -16
  20. package/Tests/Types/Date.test.ts +158 -0
  21. package/Types/Date.ts +12 -3
  22. package/Types/Icon/IconProp.ts +1 -0
  23. package/Types/Permission.ts +11 -0
  24. package/Types/Telegram/TelegramMessage.ts +9 -0
  25. package/Types/TelegramStatus.ts +14 -0
  26. package/UI/Components/Icon/Icon.tsx +15 -0
  27. package/build/dist/Models/DatabaseModels/GlobalConfig.js +59 -0
  28. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  29. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  30. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  31. package/build/dist/Models/DatabaseModels/Project.js +32 -0
  32. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  33. package/build/dist/Models/DatabaseModels/TelegramLog.js +1056 -0
  34. package/build/dist/Models/DatabaseModels/TelegramLog.js.map +1 -0
  35. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +49 -0
  36. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  37. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +19 -0
  38. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
  39. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +48 -0
  40. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  41. package/build/dist/Models/DatabaseModels/UserTelegram.js +331 -0
  42. package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -0
  43. package/build/dist/Server/API/UserTelegramAPI.js +99 -0
  44. package/build/dist/Server/API/UserTelegramAPI.js.map +1 -0
  45. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776761171349-MigrationName.js +116 -0
  46. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776761171349-MigrationName.js.map +1 -0
  47. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  48. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  49. package/build/dist/Server/Services/Index.js +6 -0
  50. package/build/dist/Server/Services/Index.js.map +1 -1
  51. package/build/dist/Server/Services/TelegramLogService.js +13 -0
  52. package/build/dist/Server/Services/TelegramLogService.js.map +1 -0
  53. package/build/dist/Server/Services/TelegramService.js +100 -0
  54. package/build/dist/Server/Services/TelegramService.js.map +1 -0
  55. package/build/dist/Server/Services/UserNotificationRuleService.js +272 -21
  56. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  57. package/build/dist/Server/Services/UserNotificationSettingService.js +94 -0
  58. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  59. package/build/dist/Server/Services/UserTelegramService.js +133 -0
  60. package/build/dist/Server/Services/UserTelegramService.js.map +1 -0
  61. package/build/dist/Server/Utils/Monitor/MonitorResource.js +25 -12
  62. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  63. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +24 -12
  64. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  65. package/build/dist/Tests/Types/Date.test.js +96 -0
  66. package/build/dist/Tests/Types/Date.test.js.map +1 -1
  67. package/build/dist/Types/Date.js +9 -3
  68. package/build/dist/Types/Date.js.map +1 -1
  69. package/build/dist/Types/Icon/IconProp.js +1 -0
  70. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  71. package/build/dist/Types/Permission.js +10 -0
  72. package/build/dist/Types/Permission.js.map +1 -1
  73. package/build/dist/Types/Telegram/TelegramMessage.js +2 -0
  74. package/build/dist/Types/Telegram/TelegramMessage.js.map +1 -0
  75. package/build/dist/Types/TelegramStatus.js +15 -0
  76. package/build/dist/Types/TelegramStatus.js.map +1 -0
  77. package/build/dist/UI/Components/Icon/Icon.js +5 -0
  78. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  79. package/package.json +2 -2
@@ -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
  });
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",
@@ -270,6 +270,7 @@ enum Permission {
270
270
 
271
271
  ReadSmsLog = "ReadSmsLog",
272
272
  ReadWhatsAppLog = "ReadWhatsAppLog",
273
+ ReadTelegramLog = "ReadTelegramLog",
273
274
  ReadEmailLog = "ReadEmailLog",
274
275
  ReadCallLog = "ReadCallLog",
275
276
  ReadPushLog = "ReadPushLog",
@@ -4520,6 +4521,16 @@ export class PermissionHelper {
4520
4521
  group: PermissionGroup.NotificationLog,
4521
4522
  },
4522
4523
 
4524
+ {
4525
+ permission: Permission.ReadTelegramLog,
4526
+ title: "Read Telegram Log",
4527
+ description: "This permission can read Telegram Log of this project.",
4528
+ isAssignableToTenant: true,
4529
+ isAccessControlPermission: false,
4530
+ isRolePermission: false,
4531
+ group: PermissionGroup.NotificationLog,
4532
+ },
4533
+
4523
4534
  {
4524
4535
  permission: Permission.ReadCallLog,
4525
4536
  title: "Read Call Log",
@@ -0,0 +1,9 @@
1
+ export interface TelegramMessagePayload {
2
+ body: string;
3
+ parseMode?: "MarkdownV2" | "HTML" | undefined;
4
+ disableWebPagePreview?: boolean | undefined;
5
+ }
6
+
7
+ export default interface TelegramMessage extends TelegramMessagePayload {
8
+ to: string;
9
+ }
@@ -0,0 +1,14 @@
1
+ enum TelegramStatus {
2
+ Success = "Success",
3
+ Sent = "Sent",
4
+ Delivered = "Delivered",
5
+ Read = "Read",
6
+ Failed = "Failed",
7
+ Queued = "Queued",
8
+ Error = "Error",
9
+ LowBalance = "Low Balance",
10
+ NotVerified = "Not Verified",
11
+ Unknown = "Unknown",
12
+ }
13
+
14
+ export default TelegramStatus;
@@ -1075,6 +1075,21 @@ const Icon: FunctionComponent<ComponentProps> = ({
1075
1075
  />
1076
1076
  </>,
1077
1077
  );
1078
+ } else if (icon === IconProp.Telegram) {
1079
+ return getSvgWrapper(
1080
+ <>
1081
+ <path
1082
+ strokeLinecap="round"
1083
+ strokeLinejoin="round"
1084
+ d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"
1085
+ />
1086
+ <path
1087
+ strokeLinecap="round"
1088
+ strokeLinejoin="round"
1089
+ d="M7 12.5l2.5 1 1 3 2-2.5 3.5 2.5 1.5-8L6 11l1 1.5z"
1090
+ />
1091
+ </>,
1092
+ );
1078
1093
  } else if (icon === IconProp.Hide) {
1079
1094
  return getSvgWrapper(
1080
1095
  <path
@@ -62,6 +62,9 @@ let GlobalConfig = class GlobalConfig extends GlobalConfigModel {
62
62
  this.metaWhatsAppAppId = undefined;
63
63
  this.metaWhatsAppAppSecret = undefined;
64
64
  this.metaWhatsAppWebhookVerifyToken = undefined;
65
+ this.telegramBotToken = undefined;
66
+ this.telegramBotUsername = undefined;
67
+ this.telegramWebhookSecretToken = undefined;
65
68
  this.emailServerType = undefined;
66
69
  this.sendgridApiKey = undefined;
67
70
  this.sendgridFromEmail = undefined;
@@ -548,6 +551,62 @@ __decorate([
548
551
  }),
549
552
  __metadata("design:type", String)
550
553
  ], GlobalConfig.prototype, "metaWhatsAppWebhookVerifyToken", void 0);
554
+ __decorate([
555
+ ColumnAccessControl({
556
+ create: [],
557
+ read: [],
558
+ update: [],
559
+ }),
560
+ TableColumn({
561
+ type: TableColumnType.VeryLongText,
562
+ title: "Telegram Bot Token",
563
+ description: "Bot token issued by @BotFather for sending Telegram messages.",
564
+ }),
565
+ Column({
566
+ type: ColumnType.VeryLongText,
567
+ nullable: true,
568
+ unique: true,
569
+ }),
570
+ __metadata("design:type", String)
571
+ ], GlobalConfig.prototype, "telegramBotToken", void 0);
572
+ __decorate([
573
+ ColumnAccessControl({
574
+ create: [],
575
+ read: [],
576
+ update: [],
577
+ }),
578
+ TableColumn({
579
+ type: TableColumnType.ShortText,
580
+ title: "Telegram Bot Username",
581
+ description: "Username of your OneUptime Telegram bot (without the leading @). Used to build verification deep links.",
582
+ }),
583
+ Column({
584
+ type: ColumnType.ShortText,
585
+ length: ColumnLength.ShortText,
586
+ nullable: true,
587
+ unique: true,
588
+ }),
589
+ __metadata("design:type", String)
590
+ ], GlobalConfig.prototype, "telegramBotUsername", void 0);
591
+ __decorate([
592
+ ColumnAccessControl({
593
+ create: [],
594
+ read: [],
595
+ update: [],
596
+ }),
597
+ TableColumn({
598
+ type: TableColumnType.ShortText,
599
+ title: "Telegram Webhook Secret Token",
600
+ description: "Secret token passed to Telegram setWebhook. Telegram sends it back in the X-Telegram-Bot-Api-Secret-Token header.",
601
+ }),
602
+ Column({
603
+ type: ColumnType.ShortText,
604
+ length: ColumnLength.ShortText,
605
+ nullable: true,
606
+ unique: true,
607
+ }),
608
+ __metadata("design:type", String)
609
+ ], GlobalConfig.prototype, "telegramWebhookSecretToken", void 0);
551
610
  __decorate([
552
611
  ColumnAccessControl({
553
612
  create: [],