@oneuptime/common 12.0.5 → 12.0.6

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.
Files changed (528) hide show
  1. package/Models/DatabaseModels/AIRun.ts +33 -0
  2. package/Models/DatabaseModels/DataSource.ts +744 -0
  3. package/Models/DatabaseModels/Index.ts +3 -0
  4. package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
  5. package/Models/DatabaseModels/User.ts +2 -0
  6. package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
  7. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
  8. package/Models/DatabaseModels/UserSession.ts +2 -0
  9. package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
  10. package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
  11. package/Server/API/AIInsightAPI.ts +47 -4
  12. package/Server/API/AIInvestigationAPI.ts +25 -0
  13. package/Server/API/DashboardAPI.ts +176 -492
  14. package/Server/API/DataSourceAPI.ts +268 -0
  15. package/Server/API/StatusPageAPI.ts +11 -1
  16. package/Server/API/UserAPI.ts +119 -0
  17. package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  22. package/Server/Infrastructure/Queue.ts +26 -0
  23. package/Server/Infrastructure/QueueWorker.ts +17 -0
  24. package/Server/Middleware/TelemetryIngest.ts +8 -3
  25. package/Server/Services/AIInsightService.ts +132 -12
  26. package/Server/Services/AIRunService.ts +34 -0
  27. package/Server/Services/AIService.ts +17 -2
  28. package/Server/Services/AlertService.ts +38 -32
  29. package/Server/Services/AnalyticsDatabaseService.ts +8 -4
  30. package/Server/Services/DataSourceService.ts +200 -0
  31. package/Server/Services/HostService.ts +92 -0
  32. package/Server/Services/IncidentService.ts +45 -0
  33. package/Server/Services/Index.ts +2 -0
  34. package/Server/Services/MonitorService.ts +49 -0
  35. package/Server/Services/MonitorStatusService.ts +166 -0
  36. package/Server/Services/OpenTelemetryIngestService.ts +255 -184
  37. package/Server/Services/RumSessionPinService.ts +7 -21
  38. package/Server/Services/StatusPageSubscriberService.ts +13 -0
  39. package/Server/Services/UserService.ts +280 -9
  40. package/Server/Static/Vendor/README.md +62 -0
  41. package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
  42. package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
  43. package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
  44. package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
  45. package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
  46. package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
  47. package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
  48. package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
  49. package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
  50. package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
  51. package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
  52. package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
  53. package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
  54. package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
  55. package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
  56. package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
  57. package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
  58. package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
  59. package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
  60. package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
  61. package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
  62. package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
  63. package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
  64. package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
  65. package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
  66. package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
  67. package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
  68. package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
  69. package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
  70. package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
  71. package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
  72. package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
  73. package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
  74. package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
  75. package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
  76. package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
  77. package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
  78. package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
  79. package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
  80. package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
  81. package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
  82. package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
  83. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
  84. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
  85. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
  86. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
  87. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
  88. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
  89. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
  90. package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
  91. package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
  92. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
  93. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
  94. package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
  95. package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
  96. package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
  97. package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
  98. package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
  99. package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
  100. package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
  101. package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
  102. package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
  103. package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
  104. package/Server/Utils/DataSource/EgressGuard.ts +446 -0
  105. package/Server/Utils/DataSource/HttpFetch.ts +221 -0
  106. package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
  107. package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
  108. package/Server/Utils/DataSource/Types.ts +93 -0
  109. package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
  110. package/Server/Utils/InProcessMemo.ts +141 -0
  111. package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
  112. package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
  113. package/Server/Utils/Monitor/MonitorResource.ts +14 -0
  114. package/Server/Utils/Response.ts +12 -1
  115. package/Server/Utils/SSRFProtection.ts +237 -0
  116. package/Server/Utils/StartServer.ts +8 -0
  117. package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
  118. package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
  119. package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
  120. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
  121. package/Server/Utils/Telemetry/Telemetry.ts +112 -1
  122. package/Server/Utils/Telemetry.ts +104 -3
  123. package/Server/Utils/VendorAssets.ts +165 -0
  124. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
  125. package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
  126. package/Server/Views/Partials/Head.ejs +21 -7
  127. package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
  128. package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
  129. package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
  130. package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
  131. package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
  132. package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
  133. package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
  134. package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
  135. package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
  136. package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
  137. package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
  138. package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
  139. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
  140. package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
  141. package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
  142. package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
  143. package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
  144. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
  145. package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
  146. package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
  147. package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
  148. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
  149. package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
  150. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
  151. package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
  152. package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
  153. package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
  154. package/Tests/Server/Services/AIInsightService.test.ts +266 -11
  155. package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
  156. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
  157. package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
  158. package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
  159. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
  160. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
  161. package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
  162. package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
  163. package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
  164. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
  165. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
  166. package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
  167. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
  168. package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
  169. package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
  170. package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
  171. package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
  172. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
  173. package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
  174. package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
  175. package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
  176. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
  177. package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
  178. package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
  179. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
  180. package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
  181. package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
  182. package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
  183. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
  184. package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
  185. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
  186. package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
  187. package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
  188. package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
  189. package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
  190. package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
  191. package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
  192. package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
  193. package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
  194. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
  195. package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
  196. package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
  197. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
  198. package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
  199. package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
  200. package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
  201. package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
  202. package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
  203. package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
  204. package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
  205. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
  206. package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
  207. package/Tests/Server/Utils/Telemetry.test.ts +87 -0
  208. package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
  209. package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
  210. package/Tests/Server/Views/OfflineRender.test.ts +297 -0
  211. package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
  212. package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
  213. package/Tests/Types/Password.test.ts +363 -0
  214. package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
  215. package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
  216. package/Tests/UI/Components/Modal.test.tsx +281 -1
  217. package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
  218. package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
  219. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
  220. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  221. package/Tests/Utils/Analytics.test.ts +23 -0
  222. package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
  223. package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
  224. package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
  225. package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
  226. package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
  227. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
  228. package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
  229. package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
  230. package/Types/AI/AIInsightEvidence.ts +6 -0
  231. package/Types/Analytics/RevenueEvent.ts +31 -0
  232. package/Types/Dashboard/DashboardComponentType.ts +31 -0
  233. package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
  234. package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
  235. package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
  236. package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
  237. package/Types/DataSource/DataSourceLimits.ts +62 -0
  238. package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
  239. package/Types/DataSource/DataSourceTableResult.ts +38 -0
  240. package/Types/DataSource/DataSourceType.ts +213 -0
  241. package/Types/Date.ts +43 -3
  242. package/Types/Password.ts +64 -0
  243. package/Types/Permission.ts +46 -0
  244. package/Types/UserAuthenticationStatus.ts +34 -0
  245. package/UI/Components/Button/Button.tsx +1 -1
  246. package/UI/Components/ErrorBoundary.tsx +459 -108
  247. package/UI/Components/Icon/Icon.tsx +10 -9
  248. package/UI/Components/Modal/ConfirmModal.tsx +6 -1
  249. package/UI/Components/Modal/Modal.tsx +267 -35
  250. package/UI/Components/Modal/ModalFooter.tsx +6 -1
  251. package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
  252. package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
  253. package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
  254. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
  255. package/UI/Components/StatusPage/ResourceList.tsx +570 -0
  256. package/UI/Styles/Theme.css +18 -0
  257. package/UI/Utils/API/RequestOptions.ts +7 -0
  258. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
  259. package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
  260. package/UI/Utils/TestDataSource.ts +61 -0
  261. package/UI/esbuild-config.js +45 -0
  262. package/Utils/Analytics.ts +15 -0
  263. package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
  264. package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
  265. package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
  266. package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
  267. package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
  268. package/Utils/Dashboard/Components/Index.ts +28 -0
  269. package/Utils/DataSource/DataSourceQueryText.ts +94 -0
  270. package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
  271. package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
  272. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
  273. package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
  274. package/Utils/StatusPage/ResourceExplorer.ts +853 -0
  275. package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
  276. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  277. package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
  278. package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
  279. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  280. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  281. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
  282. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
  283. package/build/dist/Models/DatabaseModels/User.js +2 -0
  284. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  285. package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
  286. package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
  287. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
  288. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  289. package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
  290. package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
  291. package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
  292. package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
  293. package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
  294. package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
  295. package/build/dist/Server/API/AIInsightAPI.js +34 -4
  296. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  297. package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
  298. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  299. package/build/dist/Server/API/DashboardAPI.js +119 -433
  300. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  301. package/build/dist/Server/API/DataSourceAPI.js +173 -0
  302. package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
  303. package/build/dist/Server/API/StatusPageAPI.js +11 -1
  304. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  305. package/build/dist/Server/API/UserAPI.js +94 -2
  306. package/build/dist/Server/API/UserAPI.js.map +1 -1
  307. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
  308. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  309. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
  310. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
  311. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
  312. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
  313. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
  314. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js.map +1 -0
  315. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  316. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  317. package/build/dist/Server/Infrastructure/Queue.js +27 -0
  318. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  319. package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
  320. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  321. package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
  322. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  323. package/build/dist/Server/Services/AIInsightService.js +110 -10
  324. package/build/dist/Server/Services/AIInsightService.js.map +1 -1
  325. package/build/dist/Server/Services/AIRunService.js +34 -0
  326. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  327. package/build/dist/Server/Services/AIService.js +16 -2
  328. package/build/dist/Server/Services/AIService.js.map +1 -1
  329. package/build/dist/Server/Services/AlertService.js +28 -35
  330. package/build/dist/Server/Services/AlertService.js.map +1 -1
  331. package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
  332. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  333. package/build/dist/Server/Services/DataSourceService.js +159 -0
  334. package/build/dist/Server/Services/DataSourceService.js.map +1 -0
  335. package/build/dist/Server/Services/HostService.js +74 -0
  336. package/build/dist/Server/Services/HostService.js.map +1 -1
  337. package/build/dist/Server/Services/IncidentService.js +35 -24
  338. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  339. package/build/dist/Server/Services/Index.js +2 -0
  340. package/build/dist/Server/Services/Index.js.map +1 -1
  341. package/build/dist/Server/Services/MonitorService.js +45 -0
  342. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  343. package/build/dist/Server/Services/MonitorStatusService.js +129 -0
  344. package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
  345. package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
  346. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  347. package/build/dist/Server/Services/RumSessionPinService.js +7 -15
  348. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  349. package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
  350. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  351. package/build/dist/Server/Services/UserService.js +241 -10
  352. package/build/dist/Server/Services/UserService.js.map +1 -1
  353. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
  354. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  355. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
  356. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
  357. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
  358. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
  359. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
  360. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  361. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
  362. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  363. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
  364. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  365. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
  366. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  367. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
  368. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  369. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
  370. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
  371. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
  372. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  373. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
  374. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  375. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
  376. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
  377. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
  378. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
  379. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
  380. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
  381. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
  382. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
  383. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
  384. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
  385. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
  386. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
  387. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
  388. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
  389. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
  390. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
  391. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
  392. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
  393. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
  394. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
  395. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
  396. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
  397. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
  398. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
  399. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
  400. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
  401. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
  402. package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
  403. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
  404. package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
  405. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
  406. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
  407. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
  408. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
  409. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
  410. package/build/dist/Server/Utils/DataSource/Types.js +2 -0
  411. package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
  412. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
  413. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
  414. package/build/dist/Server/Utils/InProcessMemo.js +113 -0
  415. package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
  416. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
  417. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
  418. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
  419. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  420. package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
  421. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  422. package/build/dist/Server/Utils/Response.js +9 -1
  423. package/build/dist/Server/Utils/Response.js.map +1 -1
  424. package/build/dist/Server/Utils/SSRFProtection.js +192 -0
  425. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
  426. package/build/dist/Server/Utils/StartServer.js +7 -0
  427. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  428. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
  429. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
  430. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
  431. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
  432. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
  433. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
  434. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
  435. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
  436. package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
  437. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  438. package/build/dist/Server/Utils/Telemetry.js +93 -6
  439. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  440. package/build/dist/Server/Utils/VendorAssets.js +121 -0
  441. package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
  442. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
  443. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  444. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
  445. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  446. package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
  447. package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
  448. package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
  449. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  450. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
  451. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
  452. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
  453. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
  454. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
  455. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
  456. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
  457. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
  458. package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
  459. package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
  460. package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
  461. package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
  462. package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
  463. package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
  464. package/build/dist/Types/DataSource/DataSourceType.js +183 -0
  465. package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
  466. package/build/dist/Types/Date.js +38 -3
  467. package/build/dist/Types/Date.js.map +1 -1
  468. package/build/dist/Types/Password.js +56 -0
  469. package/build/dist/Types/Password.js.map +1 -0
  470. package/build/dist/Types/Permission.js +42 -0
  471. package/build/dist/Types/Permission.js.map +1 -1
  472. package/build/dist/Types/UserAuthenticationStatus.js +2 -0
  473. package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
  474. package/build/dist/UI/Components/ErrorBoundary.js +388 -72
  475. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  476. package/build/dist/UI/Components/Icon/Icon.js +10 -4
  477. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  478. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  479. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  480. package/build/dist/UI/Components/Modal/Modal.js +178 -19
  481. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  482. package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
  483. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  484. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
  485. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  486. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
  487. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  488. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
  489. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
  490. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
  491. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
  492. package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
  493. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
  494. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
  495. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  496. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
  497. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  498. package/build/dist/UI/Utils/TestDataSource.js +39 -0
  499. package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
  500. package/build/dist/Utils/Analytics.js +4 -0
  501. package/build/dist/Utils/Analytics.js.map +1 -1
  502. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
  503. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
  504. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
  505. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
  506. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
  507. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
  508. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
  509. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
  510. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
  511. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
  512. package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
  513. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  514. package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
  515. package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
  516. package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
  517. package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
  518. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
  519. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
  520. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
  521. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
  522. package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
  523. package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
  524. package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
  525. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
  526. package/jest.config.json +1 -0
  527. package/package.json +5 -1
  528. package/tsconfig.json +12 -10
@@ -0,0 +1,200 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/DataSource";
3
+ import CreateBy from "../Types/Database/CreateBy";
4
+ import UpdateBy from "../Types/Database/UpdateBy";
5
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
6
+ import BadDataException from "../../Types/Exception/BadDataException";
7
+ import ObjectID from "../../Types/ObjectID";
8
+ import DataSourceType, {
9
+ DataSourceTypeUtil,
10
+ } from "../../Types/DataSource/DataSourceType";
11
+ import { DataSourceConnectionSettings } from "../Utils/DataSource/Types";
12
+ import Dictionary from "../../Types/Dictionary";
13
+ import { JSONObject } from "../../Types/JSON";
14
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
15
+
16
+ export class Service extends DatabaseService<Model> {
17
+ public constructor() {
18
+ super(Model);
19
+ }
20
+
21
+ /*
22
+ * Per-type shape validation so a broken data source fails at save time
23
+ * with a clear message, not at first query with a connector error.
24
+ */
25
+ private validateDataSourceShape(data: {
26
+ dataSourceType?: DataSourceType | undefined;
27
+ url?: string | undefined;
28
+ databaseHost?: string | undefined;
29
+ }): void {
30
+ const dataSourceType: DataSourceType | undefined = data.dataSourceType;
31
+
32
+ if (!dataSourceType) {
33
+ return; // updates that don't touch the type/host/url skip validation.
34
+ }
35
+
36
+ if (!DataSourceTypeUtil.getAllDataSourceTypes().includes(dataSourceType)) {
37
+ throw new BadDataException(`Unknown data source type: ${dataSourceType}`);
38
+ }
39
+
40
+ if (DataSourceTypeUtil.isHttpBasedType(dataSourceType) && !data.url) {
41
+ throw new BadDataException(
42
+ `${dataSourceType} data sources require a URL.`,
43
+ );
44
+ }
45
+
46
+ if (
47
+ DataSourceTypeUtil.isDatabaseType(dataSourceType) &&
48
+ !data.databaseHost
49
+ ) {
50
+ throw new BadDataException(
51
+ `${dataSourceType} data sources require a database host.`,
52
+ );
53
+ }
54
+ }
55
+
56
+ protected override async onBeforeCreate(
57
+ createBy: CreateBy<Model>,
58
+ ): Promise<OnCreate<Model>> {
59
+ this.validateDataSourceShape({
60
+ dataSourceType: createBy.data.dataSourceType,
61
+ url: createBy.data.url,
62
+ databaseHost: createBy.data.databaseHost,
63
+ });
64
+
65
+ return { createBy, carryForward: null };
66
+ }
67
+
68
+ protected override async onBeforeUpdate(
69
+ updateBy: UpdateBy<Model>,
70
+ ): Promise<OnUpdate<Model>> {
71
+ /*
72
+ * Only validate when the update touches the fields that participate in
73
+ * shape validation. Changing the TYPE of an existing source without
74
+ * also sending url/host can produce a source that fails validation on
75
+ * its own merits — so when dataSourceType changes, re-validate against
76
+ * the stored row's values merged with the update.
77
+ */
78
+ const data: {
79
+ dataSourceType?: DataSourceType | undefined;
80
+ url?: string | undefined;
81
+ databaseHost?: string | undefined;
82
+ } = updateBy.data as unknown as {
83
+ dataSourceType?: DataSourceType | undefined;
84
+ url?: string | undefined;
85
+ databaseHost?: string | undefined;
86
+ };
87
+
88
+ /*
89
+ * Presence check, not truthiness — an update that CLEARS url or
90
+ * databaseHost (null / empty string) must still be validated, or an
91
+ * HTTP source could be stripped of its URL and break at query time.
92
+ */
93
+ const updateData: Record<string, unknown> = updateBy.data as Record<
94
+ string,
95
+ unknown
96
+ >;
97
+ const touchesShape: boolean =
98
+ "dataSourceType" in updateData ||
99
+ "url" in updateData ||
100
+ "databaseHost" in updateData;
101
+
102
+ if (touchesShape) {
103
+ const existing: Model | null = await this.findOneBy({
104
+ query: updateBy.query,
105
+ select: {
106
+ _id: true,
107
+ dataSourceType: true,
108
+ url: true,
109
+ databaseHost: true,
110
+ },
111
+ props: { isRoot: true },
112
+ });
113
+
114
+ /*
115
+ * A field present in the update wins even when cleared (null/"" →
116
+ * undefined, which validation treats as missing); an absent field
117
+ * falls back to the stored row.
118
+ */
119
+ this.validateDataSourceShape({
120
+ dataSourceType: data.dataSourceType || existing?.dataSourceType,
121
+ url: "url" in updateData ? data.url || undefined : existing?.url,
122
+ databaseHost:
123
+ "databaseHost" in updateData
124
+ ? data.databaseHost || undefined
125
+ : existing?.databaseHost,
126
+ });
127
+ }
128
+
129
+ return { updateBy, carryForward: null };
130
+ }
131
+
132
+ /*
133
+ * Decrypted connection settings for the server-side connector paths
134
+ * (/test and /query). ROOT read — the caller MUST have already performed
135
+ * a user-scoped access check on this id (the LlmProviderAPI two-phase
136
+ * pattern). The returned object holds plaintext secrets: never serialize
137
+ * it into a response or a log line.
138
+ */
139
+ @CaptureSpan()
140
+ public async getConnectionSettingsById(
141
+ dataSourceId: ObjectID,
142
+ ): Promise<DataSourceConnectionSettings | null> {
143
+ if (!ObjectID.isValidUUID(dataSourceId.toString())) {
144
+ /*
145
+ * A non-uuid string in a uuid column is a Postgres cast error, not
146
+ * an empty result — shape-check before querying.
147
+ */
148
+ return null;
149
+ }
150
+
151
+ const dataSource: Model | null = await this.findOneById({
152
+ id: dataSourceId,
153
+ select: {
154
+ _id: true,
155
+ projectId: true,
156
+ dataSourceType: true,
157
+ url: true,
158
+ databaseHost: true,
159
+ databasePort: true,
160
+ databaseName: true,
161
+ username: true,
162
+ password: true,
163
+ apiToken: true,
164
+ customHeaders: true,
165
+ additionalOptions: true,
166
+ },
167
+ props: { isRoot: true },
168
+ });
169
+
170
+ if (!dataSource || !dataSource.dataSourceType) {
171
+ return null;
172
+ }
173
+
174
+ let customHeaders: Dictionary<string> | undefined = undefined;
175
+ if (dataSource.customHeaders) {
176
+ customHeaders = {};
177
+ const headers: JSONObject = dataSource.customHeaders;
178
+ for (const key of Object.keys(headers)) {
179
+ if (headers[key] !== null && headers[key] !== undefined) {
180
+ customHeaders[key] = headers[key]!.toString();
181
+ }
182
+ }
183
+ }
184
+
185
+ return {
186
+ dataSourceType: dataSource.dataSourceType,
187
+ url: dataSource.url,
188
+ databaseHost: dataSource.databaseHost,
189
+ databasePort: dataSource.databasePort,
190
+ databaseName: dataSource.databaseName,
191
+ username: dataSource.username,
192
+ password: dataSource.password,
193
+ apiToken: dataSource.apiToken,
194
+ customHeaders: customHeaders,
195
+ additionalOptions: dataSource.additionalOptions,
196
+ };
197
+ }
198
+ }
199
+
200
+ export default new Service();
@@ -11,6 +11,7 @@ import QueryHelper from "../Types/Database/QueryHelper";
11
11
  import OneUptimeDate from "../../Types/Date";
12
12
  import LIMIT_MAX from "../../Types/Database/LimitMax";
13
13
  import GlobalCache from "../Infrastructure/GlobalCache";
14
+ import InProcessMemo from "../Utils/InProcessMemo";
14
15
  import logger, { LogAttributes } from "../Utils/Logger";
15
16
  import { canonicalizeEntityValue } from "../../Utils/Telemetry/EntityKey";
16
17
  import crypto from "crypto";
@@ -21,6 +22,46 @@ const LAST_SEEN_THROTTLE_SECONDS: number = 60;
21
22
  const LABELS_APPLIED_CACHE_NAMESPACE: string = "host-labels-applied";
22
23
  const LABELS_APPLIED_CACHE_TTL_SECONDS: number = 60;
23
24
 
25
+ /*
26
+ * What `findOrCreateByHostIdentifier` memoizes: the resolved row's id plus
27
+ * the identifier it converged to. A flat shape rather than the Model itself
28
+ * so a memo hit hands every caller a FRESH Model instance — the resolving
29
+ * call and a memoed call must not share one mutable entity object.
30
+ */
31
+ interface HostResolution {
32
+ hostIdStr: string;
33
+ hostIdentifier: string;
34
+ }
35
+
36
+ /*
37
+ * Per-process memo for `(projectId, canonical hostIdentifier) -> Host`.
38
+ *
39
+ * `findOrCreateByHostIdentifier` ran an unconditional Postgres SELECT per
40
+ * call, and the metrics ingest path calls it once per unique host per job
41
+ * (the host-enrichment aggregation loop) on top of `autoDiscoverHost`'s
42
+ * Redis-cache misses — for a fleet of workers chewing hostmetrics batches
43
+ * that is a steady stream of identical case-insensitive lookups for rows
44
+ * that change never. Same L1 shape as the service-resolution memo in
45
+ * OpenTelemetryIngestService: 60-second TTL, bounded because host
46
+ * identifiers arrive from collector-controlled resource attributes.
47
+ *
48
+ * ONLY positive resolutions (the host exists / was just created) are
49
+ * memoized, and only on success. There is deliberately no negative
50
+ * caching: the miss path must keep falling through to the create, so a
51
+ * create that loses a concurrent race still throws its unique-violation
52
+ * into the existing catch-and-refetch handling unchanged. Staleness is a
53
+ * non-issue for the id itself (ids are immutable); a deleted host keeps
54
+ * resolving for up to the TTL, which the 24-hour host-id Redis cache in
55
+ * OtelIngestBaseService already dwarfs.
56
+ */
57
+ const HOST_RESOLUTION_MEMO_TTL_IN_MS: number = 60 * 1000;
58
+ const HOST_RESOLUTION_MEMO_MAX_ENTRIES: number = 10_000;
59
+ const hostResolutionInProcessMemo: InProcessMemo<HostResolution> =
60
+ new InProcessMemo<HostResolution>({
61
+ ttlInMs: HOST_RESOLUTION_MEMO_TTL_IN_MS,
62
+ maxEntries: HOST_RESOLUTION_MEMO_MAX_ENTRIES,
63
+ });
64
+
24
65
  export class Service extends DatabaseService<Model> {
25
66
  public constructor() {
26
67
  super(Model);
@@ -75,6 +116,24 @@ export class Service extends DatabaseService<Model> {
75
116
  */
76
117
  const hostIdentifier: string = canonicalizeEntityValue(data.hostIdentifier);
77
118
 
119
+ /*
120
+ * L1: in-process memo. The canonical identifier is the memo key, so
121
+ * every casing of one host collapses onto the entry the previous batch
122
+ * wrote — the same folding the SQL predicate below performs. A hit
123
+ * skips the SELECT (and the already-converged identifier write) that
124
+ * the resolving call performed within the last minute.
125
+ */
126
+ const memoKey: string = `${data.projectId.toString()}:${hostIdentifier}`;
127
+ const memoedResolution: HostResolution | undefined =
128
+ hostResolutionInProcessMemo.get(memoKey);
129
+ if (memoedResolution) {
130
+ const memoedHost: Model = new Model();
131
+ memoedHost._id = memoedResolution.hostIdStr;
132
+ memoedHost.projectId = data.projectId;
133
+ memoedHost.hostIdentifier = memoedResolution.hostIdentifier;
134
+ return memoedHost;
135
+ }
136
+
78
137
  /*
79
138
  * Look up case-insensitively. The unique guard on name/hostIdentifier
80
139
  * (DatabaseService.checkUniqueColumnBy -> QueryHelper.findWithSameText)
@@ -131,6 +190,18 @@ export class Service extends DatabaseService<Model> {
131
190
  }
132
191
  }
133
192
 
193
+ /*
194
+ * Memoize the identifier the row ACTUALLY holds (post-convergence,
195
+ * or the legacy casing when the best-effort update failed) so a memo
196
+ * hit returns exactly what a repeated lookup would have.
197
+ */
198
+ if (existingHost._id) {
199
+ hostResolutionInProcessMemo.set(memoKey, {
200
+ hostIdStr: existingHost._id.toString(),
201
+ hostIdentifier: existingHost.hostIdentifier || hostIdentifier,
202
+ });
203
+ }
204
+
134
205
  return existingHost;
135
206
  }
136
207
 
@@ -149,6 +220,13 @@ export class Service extends DatabaseService<Model> {
149
220
  },
150
221
  });
151
222
 
223
+ if (createdHost._id) {
224
+ hostResolutionInProcessMemo.set(memoKey, {
225
+ hostIdStr: createdHost._id.toString(),
226
+ hostIdentifier: hostIdentifier,
227
+ });
228
+ }
229
+
152
230
  return createdHost;
153
231
  } catch {
154
232
  /*
@@ -173,6 +251,20 @@ export class Service extends DatabaseService<Model> {
173
251
  });
174
252
 
175
253
  if (reFetchedHost) {
254
+ /*
255
+ * The refetch after a lost create race emits the same SELECT as
256
+ * the cold lookup, and a first-contact stampede on a brand-new
257
+ * host is exactly when this branch runs hot — memoize it like the
258
+ * other two resolution paths (mirrors the service-resolution
259
+ * cache's create-race handling).
260
+ */
261
+ if (reFetchedHost._id) {
262
+ hostResolutionInProcessMemo.set(memoKey, {
263
+ hostIdStr: reFetchedHost._id.toString(),
264
+ hostIdentifier: reFetchedHost.hostIdentifier || hostIdentifier,
265
+ });
266
+ }
267
+
176
268
  return reFetchedHost;
177
269
  }
178
270
 
@@ -56,6 +56,7 @@ import { MetricPointType } from "../../Models/AnalyticsModels/Metric";
56
56
  import ServiceType from "../../Types/Telemetry/ServiceType";
57
57
  import OneUptimeDate from "../../Types/Date";
58
58
  import TelemetryUtil from "../Utils/Telemetry/Telemetry";
59
+ import MetricResourceAttributeUtil from "../../Utils/Metrics/MetricResourceAttributeUtil";
59
60
  import logger, { LogAttributes } from "../Utils/Logger";
60
61
  import ProductAnalytics from "../Utils/ProductAnalytics";
61
62
  import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
@@ -2063,6 +2064,18 @@ ${incident.remediationNotes || "No remediation notes provided."}
2063
2064
 
2064
2065
  // only emit if the incident has been resolved
2065
2066
  if (resolvedTimeline && resolvedTimeline.startsAt) {
2067
+ /*
2068
+ * Same dimension set as every other incident metric — including
2069
+ * oneuptime.label.* / oneuptime.customField.* — so a dashboard
2070
+ * grouped by a label does not silently lose this series.
2071
+ */
2072
+ const {
2073
+ baseMetricAttributes,
2074
+ }: { baseMetricAttributes: JSONObject } =
2075
+ await this.getIncidentMetricContext({
2076
+ incidentId: incidentId,
2077
+ });
2078
+
2066
2079
  const postmortemMetric: MutableMetric = new MutableMetric();
2067
2080
  postmortemMetric.projectId = projectId;
2068
2081
  postmortemMetric.primaryEntityId = incidentId;
@@ -2076,6 +2089,7 @@ ${incident.remediationNotes || "No remediation notes provided."}
2076
2089
  resolvedTimeline.startsAt,
2077
2090
  );
2078
2091
  postmortemMetric.attributes = {
2092
+ ...baseMetricAttributes,
2079
2093
  incidentId: incidentId.toString(),
2080
2094
  projectId: projectId.toString(),
2081
2095
  };
@@ -2356,6 +2370,18 @@ ${incidentSeverity.name}
2356
2370
 
2357
2371
  // emit severity change metric
2358
2372
  try {
2373
+ /*
2374
+ * Same dimension set as every other incident metric — including
2375
+ * oneuptime.label.* / oneuptime.customField.* — so "severity
2376
+ * churn by product" is answerable from the metric store.
2377
+ */
2378
+ const {
2379
+ baseMetricAttributes,
2380
+ }: { baseMetricAttributes: JSONObject } =
2381
+ await this.getIncidentMetricContext({
2382
+ incidentId: incidentId,
2383
+ });
2384
+
2359
2385
  const severityChangeMetric: MutableMetric = new MutableMetric();
2360
2386
  severityChangeMetric.projectId = projectId;
2361
2387
  severityChangeMetric.primaryEntityId = incidentId;
@@ -2364,6 +2390,7 @@ ${incidentSeverity.name}
2364
2390
  severityChangeMetric.metricPointId = `severity-change:${ObjectID.generate().toString()}`;
2365
2391
  severityChangeMetric.value = 1;
2366
2392
  severityChangeMetric.attributes = {
2393
+ ...baseMetricAttributes,
2367
2394
  incidentId: incidentId.toString(),
2368
2395
  projectId: projectId.toString(),
2369
2396
  newIncidentSeverityId: incidentSeverity._id?.toString() || "",
@@ -3008,6 +3035,16 @@ ${incidentSeverity.name}
3008
3035
  _id: true,
3009
3036
  name: true,
3010
3037
  },
3038
+ /*
3039
+ * A project's own taxonomy. Both become metric attributes below so
3040
+ * dashboards can group MTTA/MTTR by the dimensions the project
3041
+ * actually thinks in.
3042
+ */
3043
+ labels: {
3044
+ _id: true,
3045
+ name: true,
3046
+ },
3047
+ customFields: true,
3011
3048
  },
3012
3049
  props: {
3013
3050
  isRoot: true,
@@ -3121,6 +3158,14 @@ ${incidentSeverity.name}
3121
3158
  ownerUserNames: ownerUserNames.join(", "),
3122
3159
  ownerTeamIds: ownerTeamIds.join(", "),
3123
3160
  ownerTeamNames: ownerTeamNames.join(", "),
3161
+ /*
3162
+ * oneuptime.label.* / oneuptime.customField.* — namespaced, so they can
3163
+ * never collide with the unprefixed dimensions above.
3164
+ */
3165
+ ...MetricResourceAttributeUtil.getResourceAttributes({
3166
+ labels: incident.labels,
3167
+ customFields: incident.customFields,
3168
+ }),
3124
3169
  };
3125
3170
 
3126
3171
  return { incident, baseMetricAttributes };
@@ -70,6 +70,7 @@ import CephClusterOwnerRuleService from "./CephClusterOwnerRuleService";
70
70
  import CephClusterOwnerTeamService from "./CephClusterOwnerTeamService";
71
71
  import CephClusterOwnerUserService from "./CephClusterOwnerUserService";
72
72
  import LlmProviderService from "./LlmProviderService";
73
+ import DataSourceService from "./DataSourceService";
73
74
  import AuditLogService from "./AuditLogService";
74
75
  import LogService from "./LogService";
75
76
  import MailService from "./MailService";
@@ -369,6 +370,7 @@ const services: Array<BaseService> = [
369
370
  CephClusterOwnerTeamService,
370
371
  CephClusterOwnerUserService,
371
372
  LlmProviderService,
373
+ DataSourceService,
372
374
 
373
375
  MailService,
374
376
  MonitorCustomFieldService,
@@ -3,6 +3,8 @@ import {
3
3
  AllowedActiveMonitorCountInFreePlan,
4
4
  IsBillingEnabled,
5
5
  } from "../EnvironmentConfig";
6
+ import { UpdateResult } from "typeorm";
7
+ import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
6
8
  import { ActiveMonitoringMeteredPlan } from "../Types/Billing/MeteredPlan/AllMeteredPlans";
7
9
  import CreateBy from "../Types/Database/CreateBy";
8
10
  import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
@@ -2263,5 +2265,52 @@ ${createdItem.description?.trim() || "No description provided."}
2263
2265
 
2264
2266
  return monitor.name || "";
2265
2267
  }
2268
+
2269
+ /**
2270
+ * Repoints the currentMonitorStatusId of ALREADY SOFT-DELETED monitor rows
2271
+ * away from monitor statuses that are about to be hard-deleted.
2272
+ *
2273
+ * A soft-deleted monitor keeps its physical row - and therefore its
2274
+ * currentMonitorStatusId foreign key, which is ON DELETE NO ACTION - so a
2275
+ * status that a since-deleted monitor last pointed at cannot be removed
2276
+ * until that dangling reference is cleared. This surfaced as an intermittent
2277
+ * "Monitor records still reference it" failure when Terraform destroyed a
2278
+ * fixture that deleted its monitors and then the custom statuses those
2279
+ * monitors had adopted.
2280
+ *
2281
+ * Only soft-deleted rows are touched: deleting a status that a LIVE monitor
2282
+ * is currently in still surfaces the referential-integrity guard, so a user
2283
+ * is never silently robbed of a monitor's visible status. Returns the number
2284
+ * of dead rows repointed.
2285
+ */
2286
+ @CaptureSpan()
2287
+ public async repointDeletedMonitorsAwayFromStatuses(data: {
2288
+ fromMonitorStatusIds: Array<ObjectID>;
2289
+ toMonitorStatusId: ObjectID;
2290
+ projectId: ObjectID;
2291
+ }): Promise<number> {
2292
+ if (data.fromMonitorStatusIds.length === 0) {
2293
+ return 0;
2294
+ }
2295
+
2296
+ const result: UpdateResult = await this.getRepository()
2297
+ .createQueryBuilder()
2298
+ .update(Model)
2299
+ .set({
2300
+ currentMonitorStatusId: data.toMonitorStatusId.toString(),
2301
+ } as unknown as QueryDeepPartialEntity<Model>)
2302
+ .where('"projectId" = :projectId', {
2303
+ projectId: data.projectId.toString(),
2304
+ })
2305
+ .andWhere('"deletedAt" IS NOT NULL')
2306
+ .andWhere('"currentMonitorStatusId" IN (:...fromIds)', {
2307
+ fromIds: data.fromMonitorStatusIds.map((id: ObjectID) => {
2308
+ return id.toString();
2309
+ }),
2310
+ })
2311
+ .execute();
2312
+
2313
+ return result.affected || 0;
2314
+ }
2266
2315
  }
2267
2316
  export default new Service();
@@ -11,6 +11,14 @@ import BadDataException from "../../Types/Exception/BadDataException";
11
11
  import ObjectID from "../../Types/ObjectID";
12
12
  import Model from "../../Models/DatabaseModels/MonitorStatus";
13
13
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
14
+ /*
15
+ * Statically imported despite the MonitorService <-> MonitorStatusService
16
+ * cycle: both modules reference each other only from method bodies (never at
17
+ * module-init time), so the default binding is always resolved by the time
18
+ * these methods run. A dynamic import() cannot be used here - the App build's
19
+ * TypeScript module target rejects it (TS1323).
20
+ */
21
+ import MonitorService from "./MonitorService";
14
22
 
15
23
  export class Service extends DatabaseService<Model> {
16
24
  public constructor() {
@@ -51,6 +59,17 @@ export class Service extends DatabaseService<Model> {
51
59
  );
52
60
  }
53
61
 
62
+ /*
63
+ * Clear dangling currentMonitorStatusId references held by ALREADY
64
+ * soft-deleted monitors before the hard-delete runs. A soft-deleted
65
+ * monitor keeps its row and its currentMonitorStatusId foreign key
66
+ * (ON DELETE NO ACTION), so a status a since-deleted monitor last adopted
67
+ * cannot be removed while that reference lingers - the intermittent
68
+ * "Monitor records still reference it" failure. Live monitors are left
69
+ * untouched (see repointDeletedMonitorsAwayFromStatuses).
70
+ */
71
+ await this.clearDeletedMonitorReferences(deleteBy.query);
72
+
54
73
  let monitorStatus: Model | null = null;
55
74
 
56
75
  if (!deleteBy.props.isRoot) {
@@ -72,6 +91,153 @@ export class Service extends DatabaseService<Model> {
72
91
  };
73
92
  }
74
93
 
94
+ /**
95
+ * Repoints soft-deleted monitors that still reference the statuses matched
96
+ * by `query` to each project's default (lowest-priority) operational status,
97
+ * falling back to any other remaining status when no operational status
98
+ * survives. This keeps the Monitor.currentMonitorStatusId foreign key from
99
+ * blocking a monitor-status delete on dead rows.
100
+ */
101
+ private async clearDeletedMonitorReferences(
102
+ query: DeleteBy<Model>["query"],
103
+ ): Promise<void> {
104
+ const statusesBeingDeleted: Array<Model> = await this.findBy({
105
+ query: query,
106
+ select: {
107
+ _id: true,
108
+ projectId: true,
109
+ },
110
+ limit: LIMIT_MAX,
111
+ skip: 0,
112
+ props: {
113
+ isRoot: true,
114
+ },
115
+ });
116
+
117
+ // Group the statuses being deleted by project.
118
+ const byProject: Map<
119
+ string,
120
+ { projectId: ObjectID; statusIds: Array<ObjectID> }
121
+ > = new Map();
122
+
123
+ for (const status of statusesBeingDeleted) {
124
+ if (!status.id || !status.projectId) {
125
+ continue;
126
+ }
127
+
128
+ const key: string = status.projectId.toString();
129
+
130
+ if (!byProject.has(key)) {
131
+ byProject.set(key, { projectId: status.projectId, statusIds: [] });
132
+ }
133
+
134
+ byProject.get(key)!.statusIds.push(status.id);
135
+ }
136
+
137
+ if (byProject.size === 0) {
138
+ return;
139
+ }
140
+
141
+ for (const { projectId, statusIds } of byProject.values()) {
142
+ const deletedSet: Set<string> = new Set(
143
+ statusIds.map((id: ObjectID) => {
144
+ return id.toString();
145
+ }),
146
+ );
147
+
148
+ const fallbackStatusId: ObjectID | undefined =
149
+ await this.findFallbackStatusId(projectId, deletedSet);
150
+
151
+ if (!fallbackStatusId) {
152
+ // Nothing valid to repoint to - leave the referential guard in place.
153
+ continue;
154
+ }
155
+
156
+ await MonitorService.repointDeletedMonitorsAwayFromStatuses({
157
+ fromMonitorStatusIds: statusIds,
158
+ toMonitorStatusId: fallbackStatusId,
159
+ projectId: projectId,
160
+ });
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Picks the status a repointed monitor should fall back to: the project's
166
+ * default (lowest-priority) operational status that is not itself being
167
+ * deleted, or - if none survives - any other remaining status.
168
+ */
169
+ private async findFallbackStatusId(
170
+ projectId: ObjectID,
171
+ deletedStatusIds: Set<string>,
172
+ ): Promise<ObjectID | undefined> {
173
+ const operationalStatuses: Array<Model> = await this.findBy({
174
+ query: {
175
+ projectId: projectId,
176
+ isOperationalState: true,
177
+ },
178
+ select: {
179
+ _id: true,
180
+ },
181
+ sort: {
182
+ priority: SortOrder.Ascending,
183
+ createdAt: SortOrder.Ascending,
184
+ },
185
+ limit: LIMIT_MAX,
186
+ skip: 0,
187
+ props: {
188
+ isRoot: true,
189
+ },
190
+ });
191
+
192
+ const operationalFallback: ObjectID | undefined = this.firstSurvivingId(
193
+ operationalStatuses,
194
+ deletedStatusIds,
195
+ );
196
+
197
+ if (operationalFallback) {
198
+ return operationalFallback;
199
+ }
200
+
201
+ // No operational status survives - repoint to any other remaining status.
202
+ const anyStatuses: Array<Model> = await this.findBy({
203
+ query: {
204
+ projectId: projectId,
205
+ },
206
+ select: {
207
+ _id: true,
208
+ },
209
+ sort: {
210
+ priority: SortOrder.Ascending,
211
+ },
212
+ limit: LIMIT_MAX,
213
+ skip: 0,
214
+ props: {
215
+ isRoot: true,
216
+ },
217
+ });
218
+
219
+ return this.firstSurvivingId(anyStatuses, deletedStatusIds);
220
+ }
221
+
222
+ /**
223
+ * Returns the id of the first status in `statuses` (already priority-sorted)
224
+ * that is not in `deletedStatusIds`, or undefined when none survives.
225
+ */
226
+ private firstSurvivingId(
227
+ statuses: Array<Model>,
228
+ deletedStatusIds: Set<string>,
229
+ ): ObjectID | undefined {
230
+ for (const status of statuses) {
231
+ const id: ObjectID | null | undefined = status.id;
232
+
233
+ if (id && !deletedStatusIds.has(id.toString())) {
234
+ return id;
235
+ }
236
+ }
237
+
238
+ return undefined;
239
+ }
240
+
75
241
  @CaptureSpan()
76
242
  protected override async onDeleteSuccess(
77
243
  onDelete: OnDelete<Model>,