@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.
- package/Models/AnalyticsModels/AuditLog.ts +23 -15
- package/Models/DatabaseModels/GlobalConfig.ts +24 -0
- package/Models/DatabaseModels/Monitor.ts +3 -2
- package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
- package/Scripts/generate-service-worker.js +65 -4
- package/Server/API/BaseAPI.ts +26 -5
- package/Server/API/GlobalConfigAPI.ts +228 -462
- package/Server/API/ProjectOIDC.ts +25 -16
- package/Server/API/ProjectSSO.ts +24 -16
- package/Server/API/StatusPageAPI.ts +100 -42
- package/Server/API/TeamMemberAPI.ts +10 -1
- package/Server/Enterprise/EnterpriseEdition.ts +649 -0
- package/Server/Enterprise/EnterpriseFeature.ts +48 -0
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
- package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
- package/Server/EnvironmentConfig.ts +147 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Middleware/UserAuthorization.ts +38 -16
- package/Server/Services/AuditLogService.ts +84 -1079
- package/Server/Services/EnterpriseLicenseService.ts +38 -0
- package/Server/Services/GlobalConfigService.ts +80 -1
- package/Server/Services/MonitorProbeService.ts +116 -20
- package/Server/Services/MonitorService.ts +56 -4
- package/Server/Services/MonitorStatusTimelineService.ts +284 -0
- package/Server/Services/MonitorTemplateService.ts +26 -0
- package/Server/Services/ProjectService.ts +284 -0
- package/Server/Services/RumSessionPinService.ts +25 -6
- package/Server/Services/StatusPagePrivateUserService.ts +7 -3
- package/Server/Services/StatusPageService.ts +78 -1
- package/Server/Services/TeamMemberService.ts +11 -0
- package/Server/Services/TeamService.ts +10 -0
- package/Server/Services/UserService.ts +10 -17
- package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
- package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
- package/Server/Types/Database/Permissions/TablePermission.ts +10 -3
- package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
- package/Server/Types/Markdown.ts +41 -0
- package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
- package/Server/Utils/EditionEnforcement.ts +233 -0
- package/Server/Utils/FrontendEnvironment.ts +41 -2
- package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
- package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
- package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
- package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
- package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
- package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
- package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
- package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
- package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +45 -9
- package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
- package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -83
- package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
- package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
- package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
- package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
- package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
- package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +742 -497
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +35 -909
- package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
- package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
- package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
- package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
- package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
- package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
- package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
- package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
- package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
- package/Tests/Server/Services/AuditLogService.test.ts +460 -990
- package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
- package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
- package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
- package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
- package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
- package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
- package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
- package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
- package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
- package/Tests/Server/Types/Markdown.test.ts +181 -0
- package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
- package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
- package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
- package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
- package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
- package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
- package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
- package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
- package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
- package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
- package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
- package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
- package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +84 -178
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
- package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
- package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
- package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
- package/Tests/Utils/HostAddressUtil.test.ts +138 -0
- package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
- package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
- package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
- package/Types/API/Hostname.ts +63 -16
- package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
- package/Types/Database/CanonicalModelInstance.ts +102 -0
- package/Types/Database/TableColumn.ts +20 -6
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
- package/Types/IP/IPv6.ts +6 -1
- package/Types/Monitor/MonitorStep.ts +6 -1
- package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
- package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +796 -1195
- package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
- package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/ModelList/ModelList.tsx +11 -1
- package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
- package/UI/esbuild-enterprise.js +263 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
- package/Utils/HostAddressUtil.ts +94 -0
- package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
- package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
- package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
- package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
- package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +26 -5
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +148 -344
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectOIDC.js +24 -16
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +24 -16
- package/build/dist/Server/API/ProjectSSO.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +93 -43
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +8 -1
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +84 -2
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +30 -11
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AuditLogService.js +71 -740
- package/build/dist/Server/Services/AuditLogService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/GlobalConfigService.js +63 -0
- package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
- package/build/dist/Server/Services/MonitorProbeService.js +90 -17
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +45 -4
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +214 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +24 -6
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +71 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +10 -0
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TeamService.js +9 -0
- package/build/dist/Server/Services/TeamService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +10 -16
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +10 -3
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +31 -0
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
- package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
- package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
- package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
- package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +59 -11
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
- package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
- package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
- package/build/dist/Types/Database/TableColumn.js +19 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
- package/build/dist/Types/IP/IPv6.js +6 -1
- package/build/dist/Types/IP/IPv6.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +5 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +414 -585
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
- package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
- package/build/dist/Utils/HostAddressUtil.js +80 -0
- package/build/dist/Utils/HostAddressUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
- package/jest.config.json +3 -1
- package/package.json +1 -1
- package/test-setup.sh +103 -34
- package/tsconfig.json +13 -0
- package/Server/API/EnterpriseLicenseAPI.ts +0 -925
- package/Server/API/TeamComplianceAPI.ts +0 -158
- package/Server/Middleware/SCIMAuthorization.ts +0 -141
- package/Server/Services/TeamComplianceService.ts +0 -733
- package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
- package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
- package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
- package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
- package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +0 -761
- package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
- package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +0 -321
- package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
- package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
- package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
- package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
- package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
- package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
- package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
- package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
- package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
- package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
- package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
- package/build/dist/Server/Services/TeamComplianceService.js +0 -537
- package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, jest } from "@jest/globals";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import type URL from "../../Types/API/URL";
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* The environment variables behind the Community / Enterprise split:
|
|
8
|
+
* ONEUPTIME_EDITION, ONEUPTIME_EE_DIR, ALLOW_BILLING_WITHOUT_ENTERPRISE and
|
|
9
|
+
* ENTERPRISE_LICENSE_SERVER_URL (which the two license-server endpoint URLs
|
|
10
|
+
* derive from).
|
|
11
|
+
*
|
|
12
|
+
* EnvironmentConfig reads process.env at module load, so every case here
|
|
13
|
+
* sets the environment, resets the module registry and imports afresh.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
type OneUptimeEditionSetting = "auto" | "community" | "enterprise";
|
|
17
|
+
|
|
18
|
+
interface EnvironmentConfigShape {
|
|
19
|
+
OneUptimeEdition: OneUptimeEditionSetting | null;
|
|
20
|
+
ONEUPTIME_EDITION_SETTINGS: ReadonlyArray<OneUptimeEditionSetting>;
|
|
21
|
+
parseOneUptimeEdition: (
|
|
22
|
+
rawValue: string | undefined,
|
|
23
|
+
) => OneUptimeEditionSetting | null;
|
|
24
|
+
OneUptimeEnterpriseDirectory: string | undefined;
|
|
25
|
+
AllowBillingWithoutEnterprise: boolean;
|
|
26
|
+
IsEnterpriseEdition: boolean;
|
|
27
|
+
DEFAULT_ENTERPRISE_LICENSE_SERVER_URL: string;
|
|
28
|
+
resolveEnterpriseLicenseServerUrl: (
|
|
29
|
+
rawValue: string | undefined,
|
|
30
|
+
allowInsecure: boolean,
|
|
31
|
+
) => string;
|
|
32
|
+
EnterpriseLicenseServerUrl: URL;
|
|
33
|
+
EnterpriseLicenseValidationUrl: URL;
|
|
34
|
+
EnterpriseLicenseUserCountReportUrl: URL;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const MANAGED_KEYS: Array<string> = [
|
|
38
|
+
"ONEUPTIME_EDITION",
|
|
39
|
+
"ONEUPTIME_EE_DIR",
|
|
40
|
+
"ALLOW_BILLING_WITHOUT_ENTERPRISE",
|
|
41
|
+
"ENTERPRISE_LICENSE_SERVER_URL",
|
|
42
|
+
"IS_ENTERPRISE_EDITION",
|
|
43
|
+
"ENVIRONMENT",
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const originalEnv: NodeJS.ProcessEnv = { ...process.env };
|
|
47
|
+
|
|
48
|
+
async function load(
|
|
49
|
+
overrides: Record<string, string | undefined>,
|
|
50
|
+
): Promise<EnvironmentConfigShape> {
|
|
51
|
+
for (const key of MANAGED_KEYS) {
|
|
52
|
+
delete process.env[key];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
56
|
+
if (value === undefined) {
|
|
57
|
+
delete process.env[key];
|
|
58
|
+
} else {
|
|
59
|
+
process.env[key] = value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
jest.resetModules();
|
|
64
|
+
|
|
65
|
+
return (await import(
|
|
66
|
+
"../../Server/EnvironmentConfig"
|
|
67
|
+
)) as unknown as EnvironmentConfigShape;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
afterEach(() => {
|
|
71
|
+
process.env = { ...originalEnv };
|
|
72
|
+
jest.resetModules();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("ONEUPTIME_EDITION", () => {
|
|
76
|
+
it("defaults to auto when unset", async () => {
|
|
77
|
+
const config: EnvironmentConfigShape = await load({});
|
|
78
|
+
|
|
79
|
+
expect(config.OneUptimeEdition).toBe("auto");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("reads a blank value as auto", async () => {
|
|
83
|
+
const config: EnvironmentConfigShape = await load({
|
|
84
|
+
ONEUPTIME_EDITION: " ",
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(config.OneUptimeEdition).toBe("auto");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it.each([
|
|
91
|
+
"auto",
|
|
92
|
+
"community",
|
|
93
|
+
"enterprise",
|
|
94
|
+
] as Array<OneUptimeEditionSetting>)(
|
|
95
|
+
"accepts %s",
|
|
96
|
+
async (edition: OneUptimeEditionSetting) => {
|
|
97
|
+
const config: EnvironmentConfigShape = await load({
|
|
98
|
+
ONEUPTIME_EDITION: edition,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(config.OneUptimeEdition).toBe(edition);
|
|
102
|
+
},
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
it("is case- and whitespace-insensitive", async () => {
|
|
106
|
+
const config: EnvironmentConfigShape = await load({
|
|
107
|
+
ONEUPTIME_EDITION: " Enterprise \n",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(config.OneUptimeEdition).toBe("enterprise");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it.each(["enterprize", "ce", "ee", "true", "1", "communityedition"])(
|
|
114
|
+
"reports an unrecognised value (%s) as null so the loader can refuse to boot",
|
|
115
|
+
async (rawValue: string) => {
|
|
116
|
+
const config: EnvironmentConfigShape = await load({
|
|
117
|
+
ONEUPTIME_EDITION: rawValue,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(config.OneUptimeEdition).toBeNull();
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
it("lists exactly the three settings", async () => {
|
|
125
|
+
const config: EnvironmentConfigShape = await load({});
|
|
126
|
+
|
|
127
|
+
expect([...config.ONEUPTIME_EDITION_SETTINGS]).toEqual([
|
|
128
|
+
"auto",
|
|
129
|
+
"community",
|
|
130
|
+
"enterprise",
|
|
131
|
+
]);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("parseOneUptimeEdition is a pure function of its argument", async () => {
|
|
135
|
+
const config: EnvironmentConfigShape = await load({
|
|
136
|
+
ONEUPTIME_EDITION: "community",
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(config.parseOneUptimeEdition(undefined)).toBe("auto");
|
|
140
|
+
expect(config.parseOneUptimeEdition("")).toBe("auto");
|
|
141
|
+
expect(config.parseOneUptimeEdition("COMMUNITY")).toBe("community");
|
|
142
|
+
expect(config.parseOneUptimeEdition("nope")).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("does not change what IS_ENTERPRISE_EDITION means", async () => {
|
|
146
|
+
const enterprise: EnvironmentConfigShape = await load({
|
|
147
|
+
IS_ENTERPRISE_EDITION: "true",
|
|
148
|
+
ONEUPTIME_EDITION: "community",
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(enterprise.IsEnterpriseEdition).toBe(true);
|
|
152
|
+
expect(enterprise.OneUptimeEdition).toBe("community");
|
|
153
|
+
|
|
154
|
+
const community: EnvironmentConfigShape = await load({
|
|
155
|
+
ONEUPTIME_EDITION: "enterprise",
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
expect(community.IsEnterpriseEdition).toBe(false);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe("ONEUPTIME_EE_DIR", () => {
|
|
163
|
+
it("is undefined when unset or blank", async () => {
|
|
164
|
+
expect((await load({})).OneUptimeEnterpriseDirectory).toBeUndefined();
|
|
165
|
+
expect(
|
|
166
|
+
(await load({ ONEUPTIME_EE_DIR: " " })).OneUptimeEnterpriseDirectory,
|
|
167
|
+
).toBeUndefined();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("is the trimmed path when set", async () => {
|
|
171
|
+
const config: EnvironmentConfigShape = await load({
|
|
172
|
+
ONEUPTIME_EE_DIR: " /opt/fixtures/ee \n",
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(config.OneUptimeEnterpriseDirectory).toBe("/opt/fixtures/ee");
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe("ALLOW_BILLING_WITHOUT_ENTERPRISE", () => {
|
|
180
|
+
it("is off unless exactly true", async () => {
|
|
181
|
+
expect((await load({})).AllowBillingWithoutEnterprise).toBe(false);
|
|
182
|
+
expect(
|
|
183
|
+
(await load({ ALLOW_BILLING_WITHOUT_ENTERPRISE: "1" }))
|
|
184
|
+
.AllowBillingWithoutEnterprise,
|
|
185
|
+
).toBe(false);
|
|
186
|
+
expect(
|
|
187
|
+
(await load({ ALLOW_BILLING_WITHOUT_ENTERPRISE: "TRUE" }))
|
|
188
|
+
.AllowBillingWithoutEnterprise,
|
|
189
|
+
).toBe(false);
|
|
190
|
+
expect(
|
|
191
|
+
(await load({ ALLOW_BILLING_WITHOUT_ENTERPRISE: "true" }))
|
|
192
|
+
.AllowBillingWithoutEnterprise,
|
|
193
|
+
).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("ENTERPRISE_LICENSE_SERVER_URL", () => {
|
|
198
|
+
it("defaults to oneuptime.com and keeps the historical endpoint URLs byte-identical", async () => {
|
|
199
|
+
const config: EnvironmentConfigShape = await load({});
|
|
200
|
+
|
|
201
|
+
expect(config.DEFAULT_ENTERPRISE_LICENSE_SERVER_URL).toBe(
|
|
202
|
+
"https://oneuptime.com",
|
|
203
|
+
);
|
|
204
|
+
// The URL type renders a bare origin with its root path.
|
|
205
|
+
expect(config.EnterpriseLicenseServerUrl.toString()).toBe(
|
|
206
|
+
"https://oneuptime.com/",
|
|
207
|
+
);
|
|
208
|
+
expect(config.EnterpriseLicenseValidationUrl.toString()).toBe(
|
|
209
|
+
"https://oneuptime.com/api/enterprise-license/validate",
|
|
210
|
+
);
|
|
211
|
+
expect(config.EnterpriseLicenseUserCountReportUrl.toString()).toBe(
|
|
212
|
+
"https://oneuptime.com/api/enterprise-license/report-user-count",
|
|
213
|
+
);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("derives both endpoints from an https override", async () => {
|
|
217
|
+
const config: EnvironmentConfigShape = await load({
|
|
218
|
+
ENTERPRISE_LICENSE_SERVER_URL: "https://licenses.staging.example.com/",
|
|
219
|
+
ENVIRONMENT: "production",
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(config.EnterpriseLicenseServerUrl.toString()).toBe(
|
|
223
|
+
"https://licenses.staging.example.com/",
|
|
224
|
+
);
|
|
225
|
+
expect(config.EnterpriseLicenseValidationUrl.toString()).toBe(
|
|
226
|
+
"https://licenses.staging.example.com/api/enterprise-license/validate",
|
|
227
|
+
);
|
|
228
|
+
expect(config.EnterpriseLicenseUserCountReportUrl.toString()).toBe(
|
|
229
|
+
"https://licenses.staging.example.com/api/enterprise-license/report-user-count",
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("keeps a path prefix on the override", async () => {
|
|
234
|
+
const config: EnvironmentConfigShape = await load({
|
|
235
|
+
ENTERPRISE_LICENSE_SERVER_URL: "https://relay.example.com/oneuptime//",
|
|
236
|
+
ENVIRONMENT: "production",
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
expect(config.EnterpriseLicenseValidationUrl.toString()).toBe(
|
|
240
|
+
"https://relay.example.com/oneuptime/api/enterprise-license/validate",
|
|
241
|
+
);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("refuses plain http in production (the license key would travel in the clear)", async () => {
|
|
245
|
+
const config: EnvironmentConfigShape = await load({
|
|
246
|
+
ENTERPRISE_LICENSE_SERVER_URL: "http://licenses.example.com",
|
|
247
|
+
ENVIRONMENT: "production",
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
expect(config.EnterpriseLicenseValidationUrl.toString()).toBe(
|
|
251
|
+
"https://oneuptime.com/api/enterprise-license/validate",
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it.each(["development", "test"])(
|
|
256
|
+
"allows plain http in %s",
|
|
257
|
+
async (environment: string) => {
|
|
258
|
+
const config: EnvironmentConfigShape = await load({
|
|
259
|
+
ENTERPRISE_LICENSE_SERVER_URL: "http://localhost:3002",
|
|
260
|
+
ENVIRONMENT: environment,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
expect(config.EnterpriseLicenseValidationUrl.toString()).toBe(
|
|
264
|
+
"http://localhost:3002/api/enterprise-license/validate",
|
|
265
|
+
);
|
|
266
|
+
},
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
it("resolveEnterpriseLicenseServerUrl falls back to the default for unusable values", async () => {
|
|
270
|
+
const config: EnvironmentConfigShape = await load({});
|
|
271
|
+
const resolve: EnvironmentConfigShape["resolveEnterpriseLicenseServerUrl"] =
|
|
272
|
+
config.resolveEnterpriseLicenseServerUrl;
|
|
273
|
+
|
|
274
|
+
expect(resolve(undefined, false)).toBe("https://oneuptime.com");
|
|
275
|
+
expect(resolve("", true)).toBe("https://oneuptime.com");
|
|
276
|
+
expect(resolve(" ", true)).toBe("https://oneuptime.com");
|
|
277
|
+
expect(resolve("licenses.example.com", true)).toBe("https://oneuptime.com");
|
|
278
|
+
expect(resolve("ftp://licenses.example.com", true)).toBe(
|
|
279
|
+
"https://oneuptime.com",
|
|
280
|
+
);
|
|
281
|
+
expect(resolve("javascript:alert(1)", true)).toBe("https://oneuptime.com");
|
|
282
|
+
expect(resolve("http://licenses.example.com", false)).toBe(
|
|
283
|
+
"https://oneuptime.com",
|
|
284
|
+
);
|
|
285
|
+
expect(resolve("https://", true)).toBe("https://oneuptime.com");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("resolveEnterpriseLicenseServerUrl keeps usable values, minus trailing slashes", async () => {
|
|
289
|
+
const config: EnvironmentConfigShape = await load({});
|
|
290
|
+
const resolve: EnvironmentConfigShape["resolveEnterpriseLicenseServerUrl"] =
|
|
291
|
+
config.resolveEnterpriseLicenseServerUrl;
|
|
292
|
+
|
|
293
|
+
expect(resolve("https://licenses.example.com", false)).toBe(
|
|
294
|
+
"https://licenses.example.com",
|
|
295
|
+
);
|
|
296
|
+
expect(resolve(" HTTPS://Licenses.Example.com/// ", false)).toBe(
|
|
297
|
+
"HTTPS://Licenses.Example.com",
|
|
298
|
+
);
|
|
299
|
+
expect(resolve("http://localhost:3002/", true)).toBe(
|
|
300
|
+
"http://localhost:3002",
|
|
301
|
+
);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe("EnvironmentConfig import hygiene", () => {
|
|
306
|
+
it("EnvironmentConfig never imports the enterprise module (no import cycle)", () => {
|
|
307
|
+
const source: string = fs.readFileSync(
|
|
308
|
+
path.join(__dirname, "../../Server/EnvironmentConfig.ts"),
|
|
309
|
+
"utf8",
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
expect(source).not.toContain("./Enterprise/");
|
|
313
|
+
});
|
|
314
|
+
});
|
|
@@ -59,10 +59,26 @@ interface ServiceImage {
|
|
|
59
59
|
* we must not confuse with the production one.
|
|
60
60
|
*/
|
|
61
61
|
productionStanza: string;
|
|
62
|
+
/*
|
|
63
|
+
* The whole Dockerfile as it renders for production: everything before the
|
|
64
|
+
* development branch plus the production stanza. Stages defined before the
|
|
65
|
+
* branch (a shared `base`) belong to the production build too.
|
|
66
|
+
*/
|
|
67
|
+
productionDockerfile: string;
|
|
62
68
|
// The `start` script the production CMD ultimately runs, if it runs one.
|
|
63
69
|
startScript: string | null;
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
// One `FROM` stage of a Dockerfile.
|
|
73
|
+
interface DockerfileStage {
|
|
74
|
+
// The `AS <name>`, lower-cased, or null for an unnamed stage.
|
|
75
|
+
name: string | null;
|
|
76
|
+
// What it is built FROM: an image or an earlier stage's name (lower-cased).
|
|
77
|
+
from: string;
|
|
78
|
+
// The instructions after the FROM line, up to the next FROM.
|
|
79
|
+
body: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
66
82
|
// CMD forms that hand control to `npm start`.
|
|
67
83
|
const CMD_NPM_START_EXEC_FORM: RegExp = /CMD\s*\[\s*"npm"\s*,\s*"start"\s*\]/;
|
|
68
84
|
const CMD_NPM_START_SHELL_FORM: RegExp = /CMD\s+npm\s+start/;
|
|
@@ -80,6 +96,81 @@ const RUN_COMPILE_INSTRUCTION: RegExp = /^\s*RUN\s+npm\s+run\s+compile\s*$/m;
|
|
|
80
96
|
const INCLUDE_ARRAY_BLOCK: RegExp = /"include"\s*:\s*\[([^\]]*)\]/;
|
|
81
97
|
const QUOTED_STRING: RegExp = /"[^"]*"/g;
|
|
82
98
|
|
|
99
|
+
// Where the development branch of a Dockerfile.tpl starts.
|
|
100
|
+
const DEVELOPMENT_BRANCH_START: string =
|
|
101
|
+
'{{ if eq .Env.ENVIRONMENT "development" }}';
|
|
102
|
+
// A FROM line: `FROM <image or stage> [AS <name>]`, case-insensitive like Docker.
|
|
103
|
+
const FROM_INSTRUCTION: RegExp =
|
|
104
|
+
/^\s*FROM\s+(?:--\S+\s+)*(\S+)(?:\s+AS\s+(\S+))?\s*$/i;
|
|
105
|
+
const USER_NODE_INSTRUCTION: RegExp = /^\s*USER\s+node\s*$/m;
|
|
106
|
+
// Any COPY/ADD whose source is the ee/ directory (./ee, ee, ./ee/...).
|
|
107
|
+
const COPIES_ENTERPRISE_DIRECTORY: RegExp =
|
|
108
|
+
/^\s*(?:COPY|ADD)\s+(?:--\S+\s+)*(?:\.\/)?ee(?:\/\S*)?\s/im;
|
|
109
|
+
const SETS_ONEUPTIME_EDITION: RegExp = /^\s*ENV\s+ONEUPTIME_EDITION[=\s]/m;
|
|
110
|
+
const SETS_ENTERPRISE_EDITION_MARKER: RegExp =
|
|
111
|
+
/^\s*ENV\s+ONEUPTIME_EDITION=enterprise\s*$/m;
|
|
112
|
+
|
|
113
|
+
// The final stages the edition split builds (packages/App/Dockerfile.tpl).
|
|
114
|
+
const EDITION_STAGE_NAMES: Array<string> = ["community", "enterprise"];
|
|
115
|
+
|
|
116
|
+
// Splits a Dockerfile into its FROM stages, in order.
|
|
117
|
+
const splitStages: (dockerfile: string) => Array<DockerfileStage> = (
|
|
118
|
+
dockerfile: string,
|
|
119
|
+
): Array<DockerfileStage> => {
|
|
120
|
+
const stages: Array<DockerfileStage> = [];
|
|
121
|
+
let current: DockerfileStage | null = null;
|
|
122
|
+
|
|
123
|
+
for (const line of dockerfile.split("\n")) {
|
|
124
|
+
const from: RegExpMatchArray | null = line.match(FROM_INSTRUCTION);
|
|
125
|
+
|
|
126
|
+
if (from) {
|
|
127
|
+
current = {
|
|
128
|
+
from: (from[1] ?? "").toLowerCase(),
|
|
129
|
+
name: from[2] ? from[2].toLowerCase() : null,
|
|
130
|
+
body: "",
|
|
131
|
+
};
|
|
132
|
+
stages.push(current);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (current) {
|
|
137
|
+
current.body += `${line}\n`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return stages;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// The stage and every stage it is built FROM, nearest first.
|
|
145
|
+
const stageAncestry: (
|
|
146
|
+
stages: Array<DockerfileStage>,
|
|
147
|
+
name: string,
|
|
148
|
+
) => Array<DockerfileStage> = (
|
|
149
|
+
stages: Array<DockerfileStage>,
|
|
150
|
+
name: string,
|
|
151
|
+
): Array<DockerfileStage> => {
|
|
152
|
+
const chain: Array<DockerfileStage> = [];
|
|
153
|
+
let next: string | null = name;
|
|
154
|
+
|
|
155
|
+
while (next !== null) {
|
|
156
|
+
const wanted: string = next;
|
|
157
|
+
const stage: DockerfileStage | undefined = stages.find(
|
|
158
|
+
(candidate: DockerfileStage) => {
|
|
159
|
+
return candidate.name === wanted;
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
if (!stage || chain.includes(stage)) {
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
chain.push(stage);
|
|
168
|
+
next = stage.from;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return chain;
|
|
172
|
+
};
|
|
173
|
+
|
|
83
174
|
const listServiceImages: () => Array<ServiceImage> =
|
|
84
175
|
(): Array<ServiceImage> => {
|
|
85
176
|
const images: Array<ServiceImage> = [];
|
|
@@ -103,6 +194,13 @@ const listServiceImages: () => Array<ServiceImage> =
|
|
|
103
194
|
const elseIndex: number = dockerfile.lastIndexOf("{{ else }}");
|
|
104
195
|
const productionStanza: string =
|
|
105
196
|
elseIndex === -1 ? dockerfile : dockerfile.slice(elseIndex);
|
|
197
|
+
const developmentIndex: number = dockerfile.indexOf(
|
|
198
|
+
DEVELOPMENT_BRANCH_START,
|
|
199
|
+
);
|
|
200
|
+
const productionDockerfile: string =
|
|
201
|
+
elseIndex === -1 || developmentIndex === -1
|
|
202
|
+
? dockerfile
|
|
203
|
+
: dockerfile.slice(0, developmentIndex) + productionStanza;
|
|
106
204
|
|
|
107
205
|
let startScript: string | null = null;
|
|
108
206
|
const packageJsonPath: string = path.join(
|
|
@@ -120,6 +218,7 @@ const listServiceImages: () => Array<ServiceImage> =
|
|
|
120
218
|
images.push({
|
|
121
219
|
service: path.basename(directory),
|
|
122
220
|
productionStanza,
|
|
221
|
+
productionDockerfile,
|
|
123
222
|
startScript,
|
|
124
223
|
});
|
|
125
224
|
}
|
|
@@ -378,4 +477,194 @@ describe("Container boot configuration", () => {
|
|
|
378
477
|
},
|
|
379
478
|
);
|
|
380
479
|
});
|
|
480
|
+
|
|
481
|
+
describe("images built as Community and Enterprise targets", () => {
|
|
482
|
+
/*
|
|
483
|
+
* The App builds both editions from one Dockerfile: `--target community`
|
|
484
|
+
* and `--target enterprise` (packages/App/Dockerfile.tpl). Docker does
|
|
485
|
+
* inherit USER, ENV and CMD from a parent stage, but that is exactly why
|
|
486
|
+
* the whole-stanza checks above cannot see a final stage that lost one of
|
|
487
|
+
* them: they match the instruction anywhere in the production branch. Each
|
|
488
|
+
* final stage is an image of its own, so each states its own boot
|
|
489
|
+
* configuration, and nothing the Community image is built from may copy
|
|
490
|
+
* ee/ in.
|
|
491
|
+
*/
|
|
492
|
+
const stagesOf: (service: string) => Array<DockerfileStage> = (
|
|
493
|
+
service: string,
|
|
494
|
+
): Array<DockerfileStage> => {
|
|
495
|
+
const image: ServiceImage | undefined = SERVICE_IMAGES.find(
|
|
496
|
+
(candidate: ServiceImage) => {
|
|
497
|
+
return candidate.service === service;
|
|
498
|
+
},
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
return image ? splitStages(image.productionDockerfile) : [];
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const stageNamed: (
|
|
505
|
+
service: string,
|
|
506
|
+
name: string,
|
|
507
|
+
) => DockerfileStage | undefined = (
|
|
508
|
+
service: string,
|
|
509
|
+
name: string,
|
|
510
|
+
): DockerfileStage | undefined => {
|
|
511
|
+
return stagesOf(service).find((stage: DockerfileStage) => {
|
|
512
|
+
return stage.name === name;
|
|
513
|
+
});
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
const editionServices: Array<string> = SERVICE_IMAGES.filter(
|
|
517
|
+
(image: ServiceImage) => {
|
|
518
|
+
return splitStages(image.productionDockerfile).some(
|
|
519
|
+
(stage: DockerfileStage) => {
|
|
520
|
+
return (
|
|
521
|
+
stage.name !== null && EDITION_STAGE_NAMES.includes(stage.name)
|
|
522
|
+
);
|
|
523
|
+
},
|
|
524
|
+
);
|
|
525
|
+
},
|
|
526
|
+
).map((image: ServiceImage) => {
|
|
527
|
+
return image.service;
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
const finalStages: Array<[string, string]> = editionServices.flatMap(
|
|
531
|
+
(service: string) => {
|
|
532
|
+
return EDITION_STAGE_NAMES.map((name: string): [string, string] => {
|
|
533
|
+
return [service, name];
|
|
534
|
+
});
|
|
535
|
+
},
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
test("the App is built this way, with exactly one stage per edition", () => {
|
|
539
|
+
expect(editionServices).toContain("App");
|
|
540
|
+
|
|
541
|
+
for (const name of EDITION_STAGE_NAMES) {
|
|
542
|
+
expect(
|
|
543
|
+
stagesOf("App").filter((stage: DockerfileStage) => {
|
|
544
|
+
return stage.name === name;
|
|
545
|
+
}),
|
|
546
|
+
).toHaveLength(1);
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
test.each(editionServices)(
|
|
551
|
+
"%s: the community stage is last, so a build without --target is the Community Edition",
|
|
552
|
+
(service: string) => {
|
|
553
|
+
const stages: Array<DockerfileStage> = stagesOf(service);
|
|
554
|
+
|
|
555
|
+
expect(stages[stages.length - 1]?.name).toBe("community");
|
|
556
|
+
},
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
test.each(finalStages)(
|
|
560
|
+
"%s: the %s stage runs as the node user",
|
|
561
|
+
(service: string, name: string) => {
|
|
562
|
+
expect(stageNamed(service, name)?.body).toMatch(USER_NODE_INSTRUCTION);
|
|
563
|
+
},
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
test.each(finalStages)(
|
|
567
|
+
"%s: the %s stage sets TS_NODE_TRANSPILE_ONLY=1",
|
|
568
|
+
(service: string, name: string) => {
|
|
569
|
+
expect(stageNamed(service, name)?.body).toMatch(TRANSPILE_ONLY_ENV);
|
|
570
|
+
},
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
test.each(finalStages)(
|
|
574
|
+
"%s: the %s stage boots with CMD npm start",
|
|
575
|
+
(service: string, name: string) => {
|
|
576
|
+
const body: string = stageNamed(service, name)?.body ?? "";
|
|
577
|
+
|
|
578
|
+
expect(
|
|
579
|
+
CMD_NPM_START_EXEC_FORM.test(body) ||
|
|
580
|
+
CMD_NPM_START_SHELL_FORM.test(body),
|
|
581
|
+
).toBe(true);
|
|
582
|
+
},
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
test.each(finalStages)(
|
|
586
|
+
"%s: the %s stage is type-checked at build time (npm run compile in its ancestry)",
|
|
587
|
+
(service: string, name: string) => {
|
|
588
|
+
const ancestry: Array<DockerfileStage> = stageAncestry(
|
|
589
|
+
stagesOf(service),
|
|
590
|
+
name,
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
expect(
|
|
594
|
+
ancestry.some((stage: DockerfileStage) => {
|
|
595
|
+
return RUN_COMPILE_INSTRUCTION.test(stage.body);
|
|
596
|
+
}),
|
|
597
|
+
).toBe(true);
|
|
598
|
+
},
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
test.each(editionServices)(
|
|
602
|
+
"%s: nothing the community stage is built from copies ee/",
|
|
603
|
+
(service: string) => {
|
|
604
|
+
const ancestry: Array<DockerfileStage> = stageAncestry(
|
|
605
|
+
stagesOf(service),
|
|
606
|
+
"community",
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
expect(ancestry.length).toBeGreaterThan(1);
|
|
610
|
+
expect(
|
|
611
|
+
ancestry
|
|
612
|
+
.filter((stage: DockerfileStage) => {
|
|
613
|
+
return COPIES_ENTERPRISE_DIRECTORY.test(stage.body);
|
|
614
|
+
})
|
|
615
|
+
.map((stage: DockerfileStage) => {
|
|
616
|
+
return stage.name;
|
|
617
|
+
}),
|
|
618
|
+
).toEqual([]);
|
|
619
|
+
},
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
test.each(editionServices)(
|
|
623
|
+
"%s: the enterprise stage's ancestry does copy ee/ (so the check above can fail)",
|
|
624
|
+
(service: string) => {
|
|
625
|
+
expect(
|
|
626
|
+
stageAncestry(stagesOf(service), "enterprise").some(
|
|
627
|
+
(stage: DockerfileStage) => {
|
|
628
|
+
return COPIES_ENTERPRISE_DIRECTORY.test(stage.body);
|
|
629
|
+
},
|
|
630
|
+
),
|
|
631
|
+
).toBe(true);
|
|
632
|
+
},
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
test.each(editionServices)(
|
|
636
|
+
"%s: the enterprise image is the community build plus ee/ (they share its layers)",
|
|
637
|
+
(service: string) => {
|
|
638
|
+
const communityParent: string | undefined = stageNamed(
|
|
639
|
+
service,
|
|
640
|
+
"community",
|
|
641
|
+
)?.from;
|
|
642
|
+
const enterpriseAncestry: Array<string | null> = stageAncestry(
|
|
643
|
+
stagesOf(service),
|
|
644
|
+
"enterprise",
|
|
645
|
+
).map((stage: DockerfileStage) => {
|
|
646
|
+
return stage.name;
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
expect(communityParent).toBeDefined();
|
|
650
|
+
expect(enterpriseAncestry).toContain(communityParent);
|
|
651
|
+
},
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
test.each(editionServices)(
|
|
655
|
+
"%s: only the enterprise stage sets ONEUPTIME_EDITION, to enterprise",
|
|
656
|
+
(service: string) => {
|
|
657
|
+
expect(stageNamed(service, "enterprise")?.body).toMatch(
|
|
658
|
+
SETS_ENTERPRISE_EDITION_MARKER,
|
|
659
|
+
);
|
|
660
|
+
expect(
|
|
661
|
+
stageAncestry(stagesOf(service), "community").some(
|
|
662
|
+
(stage: DockerfileStage) => {
|
|
663
|
+
return SETS_ONEUPTIME_EDITION.test(stage.body);
|
|
664
|
+
},
|
|
665
|
+
),
|
|
666
|
+
).toBe(false);
|
|
667
|
+
},
|
|
668
|
+
);
|
|
669
|
+
});
|
|
381
670
|
});
|
|
@@ -68,9 +68,13 @@ const CLICKHOUSE_HTTP_CONTAINER_PORT: string = "8123";
|
|
|
68
68
|
* The step that runs the suite:
|
|
69
69
|
* `docker compose --project-directory . -f Scripts/Dev/docker-compose.dev.yml up ... e2e`,
|
|
70
70
|
* or `... -f packages/E2E/docker-compose.e2e.yml up ... e2e` for the release image.
|
|
71
|
+
*
|
|
72
|
+
* `run --rm e2e <command>` counts too: the enterprise e2e jobs run one named
|
|
73
|
+
* suite per phase, which needs `run` to override the image's default CMD. Those
|
|
74
|
+
* suites use the same fixtures, so they need the same published port.
|
|
71
75
|
*/
|
|
72
76
|
const RUNS_E2E_SERVICE: RegExp =
|
|
73
|
-
/docker compose\s[^\n]*-f (?:Scripts\/Dev\/docker-compose\.dev|packages\/E2E\/docker-compose\.e2e)\.yml\s+up\b[^\n]*\se2e\b/;
|
|
77
|
+
/docker compose\s[^\n]*-f (?:Scripts\/Dev\/docker-compose\.dev|packages\/E2E\/docker-compose\.e2e)\.yml\s+(?:up|run)\b[^\n]*\se2e\b/;
|
|
74
78
|
const UP_SUBCOMMAND: RegExp = /\sup(?:\s|$)/;
|
|
75
79
|
const DOWN_SUBCOMMAND: RegExp = /\sdown(?:\s|$)/;
|
|
76
80
|
const ROOT_COMPOSE_FILE_FLAG: RegExp = /-f docker-compose\.yml\s/;
|