@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.
- package/Models/DatabaseModels/GlobalConfig.ts +56 -0
- package/Models/DatabaseModels/Index.ts +4 -0
- package/Models/DatabaseModels/Project.ts +30 -0
- package/Models/DatabaseModels/TelegramLog.ts +1025 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +49 -0
- package/Models/DatabaseModels/UserNotificationSetting.ts +17 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +48 -0
- package/Models/DatabaseModels/UserTelegram.ts +312 -0
- package/Server/API/UserTelegramAPI.ts +167 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776761171349-MigrationName.ts +325 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/Index.ts +6 -0
- package/Server/Services/TelegramLogService.ts +15 -0
- package/Server/Services/TelegramService.ts +139 -0
- package/Server/Services/UserNotificationRuleService.ts +350 -1
- package/Server/Services/UserNotificationSettingService.ts +114 -0
- package/Server/Services/UserTelegramService.ts +140 -0
- package/Server/Utils/Monitor/MonitorResource.ts +29 -15
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +29 -16
- package/Tests/Types/Date.test.ts +158 -0
- package/Types/Date.ts +12 -3
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/Permission.ts +11 -0
- package/Types/Telegram/TelegramMessage.ts +9 -0
- package/Types/TelegramStatus.ts +14 -0
- package/UI/Components/Icon/Icon.tsx +15 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +59 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +32 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelegramLog.js +1056 -0
- package/build/dist/Models/DatabaseModels/TelegramLog.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +49 -0
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +19 -0
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +48 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTelegram.js +331 -0
- package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -0
- package/build/dist/Server/API/UserTelegramAPI.js +99 -0
- package/build/dist/Server/API/UserTelegramAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776761171349-MigrationName.js +116 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776761171349-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/Index.js +6 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/TelegramLogService.js +13 -0
- package/build/dist/Server/Services/TelegramLogService.js.map +1 -0
- package/build/dist/Server/Services/TelegramService.js +100 -0
- package/build/dist/Server/Services/TelegramService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js +272 -21
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +94 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserTelegramService.js +133 -0
- package/build/dist/Server/Services/UserTelegramService.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +25 -12
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +24 -12
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Tests/Types/Date.test.js +96 -0
- package/build/dist/Tests/Types/Date.test.js.map +1 -1
- package/build/dist/Types/Date.js +9 -3
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Permission.js +10 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Telegram/TelegramMessage.js +2 -0
- package/build/dist/Types/Telegram/TelegramMessage.js.map +1 -0
- package/build/dist/Types/TelegramStatus.js +15 -0
- package/build/dist/Types/TelegramStatus.js.map +1 -0
- package/build/dist/UI/Components/Icon/Icon.js +5 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- 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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
297
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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) {
|
package/Tests/Types/Date.test.ts
CHANGED
|
@@ -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(
|
|
1501
|
-
|
|
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;
|
package/Types/Icon/IconProp.ts
CHANGED
package/Types/Permission.ts
CHANGED
|
@@ -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,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: [],
|