@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
@@ -0,0 +1,119 @@
1
+ import { JSONObject } from "../../../Types/JSON";
2
+ import { ComponentType, ReactElement, ReactNode } from "react";
3
+
4
+ /*
5
+ * The contract between the edition dialog (EditionLabel) and the Enterprise
6
+ * license management UI.
7
+ *
8
+ * EditionLabel only READS the license: it asks GET /global-config/license and
9
+ * shows what the answer says - the edition pill, the status, grace and trial
10
+ * notices, the version card. Everything that CHANGES the license lives in the
11
+ * Enterprise Edition (ee/AdminDashboard/License, under ee/LICENSE), because
12
+ * the routes it calls exist only in the Enterprise server: activating it with
13
+ * a key or an offline token, refreshing it from oneuptime.com, replacing it,
14
+ * and the seat usage and instance tables a master admin manages it by.
15
+ *
16
+ * The Dashboard and the Admin Dashboard hand that UI to EditionLabel as their
17
+ * LicenseManager plugin (src/Enterprise/EnterprisePlugins.ts). The Community
18
+ * stubs have none, and the login page passes none: there the dialog shows
19
+ * the license status only, and nobody is told to manage the license from it.
20
+ *
21
+ * The manager wraps the dialog rather than filling one spot inside it. Its
22
+ * sections land in three places in the dialog body (messages at the top, seat
23
+ * usage after the version card, the activation input near the bottom) and it
24
+ * drives the dialog's footer buttons, so EditionLabel gives it renderDialog
25
+ * and the manager calls it with those parts. It stays mounted while the
26
+ * dialog is closed, so what an admin typed and which input they chose
27
+ * survive closing and reopening the dialog.
28
+ *
29
+ * Types only: nothing here ships any license management code.
30
+ */
31
+
32
+ /*
33
+ * How close the installation is to its licensed seats. "breached" also
34
+ * covers an enforced limit that is exactly full: new users are already
35
+ * refused there.
36
+ */
37
+ export type SeatTone = "healthy" | "approaching" | "breached";
38
+
39
+ // What EditionLabel knows about the license, read-only.
40
+ export interface LicenseSnapshot {
41
+ /*
42
+ * The body of the last GET /global-config/license that succeeded, or null
43
+ * before the first one and after one that failed. A new object on every
44
+ * successful load, so a manager can tell that the license was (re)loaded.
45
+ */
46
+ payload: JSONObject | null;
47
+ isLoading: boolean;
48
+ // Why the last load failed; "" when it did not.
49
+ loadError: string;
50
+ /*
51
+ * A master admin, in a dialog that has a license manager. The server
52
+ * accepts license writes from a master admin only, so nobody else is
53
+ * offered them.
54
+ */
55
+ canManageLicense: boolean;
56
+ licenseValid: boolean;
57
+ // Enterprise Edition with no license yet, inside its first-run trial.
58
+ isUnlicensedTrial: boolean;
59
+ // A real license (not the trial) is valid and loaded: show what it covers.
60
+ showLicenseDetails: boolean;
61
+ // "" when the server did not say.
62
+ companyName: string;
63
+ // The stored license key; "" when there is none or the caller may not see it.
64
+ licenseKey: string;
65
+ userLimit: number | null;
66
+ // What oneuptime.com last counted across every instance on the license.
67
+ currentUserCount: number | null;
68
+ /*
69
+ * The seat figure everything is measured against: the live, enforced count
70
+ * when this installation enforces its seat limit, else currentUserCount.
71
+ */
72
+ effectiveUserCount: number | null;
73
+ isSeatLimitEnforced: boolean;
74
+ canAddMoreUsers: boolean;
75
+ seatTone: SeatTone;
76
+ // The percent of licensed seats in use, as the admin reads it (may exceed 100).
77
+ seatUsageDisplayPercent: number | null;
78
+ currentVersion: string;
79
+ latestVersion: string;
80
+ // currentVersion is a real release number that can be compared.
81
+ hasComparableVersion: boolean;
82
+ }
83
+
84
+ // What a manager adds to the dialog. Every part is optional.
85
+ export interface LicenseManagerDialogParts {
86
+ // Top of the body: the outcome of the last license action.
87
+ messages?: ReactNode | undefined;
88
+ // After the version card: seat usage and the instances on the license.
89
+ usage?: ReactNode | undefined;
90
+ // Near the bottom of the body: the license key or offline token input.
91
+ activation?: ReactNode | undefined;
92
+ // The dialog's primary button, when the manager offers one.
93
+ submitButtonText?: string | undefined;
94
+ onSubmit?: (() => void) | undefined;
95
+ isSubmitting?: boolean | undefined;
96
+ disableSubmitButton?: boolean | undefined;
97
+ // Left of the footer: actions on a license that is already in place.
98
+ leftFooterElement?: ReactElement | undefined;
99
+ }
100
+
101
+ export interface LicenseManagerProps {
102
+ license: LicenseSnapshot;
103
+ isDialogOpen: boolean;
104
+ // Asks GET /global-config/license again; the pill and the dialog follow.
105
+ reloadLicense: () => Promise<void>;
106
+ /*
107
+ * Applies the seat fields (isSeatLimitEnforced, seatsInUse, canAddMoreUsers)
108
+ * a license write answers with, the same way the GET's are applied.
109
+ */
110
+ applySeatEnforcement: (payload: JSONObject) => void;
111
+ // Renders the dialog (nothing while it is closed) with the manager's parts.
112
+ renderDialog: (parts: LicenseManagerDialogParts) => ReactElement;
113
+ }
114
+
115
+ /*
116
+ * Must render renderDialog's result, and must not be React.lazy: it is
117
+ * mounted with the edition pill on every page, and the dialog is inside it.
118
+ */
119
+ export type LicenseManagerComponent = ComponentType<LicenseManagerProps>;
@@ -39,10 +39,52 @@ export interface UptimeBarDaySummary {
39
39
  incidents: Array<UptimeBarTooltipIncident>;
40
40
  }
41
41
 
42
+ /*
43
+ * One day's reading, measured server-side.
44
+ *
45
+ * The bars used to be derived purely from the timeline rows the browser
46
+ * happened to receive, and those rows arrive under a cap that silently drops
47
+ * history (see MonitorStatusTimelineService.getDailyUptimeAggregate). A day
48
+ * whose rows were dropped is indistinguishable, client-side, from a day that
49
+ * was genuinely quiet - so the graph painted both as the status page's
50
+ * `defaultBarColor`, which is GREEN on the overwhelming majority of pages.
51
+ * The page then asserted uptime for periods it had no data for.
52
+ *
53
+ * A reading carries how much of the day was actually COVERED by data, which
54
+ * is the thing the browser cannot infer for itself.
55
+ */
56
+ export interface DayReading {
57
+ dayStart: Date;
58
+ daySeconds: number;
59
+ /* Zero means nothing was ever recorded for this day. */
60
+ coveredSeconds: number;
61
+ statusDurations: Array<StatusDuration>;
62
+ }
63
+
64
+ /*
65
+ * A day nobody measured is not a colour the operator gets to choose.
66
+ *
67
+ * `defaultBarColor` is branding for an ordinary good day; letting it also
68
+ * stand for "we have no idea" is exactly the conflation that made a page with
69
+ * a green default claim uptime it never recorded, and a page with a grey
70
+ * default look unmonitored while it was passing every check. This is a fixed
71
+ * neutral, drawn with a hatch so it is still distinguishable in greyscale and
72
+ * to a colour-blind reader.
73
+ */
74
+ export const NO_DATA_BAR_COLOR: Color = new Color("#9CA3AF");
75
+
42
76
  export interface ComponentProps {
43
77
  startDate: Date;
44
78
  endDate: Date;
45
79
  events: Array<Event>;
80
+ /*
81
+ * Server-measured coverage per day. When a day has a reading, it decides
82
+ * whether that day has data and what its durations are; the `events` array
83
+ * is then only used for incident overlays. When absent - the dashboard
84
+ * monitor page, which does not fetch the aggregate - the old event-derived
85
+ * behaviour applies unchanged.
86
+ */
87
+ dayReadings?: Array<DayReading> | undefined;
46
88
  height?: number | undefined;
47
89
  barColorRules?: Array<BarChartRule> | undefined;
48
90
  downtimeEventStatusIds?: Array<ObjectID> | undefined;
@@ -173,6 +215,20 @@ const DayUptimeGraph: FunctionComponent<ComponentProps> = (
173
215
  const startOfTheDay: Date = OneUptimeDate.getStartOfDay(todaysDay);
174
216
  const endOfTheDay: Date = OneUptimeDate.getEndOfDay(todaysDay);
175
217
 
218
+ /*
219
+ * The server's reading for this day, when the caller supplied one. It is
220
+ * matched by instant rather than by index: the window's first bucket is
221
+ * clipped to the window start, so bucket i is not necessarily day i.
222
+ */
223
+ const todaysReading: DayReading | undefined = (
224
+ props.dayReadings || []
225
+ ).find((reading: DayReading) => {
226
+ return (
227
+ reading.dayStart.getTime() >= startOfTheDay.getTime() &&
228
+ reading.dayStart.getTime() <= endOfTheDay.getTime()
229
+ );
230
+ });
231
+
176
232
  const todaysEvents: Array<Event> = props.events.filter((event: Event) => {
177
233
  let doesEventBelongsToToday: boolean = false;
178
234
 
@@ -274,32 +330,75 @@ const DayUptimeGraph: FunctionComponent<ComponentProps> = (
274
330
  }
275
331
  }
276
332
 
333
+ /*
334
+ * Does this day have data at all?
335
+ *
336
+ * With a server reading, coverage is the answer and it is authoritative -
337
+ * the browser cannot tell a quiet day from a day whose rows were dropped
338
+ * by the fetch cap. Without one, fall back to the old test.
339
+ */
340
+ const hasDataForTheDay: boolean = todaysReading
341
+ ? todaysReading.coveredSeconds > 0
342
+ : todaysEvents.length > 0;
343
+
277
344
  // now check bar rules and finalize the color of the bar
278
345
 
279
- const uptimePercentForTheDay: number =
280
- totalUptimeInSeconds + totalDowntimeInSeconds > 0
346
+ /*
347
+ * The denominator is what was MEASURED, not the length of the day. A day
348
+ * only half covered - the day a monitor was created - reports the
349
+ * percentage of the half we watched rather than being marked down for the
350
+ * hours nobody was looking.
351
+ *
352
+ * The old expression ended in `: 100`, which said "no readings at all
353
+ * means a perfect day". That is the single line that let a status page
354
+ * assert uptime for a period it had no data for, so it is gone rather
355
+ * than adjusted: with no data there is no percentage.
356
+ */
357
+ const uptimePercentForTheDay: number = !hasDataForTheDay
358
+ ? 0
359
+ : totalUptimeInSeconds + totalDowntimeInSeconds > 0
281
360
  ? (totalUptimeInSeconds /
282
361
  (totalDowntimeInSeconds + totalUptimeInSeconds)) *
283
362
  100
284
363
  : 100;
285
364
 
286
- for (const rules of props.barColorRules || []) {
287
- if (uptimePercentForTheDay >= rules.uptimePercentGreaterThanOrEqualTo) {
288
- color = rules.barColor;
289
- break;
365
+ /*
366
+ * Rules are skipped entirely on a day with no data. Otherwise a page
367
+ * configuring ">= 99% is green" would have that rule matched by a day
368
+ * nobody measured, which is the very claim this change removes. No-data
369
+ * outranks operator rules on purpose.
370
+ */
371
+ if (hasDataForTheDay) {
372
+ for (const rules of props.barColorRules || []) {
373
+ if (uptimePercentForTheDay >= rules.uptimePercentGreaterThanOrEqualTo) {
374
+ color = rules.barColor;
375
+ break;
376
+ }
290
377
  }
291
378
  }
292
379
 
293
- if (todaysEvents.length === 1 && !hasEvents) {
380
+ if (todaysEvents.length >= 1) {
294
381
  hasEvents = true;
295
382
  }
296
383
 
297
- if (todaysEvents.length === 1) {
298
- hasEvents = true;
299
- }
384
+ /*
385
+ * THE FIX.
386
+ *
387
+ * This used to read: no events today, so paint `defaultBarColor`. That
388
+ * single line is how a status page claimed uptime for days it had no data
389
+ * for - `defaultBarColor` is green on the overwhelming majority of pages,
390
+ * and "no events" was true both for a quiet day and for a day whose rows
391
+ * the fetch cap had thrown away.
392
+ *
393
+ * Now the two are separated. A day with data but no events of its own is
394
+ * an ordinary good day and still gets the operator's colour. A day with
395
+ * no data gets the fixed no-data treatment and says so.
396
+ */
397
+ hasEvents = hasDataForTheDay;
300
398
 
301
- if (todaysEvents.length === 0) {
302
- hasEvents = false;
399
+ if (!hasDataForTheDay) {
400
+ color = NO_DATA_BAR_COLOR;
401
+ } else if (todaysEvents.length === 0) {
303
402
  color = props.defaultBarColor || Green;
304
403
  }
305
404
 
@@ -315,9 +414,18 @@ const DayUptimeGraph: FunctionComponent<ComponentProps> = (
315
414
  className = "w-20 h-" + props.height;
316
415
  }
317
416
 
318
- // Build status durations for tooltip
319
- const statusDurations: Array<StatusDuration> = [];
320
- for (const key in secondsOfEvent) {
417
+ /*
418
+ * Build status durations for the tooltip and the day dialog.
419
+ *
420
+ * A server reading wins: its durations are measured over the whole day
421
+ * server-side, while `secondsOfEvent` can only describe the rows this
422
+ * browser happened to receive.
423
+ */
424
+ const statusDurations: Array<StatusDuration> = todaysReading
425
+ ? todaysReading.statusDurations
426
+ : [];
427
+
428
+ for (const key in todaysReading ? {} : secondsOfEvent) {
321
429
  statusDurations.push({
322
430
  label: eventLabels[key] || "Unknown",
323
431
  seconds: secondsOfEvent[key] || 0,
@@ -37,6 +37,13 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
37
37
  fetchRequestOptions?: RequestOptions | undefined;
38
38
  customElement?: ((item: TBaseModel) => ReactElement) | undefined;
39
39
  noItemsMessage: string;
40
+ /*
41
+ * Render nothing, rather than noItemsMessage (or its "No items found."
42
+ * fallback), when the list loads empty. For a caller that explains an empty
43
+ * list itself, e.g. from onListLoaded. A search that matches nothing still
44
+ * says so.
45
+ */
46
+ hideEmptyState?: boolean | undefined;
40
47
  headerField?: string | ((item: TBaseModel) => ReactElement) | undefined;
41
48
  onSelectChange?: ((list: Array<TBaseModel>) => void) | undefined;
42
49
  refreshToggle?: string | undefined;
@@ -260,7 +267,10 @@ const ModelList: <TBaseModel extends BaseModel>(
260
267
  {error ? <ErrorMessage message={error} /> : <></>}
261
268
  {isLoading ? <ComponentLoader /> : <></>}
262
269
 
263
- {!isLoading && !error && searchedList.length === 0 ? (
270
+ {!isLoading &&
271
+ !error &&
272
+ searchedList.length === 0 &&
273
+ (Boolean(searchText) || !props.hideEmptyState) ? (
264
274
  <ErrorMessage
265
275
  message={
266
276
  searchText
@@ -2,9 +2,16 @@ import ComponentLoader from "../ComponentLoader/ComponentLoader";
2
2
  import ErrorMessage from "../ErrorMessage/ErrorMessage";
3
3
  import DayUptimeGraph, {
4
4
  BarChartRule,
5
+ DayReading,
5
6
  Event,
6
7
  UptimeBarDaySummary,
7
8
  } from "../Graphs/DayUptimeGraph";
9
+ import { StatusDuration } from "../Graphs/UptimeDaySummary";
10
+ import Dictionary from "../../../Types/Dictionary";
11
+ import {
12
+ UptimeDayBucket,
13
+ UptimeStatusDuration,
14
+ } from "../../../Types/StatusPage/UptimeDailyAggregate";
8
15
  import UptimeUtil from "./UptimeUtil";
9
16
  import Color from "../../../Types/Color";
10
17
  import CommonMonitorEvent from "../../../Utils/Uptime/MonitorEvent";
@@ -27,6 +34,17 @@ export interface ComponentProps {
27
34
  height?: number | undefined;
28
35
  barColorRules?: Array<StatusPageHistoryChartBarColorRule> | undefined;
29
36
  downtimeMonitorStatuses: Array<MonitorStatus> | undefined;
37
+ /*
38
+ * Server-measured per-day coverage for THIS monitor.
39
+ *
40
+ * The `items` array above arrives under a fetch cap that silently drops
41
+ * history, so it cannot answer "did we have data for this day". These
42
+ * buckets can. When absent the graph keeps its old event-derived
43
+ * behaviour, which is what the dashboard monitor page still uses.
44
+ */
45
+ uptimeBuckets?: Array<UptimeDayBucket> | undefined;
46
+ /* Statuses by id, so a bucket's durations can be given a name and colour. */
47
+ monitorStatuses?: Array<MonitorStatus> | undefined;
30
48
  defaultBarColor: Color;
31
49
  incidents?: Array<UptimeBarTooltipIncident> | undefined;
32
50
  onBarClick?: (
@@ -62,6 +80,56 @@ const MonitorUptimeGraph: FunctionComponent<ComponentProps> = (
62
80
  );
63
81
  }, [props.barColorRules]);
64
82
 
83
+ /*
84
+ * Turn the server's buckets into the graph's per-day readings, resolving
85
+ * each status id to the name and colour the page already knows.
86
+ */
87
+ const dayReadings: Array<DayReading> | undefined = useMemo(() => {
88
+ if (!props.uptimeBuckets) {
89
+ return undefined;
90
+ }
91
+
92
+ const statusById: Dictionary<MonitorStatus> = {};
93
+
94
+ for (const status of props.monitorStatuses || []) {
95
+ if (status.id) {
96
+ statusById[status.id.toString()] = status;
97
+ }
98
+ }
99
+
100
+ const downtimeIds: Array<string> = (
101
+ props.downtimeMonitorStatuses || []
102
+ ).map((status: MonitorStatus) => {
103
+ return status.id?.toString() || "";
104
+ });
105
+
106
+ return props.uptimeBuckets.map((bucket: UptimeDayBucket): DayReading => {
107
+ return {
108
+ dayStart: bucket.bucketStart,
109
+ daySeconds: bucket.daySeconds,
110
+ coveredSeconds: bucket.coveredSeconds,
111
+ statusDurations: bucket.statusDurations.map(
112
+ (duration: UptimeStatusDuration): StatusDuration => {
113
+ const statusId: string = duration.monitorStatusId.toString();
114
+ const status: MonitorStatus | undefined = statusById[statusId];
115
+
116
+ return {
117
+ label: status?.name || "Unknown",
118
+ seconds: duration.seconds,
119
+ color: status?.color || props.defaultBarColor,
120
+ isDowntime: downtimeIds.includes(statusId),
121
+ };
122
+ },
123
+ ),
124
+ };
125
+ });
126
+ }, [
127
+ props.uptimeBuckets,
128
+ props.monitorStatuses,
129
+ props.downtimeMonitorStatuses,
130
+ props.defaultBarColor,
131
+ ]);
132
+
65
133
  if (props.isLoading) {
66
134
  return <ComponentLoader />;
67
135
  }
@@ -80,6 +148,7 @@ const MonitorUptimeGraph: FunctionComponent<ComponentProps> = (
80
148
  startDate={props.startDate}
81
149
  endDate={props.endDate}
82
150
  events={events}
151
+ dayReadings={dayReadings}
83
152
  defaultBarColor={props.defaultBarColor}
84
153
  height={props.height}
85
154
  barColorRules={barColorRules}
@@ -33,6 +33,18 @@ export interface TelemetryViewerProps<T> {
33
33
  error?: string | undefined;
34
34
  onRefresh?: (() => void) | undefined;
35
35
  emptyMessage?: string | undefined;
36
+ /*
37
+ * Replaces the default "No results / try adjusting filters" block when the
38
+ * list comes back empty.
39
+ *
40
+ * For most signals the default is right: an empty list means the filters
41
+ * are too narrow. For a signal that a project may not be SENDING at all,
42
+ * it is usually wrong — the reader needs the setup guide, not an
43
+ * invitation to widen a time range over data that does not exist. Takes
44
+ * precedence over `emptyMessage`; nothing is rendered around it, so the
45
+ * caller owns the whole empty area.
46
+ */
47
+ emptyContent?: ReactNode;
36
48
 
37
49
  // -- Layout --
38
50
  /** Render one item row in the main list. */
@@ -296,18 +308,22 @@ function TelemetryViewerInner<T>(props: TelemetryViewerProps<T>): ReactElement {
296
308
  <ComponentLoader />
297
309
  </div>
298
310
  ) : props.items.length === 0 ? (
299
- <div className="flex h-48 flex-col items-center justify-center gap-2 px-6 text-center">
300
- <Icon
301
- icon={IconProp.Search}
302
- className="h-8 w-8 text-gray-300"
303
- />
304
- <p className="text-sm font-medium text-gray-500">
305
- {props.emptyMessage || "No results"}
306
- </p>
307
- <p className="text-xs text-gray-400">
308
- Try adjusting filters or time range.
309
- </p>
310
- </div>
311
+ props.emptyContent !== undefined ? (
312
+ <>{props.emptyContent}</>
313
+ ) : (
314
+ <div className="flex h-48 flex-col items-center justify-center gap-2 px-6 text-center">
315
+ <Icon
316
+ icon={IconProp.Search}
317
+ className="h-8 w-8 text-gray-300"
318
+ />
319
+ <p className="text-sm font-medium text-gray-500">
320
+ {props.emptyMessage || "No results"}
321
+ </p>
322
+ <p className="text-xs text-gray-400">
323
+ Try adjusting filters or time range.
324
+ </p>
325
+ </div>
326
+ )
311
327
  ) : (
312
328
  <ul className="divide-y divide-gray-100">
313
329
  {props.items.map((item: T, index: number) => {