@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.
Files changed (384) hide show
  1. package/Models/DatabaseModels/CephCluster.ts +14 -0
  2. package/Models/DatabaseModels/CodeRepository.ts +14 -6
  3. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
  4. package/Models/DatabaseModels/DockerHost.ts +14 -0
  5. package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
  6. package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
  7. package/Models/DatabaseModels/Index.ts +2 -0
  8. package/Models/DatabaseModels/IoTFleet.ts +14 -0
  9. package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
  10. package/Models/DatabaseModels/LogDropFilter.ts +9 -20
  11. package/Models/DatabaseModels/NetworkDevice.ts +14 -0
  12. package/Models/DatabaseModels/NetworkInterface.ts +18 -0
  13. package/Models/DatabaseModels/Project.ts +16 -2
  14. package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
  15. package/Models/DatabaseModels/Service.ts +30 -0
  16. package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
  17. package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
  18. package/Models/DatabaseModels/StatusPageResource.ts +73 -0
  19. package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
  20. package/Models/DatabaseModels/User.ts +30 -0
  21. package/Server/API/AIAgentDataAPI.ts +31 -0
  22. package/Server/API/BaseAPI.ts +13 -2
  23. package/Server/API/DashboardAPI.ts +458 -12
  24. package/Server/API/GitHubAPI.ts +119 -283
  25. package/Server/API/UserAPI.ts +263 -1
  26. package/Server/EnvironmentConfig.ts +38 -4
  27. package/Server/Infrastructure/GlobalCache.ts +152 -0
  28. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  36. package/Server/Services/AlertStateTimelineService.ts +20 -0
  37. package/Server/Services/AnalyticsDatabaseService.ts +72 -1
  38. package/Server/Services/CephClusterService.ts +26 -86
  39. package/Server/Services/CloudResourceService.ts +23 -83
  40. package/Server/Services/CodeRepositoryService.ts +105 -2
  41. package/Server/Services/DatabaseService.ts +296 -2
  42. package/Server/Services/DockerHostService.ts +19 -79
  43. package/Server/Services/DockerSwarmClusterService.ts +27 -88
  44. package/Server/Services/HostService.ts +46 -99
  45. package/Server/Services/IncidentStateTimelineService.ts +20 -0
  46. package/Server/Services/Index.ts +2 -0
  47. package/Server/Services/IoTFleetService.ts +19 -79
  48. package/Server/Services/KubernetesClusterService.ts +17 -78
  49. package/Server/Services/LabelService.ts +57 -10
  50. package/Server/Services/MetricTypeService.ts +84 -0
  51. package/Server/Services/MonitorService.ts +89 -14
  52. package/Server/Services/MonitorStatusTimelineService.ts +29 -0
  53. package/Server/Services/MonitorTemplateService.ts +65 -0
  54. package/Server/Services/OpenTelemetryIngestService.ts +230 -26
  55. package/Server/Services/PodmanHostService.ts +19 -79
  56. package/Server/Services/ProxmoxClusterService.ts +23 -83
  57. package/Server/Services/RumApplicationService.ts +19 -80
  58. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
  59. package/Server/Services/ServerlessFunctionService.ts +24 -85
  60. package/Server/Services/ServiceService.ts +299 -73
  61. package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
  62. package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
  63. package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
  64. package/Server/Services/TelemetryEntityService.ts +1 -0
  65. package/Server/Services/UserService.ts +184 -0
  66. package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
  67. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
  68. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
  69. package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
  70. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
  71. package/Server/Utils/Monitor/MonitorResource.ts +63 -36
  72. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
  73. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
  74. package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
  75. package/Server/Utils/PasswordHash.ts +306 -0
  76. package/Server/Utils/SingleFlight.ts +83 -0
  77. package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
  78. package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
  79. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
  80. package/Server/Utils/Telemetry/Telemetry.ts +351 -122
  81. package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
  82. package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
  83. package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
  84. package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
  85. package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
  86. package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
  87. package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
  88. package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
  89. package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
  90. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
  91. package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
  92. package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
  93. package/Tests/Server/API/Helpers.ts +6 -1
  94. package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
  95. package/Tests/Server/Infrastructure/GlobalCache.test.ts +354 -0
  96. package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
  97. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
  98. package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
  99. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
  100. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
  101. package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
  102. package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
  103. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
  104. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
  105. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
  106. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
  107. package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
  108. package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
  109. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
  110. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
  111. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
  112. package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
  113. package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
  114. package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
  115. package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
  116. package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
  117. package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
  118. package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
  119. package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
  120. package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
  121. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
  122. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
  123. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
  124. package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
  125. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
  126. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
  127. package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
  128. package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
  129. package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
  130. package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
  131. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
  132. package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
  133. package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
  134. package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
  135. package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
  136. package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
  137. package/Tests/Types/Database/ColumnLength.test.ts +7 -1
  138. package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
  139. package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
  140. package/Tests/Types/Decimal.test.ts +37 -0
  141. package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
  142. package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
  143. package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
  144. package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
  145. package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
  146. package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
  147. package/Tests/Types/Port.test.ts +38 -0
  148. package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
  149. package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
  150. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
  151. package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
  152. package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
  153. package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
  154. package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
  155. package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
  156. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  157. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  158. package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
  159. package/Types/Dashboard/DashboardTemplates.ts +397 -0
  160. package/Types/Database/BigIntColumnTransformer.ts +74 -0
  161. package/Types/Database/ColumnLength.ts +9 -1
  162. package/Types/Database/DatabaseProperty.ts +26 -0
  163. package/Types/Database/NumericColumnValue.ts +128 -0
  164. package/Types/Database/TableColumn.ts +11 -0
  165. package/Types/Database/UnsynchronizedIndex.ts +48 -0
  166. package/Types/Decimal.ts +16 -4
  167. package/Types/HashedString.ts +139 -4
  168. package/Types/Permission.ts +48 -0
  169. package/Types/Port.ts +16 -4
  170. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
  171. package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
  172. package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
  173. package/UI/Components/Navbar/NavBar.tsx +12 -2
  174. package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
  175. package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
  176. package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
  177. package/Utils/Dashboard/Components/Index.ts +7 -0
  178. package/Utils/TeamMembersByProject.ts +237 -0
  179. package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
  180. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  181. package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
  182. package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
  183. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
  184. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  185. package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
  186. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  187. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
  188. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
  189. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
  190. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
  191. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  192. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  193. package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
  194. package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
  195. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
  196. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  197. package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
  198. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  199. package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
  200. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  201. package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
  202. package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
  203. package/build/dist/Models/DatabaseModels/Project.js +16 -2
  204. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  205. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
  206. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  207. package/build/dist/Models/DatabaseModels/Service.js +28 -1
  208. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  209. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
  210. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
  211. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
  212. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
  213. package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
  214. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  215. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
  216. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/User.js +32 -0
  218. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  219. package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
  220. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  221. package/build/dist/Server/API/BaseAPI.js +11 -1
  222. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  223. package/build/dist/Server/API/DashboardAPI.js +343 -10
  224. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  225. package/build/dist/Server/API/GitHubAPI.js +94 -167
  226. package/build/dist/Server/API/GitHubAPI.js.map +1 -1
  227. package/build/dist/Server/API/UserAPI.js +187 -2
  228. package/build/dist/Server/API/UserAPI.js.map +1 -1
  229. package/build/dist/Server/EnvironmentConfig.js +34 -4
  230. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  231. package/build/dist/Server/Infrastructure/GlobalCache.js +120 -0
  232. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
  234. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
  245. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
  246. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
  247. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  248. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  249. package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
  250. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  251. package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
  252. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  253. package/build/dist/Server/Services/CephClusterService.js +26 -76
  254. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  255. package/build/dist/Server/Services/CloudResourceService.js +23 -73
  256. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  257. package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
  258. package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
  259. package/build/dist/Server/Services/DatabaseService.js +246 -2
  260. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  261. package/build/dist/Server/Services/DockerHostService.js +19 -69
  262. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  263. package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
  264. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  265. package/build/dist/Server/Services/HostService.js +58 -102
  266. package/build/dist/Server/Services/HostService.js.map +1 -1
  267. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  268. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  269. package/build/dist/Server/Services/Index.js +2 -0
  270. package/build/dist/Server/Services/Index.js.map +1 -1
  271. package/build/dist/Server/Services/IoTFleetService.js +19 -69
  272. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  273. package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
  274. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  275. package/build/dist/Server/Services/LabelService.js +42 -7
  276. package/build/dist/Server/Services/LabelService.js.map +1 -1
  277. package/build/dist/Server/Services/MetricTypeService.js +74 -0
  278. package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
  279. package/build/dist/Server/Services/MonitorService.js +81 -9
  280. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  281. package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
  282. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  283. package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
  284. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  285. package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
  286. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  287. package/build/dist/Server/Services/PodmanHostService.js +19 -69
  288. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  289. package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
  290. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  291. package/build/dist/Server/Services/RumApplicationService.js +19 -70
  292. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  293. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
  294. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  295. package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
  296. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  297. package/build/dist/Server/Services/ServiceService.js +236 -61
  298. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  299. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
  300. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
  301. package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
  302. package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
  303. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
  304. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  305. package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
  306. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  307. package/build/dist/Server/Services/UserService.js +165 -0
  308. package/build/dist/Server/Services/UserService.js.map +1 -1
  309. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
  310. package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
  311. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
  312. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  313. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
  314. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  315. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
  316. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
  317. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
  318. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  319. package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
  320. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  321. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
  322. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  323. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
  324. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
  325. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
  326. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
  327. package/build/dist/Server/Utils/PasswordHash.js +237 -0
  328. package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
  329. package/build/dist/Server/Utils/SingleFlight.js +76 -0
  330. package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
  331. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
  332. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
  333. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
  334. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  335. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
  336. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
  337. package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
  338. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  339. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  340. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  341. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  342. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  343. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
  344. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
  345. package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
  346. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  347. package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
  348. package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
  349. package/build/dist/Types/Database/ColumnLength.js +9 -1
  350. package/build/dist/Types/Database/ColumnLength.js.map +1 -1
  351. package/build/dist/Types/Database/DatabaseProperty.js +25 -0
  352. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  353. package/build/dist/Types/Database/NumericColumnValue.js +97 -0
  354. package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
  355. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  356. package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
  357. package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
  358. package/build/dist/Types/Decimal.js +14 -3
  359. package/build/dist/Types/Decimal.js.map +1 -1
  360. package/build/dist/Types/HashedString.js +95 -5
  361. package/build/dist/Types/HashedString.js.map +1 -1
  362. package/build/dist/Types/Permission.js +42 -0
  363. package/build/dist/Types/Permission.js.map +1 -1
  364. package/build/dist/Types/Port.js +15 -5
  365. package/build/dist/Types/Port.js.map +1 -1
  366. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
  367. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  368. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
  369. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
  370. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
  371. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
  372. package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
  373. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  374. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
  375. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  376. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
  377. package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
  378. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
  379. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
  380. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  381. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  382. package/build/dist/Utils/TeamMembersByProject.js +145 -0
  383. package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
  384. package/package.json +1 -1
@@ -5,6 +5,7 @@ import Model from "../../Models/DatabaseModels/CephCluster";
5
5
  import Label from "../../Models/DatabaseModels/Label";
6
6
  import { OnCreate } from "../Types/Database/Hooks";
7
7
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
8
+ import ResourceHeartbeat from "../Utils/Telemetry/ResourceHeartbeat";
8
9
  import ObjectID from "../../Types/ObjectID";
9
10
  import QueryHelper from "../Types/Database/QueryHelper";
10
11
  import OneUptimeDate from "../../Types/Date";
@@ -181,7 +182,6 @@ export class Service extends DatabaseService<Model> {
181
182
  capacityUsedPercent?: number | undefined;
182
183
  },
183
184
  ): Promise<void> {
184
- const cacheKey: string = clusterId.toString();
185
185
  const extrasFingerprint: string = crypto
186
186
  .createHash("sha1")
187
187
  .update(
@@ -200,34 +200,6 @@ export class Service extends DatabaseService<Model> {
200
200
  )
201
201
  .digest("hex");
202
202
 
203
- let cached: string | null = null;
204
- try {
205
- cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
206
- } catch {
207
- /*
208
- * Cache unavailable — fail open and refresh lastSeenAt anyway. A
209
- * cache error must never skip the DB write below, otherwise the
210
- * resource is wrongly marked "disconnected" while telemetry is
211
- * still flowing. Mirrors shouldRunMaintenance's fail-open stance.
212
- */
213
- cached = null;
214
- }
215
-
216
- if (cached === extrasFingerprint) {
217
- return; // same data was written recently
218
- }
219
-
220
- try {
221
- await GlobalCache.setString(
222
- LAST_SEEN_CACHE_NAMESPACE,
223
- cacheKey,
224
- extrasFingerprint,
225
- { expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
226
- );
227
- } catch {
228
- // Best-effort throttle write; proceed with the DB update regardless.
229
- }
230
-
231
203
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
232
204
  const liveness: any = {
233
205
  lastSeenAt: OneUptimeDate.getCurrentDate(),
@@ -235,91 +207,59 @@ export class Service extends DatabaseService<Model> {
235
207
  };
236
208
 
237
209
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
238
- const data: any = { ...liveness };
210
+ const metadata: any = {};
239
211
 
240
212
  if (extra?.cephVersion) {
241
- data.cephVersion = extra.cephVersion;
213
+ metadata.cephVersion = extra.cephVersion;
242
214
  }
243
215
  if (extra?.fsid) {
244
- data.fsid = extra.fsid;
216
+ metadata.fsid = extra.fsid;
245
217
  }
246
218
  if (extra?.agentVersion) {
247
- data.agentVersion = extra.agentVersion;
219
+ metadata.agentVersion = extra.agentVersion;
248
220
  }
249
221
  /*
250
222
  * Counts and health: 0 is a legitimate value (healthStatus 0 = OK,
251
223
  * osdUpCount 0 = every OSD down) — gate on undefined, not falsiness.
252
224
  */
253
225
  if (extra?.monCount !== undefined) {
254
- data.monCount = extra.monCount;
226
+ metadata.monCount = extra.monCount;
255
227
  }
256
228
  if (extra?.osdCount !== undefined) {
257
- data.osdCount = extra.osdCount;
229
+ metadata.osdCount = extra.osdCount;
258
230
  }
259
231
  if (extra?.osdUpCount !== undefined) {
260
- data.osdUpCount = extra.osdUpCount;
232
+ metadata.osdUpCount = extra.osdUpCount;
261
233
  }
262
234
  if (extra?.osdInCount !== undefined) {
263
- data.osdInCount = extra.osdInCount;
235
+ metadata.osdInCount = extra.osdInCount;
264
236
  }
265
237
  if (extra?.poolCount !== undefined) {
266
- data.poolCount = extra.poolCount;
238
+ metadata.poolCount = extra.poolCount;
267
239
  }
268
240
  if (extra?.healthStatus !== undefined) {
269
- data.healthStatus = extra.healthStatus;
241
+ metadata.healthStatus = extra.healthStatus;
270
242
  }
271
243
  if (extra?.capacityUsedPercent !== undefined) {
272
- data.capacityUsedPercent = extra.capacityUsedPercent;
244
+ metadata.capacityUsedPercent = extra.capacityUsedPercent;
273
245
  }
274
246
 
275
247
  /*
276
- * Heartbeat write: a single-statement UPDATE with no hooks and no
277
- * `version` bump, avoiding the hot-row Postgres lock convoy that the
278
- * full updateOneById pipeline causes. See ServiceService.updateLastSeen.
279
- *
280
- * Collector-supplied strings are clamped to their declared column
281
- * widths inside updateColumnsByIdWithoutHooks — see
282
- * DatabaseService.truncateStringColumnsToMaxLength.
248
+ * One gated, non-blocking heartbeat write. The gates, the fail-open /
249
+ * fail-closed split and the liveness-only fallback all live in
250
+ * ResourceHeartbeat see there for why this row's throttle used to
251
+ * provide no throttling at all.
283
252
  */
284
- try {
285
- await this.updateColumnsByIdWithoutHooks({
286
- id: clusterId,
287
- data: data,
288
- });
289
- } catch (err) {
290
- /*
291
- * Liveness must survive bad metadata. The enriched write carries
292
- * `lastSeenAt` and `otelCollectorStatus` alongside optional,
293
- * collector-supplied columns; if any one of them makes Postgres
294
- * reject the statement, lastSeenAt silently stops advancing and
295
- * markDisconnectedClusters flips a perfectly healthy Ceph cluster to
296
- * "disconnected" 15 minutes later — with telemetry still arriving the
297
- * whole time. That was issue #3006 on the Host path (a host.ip list
298
- * longer than the then-varchar(500) column). Retry with liveness only
299
- * so the enrichment is what gets dropped, never the heartbeat.
300
- */
301
- logger.warn(
302
- `CephClusterService.updateLastSeen: metadata write failed for Ceph cluster ${clusterId.toString()}, falling back to a liveness-only update: ${
303
- err instanceof Error ? err.message : String(err)
304
- }`,
305
- );
306
-
307
- /*
308
- * The throttle fingerprint was already stored, so without this the
309
- * next batch would short-circuit on a cache hit and skip the retry
310
- * for the rest of the window.
311
- */
312
- try {
313
- await GlobalCache.deleteKey(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
314
- } catch {
315
- // Best-effort; the fallback write below is what actually matters.
316
- }
317
-
318
- await this.updateColumnsByIdWithoutHooks({
319
- id: clusterId,
320
- data: liveness,
321
- });
322
- }
253
+ await ResourceHeartbeat.write({
254
+ service: this,
255
+ id: clusterId,
256
+ cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
257
+ throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
258
+ liveness: liveness,
259
+ metadata: metadata,
260
+ fingerprint: extrasFingerprint,
261
+ describe: `ceph cluster ${clusterId.toString()}`,
262
+ });
323
263
  }
324
264
 
325
265
  /**
@@ -2,6 +2,7 @@ import DatabaseService from "./DatabaseService";
2
2
  import Model from "../../Models/DatabaseModels/CloudResource";
3
3
  import Label from "../../Models/DatabaseModels/Label";
4
4
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
5
+ import ResourceHeartbeat from "../Utils/Telemetry/ResourceHeartbeat";
5
6
  import ObjectID from "../../Types/ObjectID";
6
7
  import QueryHelper from "../Types/Database/QueryHelper";
7
8
  import OneUptimeDate from "../../Types/Date";
@@ -154,7 +155,6 @@ export class Service extends DatabaseService<Model> {
154
155
  runtimeVersion?: string | undefined;
155
156
  },
156
157
  ): Promise<void> {
157
- const cacheKey: string = cloudResourceId.toString();
158
158
  const extrasFingerprint: string = crypto
159
159
  .createHash("sha1")
160
160
  .update(
@@ -170,34 +170,6 @@ export class Service extends DatabaseService<Model> {
170
170
  )
171
171
  .digest("hex");
172
172
 
173
- let cached: string | null = null;
174
- try {
175
- cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
176
- } catch {
177
- /*
178
- * Cache unavailable — fail open and refresh lastSeenAt anyway. A
179
- * cache error must never skip the DB write below, otherwise the
180
- * resource is wrongly marked "disconnected" while telemetry is
181
- * still flowing. Mirrors shouldRunMaintenance's fail-open stance.
182
- */
183
- cached = null;
184
- }
185
-
186
- if (cached === extrasFingerprint) {
187
- return; // same data was written recently
188
- }
189
-
190
- try {
191
- await GlobalCache.setString(
192
- LAST_SEEN_CACHE_NAMESPACE,
193
- cacheKey,
194
- extrasFingerprint,
195
- { expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
196
- );
197
- } catch {
198
- // Best-effort throttle write; proceed with the DB update regardless.
199
- }
200
-
201
173
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
202
174
  const liveness: any = {
203
175
  lastSeenAt: OneUptimeDate.getCurrentDate(),
@@ -205,78 +177,46 @@ export class Service extends DatabaseService<Model> {
205
177
  };
206
178
 
207
179
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
208
- const data: any = { ...liveness };
180
+ const metadata: any = {};
209
181
 
210
182
  if (extra?.agentVersion) {
211
- data.agentVersion = extra.agentVersion;
183
+ metadata.agentVersion = extra.agentVersion;
212
184
  }
213
185
  if (extra?.cloudPlatform) {
214
- data.cloudPlatform = extra.cloudPlatform;
186
+ metadata.cloudPlatform = extra.cloudPlatform;
215
187
  }
216
188
  if (extra?.cloudProvider) {
217
- data.cloudProvider = extra.cloudProvider;
189
+ metadata.cloudProvider = extra.cloudProvider;
218
190
  }
219
191
  if (extra?.cloudRegion) {
220
- data.cloudRegion = extra.cloudRegion;
192
+ metadata.cloudRegion = extra.cloudRegion;
221
193
  }
222
194
  if (extra?.cloudAccountId) {
223
- data.cloudAccountId = extra.cloudAccountId;
195
+ metadata.cloudAccountId = extra.cloudAccountId;
224
196
  }
225
197
  if (extra?.runtimeName) {
226
- data.runtimeName = extra.runtimeName;
198
+ metadata.runtimeName = extra.runtimeName;
227
199
  }
228
200
  if (extra?.runtimeVersion) {
229
- data.runtimeVersion = extra.runtimeVersion;
201
+ metadata.runtimeVersion = extra.runtimeVersion;
230
202
  }
231
203
 
232
204
  /*
233
- * Heartbeat write: a single-statement UPDATE with no hooks and no
234
- * `version` bump, avoiding the hot-row Postgres lock convoy that the
235
- * full updateOneById pipeline causes. See ServiceService.updateLastSeen.
236
- *
237
- * Collector-supplied strings are clamped to their declared column
238
- * widths inside updateColumnsByIdWithoutHooks — see
239
- * DatabaseService.truncateStringColumnsToMaxLength.
205
+ * One gated, non-blocking heartbeat write. The gates, the fail-open /
206
+ * fail-closed split and the liveness-only fallback all live in
207
+ * ResourceHeartbeat see there for why this row's throttle used to
208
+ * provide no throttling at all.
240
209
  */
241
- try {
242
- await this.updateColumnsByIdWithoutHooks({
243
- id: cloudResourceId,
244
- data: data,
245
- });
246
- } catch (err) {
247
- /*
248
- * Liveness must survive bad metadata. The enriched write carries
249
- * `lastSeenAt` and `otelCollectorStatus` alongside optional,
250
- * collector-supplied columns; if any one of them makes Postgres
251
- * reject the statement, lastSeenAt silently stops advancing and
252
- * markDisconnectedResources flips a perfectly healthy cloud resource
253
- * to "disconnected" 15 minutes later — with telemetry still arriving
254
- * the whole time. That was issue #3006 on the Host path (a host.ip
255
- * list longer than the then-varchar(500) column). Retry with liveness
256
- * only so the enrichment is what gets dropped, never the heartbeat.
257
- */
258
- logger.warn(
259
- `CloudResourceService.updateLastSeen: metadata write failed for cloud resource ${cloudResourceId.toString()}, falling back to a liveness-only update: ${
260
- err instanceof Error ? err.message : String(err)
261
- }`,
262
- );
263
-
264
- /*
265
- * The throttle fingerprint was already stored, so without this the
266
- * next batch would short-circuit on a cache hit and skip the retry
267
- * for the rest of the window.
268
- */
269
- try {
270
- await GlobalCache.deleteKey(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
271
- } catch {
272
- // Best-effort; the fallback write below is what actually matters.
273
- }
274
-
275
- await this.updateColumnsByIdWithoutHooks({
276
- id: cloudResourceId,
277
- data: liveness,
278
- });
279
- }
210
+ await ResourceHeartbeat.write({
211
+ service: this,
212
+ id: cloudResourceId,
213
+ cacheNamespace: LAST_SEEN_CACHE_NAMESPACE,
214
+ throttleInSeconds: LAST_SEEN_THROTTLE_SECONDS,
215
+ liveness: liveness,
216
+ metadata: metadata,
217
+ fingerprint: extrasFingerprint,
218
+ describe: `cloud resource ${cloudResourceId.toString()}`,
219
+ });
280
220
  }
281
221
 
282
222
  @CaptureSpan()
@@ -14,6 +14,11 @@ import {
14
14
  import GitHubUtil, {
15
15
  GitHubRepository,
16
16
  } from "../Utils/CodeRepository/GitHub/GitHub";
17
+ import GitHubInstallationBinding from "../Utils/CodeRepository/GitHub/GitHubInstallationBinding";
18
+ import BadDataException from "../../Types/Exception/BadDataException";
19
+ import CreateBy from "../Types/Database/CreateBy";
20
+ import UpdateBy from "../Types/Database/UpdateBy";
21
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
17
22
 
18
23
  export interface ImportReposFromInstallationResult {
19
24
  imported: number;
@@ -25,6 +30,93 @@ export class Service extends DatabaseService<Model> {
25
30
  super(Model);
26
31
  }
27
32
 
33
+ /*
34
+ * Last line of defence for the installation-to-project binding.
35
+ *
36
+ * gitHubAppInstallationId is already closed to API callers at the column
37
+ * level, but that only stops requests that go through permission checks —
38
+ * every write here is made with `props: { isRoot: true }`, which skips them
39
+ * entirely. This hook runs regardless of isRoot, so any route (including one
40
+ * added later) that writes a row carrying another tenant's installation ID
41
+ * is refused at the service boundary rather than at the token endpoint.
42
+ *
43
+ * The legitimate writer is importReposFromInstallation, which the install
44
+ * callback and the installation webhooks only ever call for a project the
45
+ * installation is already bound to — so this check passes for them.
46
+ */
47
+ @CaptureSpan()
48
+ protected override async onBeforeCreate(
49
+ createBy: CreateBy<Model>,
50
+ ): Promise<OnCreate<Model>> {
51
+ const installationId: string | undefined =
52
+ createBy.data.gitHubAppInstallationId;
53
+
54
+ if (installationId) {
55
+ const projectId: ObjectID | undefined =
56
+ createBy.data.projectId || createBy.props.tenantId;
57
+
58
+ if (!projectId) {
59
+ throw new BadDataException(
60
+ "A project is required to connect a GitHub App installation to a repository.",
61
+ );
62
+ }
63
+
64
+ await GitHubInstallationBinding.assertInstallationBoundToProject({
65
+ projectId: projectId,
66
+ installationId: installationId,
67
+ });
68
+ }
69
+
70
+ return { createBy, carryForward: null };
71
+ }
72
+
73
+ /*
74
+ * Same rule on the update path. Clearing the installation ID is always
75
+ * allowed — that is how the `installation.deleted` webhook tears a
76
+ * disconnected installation out of the database.
77
+ */
78
+ @CaptureSpan()
79
+ protected override async onBeforeUpdate(
80
+ updateBy: UpdateBy<Model>,
81
+ ): Promise<OnUpdate<Model>> {
82
+ const installationId: string | undefined = updateBy.data
83
+ .gitHubAppInstallationId as string | undefined;
84
+
85
+ if (installationId) {
86
+ /*
87
+ * updateBy matches by query rather than by id, so check every row the
88
+ * write would touch: one unbound row is enough to leak a token.
89
+ */
90
+ const repositories: Array<Model> = await this.findBy({
91
+ query: updateBy.query,
92
+ select: {
93
+ _id: true,
94
+ projectId: true,
95
+ },
96
+ limit: LIMIT_MAX,
97
+ skip: 0,
98
+ props: {
99
+ isRoot: true,
100
+ },
101
+ });
102
+
103
+ for (const repository of repositories) {
104
+ if (!repository.projectId) {
105
+ throw new BadDataException(
106
+ "A project is required to connect a GitHub App installation to a repository.",
107
+ );
108
+ }
109
+
110
+ await GitHubInstallationBinding.assertInstallationBoundToProject({
111
+ projectId: repository.projectId,
112
+ installationId: installationId,
113
+ });
114
+ }
115
+ }
116
+
117
+ return { updateBy, carryForward: null };
118
+ }
119
+
28
120
  /*
29
121
  * Imports all repositories accessible to a GitHub App installation into a
30
122
  * project. Repositories that already exist for the project (matched on
@@ -174,13 +266,24 @@ export class Service extends DatabaseService<Model> {
174
266
  },
175
267
  });
176
268
 
177
- // Only GitHub-App-connected repos can be cloned/pushed by the agent.
269
+ /*
270
+ * Only GitHub-App-connected repos can be cloned/pushed by the agent — and
271
+ * only ones whose installation this project actually owns. Resolution
272
+ * probes EVERY candidate's file tree, so an unbound row here would have
273
+ * the server read another tenant's private repository on its own
274
+ * initiative (GHSA-xx95-gmcf-7q86).
275
+ */
276
+ const boundInstallationId: string | null =
277
+ await GitHubInstallationBinding.getBoundInstallationId(data.projectId);
278
+
178
279
  const resolvable: Array<ResolvableRepository> = repositories
179
280
  .filter((repository: Model) => {
180
281
  return (
181
282
  repository.repositoryHostedAt === CodeRepositoryType.GitHub &&
182
283
  Boolean(repository.gitHubAppInstallationId) &&
183
- Boolean(repository.id)
284
+ Boolean(repository.id) &&
285
+ Boolean(boundInstallationId) &&
286
+ repository.gitHubAppInstallationId === boundInstallationId
184
287
  );
185
288
  })
186
289
  .map((repository: Model) => {