@oneuptime/common 12.0.2 → 12.0.4
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/CephCluster.ts +14 -0
- package/Models/DatabaseModels/CodeRepository.ts +14 -6
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/DockerHost.ts +14 -0
- package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
- package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/IoTFleet.ts +14 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/NetworkDevice.ts +14 -0
- package/Models/DatabaseModels/NetworkInterface.ts +18 -0
- package/Models/DatabaseModels/Project.ts +16 -2
- package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
- package/Models/DatabaseModels/Service.ts +30 -0
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Models/DatabaseModels/User.ts +30 -0
- package/Server/API/AIAgentDataAPI.ts +31 -0
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +458 -12
- package/Server/API/GitHubAPI.ts +119 -283
- package/Server/API/UserAPI.ts +263 -1
- package/Server/EnvironmentConfig.ts +38 -4
- package/Server/Infrastructure/GlobalCache.ts +152 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/AnalyticsDatabaseService.ts +72 -1
- package/Server/Services/CephClusterService.ts +26 -86
- package/Server/Services/CloudResourceService.ts +23 -83
- package/Server/Services/CodeRepositoryService.ts +105 -2
- package/Server/Services/DatabaseService.ts +296 -2
- 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/OpenTelemetryIngestService.ts +230 -26
- 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/Services/UserService.ts +184 -0
- package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
- package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -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/PasswordHash.ts +306 -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/OverviewCustomFields.test.tsx +333 -0
- package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -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/AIAgentDataRepositoryToken.test.ts +381 -0
- package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
- package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
- package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
- package/Tests/Server/API/Helpers.ts +6 -1
- package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +354 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
- package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
- package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
- package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -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/OpenTelemetryServiceResolutionCache.test.ts +568 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
- package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
- package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
- 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/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
- package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
- package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
- package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
- package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
- package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -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/PasswordHash.test.ts +555 -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/ColumnLength.test.ts +7 -1
- 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/HashedStringPerUserSalt.test.ts +476 -0
- package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
- package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
- package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
- package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
- package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -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/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Tests/Utils/TeamMembersByProject.test.ts +655 -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/ColumnLength.ts +9 -1
- package/Types/Database/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Database/TableColumn.ts +11 -0
- package/Types/Database/UnsynchronizedIndex.ts +48 -0
- package/Types/Decimal.ts +16 -4
- package/Types/HashedString.ts +139 -4
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/UI/Components/Navbar/NavBar.tsx +12 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
- package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/Utils/TeamMembersByProject.ts +237 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- 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/DockerHost.js +16 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.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/IoTFleet.js +16 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.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/NetworkDevice.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +16 -2
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +28 -1
- package/build/dist/Models/DatabaseModels/Service.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/StatusPagePrivateUser.js +32 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
- 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/Models/DatabaseModels/User.js +32 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
- package/build/dist/Server/API/AIAgentDataAPI.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 +343 -10
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GitHubAPI.js +94 -167
- package/build/dist/Server/API/GitHubAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +187 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +34 -4
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +120 -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/1786018109307-AddPerUserPasswordSalt.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -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/AnalyticsDatabaseService.js +57 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.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/CodeRepositoryService.js +90 -2
- package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +246 -2
- 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/OpenTelemetryIngestService.js +155 -25
- package/build/dist/Server/Services/OpenTelemetryIngestService.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/Services/UserService.js +165 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
- 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/PasswordHash.js +237 -0
- package/build/dist/Server/Utils/PasswordHash.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/ColumnLength.js +9 -1
- package/build/dist/Types/Database/ColumnLength.js.map +1 -1
- 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/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
- package/build/dist/Types/Database/UnsynchronizedIndex.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/HashedString.js +95 -5
- package/build/dist/Types/HashedString.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/CustomFields/CustomFieldsDetail.js +51 -10
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.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/UI/Components/Navbar/NavBar.js +3 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.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/TeamMembersByProject.js +145 -0
- package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
- package/package.json +1 -1
|
@@ -30,6 +30,7 @@ import UpdateByID from "../Types/Database/UpdateByID";
|
|
|
30
30
|
import UpdateByIDAndFetch from "../Types/Database/UpdateByIDAndFetch";
|
|
31
31
|
import UpdateOneBy from "../Types/Database/UpdateOneBy";
|
|
32
32
|
import Encryption from "../Utils/Encryption";
|
|
33
|
+
import PasswordHash from "../Utils/PasswordHash";
|
|
33
34
|
import PostgresErrorTranslator from "../Utils/Database/PostgresErrorTranslator";
|
|
34
35
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
35
36
|
import BaseService from "./BaseService";
|
|
@@ -321,6 +322,55 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
321
322
|
return data;
|
|
322
323
|
}
|
|
323
324
|
|
|
325
|
+
/*
|
|
326
|
+
* Hash one HashedString column in place.
|
|
327
|
+
*
|
|
328
|
+
* Declaring `hashSaltColumn` on a column is what marks it a CREDENTIAL —
|
|
329
|
+
* a low-entropy, human-chosen secret. Those go through scrypt (see
|
|
330
|
+
* PasswordHash): deliberately slow and memory-hard, with a fresh per-record
|
|
331
|
+
* salt minted into the sibling column named by the metadata.
|
|
332
|
+
*
|
|
333
|
+
* Columns without a salt column are high-entropy values the server itself
|
|
334
|
+
* generated — session refresh tokens and the like. They keep the fast
|
|
335
|
+
* SHA-256 hash, which is the right tool for them: there is nothing to guess,
|
|
336
|
+
* and they have to stay searchable by hash because that is how they are
|
|
337
|
+
* looked up.
|
|
338
|
+
*
|
|
339
|
+
* The salt is written onto the SAME payload as the hash, so the pair can
|
|
340
|
+
* never be persisted out of step with each other.
|
|
341
|
+
*
|
|
342
|
+
* The salt column grants nobody create or update rights, so it has to get
|
|
343
|
+
* past the column-permission check some other way on each path. On update
|
|
344
|
+
* it does so by timing — sanitizeCreateOrUpdate runs after the check. On
|
|
345
|
+
* create the check runs after `hash()`, so the salt column relies on being
|
|
346
|
+
* declared `computed: true`, which checkDataColumnPermissions skips on
|
|
347
|
+
* create. A salt column that forgets that flag breaks every non-root
|
|
348
|
+
* create of a row carrying a password.
|
|
349
|
+
*/
|
|
350
|
+
private async hashColumnValue(data: {
|
|
351
|
+
payload: TBaseModel | PartialEntity<TBaseModel>;
|
|
352
|
+
columnName: string;
|
|
353
|
+
columnValue: HashedString;
|
|
354
|
+
}): Promise<void> {
|
|
355
|
+
const saltColumnName: string | undefined =
|
|
356
|
+
this.model.getTableColumnMetadata(data.columnName)?.hashSaltColumn;
|
|
357
|
+
|
|
358
|
+
if (!saltColumnName) {
|
|
359
|
+
await data.columnValue.hashValue(EncryptionSecret);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const salt: string = PasswordHash.generateSalt();
|
|
364
|
+
(data.payload as any)[saltColumnName] = salt;
|
|
365
|
+
|
|
366
|
+
data.columnValue.setHashedValue(
|
|
367
|
+
await PasswordHash.hash({
|
|
368
|
+
plainValue: data.columnValue.toString(),
|
|
369
|
+
salt: salt,
|
|
370
|
+
}),
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
|
|
324
374
|
protected async hash(data: TBaseModel): Promise<TBaseModel> {
|
|
325
375
|
const columns: Columns = data.getHashedColumns();
|
|
326
376
|
|
|
@@ -329,13 +379,110 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
329
379
|
data.hasValue(key) &&
|
|
330
380
|
!(data.getValue(key) as HashedString).isValueHashed()
|
|
331
381
|
) {
|
|
332
|
-
await
|
|
382
|
+
await this.hashColumnValue({
|
|
383
|
+
payload: data,
|
|
384
|
+
columnName: key,
|
|
385
|
+
columnValue: (data as any)[key] as HashedString,
|
|
386
|
+
});
|
|
333
387
|
}
|
|
334
388
|
}
|
|
335
389
|
|
|
336
390
|
return data;
|
|
337
391
|
}
|
|
338
392
|
|
|
393
|
+
/*
|
|
394
|
+
* Check a plaintext secret (a password) against a hashed column on a row
|
|
395
|
+
* that has already been read out of the database, and re-hash the stored
|
|
396
|
+
* value in place when it was not written by today's scheme.
|
|
397
|
+
*
|
|
398
|
+
* The row must have been selected with BOTH the hashed column and its salt
|
|
399
|
+
* column. Without the salt a scrypt hash cannot be checked at all, and an
|
|
400
|
+
* older SHA-256 one would be checked against the wrong scheme — either way
|
|
401
|
+
* a perfectly good password gets rejected. PasswordHash.verify throws
|
|
402
|
+
* rather than silently returning false for the scrypt case, because a
|
|
403
|
+
* missing SELECT is a bug and must not look like a bad password.
|
|
404
|
+
*
|
|
405
|
+
* Why the caller cannot just query by the hash any more: with a per-user
|
|
406
|
+
* salt the hash is not computable until the row's own salt is known, so
|
|
407
|
+
* `WHERE password = <hash>` has nothing to bind. Read the row by its
|
|
408
|
+
* identity (email, id) and verify here.
|
|
409
|
+
*
|
|
410
|
+
* THE UPGRADE. Anything that is not exactly what PasswordHash writes today
|
|
411
|
+
* is re-hashed once the password is known to be correct: the two SHA-256
|
|
412
|
+
* schemes that preceded scrypt, and any scrypt hash whose cost parameters
|
|
413
|
+
* have since been raised. That is what makes raising the cost a one-line
|
|
414
|
+
* change — users migrate as they log in, with no reset and no migration.
|
|
415
|
+
*
|
|
416
|
+
* The write is deliberately hook-free and leaves updatedAt alone: the user
|
|
417
|
+
* did not change their password, the server changed how it stores it.
|
|
418
|
+
* Firing the password-change hooks would revoke every session and mail the
|
|
419
|
+
* user about a change they did not make. A failed upgrade is logged and
|
|
420
|
+
* swallowed — a correct password must not be rejected because a
|
|
421
|
+
* bookkeeping write lost a race.
|
|
422
|
+
*/
|
|
423
|
+
@CaptureSpan()
|
|
424
|
+
public async verifyHashedColumnValue(data: {
|
|
425
|
+
item: TBaseModel;
|
|
426
|
+
columnName: string;
|
|
427
|
+
plainValue: string;
|
|
428
|
+
}): Promise<boolean> {
|
|
429
|
+
const storedHash: string | undefined = (data.item as any)[
|
|
430
|
+
data.columnName
|
|
431
|
+
]?.toString();
|
|
432
|
+
|
|
433
|
+
if (!storedHash || !data.plainValue) {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const saltColumnName: string | undefined =
|
|
438
|
+
this.model.getTableColumnMetadata(data.columnName)?.hashSaltColumn;
|
|
439
|
+
|
|
440
|
+
const salt: string | null = saltColumnName
|
|
441
|
+
? ((data.item as any)[saltColumnName] as string | undefined) || null
|
|
442
|
+
: null;
|
|
443
|
+
|
|
444
|
+
const isValid: boolean = await PasswordHash.verify({
|
|
445
|
+
plainValue: data.plainValue,
|
|
446
|
+
storedValue: storedHash,
|
|
447
|
+
salt: salt,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
if (!isValid) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (
|
|
455
|
+
saltColumnName &&
|
|
456
|
+
data.item.id &&
|
|
457
|
+
PasswordHash.needsUpgrade(storedHash)
|
|
458
|
+
) {
|
|
459
|
+
try {
|
|
460
|
+
const newSalt: string = PasswordHash.generateSalt();
|
|
461
|
+
|
|
462
|
+
await this.updateColumnsByIdWithoutHooks({
|
|
463
|
+
id: data.item.id,
|
|
464
|
+
data: {
|
|
465
|
+
[data.columnName]: await PasswordHash.hash({
|
|
466
|
+
plainValue: data.plainValue,
|
|
467
|
+
salt: newSalt,
|
|
468
|
+
}),
|
|
469
|
+
[saltColumnName]: newSalt,
|
|
470
|
+
} as unknown as PartialEntity<TBaseModel>,
|
|
471
|
+
skipUpdateDateColumn: true,
|
|
472
|
+
});
|
|
473
|
+
} catch (err) {
|
|
474
|
+
/*
|
|
475
|
+
* `data.item` is deliberately left holding the old hash and old salt,
|
|
476
|
+
* so it stays an internally consistent pair whether or not the write
|
|
477
|
+
* above landed.
|
|
478
|
+
*/
|
|
479
|
+
logger.error(err);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
|
|
339
486
|
protected async decrypt(data: TBaseModel): Promise<TBaseModel> {
|
|
340
487
|
for (const key of data.getEncryptedColumns().columns) {
|
|
341
488
|
if (!data.hasValue(key)) {
|
|
@@ -552,6 +699,26 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
552
699
|
if (this.model.isHashedStringColumn(columnName)) {
|
|
553
700
|
const columnValue: JSONValue = (data as any)[columnName];
|
|
554
701
|
|
|
702
|
+
/*
|
|
703
|
+
* A salted column's hash and its salt are only ever correct as a
|
|
704
|
+
* pair. A write that supplies the column as a bare string skips the
|
|
705
|
+
* hashing branch below, leaving the row's existing salt attached to
|
|
706
|
+
* a value it was not computed from — which locks the account out
|
|
707
|
+
* silently, at the next login, far from whatever wrote it. Refuse
|
|
708
|
+
* instead. Callers pass a HashedString; the write path hashes it.
|
|
709
|
+
*/
|
|
710
|
+
if (
|
|
711
|
+
data &&
|
|
712
|
+
columnName &&
|
|
713
|
+
columnValue &&
|
|
714
|
+
!(columnValue instanceof HashedString) &&
|
|
715
|
+
this.model.getTableColumnMetadata(columnName)?.hashSaltColumn
|
|
716
|
+
) {
|
|
717
|
+
throw new BadDataException(
|
|
718
|
+
`${columnName} is a salted column and must be supplied as a HashedString, not a pre-hashed value.`,
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
|
|
555
722
|
if (
|
|
556
723
|
data &&
|
|
557
724
|
columnName &&
|
|
@@ -559,7 +726,23 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
559
726
|
columnValue instanceof HashedString
|
|
560
727
|
) {
|
|
561
728
|
if (!columnValue.isValueHashed()) {
|
|
562
|
-
|
|
729
|
+
/*
|
|
730
|
+
* The update path never runs `hash()`, so this is where an
|
|
731
|
+
* updated password gets both hashed and salted. The salt lands
|
|
732
|
+
* on `data`, which the caller turns into the SET list, so the
|
|
733
|
+
* new hash and the salt it was computed with are written by the
|
|
734
|
+
* same statement.
|
|
735
|
+
*
|
|
736
|
+
* An update matching several rows writes the same salt to all of
|
|
737
|
+
* them, which is correct: they are all being set to the same
|
|
738
|
+
* value by one statement. Per-row salts come from per-row
|
|
739
|
+
* writes.
|
|
740
|
+
*/
|
|
741
|
+
await this.hashColumnValue({
|
|
742
|
+
payload: data,
|
|
743
|
+
columnName: columnName,
|
|
744
|
+
columnValue: columnValue,
|
|
745
|
+
});
|
|
563
746
|
}
|
|
564
747
|
|
|
565
748
|
(data as any)[columnName] = columnValue.toString();
|
|
@@ -2400,6 +2583,117 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2400
2583
|
await repository.manager.query(sql, params);
|
|
2401
2584
|
}
|
|
2402
2585
|
|
|
2586
|
+
/*
|
|
2587
|
+
* Same single-statement, hook-free, no-version-bump write as
|
|
2588
|
+
* `updateColumnsByIdWithoutHooks`, except it YIELDS instead of queueing:
|
|
2589
|
+
* if another transaction already holds the row's lock, this write is
|
|
2590
|
+
* abandoned and the method returns false. It never waits.
|
|
2591
|
+
*
|
|
2592
|
+
* Why this exists. A single-statement UPDATE holds its row lock for a very
|
|
2593
|
+
* short time, but "short" does not mean "safe" once the arrival rate on ONE
|
|
2594
|
+
* row exceeds the service rate. Postgres queues lock waiters strictly, so
|
|
2595
|
+
* every waiter pins a backend for the sum of everyone ahead of it — the
|
|
2596
|
+
* classic convoy. In the outage this was written for, ~25K concurrent
|
|
2597
|
+
* ingest jobs across 100 worker pods funnelled onto ~2,300 Service rows and
|
|
2598
|
+
* produced 1,017 active connections with 892 of them parked on row locks;
|
|
2599
|
+
* the tail had been waiting 3.7 hours. Nothing about that is fixable by
|
|
2600
|
+
* making the statement faster, and no Postgres tier changes it: the queue
|
|
2601
|
+
* is the bug.
|
|
2602
|
+
*
|
|
2603
|
+
* `FOR UPDATE SKIP LOCKED` inverts it. A contended writer does zero work
|
|
2604
|
+
* and returns immediately, so the number of backends blocked on any single
|
|
2605
|
+
* row is capped at ONE no matter how many workers arrive together. The
|
|
2606
|
+
* convoy cannot form — and cannot form even if every upstream throttle
|
|
2607
|
+
* fails open at once (Redis down, fence bug, cache flush). That is the
|
|
2608
|
+
* point: this is the structural backstop underneath the throttles, not a
|
|
2609
|
+
* second copy of them.
|
|
2610
|
+
*
|
|
2611
|
+
* Use ONLY where losing the write is harmless because a concurrent writer
|
|
2612
|
+
* is writing the same thing — liveness timestamps and other idempotent
|
|
2613
|
+
* bookkeeping, whose contended value is by construction the value the
|
|
2614
|
+
* winner is already storing. It is the WRONG primitive for a write carrying
|
|
2615
|
+
* information the winner does not have (a changed attribute); for those the
|
|
2616
|
+
* caller must re-attempt, and `false` is the signal to do it. Returning a
|
|
2617
|
+
* boolean rather than swallowing the skip is deliberate: a silent drop here
|
|
2618
|
+
* would be indistinguishable from a successful write at the call site,
|
|
2619
|
+
* which is exactly how a "reliable" heartbeat quietly stops beating.
|
|
2620
|
+
*
|
|
2621
|
+
* Returns true when the row was updated, false when it was locked (or no
|
|
2622
|
+
* longer exists).
|
|
2623
|
+
*/
|
|
2624
|
+
@CaptureSpan()
|
|
2625
|
+
public async updateColumnsByIdIfUnlockedWithoutHooks(input: {
|
|
2626
|
+
id: ObjectID;
|
|
2627
|
+
data: PartialEntity<TBaseModel>;
|
|
2628
|
+
skipUpdateDateColumn?: boolean;
|
|
2629
|
+
}): Promise<boolean> {
|
|
2630
|
+
if (!input.id) {
|
|
2631
|
+
throw new BadDataException("id is required");
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
const repository: Repository<TBaseModel> = this.getRepository();
|
|
2635
|
+
const metadata: EntityMetadata = repository.metadata;
|
|
2636
|
+
const driver: Driver = repository.manager.connection.driver;
|
|
2637
|
+
|
|
2638
|
+
const setClauses: Array<string> = [];
|
|
2639
|
+
const params: Array<unknown> = [];
|
|
2640
|
+
|
|
2641
|
+
// Clamp on a shallow COPY — see updateColumnsByIdWithoutHooks.
|
|
2642
|
+
const data: ObjectLiteral = this.truncateStringColumnsToMaxLength({
|
|
2643
|
+
...(input.data as ObjectLiteral),
|
|
2644
|
+
});
|
|
2645
|
+
|
|
2646
|
+
for (const [propertyName, value] of Object.entries(data)) {
|
|
2647
|
+
const column: ColumnMetadata | undefined =
|
|
2648
|
+
metadata.findColumnWithPropertyName(propertyName);
|
|
2649
|
+
if (!column) {
|
|
2650
|
+
throw new BadDataException(
|
|
2651
|
+
`updateColumnsByIdIfUnlockedWithoutHooks: unknown column "${propertyName}" on "${metadata.tableName}"`,
|
|
2652
|
+
);
|
|
2653
|
+
}
|
|
2654
|
+
if (typeof value === "function") {
|
|
2655
|
+
throw new BadDataException(
|
|
2656
|
+
`updateColumnsByIdIfUnlockedWithoutHooks: SQL-expression values are not supported (column "${propertyName}"); pass a literal value.`,
|
|
2657
|
+
);
|
|
2658
|
+
}
|
|
2659
|
+
params.push(driver.preparePersistentValue(value, column));
|
|
2660
|
+
setClauses.push(`"${column.databaseName}" = $${params.length}`);
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
if (setClauses.length === 0) {
|
|
2664
|
+
return false;
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
if (metadata.updateDateColumn && !input.skipUpdateDateColumn) {
|
|
2668
|
+
setClauses.push(
|
|
2669
|
+
`"${metadata.updateDateColumn.databaseName}" = CURRENT_TIMESTAMP`,
|
|
2670
|
+
);
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
const primaryColumnName: string =
|
|
2674
|
+
metadata.primaryColumns[0]?.databaseName || "_id";
|
|
2675
|
+
params.push(input.id.toString());
|
|
2676
|
+
|
|
2677
|
+
/*
|
|
2678
|
+
* The lock is taken by the sub-SELECT and inherited by the UPDATE within
|
|
2679
|
+
* the same implicit transaction, so the outer write never blocks either.
|
|
2680
|
+
* RETURNING is how the affected-row count comes back: TypeORM's `query()`
|
|
2681
|
+
* surfaces the driver's rows and a bare UPDATE returns none, so without it
|
|
2682
|
+
* a skipped write is indistinguishable from an applied one.
|
|
2683
|
+
*/
|
|
2684
|
+
const sql: string = `UPDATE "${metadata.tableName}" SET ${setClauses.join(
|
|
2685
|
+
", ",
|
|
2686
|
+
)} WHERE "${primaryColumnName}" IN (SELECT "${primaryColumnName}" FROM "${
|
|
2687
|
+
metadata.tableName
|
|
2688
|
+
}" WHERE "${primaryColumnName}" = $${
|
|
2689
|
+
params.length
|
|
2690
|
+
} FOR UPDATE SKIP LOCKED) RETURNING "${primaryColumnName}"`;
|
|
2691
|
+
|
|
2692
|
+
const result: unknown = await repository.manager.query(sql, params);
|
|
2693
|
+
|
|
2694
|
+
return Array.isArray(result) && result.length > 0;
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2403
2697
|
/*
|
|
2404
2698
|
* Atomically add to numeric columns and set literal columns for one row,
|
|
2405
2699
|
* in a SINGLE auto-committed UPDATE, with no hooks and no `version` bump.
|
|
@@ -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,34 +192,6 @@ 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
196
|
const liveness: any = {
|
|
225
197
|
lastSeenAt: OneUptimeDate.getCurrentDate(),
|
|
@@ -227,66 +199,34 @@ export class Service extends DatabaseService<Model> {
|
|
|
227
199
|
};
|
|
228
200
|
|
|
229
201
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
230
|
-
const
|
|
202
|
+
const metadata: any = {};
|
|
231
203
|
|
|
232
204
|
if (extra?.osType) {
|
|
233
|
-
|
|
205
|
+
metadata.osType = extra.osType;
|
|
234
206
|
}
|
|
235
207
|
if (extra?.osVersion) {
|
|
236
|
-
|
|
208
|
+
metadata.osVersion = extra.osVersion;
|
|
237
209
|
}
|
|
238
210
|
if (extra?.agentVersion) {
|
|
239
|
-
|
|
211
|
+
metadata.agentVersion = extra.agentVersion;
|
|
240
212
|
}
|
|
241
213
|
|
|
242
214
|
/*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* Collector-supplied strings are clamped to their declared column
|
|
248
|
-
* widths inside updateColumnsByIdWithoutHooks — see
|
|
249
|
-
* DatabaseService.truncateStringColumnsToMaxLength.
|
|
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.
|
|
250
219
|
*/
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
* reject the statement, lastSeenAt silently stops advancing and
|
|
262
|
-
* markDisconnectedHosts flips a perfectly healthy Docker host to
|
|
263
|
-
* "disconnected" 15 minutes later — with telemetry still arriving the
|
|
264
|
-
* whole time. That was issue #3006 on the Host path (a host.ip list
|
|
265
|
-
* longer than the then-varchar(500) column). Retry with liveness only
|
|
266
|
-
* so the enrichment is what gets dropped, never the heartbeat.
|
|
267
|
-
*/
|
|
268
|
-
logger.warn(
|
|
269
|
-
`DockerHostService.updateLastSeen: metadata write failed for Docker host ${hostId.toString()}, falling back to a liveness-only update: ${
|
|
270
|
-
err instanceof Error ? err.message : String(err)
|
|
271
|
-
}`,
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
/*
|
|
275
|
-
* The throttle fingerprint was already stored, so without this the
|
|
276
|
-
* next batch would short-circuit on a cache hit and skip the retry
|
|
277
|
-
* for the rest of the window.
|
|
278
|
-
*/
|
|
279
|
-
try {
|
|
280
|
-
await GlobalCache.deleteKey(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
281
|
-
} catch {
|
|
282
|
-
// Best-effort; the fallback write below is what actually matters.
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
await this.updateColumnsByIdWithoutHooks({
|
|
286
|
-
id: hostId,
|
|
287
|
-
data: liveness,
|
|
288
|
-
});
|
|
289
|
-
}
|
|
220
|
+
await ResourceHeartbeat.write({
|
|
221
|
+
service: this,
|
|
222
|
+
id: hostId,
|
|
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()}`,
|
|
229
|
+
});
|
|
290
230
|
}
|
|
291
231
|
|
|
292
232
|
/**
|
|
@@ -5,6 +5,7 @@ import Model from "../../Models/DatabaseModels/DockerSwarmCluster";
|
|
|
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";
|
|
@@ -182,7 +183,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
182
183
|
networkCount?: number | undefined;
|
|
183
184
|
},
|
|
184
185
|
): Promise<void> {
|
|
185
|
-
const cacheKey: string = clusterId.toString();
|
|
186
186
|
const extrasFingerprint: string = crypto
|
|
187
187
|
.createHash("sha1")
|
|
188
188
|
.update(
|
|
@@ -202,34 +202,6 @@ export class Service extends DatabaseService<Model> {
|
|
|
202
202
|
)
|
|
203
203
|
.digest("hex");
|
|
204
204
|
|
|
205
|
-
let cached: string | null = null;
|
|
206
|
-
try {
|
|
207
|
-
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
208
|
-
} catch {
|
|
209
|
-
/*
|
|
210
|
-
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
211
|
-
* cache error must never skip the DB write below, otherwise the
|
|
212
|
-
* resource is wrongly marked "disconnected" while telemetry is
|
|
213
|
-
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
214
|
-
*/
|
|
215
|
-
cached = null;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (cached === extrasFingerprint) {
|
|
219
|
-
return; // same data was written recently
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
try {
|
|
223
|
-
await GlobalCache.setString(
|
|
224
|
-
LAST_SEEN_CACHE_NAMESPACE,
|
|
225
|
-
cacheKey,
|
|
226
|
-
extrasFingerprint,
|
|
227
|
-
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
228
|
-
);
|
|
229
|
-
} catch {
|
|
230
|
-
// Best-effort throttle write; proceed with the DB update regardless.
|
|
231
|
-
}
|
|
232
|
-
|
|
233
205
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
234
206
|
const liveness: any = {
|
|
235
207
|
lastSeenAt: OneUptimeDate.getCurrentDate(),
|
|
@@ -237,92 +209,59 @@ export class Service extends DatabaseService<Model> {
|
|
|
237
209
|
};
|
|
238
210
|
|
|
239
211
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
240
|
-
const
|
|
212
|
+
const metadata: any = {};
|
|
241
213
|
|
|
242
214
|
if (extra?.dockerVersion) {
|
|
243
|
-
|
|
215
|
+
metadata.dockerVersion = extra.dockerVersion;
|
|
244
216
|
}
|
|
245
217
|
if (extra?.swarmId) {
|
|
246
|
-
|
|
218
|
+
metadata.swarmId = extra.swarmId;
|
|
247
219
|
}
|
|
248
220
|
if (extra?.agentVersion) {
|
|
249
|
-
|
|
221
|
+
metadata.agentVersion = extra.agentVersion;
|
|
250
222
|
}
|
|
251
223
|
// Counts: 0 is a legitimate value — gate on undefined, not falsiness.
|
|
252
224
|
if (extra?.nodeCount !== undefined) {
|
|
253
|
-
|
|
225
|
+
metadata.nodeCount = extra.nodeCount;
|
|
254
226
|
}
|
|
255
227
|
if (extra?.readyNodeCount !== undefined) {
|
|
256
|
-
|
|
228
|
+
metadata.readyNodeCount = extra.readyNodeCount;
|
|
257
229
|
}
|
|
258
230
|
if (extra?.managerNodeCount !== undefined) {
|
|
259
|
-
|
|
231
|
+
metadata.managerNodeCount = extra.managerNodeCount;
|
|
260
232
|
}
|
|
261
233
|
if (extra?.serviceCount !== undefined) {
|
|
262
|
-
|
|
234
|
+
metadata.serviceCount = extra.serviceCount;
|
|
263
235
|
}
|
|
264
236
|
if (extra?.taskCount !== undefined) {
|
|
265
|
-
|
|
237
|
+
metadata.taskCount = extra.taskCount;
|
|
266
238
|
}
|
|
267
239
|
if (extra?.runningTaskCount !== undefined) {
|
|
268
|
-
|
|
240
|
+
metadata.runningTaskCount = extra.runningTaskCount;
|
|
269
241
|
}
|
|
270
242
|
if (extra?.stackCount !== undefined) {
|
|
271
|
-
|
|
243
|
+
metadata.stackCount = extra.stackCount;
|
|
272
244
|
}
|
|
273
245
|
if (extra?.networkCount !== undefined) {
|
|
274
|
-
|
|
246
|
+
metadata.networkCount = extra.networkCount;
|
|
275
247
|
}
|
|
276
248
|
|
|
277
249
|
/*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
* Collector-supplied strings are clamped to their declared column
|
|
283
|
-
* widths inside updateColumnsByIdWithoutHooks — see
|
|
284
|
-
* DatabaseService.truncateStringColumnsToMaxLength.
|
|
250
|
+
* One gated, non-blocking heartbeat write. The gates, the fail-open /
|
|
251
|
+
* fail-closed split and the liveness-only fallback all live in
|
|
252
|
+
* ResourceHeartbeat — see there for why this row's throttle used to
|
|
253
|
+
* provide no throttling at all.
|
|
285
254
|
*/
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
* reject the statement, lastSeenAt silently stops advancing and
|
|
297
|
-
* markDisconnectedClusters flips a perfectly healthy Docker Swarm
|
|
298
|
-
* cluster to "disconnected" 15 minutes later — with telemetry still
|
|
299
|
-
* arriving the whole time. That was issue #3006 on the Host path (a
|
|
300
|
-
* host.ip list longer than the then-varchar(500) column). Retry with
|
|
301
|
-
* liveness only so the enrichment is what gets dropped, never the
|
|
302
|
-
* heartbeat.
|
|
303
|
-
*/
|
|
304
|
-
logger.warn(
|
|
305
|
-
`DockerSwarmClusterService.updateLastSeen: metadata write failed for Docker Swarm cluster ${clusterId.toString()}, falling back to a liveness-only update: ${
|
|
306
|
-
err instanceof Error ? err.message : String(err)
|
|
307
|
-
}`,
|
|
308
|
-
);
|
|
309
|
-
|
|
310
|
-
/*
|
|
311
|
-
* The throttle fingerprint was already stored, so without this the
|
|
312
|
-
* next batch would short-circuit on a cache hit and skip the retry
|
|
313
|
-
* for the rest of the window.
|
|
314
|
-
*/
|
|
315
|
-
try {
|
|
316
|
-
await GlobalCache.deleteKey(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
317
|
-
} catch {
|
|
318
|
-
// Best-effort; the fallback write below is what actually matters.
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
await this.updateColumnsByIdWithoutHooks({
|
|
322
|
-
id: clusterId,
|
|
323
|
-
data: liveness,
|
|
324
|
-
});
|
|
325
|
-
}
|
|
255
|
+
await ResourceHeartbeat.write({
|
|
256
|
+
service: this,
|
|
257
|
+
id: clusterId,
|
|
258
|
+
cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
|
|
259
|
+
throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
|
|
260
|
+
liveness: liveness,
|
|
261
|
+
metadata: metadata,
|
|
262
|
+
fingerprint: extrasFingerprint,
|
|
263
|
+
describe: `docker swarm cluster ${clusterId.toString()}`,
|
|
264
|
+
});
|
|
326
265
|
}
|
|
327
266
|
|
|
328
267
|
/**
|