@oneuptime/common 8.0.5438 → 8.0.5462
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/StatusPage.ts +80 -0
- package/Server/API/StatusPageAPI.ts +138 -52
- package/Server/EnvironmentConfig.ts +34 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1761232578396-MigrationName.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/OpenTelemetryIngestService.ts +1 -39
- package/Server/Services/StatusPageService.ts +117 -0
- package/Server/Services/TelemetryUsageBillingService.ts +208 -15
- package/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.ts +5 -0
- package/Server/Utils/Telemetry/Telemetry.ts +129 -81
- package/Server/Utils/VM/VMRunner.ts +3 -4
- package/UI/Components/Dictionary/Dictionary.tsx +3 -0
- package/UI/Components/Forms/Fields/FieldLabel.tsx +7 -3
- package/UI/Components/LogsViewer/LogItem.tsx +12 -4
- package/UI/Components/LogsViewer/LogsViewer.tsx +131 -29
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -2
- package/UI/Components/ModelFilter/Filter.ts +1 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +2 -1
- package/UI/Components/Table/TableRow.tsx +89 -77
- package/UI/esbuild-config.js +32 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +82 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +157 -74
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +14 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1761232578396-MigrationName.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1761232578396-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/OpenTelemetryIngestService.js +0 -30
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +95 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +168 -8
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.js +4 -0
- package/build/dist/Server/Types/Billing/MeteredPlan/TelemetryMeteredPlan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +84 -60
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +2 -2
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +3 -3
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FieldLabel.js +2 -1
- package/build/dist/UI/Components/Forms/Fields/FieldLabel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogItem.js +5 -3
- package/build/dist/UI/Components/LogsViewer/LogItem.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +73 -22
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +18 -6
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/package.json +4 -4
|
@@ -2331,4 +2331,84 @@ export default class StatusPage extends BaseModel {
|
|
|
2331
2331
|
create: PlanType.Free,
|
|
2332
2332
|
})
|
|
2333
2333
|
public ipWhitelist?: string = undefined;
|
|
2334
|
+
|
|
2335
|
+
@ColumnAccessControl({
|
|
2336
|
+
create: [
|
|
2337
|
+
Permission.ProjectOwner,
|
|
2338
|
+
Permission.ProjectAdmin,
|
|
2339
|
+
Permission.ProjectMember,
|
|
2340
|
+
Permission.CreateProjectStatusPage,
|
|
2341
|
+
],
|
|
2342
|
+
read: [
|
|
2343
|
+
Permission.ProjectOwner,
|
|
2344
|
+
Permission.ProjectAdmin,
|
|
2345
|
+
Permission.ProjectMember,
|
|
2346
|
+
Permission.ReadProjectStatusPage,
|
|
2347
|
+
],
|
|
2348
|
+
update: [
|
|
2349
|
+
Permission.ProjectOwner,
|
|
2350
|
+
Permission.ProjectAdmin,
|
|
2351
|
+
Permission.ProjectMember,
|
|
2352
|
+
Permission.EditProjectStatusPage,
|
|
2353
|
+
],
|
|
2354
|
+
})
|
|
2355
|
+
@TableColumn({
|
|
2356
|
+
isDefaultValueColumn: true,
|
|
2357
|
+
type: TableColumnType.Boolean,
|
|
2358
|
+
title: "Enable Embedded Overall Status Badge",
|
|
2359
|
+
description:
|
|
2360
|
+
"Enable embedded overall status badge that can be displayed on external websites?",
|
|
2361
|
+
defaultValue: false,
|
|
2362
|
+
})
|
|
2363
|
+
@Column({
|
|
2364
|
+
type: ColumnType.Boolean,
|
|
2365
|
+
default: false,
|
|
2366
|
+
nullable: false,
|
|
2367
|
+
})
|
|
2368
|
+
@ColumnBillingAccessControl({
|
|
2369
|
+
read: PlanType.Free,
|
|
2370
|
+
update: PlanType.Growth,
|
|
2371
|
+
create: PlanType.Free,
|
|
2372
|
+
})
|
|
2373
|
+
public enableEmbeddedOverallStatus?: boolean = undefined;
|
|
2374
|
+
|
|
2375
|
+
@ColumnAccessControl({
|
|
2376
|
+
create: [
|
|
2377
|
+
Permission.ProjectOwner,
|
|
2378
|
+
Permission.ProjectAdmin,
|
|
2379
|
+
Permission.ProjectMember,
|
|
2380
|
+
Permission.CreateProjectStatusPage,
|
|
2381
|
+
],
|
|
2382
|
+
read: [
|
|
2383
|
+
Permission.ProjectOwner,
|
|
2384
|
+
Permission.ProjectAdmin,
|
|
2385
|
+
Permission.ProjectMember,
|
|
2386
|
+
Permission.ReadProjectStatusPage,
|
|
2387
|
+
],
|
|
2388
|
+
update: [
|
|
2389
|
+
Permission.ProjectOwner,
|
|
2390
|
+
Permission.ProjectAdmin,
|
|
2391
|
+
Permission.ProjectMember,
|
|
2392
|
+
Permission.EditProjectStatusPage,
|
|
2393
|
+
],
|
|
2394
|
+
})
|
|
2395
|
+
@Index()
|
|
2396
|
+
@TableColumn({
|
|
2397
|
+
type: TableColumnType.ShortText,
|
|
2398
|
+
required: false,
|
|
2399
|
+
title: "Embedded Overall Status Token",
|
|
2400
|
+
description:
|
|
2401
|
+
"Security token required to access the embedded overall status badge. This token must be provided in the URL.",
|
|
2402
|
+
})
|
|
2403
|
+
@Column({
|
|
2404
|
+
type: ColumnType.ShortText,
|
|
2405
|
+
length: ColumnLength.ShortText,
|
|
2406
|
+
nullable: true,
|
|
2407
|
+
})
|
|
2408
|
+
@ColumnBillingAccessControl({
|
|
2409
|
+
read: PlanType.Free,
|
|
2410
|
+
update: PlanType.Growth,
|
|
2411
|
+
create: PlanType.Free,
|
|
2412
|
+
})
|
|
2413
|
+
public embeddedOverallStatusToken?: string = undefined;
|
|
2334
2414
|
}
|
|
@@ -276,6 +276,142 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
276
276
|
},
|
|
277
277
|
);
|
|
278
278
|
|
|
279
|
+
// embedded overall status badge api
|
|
280
|
+
this.router.get(
|
|
281
|
+
`${new this.entityType()
|
|
282
|
+
.getCrudApiPath()
|
|
283
|
+
?.toString()}/badge/:statusPageId`,
|
|
284
|
+
async (req: ExpressRequest, res: ExpressResponse) => {
|
|
285
|
+
try {
|
|
286
|
+
const statusPageId: ObjectID = new ObjectID(
|
|
287
|
+
req.params["statusPageId"] as string,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const token: string = req.query["token"] as string;
|
|
291
|
+
|
|
292
|
+
if (!token) {
|
|
293
|
+
return res.status(400).send("Token is required");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Fetch status page with security token
|
|
297
|
+
const statusPage: StatusPage | null =
|
|
298
|
+
await StatusPageService.findOneBy({
|
|
299
|
+
query: {
|
|
300
|
+
_id: statusPageId,
|
|
301
|
+
enableEmbeddedOverallStatus: true,
|
|
302
|
+
embeddedOverallStatusToken: token,
|
|
303
|
+
},
|
|
304
|
+
select: {
|
|
305
|
+
_id: true,
|
|
306
|
+
projectId: true,
|
|
307
|
+
downtimeMonitorStatuses: {
|
|
308
|
+
_id: true,
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
props: {
|
|
312
|
+
isRoot: true,
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
if (!statusPage) {
|
|
317
|
+
return res.status(404).send("Status badge not found or disabled");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Get status page resources and current statuses
|
|
321
|
+
const statusPageResources: Array<StatusPageResource> =
|
|
322
|
+
await StatusPageResourceService.findBy({
|
|
323
|
+
query: {
|
|
324
|
+
statusPageId: statusPageId,
|
|
325
|
+
},
|
|
326
|
+
select: {
|
|
327
|
+
_id: true,
|
|
328
|
+
monitor: {
|
|
329
|
+
_id: true,
|
|
330
|
+
currentMonitorStatusId: true,
|
|
331
|
+
},
|
|
332
|
+
monitorGroupId: true,
|
|
333
|
+
},
|
|
334
|
+
limit: LIMIT_PER_PROJECT,
|
|
335
|
+
skip: 0,
|
|
336
|
+
props: {
|
|
337
|
+
isRoot: true,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// Get monitor statuses
|
|
342
|
+
const monitorStatuses: Array<MonitorStatus> =
|
|
343
|
+
await MonitorStatusService.findBy({
|
|
344
|
+
query: {
|
|
345
|
+
projectId: statusPage.projectId!,
|
|
346
|
+
},
|
|
347
|
+
select: {
|
|
348
|
+
_id: true,
|
|
349
|
+
name: true,
|
|
350
|
+
color: true,
|
|
351
|
+
priority: true,
|
|
352
|
+
isOperationalState: true,
|
|
353
|
+
},
|
|
354
|
+
sort: {
|
|
355
|
+
priority: SortOrder.Ascending,
|
|
356
|
+
},
|
|
357
|
+
skip: 0,
|
|
358
|
+
limit: LIMIT_PER_PROJECT,
|
|
359
|
+
props: {
|
|
360
|
+
isRoot: true,
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// Get monitor group current statuses
|
|
365
|
+
const monitorGroupCurrentStatuses: Dictionary<ObjectID> =
|
|
366
|
+
await StatusPageService.getMonitorGroupCurrentStatuses({
|
|
367
|
+
statusPageResources,
|
|
368
|
+
monitorStatuses,
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Calculate overall status
|
|
372
|
+
const overallStatus: MonitorStatus | null =
|
|
373
|
+
StatusPageService.getOverallMonitorStatus({
|
|
374
|
+
statusPageResources,
|
|
375
|
+
monitorStatuses,
|
|
376
|
+
monitorGroupCurrentStatuses,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Generate SVG badge
|
|
380
|
+
const statusName: string = overallStatus?.name || "Unknown";
|
|
381
|
+
const statusColor: string =
|
|
382
|
+
overallStatus?.color?.toString() || "#808080";
|
|
383
|
+
|
|
384
|
+
const svg: string = `<svg xmlns="http://www.w3.org/2000/svg" width="150" height="20">
|
|
385
|
+
<linearGradient id="b" x2="0" y2="100%">
|
|
386
|
+
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
|
387
|
+
<stop offset="1" stop-opacity=".1"/>
|
|
388
|
+
</linearGradient>
|
|
389
|
+
<mask id="a">
|
|
390
|
+
<rect width="150" height="20" rx="3" fill="#fff"/>
|
|
391
|
+
</mask>
|
|
392
|
+
<g mask="url(#a)">
|
|
393
|
+
<path fill="#555" d="M0 0h50v20H0z"/>
|
|
394
|
+
<path fill="${statusColor}" d="M50 0h100v20H50z"/>
|
|
395
|
+
<path fill="url(#b)" d="M0 0h150v20H0z"/>
|
|
396
|
+
</g>
|
|
397
|
+
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
|
398
|
+
<text x="25" y="15" fill="#010101" fill-opacity=".3">status</text>
|
|
399
|
+
<text x="25" y="14">status</text>
|
|
400
|
+
<text x="100" y="15" fill="#010101" fill-opacity=".3">${statusName}</text>
|
|
401
|
+
<text x="100" y="14">${statusName}</text>
|
|
402
|
+
</g>
|
|
403
|
+
</svg>`;
|
|
404
|
+
|
|
405
|
+
res.setHeader("Content-Type", "image/svg+xml");
|
|
406
|
+
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
407
|
+
return res.send(svg);
|
|
408
|
+
} catch (err) {
|
|
409
|
+
logger.error(err);
|
|
410
|
+
return res.status(500).send("Internal Server Error");
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
);
|
|
414
|
+
|
|
279
415
|
// confirm subscription api
|
|
280
416
|
this.router.get(
|
|
281
417
|
`${new this.entityType()
|
|
@@ -1392,11 +1528,11 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1392
1528
|
});
|
|
1393
1529
|
|
|
1394
1530
|
const overallStatus: MonitorStatus | null =
|
|
1395
|
-
|
|
1531
|
+
StatusPageService.getOverallMonitorStatus({
|
|
1396
1532
|
statusPageResources,
|
|
1397
1533
|
monitorStatuses,
|
|
1398
1534
|
monitorGroupCurrentStatuses,
|
|
1399
|
-
);
|
|
1535
|
+
});
|
|
1400
1536
|
|
|
1401
1537
|
const response: JSONObject = {
|
|
1402
1538
|
overallStatus: overallStatus
|
|
@@ -3099,56 +3235,6 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
3099
3235
|
return response;
|
|
3100
3236
|
}
|
|
3101
3237
|
|
|
3102
|
-
public getOverallMonitorStatus(
|
|
3103
|
-
statusPageResources: Array<StatusPageResource>,
|
|
3104
|
-
monitorStatuses: Array<MonitorStatus>,
|
|
3105
|
-
monitorGroupCurrentStatuses: Dictionary<ObjectID>,
|
|
3106
|
-
): MonitorStatus | null {
|
|
3107
|
-
let currentStatus: MonitorStatus | null =
|
|
3108
|
-
monitorStatuses.length > 0 && monitorStatuses[0]
|
|
3109
|
-
? monitorStatuses[0]
|
|
3110
|
-
: null;
|
|
3111
|
-
|
|
3112
|
-
const dict: Dictionary<number> = {};
|
|
3113
|
-
|
|
3114
|
-
for (const resource of statusPageResources) {
|
|
3115
|
-
if (resource.monitor?.currentMonitorStatusId) {
|
|
3116
|
-
if (
|
|
3117
|
-
!Object.keys(dict).includes(
|
|
3118
|
-
resource.monitor?.currentMonitorStatusId.toString() || "",
|
|
3119
|
-
)
|
|
3120
|
-
) {
|
|
3121
|
-
dict[resource.monitor?.currentMonitorStatusId?.toString()] = 1;
|
|
3122
|
-
} else {
|
|
3123
|
-
dict[resource.monitor!.currentMonitorStatusId!.toString()]!++;
|
|
3124
|
-
}
|
|
3125
|
-
}
|
|
3126
|
-
}
|
|
3127
|
-
|
|
3128
|
-
// check status of monitor groups.
|
|
3129
|
-
|
|
3130
|
-
for (const groupId in monitorGroupCurrentStatuses) {
|
|
3131
|
-
const statusId: ObjectID | undefined =
|
|
3132
|
-
monitorGroupCurrentStatuses[groupId];
|
|
3133
|
-
|
|
3134
|
-
if (statusId) {
|
|
3135
|
-
if (!Object.keys(dict).includes(statusId.toString() || "")) {
|
|
3136
|
-
dict[statusId.toString()] = 1;
|
|
3137
|
-
} else {
|
|
3138
|
-
dict[statusId.toString()]!++;
|
|
3139
|
-
}
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
|
|
3143
|
-
for (const monitorStatus of monitorStatuses) {
|
|
3144
|
-
if (monitorStatus._id && dict[monitorStatus._id]) {
|
|
3145
|
-
currentStatus = monitorStatus;
|
|
3146
|
-
}
|
|
3147
|
-
}
|
|
3148
|
-
|
|
3149
|
-
return currentStatus;
|
|
3150
|
-
}
|
|
3151
|
-
|
|
3152
3238
|
@CaptureSpan()
|
|
3153
3239
|
public async getStatusPageResourcesAndTimelines(data: {
|
|
3154
3240
|
statusPageId: ObjectID;
|
|
@@ -23,6 +23,25 @@ export const getAllEnvVars: () => JSONObject = (): JSONObject => {
|
|
|
23
23
|
return process.env;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
const parsePositiveNumberFromEnv: (
|
|
27
|
+
envKey: string,
|
|
28
|
+
fallback: number,
|
|
29
|
+
) => number = (envKey: string, fallback: number): number => {
|
|
30
|
+
const rawValue: string | undefined = process.env[envKey];
|
|
31
|
+
|
|
32
|
+
if (!rawValue) {
|
|
33
|
+
return fallback;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const parsedValue: number = parseFloat(rawValue);
|
|
37
|
+
|
|
38
|
+
if (!Number.isFinite(parsedValue) || parsedValue <= 0) {
|
|
39
|
+
return fallback;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return parsedValue;
|
|
43
|
+
};
|
|
44
|
+
|
|
26
45
|
export const IsBillingEnabled: boolean = BillingConfig.IsBillingEnabled;
|
|
27
46
|
export const BillingPublicKey: string = BillingConfig.BillingPublicKey;
|
|
28
47
|
export const BillingPrivateKey: string = BillingConfig.BillingPrivateKey;
|
|
@@ -346,6 +365,21 @@ export const DocsClientUrl: URL = new URL(
|
|
|
346
365
|
export const DisableTelemetry: boolean =
|
|
347
366
|
process.env["DISABLE_TELEMETRY"] === "true";
|
|
348
367
|
|
|
368
|
+
export const AverageSpanRowSizeInBytes: number = parsePositiveNumberFromEnv(
|
|
369
|
+
"AVERAGE_SPAN_ROW_SIZE_IN_BYTES",
|
|
370
|
+
1024,
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
export const AverageLogRowSizeInBytes: number = parsePositiveNumberFromEnv(
|
|
374
|
+
"AVERAGE_LOG_ROW_SIZE_IN_BYTES",
|
|
375
|
+
1024,
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
export const AverageMetricRowSizeInBytes: number = parsePositiveNumberFromEnv(
|
|
379
|
+
"AVERAGE_METRIC_ROW_SIZE_IN_BYTES",
|
|
380
|
+
1024,
|
|
381
|
+
);
|
|
382
|
+
|
|
349
383
|
export const SlackAppClientId: string | null =
|
|
350
384
|
process.env["SLACK_APP_CLIENT_ID"] || null;
|
|
351
385
|
export const SlackAppClientSecret: string | null =
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1761232578396 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1761232578396";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "StatusPage" ADD "enableEmbeddedOverallStatus" boolean NOT NULL DEFAULT false`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "StatusPage" ADD "embeddedOverallStatusToken" character varying(100)`,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`CREATE INDEX "IDX_350d2250fb17e0dc10663de72a" ON "StatusPage" ("embeddedOverallStatusToken") `,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`DROP INDEX "public"."IDX_350d2250fb17e0dc10663de72a"`,
|
|
21
|
+
);
|
|
22
|
+
await queryRunner.query(
|
|
23
|
+
`ALTER TABLE "StatusPage" DROP COLUMN "embeddedOverallStatusToken"`,
|
|
24
|
+
);
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`ALTER TABLE "StatusPage" DROP COLUMN "enableEmbeddedOverallStatus"`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -177,6 +177,7 @@ import { RenameUserTwoFactorAuthToUserTotpAuth1759234532998 } from "./1759234532
|
|
|
177
177
|
import { MigrationName1759943124812 } from "./1759943124812-MigrationName";
|
|
178
178
|
import { MigrationName1760345757975 } from "./1760345757975-MigrationName";
|
|
179
179
|
import { MigrationName1760357680881 } from "./1760357680881-MigrationName";
|
|
180
|
+
import { MigrationName1761232578396 } from "./1761232578396-MigrationName";
|
|
180
181
|
|
|
181
182
|
export default [
|
|
182
183
|
InitialMigration,
|
|
@@ -358,4 +359,5 @@ export default [
|
|
|
358
359
|
MigrationName1759943124812,
|
|
359
360
|
MigrationName1760345757975,
|
|
360
361
|
MigrationName1760357680881,
|
|
362
|
+
MigrationName1761232578396,
|
|
361
363
|
];
|
|
@@ -4,11 +4,6 @@ import ObjectID from "../../Types/ObjectID";
|
|
|
4
4
|
import Metric, {
|
|
5
5
|
AggregationTemporality,
|
|
6
6
|
} from "../../Models/AnalyticsModels/Metric";
|
|
7
|
-
import Dictionary from "../../Types/Dictionary";
|
|
8
|
-
import ProductType from "../../Types/MeteredPlan/ProductType";
|
|
9
|
-
import { IsBillingEnabled } from "../../Server/EnvironmentConfig";
|
|
10
|
-
import TelemetryUsageBillingService from "../../Server/Services/TelemetryUsageBillingService";
|
|
11
|
-
import logger from "../../Server/Utils/Logger";
|
|
12
7
|
import TelemetryService from "../../Models/DatabaseModels/TelemetryService";
|
|
13
8
|
import TelemetryServiceService from "../../Server/Services/TelemetryServiceService";
|
|
14
9
|
import { DEFAULT_RETENTION_IN_DAYS } from "../../Models/DatabaseModels/TelemetryUsageBilling";
|
|
@@ -20,10 +15,9 @@ export enum OtelAggregationTemporality {
|
|
|
20
15
|
Delta = "AGGREGATION_TEMPORALITY_DELTA",
|
|
21
16
|
}
|
|
22
17
|
|
|
23
|
-
export interface
|
|
18
|
+
export interface TelemetryServiceMetadata {
|
|
24
19
|
serviceName: string;
|
|
25
20
|
serviceId: ObjectID;
|
|
26
|
-
dataIngestedInGB: number;
|
|
27
21
|
dataRententionInDays: number;
|
|
28
22
|
}
|
|
29
23
|
|
|
@@ -80,38 +74,6 @@ export default class OTelIngestService {
|
|
|
80
74
|
service.retainTelemetryDataForDays || DEFAULT_RETENTION_IN_DAYS,
|
|
81
75
|
};
|
|
82
76
|
}
|
|
83
|
-
|
|
84
|
-
@CaptureSpan()
|
|
85
|
-
public static async recordDataIngestedUsgaeBilling(data: {
|
|
86
|
-
services: Dictionary<TelemetryServiceDataIngested>;
|
|
87
|
-
projectId: ObjectID;
|
|
88
|
-
productType: ProductType;
|
|
89
|
-
}): Promise<void> {
|
|
90
|
-
if (!IsBillingEnabled) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
for (const serviceName in data.services) {
|
|
95
|
-
const serviceData: TelemetryServiceDataIngested | undefined =
|
|
96
|
-
data.services[serviceName];
|
|
97
|
-
|
|
98
|
-
if (!serviceData) {
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
TelemetryUsageBillingService.updateUsageBilling({
|
|
103
|
-
projectId: data.projectId,
|
|
104
|
-
productType: data.productType,
|
|
105
|
-
dataIngestedInGB: serviceData.dataIngestedInGB || 0,
|
|
106
|
-
telemetryServiceId: serviceData.serviceId,
|
|
107
|
-
retentionInDays: serviceData.dataRententionInDays,
|
|
108
|
-
}).catch((err: Error) => {
|
|
109
|
-
logger.error("Failed to update usage billing for OTel");
|
|
110
|
-
logger.error(err);
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
77
|
@CaptureSpan()
|
|
116
78
|
public static getMetricFromDatapoint(data: {
|
|
117
79
|
dbMetric: Metric;
|
|
@@ -1150,5 +1150,122 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
1150
1150
|
},
|
|
1151
1151
|
);
|
|
1152
1152
|
}
|
|
1153
|
+
|
|
1154
|
+
@CaptureSpan()
|
|
1155
|
+
public async getMonitorGroupCurrentStatuses(data: {
|
|
1156
|
+
statusPageResources: Array<StatusPageResource>;
|
|
1157
|
+
monitorStatuses: Array<MonitorStatus>;
|
|
1158
|
+
}): Promise<Dictionary<ObjectID>> {
|
|
1159
|
+
const monitorGroupCurrentStatuses: Dictionary<ObjectID> = {};
|
|
1160
|
+
|
|
1161
|
+
for (const resource of data.statusPageResources) {
|
|
1162
|
+
if (resource.monitorGroupId) {
|
|
1163
|
+
const monitorGroupResources: Array<MonitorGroupResource> =
|
|
1164
|
+
await MonitorGroupResourceService.findBy({
|
|
1165
|
+
query: {
|
|
1166
|
+
monitorGroupId: resource.monitorGroupId,
|
|
1167
|
+
},
|
|
1168
|
+
select: {
|
|
1169
|
+
monitorId: true,
|
|
1170
|
+
monitor: {
|
|
1171
|
+
currentMonitorStatusId: true,
|
|
1172
|
+
},
|
|
1173
|
+
},
|
|
1174
|
+
skip: 0,
|
|
1175
|
+
limit: LIMIT_PER_PROJECT,
|
|
1176
|
+
props: {
|
|
1177
|
+
isRoot: true,
|
|
1178
|
+
},
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
const statuses: Array<ObjectID> = monitorGroupResources
|
|
1182
|
+
.filter((item: MonitorGroupResource) => {
|
|
1183
|
+
return (
|
|
1184
|
+
item.monitor &&
|
|
1185
|
+
item.monitor.currentMonitorStatusId &&
|
|
1186
|
+
item.monitorId
|
|
1187
|
+
);
|
|
1188
|
+
})
|
|
1189
|
+
.map((item: MonitorGroupResource) => {
|
|
1190
|
+
return item.monitor!.currentMonitorStatusId!;
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
let worstStatus: MonitorStatus | null = null;
|
|
1194
|
+
|
|
1195
|
+
for (const statusId of statuses) {
|
|
1196
|
+
const status: MonitorStatus | undefined = data.monitorStatuses.find(
|
|
1197
|
+
(status: MonitorStatus) => {
|
|
1198
|
+
return status._id?.toString() === statusId.toString();
|
|
1199
|
+
},
|
|
1200
|
+
);
|
|
1201
|
+
|
|
1202
|
+
if (
|
|
1203
|
+
status &&
|
|
1204
|
+
(!worstStatus || status.priority! < worstStatus.priority!)
|
|
1205
|
+
) {
|
|
1206
|
+
worstStatus = status;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
if (worstStatus && worstStatus._id) {
|
|
1211
|
+
monitorGroupCurrentStatuses[resource.monitorGroupId.toString()] =
|
|
1212
|
+
new ObjectID(worstStatus._id);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
return monitorGroupCurrentStatuses;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
@CaptureSpan()
|
|
1221
|
+
public getOverallMonitorStatus(data: {
|
|
1222
|
+
statusPageResources: Array<StatusPageResource>;
|
|
1223
|
+
monitorStatuses: Array<MonitorStatus>;
|
|
1224
|
+
monitorGroupCurrentStatuses: Dictionary<ObjectID>;
|
|
1225
|
+
}): MonitorStatus | null {
|
|
1226
|
+
let currentStatus: MonitorStatus | null =
|
|
1227
|
+
data.monitorStatuses.length > 0 && data.monitorStatuses[0]
|
|
1228
|
+
? data.monitorStatuses[0]
|
|
1229
|
+
: null;
|
|
1230
|
+
|
|
1231
|
+
const dict: Dictionary<number> = {};
|
|
1232
|
+
|
|
1233
|
+
for (const resource of data.statusPageResources) {
|
|
1234
|
+
if (resource.monitor?.currentMonitorStatusId) {
|
|
1235
|
+
if (
|
|
1236
|
+
!Object.keys(dict).includes(
|
|
1237
|
+
resource.monitor?.currentMonitorStatusId.toString() || "",
|
|
1238
|
+
)
|
|
1239
|
+
) {
|
|
1240
|
+
dict[resource.monitor?.currentMonitorStatusId?.toString()] = 1;
|
|
1241
|
+
} else {
|
|
1242
|
+
dict[resource.monitor!.currentMonitorStatusId!.toString()]!++;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// check status of monitor groups.
|
|
1248
|
+
|
|
1249
|
+
for (const groupId in data.monitorGroupCurrentStatuses) {
|
|
1250
|
+
const statusId: ObjectID | undefined =
|
|
1251
|
+
data.monitorGroupCurrentStatuses[groupId];
|
|
1252
|
+
|
|
1253
|
+
if (statusId) {
|
|
1254
|
+
if (!Object.keys(dict).includes(statusId.toString() || "")) {
|
|
1255
|
+
dict[statusId.toString()] = 1;
|
|
1256
|
+
} else {
|
|
1257
|
+
dict[statusId.toString()]!++;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
for (const monitorStatus of data.monitorStatuses) {
|
|
1263
|
+
if (monitorStatus._id && dict[monitorStatus._id]) {
|
|
1264
|
+
currentStatus = monitorStatus;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
return currentStatus;
|
|
1269
|
+
}
|
|
1153
1270
|
}
|
|
1154
1271
|
export default new Service();
|