@oneuptime/common 13.0.8 → 14.0.1
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/Index.ts +4 -0
- package/Models/DatabaseModels/Monitor.ts +3 -2
- package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
- package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +2 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
- package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
- 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/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/UserAuthorization.ts +38 -16
- package/Server/Services/AuditLogService.ts +84 -1079
- package/Server/Services/DatabaseService.ts +10 -0
- package/Server/Services/EnterpriseLicenseService.ts +38 -0
- package/Server/Services/GlobalConfigService.ts +80 -1
- package/Server/Services/Index.ts +4 -0
- package/Server/Services/MonitorProbeService.ts +116 -20
- package/Server/Services/MonitorService.ts +59 -6
- 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/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
- package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
- package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
- package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
- package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +28 -0
- 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/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
- package/Server/Utils/AI/Toolbox/SloTools.ts +1 -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 +148 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +720 -465
- package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
- package/Server/Utils/Monitor/RootCauseList.ts +139 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +28 -0
- package/Server/Utils/Slo/SloRulePatternValidator.ts +42 -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/FeedFilterSortIntegration.test.tsx +1632 -0
- package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
- package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
- package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +60 -1
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +594 -3
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +646 -0
- package/Tests/App/Dashboard/ResourceFeed.test.tsx +612 -2
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
- 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/SloFeed.test.tsx +106 -1
- package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
- package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
- package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
- package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
- package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -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/Models/DatabaseModels/ResourceFeedModels.test.ts +0 -8
- package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
- package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- 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/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
- package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
- 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/DatabaseServiceRuleCriteriaValidation.test.ts +104 -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/RuleRun/LabelRuleEnginesGroupBRun.test.ts +48 -1
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -0
- package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +1170 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +162 -0
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
- 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/FeedEventTypeFilterQuery.test.ts +745 -0
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
- package/Tests/Server/Types/Markdown.test.ts +183 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
- package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +10 -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/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +1339 -96
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1101 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
- package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
- package/Tests/Server/Utils/Monitor/RootCauseList.test.ts +252 -0
- package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
- package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -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/API/Route.test.ts +56 -0
- package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -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/FeedOptions.test.ts +1404 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
- package/Tests/UI/Components/Icon.test.tsx +26 -0
- package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
- package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
- package/Tests/UI/Components/useFeedItemsViewKey.test.tsx +1017 -0
- package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -0
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
- package/Tests/Utils/HostAddressUtil.test.ts +138 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +102 -0
- package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
- package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
- package/Tests/Utils/Slo/SloDuration.test.ts +115 -24
- package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
- package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
- package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -0
- package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
- package/Types/API/Hostname.ts +63 -16
- package/Types/API/Route.ts +19 -2
- package/Types/API/URL.ts +26 -2
- 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/MetricMonitor/MetricCriteriaContext.ts +6 -5
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
- package/Types/Monitor/MonitorStep.ts +6 -1
- package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
- package/Types/Permission.ts +90 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
- package/Types/Rules/RuleRun.ts +4 -0
- 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/Feed/FeedOptions.ts +427 -0
- package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
- package/UI/Components/Feed/useFeedItems.ts +28 -9
- package/UI/Components/Feed/useFeedOptions.ts +225 -0
- package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
- package/UI/Components/Icon/Icon.tsx +1 -1
- 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/LabelRuleImportExport.ts +27 -9
- package/Utils/Monitor/MetricValueFormatter.ts +2 -2
- package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
- package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
- package/Utils/Rules/MonitorTypeRuleCriteria.ts +31 -0
- package/Utils/Slo/SloDuration.ts +22 -6
- package/Utils/Slo/SloHealth.ts +8 -7
- package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
- package/Utils/Slo/SloOverviewText.ts +33 -0
- package/Utils/Slo/SloProjection.ts +4 -3
- 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/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.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/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
- 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/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -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/DatabaseService.js +7 -0
- package/build/dist/Server/Services/DatabaseService.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/Index.js +4 -0
- package/build/dist/Server/Services/Index.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 +48 -6
- 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/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +21 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.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/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.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 +67 -0
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
- 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/Criteria/MetricMonitorCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +585 -367
- 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/Monitor/RootCauseList.js +101 -0
- package/build/dist/Server/Utils/Monitor/RootCauseList.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +28 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.js +28 -0
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.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/API/Route.js +18 -2
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/API/URL.js +23 -2
- package/build/dist/Types/API/URL.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/Permission.js +84 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +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/Feed/FeedOptions.js +235 -0
- package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
- package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
- package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
- package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
- package/build/dist/UI/Components/Feed/useFeedOptions.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/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.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/LabelRuleImportExport.js +22 -6
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +2 -2
- 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/Rules/MonitorTypeRuleCriteria.js +18 -0
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.js.map +1 -0
- package/build/dist/Utils/Slo/SloDuration.js +13 -5
- package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
- package/build/dist/Utils/Slo/SloHealth.js +8 -7
- package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
- package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
- package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
- package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
- 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
|
@@ -21,6 +21,11 @@ import { MonitorFeedEventType } from "../../Models/DatabaseModels/MonitorFeed";
|
|
|
21
21
|
import MonitorStatus from "../../Models/DatabaseModels/MonitorStatus";
|
|
22
22
|
import MonitorStatusService from "./MonitorStatusService";
|
|
23
23
|
import ServerException from "../../Types/Exception/ServerException";
|
|
24
|
+
import {
|
|
25
|
+
MonitorUptimeDailyAggregate,
|
|
26
|
+
UptimeDailyAggregate,
|
|
27
|
+
UptimeDayBucket,
|
|
28
|
+
} from "../../Types/StatusPage/UptimeDailyAggregate";
|
|
24
29
|
|
|
25
30
|
/*
|
|
26
31
|
* Thrown by onBeforeCreate when the incoming status is the same as the status of
|
|
@@ -45,6 +50,285 @@ export class Service extends DatabaseService<MonitorStatusTimeline> {
|
|
|
45
50
|
super(MonitorStatusTimeline);
|
|
46
51
|
}
|
|
47
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Per-monitor, per-day status durations for the status-page uptime bars.
|
|
55
|
+
*
|
|
56
|
+
* WHY THIS EXISTS
|
|
57
|
+
*
|
|
58
|
+
* The bars used to be painted from raw rows fetched with a single
|
|
59
|
+
* `limit: LIMIT_MAX` (10,000) across EVERY monitor on the page, sorted
|
|
60
|
+
* `startsAt DESC`. Measured on a real status page: 254,550 rows matched the
|
|
61
|
+
* 60-day window and 10,000 came back. The oldest surviving row started four
|
|
62
|
+
* days before the request, so 56 of 60 bars were painted from data that had
|
|
63
|
+
* been silently dropped - and the uptime percentage was computed from the
|
|
64
|
+
* same 3.9%.
|
|
65
|
+
*
|
|
66
|
+
* The cap being GLOBAL across monitors is what makes it vicious. A quiet,
|
|
67
|
+
* healthy monitor's covering row has an OLD `startsAt`, so under
|
|
68
|
+
* `startsAt DESC` it sorts last and is cut first: on the page that reported
|
|
69
|
+
* this, three flapping monitors held 9,995 of the 10,000 slots. The
|
|
70
|
+
* healthier a monitor is, the more likely it is to lose its entire history
|
|
71
|
+
* to a noisy neighbour on the same page.
|
|
72
|
+
*
|
|
73
|
+
* Shipping more rows cannot fix it - 254,550 rows is ~105 MB of this
|
|
74
|
+
* codebase's typed JSON, into a response held in a 500-entry in-memory
|
|
75
|
+
* cache and then filtered per-bar on the client's main thread. So this
|
|
76
|
+
* returns DURATIONS. The result is O(monitors x days x statuses) and cannot
|
|
77
|
+
* be inflated by a flapping monitor.
|
|
78
|
+
*
|
|
79
|
+
* THE SQL, AND THE THREE TRAPS IN IT
|
|
80
|
+
*
|
|
81
|
+
* 1. An open row (`endsAt IS NULL`) is capped at the NEXT row's `startsAt`,
|
|
82
|
+
* not at `now()`. `COALESCE(endsAt, now())` would overlap the successor
|
|
83
|
+
* and double-count that span. This mirrors what
|
|
84
|
+
* UptimeUtil.getMonitorEventsForId already does client-side.
|
|
85
|
+
*
|
|
86
|
+
* 2. Periods are split into days ARITHMETICALLY (`generate_series` over the
|
|
87
|
+
* days a period touches) and then hash-aggregated, rather than joined
|
|
88
|
+
* against a bucket grid. Both give the same answer; the join does not
|
|
89
|
+
* scale. Joining emitted one row per timeline row - 255,486 on the
|
|
90
|
+
* reporting page - which then had to be sorted for the GroupAggregate
|
|
91
|
+
* and spilled 26 MB to disk, ~4.7s. Splitting first aggregates 255k rows
|
|
92
|
+
* into ~450 with no sort: ~1.5s.
|
|
93
|
+
*
|
|
94
|
+
* A period that lies inside one day yields exactly one segment, which is
|
|
95
|
+
* the overwhelmingly common case - a flapping monitor's rows last well
|
|
96
|
+
* under a second. Only a long-running period fans out, one segment per
|
|
97
|
+
* day it spans.
|
|
98
|
+
*
|
|
99
|
+
* 3. Seconds are summed as `double precision` and NEVER rounded per group.
|
|
100
|
+
* Rounding each (monitor, day, status) sum to a bigint loses up to half a
|
|
101
|
+
* second per status, so a fully covered day with two statuses reported
|
|
102
|
+
* 86,399 of 86,400 seconds - a day that is complete looking one second
|
|
103
|
+
* short. Coverage is compared with a tolerance by the client rather than
|
|
104
|
+
* for exact equality.
|
|
105
|
+
*
|
|
106
|
+
* A trap that is worth recording even though this SQL no longer contains it:
|
|
107
|
+
* PostgreSQL's LEAST/GREATEST IGNORE NULLs, returning the smallest or
|
|
108
|
+
* largest NON-NULL argument. An earlier draft joined rows to buckets, and on
|
|
109
|
+
* an unmatched LEFT JOIN row `LEAST(NULL, bucketEnd) - GREATEST(NULL,
|
|
110
|
+
* bucketStart)` silently became a FULL DAY of coverage - reporting a monitor
|
|
111
|
+
* as up before it existed. It was caught against real data, not in review.
|
|
112
|
+
* Any future rewrite that reintroduces a row-to-bucket join must handle it.
|
|
113
|
+
*
|
|
114
|
+
* Day buckets are local calendar days converted to UTC instants, so a DST
|
|
115
|
+
* day is genuinely 23 or 25 hours - `daySeconds` is never hardcoded to
|
|
116
|
+
* 86400. The window's first and last buckets are clipped to the window, and
|
|
117
|
+
* the end is clamped to `now()` so a future day is never emitted and can
|
|
118
|
+
* never be reported as 100%.
|
|
119
|
+
*/
|
|
120
|
+
@CaptureSpan()
|
|
121
|
+
public async getDailyUptimeAggregate(data: {
|
|
122
|
+
monitorIds: Array<ObjectID>;
|
|
123
|
+
startDate: Date;
|
|
124
|
+
endDate: Date;
|
|
125
|
+
timezone?: string | undefined;
|
|
126
|
+
}): Promise<UptimeDailyAggregate> {
|
|
127
|
+
const empty: UptimeDailyAggregate = {
|
|
128
|
+
monitors: [],
|
|
129
|
+
isComplete: true,
|
|
130
|
+
completeFrom: null,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
if (data.monitorIds.length === 0) {
|
|
134
|
+
return empty;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/*
|
|
138
|
+
* The overlap predicate is preserved verbatim from the row fetch this
|
|
139
|
+
* replaces: a row counts if it started on or before the window ends and
|
|
140
|
+
* either ended on or after the window started or is still open. The
|
|
141
|
+
* inclusive `>=` is deliberate - a row ending exactly at startDate still
|
|
142
|
+
* overlaps - and a strict `>` is NOT interchangeable.
|
|
143
|
+
*/
|
|
144
|
+
const sql: string = `
|
|
145
|
+
WITH params AS (
|
|
146
|
+
SELECT $2::timestamptz AS win_start,
|
|
147
|
+
LEAST($3::timestamptz, now()) AS eff_end,
|
|
148
|
+
$4::text AS tz
|
|
149
|
+
),
|
|
150
|
+
mons AS (
|
|
151
|
+
SELECT UNNEST($1::uuid[]) AS monitor_id
|
|
152
|
+
),
|
|
153
|
+
src AS (
|
|
154
|
+
SELECT t."monitorId" AS monitor_id,
|
|
155
|
+
t."monitorStatusId" AS status_id,
|
|
156
|
+
GREATEST(t."startsAt", p.win_start) AS s,
|
|
157
|
+
LEAST(
|
|
158
|
+
COALESCE(
|
|
159
|
+
t."endsAt",
|
|
160
|
+
LEAD(t."startsAt") OVER (
|
|
161
|
+
PARTITION BY t."monitorId" ORDER BY t."startsAt"
|
|
162
|
+
),
|
|
163
|
+
p.eff_end
|
|
164
|
+
),
|
|
165
|
+
p.eff_end
|
|
166
|
+
) AS e
|
|
167
|
+
FROM "MonitorStatusTimeline" t
|
|
168
|
+
CROSS JOIN params p
|
|
169
|
+
WHERE t."monitorId" = ANY($1::uuid[])
|
|
170
|
+
AND t."deletedAt" IS NULL
|
|
171
|
+
AND t."startsAt" <= p.eff_end
|
|
172
|
+
AND (t."endsAt" >= p.win_start OR t."endsAt" IS NULL)
|
|
173
|
+
),
|
|
174
|
+
split AS (
|
|
175
|
+
SELECT src.monitor_id,
|
|
176
|
+
src.status_id,
|
|
177
|
+
(gs AT TIME ZONE p.tz) AS day_start,
|
|
178
|
+
GREATEST(src.s, (gs AT TIME ZONE p.tz)) AS seg_start,
|
|
179
|
+
LEAST(src.e, ((gs + interval '1 day') AT TIME ZONE p.tz))
|
|
180
|
+
AS seg_end
|
|
181
|
+
FROM src
|
|
182
|
+
CROSS JOIN params p,
|
|
183
|
+
LATERAL generate_series(
|
|
184
|
+
date_trunc('day', src.s AT TIME ZONE p.tz),
|
|
185
|
+
date_trunc('day', (src.e - interval '1 microsecond') AT TIME ZONE p.tz),
|
|
186
|
+
interval '1 day'
|
|
187
|
+
) AS gs
|
|
188
|
+
WHERE src.e > src.s
|
|
189
|
+
),
|
|
190
|
+
agg AS (
|
|
191
|
+
SELECT monitor_id,
|
|
192
|
+
day_start,
|
|
193
|
+
status_id,
|
|
194
|
+
SUM(EXTRACT(EPOCH FROM (seg_end - seg_start)))::double precision
|
|
195
|
+
AS seconds
|
|
196
|
+
FROM split
|
|
197
|
+
WHERE seg_end > seg_start
|
|
198
|
+
GROUP BY monitor_id, day_start, status_id
|
|
199
|
+
),
|
|
200
|
+
buckets AS (
|
|
201
|
+
SELECT (d AT TIME ZONE p.tz) AS day_start,
|
|
202
|
+
GREATEST((d AT TIME ZONE p.tz), p.win_start) AS bucket_start,
|
|
203
|
+
LEAST(((d + interval '1 day') AT TIME ZONE p.tz), p.eff_end)
|
|
204
|
+
AS bucket_end
|
|
205
|
+
FROM params p,
|
|
206
|
+
generate_series(
|
|
207
|
+
date_trunc('day', p.win_start AT TIME ZONE p.tz),
|
|
208
|
+
date_trunc('day', p.eff_end AT TIME ZONE p.tz),
|
|
209
|
+
interval '1 day'
|
|
210
|
+
) AS d
|
|
211
|
+
)
|
|
212
|
+
SELECT m.monitor_id AS "monitorId",
|
|
213
|
+
b.bucket_start AS "bucketStart",
|
|
214
|
+
b.bucket_end AS "bucketEnd",
|
|
215
|
+
EXTRACT(EPOCH FROM (b.bucket_end - b.bucket_start))::bigint
|
|
216
|
+
AS "daySeconds",
|
|
217
|
+
a.status_id AS "monitorStatusId",
|
|
218
|
+
COALESCE(a.seconds, 0)::double precision AS "seconds"
|
|
219
|
+
FROM mons m
|
|
220
|
+
CROSS JOIN buckets b
|
|
221
|
+
LEFT JOIN agg a ON a.monitor_id = m.monitor_id
|
|
222
|
+
AND a.day_start = b.day_start
|
|
223
|
+
WHERE b.bucket_end > b.bucket_start
|
|
224
|
+
ORDER BY m.monitor_id, b.bucket_start
|
|
225
|
+
`;
|
|
226
|
+
|
|
227
|
+
const rows: Array<{
|
|
228
|
+
monitorId: string;
|
|
229
|
+
bucketStart: Date;
|
|
230
|
+
bucketEnd: Date;
|
|
231
|
+
daySeconds: string | number;
|
|
232
|
+
monitorStatusId: string | null;
|
|
233
|
+
seconds: string | number;
|
|
234
|
+
}> = await this.getRepository().manager.query(sql, [
|
|
235
|
+
data.monitorIds.map((id: ObjectID) => {
|
|
236
|
+
return id.toString();
|
|
237
|
+
}),
|
|
238
|
+
data.startDate,
|
|
239
|
+
data.endDate,
|
|
240
|
+
data.timezone || "UTC",
|
|
241
|
+
]);
|
|
242
|
+
|
|
243
|
+
return Service.toUptimeDailyAggregate(rows);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/*
|
|
247
|
+
* Exported shape-only step, so the bucketing can be tested without a
|
|
248
|
+
* database. One row per (monitor, day, status); a day with no coverage
|
|
249
|
+
* comes back as a single row with a NULL status and zero seconds, which is
|
|
250
|
+
* what makes a no-data day distinguishable from a day spent up.
|
|
251
|
+
*/
|
|
252
|
+
public static toUptimeDailyAggregate(
|
|
253
|
+
rows: Array<{
|
|
254
|
+
monitorId: string;
|
|
255
|
+
bucketStart: Date;
|
|
256
|
+
bucketEnd: Date;
|
|
257
|
+
daySeconds: string | number;
|
|
258
|
+
monitorStatusId: string | null;
|
|
259
|
+
seconds: string | number;
|
|
260
|
+
}>,
|
|
261
|
+
): UptimeDailyAggregate {
|
|
262
|
+
const byMonitor: Map<string, Map<number, UptimeDayBucket>> = new Map();
|
|
263
|
+
|
|
264
|
+
for (const row of rows) {
|
|
265
|
+
let buckets: Map<number, UptimeDayBucket> | undefined = byMonitor.get(
|
|
266
|
+
row.monitorId,
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
if (!buckets) {
|
|
270
|
+
buckets = new Map<number, UptimeDayBucket>();
|
|
271
|
+
byMonitor.set(row.monitorId, buckets);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const bucketStart: Date = new Date(row.bucketStart);
|
|
275
|
+
const key: number = bucketStart.getTime();
|
|
276
|
+
|
|
277
|
+
let bucket: UptimeDayBucket | undefined = buckets.get(key);
|
|
278
|
+
|
|
279
|
+
if (!bucket) {
|
|
280
|
+
bucket = {
|
|
281
|
+
bucketStart: bucketStart,
|
|
282
|
+
bucketEnd: new Date(row.bucketEnd),
|
|
283
|
+
daySeconds: Number(row.daySeconds),
|
|
284
|
+
coveredSeconds: 0,
|
|
285
|
+
statusDurations: [],
|
|
286
|
+
};
|
|
287
|
+
buckets.set(key, bucket);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const seconds: number = Number(row.seconds);
|
|
291
|
+
|
|
292
|
+
/*
|
|
293
|
+
* A NULL status is the LEFT JOIN's "nothing overlapped this day" row.
|
|
294
|
+
* It must not become a status duration, and it contributes no coverage.
|
|
295
|
+
*/
|
|
296
|
+
if (!row.monitorStatusId || seconds <= 0) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
bucket.coveredSeconds += seconds;
|
|
301
|
+
bucket.statusDurations.push({
|
|
302
|
+
monitorStatusId: new ObjectID(row.monitorStatusId),
|
|
303
|
+
seconds: seconds,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const monitors: Array<MonitorUptimeDailyAggregate> = [];
|
|
308
|
+
|
|
309
|
+
for (const [monitorId, buckets] of byMonitor) {
|
|
310
|
+
monitors.push({
|
|
311
|
+
monitorId: new ObjectID(monitorId),
|
|
312
|
+
buckets: Array.from(buckets.values()).sort(
|
|
313
|
+
(a: UptimeDayBucket, b: UptimeDayBucket) => {
|
|
314
|
+
return a.bucketStart.getTime() - b.bucketStart.getTime();
|
|
315
|
+
},
|
|
316
|
+
),
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/*
|
|
321
|
+
* There is no cap in this path, so the window is always fully covered.
|
|
322
|
+
* The fields exist so that a bound added later has to SHOW rather than be
|
|
323
|
+
* absorbed into a green bar - which is the failure this replaces.
|
|
324
|
+
*/
|
|
325
|
+
return {
|
|
326
|
+
monitors: monitors,
|
|
327
|
+
isComplete: true,
|
|
328
|
+
completeFrom: null,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
48
332
|
@CaptureSpan()
|
|
49
333
|
public override async create(
|
|
50
334
|
createBy: CreateBy<MonitorStatusTimeline>,
|
|
@@ -10,6 +10,7 @@ import DeleteBy from "../Types/Database/DeleteBy";
|
|
|
10
10
|
import UpdateBy from "../Types/Database/UpdateBy";
|
|
11
11
|
import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
|
|
12
12
|
import MonitorStepsProjectValidator from "../Utils/Monitor/MonitorStepsProjectValidator";
|
|
13
|
+
import MonitoringIntervalValidator from "../Utils/Monitor/MonitoringIntervalValidator";
|
|
13
14
|
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
14
15
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
15
16
|
import Includes from "../../Types/BaseDatabase/Includes";
|
|
@@ -116,6 +117,18 @@ export class Service extends DatabaseService<Model> {
|
|
|
116
117
|
protected override async onBeforeCreate(
|
|
117
118
|
createBy: CreateBy<Model>,
|
|
118
119
|
): Promise<OnCreate<Model>> {
|
|
120
|
+
/*
|
|
121
|
+
* A template's interval is copied onto every monitor it creates or syncs,
|
|
122
|
+
* so an unvalidated value here becomes many broken monitors later.
|
|
123
|
+
*/
|
|
124
|
+
if (createBy.data.monitoringInterval !== undefined) {
|
|
125
|
+
(createBy.data as unknown as Record<string, unknown>)[
|
|
126
|
+
"monitoringInterval"
|
|
127
|
+
] = MonitoringIntervalValidator.validateAndNormalize(
|
|
128
|
+
createBy.data.monitoringInterval,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
119
132
|
this.validateSyncFields(
|
|
120
133
|
createBy.data.monitorSteps,
|
|
121
134
|
createBy.data.monitorType,
|
|
@@ -132,6 +145,19 @@ export class Service extends DatabaseService<Model> {
|
|
|
132
145
|
protected override async onBeforeUpdate(
|
|
133
146
|
updateBy: UpdateBy<Model>,
|
|
134
147
|
): Promise<OnUpdate<Model>> {
|
|
148
|
+
/*
|
|
149
|
+
* Ahead of the early return below: an update that touches only the
|
|
150
|
+
* interval must still be validated, and key presence is the test for the
|
|
151
|
+
* same reason it is in MonitorService.onBeforeUpdate.
|
|
152
|
+
*/
|
|
153
|
+
if (Object.keys(updateBy.data || {}).includes("monitoringInterval")) {
|
|
154
|
+
(updateBy.data as unknown as Record<string, unknown>)[
|
|
155
|
+
"monitoringInterval"
|
|
156
|
+
] = MonitoringIntervalValidator.validateAndNormalize(
|
|
157
|
+
updateBy.data.monitoringInterval as string | null | undefined,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
135
161
|
if (!updateBy.data.monitorSteps && !updateBy.data.monitorType) {
|
|
136
162
|
return { updateBy, carryForward: null };
|
|
137
163
|
}
|
|
@@ -14,6 +14,9 @@ import { OnCreate, OnDelete, OnFind, OnUpdate } from "../Types/Database/Hooks";
|
|
|
14
14
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
15
15
|
import UpdateBy from "../Types/Database/UpdateBy";
|
|
16
16
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
17
|
+
import EditionEnforcement from "../Utils/EditionEnforcement";
|
|
18
|
+
import EnterpriseEdition from "../Enterprise/EnterpriseEdition";
|
|
19
|
+
import EnterpriseFeature from "../Enterprise/EnterpriseFeature";
|
|
17
20
|
import Errors from "../Utils/Errors";
|
|
18
21
|
import ProductAnalytics from "../Utils/ProductAnalytics";
|
|
19
22
|
import MarketingEventUtil, {
|
|
@@ -142,6 +145,107 @@ export type ProjectBalanceColumnName =
|
|
|
142
145
|
*/
|
|
143
146
|
export const MAX_BALANCE_ADJUSTMENT_IN_USD_CENTS: number = 10_000 * 100;
|
|
144
147
|
|
|
148
|
+
/*
|
|
149
|
+
* The project columns that hold its SSO requirement: the ones onFindSuccess
|
|
150
|
+
* masks and onBeforeUpdate guards while SSO is not active.
|
|
151
|
+
*/
|
|
152
|
+
export const PROJECT_SSO_REQUIREMENT_COLUMNS: ReadonlyArray<string> = [
|
|
153
|
+
"requireSsoForLogin",
|
|
154
|
+
"requireSsoWithSsoProviderId",
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
// The project columns that decide what the audit log records and keeps.
|
|
158
|
+
export interface ProjectAuditLogSettings {
|
|
159
|
+
enableAuditLogs: boolean;
|
|
160
|
+
storeSystemEventsInAuditLogs: boolean;
|
|
161
|
+
auditLogsRetentionInDays: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/*
|
|
165
|
+
* The settings as a project row holds them: a column that was not selected,
|
|
166
|
+
* or predates its default, reads as undefined or null.
|
|
167
|
+
*/
|
|
168
|
+
export interface StoredProjectAuditLogSettings {
|
|
169
|
+
enableAuditLogs?: boolean | null | undefined;
|
|
170
|
+
storeSystemEventsInAuditLogs?: boolean | null | undefined;
|
|
171
|
+
auditLogsRetentionInDays?: number | null | undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// What a new project starts with: the three columns' defaults.
|
|
175
|
+
export const DEFAULT_PROJECT_AUDIT_LOG_SETTINGS: Readonly<ProjectAuditLogSettings> =
|
|
176
|
+
{
|
|
177
|
+
enableAuditLogs: false,
|
|
178
|
+
storeSystemEventsInAuditLogs: false,
|
|
179
|
+
auditLogsRetentionInDays: 7,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/*
|
|
183
|
+
* The narrowest settings there are. A write that widens even these could
|
|
184
|
+
* widen some project's; one that does not can only keep or narrow.
|
|
185
|
+
*/
|
|
186
|
+
const NARROWEST_AUDIT_LOG_SETTINGS: Readonly<ProjectAuditLogSettings> = {
|
|
187
|
+
enableAuditLogs: false,
|
|
188
|
+
storeSystemEventsInAuditLogs: false,
|
|
189
|
+
auditLogsRetentionInDays: Number.NEGATIVE_INFINITY,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/*
|
|
193
|
+
* Whether a written switch value turns the switch on. Only false (and a
|
|
194
|
+
* missing value) means off: anything else a database could read as true
|
|
195
|
+
* ("true", 1) counts as on, so an odd value is judged by the license check
|
|
196
|
+
* rather than waved through.
|
|
197
|
+
*/
|
|
198
|
+
const isSwitchedOn: (value: unknown) => boolean = (value: unknown): boolean => {
|
|
199
|
+
return value !== undefined && value !== null && value !== false;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/*
|
|
203
|
+
* Whether writing `requested` over `current` widens audit logging: switches
|
|
204
|
+
* it on, starts recording system events, or keeps entries for longer.
|
|
205
|
+
* Switching off, recording less and shortening retention never widen, and
|
|
206
|
+
* neither does writing the value a project already has.
|
|
207
|
+
*/
|
|
208
|
+
export const widensAuditLogging: (
|
|
209
|
+
requested: Record<string, unknown>,
|
|
210
|
+
current: StoredProjectAuditLogSettings,
|
|
211
|
+
) => boolean = (
|
|
212
|
+
requested: Record<string, unknown>,
|
|
213
|
+
current: StoredProjectAuditLogSettings,
|
|
214
|
+
): boolean => {
|
|
215
|
+
if (
|
|
216
|
+
isSwitchedOn(requested["enableAuditLogs"]) &&
|
|
217
|
+
current.enableAuditLogs !== true
|
|
218
|
+
) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (
|
|
223
|
+
isSwitchedOn(requested["storeSystemEventsInAuditLogs"]) &&
|
|
224
|
+
current.storeSystemEventsInAuditLogs !== true
|
|
225
|
+
) {
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const requestedRetention: unknown = requested["auditLogsRetentionInDays"];
|
|
230
|
+
|
|
231
|
+
if (requestedRetention === undefined || requestedRetention === null) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const requestedDays: number =
|
|
236
|
+
typeof requestedRetention === "number"
|
|
237
|
+
? requestedRetention
|
|
238
|
+
: Number(requestedRetention);
|
|
239
|
+
|
|
240
|
+
const currentDays: number =
|
|
241
|
+
typeof current.auditLogsRetentionInDays === "number"
|
|
242
|
+
? current.auditLogsRetentionInDays
|
|
243
|
+
: DEFAULT_PROJECT_AUDIT_LOG_SETTINGS.auditLogsRetentionInDays;
|
|
244
|
+
|
|
245
|
+
// A value that is not a number cannot be judged: treat it as widening.
|
|
246
|
+
return !Number.isFinite(requestedDays) || requestedDays > currentDays;
|
|
247
|
+
};
|
|
248
|
+
|
|
145
249
|
export class ProjectService extends DatabaseService<Model> {
|
|
146
250
|
/*
|
|
147
251
|
* Suppresses repeated `lastActive` UPDATEs from a single API node. 60s of
|
|
@@ -231,6 +335,17 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
231
335
|
);
|
|
232
336
|
}
|
|
233
337
|
|
|
338
|
+
// A new project starts from the defaults, so any audit setting above them widens.
|
|
339
|
+
await this.assertAuditLogSettingsChangeIsLicensed({
|
|
340
|
+
requested: data.data as unknown as Record<string, unknown>,
|
|
341
|
+
props: data.props,
|
|
342
|
+
loadCurrentSettings: async (): Promise<
|
|
343
|
+
Array<StoredProjectAuditLogSettings>
|
|
344
|
+
> => {
|
|
345
|
+
return [DEFAULT_PROJECT_AUDIT_LOG_SETTINGS];
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
|
|
234
349
|
logger.debug("Creating project for user " + data.props.userId, {
|
|
235
350
|
userId: data.props.userId?.toString(),
|
|
236
351
|
} as LogAttributes);
|
|
@@ -546,6 +661,17 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
546
661
|
protected override async onBeforeUpdate(
|
|
547
662
|
updateBy: UpdateBy<Model>,
|
|
548
663
|
): Promise<OnUpdate<Model>> {
|
|
664
|
+
/*
|
|
665
|
+
* While SSO is not active this caller only ever read the requirement as
|
|
666
|
+
* off (see onFindSuccess), so a write of it must not switch the stored
|
|
667
|
+
* requirement off, or set one nobody could see. Drops or refuses it.
|
|
668
|
+
*/
|
|
669
|
+
EditionEnforcement.guardSsoRequirementWrite({
|
|
670
|
+
props: updateBy.props,
|
|
671
|
+
data: updateBy.data as unknown as Record<string, unknown>,
|
|
672
|
+
columns: PROJECT_SSO_REQUIREMENT_COLUMNS,
|
|
673
|
+
});
|
|
674
|
+
|
|
549
675
|
/*
|
|
550
676
|
* Any project field could have changed; invalidate the in-process cache
|
|
551
677
|
* of the SSO flag. Cheap to refetch on the next request.
|
|
@@ -560,6 +686,19 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
560
686
|
|
|
561
687
|
this.applyDataResidencyRules(updateBy.data);
|
|
562
688
|
|
|
689
|
+
await this.assertAuditLogSettingsChangeIsLicensed({
|
|
690
|
+
requested: updateBy.data as unknown as Record<string, unknown>,
|
|
691
|
+
props: updateBy.props,
|
|
692
|
+
loadCurrentSettings: (): Promise<
|
|
693
|
+
Array<StoredProjectAuditLogSettings>
|
|
694
|
+
> => {
|
|
695
|
+
return this.findAuditLogSettingsInCallerScope({
|
|
696
|
+
query: updateBy.query,
|
|
697
|
+
props: updateBy.props,
|
|
698
|
+
});
|
|
699
|
+
},
|
|
700
|
+
});
|
|
701
|
+
|
|
563
702
|
if (IsBillingEnabled) {
|
|
564
703
|
if (
|
|
565
704
|
updateBy.data.businessDetails ||
|
|
@@ -653,6 +792,113 @@ export class ProjectService extends DatabaseService<Model> {
|
|
|
653
792
|
return { updateBy, carryForward: [] };
|
|
654
793
|
}
|
|
655
794
|
|
|
795
|
+
/*
|
|
796
|
+
* Audit logging is an Enterprise feature (EnterpriseFeature.AuditLogs), but
|
|
797
|
+
* its settings are ordinary Project columns, gated only by the plan when
|
|
798
|
+
* billing is on. Without billing, switching audit logging on - or widening
|
|
799
|
+
* it: recording system events too, keeping entries longer - needs a license
|
|
800
|
+
* that includes audit logs, like creating or changing any other enterprise
|
|
801
|
+
* configuration. On the Community Edition it is refused.
|
|
802
|
+
*
|
|
803
|
+
* Never refused: switching it off or narrowing it (an administrator must
|
|
804
|
+
* always be able to record less), a write that keeps a project's settings
|
|
805
|
+
* as they are (the settings form sends every field), internal root writes,
|
|
806
|
+
* and anything when billing is on, where the plan gates apply. Whether
|
|
807
|
+
* entries are recorded at all is decided per entry by the recorder
|
|
808
|
+
* (EnterpriseEdition.isFeatureActive(AuditLogs)): recording stops while a
|
|
809
|
+
* self-hosted license is lapsed and resumes when it is renewed.
|
|
810
|
+
*
|
|
811
|
+
* The current settings are loaded only when the write could widen them and
|
|
812
|
+
* the license does not allow it.
|
|
813
|
+
*/
|
|
814
|
+
@CaptureSpan()
|
|
815
|
+
public async assertAuditLogSettingsChangeIsLicensed(data: {
|
|
816
|
+
requested: Record<string, unknown>;
|
|
817
|
+
props: DatabaseCommonInteractionProps;
|
|
818
|
+
// The settings the write replaces, one entry per project it writes.
|
|
819
|
+
loadCurrentSettings: () => Promise<Array<StoredProjectAuditLogSettings>>;
|
|
820
|
+
}): Promise<void> {
|
|
821
|
+
if (IsBillingEnabled || data.props.isRoot) {
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
if (!widensAuditLogging(data.requested, NARROWEST_AUDIT_LOG_SETTINGS)) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
if (
|
|
830
|
+
await EnterpriseEdition.isFeatureAvailable(EnterpriseFeature.AuditLogs)
|
|
831
|
+
) {
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
const currentSettings: Array<StoredProjectAuditLogSettings> =
|
|
836
|
+
await data.loadCurrentSettings();
|
|
837
|
+
|
|
838
|
+
const widens: boolean = currentSettings.some(
|
|
839
|
+
(current: StoredProjectAuditLogSettings): boolean => {
|
|
840
|
+
return widensAuditLogging(data.requested, current);
|
|
841
|
+
},
|
|
842
|
+
);
|
|
843
|
+
|
|
844
|
+
if (!widens) {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Throws the Community Edition or the license message, as the case may be.
|
|
849
|
+
await EnterpriseEdition.assertFeatureAvailable(EnterpriseFeature.AuditLogs);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/*
|
|
853
|
+
* The audit settings of the projects an update matches, limited to the
|
|
854
|
+
* projects the caller belongs to (every project for a master admin). This
|
|
855
|
+
* runs in onBeforeUpdate, before the permission check: a refusal that
|
|
856
|
+
* depended on another project's settings would disclose them, and a write
|
|
857
|
+
* to a project the caller does not belong to is refused by the permission
|
|
858
|
+
* check that follows anyway.
|
|
859
|
+
*/
|
|
860
|
+
private async findAuditLogSettingsInCallerScope(data: {
|
|
861
|
+
query: Query<Model>;
|
|
862
|
+
props: DatabaseCommonInteractionProps;
|
|
863
|
+
}): Promise<Array<StoredProjectAuditLogSettings>> {
|
|
864
|
+
const projects: Array<Model> = await this.findBy({
|
|
865
|
+
query: data.query,
|
|
866
|
+
select: {
|
|
867
|
+
_id: true,
|
|
868
|
+
enableAuditLogs: true,
|
|
869
|
+
storeSystemEventsInAuditLogs: true,
|
|
870
|
+
auditLogsRetentionInDays: true,
|
|
871
|
+
},
|
|
872
|
+
limit: LIMIT_MAX,
|
|
873
|
+
skip: 0,
|
|
874
|
+
props: { isRoot: true },
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
const callerProjectIds: Array<string> = data.props.isMasterAdmin
|
|
878
|
+
? []
|
|
879
|
+
: [
|
|
880
|
+
...(data.props.tenantId ? [data.props.tenantId] : []),
|
|
881
|
+
...(data.props.userGlobalAccessPermission?.projectIds || []),
|
|
882
|
+
].map((projectId: ObjectID): string => {
|
|
883
|
+
return projectId.toString();
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
return projects
|
|
887
|
+
.filter((project: Model): boolean => {
|
|
888
|
+
return (
|
|
889
|
+
Boolean(data.props.isMasterAdmin) ||
|
|
890
|
+
callerProjectIds.includes(String(project._id))
|
|
891
|
+
);
|
|
892
|
+
})
|
|
893
|
+
.map((project: Model): StoredProjectAuditLogSettings => {
|
|
894
|
+
return {
|
|
895
|
+
enableAuditLogs: project.enableAuditLogs,
|
|
896
|
+
storeSystemEventsInAuditLogs: project.storeSystemEventsInAuditLogs,
|
|
897
|
+
auditLogsRetentionInDays: project.auditLogsRetentionInDays,
|
|
898
|
+
};
|
|
899
|
+
});
|
|
900
|
+
}
|
|
901
|
+
|
|
656
902
|
@CaptureSpan()
|
|
657
903
|
public async changePlan(params: {
|
|
658
904
|
projectId: ObjectID;
|
|
@@ -2570,6 +2816,44 @@ These are no longer recorded against the project and have to be cancelled by han
|
|
|
2570
2816
|
return { findBy, carryForward: null };
|
|
2571
2817
|
}
|
|
2572
2818
|
|
|
2819
|
+
/*
|
|
2820
|
+
* While SSO is not active a project's SSO requirement is not enforced: on
|
|
2821
|
+
* the Community Edition (the SSO login routes are part of the Enterprise
|
|
2822
|
+
* Edition), and on an Enterprise install whose license is lapsed or does
|
|
2823
|
+
* not include SSO (the routes refuse). Reads made for a caller then report
|
|
2824
|
+
* the EFFECTIVE value: not required, no required provider. Clients decide
|
|
2825
|
+
* from these columns whether to start an SSO flow - the mobile app hides a
|
|
2826
|
+
* project's on-call data behind an SSO login it cannot complete, and its
|
|
2827
|
+
* store builds cannot be patched.
|
|
2828
|
+
*
|
|
2829
|
+
* Only the returned objects change. Internal (root) reads, and the stored
|
|
2830
|
+
* row, keep the real value, and onBeforeUpdate keeps a caller from writing
|
|
2831
|
+
* the masked value back. So running the Enterprise Edition with a valid
|
|
2832
|
+
* license (or in its trial or grace period) again restores enforcement
|
|
2833
|
+
* exactly as configured.
|
|
2834
|
+
*/
|
|
2835
|
+
@CaptureSpan()
|
|
2836
|
+
protected override async onFindSuccess(
|
|
2837
|
+
onFind: OnFind<Model>,
|
|
2838
|
+
items: Array<Model>,
|
|
2839
|
+
): Promise<OnFind<Model>> {
|
|
2840
|
+
if (
|
|
2841
|
+
EditionEnforcement.shouldMaskSsoRequirementOnRead(onFind.findBy.props)
|
|
2842
|
+
) {
|
|
2843
|
+
for (const item of items) {
|
|
2844
|
+
if (item.requireSsoForLogin !== undefined) {
|
|
2845
|
+
item.requireSsoForLogin = false;
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
if (item.requireSsoWithSsoProviderId !== undefined) {
|
|
2849
|
+
item.requireSsoWithSsoProviderId = null!;
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
|
|
2854
|
+
return { ...onFind, carryForward: items };
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2573
2857
|
@CaptureSpan()
|
|
2574
2858
|
protected override async onBeforeDelete(
|
|
2575
2859
|
deleteBy: DeleteBy<Model>,
|