@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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Widen every HashedString column from varchar(64) to varchar(255).
|
|
5
|
+
*
|
|
6
|
+
* A bare SHA-256 digest is 64 hex characters, which is exactly what the old
|
|
7
|
+
* column held. User passwords are now scrypt hashes that carry the cost
|
|
8
|
+
* parameters they were produced with — `scrypt$N=16384,r=8,p=1$<64 hex>`,
|
|
9
|
+
* about 90 characters — so that raising the cost later needs no migration and
|
|
10
|
+
* no password reset. The other HashedString columns (session refresh tokens,
|
|
11
|
+
* master passwords) still store a bare digest and simply do not use the room;
|
|
12
|
+
* they are widened only because they share ColumnLength.HashedString.
|
|
13
|
+
*
|
|
14
|
+
* WHY THIS FILE IS NOT WHAT `migration:generate` PRODUCED.
|
|
15
|
+
*
|
|
16
|
+
* TypeORM does not express a varchar length change as ALTER COLUMN TYPE. It
|
|
17
|
+
* emits DROP COLUMN followed by ADD COLUMN, which for these six columns means
|
|
18
|
+
* deleting every password, every status page and dashboard master password,
|
|
19
|
+
* and every active session in the instance. The generated `up()` also had to
|
|
20
|
+
* drop and recreate the refresh-token unique constraints and indexes, purely
|
|
21
|
+
* to work around its own DROP.
|
|
22
|
+
*
|
|
23
|
+
* ALTER COLUMN TYPE to a WIDER varchar is the correct statement and is
|
|
24
|
+
* cheaper besides: Postgres skips the table rewrite entirely when a varchar
|
|
25
|
+
* is only being lengthened (no rewrite means the unique indexes survive
|
|
26
|
+
* untouched too), so this is a catalog update behind a brief ACCESS EXCLUSIVE
|
|
27
|
+
* lock rather than a full copy of six tables.
|
|
28
|
+
*
|
|
29
|
+
* The resulting schema is identical to what the generated migration would
|
|
30
|
+
* have produced, which is what `npm run check-postgres-schema-drift` verifies.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const WIDENED_COLUMNS: ReadonlyArray<{ table: string; column: string }> = [
|
|
34
|
+
{ table: "User", column: "password" },
|
|
35
|
+
{ table: "StatusPagePrivateUser", column: "password" },
|
|
36
|
+
{ table: "StatusPage", column: "masterPassword" },
|
|
37
|
+
{ table: "Dashboard", column: "masterPassword" },
|
|
38
|
+
{ table: "UserSession", column: "refreshToken" },
|
|
39
|
+
{ table: "StatusPagePrivateUserSession", column: "refreshToken" },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
export class WidenHashedStringColumnsForScrypt1786023262402
|
|
43
|
+
implements MigrationInterface
|
|
44
|
+
{
|
|
45
|
+
public name = "WidenHashedStringColumnsForScrypt1786023262402";
|
|
46
|
+
|
|
47
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
48
|
+
for (const { table, column } of WIDENED_COLUMNS) {
|
|
49
|
+
await queryRunner.query(
|
|
50
|
+
`ALTER TABLE "${table}" ALTER COLUMN "${column}" TYPE character varying(255)`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* Narrowing back to 64 fails loudly if any scrypt hash has been written,
|
|
57
|
+
* because those do not fit. That is the intended behaviour: a down
|
|
58
|
+
* migration that truncated password hashes would lock every user out
|
|
59
|
+
* silently instead of refusing.
|
|
60
|
+
*/
|
|
61
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
62
|
+
for (const { table, column } of [...WIDENED_COLUMNS].reverse()) {
|
|
63
|
+
await queryRunner.query(
|
|
64
|
+
`ALTER TABLE "${table}" ALTER COLUMN "${column}" TYPE character varying(64)`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Restores the expression index that serves the telemetry ingest path's
|
|
5
|
+
* `service.name` lookup, and that an autogenerated migration deleted.
|
|
6
|
+
*
|
|
7
|
+
* WHAT BROKE
|
|
8
|
+
*
|
|
9
|
+
* `OTelIngestService.findOrCreateTelemetryService` resolves an OTLP resource's
|
|
10
|
+
* `service.name` to a Service row through `QueryHelper.findWithSameText`, which
|
|
11
|
+
* emits a case-insensitive predicate:
|
|
12
|
+
*
|
|
13
|
+
* WHERE "projectId" = $1 AND LOWER("name") = $2 AND "deletedAt" IS NULL
|
|
14
|
+
* ORDER BY "createdAt" ASC LIMIT 1
|
|
15
|
+
*
|
|
16
|
+
* Every live index on Service is over the RAW `name` column. Postgres cannot
|
|
17
|
+
* invert `lower()` (it is not order-preserving under any non-C collation) and
|
|
18
|
+
* PG16 has no index-condition pushdown, so `LOWER("name") = $2` degrades to a
|
|
19
|
+
* post-heap-fetch Filter. Only the `projectId` prefix acts as an index qual,
|
|
20
|
+
* which makes each lookup cost O(services in that project) heap fetches plus
|
|
21
|
+
* that many collation-aware lower() calls — not O(log n).
|
|
22
|
+
*
|
|
23
|
+
* That query runs once per OTLP *resource* (hundreds per batch for the shipped
|
|
24
|
+
* host-metrics collector config), again per signal type, from every ingest pod.
|
|
25
|
+
* Production sat at 100% CPU against a 100% buffer cache hit ratio and ~110 B/s
|
|
26
|
+
* of reads: entirely in memory, entirely CPU.
|
|
27
|
+
*
|
|
28
|
+
* WHY IT BROKE
|
|
29
|
+
*
|
|
30
|
+
* The correct index existed. Migration 1774559064921 created:
|
|
31
|
+
*
|
|
32
|
+
* CREATE UNIQUE INDEX "IDX_SERVICE_PROJECT_NAME_UNIQUE"
|
|
33
|
+
* ON "Service" ("projectId", LOWER("name")) WHERE "deletedAt" IS NULL
|
|
34
|
+
*
|
|
35
|
+
* Migration 1775735059360 — otherwise two unrelated column-default changes —
|
|
36
|
+
* dropped it. TypeORM's schema builder matches database indexes to entity
|
|
37
|
+
* metadata BY NAME and drops any it cannot find, and a hand-named expression
|
|
38
|
+
* index created only in a migration is invisible to entity metadata. Its down()
|
|
39
|
+
* "restores" the index as ("projectId") alone, silently losing the expression,
|
|
40
|
+
* which is the signature of a machine-generated statement rather than a
|
|
41
|
+
* deliberate one.
|
|
42
|
+
*
|
|
43
|
+
* The Service entity now declares this index with `synchronize: false`, which
|
|
44
|
+
* makes the schema builder skip it in both directions. Without that decorator
|
|
45
|
+
* this migration would be undone again by the next autogenerated one.
|
|
46
|
+
*
|
|
47
|
+
* WHY THIS IS HAND-WRITTEN
|
|
48
|
+
*
|
|
49
|
+
* AGENTS.md requires migrations to come from `npm run generate-postgres-migration`.
|
|
50
|
+
* That cannot produce this one: `@Index()` has no way to express `LOWER(...)`,
|
|
51
|
+
* so TypeORM can neither generate nor round-trip an expression index. The
|
|
52
|
+
* generator is authoritative for column and table drift; expression indexes are
|
|
53
|
+
* outside what it can model.
|
|
54
|
+
*
|
|
55
|
+
* NOT UNIQUE, DELIBERATELY
|
|
56
|
+
*
|
|
57
|
+
* The dropped original was UNIQUE. Recreating it that way would make this
|
|
58
|
+
* migration fail on any project that already holds two services whose names
|
|
59
|
+
* differ only by case — which the surviving raw-name unique index permits, so
|
|
60
|
+
* such rows can legitimately exist today. Failing a deploy to enforce an
|
|
61
|
+
* invariant nobody is currently relying on is the wrong trade: this index
|
|
62
|
+
* exists to make the lookup fast. `ORDER BY "createdAt" ASC LIMIT 1` in the
|
|
63
|
+
* query already resolves duplicates deterministically (oldest wins), and the
|
|
64
|
+
* raw-name unique index still blocks exact-duplicate races. Restoring
|
|
65
|
+
* case-insensitive uniqueness would need a dedupe pass first, as migration
|
|
66
|
+
* 1778100000000 did, and belongs in its own change.
|
|
67
|
+
*
|
|
68
|
+
* `createdAt` is the third key column so the ORDER BY is satisfied by the index
|
|
69
|
+
* itself and the plan carries no Sort node even when duplicates exist.
|
|
70
|
+
*
|
|
71
|
+
* NOT CONCURRENT, DELIBERATELY
|
|
72
|
+
*
|
|
73
|
+
* DataSourceOptions sets no `migrationsTransactionMode`, so TypeORM defaults to
|
|
74
|
+
* "all" and runs every migration inside ONE transaction. `CREATE INDEX
|
|
75
|
+
* CONCURRENTLY` cannot run inside a transaction block and would abort the whole
|
|
76
|
+
* run. A plain CREATE INDEX takes a SHARE lock that blocks writes to Service
|
|
77
|
+
* for the duration, which is milliseconds at this table's size (thousands of
|
|
78
|
+
* live rows).
|
|
79
|
+
*
|
|
80
|
+
* IF NOT EXISTS makes this a no-op when an operator has already created the
|
|
81
|
+
* index by hand — the recommended incident mitigation is to build it
|
|
82
|
+
* CONCURRENTLY on the live database under this exact name, ahead of the deploy.
|
|
83
|
+
*/
|
|
84
|
+
export class RestoreServiceLowerNameIndex1786100000000
|
|
85
|
+
implements MigrationInterface
|
|
86
|
+
{
|
|
87
|
+
public name: string = "RestoreServiceLowerNameIndex1786100000000";
|
|
88
|
+
|
|
89
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
90
|
+
await queryRunner.query(
|
|
91
|
+
`CREATE INDEX IF NOT EXISTS "IDX_SERVICE_PROJECT_LOWER_NAME" ON "Service" ("projectId", LOWER("name"), "createdAt") WHERE "deletedAt" IS NULL`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
96
|
+
await queryRunner.query(
|
|
97
|
+
`DROP INDEX IF EXISTS "public"."IDX_SERVICE_PROJECT_LOWER_NAME"`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Restores nine UNIQUE indexes that autogenerated migrations deleted, and that
|
|
5
|
+
* no migration ever put back.
|
|
6
|
+
*
|
|
7
|
+
* HOW THEY WENT MISSING
|
|
8
|
+
*
|
|
9
|
+
* TypeORM's schema builder matches database indexes to entity metadata BY NAME
|
|
10
|
+
* (RdbmsSchemaBuilder.dropOldIndices) and drops every index it cannot find a
|
|
11
|
+
* match for. A hand-named index created only in a migration is invisible to
|
|
12
|
+
* entity metadata, so the next `migration:generate` emits a DROP for it, mixed
|
|
13
|
+
* in with whatever change was actually intended.
|
|
14
|
+
*
|
|
15
|
+
* Found by auditing every hand-named index ever created in a migration up()
|
|
16
|
+
* against a database with all 504 registered migrations applied. 308 such
|
|
17
|
+
* indexes exist; 302 are absent from the migrated schema. Almost all of those
|
|
18
|
+
* are benign — TypeORM re-created an equivalent plain index under its own
|
|
19
|
+
* canonical IDX_<hash> name. The ten that were NOT benign carried a UNIQUE,
|
|
20
|
+
* partial, or expression definition that TypeORM cannot express and therefore
|
|
21
|
+
* never re-created. One of the ten (the Service LOWER(name) index) was restored
|
|
22
|
+
* separately in migration 1786100000000. This handles the other nine.
|
|
23
|
+
*
|
|
24
|
+
* Each one is a lost integrity guarantee, not just a lost index:
|
|
25
|
+
*
|
|
26
|
+
* slug uniqueness CephCluster, DockerHost, IoTFleet, KubernetesCluster,
|
|
27
|
+
* NetworkDevice, ProxmoxCluster — the slug is the public URL
|
|
28
|
+
* identifier for the resource. These six tables currently
|
|
29
|
+
* have NO index on slug at all, so slug lookups are also
|
|
30
|
+
* sequential scans.
|
|
31
|
+
* SSO/OIDC binding GlobalOIDCProject, GlobalSSOProject — one mapping row per
|
|
32
|
+
* (provider, project). Duplicates make a project's identity
|
|
33
|
+
* binding ambiguous.
|
|
34
|
+
* interface ids NetworkInterface — a device cannot have two live
|
|
35
|
+
* interfaces at the same SNMP ifIndex. App-level
|
|
36
|
+
* @UniqueColumnBy does a find-then-insert, which races under
|
|
37
|
+
* concurrency exactly as it did for Service before migration
|
|
38
|
+
* 1778100000000 added a DB constraint.
|
|
39
|
+
*
|
|
40
|
+
* PARTIAL, WHERE THE ORIGINALS MOSTLY WERE NOT
|
|
41
|
+
*
|
|
42
|
+
* Only IDX_network_interface_device_ifindex was originally partial. The other
|
|
43
|
+
* eight were unqualified UNIQUE indexes, and they are restored here as partial
|
|
44
|
+
* on `deletedAt IS NULL`. This is a deliberate change, for three reasons:
|
|
45
|
+
*
|
|
46
|
+
* 1. It matches how the data is actually read. Every query the app issues
|
|
47
|
+
* carries `deletedAt IS NULL` (DatabaseService adds it to every read that
|
|
48
|
+
* is not explicitly withDeleted), so nothing loses index coverage.
|
|
49
|
+
* 2. An unqualified unique index lets a soft-deleted row hold a slug
|
|
50
|
+
* hostage forever — delete a cluster and you can never create another one
|
|
51
|
+
* that slugifies the same way. That is a bug, and restoring it verbatim
|
|
52
|
+
* would restore the bug with it.
|
|
53
|
+
* 3. It makes the repair below non-destructive: a duplicate can be taken out
|
|
54
|
+
* of the index by soft-deleting it rather than by hard-deleting a row.
|
|
55
|
+
*
|
|
56
|
+
* REPAIR BEFORE CREATE
|
|
57
|
+
*
|
|
58
|
+
* A UNIQUE index cannot be created over existing duplicates, and a migration
|
|
59
|
+
* that can fail on real data is a migration that blocks a deploy. Each index is
|
|
60
|
+
* therefore preceded by a repair step that is deterministic (oldest row always
|
|
61
|
+
* wins: createdAt, then _id) and non-destructive:
|
|
62
|
+
*
|
|
63
|
+
* - slugs are RENAMED, not deleted. The losing row keeps all its data and
|
|
64
|
+
* gets a discriminator appended. Slugs already end in a random 10-digit
|
|
65
|
+
* suffix (see Common/Utils/Slug.ts), so collisions should not exist at all;
|
|
66
|
+
* this is a guard, not an expected path.
|
|
67
|
+
* - duplicate mapping / interface rows are SOFT-deleted, which removes them
|
|
68
|
+
* from a partial index without destroying anything. Nothing is hard-deleted
|
|
69
|
+
* and no foreign key is touched, so no cascade can fire.
|
|
70
|
+
*
|
|
71
|
+
* Every statement is idempotent: the repairs are no-ops when there are no
|
|
72
|
+
* duplicates, and each CREATE uses IF NOT EXISTS so an index built by hand
|
|
73
|
+
* ahead of the deploy does not fail the migration.
|
|
74
|
+
*
|
|
75
|
+
* NOT CONCURRENT
|
|
76
|
+
*
|
|
77
|
+
* DataSourceOptions sets no `migrationsTransactionMode`, so TypeORM defaults to
|
|
78
|
+
* "all" and runs every migration inside one transaction. CREATE INDEX
|
|
79
|
+
* CONCURRENTLY cannot run inside a transaction block and would abort the run.
|
|
80
|
+
* These tables are small (infrastructure inventory, not telemetry), so the
|
|
81
|
+
* brief SHARE lock is not a concern.
|
|
82
|
+
*
|
|
83
|
+
* HAND-WRITTEN
|
|
84
|
+
*
|
|
85
|
+
* AGENTS.md requires migrations to come from generate-postgres-migration. It
|
|
86
|
+
* cannot produce this one: @Index() can express neither a partial index nor the
|
|
87
|
+
* repair steps. The entities declare each index with `synchronize: false` so
|
|
88
|
+
* the generator leaves them alone from here on.
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
interface SlugTable {
|
|
92
|
+
table: string;
|
|
93
|
+
indexName: string;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const SLUG_TABLES: Array<SlugTable> = [
|
|
97
|
+
{ table: "CephCluster", indexName: "IDX_ceph_cluster_slug" },
|
|
98
|
+
{ table: "DockerHost", indexName: "IDX_docker_host_slug" },
|
|
99
|
+
{ table: "IoTFleet", indexName: "IDX_iot_fleet_slug" },
|
|
100
|
+
{ table: "KubernetesCluster", indexName: "IDX_kubernetes_cluster_slug" },
|
|
101
|
+
{ table: "NetworkDevice", indexName: "IDX_network_device_slug" },
|
|
102
|
+
{ table: "ProxmoxCluster", indexName: "IDX_proxmox_cluster_slug" },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
interface PairIndex {
|
|
106
|
+
table: string;
|
|
107
|
+
indexName: string;
|
|
108
|
+
columns: Array<string>;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const PAIR_INDEXES: Array<PairIndex> = [
|
|
112
|
+
{
|
|
113
|
+
table: "GlobalOIDCProject",
|
|
114
|
+
indexName: "IDX_GlobalOIDCProject_globalOidcId_projectId",
|
|
115
|
+
columns: ["globalOidcId", "projectId"],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
table: "GlobalSSOProject",
|
|
119
|
+
indexName: "IDX_GlobalSSOProject_globalSsoId_projectId",
|
|
120
|
+
columns: ["globalSsoId", "projectId"],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
table: "NetworkInterface",
|
|
124
|
+
indexName: "IDX_network_interface_device_ifindex",
|
|
125
|
+
columns: ["networkDeviceId", "interfaceIndex"],
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
export class RestoreDroppedUniqueIndexes1786200000000
|
|
130
|
+
implements MigrationInterface
|
|
131
|
+
{
|
|
132
|
+
public name: string = "RestoreDroppedUniqueIndexes1786200000000";
|
|
133
|
+
|
|
134
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
135
|
+
/*
|
|
136
|
+
* Slug tables: rename the losers rather than removing them. Oldest row
|
|
137
|
+
* keeps the slug; every other live row with the same slug gets the first
|
|
138
|
+
* eight characters of its own _id appended, which is unique by
|
|
139
|
+
* construction and keeps the slug URL-safe.
|
|
140
|
+
*/
|
|
141
|
+
for (const { table, indexName } of SLUG_TABLES) {
|
|
142
|
+
await queryRunner.query(`
|
|
143
|
+
WITH ranked AS (
|
|
144
|
+
SELECT _id,
|
|
145
|
+
ROW_NUMBER() OVER (
|
|
146
|
+
PARTITION BY "slug"
|
|
147
|
+
ORDER BY "createdAt" ASC, _id ASC
|
|
148
|
+
) AS rn
|
|
149
|
+
FROM "${table}"
|
|
150
|
+
WHERE "deletedAt" IS NULL AND "slug" IS NOT NULL
|
|
151
|
+
)
|
|
152
|
+
UPDATE "${table}" t
|
|
153
|
+
SET "slug" = t."slug" || '-' || LEFT(t._id::text, 8)
|
|
154
|
+
FROM ranked r
|
|
155
|
+
WHERE t._id = r._id AND r.rn > 1;
|
|
156
|
+
`);
|
|
157
|
+
|
|
158
|
+
await queryRunner.query(
|
|
159
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS "${indexName}" ON "${table}" ("slug") WHERE "deletedAt" IS NULL`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/*
|
|
164
|
+
* Composite tables: soft-delete the losers. The index is partial on
|
|
165
|
+
* `deletedAt IS NULL`, so stamping deletedAt removes the row from the
|
|
166
|
+
* index while leaving every column intact and every foreign key valid.
|
|
167
|
+
*/
|
|
168
|
+
for (const { table, indexName, columns } of PAIR_INDEXES) {
|
|
169
|
+
const partitionBy: string = columns
|
|
170
|
+
.map((column: string): string => {
|
|
171
|
+
return `"${column}"`;
|
|
172
|
+
})
|
|
173
|
+
.join(", ");
|
|
174
|
+
|
|
175
|
+
const notNull: string = columns
|
|
176
|
+
.map((column: string): string => {
|
|
177
|
+
return `"${column}" IS NOT NULL`;
|
|
178
|
+
})
|
|
179
|
+
.join(" AND ");
|
|
180
|
+
|
|
181
|
+
await queryRunner.query(`
|
|
182
|
+
WITH ranked AS (
|
|
183
|
+
SELECT _id,
|
|
184
|
+
ROW_NUMBER() OVER (
|
|
185
|
+
PARTITION BY ${partitionBy}
|
|
186
|
+
ORDER BY "createdAt" ASC, _id ASC
|
|
187
|
+
) AS rn
|
|
188
|
+
FROM "${table}"
|
|
189
|
+
WHERE "deletedAt" IS NULL AND ${notNull}
|
|
190
|
+
)
|
|
191
|
+
UPDATE "${table}" t
|
|
192
|
+
SET "deletedAt" = CURRENT_TIMESTAMP
|
|
193
|
+
FROM ranked r
|
|
194
|
+
WHERE t._id = r._id AND r.rn > 1;
|
|
195
|
+
`);
|
|
196
|
+
|
|
197
|
+
await queryRunner.query(
|
|
198
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS "${indexName}" ON "${table}" (${partitionBy}) WHERE "deletedAt" IS NULL`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
204
|
+
/*
|
|
205
|
+
* Only the indexes are reversible. The repair is not: a renamed slug and a
|
|
206
|
+
* soft-deleted duplicate cannot be distinguished afterwards from one the
|
|
207
|
+
* application produced, and guessing would corrupt live data. Dropping the
|
|
208
|
+
* indexes is enough to restore the previous schema shape.
|
|
209
|
+
*/
|
|
210
|
+
for (const { indexName } of [...SLUG_TABLES, ...PAIR_INDEXES]) {
|
|
211
|
+
await queryRunner.query(`DROP INDEX IF EXISTS "public"."${indexName}"`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* GHSA-xx95-gmcf-7q86 — data-side remediation.
|
|
5
|
+
*
|
|
6
|
+
* The code fix closes every way to write an installation ID that a project
|
|
7
|
+
* does not own, and re-derives the binding from Project.gitHubAppInstallationId
|
|
8
|
+
* before minting a token. Neither reaches rows that are ALREADY in the
|
|
9
|
+
* database. Before the fix a caller could put any installation ID on a
|
|
10
|
+
* CodeRepository row in a project they owned, and several code paths (the AI
|
|
11
|
+
* chat read/write tools, the pull-request sync worker) mint from that column
|
|
12
|
+
* directly. So any such row left in place stays a live cross-tenant token.
|
|
13
|
+
*
|
|
14
|
+
* This migration NULLs the installation ID on every repository row that
|
|
15
|
+
* contradicts its own project. That is decidable and safe: the project's own
|
|
16
|
+
* binding is the authority, so a row disagreeing with it was either poisoned
|
|
17
|
+
* or is stale after a reinstall. Nothing is deleted — the repository row keeps
|
|
18
|
+
* its name, commands and history, and reconnecting the GitHub App repopulates
|
|
19
|
+
* the ID through the verified callback.
|
|
20
|
+
*
|
|
21
|
+
* Hand-written on purpose: this changes no schema, so the entity-diff
|
|
22
|
+
* generator (npm run generate-postgres-migration) produces nothing for it, and
|
|
23
|
+
* the schema-drift check is unaffected.
|
|
24
|
+
*
|
|
25
|
+
* WHAT THIS MIGRATION DELIBERATELY DOES NOT DO
|
|
26
|
+
*
|
|
27
|
+
* It does not clear Project.gitHubAppInstallationId. Every one of those was
|
|
28
|
+
* written by the old, unverified callback, but they are overwhelmingly
|
|
29
|
+
* legitimate and nulling them would disconnect every existing GitHub
|
|
30
|
+
* integration. There is also no way in SQL to tell a forged binding from a
|
|
31
|
+
* real one.
|
|
32
|
+
*
|
|
33
|
+
* Operators who want to audit for the callback-forgery path should look for
|
|
34
|
+
* one installation bound to several projects, which is the fingerprint (it is
|
|
35
|
+
* legal — an org can connect one installation to several projects — so review
|
|
36
|
+
* the results, do not delete them):
|
|
37
|
+
*
|
|
38
|
+
* SELECT "gitHubAppInstallationId", count(*), array_agg("_id")
|
|
39
|
+
* FROM "Project"
|
|
40
|
+
* WHERE "gitHubAppInstallationId" IS NOT NULL AND "deletedAt" IS NULL
|
|
41
|
+
* GROUP BY "gitHubAppInstallationId" HAVING count(*) > 1;
|
|
42
|
+
*/
|
|
43
|
+
export class QuarantineUnboundGitHubInstallations1786300000000
|
|
44
|
+
implements MigrationInterface
|
|
45
|
+
{
|
|
46
|
+
public name = "QuarantineUnboundGitHubInstallations1786300000000";
|
|
47
|
+
|
|
48
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
49
|
+
await queryRunner.query(`
|
|
50
|
+
UPDATE "CodeRepository" AS cr
|
|
51
|
+
SET "gitHubAppInstallationId" = NULL
|
|
52
|
+
FROM "Project" AS p
|
|
53
|
+
WHERE cr."projectId" = p."_id"
|
|
54
|
+
AND cr."gitHubAppInstallationId" IS NOT NULL
|
|
55
|
+
AND (
|
|
56
|
+
p."gitHubAppInstallationId" IS NULL
|
|
57
|
+
OR p."gitHubAppInstallationId" <> cr."gitHubAppInstallationId"
|
|
58
|
+
)
|
|
59
|
+
`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public async down(): Promise<void> {
|
|
63
|
+
/*
|
|
64
|
+
* Not reversible, and must not be. The quarantined values are exactly the
|
|
65
|
+
* bindings that could not be justified; restoring them would restore the
|
|
66
|
+
* vulnerability, and the rows carry no record of what was cleared.
|
|
67
|
+
*/
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -498,6 +498,12 @@ import { WidenInventoryResourceNameColumns1785930000000 } from "./1785930000000-
|
|
|
498
498
|
import { AddStatusPageReportPeriod1785930709405 } from "./1785930709405-AddStatusPageReportPeriod";
|
|
499
499
|
import { AddIncidentInvestigationGating1785790000000 } from "./1785790000000-AddIncidentInvestigationGating";
|
|
500
500
|
import { AddRemediationVerification1785768089408 } from "./1785768089408-AddRemediationVerification";
|
|
501
|
+
import { AddStatusPageMonitorRule1786005052769 } from "./1786005052769-AddStatusPageMonitorRule";
|
|
502
|
+
import { AddPerUserPasswordSalt1786018109307 } from "./1786018109307-AddPerUserPasswordSalt";
|
|
503
|
+
import { WidenHashedStringColumnsForScrypt1786023262402 } from "./1786023262402-WidenHashedStringColumnsForScrypt";
|
|
504
|
+
import { RestoreServiceLowerNameIndex1786100000000 } from "./1786100000000-RestoreServiceLowerNameIndex";
|
|
505
|
+
import { RestoreDroppedUniqueIndexes1786200000000 } from "./1786200000000-RestoreDroppedUniqueIndexes";
|
|
506
|
+
import { QuarantineUnboundGitHubInstallations1786300000000 } from "./1786300000000-QuarantineUnboundGitHubInstallations";
|
|
501
507
|
|
|
502
508
|
export default [
|
|
503
509
|
InitialMigration,
|
|
@@ -1000,4 +1006,10 @@ export default [
|
|
|
1000
1006
|
IncreaseHostIpAddressesLength1785915638551,
|
|
1001
1007
|
WidenInventoryResourceNameColumns1785930000000,
|
|
1002
1008
|
AddStatusPageReportPeriod1785930709405,
|
|
1009
|
+
AddStatusPageMonitorRule1786005052769,
|
|
1010
|
+
AddPerUserPasswordSalt1786018109307,
|
|
1011
|
+
WidenHashedStringColumnsForScrypt1786023262402,
|
|
1012
|
+
RestoreServiceLowerNameIndex1786100000000,
|
|
1013
|
+
RestoreDroppedUniqueIndexes1786200000000,
|
|
1014
|
+
QuarantineUnboundGitHubInstallations1786300000000,
|
|
1003
1015
|
];
|
|
@@ -19,6 +19,7 @@ import { JSONObject } from "../../Types/JSON";
|
|
|
19
19
|
import AlertInternalNote from "../../Models/DatabaseModels/AlertInternalNote";
|
|
20
20
|
import AlertInternalNoteService from "./AlertInternalNoteService";
|
|
21
21
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
22
|
+
import ProjectScopedReferenceValidator from "../Utils/Database/ProjectScopedReferenceValidator";
|
|
22
23
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
23
24
|
import AlertFeedService from "./AlertFeedService";
|
|
24
25
|
import { AlertFeedEventType } from "../../Models/DatabaseModels/AlertFeed";
|
|
@@ -118,6 +119,25 @@ export class Service extends DatabaseService<AlertStateTimeline> {
|
|
|
118
119
|
throw new BadDataException("alertStateId is null");
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
/*
|
|
123
|
+
* Same guard, same reason, as MonitorStatusTimelineService: an id that
|
|
124
|
+
* exists nowhere (or belongs to another project) used to reach Postgres
|
|
125
|
+
* and come back as a raw foreign key violation. The ordering check below
|
|
126
|
+
* does look the state up, but only when there IS a preceding row with an
|
|
127
|
+
* order, so the first timeline row for an alert went through unchecked.
|
|
128
|
+
*/
|
|
129
|
+
await ProjectScopedReferenceValidator.validateReferencesBelongToProject({
|
|
130
|
+
projectId: createBy.props.tenantId || createBy.data.projectId,
|
|
131
|
+
subject: "alert state timeline",
|
|
132
|
+
references: [
|
|
133
|
+
{
|
|
134
|
+
modelName: "Alert State",
|
|
135
|
+
id: alertStateId,
|
|
136
|
+
service: AlertStateService,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
|
|
121
141
|
const stateBeforeThis: AlertStateTimeline | null = await this.findOneBy({
|
|
122
142
|
query: {
|
|
123
143
|
alertId: createBy.data.alertId,
|
|
@@ -1440,6 +1440,75 @@ export default class AnalyticsDatabaseService<
|
|
|
1440
1440
|
return { statement, columns: select.columns };
|
|
1441
1441
|
}
|
|
1442
1442
|
|
|
1443
|
+
/**
|
|
1444
|
+
* Append the unique `_id` as a final sort key so the ORDER BY is a TOTAL
|
|
1445
|
+
* order.
|
|
1446
|
+
*
|
|
1447
|
+
* `ORDER BY time DESC` alone is not: rows tying on `time` may come back
|
|
1448
|
+
* in any order, and ClickHouse is free to order them differently between
|
|
1449
|
+
* two executions of the same query (part order, thread scheduling and
|
|
1450
|
+
* `optimize_read_in_order` all feed into it). Paging with LIMIT/OFFSET on
|
|
1451
|
+
* top of a non-total order silently drops rows at page boundaries and
|
|
1452
|
+
* repeats others: a row that sat at offset 49 on page 1 can shift to
|
|
1453
|
+
* offset 50 before page 2 is fetched, so nobody ever sees it.
|
|
1454
|
+
*
|
|
1455
|
+
* Ties are the normal case, not an edge case — a service emitting a burst
|
|
1456
|
+
* of logs writes many rows with an identical timestamp.
|
|
1457
|
+
*
|
|
1458
|
+
* `_id` is stamped per row with `ObjectID.generateTimeOrdered()`, so it is
|
|
1459
|
+
* unique and its ordering agrees with time order.
|
|
1460
|
+
*
|
|
1461
|
+
* Two cases must NOT get the tiebreaker:
|
|
1462
|
+
* - GROUP BY queries, where `_id` is neither a grouping key nor an
|
|
1463
|
+
* aggregate, so referencing it is an error.
|
|
1464
|
+
* - Models with no `_id` column: derived aggregate targets deliberately
|
|
1465
|
+
* omit it because the aggregation key is the row identity (see _findBy).
|
|
1466
|
+
*/
|
|
1467
|
+
private toPaginationStableSort(
|
|
1468
|
+
sort: Sort<TBaseModel>,
|
|
1469
|
+
options: { hasGroupBy: boolean },
|
|
1470
|
+
): Sort<TBaseModel> {
|
|
1471
|
+
if (options.hasGroupBy) {
|
|
1472
|
+
return sort;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
const sortKeys: Array<string> = Object.keys(sort || {});
|
|
1476
|
+
|
|
1477
|
+
// Already a total order; adding `_id` again would be a no-op term.
|
|
1478
|
+
if (sortKeys.includes("_id")) {
|
|
1479
|
+
return sort;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const hasIdColumn: boolean = this.model.tableColumns.some(
|
|
1483
|
+
(column: AnalyticsTableColumn): boolean => {
|
|
1484
|
+
return column.key === "_id";
|
|
1485
|
+
},
|
|
1486
|
+
);
|
|
1487
|
+
|
|
1488
|
+
if (!hasIdColumn) {
|
|
1489
|
+
return sort;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
/*
|
|
1493
|
+
* Tie-break in the same direction as the last declared sort key, so a
|
|
1494
|
+
* newest-first list stays newest-first within a single timestamp.
|
|
1495
|
+
*/
|
|
1496
|
+
const lastSortKey: string | undefined = sortKeys[sortKeys.length - 1];
|
|
1497
|
+
|
|
1498
|
+
const direction: SortOrder =
|
|
1499
|
+
(lastSortKey
|
|
1500
|
+
? ((sort as Record<string, SortOrder | undefined>)[lastSortKey] as
|
|
1501
|
+
| SortOrder
|
|
1502
|
+
| undefined)
|
|
1503
|
+
: undefined) ?? SortOrder.Descending;
|
|
1504
|
+
|
|
1505
|
+
/*
|
|
1506
|
+
* `_id` is absent from `sort` (checked above), so spreading appends it
|
|
1507
|
+
* last and the caller's own keys keep their relative order.
|
|
1508
|
+
*/
|
|
1509
|
+
return { ...sort, _id: direction } as Sort<TBaseModel>;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1443
1512
|
public toFindStatement(findBy: FindBy<TBaseModel>): {
|
|
1444
1513
|
statement: Statement;
|
|
1445
1514
|
columns: Array<string>;
|
|
@@ -1470,7 +1539,9 @@ export default class AnalyticsDatabaseService<
|
|
|
1470
1539
|
);
|
|
1471
1540
|
|
|
1472
1541
|
const sortStatement: Statement = this.statementGenerator.toSortStatement(
|
|
1473
|
-
findBy.sort!,
|
|
1542
|
+
this.toPaginationStableSort(findBy.sort!, {
|
|
1543
|
+
hasGroupBy: Boolean(groupByStatement),
|
|
1544
|
+
}),
|
|
1474
1545
|
);
|
|
1475
1546
|
|
|
1476
1547
|
const statement: Statement = SQL``;
|