@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
@@ -0,0 +1,220 @@
1
+ import { WidenHashedStringColumnsForScrypt1786023262402 } from "../../../Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt";
2
+ import SchemaMigrations from "../../../Server/Infrastructure/Postgres/SchemaMigrations/Index";
3
+ import ColumnLength from "../../../Types/Database/ColumnLength";
4
+ import { QueryRunner } from "typeorm";
5
+ import { describe, expect, test } from "@jest/globals";
6
+
7
+ /*
8
+ * The migration that widens every HashedString column so a scrypt hash, which
9
+ * carries its own cost parameters, fits.
10
+ *
11
+ * THE REASON THIS FILE EXISTS.
12
+ *
13
+ * `typeorm migration:generate` does not express a varchar length change as
14
+ * ALTER COLUMN TYPE. It emits DROP COLUMN followed by ADD COLUMN — which for
15
+ * these six columns means deleting every password, every status page and
16
+ * dashboard master password, and every active session in the instance. The
17
+ * generated file was replaced by hand for exactly that reason.
18
+ *
19
+ * Anyone who regenerates this migration (or writes the next widening the same
20
+ * way) gets the destructive version back, and it looks perfectly ordinary in
21
+ * review. The first test below is the guard: no statement in this migration
22
+ * may drop a column.
23
+ */
24
+
25
+ type MakeQueryRunnerResult = {
26
+ runner: QueryRunner;
27
+ statements: Array<string>;
28
+ };
29
+
30
+ type MakeQueryRunnerFunction = () => MakeQueryRunnerResult;
31
+
32
+ const makeQueryRunner: MakeQueryRunnerFunction = (): MakeQueryRunnerResult => {
33
+ const statements: Array<string> = [];
34
+
35
+ const query: (...args: Array<unknown>) => Promise<undefined> = (
36
+ ...args: Array<unknown>
37
+ ): Promise<undefined> => {
38
+ statements.push(String(args[0]));
39
+ return Promise.resolve(undefined);
40
+ };
41
+
42
+ return {
43
+ runner: { query } as unknown as QueryRunner,
44
+ statements,
45
+ };
46
+ };
47
+
48
+ const WIDENED: ReadonlyArray<[string, string]> = [
49
+ ["User", "password"],
50
+ ["StatusPagePrivateUser", "password"],
51
+ ["StatusPage", "masterPassword"],
52
+ ["Dashboard", "masterPassword"],
53
+ ["UserSession", "refreshToken"],
54
+ ["StatusPagePrivateUserSession", "refreshToken"],
55
+ ];
56
+
57
+ describe("WidenHashedStringColumnsForScrypt — must not destroy data", () => {
58
+ test("up() never drops a column", async () => {
59
+ /*
60
+ * The whole point. A DROP COLUMN here silently deletes every credential
61
+ * in the database, and the migration would still 'succeed'.
62
+ */
63
+ const { runner, statements } = makeQueryRunner();
64
+
65
+ await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
66
+
67
+ for (const statement of statements) {
68
+ expect(statement).not.toMatch(/DROP COLUMN/i);
69
+ }
70
+ });
71
+
72
+ test("down() never drops a column either", async () => {
73
+ const { runner, statements } = makeQueryRunner();
74
+
75
+ await new WidenHashedStringColumnsForScrypt1786023262402().down(runner);
76
+
77
+ for (const statement of statements) {
78
+ expect(statement).not.toMatch(/DROP COLUMN/i);
79
+ }
80
+ });
81
+
82
+ test("up() is nothing but ALTER COLUMN TYPE statements", async () => {
83
+ const { runner, statements } = makeQueryRunner();
84
+
85
+ await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
86
+
87
+ for (const statement of statements) {
88
+ expect(statement).toMatch(
89
+ /^ALTER TABLE "[A-Za-z]+" ALTER COLUMN "[A-Za-z]+" TYPE character varying\(\d+\)$/,
90
+ );
91
+ }
92
+ });
93
+
94
+ test("it does not touch constraints or indexes", async () => {
95
+ /*
96
+ * The generated version dropped and recreated the refresh-token unique
97
+ * constraints and indexes purely to work around its own DROP COLUMN.
98
+ * Widening a varchar needs no table rewrite, so the indexes survive
99
+ * untouched and none of that churn is necessary.
100
+ */
101
+ const { runner, statements } = makeQueryRunner();
102
+
103
+ await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
104
+
105
+ for (const statement of statements) {
106
+ expect(statement).not.toMatch(/CONSTRAINT/i);
107
+ expect(statement).not.toMatch(/INDEX/i);
108
+ }
109
+ });
110
+ });
111
+
112
+ describe("WidenHashedStringColumnsForScrypt — SQL contract", () => {
113
+ test("up() widens exactly the six HashedString columns", async () => {
114
+ const { runner, statements } = makeQueryRunner();
115
+
116
+ await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
117
+
118
+ expect(statements).toHaveLength(WIDENED.length);
119
+
120
+ for (const [table, column] of WIDENED) {
121
+ expect(statements).toContain(
122
+ `ALTER TABLE "${table}" ALTER COLUMN "${column}" TYPE character varying(255)`,
123
+ );
124
+ }
125
+ });
126
+
127
+ test("it widens to whatever ColumnLength.HashedString says", async () => {
128
+ /*
129
+ * The entity metadata and the migration have to agree, or the drift check
130
+ * fails and the next generated migration tries to 'fix' it.
131
+ */
132
+ const { runner, statements } = makeQueryRunner();
133
+
134
+ await new WidenHashedStringColumnsForScrypt1786023262402().up(runner);
135
+
136
+ for (const statement of statements) {
137
+ expect(statement).toContain(
138
+ `character varying(${ColumnLength.HashedString})`,
139
+ );
140
+ }
141
+ });
142
+
143
+ test("down() returns the same six columns to 64", async () => {
144
+ const { runner, statements } = makeQueryRunner();
145
+
146
+ await new WidenHashedStringColumnsForScrypt1786023262402().down(runner);
147
+
148
+ expect(statements).toHaveLength(WIDENED.length);
149
+
150
+ for (const [table, column] of WIDENED) {
151
+ expect(statements).toContain(
152
+ `ALTER TABLE "${table}" ALTER COLUMN "${column}" TYPE character varying(64)`,
153
+ );
154
+ }
155
+ });
156
+
157
+ test("down() reverses up()'s order", async () => {
158
+ const up: MakeQueryRunnerResult = makeQueryRunner();
159
+ const down: MakeQueryRunnerResult = makeQueryRunner();
160
+
161
+ await new WidenHashedStringColumnsForScrypt1786023262402().up(up.runner);
162
+ await new WidenHashedStringColumnsForScrypt1786023262402().down(
163
+ down.runner,
164
+ );
165
+
166
+ type ColumnsFunction = (statements: Array<string>) => Array<string>;
167
+
168
+ const columns: ColumnsFunction = (
169
+ statements: Array<string>,
170
+ ): Array<string> => {
171
+ return statements.map((statement: string) => {
172
+ return statement
173
+ .match(/ALTER TABLE "([^"]+)" ALTER COLUMN "([^"]+)"/)!
174
+ .slice(1, 3)
175
+ .join(".");
176
+ });
177
+ };
178
+
179
+ expect(columns(down.statements)).toEqual(columns(up.statements).reverse());
180
+ });
181
+
182
+ test("the widened column holds a scrypt hash with room to spare", () => {
183
+ // `scrypt$N=16384,r=8,p=1$` + 64 hex characters.
184
+ const worstCaseToday: number = "scrypt$N=1048576,r=32,p=16$".length + 64;
185
+
186
+ expect(ColumnLength.HashedString).toBeGreaterThan(worstCaseToday);
187
+ });
188
+ });
189
+
190
+ describe("WidenHashedStringColumnsForScrypt — registration", () => {
191
+ test("is registered in SchemaMigrations/Index.ts", () => {
192
+ expect(SchemaMigrations).toContain(
193
+ WidenHashedStringColumnsForScrypt1786023262402,
194
+ );
195
+ });
196
+
197
+ test("appears exactly once", () => {
198
+ const occurrences: number = SchemaMigrations.filter(
199
+ (migration: { name: string }) => {
200
+ return (
201
+ migration.name === "WidenHashedStringColumnsForScrypt1786023262402"
202
+ );
203
+ },
204
+ ).length;
205
+
206
+ expect(occurrences).toBe(1);
207
+ });
208
+
209
+ test("runs after the migration that added the salt columns", () => {
210
+ const names: Array<string> = SchemaMigrations.map(
211
+ (migration: { name: string }) => {
212
+ return migration.name;
213
+ },
214
+ );
215
+
216
+ expect(
217
+ names.indexOf("WidenHashedStringColumnsForScrypt1786023262402"),
218
+ ).toBeGreaterThan(names.indexOf("AddPerUserPasswordSalt1786018109307"));
219
+ });
220
+ });
@@ -15,7 +15,8 @@ import CodeRepositoryModel from "../../../../Models/DatabaseModels/CodeRepositor
15
15
  import TelemetryException from "../../../../Models/DatabaseModels/TelemetryException";
16
16
  import CodeRepositoryType from "../../../../Types/CodeRepository/CodeRepositoryType";
17
17
  import ObjectID from "../../../../Types/ObjectID";
18
- import { describe, expect, test, afterEach } from "@jest/globals";
18
+ import GitHubInstallationBinding from "../../../../Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding";
19
+ import { describe, expect, test, afterEach, beforeEach } from "@jest/globals";
19
20
 
20
21
  /*
21
22
  * The code tools are the bridge from a telemetry signal to the source that
@@ -86,6 +87,19 @@ function mockFileContent(content: string): void {
86
87
  });
87
88
  }
88
89
 
90
+ /*
91
+ * findReadableRepositories now also proves each row's GitHub App installation
92
+ * belongs to the row's project (GHSA-xx95-gmcf-7q86), which is a database
93
+ * read. That guard has its own tests; here it would just be a Postgres
94
+ * connection these tool tests do not need, so treat every fixture row as
95
+ * bound.
96
+ */
97
+ beforeEach(() => {
98
+ jest
99
+ .spyOn(GitHubInstallationBinding, "getBoundInstallationId")
100
+ .mockResolvedValue("12345");
101
+ });
102
+
89
103
  afterEach(() => {
90
104
  jest.restoreAllMocks();
91
105
  });
@@ -119,6 +133,38 @@ describe("list_code_repositories", () => {
119
133
  expect(result.rowCount).toBe(1);
120
134
  });
121
135
 
136
+ /*
137
+ * GHSA-xx95-gmcf-7q86. A row can be in this project and still name a GitHub
138
+ * App installation the project does not own — that combination used to be
139
+ * creatable, and these tools would then read, commit and open pull requests
140
+ * in another tenant's private repositories. Being in the project is not the
141
+ * same as owning the installation, so an unbound row must be invisible here
142
+ * exactly like an uninstalled one.
143
+ */
144
+ test("hides a repository whose installation is not bound to the project", async () => {
145
+ jest
146
+ .spyOn(GitHubInstallationBinding, "getBoundInstallationId")
147
+ .mockResolvedValue("12345");
148
+
149
+ mockRepositories([
150
+ buildRepository({ name: "our-repo", installationId: "12345" }),
151
+ buildRepository({
152
+ id: ObjectID.generate(),
153
+ name: "someone-elses-repo",
154
+ installationId: "99999999",
155
+ }),
156
+ ]);
157
+
158
+ const result: ToolExecutionResult = await ListCodeRepositoriesTool.execute(
159
+ {},
160
+ ctx,
161
+ );
162
+
163
+ expect(result.dataForLlm).toContain("our-repo");
164
+ expect(result.dataForLlm).not.toContain("someone-elses-repo");
165
+ expect(result.rowCount).toBe(1);
166
+ });
167
+
122
168
  test("says plainly when no repository is connected", async () => {
123
169
  mockRepositories([]);
124
170
 
@@ -23,6 +23,7 @@ import AIAgentTaskPullRequest from "../../../../Models/DatabaseModels/AIAgentTas
23
23
  import CodeRepositoryType from "../../../../Types/CodeRepository/CodeRepositoryType";
24
24
  import PullRequestState from "../../../../Types/CodeRepository/PullRequestState";
25
25
  import ObjectID from "../../../../Types/ObjectID";
26
+ import GitHubInstallationBinding from "../../../../Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding";
26
27
  import { describe, expect, test, afterEach, beforeEach } from "@jest/globals";
27
28
 
28
29
  /*
@@ -68,6 +69,15 @@ beforeEach(() => {
68
69
  .spyOn(CodeRepositoryService, "findBy")
69
70
  .mockResolvedValue([buildRepository()] as never);
70
71
  jest.spyOn(GitHubUtil, "getDefaultBranchName").mockResolvedValue("main");
72
+
73
+ /*
74
+ * resolveTargetRepository now proves each row's GitHub App installation
75
+ * belongs to the row's project (GHSA-xx95-gmcf-7q86), which is a database
76
+ * read. That guard has its own tests; treat the fixture row as bound.
77
+ */
78
+ jest
79
+ .spyOn(GitHubInstallationBinding, "getBoundInstallationId")
80
+ .mockResolvedValue("12345");
71
81
  });
72
82
 
73
83
  afterEach(() => {
@@ -0,0 +1,267 @@
1
+ import {
2
+ afterAll,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ it,
7
+ jest,
8
+ } from "@jest/globals";
9
+ import APIKeyAccessPermission from "../../../../Server/Utils/APIKey/AccessPermission";
10
+ import ApiKeyPermissionService from "../../../../Server/Services/ApiKeyPermissionService";
11
+ import APIKeyPermission from "../../../../Models/DatabaseModels/ApiKeyPermission";
12
+ import Label from "../../../../Models/DatabaseModels/Label";
13
+ import ObjectID from "../../../../Types/ObjectID";
14
+ import Permission, {
15
+ UserGlobalAccessPermission,
16
+ UserPermission,
17
+ UserTenantAccessPermission,
18
+ } from "../../../../Types/Permission";
19
+ import { getJestSpyOn } from "../../../Spy";
20
+
21
+ /*
22
+ * This class turns an API key into the permission set a request runs under, so
23
+ * every method here is a privilege-granting decision. The security invariant
24
+ * that must hold across all of them: a *default* (ordinary) API key is never
25
+ * silently handed ProjectOwner, while a *master* key deliberately is. A
26
+ * regression that leaks ProjectOwner into the default path would give every API
27
+ * key full control of the project; one that dropped it from the master path
28
+ * would break trusted internal automation. Both directions are pinned below.
29
+ *
30
+ * getApiTenantAccessPermission also reshapes stored permission rows into the
31
+ * runtime UserPermission shape (labels -> labelIds, block flag preserved). That
32
+ * mapping is where a dropped label would quietly widen a scoped permission, so
33
+ * it is tested against a spied service rather than a live database.
34
+ */
35
+
36
+ const projectId: ObjectID = ObjectID.generate();
37
+ const apiKeyId: ObjectID = ObjectID.generate();
38
+
39
+ const permissionValues: (
40
+ permissions: Array<UserPermission>,
41
+ ) => Array<Permission> = (
42
+ permissions: Array<UserPermission>,
43
+ ): Array<Permission> => {
44
+ return permissions.map((p: UserPermission) => {
45
+ return p.permission;
46
+ });
47
+ };
48
+
49
+ describe("APIKeyAccessPermission.getDefaultApiGlobalPermission", () => {
50
+ it("grants Public, User and CurrentUser for the project", async () => {
51
+ const perm: UserGlobalAccessPermission =
52
+ await APIKeyAccessPermission.getDefaultApiGlobalPermission(projectId);
53
+
54
+ expect(perm._type).toBe("UserGlobalAccessPermission");
55
+ expect(
56
+ perm.projectIds.map((p: ObjectID) => {
57
+ return p.toString();
58
+ }),
59
+ ).toEqual([projectId.toString()]);
60
+ expect(perm.globalPermissions).toEqual([
61
+ Permission.Public,
62
+ Permission.User,
63
+ Permission.CurrentUser,
64
+ ]);
65
+ });
66
+
67
+ it("does NOT grant ProjectOwner to an ordinary key", async () => {
68
+ // The core boundary: a default key must never be an owner.
69
+ const perm: UserGlobalAccessPermission =
70
+ await APIKeyAccessPermission.getDefaultApiGlobalPermission(projectId);
71
+ expect(perm.globalPermissions).not.toContain(Permission.ProjectOwner);
72
+ });
73
+ });
74
+
75
+ describe("APIKeyAccessPermission.getMasterKeyApiGlobalPermission", () => {
76
+ it("grants everything the default key has, plus ProjectOwner", async () => {
77
+ const master: UserGlobalAccessPermission =
78
+ await APIKeyAccessPermission.getMasterKeyApiGlobalPermission(projectId);
79
+ const def: UserGlobalAccessPermission =
80
+ await APIKeyAccessPermission.getDefaultApiGlobalPermission(projectId);
81
+
82
+ // Superset: no default grant is lost when elevating to master.
83
+ for (const p of def.globalPermissions) {
84
+ expect(master.globalPermissions).toContain(p);
85
+ }
86
+ // The one deliberate elevation.
87
+ expect(master.globalPermissions).toContain(Permission.ProjectOwner);
88
+ });
89
+
90
+ it("scopes to exactly the one project", async () => {
91
+ const master: UserGlobalAccessPermission =
92
+ await APIKeyAccessPermission.getMasterKeyApiGlobalPermission(projectId);
93
+ expect(
94
+ master.projectIds.map((p: ObjectID) => {
95
+ return p.toString();
96
+ }),
97
+ ).toEqual([projectId.toString()]);
98
+ });
99
+ });
100
+
101
+ describe("APIKeyAccessPermission.getMasterApiTenantAccessPermission", () => {
102
+ it("elevates the default tenant permission with ProjectOwner", async () => {
103
+ const perm: UserTenantAccessPermission =
104
+ await APIKeyAccessPermission.getMasterApiTenantAccessPermission(
105
+ projectId,
106
+ );
107
+
108
+ expect(perm._type).toBe("UserTenantAccessPermission");
109
+ expect(perm.projectId.toString()).toBe(projectId.toString());
110
+ expect(permissionValues(perm.permissions)).toContain(
111
+ Permission.ProjectOwner,
112
+ );
113
+ // The default tenant baseline is retained alongside the elevation.
114
+ expect(permissionValues(perm.permissions)).toContain(
115
+ Permission.CurrentUser,
116
+ );
117
+ });
118
+ });
119
+
120
+ describe("APIKeyAccessPermission.getApiTenantAccessPermission", () => {
121
+ const spyFindBy: jest.SpyInstance = getJestSpyOn(
122
+ ApiKeyPermissionService,
123
+ "findBy",
124
+ );
125
+
126
+ beforeEach(() => {
127
+ jest.clearAllMocks();
128
+ });
129
+
130
+ afterAll(() => {
131
+ jest.restoreAllMocks();
132
+ });
133
+
134
+ const makeRow: (data: {
135
+ permission: Permission;
136
+ labelIds?: Array<ObjectID>;
137
+ isBlockPermission?: boolean;
138
+ }) => APIKeyPermission = (data: {
139
+ permission: Permission;
140
+ labelIds?: Array<ObjectID>;
141
+ isBlockPermission?: boolean;
142
+ }): APIKeyPermission => {
143
+ const row: APIKeyPermission = new APIKeyPermission();
144
+ row.permission = data.permission;
145
+ if (data.labelIds) {
146
+ row.labels = data.labelIds.map((id: ObjectID) => {
147
+ const label: Label = new Label();
148
+ label.id = id;
149
+ return label;
150
+ });
151
+ }
152
+ row.isBlockPermission = data.isBlockPermission ?? false;
153
+ return row;
154
+ };
155
+
156
+ it("queries the permissions for exactly this api key", async () => {
157
+ spyFindBy.mockResolvedValue([] as never);
158
+
159
+ await APIKeyAccessPermission.getApiTenantAccessPermission(
160
+ projectId,
161
+ apiKeyId,
162
+ );
163
+
164
+ expect(spyFindBy).toHaveBeenCalledTimes(1);
165
+ const arg: { query: { apiKeyId: ObjectID } } = spyFindBy.mock
166
+ .calls[0]![0] as { query: { apiKeyId: ObjectID } };
167
+ expect(arg.query.apiKeyId.toString()).toBe(apiKeyId.toString());
168
+ });
169
+
170
+ it("returns just the default baseline when the key has no permissions", async () => {
171
+ spyFindBy.mockResolvedValue([] as never);
172
+
173
+ const perm: UserTenantAccessPermission =
174
+ await APIKeyAccessPermission.getApiTenantAccessPermission(
175
+ projectId,
176
+ apiKeyId,
177
+ );
178
+
179
+ // No stored rows -> no ProjectOwner. An empty key is not an owner.
180
+ expect(permissionValues(perm.permissions)).not.toContain(
181
+ Permission.ProjectOwner,
182
+ );
183
+ expect(permissionValues(perm.permissions)).toContain(
184
+ Permission.CurrentUser,
185
+ );
186
+ });
187
+
188
+ it("maps each stored permission row onto the tenant permission", async () => {
189
+ const labelId: ObjectID = ObjectID.generate();
190
+ spyFindBy.mockResolvedValue([
191
+ makeRow({
192
+ permission: Permission.CreateProjectIncident,
193
+ labelIds: [labelId],
194
+ isBlockPermission: false,
195
+ }),
196
+ ] as never);
197
+
198
+ const perm: UserTenantAccessPermission =
199
+ await APIKeyAccessPermission.getApiTenantAccessPermission(
200
+ projectId,
201
+ apiKeyId,
202
+ );
203
+
204
+ const mapped: UserPermission | undefined = perm.permissions.find(
205
+ (p: UserPermission) => {
206
+ return p.permission === Permission.CreateProjectIncident;
207
+ },
208
+ );
209
+ expect(mapped).toBeDefined();
210
+ /*
211
+ * The label scope must survive the mapping, or the permission silently
212
+ * widens from "these labels" to "all".
213
+ */
214
+ expect(
215
+ mapped!.labelIds.map((l: ObjectID) => {
216
+ return l.toString();
217
+ }),
218
+ ).toEqual([labelId.toString()]);
219
+ });
220
+
221
+ it("preserves the block flag so a deny row stays a deny row", async () => {
222
+ spyFindBy.mockResolvedValue([
223
+ makeRow({
224
+ permission: Permission.CreateProjectIncident,
225
+ labelIds: [],
226
+ isBlockPermission: true,
227
+ }),
228
+ ] as never);
229
+
230
+ const perm: UserTenantAccessPermission =
231
+ await APIKeyAccessPermission.getApiTenantAccessPermission(
232
+ projectId,
233
+ apiKeyId,
234
+ );
235
+
236
+ const mapped: UserPermission | undefined = perm.permissions.find(
237
+ (p: UserPermission) => {
238
+ return p.permission === Permission.CreateProjectIncident;
239
+ },
240
+ );
241
+ // Flipping a block permission to an allow would invert an admin's intent.
242
+ expect(mapped!.isBlockPermission).toBe(true);
243
+ });
244
+
245
+ it("treats a row with no labels as an empty (unscoped) label set", async () => {
246
+ const row: APIKeyPermission = new APIKeyPermission();
247
+ row.permission = Permission.CreateProjectIncident;
248
+ // labels intentionally left undefined.
249
+ row.isBlockPermission = false;
250
+ spyFindBy.mockResolvedValue([row] as never);
251
+
252
+ const perm: UserTenantAccessPermission =
253
+ await APIKeyAccessPermission.getApiTenantAccessPermission(
254
+ projectId,
255
+ apiKeyId,
256
+ );
257
+
258
+ const mapped: UserPermission | undefined = perm.permissions.find(
259
+ (p: UserPermission) => {
260
+ return p.permission === Permission.CreateProjectIncident;
261
+ },
262
+ );
263
+ expect(mapped).toBeDefined();
264
+ // No crash on undefined labels, and it becomes [] rather than undefined.
265
+ expect(mapped!.labelIds).toEqual([]);
266
+ });
267
+ });