@oneuptime/common 13.0.8 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +23 -15
  2. package/Models/DatabaseModels/GlobalConfig.ts +24 -0
  3. package/Models/DatabaseModels/Monitor.ts +3 -2
  4. package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
  5. package/Scripts/generate-service-worker.js +65 -4
  6. package/Server/API/BaseAPI.ts +26 -5
  7. package/Server/API/GlobalConfigAPI.ts +228 -462
  8. package/Server/API/ProjectOIDC.ts +25 -16
  9. package/Server/API/ProjectSSO.ts +24 -16
  10. package/Server/API/StatusPageAPI.ts +100 -42
  11. package/Server/API/TeamMemberAPI.ts +10 -1
  12. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  13. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  14. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  15. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  16. package/Server/EnvironmentConfig.ts +147 -2
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  19. package/Server/Middleware/UserAuthorization.ts +38 -16
  20. package/Server/Services/AuditLogService.ts +84 -1079
  21. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  22. package/Server/Services/GlobalConfigService.ts +80 -1
  23. package/Server/Services/MonitorProbeService.ts +116 -20
  24. package/Server/Services/MonitorService.ts +56 -4
  25. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  26. package/Server/Services/MonitorTemplateService.ts +26 -0
  27. package/Server/Services/ProjectService.ts +284 -0
  28. package/Server/Services/RumSessionPinService.ts +25 -6
  29. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  30. package/Server/Services/StatusPageService.ts +78 -1
  31. package/Server/Services/TeamMemberService.ts +11 -0
  32. package/Server/Services/TeamService.ts +10 -0
  33. package/Server/Services/UserService.ts +10 -17
  34. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  35. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  36. package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
  37. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  38. package/Server/Types/Markdown.ts +41 -0
  39. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  40. package/Server/Utils/EditionEnforcement.ts +233 -0
  41. package/Server/Utils/FrontendEnvironment.ts +41 -2
  42. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  43. package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
  44. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  45. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
  46. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  47. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  48. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  49. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  50. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  51. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  52. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  53. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  54. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  55. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  56. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  57. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  58. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  59. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  60. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
  61. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  62. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  63. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
  64. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  65. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  66. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  67. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  68. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  69. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  70. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  71. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  72. package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
  73. package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
  74. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  75. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  76. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  77. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  78. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  79. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  80. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  81. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  82. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  83. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  84. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  85. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  86. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  87. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  88. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  89. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  90. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  91. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  92. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  93. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  94. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  95. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  96. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  97. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  98. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  99. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  100. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  101. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  102. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  103. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  104. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  105. package/Tests/Server/Types/Markdown.test.ts +181 -0
  106. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  107. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  108. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  109. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  110. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  111. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  112. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
  113. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  114. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  115. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  116. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  117. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  118. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  119. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  120. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
  121. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  122. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  123. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  124. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  125. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  126. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  127. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  128. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  129. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  130. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  131. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  132. package/Types/API/Hostname.ts +63 -16
  133. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  134. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  135. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  136. package/Types/Database/CanonicalModelInstance.ts +102 -0
  137. package/Types/Database/TableColumn.ts +20 -6
  138. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  139. package/Types/IP/IPv6.ts +6 -1
  140. package/Types/Monitor/MonitorStep.ts +6 -1
  141. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  142. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  143. package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
  144. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  145. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  146. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  147. package/UI/Components/ModelList/ModelList.tsx +11 -1
  148. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  149. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  150. package/UI/esbuild-enterprise.js +263 -0
  151. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  152. package/Utils/HostAddressUtil.ts +94 -0
  153. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  154. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  155. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  156. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  157. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  158. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  159. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  160. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  161. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  162. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  163. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  164. package/build/dist/Server/API/BaseAPI.js +26 -5
  165. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  166. package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
  167. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  168. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  169. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  170. package/build/dist/Server/API/ProjectSSO.js +24 -16
  171. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  172. package/build/dist/Server/API/StatusPageAPI.js +93 -43
  173. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  174. package/build/dist/Server/API/TeamMemberAPI.js +8 -1
  175. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  176. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  177. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  178. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  179. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  180. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  181. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  182. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  183. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  184. package/build/dist/Server/EnvironmentConfig.js +84 -2
  185. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  186. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  187. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  188. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  190. package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
  191. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  192. package/build/dist/Server/Services/AuditLogService.js +71 -740
  193. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  194. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  195. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  196. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  197. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  198. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  199. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  200. package/build/dist/Server/Services/MonitorService.js +45 -4
  201. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  202. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  203. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  204. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  205. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  206. package/build/dist/Server/Services/ProjectService.js +214 -0
  207. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  208. package/build/dist/Server/Services/RumSessionPinService.js +24 -6
  209. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  210. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  211. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  212. package/build/dist/Server/Services/StatusPageService.js +71 -0
  213. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  214. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  215. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  216. package/build/dist/Server/Services/TeamService.js +9 -0
  217. package/build/dist/Server/Services/TeamService.js.map +1 -1
  218. package/build/dist/Server/Services/UserService.js +10 -16
  219. package/build/dist/Server/Services/UserService.js.map +1 -1
  220. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  221. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  222. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  223. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  224. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
  225. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  226. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  227. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  228. package/build/dist/Server/Types/Markdown.js +31 -0
  229. package/build/dist/Server/Types/Markdown.js.map +1 -1
  230. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  231. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  232. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  233. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  234. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  235. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  236. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  237. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  238. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
  239. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  240. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  241. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  242. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
  243. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  245. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  246. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  247. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  248. package/build/dist/Types/API/Hostname.js +59 -11
  249. package/build/dist/Types/API/Hostname.js.map +1 -1
  250. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  251. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  252. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  253. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  254. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  255. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  256. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  257. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  258. package/build/dist/Types/Database/TableColumn.js +19 -6
  259. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  260. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  261. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  262. package/build/dist/Types/IP/IPv6.js +6 -1
  263. package/build/dist/Types/IP/IPv6.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  265. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  266. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  267. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  268. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  269. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
  270. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  271. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  272. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  273. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  274. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  275. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  276. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  277. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  278. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  279. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  280. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  281. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  282. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  283. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  284. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  285. package/build/dist/Utils/HostAddressUtil.js +80 -0
  286. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  287. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  288. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  289. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  290. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  291. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  292. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  293. package/jest.config.json +3 -1
  294. package/package.json +1 -1
  295. package/test-setup.sh +103 -34
  296. package/tsconfig.json +13 -0
  297. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  298. package/Server/API/TeamComplianceAPI.ts +0 -158
  299. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  300. package/Server/Services/TeamComplianceService.ts +0 -733
  301. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  302. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  303. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  304. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  305. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
  306. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  307. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  308. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
  309. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  310. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  311. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  312. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  313. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  314. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  315. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  316. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  317. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  318. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  319. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  320. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  321. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  322. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  323. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  324. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  325. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  326. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  327. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  328. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  329. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  330. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  331. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  332. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  333. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -1,925 +0,0 @@
1
- import EnterpriseLicense from "../../Models/DatabaseModels/EnterpriseLicense";
2
- import EnterpriseLicenseInstance from "../../Models/DatabaseModels/EnterpriseLicenseInstance";
3
- import BadDataException from "../../Types/Exception/BadDataException";
4
- import PartialEntity from "../../Types/Database/PartialEntity";
5
- import EnterpriseLicenseInstanceSummary from "../../Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary";
6
- import EnterpriseLicenseUsageSnapshot from "../../Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot";
7
- import EnterpriseLicenseUserCountSource from "../../Types/EnterpriseLicense/EnterpriseLicenseUserCountSource";
8
- import EnterpriseLicenseUsageUtil from "../../Utils/EnterpriseLicense/EnterpriseLicenseUsage";
9
- import VersionUtil from "../../Utils/VersionUtil";
10
- import LIMIT_MAX from "../../Types/Database/LimitMax";
11
- import ObjectID from "../../Types/ObjectID";
12
- import PositiveNumber from "../../Types/PositiveNumber";
13
- import { JSONObject } from "../../Types/JSON";
14
- import SortOrder from "../../Types/BaseDatabase/SortOrder";
15
- import EnterpriseLicenseService, {
16
- Service as EnterpriseLicenseServiceType,
17
- } from "../Services/EnterpriseLicenseService";
18
- import EnterpriseLicenseInstanceService from "../Services/EnterpriseLicenseInstanceService";
19
- import UserMiddleware from "../Middleware/UserAuthorization";
20
- import JSONWebToken from "../Utils/JsonWebToken";
21
- import OneUptimeDate from "../../Types/Date";
22
- import Response from "../Utils/Response";
23
- import {
24
- ExpressRequest,
25
- ExpressResponse,
26
- NextFunction,
27
- } from "../Utils/Express";
28
- import BaseAPI from "./BaseAPI";
29
- import MasterAdminAuthorization from "../Middleware/MasterAdminAuthorization";
30
- // import { Host } from "../EnvironmentConfig";
31
-
32
- /*
33
- * The license state every installation of a license key mirrors locally.
34
- * Returned by both /validate and /report-user-count so that the daily report
35
- * keeps a self-hosted installation current on its own.
36
- */
37
- export interface LicenseTerms {
38
- companyName: string;
39
- // ISO 8601, or null for a license with no expiry set.
40
- expiresAt: string | null;
41
- licenseKey: string;
42
- // Null means the license carries no seat limit.
43
- userLimit: number | null;
44
- }
45
-
46
- export interface LicenseInstanceUpsert {
47
- licenseId: ObjectID;
48
- instanceId: string;
49
- host: string | undefined;
50
- // OneUptime version the instance is running. Absent on instances too old to report it.
51
- oneuptimeVersion?: string | null | undefined;
52
- // Usage fields are only set on report-user-count, not on validate.
53
- userCount?: number | undefined;
54
- userEmailHashes?: Array<string> | undefined;
55
- masterAdminEmails?: Array<string> | undefined;
56
- lastReportedAt?: Date | undefined;
57
- }
58
-
59
- interface LicenseUsageReportResult {
60
- reportedAt: Date;
61
- instances: Array<EnterpriseLicenseInstance>;
62
- currentUserCount: number;
63
- }
64
-
65
- interface LicenseValidationResult {
66
- instances: Array<EnterpriseLicenseInstance>;
67
- currentUserCount: number | null;
68
- userCountUpdatedAt: Date | null;
69
- calculatedAt: Date;
70
- }
71
-
72
- /*
73
- * Bounds how many instance rows one license key can register. Real customers
74
- * run a handful of instances; this stops a leaked key from being used to
75
- * fill the table with junk rows.
76
- */
77
- const MAX_INSTANCES_PER_LICENSE: number = 100;
78
-
79
- export default class EnterpriseLicenseAPI extends BaseAPI<
80
- EnterpriseLicense,
81
- EnterpriseLicenseServiceType
82
- > {
83
- public constructor() {
84
- super(EnterpriseLicense, EnterpriseLicenseService);
85
-
86
- /*
87
- * The dashboard needs a count and instance statuses from one cutoff
88
- * instant. Calculate both on the server so the per-user hashes used for
89
- * cross-instance deduplication never have to be sent to the browser.
90
- */
91
- this.router.get(
92
- `${new this.entityType().getCrudApiPath()?.toString()}/:enterpriseLicenseId/active-usage`,
93
- MasterAdminAuthorization.isAuthorizedMasterAdminMiddleware,
94
- async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
95
- try {
96
- const licenseId: ObjectID = new ObjectID(
97
- req.params["enterpriseLicenseId"] as string,
98
- );
99
- const snapshot: EnterpriseLicenseUsageSnapshot =
100
- await EnterpriseLicenseService.runWithUsageAggregationLock({
101
- licenseId,
102
- fn: async (): Promise<EnterpriseLicenseUsageSnapshot> => {
103
- /*
104
- * A report updates its instance row and then the license-wide
105
- * timestamp. Read both under the same lock so the dashboard
106
- * can never combine the new instance state with the previous
107
- * license state (or vice versa).
108
- */
109
- const license: EnterpriseLicense | null =
110
- await EnterpriseLicenseService.findOneById({
111
- id: licenseId,
112
- select: {
113
- _id: true,
114
- currentUserCount: true,
115
- userCountUpdatedAt: true,
116
- userCountSource: true,
117
- legacyUserCount: true,
118
- legacyUserCountUpdatedAt: true,
119
- },
120
- props: {
121
- isRoot: true,
122
- },
123
- });
124
-
125
- if (!license) {
126
- throw new BadDataException("Enterprise license not found");
127
- }
128
-
129
- const instances: Array<EnterpriseLicenseInstance> =
130
- await this.findLicenseInstances(licenseId);
131
- const calculatedAt: Date = OneUptimeDate.getCurrentDate();
132
- const activeInstances: Array<EnterpriseLicenseInstance> =
133
- instances.filter(
134
- (instance: EnterpriseLicenseInstance): boolean => {
135
- return EnterpriseLicenseUsageUtil.isInstanceCountedTowardsUsage(
136
- instance,
137
- calculatedAt,
138
- );
139
- },
140
- );
141
- const currentUserCount: number | null =
142
- EnterpriseLicenseUsageUtil.getEffectiveUserCount({
143
- instances,
144
- storedUserCount: license.currentUserCount,
145
- storedUserCountUpdatedAt: license.userCountUpdatedAt,
146
- storedUserCountSource: license.userCountSource,
147
- legacyUserCount: license.legacyUserCount,
148
- legacyUserCountUpdatedAt: license.legacyUserCountUpdatedAt,
149
- now: calculatedAt,
150
- });
151
- const masterAdminEmailSet: Set<string> = new Set<string>();
152
-
153
- for (const instance of instances) {
154
- for (const email of instance.masterAdminEmails || []) {
155
- const normalizedEmail: string = email.trim().toLowerCase();
156
-
157
- if (normalizedEmail) {
158
- masterAdminEmailSet.add(normalizedEmail);
159
- }
160
- }
161
- }
162
-
163
- const masterAdminEmails: Array<string> =
164
- Array.from(masterAdminEmailSet).sort();
165
- const nextInstanceStatusChangeAt: Date | null =
166
- activeInstances.reduce(
167
- (
168
- earliestChange: Date | null,
169
- instance: EnterpriseLicenseInstance,
170
- ): Date | null => {
171
- const lastCommunicatedAt: Date | undefined =
172
- EnterpriseLicenseUsageUtil.getInstanceLastCommunicatedAt(
173
- instance,
174
- );
175
-
176
- if (!lastCommunicatedAt) {
177
- return earliestChange;
178
- }
179
-
180
- const inactiveAt: Date = new Date(
181
- lastCommunicatedAt.getTime() +
182
- EnterpriseLicenseUsageUtil.InstanceUsageFreshnessInDays *
183
- 24 *
184
- 60 *
185
- 60 *
186
- 1000,
187
- );
188
-
189
- return !earliestChange || inactiveAt < earliestChange
190
- ? inactiveAt
191
- : earliestChange;
192
- },
193
- null,
194
- );
195
- const usesActiveModernUsage: boolean =
196
- EnterpriseLicenseUsageUtil.hasActiveReportedInstanceUsage(
197
- instances,
198
- calculatedAt,
199
- );
200
- let activeLegacyUsageUpdatedAt: Date | undefined;
201
-
202
- if (!usesActiveModernUsage) {
203
- if (
204
- EnterpriseLicenseUsageUtil.isTimestampWithinUsageWindow(
205
- license.legacyUserCountUpdatedAt,
206
- calculatedAt,
207
- )
208
- ) {
209
- activeLegacyUsageUpdatedAt =
210
- license.legacyUserCountUpdatedAt;
211
- } else if (
212
- !license.legacyUserCountUpdatedAt &&
213
- license.userCountSource !==
214
- EnterpriseLicenseUserCountSource.Instance &&
215
- (license.userCountSource ===
216
- EnterpriseLicenseUserCountSource.Legacy ||
217
- !EnterpriseLicenseUsageUtil.hasReportedInstanceUsage(
218
- instances,
219
- )) &&
220
- EnterpriseLicenseUsageUtil.isTimestampWithinUsageWindow(
221
- license.userCountUpdatedAt,
222
- calculatedAt,
223
- )
224
- ) {
225
- /*
226
- * Compatibility for rows written before the dedicated
227
- * legacy heartbeat columns were introduced.
228
- */
229
- activeLegacyUsageUpdatedAt = license.userCountUpdatedAt;
230
- }
231
- }
232
-
233
- const legacyUsageChangeAt: Date | null =
234
- activeLegacyUsageUpdatedAt
235
- ? new Date(
236
- activeLegacyUsageUpdatedAt.getTime() +
237
- EnterpriseLicenseUsageUtil.InstanceUsageFreshnessInDays *
238
- 24 *
239
- 60 *
240
- 60 *
241
- 1000,
242
- )
243
- : null;
244
- let nextUsageChangeAt: Date | null = nextInstanceStatusChangeAt;
245
-
246
- if (
247
- legacyUsageChangeAt &&
248
- (!nextUsageChangeAt ||
249
- legacyUsageChangeAt < nextUsageChangeAt)
250
- ) {
251
- nextUsageChangeAt = legacyUsageChangeAt;
252
- }
253
- const usageReportTimestamps: Array<Date> = (
254
- [
255
- license.userCountUpdatedAt,
256
- license.legacyUserCountUpdatedAt,
257
- ...instances.map(
258
- (
259
- instance: EnterpriseLicenseInstance,
260
- ): Date | undefined => {
261
- return instance.lastReportedAt;
262
- },
263
- ),
264
- ] as Array<Date | undefined>
265
- ).filter((timestamp: Date | undefined): timestamp is Date => {
266
- return Boolean(timestamp);
267
- });
268
- const lastUsageReportedAt: Date | null =
269
- usageReportTimestamps.reduce(
270
- (latest: Date | null, timestamp: Date): Date => {
271
- return !latest || timestamp > latest ? timestamp : latest;
272
- },
273
- null,
274
- );
275
-
276
- return {
277
- currentUserCount,
278
- activeInstanceIds: activeInstances.map(
279
- (instance: EnterpriseLicenseInstance): string => {
280
- return instance.id!.toString();
281
- },
282
- ),
283
- masterAdminEmails,
284
- calculatedAt: calculatedAt.toISOString(),
285
- lastUsageReportedAt:
286
- lastUsageReportedAt?.toISOString() || null,
287
- nextInstanceStatusChangeAt:
288
- nextUsageChangeAt?.toISOString() || null,
289
- };
290
- },
291
- });
292
-
293
- return Response.sendJsonObjectResponse(
294
- req,
295
- res,
296
- snapshot as unknown as JSONObject,
297
- );
298
- } catch (err) {
299
- next(err);
300
- }
301
- },
302
- );
303
-
304
- this.router.post(
305
- `${new this.entityType().getCrudApiPath()?.toString()}/validate`,
306
- UserMiddleware.getUserMiddleware,
307
- async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
308
- try {
309
- const licenseKey: string | undefined = req.body["licenseKey"];
310
-
311
- if (!licenseKey) {
312
- throw new BadDataException("License key is required");
313
- }
314
-
315
- //const serverHost: string = Host.toString();
316
-
317
- /*
318
- * if (!serverHost.includes("oneuptime.com")) {
319
- * throw new BadDataException(
320
- * "Enterprise license validation is only available on oneuptime.com",
321
- * );
322
- * }
323
- */
324
-
325
- const license: EnterpriseLicense | null =
326
- await EnterpriseLicenseService.findOneBy({
327
- query: {
328
- licenseKey: licenseKey,
329
- },
330
- select: {
331
- _id: true,
332
- companyName: true,
333
- expiresAt: true,
334
- licenseKey: true,
335
- userLimit: true,
336
- currentUserCount: true,
337
- userCountUpdatedAt: true,
338
- isEvaluationLicense: true,
339
- },
340
- props: {
341
- isRoot: true,
342
- },
343
- });
344
-
345
- if (!license) {
346
- throw new BadDataException("License key is invalid");
347
- }
348
-
349
- if (!license.expiresAt) {
350
- throw new BadDataException("License expiration is not set");
351
- }
352
-
353
- const now: number = Date.now();
354
- const expiresAtMs: number = license.expiresAt.getTime();
355
- const secondsUntilExpiry: number = Math.floor(
356
- (expiresAtMs - now) / 1000,
357
- );
358
-
359
- if (secondsUntilExpiry <= 0) {
360
- throw new BadDataException("License key has expired");
361
- }
362
-
363
- /*
364
- * The validating instance sends its instanceId and host so it
365
- * shows up in the instance list right away (usage is reported
366
- * later by the daily job on the instance).
367
- */
368
- const instanceId: string = this.parseShortText(
369
- req.body["instanceId"],
370
- );
371
- const instanceHost: string = this.parseShortText(req.body["host"]);
372
- const instanceVersion: string | null | undefined = this.parseVersion(
373
- req.body["version"],
374
- );
375
-
376
- const validationResult: LicenseValidationResult =
377
- await EnterpriseLicenseService.runWithUsageAggregationLock({
378
- licenseId: license.id!,
379
- fn: async (): Promise<LicenseValidationResult> => {
380
- /*
381
- * Registration is the instance's first communication and can
382
- * keep an upgrade-era count in its grace window. Serialize it
383
- * with reporting and reconciliation so a sweep cannot read the
384
- * old rows, then overwrite the count after this row appears.
385
- */
386
- if (instanceId) {
387
- await this.upsertLicenseInstance({
388
- licenseId: license.id!,
389
- instanceId: instanceId,
390
- host: instanceHost || undefined,
391
- oneuptimeVersion: instanceVersion,
392
- });
393
- }
394
-
395
- const instances: Array<EnterpriseLicenseInstance> =
396
- await this.findLicenseInstances(license.id!);
397
- const currentLicense: EnterpriseLicense | null =
398
- await EnterpriseLicenseService.findOneById({
399
- id: license.id!,
400
- select: {
401
- currentUserCount: true,
402
- userCountUpdatedAt: true,
403
- userCountSource: true,
404
- legacyUserCount: true,
405
- legacyUserCountUpdatedAt: true,
406
- },
407
- props: {
408
- isRoot: true,
409
- },
410
- });
411
-
412
- if (!currentLicense) {
413
- throw new BadDataException("License key is invalid");
414
- }
415
-
416
- const calculatedAt: Date = OneUptimeDate.getCurrentDate();
417
- const currentUserCount: number | null =
418
- EnterpriseLicenseUsageUtil.getEffectiveUserCount({
419
- instances,
420
- storedUserCount: currentLicense.currentUserCount,
421
- storedUserCountUpdatedAt: currentLicense.userCountUpdatedAt,
422
- storedUserCountSource: currentLicense.userCountSource,
423
- legacyUserCount: currentLicense.legacyUserCount,
424
- legacyUserCountUpdatedAt:
425
- currentLicense.legacyUserCountUpdatedAt,
426
- now: calculatedAt,
427
- });
428
-
429
- return {
430
- instances,
431
- currentUserCount,
432
- userCountUpdatedAt: currentLicense.userCountUpdatedAt || null,
433
- calculatedAt,
434
- };
435
- },
436
- });
437
-
438
- const terms: LicenseTerms = this.getLicenseTerms(license);
439
-
440
- const token: string | null = this.signLicenseToken(license);
441
-
442
- return Response.sendJsonObjectResponse(req, res, {
443
- companyName: terms.companyName,
444
- expiresAt: terms.expiresAt,
445
- licenseKey: terms.licenseKey,
446
- userLimit: terms.userLimit,
447
- currentUserCount: validationResult.currentUserCount,
448
- userCountUpdatedAt: validationResult.userCountUpdatedAt
449
- ? validationResult.userCountUpdatedAt.toISOString()
450
- : null,
451
- isEvaluationLicense: Boolean(license.isEvaluationLicense),
452
- instances: this.getInstanceSummaries(
453
- validationResult.instances,
454
- validationResult.calculatedAt,
455
- ),
456
- token,
457
- });
458
- } catch (err) {
459
- next(err);
460
- }
461
- },
462
- );
463
-
464
- this.router.post(
465
- `${new this.entityType().getCrudApiPath()?.toString()}/report-user-count`,
466
- async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
467
- try {
468
- const licenseKey: string | undefined = (
469
- req.body["licenseKey"] as string | undefined
470
- )?.trim();
471
- const rawUserCount: unknown = req.body["userCount"];
472
-
473
- if (!licenseKey) {
474
- throw new BadDataException("License key is required");
475
- }
476
-
477
- const userCount: number = Number(rawUserCount);
478
-
479
- if (
480
- !Number.isFinite(userCount) ||
481
- userCount < 0 ||
482
- !Number.isInteger(userCount)
483
- ) {
484
- throw new BadDataException(
485
- "userCount must be a non-negative integer",
486
- );
487
- }
488
-
489
- const license: EnterpriseLicense | null =
490
- await EnterpriseLicenseService.findOneBy({
491
- query: {
492
- licenseKey: licenseKey,
493
- },
494
- /*
495
- * The full set of terms, not just the usage columns: this
496
- * response is what a self-hosted installation mirrors into its
497
- * own GlobalConfig every day, so anything it is missing here is
498
- * a field the customer keeps the stale value of until somebody
499
- * re-types the license key by hand. Same indexed lookup either
500
- * way.
501
- */
502
- select: {
503
- _id: true,
504
- companyName: true,
505
- expiresAt: true,
506
- licenseKey: true,
507
- userLimit: true,
508
- isEvaluationLicense: true,
509
- },
510
- props: {
511
- isRoot: true,
512
- },
513
- });
514
-
515
- if (!license) {
516
- throw new BadDataException("License key is invalid");
517
- }
518
-
519
- const instanceId: string = this.parseShortText(
520
- req.body["instanceId"],
521
- );
522
- const instanceHost: string = this.parseShortText(req.body["host"]);
523
- const instanceVersion: string | null | undefined = this.parseVersion(
524
- req.body["version"],
525
- );
526
- const userEmailHashes: Array<string> =
527
- EnterpriseLicenseUsageUtil.sanitizeUserEmailHashes(
528
- req.body["userEmailHashes"],
529
- );
530
- const masterAdminEmails: Array<string> =
531
- EnterpriseLicenseUsageUtil.sanitizeMasterAdminEmails(
532
- req.body["masterAdminEmails"],
533
- );
534
- const usageReport: LicenseUsageReportResult =
535
- await EnterpriseLicenseService.runWithUsageAggregationLock({
536
- licenseId: license.id!,
537
- fn: async (): Promise<LicenseUsageReportResult> => {
538
- /*
539
- * Capture this after acquiring the lock. Waiting reports can
540
- * never complete later with an earlier timestamp, and the
541
- * upsert/read/aggregate/write sequence is one serialized
542
- * critical section across every API replica.
543
- */
544
- const reportedAt: Date = OneUptimeDate.getCurrentDate();
545
-
546
- if (instanceId) {
547
- /*
548
- * Multi-instance report: track usage per instance and count
549
- * users uniquely across all instances of this license.
550
- */
551
- await this.upsertLicenseInstance({
552
- licenseId: license.id!,
553
- instanceId: instanceId,
554
- host: instanceHost || undefined,
555
- oneuptimeVersion: instanceVersion,
556
- userCount: userCount,
557
- userEmailHashes: userEmailHashes,
558
- masterAdminEmails: masterAdminEmails,
559
- lastReportedAt: reportedAt,
560
- });
561
- }
562
-
563
- const instances: Array<EnterpriseLicenseInstance> =
564
- await this.findLicenseInstances(license.id!);
565
- const hasActiveReportedInstanceUsage: boolean =
566
- EnterpriseLicenseUsageUtil.hasActiveReportedInstanceUsage(
567
- instances,
568
- reportedAt,
569
- );
570
- const currentUserCount: number = hasActiveReportedInstanceUsage
571
- ? EnterpriseLicenseUsageUtil.getUniqueUserCount(
572
- instances,
573
- reportedAt,
574
- )
575
- : userCount;
576
- const usageUpdate: PartialEntity<EnterpriseLicense> = {};
577
-
578
- /*
579
- * Legacy reports (no instanceId) drive the license-wide count
580
- * only while no modern instance is active. Their separate
581
- * heartbeat is always retained, though, so a legacy reporter
582
- * can take over immediately when the last modern report ages
583
- * out instead of disappearing until its next daily call.
584
- */
585
- if (!instanceId) {
586
- usageUpdate.legacyUserCount = userCount;
587
- usageUpdate.legacyUserCountUpdatedAt = reportedAt;
588
- }
589
-
590
- if (instanceId || !hasActiveReportedInstanceUsage) {
591
- usageUpdate.currentUserCount = currentUserCount;
592
- usageUpdate.userCountUpdatedAt = reportedAt;
593
- usageUpdate.userCountSource = instanceId
594
- ? EnterpriseLicenseUserCountSource.Instance
595
- : EnterpriseLicenseUserCountSource.Legacy;
596
- }
597
-
598
- if (Object.keys(usageUpdate).length > 0) {
599
- await EnterpriseLicenseService.updateOneById({
600
- id: license.id!,
601
- data: usageUpdate,
602
- props: {
603
- isRoot: true,
604
- ignoreHooks: true,
605
- },
606
- });
607
- }
608
-
609
- return {
610
- reportedAt,
611
- instances,
612
- currentUserCount,
613
- };
614
- },
615
- });
616
-
617
- const { reportedAt, instances, currentUserCount } = usageReport;
618
-
619
- const terms: LicenseTerms = this.getLicenseTerms(license);
620
-
621
- return Response.sendJsonObjectResponse(req, res, {
622
- companyName: terms.companyName,
623
- expiresAt: terms.expiresAt,
624
- licenseKey: terms.licenseKey,
625
- userLimit: terms.userLimit,
626
- currentUserCount: currentUserCount,
627
- userCountUpdatedAt: reportedAt.toISOString(),
628
- isEvaluationLicense: Boolean(license.isEvaluationLicense),
629
- instances: this.getInstanceSummaries(instances, reportedAt),
630
- /*
631
- * Null once the license has expired. Unlike /validate this route
632
- * does not reject an expired license - the instance is still
633
- * expected to report, and the expiry notification emails are built
634
- * from what it reports - but it must not keep being handed a fresh
635
- * token, and the expiresAt above is what tells it the truth.
636
- */
637
- token: this.signLicenseToken(license),
638
- });
639
- } catch (err) {
640
- next(err);
641
- }
642
- },
643
- );
644
- }
645
-
646
- /*
647
- * The license terms a self-hosted installation mirrors locally. Built in one
648
- * place so /validate and /report-user-count cannot drift: the seat limit was
649
- * missing from what an instance could refresh for exactly as long as the two
650
- * responses were written out by hand, separately.
651
- */
652
- private getLicenseTerms(license: EnterpriseLicense): LicenseTerms {
653
- return {
654
- companyName: license.companyName || "",
655
- expiresAt: license.expiresAt ? license.expiresAt.toISOString() : null,
656
- licenseKey: license.licenseKey || "",
657
- userLimit:
658
- typeof license.userLimit === "number" ? license.userLimit : null,
659
- };
660
- }
661
-
662
- /*
663
- * Signed with the license server's own secret, so it is opaque to the
664
- * installation holding it - it is the proof that oneuptime.com recognized
665
- * the key, and the instance only checks that it exists. An expired license
666
- * gets none, which is why this returns null rather than a short-lived token.
667
- */
668
- private signLicenseToken(license: EnterpriseLicense): string | null {
669
- if (!license.expiresAt) {
670
- return null;
671
- }
672
-
673
- const secondsUntilExpiry: number = Math.floor(
674
- (license.expiresAt.getTime() - Date.now()) / 1000,
675
- );
676
-
677
- if (secondsUntilExpiry <= 0) {
678
- return null;
679
- }
680
-
681
- const terms: LicenseTerms = this.getLicenseTerms(license);
682
-
683
- return JSONWebToken.signJsonPayload(
684
- {
685
- companyName: terms.companyName,
686
- expiresAt: terms.expiresAt,
687
- licenseKey: terms.licenseKey,
688
- userLimit: terms.userLimit,
689
- },
690
- Math.max(secondsUntilExpiry, 1),
691
- );
692
- }
693
-
694
- private parseShortText(value: unknown): string {
695
- if (typeof value !== "string") {
696
- return "";
697
- }
698
-
699
- return value.trim().substring(0, 100);
700
- }
701
-
702
- /*
703
- * report-user-count is unauthenticated, so the reported version is only
704
- * stored when it is a version we could actually compare against a release —
705
- * garbage never reaches the admin dashboard or the customer's modal.
706
- *
707
- * Three outcomes, and the callers depend on the distinction:
708
- * undefined — the instance sent no version at all (it predates this
709
- * field). Leave whatever is stored alone.
710
- * null — it sent something, but not a version. Clear the stored
711
- * value, so an instance rolled back to a build with no
712
- * APP_VERSION stops advertising the version it used to run.
713
- * string — a valid, canonical version to store.
714
- */
715
- private parseVersion(value: unknown): string | null | undefined {
716
- if (value === undefined || value === null) {
717
- return undefined;
718
- }
719
-
720
- /*
721
- * Canonicalized rather than passed through, so the stored string is never
722
- * something the UIs would render with a doubled "v".
723
- */
724
- return VersionUtil.canonicalize(value);
725
- }
726
-
727
- private async findLicenseInstances(
728
- licenseId: ObjectID,
729
- ): Promise<Array<EnterpriseLicenseInstance>> {
730
- return EnterpriseLicenseInstanceService.findBy({
731
- query: {
732
- enterpriseLicenseId: licenseId,
733
- },
734
- select: {
735
- _id: true,
736
- createdAt: true,
737
- instanceId: true,
738
- host: true,
739
- userCount: true,
740
- userEmailHashes: true,
741
- masterAdminEmails: true,
742
- lastReportedAt: true,
743
- oneuptimeVersion: true,
744
- },
745
- sort: {
746
- createdAt: SortOrder.Ascending,
747
- },
748
- skip: 0,
749
- limit: LIMIT_MAX,
750
- props: {
751
- isRoot: true,
752
- },
753
- });
754
- }
755
-
756
- private getInstanceSummaries(
757
- instances: Array<EnterpriseLicenseInstance>,
758
- calculatedAt: Date,
759
- ): Array<EnterpriseLicenseInstanceSummary> {
760
- return instances.map(
761
- (
762
- instance: EnterpriseLicenseInstance,
763
- ): EnterpriseLicenseInstanceSummary => {
764
- return {
765
- instanceId: instance.instanceId || "",
766
- host: instance.host || null,
767
- userCount:
768
- typeof instance.userCount === "number" ? instance.userCount : null,
769
- isCountedTowardsUsage:
770
- EnterpriseLicenseUsageUtil.isInstanceCountedTowardsUsage(
771
- instance,
772
- calculatedAt,
773
- ),
774
- lastReportedAt: instance.lastReportedAt
775
- ? instance.lastReportedAt.toISOString()
776
- : null,
777
- version: instance.oneuptimeVersion || null,
778
- };
779
- },
780
- );
781
- }
782
-
783
- private async upsertLicenseInstance(
784
- data: LicenseInstanceUpsert,
785
- ): Promise<void> {
786
- const updated: boolean = await this.updateLicenseInstanceIfExists(data);
787
-
788
- if (updated) {
789
- return;
790
- }
791
-
792
- const instanceCount: PositiveNumber =
793
- await EnterpriseLicenseInstanceService.countBy({
794
- query: {
795
- enterpriseLicenseId: data.licenseId,
796
- },
797
- props: {
798
- isRoot: true,
799
- },
800
- });
801
-
802
- if (instanceCount.toNumber() >= MAX_INSTANCES_PER_LICENSE) {
803
- throw new BadDataException(
804
- "Too many instances are registered for this license. Please contact support@oneuptime.com.",
805
- );
806
- }
807
-
808
- const newInstance: EnterpriseLicenseInstance =
809
- new EnterpriseLicenseInstance();
810
- newInstance.enterpriseLicenseId = data.licenseId;
811
- newInstance.instanceId = data.instanceId;
812
-
813
- if (data.host !== undefined) {
814
- newInstance.host = data.host;
815
- }
816
-
817
- // Null means "reported something that is not a version" — nothing to store.
818
- if (data.oneuptimeVersion) {
819
- newInstance.oneuptimeVersion = data.oneuptimeVersion;
820
- }
821
-
822
- if (data.userCount !== undefined) {
823
- newInstance.userCount = data.userCount;
824
- }
825
-
826
- if (data.userEmailHashes !== undefined) {
827
- newInstance.userEmailHashes = data.userEmailHashes;
828
- }
829
-
830
- if (data.masterAdminEmails !== undefined) {
831
- newInstance.masterAdminEmails = data.masterAdminEmails;
832
- }
833
-
834
- if (data.lastReportedAt !== undefined) {
835
- newInstance.lastReportedAt = data.lastReportedAt;
836
- }
837
-
838
- try {
839
- await EnterpriseLicenseInstanceService.create({
840
- data: newInstance,
841
- props: {
842
- isRoot: true,
843
- },
844
- });
845
- } catch (err) {
846
- /*
847
- * A concurrent request created the row between our check and this
848
- * insert — the unique index on (enterpriseLicenseId, instanceId)
849
- * rejected the duplicate. Apply the report as an update instead.
850
- */
851
- const retried: boolean = await this.updateLicenseInstanceIfExists(data);
852
-
853
- if (!retried) {
854
- throw err;
855
- }
856
- }
857
- }
858
-
859
- private async updateLicenseInstanceIfExists(
860
- data: LicenseInstanceUpsert,
861
- ): Promise<boolean> {
862
- const existingInstance: EnterpriseLicenseInstance | null =
863
- await EnterpriseLicenseInstanceService.findOneBy({
864
- query: {
865
- enterpriseLicenseId: data.licenseId,
866
- instanceId: data.instanceId,
867
- },
868
- select: {
869
- _id: true,
870
- },
871
- props: {
872
- isRoot: true,
873
- },
874
- });
875
-
876
- if (!existingInstance) {
877
- return false;
878
- }
879
-
880
- const updateData: PartialEntity<EnterpriseLicenseInstance> = {};
881
-
882
- if (data.host !== undefined) {
883
- updateData.host = data.host;
884
- }
885
-
886
- /*
887
- * Null clears the column: an instance rebuilt onto a build with no
888
- * APP_VERSION must stop advertising the version it used to run, because
889
- * lastReportedAt is refreshed on the same request and would otherwise
890
- * read as "confirmed running v11.5.13 as of today".
891
- */
892
- if (data.oneuptimeVersion !== undefined) {
893
- updateData.oneuptimeVersion = data.oneuptimeVersion;
894
- }
895
-
896
- if (data.userCount !== undefined) {
897
- updateData.userCount = data.userCount;
898
- }
899
-
900
- if (data.userEmailHashes !== undefined) {
901
- updateData.userEmailHashes = data.userEmailHashes;
902
- }
903
-
904
- if (data.masterAdminEmails !== undefined) {
905
- updateData.masterAdminEmails = data.masterAdminEmails;
906
- }
907
-
908
- if (data.lastReportedAt !== undefined) {
909
- updateData.lastReportedAt = data.lastReportedAt;
910
- }
911
-
912
- if (Object.keys(updateData).length > 0) {
913
- await EnterpriseLicenseInstanceService.updateOneById({
914
- id: existingInstance.id!,
915
- data: updateData,
916
- props: {
917
- isRoot: true,
918
- ignoreHooks: true,
919
- },
920
- });
921
- }
922
-
923
- return true;
924
- }
925
- }