@oneuptime/common 14.0.7 → 14.0.9
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/DatabaseModels/Incident.ts +267 -1
- package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
- package/Models/DatabaseModels/Project.ts +55 -5
- package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
- package/Models/DatabaseModels/RunnerJob.ts +75 -0
- package/Models/DatabaseModels/StatusPage.ts +55 -0
- package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
- package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
- package/Server/API/AIChatAPI.ts +6 -3
- package/Server/API/AIInvestigationAPI.ts +17 -10
- package/Server/API/AutoRemediationAPI.ts +207 -25
- package/Server/API/IncidentAPI.ts +162 -0
- package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
- package/Server/API/MicrosoftTeamsAPI.ts +93 -14
- package/Server/API/ProxmoxResourceAPI.ts +244 -14
- package/Server/API/StatusPageAPI.ts +487 -171
- package/Server/API/StatusPageSubscriberAPI.ts +63 -15
- package/Server/Infrastructure/GlobalCache.ts +32 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +20 -0
- package/Server/Infrastructure/Semaphore.ts +10 -0
- package/Server/Middleware/IdentityRateLimit.ts +108 -3
- package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
- package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
- package/Server/Middleware/TelemetryIngest.ts +41 -18
- package/Server/Services/AIService.ts +70 -1
- package/Server/Services/AlertService.ts +352 -11
- package/Server/Services/AlertSeverityService.ts +65 -15
- package/Server/Services/AutoRemediationRuleService.ts +1 -1
- package/Server/Services/DatabaseService.ts +93 -10
- package/Server/Services/IncidentCustomFieldService.ts +276 -1
- package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
- package/Server/Services/IncidentEpisodeService.ts +15 -0
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
- package/Server/Services/IncidentPublicNoteService.ts +30 -1
- package/Server/Services/IncidentService.ts +1788 -142
- package/Server/Services/IncidentSeverityService.ts +66 -15
- package/Server/Services/IncidentStateTimelineService.ts +13 -0
- package/Server/Services/IncidentTemplateService.ts +109 -0
- package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
- package/Server/Services/KubernetesAiAgentService.ts +1634 -0
- package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
- package/Server/Services/KubernetesClusterService.ts +129 -21
- package/Server/Services/ProjectService.ts +32 -0
- package/Server/Services/ProxmoxResourceService.ts +322 -5
- package/Server/Services/RunnerJobService.ts +241 -104
- package/Server/Services/RunnerService.ts +1 -1
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
- package/Server/Services/ScheduledMaintenanceService.ts +805 -19
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
- package/Server/Services/SmsService.ts +10 -0
- package/Server/Services/StatusPageService.ts +65 -8
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
- package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
- package/Server/Services/StatusPageSubscriberService.ts +1161 -64
- package/Server/Services/TeamComplianceSettingService.ts +1125 -9
- package/Server/Types/Markdown.ts +153 -6
- package/Server/Types/Workflow/Components/Email.ts +61 -2
- package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
- package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
- package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
- package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
- package/Server/Utils/DataSource/EgressGuard.ts +66 -21
- package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
- package/Server/Utils/SSRFProtection.ts +76 -16
- package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
- package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
- package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
- package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
- package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
- package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
- package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
- package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
- package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
- package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
- package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
- package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
- package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
- package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
- package/Server/Utils/StatusPageResource.ts +51 -20
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
- package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
- package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
- package/Server/Utils/VM/VMAPI.ts +1 -0
- package/Server/Utils/VM/VMRunner.ts +10 -0
- package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
- package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
- package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
- package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
- package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
- package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
- package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
- package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
- package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
- package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
- package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
- package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
- package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
- package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
- package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
- package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
- package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
- package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
- package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
- package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
- package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
- package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
- package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
- package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
- package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
- package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
- package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
- package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
- package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
- package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
- package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
- package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
- package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
- package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
- package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
- package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
- package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
- package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
- package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
- package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
- package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +778 -2
- package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
- package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +828 -17
- package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
- package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
- package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
- package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
- package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
- package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
- package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
- package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
- package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
- package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
- package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
- package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
- package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
- package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
- package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
- package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
- package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
- package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
- package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
- package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
- package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
- package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
- package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
- package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
- package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
- package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
- package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
- package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
- package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
- package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
- package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
- package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
- package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
- package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
- package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
- package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
- package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
- package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
- package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
- package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
- package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
- package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
- package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
- package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
- package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
- package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
- package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
- package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
- package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
- package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
- package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
- package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
- package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
- package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
- package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
- package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
- package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
- package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
- package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
- package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
- package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
- package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
- package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
- package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
- package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
- package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
- package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
- package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
- package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
- package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
- package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
- package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
- package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
- package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
- package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
- package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
- package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
- package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
- package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
- package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
- package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
- package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
- package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
- package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
- package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
- package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
- package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
- package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
- package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
- package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
- package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
- package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
- package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
- package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
- package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
- package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
- package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
- package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
- package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
- package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
- package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
- package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
- package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
- package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
- package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
- package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
- package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
- package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
- package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
- package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
- package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
- package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
- package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
- package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
- package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
- package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
- package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
- package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
- package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
- package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
- package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
- package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
- package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
- package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
- package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
- package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
- package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
- package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
- package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
- package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
- package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
- package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
- package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
- package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
- package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
- package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
- package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
- package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
- package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
- package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
- package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
- package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
- package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
- package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
- package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
- package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
- package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
- package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
- package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
- package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
- package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
- package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
- package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
- package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
- package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
- package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
- package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
- package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
- package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
- package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
- package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
- package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
- package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
- package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
- package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
- package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
- package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
- package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
- package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
- package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
- package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
- package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
- package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
- package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
- package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
- package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
- package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
- package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
- package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
- package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
- package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
- package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
- package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
- package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
- package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
- package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
- package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
- package/Tests/Types/SafeHtml.test.ts +102 -0
- package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
- package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
- package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
- package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
- package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
- package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
- package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
- package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
- package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
- package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
- package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
- package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
- package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
- package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
- package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
- package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
- package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
- package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
- package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
- package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
- package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
- package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
- package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
- package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
- package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
- package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
- package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
- package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
- package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
- package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
- package/Tests/UI/Components/Input.test.tsx +80 -1
- package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
- package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
- package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
- package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
- package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
- package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
- package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
- package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
- package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
- package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
- package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
- package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
- package/Tests/UI/Components/Toggle.test.tsx +66 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
- package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
- package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
- package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
- package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
- package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
- package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
- package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
- package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
- package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
- package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
- package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
- package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
- package/Types/AI/AIRunType.ts +9 -4
- package/Types/AI/InvestigationNotStartedReason.ts +6 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
- package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
- package/Types/CustomField/CustomFieldOrder.ts +55 -0
- package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
- package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
- package/Types/CustomField/CustomFieldType.ts +14 -0
- package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
- package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
- package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
- package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
- package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
- package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
- package/Types/SafeHtml.ts +93 -0
- package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
- package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
- package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
- package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
- package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
- package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
- package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
- package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
- package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
- package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
- package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
- package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
- package/Types/Team/ComplianceNotificationChannel.ts +22 -0
- package/Types/Team/ComplianceRule.ts +434 -0
- package/Types/Team/ComplianceRuleType.ts +21 -0
- package/Types/Team/TeamComplianceStatus.ts +81 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
- package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
- package/UI/Components/ActionButton/RowActions.tsx +238 -0
- package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
- package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
- package/UI/Components/Charts/Line/LineChart.tsx +32 -2
- package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
- package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
- package/UI/Components/Charts/Utils/XAxis.ts +75 -0
- package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
- package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
- package/UI/Components/Forms/Fields/FormField.tsx +10 -0
- package/UI/Components/Forms/ModelForm.tsx +70 -8
- package/UI/Components/Forms/Validation.ts +1 -1
- package/UI/Components/Input/Input.tsx +90 -57
- package/UI/Components/List/ListRow.tsx +8 -74
- package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
- package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
- package/UI/Components/ModelTable/BaseModelTable.tsx +21 -9
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
- package/UI/Components/ModelTable/ModelTable.tsx +6 -7
- package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
- package/UI/Components/OrderedStatesList/Item.tsx +7 -72
- package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
- package/UI/Components/Table/TableBody.tsx +8 -2
- package/UI/Components/Table/TableRow.tsx +20 -140
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
- package/UI/Components/TelemetryViewer/types.ts +2 -0
- package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
- package/UI/Components/Toggle/Toggle.tsx +26 -0
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Styles/Theme.css +14 -2
- package/UI/Utils/SensitiveUrlToken.ts +10 -5
- package/Utils/API.ts +247 -2
- package/Utils/AiRemediation/CommandPolicy.ts +1 -1
- package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
- package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
- package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
- package/Utils/Markdown/MarkdownEscape.ts +65 -0
- package/Utils/ProbeNetworkFailureUtil.ts +673 -0
- package/build/dist/Models/DatabaseModels/Incident.js +267 -1
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +56 -5
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +6 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +100 -1
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
- package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +417 -176
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
- package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
- package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +59 -1
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +248 -13
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +51 -15
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +56 -7
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +1251 -72
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +22 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +178 -92
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -1
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +582 -20
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/SmsService.js +2 -22
- package/build/dist/Server/Services/SmsService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +47 -9
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +110 -6
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +48 -6
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageResource.js +39 -7
- package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIRunType.js +9 -4
- package/build/dist/Types/AI/AIRunType.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
- package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
- package/build/dist/Types/SafeHtml.js +84 -0
- package/build/dist/Types/SafeHtml.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRule.js +304 -0
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
- package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
- package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
- package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
- package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +1 -1
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +69 -54
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/List/ListRow.js +4 -39
- package/build/dist/UI/Components/List/ListRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +12 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
- package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +6 -2
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +12 -75
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
- package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
- package/build/dist/Utils/API.js +177 -1
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
- package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
- package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -13,8 +13,10 @@ import { AllowedSubscribersCountInFreePlan, IsBillingEnabled, } from "../Environ
|
|
|
13
13
|
import ProjectSMTPConfigService from "../Services/ProjectSmtpConfigService";
|
|
14
14
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
15
15
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
16
|
+
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
16
17
|
import logger from "../Utils/Logger";
|
|
17
18
|
import DatabaseService from "./DatabaseService";
|
|
19
|
+
import GlobalCache from "../Infrastructure/GlobalCache";
|
|
18
20
|
import MailService from "./MailService";
|
|
19
21
|
import ProjectCallSMSConfigService from "./ProjectCallSMSConfigService";
|
|
20
22
|
import ProjectService from "./ProjectService";
|
|
@@ -36,12 +38,109 @@ import MicrosoftTeamsUtil from "../Utils/Workspace/MicrosoftTeams/MicrosoftTeams
|
|
|
36
38
|
import StatusPageSubscriberWebhookUtil from "../Utils/StatusPageSubscriberWebhook";
|
|
37
39
|
import SSRFProtection from "../Utils/SSRFProtection";
|
|
38
40
|
import StatusPageSubscriberNotificationTemplateService, { Service as StatusPageSubscriberNotificationTemplateServiceClass, } from "./StatusPageSubscriberNotificationTemplateService";
|
|
41
|
+
import TeamMemberService from "./TeamMemberService";
|
|
42
|
+
import UserService from "./UserService";
|
|
43
|
+
import OneUptimeDate from "../../Types/Date";
|
|
44
|
+
import ModelEventType from "../../Types/Realtime/ModelEventType";
|
|
45
|
+
import StatusPageSubscriberUnsubscribe, { StatusPageSubscriberUnsubscribeState, } from "../../Types/StatusPage/StatusPageSubscriberUnsubscribe";
|
|
46
|
+
import StatusPageSubscriberUnsubscribeToken from "../Utils/StatusPage/StatusPageSubscriberUnsubscribeToken";
|
|
47
|
+
import StatusPageSubscriberUnsubscribeNotice, { StatusPageSubscriberUnsubscribeSource, } from "../Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice";
|
|
48
|
+
const returnedRows = (result) => {
|
|
49
|
+
if (!Array.isArray(result)) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
if (result.length === 2 &&
|
|
53
|
+
Array.isArray(result[0]) &&
|
|
54
|
+
typeof result[1] === "number") {
|
|
55
|
+
return result[0];
|
|
56
|
+
}
|
|
57
|
+
return result.filter((row) => {
|
|
58
|
+
return Boolean(row) && typeof row === "object" && !Array.isArray(row);
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const affectedRowCount = (result) => {
|
|
62
|
+
if (Array.isArray(result) &&
|
|
63
|
+
result.length === 2 &&
|
|
64
|
+
typeof result[1] === "number") {
|
|
65
|
+
return result[1];
|
|
66
|
+
}
|
|
67
|
+
return 0;
|
|
68
|
+
};
|
|
69
|
+
/*
|
|
70
|
+
* The subscriber rows being created for a visitor who signed up on the status
|
|
71
|
+
* page itself (see createFromStatusPageSignUp). Every other create - a
|
|
72
|
+
* teammate on the dashboard, an API key, a workflow - is the team adding a
|
|
73
|
+
* subscriber, and onBeforeCreate marks it so (Is Added By Team).
|
|
74
|
+
*
|
|
75
|
+
* The very model instances, held only for the length of the create: nothing
|
|
76
|
+
* a request body carries can put a row in here, so no client can pass its
|
|
77
|
+
* subscriber off as a sign-up and keep the page's owners from hearing when
|
|
78
|
+
* it unsubscribes.
|
|
79
|
+
*/
|
|
80
|
+
const statusPageSignUps = new WeakSet();
|
|
81
|
+
// How many subscribers the backfill and the token top-up handle per statement.
|
|
82
|
+
const UNSUBSCRIBE_COLUMNS_BATCH_SIZE = 1000;
|
|
39
83
|
export class Service extends DatabaseService {
|
|
40
84
|
constructor() {
|
|
41
85
|
super(Model);
|
|
42
86
|
}
|
|
87
|
+
/*
|
|
88
|
+
* A create hands its caller the row it saved, and every caller passes it
|
|
89
|
+
* on whole: BaseAPI serializes it into the response of POST
|
|
90
|
+
* /status-page-subscriber without a column read check (a teammate's Add
|
|
91
|
+
* Subscriber and Add in Bulk, any API key), and a workflow's Create Status
|
|
92
|
+
* Page Subscriber step returns it, to be kept in the workflow's logs.
|
|
93
|
+
*
|
|
94
|
+
* So the two secrets onBeforeCreate minted are taken off it first: the
|
|
95
|
+
* unsubscribe token, which lets whoever holds it cancel the subscription
|
|
96
|
+
* without signing in - and makes the team's notice say the subscriber did -
|
|
97
|
+
* and the six-digit confirmation code. Nobody can read either column
|
|
98
|
+
* (read: []). They leave the server only inside the messages to the
|
|
99
|
+
* subscription's own contact, which onCreateSuccess has sent by now; the
|
|
100
|
+
* confirmation email reads its code back from the database.
|
|
101
|
+
*/
|
|
102
|
+
async create(createBy) {
|
|
103
|
+
const created = await super.create(createBy);
|
|
104
|
+
/*
|
|
105
|
+
* Cleared rather than deleted, as BaseAPI.createItem clears a model's
|
|
106
|
+
* `_id`: an unset column holds undefined, which serializes as nothing.
|
|
107
|
+
*/
|
|
108
|
+
const row = created;
|
|
109
|
+
row["unsubscribeToken"] = undefined;
|
|
110
|
+
row["subscriptionConfirmationToken"] = undefined;
|
|
111
|
+
return created;
|
|
112
|
+
}
|
|
113
|
+
/*
|
|
114
|
+
* Create the subscriber a visitor signed up for on the status page itself
|
|
115
|
+
* (StatusPageAPI's subscribe endpoint). The one create that is not the team
|
|
116
|
+
* adding a subscriber: it is created as root, like the rest of that
|
|
117
|
+
* endpoint, and Is Added By Team is left off.
|
|
118
|
+
*/
|
|
119
|
+
async createFromStatusPageSignUp(data) {
|
|
120
|
+
statusPageSignUps.add(data);
|
|
121
|
+
try {
|
|
122
|
+
return await this.create({
|
|
123
|
+
data: data,
|
|
124
|
+
props: {
|
|
125
|
+
isRoot: true,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
statusPageSignUps.delete(data);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/*
|
|
134
|
+
* Whether the team added this subscriber, rather than it signing up on the
|
|
135
|
+
* status page. Is Added By Team says so for every subscriber created since
|
|
136
|
+
* it existed; Created By is read as well for the ones created before, until
|
|
137
|
+
* the backfill has marked them (every create with a creator is the team's).
|
|
138
|
+
*/
|
|
139
|
+
isAddedByTeam(subscriber) {
|
|
140
|
+
return (subscriber.isAddedByTeam === true || Boolean(subscriber.createdByUserId));
|
|
141
|
+
}
|
|
43
142
|
async onBeforeCreate(data) {
|
|
44
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35
|
|
143
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35;
|
|
45
144
|
logger.debug("onBeforeCreate called with data:", {
|
|
46
145
|
projectId: (_a = data.data.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
47
146
|
statusPageId: (_b = data.data.statusPageId) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
@@ -282,26 +381,434 @@ export class Service extends DatabaseService {
|
|
|
282
381
|
await SSRFProtection.validateWebhookTargetIsSafe(data.data.subscriberWebhook);
|
|
283
382
|
}
|
|
284
383
|
data.data.subscriptionConfirmationToken = NumberUtil.getRandomNumber(100000, 999999).toString();
|
|
384
|
+
/*
|
|
385
|
+
* The secret in this subscriber's unsubscribe link. Always minted here,
|
|
386
|
+
* whatever the request carried: the column is computed, so the create
|
|
387
|
+
* column check lets a client send one, and a token the client chose would
|
|
388
|
+
* be one somebody else could know. A re-subscribe deletes the old row
|
|
389
|
+
* above and gets a new token, so links to the old subscription stop
|
|
390
|
+
* working.
|
|
391
|
+
*/
|
|
392
|
+
data.data.unsubscribeToken =
|
|
393
|
+
StatusPageSubscriberUnsubscribeToken.generate();
|
|
394
|
+
/*
|
|
395
|
+
* Never taken from a client either: whether the team added this
|
|
396
|
+
* subscriber. Only a sign-up on the status page is not the team's, and
|
|
397
|
+
* only createFromStatusPageSignUp creates one. A teammate's create carries
|
|
398
|
+
* a creator (Created By) but an API key's or a workflow's does not, so
|
|
399
|
+
* this is the column that says it for all of them.
|
|
400
|
+
*/
|
|
401
|
+
data.data.isAddedByTeam = !statusPageSignUps.has(data.data);
|
|
402
|
+
/*
|
|
403
|
+
* Likewise never taken from a client. A subscriber is only ever created
|
|
404
|
+
* unsubscribed through the API, and then it was cancelled now.
|
|
405
|
+
*/
|
|
406
|
+
if (data.data.isUnsubscribed) {
|
|
407
|
+
data.data.unsubscribedAt = OneUptimeDate.getCurrentDate();
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
delete data.data.unsubscribedAt;
|
|
411
|
+
}
|
|
285
412
|
logger.debug(`Subscription Confirmation Token: ${data.data.subscriptionConfirmationToken}`, {
|
|
286
413
|
projectId: (_32 = data.data.projectId) === null || _32 === void 0 ? void 0 : _32.toString(),
|
|
287
414
|
statusPageId: (_33 = data.data.statusPageId) === null || _33 === void 0 ? void 0 : _33.toString(),
|
|
288
415
|
});
|
|
289
|
-
|
|
416
|
+
// Not the data itself: it now carries the unsubscribe token.
|
|
417
|
+
logger.debug("onBeforeCreate processed data.", {
|
|
290
418
|
projectId: (_34 = data.data.projectId) === null || _34 === void 0 ? void 0 : _34.toString(),
|
|
291
419
|
statusPageId: (_35 = data.data.statusPageId) === null || _35 === void 0 ? void 0 : _35.toString(),
|
|
292
420
|
});
|
|
293
|
-
logger.debug(data, {
|
|
294
|
-
projectId: (_36 = data.data.projectId) === null || _36 === void 0 ? void 0 : _36.toString(),
|
|
295
|
-
statusPageId: (_37 = data.data.statusPageId) === null || _37 === void 0 ? void 0 : _37.toString(),
|
|
296
|
-
});
|
|
297
421
|
return { createBy: data, carryForward: statuspage };
|
|
298
422
|
}
|
|
423
|
+
/*
|
|
424
|
+
* Unsubscribed At follows Is Unsubscribed on every write that sets it - a
|
|
425
|
+
* teammate's toggle on the dashboard, the API, a workflow - not only on the
|
|
426
|
+
* unsubscribe link, which sets both itself (see unsubscribe()).
|
|
427
|
+
*
|
|
428
|
+
* Which rows an update cancels has to be known before it runs: afterwards
|
|
429
|
+
* every matched row reads unsubscribed, and a row that already was would
|
|
430
|
+
* get today's date for a cancellation that happened long ago (or before the
|
|
431
|
+
* column existed). The stamp itself is written after the update, as root,
|
|
432
|
+
* by a plain UPDATE: through updateBy it would be a second update of the
|
|
433
|
+
* row and fire the "on update" workflow trigger a second time.
|
|
434
|
+
*/
|
|
435
|
+
async onBeforeUpdate(updateBy) {
|
|
436
|
+
var _a;
|
|
437
|
+
const isUnsubscribed = (_a = updateBy.data) === null || _a === void 0 ? void 0 : _a["isUnsubscribed"];
|
|
438
|
+
const carryForward = {
|
|
439
|
+
isUnsubscribed: typeof isUnsubscribed === "boolean" ? isUnsubscribed : null,
|
|
440
|
+
subscriberIdsBeingUnsubscribed: [],
|
|
441
|
+
};
|
|
442
|
+
if (carryForward.isUnsubscribed === true) {
|
|
443
|
+
const matched = await this.findBy({
|
|
444
|
+
query: updateBy.query,
|
|
445
|
+
select: {
|
|
446
|
+
_id: true,
|
|
447
|
+
isUnsubscribed: true,
|
|
448
|
+
},
|
|
449
|
+
skip: 0,
|
|
450
|
+
limit: LIMIT_MAX,
|
|
451
|
+
props: {
|
|
452
|
+
isRoot: true,
|
|
453
|
+
ignoreHooks: true,
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
carryForward.subscriberIdsBeingUnsubscribed = matched
|
|
457
|
+
.filter((subscriber) => {
|
|
458
|
+
return Boolean(subscriber._id) && subscriber.isUnsubscribed !== true;
|
|
459
|
+
})
|
|
460
|
+
.map((subscriber) => {
|
|
461
|
+
return subscriber._id.toString();
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
return { updateBy, carryForward };
|
|
465
|
+
}
|
|
466
|
+
async onUpdateSuccess(onUpdate, updatedItemIds) {
|
|
467
|
+
const carryForward = onUpdate.carryForward;
|
|
468
|
+
if (!carryForward || carryForward.isUnsubscribed === null) {
|
|
469
|
+
return onUpdate;
|
|
470
|
+
}
|
|
471
|
+
const updatedIds = updatedItemIds.map((id) => {
|
|
472
|
+
return id.toString();
|
|
473
|
+
});
|
|
474
|
+
if (carryForward.isUnsubscribed === true) {
|
|
475
|
+
const cancelledIds = carryForward.subscriberIdsBeingUnsubscribed.filter((id) => {
|
|
476
|
+
return updatedIds.includes(id);
|
|
477
|
+
});
|
|
478
|
+
if (cancelledIds.length > 0) {
|
|
479
|
+
await this.getRepository().manager.query(`UPDATE "StatusPageSubscriber" SET "unsubscribedAt" = $1 WHERE "_id" = ANY($2::uuid[]) AND "isUnsubscribed" = true AND "unsubscribedAt" IS NULL`, [OneUptimeDate.getCurrentDate(), cancelledIds]);
|
|
480
|
+
}
|
|
481
|
+
return onUpdate;
|
|
482
|
+
}
|
|
483
|
+
// Subscribed again: the old date no longer describes this subscription.
|
|
484
|
+
if (updatedIds.length > 0) {
|
|
485
|
+
await this.getRepository().manager.query(`UPDATE "StatusPageSubscriber" SET "unsubscribedAt" = NULL WHERE "_id" = ANY($1::uuid[]) AND "isUnsubscribed" = false AND "unsubscribedAt" IS NOT NULL`, [updatedIds]);
|
|
486
|
+
}
|
|
487
|
+
return onUpdate;
|
|
488
|
+
}
|
|
489
|
+
/*
|
|
490
|
+
* Cancel a subscription, once. Returns whether this call cancelled it:
|
|
491
|
+
* false when it was already cancelled (or does not exist), which makes the
|
|
492
|
+
* unsubscribe link idempotent.
|
|
493
|
+
*
|
|
494
|
+
* ONE STATEMENT, ON PURPOSE: the WHERE clause's `"isUnsubscribed" = false`
|
|
495
|
+
* lets Postgres decide which of two simultaneous requests - a double click,
|
|
496
|
+
* a mail client that follows the confirmation twice - cancels the
|
|
497
|
+
* subscription, so its Unsubscribed At is written once and the team is told
|
|
498
|
+
* once. `RETURNING` is what tells this call whether it was the one.
|
|
499
|
+
*
|
|
500
|
+
* Written as raw SQL because no DatabaseService write both takes that
|
|
501
|
+
* predicate and reports what it matched. That skips the update hooks, so
|
|
502
|
+
* the "on update" workflow trigger and the realtime event a DatabaseService
|
|
503
|
+
* update fires are fired here, by hand, with the two columns this wrote.
|
|
504
|
+
*/
|
|
505
|
+
async unsubscribe(data) {
|
|
506
|
+
var _a;
|
|
507
|
+
const unsubscribedAt = OneUptimeDate.getCurrentDate();
|
|
508
|
+
const rows = returnedRows(await this.getRepository().manager.query(`UPDATE "StatusPageSubscriber"
|
|
509
|
+
SET "isUnsubscribed" = true,
|
|
510
|
+
"unsubscribedAt" = $1,
|
|
511
|
+
"updatedAt" = CURRENT_TIMESTAMP
|
|
512
|
+
WHERE "_id" = $2
|
|
513
|
+
AND "isUnsubscribed" = false
|
|
514
|
+
AND "deletedAt" IS NULL
|
|
515
|
+
RETURNING "_id", "projectId"`, [unsubscribedAt, data.subscriberId.toString()]));
|
|
516
|
+
const row = rows[0];
|
|
517
|
+
if (!row || !row["_id"]) {
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
const projectId = row["projectId"]
|
|
521
|
+
? new ObjectID(row["projectId"].toString())
|
|
522
|
+
: null;
|
|
523
|
+
if (projectId) {
|
|
524
|
+
if ((_a = this.getModel().enableWorkflowOn) === null || _a === void 0 ? void 0 : _a.update) {
|
|
525
|
+
await this.onTriggerWorkflow(data.subscriberId, projectId, "on-update", {
|
|
526
|
+
updatedFields: {
|
|
527
|
+
isUnsubscribed: true,
|
|
528
|
+
unsubscribedAt: unsubscribedAt.toISOString(),
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
await this.onTriggerRealtime(data.subscriberId, projectId, ModelEventType.Update);
|
|
533
|
+
}
|
|
534
|
+
/*
|
|
535
|
+
* The subscription is cancelled either way; a failure to tell the team
|
|
536
|
+
* must not turn that into an error for the person who asked.
|
|
537
|
+
*/
|
|
538
|
+
try {
|
|
539
|
+
await this.notifyTeamOfUnsubscribe({
|
|
540
|
+
subscriberId: data.subscriberId,
|
|
541
|
+
source: data.source,
|
|
542
|
+
unsubscribedAt: unsubscribedAt,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
catch (err) {
|
|
546
|
+
logger.error(err, {
|
|
547
|
+
statusPageSubscriberId: data.subscriberId.toString(),
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
/*
|
|
553
|
+
* What the status page's unsubscribe page shows for a link, before anything
|
|
554
|
+
* is changed. Opening the page calls this; only a POST unsubscribes.
|
|
555
|
+
*
|
|
556
|
+
* Every way a link can be wrong - a malformed id or token, no such
|
|
557
|
+
* subscriber, a deleted one, one on another status page, a token that does
|
|
558
|
+
* not match - reads the same, Invalid, after the same constant-time token
|
|
559
|
+
* comparison, so the answer cannot be used to tell them apart.
|
|
560
|
+
*/
|
|
561
|
+
async getUnsubscribeLinkDetails(data) {
|
|
562
|
+
const subscriber = await this.findSubscriberByUnsubscribeLink(data);
|
|
563
|
+
if (!subscriber) {
|
|
564
|
+
return { state: StatusPageSubscriberUnsubscribeState.Invalid };
|
|
565
|
+
}
|
|
566
|
+
const contact = StatusPageSubscriberUnsubscribe.describeContact(subscriber);
|
|
567
|
+
return {
|
|
568
|
+
state: subscriber.isUnsubscribed
|
|
569
|
+
? StatusPageSubscriberUnsubscribeState.Unsubscribed
|
|
570
|
+
: StatusPageSubscriberUnsubscribeState.Subscribed,
|
|
571
|
+
channel: contact === null || contact === void 0 ? void 0 : contact.channel,
|
|
572
|
+
contact: contact === null || contact === void 0 ? void 0 : contact.contact,
|
|
573
|
+
wasAddedByTeam: this.isAddedByTeam(subscriber),
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
/*
|
|
577
|
+
* The unsubscribe page's POST: cancel the subscription the link belongs to.
|
|
578
|
+
* Idempotent - a second confirmation, or one for a subscription already
|
|
579
|
+
* cancelled some other way, answers Unsubscribed again and changes nothing.
|
|
580
|
+
* A bad link answers Invalid, exactly as getUnsubscribeLinkDetails does.
|
|
581
|
+
*/
|
|
582
|
+
async unsubscribeWithLink(data) {
|
|
583
|
+
const subscriber = await this.findSubscriberByUnsubscribeLink(data);
|
|
584
|
+
if (!subscriber || !subscriber.id) {
|
|
585
|
+
return StatusPageSubscriberUnsubscribeState.Invalid;
|
|
586
|
+
}
|
|
587
|
+
if (!subscriber.isUnsubscribed) {
|
|
588
|
+
await this.unsubscribe({
|
|
589
|
+
subscriberId: subscriber.id,
|
|
590
|
+
source: StatusPageSubscriberUnsubscribeSource.UnsubscribeLink,
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
return StatusPageSubscriberUnsubscribeState.Unsubscribed;
|
|
594
|
+
}
|
|
595
|
+
/*
|
|
596
|
+
* The subscriber a link belongs to, or null when the link is not good. The
|
|
597
|
+
* subscriber is found by its primary key, never by the token, and the token
|
|
598
|
+
* is then compared in constant time - a query on the token would let the
|
|
599
|
+
* database's own comparison time a guess. The comparison runs even when
|
|
600
|
+
* there is no subscriber to compare with.
|
|
601
|
+
*/
|
|
602
|
+
async findSubscriberByUnsubscribeLink(data) {
|
|
603
|
+
const isWellFormed = ObjectID.isValidUUID(data.statusPageId) &&
|
|
604
|
+
ObjectID.isValidUUID(data.subscriberId);
|
|
605
|
+
const subscriber = isWellFormed
|
|
606
|
+
? await this.findOneBy({
|
|
607
|
+
query: {
|
|
608
|
+
_id: data.subscriberId,
|
|
609
|
+
statusPageId: new ObjectID(data.statusPageId),
|
|
610
|
+
},
|
|
611
|
+
select: {
|
|
612
|
+
_id: true,
|
|
613
|
+
statusPageId: true,
|
|
614
|
+
unsubscribeToken: true,
|
|
615
|
+
isUnsubscribed: true,
|
|
616
|
+
isAddedByTeam: true,
|
|
617
|
+
createdByUserId: true,
|
|
618
|
+
subscriberEmail: true,
|
|
619
|
+
subscriberPhone: true,
|
|
620
|
+
slackIncomingWebhookUrl: true,
|
|
621
|
+
slackWorkspaceName: true,
|
|
622
|
+
microsoftTeamsIncomingWebhookUrl: true,
|
|
623
|
+
microsoftTeamsWorkspaceName: true,
|
|
624
|
+
subscriberWebhook: true,
|
|
625
|
+
},
|
|
626
|
+
props: {
|
|
627
|
+
isRoot: true,
|
|
628
|
+
ignoreHooks: true,
|
|
629
|
+
},
|
|
630
|
+
})
|
|
631
|
+
: null;
|
|
632
|
+
const tokenMatches = StatusPageSubscriberUnsubscribeToken.matches({
|
|
633
|
+
stored: subscriber === null || subscriber === void 0 ? void 0 : subscriber.unsubscribeToken,
|
|
634
|
+
presented: data.token,
|
|
635
|
+
});
|
|
636
|
+
if (!subscriber || !tokenMatches) {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
return subscriber;
|
|
640
|
+
}
|
|
641
|
+
/*
|
|
642
|
+
* Tell the team that a subscriber it added has unsubscribed itself (see
|
|
643
|
+
* StatusPageSubscriberUnsubscribeNotice for why). Subscribers people signed
|
|
644
|
+
* up for themselves on the status page tell nobody.
|
|
645
|
+
*
|
|
646
|
+
* A subscriber the team added is one with Is Added By Team - from the
|
|
647
|
+
* dashboard, with an API key or by a workflow - or, created before that
|
|
648
|
+
* column, with a creator (see isAddedByTeam).
|
|
649
|
+
*
|
|
650
|
+
* It goes to the status page's owners - its owner users and the members of
|
|
651
|
+
* its owner teams - and to the teammate who added the subscriber, when a
|
|
652
|
+
* teammate did, each once and only while they are still members of the
|
|
653
|
+
* project, as one plain email through MailService rather than a
|
|
654
|
+
* notification rule: it is about a subscriber list, not an event anyone is
|
|
655
|
+
* on call for. A page with no owners tells only the teammate who added the
|
|
656
|
+
* subscriber; one an API key or a workflow added, on a page with no owners,
|
|
657
|
+
* tells nobody.
|
|
658
|
+
*/
|
|
659
|
+
async notifyTeamOfUnsubscribe(data) {
|
|
660
|
+
var _a, _b, _c;
|
|
661
|
+
const subscriber = await this.findOneById({
|
|
662
|
+
id: data.subscriberId,
|
|
663
|
+
select: {
|
|
664
|
+
_id: true,
|
|
665
|
+
projectId: true,
|
|
666
|
+
statusPageId: true,
|
|
667
|
+
isAddedByTeam: true,
|
|
668
|
+
createdByUserId: true,
|
|
669
|
+
createdAt: true,
|
|
670
|
+
subscriberEmail: true,
|
|
671
|
+
subscriberPhone: true,
|
|
672
|
+
slackIncomingWebhookUrl: true,
|
|
673
|
+
slackWorkspaceName: true,
|
|
674
|
+
microsoftTeamsIncomingWebhookUrl: true,
|
|
675
|
+
microsoftTeamsWorkspaceName: true,
|
|
676
|
+
subscriberWebhook: true,
|
|
677
|
+
},
|
|
678
|
+
props: {
|
|
679
|
+
isRoot: true,
|
|
680
|
+
ignoreHooks: true,
|
|
681
|
+
},
|
|
682
|
+
});
|
|
683
|
+
if (!subscriber ||
|
|
684
|
+
!this.isAddedByTeam(subscriber) ||
|
|
685
|
+
!subscriber.projectId ||
|
|
686
|
+
!subscriber.statusPageId) {
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const contact = StatusPageSubscriberUnsubscribe.describeContact(subscriber, {
|
|
690
|
+
maskPhone: false,
|
|
691
|
+
});
|
|
692
|
+
if (!contact) {
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
const statusPage = await StatusPageService.findOneById({
|
|
696
|
+
id: subscriber.statusPageId,
|
|
697
|
+
select: {
|
|
698
|
+
_id: true,
|
|
699
|
+
name: true,
|
|
700
|
+
pageTitle: true,
|
|
701
|
+
},
|
|
702
|
+
props: {
|
|
703
|
+
isRoot: true,
|
|
704
|
+
ignoreHooks: true,
|
|
705
|
+
},
|
|
706
|
+
});
|
|
707
|
+
if (!statusPage) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
// Only a teammate's create has a creator; an API key's or a workflow's does not.
|
|
711
|
+
const creator = subscriber.createdByUserId
|
|
712
|
+
? await UserService.findOneById({
|
|
713
|
+
id: subscriber.createdByUserId,
|
|
714
|
+
select: {
|
|
715
|
+
_id: true,
|
|
716
|
+
name: true,
|
|
717
|
+
email: true,
|
|
718
|
+
},
|
|
719
|
+
props: {
|
|
720
|
+
isRoot: true,
|
|
721
|
+
},
|
|
722
|
+
})
|
|
723
|
+
: null;
|
|
724
|
+
const candidates = [
|
|
725
|
+
...(await StatusPageService.findOwners(subscriber.statusPageId)),
|
|
726
|
+
...(creator ? [creator] : []),
|
|
727
|
+
];
|
|
728
|
+
const recipients = [];
|
|
729
|
+
const seenUserIds = new Set();
|
|
730
|
+
for (const user of await TeamMemberService.filterUsersToProjectMembers({
|
|
731
|
+
projectId: subscriber.projectId,
|
|
732
|
+
users: candidates,
|
|
733
|
+
})) {
|
|
734
|
+
const userId = ((_a = user.id) === null || _a === void 0 ? void 0 : _a.toString().toLowerCase()) || "";
|
|
735
|
+
if (!userId || !user.email || seenUserIds.has(userId)) {
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
seenUserIds.add(userId);
|
|
739
|
+
recipients.push(user);
|
|
740
|
+
}
|
|
741
|
+
if (recipients.length === 0) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
if (!(await this.isFirstUnsubscribeNoticeInWindow(subscriber.id))) {
|
|
745
|
+
logger.debug(`The team was told about subscriber ${subscriber.id.toString()} unsubscribing within the last ${StatusPageSubscriberUnsubscribeNotice.noticeWindowInHours} hours; not telling it again.`, {
|
|
746
|
+
projectId: subscriber.projectId.toString(),
|
|
747
|
+
statusPageId: subscriber.statusPageId.toString(),
|
|
748
|
+
});
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
const subscriberListUrl = (await StatusPageService.getStatusPageLinkInDashboard(subscriber.projectId, subscriber.statusPageId))
|
|
752
|
+
.addRoute(`/${StatusPageSubscriberUnsubscribeNotice.getSubscriberListRoute(contact.channel)}`)
|
|
753
|
+
.toString();
|
|
754
|
+
const email = StatusPageSubscriberUnsubscribeNotice.build({
|
|
755
|
+
statusPageName: statusPage.name || statusPage.pageTitle || "Status Page",
|
|
756
|
+
contact: contact,
|
|
757
|
+
source: data.source,
|
|
758
|
+
unsubscribedAt: data.unsubscribedAt,
|
|
759
|
+
addedByName: ((_b = creator === null || creator === void 0 ? void 0 : creator.name) === null || _b === void 0 ? void 0 : _b.toString()) || ((_c = creator === null || creator === void 0 ? void 0 : creator.email) === null || _c === void 0 ? void 0 : _c.toString()),
|
|
760
|
+
addedAt: subscriber.createdAt,
|
|
761
|
+
subscriberListUrl: subscriberListUrl,
|
|
762
|
+
});
|
|
763
|
+
for (const recipient of recipients) {
|
|
764
|
+
MailService.sendMail({
|
|
765
|
+
toEmail: recipient.email,
|
|
766
|
+
templateType: EmailTemplateType.SimpleMessage,
|
|
767
|
+
vars: {
|
|
768
|
+
subject: email.subject,
|
|
769
|
+
message: email.message,
|
|
770
|
+
},
|
|
771
|
+
subject: email.subject,
|
|
772
|
+
isSubjectLiteral: true,
|
|
773
|
+
}, {
|
|
774
|
+
projectId: subscriber.projectId,
|
|
775
|
+
userId: recipient.id,
|
|
776
|
+
}).catch((err) => {
|
|
777
|
+
var _a, _b;
|
|
778
|
+
logger.error(err, {
|
|
779
|
+
projectId: (_a = subscriber.projectId) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
780
|
+
statusPageId: (_b = subscriber.statusPageId) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
781
|
+
});
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
/*
|
|
786
|
+
* The team is told about a subscriber unsubscribing at most once per
|
|
787
|
+
* window. The public manage page both re-subscribes a subscriber and
|
|
788
|
+
* cancels it again, and it is open to anyone who can see the status page
|
|
789
|
+
* and knows the subscriber's id (it is in the manage link), so without this
|
|
790
|
+
* a loop of the two would email every owner of the page on every turn.
|
|
791
|
+
*
|
|
792
|
+
* One atomic SET NX per subscriber: exactly one notice wins the window
|
|
793
|
+
* however many cancellations arrive together. If the cache cannot be
|
|
794
|
+
* reached the notice goes out: telling the team twice is better than not
|
|
795
|
+
* at all.
|
|
796
|
+
*/
|
|
797
|
+
async isFirstUnsubscribeNoticeInWindow(subscriberId) {
|
|
798
|
+
try {
|
|
799
|
+
return await GlobalCache.setStringIfNotExists(StatusPageSubscriberUnsubscribeNotice.noticeCacheNamespace, subscriberId.toString().toLowerCase(), OneUptimeDate.getCurrentDate().toISOString(), {
|
|
800
|
+
expiresInSeconds: StatusPageSubscriberUnsubscribeNotice.noticeWindowInHours * 60 * 60,
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
catch (err) {
|
|
804
|
+
logger.warn(`Could not check when the team was last told about subscriber ${subscriberId.toString()} unsubscribing; telling it: ${err instanceof Error ? err.message : String(err)}`);
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
299
808
|
async onCreateSuccess(onCreate, createdItem) {
|
|
300
809
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
});
|
|
304
|
-
logger.debug(createdItem, {
|
|
810
|
+
// Not the item itself: it carries the unsubscribe token.
|
|
811
|
+
logger.debug(`onCreateSuccess called with createdItem ${(_a = createdItem.id) === null || _a === void 0 ? void 0 : _a.toString()}.`, {
|
|
305
812
|
projectId: (_b = createdItem.projectId) === null || _b === void 0 ? void 0 : _b.toString(),
|
|
306
813
|
});
|
|
307
814
|
if (!createdItem.statusPageId) {
|
|
@@ -320,15 +827,13 @@ export class Service extends DatabaseService {
|
|
|
320
827
|
logger.debug(`Status Page Name: ${statusPageName}`, {
|
|
321
828
|
projectId: (_e = createdItem.projectId) === null || _e === void 0 ? void 0 : _e.toString(),
|
|
322
829
|
});
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
projectId: (_f = createdItem.projectId) === null || _f === void 0 ? void 0 : _f.toString(),
|
|
326
|
-
});
|
|
830
|
+
// createdItem carries the unsubscribe token onBeforeCreate minted.
|
|
831
|
+
const unsubscribeLink = this.getUnsubscribeLink(URL.fromString(statusPageURL), createdItem).toString();
|
|
327
832
|
if (createdItem.statusPageId &&
|
|
328
833
|
createdItem.subscriberPhone &&
|
|
329
834
|
createdItem._id &&
|
|
330
835
|
createdItem.sendYouHaveSubscribedMessage) {
|
|
331
|
-
logger.debug("Subscriber has a phone number and sendYouHaveSubscribedMessage is true.", { projectId: (
|
|
836
|
+
logger.debug("Subscriber has a phone number and sendYouHaveSubscribedMessage is true.", { projectId: (_f = createdItem.projectId) === null || _f === void 0 ? void 0 : _f.toString() });
|
|
332
837
|
const statusPage = await StatusPageService.findOneBy({
|
|
333
838
|
query: {
|
|
334
839
|
_id: createdItem.statusPageId.toString(),
|
|
@@ -349,14 +854,25 @@ export class Service extends DatabaseService {
|
|
|
349
854
|
});
|
|
350
855
|
if (!statusPage) {
|
|
351
856
|
logger.debug("Status Page not found.", {
|
|
352
|
-
projectId: (
|
|
857
|
+
projectId: (_g = createdItem.projectId) === null || _g === void 0 ? void 0 : _g.toString(),
|
|
353
858
|
});
|
|
354
859
|
return createdItem;
|
|
355
860
|
}
|
|
356
|
-
logger.debug(`Status Page Call SMS Config: ${JSON.stringify(statusPage.callSmsConfig)}`, { projectId: (
|
|
861
|
+
logger.debug(`Status Page Call SMS Config: ${JSON.stringify(statusPage.callSmsConfig)}`, { projectId: (_h = createdItem.projectId) === null || _h === void 0 ? void 0 : _h.toString() });
|
|
862
|
+
/*
|
|
863
|
+
* On a public status page the SMS keeps the shorter manage link, which
|
|
864
|
+
* works there without signing in: an SMS is billed by the segment (see
|
|
865
|
+
* StatusPageSubscriberUnsubscribe.buildSmsLink).
|
|
866
|
+
*/
|
|
867
|
+
const smsUnsubscribeLink = StatusPageSubscriberUnsubscribe.buildSmsLink({
|
|
868
|
+
isPublicStatusPage: (_j = onCreate.carryForward) === null || _j === void 0 ? void 0 : _j.isPublicStatusPage,
|
|
869
|
+
statusPageUrl: statusPageURL,
|
|
870
|
+
subscriberId: createdItem.id,
|
|
871
|
+
unsubscribeUrl: unsubscribeLink,
|
|
872
|
+
});
|
|
357
873
|
SmsService.sendSms({
|
|
358
874
|
to: createdItem.subscriberPhone,
|
|
359
|
-
message: `You have been subscribed to ${statusPageName}. To unsubscribe, click on the link: ${
|
|
875
|
+
message: `You have been subscribed to ${statusPageName}. To unsubscribe, click on the link: ${smsUnsubscribeLink}`,
|
|
360
876
|
}, {
|
|
361
877
|
projectId: createdItem.projectId,
|
|
362
878
|
isSensitive: false,
|
|
@@ -402,7 +918,7 @@ export class Service extends DatabaseService {
|
|
|
402
918
|
**You have successfully subscribed to receive status updates!**
|
|
403
919
|
|
|
404
920
|
🔗 **Status Page:** [${statusPageName}](${statusPageURL})
|
|
405
|
-
|
|
921
|
+
🔕 **Unsubscribe:** [Stop these notifications](${unsubscribeLink})
|
|
406
922
|
|
|
407
923
|
You will receive real-time notifications for:
|
|
408
924
|
• Incidents and outages
|
|
@@ -480,7 +996,7 @@ Stay informed about service availability! 🚀`;
|
|
|
480
996
|
**You have successfully subscribed to receive status updates!**
|
|
481
997
|
|
|
482
998
|
🔗 **Status Page:** [${statusPageName}](${statusPageURL})
|
|
483
|
-
|
|
999
|
+
🔕 **Unsubscribe:** [Stop these notifications](${unsubscribeLink})
|
|
484
1000
|
|
|
485
1001
|
You will receive real-time notifications for:
|
|
486
1002
|
• Incidents and outages
|
|
@@ -537,22 +1053,25 @@ Stay informed about service availability! 🚀`;
|
|
|
537
1053
|
projectId: true,
|
|
538
1054
|
subscriptionConfirmationToken: true,
|
|
539
1055
|
sendYouHaveSubscribedMessage: true,
|
|
1056
|
+
unsubscribeToken: true,
|
|
540
1057
|
},
|
|
541
1058
|
props: {
|
|
542
1059
|
isRoot: true,
|
|
543
1060
|
ignoreHooks: true,
|
|
544
1061
|
},
|
|
545
1062
|
});
|
|
546
|
-
logger.debug(`Found Subscriber: ${
|
|
547
|
-
statusPageSubscriberId: (
|
|
1063
|
+
logger.debug(`Found Subscriber: ${(_c = subscriber === null || subscriber === void 0 ? void 0 : subscriber.id) === null || _c === void 0 ? void 0 : _c.toString()}`, {
|
|
1064
|
+
statusPageSubscriberId: (_d = data.subscriberId) === null || _d === void 0 ? void 0 : _d.toString(),
|
|
548
1065
|
});
|
|
549
1066
|
// get status page
|
|
550
1067
|
if (!subscriber || !subscriber.statusPageId) {
|
|
551
1068
|
logger.debug("Subscriber or Status Page ID is missing.", {
|
|
552
|
-
statusPageSubscriberId: (
|
|
1069
|
+
statusPageSubscriberId: (_e = data.subscriberId) === null || _e === void 0 ? void 0 : _e.toString(),
|
|
553
1070
|
});
|
|
554
1071
|
return;
|
|
555
1072
|
}
|
|
1073
|
+
// Its unsubscribe link needs its token (see ensureUnsubscribeTokens).
|
|
1074
|
+
await this.ensureUnsubscribeTokens([subscriber]);
|
|
556
1075
|
const statusPage = await StatusPageService.findOneBy({
|
|
557
1076
|
query: {
|
|
558
1077
|
_id: subscriber.statusPageId.toString(),
|
|
@@ -586,46 +1105,43 @@ Stay informed about service availability! 🚀`;
|
|
|
586
1105
|
},
|
|
587
1106
|
});
|
|
588
1107
|
logger.debug(`Found Status Page: ${JSON.stringify(statusPage)}`, {
|
|
589
|
-
statusPageSubscriberId: (
|
|
1108
|
+
statusPageSubscriberId: (_f = data.subscriberId) === null || _f === void 0 ? void 0 : _f.toString(),
|
|
590
1109
|
});
|
|
591
1110
|
if (!statusPage || !statusPage.id) {
|
|
592
1111
|
logger.debug("Status Page not found or ID is missing.", {
|
|
593
|
-
statusPageSubscriberId: (
|
|
1112
|
+
statusPageSubscriberId: (_g = data.subscriberId) === null || _g === void 0 ? void 0 : _g.toString(),
|
|
594
1113
|
});
|
|
595
1114
|
return;
|
|
596
1115
|
}
|
|
597
1116
|
const statusPageURL = await StatusPageService.getStatusPageURL(statusPage.id);
|
|
598
1117
|
logger.debug(`Status Page URL: ${statusPageURL}`, {
|
|
599
|
-
statusPageSubscriberId: (
|
|
1118
|
+
statusPageSubscriberId: (_h = data.subscriberId) === null || _h === void 0 ? void 0 : _h.toString(),
|
|
600
1119
|
});
|
|
601
1120
|
const statusPageName = statusPage.pageTitle || statusPage.name || "Status Page";
|
|
602
1121
|
logger.debug(`Status Page Name: ${statusPageName}`, {
|
|
603
|
-
statusPageSubscriberId: (
|
|
1122
|
+
statusPageSubscriberId: (_j = data.subscriberId) === null || _j === void 0 ? void 0 : _j.toString(),
|
|
604
1123
|
});
|
|
605
1124
|
const host = await DatabaseConfig.getHost();
|
|
606
1125
|
logger.debug(`Host: ${host}`, {
|
|
607
|
-
statusPageSubscriberId: (
|
|
1126
|
+
statusPageSubscriberId: (_k = data.subscriberId) === null || _k === void 0 ? void 0 : _k.toString(),
|
|
608
1127
|
});
|
|
609
1128
|
const httpProtocol = await DatabaseConfig.getHttpProtocol();
|
|
610
1129
|
logger.debug(`HTTP Protocol: ${httpProtocol}`, {
|
|
611
|
-
statusPageSubscriberId: (
|
|
1130
|
+
statusPageSubscriberId: (_l = data.subscriberId) === null || _l === void 0 ? void 0 : _l.toString(),
|
|
612
1131
|
});
|
|
613
|
-
const statusPageIdString = ((
|
|
1132
|
+
const statusPageIdString = ((_m = statusPage.id) === null || _m === void 0 ? void 0 : _m.toString()) || ((_o = statusPage._id) === null || _o === void 0 ? void 0 : _o.toString()) || null;
|
|
614
1133
|
const confirmSubscriptionLink = this.getConfirmSubscriptionLink({
|
|
615
1134
|
statusPageUrl: statusPageURL,
|
|
616
1135
|
confirmationToken: subscriber.subscriptionConfirmationToken || "",
|
|
617
1136
|
statusPageSubscriberId: subscriber.id,
|
|
618
1137
|
}).toString();
|
|
619
1138
|
logger.debug(`Confirm Subscription Link: ${confirmSubscriptionLink}`, {
|
|
620
|
-
statusPageSubscriberId: (
|
|
1139
|
+
statusPageSubscriberId: (_p = data.subscriberId) === null || _p === void 0 ? void 0 : _p.toString(),
|
|
621
1140
|
});
|
|
622
1141
|
if (subscriber.statusPageId &&
|
|
623
1142
|
subscriber.subscriberEmail &&
|
|
624
1143
|
subscriber._id) {
|
|
625
|
-
const unsubscribeUrl = this.getUnsubscribeLink(URL.fromString(statusPageURL), subscriber
|
|
626
|
-
logger.debug(`Unsubscribe URL: ${unsubscribeUrl}`, {
|
|
627
|
-
statusPageSubscriberId: (_p = data.subscriberId) === null || _p === void 0 ? void 0 : _p.toString(),
|
|
628
|
-
});
|
|
1144
|
+
const unsubscribeUrl = this.getUnsubscribeLink(URL.fromString(statusPageURL), subscriber).toString();
|
|
629
1145
|
const customTemplate = await StatusPageSubscriberNotificationTemplateService.getTemplateForStatusPage({
|
|
630
1146
|
statusPageId: statusPage.id,
|
|
631
1147
|
eventType: StatusPageSubscriberNotificationEventType.SubscriberSubscriptionConfirmation,
|
|
@@ -643,7 +1159,8 @@ Stay informed about service availability! 🚀`;
|
|
|
643
1159
|
* pattern used elsewhere — without custom SMTP we keep the styled
|
|
644
1160
|
* OneUptime default so emails still look right out-of-the-box).
|
|
645
1161
|
*/
|
|
646
|
-
|
|
1162
|
+
// The body is HTML, so the (plain-text) values are escaped into it.
|
|
1163
|
+
const compiledBody = StatusPageSubscriberNotificationTemplateServiceClass.compileEmailBodyTemplate(customTemplate.templateBody, templateVariables);
|
|
647
1164
|
const compiledSubject = customTemplate.emailSubject
|
|
648
1165
|
? StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(customTemplate.emailSubject, templateVariables)
|
|
649
1166
|
: "Confirm your subscription to " + statusPageName;
|
|
@@ -727,22 +1244,25 @@ Stay informed about service availability! 🚀`;
|
|
|
727
1244
|
subscriberPhone: true,
|
|
728
1245
|
projectId: true,
|
|
729
1246
|
sendYouHaveSubscribedMessage: true,
|
|
1247
|
+
unsubscribeToken: true,
|
|
730
1248
|
},
|
|
731
1249
|
props: {
|
|
732
1250
|
isRoot: true,
|
|
733
1251
|
ignoreHooks: true,
|
|
734
1252
|
},
|
|
735
1253
|
});
|
|
736
|
-
logger.debug(`Found Subscriber: ${
|
|
737
|
-
statusPageSubscriberId: (
|
|
1254
|
+
logger.debug(`Found Subscriber: ${(_c = subscriber === null || subscriber === void 0 ? void 0 : subscriber.id) === null || _c === void 0 ? void 0 : _c.toString()}`, {
|
|
1255
|
+
statusPageSubscriberId: (_d = data.subscriberId) === null || _d === void 0 ? void 0 : _d.toString(),
|
|
738
1256
|
});
|
|
739
1257
|
// get status page
|
|
740
1258
|
if (!subscriber || !subscriber.statusPageId) {
|
|
741
1259
|
logger.debug("Subscriber or Status Page ID is missing.", {
|
|
742
|
-
statusPageSubscriberId: (
|
|
1260
|
+
statusPageSubscriberId: (_e = data.subscriberId) === null || _e === void 0 ? void 0 : _e.toString(),
|
|
743
1261
|
});
|
|
744
1262
|
return;
|
|
745
1263
|
}
|
|
1264
|
+
// Its unsubscribe link needs its token (see ensureUnsubscribeTokens).
|
|
1265
|
+
await this.ensureUnsubscribeTokens([subscriber]);
|
|
746
1266
|
const statusPage = await StatusPageService.findOneBy({
|
|
747
1267
|
query: {
|
|
748
1268
|
_id: subscriber.statusPageId.toString(),
|
|
@@ -776,35 +1296,32 @@ Stay informed about service availability! 🚀`;
|
|
|
776
1296
|
},
|
|
777
1297
|
});
|
|
778
1298
|
logger.debug(`Found Status Page: ${JSON.stringify(statusPage)}`, {
|
|
779
|
-
statusPageSubscriberId: (
|
|
1299
|
+
statusPageSubscriberId: (_f = data.subscriberId) === null || _f === void 0 ? void 0 : _f.toString(),
|
|
780
1300
|
});
|
|
781
1301
|
if (!statusPage || !statusPage.id) {
|
|
782
1302
|
logger.debug("Status Page not found or ID is missing.", {
|
|
783
|
-
statusPageSubscriberId: (
|
|
1303
|
+
statusPageSubscriberId: (_g = data.subscriberId) === null || _g === void 0 ? void 0 : _g.toString(),
|
|
784
1304
|
});
|
|
785
1305
|
return;
|
|
786
1306
|
}
|
|
787
1307
|
const statusPageURL = await StatusPageService.getStatusPageURL(statusPage.id);
|
|
788
1308
|
logger.debug(`Status Page URL: ${statusPageURL}`, {
|
|
789
|
-
statusPageSubscriberId: (
|
|
1309
|
+
statusPageSubscriberId: (_h = data.subscriberId) === null || _h === void 0 ? void 0 : _h.toString(),
|
|
790
1310
|
});
|
|
791
1311
|
const statusPageName = statusPage.pageTitle || statusPage.name || "Status Page";
|
|
792
1312
|
logger.debug(`Status Page Name: ${statusPageName}`, {
|
|
793
|
-
statusPageSubscriberId: (
|
|
1313
|
+
statusPageSubscriberId: (_j = data.subscriberId) === null || _j === void 0 ? void 0 : _j.toString(),
|
|
794
1314
|
});
|
|
795
1315
|
const host = await DatabaseConfig.getHost();
|
|
796
1316
|
logger.debug(`Host: ${host}`, {
|
|
797
|
-
statusPageSubscriberId: (
|
|
1317
|
+
statusPageSubscriberId: (_k = data.subscriberId) === null || _k === void 0 ? void 0 : _k.toString(),
|
|
798
1318
|
});
|
|
799
1319
|
const httpProtocol = await DatabaseConfig.getHttpProtocol();
|
|
800
1320
|
logger.debug(`HTTP Protocol: ${httpProtocol}`, {
|
|
801
|
-
statusPageSubscriberId: (
|
|
802
|
-
});
|
|
803
|
-
const statusPageIdString = ((_l = statusPage.id) === null || _l === void 0 ? void 0 : _l.toString()) || ((_m = statusPage._id) === null || _m === void 0 ? void 0 : _m.toString()) || null;
|
|
804
|
-
const unsubscribeLink = this.getUnsubscribeLink(URL.fromString(statusPageURL), subscriber.id).toString();
|
|
805
|
-
logger.debug(`Unsubscribe Link: ${unsubscribeLink}`, {
|
|
806
|
-
statusPageSubscriberId: (_o = data.subscriberId) === null || _o === void 0 ? void 0 : _o.toString(),
|
|
1321
|
+
statusPageSubscriberId: (_l = data.subscriberId) === null || _l === void 0 ? void 0 : _l.toString(),
|
|
807
1322
|
});
|
|
1323
|
+
const statusPageIdString = ((_m = statusPage.id) === null || _m === void 0 ? void 0 : _m.toString()) || ((_o = statusPage._id) === null || _o === void 0 ? void 0 : _o.toString()) || null;
|
|
1324
|
+
const unsubscribeLink = this.getUnsubscribeLink(URL.fromString(statusPageURL), subscriber).toString();
|
|
808
1325
|
if (subscriber.statusPageId &&
|
|
809
1326
|
subscriber.subscriberEmail &&
|
|
810
1327
|
subscriber._id) {
|
|
@@ -822,7 +1339,8 @@ Stay informed about service availability! 🚀`;
|
|
|
822
1339
|
unsubscribeUrl: unsubscribeLink,
|
|
823
1340
|
};
|
|
824
1341
|
if ((customTemplate === null || customTemplate === void 0 ? void 0 : customTemplate.templateBody) && statusPage.smtpConfig) {
|
|
825
|
-
|
|
1342
|
+
// The body is HTML, so the (plain-text) values are escaped into it.
|
|
1343
|
+
const compiledBody = StatusPageSubscriberNotificationTemplateServiceClass.compileEmailBodyTemplate(customTemplate.templateBody, templateVariables);
|
|
826
1344
|
const compiledSubject = customTemplate.emailSubject
|
|
827
1345
|
? StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(customTemplate.emailSubject, templateVariables)
|
|
828
1346
|
: "You have been subscribed to " + statusPageName;
|
|
@@ -903,7 +1421,19 @@ Stay informed about service availability! 🚀`;
|
|
|
903
1421
|
});
|
|
904
1422
|
return confirmSubscriptionLink;
|
|
905
1423
|
}
|
|
906
|
-
|
|
1424
|
+
/*
|
|
1425
|
+
* The confirmed, still-subscribed subscribers of a status page, in _id
|
|
1426
|
+
* order, at most `limit` (LIMIT_MAX by default) of them.
|
|
1427
|
+
*
|
|
1428
|
+
* One read stops at the limit, so a page with more subscribers than that
|
|
1429
|
+
* is read in batches: pass the _id of the last subscriber of one batch as
|
|
1430
|
+
* `afterId` to read the next, until a batch comes back smaller than the
|
|
1431
|
+
* limit (SubscriberNotificationFanOut does this for the subscriber jobs).
|
|
1432
|
+
* The cursor is an _id rather than an offset, so a subscriber who
|
|
1433
|
+
* unsubscribes while a send is part-way through cannot shift the rows
|
|
1434
|
+
* after it into a batch already read, and nobody is skipped.
|
|
1435
|
+
*/
|
|
1436
|
+
async getSubscribersByStatusPage(statusPageId, props, options) {
|
|
907
1437
|
logger.debug("getSubscribersByStatusPage called with statusPageId:", {
|
|
908
1438
|
statusPageId: statusPageId === null || statusPageId === void 0 ? void 0 : statusPageId.toString(),
|
|
909
1439
|
});
|
|
@@ -917,11 +1447,9 @@ Stay informed about service availability! 🚀`;
|
|
|
917
1447
|
statusPageId: statusPageId === null || statusPageId === void 0 ? void 0 : statusPageId.toString(),
|
|
918
1448
|
});
|
|
919
1449
|
const subscribers = await this.findBy({
|
|
920
|
-
query: {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
isSubscriptionConfirmed: true,
|
|
924
|
-
},
|
|
1450
|
+
query: Object.assign({ statusPageId: statusPageId, isUnsubscribed: false, isSubscriptionConfirmed: true }, ((options === null || options === void 0 ? void 0 : options.afterId)
|
|
1451
|
+
? { _id: QueryHelper.greaterThan(options.afterId) }
|
|
1452
|
+
: {})),
|
|
925
1453
|
select: {
|
|
926
1454
|
_id: true,
|
|
927
1455
|
subscriberEmail: true,
|
|
@@ -933,53 +1461,278 @@ Stay informed about service availability! 🚀`;
|
|
|
933
1461
|
statusPageResources: true,
|
|
934
1462
|
isSubscribedToAllEventTypes: true,
|
|
935
1463
|
statusPageEventTypes: true,
|
|
1464
|
+
// Every sender puts this subscriber's unsubscribe link in its message.
|
|
1465
|
+
unsubscribeToken: true,
|
|
1466
|
+
/*
|
|
1467
|
+
* Whether the team added this subscriber rather than it signing up
|
|
1468
|
+
* on the status page (see isAddedByTeam).
|
|
1469
|
+
*/
|
|
1470
|
+
isAddedByTeam: true,
|
|
1471
|
+
createdByUserId: true,
|
|
1472
|
+
},
|
|
1473
|
+
// A stable order, which the afterId cursor reads on from.
|
|
1474
|
+
sort: {
|
|
1475
|
+
_id: SortOrder.Ascending,
|
|
936
1476
|
},
|
|
937
1477
|
skip: 0,
|
|
938
|
-
limit: LIMIT_MAX,
|
|
1478
|
+
limit: (options === null || options === void 0 ? void 0 : options.limit) || LIMIT_MAX,
|
|
939
1479
|
props: props,
|
|
940
1480
|
});
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1481
|
+
await this.ensureUnsubscribeTokens(subscribers);
|
|
1482
|
+
/*
|
|
1483
|
+
* The count, not the rows: each row now carries its unsubscribe token,
|
|
1484
|
+
* which is a credential and has no place in a log line.
|
|
1485
|
+
*/
|
|
1486
|
+
logger.debug(`Found ${subscribers.length} subscribers.`, {
|
|
945
1487
|
statusPageId: statusPageId === null || statusPageId === void 0 ? void 0 : statusPageId.toString(),
|
|
946
1488
|
});
|
|
947
1489
|
return subscribers;
|
|
948
1490
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1491
|
+
/*
|
|
1492
|
+
* How many subscribers of each status page each channel reaches, for the
|
|
1493
|
+
* audience summary shown before an incident or a public note is sent
|
|
1494
|
+
* (IncidentSubscriberAudience). The same subscribers getSubscribersByStatusPage
|
|
1495
|
+
* sends to - confirmed, not unsubscribed - counted per channel in one
|
|
1496
|
+
* aggregate query rather than read out: it never loads an address.
|
|
1497
|
+
*
|
|
1498
|
+
* A channel counts a subscriber whenever the job would send on it, which is
|
|
1499
|
+
* whenever that column holds a value; a subscription with both an email and
|
|
1500
|
+
* a phone counts once on each. Rows are pinned to the project, whatever ids
|
|
1501
|
+
* the caller passes.
|
|
1502
|
+
*
|
|
1503
|
+
* Keyed by the lower-cased status page id. A page with no active subscriber
|
|
1504
|
+
* has no entry.
|
|
1505
|
+
*/
|
|
1506
|
+
async countActiveSubscribersByChannel(data) {
|
|
1507
|
+
const counts = {};
|
|
1508
|
+
const statusPageIds = [];
|
|
1509
|
+
for (const statusPageId of data.statusPageIds) {
|
|
1510
|
+
const id = statusPageId.toString().trim().toLowerCase();
|
|
1511
|
+
if (id && !statusPageIds.includes(id)) {
|
|
1512
|
+
statusPageIds.push(id);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
if (statusPageIds.length === 0) {
|
|
1516
|
+
return counts;
|
|
1517
|
+
}
|
|
1518
|
+
const rows = await this.getRepository().manager.query(`SELECT
|
|
1519
|
+
"statusPageId"::text AS "statusPageId",
|
|
1520
|
+
COUNT(*) FILTER (WHERE NULLIF(TRIM("subscriberEmail"), '') IS NOT NULL)::text AS "email",
|
|
1521
|
+
COUNT(*) FILTER (WHERE NULLIF(TRIM("subscriberPhone"), '') IS NOT NULL)::text AS "sms",
|
|
1522
|
+
COUNT(*) FILTER (WHERE NULLIF(TRIM("slackIncomingWebhookUrl"), '') IS NOT NULL)::text AS "slack",
|
|
1523
|
+
COUNT(*) FILTER (WHERE NULLIF(TRIM("microsoftTeamsIncomingWebhookUrl"), '') IS NOT NULL)::text AS "microsoftTeams",
|
|
1524
|
+
COUNT(*) FILTER (WHERE NULLIF(TRIM("subscriberWebhook"), '') IS NOT NULL)::text AS "webhook"
|
|
1525
|
+
FROM "StatusPageSubscriber"
|
|
1526
|
+
WHERE "projectId" = $1
|
|
1527
|
+
AND "statusPageId" = ANY($2::uuid[])
|
|
1528
|
+
AND "isUnsubscribed" = false
|
|
1529
|
+
AND "isSubscriptionConfirmed" = true
|
|
1530
|
+
AND "deletedAt" IS NULL
|
|
1531
|
+
GROUP BY "statusPageId"`, [data.projectId.toString(), statusPageIds]);
|
|
1532
|
+
const toCount = (value) => {
|
|
1533
|
+
const count = parseInt(value || "0", 10);
|
|
1534
|
+
return Number.isFinite(count) && count > 0 ? count : 0;
|
|
1535
|
+
};
|
|
1536
|
+
for (const row of rows) {
|
|
1537
|
+
counts[row.statusPageId.toLowerCase()] = {
|
|
1538
|
+
email: toCount(row.email),
|
|
1539
|
+
sms: toCount(row.sms),
|
|
1540
|
+
slack: toCount(row.slack),
|
|
1541
|
+
microsoftTeams: toCount(row.microsoftTeams),
|
|
1542
|
+
webhook: toCount(row.webhook),
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
return counts;
|
|
1546
|
+
}
|
|
1547
|
+
/*
|
|
1548
|
+
* The link to a subscriber's unsubscribe page, which every notification to
|
|
1549
|
+
* it carries: {statusPageUrl}/unsubscribe/{id}-{token}. It works on public
|
|
1550
|
+
* and private status pages alike and without signing in, and it only ever
|
|
1551
|
+
* asks - the page unsubscribes when its reader confirms (see
|
|
1552
|
+
* Common/Types/StatusPage/StatusPageSubscriberUnsubscribe).
|
|
1553
|
+
*
|
|
1554
|
+
* Pass the subscriber read with its unsubscribeToken: every loader that
|
|
1555
|
+
* feeds a sender selects it (getSubscribersByStatusPage and the subscribe
|
|
1556
|
+
* and manage emails). A subscriber without one still gets a link, to the
|
|
1557
|
+
* page that says the link is out of date, rather than none; that is logged,
|
|
1558
|
+
* because only a subscriber created around the migration that added the
|
|
1559
|
+
* token, or one written with hooks skipped, can lack it.
|
|
1560
|
+
*/
|
|
1561
|
+
getUnsubscribeLink(statusPageUrl, subscriber) {
|
|
1562
|
+
const subscriberId = subscriber.id;
|
|
1563
|
+
if (!subscriberId) {
|
|
1564
|
+
throw new BadDataException("A subscriber id is required to build an unsubscribe link.");
|
|
1565
|
+
}
|
|
1566
|
+
if (!StatusPageSubscriberUnsubscribe.isWellFormedToken(subscriber.unsubscribeToken)) {
|
|
1567
|
+
logger.error(`Status page subscriber ${subscriberId.toString()} has no unsubscribe token; its unsubscribe link leads to the out-of-date link page.`, {
|
|
1568
|
+
statusPageSubscriberId: subscriberId.toString(),
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
return StatusPageSubscriberUnsubscribe.buildLink({
|
|
1572
|
+
statusPageUrl: statusPageUrl,
|
|
1573
|
+
subscriberId: subscriberId,
|
|
1574
|
+
unsubscribeToken: subscriber.unsubscribeToken,
|
|
961
1575
|
});
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1576
|
+
}
|
|
1577
|
+
/*
|
|
1578
|
+
* The subscriber's Update Subscription page, where it can choose resources
|
|
1579
|
+
* and event types (and unsubscribe). The "manage your subscription" email a
|
|
1580
|
+
* visitor asks for from the Subscribe page links here. On a private status
|
|
1581
|
+
* page it needs a signed-in visitor, like the rest of the page.
|
|
1582
|
+
*/
|
|
1583
|
+
getManageSubscriptionLink(statusPageUrl, statusPageSubscriberId) {
|
|
1584
|
+
return StatusPageSubscriberUnsubscribe.buildManageSubscriptionLink({
|
|
1585
|
+
statusPageUrl: statusPageUrl,
|
|
1586
|
+
subscriberId: statusPageSubscriberId,
|
|
965
1587
|
});
|
|
966
|
-
|
|
967
|
-
|
|
1588
|
+
}
|
|
1589
|
+
/*
|
|
1590
|
+
* Give every subscriber in the list an unsubscribe token it lacks, in place,
|
|
1591
|
+
* so the links built from them work. Every create mints one, and the
|
|
1592
|
+
* BackfillStatusPageSubscriberUnsubscribeColumns data migration gives one
|
|
1593
|
+
* to every subscriber that existed before; this covers the ones that
|
|
1594
|
+
* migration has not reached yet (it runs after the upgrade, in batches), a
|
|
1595
|
+
* subscriber created in between, and one written with hooks skipped.
|
|
1596
|
+
*
|
|
1597
|
+
* One statement per batch of subscribers, not per subscriber: until the
|
|
1598
|
+
* backfill has run, a whole page's list can be missing its tokens.
|
|
1599
|
+
*
|
|
1600
|
+
* The write only fills an empty column, so two senders racing on the same
|
|
1601
|
+
* subscriber - or a sender and the backfill - cannot hand out two different
|
|
1602
|
+
* tokens: the loser reads back the winner's.
|
|
1603
|
+
*/
|
|
1604
|
+
async ensureUnsubscribeTokens(subscribers) {
|
|
1605
|
+
// The rows that lack one, by id: a list can hold the same subscriber twice.
|
|
1606
|
+
const missing = new Map();
|
|
1607
|
+
for (const subscriber of subscribers) {
|
|
1608
|
+
if (!subscriber._id ||
|
|
1609
|
+
StatusPageSubscriberUnsubscribe.isWellFormedToken(subscriber.unsubscribeToken)) {
|
|
1610
|
+
continue;
|
|
1611
|
+
}
|
|
1612
|
+
const id = subscriber._id.toString().toLowerCase();
|
|
1613
|
+
missing.set(id, [...(missing.get(id) || []), subscriber]);
|
|
1614
|
+
}
|
|
1615
|
+
const ids = Array.from(missing.keys());
|
|
1616
|
+
for (let start = 0; start < ids.length; start += UNSUBSCRIBE_COLUMNS_BATCH_SIZE) {
|
|
1617
|
+
const batch = ids.slice(start, start + UNSUBSCRIBE_COLUMNS_BATCH_SIZE);
|
|
1618
|
+
await this.fillMissingUnsubscribeTokens(batch);
|
|
1619
|
+
const stored = await this.findBy({
|
|
1620
|
+
query: {
|
|
1621
|
+
_id: QueryHelper.any(batch),
|
|
1622
|
+
},
|
|
1623
|
+
select: {
|
|
1624
|
+
_id: true,
|
|
1625
|
+
unsubscribeToken: true,
|
|
1626
|
+
},
|
|
1627
|
+
skip: 0,
|
|
1628
|
+
limit: batch.length,
|
|
1629
|
+
props: {
|
|
1630
|
+
isRoot: true,
|
|
1631
|
+
ignoreHooks: true,
|
|
1632
|
+
},
|
|
1633
|
+
});
|
|
1634
|
+
for (const row of stored) {
|
|
1635
|
+
if (!row._id ||
|
|
1636
|
+
!StatusPageSubscriberUnsubscribe.isWellFormedToken(row.unsubscribeToken)) {
|
|
1637
|
+
continue;
|
|
1638
|
+
}
|
|
1639
|
+
for (const subscriber of missing.get(row._id.toString().toLowerCase()) || []) {
|
|
1640
|
+
subscriber.unsubscribeToken = row.unsubscribeToken;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
/*
|
|
1646
|
+
* The BackfillStatusPageSubscriberUnsubscribeColumns data migration: give
|
|
1647
|
+
* every subscriber that existed before the upgrade its unsubscribe token,
|
|
1648
|
+
* and mark the ones with a creator as added by the team (Is Added By
|
|
1649
|
+
* Team). Soft-deleted subscribers are included: one that is restored must
|
|
1650
|
+
* not come back without a token.
|
|
1651
|
+
*
|
|
1652
|
+
* The table is walked in primary key order, a batch of ids at a time, and
|
|
1653
|
+
* each batch is written by primary key in short statements of their own.
|
|
1654
|
+
* So however large the table, no statement holds its rows for long or runs
|
|
1655
|
+
* into the connection's statement timeout, and notifications, sign-ups and
|
|
1656
|
+
* the subscriber lists carry on while it runs - which one UPDATE of every
|
|
1657
|
+
* row, in the migration that added the columns, would have stopped.
|
|
1658
|
+
*
|
|
1659
|
+
* Idempotent, and safe to run twice at once (the data migration runner is
|
|
1660
|
+
* not serialized): every write only fills what is still empty, and
|
|
1661
|
+
* re-checks that under the row lock, so a token a sender has already put
|
|
1662
|
+
* in a message is never replaced.
|
|
1663
|
+
*/
|
|
1664
|
+
async backfillUnsubscribeColumns(options) {
|
|
1665
|
+
const batchSize = Math.max(1, Math.floor((options === null || options === void 0 ? void 0 : options.batchSize) || UNSUBSCRIBE_COLUMNS_BATCH_SIZE));
|
|
1666
|
+
const result = {
|
|
1667
|
+
tokensGiven: 0,
|
|
1668
|
+
markedAddedByTeam: 0,
|
|
1669
|
+
};
|
|
1670
|
+
let lastId = null;
|
|
1671
|
+
for (;;) {
|
|
1672
|
+
const rows = await this.getRepository().manager.query(`SELECT "_id"::text AS "_id",
|
|
1673
|
+
("unsubscribeToken" IS NULL) AS "needsToken",
|
|
1674
|
+
("isAddedByTeam" = false AND "createdByUserId" IS NOT NULL) AS "needsAddedByTeam"
|
|
1675
|
+
FROM "StatusPageSubscriber"
|
|
1676
|
+
WHERE ($2::uuid IS NULL OR "_id" > $2::uuid)
|
|
1677
|
+
ORDER BY "_id" ASC
|
|
1678
|
+
LIMIT $1`, [batchSize, lastId]);
|
|
1679
|
+
if (rows.length === 0) {
|
|
1680
|
+
break;
|
|
1681
|
+
}
|
|
1682
|
+
const needToken = rows
|
|
1683
|
+
.filter((row) => {
|
|
1684
|
+
return row.needsToken === true;
|
|
1685
|
+
})
|
|
1686
|
+
.map((row) => {
|
|
1687
|
+
return row._id;
|
|
1688
|
+
});
|
|
1689
|
+
const needAddedByTeam = rows
|
|
1690
|
+
.filter((row) => {
|
|
1691
|
+
return row.needsAddedByTeam === true;
|
|
1692
|
+
})
|
|
1693
|
+
.map((row) => {
|
|
1694
|
+
return row._id;
|
|
1695
|
+
});
|
|
1696
|
+
result.tokensGiven += await this.fillMissingUnsubscribeTokens(needToken);
|
|
1697
|
+
if (needAddedByTeam.length > 0) {
|
|
1698
|
+
result.markedAddedByTeam += affectedRowCount(await this.getRepository().manager.query(`UPDATE "StatusPageSubscriber" SET "isAddedByTeam" = true WHERE "_id" = ANY($1::uuid[]) AND "createdByUserId" IS NOT NULL AND "isAddedByTeam" = false`, [needAddedByTeam]));
|
|
1699
|
+
}
|
|
1700
|
+
lastId = rows[rows.length - 1]._id;
|
|
1701
|
+
if (rows.length < batchSize) {
|
|
1702
|
+
break;
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
return result;
|
|
1706
|
+
}
|
|
1707
|
+
/*
|
|
1708
|
+
* Mint a token, from Node's CSPRNG like every other, for each of these
|
|
1709
|
+
* subscribers that has none, in one statement. Returns how many were given
|
|
1710
|
+
* one. `"unsubscribeToken" IS NULL` is checked again under each row's lock,
|
|
1711
|
+
* so a token another writer stored first is kept.
|
|
1712
|
+
*/
|
|
1713
|
+
async fillMissingUnsubscribeTokens(subscriberIds) {
|
|
1714
|
+
if (subscriberIds.length === 0) {
|
|
1715
|
+
return 0;
|
|
1716
|
+
}
|
|
1717
|
+
const tokens = subscriberIds.map(() => {
|
|
1718
|
+
return StatusPageSubscriberUnsubscribeToken.generate();
|
|
968
1719
|
});
|
|
969
|
-
return
|
|
1720
|
+
return affectedRowCount(await this.getRepository().manager.query(`UPDATE "StatusPageSubscriber" AS "subscriber" SET "unsubscribeToken" = "minted"."token" FROM unnest($1::uuid[], $2::text[]) AS "minted"("id", "token") WHERE "subscriber"."_id" = "minted"."id" AND "subscriber"."unsubscribeToken" IS NULL`, [subscriberIds, tokens]));
|
|
970
1721
|
}
|
|
971
1722
|
shouldSendNotification(data) {
|
|
972
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1;
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1723
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
|
|
1724
|
+
/*
|
|
1725
|
+
* Ids only: the subscriber carries its unsubscribe token, which is a
|
|
1726
|
+
* credential (it cancels the subscription, private pages included), and
|
|
1727
|
+
* every subscriber job calls this for every subscriber.
|
|
1728
|
+
*/
|
|
1729
|
+
logger.debug(`shouldSendNotification called for subscriber ${((_b = (_a = data.subscriber) === null || _a === void 0 ? void 0 : _a._id) === null || _b === void 0 ? void 0 : _b.toString()) || ""} (${data.eventType}, ${((_c = data.statusPageResources) === null || _c === void 0 ? void 0 : _c.length) || 0} resource(s)).`, {
|
|
1730
|
+
statusPageId: (_e = (_d = data.statusPage) === null || _d === void 0 ? void 0 : _d.id) === null || _e === void 0 ? void 0 : _e.toString(),
|
|
978
1731
|
});
|
|
979
1732
|
let shouldSendNotification = true; // default to true.
|
|
980
1733
|
if (data.subscriber.isUnsubscribed) {
|
|
981
1734
|
logger.debug("Subscriber is unsubscribed.", {
|
|
982
|
-
statusPageId: (
|
|
1735
|
+
statusPageId: (_g = (_f = data.statusPage) === null || _f === void 0 ? void 0 : _f.id) === null || _g === void 0 ? void 0 : _g.toString(),
|
|
983
1736
|
});
|
|
984
1737
|
shouldSendNotification = false;
|
|
985
1738
|
return shouldSendNotification;
|
|
@@ -989,29 +1742,29 @@ Stay informed about service availability! 🚀`;
|
|
|
989
1742
|
!(data.eventType === StatusPageEventType.Announcement &&
|
|
990
1743
|
data.statusPageResources.length === 0) // announcements with no monitors don't use resource filtering
|
|
991
1744
|
) {
|
|
992
|
-
logger.debug("Subscriber can choose resources and is not subscribed to all resources.", { statusPageId: (
|
|
993
|
-
const subscriberResourceIds = ((
|
|
1745
|
+
logger.debug("Subscriber can choose resources and is not subscribed to all resources.", { statusPageId: (_j = (_h = data.statusPage) === null || _h === void 0 ? void 0 : _h.id) === null || _j === void 0 ? void 0 : _j.toString() });
|
|
1746
|
+
const subscriberResourceIds = ((_k = data.subscriber.statusPageResources) === null || _k === void 0 ? void 0 : _k.map((resource) => {
|
|
994
1747
|
var _a;
|
|
995
1748
|
return (_a = resource.id) === null || _a === void 0 ? void 0 : _a.toString();
|
|
996
1749
|
})) || [];
|
|
997
1750
|
logger.debug(`Subscriber Resource IDs: ${subscriberResourceIds}`, {
|
|
998
|
-
statusPageId: (
|
|
1751
|
+
statusPageId: (_m = (_l = data.statusPage) === null || _l === void 0 ? void 0 : _l.id) === null || _m === void 0 ? void 0 : _m.toString(),
|
|
999
1752
|
});
|
|
1000
1753
|
let shouldSendNotificationForResource = false;
|
|
1001
1754
|
if (subscriberResourceIds.length === 0) {
|
|
1002
1755
|
logger.debug("Subscriber has no resource IDs.", {
|
|
1003
|
-
statusPageId: (
|
|
1756
|
+
statusPageId: (_p = (_o = data.statusPage) === null || _o === void 0 ? void 0 : _o.id) === null || _p === void 0 ? void 0 : _p.toString(),
|
|
1004
1757
|
});
|
|
1005
1758
|
shouldSendNotificationForResource = false;
|
|
1006
1759
|
}
|
|
1007
1760
|
else {
|
|
1008
1761
|
for (const resource of data.statusPageResources) {
|
|
1009
1762
|
logger.debug(`Checking resource: ${resource.id}`, {
|
|
1010
|
-
statusPageId: (
|
|
1763
|
+
statusPageId: (_r = (_q = data.statusPage) === null || _q === void 0 ? void 0 : _q.id) === null || _r === void 0 ? void 0 : _r.toString(),
|
|
1011
1764
|
});
|
|
1012
|
-
if (subscriberResourceIds.includes((
|
|
1765
|
+
if (subscriberResourceIds.includes((_s = resource.id) === null || _s === void 0 ? void 0 : _s.toString())) {
|
|
1013
1766
|
logger.debug("Resource ID matches subscriber's resource ID.", {
|
|
1014
|
-
statusPageId: (
|
|
1767
|
+
statusPageId: (_u = (_t = data.statusPage) === null || _t === void 0 ? void 0 : _t.id) === null || _u === void 0 ? void 0 : _u.toString(),
|
|
1015
1768
|
});
|
|
1016
1769
|
shouldSendNotificationForResource = true;
|
|
1017
1770
|
}
|
|
@@ -1019,7 +1772,7 @@ Stay informed about service availability! 🚀`;
|
|
|
1019
1772
|
}
|
|
1020
1773
|
if (!shouldSendNotificationForResource) {
|
|
1021
1774
|
logger.debug("Should not send notification for resource.", {
|
|
1022
|
-
statusPageId: (
|
|
1775
|
+
statusPageId: (_w = (_v = data.statusPage) === null || _v === void 0 ? void 0 : _v.id) === null || _w === void 0 ? void 0 : _w.toString(),
|
|
1023
1776
|
});
|
|
1024
1777
|
shouldSendNotification = false;
|
|
1025
1778
|
}
|
|
@@ -1027,15 +1780,15 @@ Stay informed about service availability! 🚀`;
|
|
|
1027
1780
|
// now do for event types
|
|
1028
1781
|
if (data.statusPage.allowSubscribersToChooseEventTypes &&
|
|
1029
1782
|
!data.subscriber.isSubscribedToAllEventTypes) {
|
|
1030
|
-
logger.debug("Subscriber can choose event types and is not subscribed to all event types.", { statusPageId: (
|
|
1783
|
+
logger.debug("Subscriber can choose event types and is not subscribed to all event types.", { statusPageId: (_y = (_x = data.statusPage) === null || _x === void 0 ? void 0 : _x.id) === null || _y === void 0 ? void 0 : _y.toString() });
|
|
1031
1784
|
const subscriberEventTypes = data.subscriber.statusPageEventTypes || [];
|
|
1032
1785
|
logger.debug(`Subscriber Event Types: ${subscriberEventTypes}`, {
|
|
1033
|
-
statusPageId: (
|
|
1786
|
+
statusPageId: (_0 = (_z = data.statusPage) === null || _z === void 0 ? void 0 : _z.id) === null || _0 === void 0 ? void 0 : _0.toString(),
|
|
1034
1787
|
});
|
|
1035
1788
|
let shouldSendNotificationForEventType = false;
|
|
1036
1789
|
if (subscriberEventTypes.includes(data.eventType)) {
|
|
1037
1790
|
logger.debug("Event type matches subscriber's event type.", {
|
|
1038
|
-
statusPageId: (
|
|
1791
|
+
statusPageId: (_2 = (_1 = data.statusPage) === null || _1 === void 0 ? void 0 : _1.id) === null || _2 === void 0 ? void 0 : _2.toString(),
|
|
1039
1792
|
});
|
|
1040
1793
|
shouldSendNotificationForEventType = true;
|
|
1041
1794
|
}
|
|
@@ -1110,6 +1863,13 @@ Stay informed about service availability! 🚀`;
|
|
|
1110
1863
|
* every episode notification from being sent.
|
|
1111
1864
|
*/
|
|
1112
1865
|
showEpisodesOnStatusPage: true,
|
|
1866
|
+
/*
|
|
1867
|
+
* IncidentStatusPageScope decides from this which incidents reach a
|
|
1868
|
+
* page, and treats a page without it as showing only incidents
|
|
1869
|
+
* limited to it - so leaving it out would silence every unscoped
|
|
1870
|
+
* incident's notifications.
|
|
1871
|
+
*/
|
|
1872
|
+
onlyShowScopedIncidents: true,
|
|
1113
1873
|
},
|
|
1114
1874
|
});
|
|
1115
1875
|
logger.debug("Found status pages:", {});
|
|
@@ -1226,12 +1986,60 @@ You will receive real-time notifications for:
|
|
|
1226
1986
|
}
|
|
1227
1987
|
}
|
|
1228
1988
|
}
|
|
1989
|
+
__decorate([
|
|
1990
|
+
CaptureSpan(),
|
|
1991
|
+
__metadata("design:type", Function),
|
|
1992
|
+
__metadata("design:paramtypes", [Object]),
|
|
1993
|
+
__metadata("design:returntype", Promise)
|
|
1994
|
+
], Service.prototype, "create", null);
|
|
1995
|
+
__decorate([
|
|
1996
|
+
CaptureSpan(),
|
|
1997
|
+
__metadata("design:type", Function),
|
|
1998
|
+
__metadata("design:paramtypes", [Model]),
|
|
1999
|
+
__metadata("design:returntype", Promise)
|
|
2000
|
+
], Service.prototype, "createFromStatusPageSignUp", null);
|
|
1229
2001
|
__decorate([
|
|
1230
2002
|
CaptureSpan(),
|
|
1231
2003
|
__metadata("design:type", Function),
|
|
1232
2004
|
__metadata("design:paramtypes", [Object]),
|
|
1233
2005
|
__metadata("design:returntype", Promise)
|
|
1234
2006
|
], Service.prototype, "onBeforeCreate", null);
|
|
2007
|
+
__decorate([
|
|
2008
|
+
CaptureSpan(),
|
|
2009
|
+
__metadata("design:type", Function),
|
|
2010
|
+
__metadata("design:paramtypes", [Object]),
|
|
2011
|
+
__metadata("design:returntype", Promise)
|
|
2012
|
+
], Service.prototype, "onBeforeUpdate", null);
|
|
2013
|
+
__decorate([
|
|
2014
|
+
CaptureSpan(),
|
|
2015
|
+
__metadata("design:type", Function),
|
|
2016
|
+
__metadata("design:paramtypes", [Object, Array]),
|
|
2017
|
+
__metadata("design:returntype", Promise)
|
|
2018
|
+
], Service.prototype, "onUpdateSuccess", null);
|
|
2019
|
+
__decorate([
|
|
2020
|
+
CaptureSpan(),
|
|
2021
|
+
__metadata("design:type", Function),
|
|
2022
|
+
__metadata("design:paramtypes", [Object]),
|
|
2023
|
+
__metadata("design:returntype", Promise)
|
|
2024
|
+
], Service.prototype, "unsubscribe", null);
|
|
2025
|
+
__decorate([
|
|
2026
|
+
CaptureSpan(),
|
|
2027
|
+
__metadata("design:type", Function),
|
|
2028
|
+
__metadata("design:paramtypes", [Object]),
|
|
2029
|
+
__metadata("design:returntype", Promise)
|
|
2030
|
+
], Service.prototype, "getUnsubscribeLinkDetails", null);
|
|
2031
|
+
__decorate([
|
|
2032
|
+
CaptureSpan(),
|
|
2033
|
+
__metadata("design:type", Function),
|
|
2034
|
+
__metadata("design:paramtypes", [Object]),
|
|
2035
|
+
__metadata("design:returntype", Promise)
|
|
2036
|
+
], Service.prototype, "unsubscribeWithLink", null);
|
|
2037
|
+
__decorate([
|
|
2038
|
+
CaptureSpan(),
|
|
2039
|
+
__metadata("design:type", Function),
|
|
2040
|
+
__metadata("design:paramtypes", [Object]),
|
|
2041
|
+
__metadata("design:returntype", Promise)
|
|
2042
|
+
], Service.prototype, "notifyTeamOfUnsubscribe", null);
|
|
1235
2043
|
__decorate([
|
|
1236
2044
|
CaptureSpan(),
|
|
1237
2045
|
__metadata("design:type", Function),
|
|
@@ -1253,9 +2061,27 @@ __decorate([
|
|
|
1253
2061
|
__decorate([
|
|
1254
2062
|
CaptureSpan(),
|
|
1255
2063
|
__metadata("design:type", Function),
|
|
1256
|
-
__metadata("design:paramtypes", [ObjectID, Object]),
|
|
2064
|
+
__metadata("design:paramtypes", [ObjectID, Object, Object]),
|
|
1257
2065
|
__metadata("design:returntype", Promise)
|
|
1258
2066
|
], Service.prototype, "getSubscribersByStatusPage", null);
|
|
2067
|
+
__decorate([
|
|
2068
|
+
CaptureSpan(),
|
|
2069
|
+
__metadata("design:type", Function),
|
|
2070
|
+
__metadata("design:paramtypes", [Object]),
|
|
2071
|
+
__metadata("design:returntype", Promise)
|
|
2072
|
+
], Service.prototype, "countActiveSubscribersByChannel", null);
|
|
2073
|
+
__decorate([
|
|
2074
|
+
CaptureSpan(),
|
|
2075
|
+
__metadata("design:type", Function),
|
|
2076
|
+
__metadata("design:paramtypes", [Array]),
|
|
2077
|
+
__metadata("design:returntype", Promise)
|
|
2078
|
+
], Service.prototype, "ensureUnsubscribeTokens", null);
|
|
2079
|
+
__decorate([
|
|
2080
|
+
CaptureSpan(),
|
|
2081
|
+
__metadata("design:type", Function),
|
|
2082
|
+
__metadata("design:paramtypes", [Object]),
|
|
2083
|
+
__metadata("design:returntype", Promise)
|
|
2084
|
+
], Service.prototype, "backfillUnsubscribeColumns", null);
|
|
1259
2085
|
__decorate([
|
|
1260
2086
|
CaptureSpan(),
|
|
1261
2087
|
__metadata("design:type", Function),
|