@oneuptime/common 13.0.8 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/Models/AnalyticsModels/AuditLog.ts +23 -15
  2. package/Models/DatabaseModels/GlobalConfig.ts +24 -0
  3. package/Models/DatabaseModels/Monitor.ts +3 -2
  4. package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
  5. package/Scripts/generate-service-worker.js +65 -4
  6. package/Server/API/BaseAPI.ts +26 -5
  7. package/Server/API/GlobalConfigAPI.ts +228 -462
  8. package/Server/API/ProjectOIDC.ts +25 -16
  9. package/Server/API/ProjectSSO.ts +24 -16
  10. package/Server/API/StatusPageAPI.ts +100 -42
  11. package/Server/API/TeamMemberAPI.ts +10 -1
  12. package/Server/Enterprise/EnterpriseEdition.ts +649 -0
  13. package/Server/Enterprise/EnterpriseFeature.ts +48 -0
  14. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
  15. package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
  16. package/Server/EnvironmentConfig.ts +147 -2
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  19. package/Server/Middleware/UserAuthorization.ts +38 -16
  20. package/Server/Services/AuditLogService.ts +84 -1079
  21. package/Server/Services/EnterpriseLicenseService.ts +38 -0
  22. package/Server/Services/GlobalConfigService.ts +80 -1
  23. package/Server/Services/MonitorProbeService.ts +116 -20
  24. package/Server/Services/MonitorService.ts +56 -4
  25. package/Server/Services/MonitorStatusTimelineService.ts +284 -0
  26. package/Server/Services/MonitorTemplateService.ts +26 -0
  27. package/Server/Services/ProjectService.ts +284 -0
  28. package/Server/Services/RumSessionPinService.ts +25 -6
  29. package/Server/Services/StatusPagePrivateUserService.ts +7 -3
  30. package/Server/Services/StatusPageService.ts +78 -1
  31. package/Server/Services/TeamMemberService.ts +11 -0
  32. package/Server/Services/TeamService.ts +10 -0
  33. package/Server/Services/UserService.ts +10 -17
  34. package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
  35. package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
  36. package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
  37. package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
  38. package/Server/Types/Markdown.ts +41 -0
  39. package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
  40. package/Server/Utils/EditionEnforcement.ts +233 -0
  41. package/Server/Utils/FrontendEnvironment.ts +41 -2
  42. package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
  43. package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
  44. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
  45. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
  46. package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
  47. package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
  48. package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
  49. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
  50. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
  51. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
  52. package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
  53. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
  54. package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
  55. package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
  56. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
  57. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
  58. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
  59. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
  60. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
  61. package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
  62. package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
  63. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
  64. package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
  65. package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
  66. package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
  67. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
  68. package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
  69. package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
  70. package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
  71. package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
  72. package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
  73. package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
  74. package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
  75. package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
  76. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
  77. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
  78. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
  79. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
  80. package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
  81. package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
  82. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
  83. package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
  84. package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
  85. package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
  86. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
  87. package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
  88. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
  89. package/Tests/Server/Services/AuditLogService.test.ts +460 -990
  90. package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
  91. package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
  92. package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
  93. package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
  94. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
  95. package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
  96. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
  97. package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
  98. package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
  99. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
  100. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
  101. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
  102. package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
  103. package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
  104. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
  105. package/Tests/Server/Types/Markdown.test.ts +181 -0
  106. package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
  107. package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
  108. package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
  109. package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
  110. package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
  111. package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
  112. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
  113. package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
  114. package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
  115. package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
  116. package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
  117. package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
  118. package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
  119. package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
  120. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
  121. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
  122. package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
  123. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
  124. package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
  125. package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
  126. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
  127. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
  128. package/Tests/Utils/HostAddressUtil.test.ts +138 -0
  129. package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
  130. package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
  131. package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
  132. package/Types/API/Hostname.ts +63 -16
  133. package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
  134. package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
  135. package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
  136. package/Types/Database/CanonicalModelInstance.ts +102 -0
  137. package/Types/Database/TableColumn.ts +20 -6
  138. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
  139. package/Types/IP/IPv6.ts +6 -1
  140. package/Types/Monitor/MonitorStep.ts +6 -1
  141. package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
  142. package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
  143. package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
  144. package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
  145. package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
  146. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  147. package/UI/Components/ModelList/ModelList.tsx +11 -1
  148. package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
  149. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
  150. package/UI/esbuild-enterprise.js +263 -0
  151. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
  152. package/Utils/HostAddressUtil.ts +94 -0
  153. package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
  154. package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
  155. package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
  156. package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
  157. package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
  158. package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
  159. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  160. package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
  161. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  162. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
  163. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  164. package/build/dist/Server/API/BaseAPI.js +26 -5
  165. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  166. package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
  167. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  168. package/build/dist/Server/API/ProjectOIDC.js +24 -16
  169. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  170. package/build/dist/Server/API/ProjectSSO.js +24 -16
  171. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  172. package/build/dist/Server/API/StatusPageAPI.js +93 -43
  173. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  174. package/build/dist/Server/API/TeamMemberAPI.js +8 -1
  175. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  176. package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
  177. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
  178. package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
  179. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
  180. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
  181. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
  182. package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
  183. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
  184. package/build/dist/Server/EnvironmentConfig.js +84 -2
  185. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  186. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
  187. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
  188. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  190. package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
  191. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  192. package/build/dist/Server/Services/AuditLogService.js +71 -740
  193. package/build/dist/Server/Services/AuditLogService.js.map +1 -1
  194. package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
  195. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  196. package/build/dist/Server/Services/GlobalConfigService.js +63 -0
  197. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  198. package/build/dist/Server/Services/MonitorProbeService.js +90 -17
  199. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  200. package/build/dist/Server/Services/MonitorService.js +45 -4
  201. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  202. package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
  203. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  204. package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
  205. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  206. package/build/dist/Server/Services/ProjectService.js +214 -0
  207. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  208. package/build/dist/Server/Services/RumSessionPinService.js +24 -6
  209. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  210. package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
  211. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  212. package/build/dist/Server/Services/StatusPageService.js +71 -0
  213. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  214. package/build/dist/Server/Services/TeamMemberService.js +10 -0
  215. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  216. package/build/dist/Server/Services/TeamService.js +9 -0
  217. package/build/dist/Server/Services/TeamService.js.map +1 -1
  218. package/build/dist/Server/Services/UserService.js +10 -16
  219. package/build/dist/Server/Services/UserService.js.map +1 -1
  220. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
  221. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  222. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
  223. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  224. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
  225. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  226. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
  227. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  228. package/build/dist/Server/Types/Markdown.js +31 -0
  229. package/build/dist/Server/Types/Markdown.js.map +1 -1
  230. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
  231. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
  232. package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
  233. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
  234. package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
  235. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
  236. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
  237. package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
  238. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
  239. package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
  240. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  241. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  242. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
  243. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
  245. package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
  246. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
  247. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  248. package/build/dist/Types/API/Hostname.js +59 -11
  249. package/build/dist/Types/API/Hostname.js.map +1 -1
  250. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
  251. package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
  252. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
  253. package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
  254. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
  255. package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
  256. package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
  257. package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
  258. package/build/dist/Types/Database/TableColumn.js +19 -6
  259. package/build/dist/Types/Database/TableColumn.js.map +1 -1
  260. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
  261. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
  262. package/build/dist/Types/IP/IPv6.js +6 -1
  263. package/build/dist/Types/IP/IPv6.js.map +1 -1
  264. package/build/dist/Types/Monitor/MonitorStep.js +5 -0
  265. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  266. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
  267. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
  268. package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
  269. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
  270. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  271. package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
  272. package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
  273. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
  274. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  275. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  276. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  277. package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
  278. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  279. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
  280. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  281. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
  282. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  283. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
  284. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  285. package/build/dist/Utils/HostAddressUtil.js +80 -0
  286. package/build/dist/Utils/HostAddressUtil.js.map +1 -0
  287. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
  288. package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
  289. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
  290. package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
  291. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
  292. package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
  293. package/jest.config.json +3 -1
  294. package/package.json +1 -1
  295. package/test-setup.sh +103 -34
  296. package/tsconfig.json +13 -0
  297. package/Server/API/EnterpriseLicenseAPI.ts +0 -925
  298. package/Server/API/TeamComplianceAPI.ts +0 -158
  299. package/Server/Middleware/SCIMAuthorization.ts +0 -141
  300. package/Server/Services/TeamComplianceService.ts +0 -733
  301. package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
  302. package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
  303. package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
  304. package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
  305. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
  306. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
  307. package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
  308. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
  309. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
  310. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
  311. package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
  312. package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
  313. package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
  314. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
  315. package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
  316. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
  317. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
  318. package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
  319. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
  320. package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
  321. package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
  322. package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
  323. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
  324. package/build/dist/Server/Services/TeamComplianceService.js +0 -537
  325. package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
  326. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
  327. package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
  328. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
  329. package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
  330. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
  331. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
  332. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
  333. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Enterprise Edition resolution for the frontend builds (Dashboard and
3
+ * AdminDashboard).
4
+ *
5
+ * Each of those frontends imports its Enterprise UI through one bare
6
+ * specifier, from one file (src/Enterprise/Plugins.ts):
7
+ *
8
+ * Dashboard "@oneuptime/ee-dashboard" -> ee/Dashboard/Index.tsx
9
+ * AdminDashboard "@oneuptime/ee-admin-dashboard" -> ee/AdminDashboard/Index.tsx
10
+ *
11
+ * This module decides what that specifier means for one build and returns the
12
+ * esbuild aliases that make it so. The frontends pass the result to
13
+ * createConfig({ additionalAlias }) - nothing else about the build changes.
14
+ *
15
+ * ONEUPTIME_EDITION picks the edition:
16
+ *
17
+ * auto (default) Enterprise when the ee plugin file is on disk, Community
18
+ * otherwise. A plain checkout builds whatever it contains.
19
+ * community Always the Community stub, even with ee/ present.
20
+ * enterprise The ee plugin, and a hard error when it is missing. The
21
+ * Enterprise image sets this, so a broken COPY of ee/ fails
22
+ * the build instead of quietly shipping the Community UI
23
+ * under an Enterprise tag.
24
+ *
25
+ * Where ee/ is looked for, relative to the frontend directory:
26
+ *
27
+ * <frontend>/../../../../ee the repository: packages/App/FeatureSet/<F>
28
+ * <frontend>/../../../ee the image: /usr/src/app/FeatureSet/<F> -> /usr/src/ee
29
+ *
30
+ * ONEUPTIME_EE_DIR replaces both with one directory (fixtures, tests, odd
31
+ * layouts) - the same variable the server-side loader honours.
32
+ *
33
+ * "Found" means the plugin's Index.tsx FILE exists. The alias points at that
34
+ * file rather than its directory: esbuild resolves a directory alias to a
35
+ * lowercase index.* and would miss Index.tsx on a case-sensitive filesystem.
36
+ *
37
+ * Two more aliases are always returned, so ee code can reach core without
38
+ * relative paths and without an install of its own:
39
+ *
40
+ * "@oneuptime/dashboard/..." (or "@oneuptime/admin-dashboard/...")
41
+ * -> <frontend>/src/...
42
+ * "Common/..." -> <frontend>/node_modules/Common/...
43
+ *
44
+ * Bundling therefore never depends on ee/node_modules. Both are applied in
45
+ * the Community build too, so core resolves identically in both editions.
46
+ *
47
+ * CommonJS, like esbuild-config.js: the frontends' esbuild.config.js files
48
+ * are plain node scripts.
49
+ */
50
+
51
+ const fs = require("fs");
52
+ const path = require("path");
53
+
54
+ const EDITION_ENV_VAR = "ONEUPTIME_EDITION";
55
+ const EE_DIR_ENV_VAR = "ONEUPTIME_EE_DIR";
56
+
57
+ const EDITION_AUTO = "auto";
58
+ const EDITION_COMMUNITY = "community";
59
+ const EDITION_ENTERPRISE = "enterprise";
60
+
61
+ const VALID_EDITIONS = [EDITION_AUTO, EDITION_COMMUNITY, EDITION_ENTERPRISE];
62
+
63
+ const PLUGIN_ENTRY_FILENAME = "Index.tsx";
64
+
65
+ /*
66
+ * The Community stub, relative to the frontend directory. Every tsconfig and
67
+ * jest config maps the plugin specifier to this same file.
68
+ */
69
+ const COMMUNITY_STUB_PATH = path.join(
70
+ "src",
71
+ "Enterprise",
72
+ "CommunityPlugins.ts",
73
+ );
74
+
75
+ function isFile(filePath) {
76
+ try {
77
+ return fs.statSync(filePath).isFile();
78
+ } catch (error) {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * The edition the environment asks for: "auto", "community" or "enterprise".
85
+ * Unset or blank means "auto". Anything else is a typo, and a typo must not
86
+ * silently build the wrong edition, so it throws.
87
+ * @param {Object} [env] - Environment to read (defaults to process.env)
88
+ * @returns {string}
89
+ */
90
+ function readRequestedEdition(env) {
91
+ const source = env || process.env;
92
+ const raw = source[EDITION_ENV_VAR];
93
+
94
+ if (raw === undefined || raw === null || String(raw).trim() === "") {
95
+ return EDITION_AUTO;
96
+ }
97
+
98
+ const value = String(raw).trim().toLowerCase();
99
+
100
+ if (!VALID_EDITIONS.includes(value)) {
101
+ throw new Error(
102
+ `${EDITION_ENV_VAR} must be one of ${VALID_EDITIONS.join(", ")} (got "${raw}").`,
103
+ );
104
+ }
105
+
106
+ return value;
107
+ }
108
+
109
+ /**
110
+ * Every path the plugin entry may live at, in the order they are tried.
111
+ * @param {string} frontendDir - Absolute path of the frontend package
112
+ * @param {string} eeSubdir - "Dashboard" or "AdminDashboard"
113
+ * @param {Object} [env] - Environment to read (defaults to process.env)
114
+ * @returns {Array<string>}
115
+ */
116
+ function getEnterpriseCandidates(frontendDir, eeSubdir, env) {
117
+ const source = env || process.env;
118
+ const override = source[EE_DIR_ENV_VAR];
119
+
120
+ if (override !== undefined && override !== null && String(override).trim()) {
121
+ return [
122
+ path.resolve(String(override).trim(), eeSubdir, PLUGIN_ENTRY_FILENAME),
123
+ ];
124
+ }
125
+
126
+ return [
127
+ // Repository: packages/App/FeatureSet/<Frontend> -> <repo>/ee
128
+ path.resolve(
129
+ frontendDir,
130
+ "..",
131
+ "..",
132
+ "..",
133
+ "..",
134
+ "ee",
135
+ eeSubdir,
136
+ PLUGIN_ENTRY_FILENAME,
137
+ ),
138
+ // Image: /usr/src/app/FeatureSet/<Frontend> -> /usr/src/ee
139
+ path.resolve(
140
+ frontendDir,
141
+ "..",
142
+ "..",
143
+ "..",
144
+ "ee",
145
+ eeSubdir,
146
+ PLUGIN_ENTRY_FILENAME,
147
+ ),
148
+ ];
149
+ }
150
+
151
+ function assertNonEmptyString(value, name) {
152
+ if (typeof value !== "string" || value.trim() === "") {
153
+ throw new Error(`resolveEnterpriseBuild: "${name}" is required.`);
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Decide the edition for one frontend build.
159
+ * @param {Object} options
160
+ * @param {string} options.frontendDir - Absolute path of the frontend package (its esbuild.config.js __dirname)
161
+ * @param {string} options.pluginSpecifier - e.g. "@oneuptime/ee-dashboard"
162
+ * @param {string} options.eeSubdir - Directory under ee/, e.g. "Dashboard"
163
+ * @param {string} options.internalSpecifier - e.g. "@oneuptime/dashboard"; ee code imports <frontend>/src through it
164
+ * @param {Object} [options.env] - Environment to read (defaults to process.env)
165
+ * @returns {{ edition: string, requestedEdition: string, pluginEntry: (string|null), communityStub: string, candidates: Array<string>, alias: Object }}
166
+ */
167
+ function resolveEnterpriseBuild(options) {
168
+ const {
169
+ frontendDir,
170
+ pluginSpecifier,
171
+ eeSubdir,
172
+ internalSpecifier,
173
+ env = process.env,
174
+ } = options || {};
175
+
176
+ assertNonEmptyString(frontendDir, "frontendDir");
177
+ assertNonEmptyString(pluginSpecifier, "pluginSpecifier");
178
+ assertNonEmptyString(eeSubdir, "eeSubdir");
179
+ assertNonEmptyString(internalSpecifier, "internalSpecifier");
180
+
181
+ if (!path.isAbsolute(frontendDir)) {
182
+ throw new Error(
183
+ `resolveEnterpriseBuild: "frontendDir" must be absolute (got "${frontendDir}").`,
184
+ );
185
+ }
186
+
187
+ const communityStub = path.join(frontendDir, COMMUNITY_STUB_PATH);
188
+
189
+ if (!isFile(communityStub)) {
190
+ throw new Error(
191
+ `The Community plugin stub is missing: ${communityStub}. Every frontend that imports ${pluginSpecifier} needs one.`,
192
+ );
193
+ }
194
+
195
+ const requestedEdition = readRequestedEdition(env);
196
+ const candidates = getEnterpriseCandidates(frontendDir, eeSubdir, env);
197
+
198
+ const pluginEntry =
199
+ requestedEdition === EDITION_COMMUNITY
200
+ ? null
201
+ : candidates.find(isFile) || null;
202
+
203
+ if (requestedEdition === EDITION_ENTERPRISE && !pluginEntry) {
204
+ throw new Error(
205
+ `${EDITION_ENV_VAR}=${EDITION_ENTERPRISE}, but the Enterprise UI plugin for ${eeSubdir} was not found. Looked for: ${candidates.join(
206
+ ", ",
207
+ )}. Copy the ee/ directory into the build, or unset ${EDITION_ENV_VAR} to build the Community Edition.`,
208
+ );
209
+ }
210
+
211
+ return {
212
+ edition: pluginEntry ? EDITION_ENTERPRISE : EDITION_COMMUNITY,
213
+ requestedEdition,
214
+ pluginEntry,
215
+ communityStub,
216
+ candidates,
217
+ alias: {
218
+ [pluginSpecifier]: pluginEntry || communityStub,
219
+ [internalSpecifier]: path.join(frontendDir, "src"),
220
+ Common: path.join(frontendDir, "node_modules", "Common"),
221
+ },
222
+ };
223
+ }
224
+
225
+ /**
226
+ * The esbuild aliases for one frontend build - what esbuild.config.js passes
227
+ * to createConfig({ additionalAlias }). Same options as resolveEnterpriseBuild.
228
+ * @param {Object} options
229
+ * @returns {Object}
230
+ */
231
+ function resolveEnterpriseAliases(options) {
232
+ return resolveEnterpriseBuild(options).alias;
233
+ }
234
+
235
+ /**
236
+ * One line for the build log, so a CI log shows which edition was bundled.
237
+ * @param {string} serviceName
238
+ * @param {Object} build - A resolveEnterpriseBuild() result
239
+ * @returns {string}
240
+ */
241
+ function describeEnterpriseBuild(serviceName, build) {
242
+ if (build.edition === EDITION_ENTERPRISE) {
243
+ return `${serviceName}: building the Enterprise Edition UI from ${build.pluginEntry}`;
244
+ }
245
+
246
+ return `${serviceName}: building the Community Edition UI (${EDITION_ENV_VAR}=${build.requestedEdition})`;
247
+ }
248
+
249
+ module.exports = {
250
+ EDITION_ENV_VAR,
251
+ EE_DIR_ENV_VAR,
252
+ EDITION_AUTO,
253
+ EDITION_COMMUNITY,
254
+ EDITION_ENTERPRISE,
255
+ VALID_EDITIONS,
256
+ PLUGIN_ENTRY_FILENAME,
257
+ COMMUNITY_STUB_PATH,
258
+ readRequestedEdition,
259
+ getEnterpriseCandidates,
260
+ resolveEnterpriseBuild,
261
+ resolveEnterpriseAliases,
262
+ describeEnterpriseBuild,
263
+ };
@@ -1,3 +1,4 @@
1
+ import { ENTERPRISE_LICENSE_GRACE_PERIOD_IN_DAYS } from "../../Types/EnterpriseLicense/EnterpriseLicensePeriods";
1
2
  import EnterpriseLicenseUserCountSource from "../../Types/EnterpriseLicense/EnterpriseLicenseUserCountSource";
2
3
 
3
4
  /*
@@ -49,9 +50,13 @@ export default class EnterpriseLicenseUsageUtil {
49
50
  /*
50
51
  * Expired licenses keep getting a daily "expired" email for this many
51
52
  * days after expiry, then go quiet — an abandoned license should not be
52
- * emailed forever.
53
+ * emailed forever. It has to outlast the grace period by a clear margin:
54
+ * the email that says single sign-on, SCIM provisioning and audit logging
55
+ * have now stopped is only worth sending after they have, which is when
56
+ * the grace period ends.
53
57
  */
54
- public static readonly expiredNotificationCutoffDays: number = 30;
58
+ public static readonly expiredNotificationCutoffDays: number =
59
+ ENTERPRISE_LICENSE_GRACE_PERIOD_IN_DAYS + 14;
55
60
 
56
61
  /*
57
62
  * License keys are shown/emailed masked — enough to identify the key
@@ -0,0 +1,94 @@
1
+ import IP from "../Types/IP/IP";
2
+
3
+ /*
4
+ * The two things every caller that holds a host string needs to get right
5
+ * when that host might be an IPv6 literal, in one place.
6
+ *
7
+ * Both are pure text problems that only exist for IPv6, which is exactly why
8
+ * they kept being got wrong one call site at a time:
9
+ *
10
+ * 1. BRACKETS ARE URL SYNTAX, NOT PART OF THE ADDRESS. "[2001:db8::1]" is
11
+ * how RFC 3986 writes an IPv6 host inside a URL authority, and that is
12
+ * the form URL.fromString and Hostname.fromAuthority hand back. Node
13
+ * does NOT strip them: net.connect({host: "[::1]"}) and
14
+ * https.get({host: "[::1]"}) both go to the resolver and come back
15
+ * ENOTFOUND, and `ping [::1]` fails the same way. An IPv4 host never
16
+ * carries brackets, so every one of those call sites worked until
17
+ * somebody pointed it at IPv6.
18
+ *
19
+ * 2. "host:port" IS AMBIGUOUS FOR IPv6, AND NOT IN A WAY THAT LOOKS WRONG.
20
+ * "192.0.2.1:179" cannot be mistaken for an address. Appending ":179" to
21
+ * "2001:518:2800:9::2" produces "2001:518:2800:9::2:179", which node's
22
+ * net.isIP() reports as a PERFECTLY VALID IPv6 address - a different
23
+ * host from the one that was meant. So the unbracketed spelling does not
24
+ * merely read oddly, it silently renames the target.
25
+ */
26
+ export default class HostAddressUtil {
27
+ /*
28
+ * True when the value is an IPv6 literal, written bare ("2001:db8::1") or
29
+ * bracketed ("[2001:db8::1]"). Anything else - a DNS name, an IPv4 literal,
30
+ * an authority with a port - is false.
31
+ */
32
+ public static isIPv6(host: string): boolean {
33
+ const bare: string = HostAddressUtil.stripBrackets(host);
34
+
35
+ if (!bare) {
36
+ return false;
37
+ }
38
+
39
+ return IP.isIP(bare) && new IP(bare).isIPv6();
40
+ }
41
+
42
+ /*
43
+ * The address to hand to a socket, a resolver or the ping binary: the same
44
+ * host with any IPv6 URL brackets removed.
45
+ *
46
+ * Brackets are only removed when what is inside them really is an IPv6
47
+ * address. "[not-an-address]" is returned untouched so it fails validation
48
+ * downstream as the nonsense it is, rather than being quietly laundered
49
+ * into a bare hostname.
50
+ */
51
+ public static stripBrackets(host: string): string {
52
+ const trimmed: string = (host || "").trim();
53
+
54
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
55
+ return trimmed;
56
+ }
57
+
58
+ const inner: string = trimmed.substring(1, trimmed.length - 1);
59
+
60
+ if (!inner || !IP.isIP(inner) || !new IP(inner).isIPv6()) {
61
+ return trimmed;
62
+ }
63
+
64
+ return inner;
65
+ }
66
+
67
+ /*
68
+ * The authority form: an IPv6 host is bracketed so the ":" before the port
69
+ * cannot be read as another group of the address. Already-bracketed input
70
+ * is not bracketed twice.
71
+ *
72
+ * A missing/empty port returns the host on its own, bare. That is
73
+ * deliberate: a port-less destination is stored and pinged as the address
74
+ * itself, and wrapping it in brackets there would change what is written to
75
+ * the database and shown to the user for no benefit - the ambiguity this
76
+ * guards against only exists once a ":port" is appended.
77
+ */
78
+ public static formatHostAndPort(data: {
79
+ host: string;
80
+ port?: string | number | undefined;
81
+ }): string {
82
+ const bare: string = HostAddressUtil.stripBrackets(data.host);
83
+ const port: string =
84
+ data.port === undefined || data.port === null ? "" : String(data.port);
85
+
86
+ if (!port) {
87
+ return bare;
88
+ }
89
+
90
+ return HostAddressUtil.isIPv6(bare)
91
+ ? `[${bare}]:${port}`
92
+ : `${bare}:${port}`;
93
+ }
94
+ }
@@ -0,0 +1,157 @@
1
+ import Hostname from "../../Types/API/Hostname";
2
+ import URL from "../../Types/API/URL";
3
+ import Exception from "../../Types/Exception/Exception";
4
+ import IP from "../../Types/IP/IP";
5
+ import MonitorType from "../../Types/Monitor/MonitorType";
6
+ import HostAddressUtil from "../HostAddressUtil";
7
+
8
+ export interface ParsedMonitorDestination {
9
+ destination: IP | URL | Hostname | undefined;
10
+ // Null when the value parsed. A string is the message to show the operator.
11
+ error: string | null;
12
+ }
13
+
14
+ /*
15
+ * Turns what somebody typed (or, far more often, PASTED) into the monitor
16
+ * destination for a monitor type, or into the reason it cannot be one.
17
+ *
18
+ * Lives here rather than inline in the dashboard form so the parsing is
19
+ * testable on its own — the form's job is state, not address syntax.
20
+ *
21
+ * Two IPv6 shapes have to be handled that IPv4 never needed, and getting
22
+ * either wrong is SILENT rather than loud:
23
+ *
24
+ * - SURROUNDING WHITESPACE. An address copied out of a looking glass, a
25
+ * router config or a terminal usually arrives with a trailing space or a
26
+ * newline. IP.isIP deliberately does not trim, so " 2001:db8::1" was not
27
+ * an IP; it fell through to Hostname.fromString, which split it on the
28
+ * first colon into host "2001" / port 518 — a legal Port, so nothing
29
+ * threw, no error appeared, and the monitor saved was pointed at a host
30
+ * called "2001". The IPv4 equivalent, "1.1.1.1 ", has no colon and
31
+ * Hostname trims, so it always worked. That asymmetry is the whole bug:
32
+ * the same paste that works for v4 corrupts v6.
33
+ *
34
+ * - BRACKETS. "[2001:db8::1]" is how an IPv6 host is written inside a URL,
35
+ * so it is what people have in front of them. For a Ping/IP/Port
36
+ * destination there is no URL, so the brackets are noise and are dropped
37
+ * — rather than rejected with "Hostname [2001 is not in valid format".
38
+ */
39
+ export default class MonitorDestinationUtil {
40
+ public static parse(data: {
41
+ value: string;
42
+ monitorType: MonitorType;
43
+ }): ParsedMonitorDestination {
44
+ const value: string = (data.value || "").trim();
45
+
46
+ if (!value) {
47
+ return { destination: undefined, error: "Destination is required" };
48
+ }
49
+
50
+ try {
51
+ switch (data.monitorType) {
52
+ case MonitorType.IP:
53
+ return {
54
+ destination: IP.fromString(HostAddressUtil.stripBrackets(value)),
55
+ error: null,
56
+ };
57
+
58
+ case MonitorType.Ping:
59
+ case MonitorType.Port: {
60
+ const host: string = HostAddressUtil.stripBrackets(value);
61
+
62
+ if (IP.isIP(host)) {
63
+ return { destination: IP.fromString(host), error: null };
64
+ }
65
+
66
+ /*
67
+ * A trailing colon is a port that was not typed, and
68
+ * Hostname.fromAuthority DROPS it — deliberately, because a stored
69
+ * "host:" should still read back as a host. Here that would be the
70
+ * same class of bug this whole file exists to stop: "2001:" would
71
+ * be stored as "2001", which is not what anybody typed. Say so
72
+ * instead.
73
+ */
74
+ if (host.endsWith(":")) {
75
+ return {
76
+ destination: undefined,
77
+ error: `${value} is not a valid hostname or IP address.`,
78
+ };
79
+ }
80
+
81
+ /*
82
+ * Not an address, so it is a name — possibly with a ":port", which
83
+ * is what someone pasting "rs1.example.net:179" means.
84
+ *
85
+ * fromAuthority rather than `new Hostname`: the constructor accepts
86
+ * a whole authority and stores it as the HOST, so the destination
87
+ * would be a host literally named "rs1.example.net:179" and the
88
+ * socket would go looking for it in DNS. fromAuthority splits the
89
+ * port off properly, and — unlike the fromString this used to
90
+ * call — leaves a bare IPv6 literal alone.
91
+ */
92
+ return { destination: Hostname.fromAuthority(host), error: null };
93
+ }
94
+
95
+ case MonitorType.Website:
96
+ case MonitorType.API:
97
+ case MonitorType.SSLCertificate:
98
+ return {
99
+ destination: URL.fromString(
100
+ MonitorDestinationUtil.toUrlWithBracketedIpv6Host(value),
101
+ ),
102
+ error: null,
103
+ };
104
+
105
+ default:
106
+ return { destination: undefined, error: null };
107
+ }
108
+ } catch (err: unknown) {
109
+ return {
110
+ destination: undefined,
111
+ error:
112
+ err instanceof Exception
113
+ ? err.getMessage()
114
+ : (err as Error)?.message || "Invalid Destination",
115
+ };
116
+ }
117
+ }
118
+
119
+ /*
120
+ * A URL whose host is a BARE IPv6 literal is not a URL: "https://2001:db8::1/"
121
+ * has to be written "https://[2001:db8::1]/", because otherwise the first
122
+ * colon reads as the port separator. Node's own URL parser, axios and every
123
+ * browser reject the bare form, so a Website or API monitor saved with one
124
+ * failed permanently with an opaque target error.
125
+ *
126
+ * Brackets the host when it needs them, and leaves every other URL — every
127
+ * IPv4 and DNS one — byte for byte alone.
128
+ */
129
+ public static toUrlWithBracketedIpv6Host(value: string): string {
130
+ const trimmed: string = value.trim();
131
+ const schemeMatch: RegExpMatchArray | null = trimmed.match(
132
+ /^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)(.*)$/,
133
+ );
134
+
135
+ const scheme: string = schemeMatch ? (schemeMatch[1] as string) : "";
136
+ const rest: string = schemeMatch ? (schemeMatch[2] as string) : trimmed;
137
+
138
+ // The authority ends at the first "/", "?" or "#".
139
+ const authorityEnd: number = rest.search(/[/?#]/);
140
+ const authority: string =
141
+ authorityEnd === -1 ? rest : rest.substring(0, authorityEnd);
142
+ const remainder: string =
143
+ authorityEnd === -1 ? "" : rest.substring(authorityEnd);
144
+
145
+ // Userinfo is not a host; everything after the last "@" is.
146
+ const atIndex: number = authority.lastIndexOf("@");
147
+ const userInfo: string =
148
+ atIndex === -1 ? "" : authority.substring(0, atIndex + 1);
149
+ const host: string = authority.substring(atIndex + 1);
150
+
151
+ if (!HostAddressUtil.isIPv6(host) || host.startsWith("[")) {
152
+ return trimmed;
153
+ }
154
+
155
+ return `${scheme}${userInfo}[${host}]${remainder}`;
156
+ }
157
+ }