@oneuptime/common 8.0.5514 → 8.0.5544
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/AcmeChallenge.ts +3 -0
- package/Models/DatabaseModels/Domain.ts +6 -1
- package/Server/API/AcmeChallengeAPI.ts +64 -0
- package/Server/EnvironmentConfig.ts +54 -0
- package/Server/Infrastructure/Queue.ts +12 -16
- package/Server/Infrastructure/QueueWorker.ts +2 -6
- package/Server/Services/DomainService.ts +48 -40
- package/Server/Utils/AnalyticsDatabase/Statement.ts +8 -4
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +47 -15
- package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +13 -12
- package/Server/Utils/Monitor/MonitorAlert.ts +46 -1
- package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +208 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +439 -0
- package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +141 -0
- package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +88 -0
- package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +196 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +1123 -0
- package/Server/Utils/Monitor/MonitorIncident.ts +46 -1
- package/Server/Utils/Monitor/MonitorLogUtil.ts +44 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +482 -0
- package/Server/Utils/Monitor/MonitorResource.ts +185 -914
- package/Server/Utils/StartServer.ts +14 -6
- package/Types/Email.ts +3 -7
- package/Types/Monitor/IncomingMonitor/IncomingMonitorRequest.ts +2 -0
- package/Types/Monitor/LogMonitor/LogMonitorResponse.ts +2 -0
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +2 -0
- package/Types/Monitor/MonitorEvaluationSummary.ts +48 -0
- package/Types/Monitor/ServerMonitor/ServerMonitorResponse.ts +2 -0
- package/Types/Monitor/TraceMonitor/TraceMonitorResponse.ts +2 -0
- package/Types/Probe/ProbeApiIngestResponse.ts +2 -0
- package/Types/Probe/ProbeMonitorResponse.ts +2 -0
- package/UI/Components/Filters/FiltersForm.tsx +4 -1
- package/build/dist/Models/DatabaseModels/AcmeChallenge.js +3 -0
- package/build/dist/Models/DatabaseModels/AcmeChallenge.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Domain.js +6 -1
- package/build/dist/Models/DatabaseModels/Domain.js.map +1 -1
- package/build/dist/Server/API/AcmeChallengeAPI.js +39 -0
- package/build/dist/Server/API/AcmeChallengeAPI.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +44 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +8 -11
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +2 -6
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Services/DomainService.js +31 -29
- package/build/dist/Server/Services/DomainService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +2 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +34 -16
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +12 -12
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +42 -4
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +119 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +312 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +109 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +45 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +132 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +583 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +42 -4
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +32 -0
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +361 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +133 -666
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +7 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Types/Email.js +2 -5
- package/build/dist/Types/Email.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorEvaluationSummary.js +2 -0
- package/build/dist/Types/Monitor/MonitorEvaluationSummary.js.map +1 -0
- package/build/dist/UI/Components/Filters/FiltersForm.js +2 -1
- package/build/dist/UI/Components/Filters/FiltersForm.js.map +1 -1
- package/package.json +5 -5
|
@@ -4,11 +4,13 @@ import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccess
|
|
|
4
4
|
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
|
|
5
5
|
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
6
6
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
7
|
+
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
7
8
|
import TableColumn from "../../Types/Database/TableColumn";
|
|
8
9
|
import TableColumnType from "../../Types/Database/TableColumnType";
|
|
9
10
|
import TableMetadata from "../../Types/Database/TableMetadata";
|
|
10
11
|
import IconProp from "../../Types/Icon/IconProp";
|
|
11
12
|
import ObjectID from "../../Types/ObjectID";
|
|
13
|
+
import Route from "../../Types/API/Route";
|
|
12
14
|
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
13
15
|
|
|
14
16
|
@TableAccessControl({
|
|
@@ -24,6 +26,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
|
24
26
|
icon: IconProp.Lock,
|
|
25
27
|
tableDescription: "HTTP Challege for Lets Encrypt Certificates",
|
|
26
28
|
})
|
|
29
|
+
@CrudApiEndpoint(new Route("/acme-challenge"))
|
|
27
30
|
@Entity({
|
|
28
31
|
name: "AcmeChallenge",
|
|
29
32
|
})
|
|
@@ -301,7 +301,12 @@ export default class Domain extends BaseModel {
|
|
|
301
301
|
public deletedByUserId?: ObjectID = undefined;
|
|
302
302
|
|
|
303
303
|
@ColumnAccessControl({
|
|
304
|
-
create: [
|
|
304
|
+
create: [
|
|
305
|
+
Permission.ProjectOwner,
|
|
306
|
+
Permission.ProjectAdmin,
|
|
307
|
+
Permission.ProjectMember,
|
|
308
|
+
Permission.CreateProjectDomain,
|
|
309
|
+
],
|
|
305
310
|
read: [
|
|
306
311
|
Permission.ProjectOwner,
|
|
307
312
|
Permission.ProjectAdmin,
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import AcmeChallenge from "../../Models/DatabaseModels/AcmeChallenge";
|
|
2
|
+
import NotFoundException from "../../Types/Exception/NotFoundException";
|
|
3
|
+
import AcmeChallengeService, {
|
|
4
|
+
Service as AcmeChallengeServiceType,
|
|
5
|
+
} from "../Services/AcmeChallengeService";
|
|
6
|
+
import Express, {
|
|
7
|
+
ExpressRequest,
|
|
8
|
+
ExpressResponse,
|
|
9
|
+
ExpressRouter,
|
|
10
|
+
NextFunction,
|
|
11
|
+
} from "../Utils/Express";
|
|
12
|
+
import Response from "../Utils/Response";
|
|
13
|
+
import BaseAPI from "./BaseAPI";
|
|
14
|
+
|
|
15
|
+
export default class AcmeChallengeAPI extends BaseAPI<
|
|
16
|
+
AcmeChallenge,
|
|
17
|
+
AcmeChallengeServiceType
|
|
18
|
+
> {
|
|
19
|
+
private wellKnownRouter: ExpressRouter;
|
|
20
|
+
|
|
21
|
+
public constructor() {
|
|
22
|
+
super(AcmeChallenge, AcmeChallengeService);
|
|
23
|
+
|
|
24
|
+
this.wellKnownRouter = Express.getRouter();
|
|
25
|
+
|
|
26
|
+
this.wellKnownRouter.get(
|
|
27
|
+
"/acme-challenge/.well-known/:token",
|
|
28
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
29
|
+
try {
|
|
30
|
+
const challenge: AcmeChallenge | null =
|
|
31
|
+
await AcmeChallengeService.findOneBy({
|
|
32
|
+
query: {
|
|
33
|
+
token: req.params["token"] as string,
|
|
34
|
+
},
|
|
35
|
+
select: {
|
|
36
|
+
challenge: true,
|
|
37
|
+
},
|
|
38
|
+
props: {
|
|
39
|
+
isRoot: true,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!challenge) {
|
|
44
|
+
return next(new NotFoundException("Challenge not found"));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return Response.sendTextResponse(
|
|
48
|
+
req,
|
|
49
|
+
res,
|
|
50
|
+
challenge.challenge as string,
|
|
51
|
+
);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
return next(err);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
this.router.use("/", this.wellKnownRouter);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public getWellKnownRouter(): ExpressRouter {
|
|
62
|
+
return this.wellKnownRouter;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -23,6 +23,58 @@ export const getAllEnvVars: () => JSONObject = (): JSONObject => {
|
|
|
23
23
|
return process.env;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
const FRONTEND_ENV_ALLOW_LIST: Array<string> = [
|
|
27
|
+
"NODE_ENV",
|
|
28
|
+
"HTTP_PROTOCOL",
|
|
29
|
+
"HOST",
|
|
30
|
+
"BILLING_ENABLED",
|
|
31
|
+
"BILLING_PUBLIC_KEY",
|
|
32
|
+
"IS_ENTERPRISE_EDITION",
|
|
33
|
+
"STRIPE_PUBLIC_KEY",
|
|
34
|
+
"VAPID_PUBLIC_KEY",
|
|
35
|
+
"VAPID_SUBJECT",
|
|
36
|
+
"VERSION",
|
|
37
|
+
"STATUS_PAGE_CNAME_RECORD",
|
|
38
|
+
"ANALYTICS_KEY",
|
|
39
|
+
"ANALYTICS_HOST",
|
|
40
|
+
"GIT_SHA",
|
|
41
|
+
"APP_VERSION",
|
|
42
|
+
"OPENTELEMETRY_EXPORTER_OTLP_ENDPOINT",
|
|
43
|
+
"OPENTELEMETRY_EXPORTER_OTLP_HEADERS",
|
|
44
|
+
"DISABLE_TELEMETRY",
|
|
45
|
+
"SLACK_APP_CLIENT_ID",
|
|
46
|
+
"MICROSOFT_TEAMS_APP_CLIENT_ID",
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const FRONTEND_ENV_ALLOW_PREFIXES: Array<string> = [
|
|
50
|
+
"SUBSCRIPTION_PLAN_",
|
|
51
|
+
"PUBLIC_",
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
export const getFrontendEnvVars: () => JSONObject = (): JSONObject => {
|
|
55
|
+
const frontendEnv: JSONObject = {};
|
|
56
|
+
|
|
57
|
+
for (const key of Object.keys(process.env)) {
|
|
58
|
+
const shouldInclude: boolean =
|
|
59
|
+
FRONTEND_ENV_ALLOW_LIST.includes(key) ||
|
|
60
|
+
FRONTEND_ENV_ALLOW_PREFIXES.some((prefix: string) => {
|
|
61
|
+
return key.startsWith(prefix);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!shouldInclude) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const value: string | undefined = process.env[key];
|
|
69
|
+
|
|
70
|
+
if (typeof value !== "undefined") {
|
|
71
|
+
frontendEnv[key] = value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return frontendEnv;
|
|
76
|
+
};
|
|
77
|
+
|
|
26
78
|
const parsePositiveNumberFromEnv: (
|
|
27
79
|
envKey: string,
|
|
28
80
|
fallback: number,
|
|
@@ -276,6 +328,8 @@ export const HttpProtocol: Protocol =
|
|
|
276
328
|
|
|
277
329
|
export const Host: string = process.env["HOST"] || "";
|
|
278
330
|
|
|
331
|
+
export const ProvisionSsl: boolean = process.env["PROVISION_SSL"] === "true";
|
|
332
|
+
|
|
279
333
|
export const WorkflowScriptTimeoutInMS: number = process.env[
|
|
280
334
|
"WORKFLOW_SCRIPT_TIMEOUT_IN_MS"
|
|
281
335
|
]
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ClusterKey,
|
|
3
|
-
RedisHostname,
|
|
4
|
-
RedisPassword,
|
|
5
|
-
RedisPort,
|
|
6
|
-
} from "../EnvironmentConfig";
|
|
1
|
+
import { ClusterKey } from "../EnvironmentConfig";
|
|
7
2
|
import Dictionary from "../../Types/Dictionary";
|
|
8
3
|
import { JSONObject } from "../../Types/JSON";
|
|
9
4
|
import { Queue as BullQueue, Job, JobsOptions } from "bullmq";
|
|
@@ -13,6 +8,7 @@ import { BullMQAdapter } from "@bull-board/api/bullMQAdapter";
|
|
|
13
8
|
import { ExpressRouter } from "../Utils/Express";
|
|
14
9
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
15
10
|
import logger from "../Utils/Logger";
|
|
11
|
+
import Redis from "./Redis";
|
|
16
12
|
|
|
17
13
|
export enum QueueName {
|
|
18
14
|
Workflow = "Workflow",
|
|
@@ -25,6 +21,7 @@ export enum QueueName {
|
|
|
25
21
|
}
|
|
26
22
|
|
|
27
23
|
export type QueueJob = Job;
|
|
24
|
+
type BullBoardQueues = Parameters<typeof createBullBoard>[0]["queues"];
|
|
28
25
|
|
|
29
26
|
export default class Queue {
|
|
30
27
|
private static queueDict: Dictionary<BullQueue> = {};
|
|
@@ -82,11 +79,7 @@ export default class Queue {
|
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
const queue: BullQueue = new BullQueue(queueName, {
|
|
85
|
-
connection:
|
|
86
|
-
host: RedisHostname.toString(),
|
|
87
|
-
port: RedisPort.toNumber(),
|
|
88
|
-
password: RedisPassword,
|
|
89
|
-
},
|
|
82
|
+
connection: Redis.getRedisOptions(),
|
|
90
83
|
// Keep BullMQ data under control to avoid Redis bloat
|
|
91
84
|
defaultJobOptions: {
|
|
92
85
|
// keep only recent completed/failed jobs
|
|
@@ -157,12 +150,15 @@ export default class Queue {
|
|
|
157
150
|
public static getQueueInspectorRouter(): ExpressRouter {
|
|
158
151
|
const serverAdapter: ExpressAdapter = new ExpressAdapter();
|
|
159
152
|
|
|
153
|
+
const queueAdapters: BullMQAdapter[] = Object.values(QueueName).map(
|
|
154
|
+
(queueName: QueueName) => {
|
|
155
|
+
return new BullMQAdapter(this.getQueue(queueName));
|
|
156
|
+
},
|
|
157
|
+
);
|
|
158
|
+
|
|
160
159
|
createBullBoard({
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
return new BullMQAdapter(this.getQueue(queueName));
|
|
164
|
-
}),
|
|
165
|
-
],
|
|
160
|
+
// Cast keeps compatibility until bull-board widens QueueJob.progress
|
|
161
|
+
queues: queueAdapters as unknown as BullBoardQueues,
|
|
166
162
|
serverAdapter: serverAdapter,
|
|
167
163
|
});
|
|
168
164
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { RedisHostname, RedisPassword, RedisPort } from "../EnvironmentConfig";
|
|
2
1
|
import { QueueJob, QueueName } from "./Queue";
|
|
3
2
|
import TimeoutException from "../../Types/Exception/TimeoutException";
|
|
4
3
|
import {
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
} from "../../Types/FunctionTypes";
|
|
9
8
|
import { Worker } from "bullmq";
|
|
10
9
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
10
|
+
import Redis from "./Redis";
|
|
11
11
|
|
|
12
12
|
export default class QueueWorker {
|
|
13
13
|
@CaptureSpan()
|
|
@@ -30,11 +30,7 @@ export default class QueueWorker {
|
|
|
30
30
|
},
|
|
31
31
|
): Worker {
|
|
32
32
|
const worker: Worker = new Worker(queueName, onJobInQueue, {
|
|
33
|
-
connection:
|
|
34
|
-
host: RedisHostname.toString(),
|
|
35
|
-
port: RedisPort.toNumber(),
|
|
36
|
-
password: RedisPassword,
|
|
37
|
-
},
|
|
33
|
+
connection: Redis.getRedisOptions(),
|
|
38
34
|
concurrency: options.concurrency,
|
|
39
35
|
// Only set these values if provided so we do not override BullMQ defaults
|
|
40
36
|
...(options.lockDuration ? { lockDuration: options.lockDuration } : {}),
|
|
@@ -7,6 +7,9 @@ import BadDataException from "../../Types/Exception/BadDataException";
|
|
|
7
7
|
import Text from "../../Types/Text";
|
|
8
8
|
import Model from "../../Models/DatabaseModels/Domain";
|
|
9
9
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
10
|
+
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
11
|
+
import ObjectID from "../../Types/ObjectID";
|
|
12
|
+
import { FindWhere } from "../../Types/BaseDatabase/Query";
|
|
10
13
|
export class Service extends DatabaseService<Model> {
|
|
11
14
|
public constructor() {
|
|
12
15
|
super(Model);
|
|
@@ -32,6 +35,12 @@ export class Service extends DatabaseService<Model> {
|
|
|
32
35
|
createBy.data.domain = new Domain(domain.trim().toLowerCase());
|
|
33
36
|
}
|
|
34
37
|
|
|
38
|
+
if (!createBy.props.isRoot && createBy.data.isVerified) {
|
|
39
|
+
throw new BadDataException(
|
|
40
|
+
"Domain cannot be verified during creation. Please verify the domain after creation. Please set isVerified to false.",
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
35
44
|
createBy.data.domainVerificationText =
|
|
36
45
|
"oneuptime-verification-" + Text.generateRandomText(20);
|
|
37
46
|
return Promise.resolve({ createBy, carryForward: null });
|
|
@@ -41,67 +50,66 @@ export class Service extends DatabaseService<Model> {
|
|
|
41
50
|
protected override async onBeforeUpdate(
|
|
42
51
|
updateBy: UpdateBy<Model>,
|
|
43
52
|
): Promise<OnUpdate<Model>> {
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
if (updateBy.data.isVerified && !updateBy.props.isRoot) {
|
|
54
|
+
const projectId: FindWhere<ObjectID> | undefined =
|
|
55
|
+
updateBy.query.projectId || updateBy.props.tenantId;
|
|
56
|
+
|
|
57
|
+
if (!projectId) {
|
|
58
|
+
throw new BadDataException(
|
|
59
|
+
"Project ID is required to verify the domain.",
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
49
63
|
// check the verification of the domain.
|
|
50
64
|
|
|
51
65
|
const items: Array<Model> = await this.findBy({
|
|
52
66
|
query: {
|
|
53
|
-
|
|
54
|
-
|
|
67
|
+
projectId,
|
|
68
|
+
...updateBy.query,
|
|
55
69
|
},
|
|
56
70
|
select: {
|
|
57
71
|
domain: true,
|
|
58
72
|
domainVerificationText: true,
|
|
59
73
|
},
|
|
60
74
|
|
|
61
|
-
limit:
|
|
75
|
+
limit: LIMIT_PER_PROJECT,
|
|
62
76
|
skip: 0,
|
|
63
77
|
props: {
|
|
64
78
|
isRoot: true,
|
|
65
79
|
},
|
|
66
80
|
});
|
|
67
81
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
82
|
+
for (const item of items) {
|
|
83
|
+
const domain: string | undefined = item?.domain?.toString();
|
|
84
|
+
const verificationText: string | undefined =
|
|
85
|
+
item?.domainVerificationText?.toString();
|
|
73
86
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
if (!domain) {
|
|
88
|
+
throw new BadDataException("Domain not found.");
|
|
89
|
+
}
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
if (!verificationText) {
|
|
92
|
+
throw new BadDataException(
|
|
93
|
+
"Domain verification text with id " +
|
|
94
|
+
updateBy.query._id +
|
|
95
|
+
" not found.",
|
|
96
|
+
);
|
|
97
|
+
}
|
|
83
98
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
updateBy.query._id +
|
|
88
|
-
" not found.",
|
|
99
|
+
const isVerified: boolean = await Domain.verifyTxtRecord(
|
|
100
|
+
domain,
|
|
101
|
+
verificationText,
|
|
89
102
|
);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const isVerified: boolean = await Domain.verifyTxtRecord(
|
|
93
|
-
domain,
|
|
94
|
-
verificationText,
|
|
95
|
-
);
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
if (!isVerified) {
|
|
105
|
+
throw new BadDataException(
|
|
106
|
+
"Verification TXT record " +
|
|
107
|
+
verificationText +
|
|
108
|
+
" not found in domain " +
|
|
109
|
+
domain +
|
|
110
|
+
". Please add a TXT record to verify the domain. If you have already added the TXT record, please wait for few hours to let DNS to propagate.",
|
|
111
|
+
);
|
|
112
|
+
}
|
|
105
113
|
}
|
|
106
114
|
}
|
|
107
115
|
|
|
@@ -177,11 +177,15 @@ export class Statement implements BaseQueryParams {
|
|
|
177
177
|
};
|
|
178
178
|
|
|
179
179
|
if ((statementParam as StatementParameter).value instanceof Includes) {
|
|
180
|
-
const
|
|
180
|
+
const includesValues: Array<string | number | ObjectID> = (
|
|
181
181
|
(statementParam as StatementParameter).value as Includes
|
|
182
|
-
).values
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
).values as Array<string | number | ObjectID>;
|
|
183
|
+
|
|
184
|
+
const isNumberArray: boolean = includesValues.every(
|
|
185
|
+
(v: string | number | ObjectID) => {
|
|
186
|
+
return typeof v === "number";
|
|
187
|
+
},
|
|
188
|
+
);
|
|
185
189
|
|
|
186
190
|
if (isNumberArray) {
|
|
187
191
|
return "Array(Int32)";
|
|
@@ -563,39 +563,39 @@ export default class CompareCriteria {
|
|
|
563
563
|
data.criteriaFilter.filterType !== FilterType.True &&
|
|
564
564
|
data.criteriaFilter.filterType !== FilterType.False
|
|
565
565
|
) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
566
|
+
const formattedValues: string = CompareCriteria.formatCriteriaValues(
|
|
567
|
+
data.values,
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
message += ` is ${formattedValues}`;
|
|
571
571
|
|
|
572
572
|
message += " which is";
|
|
573
573
|
}
|
|
574
574
|
|
|
575
575
|
switch (data.criteriaFilter.filterType) {
|
|
576
576
|
case FilterType.GreaterThan:
|
|
577
|
-
message += ` greater than ${data.threshold}. `;
|
|
577
|
+
message += ` greater than ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
578
578
|
break;
|
|
579
579
|
case FilterType.GreaterThanOrEqualTo:
|
|
580
|
-
message += ` greater than or equal to ${data.threshold}. `;
|
|
580
|
+
message += ` greater than or equal to ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
581
581
|
break;
|
|
582
582
|
case FilterType.LessThan:
|
|
583
|
-
message += ` less than ${data.threshold}. `;
|
|
583
|
+
message += ` less than ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
584
584
|
break;
|
|
585
585
|
case FilterType.LessThanOrEqualTo:
|
|
586
|
-
message += ` less than or equal to ${data.threshold}. `;
|
|
586
|
+
message += ` less than or equal to ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
587
587
|
break;
|
|
588
588
|
case FilterType.NotEqualTo:
|
|
589
|
-
message += ` not equal to ${data.threshold}. `;
|
|
589
|
+
message += ` not equal to ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
590
590
|
break;
|
|
591
591
|
case FilterType.EqualTo:
|
|
592
|
-
message += ` equal to ${data.threshold}. `;
|
|
592
|
+
message += ` equal to ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
593
593
|
break;
|
|
594
594
|
case FilterType.Contains:
|
|
595
|
-
message += ` contains ${data.threshold}. `;
|
|
595
|
+
message += ` contains ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
596
596
|
break;
|
|
597
597
|
case FilterType.NotContains:
|
|
598
|
-
message += ` does not contain ${data.threshold}. `;
|
|
598
|
+
message += ` does not contain ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
599
599
|
break;
|
|
600
600
|
case FilterType.True:
|
|
601
601
|
message += ` is ${data.threshold}. `;
|
|
@@ -604,13 +604,45 @@ export default class CompareCriteria {
|
|
|
604
604
|
message += ` is ${data.threshold}. `;
|
|
605
605
|
break;
|
|
606
606
|
case FilterType.StartsWith:
|
|
607
|
-
message += ` starts with ${data.threshold}. `;
|
|
607
|
+
message += ` starts with ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
608
608
|
break;
|
|
609
609
|
case FilterType.EndsWith:
|
|
610
|
-
message += ` ends with ${data.threshold}. `;
|
|
610
|
+
message += ` ends with ${CompareCriteria.formatSingleValue(data.threshold)}. `;
|
|
611
611
|
break;
|
|
612
612
|
}
|
|
613
613
|
|
|
614
614
|
return message.trim();
|
|
615
615
|
}
|
|
616
|
+
|
|
617
|
+
private static formatCriteriaValues(
|
|
618
|
+
values: Array<number | boolean> | number | boolean | string,
|
|
619
|
+
): string {
|
|
620
|
+
if (Array.isArray(values)) {
|
|
621
|
+
return values
|
|
622
|
+
.map((value: number | boolean) => {
|
|
623
|
+
return CompareCriteria.formatSingleValue(value);
|
|
624
|
+
})
|
|
625
|
+
.join(", ");
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return CompareCriteria.formatSingleValue(values);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
private static formatSingleValue(
|
|
632
|
+
value: number | boolean | string | null | undefined,
|
|
633
|
+
): string {
|
|
634
|
+
if (value === null || value === undefined) {
|
|
635
|
+
return "unknown";
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (typeof value === Typeof.Number) {
|
|
639
|
+
return (value as number).toFixed(2);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (typeof value === Typeof.Boolean) {
|
|
643
|
+
return value ? "true" : "false";
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
return value.toString();
|
|
647
|
+
}
|
|
616
648
|
}
|
|
@@ -169,20 +169,21 @@ export default class ServerMonitorCriteria {
|
|
|
169
169
|
const diskPath: string =
|
|
170
170
|
input.criteriaFilter.serverMonitorOptions?.diskPath || "/";
|
|
171
171
|
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
)
|
|
176
|
-
(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
172
|
+
const diskMetric: BasicDiskMetrics | undefined = (
|
|
173
|
+
input.dataToProcess as ServerMonitorResponse
|
|
174
|
+
).basicInfrastructureMetrics?.diskMetrics.find(
|
|
175
|
+
(item: BasicDiskMetrics) => {
|
|
176
|
+
return (
|
|
177
|
+
item.diskPath.trim().toLowerCase() === diskPath.trim().toLowerCase()
|
|
178
|
+
);
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const diskUsagePercent: number =
|
|
183
|
+
diskMetric?.percentUsed ?? diskMetric?.percentFree ?? 0;
|
|
183
184
|
|
|
184
185
|
return CompareCriteria.compareCriteriaNumbers({
|
|
185
|
-
value:
|
|
186
|
+
value: diskUsagePercent,
|
|
186
187
|
threshold: threshold as number,
|
|
187
188
|
criteriaFilter: input.criteriaFilter,
|
|
188
189
|
});
|
|
@@ -21,6 +21,8 @@ import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
|
21
21
|
import DataToProcess from "./DataToProcess";
|
|
22
22
|
import MonitorTemplateUtil from "./MonitorTemplateUtil";
|
|
23
23
|
import { JSONObject } from "../../../Types/JSON";
|
|
24
|
+
import OneUptimeDate from "../../../Types/Date";
|
|
25
|
+
import MonitorEvaluationSummary from "../../../Types/Monitor/MonitorEvaluationSummary";
|
|
24
26
|
|
|
25
27
|
export default class MonitorAlert {
|
|
26
28
|
@CaptureSpan()
|
|
@@ -30,6 +32,7 @@ export default class MonitorAlert {
|
|
|
30
32
|
rootCause: string;
|
|
31
33
|
criteriaInstance: MonitorCriteriaInstance | null;
|
|
32
34
|
dataToProcess: DataToProcess;
|
|
35
|
+
evaluationSummary?: MonitorEvaluationSummary | undefined;
|
|
33
36
|
}): Promise<Array<Alert>> {
|
|
34
37
|
// check active alerts and if there are open alerts, do not cretae anothr alert.
|
|
35
38
|
const openAlerts: Array<Alert> = await AlertService.findBy({
|
|
@@ -45,6 +48,7 @@ export default class MonitorAlert {
|
|
|
45
48
|
_id: true,
|
|
46
49
|
createdCriteriaId: true,
|
|
47
50
|
projectId: true,
|
|
51
|
+
alertNumber: true,
|
|
48
52
|
},
|
|
49
53
|
props: {
|
|
50
54
|
isRoot: true,
|
|
@@ -68,6 +72,17 @@ export default class MonitorAlert {
|
|
|
68
72
|
rootCause: input.rootCause,
|
|
69
73
|
dataToProcess: input.dataToProcess,
|
|
70
74
|
});
|
|
75
|
+
|
|
76
|
+
input.evaluationSummary?.events.push({
|
|
77
|
+
type: "alert-resolved",
|
|
78
|
+
title: `Alert resolved: ${openAlert.id?.toString()}`,
|
|
79
|
+
message:
|
|
80
|
+
"Alert auto-resolved because autoresolve is enabled for this criteria.",
|
|
81
|
+
relatedAlertId: openAlert.id?.toString(),
|
|
82
|
+
relatedAlertNumber: openAlert.alertNumber,
|
|
83
|
+
relatedCriteriaId: input.criteriaInstance?.data?.id,
|
|
84
|
+
at: OneUptimeDate.getCurrentDate(),
|
|
85
|
+
});
|
|
71
86
|
}
|
|
72
87
|
}
|
|
73
88
|
|
|
@@ -81,6 +96,7 @@ export default class MonitorAlert {
|
|
|
81
96
|
dataToProcess: DataToProcess;
|
|
82
97
|
rootCause: string;
|
|
83
98
|
autoResolveCriteriaInstanceIdAlertIdsDictionary: Dictionary<Array<string>>;
|
|
99
|
+
evaluationSummary?: MonitorEvaluationSummary | undefined;
|
|
84
100
|
props: {
|
|
85
101
|
telemetryQuery?: TelemetryQuery | undefined;
|
|
86
102
|
};
|
|
@@ -96,6 +112,7 @@ export default class MonitorAlert {
|
|
|
96
112
|
rootCause: input.rootCause,
|
|
97
113
|
criteriaInstance: input.criteriaInstance,
|
|
98
114
|
dataToProcess: input.dataToProcess,
|
|
115
|
+
evaluationSummary: input.evaluationSummary,
|
|
99
116
|
});
|
|
100
117
|
|
|
101
118
|
if (input.criteriaInstance.data?.createAlerts) {
|
|
@@ -124,6 +141,16 @@ export default class MonitorAlert {
|
|
|
124
141
|
);
|
|
125
142
|
|
|
126
143
|
if (hasAlreadyOpenAlert) {
|
|
144
|
+
input.evaluationSummary?.events.push({
|
|
145
|
+
type: "alert-skipped",
|
|
146
|
+
title: `Alert already active: ${criteriaAlert.title}`,
|
|
147
|
+
message:
|
|
148
|
+
"Skipped creating a new alert because an active alert exists for this criteria.",
|
|
149
|
+
relatedCriteriaId: input.criteriaInstance.data?.id,
|
|
150
|
+
relatedAlertId: alreadyOpenAlert?.id?.toString(),
|
|
151
|
+
relatedAlertNumber: alreadyOpenAlert?.alertNumber,
|
|
152
|
+
at: OneUptimeDate.getCurrentDate(),
|
|
153
|
+
});
|
|
127
154
|
continue;
|
|
128
155
|
}
|
|
129
156
|
|
|
@@ -214,15 +241,33 @@ export default class MonitorAlert {
|
|
|
214
241
|
}
|
|
215
242
|
|
|
216
243
|
if (DisableAutomaticAlertCreation) {
|
|
244
|
+
input.evaluationSummary?.events.push({
|
|
245
|
+
type: "alert-skipped",
|
|
246
|
+
title: "Alert creation skipped",
|
|
247
|
+
message:
|
|
248
|
+
"Automatic alert creation is disabled by environment configuration.",
|
|
249
|
+
relatedCriteriaId: input.criteriaInstance.data?.id,
|
|
250
|
+
at: OneUptimeDate.getCurrentDate(),
|
|
251
|
+
});
|
|
217
252
|
return;
|
|
218
253
|
}
|
|
219
254
|
|
|
220
|
-
await AlertService.create({
|
|
255
|
+
const createdAlert: Alert = await AlertService.create({
|
|
221
256
|
data: alert,
|
|
222
257
|
props: {
|
|
223
258
|
isRoot: true,
|
|
224
259
|
},
|
|
225
260
|
});
|
|
261
|
+
|
|
262
|
+
input.evaluationSummary?.events.push({
|
|
263
|
+
type: "alert-created",
|
|
264
|
+
title: `Alert created: ${createdAlert.title || criteriaAlert.title}`,
|
|
265
|
+
message: `Alert triggered from criteria "${input.criteriaInstance.data?.name || "Unnamed criteria"}".`,
|
|
266
|
+
relatedCriteriaId: input.criteriaInstance.data?.id,
|
|
267
|
+
relatedAlertId: createdAlert.id?.toString(),
|
|
268
|
+
relatedAlertNumber: createdAlert.alertNumber,
|
|
269
|
+
at: OneUptimeDate.getCurrentDate(),
|
|
270
|
+
});
|
|
226
271
|
}
|
|
227
272
|
}
|
|
228
273
|
}
|