@oneuptime/common 12.0.1 → 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/Host.ts +12 -4
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
- package/Models/DatabaseModels/KubernetesResource.ts +8 -7
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/PodmanResource.ts +6 -6
- package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
- package/Models/DatabaseModels/StatusPage.ts +95 -0
- 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/API/TeamMemberAPI.ts +85 -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/1785900000000-AddSloMonitorLabelRule.ts +84 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Middleware/ProjectAuthorization.ts +66 -4
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/BillingService.ts +600 -51
- package/Server/Services/CephClusterService.ts +27 -45
- package/Server/Services/CephResourceService.ts +43 -4
- package/Server/Services/CloudResourceService.ts +24 -42
- package/Server/Services/DatabaseService.ts +198 -6
- package/Server/Services/DockerHostService.ts +20 -38
- package/Server/Services/DockerResourceService.ts +62 -8
- package/Server/Services/DockerSwarmClusterService.ts +28 -46
- package/Server/Services/DockerSwarmResourceService.ts +53 -4
- package/Server/Services/HostService.ts +47 -57
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTDeviceService.ts +1 -17
- package/Server/Services/IoTFleetService.ts +20 -38
- package/Server/Services/KubernetesClusterService.ts +18 -36
- package/Server/Services/KubernetesContainerService.ts +50 -9
- package/Server/Services/KubernetesResourceService.ts +53 -7
- package/Server/Services/LabelService.ts +153 -1
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +146 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/PodmanHostService.ts +20 -38
- package/Server/Services/PodmanResourceService.ts +62 -8
- package/Server/Services/ProjectService.ts +165 -35
- package/Server/Services/ProxmoxClusterService.ts +24 -42
- package/Server/Services/ProxmoxResourceService.ts +43 -4
- package/Server/Services/RumApplicationService.ts +20 -38
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +25 -43
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/StatusPageService.ts +95 -36
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
- package/Server/Services/StatusPageSubscriberService.ts +3 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- package/Server/Types/Database/JSONColumnQuery.ts +621 -0
- package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
- package/Server/Types/Database/QueryHelper.ts +17 -32
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
- 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/UsersTableGroupsByPerson.test.tsx +448 -0
- 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/BaseAPIApiKeyAuth.test.ts +704 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +226 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
- package/Tests/Server/Services/BillingService.test.ts +78 -3
- package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
- package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
- package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +658 -0
- package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
- package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
- package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
- 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/ProjectServiceChangePlan.test.ts +650 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +636 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -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/Services/StatusPageSubscriberReport.test.ts +29 -13
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
- package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
- package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -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/Database/TruncateColumnValue.test.ts +179 -0
- 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/Events/Recurring.test.ts +157 -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/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
- package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
- package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
- package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
- package/Types/CustomField/CustomFieldDefinition.ts +24 -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/Date.ts +112 -0
- package/Types/Decimal.ts +16 -4
- package/Types/Events/Recurring.ts +99 -1
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/Types/StatusPage/StatusPageReport.ts +11 -0
- package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
- package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
- package/UI/Utils/TeamMembersByUser.ts +302 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/Utils/StatusPage/ReportPeriod.ts +236 -0
- package/Utils/Telemetry/HostIpAddresses.ts +98 -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/Host.js +12 -4
- package/build/dist/Models/DatabaseModels/Host.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/KubernetesContainer.js +4 -4
- package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
- package/build/dist/Models/DatabaseModels/KubernetesResource.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/PodmanResource.js +6 -6
- package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
- package/build/dist/Models/DatabaseModels/StatusPage.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/API/TeamMemberAPI.js +57 -2
- package/build/dist/Server/API/TeamMemberAPI.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/1785900000000-AddSloMonitorLabelRule.js +45 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
- 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 +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
- package/build/dist/Server/Middleware/ProjectAuthorization.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/BillingService.js +466 -36
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +26 -39
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CephResourceService.js +28 -3
- package/build/dist/Server/Services/CephResourceService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -36
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +155 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -32
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerResourceService.js +34 -4
- package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -40
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
- package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -64
- 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/IoTDeviceService.js +1 -8
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +19 -32
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -30
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
- package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +127 -0
- 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 +128 -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 -32
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/PodmanResourceService.js +34 -4
- package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +135 -21
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -36
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -32
- 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 -37
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.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/StatusPageService.js +69 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- 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/Types/Database/JSONColumnQuery.js +402 -0
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
- package/build/dist/Server/Types/Database/QueryHelper.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/Database/TruncateColumnValue.js +32 -0
- package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
- 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/CustomField/CustomFieldDefinition.js +2 -0
- package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
- 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/Date.js +68 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +63 -0
- package/build/dist/Types/Events/Recurring.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/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
- package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
- 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/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
- package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
- package/build/dist/UI/Utils/TeamMembersByUser.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/build/dist/Utils/StatusPage/ReportPeriod.js +131 -0
- package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +12 -1
|
@@ -5,6 +5,7 @@ import Model from "../../Models/DatabaseModels/CephCluster";
|
|
|
5
5
|
import Label from "../../Models/DatabaseModels/Label";
|
|
6
6
|
import { OnCreate } from "../Types/Database/Hooks";
|
|
7
7
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
8
|
+
import ResourceHeartbeat from "../Utils/Telemetry/ResourceHeartbeat";
|
|
8
9
|
import ObjectID from "../../Types/ObjectID";
|
|
9
10
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
10
11
|
import OneUptimeDate from "../../Types/Date";
|
|
@@ -181,7 +182,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
181
182
|
capacityUsedPercent?: number | undefined;
|
|
182
183
|
},
|
|
183
184
|
): Promise<void> {
|
|
184
|
-
const cacheKey: string = clusterId.toString();
|
|
185
185
|
const extrasFingerprint: string = crypto
|
|
186
186
|
.createHash("sha1")
|
|
187
187
|
.update(
|
|
@@ -200,83 +200,65 @@ export class Service extends DatabaseService<Model> {
|
|
|
200
200
|
)
|
|
201
201
|
.digest("hex");
|
|
202
202
|
|
|
203
|
-
let cached: string | null = null;
|
|
204
|
-
try {
|
|
205
|
-
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
206
|
-
} catch {
|
|
207
|
-
/*
|
|
208
|
-
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
209
|
-
* cache error must never skip the DB write below, otherwise the
|
|
210
|
-
* resource is wrongly marked "disconnected" while telemetry is
|
|
211
|
-
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
212
|
-
*/
|
|
213
|
-
cached = null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (cached === extrasFingerprint) {
|
|
217
|
-
return; // same data was written recently
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
await GlobalCache.setString(
|
|
222
|
-
LAST_SEEN_CACHE_NAMESPACE,
|
|
223
|
-
cacheKey,
|
|
224
|
-
extrasFingerprint,
|
|
225
|
-
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
226
|
-
);
|
|
227
|
-
} catch {
|
|
228
|
-
// Best-effort throttle write; proceed with the DB update regardless.
|
|
229
|
-
}
|
|
230
|
-
|
|
231
203
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
232
|
-
const
|
|
204
|
+
const liveness: any = {
|
|
233
205
|
lastSeenAt: OneUptimeDate.getCurrentDate(),
|
|
234
206
|
otelCollectorStatus: "connected",
|
|
235
207
|
};
|
|
236
208
|
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
210
|
+
const metadata: any = {};
|
|
211
|
+
|
|
237
212
|
if (extra?.cephVersion) {
|
|
238
|
-
|
|
213
|
+
metadata.cephVersion = extra.cephVersion;
|
|
239
214
|
}
|
|
240
215
|
if (extra?.fsid) {
|
|
241
|
-
|
|
216
|
+
metadata.fsid = extra.fsid;
|
|
242
217
|
}
|
|
243
218
|
if (extra?.agentVersion) {
|
|
244
|
-
|
|
219
|
+
metadata.agentVersion = extra.agentVersion;
|
|
245
220
|
}
|
|
246
221
|
/*
|
|
247
222
|
* Counts and health: 0 is a legitimate value (healthStatus 0 = OK,
|
|
248
223
|
* osdUpCount 0 = every OSD down) — gate on undefined, not falsiness.
|
|
249
224
|
*/
|
|
250
225
|
if (extra?.monCount !== undefined) {
|
|
251
|
-
|
|
226
|
+
metadata.monCount = extra.monCount;
|
|
252
227
|
}
|
|
253
228
|
if (extra?.osdCount !== undefined) {
|
|
254
|
-
|
|
229
|
+
metadata.osdCount = extra.osdCount;
|
|
255
230
|
}
|
|
256
231
|
if (extra?.osdUpCount !== undefined) {
|
|
257
|
-
|
|
232
|
+
metadata.osdUpCount = extra.osdUpCount;
|
|
258
233
|
}
|
|
259
234
|
if (extra?.osdInCount !== undefined) {
|
|
260
|
-
|
|
235
|
+
metadata.osdInCount = extra.osdInCount;
|
|
261
236
|
}
|
|
262
237
|
if (extra?.poolCount !== undefined) {
|
|
263
|
-
|
|
238
|
+
metadata.poolCount = extra.poolCount;
|
|
264
239
|
}
|
|
265
240
|
if (extra?.healthStatus !== undefined) {
|
|
266
|
-
|
|
241
|
+
metadata.healthStatus = extra.healthStatus;
|
|
267
242
|
}
|
|
268
243
|
if (extra?.capacityUsedPercent !== undefined) {
|
|
269
|
-
|
|
244
|
+
metadata.capacityUsedPercent = extra.capacityUsedPercent;
|
|
270
245
|
}
|
|
271
246
|
|
|
272
247
|
/*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
248
|
+
* One gated, non-blocking heartbeat write. The gates, the fail-open /
|
|
249
|
+
* fail-closed split and the liveness-only fallback all live in
|
|
250
|
+
* ResourceHeartbeat — see there for why this row's throttle used to
|
|
251
|
+
* provide no throttling at all.
|
|
276
252
|
*/
|
|
277
|
-
await
|
|
253
|
+
await ResourceHeartbeat.write({
|
|
254
|
+
service: this,
|
|
278
255
|
id: clusterId,
|
|
279
|
-
|
|
256
|
+
cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
|
|
257
|
+
throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
|
|
258
|
+
liveness: liveness,
|
|
259
|
+
metadata: metadata,
|
|
260
|
+
fingerprint: extrasFingerprint,
|
|
261
|
+
describe: `ceph cluster ${clusterId.toString()}`,
|
|
280
262
|
});
|
|
281
263
|
}
|
|
282
264
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import DatabaseService from "./DatabaseService";
|
|
2
2
|
import Model from "../../Models/DatabaseModels/CephResource";
|
|
3
3
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
4
|
+
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
4
5
|
import ObjectID from "../../Types/ObjectID";
|
|
5
6
|
import OneUptimeDate from "../../Types/Date";
|
|
7
|
+
import { truncateShortText } from "../Utils/Database/TruncateColumnValue";
|
|
6
8
|
import logger from "../Utils/Logger";
|
|
7
9
|
|
|
8
10
|
/*
|
|
@@ -86,6 +88,26 @@ const UPSERT_COLUMNS: Array<string> = [
|
|
|
86
88
|
"version",
|
|
87
89
|
];
|
|
88
90
|
|
|
91
|
+
/*
|
|
92
|
+
* CephResource's text columns are all ShortText (100 chars). The bulk
|
|
93
|
+
* paths below go through manager.query(), which skips the length
|
|
94
|
+
* validation DatabaseService applies to ordinary writes, so a single
|
|
95
|
+
* oversized value aborts the whole 500-row INSERT chunk it rides in.
|
|
96
|
+
* Clamp per value instead, so one pathological daemon can never drop
|
|
97
|
+
* the rest of the scrape.
|
|
98
|
+
*/
|
|
99
|
+
function sanitizeResource(r: ParsedCephResource): ParsedCephResource {
|
|
100
|
+
return {
|
|
101
|
+
...r,
|
|
102
|
+
kind: truncateShortText(r.kind),
|
|
103
|
+
externalId: truncateShortText(r.externalId),
|
|
104
|
+
name: truncateShortText(r.name),
|
|
105
|
+
hostname: truncateShortText(r.hostname),
|
|
106
|
+
daemonVersion: truncateShortText(r.daemonVersion),
|
|
107
|
+
deviceClass: truncateShortText(r.deviceClass),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
89
111
|
export class Service extends DatabaseService<Model> {
|
|
90
112
|
public constructor() {
|
|
91
113
|
super(Model);
|
|
@@ -112,9 +134,21 @@ export class Service extends DatabaseService<Model> {
|
|
|
112
134
|
return;
|
|
113
135
|
}
|
|
114
136
|
|
|
137
|
+
const resources: Array<ParsedCephResource> = data.resources.map(
|
|
138
|
+
(r: ParsedCephResource) => {
|
|
139
|
+
const sanitized: ParsedCephResource = sanitizeResource(r);
|
|
140
|
+
if (sanitized.externalId !== r.externalId) {
|
|
141
|
+
logger.warn(
|
|
142
|
+
`CephResource externalId exceeds ${ColumnLength.ShortText} chars; truncated to "${sanitized.externalId}" (cluster ${data.cephClusterId.toString()}).`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return sanitized;
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
|
|
115
149
|
// Chunk to keep individual statement parameter counts reasonable.
|
|
116
|
-
for (let i: number = 0; i <
|
|
117
|
-
const chunk: Array<ParsedCephResource> =
|
|
150
|
+
for (let i: number = 0; i < resources.length; i += UPSERT_BATCH_SIZE) {
|
|
151
|
+
const chunk: Array<ParsedCephResource> = resources.slice(
|
|
118
152
|
i,
|
|
119
153
|
i + UPSERT_BATCH_SIZE,
|
|
120
154
|
);
|
|
@@ -218,9 +252,14 @@ export class Service extends DatabaseService<Model> {
|
|
|
218
252
|
valueFragments.push(
|
|
219
253
|
`($${paramIndex++}, $${paramIndex++}, $${paramIndex++}::bigint, $${paramIndex++}::bigint, $${paramIndex++}::numeric, $${paramIndex++}::numeric, $${paramIndex++}::integer, $${paramIndex++}::bigint, $${paramIndex++}::bigint, $${paramIndex++}::bigint, $${paramIndex++}::bigint, $${paramIndex++}::bigint, $${paramIndex++}::timestamptz)`,
|
|
220
254
|
);
|
|
255
|
+
/*
|
|
256
|
+
* The identity clamps MUST match bulkUpsert's, or this mirror
|
|
257
|
+
* UPDATE would silently miss the row the upsert just wrote
|
|
258
|
+
* under the truncated externalId.
|
|
259
|
+
*/
|
|
221
260
|
params.push(
|
|
222
|
-
m.kind,
|
|
223
|
-
m.externalId,
|
|
261
|
+
truncateShortText(m.kind),
|
|
262
|
+
truncateShortText(m.externalId),
|
|
224
263
|
bigintOrNull(m.statBytes),
|
|
225
264
|
bigintOrNull(m.statBytesUsed),
|
|
226
265
|
m.applyLatencyMs !== null && m.applyLatencyMs !== undefined
|
|
@@ -2,6 +2,7 @@ import DatabaseService from "./DatabaseService";
|
|
|
2
2
|
import Model from "../../Models/DatabaseModels/CloudResource";
|
|
3
3
|
import Label from "../../Models/DatabaseModels/Label";
|
|
4
4
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
5
|
+
import ResourceHeartbeat from "../Utils/Telemetry/ResourceHeartbeat";
|
|
5
6
|
import ObjectID from "../../Types/ObjectID";
|
|
6
7
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
7
8
|
import OneUptimeDate from "../../Types/Date";
|
|
@@ -154,7 +155,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
154
155
|
runtimeVersion?: string | undefined;
|
|
155
156
|
},
|
|
156
157
|
): Promise<void> {
|
|
157
|
-
const cacheKey: string = cloudResourceId.toString();
|
|
158
158
|
const extrasFingerprint: string = crypto
|
|
159
159
|
.createHash("sha1")
|
|
160
160
|
.update(
|
|
@@ -170,70 +170,52 @@ export class Service extends DatabaseService<Model> {
|
|
|
170
170
|
)
|
|
171
171
|
.digest("hex");
|
|
172
172
|
|
|
173
|
-
let cached: string | null = null;
|
|
174
|
-
try {
|
|
175
|
-
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
176
|
-
} catch {
|
|
177
|
-
/*
|
|
178
|
-
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
179
|
-
* cache error must never skip the DB write below, otherwise the
|
|
180
|
-
* resource is wrongly marked "disconnected" while telemetry is
|
|
181
|
-
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
182
|
-
*/
|
|
183
|
-
cached = null;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (cached === extrasFingerprint) {
|
|
187
|
-
return; // same data was written recently
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
await GlobalCache.setString(
|
|
192
|
-
LAST_SEEN_CACHE_NAMESPACE,
|
|
193
|
-
cacheKey,
|
|
194
|
-
extrasFingerprint,
|
|
195
|
-
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
196
|
-
);
|
|
197
|
-
} catch {
|
|
198
|
-
// Best-effort throttle write; proceed with the DB update regardless.
|
|
199
|
-
}
|
|
200
|
-
|
|
201
173
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
202
|
-
const
|
|
174
|
+
const liveness: any = {
|
|
203
175
|
lastSeenAt: OneUptimeDate.getCurrentDate(),
|
|
204
176
|
otelCollectorStatus: "connected",
|
|
205
177
|
};
|
|
206
178
|
|
|
179
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
180
|
+
const metadata: any = {};
|
|
181
|
+
|
|
207
182
|
if (extra?.agentVersion) {
|
|
208
|
-
|
|
183
|
+
metadata.agentVersion = extra.agentVersion;
|
|
209
184
|
}
|
|
210
185
|
if (extra?.cloudPlatform) {
|
|
211
|
-
|
|
186
|
+
metadata.cloudPlatform = extra.cloudPlatform;
|
|
212
187
|
}
|
|
213
188
|
if (extra?.cloudProvider) {
|
|
214
|
-
|
|
189
|
+
metadata.cloudProvider = extra.cloudProvider;
|
|
215
190
|
}
|
|
216
191
|
if (extra?.cloudRegion) {
|
|
217
|
-
|
|
192
|
+
metadata.cloudRegion = extra.cloudRegion;
|
|
218
193
|
}
|
|
219
194
|
if (extra?.cloudAccountId) {
|
|
220
|
-
|
|
195
|
+
metadata.cloudAccountId = extra.cloudAccountId;
|
|
221
196
|
}
|
|
222
197
|
if (extra?.runtimeName) {
|
|
223
|
-
|
|
198
|
+
metadata.runtimeName = extra.runtimeName;
|
|
224
199
|
}
|
|
225
200
|
if (extra?.runtimeVersion) {
|
|
226
|
-
|
|
201
|
+
metadata.runtimeVersion = extra.runtimeVersion;
|
|
227
202
|
}
|
|
228
203
|
|
|
229
204
|
/*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
205
|
+
* One gated, non-blocking heartbeat write. The gates, the fail-open /
|
|
206
|
+
* fail-closed split and the liveness-only fallback all live in
|
|
207
|
+
* ResourceHeartbeat — see there for why this row's throttle used to
|
|
208
|
+
* provide no throttling at all.
|
|
233
209
|
*/
|
|
234
|
-
await
|
|
210
|
+
await ResourceHeartbeat.write({
|
|
211
|
+
service: this,
|
|
235
212
|
id: cloudResourceId,
|
|
236
|
-
|
|
213
|
+
cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
|
|
214
|
+
throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
|
|
215
|
+
liveness: liveness,
|
|
216
|
+
metadata: metadata,
|
|
217
|
+
fingerprint: extrasFingerprint,
|
|
218
|
+
describe: `cloud resource ${cloudResourceId.toString()}`,
|
|
237
219
|
});
|
|
238
220
|
}
|
|
239
221
|
|
|
@@ -1046,6 +1046,61 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
1046
1046
|
return data;
|
|
1047
1047
|
}
|
|
1048
1048
|
|
|
1049
|
+
/*
|
|
1050
|
+
* Clamp string values to the max length their column declares, instead of
|
|
1051
|
+
* rejecting the whole write the way checkMaxLengthOfFields does.
|
|
1052
|
+
*
|
|
1053
|
+
* For machine-stamped metadata harvested from OpenTelemetry resource
|
|
1054
|
+
* attributes there is no user to show a validation error to, and the
|
|
1055
|
+
* write these values ride along with also carries liveness columns
|
|
1056
|
+
* (lastSeenAt / otelCollectorStatus). Letting one oversized optional
|
|
1057
|
+
* attribute abort that statement strands a healthy resource as
|
|
1058
|
+
* "disconnected" — the failure mode in issue #3006. A clipped display
|
|
1059
|
+
* string is strictly better than a lost heartbeat.
|
|
1060
|
+
*
|
|
1061
|
+
* Only string values on columns with a declared max length are touched;
|
|
1062
|
+
* `text` columns (VeryLongText and friends) declare none and pass
|
|
1063
|
+
* through untouched, as do numbers, dates and ObjectIDs.
|
|
1064
|
+
*
|
|
1065
|
+
* The raw, hook-free write paths (updateColumnsByIdWithoutHooks and
|
|
1066
|
+
* atomicAddToColumnsByIdWithoutHooks) call this themselves, so callers do
|
|
1067
|
+
* NOT need to — and must not rely on being able to skip it by writing a
|
|
1068
|
+
* new raw path of their own. It is NOT a substitute for sizing a column
|
|
1069
|
+
* correctly: if a field is legitimately long, widen the column.
|
|
1070
|
+
*/
|
|
1071
|
+
public truncateStringColumnsToMaxLength(
|
|
1072
|
+
data: Record<string, unknown>,
|
|
1073
|
+
): Record<string, unknown> {
|
|
1074
|
+
for (const column of Object.keys(data)) {
|
|
1075
|
+
const value: unknown = data[column];
|
|
1076
|
+
|
|
1077
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/*
|
|
1082
|
+
* Returns undefined for an unmapped column name despite its type —
|
|
1083
|
+
* leave those alone and let the write path complain about them.
|
|
1084
|
+
*/
|
|
1085
|
+
const metadata: TableColumnMetadata | undefined =
|
|
1086
|
+
this.model.getTableColumnMetadata(column);
|
|
1087
|
+
|
|
1088
|
+
if (!metadata?.type) {
|
|
1089
|
+
continue;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
const maxLength: number | undefined = getMaxLengthFromTableColumnType(
|
|
1093
|
+
metadata.type,
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
if (maxLength !== undefined && value.length > maxLength) {
|
|
1097
|
+
data[column] = value.substring(0, maxLength);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return data;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1049
1104
|
private async checkTotalItemsBy(
|
|
1050
1105
|
createdBy: CreateBy<TBaseModel>,
|
|
1051
1106
|
): Promise<void> {
|
|
@@ -2248,6 +2303,17 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2248
2303
|
* the caller), and every value is bound as a parameter, so this is not an
|
|
2249
2304
|
* injection surface. Use ONLY for trusted, internal, side-effect-free
|
|
2250
2305
|
* column writes where the full pipeline is pure overhead.
|
|
2306
|
+
*
|
|
2307
|
+
* Oversized strings are CLAMPED here (truncateStringColumnsToMaxLength)
|
|
2308
|
+
* rather than rejected. Skipping the hooks also skips
|
|
2309
|
+
* checkMaxLengthOfFields, so before this an over-long value reached
|
|
2310
|
+
* Postgres raw and failed the ENTIRE statement — including whatever else
|
|
2311
|
+
* rode along with it. On the telemetry liveness writes that is fatal: one
|
|
2312
|
+
* over-long OpenTelemetry resource attribute took `lastSeenAt` down with
|
|
2313
|
+
* it and markDisconnected* stranded a healthy resource as "disconnected"
|
|
2314
|
+
* 15 minutes later while its data kept arriving (issue #3006). Clamping
|
|
2315
|
+
* in here, at the choke point, is what makes that unforgettable — every
|
|
2316
|
+
* caller of this path gets it, including ones not written yet.
|
|
2251
2317
|
*/
|
|
2252
2318
|
@CaptureSpan()
|
|
2253
2319
|
public async updateColumnsByIdWithoutHooks(input: {
|
|
@@ -2273,9 +2339,16 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2273
2339
|
const setClauses: Array<string> = [];
|
|
2274
2340
|
const params: Array<unknown> = [];
|
|
2275
2341
|
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2342
|
+
/*
|
|
2343
|
+
* Clamp on a shallow COPY: callers must not have their own object
|
|
2344
|
+
* mutated behind their back (a liveness-only retry, for instance,
|
|
2345
|
+
* rebuilds its payload from the same source values).
|
|
2346
|
+
*/
|
|
2347
|
+
const data: ObjectLiteral = this.truncateStringColumnsToMaxLength({
|
|
2348
|
+
...(input.data as ObjectLiteral),
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
for (const [propertyName, value] of Object.entries(data)) {
|
|
2279
2352
|
const column: ColumnMetadata | undefined =
|
|
2280
2353
|
metadata.findColumnWithPropertyName(propertyName);
|
|
2281
2354
|
if (!column) {
|
|
@@ -2327,6 +2400,117 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2327
2400
|
await repository.manager.query(sql, params);
|
|
2328
2401
|
}
|
|
2329
2402
|
|
|
2403
|
+
/*
|
|
2404
|
+
* Same single-statement, hook-free, no-version-bump write as
|
|
2405
|
+
* `updateColumnsByIdWithoutHooks`, except it YIELDS instead of queueing:
|
|
2406
|
+
* if another transaction already holds the row's lock, this write is
|
|
2407
|
+
* abandoned and the method returns false. It never waits.
|
|
2408
|
+
*
|
|
2409
|
+
* Why this exists. A single-statement UPDATE holds its row lock for a very
|
|
2410
|
+
* short time, but "short" does not mean "safe" once the arrival rate on ONE
|
|
2411
|
+
* row exceeds the service rate. Postgres queues lock waiters strictly, so
|
|
2412
|
+
* every waiter pins a backend for the sum of everyone ahead of it — the
|
|
2413
|
+
* classic convoy. In the outage this was written for, ~25K concurrent
|
|
2414
|
+
* ingest jobs across 100 worker pods funnelled onto ~2,300 Service rows and
|
|
2415
|
+
* produced 1,017 active connections with 892 of them parked on row locks;
|
|
2416
|
+
* the tail had been waiting 3.7 hours. Nothing about that is fixable by
|
|
2417
|
+
* making the statement faster, and no Postgres tier changes it: the queue
|
|
2418
|
+
* is the bug.
|
|
2419
|
+
*
|
|
2420
|
+
* `FOR UPDATE SKIP LOCKED` inverts it. A contended writer does zero work
|
|
2421
|
+
* and returns immediately, so the number of backends blocked on any single
|
|
2422
|
+
* row is capped at ONE no matter how many workers arrive together. The
|
|
2423
|
+
* convoy cannot form — and cannot form even if every upstream throttle
|
|
2424
|
+
* fails open at once (Redis down, fence bug, cache flush). That is the
|
|
2425
|
+
* point: this is the structural backstop underneath the throttles, not a
|
|
2426
|
+
* second copy of them.
|
|
2427
|
+
*
|
|
2428
|
+
* Use ONLY where losing the write is harmless because a concurrent writer
|
|
2429
|
+
* is writing the same thing — liveness timestamps and other idempotent
|
|
2430
|
+
* bookkeeping, whose contended value is by construction the value the
|
|
2431
|
+
* winner is already storing. It is the WRONG primitive for a write carrying
|
|
2432
|
+
* information the winner does not have (a changed attribute); for those the
|
|
2433
|
+
* caller must re-attempt, and `false` is the signal to do it. Returning a
|
|
2434
|
+
* boolean rather than swallowing the skip is deliberate: a silent drop here
|
|
2435
|
+
* would be indistinguishable from a successful write at the call site,
|
|
2436
|
+
* which is exactly how a "reliable" heartbeat quietly stops beating.
|
|
2437
|
+
*
|
|
2438
|
+
* Returns true when the row was updated, false when it was locked (or no
|
|
2439
|
+
* longer exists).
|
|
2440
|
+
*/
|
|
2441
|
+
@CaptureSpan()
|
|
2442
|
+
public async updateColumnsByIdIfUnlockedWithoutHooks(input: {
|
|
2443
|
+
id: ObjectID;
|
|
2444
|
+
data: PartialEntity<TBaseModel>;
|
|
2445
|
+
skipUpdateDateColumn?: boolean;
|
|
2446
|
+
}): Promise<boolean> {
|
|
2447
|
+
if (!input.id) {
|
|
2448
|
+
throw new BadDataException("id is required");
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
const repository: Repository<TBaseModel> = this.getRepository();
|
|
2452
|
+
const metadata: EntityMetadata = repository.metadata;
|
|
2453
|
+
const driver: Driver = repository.manager.connection.driver;
|
|
2454
|
+
|
|
2455
|
+
const setClauses: Array<string> = [];
|
|
2456
|
+
const params: Array<unknown> = [];
|
|
2457
|
+
|
|
2458
|
+
// Clamp on a shallow COPY — see updateColumnsByIdWithoutHooks.
|
|
2459
|
+
const data: ObjectLiteral = this.truncateStringColumnsToMaxLength({
|
|
2460
|
+
...(input.data as ObjectLiteral),
|
|
2461
|
+
});
|
|
2462
|
+
|
|
2463
|
+
for (const [propertyName, value] of Object.entries(data)) {
|
|
2464
|
+
const column: ColumnMetadata | undefined =
|
|
2465
|
+
metadata.findColumnWithPropertyName(propertyName);
|
|
2466
|
+
if (!column) {
|
|
2467
|
+
throw new BadDataException(
|
|
2468
|
+
`updateColumnsByIdIfUnlockedWithoutHooks: unknown column "${propertyName}" on "${metadata.tableName}"`,
|
|
2469
|
+
);
|
|
2470
|
+
}
|
|
2471
|
+
if (typeof value === "function") {
|
|
2472
|
+
throw new BadDataException(
|
|
2473
|
+
`updateColumnsByIdIfUnlockedWithoutHooks: SQL-expression values are not supported (column "${propertyName}"); pass a literal value.`,
|
|
2474
|
+
);
|
|
2475
|
+
}
|
|
2476
|
+
params.push(driver.preparePersistentValue(value, column));
|
|
2477
|
+
setClauses.push(`"${column.databaseName}" = $${params.length}`);
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
if (setClauses.length === 0) {
|
|
2481
|
+
return false;
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
if (metadata.updateDateColumn && !input.skipUpdateDateColumn) {
|
|
2485
|
+
setClauses.push(
|
|
2486
|
+
`"${metadata.updateDateColumn.databaseName}" = CURRENT_TIMESTAMP`,
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
const primaryColumnName: string =
|
|
2491
|
+
metadata.primaryColumns[0]?.databaseName || "_id";
|
|
2492
|
+
params.push(input.id.toString());
|
|
2493
|
+
|
|
2494
|
+
/*
|
|
2495
|
+
* The lock is taken by the sub-SELECT and inherited by the UPDATE within
|
|
2496
|
+
* the same implicit transaction, so the outer write never blocks either.
|
|
2497
|
+
* RETURNING is how the affected-row count comes back: TypeORM's `query()`
|
|
2498
|
+
* surfaces the driver's rows and a bare UPDATE returns none, so without it
|
|
2499
|
+
* a skipped write is indistinguishable from an applied one.
|
|
2500
|
+
*/
|
|
2501
|
+
const sql: string = `UPDATE "${metadata.tableName}" SET ${setClauses.join(
|
|
2502
|
+
", ",
|
|
2503
|
+
)} WHERE "${primaryColumnName}" IN (SELECT "${primaryColumnName}" FROM "${
|
|
2504
|
+
metadata.tableName
|
|
2505
|
+
}" WHERE "${primaryColumnName}" = $${
|
|
2506
|
+
params.length
|
|
2507
|
+
} FOR UPDATE SKIP LOCKED) RETURNING "${primaryColumnName}"`;
|
|
2508
|
+
|
|
2509
|
+
const result: unknown = await repository.manager.query(sql, params);
|
|
2510
|
+
|
|
2511
|
+
return Array.isArray(result) && result.length > 0;
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2330
2514
|
/*
|
|
2331
2515
|
* Atomically add to numeric columns and set literal columns for one row,
|
|
2332
2516
|
* in a SINGLE auto-committed UPDATE, with no hooks and no `version` bump.
|
|
@@ -2347,6 +2531,11 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2347
2531
|
* caller) and every value is bound as a parameter, so this is not an
|
|
2348
2532
|
* injection surface. Use ONLY for trusted, internal, side-effect-free
|
|
2349
2533
|
* column writes.
|
|
2534
|
+
*
|
|
2535
|
+
* `set` values are clamped to their column widths for the same reason
|
|
2536
|
+
* updateColumnsByIdWithoutHooks clamps: this path also skips
|
|
2537
|
+
* checkMaxLengthOfFields, and an over-long string here would abort the
|
|
2538
|
+
* counter increments riding in the same statement.
|
|
2350
2539
|
*/
|
|
2351
2540
|
@CaptureSpan()
|
|
2352
2541
|
public async atomicAddToColumnsByIdWithoutHooks(input: {
|
|
@@ -2393,9 +2582,12 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2393
2582
|
setClauses.push(`${quoted} = COALESCE(${quoted}, 0) + $${params.length}`);
|
|
2394
2583
|
}
|
|
2395
2584
|
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2585
|
+
// Shallow copy — never mutate the caller's object. See the clamp note above.
|
|
2586
|
+
const set: ObjectLiteral = this.truncateStringColumnsToMaxLength({
|
|
2587
|
+
...((input.set || {}) as ObjectLiteral),
|
|
2588
|
+
});
|
|
2589
|
+
|
|
2590
|
+
for (const [propertyName, value] of Object.entries(set)) {
|
|
2399
2591
|
if (typeof value === "function") {
|
|
2400
2592
|
throw new BadDataException(
|
|
2401
2593
|
`atomicAddToColumnsByIdWithoutHooks: SQL-expression values are not supported (column "${propertyName}"); pass a literal value.`,
|
|
@@ -5,6 +5,7 @@ import Model from "../../Models/DatabaseModels/DockerHost";
|
|
|
5
5
|
import Label from "../../Models/DatabaseModels/Label";
|
|
6
6
|
import { OnCreate } from "../Types/Database/Hooks";
|
|
7
7
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
8
|
+
import ResourceHeartbeat from "../Utils/Telemetry/ResourceHeartbeat";
|
|
8
9
|
import ObjectID from "../../Types/ObjectID";
|
|
9
10
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
10
11
|
import OneUptimeDate from "../../Types/Date";
|
|
@@ -180,7 +181,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
180
181
|
agentVersion?: string | undefined;
|
|
181
182
|
},
|
|
182
183
|
): Promise<void> {
|
|
183
|
-
const cacheKey: string = hostId.toString();
|
|
184
184
|
const extrasFingerprint: string = crypto
|
|
185
185
|
.createHash("sha1")
|
|
186
186
|
.update(
|
|
@@ -192,58 +192,40 @@ export class Service extends DatabaseService<Model> {
|
|
|
192
192
|
)
|
|
193
193
|
.digest("hex");
|
|
194
194
|
|
|
195
|
-
let cached: string | null = null;
|
|
196
|
-
try {
|
|
197
|
-
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
198
|
-
} catch {
|
|
199
|
-
/*
|
|
200
|
-
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
201
|
-
* cache error must never skip the DB write below, otherwise the
|
|
202
|
-
* resource is wrongly marked "disconnected" while telemetry is
|
|
203
|
-
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
204
|
-
*/
|
|
205
|
-
cached = null;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (cached === extrasFingerprint) {
|
|
209
|
-
return; // same data was written recently
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
await GlobalCache.setString(
|
|
214
|
-
LAST_SEEN_CACHE_NAMESPACE,
|
|
215
|
-
cacheKey,
|
|
216
|
-
extrasFingerprint,
|
|
217
|
-
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
218
|
-
);
|
|
219
|
-
} catch {
|
|
220
|
-
// Best-effort throttle write; proceed with the DB update regardless.
|
|
221
|
-
}
|
|
222
|
-
|
|
223
195
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
224
|
-
const
|
|
196
|
+
const liveness: any = {
|
|
225
197
|
lastSeenAt: OneUptimeDate.getCurrentDate(),
|
|
226
198
|
otelCollectorStatus: "connected",
|
|
227
199
|
};
|
|
228
200
|
|
|
201
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
202
|
+
const metadata: any = {};
|
|
203
|
+
|
|
229
204
|
if (extra?.osType) {
|
|
230
|
-
|
|
205
|
+
metadata.osType = extra.osType;
|
|
231
206
|
}
|
|
232
207
|
if (extra?.osVersion) {
|
|
233
|
-
|
|
208
|
+
metadata.osVersion = extra.osVersion;
|
|
234
209
|
}
|
|
235
210
|
if (extra?.agentVersion) {
|
|
236
|
-
|
|
211
|
+
metadata.agentVersion = extra.agentVersion;
|
|
237
212
|
}
|
|
238
213
|
|
|
239
214
|
/*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
215
|
+
* One gated, non-blocking heartbeat write. The gates, the fail-open /
|
|
216
|
+
* fail-closed split and the liveness-only fallback all live in
|
|
217
|
+
* ResourceHeartbeat — see there for why this row's throttle used to
|
|
218
|
+
* provide no throttling at all.
|
|
243
219
|
*/
|
|
244
|
-
await
|
|
220
|
+
await ResourceHeartbeat.write({
|
|
221
|
+
service: this,
|
|
245
222
|
id: hostId,
|
|
246
|
-
|
|
223
|
+
cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
|
|
224
|
+
throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
|
|
225
|
+
liveness: liveness,
|
|
226
|
+
metadata: metadata,
|
|
227
|
+
fingerprint: extrasFingerprint,
|
|
228
|
+
describe: `docker host ${hostId.toString()}`,
|
|
247
229
|
});
|
|
248
230
|
}
|
|
249
231
|
|