@oneuptime/common 13.0.7 → 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 (759) 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/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
  6. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
  7. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
  8. package/Scripts/generate-service-worker.js +65 -4
  9. package/Server/API/AIBillingAPI.ts +1 -0
  10. package/Server/API/AIInsightAPI.ts +1 -4
  11. package/Server/API/AIInvestigationAPI.ts +1 -4
  12. package/Server/API/AIReadinessAPI.ts +4 -5
  13. package/Server/API/AlertAPI.ts +1 -0
  14. package/Server/API/AutoRemediationAPI.ts +1 -4
  15. package/Server/API/BaseAPI.ts +34 -0
  16. package/Server/API/BaseAnalyticsAPI.ts +80 -9
  17. package/Server/API/BillingAPI.ts +2 -0
  18. package/Server/API/BillingInvoiceAPI.ts +1 -0
  19. package/Server/API/BillingPaymentMethodAPI.ts +2 -0
  20. package/Server/API/CodeFixRunAPI.ts +1 -3
  21. package/Server/API/CommonAPI.ts +68 -4
  22. package/Server/API/DashboardAPI.ts +12 -12
  23. package/Server/API/GlobalConfigAPI.ts +243 -460
  24. package/Server/API/IncidentAPI.ts +2 -0
  25. package/Server/API/IncidentEpisodeAPI.ts +1 -0
  26. package/Server/API/NotificationAPI.ts +1 -0
  27. package/Server/API/OnCallCalendarAPI.ts +4 -5
  28. package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
  29. package/Server/API/OnCallReadinessAPI.ts +4 -1
  30. package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
  31. package/Server/API/ProjectAPI.ts +1 -0
  32. package/Server/API/ProjectOIDC.ts +25 -16
  33. package/Server/API/ProjectSSO.ts +24 -16
  34. package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
  35. package/Server/API/StatusPageAPI.ts +261 -74
  36. package/Server/API/TeamMemberAPI.ts +13 -1
  37. package/Server/API/UserCallAPI.ts +2 -0
  38. package/Server/API/UserEmailAPI.ts +2 -0
  39. package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
  40. package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
  41. package/Server/API/UserSlackAPI.ts +1 -0
  42. package/Server/API/UserSmsAPI.ts +2 -0
  43. package/Server/API/UserTotpAuthAPI.ts +1 -0
  44. package/Server/API/UserWebAuthnAPI.ts +2 -0
  45. package/Server/API/UserWebhookAPI.ts +1 -0
  46. package/Server/API/UserWhatsAppAPI.ts +2 -0
  47. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  48. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  49. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  50. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  51. package/Server/EnvironmentConfig.ts +147 -2
  52. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  53. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  54. package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
  55. package/Server/Middleware/UserAuthorization.ts +117 -21
  56. package/Server/Services/ApiKeyPermissionService.ts +101 -4
  57. package/Server/Services/AuditLogService.ts +84 -1079
  58. package/Server/Services/DatabaseService.ts +245 -0
  59. package/Server/Services/DetectionRuleService.ts +5 -4
  60. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  61. package/Server/Services/GlobalConfigService.ts +80 -1
  62. package/Server/Services/MonitorProbeService.ts +116 -20
  63. package/Server/Services/MonitorService.ts +56 -4
  64. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  65. package/Server/Services/MonitorTemplateService.ts +26 -0
  66. package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
  67. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
  68. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
  69. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
  70. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
  71. package/Server/Services/PayAsYouGoBillingService.ts +23 -28
  72. package/Server/Services/ProjectService.ts +302 -0
  73. package/Server/Services/RumSessionPinService.ts +293 -22
  74. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  75. package/Server/Services/StatusPageService.ts +78 -1
  76. package/Server/Services/TeamMemberService.ts +11 -0
  77. package/Server/Services/TeamPermissionService.ts +120 -26
  78. package/Server/Services/TeamService.ts +10 -0
  79. package/Server/Services/ThreatIntelFeedService.ts +6 -3
  80. package/Server/Services/UserService.ts +10 -17
  81. package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
  82. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  83. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  84. package/Server/Types/Database/Permissions/Index.ts +75 -11
  85. package/Server/Types/Database/Permissions/TablePermission.ts +34 -31
  86. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  87. package/Server/Types/Markdown.ts +41 -0
  88. package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
  89. package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
  90. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  91. package/Server/Utils/EditionEnforcement.ts +233 -0
  92. package/Server/Utils/FrontendEnvironment.ts +41 -2
  93. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  94. package/Server/Utils/MasterAdminPageAccess.ts +61 -0
  95. package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
  96. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  97. package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
  98. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
  99. package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
  100. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  101. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
  102. package/Server/Utils/Realtime.ts +163 -45
  103. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
  104. package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
  105. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
  106. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
  107. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
  108. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  109. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  110. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  111. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  112. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  113. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  114. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  115. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  116. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
  117. package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
  118. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
  119. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
  120. package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
  121. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  122. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
  123. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
  124. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
  125. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
  126. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  127. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  128. package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
  129. package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
  130. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
  131. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  132. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
  133. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  134. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
  135. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
  136. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
  137. package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
  138. package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
  139. package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
  140. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +133 -70
  141. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  142. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  143. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
  144. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -58
  145. package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
  146. package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
  147. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  148. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
  149. package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
  150. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  151. package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
  152. package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
  153. package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
  154. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  155. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
  156. package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
  157. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  158. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  159. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  160. package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
  161. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  162. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
  163. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
  164. package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
  165. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
  166. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  167. package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
  168. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
  169. package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
  170. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
  171. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
  172. package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
  173. package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
  174. package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
  175. package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
  176. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
  177. package/Tests/Server/API/GlobalConfigLicense.test.ts +766 -381
  178. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
  179. package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
  180. package/Tests/Server/API/OnCallReadinessAPI.test.ts +196 -912
  181. package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
  182. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
  183. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
  184. package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
  185. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
  186. package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
  187. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  188. package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
  189. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
  190. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
  191. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  192. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
  193. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
  194. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
  195. package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
  196. package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
  197. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
  198. package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
  199. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
  200. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
  201. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  202. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  203. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  204. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  205. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  206. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  207. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  208. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  209. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  210. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  211. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  212. package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
  213. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
  214. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
  215. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  216. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  217. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
  218. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
  219. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
  220. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  221. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  222. package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
  223. package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
  224. package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
  225. package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
  226. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  227. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  228. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  229. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
  230. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  231. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  232. package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
  233. package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
  234. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
  235. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
  236. package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
  237. package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
  238. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  239. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  240. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
  241. package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
  242. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  243. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  244. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  245. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  246. package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
  247. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
  248. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
  249. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  250. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
  251. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  252. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  253. package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
  254. package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
  255. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
  256. package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
  257. package/Tests/Server/Types/Markdown.test.ts +181 -0
  258. package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
  259. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
  260. package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
  261. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
  262. package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
  263. package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
  264. package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
  265. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  266. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  267. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  268. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  269. package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
  270. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  271. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  272. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
  273. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
  274. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  275. package/Tests/Server/Utils/Realtime.test.ts +502 -0
  276. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
  277. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
  278. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
  279. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
  280. package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
  281. package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
  282. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
  283. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
  284. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
  285. package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
  286. package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
  287. package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
  288. package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
  289. package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
  290. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  291. package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
  292. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  293. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  294. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
  295. package/Tests/Types/Call/CallProvider.test.ts +528 -0
  296. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  297. package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
  298. package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
  299. package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
  300. package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
  301. package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
  302. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
  303. package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
  304. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  305. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  306. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +165 -171
  307. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  308. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  309. package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
  310. package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
  311. package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
  312. package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
  313. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  314. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  315. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  316. package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
  317. package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
  318. package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
  319. package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
  320. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  321. package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
  322. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  323. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
  324. package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
  325. package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
  326. package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
  327. package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
  328. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  329. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  330. package/Tests/UI/Utils/Cookie.test.ts +440 -0
  331. package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
  332. package/Tests/UI/Utils/Navigation.test.ts +33 -0
  333. package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
  334. package/Tests/UI/Utils/Realtime.test.ts +759 -0
  335. package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
  336. package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
  337. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  338. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  339. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  340. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
  341. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  342. package/Types/API/Hostname.ts +63 -16
  343. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
  344. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  345. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  346. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  347. package/Types/Database/CanonicalModelInstance.ts +102 -0
  348. package/Types/Database/TableColumn.ts +20 -6
  349. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  350. package/Types/IP/IPv6.ts +6 -1
  351. package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
  352. package/Types/Monitor/MonitorStep.ts +6 -1
  353. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  354. package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
  355. package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
  356. package/Types/Realtime/EventName.ts +11 -0
  357. package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
  358. package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
  359. package/Types/SecurityEvent/SigmaRule.ts +51 -0
  360. package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
  361. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  362. package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
  363. package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
  364. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  365. package/UI/Components/EditionLabel/EditionLabel.tsx +808 -1195
  366. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  367. package/UI/Components/EventItem/EventItem.tsx +12 -1
  368. package/UI/Components/Forms/BasicForm.tsx +151 -126
  369. package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
  370. package/UI/Components/Forms/Fields/FormField.tsx +10 -4
  371. package/UI/Components/Forms/FormSummary.tsx +7 -0
  372. package/UI/Components/Forms/Types/Field.ts +10 -0
  373. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  374. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  375. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
  376. package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
  377. package/UI/Components/ModelList/ModelList.tsx +11 -1
  378. package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
  379. package/UI/Components/ModelTable/TableView.tsx +1 -1
  380. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  381. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  382. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
  383. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  384. package/UI/Utils/API/API.ts +173 -12
  385. package/UI/Utils/API/RequestOptions.ts +11 -0
  386. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
  387. package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
  388. package/UI/Utils/Navigation.ts +9 -0
  389. package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
  390. package/UI/Utils/Realtime.ts +176 -30
  391. package/UI/esbuild-enterprise.js +263 -0
  392. package/Utils/API.ts +14 -5
  393. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  394. package/Utils/HostAddressUtil.ts +94 -0
  395. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  396. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  397. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
  398. package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
  399. package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
  400. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  401. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  402. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  403. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  404. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  405. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  406. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  407. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  408. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  409. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
  410. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
  411. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
  412. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
  413. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
  414. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
  415. package/build/dist/Server/API/AIBillingAPI.js +1 -1
  416. package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
  417. package/build/dist/Server/API/AIInsightAPI.js +1 -4
  418. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  419. package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
  420. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  421. package/build/dist/Server/API/AIReadinessAPI.js +1 -3
  422. package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
  423. package/build/dist/Server/API/AlertAPI.js +1 -1
  424. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  425. package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
  426. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  427. package/build/dist/Server/API/BaseAPI.js +33 -0
  428. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  429. package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
  430. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  431. package/build/dist/Server/API/BillingAPI.js +2 -2
  432. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  433. package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
  434. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  435. package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
  436. package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
  437. package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
  438. package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
  439. package/build/dist/Server/API/CommonAPI.js +50 -5
  440. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  441. package/build/dist/Server/API/DashboardAPI.js +12 -12
  442. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  443. package/build/dist/Server/API/GlobalConfigAPI.js +158 -342
  444. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  445. package/build/dist/Server/API/IncidentAPI.js +2 -2
  446. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  447. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  448. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  449. package/build/dist/Server/API/NotificationAPI.js +1 -1
  450. package/build/dist/Server/API/NotificationAPI.js.map +1 -1
  451. package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
  452. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
  453. package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
  454. package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
  455. package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
  456. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
  457. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
  458. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
  459. package/build/dist/Server/API/ProjectAPI.js +1 -1
  460. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  461. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  462. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  463. package/build/dist/Server/API/ProjectSSO.js +24 -16
  464. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  465. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  466. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  467. package/build/dist/Server/API/StatusPageAPI.js +218 -76
  468. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  469. package/build/dist/Server/API/TeamMemberAPI.js +11 -4
  470. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  471. package/build/dist/Server/API/UserCallAPI.js +2 -2
  472. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  473. package/build/dist/Server/API/UserEmailAPI.js +2 -2
  474. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  475. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
  476. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  477. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
  478. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
  479. package/build/dist/Server/API/UserSlackAPI.js +1 -1
  480. package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
  481. package/build/dist/Server/API/UserSmsAPI.js +2 -2
  482. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  483. package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
  484. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
  485. package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
  486. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  487. package/build/dist/Server/API/UserWebhookAPI.js +1 -1
  488. package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
  489. package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
  490. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  491. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  492. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  493. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  494. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  495. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  496. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  497. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  498. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  499. package/build/dist/Server/EnvironmentConfig.js +84 -2
  500. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  501. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  502. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  503. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  504. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  505. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
  506. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  507. package/build/dist/Server/Middleware/UserAuthorization.js +94 -15
  508. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  509. package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
  510. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  511. package/build/dist/Server/Services/AuditLogService.js +71 -740
  512. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  513. package/build/dist/Server/Services/DatabaseService.js +171 -0
  514. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  515. package/build/dist/Server/Services/DetectionRuleService.js +4 -4
  516. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  517. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  518. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  519. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  520. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  521. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  522. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  523. package/build/dist/Server/Services/MonitorService.js +45 -4
  524. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  525. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  526. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  527. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  528. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  529. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
  530. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
  531. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
  532. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  533. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
  534. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  535. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
  536. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  537. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
  538. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  539. package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
  540. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
  541. package/build/dist/Server/Services/ProjectService.js +231 -0
  542. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  543. package/build/dist/Server/Services/RumSessionPinService.js +198 -16
  544. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  545. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  546. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  547. package/build/dist/Server/Services/StatusPageService.js +71 -0
  548. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  549. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  550. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  551. package/build/dist/Server/Services/TeamPermissionService.js +89 -27
  552. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  553. package/build/dist/Server/Services/TeamService.js +9 -0
  554. package/build/dist/Server/Services/TeamService.js.map +1 -1
  555. package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
  556. package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
  557. package/build/dist/Server/Services/UserService.js +10 -16
  558. package/build/dist/Server/Services/UserService.js.map +1 -1
  559. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
  560. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  561. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  562. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  563. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  564. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  565. package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
  566. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  567. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +24 -16
  568. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  569. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  570. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  571. package/build/dist/Server/Types/Markdown.js +31 -0
  572. package/build/dist/Server/Types/Markdown.js.map +1 -1
  573. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
  574. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
  575. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
  576. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
  577. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  578. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  579. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  580. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  581. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  582. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  583. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  584. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  585. package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
  586. package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
  587. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
  588. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  589. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  590. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  591. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
  592. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  593. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
  594. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  595. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
  596. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  597. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  598. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  599. package/build/dist/Server/Utils/Realtime.js +126 -37
  600. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  601. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
  602. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  603. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
  604. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
  605. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
  606. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  607. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
  608. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
  609. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
  610. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
  611. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  612. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  613. package/build/dist/Types/API/Hostname.js +59 -11
  614. package/build/dist/Types/API/Hostname.js.map +1 -1
  615. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
  616. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  617. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  618. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  619. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  620. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  621. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  622. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  623. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  624. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  625. package/build/dist/Types/Database/TableColumn.js +19 -6
  626. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  627. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  628. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  629. package/build/dist/Types/IP/IPv6.js +6 -1
  630. package/build/dist/Types/IP/IPv6.js.map +1 -1
  631. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  632. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  633. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  634. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
  635. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
  636. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
  637. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
  638. package/build/dist/Types/Realtime/EventName.js +10 -0
  639. package/build/dist/Types/Realtime/EventName.js.map +1 -1
  640. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
  641. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  642. package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
  643. package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
  644. package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
  645. package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
  646. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
  647. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
  648. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  649. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  650. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
  651. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
  652. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
  653. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
  654. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
  655. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  656. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +425 -585
  657. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  658. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  659. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  660. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  661. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  662. package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
  663. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  664. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
  665. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
  666. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
  667. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  668. package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
  669. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  670. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  671. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  672. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  673. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  674. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
  675. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  676. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
  677. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
  678. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  679. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  680. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
  681. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  682. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  683. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  684. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  685. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  686. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  687. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  688. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
  689. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  690. package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
  691. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  692. package/build/dist/UI/Utils/API/API.js +143 -19
  693. package/build/dist/UI/Utils/API/API.js.map +1 -1
  694. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
  695. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  696. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
  697. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  698. package/build/dist/UI/Utils/Navigation.js +8 -0
  699. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  700. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
  701. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
  702. package/build/dist/UI/Utils/Realtime.js +120 -15
  703. package/build/dist/UI/Utils/Realtime.js.map +1 -1
  704. package/build/dist/Utils/API.js +13 -5
  705. package/build/dist/Utils/API.js.map +1 -1
  706. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  707. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  708. package/build/dist/Utils/HostAddressUtil.js +80 -0
  709. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  710. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  711. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  712. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  713. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  714. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
  715. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  716. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
  717. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
  718. package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
  719. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  720. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  721. package/jest.config.json +3 -1
  722. package/package.json +1 -1
  723. package/test-setup.sh +103 -34
  724. package/tsconfig.json +13 -0
  725. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  726. package/Server/API/TeamComplianceAPI.ts +0 -158
  727. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  728. package/Server/Services/TeamComplianceService.ts +0 -733
  729. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  730. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  731. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  732. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  733. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  734. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  735. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  736. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  737. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  738. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  739. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  740. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  741. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  742. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  743. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  744. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  745. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  746. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  747. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  748. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  749. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  750. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  751. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  752. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  753. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  754. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  755. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  756. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  757. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  758. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  759. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -1,18 +1,17 @@
1
1
  import Modal, { ModalWidth } from "../Modal/Modal";
2
2
  import Icon from "../Icon/Icon";
3
3
  import IconProp from "../../../Types/Icon/IconProp";
4
- import Input from "../Input/Input";
5
4
  import Button, { ButtonStyleType } from "../Button/Button";
6
- import React, { useCallback, useEffect, useMemo, useRef, useState, } from "react";
5
+ import React, { useCallback, useEffect, useMemo, useState, } from "react";
7
6
  import GlobalConfig from "../../../Models/DatabaseModels/GlobalConfig";
8
7
  import API from "../../Utils/API/API";
9
8
  import OneUptimeDate from "../../../Types/Date";
10
9
  import HTTPMethod from "../../../Types/API/HTTPMethod";
11
10
  import Route from "../../../Types/API/Route";
12
11
  import URL from "../../../Types/API/URL";
13
- import { APP_API_URL, BILLING_ENABLED, IS_ENTERPRISE_EDITION, } from "../../Config";
14
- import Alert, { AlertType } from "../Alerts/Alert";
12
+ import { APP_API_URL, BILLING_ENABLED, IS_ENTERPRISE_EDITION, env, } from "../../Config";
15
13
  import VersionUtil from "../../../Utils/VersionUtil";
14
+ import { ENTERPRISE_LICENSE_GRACE_PERIOD_IN_DAYS, ENTERPRISE_LICENSE_TRIAL_PERIOD_IN_DAYS, } from "../../../Types/EnterpriseLicense/EnterpriseLicensePeriods";
16
15
  import UserUtil from "../../Utils/User";
17
16
  const ENTERPRISE_URL = "https://oneuptime.com/enterprise/demo";
18
17
  /*
@@ -24,68 +23,60 @@ const ENTERPRISE_URL = "https://oneuptime.com/enterprise/demo";
24
23
  * the docs move in the repository.
25
24
  */
26
25
  const UPGRADE_GUIDE_URL = "https://oneuptime.com/docs/installation/upgrading";
27
- const SALES_EMAIL = "sales@oneuptime.com";
28
26
  const SALES_MAILTO_URL = "mailto:sales@oneuptime.com";
27
+ const DAY_IN_MS = 24 * 60 * 60 * 1000;
28
+ /*
29
+ * What stops when the license lapses, in one sentence set, so every notice in
30
+ * the dialog says it the same way. It has to be said plainly and before it
31
+ * happens: after the trial or the grace period, single sign-on (SAML and
32
+ * OIDC) and SCIM provisioning stop, "Require SSO" is no longer enforced (so
33
+ * nobody is locked out: users sign in with their password), and audit logging
34
+ * stops recording - the Community Edition's behaviour - until a license is
35
+ * activated, when everything resumes without a restart. Core monitoring is
36
+ * never touched.
37
+ */
38
+ export const LICENSE_LAPSE_CONSEQUENCES = 'single sign-on (SAML and OIDC) stops and "Require SSO" is no longer enforced, so users sign in with their password; SCIM provisioning stops; audit logging stops recording; enterprise configuration becomes read-only; and the enterprise admin dashboards are locked. Everything resumes as soon as a license is activated, and core monitoring is never affected.';
39
+ /*
40
+ * The two periods have different names and different lengths: an unlicensed
41
+ * installation's first ENTERPRISE_LICENSE_TRIAL_PERIOD_IN_DAYS days are its
42
+ * trial, and only a license that expired has a grace period
43
+ * (ENTERPRISE_LICENSE_GRACE_PERIOD_IN_DAYS days after its expiry). The
44
+ * numbers come from the constants the license classifier uses.
45
+ */
46
+ export const TRIAL_ENFORCEMENT_SUMMARY = `Without a valid license (after the ${ENTERPRISE_LICENSE_TRIAL_PERIOD_IN_DAYS}-day trial), ${LICENSE_LAPSE_CONSEQUENCES}`;
47
+ export const GRACE_ENFORCEMENT_SUMMARY = `Without a valid license (after the ${ENTERPRISE_LICENSE_GRACE_PERIOD_IN_DAYS}-day grace period), ${LICENSE_LAPSE_CONSEQUENCES}`;
48
+ /*
49
+ * What has stopped once the license lapsed (expired past its grace period,
50
+ * missing after the trial, or invalid), after the reason the notice gives.
51
+ */
52
+ export const LICENSE_LAPSED_STATE = 'Single sign-on (SAML and OIDC) and SCIM provisioning are off: "Require SSO" is not enforced, so users sign in with their password, and your identity provider\'s SCIM requests are refused. Audit logging is not recording. Enterprise configuration is read-only and the enterprise admin dashboards are locked. Everything resumes, without a restart, as soon as a valid license is added, and core monitoring is never affected.';
53
+ const parseLicenseStatus = (value) => {
54
+ if (value === "valid" ||
55
+ value === "grace" ||
56
+ value === "expired" ||
57
+ value === "missing" ||
58
+ value === "invalid") {
59
+ return value;
60
+ }
61
+ return null;
62
+ };
29
63
  /*
30
64
  * Seat usage at or above this percent asks the admin to talk to OneUptime about
31
65
  * expanding. Matches CRITICAL_CAPACITY_PERCENT in
32
66
  * App/FeatureSet/Workers/Jobs/InstanceHealth/EvaluateClickhouseCapacity.ts.
33
67
  */
34
68
  const SEAT_WARNING_PERCENT = 90;
35
- const parseLicenseInstances = (value) => {
36
- if (!Array.isArray(value)) {
37
- return [];
38
- }
39
- const instances = [];
40
- for (const item of value) {
41
- if (!item || typeof item !== "object") {
42
- continue;
43
- }
44
- const instance = item;
45
- instances.push({
46
- instanceId: typeof instance["instanceId"] === "string"
47
- ? instance["instanceId"]
48
- : "",
49
- host: typeof instance["host"] === "string" && instance["host"]
50
- ? instance["host"]
51
- : null,
52
- userCount: typeof instance["userCount"] === "number"
53
- ? instance["userCount"]
54
- : null,
55
- isCountedTowardsUsage: typeof instance["isCountedTowardsUsage"] === "boolean"
56
- ? instance["isCountedTowardsUsage"]
57
- : undefined,
58
- lastReportedAt: typeof instance["lastReportedAt"] === "string"
59
- ? instance["lastReportedAt"]
60
- : null,
61
- version: typeof instance["version"] === "string" && instance["version"]
62
- ? instance["version"]
63
- : null,
64
- });
65
- }
66
- return instances;
67
- };
68
- const formatInstanceReportedAt = (lastReportedAt) => {
69
- if (!lastReportedAt) {
70
- return "No usage reported yet.";
71
- }
72
- const reportedAt = OneUptimeDate.fromString(lastReportedAt);
73
- if (Number.isNaN(reportedAt.getTime())) {
74
- return "No usage reported yet.";
75
- }
76
- return `Last reported ${reportedAt.toLocaleString()}.`;
77
- };
78
69
  const EditionLabel = (props) => {
79
70
  const [isDialogOpen, setIsDialogOpen] = useState(false);
80
71
  const [globalConfig, setGlobalConfig] = useState(null);
72
+ /*
73
+ * The last successful license GET, as the server sent it. Handed to the
74
+ * license manager, which reads its own fields (the instances, the
75
+ * activation mode) from it.
76
+ */
77
+ const [licensePayload, setLicensePayload] = useState(null);
81
78
  const [isConfigLoading, setIsConfigLoading] = useState(false);
82
79
  const [configError, setConfigError] = useState("");
83
- const [licenseKeyInput, setLicenseKeyInput] = useState("");
84
- const [validationError, setValidationError] = useState("");
85
- const [successMessage, setSuccessMessage] = useState("");
86
- const [isValidating, setIsValidating] = useState(false);
87
- const [isRefreshingLicense, setIsRefreshingLicense] = useState(false);
88
- const [isChangingLicense, setIsChangingLicense] = useState(false);
89
80
  /*
90
81
  * The seat limit as this installation actually enforces it, rather than as
91
82
  * oneuptime.com last reported it. The server computes it from the live User
@@ -98,8 +89,6 @@ const EditionLabel = (props) => {
98
89
  const [isSeatLimitEnforced, setIsSeatLimitEnforced] = useState(false);
99
90
  const [enforcedSeatsInUse, setEnforcedSeatsInUse] = useState(null);
100
91
  const [canAddMoreUsers, setCanAddMoreUsers] = useState(true);
101
- const [licenseInstances, setLicenseInstances] = useState([]);
102
- const [thisInstanceId, setThisInstanceId] = useState("");
103
92
  /*
104
93
  * Version state comes from the server rather than APP_VERSION in the
105
94
  * browser bundle: the frontend env var is empty in every build that was
@@ -124,13 +113,56 @@ const EditionLabel = (props) => {
124
113
  * cannot always derive validity from the token itself.
125
114
  */
126
115
  const [serverLicenseValid, setServerLicenseValid] = useState(null);
127
- const licenseInputEditedRef = useRef(false);
128
- if (BILLING_ENABLED) {
129
- return React.createElement(React.Fragment, null);
130
- }
131
116
  /*
132
- * Both the license GET and the two license POSTs answer with the same seat
133
- * fields, so the three responses are unpacked in one place.
117
+ * Where the license stands, as the Enterprise license client classified it:
118
+ * valid, grace (an expired license's grace period, or an unlicensed
119
+ * installation's trial counted from its first run), expired, missing or
120
+ * invalid - and whether it could be verified offline.
121
+ */
122
+ const [licenseStatus, setLicenseStatus] = useState(null);
123
+ const [licenseVerification, setLicenseVerification] = useState("");
124
+ const [graceReason, setGraceReason] = useState("");
125
+ const [graceEndsAt, setGraceEndsAt] = useState("");
126
+ // Why the license is not valid, when the server says (master admins only).
127
+ const [licenseMessage, setLicenseMessage] = useState("");
128
+ const isMasterAdmin = useMemo(() => {
129
+ return Boolean(UserUtil.isMasterAdmin());
130
+ }, []);
131
+ /*
132
+ * The Enterprise Edition's license manager, when the caller passed one (the
133
+ * Dashboard and the Admin Dashboard of the Enterprise image do). Never on
134
+ * the Community Edition, whatever the bundle holds: there is no license to
135
+ * manage there.
136
+ */
137
+ const LicenseManager = IS_ENTERPRISE_EDITION ? props.licenseManager : undefined;
138
+ /*
139
+ * Activating, replacing and refreshing the license are master-admin actions:
140
+ * the license is instance-wide, and its seat limit is what UserService
141
+ * enforces against, so being able to rewrite it is being able to raise the
142
+ * ceiling on the whole installation. The server enforces this
143
+ * (MasterAdminAuthorization on both POSTs, and it answers everybody else
144
+ * with the edition pill's fields only); hiding the controls just stops
145
+ * everyone else being offered a button that can only fail.
146
+ *
147
+ * It also takes a license manager to manage the license with. The login
148
+ * page (where a signed-out visitor sees the same pill) and a Community
149
+ * build pass none, and telling somebody to "add a license below" there
150
+ * would point at nothing.
151
+ */
152
+ const canManageLicense = isMasterAdmin && Boolean(LicenseManager);
153
+ /*
154
+ * The server says it was asked for the Enterprise Edition
155
+ * (IS_ENTERPRISE_EDITION=true) but runs the Community Edition image, which
156
+ * has no enterprise code. Only a master admin can fix that, so only a
157
+ * master admin is told.
158
+ */
159
+ const showEditionMismatchNotice = !IS_ENTERPRISE_EDITION &&
160
+ isMasterAdmin &&
161
+ env("ENTERPRISE_EDITION_REQUESTED_BUT_NOT_LOADED") === "true";
162
+ /*
163
+ * The license GET and the license manager's writes answer with the same
164
+ * seat fields, so every response is unpacked in one place (the manager
165
+ * calls this through applySeatEnforcement).
134
166
  * isSeatLimitEnforced is what tells "this installation does not enforce a
135
167
  * seat limit" apart from "this build did not report one" - without it, an
136
168
  * older server's silence would read as a limit of zero.
@@ -157,6 +189,17 @@ const EditionLabel = (props) => {
157
189
  setConfigError("");
158
190
  try {
159
191
  const licenseUrl = URL.fromURL(APP_API_URL).addRoute(new Route("/global-config/license"));
192
+ /*
193
+ * The route also serves the login page, so on its own it answers a
194
+ * caller with no session with the reduced anonymous payload and a 200.
195
+ * Inside a signed-in app that caller is us with an expired session,
196
+ * and the reduced payload reads as "no license, no instances". Saying
197
+ * we expect to be signed in makes the server answer 401 instead,
198
+ * which the API client refreshes and replays.
199
+ */
200
+ if (UserUtil.isLoggedIn()) {
201
+ licenseUrl.addQueryParam("signedIn", "true");
202
+ }
160
203
  const response = await API.fetch({
161
204
  method: HTTPMethod.GET,
162
205
  url: licenseUrl,
@@ -184,19 +227,24 @@ const EditionLabel = (props) => {
184
227
  if (typeof payload["currentUserCount"] === "number") {
185
228
  configModel.enterpriseLicenseCurrentUserCount = payload["currentUserCount"];
186
229
  }
187
- if (payload["userCountUpdatedAt"]) {
188
- configModel.enterpriseLicenseUserCountUpdatedAt =
189
- OneUptimeDate.fromString(payload["userCountUpdatedAt"]);
190
- }
191
230
  setGlobalConfig(configModel);
192
- setLicenseInstances(parseLicenseInstances(payload["instances"]));
193
- setThisInstanceId(typeof payload["instanceId"] === "string"
194
- ? payload["instanceId"]
195
- : "");
231
+ setLicensePayload(payload);
196
232
  setServerLicenseValid(typeof payload["licenseValid"] === "boolean"
197
233
  ? payload["licenseValid"]
198
234
  : null);
199
- setIsEvaluationLicense(payload["isEvaluationLicense"] === true);
235
+ setIsEvaluationLicense(payload["isEvaluation"] === true ||
236
+ payload["isEvaluationLicense"] === true);
237
+ setLicenseStatus(parseLicenseStatus(payload["status"]));
238
+ setLicenseVerification(typeof payload["verification"] === "string"
239
+ ? payload["verification"]
240
+ : "");
241
+ setGraceReason(typeof payload["graceReason"] === "string"
242
+ ? payload["graceReason"]
243
+ : "");
244
+ setGraceEndsAt(typeof payload["graceEndsAt"] === "string"
245
+ ? payload["graceEndsAt"]
246
+ : "");
247
+ setLicenseMessage(typeof payload["message"] === "string" ? payload["message"] : "");
200
248
  setCurrentVersion(typeof payload["currentVersion"] === "string"
201
249
  ? payload["currentVersion"]
202
250
  : "");
@@ -212,15 +260,17 @@ const EditionLabel = (props) => {
212
260
  setIsUpdateAvailable(payload["isUpdateAvailable"] === true);
213
261
  setIsUpdateCheckDisabled(payload["isUpdateCheckDisabled"] === true);
214
262
  applySeatEnforcementPayload(payload);
215
- if (!licenseInputEditedRef.current) {
216
- setLicenseKeyInput(configModel.enterpriseLicenseKey || "");
217
- }
218
263
  }
219
264
  catch (err) {
220
265
  setGlobalConfig(null);
221
- setLicenseInstances([]);
266
+ setLicensePayload(null);
222
267
  setServerLicenseValid(null);
223
268
  setIsEvaluationLicense(false);
269
+ setLicenseStatus(null);
270
+ setLicenseVerification("");
271
+ setGraceReason("");
272
+ setGraceEndsAt("");
273
+ setLicenseMessage("");
224
274
  setCurrentVersion("");
225
275
  setLatestVersion("");
226
276
  setLatestVersionPublishedAt("");
@@ -243,7 +293,7 @@ const EditionLabel = (props) => {
243
293
  * opened rather than adding a request to every page load.
244
294
  */
245
295
  useEffect(() => {
246
- if (!IS_ENTERPRISE_EDITION) {
296
+ if (BILLING_ENABLED || !IS_ENTERPRISE_EDITION) {
247
297
  return;
248
298
  }
249
299
  void fetchGlobalConfig();
@@ -267,6 +317,70 @@ const EditionLabel = (props) => {
267
317
  globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseToken,
268
318
  serverLicenseValid,
269
319
  ]);
320
+ /*
321
+ * Whether a license token IS installed, whatever the license client made of
322
+ * it.
323
+ *
324
+ * It matters because "the unlicensed trial" is no longer only for an
325
+ * installation with no license: one whose token has no expiry recorded
326
+ * beside it falls back to the same trial and the same lapse (the license
327
+ * client's classifyUnverifiedWithoutExpiry), so the copy below must not tell
328
+ * a paying customer that no license is installed.
329
+ *
330
+ * Two signals, because the two audiences are told different things. A master
331
+ * admin is sent the token itself. Everybody else is sent only the
332
+ * verification, and the license client reports "none" there exactly when
333
+ * there was no token to judge - so an "unverified" or "verified" grace
334
+ * period is a grace period with a license behind it. Neither field is new:
335
+ * both already come back from GET /global-config/license.
336
+ */
337
+ const hasLicenseInstalled = IS_ENTERPRISE_EDITION &&
338
+ (Boolean(globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseToken) ||
339
+ licenseVerification === "unverified" ||
340
+ licenseVerification === "verified");
341
+ /*
342
+ * The two faces of "grace". An unlicensed installation's trial (counted from
343
+ * the first run of the Enterprise Edition) is not an expired license, and
344
+ * the dialog must not tell somebody who never had a license to renew one.
345
+ */
346
+ const isUnlicensedTrial = IS_ENTERPRISE_EDITION &&
347
+ licenseStatus === "grace" &&
348
+ graceReason === "unlicensed";
349
+ const isExpiredGrace = IS_ENTERPRISE_EDITION &&
350
+ licenseStatus === "grace" &&
351
+ graceReason !== "unlicensed";
352
+ /*
353
+ * A license issued before OneUptime signed its licenses: accepted, but it
354
+ * cannot be verified offline.
355
+ */
356
+ const isUnverifiedLegacyLicense = IS_ENTERPRISE_EDITION &&
357
+ licenseValid &&
358
+ !isUnlicensedTrial &&
359
+ licenseVerification === "unverified";
360
+ const graceEndsAtText = useMemo(() => {
361
+ if (!graceEndsAt) {
362
+ return null;
363
+ }
364
+ const endsAt = OneUptimeDate.fromString(graceEndsAt);
365
+ if (Number.isNaN(endsAt.getTime())) {
366
+ return null;
367
+ }
368
+ return endsAt.toLocaleDateString();
369
+ }, [graceEndsAt]);
370
+ // Whole days left in the trial or the grace period, rounded up; never negative.
371
+ const graceDaysLeft = useMemo(() => {
372
+ if (!graceEndsAt) {
373
+ return null;
374
+ }
375
+ const endsAt = OneUptimeDate.fromString(graceEndsAt);
376
+ if (Number.isNaN(endsAt.getTime())) {
377
+ return null;
378
+ }
379
+ return Math.max(0, Math.ceil((endsAt.getTime() - Date.now()) / DAY_IN_MS));
380
+ }, [graceEndsAt]);
381
+ const graceDaysLeftText = graceDaysLeft === null
382
+ ? ""
383
+ : `${graceDaysLeft} ${graceDaysLeft === 1 ? "day" : "days"} left`;
270
384
  const licenseExpiresAtText = useMemo(() => {
271
385
  if (!(globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseExpiresAt)) {
272
386
  return null;
@@ -381,16 +495,6 @@ const EditionLabel = (props) => {
381
495
  }
382
496
  return currentUserCount;
383
497
  }, [isSeatLimitEnforced, enforcedSeatsInUse, currentUserCount]);
384
- const userCountUpdatedAtText = useMemo(() => {
385
- if (!(globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseUserCountUpdatedAt)) {
386
- return null;
387
- }
388
- const reportedAt = OneUptimeDate.fromString(globalConfig.enterpriseLicenseUserCountUpdatedAt);
389
- if (Number.isNaN(reportedAt.getTime())) {
390
- return null;
391
- }
392
- return reportedAt.toLocaleString();
393
- }, [globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseUserCountUpdatedAt]);
394
498
  const isUserLimitBreached = useMemo(() => {
395
499
  if (!licenseValid) {
396
500
  return false;
@@ -433,41 +537,6 @@ const EditionLabel = (props) => {
433
537
  }
434
538
  return rounded;
435
539
  }, [rawUserUsagePercent]);
436
- /*
437
- * The displayed percent clamped to 0-100. Drives the bar width and
438
- * aria-valuenow, so the fill can never overflow its track and aria-valuenow
439
- * can never exceed aria-valuemax. Derived from the displayed percent rather
440
- * than the raw ratio so the fill always agrees with the caption beneath it:
441
- * 599/600 reads 99% and fills to 99%, not to a visually full 100%. The true
442
- * figure for a breach goes in aria-valuetext.
443
- */
444
- const seatUsageBarPercent = useMemo(() => {
445
- if (seatUsageDisplayPercent === null) {
446
- return 0;
447
- }
448
- return Math.min(100, Math.max(0, seatUsageDisplayPercent));
449
- }, [seatUsageDisplayPercent]);
450
- const seatsRemaining = useMemo(() => {
451
- if (typeof userLimit !== "number" || userLimit <= 0) {
452
- return null;
453
- }
454
- if (typeof effectiveUserCount !== "number") {
455
- return null;
456
- }
457
- return userLimit - effectiveUserCount;
458
- }, [userLimit, effectiveUserCount]);
459
- const seatsRemainingText = useMemo(() => {
460
- if (typeof seatsRemaining !== "number") {
461
- return "";
462
- }
463
- if (seatsRemaining > 0) {
464
- return `${seatsRemaining.toLocaleString()} ${seatsRemaining === 1 ? "seat" : "seats"} remaining`;
465
- }
466
- if (seatsRemaining === 0) {
467
- return "No seats remaining";
468
- }
469
- return `${Math.abs(seatsRemaining).toLocaleString()} over limit`;
470
- }, [seatsRemaining]);
471
540
  /*
472
541
  * Total and mutually exclusive. isUserLimitBreached is tested first, so
473
542
  * reaching the >= 90 test structurally guarantees the limit is not exceeded
@@ -504,89 +573,39 @@ const EditionLabel = (props) => {
504
573
  canAddMoreUsers,
505
574
  seatUsageDisplayPercent,
506
575
  ]);
507
- /*
508
- * Copy containing apostrophes lives in string literals rather than JSX text,
509
- * because react/no-unescaped-entities is enforced in this repo.
510
- */
511
- const seatAdvisoryTitle = useMemo(() => {
512
- if (seatTone === "breached") {
513
- const over = Math.abs(seatsRemaining || 0);
514
- /*
515
- * Reachable now that "every seat taken" is a breach in its own right:
516
- * an installation sitting exactly on its limit is not "0 users over".
517
- */
518
- if (over === 0) {
519
- return "Every licensed seat is in use";
520
- }
521
- return over === 1
522
- ? "1 user over your licensed seats"
523
- : `${over.toLocaleString()} users over your licensed seats`;
576
+ const editionName = useMemo(() => {
577
+ if (!IS_ENTERPRISE_EDITION) {
578
+ return "Community Edition";
524
579
  }
525
- if (seatTone === "approaching") {
526
- if (seatsRemaining === 0) {
527
- return "Every licensed seat is in use";
528
- }
529
- return seatsRemaining === 1
530
- ? "Only 1 seat left on your license"
531
- : `Only ${(seatsRemaining || 0).toLocaleString()} seats left on your license`;
532
- }
533
- return "";
534
- }, [seatTone, seatsRemaining]);
535
- const seatAdvisoryBody = useMemo(() => {
536
- const limitText = (userLimit || 0).toLocaleString();
537
- const inUse = `${(effectiveUserCount || 0).toLocaleString()} of ${limitText}`;
538
- if (seatTone === "breached") {
539
- /*
540
- * When the limit is being enforced this is no longer advice, it is a
541
- * description of what the installation is already doing to invitations
542
- * and signups - so it says so plainly rather than suggesting an upgrade
543
- * as though adding people were still an option.
544
- */
545
- if (isSeatLimitEnforced && !canAddMoreUsers) {
546
- return `This installation is using ${inUse} licensed seats. New users cannot be invited, signed up or provisioned until a seat is freed up or the license is expanded.`;
547
- }
548
- return `This installation is using ${inUse} licensed seats. Expand your license so it covers everyone on the platform.`;
580
+ if (isConfigLoading) {
581
+ return "Enterprise Edition (Checking...)";
549
582
  }
550
- if (seatTone === "approaching") {
551
- return `You're using ${inUse} licensed seats. Anyone you add beyond ${limitText} puts this installation over its licensed limit — if more people are joining, it's worth talking to OneUptime about expanding now.`;
583
+ if (isUnlicensedTrial) {
584
+ return graceDaysLeftText
585
+ ? `Enterprise Edition (Trial, ${graceDaysLeftText})`
586
+ : "Enterprise Edition (Trial)";
552
587
  }
553
- return "";
554
- }, [
555
- seatTone,
556
- effectiveUserCount,
557
- userLimit,
558
- isSeatLimitEnforced,
559
- canAddMoreUsers,
560
- ]);
561
- const seatUsageAriaValueText = useMemo(() => {
562
- if (typeof seatUsageDisplayPercent !== "number") {
563
- return "";
588
+ if (isExpiredGrace) {
589
+ return "Enterprise Edition (License Expired, Grace Period)";
564
590
  }
565
- if (seatTone === "breached") {
566
- return `${seatUsageDisplayPercent}% of licensed seats in use — ${seatAdvisoryTitle}`;
591
+ if (licenseValid) {
592
+ return "Enterprise Edition";
593
+ }
594
+ if (licenseStatus === "expired") {
595
+ return "Enterprise Edition (License Expired)";
567
596
  }
568
- if (typeof seatsRemaining === "number") {
569
- return `${seatUsageDisplayPercent}% of licensed seats in use — ${seatsRemainingText}`;
597
+ if (licenseStatus === "invalid") {
598
+ return "Enterprise Edition (License Invalid)";
570
599
  }
571
- return `${seatUsageDisplayPercent}% of licensed seats in use`;
600
+ return "Enterprise Edition (License Required)";
572
601
  }, [
573
- seatUsageDisplayPercent,
574
- seatTone,
575
- seatAdvisoryTitle,
576
- seatsRemaining,
577
- seatsRemainingText,
602
+ isConfigLoading,
603
+ licenseValid,
604
+ licenseStatus,
605
+ isUnlicensedTrial,
606
+ isExpiredGrace,
607
+ graceDaysLeftText,
578
608
  ]);
579
- const editionName = useMemo(() => {
580
- if (!IS_ENTERPRISE_EDITION) {
581
- return "Community Edition";
582
- }
583
- if (isConfigLoading) {
584
- return "Enterprise Edition (Checking...)";
585
- }
586
- return licenseValid
587
- ? "Enterprise Edition"
588
- : "Enterprise Edition (License Required)";
589
- }, [isConfigLoading, licenseValid]);
590
609
  const indicatorColor = useMemo(() => {
591
610
  if (!IS_ENTERPRISE_EDITION) {
592
611
  return "bg-indigo-400";
@@ -600,19 +619,31 @@ const EditionLabel = (props) => {
600
619
  if (seatTone === "breached") {
601
620
  return "bg-red-500";
602
621
  }
603
- if (seatTone === "approaching") {
622
+ if (seatTone === "approaching" || licenseStatus === "grace") {
604
623
  return "bg-amber-500";
605
624
  }
606
625
  return "bg-emerald-500";
607
- }, [isConfigLoading, licenseValid, seatTone]);
626
+ }, [isConfigLoading, licenseValid, seatTone, licenseStatus]);
608
627
  const ctaLabel = useMemo(() => {
609
628
  if (!IS_ENTERPRISE_EDITION) {
610
- return "Learn more";
629
+ return showEditionMismatchNotice ? "Action needed" : "Learn more";
611
630
  }
612
631
  if (isConfigLoading) {
613
632
  return "Checking";
614
633
  }
634
+ if (isUnlicensedTrial) {
635
+ return "Add license";
636
+ }
637
+ if (isExpiredGrace) {
638
+ return "Renew license";
639
+ }
615
640
  if (!licenseValid) {
641
+ if (licenseStatus === "expired") {
642
+ return "Renew license";
643
+ }
644
+ if (licenseStatus === "invalid") {
645
+ return "Fix license";
646
+ }
616
647
  return "Validate license";
617
648
  }
618
649
  if (seatTone === "breached") {
@@ -622,24 +653,40 @@ const EditionLabel = (props) => {
622
653
  return "Seats nearly full";
623
654
  }
624
655
  return "View details";
625
- }, [isConfigLoading, licenseValid, seatTone]);
656
+ }, [
657
+ isConfigLoading,
658
+ licenseValid,
659
+ licenseStatus,
660
+ seatTone,
661
+ isUnlicensedTrial,
662
+ isExpiredGrace,
663
+ showEditionMismatchNotice,
664
+ ]);
626
665
  const pillTone = useMemo(() => {
627
- if (!IS_ENTERPRISE_EDITION || isConfigLoading) {
666
+ if (!IS_ENTERPRISE_EDITION) {
667
+ return showEditionMismatchNotice ? "warning" : "normal";
668
+ }
669
+ if (isConfigLoading) {
628
670
  return "normal";
629
671
  }
630
672
  if (seatTone === "breached" ||
631
- (!licenseValid && Boolean(globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseKey))) {
673
+ (!licenseValid &&
674
+ (Boolean(globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseKey) ||
675
+ licenseStatus === "expired" ||
676
+ licenseStatus === "invalid"))) {
632
677
  return "alerted";
633
678
  }
634
- if (seatTone === "approaching") {
679
+ if (seatTone === "approaching" || licenseStatus === "grace") {
635
680
  return "warning";
636
681
  }
637
682
  return "normal";
638
683
  }, [
639
684
  isConfigLoading,
640
685
  licenseValid,
686
+ licenseStatus,
641
687
  seatTone,
642
688
  globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseKey,
689
+ showEditionMismatchNotice,
643
690
  ]);
644
691
  const modalDescription = useMemo(() => {
645
692
  if (!IS_ENTERPRISE_EDITION) {
@@ -648,33 +695,17 @@ const EditionLabel = (props) => {
648
695
  if (isConfigLoading) {
649
696
  return "Checking your license with OneUptime...";
650
697
  }
698
+ if (isUnlicensedTrial) {
699
+ return "No license is installed yet. Enterprise features are available during the trial.";
700
+ }
701
+ if (isExpiredGrace) {
702
+ return "Your license has expired. Renew it before the grace period ends.";
703
+ }
651
704
  if (!licenseValid) {
652
- return "Validate your license key to activate Enterprise Edition.";
705
+ return "Add a valid license to turn single sign-on, SCIM and audit logging back on and make enterprise configuration editable.";
653
706
  }
654
707
  return "License, version, seat usage, and the instances covered by this key.";
655
- }, [isConfigLoading, licenseValid]);
656
- /*
657
- * The per-instance counts only exceed the unique total when someone actually
658
- * appears on more than one instance, and the component cannot know whether
659
- * they do — it receives per-instance counts and the unique total separately.
660
- * So the summation is stated as a possibility, and is dropped entirely when
661
- * there is no unique total to compare against. It names the unique user count
662
- * rather than "the total above", which would be ambiguous next to a hero
663
- * reading "119 / 120 seats".
664
- */
665
- const instanceOverlapText = useMemo(() => {
666
- const countingRule = `Seats are counted uniquely across all ${licenseInstances.length} instances that share this license — the same person on multiple instances uses one seat.`;
667
- if (typeof currentUserCount !== "number") {
668
- return countingRule;
669
- }
670
- return `${countingRule} Per-instance counts can therefore add up to more than the ${currentUserCount.toLocaleString()} unique ${currentUserCount === 1 ? "user" : "users"} counted above.`;
671
- }, [licenseInstances.length, currentUserCount]);
672
- const licenseKeyHelperText = useMemo(() => {
673
- if (isChangingLicense) {
674
- return "Enter the new enterprise license key and validate it to replace the current one. Your existing license stays active until the new key is validated.";
675
- }
676
- return "You have installed Enterprise Edition of OneUptime. You need to validate your license key. Need a license key? Contact our sales team at";
677
- }, [isChangingLicense]);
708
+ }, [isConfigLoading, licenseValid, isUnlicensedTrial, isExpiredGrace]);
678
709
  const communityFeatures = useMemo(() => {
679
710
  return [
680
711
  "Full OneUptime platform with incident response, status pages, and workflow automation.",
@@ -694,30 +725,16 @@ const EditionLabel = (props) => {
694
725
  "Audit logs and many more enterprise-focused features.",
695
726
  ];
696
727
  }, []);
728
+ /*
729
+ * The license manager clears its own messages (and abandons a license
730
+ * change in progress) when the dialog opens or closes.
731
+ */
697
732
  const openDialog = () => {
698
733
  setIsDialogOpen(true);
699
- setValidationError("");
700
- setSuccessMessage("");
701
734
  void fetchGlobalConfig();
702
735
  };
703
736
  const closeDialog = () => {
704
737
  setIsDialogOpen(false);
705
- setValidationError("");
706
- setSuccessMessage("");
707
- setIsChangingLicense(false);
708
- };
709
- const handleStartChangingLicense = () => {
710
- setIsChangingLicense(true);
711
- setValidationError("");
712
- setSuccessMessage("");
713
- setLicenseKeyInput("");
714
- licenseInputEditedRef.current = true;
715
- };
716
- const handleCancelChangingLicense = () => {
717
- setIsChangingLicense(false);
718
- setValidationError("");
719
- licenseInputEditedRef.current = false;
720
- setLicenseKeyInput((globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseKey) || "");
721
738
  };
722
739
  const handlePrimaryAction = () => {
723
740
  if (typeof window !== "undefined") {
@@ -725,134 +742,11 @@ const EditionLabel = (props) => {
725
742
  }
726
743
  closeDialog();
727
744
  };
728
- const buildSeatExpansionMailto = () => {
729
- const subject = seatTone === "breached"
730
- ? "Expand OneUptime license - over seat limit"
731
- : "Expand OneUptime license - nearly out of seats";
732
- const body = [
733
- "Hi OneUptime,",
734
- "",
735
- "We would like to expand the seat count on our enterprise license.",
736
- "",
737
- `Company: ${(globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseCompanyName) || "Not specified"}`,
738
- `Seats in use: ${(effectiveUserCount || 0).toLocaleString()} of ${(userLimit || 0).toLocaleString()}`,
739
- `Instances on this license: ${licenseInstances.length}`,
740
- ].join("\n");
741
- return `${SALES_MAILTO_URL}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
742
- };
743
- const handleRequestMoreSeats = () => {
744
- if (typeof window !== "undefined") {
745
- window.location.href = buildSeatExpansionMailto();
746
- }
747
- };
748
- const runLicenseValidation = useCallback(async (key, setLoading) => {
749
- const trimmedKey = key.trim();
750
- if (!trimmedKey) {
751
- setValidationError("Please enter a license key before validating.");
752
- setSuccessMessage("");
753
- return;
754
- }
755
- setValidationError("");
756
- setSuccessMessage("");
757
- setLoading(true);
758
- try {
759
- const validationUrl = URL.fromURL(APP_API_URL).addRoute(new Route("/global-config/license"));
760
- const response = await API.fetch({
761
- method: HTTPMethod.POST,
762
- url: validationUrl,
763
- data: {
764
- licenseKey: trimmedKey,
765
- },
766
- });
767
- if (!response.isSuccess()) {
768
- throw response;
769
- }
770
- const payload = response.data;
771
- licenseInputEditedRef.current = false;
772
- setLicenseKeyInput(payload["licenseKey"] || trimmedKey);
773
- setSuccessMessage("License validated successfully.");
774
- setIsChangingLicense(false);
775
- await fetchGlobalConfig();
776
- }
777
- catch (err) {
778
- setValidationError(API.getFriendlyMessage(err));
779
- }
780
- finally {
781
- setLoading(false);
782
- }
783
- }, [fetchGlobalConfig]);
784
- const handleValidateClick = () => {
785
- if (isValidating) {
786
- return;
787
- }
788
- void runLicenseValidation(licenseKeyInput, setIsValidating);
789
- };
790
- /*
791
- * Re-ask oneuptime.com about the license this installation already holds.
792
- *
793
- * The seat limit and the expiry are changed on oneuptime.com, and the daily
794
- * report job is what normally carries them here - so without this, an
795
- * administrator who has just bought seats waits up to a day before the
796
- * installation stops refusing invitations. Deliberately sends no key: the
797
- * server refreshes the stored one.
798
- */
799
- const handleRefreshLicense = useCallback(() => {
800
- if (isRefreshingLicense) {
801
- return;
802
- }
803
- const refresh = async () => {
804
- setValidationError("");
805
- setSuccessMessage("");
806
- setIsRefreshingLicense(true);
807
- try {
808
- const refreshUrl = URL.fromURL(APP_API_URL).addRoute(new Route("/global-config/license/refresh"));
809
- const response = await API.fetch({
810
- method: HTTPMethod.POST,
811
- url: refreshUrl,
812
- });
813
- if (!response.isSuccess()) {
814
- throw response;
815
- }
816
- applySeatEnforcementPayload(response.data);
817
- setSuccessMessage("License refreshed from OneUptime.");
818
- /*
819
- * The POST already returned the new terms, but the modal renders from
820
- * what the GET reports - re-reading keeps one source of truth for the
821
- * whole dialog instead of two that can disagree.
822
- */
823
- await fetchGlobalConfig();
824
- }
825
- catch (err) {
826
- setValidationError(API.getFriendlyMessage(err));
827
- }
828
- finally {
829
- setIsRefreshingLicense(false);
830
- }
831
- };
832
- void refresh();
833
- }, [isRefreshingLicense, fetchGlobalConfig, applySeatEnforcementPayload]);
834
745
  const handleRetryFetch = () => {
835
746
  if (!isConfigLoading) {
836
747
  void fetchGlobalConfig();
837
748
  }
838
749
  };
839
- /*
840
- * Activating, replacing and refreshing the license are master-admin actions:
841
- * the license is instance-wide, and its seat limit is what UserService
842
- * enforces against, so being able to rewrite it is being able to raise the
843
- * ceiling on the whole installation. The server enforces this
844
- * (MasterAdminAuthorization on both POSTs); hiding the controls here just
845
- * stops everyone else being offered a button that can only fail.
846
- *
847
- * False for a signed-out visitor, which is what keeps the license key input
848
- * off the login page where this same component renders the edition pill.
849
- */
850
- const canManageLicense = useMemo(() => {
851
- return Boolean(UserUtil.isMasterAdmin());
852
- }, []);
853
- const showLicenseKeyInput = IS_ENTERPRISE_EDITION &&
854
- canManageLicense &&
855
- (!licenseValid || isChangingLicense);
856
750
  /*
857
751
  * Someone who cannot fix the license still needs to be told why the
858
752
  * installation is behaving like an unlicensed one, and who can fix it.
@@ -862,12 +756,18 @@ const EditionLabel = (props) => {
862
756
  !isConfigLoading &&
863
757
  !licenseValid &&
864
758
  !canManageLicense;
865
- const shouldShowEnterpriseValidationButton = showLicenseKeyInput;
866
759
  /*
867
760
  * Collapses the !configError && !isConfigLoading && licenseValid chain that
868
- * gates the stat strip, the seat card and the instances card.
761
+ * gates the stat strip here, and the license manager's seat card, instances
762
+ * card and footer actions.
869
763
  */
870
- const showLicenseDetails = IS_ENTERPRISE_EDITION && !configError && !isConfigLoading && licenseValid;
764
+ const showLicenseDetails = IS_ENTERPRISE_EDITION &&
765
+ !configError &&
766
+ !isConfigLoading &&
767
+ licenseValid &&
768
+ !isUnlicensedTrial;
769
+ // Status notices describe the license, so they wait for it to load.
770
+ const showLicenseStatusNotices = IS_ENTERPRISE_EDITION && !configError && !isConfigLoading;
871
771
  /*
872
772
  * The evaluation notice rides along with the license details: it is only
873
773
  * meaningful once there is a valid license to describe, and it sits at the
@@ -879,7 +779,10 @@ const EditionLabel = (props) => {
879
779
  * admin sees "Evaluation" without opening the modal.
880
780
  */
881
781
  const showEvaluationTag = IS_ENTERPRISE_EDITION && licenseValid && isEvaluationLicense;
882
- const showEnterpriseFeatureList = IS_ENTERPRISE_EDITION && !configError && !isConfigLoading && !licenseValid;
782
+ const showEnterpriseFeatureList = IS_ENTERPRISE_EDITION &&
783
+ !configError &&
784
+ !isConfigLoading &&
785
+ (!licenseValid || isUnlicensedTrial);
883
786
  /*
884
787
  * Gated on neither edition nor licenseValid: which build you run, and
885
788
  * whether a newer one exists, is a property of the installation rather than
@@ -888,32 +791,6 @@ const EditionLabel = (props) => {
888
791
  * anonymous visitor on the login page falls out here.
889
792
  */
890
793
  const showVersionCard = !configError && !isConfigLoading && Boolean(currentVersion);
891
- /*
892
- * GlobalConfigAPI returns userLimit and currentUserCount to anonymous callers
893
- * because the same route serves the signed-out login page, and gates only
894
- * licenseKey, token, instances and instanceId on isAuthenticatedUser. Gate
895
- * the sales ask on a field the server does redact, so a signed-out visitor is
896
- * never shown an administrator-targeted CTA.
897
- */
898
- const canSeeLicenseAdmin = Boolean(globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseKey);
899
- const modalSubmitButtonText = IS_ENTERPRISE_EDITION
900
- ? shouldShowEnterpriseValidationButton
901
- ? "Validate License"
902
- : undefined
903
- : "Talk to Sales";
904
- const modalOnSubmit = IS_ENTERPRISE_EDITION
905
- ? shouldShowEnterpriseValidationButton
906
- ? handleValidateClick
907
- : undefined
908
- : handlePrimaryAction;
909
- const modalIsLoading = IS_ENTERPRISE_EDITION && shouldShowEnterpriseValidationButton
910
- ? isValidating
911
- : false;
912
- const modalDisableSubmitButton = IS_ENTERPRISE_EDITION
913
- ? shouldShowEnterpriseValidationButton
914
- ? !licenseKeyInput.trim() || isValidating || isConfigLoading
915
- : undefined
916
- : false;
917
794
  const modalRightElement = showLicenseDetails ? (React.createElement("span", { className: `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-0.5 text-xs font-medium ${seatTone === "breached"
918
795
  ? "border-red-200 bg-red-50 text-red-700"
919
796
  : seatTone === "approaching"
@@ -928,10 +805,9 @@ const EditionLabel = (props) => {
928
805
  ? "Seat limit exceeded"
929
806
  : seatTone === "approaching"
930
807
  ? "Seats nearly full"
931
- : "License active")) : undefined;
932
- const modalLeftFooterElement = showLicenseDetails && !isChangingLicense && canManageLicense ? (React.createElement("div", { className: "flex flex-wrap items-center gap-2" },
933
- React.createElement(Button, { title: "Refresh license", icon: IconProp.Refresh, buttonStyle: ButtonStyleType.NORMAL, onClick: handleRefreshLicense, isLoading: isRefreshingLicense, disabled: isRefreshingLicense || isConfigLoading, tooltip: "Fetch the latest seat limit and expiry for this license from OneUptime.", dataTestId: "refresh-enterprise-license", className: "!mt-0 md:!ml-0" }),
934
- React.createElement(Button, { title: "Change license key", icon: IconProp.Edit, buttonStyle: ButtonStyleType.NORMAL, onClick: handleStartChangingLicense, disabled: isRefreshingLicense, className: "!mt-0 md:!ml-0" }))) : undefined;
808
+ : isExpiredGrace
809
+ ? "Grace period"
810
+ : "License active")) : undefined;
935
811
  const pillClassName = pillTone === "alerted"
936
812
  ? "group inline-flex items-center gap-2 rounded-full border border-red-200 bg-red-50 px-3 py-1 text-xs font-medium text-red-700 shadow-sm transition hover:border-red-300 hover:bg-red-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-400"
937
813
  : pillTone === "warning"
@@ -997,19 +873,123 @@ const EditionLabel = (props) => {
997
873
  React.createElement("a", { href: `${SALES_MAILTO_URL}?subject=${encodeURIComponent("Moving our OneUptime evaluation to production")}`, className: "inline-flex items-center gap-1.5 rounded-md bg-violet-600 px-3 py-1.5 text-xs font-medium text-white shadow-sm transition hover:bg-violet-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-violet-400 focus-visible:ring-offset-2" },
998
874
  React.createElement(Icon, { icon: IconProp.Email, className: "h-3 w-3 shrink-0 text-white" }),
999
875
  "Talk to sales about production")))))) : null;
1000
- return (React.createElement(React.Fragment, null,
1001
- React.createElement("button", { type: "button", onClick: openDialog, className: `${pillClassName} ${props.className ? props.className : ""}`, "aria-label": `${editionName}${showEvaluationTag ? ", Evaluation" : ""}, ${ctaLabel}` },
1002
- pillTone !== "normal" && (React.createElement(Icon, { icon: pillTone === "alerted"
1003
- ? IconProp.Alert
1004
- : IconProp.ExclaimationCircle, className: `h-3 w-3 ${pillTone === "alerted" ? "text-red-600" : "text-amber-600"}` })),
1005
- React.createElement("span", { className: `h-2 w-2 rounded-full transition group-hover:scale-110 ${indicatorColor}` }),
1006
- React.createElement("span", { className: "tracking-wide" }, editionName),
1007
- showEvaluationTag && (React.createElement("span", { className: "inline-flex items-center rounded-full bg-violet-100 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-violet-700" }, "Evaluation")),
1008
- React.createElement("span", { className: pillCtaTextClassName }, ctaLabel)),
1009
- isDialogOpen && (React.createElement(Modal, { title: editionName, description: modalDescription, rightElement: modalRightElement, submitButtonText: modalSubmitButtonText, closeButtonText: "Close", onClose: closeDialog, onSubmit: modalOnSubmit, modalWidth: ModalWidth.Medium, isLoading: modalIsLoading, disableSubmitButton: modalDisableSubmitButton, isBodyLoading: IS_ENTERPRISE_EDITION ? isConfigLoading : false, leftFooterElement: modalLeftFooterElement },
876
+ /*
877
+ * What the license status means for this installation, in plain words.
878
+ * Before the lapse (the trial, the grace period) every branch warns what
879
+ * will stop and when (LICENSE_LAPSE_CONSEQUENCES); after it, what has
880
+ * stopped (LICENSE_LAPSED_STATE).
881
+ */
882
+ const licenseStatusNoticeElement = (() => {
883
+ if (!showLicenseStatusNotices) {
884
+ return null;
885
+ }
886
+ if (isUnlicensedTrial) {
887
+ return (React.createElement("section", { role: "status", "data-testid": "enterprise-license-trial-notice", className: "rounded-xl border border-amber-200 bg-amber-50 p-5" },
888
+ React.createElement("h4", { className: "text-sm font-semibold text-amber-900" }, graceDaysLeftText
889
+ ? `Enterprise Edition trial: ${graceDaysLeftText}`
890
+ : "Enterprise Edition trial"),
891
+ React.createElement("p", { className: "mt-1 text-xs leading-relaxed text-amber-800" }, `${hasLicenseInstalled
892
+ ? "A license is installed, but this installation cannot tell whether it is still current, so it is treating this as an unlicensed install."
893
+ : "No Enterprise license is installed."} Enterprise features stay fully available${graceEndsAtText ? ` until ${graceEndsAtText}` : ""}, counted from the first time this installation ran the Enterprise Edition. ${TRIAL_ENFORCEMENT_SUMMARY}`),
894
+ hasLicenseInstalled && licenseMessage && (React.createElement("p", { "data-testid": "enterprise-license-status-message", className: "mt-2 text-xs leading-relaxed text-amber-800" }, licenseMessage)),
895
+ React.createElement("p", { className: "mt-2 text-xs leading-relaxed text-amber-800" }, canManageLicense
896
+ ? hasLicenseInstalled
897
+ ? "Re-activate the license below before the trial ends, or let the daily license sync fetch its expiry from OneUptime, to keep single sign-on, SCIM and audit logging running and enterprise configuration editable."
898
+ : "Add a license below before the trial ends to keep single sign-on, SCIM and audit logging running and enterprise configuration editable."
899
+ : hasLicenseInstalled
900
+ ? "Ask a master admin of this installation to re-activate the license."
901
+ : "Ask a master admin of this installation to add a license.")));
902
+ }
903
+ if (isExpiredGrace) {
904
+ return (React.createElement("section", { role: "status", "data-testid": "enterprise-license-grace-notice", className: "rounded-xl border border-amber-200 bg-amber-50 p-5" },
905
+ React.createElement("h4", { className: "text-sm font-semibold text-amber-900" }, graceEndsAtText
906
+ ? `License expired, grace period until ${graceEndsAtText}`
907
+ : "License expired, grace period"),
908
+ React.createElement("p", { className: "mt-1 text-xs leading-relaxed text-amber-800" }, `The Enterprise license expired${licenseExpiresAtText ? ` on ${licenseExpiresAtText}` : ""}. Every enterprise feature keeps working until the grace period ends${graceDaysLeftText ? ` (${graceDaysLeftText})` : ""}. ${GRACE_ENFORCEMENT_SUMMARY}`),
909
+ React.createElement("p", { className: "mt-2 text-xs leading-relaxed text-amber-800" }, canManageLicense
910
+ ? "Renew the license with OneUptime, then refresh it here."
911
+ : "Ask a master admin of this installation to renew the license.")));
912
+ }
913
+ if (!licenseValid) {
914
+ const title = licenseStatus === "expired"
915
+ ? "License expired"
916
+ : licenseStatus === "invalid"
917
+ ? "License not valid"
918
+ : "No valid license";
919
+ const reason = licenseStatus === "expired"
920
+ ? `The Enterprise license expired${licenseExpiresAtText ? ` on ${licenseExpiresAtText}` : ""} and its grace period is over.`
921
+ : licenseStatus === "invalid"
922
+ ? licenseMessage ||
923
+ "The stored license could not be verified. Validate the license again."
924
+ : hasLicenseInstalled
925
+ ? "A license is installed, but this installation cannot confirm that it is current, so nothing is licensed by it."
926
+ : "This installation has no valid Enterprise license.";
927
+ /*
928
+ * Whatever the license client said, for EVERY status and not only
929
+ * "invalid".
930
+ *
931
+ * The reason above is this component's own reading of a status code, and
932
+ * a status code cannot carry a diagnosis: "missing" is both "no license"
933
+ * and "a license whose expiry was never recorded, re-activate it or let
934
+ * the daily sync fetch it" (the license client's
935
+ * classifyUnverifiedWithoutExpiry). The second one is the whole point of
936
+ * the message field, and it used to be rendered nowhere but here in the
937
+ * "invalid" branch - which that state deliberately no longer is.
938
+ *
939
+ * Skipped when the reason already IS the message ("invalid"), so a master
940
+ * admin is never shown the same sentence twice.
941
+ */
942
+ const explanation = licenseMessage && licenseMessage !== reason ? licenseMessage : "";
943
+ return (React.createElement("section", { role: "alert", "data-testid": "enterprise-license-required-notice", className: "rounded-xl border border-red-200 bg-red-50 p-5" },
944
+ React.createElement("h4", { className: "text-sm font-semibold text-red-900" }, title),
945
+ explanation && (React.createElement("p", { "data-testid": "enterprise-license-status-message", className: "mt-1 text-xs leading-relaxed text-red-800" }, explanation)),
946
+ React.createElement("p", { className: "mt-1 text-xs leading-relaxed text-red-800" }, `${reason} ${LICENSE_LAPSED_STATE}`)));
947
+ }
948
+ if (isUnverifiedLegacyLicense && canManageLicense) {
949
+ return (React.createElement("section", { "data-testid": "enterprise-license-unverified-notice", className: "rounded-xl border border-gray-200 bg-gray-50 p-4" },
950
+ React.createElement("h4", { className: "text-sm font-semibold text-gray-900" }, "Legacy license"),
951
+ React.createElement("p", { className: "mt-1 text-xs leading-relaxed text-gray-600" }, "This license was issued before OneUptime signed its licenses, so this installation cannot verify it offline. It keeps working; refreshing the license replaces it with a signed one once OneUptime issues them.")));
952
+ }
953
+ return null;
954
+ })();
955
+ /*
956
+ * What the license manager needs to know, all of it read from the license
957
+ * GET (see LicenseManager.ts).
958
+ */
959
+ const licenseSnapshot = {
960
+ payload: licensePayload,
961
+ isLoading: isConfigLoading,
962
+ loadError: configError,
963
+ canManageLicense: canManageLicense,
964
+ licenseValid: licenseValid,
965
+ isUnlicensedTrial: isUnlicensedTrial,
966
+ showLicenseDetails: showLicenseDetails,
967
+ companyName: (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseCompanyName) || "",
968
+ licenseKey: (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseKey) || "",
969
+ userLimit: userLimit,
970
+ currentUserCount: currentUserCount,
971
+ effectiveUserCount: effectiveUserCount,
972
+ isSeatLimitEnforced: isSeatLimitEnforced,
973
+ canAddMoreUsers: canAddMoreUsers,
974
+ seatTone: seatTone,
975
+ seatUsageDisplayPercent: seatUsageDisplayPercent,
976
+ currentVersion: currentVersion,
977
+ latestVersion: latestVersion,
978
+ hasComparableVersion: hasComparableVersion,
979
+ };
980
+ /*
981
+ * The dialog, with whatever the license manager adds to it: its messages,
982
+ * the seat usage and instance cards, the activation input and the footer
983
+ * actions. With no manager (the Community Edition, the login page) the
984
+ * parts are empty and the dialog shows the license status only.
985
+ */
986
+ const renderDialog = (parts) => {
987
+ if (!isDialogOpen) {
988
+ return React.createElement(React.Fragment, null);
989
+ }
990
+ return (React.createElement(Modal, { title: editionName, description: modalDescription, rightElement: modalRightElement, submitButtonText: IS_ENTERPRISE_EDITION ? parts.submitButtonText : "Talk to Sales", closeButtonText: "Close", onClose: closeDialog, onSubmit: IS_ENTERPRISE_EDITION ? parts.onSubmit : handlePrimaryAction, modalWidth: ModalWidth.Medium, isLoading: IS_ENTERPRISE_EDITION ? Boolean(parts.isSubmitting) : false, disableSubmitButton: IS_ENTERPRISE_EDITION ? parts.disableSubmitButton : false, isBodyLoading: IS_ENTERPRISE_EDITION ? isConfigLoading : false, leftFooterElement: IS_ENTERPRISE_EDITION ? parts.leftFooterElement : undefined },
1010
991
  React.createElement("div", { className: "space-y-4 text-sm text-gray-600" }, IS_ENTERPRISE_EDITION ? (React.createElement(React.Fragment, null,
1011
- !configError && successMessage && (React.createElement(Alert, { type: AlertType.SUCCESS, title: successMessage })),
1012
- !configError && validationError && !showLicenseKeyInput && (React.createElement(Alert, { type: AlertType.DANGER, title: validationError })),
992
+ parts.messages,
1013
993
  configError && (React.createElement("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4" },
1014
994
  React.createElement("div", { className: "flex items-start gap-2.5" },
1015
995
  React.createElement(Icon, { icon: IconProp.Alert, className: "mt-0.5 h-4 w-4 shrink-0 text-red-600" }),
@@ -1019,6 +999,7 @@ const EditionLabel = (props) => {
1019
999
  React.createElement("div", { className: "mt-3" },
1020
1000
  React.createElement(Button, { title: "Try again", buttonStyle: ButtonStyleType.DANGER, onClick: handleRetryFetch, isLoading: isConfigLoading, className: "!mt-0 md:!ml-0" })))))),
1021
1001
  evaluationNoticeElement,
1002
+ licenseStatusNoticeElement,
1022
1003
  showLicenseDetails && (React.createElement("dl", { className: "grid grid-cols-1 gap-px overflow-hidden rounded-xl border border-gray-200 bg-gray-200 sm:grid-cols-2" },
1023
1004
  React.createElement("div", { className: "min-w-0 bg-white px-4 py-3" },
1024
1005
  React.createElement("dt", { className: "text-[11px] font-medium uppercase tracking-wide text-gray-500" }, "Licensed to"),
@@ -1027,187 +1008,28 @@ const EditionLabel = (props) => {
1027
1008
  React.createElement("dt", { className: "text-[11px] font-medium uppercase tracking-wide text-gray-500" }, "Expires"),
1028
1009
  React.createElement("dd", { className: "mt-1 text-sm font-medium tabular-nums text-gray-900" }, licenseExpiresAtText || "—")))),
1029
1010
  versionCardElement,
1030
- showLicenseDetails && (React.createElement("section", { "aria-labelledby": "edition-seats-heading", className: "rounded-xl border border-gray-200 bg-white p-5" },
1031
- React.createElement("div", { className: "flex items-start justify-between gap-4" },
1032
- React.createElement("div", { className: "min-w-0" },
1033
- React.createElement("h4", { id: "edition-seats-heading", className: "text-sm font-semibold text-gray-900" }, "Licensed seats"),
1034
- React.createElement("p", { className: "mt-0.5 text-xs text-gray-500" }, "Unique users across every instance on this license.")),
1035
- seatTone !== "healthy" && (React.createElement("span", { className: `inline-flex shrink-0 items-center rounded-full px-2 py-0.5 text-xs font-medium ${seatTone === "breached"
1036
- ? "bg-red-100 text-red-700"
1037
- : "bg-amber-100 text-amber-800"}` }, seatTone === "breached"
1038
- ? "Limit exceeded"
1039
- : "Nearly full"))),
1040
- React.createElement("div", { className: "mt-4 flex items-end justify-between gap-4" },
1041
- React.createElement("p", { className: "flex items-baseline gap-1.5" },
1042
- React.createElement("span", { className: `text-3xl font-semibold leading-none tabular-nums ${typeof effectiveUserCount !== "number"
1043
- ? "text-gray-300"
1044
- : seatTone === "breached"
1045
- ? "text-red-700"
1046
- : "text-gray-900"}` }, typeof effectiveUserCount === "number"
1047
- ? effectiveUserCount.toLocaleString()
1048
- : "—"),
1049
- React.createElement("span", { className: "text-sm tabular-nums text-gray-500" },
1050
- " / ",
1051
- typeof userLimit === "number" && userLimit > 0
1052
- ? `${userLimit.toLocaleString()} seats`
1053
- : "unlimited")),
1054
- typeof seatsRemaining === "number" && (React.createElement("p", { className: `text-xs font-medium tabular-nums ${seatTone === "breached"
1055
- ? "text-red-700"
1056
- : seatTone === "approaching"
1057
- ? "text-amber-700"
1058
- : "text-gray-500"}` }, seatsRemainingText))),
1059
- typeof seatUsageDisplayPercent === "number" && (React.createElement("div", { className: "mt-2.5" },
1060
- React.createElement("div", { className: "h-2.5 w-full overflow-hidden rounded-full bg-gray-100", role: "progressbar", "aria-label": "Licensed seat usage", "aria-valuenow": seatUsageBarPercent, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuetext": seatUsageAriaValueText },
1061
- React.createElement("div", { className: `h-full rounded-full transition-all duration-300 ease-out ${seatTone === "breached"
1062
- ? "bg-red-500"
1063
- : seatTone === "approaching"
1064
- ? "bg-amber-500"
1065
- : "bg-emerald-500"}`, style: { width: `${seatUsageBarPercent}%` } })),
1066
- React.createElement("p", { className: "mt-1.5 text-xs tabular-nums text-gray-500" },
1067
- seatUsageDisplayPercent,
1068
- "% of licensed seats in use"))),
1069
- seatTone !== "healthy" && canSeeLicenseAdmin && (React.createElement("div", { role: seatTone === "breached" ? "alert" : "status", className: `mt-4 rounded-lg border p-3.5 ${seatTone === "breached"
1070
- ? "border-red-200 bg-red-50"
1071
- : "border-amber-200 bg-amber-50"}` },
1072
- React.createElement("div", { className: "flex items-start gap-2.5" },
1073
- React.createElement(Icon, { icon: seatTone === "breached"
1074
- ? IconProp.Alert
1075
- : IconProp.ExclaimationCircle, className: `mt-0.5 h-4 w-4 shrink-0 ${seatTone === "breached"
1076
- ? "text-red-600"
1077
- : "text-amber-600"}` }),
1078
- React.createElement("div", { className: "min-w-0 flex-1" },
1079
- React.createElement("h5", { className: `text-sm font-semibold ${seatTone === "breached"
1080
- ? "text-red-900"
1081
- : "text-amber-900"}` }, seatAdvisoryTitle),
1082
- React.createElement("p", { className: `mt-1 text-xs leading-relaxed ${seatTone === "breached"
1083
- ? "text-red-800"
1084
- : "text-amber-800"}` }, seatAdvisoryBody),
1085
- React.createElement("div", { className: "mt-3 flex flex-wrap items-center gap-x-3 gap-y-2" },
1086
- React.createElement(Button, { title: seatTone === "breached"
1087
- ? "Expand your license"
1088
- : "Request more seats", icon: IconProp.Email, buttonStyle: seatTone === "breached"
1089
- ? ButtonStyleType.DANGER
1090
- : ButtonStyleType.PRIMARY, onClick: handleRequestMoreSeats, className: "!mt-0 md:!ml-0" }),
1091
- React.createElement("span", { className: `text-[11px] ${seatTone === "breached"
1092
- ? "text-red-700"
1093
- : "text-amber-700"}` },
1094
- "Or email",
1095
- " ",
1096
- React.createElement("a", { href: SALES_MAILTO_URL, className: `font-medium underline ${seatTone === "breached"
1097
- ? "text-red-800 hover:text-red-900"
1098
- : "text-amber-900 hover:text-amber-950"}` }, SALES_EMAIL),
1099
- " ",
1100
- "directly.")))))),
1101
- React.createElement("div", { className: "mt-4 border-t border-gray-100 pt-3" },
1102
- React.createElement("p", { className: "text-xs text-gray-500" },
1103
- userCountUpdatedAtText
1104
- ? `Last reported to OneUptime on ${userCountUpdatedAtText}.`
1105
- : "User count has not been reported to OneUptime yet. The first report will be sent within 24 hours.",
1106
- isSeatLimitEnforced && (React.createElement(React.Fragment, null,
1107
- " ",
1108
- "This installation enforces the seat limit: users above it cannot be invited, signed up or provisioned.")),
1109
- isSeatLimitEnforced && canManageLicense && (React.createElement(React.Fragment, null,
1110
- " ",
1111
- "Bought more seats? Use \"Refresh license\" below to apply the new limit without waiting for the next daily report.")))))),
1112
- showLicenseDetails && licenseInstances.length > 0 && (React.createElement("section", { "aria-labelledby": "edition-instances-heading", className: "rounded-xl border border-gray-200 bg-white p-5" },
1113
- React.createElement("div", { className: "flex items-center justify-between gap-3" },
1114
- React.createElement("h4", { id: "edition-instances-heading", className: "text-sm font-semibold text-gray-900" }, "Instances on this license"),
1115
- React.createElement("span", { className: "shrink-0 rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium tabular-nums text-gray-600" },
1116
- licenseInstances.length,
1117
- " ",
1118
- licenseInstances.length === 1
1119
- ? "instance"
1120
- : "instances")),
1121
- React.createElement("p", { className: "mt-1 text-xs leading-relaxed text-gray-500" }, "Use the same license key on every instance you deploy (staging, production, and so on). Each instance reports its usage and the version it runs once a day."),
1122
- licenseInstances.length > 1 && (React.createElement("p", { className: "mt-1.5 text-xs leading-relaxed text-gray-500" }, instanceOverlapText)),
1123
- React.createElement("ul", { className: "mt-3 divide-y divide-gray-100 overflow-hidden rounded-lg border border-gray-200" }, licenseInstances.map((instance, index) => {
1124
- const isThisInstance = Boolean(thisInstanceId) &&
1125
- instance.instanceId === thisInstanceId;
1126
- /*
1127
- * The list is a snapshot of what each instance last
1128
- * reported, refreshed at most once a day. For the
1129
- * instance serving this page the server just told us
1130
- * what it is running right now, so prefer that —
1131
- * otherwise this row contradicts the "This
1132
- * installation" card above it for up to a day after
1133
- * an upgrade. hasComparableVersion keeps a dev build
1134
- * from rendering "vunknown".
1135
- */
1136
- const displayVersion = isThisInstance && hasComparableVersion
1137
- ? currentVersion
1138
- : instance.version;
1139
- /*
1140
- * Per-instance, so an admin can see which of their
1141
- * deployments are lagging rather than only the one
1142
- * they happen to be signed in to.
1143
- */
1144
- const isInstanceOutdated = VersionUtil.isUpdateAvailable({
1145
- currentVersion: displayVersion,
1146
- latestVersion: latestVersion,
1147
- });
1148
- return (React.createElement("li", { key: instance.instanceId || index, className: "flex items-center justify-between gap-4 bg-white px-3 py-2.5 transition-colors hover:bg-gray-50" },
1149
- React.createElement("div", { className: "min-w-0 flex-1" },
1150
- React.createElement("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1" },
1151
- React.createElement("span", { className: "min-w-0 max-w-full truncate text-sm font-medium text-gray-900", title: instance.host || "Unknown host" }, instance.host || "Unknown host"),
1152
- isThisInstance && (React.createElement("span", { className: "shrink-0 rounded-full bg-indigo-50 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-indigo-700" }, "This instance")),
1153
- displayVersion && (React.createElement("span", { className: `shrink-0 rounded-full px-1.5 py-0.5 text-[10px] font-medium tabular-nums ${isInstanceOutdated
1154
- ? "bg-amber-50 text-amber-800"
1155
- : "bg-gray-100 text-gray-600"}`, title: isInstanceOutdated
1156
- ? `Running v${displayVersion}. OneUptime v${latestVersion} is available.`
1157
- : `Running v${displayVersion}.` },
1158
- "v",
1159
- displayVersion))),
1160
- React.createElement("p", { className: "mt-0.5 truncate text-xs text-gray-500" },
1161
- formatInstanceReportedAt(instance.lastReportedAt),
1162
- isInstanceOutdated
1163
- ? " Update available."
1164
- : "")),
1165
- React.createElement("div", { className: "flex shrink-0 items-baseline justify-end gap-1" }, typeof instance.userCount === "number" ? (React.createElement(React.Fragment, null,
1166
- React.createElement("span", { className: "text-sm font-medium tabular-nums text-gray-900" }, instance.userCount.toLocaleString()),
1167
- React.createElement("span", { className: "text-xs text-gray-500" }, instance.userCount === 1
1168
- ? "user"
1169
- : "users"))) : (React.createElement("span", { className: "text-sm text-gray-400" }, "\u2014")))));
1170
- })))),
1171
- !configError &&
1172
- !isConfigLoading &&
1173
- !licenseValid &&
1174
- (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.enterpriseLicenseKey) && (React.createElement("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4" },
1175
- React.createElement("p", { className: "text-sm font-semibold text-red-900" }, "License validation required"),
1176
- React.createElement("p", { className: "mt-1 text-xs leading-relaxed text-red-800" }, "The stored license information could not be verified. Please validate the license key again."))),
1011
+ parts.usage,
1177
1012
  showLicenseAdminRequiredNotice && (React.createElement("section", { className: "rounded-xl border border-amber-200 bg-amber-50 p-5" },
1178
1013
  React.createElement("h4", { className: "text-sm font-semibold text-amber-900" }, "A master admin has to activate this license"),
1179
1014
  React.createElement("p", { className: "mt-1 text-xs leading-relaxed text-amber-800" }, "This installation does not have a valid enterprise license. Ask a master admin of this OneUptime installation to enter or refresh the license key from this dialog."))),
1180
- !configError && showLicenseKeyInput && (React.createElement("section", { className: "rounded-xl border border-gray-200 bg-white p-5" },
1181
- React.createElement("div", { className: "flex items-start justify-between gap-4" },
1182
- React.createElement("div", { className: "min-w-0" },
1183
- React.createElement("label", { htmlFor: "enterprise-license-key", className: "text-sm font-semibold text-gray-900" }, isChangingLicense
1184
- ? "New license key"
1185
- : "License key"),
1186
- React.createElement("p", { className: "mt-0.5 text-xs leading-relaxed text-gray-500" },
1187
- licenseKeyHelperText,
1188
- !isChangingLicense && (React.createElement(React.Fragment, null,
1189
- " ",
1190
- React.createElement("a", { href: SALES_MAILTO_URL, className: "font-medium text-indigo-600 hover:text-indigo-700" }, SALES_EMAIL),
1191
- ".")))),
1192
- isChangingLicense && (React.createElement(Button, { title: "Cancel", buttonStyle: ButtonStyleType.NORMAL, onClick: handleCancelChangingLicense, className: "!mt-0 shrink-0 md:!ml-0" }))),
1193
- React.createElement("div", { className: "mt-3" },
1194
- React.createElement(Input, { id: "enterprise-license-key", value: licenseKeyInput, onChange: (value) => {
1195
- setLicenseKeyInput(value);
1196
- licenseInputEditedRef.current = true;
1197
- }, placeholder: "Enter your enterprise license key", disableSpellCheck: true })),
1198
- validationError && (React.createElement("div", { className: "mt-3" },
1199
- React.createElement(Alert, { type: AlertType.DANGER, title: validationError }))))),
1015
+ parts.activation,
1200
1016
  showEnterpriseFeatureList && (React.createElement("section", { "aria-labelledby": "edition-features-heading", className: "rounded-xl border border-indigo-100 bg-gradient-to-br from-indigo-50 via-white to-white p-5" },
1201
1017
  React.createElement("h4", { id: "edition-features-heading", className: "text-sm font-semibold text-indigo-900" }, "What your license unlocks"),
1202
- React.createElement("p", { className: "mt-0.5 text-xs text-indigo-700" }, canManageLicense
1203
- ? "Validate your key above to turn these on immediately."
1204
- : "A master admin can activate the license to turn these on."),
1018
+ React.createElement("p", { className: "mt-0.5 text-xs text-indigo-700" },
1019
+ canManageLicense
1020
+ ? "A valid license that includes them keeps single sign-on, SCIM provisioning and audit logging running, enterprise configuration (SSO, SCIM, team compliance, audit log settings) editable and the enterprise admin dashboards unlocked."
1021
+ : "A master admin can add the license to keep single sign-on, SCIM provisioning and audit logging running and enterprise configuration editable.",
1022
+ " ",
1023
+ "Core monitoring never depends on it."),
1205
1024
  React.createElement("ul", { className: "mt-3 grid grid-cols-1 gap-2 sm:grid-cols-2" }, enterpriseFeatures.map((feature, index) => {
1206
1025
  return (React.createElement("li", { key: index, className: "flex items-start gap-2 rounded-lg border border-gray-100 bg-white px-3 py-2.5" },
1207
1026
  React.createElement("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-indigo-50" },
1208
1027
  React.createElement(Icon, { icon: IconProp.Check, className: "h-3 w-3 text-indigo-600" })),
1209
1028
  React.createElement("span", { className: "text-xs leading-snug text-gray-700" }, feature)));
1210
1029
  })))))) : (React.createElement(React.Fragment, null,
1030
+ showEditionMismatchNotice && (React.createElement("section", { role: "alert", "data-testid": "enterprise-edition-image-mismatch", className: "rounded-xl border border-amber-200 bg-amber-50 p-5" },
1031
+ React.createElement("h4", { className: "text-sm font-semibold text-amber-900" }, "This is the Community Edition image"),
1032
+ React.createElement("p", { className: "mt-1 text-xs leading-relaxed text-amber-800" }, "IS_ENTERPRISE_EDITION is set but this is the Community Edition image \u2014 switch to the enterprise image (the enterprise-* image tags) to use Enterprise Edition features. Until then they are off; everything else keeps working."))),
1211
1033
  versionCardElement,
1212
1034
  React.createElement("p", null, "You are running the Community Edition of OneUptime. Here is a quick comparison to help you decide if Enterprise is the right fit for your team."),
1213
1035
  React.createElement("div", { className: "grid gap-4 md:grid-cols-2" },
@@ -1227,7 +1049,25 @@ const EditionLabel = (props) => {
1227
1049
  React.createElement("span", { className: "leading-snug" }, feature)));
1228
1050
  })),
1229
1051
  React.createElement("p", { className: "mt-3 text-xs text-indigo-700" }, "Everything in Community plus white-glove onboarding, enterprise SLAs, and a partner dedicated to your reliability goals."))),
1230
- React.createElement("p", { className: "text-xs text-gray-500" }, "Ready to unlock enterprise capabilities? Click \"Talk to Sales\" to start the conversation."))))))));
1052
+ React.createElement("p", { className: "text-xs text-gray-500" }, "Ready to unlock enterprise capabilities? Click \"Talk to Sales\" to start the conversation."))))));
1053
+ };
1054
+ /*
1055
+ * Kept after every hook: oneuptime.com (billing enabled) bounds seats and
1056
+ * plans through subscriptions and shows no edition pill at all.
1057
+ */
1058
+ if (BILLING_ENABLED) {
1059
+ return React.createElement(React.Fragment, null);
1060
+ }
1061
+ return (React.createElement(React.Fragment, null,
1062
+ React.createElement("button", { type: "button", onClick: openDialog, className: `${pillClassName} ${props.className ? props.className : ""}`, "aria-label": `${editionName}${showEvaluationTag ? ", Evaluation" : ""}, ${ctaLabel}` },
1063
+ pillTone !== "normal" && (React.createElement(Icon, { icon: pillTone === "alerted"
1064
+ ? IconProp.Alert
1065
+ : IconProp.ExclaimationCircle, className: `h-3 w-3 ${pillTone === "alerted" ? "text-red-600" : "text-amber-600"}` })),
1066
+ React.createElement("span", { className: `h-2 w-2 rounded-full transition group-hover:scale-110 ${indicatorColor}` }),
1067
+ React.createElement("span", { className: "tracking-wide" }, editionName),
1068
+ showEvaluationTag && (React.createElement("span", { className: "inline-flex items-center rounded-full bg-violet-100 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-violet-700" }, "Evaluation")),
1069
+ React.createElement("span", { className: pillCtaTextClassName }, ctaLabel)),
1070
+ LicenseManager ? (React.createElement(LicenseManager, { license: licenseSnapshot, isDialogOpen: isDialogOpen, reloadLicense: fetchGlobalConfig, applySeatEnforcement: applySeatEnforcementPayload, renderDialog: renderDialog })) : (renderDialog({}))));
1231
1071
  };
1232
1072
  export default EditionLabel;
1233
1073
  //# sourceMappingURL=EditionLabel.js.map