@oneuptime/common 12.0.2 → 12.0.3
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/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +39 -0
- package/Server/EnvironmentConfig.ts +31 -1
- package/Server/Infrastructure/GlobalCache.ts +115 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/CephClusterService.ts +26 -86
- package/Server/Services/CloudResourceService.ts +23 -83
- package/Server/Services/DatabaseService.ts +111 -0
- package/Server/Services/DockerHostService.ts +19 -79
- package/Server/Services/DockerSwarmClusterService.ts +27 -88
- package/Server/Services/HostService.ts +46 -99
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTFleetService.ts +19 -79
- package/Server/Services/KubernetesClusterService.ts +17 -78
- package/Server/Services/LabelService.ts +57 -10
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +89 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/PodmanHostService.ts +19 -79
- package/Server/Services/ProxmoxClusterService.ts +23 -83
- package/Server/Services/RumApplicationService.ts +19 -80
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +24 -85
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Monitor/MonitorResource.ts +63 -36
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
- package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
- package/Server/Utils/SingleFlight.ts +83 -0
- package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
- package/Server/Utils/Telemetry/Telemetry.ts +351 -122
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
- package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
- package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
- package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +226 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
- package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
- package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
- package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
- package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
- package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
- package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
- package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
- package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
- package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
- package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
- package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
- package/Tests/Types/Decimal.test.ts +37 -0
- package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Types/Dashboard/DashboardComponentType.ts +1 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
- package/Types/Dashboard/DashboardTemplates.ts +397 -0
- package/Types/Database/BigIntColumnTransformer.ts +74 -0
- package/Types/Database/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Decimal.ts +16 -4
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +11 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +39 -0
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +27 -1
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +91 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.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/AlertStateTimelineService.js +19 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +26 -76
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -73
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +86 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -69
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -102
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +19 -69
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +42 -7
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MetricTypeService.js +74 -0
- package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +81 -9
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -69
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -70
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +236 -61
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
- package/build/dist/Server/Utils/SingleFlight.js +76 -0
- package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
- package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
- package/build/dist/Types/Database/DatabaseProperty.js +25 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/NumericColumnValue.js +97 -0
- package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
- package/build/dist/Types/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Port.js +15 -5
- package/build/dist/Types/Port.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import MonitorGroup from "./MonitorGroup";
|
|
|
3
3
|
import Project from "./Project";
|
|
4
4
|
import StatusPage from "./StatusPage";
|
|
5
5
|
import StatusPageGroup from "./StatusPageGroup";
|
|
6
|
+
import StatusPageMonitorRule from "./StatusPageMonitorRule";
|
|
6
7
|
import User from "./User";
|
|
7
8
|
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
|
8
9
|
import Route from "../../Types/API/Route";
|
|
@@ -520,6 +521,78 @@ export default class StatusPageResource extends BaseModel {
|
|
|
520
521
|
})
|
|
521
522
|
public statusPageGroupId?: ObjectID = undefined;
|
|
522
523
|
|
|
524
|
+
/*
|
|
525
|
+
* Set only on resources a StatusPageMonitorRule created. It is what lets the
|
|
526
|
+
* rule engine undo its own work without ever touching a resource a human
|
|
527
|
+
* added by hand, so it is deliberately not writable through the API - the
|
|
528
|
+
* engine sets it as root.
|
|
529
|
+
*/
|
|
530
|
+
@ColumnAccessControl({
|
|
531
|
+
create: [],
|
|
532
|
+
read: [
|
|
533
|
+
Permission.ProjectOwner,
|
|
534
|
+
Permission.ProjectAdmin,
|
|
535
|
+
Permission.ProjectMember,
|
|
536
|
+
Permission.Viewer,
|
|
537
|
+
Permission.StatusPageAdmin,
|
|
538
|
+
Permission.StatusPageMember,
|
|
539
|
+
Permission.StatusPageViewer,
|
|
540
|
+
Permission.ReadStatusPageResource,
|
|
541
|
+
],
|
|
542
|
+
update: [],
|
|
543
|
+
})
|
|
544
|
+
@TableColumn({
|
|
545
|
+
manyToOneRelationColumn: "statusPageMonitorRuleId",
|
|
546
|
+
type: TableColumnType.Entity,
|
|
547
|
+
modelType: StatusPageMonitorRule,
|
|
548
|
+
title: "Status Page Monitor Rule",
|
|
549
|
+
description:
|
|
550
|
+
"Rule that added this resource to the status page, if it was added by a rule instead of by hand",
|
|
551
|
+
})
|
|
552
|
+
@ManyToOne(
|
|
553
|
+
() => {
|
|
554
|
+
return StatusPageMonitorRule;
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
eager: false,
|
|
558
|
+
nullable: true,
|
|
559
|
+
onDelete: "CASCADE",
|
|
560
|
+
orphanedRowAction: "nullify",
|
|
561
|
+
},
|
|
562
|
+
)
|
|
563
|
+
@JoinColumn({ name: "statusPageMonitorRuleId" })
|
|
564
|
+
public statusPageMonitorRule?: StatusPageMonitorRule = undefined;
|
|
565
|
+
|
|
566
|
+
@ColumnAccessControl({
|
|
567
|
+
create: [],
|
|
568
|
+
read: [
|
|
569
|
+
Permission.ProjectOwner,
|
|
570
|
+
Permission.ProjectAdmin,
|
|
571
|
+
Permission.ProjectMember,
|
|
572
|
+
Permission.Viewer,
|
|
573
|
+
Permission.StatusPageAdmin,
|
|
574
|
+
Permission.StatusPageMember,
|
|
575
|
+
Permission.StatusPageViewer,
|
|
576
|
+
Permission.ReadStatusPageResource,
|
|
577
|
+
],
|
|
578
|
+
update: [],
|
|
579
|
+
})
|
|
580
|
+
@Index()
|
|
581
|
+
@TableColumn({
|
|
582
|
+
type: TableColumnType.ObjectID,
|
|
583
|
+
required: false,
|
|
584
|
+
title: "Status Page Monitor Rule ID",
|
|
585
|
+
description:
|
|
586
|
+
"ID of the rule that added this resource, if it was added by a rule instead of by hand",
|
|
587
|
+
example: "d0e1f2a3-b4c5-4678-9abc-def012345678",
|
|
588
|
+
})
|
|
589
|
+
@Column({
|
|
590
|
+
type: ColumnType.ObjectID,
|
|
591
|
+
nullable: true,
|
|
592
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
593
|
+
})
|
|
594
|
+
public statusPageMonitorRuleId?: ObjectID = undefined;
|
|
595
|
+
|
|
523
596
|
@ColumnAccessControl({
|
|
524
597
|
create: [
|
|
525
598
|
Permission.ProjectOwner,
|
|
@@ -7,6 +7,7 @@ import TableAccessControl from "../../Types/Database/AccessControl/TableAccessCo
|
|
|
7
7
|
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
|
|
8
8
|
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
9
9
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
10
|
+
import { getBigIntDatabaseTransformer } from "../../Types/Database/BigIntColumnTransformer";
|
|
10
11
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
11
12
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
12
13
|
import TableColumn from "../../Types/Database/TableColumn";
|
|
@@ -436,26 +437,14 @@ export default class TraceDropFilter extends BaseModel {
|
|
|
436
437
|
type: ColumnType.BigPositiveNumber,
|
|
437
438
|
nullable: false,
|
|
438
439
|
default: 0,
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
* Postgres bigint comes back from the driver as a string. The UI
|
|
448
|
-
* renders this as a number, so normalize here rather than making
|
|
449
|
-
* every reader remember.
|
|
450
|
-
*/
|
|
451
|
-
from: (value: string | null | undefined): number | null => {
|
|
452
|
-
if (value === null || value === undefined) {
|
|
453
|
-
return null;
|
|
454
|
-
}
|
|
455
|
-
const parsed: number = parseInt(value, 10);
|
|
456
|
-
return isNaN(parsed) ? null : parsed;
|
|
457
|
-
},
|
|
458
|
-
},
|
|
440
|
+
/*
|
|
441
|
+
* Shared transformer, not an inline one: it has to preserve `undefined`
|
|
442
|
+
* so this NOT NULL column's DEFAULT 0 is what a create actually gets.
|
|
443
|
+
* An inline transformer that mapped `undefined` to `null` made every
|
|
444
|
+
* drop-filter insert fail the not-null constraint. See
|
|
445
|
+
* Types/Database/BigIntColumnTransformer.
|
|
446
|
+
*/
|
|
447
|
+
transformer: getBigIntDatabaseTransformer(),
|
|
459
448
|
})
|
|
460
449
|
public droppedCount?: number = undefined;
|
|
461
450
|
|
package/Server/API/BaseAPI.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
LIMIT_PER_PROJECT,
|
|
23
23
|
} from "../../Types/Database/LimitMax";
|
|
24
24
|
import PartialEntity from "../../Types/Database/PartialEntity";
|
|
25
|
+
import { coerceNumericColumnsInJSON } from "../../Types/Database/NumericColumnValue";
|
|
25
26
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
26
27
|
import BadRequestException from "../../Types/Exception/BadRequestException";
|
|
27
28
|
import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
|
|
@@ -418,8 +419,18 @@ export default class BaseAPI<
|
|
|
418
419
|
);
|
|
419
420
|
}
|
|
420
421
|
|
|
421
|
-
|
|
422
|
-
|
|
422
|
+
/*
|
|
423
|
+
* Coerced the same way createItem's BaseModel.fromJSON coerces, so a
|
|
424
|
+
* PATCH and a POST agree on what a number column holds. The dashboard's
|
|
425
|
+
* number fields are `<input type="number">`, whose value is a string,
|
|
426
|
+
* and the save-time hooks read these values before Postgres would
|
|
427
|
+
* coerce them — a sample percentage of "10" was rejected as "not a
|
|
428
|
+
* number" on a form that had one filled in.
|
|
429
|
+
* github.com/OneUptime/oneuptime/issues/3027
|
|
430
|
+
*/
|
|
431
|
+
const item: PartialEntity<TBaseModel> = coerceNumericColumnsInJSON(
|
|
432
|
+
JSONFunctions.deserialize(dataInBody as JSONObject),
|
|
433
|
+
new this.entityType(),
|
|
423
434
|
) as PartialEntity<TBaseModel>;
|
|
424
435
|
|
|
425
436
|
delete (item as any)["_id"];
|
|
@@ -50,6 +50,7 @@ import PodmanResource from "../../Models/DatabaseModels/PodmanResource";
|
|
|
50
50
|
import ProxmoxResource from "../../Models/DatabaseModels/ProxmoxResource";
|
|
51
51
|
import CephResource from "../../Models/DatabaseModels/CephResource";
|
|
52
52
|
import DockerSwarmResource from "../../Models/DatabaseModels/DockerSwarmResource";
|
|
53
|
+
import NetworkSite from "../../Models/DatabaseModels/NetworkSite";
|
|
53
54
|
import Span from "../../Models/AnalyticsModels/Span";
|
|
54
55
|
import Log from "../../Models/AnalyticsModels/Log";
|
|
55
56
|
import IncidentService from "../Services/IncidentService";
|
|
@@ -64,6 +65,7 @@ import PodmanResourceService from "../Services/PodmanResourceService";
|
|
|
64
65
|
import ProxmoxResourceService from "../Services/ProxmoxResourceService";
|
|
65
66
|
import CephResourceService from "../Services/CephResourceService";
|
|
66
67
|
import DockerSwarmResourceService from "../Services/DockerSwarmResourceService";
|
|
68
|
+
import NetworkSiteService from "../Services/NetworkSiteService";
|
|
67
69
|
import SpanService from "../Services/SpanService";
|
|
68
70
|
import LogService from "../Services/LogService";
|
|
69
71
|
import DashboardComponentType from "../../Types/Dashboard/DashboardComponentType";
|
|
@@ -165,6 +167,43 @@ const PUBLIC_DASHBOARD_RESOURCES: Record<
|
|
|
165
167
|
currentMonitorStatus: { name: true, color: true },
|
|
166
168
|
},
|
|
167
169
|
},
|
|
170
|
+
"network-site": {
|
|
171
|
+
modelType: NetworkSite,
|
|
172
|
+
service: NetworkSiteService,
|
|
173
|
+
widgets: {
|
|
174
|
+
[DashboardComponentType.NetworkMap]: null,
|
|
175
|
+
},
|
|
176
|
+
/*
|
|
177
|
+
* The map filters by site type and by whether the rolled-up status is
|
|
178
|
+
* operational — nothing else. Notably NOT `parentSiteId` or
|
|
179
|
+
* `materializedPath`: the widget draws a flat set of pins rather than
|
|
180
|
+
* walking a hierarchy, so exposing the tree shape here would hand an
|
|
181
|
+
* anonymous viewer a structure the map never shows them.
|
|
182
|
+
*/
|
|
183
|
+
allowedQueryKeys: [
|
|
184
|
+
"networkSiteTypeId",
|
|
185
|
+
"currentMonitorStatus",
|
|
186
|
+
"currentMonitorStatusId",
|
|
187
|
+
],
|
|
188
|
+
select: {
|
|
189
|
+
_id: true,
|
|
190
|
+
name: true,
|
|
191
|
+
/*
|
|
192
|
+
* The type NAME comes from the networkSiteType relation. NetworkSite
|
|
193
|
+
* .siteType is the deprecated pre-lookup-table string and is dropped
|
|
194
|
+
* in a follow-up PR, so it must not be read from new code.
|
|
195
|
+
*/
|
|
196
|
+
networkSiteType: { name: true },
|
|
197
|
+
latitude: true,
|
|
198
|
+
longitude: true,
|
|
199
|
+
currentMonitorStatus: {
|
|
200
|
+
name: true,
|
|
201
|
+
color: true,
|
|
202
|
+
priority: true,
|
|
203
|
+
isOperationalState: true,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
168
207
|
host: {
|
|
169
208
|
modelType: Host,
|
|
170
209
|
service: HostService,
|
|
@@ -167,13 +167,43 @@ export const PostgresStatementTimeoutMs: number = parseInt(
|
|
|
167
167
|
10,
|
|
168
168
|
);
|
|
169
169
|
|
|
170
|
+
/*
|
|
171
|
+
* Postgres-side lock timeout (ms). Caps how long a statement will WAIT for a
|
|
172
|
+
* row/table lock before giving up — distinct from statement_timeout, which
|
|
173
|
+
* caps total execution.
|
|
174
|
+
*
|
|
175
|
+
* Without it, contention on a hot row degrades into a strictly-ordered queue:
|
|
176
|
+
* every waiter pins a backend for the sum of everyone ahead of it. That is the
|
|
177
|
+
* row-lock convoy that took production down — 892 connections parked on locks,
|
|
178
|
+
* the tail waiting 3.7 hours — and no amount of database capacity changes it,
|
|
179
|
+
* because the queue itself is the failure. A short lock_timeout converts an
|
|
180
|
+
* unbounded wait into a fast, retryable error.
|
|
181
|
+
*
|
|
182
|
+
* Must stay well below statement_timeout so a lock wait surfaces as
|
|
183
|
+
* `lock_not_available` (55P03) rather than a generic statement timeout — the
|
|
184
|
+
* two want very different handling.
|
|
185
|
+
*/
|
|
186
|
+
export const PostgresLockTimeoutMs: number = parseInt(
|
|
187
|
+
process.env["DATABASE_LOCK_TIMEOUT_MS"] || "3000",
|
|
188
|
+
10,
|
|
189
|
+
);
|
|
190
|
+
|
|
170
191
|
/*
|
|
171
192
|
* Node-postgres client-side query timeout (ms). Belt-and-braces for the
|
|
172
193
|
* server-side statement_timeout — fires even if the connection has gone
|
|
173
194
|
* silent or the server-side timeout doesn't kick in.
|
|
195
|
+
*
|
|
196
|
+
* Deliberately LONGER than statement_timeout. These two used to be equal, and
|
|
197
|
+
* because the client timer starts before the packet even reaches the backend,
|
|
198
|
+
* the client always won by a round trip — so the app never observed Postgres's
|
|
199
|
+
* real SQLSTATE and, worse, the client-side timeout only ABANDONS the query:
|
|
200
|
+
* the backend keeps running and keeps its place in the lock queue. Letting the
|
|
201
|
+
* server win means contention is cancelled server-side instead of accumulating
|
|
202
|
+
* invisibly behind a pooler.
|
|
174
203
|
*/
|
|
175
204
|
export const PostgresQueryTimeoutMs: number = parseInt(
|
|
176
|
-
process.env["DATABASE_QUERY_TIMEOUT_MS"] ||
|
|
205
|
+
process.env["DATABASE_QUERY_TIMEOUT_MS"] ||
|
|
206
|
+
String(PostgresStatementTimeoutMs + 5000),
|
|
177
207
|
10,
|
|
178
208
|
);
|
|
179
209
|
|
|
@@ -176,6 +176,121 @@ export default abstract class GlobalCache {
|
|
|
176
176
|
await client.set(`${namespace}-${key}`, value, "EX", expiresInSeconds);
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/*
|
|
180
|
+
* Atomic acquire-once fence: SET ... EX ... NX. Returns true ONLY for the
|
|
181
|
+
* caller that created the key; every concurrent caller gets false until it
|
|
182
|
+
* expires.
|
|
183
|
+
*
|
|
184
|
+
* This exists because `getString()` followed by `setString()` is a
|
|
185
|
+
* check-then-act race, and at ingest concurrency that race is not
|
|
186
|
+
* theoretical — it is the whole problem. When N workers resolve the same
|
|
187
|
+
* row in the same instant they ALL read a miss and they ALL proceed, so a
|
|
188
|
+
* fence meant to admit one writer per window admits N. With 100 worker pods
|
|
189
|
+
* that turned a once-a-minute heartbeat into thousands of simultaneous
|
|
190
|
+
* UPDATEs against the same handful of rows, and the resulting row-lock
|
|
191
|
+
* convoy starved the Postgres connection pool (some statements queued for
|
|
192
|
+
* hours). Redis evaluates SET NX atomically, so exactly one caller wins no
|
|
193
|
+
* matter how many arrive together.
|
|
194
|
+
*
|
|
195
|
+
* Fence keys should carry the TTL jitter from `withJitter()` — see there for
|
|
196
|
+
* why synchronized expiry re-creates the herd this is meant to prevent.
|
|
197
|
+
*/
|
|
198
|
+
@CaptureSpan()
|
|
199
|
+
public static async setStringIfNotExists(
|
|
200
|
+
namespace: string,
|
|
201
|
+
key: string,
|
|
202
|
+
value: string,
|
|
203
|
+
options?: CacheSetOptions,
|
|
204
|
+
): Promise<boolean> {
|
|
205
|
+
const client: ClientType | null = Redis.getClient();
|
|
206
|
+
|
|
207
|
+
if (!client || !Redis.isConnected()) {
|
|
208
|
+
throw new DatabaseNotConnectedException("Cache is not connected");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const expiresInSeconds: number =
|
|
212
|
+
options?.expiresInSeconds ?? OneUptimeDate.getSecondsInDays(30);
|
|
213
|
+
|
|
214
|
+
const result: string | null = await client.set(
|
|
215
|
+
`${namespace}-${key}`,
|
|
216
|
+
value,
|
|
217
|
+
"EX",
|
|
218
|
+
expiresInSeconds,
|
|
219
|
+
"NX",
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
/*
|
|
223
|
+
* ioredis resolves to "OK" when the key was created and to null when it
|
|
224
|
+
* already existed. Anything else (a driver/protocol change) is treated as
|
|
225
|
+
* "did not acquire" — losing the fence is safe, winning it wrongly is not.
|
|
226
|
+
*/
|
|
227
|
+
return result === "OK";
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/*
|
|
231
|
+
* Atomic compare-and-claim: claim the window unless the key ALREADY holds
|
|
232
|
+
* `value`. Returns true only for the caller that claimed it.
|
|
233
|
+
*
|
|
234
|
+
* `setStringIfNotExists` is the right primitive for a presence fence and the
|
|
235
|
+
* wrong one for a fingerprint throttle. Plain NX fails whenever the key
|
|
236
|
+
* exists — including when it holds a STALE fingerprint — so a genuinely
|
|
237
|
+
* changed payload (a new service.version after a deploy, a changed host IP)
|
|
238
|
+
* would be suppressed for the whole window and nobody would persist it. This
|
|
239
|
+
* keeps the bust-on-change behaviour that callers depend on while collapsing
|
|
240
|
+
* the GET-then-SET race into one atomic evaluation.
|
|
241
|
+
*
|
|
242
|
+
* The key is passed as KEYS[1] rather than inlined into the script body so
|
|
243
|
+
* the script stays correct on Redis Cluster, which routes by declared keys.
|
|
244
|
+
*/
|
|
245
|
+
@CaptureSpan()
|
|
246
|
+
public static async setStringIfChanged(
|
|
247
|
+
namespace: string,
|
|
248
|
+
key: string,
|
|
249
|
+
value: string,
|
|
250
|
+
options?: CacheSetOptions,
|
|
251
|
+
): Promise<boolean> {
|
|
252
|
+
const client: ClientType | null = Redis.getClient();
|
|
253
|
+
|
|
254
|
+
if (!client || !Redis.isConnected()) {
|
|
255
|
+
throw new DatabaseNotConnectedException("Cache is not connected");
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const expiresInSeconds: number =
|
|
259
|
+
options?.expiresInSeconds ?? OneUptimeDate.getSecondsInDays(30);
|
|
260
|
+
|
|
261
|
+
const result: unknown = await client.eval(
|
|
262
|
+
"if redis.call('GET', KEYS[1]) == ARGV[1] then return 0 " +
|
|
263
|
+
"else redis.call('SET', KEYS[1], ARGV[1], 'EX', ARGV[2]) return 1 end",
|
|
264
|
+
1,
|
|
265
|
+
`${namespace}-${key}`,
|
|
266
|
+
value,
|
|
267
|
+
String(expiresInSeconds),
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
return result === 1;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/*
|
|
274
|
+
* Spread a fence TTL over [ttl, ttl + 25%].
|
|
275
|
+
*
|
|
276
|
+
* A fixed TTL makes every fence for a fleet of rows expire in lockstep once
|
|
277
|
+
* their writes have been aligned by a common event — a deploy, a worker
|
|
278
|
+
* scale-up, a Redis restart. The window then reopens for thousands of rows
|
|
279
|
+
* in the same second and the herd re-forms on a one-minute period. Jitter
|
|
280
|
+
* breaks that alignment permanently: the fences drift apart after the first
|
|
281
|
+
* window and stay apart.
|
|
282
|
+
*
|
|
283
|
+
* The upper bound stays well inside the 15-minute disconnection sweep, so a
|
|
284
|
+
* jittered heartbeat can never make a live resource look disconnected.
|
|
285
|
+
*/
|
|
286
|
+
public static withJitter(expiresInSeconds: number): number {
|
|
287
|
+
if (expiresInSeconds <= 0) {
|
|
288
|
+
return expiresInSeconds;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return Math.ceil(expiresInSeconds * (1 + Math.random() * 0.25));
|
|
292
|
+
}
|
|
293
|
+
|
|
179
294
|
@CaptureSpan()
|
|
180
295
|
public static async deleteKey(namespace: string, key: string): Promise<void> {
|
|
181
296
|
const client: ClientType | null = Redis.getClient();
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
DatabaseUsername,
|
|
11
11
|
MaxPostgresConnections,
|
|
12
12
|
PostgresConnectionAcquireTimeoutMs,
|
|
13
|
+
PostgresLockTimeoutMs,
|
|
13
14
|
PostgresIdleInTransactionTimeoutMs,
|
|
14
15
|
PostgresIdleSessionTimeoutMs,
|
|
15
16
|
PostgresIdleTimeoutMs,
|
|
@@ -61,6 +62,21 @@ const dataSourceOptions: DataSourceOptions = {
|
|
|
61
62
|
statement_timeout: PostgresStatementTimeoutMs,
|
|
62
63
|
query_timeout: PostgresQueryTimeoutMs,
|
|
63
64
|
idle_in_transaction_session_timeout: PostgresIdleInTransactionTimeoutMs,
|
|
65
|
+
/*
|
|
66
|
+
* Bound how long a statement WAITS for a lock, so contention on a hot row
|
|
67
|
+
* fails fast instead of forming a queue (see PostgresLockTimeoutMs).
|
|
68
|
+
*
|
|
69
|
+
* Excluded on the migration path, and that exclusion is load-bearing:
|
|
70
|
+
* App/Migrate.ts loads these same options and connects DIRECTLY to the
|
|
71
|
+
* backend (bypassing any pooler), where startup parameters really do take
|
|
72
|
+
* effect. A 3s lock_timeout there would abort ACCESS EXCLUSIVE DDL on any
|
|
73
|
+
* table with live traffic — only the two migrations that set their own
|
|
74
|
+
* `SET LOCAL lock_timeout` expect to fail that way; the rest must be free
|
|
75
|
+
* to wait.
|
|
76
|
+
*/
|
|
77
|
+
...(PostgresLockTimeoutMs > 0 && !RunDatabaseMigrationsOnBoot
|
|
78
|
+
? { lock_timeout: PostgresLockTimeoutMs }
|
|
79
|
+
: {}),
|
|
64
80
|
/*
|
|
65
81
|
* Detect dead TCP peers (ungraceful client exit / network partition) so
|
|
66
82
|
* orphaned server-side connections get torn down instead of lingering
|
package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddStatusPageMonitorRule1786005052769
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name = "AddStatusPageMonitorRule1786005052769";
|
|
7
|
+
|
|
8
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(
|
|
10
|
+
`CREATE TABLE "StatusPageMonitorRule" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "statusPageId" uuid NOT NULL, "name" character varying(100) NOT NULL, "description" character varying(500), "isEnabled" boolean NOT NULL DEFAULT true, "monitorNamePattern" character varying(500), "monitorDescriptionPattern" character varying(500), "statusPageGroupId" uuid, "showCurrentStatus" boolean NOT NULL DEFAULT true, "showUptimePercent" boolean NOT NULL DEFAULT true, "uptimePercentPrecision" character varying, "showStatusHistoryChart" boolean NOT NULL DEFAULT true, "createdByUserId" uuid, "deletedByUserId" uuid, CONSTRAINT "PK_f8d44792805d19b9a80921b6aa6" PRIMARY KEY ("_id"))`,
|
|
11
|
+
);
|
|
12
|
+
await queryRunner.query(
|
|
13
|
+
`CREATE INDEX "IDX_1dfd95ad32f7b687ef4d7ba591" ON "StatusPageMonitorRule" ("projectId") `,
|
|
14
|
+
);
|
|
15
|
+
await queryRunner.query(
|
|
16
|
+
`CREATE INDEX "IDX_473317deaa55a8305a047e4098" ON "StatusPageMonitorRule" ("statusPageId") `,
|
|
17
|
+
);
|
|
18
|
+
await queryRunner.query(
|
|
19
|
+
`CREATE INDEX "IDX_61196848685b7e7f5a6a54278b" ON "StatusPageMonitorRule" ("name") `,
|
|
20
|
+
);
|
|
21
|
+
await queryRunner.query(
|
|
22
|
+
`CREATE INDEX "IDX_71d1869039530178fd5f7e6faa" ON "StatusPageMonitorRule" ("isEnabled") `,
|
|
23
|
+
);
|
|
24
|
+
await queryRunner.query(
|
|
25
|
+
`CREATE INDEX "IDX_3e6d0bc4d444371aaa13a1c14c" ON "StatusPageMonitorRule" ("statusPageGroupId") `,
|
|
26
|
+
);
|
|
27
|
+
await queryRunner.query(
|
|
28
|
+
`CREATE TABLE "StatusPageMonitorRuleMonitorLabel" ("statusPageMonitorRuleId" uuid NOT NULL, "labelId" uuid NOT NULL, CONSTRAINT "PK_59234d33b435d8e771cd3728ea4" PRIMARY KEY ("statusPageMonitorRuleId", "labelId"))`,
|
|
29
|
+
);
|
|
30
|
+
await queryRunner.query(
|
|
31
|
+
`CREATE INDEX "IDX_0eb2b2a3601ea9813b8e162bd3" ON "StatusPageMonitorRuleMonitorLabel" ("statusPageMonitorRuleId") `,
|
|
32
|
+
);
|
|
33
|
+
await queryRunner.query(
|
|
34
|
+
`CREATE INDEX "IDX_bc08b888a870ecc9167516230a" ON "StatusPageMonitorRuleMonitorLabel" ("labelId") `,
|
|
35
|
+
);
|
|
36
|
+
await queryRunner.query(
|
|
37
|
+
`ALTER TABLE "StatusPageResource" ADD "statusPageMonitorRuleId" uuid`,
|
|
38
|
+
);
|
|
39
|
+
await queryRunner.query(
|
|
40
|
+
`CREATE INDEX "IDX_4e1ed45d9506da817dc7811601" ON "StatusPageResource" ("statusPageMonitorRuleId") `,
|
|
41
|
+
);
|
|
42
|
+
await queryRunner.query(
|
|
43
|
+
`ALTER TABLE "StatusPageMonitorRule" ADD CONSTRAINT "FK_1dfd95ad32f7b687ef4d7ba5910" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
44
|
+
);
|
|
45
|
+
await queryRunner.query(
|
|
46
|
+
`ALTER TABLE "StatusPageMonitorRule" ADD CONSTRAINT "FK_473317deaa55a8305a047e4098b" FOREIGN KEY ("statusPageId") REFERENCES "StatusPage"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
47
|
+
);
|
|
48
|
+
await queryRunner.query(
|
|
49
|
+
`ALTER TABLE "StatusPageMonitorRule" ADD CONSTRAINT "FK_3e6d0bc4d444371aaa13a1c14cc" FOREIGN KEY ("statusPageGroupId") REFERENCES "StatusPageGroup"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
50
|
+
);
|
|
51
|
+
await queryRunner.query(
|
|
52
|
+
`ALTER TABLE "StatusPageMonitorRule" ADD CONSTRAINT "FK_60d22031be1695cbd1405376cbf" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
|
53
|
+
);
|
|
54
|
+
await queryRunner.query(
|
|
55
|
+
`ALTER TABLE "StatusPageMonitorRule" ADD CONSTRAINT "FK_3416ec12899c9dde670d8dea290" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
|
56
|
+
);
|
|
57
|
+
await queryRunner.query(
|
|
58
|
+
`ALTER TABLE "StatusPageResource" ADD CONSTRAINT "FK_4e1ed45d9506da817dc78116010" FOREIGN KEY ("statusPageMonitorRuleId") REFERENCES "StatusPageMonitorRule"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
59
|
+
);
|
|
60
|
+
await queryRunner.query(
|
|
61
|
+
`ALTER TABLE "StatusPageMonitorRuleMonitorLabel" ADD CONSTRAINT "FK_0eb2b2a3601ea9813b8e162bd3b" FOREIGN KEY ("statusPageMonitorRuleId") REFERENCES "StatusPageMonitorRule"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
62
|
+
);
|
|
63
|
+
await queryRunner.query(
|
|
64
|
+
`ALTER TABLE "StatusPageMonitorRuleMonitorLabel" ADD CONSTRAINT "FK_bc08b888a870ecc9167516230ad" FOREIGN KEY ("labelId") REFERENCES "Label"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
69
|
+
await queryRunner.query(
|
|
70
|
+
`ALTER TABLE "StatusPageMonitorRuleMonitorLabel" DROP CONSTRAINT "FK_bc08b888a870ecc9167516230ad"`,
|
|
71
|
+
);
|
|
72
|
+
await queryRunner.query(
|
|
73
|
+
`ALTER TABLE "StatusPageMonitorRuleMonitorLabel" DROP CONSTRAINT "FK_0eb2b2a3601ea9813b8e162bd3b"`,
|
|
74
|
+
);
|
|
75
|
+
await queryRunner.query(
|
|
76
|
+
`ALTER TABLE "StatusPageResource" DROP CONSTRAINT "FK_4e1ed45d9506da817dc78116010"`,
|
|
77
|
+
);
|
|
78
|
+
await queryRunner.query(
|
|
79
|
+
`ALTER TABLE "StatusPageMonitorRule" DROP CONSTRAINT "FK_3416ec12899c9dde670d8dea290"`,
|
|
80
|
+
);
|
|
81
|
+
await queryRunner.query(
|
|
82
|
+
`ALTER TABLE "StatusPageMonitorRule" DROP CONSTRAINT "FK_60d22031be1695cbd1405376cbf"`,
|
|
83
|
+
);
|
|
84
|
+
await queryRunner.query(
|
|
85
|
+
`ALTER TABLE "StatusPageMonitorRule" DROP CONSTRAINT "FK_3e6d0bc4d444371aaa13a1c14cc"`,
|
|
86
|
+
);
|
|
87
|
+
await queryRunner.query(
|
|
88
|
+
`ALTER TABLE "StatusPageMonitorRule" DROP CONSTRAINT "FK_473317deaa55a8305a047e4098b"`,
|
|
89
|
+
);
|
|
90
|
+
await queryRunner.query(
|
|
91
|
+
`ALTER TABLE "StatusPageMonitorRule" DROP CONSTRAINT "FK_1dfd95ad32f7b687ef4d7ba5910"`,
|
|
92
|
+
);
|
|
93
|
+
await queryRunner.query(
|
|
94
|
+
`DROP INDEX "public"."IDX_4e1ed45d9506da817dc7811601"`,
|
|
95
|
+
);
|
|
96
|
+
await queryRunner.query(
|
|
97
|
+
`ALTER TABLE "StatusPageResource" DROP COLUMN "statusPageMonitorRuleId"`,
|
|
98
|
+
);
|
|
99
|
+
await queryRunner.query(
|
|
100
|
+
`DROP INDEX "public"."IDX_bc08b888a870ecc9167516230a"`,
|
|
101
|
+
);
|
|
102
|
+
await queryRunner.query(
|
|
103
|
+
`DROP INDEX "public"."IDX_0eb2b2a3601ea9813b8e162bd3"`,
|
|
104
|
+
);
|
|
105
|
+
await queryRunner.query(`DROP TABLE "StatusPageMonitorRuleMonitorLabel"`);
|
|
106
|
+
await queryRunner.query(
|
|
107
|
+
`DROP INDEX "public"."IDX_3e6d0bc4d444371aaa13a1c14c"`,
|
|
108
|
+
);
|
|
109
|
+
await queryRunner.query(
|
|
110
|
+
`DROP INDEX "public"."IDX_71d1869039530178fd5f7e6faa"`,
|
|
111
|
+
);
|
|
112
|
+
await queryRunner.query(
|
|
113
|
+
`DROP INDEX "public"."IDX_61196848685b7e7f5a6a54278b"`,
|
|
114
|
+
);
|
|
115
|
+
await queryRunner.query(
|
|
116
|
+
`DROP INDEX "public"."IDX_473317deaa55a8305a047e4098"`,
|
|
117
|
+
);
|
|
118
|
+
await queryRunner.query(
|
|
119
|
+
`DROP INDEX "public"."IDX_1dfd95ad32f7b687ef4d7ba591"`,
|
|
120
|
+
);
|
|
121
|
+
await queryRunner.query(`DROP TABLE "StatusPageMonitorRule"`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -498,6 +498,7 @@ import { WidenInventoryResourceNameColumns1785930000000 } from "./1785930000000-
|
|
|
498
498
|
import { AddStatusPageReportPeriod1785930709405 } from "./1785930709405-AddStatusPageReportPeriod";
|
|
499
499
|
import { AddIncidentInvestigationGating1785790000000 } from "./1785790000000-AddIncidentInvestigationGating";
|
|
500
500
|
import { AddRemediationVerification1785768089408 } from "./1785768089408-AddRemediationVerification";
|
|
501
|
+
import { AddStatusPageMonitorRule1786005052769 } from "./1786005052769-AddStatusPageMonitorRule";
|
|
501
502
|
|
|
502
503
|
export default [
|
|
503
504
|
InitialMigration,
|
|
@@ -1000,4 +1001,5 @@ export default [
|
|
|
1000
1001
|
IncreaseHostIpAddressesLength1785915638551,
|
|
1001
1002
|
WidenInventoryResourceNameColumns1785930000000,
|
|
1002
1003
|
AddStatusPageReportPeriod1785930709405,
|
|
1004
|
+
AddStatusPageMonitorRule1786005052769,
|
|
1003
1005
|
];
|
|
@@ -19,6 +19,7 @@ import { JSONObject } from "../../Types/JSON";
|
|
|
19
19
|
import AlertInternalNote from "../../Models/DatabaseModels/AlertInternalNote";
|
|
20
20
|
import AlertInternalNoteService from "./AlertInternalNoteService";
|
|
21
21
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
22
|
+
import ProjectScopedReferenceValidator from "../Utils/Database/ProjectScopedReferenceValidator";
|
|
22
23
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
23
24
|
import AlertFeedService from "./AlertFeedService";
|
|
24
25
|
import { AlertFeedEventType } from "../../Models/DatabaseModels/AlertFeed";
|
|
@@ -118,6 +119,25 @@ export class Service extends DatabaseService<AlertStateTimeline> {
|
|
|
118
119
|
throw new BadDataException("alertStateId is null");
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
/*
|
|
123
|
+
* Same guard, same reason, as MonitorStatusTimelineService: an id that
|
|
124
|
+
* exists nowhere (or belongs to another project) used to reach Postgres
|
|
125
|
+
* and come back as a raw foreign key violation. The ordering check below
|
|
126
|
+
* does look the state up, but only when there IS a preceding row with an
|
|
127
|
+
* order, so the first timeline row for an alert went through unchecked.
|
|
128
|
+
*/
|
|
129
|
+
await ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
130
|
+
projectId: createBy.props.tenantId || createBy.data.projectId,
|
|
131
|
+
subject: "alert state timeline",
|
|
132
|
+
references: [
|
|
133
|
+
{
|
|
134
|
+
modelName: "Alert State",
|
|
135
|
+
id: alertStateId,
|
|
136
|
+
service: AlertStateService,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
|
|
121
141
|
const stateBeforeThis: AlertStateTimeline | null = await this.findOneBy({
|
|
122
142
|
query: {
|
|
123
143
|
alertId: createBy.data.alertId,
|