@oneuptime/common 9.5.9 → 9.5.12

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 (61) hide show
  1. package/Server/API/OpenSourceDeploymentAPI.ts +8 -0
  2. package/Server/Middleware/UserAuthorization.ts +8 -3
  3. package/Server/Services/MonitorService.ts +8 -0
  4. package/Server/Services/WorkspaceNotificationRuleService.ts +11 -2
  5. package/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.ts +183 -0
  6. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +13 -0
  7. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +37 -0
  8. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +11 -7
  9. package/Server/Utils/Workspace/Slack/Slack.ts +15 -12
  10. package/Tests/Server/Middleware/UserAuthorization.test.ts +10 -2
  11. package/Types/Monitor/CriteriaFilter.ts +20 -3
  12. package/Types/Monitor/DnsMonitor/DnsMonitorResponse.ts +16 -0
  13. package/Types/Monitor/DnsMonitor/DnsRecordType.ts +14 -0
  14. package/Types/Monitor/MonitorCriteriaInstance.ts +67 -0
  15. package/Types/Monitor/MonitorStep.ts +30 -0
  16. package/Types/Monitor/MonitorStepDnsMonitor.ts +46 -0
  17. package/Types/Monitor/MonitorType.ts +15 -2
  18. package/Types/Probe/ProbeMonitorResponse.ts +2 -0
  19. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +56 -1
  20. package/UI/esbuild-config.js +52 -3
  21. package/UI/index.d.ts +26 -0
  22. package/Utils/Monitor/MonitorMetricType.ts +2 -1
  23. package/build/dist/Server/API/OpenSourceDeploymentAPI.js +5 -0
  24. package/build/dist/Server/API/OpenSourceDeploymentAPI.js.map +1 -1
  25. package/build/dist/Server/Middleware/UserAuthorization.js +2 -3
  26. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  27. package/build/dist/Server/Services/MonitorService.js +8 -1
  28. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  29. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +10 -1
  30. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  31. package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js +138 -0
  32. package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js.map +1 -0
  33. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +10 -0
  34. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  35. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +24 -0
  36. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  37. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +10 -7
  38. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  39. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +15 -11
  40. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  41. package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js +4 -2
  42. package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js.map +1 -1
  43. package/build/dist/Types/Monitor/CriteriaFilter.js +15 -3
  44. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  45. package/build/dist/Types/Monitor/DnsMonitor/DnsMonitorResponse.js +2 -0
  46. package/build/dist/Types/Monitor/DnsMonitor/DnsMonitorResponse.js.map +1 -0
  47. package/build/dist/Types/Monitor/DnsMonitor/DnsRecordType.js +15 -0
  48. package/build/dist/Types/Monitor/DnsMonitor/DnsRecordType.js.map +1 -0
  49. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +62 -0
  50. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  51. package/build/dist/Types/Monitor/MonitorStep.js +22 -0
  52. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  53. package/build/dist/Types/Monitor/MonitorStepDnsMonitor.js +34 -0
  54. package/build/dist/Types/Monitor/MonitorStepDnsMonitor.js.map +1 -0
  55. package/build/dist/Types/Monitor/MonitorType.js +13 -2
  56. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  57. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +55 -1
  58. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  59. package/build/dist/Utils/Monitor/MonitorMetricType.js +2 -1
  60. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  61. package/package.json +1 -1
@@ -39,6 +39,14 @@ export default class OpenSourceDeploymentAPI extends BaseAPI<
39
39
  (body["oneuptimeVersion"] as string) || "unknown";
40
40
  deployment.instanceUrl = (body["instanceUrl"] as string) || "";
41
41
 
42
+ // Skip localhost instances - these are default/unconfigured deployments.
43
+ if (
44
+ deployment.instanceUrl === "http://localhost/" ||
45
+ deployment.instanceUrl === "http://localhost"
46
+ ) {
47
+ return Response.sendEmptySuccessResponse(req, res);
48
+ }
49
+
42
50
  await OpenSourceDeploymentService.create({
43
51
  data: deployment,
44
52
  props: {
@@ -177,10 +177,15 @@ export default class UserMiddleware {
177
177
  try {
178
178
  oneuptimeRequest.userAuthorization = JSONWebToken.decode(accessToken);
179
179
  } catch (err) {
180
- // if the token is invalid or expired, it'll throw this error.
180
+ // if the token is invalid or expired, return 401 so clients can refresh the token.
181
181
  logger.error(err);
182
- oneuptimeRequest.userType = UserType.Public;
183
- return next();
182
+ return Response.sendErrorResponse(
183
+ req,
184
+ res,
185
+ new NotAuthenticatedException(
186
+ "AccessToken is invalid or expired. Please refresh your token.",
187
+ ),
188
+ );
184
189
  }
185
190
 
186
191
  if (oneuptimeRequest.userAuthorization.isMasterAdmin) {
@@ -127,6 +127,14 @@ export class Service extends DatabaseService<Model> {
127
127
  monitorDestination = `${monitorDestination}:${port}`;
128
128
  }
129
129
  }
130
+
131
+ // For DNS monitors, use the queryName from dnsMonitor config
132
+ if (monitorType === MonitorType.DNS && firstStep?.data?.dnsMonitor) {
133
+ monitorDestination = firstStep.data.dnsMonitor.queryName || "";
134
+ if (firstStep.data.dnsMonitor.hostname) {
135
+ monitorDestination = `${monitorDestination} @${firstStep.data.dnsMonitor.hostname}`;
136
+ }
137
+ }
130
138
  }
131
139
  }
132
140
 
@@ -1805,9 +1805,18 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
1805
1805
  logger.debug("New channel template name:");
1806
1806
  logger.debug(notificationChannels);
1807
1807
 
1808
+ /*
1809
+ * Sanitize the suffix for workspace channel names.
1810
+ * When no custom prefix is set, the default "#" prefix (e.g. "#42") produces
1811
+ * invalid Slack channel names (e.g. "oneuptime-alert-#42"). Strip characters
1812
+ * that are not valid in Slack/Teams channel names.
1813
+ */
1814
+ const sanitizedSuffix: string = data.channelNameSiffix
1815
+ .toLowerCase()
1816
+ .replace(/[^a-z0-9\-_]/g, "");
1817
+
1808
1818
  // add suffix and then check if it is already added or not.
1809
- const channelName: string =
1810
- notificationChannels + data.channelNameSiffix;
1819
+ const channelName: string = notificationChannels + sanitizedSuffix;
1811
1820
 
1812
1821
  logger.debug("Final channel name with suffix:");
1813
1822
  logger.debug(channelName);
@@ -0,0 +1,183 @@
1
+ import DataToProcess from "../DataToProcess";
2
+ import CompareCriteria from "./CompareCriteria";
3
+ import {
4
+ CheckOn,
5
+ CriteriaFilter,
6
+ FilterType,
7
+ } from "../../../../Types/Monitor/CriteriaFilter";
8
+ import DnsMonitorResponse from "../../../../Types/Monitor/DnsMonitor/DnsMonitorResponse";
9
+ import ProbeMonitorResponse from "../../../../Types/Probe/ProbeMonitorResponse";
10
+ import EvaluateOverTime from "./EvaluateOverTime";
11
+ import CaptureSpan from "../../Telemetry/CaptureSpan";
12
+ import logger from "../../Logger";
13
+
14
+ export default class DnsMonitorCriteria {
15
+ @CaptureSpan()
16
+ public static async isMonitorInstanceCriteriaFilterMet(input: {
17
+ dataToProcess: DataToProcess;
18
+ criteriaFilter: CriteriaFilter;
19
+ }): Promise<string | null> {
20
+ let threshold: number | string | undefined | null =
21
+ input.criteriaFilter.value;
22
+
23
+ const dataToProcess: ProbeMonitorResponse =
24
+ input.dataToProcess as ProbeMonitorResponse;
25
+
26
+ const dnsResponse: DnsMonitorResponse | undefined =
27
+ dataToProcess.dnsResponse;
28
+
29
+ let overTimeValue: Array<number | boolean> | number | boolean | undefined =
30
+ undefined;
31
+
32
+ if (
33
+ input.criteriaFilter.evaluateOverTime &&
34
+ input.criteriaFilter.evaluateOverTimeOptions
35
+ ) {
36
+ try {
37
+ overTimeValue = await EvaluateOverTime.getValueOverTime({
38
+ projectId: (input.dataToProcess as ProbeMonitorResponse).projectId,
39
+ monitorId: input.dataToProcess.monitorId!,
40
+ evaluateOverTimeOptions: input.criteriaFilter.evaluateOverTimeOptions,
41
+ metricType: input.criteriaFilter.checkOn,
42
+ });
43
+
44
+ if (Array.isArray(overTimeValue) && overTimeValue.length === 0) {
45
+ overTimeValue = undefined;
46
+ }
47
+ } catch (err) {
48
+ logger.error(
49
+ `Error in getting over time value for ${input.criteriaFilter.checkOn}`,
50
+ );
51
+ logger.error(err);
52
+ overTimeValue = undefined;
53
+ }
54
+ }
55
+
56
+ // Check if DNS is online
57
+ if (input.criteriaFilter.checkOn === CheckOn.DnsIsOnline) {
58
+ const currentIsOnline: boolean | Array<boolean> =
59
+ (overTimeValue as Array<boolean>) ||
60
+ (input.dataToProcess as ProbeMonitorResponse).isOnline;
61
+
62
+ return CompareCriteria.compareCriteriaBoolean({
63
+ value: currentIsOnline,
64
+ criteriaFilter: input.criteriaFilter,
65
+ });
66
+ }
67
+
68
+ // Check DNS response time
69
+ if (input.criteriaFilter.checkOn === CheckOn.DnsResponseTime) {
70
+ threshold = CompareCriteria.convertToNumber(threshold);
71
+
72
+ if (threshold === null || threshold === undefined) {
73
+ return null;
74
+ }
75
+
76
+ const currentResponseTime: number | Array<number> =
77
+ (overTimeValue as Array<number>) ||
78
+ dnsResponse?.responseTimeInMs ||
79
+ (input.dataToProcess as ProbeMonitorResponse).responseTimeInMs;
80
+
81
+ if (currentResponseTime === null || currentResponseTime === undefined) {
82
+ return null;
83
+ }
84
+
85
+ return CompareCriteria.compareCriteriaNumbers({
86
+ value: currentResponseTime,
87
+ threshold: threshold as number,
88
+ criteriaFilter: input.criteriaFilter,
89
+ });
90
+ }
91
+
92
+ // Check if DNS record exists
93
+ if (input.criteriaFilter.checkOn === CheckOn.DnsRecordExists) {
94
+ const exists: boolean = Boolean(
95
+ dnsResponse?.records && dnsResponse.records.length > 0,
96
+ );
97
+
98
+ const isTrue: boolean =
99
+ input.criteriaFilter.filterType === FilterType.True;
100
+ const isFalse: boolean =
101
+ input.criteriaFilter.filterType === FilterType.False;
102
+
103
+ if (exists && isTrue) {
104
+ return `DNS records exist for the query.`;
105
+ }
106
+
107
+ if (!exists && isFalse) {
108
+ return `No DNS records found for the query.`;
109
+ }
110
+
111
+ return null;
112
+ }
113
+
114
+ // Check DNSSEC validity
115
+ if (input.criteriaFilter.checkOn === CheckOn.DnssecIsValid) {
116
+ const isTrue: boolean =
117
+ input.criteriaFilter.filterType === FilterType.True;
118
+ const isFalse: boolean =
119
+ input.criteriaFilter.filterType === FilterType.False;
120
+
121
+ if (dnsResponse?.isDnssecValid === undefined) {
122
+ return null;
123
+ }
124
+
125
+ if (dnsResponse.isDnssecValid && isTrue) {
126
+ return `DNSSEC is valid.`;
127
+ }
128
+
129
+ if (!dnsResponse.isDnssecValid && isFalse) {
130
+ return `DNSSEC is not valid.`;
131
+ }
132
+
133
+ return null;
134
+ }
135
+
136
+ // Check DNS record value
137
+ if (input.criteriaFilter.checkOn === CheckOn.DnsRecordValue) {
138
+ if (!dnsResponse?.records || dnsResponse.records.length === 0) {
139
+ return null;
140
+ }
141
+
142
+ // Check if any record value matches the criteria
143
+ for (const record of dnsResponse.records) {
144
+ const recordValue: string = record.value;
145
+
146
+ // Try numeric comparison first
147
+ if (
148
+ typeof threshold === "number" ||
149
+ (typeof threshold === "string" && !isNaN(Number(threshold)))
150
+ ) {
151
+ const numericThreshold: number | null =
152
+ CompareCriteria.convertToNumber(threshold);
153
+
154
+ if (numericThreshold !== null && !isNaN(Number(recordValue))) {
155
+ const result: string | null =
156
+ CompareCriteria.compareCriteriaNumbers({
157
+ value: Number(recordValue),
158
+ threshold: numericThreshold,
159
+ criteriaFilter: input.criteriaFilter,
160
+ });
161
+
162
+ if (result) {
163
+ return `DNS record (${record.type}): ${result}`;
164
+ }
165
+ }
166
+ }
167
+
168
+ // String comparison
169
+ const result: string | null = CompareCriteria.compareCriteriaStrings({
170
+ value: recordValue,
171
+ threshold: String(threshold),
172
+ criteriaFilter: input.criteriaFilter,
173
+ });
174
+
175
+ if (result) {
176
+ return `DNS record (${record.type}): ${result}`;
177
+ }
178
+ }
179
+ }
180
+
181
+ return null;
182
+ }
183
+ }
@@ -12,6 +12,7 @@ import MetricMonitorCriteria from "./Criteria/MetricMonitorCriteria";
12
12
  import TraceMonitorCriteria from "./Criteria/TraceMonitorCriteria";
13
13
  import ExceptionMonitorCriteria from "./Criteria/ExceptionMonitorCriteria";
14
14
  import SnmpMonitorCriteria from "./Criteria/SnmpMonitorCriteria";
15
+ import DnsMonitorCriteria from "./Criteria/DnsMonitorCriteria";
15
16
  import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
16
17
  import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
17
18
  import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
@@ -493,6 +494,18 @@ ${contextBlock}
493
494
  }
494
495
  }
495
496
 
497
+ if (input.monitor.monitorType === MonitorType.DNS) {
498
+ const dnsMonitorResult: string | null =
499
+ await DnsMonitorCriteria.isMonitorInstanceCriteriaFilterMet({
500
+ dataToProcess: input.dataToProcess,
501
+ criteriaFilter: input.criteriaFilter,
502
+ });
503
+
504
+ if (dnsMonitorResult) {
505
+ return dnsMonitorResult;
506
+ }
507
+ }
508
+
496
509
  return null;
497
510
  }
498
511
 
@@ -14,6 +14,9 @@ import SyntheticMonitorResponse from "../../../Types/Monitor/SyntheticMonitors/S
14
14
  import SnmpMonitorResponse, {
15
15
  SnmpOidResponse,
16
16
  } from "../../../Types/Monitor/SnmpMonitor/SnmpMonitorResponse";
17
+ import DnsMonitorResponse, {
18
+ DnsRecordResponse,
19
+ } from "../../../Types/Monitor/DnsMonitor/DnsMonitorResponse";
17
20
  import Typeof from "../../../Types/Typeof";
18
21
  import VMUtil from "../VM/VMAPI";
19
22
  import DataToProcess from "./DataToProcess";
@@ -240,6 +243,40 @@ export default class MonitorTemplateUtil {
240
243
  }
241
244
  }
242
245
  }
246
+
247
+ if (data.monitorType === MonitorType.DNS) {
248
+ const dnsResponse: DnsMonitorResponse | undefined = (
249
+ data.dataToProcess as ProbeMonitorResponse
250
+ ).dnsResponse;
251
+
252
+ storageMap = {
253
+ isOnline: (data.dataToProcess as ProbeMonitorResponse).isOnline,
254
+ responseTimeInMs: dnsResponse?.responseTimeInMs,
255
+ failureCause: dnsResponse?.failureCause,
256
+ isTimeout: dnsResponse?.isTimeout,
257
+ isDnssecValid: dnsResponse?.isDnssecValid,
258
+ } as JSONObject;
259
+
260
+ // Add DNS records
261
+ if (dnsResponse?.records) {
262
+ storageMap["records"] = dnsResponse.records.map(
263
+ (record: DnsRecordResponse) => {
264
+ return {
265
+ type: record.type,
266
+ value: record.value,
267
+ ttl: record.ttl,
268
+ };
269
+ },
270
+ );
271
+
272
+ // Add record values as a flat array for easier templating
273
+ storageMap["recordValues"] = dnsResponse.records.map(
274
+ (record: DnsRecordResponse) => {
275
+ return record.value;
276
+ },
277
+ );
278
+ }
279
+ }
243
280
  } catch (err) {
244
281
  logger.error(err);
245
282
  }
@@ -780,13 +780,14 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
780
780
 
781
781
  const workspaceChannels: Array<WorkspaceChannel> = [];
782
782
 
783
- for (let channelName of data.channelNames) {
784
- // Normalize channel name - Teams has different naming requirements
785
- if (channelName && channelName.startsWith("#")) {
786
- channelName = channelName.substring(1);
787
- }
788
- // Teams channels cannot have spaces in the name for some operations
789
- const normalizedChannelName: string = channelName.replace(/\s+/g, "-");
783
+ for (const channelName of data.channelNames) {
784
+ /*
785
+ * Normalize channel name: replace spaces with hyphens, then strip
786
+ * characters not valid in Teams channel names (e.g. #, %, &, *, etc.).
787
+ */
788
+ const normalizedChannelName: string = channelName
789
+ .replace(/\s+/g, "-")
790
+ .replace(/[^a-zA-Z0-9\-_]/g, "");
790
791
 
791
792
  // Check if channel exists
792
793
  const existingChannel: WorkspaceChannel | null =
@@ -839,6 +840,9 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
839
840
  }): Promise<WorkspaceChannel> {
840
841
  const teamId: string = data.teamId;
841
842
 
843
+ // Sanitize channel name: strip characters not valid in Teams channel names.
844
+ data.channelName = data.channelName.replace(/[^a-zA-Z0-9\-_\s]/g, "");
845
+
842
846
  // Get valid access token
843
847
  const accessToken: string = await this.getValidAccessToken({
844
848
  authToken: data.authToken,
@@ -442,12 +442,14 @@ export default class SlackUtil extends WorkspaceBase {
442
442
  const workspaceChannels: Array<WorkspaceChannel> = [];
443
443
 
444
444
  for (let channelName of data.channelNames) {
445
- // Normalize channel name
446
- if (channelName && channelName.startsWith("#")) {
447
- channelName = channelName.substring(1);
448
- }
449
- channelName = channelName.toLowerCase();
450
- channelName = channelName.replace(/\s+/g, "-");
445
+ /*
446
+ * Normalize channel name: replace spaces with hyphens, then strip
447
+ * any characters not valid in Slack channel names.
448
+ */
449
+ channelName = channelName
450
+ .toLowerCase()
451
+ .replace(/\s+/g, "-")
452
+ .replace(/[^a-z0-9\-_]/g, "");
451
453
 
452
454
  // Check if channel exists using optimized method
453
455
  const existingChannel: WorkspaceChannel | null =
@@ -1345,12 +1347,13 @@ export default class SlackUtil extends WorkspaceBase {
1345
1347
  channelName: string;
1346
1348
  projectId: ObjectID;
1347
1349
  }): Promise<WorkspaceChannel> {
1348
- if (data.channelName && data.channelName.startsWith("#")) {
1349
- data.channelName = data.channelName.substring(1);
1350
- }
1351
-
1352
- // lower case channel name
1353
- data.channelName = data.channelName.toLowerCase();
1350
+ /*
1351
+ * Sanitize channel name: Slack only allows lowercase letters, numbers,
1352
+ * hyphens, and underscores. Remove all other characters (including #).
1353
+ */
1354
+ data.channelName = data.channelName
1355
+ .toLowerCase()
1356
+ .replace(/[^a-z0-9\-_]/g, "");
1354
1357
 
1355
1358
  logger.debug("Creating channel with data:");
1356
1359
  logger.debug(data);
@@ -14,6 +14,7 @@ import Response from "../../../Server/Utils/Response";
14
14
  import Dictionary from "../../../Types/Dictionary";
15
15
  import Email from "../../../Types/Email";
16
16
  import BadDataException from "../../../Types/Exception/BadDataException";
17
+ import NotAuthenticatedException from "../../../Types/Exception/NotAuthenticatedException";
17
18
  import SsoAuthorizationException from "../../../Types/Exception/SsoAuthorizationException";
18
19
  import HashedString from "../../../Types/HashedString";
19
20
  import JSONFunctions from "../../../Types/JSONFunctions";
@@ -330,7 +331,7 @@ describe("UserMiddleware", () => {
330
331
  expect(JSONWebToken.decode).not.toHaveBeenCalled();
331
332
  });
332
333
 
333
- test("should call function 'next' and return, when accessToken can not be decoded", async () => {
334
+ test("should call Response.sendErrorResponse with NotAuthenticatedException, when accessToken can not be decoded", async () => {
334
335
  const error: Error = new Error("Invalid access token");
335
336
 
336
337
  const spyJWTDecode: jest.SpyInstance = getJestSpyOn(
@@ -342,7 +343,14 @@ describe("UserMiddleware", () => {
342
343
 
343
344
  await UserMiddleware.getUserMiddleware(req, res, next);
344
345
 
345
- expect(next).toHaveBeenCalled();
346
+ expect(Response.sendErrorResponse).toHaveBeenCalledWith(
347
+ req,
348
+ res,
349
+ new NotAuthenticatedException(
350
+ "AccessToken is invalid or expired. Please refresh your token.",
351
+ ),
352
+ );
353
+ expect(next).not.toHaveBeenCalled();
346
354
  expect(spyJWTDecode).toHaveBeenCalledWith(mockedAccessToken);
347
355
  expect(UserService.updateOneBy).not.toHaveBeenCalled();
348
356
  });
@@ -60,6 +60,13 @@ export enum CheckOn {
60
60
  SnmpOidExists = "SNMP OID Exists",
61
61
  SnmpResponseTime = "SNMP Response Time (in ms)",
62
62
  SnmpIsOnline = "SNMP Device Is Online",
63
+
64
+ // DNS monitors.
65
+ DnsResponseTime = "DNS Response Time (in ms)",
66
+ DnsIsOnline = "DNS Is Online",
67
+ DnsRecordValue = "DNS Record Value",
68
+ DnssecIsValid = "DNSSEC Is Valid",
69
+ DnsRecordExists = "DNS Record Exists",
63
70
  }
64
71
 
65
72
  export interface ServerMonitorOptions {
@@ -141,7 +148,11 @@ export class CriteriaFilterUtil {
141
148
  }): boolean {
142
149
  const { checkOn } = data;
143
150
 
144
- if (checkOn === CheckOn.IsOnline || checkOn === CheckOn.SnmpIsOnline) {
151
+ if (
152
+ checkOn === CheckOn.IsOnline ||
153
+ checkOn === CheckOn.SnmpIsOnline ||
154
+ checkOn === CheckOn.DnsIsOnline
155
+ ) {
145
156
  return false;
146
157
  }
147
158
 
@@ -149,7 +160,11 @@ export class CriteriaFilterUtil {
149
160
  return false;
150
161
  }
151
162
 
152
- if (checkOn === CheckOn.SnmpOidExists) {
163
+ if (
164
+ checkOn === CheckOn.SnmpOidExists ||
165
+ checkOn === CheckOn.DnssecIsValid ||
166
+ checkOn === CheckOn.DnsRecordExists
167
+ ) {
153
168
  return false;
154
169
  }
155
170
 
@@ -204,7 +219,9 @@ export class CriteriaFilterUtil {
204
219
  checkOn === CheckOn.MemoryUsagePercent ||
205
220
  checkOn === CheckOn.IsOnline ||
206
221
  checkOn === CheckOn.SnmpResponseTime ||
207
- checkOn === CheckOn.SnmpIsOnline
222
+ checkOn === CheckOn.SnmpIsOnline ||
223
+ checkOn === CheckOn.DnsResponseTime ||
224
+ checkOn === CheckOn.DnsIsOnline
208
225
  );
209
226
  }
210
227
  }
@@ -0,0 +1,16 @@
1
+ import DnsRecordType from "./DnsRecordType";
2
+
3
+ export interface DnsRecordResponse {
4
+ type: DnsRecordType;
5
+ value: string;
6
+ ttl?: number | undefined;
7
+ }
8
+
9
+ export default interface DnsMonitorResponse {
10
+ isOnline: boolean;
11
+ responseTimeInMs: number;
12
+ failureCause: string;
13
+ records: Array<DnsRecordResponse>;
14
+ isDnssecValid?: boolean | undefined;
15
+ isTimeout?: boolean | undefined;
16
+ }
@@ -0,0 +1,14 @@
1
+ enum DnsRecordType {
2
+ A = "A",
3
+ AAAA = "AAAA",
4
+ CNAME = "CNAME",
5
+ MX = "MX",
6
+ NS = "NS",
7
+ TXT = "TXT",
8
+ SOA = "SOA",
9
+ PTR = "PTR",
10
+ SRV = "SRV",
11
+ CAA = "CAA",
12
+ }
13
+
14
+ export default DnsRecordType;
@@ -394,6 +394,33 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
394
394
  return monitorCriteriaInstance;
395
395
  }
396
396
 
397
+ if (arg.monitorType === MonitorType.DNS) {
398
+ const monitorCriteriaInstance: MonitorCriteriaInstance =
399
+ new MonitorCriteriaInstance();
400
+
401
+ monitorCriteriaInstance.data = {
402
+ id: ObjectID.generate().toString(),
403
+ monitorStatusId: arg.monitorStatusId,
404
+ filterCondition: FilterCondition.All,
405
+ filters: [
406
+ {
407
+ checkOn: CheckOn.DnsIsOnline,
408
+ filterType: FilterType.True,
409
+ value: undefined,
410
+ },
411
+ ],
412
+ incidents: [],
413
+ alerts: [],
414
+ createAlerts: false,
415
+ changeMonitorStatus: true,
416
+ createIncidents: false,
417
+ name: `Check if ${arg.monitorName} is online`,
418
+ description: `This criteria checks if the ${arg.monitorName} DNS resolution is online`,
419
+ };
420
+
421
+ return monitorCriteriaInstance;
422
+ }
423
+
397
424
  return null;
398
425
  }
399
426
 
@@ -495,6 +522,46 @@ export default class MonitorCriteriaInstance extends DatabaseProperty {
495
522
  };
496
523
  }
497
524
 
525
+ if (arg.monitorType === MonitorType.DNS) {
526
+ monitorCriteriaInstance.data = {
527
+ id: ObjectID.generate().toString(),
528
+ monitorStatusId: arg.monitorStatusId,
529
+ filterCondition: FilterCondition.Any,
530
+ filters: [
531
+ {
532
+ checkOn: CheckOn.DnsIsOnline,
533
+ filterType: FilterType.False,
534
+ value: undefined,
535
+ },
536
+ ],
537
+ incidents: [
538
+ {
539
+ title: `${arg.monitorName} is offline`,
540
+ description: `${arg.monitorName} DNS resolution is currently failing.`,
541
+ incidentSeverityId: arg.incidentSeverityId,
542
+ autoResolveIncident: true,
543
+ id: ObjectID.generate().toString(),
544
+ onCallPolicyIds: [],
545
+ },
546
+ ],
547
+ changeMonitorStatus: true,
548
+ createIncidents: true,
549
+ createAlerts: false,
550
+ alerts: [
551
+ {
552
+ title: `${arg.monitorName} is offline`,
553
+ description: `${arg.monitorName} DNS resolution is currently failing.`,
554
+ alertSeverityId: arg.alertSeverityId,
555
+ autoResolveAlert: true,
556
+ id: ObjectID.generate().toString(),
557
+ onCallPolicyIds: [],
558
+ },
559
+ ],
560
+ name: `Check if ${arg.monitorName} is offline`,
561
+ description: `This criteria checks if the ${arg.monitorName} DNS resolution is failing`,
562
+ };
563
+ }
564
+
498
565
  if (
499
566
  arg.monitorType === MonitorType.API ||
500
567
  arg.monitorType === MonitorType.Website