@oneuptime/common 13.0.8 → 14.0.0

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 (333) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +23 -15
  2. package/Models/DatabaseModels/GlobalConfig.ts +24 -0
  3. package/Models/DatabaseModels/Monitor.ts +3 -2
  4. package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
  5. package/Scripts/generate-service-worker.js +65 -4
  6. package/Server/API/BaseAPI.ts +26 -5
  7. package/Server/API/GlobalConfigAPI.ts +228 -462
  8. package/Server/API/ProjectOIDC.ts +25 -16
  9. package/Server/API/ProjectSSO.ts +24 -16
  10. package/Server/API/StatusPageAPI.ts +100 -42
  11. package/Server/API/TeamMemberAPI.ts +10 -1
  12. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  13. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  14. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  15. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  16. package/Server/EnvironmentConfig.ts +147 -2
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  19. package/Server/Middleware/UserAuthorization.ts +38 -16
  20. package/Server/Services/AuditLogService.ts +84 -1079
  21. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  22. package/Server/Services/GlobalConfigService.ts +80 -1
  23. package/Server/Services/MonitorProbeService.ts +116 -20
  24. package/Server/Services/MonitorService.ts +56 -4
  25. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  26. package/Server/Services/MonitorTemplateService.ts +26 -0
  27. package/Server/Services/ProjectService.ts +284 -0
  28. package/Server/Services/RumSessionPinService.ts +25 -6
  29. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  30. package/Server/Services/StatusPageService.ts +78 -1
  31. package/Server/Services/TeamMemberService.ts +11 -0
  32. package/Server/Services/TeamService.ts +10 -0
  33. package/Server/Services/UserService.ts +10 -17
  34. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  35. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  36. package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
  37. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  38. package/Server/Types/Markdown.ts +41 -0
  39. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  40. package/Server/Utils/EditionEnforcement.ts +233 -0
  41. package/Server/Utils/FrontendEnvironment.ts +41 -2
  42. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  43. package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
  44. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  45. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
  46. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  47. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  48. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  49. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  50. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  51. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  52. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  53. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  54. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  55. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  56. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  57. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  58. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  59. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  60. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
  61. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  62. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  63. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
  64. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  65. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  66. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  67. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  68. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  69. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  70. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  71. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  72. package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
  73. package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
  74. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  75. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  76. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  77. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  78. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  79. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  80. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  81. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  82. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  83. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  84. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  85. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  86. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  87. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  88. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  89. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  90. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  91. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  92. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  93. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  94. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  95. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  96. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  97. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  98. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  99. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  100. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  101. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  102. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  103. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  104. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  105. package/Tests/Server/Types/Markdown.test.ts +181 -0
  106. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  107. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  108. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  109. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  110. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  111. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  112. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
  113. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  114. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  115. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  116. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  117. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  118. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  119. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  120. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
  121. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  122. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  123. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  124. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  125. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  126. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  127. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  128. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  129. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  130. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  131. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  132. package/Types/API/Hostname.ts +63 -16
  133. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  134. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  135. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  136. package/Types/Database/CanonicalModelInstance.ts +102 -0
  137. package/Types/Database/TableColumn.ts +20 -6
  138. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  139. package/Types/IP/IPv6.ts +6 -1
  140. package/Types/Monitor/MonitorStep.ts +6 -1
  141. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  142. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  143. package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
  144. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  145. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  146. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  147. package/UI/Components/ModelList/ModelList.tsx +11 -1
  148. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  149. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  150. package/UI/esbuild-enterprise.js +263 -0
  151. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  152. package/Utils/HostAddressUtil.ts +94 -0
  153. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  154. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  155. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  156. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  157. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  158. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  159. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  160. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  161. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  162. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  163. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  164. package/build/dist/Server/API/BaseAPI.js +26 -5
  165. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  166. package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
  167. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  168. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  169. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  170. package/build/dist/Server/API/ProjectSSO.js +24 -16
  171. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  172. package/build/dist/Server/API/StatusPageAPI.js +93 -43
  173. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  174. package/build/dist/Server/API/TeamMemberAPI.js +8 -1
  175. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  176. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  177. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  178. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  179. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  180. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  181. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  182. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  183. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  184. package/build/dist/Server/EnvironmentConfig.js +84 -2
  185. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  186. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  187. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  188. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  190. package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
  191. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  192. package/build/dist/Server/Services/AuditLogService.js +71 -740
  193. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  194. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  195. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  196. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  197. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  198. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  199. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  200. package/build/dist/Server/Services/MonitorService.js +45 -4
  201. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  202. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  203. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  204. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  205. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  206. package/build/dist/Server/Services/ProjectService.js +214 -0
  207. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  208. package/build/dist/Server/Services/RumSessionPinService.js +24 -6
  209. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  210. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  211. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  212. package/build/dist/Server/Services/StatusPageService.js +71 -0
  213. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  214. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  215. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  216. package/build/dist/Server/Services/TeamService.js +9 -0
  217. package/build/dist/Server/Services/TeamService.js.map +1 -1
  218. package/build/dist/Server/Services/UserService.js +10 -16
  219. package/build/dist/Server/Services/UserService.js.map +1 -1
  220. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  221. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  222. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  223. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  224. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
  225. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  226. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  227. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  228. package/build/dist/Server/Types/Markdown.js +31 -0
  229. package/build/dist/Server/Types/Markdown.js.map +1 -1
  230. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  231. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  232. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  233. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  234. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  235. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  236. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  237. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  238. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
  239. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  240. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  241. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  242. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
  243. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  245. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  246. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  247. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  248. package/build/dist/Types/API/Hostname.js +59 -11
  249. package/build/dist/Types/API/Hostname.js.map +1 -1
  250. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  251. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  252. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  253. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  254. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  255. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  256. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  257. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  258. package/build/dist/Types/Database/TableColumn.js +19 -6
  259. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  260. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  261. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  262. package/build/dist/Types/IP/IPv6.js +6 -1
  263. package/build/dist/Types/IP/IPv6.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  265. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  266. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  267. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  268. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  269. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
  270. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  271. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  272. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  273. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  274. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  275. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  276. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  277. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  278. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  279. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  280. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  281. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  282. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  283. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  284. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  285. package/build/dist/Utils/HostAddressUtil.js +80 -0
  286. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  287. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  288. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  289. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  290. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  291. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  292. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  293. package/jest.config.json +3 -1
  294. package/package.json +1 -1
  295. package/test-setup.sh +103 -34
  296. package/tsconfig.json +13 -0
  297. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  298. package/Server/API/TeamComplianceAPI.ts +0 -158
  299. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  300. package/Server/Services/TeamComplianceService.ts +0 -733
  301. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  302. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  303. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  304. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  305. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
  306. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  307. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  308. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
  309. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  310. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  311. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  312. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  313. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  314. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  315. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  316. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  317. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  318. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  319. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  320. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  321. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  322. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  323. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  324. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  325. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  326. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  327. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  328. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  329. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  330. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  331. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  332. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  333. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -26,6 +26,7 @@ import ExternalStatusPageMonitorCriteria from "./Criteria/ExternalStatusPageMoni
26
26
  import MonitorCriteriaMessageBuilder from "./MonitorCriteriaMessageBuilder";
27
27
  import MonitorCriteriaDataExtractor from "./MonitorCriteriaDataExtractor";
28
28
  import MonitorCriteriaMessageFormatter from "./MonitorCriteriaMessageFormatter";
29
+ import AffectedResourceList from "./AffectedResourceList";
29
30
  import MonitorCriteriaInstance from "../../../Types/Monitor/MonitorCriteriaInstance";
30
31
  import MonitorStep from "../../../Types/Monitor/MonitorStep";
31
32
  import FilterCondition from "../../../Types/Filter/FilterCondition";
@@ -1436,7 +1437,7 @@ ${contextBlock}
1436
1437
  return ` _(scope partially carried — not applied: ${dropped.join(", ")})_`;
1437
1438
  }
1438
1439
  /**
1439
- * The unit for a value in a per-platform "Affected Resources" table.
1440
+ * The unit for a value in a per-platform "Affected Resources" list.
1440
1441
  *
1441
1442
  * These rows are NOT the same numbers as the criteria's breaching
1442
1443
  * samples: the worker collects them with a raw `MetricService.findBy`
@@ -1475,10 +1476,11 @@ ${contextBlock}
1475
1476
  }
1476
1477
  }
1477
1478
  /**
1478
- * Render one cell of a platform breakdown table's Value column.
1479
+ * Render the value of one resource in a platform "Affected Resources"
1480
+ * list.
1479
1481
  *
1480
- * Bold, because that is how these tables have always drawn the value —
1481
- * it is the one number in the row a reader scans for.
1482
+ * Bold, because that is how the breakdown has always drawn the value —
1483
+ * it is the one number on the line a reader scans for.
1482
1484
  */
1483
1485
  static formatPlatformResourceValue(input) {
1484
1486
  return `**${MetricValueFormatter.format({
@@ -1490,6 +1492,78 @@ ${contextBlock}
1490
1492
  metricName: input.metricName,
1491
1493
  })}**`;
1492
1494
  }
1495
+ /*
1496
+ * One Kubernetes resource as an "Affected Resources" list item.
1497
+ *
1498
+ * The item is titled by the most specific object the series names —
1499
+ * container, then pod, then workload, then node, then namespace — because
1500
+ * that is the thing that is actually breaching; everything above it in
1501
+ * the hierarchy is listed underneath as context. A series with no
1502
+ * Kubernetes identity at all is a cluster-level series, so it is titled
1503
+ * by the cluster.
1504
+ *
1505
+ * A container is titled together with its pod. Every replica of a
1506
+ * workload runs a container of the same name, so "Container `checkout`"
1507
+ * alone would title three crash-looping replicas identically and leave
1508
+ * the pod that tells them apart buried in the bullets.
1509
+ */
1510
+ static getKubernetesAffectedResourceEntry(input) {
1511
+ const resource = input.resource;
1512
+ // "Deployment", "StatefulSet", ... — the worker only ever sets one of those.
1513
+ const workloadLabel = resource.workloadType || "Workload";
1514
+ let kind = "Cluster";
1515
+ let name = AffectedResourceList.code(input.clusterName);
1516
+ if (resource.containerName) {
1517
+ kind = "Container";
1518
+ name = resource.podName
1519
+ ? `${AffectedResourceList.code(resource.containerName)} in pod ${AffectedResourceList.code(resource.podName)}`
1520
+ : AffectedResourceList.code(resource.containerName);
1521
+ }
1522
+ else if (resource.podName) {
1523
+ kind = "Pod";
1524
+ name = AffectedResourceList.code(resource.podName);
1525
+ }
1526
+ else if (resource.workloadName) {
1527
+ kind = workloadLabel;
1528
+ name = AffectedResourceList.code(resource.workloadName);
1529
+ }
1530
+ else if (resource.nodeName) {
1531
+ kind = "Node";
1532
+ name = AffectedResourceList.code(resource.nodeName);
1533
+ }
1534
+ else if (resource.namespace) {
1535
+ kind = "Namespace";
1536
+ name = AffectedResourceList.code(resource.namespace);
1537
+ }
1538
+ const details = [];
1539
+ if (resource.namespace && kind !== "Namespace") {
1540
+ details.push({
1541
+ label: "Namespace",
1542
+ value: AffectedResourceList.code(resource.namespace),
1543
+ });
1544
+ }
1545
+ const titledByWorkload = !resource.containerName &&
1546
+ !resource.podName &&
1547
+ Boolean(resource.workloadName);
1548
+ if (resource.workloadName && !titledByWorkload) {
1549
+ details.push({
1550
+ label: workloadLabel,
1551
+ value: AffectedResourceList.code(resource.workloadName),
1552
+ });
1553
+ }
1554
+ if (resource.nodeName && kind !== "Node") {
1555
+ details.push({
1556
+ label: "Node",
1557
+ value: AffectedResourceList.code(resource.nodeName),
1558
+ });
1559
+ }
1560
+ return {
1561
+ kind: kind,
1562
+ name: name,
1563
+ value: input.value,
1564
+ details: details,
1565
+ };
1566
+ }
1493
1567
  static async buildKubernetesRootCauseContext(input) {
1494
1568
  var _a;
1495
1569
  const metricResponse = input.dataToProcess;
@@ -1508,7 +1582,6 @@ ${contextBlock}
1508
1582
  sections.push(`**Kubernetes Cluster Details**\n${clusterDetails.join("\n")}`);
1509
1583
  // Affected resources
1510
1584
  if (breakdown.affectedResources && breakdown.affectedResources.length > 0) {
1511
- const resourceLines = [];
1512
1585
  // Sort by metric value descending (worst first) and filter out zero-value resources
1513
1586
  const sortedResources = [
1514
1587
  ...breakdown.affectedResources,
@@ -1523,79 +1596,23 @@ ${contextBlock}
1523
1596
  return sections.join("\n");
1524
1597
  }
1525
1598
  // Show top 10 affected resources
1526
- const resourcesToShow = sortedResources.slice(0, 10);
1527
- // Determine which columns are present across all resources
1528
- const hasNamespace = resourcesToShow.some((r) => {
1529
- return r.namespace;
1530
- });
1531
- const hasWorkload = resourcesToShow.some((r) => {
1532
- return r.workloadType && r.workloadName;
1533
- });
1534
- const hasPod = resourcesToShow.some((r) => {
1535
- return r.podName;
1536
- });
1537
- const hasContainer = resourcesToShow.some((r) => {
1538
- return r.containerName;
1539
- });
1540
- const hasNode = resourcesToShow.some((r) => {
1541
- return r.nodeName;
1542
- });
1543
- // Build table header
1544
- const headerCells = [];
1545
- if (hasNamespace) {
1546
- headerCells.push("Namespace");
1547
- }
1548
- if (hasWorkload) {
1549
- headerCells.push("Workload Type");
1550
- headerCells.push("Workload");
1551
- }
1552
- if (hasPod) {
1553
- headerCells.push("Pod");
1554
- }
1555
- if (hasContainer) {
1556
- headerCells.push("Container");
1557
- }
1558
- if (hasNode) {
1559
- headerCells.push("Node");
1560
- }
1561
- headerCells.push("Value");
1562
- const headerRow = `| ${headerCells.join(" | ")} |`;
1563
- const separatorRow = `| ${headerCells
1564
- .map(() => {
1565
- return "---";
1566
- })
1567
- .join(" | ")} |`;
1568
- resourceLines.push(headerRow);
1569
- resourceLines.push(separatorRow);
1570
- for (const resource of resourcesToShow) {
1571
- const cells = [];
1572
- if (hasNamespace) {
1573
- cells.push(resource.namespace ? `\`${resource.namespace}\`` : "-");
1574
- }
1575
- if (hasWorkload) {
1576
- cells.push(resource.workloadType ? `${resource.workloadType}` : "-");
1577
- cells.push(resource.workloadName ? `\`${resource.workloadName}\`` : "-");
1578
- }
1579
- if (hasPod) {
1580
- cells.push(resource.podName ? `\`${resource.podName}\`` : "-");
1581
- }
1582
- if (hasContainer) {
1583
- cells.push(resource.containerName ? `\`${resource.containerName}\`` : "-");
1584
- }
1585
- if (hasNode) {
1586
- cells.push(resource.nodeName ? `\`${resource.nodeName}\`` : "-");
1587
- }
1588
- cells.push(MonitorCriteriaEvaluator.formatPlatformResourceValue({
1589
- platform: "kubernetes",
1590
- metricName: breakdown.metricName,
1591
- value: resource.metricValue,
1592
- }));
1593
- resourceLines.push(`| ${cells.join(" | ")} |`);
1594
- }
1595
- if (sortedResources.length > 10) {
1596
- resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected resources*`);
1597
- }
1598
- sections.push(`\n\n**Affected Resources** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
1599
+ const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
1600
+ sections.push(AffectedResourceList.render({
1601
+ heading: "Affected Resources",
1602
+ overflowNoun: "affected resources",
1603
+ totalCount: sortedResources.length,
1604
+ entries: resourcesToShow.map((resource) => {
1605
+ return MonitorCriteriaEvaluator.getKubernetesAffectedResourceEntry({
1606
+ resource: resource,
1607
+ clusterName: breakdown.clusterName,
1608
+ value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
1609
+ platform: "kubernetes",
1610
+ metricName: breakdown.metricName,
1611
+ value: resource.metricValue,
1612
+ }),
1613
+ });
1614
+ }),
1615
+ }));
1599
1616
  // Add root cause analysis based on metric type
1600
1617
  const analysis = MonitorCriteriaEvaluator.buildKubernetesRootCauseAnalysis({
1601
1618
  breakdown: breakdown,
@@ -1771,6 +1788,75 @@ ${contextBlock}
1771
1788
  }
1772
1789
  return sections.length > 0 ? sections.join("\n") : null;
1773
1790
  }
1791
+ /*
1792
+ * What a Proxmox resource is, from the `pve.type` / `pve.scope` the
1793
+ * agent's transform/pve-identity processor derives from its `id`
1794
+ * (node/…, qemu/…, lxc/…, storage/…, cluster/…). A value that processor
1795
+ * does not produce is shown as it arrived rather than guessed at.
1796
+ */
1797
+ static getProxmoxAffectedResourceKind(resource) {
1798
+ switch (resource.resourceType) {
1799
+ case "qemu":
1800
+ return "Virtual Machine";
1801
+ case "lxc":
1802
+ return "Container";
1803
+ case "node":
1804
+ return "Node";
1805
+ case "storage":
1806
+ return "Storage";
1807
+ default:
1808
+ break;
1809
+ }
1810
+ switch (resource.scope) {
1811
+ case "guest":
1812
+ return "Guest";
1813
+ case "node":
1814
+ return "Node";
1815
+ case "storage":
1816
+ return "Storage";
1817
+ case "cluster":
1818
+ return "Cluster";
1819
+ default:
1820
+ break;
1821
+ }
1822
+ return resource.resourceType || resource.scope || "Resource";
1823
+ }
1824
+ /*
1825
+ * One Proxmox resource as an "Affected Resources" list item: its kind,
1826
+ * its name next to its `qemu/100`-style id, and the node it runs on.
1827
+ */
1828
+ static getProxmoxAffectedResourceEntry(input) {
1829
+ const resource = input.resource;
1830
+ let kind = MonitorCriteriaEvaluator.getProxmoxAffectedResourceKind(resource);
1831
+ let name = AffectedResourceList.codeWithId({
1832
+ name: resource.resourceName,
1833
+ id: resource.resourceId,
1834
+ });
1835
+ if (!name && resource.nodeName) {
1836
+ // A per-node series that carries no id of its own.
1837
+ kind = "Node";
1838
+ name = AffectedResourceList.code(resource.nodeName);
1839
+ }
1840
+ if (!name) {
1841
+ // Nothing narrower than the cluster — a cluster-wide series.
1842
+ kind = "Cluster";
1843
+ name = AffectedResourceList.code(input.clusterName);
1844
+ }
1845
+ const details = [];
1846
+ // A node's own row would only repeat its name as "Node: …".
1847
+ if (resource.nodeName && kind !== "Node") {
1848
+ details.push({
1849
+ label: "Node",
1850
+ value: AffectedResourceList.code(resource.nodeName),
1851
+ });
1852
+ }
1853
+ return {
1854
+ kind: kind,
1855
+ name: name,
1856
+ value: input.value,
1857
+ details: details,
1858
+ };
1859
+ }
1774
1860
  static buildProxmoxRootCauseContext(input) {
1775
1861
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1776
1862
  const metricResponse = input.dataToProcess;
@@ -1807,15 +1893,15 @@ ${contextBlock}
1807
1893
  }
1808
1894
  sections.push(`**Proxmox Cluster Details**\n${clusterDetails.join("\n")}`);
1809
1895
  }
1810
- // Affected resources (Resource / Type / Node / Value)
1811
- let renderedBreakdownTable = false;
1896
+ // Affected resources: a ranked list of kind, name and node
1897
+ let renderedBreakdownList = false;
1812
1898
  if (breakdown && breakdown.affectedResources.length > 0) {
1813
1899
  /*
1814
1900
  * K8s-parity render: drop zero-value rows, worst (highest)
1815
1901
  * first, top 10. Note that for availability metrics (pve_up)
1816
1902
  * zero-valued rows ARE the down resources — those still drive
1817
1903
  * alerting through the per-series criteria evaluation; this
1818
- * table is supplementary context only.
1904
+ * list is supplementary context only.
1819
1905
  */
1820
1906
  const sortedResources = [
1821
1907
  ...breakdown.affectedResources,
@@ -1827,47 +1913,35 @@ ${contextBlock}
1827
1913
  return b.metricValue - a.metricValue;
1828
1914
  });
1829
1915
  /*
1830
- * Skip the table when no row carries any identity label
1916
+ * Skip the list when no row carries any identity label
1831
1917
  * (cluster-wide series) — it would add nothing.
1832
1918
  */
1833
1919
  const hasIdentity = sortedResources.some((r) => {
1834
1920
  return r.resourceId || r.resourceName || r.nodeName;
1835
1921
  });
1836
1922
  if (sortedResources.length > 0 && hasIdentity) {
1837
- const resourcesToShow = sortedResources.slice(0, 10);
1838
- const resourceLines = [];
1839
- resourceLines.push(`| Resource | Type | Node | Value |`);
1840
- resourceLines.push(`| --- | --- | --- | --- |`);
1841
- for (const resource of resourcesToShow) {
1842
- let resourceCell = "-";
1843
- if (resource.resourceName && resource.resourceId) {
1844
- resourceCell = `\`${resource.resourceName}\` (\`${resource.resourceId}\`)`;
1845
- }
1846
- else if (resource.resourceId) {
1847
- resourceCell = `\`${resource.resourceId}\``;
1848
- }
1849
- else if (resource.resourceName) {
1850
- resourceCell = `\`${resource.resourceName}\``;
1851
- }
1852
- const typeCell = resource.resourceType || resource.scope || "-";
1853
- const nodeCell = resource.nodeName
1854
- ? `\`${resource.nodeName}\``
1855
- : "-";
1856
- resourceLines.push(`| ${resourceCell} | ${typeCell} | ${nodeCell} | ${MonitorCriteriaEvaluator.formatPlatformResourceValue({
1857
- platform: "proxmox",
1858
- metricName: breakdown.metricName,
1859
- value: resource.metricValue,
1860
- })} |`);
1861
- }
1862
- if (sortedResources.length > 10) {
1863
- resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected resources*`);
1864
- }
1865
- sections.push(`\n\n**Affected Resources** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
1866
- renderedBreakdownTable = true;
1923
+ const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
1924
+ sections.push(AffectedResourceList.render({
1925
+ heading: "Affected Resources",
1926
+ overflowNoun: "affected resources",
1927
+ totalCount: sortedResources.length,
1928
+ entries: resourcesToShow.map((resource) => {
1929
+ return MonitorCriteriaEvaluator.getProxmoxAffectedResourceEntry({
1930
+ resource: resource,
1931
+ clusterName: breakdown.clusterName,
1932
+ value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
1933
+ platform: "proxmox",
1934
+ metricName: breakdown.metricName,
1935
+ value: resource.metricValue,
1936
+ }),
1937
+ });
1938
+ }),
1939
+ }));
1940
+ renderedBreakdownList = true;
1867
1941
  }
1868
1942
  }
1869
- // Metric results summary (fallback context when no table rendered)
1870
- if (!renderedBreakdownTable &&
1943
+ // Metric results summary (fallback context when no list rendered)
1944
+ if (!renderedBreakdownList &&
1871
1945
  metricResponse.metricResult &&
1872
1946
  metricResponse.metricResult.length > 0) {
1873
1947
  const resultDetails = [];
@@ -1886,8 +1960,8 @@ ${contextBlock}
1886
1960
  * Which vSphere object one VMware breakdown row describes, resolved
1887
1961
  * from the identity attributes the vcenter receiver stamped on it —
1888
1962
  * most specific first, in the same order the ingest inventory scan
1889
- * uses, so the Kind column here and the inventory's `kind` never
1890
- * disagree about the same series. A VM row carries its parent host
1963
+ * uses, so the kind an Affected Resources item is titled with and the
1964
+ * inventory's `kind` never disagree about the same series. A VM row carries its parent host
1891
1965
  * (and often a resource pool), so the VM check must run before the
1892
1966
  * host and pool checks.
1893
1967
  */
@@ -1912,6 +1986,84 @@ ${contextBlock}
1912
1986
  }
1913
1987
  return undefined;
1914
1988
  }
1989
+ /*
1990
+ * One vSphere object as an "Affected Resources" list item, titled by
1991
+ * the object itself, with the ESXi host and cluster it sits under
1992
+ * listed beneath it.
1993
+ */
1994
+ static getVMwareAffectedResourceEntry(input) {
1995
+ const resource = input.resource;
1996
+ const kind = MonitorCriteriaEvaluator.getVMwareAffectedResourceKind(resource);
1997
+ let name = "";
1998
+ let showHost = false;
1999
+ switch (kind) {
2000
+ case "Virtual Machine":
2001
+ /*
2002
+ * The VM's display name, falling back to its instance UUID
2003
+ * (`vcenter.vm.id`) when the receiver stamped no name. The Host
2004
+ * detail is the ESXi host the VM is running on.
2005
+ */
2006
+ name = AffectedResourceList.code(resource.vmName || resource.vmId);
2007
+ showHost = true;
2008
+ break;
2009
+ case "Resource Pool":
2010
+ /*
2011
+ * Pool names are only unique within a parent, so the inventory
2012
+ * path is shown next to the name. The Host detail is the owner
2013
+ * host for a pool on a standalone ESXi host (pools under a
2014
+ * cluster show the cluster).
2015
+ */
2016
+ name = AffectedResourceList.codeWithId({
2017
+ name: resource.resourcePoolName,
2018
+ id: resource.resourcePoolPath,
2019
+ });
2020
+ showHost = true;
2021
+ break;
2022
+ case "Host":
2023
+ // The host IS the resource; a "Host:" detail would repeat it.
2024
+ name = AffectedResourceList.code(resource.hostName);
2025
+ break;
2026
+ case "Datastore":
2027
+ name = AffectedResourceList.code(resource.datastoreName);
2028
+ break;
2029
+ case "Cluster":
2030
+ name = AffectedResourceList.code(resource.clusterName);
2031
+ break;
2032
+ case "Datacenter":
2033
+ name = AffectedResourceList.code(resource.datacenterName);
2034
+ break;
2035
+ default:
2036
+ break;
2037
+ }
2038
+ const details = [];
2039
+ if (showHost && resource.hostName) {
2040
+ details.push({
2041
+ label: "Host",
2042
+ value: AffectedResourceList.code(resource.hostName),
2043
+ });
2044
+ }
2045
+ if (kind !== "Cluster" && resource.clusterName) {
2046
+ details.push({
2047
+ label: "Cluster",
2048
+ value: AffectedResourceList.code(resource.clusterName),
2049
+ });
2050
+ }
2051
+ if (!kind || !name) {
2052
+ // No vSphere identity on the series — it describes the vCenter as a whole.
2053
+ return {
2054
+ kind: "vCenter",
2055
+ name: AffectedResourceList.code(input.vcenterName),
2056
+ value: input.value,
2057
+ details: details,
2058
+ };
2059
+ }
2060
+ return {
2061
+ kind: kind,
2062
+ name: name,
2063
+ value: input.value,
2064
+ details: details,
2065
+ };
2066
+ }
1915
2067
  static buildVMwareRootCauseContext(input) {
1916
2068
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
1917
2069
  const metricResponse = input.dataToProcess;
@@ -1959,17 +2111,17 @@ ${contextBlock}
1959
2111
  }
1960
2112
  sections.push(`**vCenter Details**\n${vcenterDetails.join("\n")}`);
1961
2113
  }
1962
- // Affected resources (Resource / Kind / Host / Cluster / Value)
1963
- let renderedBreakdownTable = false;
2114
+ // Affected resources: a ranked list of object, host and cluster
2115
+ let renderedBreakdownList = false;
1964
2116
  if (breakdown && breakdown.affectedResources.length > 0) {
1965
2117
  /*
1966
2118
  * K8s/Proxmox-parity render: drop zero-value rows, worst (highest)
1967
2119
  * first, top 10. For count metrics filtered to an unhealthy state
1968
2120
  * (`vcenter.datacenter.host.count{status=red}`) the non-zero rows
1969
- * ARE the objects with a problem, so the table reads correctly;
2121
+ * ARE the objects with a problem, so the list reads correctly;
1970
2122
  * a zero row means "nothing in that state" and adds nothing. The
1971
2123
  * per-series criteria evaluation is what actually alerts; this
1972
- * table is supplementary context only.
2124
+ * list is supplementary context only.
1973
2125
  */
1974
2126
  const sortedResources = [
1975
2127
  ...breakdown.affectedResources,
@@ -1981,7 +2133,7 @@ ${contextBlock}
1981
2133
  return b.metricValue - a.metricValue;
1982
2134
  });
1983
2135
  /*
1984
- * Skip the table when no row carries any identity attribute — it
2136
+ * Skip the list when no row carries any identity attribute — it
1985
2137
  * would add nothing. Every vcenter-receiver series carries at
1986
2138
  * least `vcenter.datacenter.name`, so this only trips on a
1987
2139
  * breakdown the worker could not attribute at all.
@@ -1990,77 +2142,28 @@ ${contextBlock}
1990
2142
  return Boolean(MonitorCriteriaEvaluator.getVMwareAffectedResourceKind(r));
1991
2143
  });
1992
2144
  if (sortedResources.length > 0 && hasIdentity) {
1993
- const resourcesToShow = sortedResources.slice(0, 10);
1994
- const resourceLines = [];
1995
- resourceLines.push(`| Resource | Kind | Host | Cluster | Value |`);
1996
- resourceLines.push(`| --- | --- | --- | --- | --- |`);
1997
- for (const resource of resourcesToShow) {
1998
- const kind = MonitorCriteriaEvaluator.getVMwareAffectedResourceKind(resource);
1999
- let resourceCell = "-";
2000
- let hostCell = "-";
2001
- switch (kind) {
2002
- case "Virtual Machine":
2003
- /*
2004
- * The VM's display name, falling back to its instance
2005
- * UUID (`vcenter.vm.id`) when the receiver stamped no
2006
- * name. The Host column carries the ESXi host the VM is
2007
- * running on.
2008
- */
2009
- resourceCell = resource.vmName
2010
- ? `\`${resource.vmName}\``
2011
- : `\`${resource.vmId}\``;
2012
- hostCell = resource.hostName ? `\`${resource.hostName}\`` : "-";
2013
- break;
2014
- case "Resource Pool":
2015
- /*
2016
- * Pool names are only unique within a parent, so the
2017
- * inventory path is shown next to the name. The Host
2018
- * column is the owner host for a pool on a standalone
2019
- * ESXi host (pools under a cluster show the cluster).
2020
- */
2021
- if (resource.resourcePoolName && resource.resourcePoolPath) {
2022
- resourceCell = `\`${resource.resourcePoolName}\` (\`${resource.resourcePoolPath}\`)`;
2023
- }
2024
- else {
2025
- resourceCell = `\`${resource.resourcePoolPath || resource.resourcePoolName}\``;
2026
- }
2027
- hostCell = resource.hostName ? `\`${resource.hostName}\`` : "-";
2028
- break;
2029
- case "Host":
2030
- // The host IS the resource; repeating it in the Host column adds nothing.
2031
- resourceCell = `\`${resource.hostName}\``;
2032
- break;
2033
- case "Datastore":
2034
- resourceCell = `\`${resource.datastoreName}\``;
2035
- break;
2036
- case "Cluster":
2037
- resourceCell = `\`${resource.clusterName}\``;
2038
- break;
2039
- case "Datacenter":
2040
- resourceCell = `\`${resource.datacenterName}\``;
2041
- break;
2042
- default:
2043
- break;
2044
- }
2045
- const kindCell = kind || "-";
2046
- const clusterCell = kind !== "Cluster" && resource.clusterName
2047
- ? `\`${resource.clusterName}\``
2048
- : "-";
2049
- resourceLines.push(`| ${resourceCell} | ${kindCell} | ${hostCell} | ${clusterCell} | ${MonitorCriteriaEvaluator.formatPlatformResourceValue({
2050
- platform: "vmware",
2051
- metricName: breakdown.metricName,
2052
- value: resource.metricValue,
2053
- })} |`);
2054
- }
2055
- if (sortedResources.length > 10) {
2056
- resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected resources*`);
2057
- }
2058
- sections.push(`\n\n**Affected Resources** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
2059
- renderedBreakdownTable = true;
2145
+ const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
2146
+ sections.push(AffectedResourceList.render({
2147
+ heading: "Affected Resources",
2148
+ overflowNoun: "affected resources",
2149
+ totalCount: sortedResources.length,
2150
+ entries: resourcesToShow.map((resource) => {
2151
+ return MonitorCriteriaEvaluator.getVMwareAffectedResourceEntry({
2152
+ resource: resource,
2153
+ vcenterName: breakdown.vcenterName,
2154
+ value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
2155
+ platform: "vmware",
2156
+ metricName: breakdown.metricName,
2157
+ value: resource.metricValue,
2158
+ }),
2159
+ });
2160
+ }),
2161
+ }));
2162
+ renderedBreakdownList = true;
2060
2163
  }
2061
2164
  }
2062
- // Metric results summary (fallback context when no table rendered)
2063
- if (!renderedBreakdownTable &&
2165
+ // Metric results summary (fallback context when no list rendered)
2166
+ if (!renderedBreakdownList &&
2064
2167
  metricResponse.metricResult &&
2065
2168
  metricResponse.metricResult.length > 0) {
2066
2169
  const resultDetails = [];
@@ -2075,6 +2178,46 @@ ${contextBlock}
2075
2178
  }
2076
2179
  return sections.length > 0 ? sections.join("\n") : null;
2077
2180
  }
2181
+ /*
2182
+ * One Docker Swarm task as an "Affected Tasks" list item, with the
2183
+ * service it belongs to and the node it is scheduled on beneath it.
2184
+ */
2185
+ static getDockerSwarmAffectedResourceEntry(input) {
2186
+ const resource = input.resource;
2187
+ let kind = "Cluster";
2188
+ let name = AffectedResourceList.code(input.clusterName);
2189
+ if (resource.containerName) {
2190
+ kind = "Task";
2191
+ name = AffectedResourceList.code(resource.containerName);
2192
+ }
2193
+ else if (resource.serviceName) {
2194
+ kind = "Service";
2195
+ name = AffectedResourceList.code(resource.serviceName);
2196
+ }
2197
+ else if (resource.nodeName) {
2198
+ kind = "Node";
2199
+ name = AffectedResourceList.code(resource.nodeName);
2200
+ }
2201
+ const details = [];
2202
+ if (resource.serviceName && kind !== "Service") {
2203
+ details.push({
2204
+ label: "Service",
2205
+ value: AffectedResourceList.code(resource.serviceName),
2206
+ });
2207
+ }
2208
+ if (resource.nodeName && kind !== "Node") {
2209
+ details.push({
2210
+ label: "Node",
2211
+ value: AffectedResourceList.code(resource.nodeName),
2212
+ });
2213
+ }
2214
+ return {
2215
+ kind: kind,
2216
+ name: name,
2217
+ value: input.value,
2218
+ details: details,
2219
+ };
2220
+ }
2078
2221
  static buildDockerSwarmRootCauseContext(input) {
2079
2222
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
2080
2223
  const metricResponse = input.dataToProcess;
@@ -2111,14 +2254,14 @@ ${contextBlock}
2111
2254
  }
2112
2255
  sections.push(`**Docker Swarm Cluster Details**\n${clusterDetails.join("\n")}`);
2113
2256
  }
2114
- // Affected resources (Task / Service / Node / Value)
2115
- let renderedBreakdownTable = false;
2257
+ // Affected tasks: a ranked list of task, service and node
2258
+ let renderedBreakdownList = false;
2116
2259
  if (breakdown && breakdown.affectedResources.length > 0) {
2117
2260
  /*
2118
2261
  * K8s/Proxmox-parity render: drop zero-value rows, worst (highest)
2119
2262
  * first, top 10. For the task-down template the breaching rows ARE
2120
2263
  * the zero-uptime ones — those still drive alerting through the
2121
- * per-series criteria evaluation; this table is supplementary
2264
+ * per-series criteria evaluation; this list is supplementary
2122
2265
  * context only.
2123
2266
  */
2124
2267
  const sortedResources = [
@@ -2131,42 +2274,35 @@ ${contextBlock}
2131
2274
  return b.metricValue - a.metricValue;
2132
2275
  });
2133
2276
  /*
2134
- * Skip the table when no row carries any identity label
2277
+ * Skip the list when no row carries any identity label
2135
2278
  * (cluster-wide series) — it would add nothing.
2136
2279
  */
2137
2280
  const hasIdentity = sortedResources.some((r) => {
2138
2281
  return r.containerName || r.serviceName || r.nodeName;
2139
2282
  });
2140
2283
  if (sortedResources.length > 0 && hasIdentity) {
2141
- const resourcesToShow = sortedResources.slice(0, 10);
2142
- const resourceLines = [];
2143
- resourceLines.push(`| Task / Container | Service | Node | Value |`);
2144
- resourceLines.push(`| --- | --- | --- | --- |`);
2145
- for (const resource of resourcesToShow) {
2146
- const taskCell = resource.containerName
2147
- ? `\`${resource.containerName}\``
2148
- : "-";
2149
- const serviceCell = resource.serviceName
2150
- ? `\`${resource.serviceName}\``
2151
- : "-";
2152
- const nodeCell = resource.nodeName
2153
- ? `\`${resource.nodeName}\``
2154
- : "-";
2155
- resourceLines.push(`| ${taskCell} | ${serviceCell} | ${nodeCell} | ${MonitorCriteriaEvaluator.formatPlatformResourceValue({
2156
- platform: "dockerSwarm",
2157
- metricName: breakdown.metricName,
2158
- value: resource.metricValue,
2159
- })} |`);
2160
- }
2161
- if (sortedResources.length > 10) {
2162
- resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected tasks*`);
2163
- }
2164
- sections.push(`\n\n**Affected Tasks** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
2165
- renderedBreakdownTable = true;
2284
+ const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
2285
+ sections.push(AffectedResourceList.render({
2286
+ heading: "Affected Tasks",
2287
+ overflowNoun: "affected tasks",
2288
+ totalCount: sortedResources.length,
2289
+ entries: resourcesToShow.map((resource) => {
2290
+ return MonitorCriteriaEvaluator.getDockerSwarmAffectedResourceEntry({
2291
+ resource: resource,
2292
+ clusterName: breakdown.clusterName,
2293
+ value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
2294
+ platform: "dockerSwarm",
2295
+ metricName: breakdown.metricName,
2296
+ value: resource.metricValue,
2297
+ }),
2298
+ });
2299
+ }),
2300
+ }));
2301
+ renderedBreakdownList = true;
2166
2302
  }
2167
2303
  }
2168
- // Metric results summary (fallback context when no table rendered)
2169
- if (!renderedBreakdownTable &&
2304
+ // Metric results summary (fallback context when no list rendered)
2305
+ if (!renderedBreakdownList &&
2170
2306
  metricResponse.metricResult &&
2171
2307
  metricResponse.metricResult.length > 0) {
2172
2308
  const resultDetails = [];
@@ -2182,21 +2318,21 @@ ${contextBlock}
2182
2318
  return sections.length > 0 ? sections.join("\n") : null;
2183
2319
  }
2184
2320
  /*
2185
- * Which rows of the Ceph affected-resources table actually BREACHED.
2321
+ * Which rows of the Ceph affected-resources list actually BREACHED.
2186
2322
  *
2187
2323
  * The blanket `metricValue > 0` below is right for every count- or
2188
2324
  * level-style criteria ("> 0 active health checks", "> 85 % used"), but
2189
2325
  * it is exactly backwards for the availability signals: the OSD Down /
2190
2326
  * OSD Out / Quorum Degraded criteria fire on `< 1` over ceph_osd_up /
2191
2327
  * ceph_osd_in / ceph_mon_quorum_status, so the ZERO rows are the down
2192
- * daemons. Dropping them rendered a table of the HEALTHY daemons under
2328
+ * daemons. Dropping them rendered a list of the HEALTHY daemons under
2193
2329
  * an incident that tells the reader to "check the root cause for the
2194
2330
  * affected ceph_daemon label".
2195
2331
  *
2196
2332
  * Invert only for a criteria that fires when the metric FALLS.
2197
2333
  * Everything else — every `> 0` health check, PG count, latency and
2198
2334
  * capacity ratio, and every `= 0` recovery criteria — keeps the
2199
- * existing `> 0`, worst-highest-first table byte for byte.
2335
+ * existing `> 0`, worst-highest-first list byte for byte.
2200
2336
  */
2201
2337
  static getCephBreachPredicate(criteriaInstance) {
2202
2338
  var _a;
@@ -2232,6 +2368,71 @@ ${contextBlock}
2232
2368
  worstIsLowest: true,
2233
2369
  };
2234
2370
  }
2371
+ /*
2372
+ * One Ceph daemon or pool as an "Affected Resources" list item, with
2373
+ * the pool and host it belongs to beneath it.
2374
+ */
2375
+ static getCephAffectedResourceEntry(input) {
2376
+ const resource = input.resource;
2377
+ /*
2378
+ * The worker stores every series' `name` label as poolName, but only
2379
+ * ceph_pool_metadata's `name` is a pool — and that series always
2380
+ * carries pool_id too. On ceph_health_detail, `name` is the health
2381
+ * check (RECENT_CRASH, OSD_NEARFULL, ...), and titling it "Pool" told
2382
+ * the reader a pool called OSD_NEARFULL was the problem. So a name is
2383
+ * treated as a pool only when it arrives with a pool id.
2384
+ */
2385
+ const pool = resource.poolId
2386
+ ? AffectedResourceList.codeWithId({
2387
+ name: resource.poolName,
2388
+ id: resource.poolId,
2389
+ })
2390
+ : "";
2391
+ const otherName = resource.poolId
2392
+ ? ""
2393
+ : AffectedResourceList.code(resource.poolName);
2394
+ const otherNameKind = input.metricName === "ceph_health_detail" ? "Health Check" : "Resource";
2395
+ let kind = "Cluster";
2396
+ let name = AffectedResourceList.code(input.clusterName);
2397
+ if (resource.daemon) {
2398
+ kind = "Daemon";
2399
+ name = AffectedResourceList.code(resource.daemon);
2400
+ }
2401
+ else if (pool) {
2402
+ kind = "Pool";
2403
+ name = pool;
2404
+ }
2405
+ else if (otherName) {
2406
+ kind = otherNameKind;
2407
+ name = otherName;
2408
+ }
2409
+ else if (resource.hostname) {
2410
+ kind = "Host";
2411
+ name = AffectedResourceList.code(resource.hostname);
2412
+ }
2413
+ const details = [];
2414
+ if (pool && name !== pool) {
2415
+ details.push({ label: "Pool", value: pool });
2416
+ }
2417
+ if (otherName && name !== otherName) {
2418
+ details.push({
2419
+ label: otherNameKind === "Resource" ? "Name" : otherNameKind,
2420
+ value: otherName,
2421
+ });
2422
+ }
2423
+ if (resource.hostname && kind !== "Host") {
2424
+ details.push({
2425
+ label: "Host",
2426
+ value: AffectedResourceList.code(resource.hostname),
2427
+ });
2428
+ }
2429
+ return {
2430
+ kind: kind,
2431
+ name: name,
2432
+ value: input.value,
2433
+ details: details,
2434
+ };
2435
+ }
2235
2436
  static buildCephRootCauseContext(input) {
2236
2437
  var _a, _b, _c, _d, _e, _f, _g;
2237
2438
  const metricResponse = input.dataToProcess;
@@ -2262,8 +2463,8 @@ ${contextBlock}
2262
2463
  }
2263
2464
  sections.push(`**Ceph Cluster Details**\n${clusterDetails.join("\n")}`);
2264
2465
  }
2265
- // Affected resources (Daemon / Pool / Host / Value)
2266
- let renderedBreakdownTable = false;
2466
+ // Affected resources: a ranked list of daemon, pool and host
2467
+ let renderedBreakdownList = false;
2267
2468
  if (breakdown && breakdown.affectedResources.length > 0) {
2268
2469
  /*
2269
2470
  * Keep the rows that satisfy the criteria that just matched, worst
@@ -2284,7 +2485,7 @@ ${contextBlock}
2284
2485
  : b.metricValue - a.metricValue;
2285
2486
  });
2286
2487
  /*
2287
- * Skip the table when no row carries any identity label
2488
+ * Skip the list when no row carries any identity label
2288
2489
  * (cluster-wide series like ceph_health_status) — it would add
2289
2490
  * nothing.
2290
2491
  */
@@ -2292,42 +2493,29 @@ ${contextBlock}
2292
2493
  return r.daemon || r.poolId || r.poolName || r.hostname;
2293
2494
  });
2294
2495
  if (sortedResources.length > 0 && hasIdentity) {
2295
- const resourcesToShow = sortedResources.slice(0, 10);
2296
- const resourceLines = [];
2297
- resourceLines.push(`| Daemon | Pool | Host | Value |`);
2298
- resourceLines.push(`| --- | --- | --- | --- |`);
2299
- for (const resource of resourcesToShow) {
2300
- const daemonCell = resource.daemon
2301
- ? `\`${resource.daemon}\``
2302
- : "-";
2303
- let poolCell = "-";
2304
- if (resource.poolName && resource.poolId) {
2305
- poolCell = `\`${resource.poolName}\` (\`${resource.poolId}\`)`;
2306
- }
2307
- else if (resource.poolName) {
2308
- poolCell = `\`${resource.poolName}\``;
2309
- }
2310
- else if (resource.poolId) {
2311
- poolCell = `\`${resource.poolId}\``;
2312
- }
2313
- const hostCell = resource.hostname
2314
- ? `\`${resource.hostname}\``
2315
- : "-";
2316
- resourceLines.push(`| ${daemonCell} | ${poolCell} | ${hostCell} | ${MonitorCriteriaEvaluator.formatPlatformResourceValue({
2317
- platform: "ceph",
2318
- metricName: breakdown.metricName,
2319
- value: resource.metricValue,
2320
- })} |`);
2321
- }
2322
- if (sortedResources.length > 10) {
2323
- resourceLines.push(`\n*... and ${sortedResources.length - 10} more affected resources*`);
2324
- }
2325
- sections.push(`\n\n**Affected Resources** (${sortedResources.length} total)\n\n${resourceLines.join("\n")}`);
2326
- renderedBreakdownTable = true;
2496
+ const resourcesToShow = sortedResources.slice(0, AffectedResourceList.MAX_ENTRIES);
2497
+ sections.push(AffectedResourceList.render({
2498
+ heading: "Affected Resources",
2499
+ overflowNoun: "affected resources",
2500
+ totalCount: sortedResources.length,
2501
+ entries: resourcesToShow.map((resource) => {
2502
+ return MonitorCriteriaEvaluator.getCephAffectedResourceEntry({
2503
+ resource: resource,
2504
+ clusterName: breakdown.clusterName,
2505
+ metricName: breakdown.metricName,
2506
+ value: MonitorCriteriaEvaluator.formatPlatformResourceValue({
2507
+ platform: "ceph",
2508
+ metricName: breakdown.metricName,
2509
+ value: resource.metricValue,
2510
+ }),
2511
+ });
2512
+ }),
2513
+ }));
2514
+ renderedBreakdownList = true;
2327
2515
  }
2328
2516
  }
2329
- // Metric results summary (fallback context when no table rendered)
2330
- if (!renderedBreakdownTable &&
2517
+ // Metric results summary (fallback context when no list rendered)
2518
+ if (!renderedBreakdownList &&
2331
2519
  metricResponse.metricResult &&
2332
2520
  metricResponse.metricResult.length > 0) {
2333
2521
  const resultDetails = [];