@oneuptime/common 12.0.7 → 12.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/AnalyticsModels/MetricItemAggMV1mByK8sCluster.ts +1 -1
- package/Models/AnalyticsModels/MetricItemAggMV1mByService.ts +1 -1
- package/Models/DatabaseModels/AIConversation.ts +32 -0
- package/Models/DatabaseModels/AIConversationMessage.ts +41 -0
- package/Models/DatabaseModels/AlertEpisodeMember.ts +27 -0
- package/Models/DatabaseModels/IncidentEpisodeMember.ts +28 -0
- package/Models/DatabaseModels/Index.ts +12 -4
- package/Models/DatabaseModels/{TelemetryEntity.ts → InventoryItem.ts} +184 -9
- package/Models/DatabaseModels/InventoryItemCustomField.ts +434 -0
- package/Models/DatabaseModels/{TelemetryEntityRelationship.ts → InventoryItemRelationship.ts} +33 -7
- package/Models/DatabaseModels/NetworkDevice.ts +141 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +699 -0
- package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +467 -0
- package/Models/DatabaseModels/NetworkTopologySuppression.ts +429 -0
- package/Models/DatabaseModels/OnCallDutyPolicyFeed.ts +9 -0
- package/Models/DatabaseModels/Project.ts +78 -0
- package/Models/DatabaseModels/UserCall.ts +42 -0
- package/Models/DatabaseModels/UserEmail.ts +44 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +425 -55
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +24 -2
- package/Models/DatabaseModels/UserPush.ts +49 -0
- package/Models/DatabaseModels/UserSMS.ts +43 -0
- package/Models/DatabaseModels/UserTelegram.ts +59 -0
- package/Models/DatabaseModels/UserWebhook.ts +52 -0
- package/Models/DatabaseModels/UserWhatsApp.ts +41 -0
- package/Models/DatabaseModels/WorkflowLog.ts +38 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +12 -0
- package/Server/API/AIChatAPI.ts +315 -1
- package/Server/API/DashboardAPI.ts +217 -1
- package/Server/API/OnCallReadinessAPI.ts +841 -0
- package/Server/API/TeamComplianceAPI.ts +69 -17
- package/Server/API/TelemetryAPI.ts +220 -12
- package/Server/API/UserAPI.ts +16 -1
- package/Server/EnvironmentConfig.ts +52 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +4 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +5 -5
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.ts +82 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.ts +91 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.ts +255 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.ts +107 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.ts +90 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +24 -0
- package/Server/Infrastructure/Queue.ts +78 -13
- package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
- package/Server/Middleware/PublicDashboardRateLimit.ts +593 -0
- package/Server/Services/AIService.ts +7 -0
- package/Server/Services/AlertEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/AlertSeverityService.ts +63 -0
- package/Server/Services/DashboardService.ts +9 -10
- package/Server/Services/DatabaseService.ts +32 -2
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +29 -0
- package/Server/Services/IncidentSeverityService.ts +76 -0
- package/Server/Services/Index.ts +12 -4
- package/Server/Services/InventoryItemCustomFieldService.ts +9 -0
- package/Server/Services/{TelemetryEntityRelationshipService.ts → InventoryItemRelationshipService.ts} +7 -4
- package/Server/Services/{TelemetryEntityService.ts → InventoryItemService.ts} +203 -21
- package/Server/Services/LogAggregationService.ts +45 -8
- package/Server/Services/MetricAggregationService.ts +121 -0
- package/Server/Services/MetricService.ts +7 -7
- package/Server/Services/NetworkDeviceLinkRuleService.ts +10 -0
- package/Server/Services/NetworkDeviceLinkService.ts +84 -0
- package/Server/Services/NetworkDeviceService.ts +140 -0
- package/Server/Services/NetworkSiteService.ts +77 -25
- package/Server/Services/NetworkTopologySuppressionService.ts +84 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +41 -29
- package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +8 -0
- package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +62 -13
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +61 -1
- package/Server/Services/OnCallNotificationAlertingService.ts +742 -0
- package/Server/Services/OnCallReadinessService.ts +2803 -0
- package/Server/Services/OnCallSetupReminderService.ts +955 -0
- package/Server/Services/ProfileAggregationService.ts +123 -0
- package/Server/Services/StatusPageService.ts +9 -10
- package/Server/Services/TeamComplianceService.ts +429 -252
- package/Server/Services/UserCallService.ts +26 -1
- package/Server/Services/UserEmailService.ts +26 -1
- package/Server/Services/UserNotificationRuleAdminService.ts +1183 -0
- package/Server/Services/UserNotificationRuleService.ts +3812 -333
- package/Server/Services/UserOnCallLogService.ts +561 -48
- package/Server/Services/UserPushService.ts +29 -0
- package/Server/Services/UserService.ts +11 -0
- package/Server/Services/UserSmsService.ts +26 -1
- package/Server/Services/UserTelegramService.ts +24 -1
- package/Server/Services/UserWebhookService.ts +28 -1
- package/Server/Services/UserWhatsAppService.ts +24 -1
- package/Server/Types/Database/Permissions/BasePermission.ts +19 -0
- package/Server/Types/Database/Permissions/CreatePermission.ts +164 -0
- package/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.ts +340 -0
- package/Server/Types/Database/Permissions/QueryPermission.ts +48 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +8 -1
- package/Server/Types/Workflow/Components/API/Delete.ts +1 -1
- package/Server/Types/Workflow/Components/API/Get.ts +1 -1
- package/Server/Types/Workflow/Components/API/Patch.ts +1 -1
- package/Server/Types/Workflow/Components/API/Post.ts +1 -1
- package/Server/Types/Workflow/Components/API/Put.ts +1 -1
- package/Server/Types/Workflow/Components/API/Utils.ts +44 -1
- package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +29 -5
- package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +18 -10
- package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +55 -0
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +3 -17
- package/Server/Types/Workflow/Components/Email.ts +25 -7
- package/Server/Types/Workflow/Components/JavaScript.ts +10 -3
- package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +1 -1
- package/Server/Types/Workflow/TriggerCode.ts +12 -0
- package/Server/Types/Workflow/Workflow.ts +5 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +643 -48
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +32 -3
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +20 -6
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +7 -0
- package/Server/Utils/AI/Toolbox/AIActionTools.ts +2 -2
- package/Server/Utils/AI/Toolbox/AIMetaTools.ts +863 -0
- package/Server/Utils/AI/Toolbox/AlertTools.ts +177 -15
- package/Server/Utils/AI/Toolbox/IncidentTools.ts +191 -10
- package/Server/Utils/AI/Toolbox/Index.ts +48 -0
- package/Server/Utils/AI/Toolbox/MonitorTools.ts +298 -11
- package/Server/Utils/AI/Toolbox/NoteWriteTools.ts +295 -0
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +1246 -0
- package/Server/Utils/AI/Toolbox/RunbookTools.ts +424 -0
- package/Server/Utils/AI/Toolbox/SloTools.ts +456 -0
- package/Server/Utils/AI/Toolbox/StatusPageTools.ts +559 -0
- package/Server/Utils/AI/Toolbox/TeamTools.ts +327 -0
- package/Server/Utils/AI/Toolbox/TimelineTools.ts +615 -0
- package/Server/Utils/AI/Toolbox/WorkflowProbeTools.ts +664 -0
- package/Server/Utils/ClientIp.ts +221 -0
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +47 -0
- package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +163 -0
- package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +147 -0
- package/Server/Utils/Express.ts +12 -17
- package/Server/Utils/LLM/LLMService.ts +85 -8
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +204 -10
- package/Server/Utils/SSRFProtection.ts +98 -23
- package/Server/Utils/StartServer.ts +12 -3
- package/Server/Utils/Telemetry/EntityRegistry.ts +205 -18
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +689 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +160 -52
- package/Server/Utils/VM/VMAPI.ts +56 -8
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +7 -3
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +2146 -0
- package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +561 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +2470 -0
- package/Tests/App/Dashboard/MonitorCriteriaAttributeFilter.test.tsx +574 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +1897 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +3606 -0
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +784 -0
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +1119 -0
- package/Tests/App/Dashboard/SloWidgetFetching.test.tsx +531 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +1312 -0
- package/Tests/App/Dashboard/UserSettingsSetupChecklistPage.test.tsx +1390 -0
- package/Tests/Models/InventoryItemModel.test.ts +174 -0
- package/Tests/Models/InventoryItemNaming.test.ts +302 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +437 -0
- package/Tests/Server/API/DashboardPublicRateLimit.test.ts +659 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +18 -0
- package/Tests/Server/API/DashboardPublicSloAPI.test.ts +880 -0
- package/Tests/Server/API/Helpers.ts +24 -15
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +2680 -0
- package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +915 -0
- package/Tests/Server/API/UserProjectsAPI.test.ts +478 -4
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +533 -0
- package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +213 -0
- package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +432 -0
- package/Tests/Server/Infrastructure/Queue.test.ts +293 -0
- package/Tests/Server/Middleware/PublicDashboardRateLimit.test.ts +1645 -0
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +2848 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1393 -0
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +1802 -0
- package/Tests/Server/Services/EpisodeStateTimelineNote.test.ts +304 -0
- package/Tests/Server/Services/InventoryItemDisplayName.test.ts +339 -0
- package/Tests/Server/Services/InventoryItemManualCreate.test.ts +248 -0
- package/Tests/Server/Services/IpAllowlistSpoofing.test.ts +450 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +235 -1
- package/Tests/Server/Services/MetricAggregationService.test.ts +231 -0
- package/Tests/Server/Services/MetricEntityMVKeyParity.test.ts +80 -29
- package/Tests/Server/Services/MetricServiceAggregate.test.ts +30 -30
- package/Tests/Server/Services/NetworkSiteService.test.ts +18 -3
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +1728 -0
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +2402 -0
- package/Tests/Server/Services/OnCallDutyPolicyExecutionLogTimelineGapFeed.test.ts +394 -0
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +1744 -0
- package/Tests/Server/Services/OnCallReadinessService.test.ts +4295 -0
- package/Tests/Server/Services/OnCallSetupReminder.test.ts +1272 -0
- package/Tests/Server/Services/OnCallWeeklyReadinessDigest.test.ts +1021 -0
- package/Tests/Server/Services/ProfileAggregationService.test.ts +296 -0
- package/Tests/Server/Services/SeverityCreationRuleBackfill.test.ts +1536 -0
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +1818 -0
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +1845 -0
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +1394 -0
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +1166 -0
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1468 -0
- package/Tests/Server/Services/UserOnCallLogNoNotificationRules.test.ts +1457 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +1546 -0
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +529 -0
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +1219 -0
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +1089 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +2 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentHeaders.test.ts +192 -0
- package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +190 -0
- package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +44 -14
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +151 -0
- package/Tests/Server/Types/Workflow/Components/IfElse.test.ts +98 -0
- package/Tests/Server/Types/Workflow/Components/JavaScript.test.ts +51 -0
- package/Tests/Server/Utils/AI/AIMetaTools.test.ts +586 -0
- package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +582 -0
- package/Tests/Server/Utils/AI/ChatAgentRunner.test.ts +726 -0
- package/Tests/Server/Utils/AI/IncidentToolsFilters.test.ts +315 -0
- package/Tests/Server/Utils/AI/LLMServiceStopReason.test.ts +314 -0
- package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +26 -3
- package/Tests/Server/Utils/AI/NoteWriteTools.test.ts +268 -0
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +169 -0
- package/Tests/Server/Utils/AI/OnCallTools.test.ts +664 -0
- package/Tests/Server/Utils/AI/RunbookTools.test.ts +325 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +306 -0
- package/Tests/Server/Utils/AI/StatusPageTools.test.ts +391 -0
- package/Tests/Server/Utils/AI/TeamTools.test.ts +257 -0
- package/Tests/Server/Utils/AI/TimelineTools.test.ts +472 -0
- package/Tests/Server/Utils/AI/WorkflowProbeTools.test.ts +428 -0
- package/Tests/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -0
- package/Tests/Server/Utils/ClientIp.test.ts +438 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +171 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +383 -0
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +23 -25
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +6 -0
- package/Tests/Server/Utils/Monitor/Criteria/DnssecMonitorCriteria.test.ts +307 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +468 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorTelemetryDeepLinks.test.ts +460 -0
- package/Tests/Server/Utils/ResponseRateLimitStatusCodes.test.ts +137 -0
- package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +40 -8
- package/Tests/Server/Utils/SSRFProtectionUserInfo.test.ts +351 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistryRetirement.test.ts +531 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +322 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +356 -39
- package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
- package/Tests/Types/IP/IP.test.ts +263 -0
- package/Tests/Types/IP/IPWhitelist.test.ts +197 -0
- package/Tests/Types/IP/IPv6.test.ts +12 -1
- package/Tests/Types/Monitor/SnmpOid.test.ts +64 -0
- package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +110 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +106 -0
- package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeDifferential.test.ts +511 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageEndToEnd.test.ts +489 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageGapTolerance.test.ts +479 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleCoverageState.test.ts +822 -0
- package/Tests/Types/SerializableObjectDictionaryImportCycle.test.ts +197 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +429 -0
- package/Tests/Types/Workflow/Components/BaseModel.test.ts +225 -0
- package/Tests/Types/Workflow/IntegrationCredentialMetadata.test.ts +100 -0
- package/Tests/Types/Workflow/StepTrace.test.ts +235 -0
- package/Tests/Types/Workflow/TemplateSyntax.test.ts +491 -0
- package/Tests/Types/Workflow/Templates.test.ts +1218 -0
- package/Tests/UI/Components/ActiveFilterChipsOpenRoute.test.tsx +82 -0
- package/Tests/UI/Components/ComponentsModal.test.tsx +564 -7
- package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
- package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
- package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
- package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
- package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
- package/Tests/UI/Components/KeyboardShortcut.test.tsx +95 -0
- package/Tests/UI/Components/LogDetailsPanelCrossSignal.test.tsx +448 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
- package/Tests/UI/Components/LogsTableCrossLinks.test.tsx +263 -0
- package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -0
- package/Tests/UI/Components/PendingProjectInvitations.test.tsx +913 -0
- package/Tests/UI/Components/SimpleLogViewer.test.tsx +228 -0
- package/Tests/UI/Components/TableRowSelectability.test.tsx +312 -0
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +49 -7
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
- package/Tests/UI/Components/Workflow/GraphLint.test.ts +893 -0
- package/Tests/UI/Components/Workflow/GraphLintSummary.test.ts +755 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +522 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +193 -0
- package/Tests/UI/Components/Workflow/ModelSchema.test.ts +388 -0
- package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +210 -0
- package/Tests/UI/Components/Workflow/StepTraceViewer.test.tsx +256 -0
- package/Tests/UI/Components/Workflow/UseRunWatch.test.tsx +665 -0
- package/Tests/UI/Components/Workflow/Utils.test.ts +192 -0
- package/Tests/UI/Components/Workflow/WorkflowIssuesModal.test.tsx +485 -0
- package/Tests/UI/Components/Workflow/WorkflowLogModal.test.tsx +478 -0
- package/Tests/UI/Components/Workflow/WorkflowStatusBar.test.tsx +379 -0
- package/Tests/UI/EsbuildConfig.test.ts +607 -0
- package/Tests/UI/Monitor/MonitorStepCriteriaView.test.tsx +432 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +11 -4
- package/Tests/UI/Utils/ModelAPICreateMiscData.test.ts +94 -0
- package/Tests/UI/Utils/Platform.test.ts +147 -0
- package/Tests/UI/Utils/ProjectInvitationDisplay.test.ts +357 -0
- package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +198 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +514 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +727 -0
- package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
- package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
- package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +698 -0
- package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -0
- package/Tests/__mocks__/bullmq.js +55 -0
- package/Types/AI/AIChatMessageStatus.ts +8 -1
- package/Types/AI/AIChatTypes.ts +12 -0
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +88 -0
- package/Types/Exception/ExceptionCode.ts +2 -0
- package/Types/Exception/ServiceUnavailableException.ts +8 -0
- package/Types/Exception/TooManyRequestsException.ts +8 -0
- package/Types/IP/IP.ts +93 -47
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +80 -3
- package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +55 -0
- package/Types/OnCallDutyPolicy/Layer.ts +203 -149
- package/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.ts +13 -0
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +155 -10
- package/Types/Permission.ts +193 -0
- package/Types/SerializableObjectDictionary.ts +133 -39
- package/Types/Telemetry/EntityRelationshipType.ts +1 -1
- package/Types/Telemetry/EntitySource.ts +40 -0
- package/Types/Telemetry/EntityType.ts +28 -1
- package/Types/Telemetry/EntityTypeGroups.ts +65 -0
- package/Types/Workflow/Component.ts +29 -0
- package/Types/Workflow/Components/API.ts +35 -0
- package/Types/Workflow/Components/BaseModel.ts +77 -27
- package/Types/Workflow/Components/Discord.ts +1 -0
- package/Types/Workflow/Components/Email.ts +12 -3
- package/Types/Workflow/Components/JavaScript.ts +7 -0
- package/Types/Workflow/Components/MicrosoftTeams.ts +3 -2
- package/Types/Workflow/Components/Slack.ts +1 -0
- package/Types/Workflow/Components/Telegram.ts +1 -0
- package/Types/Workflow/StepTrace.ts +181 -0
- package/Types/Workflow/TemplateSyntax.ts +543 -0
- package/Types/Workflow/Templates.ts +2368 -0
- package/UI/Components/Calendar/Calendar.css +43 -0
- package/UI/Components/Calendar/Calendar.tsx +8 -0
- package/UI/Components/Card/Card.tsx +2 -2
- package/UI/Components/Checkbox/Checkbox.tsx +16 -0
- package/UI/Components/Date/CustomTimeRangeModal.tsx +278 -0
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
- package/UI/Components/Dictionary/Dictionary.tsx +71 -15
- package/UI/Components/FormModal/BasicFormModal.tsx +2 -1
- package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
- package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
- package/UI/Components/Forms/Types/Field.ts +7 -0
- package/UI/Components/Forms/Validation.ts +63 -1
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +53 -3
- package/UI/Components/Input/Input.tsx +32 -3
- package/UI/Components/KeyboardShortcut/KeyboardKey.ts +185 -0
- package/UI/Components/KeyboardShortcut/KeyboardShortcut.tsx +87 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +28 -0
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +31 -0
- package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +18 -18
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +363 -14
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +11 -216
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +11 -0
- package/UI/Components/LogsViewer/components/LogsTable.tsx +155 -12
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +29 -0
- package/UI/Components/LogsViewer/types.ts +23 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +9 -2
- package/UI/Components/Modal/Modal.tsx +37 -4
- package/UI/Components/Navbar/NavBarMenuModal.tsx +15 -30
- package/UI/Components/ProjectInvitations/PendingProjectInvitations.tsx +442 -0
- package/UI/Components/SimpleLogViewer/SimpleLogViewer.tsx +23 -1
- package/UI/Components/Table/Table.tsx +49 -16
- package/UI/Components/Table/TableBody.tsx +53 -28
- package/UI/Components/Table/TableHeader.tsx +13 -0
- package/UI/Components/Table/TableRow.tsx +58 -26
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
- package/UI/Components/Workflow/ArgumentsForm.tsx +341 -8
- package/UI/Components/Workflow/Component.tsx +28 -26
- package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +26 -0
- package/UI/Components/Workflow/ComponentSettingsModal.tsx +79 -9
- package/UI/Components/Workflow/ComponentValuePickerModal.tsx +135 -7
- package/UI/Components/Workflow/ComponentsModal.tsx +116 -22
- package/UI/Components/Workflow/DocumentationViewer.tsx +59 -9
- package/UI/Components/Workflow/GraphLint.ts +628 -0
- package/UI/Components/Workflow/GraphLintSummary.ts +390 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +528 -0
- package/UI/Components/Workflow/ModelSchema.ts +278 -0
- package/UI/Components/Workflow/RunForm.tsx +41 -7
- package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
- package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
- package/UI/Components/Workflow/UseRunWatch.ts +212 -0
- package/UI/Components/Workflow/Utils.ts +93 -1
- package/UI/Components/Workflow/VariableModal.tsx +6 -2
- package/UI/Components/Workflow/Workflow.tsx +126 -7
- package/UI/Components/Workflow/WorkflowIssuesModal.tsx +255 -0
- package/UI/Components/Workflow/WorkflowLogModal.tsx +128 -0
- package/UI/Components/Workflow/WorkflowStatusBar.tsx +224 -0
- package/UI/Types/LayeredDismissal.ts +31 -0
- package/UI/Types/UseAnchoredFieldPopup.ts +99 -0
- package/UI/Utils/AIChatExport/ConversationMarkdown.ts +10 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +7 -1
- package/UI/Utils/Platform.ts +149 -0
- package/UI/Utils/ProjectInvitationDisplay.ts +118 -0
- package/UI/esbuild-config.js +22 -1
- package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +187 -0
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +533 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +917 -155
- package/Utils/Telemetry/CrossSignalScope.ts +502 -0
- package/Utils/Telemetry/EntityKey.ts +71 -5
- package/Utils/Telemetry/EntityRelationship.ts +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByK8sCluster.js +1 -1
- package/build/dist/Models/AnalyticsModels/MetricItemAggMV1mByService.js +1 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js +32 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js +42 -0
- package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +28 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +29 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +12 -4
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/{TelemetryEntity.js → InventoryItem.js} +212 -29
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +454 -0
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -0
- package/build/dist/Models/DatabaseModels/{TelemetryEntityRelationship.js → InventoryItemRelationship.js} +52 -25
- package/build/dist/Models/DatabaseModels/InventoryItemRelationship.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +141 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +719 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +475 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js +446 -0
- package/build/dist/Models/DatabaseModels/NetworkTopologySuppression.js.map +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js +9 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +80 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserCall.js +46 -2
- package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserEmail.js +48 -2
- package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +424 -55
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +24 -2
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserPush.js +51 -1
- package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSMS.js +47 -2
- package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTelegram.js +65 -3
- package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebhook.js +56 -2
- package/build/dist/Models/DatabaseModels/UserWebhook.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js +45 -2
- package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowLog.js +39 -0
- package/build/dist/Models/DatabaseModels/WorkflowLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +12 -0
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +237 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +165 -13
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js +599 -0
- package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -0
- package/build/dist/Server/API/TeamComplianceAPI.js +68 -9
- package/build/dist/Server/API/TeamComplianceAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +129 -18
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +16 -1
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +45 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +4 -4
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786625176831-AddMonitoringMethodToNetworkDevice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js +39 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786634985763-AddNetworkDeviceLink.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639512056-AddNetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786639972982-AddNetworkTopologySuppression.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js +150 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786800000000-RenameTelemetryEntityToInventoryItem.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786900000000-AddInventoryItemArchiveAndCustomFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js +69 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787000000000-AddOnCallNotificationFallbackColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787100000000-AddAIConversationPageContext.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787200000000-AddAIChatMessageFeedback.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787300000000-AddEpisodeMemberNotifyIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +72 -13
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -6
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js +399 -0
- package/build/dist/Server/Middleware/PublicDashboardRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AIService.js +1 -0
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/AlertEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +54 -0
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/DashboardService.js +10 -9
- package/build/dist/Server/Services/DashboardService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +24 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +24 -3
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +67 -0
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +12 -4
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js +9 -0
- package/build/dist/Server/Services/InventoryItemCustomFieldService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityRelationshipService.js → InventoryItemRelationshipService.js} +9 -6
- package/build/dist/Server/Services/InventoryItemRelationshipService.js.map +1 -0
- package/build/dist/Server/Services/{TelemetryEntityService.js → InventoryItemService.js} +158 -23
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -0
- package/build/dist/Server/Services/LogAggregationService.js +27 -8
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricAggregationService.js +80 -0
- package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +6 -6
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js +9 -0
- package/build/dist/Server/Services/NetworkDeviceLinkRuleService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +71 -0
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceService.js +113 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +53 -13
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js +85 -0
- package/build/dist/Server/Services/NetworkTopologySuppressionService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +48 -32
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +8 -0
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +51 -12
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +57 -13
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js +548 -0
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -0
- package/build/dist/Server/Services/OnCallReadinessService.js +1961 -0
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js +738 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -0
- package/build/dist/Server/Services/ProfileAggregationService.js +68 -4
- package/build/dist/Server/Services/ProfileAggregationService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +11 -10
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceService.js +312 -160
- package/build/dist/Server/Services/TeamComplianceService.js.map +1 -1
- package/build/dist/Server/Services/UserCallService.js +24 -1
- package/build/dist/Server/Services/UserCallService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +24 -1
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +858 -0
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js +2830 -175
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogService.js +488 -43
- package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
- package/build/dist/Server/Services/UserPushService.js +26 -0
- package/build/dist/Server/Services/UserPushService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +10 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSmsService.js +24 -1
- package/build/dist/Server/Services/UserSmsService.js.map +1 -1
- package/build/dist/Server/Services/UserTelegramService.js +22 -1
- package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
- package/build/dist/Server/Services/UserWebhookService.js +25 -1
- package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
- package/build/dist/Server/Services/UserWhatsAppService.js +22 -1
- package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +12 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +126 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js +254 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerOnlyColumnPermission.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js +47 -2
- package/build/dist/Server/Types/Database/Permissions/QueryPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +7 -0
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js +28 -0
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +21 -5
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +14 -10
- package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +31 -0
- package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +3 -9
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +15 -4
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +7 -2
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +1 -1
- package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
- package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +514 -56
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +20 -3
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +19 -6
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +7 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +2 -2
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js +692 -0
- package/build/dist/Server/Utils/AI/Toolbox/AIMetaTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +148 -12
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +157 -10
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +37 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +259 -14
- package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js +235 -0
- package/build/dist/Server/Utils/AI/Toolbox/NoteWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +1000 -0
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js +356 -0
- package/build/dist/Server/Utils/AI/Toolbox/RunbookTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +394 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js +465 -0
- package/build/dist/Server/Utils/AI/Toolbox/StatusPageTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js +280 -0
- package/build/dist/Server/Utils/AI/Toolbox/TeamTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +527 -0
- package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js +548 -0
- package/build/dist/Server/Utils/AI/Toolbox/WorkflowProbeTools.js.map +1 -0
- package/build/dist/Server/Utils/ClientIp.js +137 -0
- package/build/dist/Server/Utils/ClientIp.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +38 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +89 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +77 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -0
- package/build/dist/Server/Utils/Express.js +12 -12
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +70 -7
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +121 -11
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +82 -21
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +165 -18
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +507 -0
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +122 -47
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMAPI.js +51 -4
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +7 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js +8 -1
- package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -1
- package/build/dist/Types/AI/AIChatTypes.js +12 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +60 -0
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +2 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/ServiceUnavailableException.js +8 -0
- package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js +8 -0
- package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -0
- package/build/dist/Types/IP/IP.js +87 -43
- package/build/dist/Types/IP/IP.js.map +1 -1
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +50 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/Layer.js +186 -123
- package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js +13 -0
- package/build/dist/Types/OnCallDutyPolicy/OnCallDutyPolicyStatus.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +105 -11
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
- package/build/dist/Types/Permission.js +174 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/SerializableObjectDictionary.js +133 -39
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityRelationshipType.js +1 -1
- package/build/dist/Types/Telemetry/EntitySource.js +39 -0
- package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
- package/build/dist/Types/Telemetry/EntityType.js +28 -1
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
- package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
- package/build/dist/Types/Workflow/Component.js +21 -0
- package/build/dist/Types/Workflow/Component.js.map +1 -1
- package/build/dist/Types/Workflow/Components/API.js +35 -0
- package/build/dist/Types/Workflow/Components/API.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +68 -26
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Discord.js +1 -0
- package/build/dist/Types/Workflow/Components/Discord.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Email.js +12 -3
- package/build/dist/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Types/Workflow/Components/JavaScript.js +7 -0
- package/build/dist/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js +3 -2
- package/build/dist/Types/Workflow/Components/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Slack.js +1 -0
- package/build/dist/Types/Workflow/Components/Slack.js.map +1 -1
- package/build/dist/Types/Workflow/Components/Telegram.js +1 -0
- package/build/dist/Types/Workflow/Components/Telegram.js.map +1 -1
- package/build/dist/Types/Workflow/StepTrace.js +104 -0
- package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js +338 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
- package/build/dist/Types/Workflow/Templates.js +2111 -0
- package/build/dist/Types/Workflow/Templates.js.map +1 -0
- package/build/dist/UI/Components/Calendar/Calendar.js +1 -1
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js +1 -1
- package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +126 -0
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
- package/build/dist/UI/Components/Dictionary/Dictionary.js +47 -17
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +52 -13
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +44 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +27 -4
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +13 -4
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js +163 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardKey.js.map +1 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js +47 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcut.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +4 -4
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +11 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +10 -8
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +227 -14
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +5 -108
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +5 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +69 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +8 -0
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +9 -2
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +31 -5
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +8 -21
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js +251 -0
- package/build/dist/UI/Components/ProjectInvitations/PendingProjectInvitations.js.map +1 -0
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js +9 -2
- package/build/dist/UI/Components/SimpleLogViewer/SimpleLogViewer.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +27 -15
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +24 -18
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +9 -1
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +29 -21
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +261 -14
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Component.js +20 -19
- package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +10 -1
- package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +35 -7
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js +57 -7
- package/build/dist/UI/Components/Workflow/ComponentValuePickerModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +53 -18
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js +19 -6
- package/build/dist/UI/Components/Workflow/DocumentationViewer.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLint.js +425 -0
- package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js +231 -0
- package/build/dist/UI/Components/Workflow/GraphLintSummary.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +320 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js +156 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
- package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
- package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
- package/build/dist/UI/Components/Workflow/UseRunWatch.js +123 -0
- package/build/dist/UI/Components/Workflow/UseRunWatch.js.map +1 -0
- package/build/dist/UI/Components/Workflow/Utils.js +73 -1
- package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
- package/build/dist/UI/Components/Workflow/VariableModal.js +3 -2
- package/build/dist/UI/Components/Workflow/VariableModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Workflow.js +92 -7
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js +99 -0
- package/build/dist/UI/Components/Workflow/WorkflowIssuesModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js +56 -0
- package/build/dist/UI/Components/Workflow/WorkflowLogModal.js.map +1 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js +92 -0
- package/build/dist/UI/Components/Workflow/WorkflowStatusBar.js.map +1 -0
- package/build/dist/UI/Types/LayeredDismissal.js +21 -0
- package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +9 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Platform.js +118 -0
- package/build/dist/UI/Utils/Platform.js.map +1 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js +106 -0
- package/build/dist/UI/Utils/ProjectInvitationDisplay.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +108 -0
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +386 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +661 -126
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +328 -0
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +57 -5
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/build/dist/Utils/Telemetry/EntityRelationship.js +1 -1
- package/jest.config.json +1 -0
- package/package.json +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +0 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +0 -1
- package/build/dist/Server/Services/TelemetryEntityService.js.map +0 -1
|
@@ -0,0 +1,1183 @@
|
|
|
1
|
+
import DatabaseConfig from "../DatabaseConfig";
|
|
2
|
+
import BaseService from "./BaseService";
|
|
3
|
+
import MailService from "./MailService";
|
|
4
|
+
import ProjectService from "./ProjectService";
|
|
5
|
+
import TeamMemberService from "./TeamMemberService";
|
|
6
|
+
import UserCallService from "./UserCallService";
|
|
7
|
+
import UserEmailService from "./UserEmailService";
|
|
8
|
+
import UserPushService from "./UserPushService";
|
|
9
|
+
import UserService from "./UserService";
|
|
10
|
+
import UserSmsService from "./UserSmsService";
|
|
11
|
+
import UserTelegramService from "./UserTelegramService";
|
|
12
|
+
import UserWebhookService from "./UserWebhookService";
|
|
13
|
+
import UserWhatsAppService from "./UserWhatsAppService";
|
|
14
|
+
import type AuditLogServiceType from "./AuditLogService";
|
|
15
|
+
import { resolveReferenceId } from "../Utils/Database/ProjectScopedReferenceValidator";
|
|
16
|
+
import logger, { LogAttributes } from "../Utils/Logger";
|
|
17
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
18
|
+
import URL from "../../Types/API/URL";
|
|
19
|
+
import AuditLogAction from "../../Types/AuditLog/AuditLogAction";
|
|
20
|
+
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
21
|
+
import Dictionary from "../../Types/Dictionary";
|
|
22
|
+
import EmailTemplateType from "../../Types/Email/EmailTemplateType";
|
|
23
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
24
|
+
import { JSONObject } from "../../Types/JSON";
|
|
25
|
+
import ObjectID from "../../Types/ObjectID";
|
|
26
|
+
import Project from "../../Models/DatabaseModels/Project";
|
|
27
|
+
import TeamMember from "../../Models/DatabaseModels/TeamMember";
|
|
28
|
+
import User from "../../Models/DatabaseModels/User";
|
|
29
|
+
import UserNotificationRule from "../../Models/DatabaseModels/UserNotificationRule";
|
|
30
|
+
|
|
31
|
+
/*
|
|
32
|
+
* THE SHAPE OF THE PROBLEM THIS FILE EXISTS TO SOLVE
|
|
33
|
+
*
|
|
34
|
+
* A UserNotificationRule row is a pair: an OWNERSHIP column (userId) that
|
|
35
|
+
* decides whose on-call pages select the row, and a METHOD foreign key that
|
|
36
|
+
* decides which address the page is delivered to. Those two halves are read at
|
|
37
|
+
* completely different moments by completely different code — the ownership
|
|
38
|
+
* column by UserOnCallLogService when it works out who to page, the method
|
|
39
|
+
* relation by UserNotificationRuleService when it works out where to send — and
|
|
40
|
+
* NOTHING in the ORM, the permission layer or the API surface ever compares
|
|
41
|
+
* them.
|
|
42
|
+
*
|
|
43
|
+
* While every notification model was CurrentUser-only that mismatch was
|
|
44
|
+
* unreachable in practice, because a caller could only ever name their own id
|
|
45
|
+
* in both halves. Phase 3 deliberately widens the rule model so a project admin
|
|
46
|
+
* can repair somebody else's configuration, and the moment an admin may write a
|
|
47
|
+
* row whose userId is not their own, "userId says Bob, userWebhookId says the
|
|
48
|
+
* admin's own webhook" becomes a row an attacker can ask for. It would route
|
|
49
|
+
* every one of Bob's pages to an endpoint the attacker controls, and Bob would
|
|
50
|
+
* see nothing: his rules page still says he is paged, his on-call log still
|
|
51
|
+
* says a page went out.
|
|
52
|
+
*
|
|
53
|
+
* So the widened permission is only half the feature. The other half is here:
|
|
54
|
+
* the value-level invariants that the permission layer structurally cannot
|
|
55
|
+
* express, because it reasons about tables and columns and never about the
|
|
56
|
+
* relationship BETWEEN two column values in one row.
|
|
57
|
+
*
|
|
58
|
+
* R1 A rule may only be written for a user who is a member of the project
|
|
59
|
+
* the write is scoped to. Without this, holding ProjectAdmin anywhere
|
|
60
|
+
* lets you write rules for a user id belonging to any other project.
|
|
61
|
+
*
|
|
62
|
+
* R3 A rule's method FK must reference a method row owned by the SAME user
|
|
63
|
+
* as the rule. This is the anti-hijack invariant, and on the update path
|
|
64
|
+
* the rule's owner is re-read FROM THE DATABASE — a userId in the request
|
|
65
|
+
* body is the attacker's own input and proves nothing.
|
|
66
|
+
*
|
|
67
|
+
* R6 When the actor is not the owner, say so out loud: an audit trail keyed
|
|
68
|
+
* on the server-resolved actor versus the row's PERSISTED owner, and a
|
|
69
|
+
* mail to the person whose pages just changed.
|
|
70
|
+
*
|
|
71
|
+
* None of these are UI concerns. POST/PATCH /api/user-notification-rule stays
|
|
72
|
+
* reachable with any member session and no dashboard involved, so every one of
|
|
73
|
+
* them is enforced in the service hooks that the API route runs through.
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* One of the seven ways a rule can name a delivery address, reduced to what the
|
|
78
|
+
* ownership guard needs: which column carried it (for the error message and the
|
|
79
|
+
* audit line) and which row it points at.
|
|
80
|
+
*/
|
|
81
|
+
export interface NotificationMethodReference {
|
|
82
|
+
/** The FK column on UserNotificationRule, e.g. "userWebhookId". */
|
|
83
|
+
idColumn: string;
|
|
84
|
+
/*
|
|
85
|
+
* The property the payload actually used to carry this id: either the FK
|
|
86
|
+
* column itself or the relation slot beside it. Both write the one database
|
|
87
|
+
* column, and a payload may carry both — so a reference has to remember which
|
|
88
|
+
* spelling it came out of, or the ambiguity check below has nothing to talk
|
|
89
|
+
* about.
|
|
90
|
+
*/
|
|
91
|
+
suppliedColumn: string;
|
|
92
|
+
/** Human-readable channel name used verbatim in rejection messages. */
|
|
93
|
+
label: string;
|
|
94
|
+
/** The notification-method row the rule points at. */
|
|
95
|
+
methodId: ObjectID;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/*
|
|
99
|
+
* A rule-shaped bag of values to read the guarded columns out of — the seven
|
|
100
|
+
* method references AND the ownership column, because both halves of the pair
|
|
101
|
+
* this file exists to compare arrive through the same payload.
|
|
102
|
+
*
|
|
103
|
+
* Create hands us a hydrated UserNotificationRule; update hands us a
|
|
104
|
+
* PartialEntity whose slots may hold an ObjectID, a bare uuid string or a
|
|
105
|
+
* relation object depending on how far through sanitisation the request has
|
|
106
|
+
* travelled. Rather than overload on those, callers cast to this and
|
|
107
|
+
* resolveReferenceId untangles the shapes — the same helper the project-scoped
|
|
108
|
+
* reference guards use, for exactly the same reason.
|
|
109
|
+
*/
|
|
110
|
+
export type RuleColumnCarrier = Record<string, unknown>;
|
|
111
|
+
|
|
112
|
+
/*
|
|
113
|
+
* The ownership column in both of its spellings, resolved from the model rather
|
|
114
|
+
* than restated as two string literals.
|
|
115
|
+
*/
|
|
116
|
+
interface RuleOwnerColumns {
|
|
117
|
+
/** The scalar @CurrentUserCanAccessRecordBy points at, i.e. "userId". */
|
|
118
|
+
idColumn: string;
|
|
119
|
+
/** The ManyToOne beside it whose @JoinColumn names that same scalar. */
|
|
120
|
+
relationColumn: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/*
|
|
124
|
+
* Why this reads a row rather than being handed one.
|
|
125
|
+
*
|
|
126
|
+
* Every lookup below runs with isRoot props. That is not laziness about
|
|
127
|
+
* permissions: the question being asked is a question about the DATABASE'S
|
|
128
|
+
* state ("who really owns method X", "is user Y really in project P"), and
|
|
129
|
+
* asking it through the caller's own permission scope would let the caller's
|
|
130
|
+
* scope shape the answer. A caller who cannot READ the victim's method row
|
|
131
|
+
* would get `null` back and, under any "not found means fine" reading, sail
|
|
132
|
+
* straight through the guard that exists to stop them.
|
|
133
|
+
*/
|
|
134
|
+
interface NotificationMethodDescriptor {
|
|
135
|
+
idColumn: string;
|
|
136
|
+
relationColumn: string;
|
|
137
|
+
label: string;
|
|
138
|
+
findOwnerUserId: (methodId: ObjectID) => Promise<ObjectID | undefined>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export class UserNotificationRuleAdminService extends BaseService {
|
|
142
|
+
/*
|
|
143
|
+
* Built per call, never hoisted to module scope.
|
|
144
|
+
*
|
|
145
|
+
* UserEmailService and its six siblings import UserNotificationRuleService,
|
|
146
|
+
* which imports this file — a genuine require cycle. Capturing the service
|
|
147
|
+
* objects in a module-level constant would read `default` while the sibling
|
|
148
|
+
* module is still mid-evaluation and freeze `undefined` into the table. Built
|
|
149
|
+
* inside a method, every service reference resolves at call time, long after
|
|
150
|
+
* the cycle has settled.
|
|
151
|
+
*/
|
|
152
|
+
private getNotificationMethodDescriptors(): Array<NotificationMethodDescriptor> {
|
|
153
|
+
return [
|
|
154
|
+
{
|
|
155
|
+
idColumn: "userEmailId",
|
|
156
|
+
relationColumn: "userEmail",
|
|
157
|
+
label: "Email",
|
|
158
|
+
findOwnerUserId: async (
|
|
159
|
+
methodId: ObjectID,
|
|
160
|
+
): Promise<ObjectID | undefined> => {
|
|
161
|
+
return (
|
|
162
|
+
await UserEmailService.findOneById({
|
|
163
|
+
id: methodId,
|
|
164
|
+
select: { _id: true, userId: true },
|
|
165
|
+
props: { isRoot: true },
|
|
166
|
+
})
|
|
167
|
+
)?.userId;
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
idColumn: "userSmsId",
|
|
172
|
+
relationColumn: "userSms",
|
|
173
|
+
label: "SMS",
|
|
174
|
+
findOwnerUserId: async (
|
|
175
|
+
methodId: ObjectID,
|
|
176
|
+
): Promise<ObjectID | undefined> => {
|
|
177
|
+
return (
|
|
178
|
+
await UserSmsService.findOneById({
|
|
179
|
+
id: methodId,
|
|
180
|
+
select: { _id: true, userId: true },
|
|
181
|
+
props: { isRoot: true },
|
|
182
|
+
})
|
|
183
|
+
)?.userId;
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
idColumn: "userCallId",
|
|
188
|
+
relationColumn: "userCall",
|
|
189
|
+
label: "Call",
|
|
190
|
+
findOwnerUserId: async (
|
|
191
|
+
methodId: ObjectID,
|
|
192
|
+
): Promise<ObjectID | undefined> => {
|
|
193
|
+
return (
|
|
194
|
+
await UserCallService.findOneById({
|
|
195
|
+
id: methodId,
|
|
196
|
+
select: { _id: true, userId: true },
|
|
197
|
+
props: { isRoot: true },
|
|
198
|
+
})
|
|
199
|
+
)?.userId;
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
idColumn: "userWhatsAppId",
|
|
204
|
+
relationColumn: "userWhatsApp",
|
|
205
|
+
label: "WhatsApp",
|
|
206
|
+
findOwnerUserId: async (
|
|
207
|
+
methodId: ObjectID,
|
|
208
|
+
): Promise<ObjectID | undefined> => {
|
|
209
|
+
return (
|
|
210
|
+
await UserWhatsAppService.findOneById({
|
|
211
|
+
id: methodId,
|
|
212
|
+
select: { _id: true, userId: true },
|
|
213
|
+
props: { isRoot: true },
|
|
214
|
+
})
|
|
215
|
+
)?.userId;
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
idColumn: "userTelegramId",
|
|
220
|
+
relationColumn: "userTelegram",
|
|
221
|
+
label: "Telegram",
|
|
222
|
+
findOwnerUserId: async (
|
|
223
|
+
methodId: ObjectID,
|
|
224
|
+
): Promise<ObjectID | undefined> => {
|
|
225
|
+
return (
|
|
226
|
+
await UserTelegramService.findOneById({
|
|
227
|
+
id: methodId,
|
|
228
|
+
select: { _id: true, userId: true },
|
|
229
|
+
props: { isRoot: true },
|
|
230
|
+
})
|
|
231
|
+
)?.userId;
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
idColumn: "userPushId",
|
|
236
|
+
relationColumn: "userPush",
|
|
237
|
+
label: "Push",
|
|
238
|
+
findOwnerUserId: async (
|
|
239
|
+
methodId: ObjectID,
|
|
240
|
+
): Promise<ObjectID | undefined> => {
|
|
241
|
+
return (
|
|
242
|
+
await UserPushService.findOneById({
|
|
243
|
+
id: methodId,
|
|
244
|
+
select: { _id: true, userId: true },
|
|
245
|
+
props: { isRoot: true },
|
|
246
|
+
})
|
|
247
|
+
)?.userId;
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
idColumn: "userWebhookId",
|
|
252
|
+
relationColumn: "userWebhook",
|
|
253
|
+
label: "Webhook",
|
|
254
|
+
findOwnerUserId: async (
|
|
255
|
+
methodId: ObjectID,
|
|
256
|
+
): Promise<ObjectID | undefined> => {
|
|
257
|
+
return (
|
|
258
|
+
await UserWebhookService.findOneById({
|
|
259
|
+
id: methodId,
|
|
260
|
+
select: { _id: true, userId: true },
|
|
261
|
+
props: { isRoot: true },
|
|
262
|
+
})
|
|
263
|
+
)?.userId;
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/*
|
|
270
|
+
* The seven FK columns, in one place, so a future eighth channel is a single
|
|
271
|
+
* entry rather than an eighth thing to remember. A guard that covers six of
|
|
272
|
+
* seven covers none: the attacker picks the seventh.
|
|
273
|
+
*/
|
|
274
|
+
public getNotificationMethodIdColumns(): Array<string> {
|
|
275
|
+
return this.getNotificationMethodDescriptors().map(
|
|
276
|
+
(descriptor: NotificationMethodDescriptor): string => {
|
|
277
|
+
return descriptor.idColumn;
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/*
|
|
283
|
+
* The ownership column's two spellings, derived from the model rather than
|
|
284
|
+
* restated here as literals.
|
|
285
|
+
*
|
|
286
|
+
* `userId` is a declared @Column; `user` is a @ManyToOne whose @JoinColumn
|
|
287
|
+
* names that same physical column. Two decorated members, one join column,
|
|
288
|
+
* and a payload may carry either or both — exactly the situation that made
|
|
289
|
+
* the method FKs bypassable, reproduced on the column that decides whose
|
|
290
|
+
* pages the row selects.
|
|
291
|
+
*
|
|
292
|
+
* The derivation ("relation name is the scalar minus its `Id` suffix") is the
|
|
293
|
+
* one CreatePermission.checkCreateOwnership uses, and it is deliberate that
|
|
294
|
+
* both places derive rather than hard-code: a model that renames its
|
|
295
|
+
* ownership column must not leave a guard silently reading a property that no
|
|
296
|
+
* longer exists.
|
|
297
|
+
*/
|
|
298
|
+
private getRuleOwnerColumns(): RuleOwnerColumns | null {
|
|
299
|
+
const idColumn: string | null = new UserNotificationRule().getUserColumn();
|
|
300
|
+
|
|
301
|
+
if (!idColumn || !idColumn.endsWith("Id")) {
|
|
302
|
+
/*
|
|
303
|
+
* No user-scoped column, or one that does not follow the convention, so
|
|
304
|
+
* there is no second spelling to reconcile. Genuinely a no-op rather than
|
|
305
|
+
* a guard opening: with no ownership column there is no ownership to
|
|
306
|
+
* disagree about. Same reasoning as CreatePermission's `if (!userColumn)`.
|
|
307
|
+
*/
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
idColumn: idColumn,
|
|
313
|
+
relationColumn: idColumn.slice(0, -2),
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Refuse a payload whose two spellings of the ownership column name two
|
|
319
|
+
* different users.
|
|
320
|
+
*
|
|
321
|
+
* The exact counterpart of assertOneMethodPerNotificationChannel below, on
|
|
322
|
+
* the other half of the pair this file exists to keep in agreement. A payload
|
|
323
|
+
* saying `userId: <victim>, user: { _id: <me> }` is not a client mistake — no
|
|
324
|
+
* dashboard sends both — it is a request for the guard and the ORM to read
|
|
325
|
+
* different owners, which is precisely how a validated write becomes a
|
|
326
|
+
* different persisted row.
|
|
327
|
+
*
|
|
328
|
+
* Refusing rather than silently preferring one spelling means the correctness
|
|
329
|
+
* of this file never rests on our reading of TypeORM's precedence rule. The
|
|
330
|
+
* fold below then removes the surviving redundancy, so nothing downstream —
|
|
331
|
+
* the roster check, the method-ownership check, CreatePermission, the audit
|
|
332
|
+
* line — has a precedence question left to resolve.
|
|
333
|
+
*/
|
|
334
|
+
public assertOneRuleOwner(carrier: RuleColumnCarrier): void {
|
|
335
|
+
const columns: RuleOwnerColumns | null = this.getRuleOwnerColumns();
|
|
336
|
+
|
|
337
|
+
if (!columns) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const fromIdColumn: ObjectID | string | undefined = resolveReferenceId(
|
|
342
|
+
carrier[columns.idColumn],
|
|
343
|
+
);
|
|
344
|
+
const fromRelation: ObjectID | string | undefined = resolveReferenceId(
|
|
345
|
+
carrier[columns.relationColumn],
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
if (!fromIdColumn || !fromRelation) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/*
|
|
353
|
+
* Case-insensitively, for the reason ProjectScopedReferenceValidator
|
|
354
|
+
* normalises ids: ObjectID keeps whatever case it was handed while Postgres
|
|
355
|
+
* renders `uuid` lower case, so one id in two cases is still one id and
|
|
356
|
+
* must not read as a contradiction.
|
|
357
|
+
*/
|
|
358
|
+
if (
|
|
359
|
+
fromIdColumn.toString().toLowerCase() ===
|
|
360
|
+
fromRelation.toString().toLowerCase()
|
|
361
|
+
) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
throw new BadDataException(
|
|
366
|
+
`This notification rule names two different users: ${columns.idColumn} says ${fromIdColumn.toString()} and ${columns.relationColumn} says ${fromRelation.toString()}. Send only one of them.`,
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Fold the ownership relation into the ownership scalar so exactly one
|
|
372
|
+
* spelling survives a create.
|
|
373
|
+
*
|
|
374
|
+
* This is the same reduction collapseNotificationMethodRelationsOnCreate
|
|
375
|
+
* performs for the seven method FKs, and the same one
|
|
376
|
+
* CreatePermission.checkCreateOwnership performs for this very column — it
|
|
377
|
+
* validates `user` identically to `userId` and then CLEARS it, because after
|
|
378
|
+
* clearing there is no second source of truth left to disagree with the
|
|
379
|
+
* first. What CreatePermission cannot do is cover the administrator: it
|
|
380
|
+
* returns early for a caller holding a real role permission in the model's
|
|
381
|
+
* create list, which is exactly the caller this phase introduced and exactly
|
|
382
|
+
* the caller who may legitimately name somebody else. On that path the
|
|
383
|
+
* relation reached TypeORM unreduced.
|
|
384
|
+
*
|
|
385
|
+
* The relation's id is what is kept, because that is the value TypeORM would
|
|
386
|
+
* have written: the declared @Column and the relation's @JoinColumn share one
|
|
387
|
+
* ColumnMetadata, and getEntityValue reads the relation slot first, falling
|
|
388
|
+
* back to the scalar only when the relation holds no object. A relation slot
|
|
389
|
+
* that is present but carries no id therefore resolves to nothing, and
|
|
390
|
+
* clearing the scalar alongside it reproduces the NULL the ORM would have
|
|
391
|
+
* written rather than inventing an owner the row will not have.
|
|
392
|
+
*
|
|
393
|
+
* CREATE ONLY, though for a different reason than the method columns: `user`
|
|
394
|
+
* and `userId` are `update: []` for BOTH spellings on this model, so there is
|
|
395
|
+
* no update-path fold to consider at all — an update naming either one is
|
|
396
|
+
* refused outright by ColumnPermission.
|
|
397
|
+
*/
|
|
398
|
+
public collapseRuleOwnerRelationOnCreate(carrier: RuleColumnCarrier): void {
|
|
399
|
+
const columns: RuleOwnerColumns | null = this.getRuleOwnerColumns();
|
|
400
|
+
|
|
401
|
+
if (!columns) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const relationValue: unknown = carrier[columns.relationColumn];
|
|
406
|
+
|
|
407
|
+
if (relationValue === undefined || relationValue === null) {
|
|
408
|
+
// Nothing in this spelling; TypeORM falls through to the scalar already.
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const resolved: ObjectID | string | undefined =
|
|
413
|
+
resolveReferenceId(relationValue);
|
|
414
|
+
|
|
415
|
+
carrier[columns.idColumn] = resolved
|
|
416
|
+
? resolved instanceof ObjectID
|
|
417
|
+
? resolved
|
|
418
|
+
: new ObjectID(resolved)
|
|
419
|
+
: undefined;
|
|
420
|
+
|
|
421
|
+
carrier[columns.relationColumn] = undefined;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Pull EVERY notification-method reference out of a create payload or an
|
|
426
|
+
* update patch — every channel, and every spelling of every channel.
|
|
427
|
+
*
|
|
428
|
+
* THE BUG THIS SHAPE EXISTS TO PREVENT. `userWebhookId` and `userWebhook` are
|
|
429
|
+
* two decorated members over ONE physical join column, and a request may
|
|
430
|
+
* carry both. This method used to resolve them as
|
|
431
|
+
* `carrier[idColumn] || carrier[relationColumn]`, which validates the FK
|
|
432
|
+
* column and never looks at the relation when both are present. TypeORM
|
|
433
|
+
* resolves the same conflict the other way round: the declared @Column and
|
|
434
|
+
* the relation's @JoinColumn share a single ColumnMetadata, and
|
|
435
|
+
* ColumnMetadata.getEntityValue reads the RELATION first, falling back to the
|
|
436
|
+
* scalar only when the relation slot holds no object.
|
|
437
|
+
*
|
|
438
|
+
* So the id that was checked and the id that was written were different ids,
|
|
439
|
+
* and the caller chose both. Put the rule owner's own method in the FK column
|
|
440
|
+
* to satisfy the guard, and your own row in the relation slot to be
|
|
441
|
+
* persisted, and R3 — the entire anti-hijack invariant — is a formality.
|
|
442
|
+
*
|
|
443
|
+
* The rule is therefore: never short-circuit. Emit one reference per spelling
|
|
444
|
+
* actually present, so the ownership check below has to answer for all of
|
|
445
|
+
* them, and a payload can no longer be right in the half that is inspected
|
|
446
|
+
* and wrong in the half that is written.
|
|
447
|
+
*
|
|
448
|
+
* Duplicates are folded only when they are the SAME id in the same column,
|
|
449
|
+
* where the second lookup could not produce a different answer. Two spellings
|
|
450
|
+
* naming two DIFFERENT ids stay two references on purpose — that is precisely
|
|
451
|
+
* the case worth an extra query.
|
|
452
|
+
*
|
|
453
|
+
* A rule that names no method at all yields an empty array and the ownership
|
|
454
|
+
* guard becomes a no-op — correct, because an opt-out row genuinely has no
|
|
455
|
+
* address to hijack.
|
|
456
|
+
*/
|
|
457
|
+
public collectNotificationMethodReferences(
|
|
458
|
+
carrier: RuleColumnCarrier,
|
|
459
|
+
): Array<NotificationMethodReference> {
|
|
460
|
+
const references: Array<NotificationMethodReference> = [];
|
|
461
|
+
const seen: Set<string> = new Set<string>();
|
|
462
|
+
|
|
463
|
+
for (const descriptor of this.getNotificationMethodDescriptors()) {
|
|
464
|
+
const spellings: Array<string> = [
|
|
465
|
+
descriptor.idColumn,
|
|
466
|
+
descriptor.relationColumn,
|
|
467
|
+
];
|
|
468
|
+
|
|
469
|
+
for (const suppliedColumn of spellings) {
|
|
470
|
+
const resolved: ObjectID | string | undefined = resolveReferenceId(
|
|
471
|
+
carrier[suppliedColumn],
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
if (!resolved) {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const methodId: ObjectID =
|
|
479
|
+
resolved instanceof ObjectID ? resolved : new ObjectID(resolved);
|
|
480
|
+
|
|
481
|
+
/*
|
|
482
|
+
* Ids are compared case-insensitively for the same reason
|
|
483
|
+
* ProjectScopedReferenceValidator normalises them: ObjectID keeps
|
|
484
|
+
* whatever case it was handed, while Postgres renders `uuid` in lower
|
|
485
|
+
* case, so an uppercase spelling and a lowercase one are one id.
|
|
486
|
+
*/
|
|
487
|
+
const key: string = `${descriptor.idColumn}:${methodId
|
|
488
|
+
.toString()
|
|
489
|
+
.toLowerCase()}`;
|
|
490
|
+
|
|
491
|
+
if (seen.has(key)) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
seen.add(key);
|
|
496
|
+
|
|
497
|
+
references.push({
|
|
498
|
+
idColumn: descriptor.idColumn,
|
|
499
|
+
suppliedColumn: suppliedColumn,
|
|
500
|
+
label: descriptor.label,
|
|
501
|
+
methodId: methodId,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
return references;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Refuse a payload whose two spellings of one method column name two
|
|
511
|
+
* different methods.
|
|
512
|
+
*
|
|
513
|
+
* Validating both spellings (above) closes the hijack: whichever one the ORM
|
|
514
|
+
* picks, it has been checked against the rule's owner. What it does not do is
|
|
515
|
+
* make the write PREDICTABLE — with both slots filled, which address the rule
|
|
516
|
+
* ends up delivering to is decided by a TypeORM precedence rule that no
|
|
517
|
+
* reader of this codebase should have to know, and that a TypeORM upgrade is
|
|
518
|
+
* free to change.
|
|
519
|
+
*
|
|
520
|
+
* "Both spellings, two different methods" is not something a client does by
|
|
521
|
+
* accident; the dashboard sends one. So there is no legitimate request to
|
|
522
|
+
* preserve here, and refusing is strictly better than guessing: the caller is
|
|
523
|
+
* told exactly what is contradictory, and nothing downstream has a precedence
|
|
524
|
+
* question left to resolve.
|
|
525
|
+
*
|
|
526
|
+
* Deliberately NOT run before the ownership check. A payload that is both
|
|
527
|
+
* ambiguous and a hijack attempt should be reported as the hijack — that is
|
|
528
|
+
* the finding an operator needs to see, and "you sent the same field twice"
|
|
529
|
+
* would bury it.
|
|
530
|
+
*/
|
|
531
|
+
public assertOneMethodPerNotificationChannel(
|
|
532
|
+
carrier: RuleColumnCarrier,
|
|
533
|
+
): void {
|
|
534
|
+
for (const descriptor of this.getNotificationMethodDescriptors()) {
|
|
535
|
+
const fromIdColumn: ObjectID | string | undefined = resolveReferenceId(
|
|
536
|
+
carrier[descriptor.idColumn],
|
|
537
|
+
);
|
|
538
|
+
const fromRelation: ObjectID | string | undefined = resolveReferenceId(
|
|
539
|
+
carrier[descriptor.relationColumn],
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
if (!fromIdColumn || !fromRelation) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (
|
|
547
|
+
fromIdColumn.toString().toLowerCase() ===
|
|
548
|
+
fromRelation.toString().toLowerCase()
|
|
549
|
+
) {
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
throw new BadDataException(
|
|
554
|
+
`This notification rule names two different ${descriptor.label} notification methods: ${descriptor.idColumn} says ${fromIdColumn.toString()} and ${descriptor.relationColumn} says ${fromRelation.toString()}. Send only one of them.`,
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Fold the relation spelling into the FK column so exactly one spelling
|
|
561
|
+
* survives — the create-path reduction, and the reason the ORM never gets to
|
|
562
|
+
* choose.
|
|
563
|
+
*
|
|
564
|
+
* This mirrors CreatePermission.checkCreateOwnership, which validates the
|
|
565
|
+
* `user` relation identically to the `userId` scalar and then CLEARS it. The
|
|
566
|
+
* point of clearing rather than merely checking is that after it there is no
|
|
567
|
+
* second source of truth left to disagree with the first.
|
|
568
|
+
*
|
|
569
|
+
* The relation's id is what is kept, because that is the value TypeORM would
|
|
570
|
+
* have written: getEntityValue reads the relation slot first. A relation slot
|
|
571
|
+
* that is present but carries no id resolves to nothing, and clearing the FK
|
|
572
|
+
* column alongside it reproduces the NULL the ORM would have written — which
|
|
573
|
+
* matters, because that NULL is what turns a rule into one that pages
|
|
574
|
+
* nowhere, and the create invariants below have to be able to see it.
|
|
575
|
+
*
|
|
576
|
+
* CREATE ONLY. On update the two spellings do not have the same access
|
|
577
|
+
* control: the relation members are `update: []` on UserNotificationRule
|
|
578
|
+
* while the `*Id` members are open to an administrator, so moving a value
|
|
579
|
+
* from the relation slot into the FK column would launder a column write that
|
|
580
|
+
* ColumnPermission (which runs after the update hook) exists to refuse. On
|
|
581
|
+
* that path ambiguity is resolved by refusal instead — see
|
|
582
|
+
* assertOneMethodPerNotificationChannel.
|
|
583
|
+
*/
|
|
584
|
+
public collapseNotificationMethodRelationsOnCreate(
|
|
585
|
+
carrier: RuleColumnCarrier,
|
|
586
|
+
): void {
|
|
587
|
+
for (const descriptor of this.getNotificationMethodDescriptors()) {
|
|
588
|
+
const relationValue: unknown = carrier[descriptor.relationColumn];
|
|
589
|
+
|
|
590
|
+
if (relationValue === undefined || relationValue === null) {
|
|
591
|
+
/*
|
|
592
|
+
* Nothing supplied in this spelling. TypeORM falls through to the
|
|
593
|
+
* scalar in exactly this case, so there is nothing to fold and nothing
|
|
594
|
+
* to clear.
|
|
595
|
+
*/
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const resolved: ObjectID | string | undefined =
|
|
600
|
+
resolveReferenceId(relationValue);
|
|
601
|
+
|
|
602
|
+
carrier[descriptor.idColumn] = resolved
|
|
603
|
+
? resolved instanceof ObjectID
|
|
604
|
+
? resolved
|
|
605
|
+
: new ObjectID(resolved)
|
|
606
|
+
: undefined;
|
|
607
|
+
|
|
608
|
+
carrier[descriptor.relationColumn] = undefined;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Does this payload name a delivery address at all, in any channel, in either
|
|
614
|
+
* spelling?
|
|
615
|
+
*
|
|
616
|
+
* Driven off the descriptor table rather than a hand-written list of fourteen
|
|
617
|
+
* property names. A hand-written list is how an eighth channel ends up
|
|
618
|
+
* exempt from the "a rule must be able to reach somebody" invariant, and an
|
|
619
|
+
* exempt channel is a rule that silently pages nobody.
|
|
620
|
+
*/
|
|
621
|
+
public carriesAnyNotificationMethod(carrier: RuleColumnCarrier): boolean {
|
|
622
|
+
for (const descriptor of this.getNotificationMethodDescriptors()) {
|
|
623
|
+
if (carrier[descriptor.idColumn] || carrier[descriptor.relationColumn]) {
|
|
624
|
+
return true;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Does this patch MENTION a delivery address, whatever it sets it to?
|
|
633
|
+
*
|
|
634
|
+
* Distinct from carriesAnyNotificationMethod, and the difference is the whole
|
|
635
|
+
* point: `userEmailId: null` carries no method but very much mentions one. It
|
|
636
|
+
* is the write that removes a rule's only way of reaching somebody, so it is
|
|
637
|
+
* exactly the patch the coherence check must not skip.
|
|
638
|
+
*/
|
|
639
|
+
public mentionsAnyNotificationMethodColumn(
|
|
640
|
+
carrier: RuleColumnCarrier,
|
|
641
|
+
): boolean {
|
|
642
|
+
for (const descriptor of this.getNotificationMethodDescriptors()) {
|
|
643
|
+
if (
|
|
644
|
+
carrier[descriptor.idColumn] !== undefined ||
|
|
645
|
+
carrier[descriptor.relationColumn] !== undefined
|
|
646
|
+
) {
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* The row's delivery addresses as they would stand AFTER a patch is applied.
|
|
656
|
+
*
|
|
657
|
+
* The create-time invariants ("a rule that pages must name a method", "an
|
|
658
|
+
* opt-out rule must not") are properties of the ROW, and update can break
|
|
659
|
+
* both of them: flipping `isOptOut` on a rule that carries an email, or
|
|
660
|
+
* nulling the only method on a rule that is still meant to page. Neither is
|
|
661
|
+
* visible from the patch alone, so the persisted row has to be read and the
|
|
662
|
+
* two combined.
|
|
663
|
+
*
|
|
664
|
+
* Precedence between the two spellings is the ORM's, not ours: the relation
|
|
665
|
+
* slot wins when it is present, because that is what would be written. A
|
|
666
|
+
* column the patch does not mention keeps whatever the row already had.
|
|
667
|
+
*/
|
|
668
|
+
public getNotificationMethodIdsAfterPatch(data: {
|
|
669
|
+
patch: RuleColumnCarrier;
|
|
670
|
+
currentRow: RuleColumnCarrier;
|
|
671
|
+
}): Array<ObjectID> {
|
|
672
|
+
const methodIds: Array<ObjectID> = [];
|
|
673
|
+
|
|
674
|
+
for (const descriptor of this.getNotificationMethodDescriptors()) {
|
|
675
|
+
let resolved: ObjectID | string | undefined = undefined;
|
|
676
|
+
|
|
677
|
+
if (data.patch[descriptor.relationColumn] !== undefined) {
|
|
678
|
+
resolved = resolveReferenceId(data.patch[descriptor.relationColumn]);
|
|
679
|
+
} else if (data.patch[descriptor.idColumn] !== undefined) {
|
|
680
|
+
resolved = resolveReferenceId(data.patch[descriptor.idColumn]);
|
|
681
|
+
} else {
|
|
682
|
+
resolved = resolveReferenceId(data.currentRow[descriptor.idColumn]);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if (!resolved) {
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
methodIds.push(
|
|
690
|
+
resolved instanceof ObjectID ? resolved : new ObjectID(resolved),
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
return methodIds;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* R1 — the target of an on-behalf-of write must belong to the project the
|
|
699
|
+
* write is scoped to.
|
|
700
|
+
*
|
|
701
|
+
* The create-path ownership gate (CreatePermission.checkCreateOwnership)
|
|
702
|
+
* deliberately steps aside for a caller who holds a real role permission in
|
|
703
|
+
* the model's create list, so that this feature can exist at all. What it
|
|
704
|
+
* hands over is a caller who may name SOME other user — it says nothing about
|
|
705
|
+
* WHICH other users are in scope, because it has no notion of a project
|
|
706
|
+
* roster. Left there, holding ProjectAdmin on one throwaway project would be
|
|
707
|
+
* a licence to write notification rules for any user id in the installation.
|
|
708
|
+
*
|
|
709
|
+
* Membership is read straight from TeamMember with isRoot props and no cache.
|
|
710
|
+
* TeamMemberService.getTeamIdsForUser would have answered the same question
|
|
711
|
+
* with one fewer query, but it memoises for 60 seconds — and a security
|
|
712
|
+
* decision that keeps saying "yes" for a minute after the user was removed
|
|
713
|
+
* from the project is not a security decision.
|
|
714
|
+
*
|
|
715
|
+
* `hasAcceptedInvitation` is part of the test because it is this codebase's
|
|
716
|
+
* own definition of membership (the Owned permission scope resolves teams the
|
|
717
|
+
* same way), and because a pending invitation is an admin-created row: any
|
|
718
|
+
* admin can create one for any email address, so treating one as membership
|
|
719
|
+
* would hand back most of what this guard just took away. Nothing legitimate
|
|
720
|
+
* is lost — a rule must carry a notification method to be created at all, and
|
|
721
|
+
* a user who has not joined the project has none.
|
|
722
|
+
*/
|
|
723
|
+
@CaptureSpan()
|
|
724
|
+
public async assertTargetUserIsProjectMember(data: {
|
|
725
|
+
targetUserId: ObjectID;
|
|
726
|
+
props: DatabaseCommonInteractionProps;
|
|
727
|
+
}): Promise<void> {
|
|
728
|
+
const projectId: ObjectID | undefined = data.props.tenantId;
|
|
729
|
+
|
|
730
|
+
if (!projectId) {
|
|
731
|
+
/*
|
|
732
|
+
* A non-root caller writing another user's rule without a tenant scope
|
|
733
|
+
* has nothing to be an admin OF. There is no roster to check them
|
|
734
|
+
* against, so there is no way to say yes.
|
|
735
|
+
*/
|
|
736
|
+
throw new BadDataException(
|
|
737
|
+
"A project is required to create a notification rule for another user.",
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const membership: TeamMember | null = await TeamMemberService.findOneBy({
|
|
742
|
+
query: {
|
|
743
|
+
userId: data.targetUserId,
|
|
744
|
+
projectId: projectId,
|
|
745
|
+
hasAcceptedInvitation: true,
|
|
746
|
+
},
|
|
747
|
+
select: {
|
|
748
|
+
_id: true,
|
|
749
|
+
},
|
|
750
|
+
props: {
|
|
751
|
+
isRoot: true,
|
|
752
|
+
},
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
if (!membership) {
|
|
756
|
+
throw new BadDataException(
|
|
757
|
+
`Cannot create a notification rule for user ${data.targetUserId.toString()} because they are not a member of this project.`,
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* R3 — the anti-hijack invariant, and the single most important guard in this
|
|
764
|
+
* phase.
|
|
765
|
+
*
|
|
766
|
+
* A rule's method FK is the address its pages are delivered to. Its userId is
|
|
767
|
+
* whose pages select it. If those two can name different people, an admin
|
|
768
|
+
* editing a colleague's rule to point at the ADMIN's own webhook silently
|
|
769
|
+
* redirects that colleague's pages, and every surface either of them can see
|
|
770
|
+
* still reports a healthy configuration.
|
|
771
|
+
*
|
|
772
|
+
* `ownerUserId` MUST be the rule's persisted owner, not a value out of the
|
|
773
|
+
* request body. On update the caller controls the body, so a body-supplied
|
|
774
|
+
* userId lets them claim the row is theirs while writing somebody else's; the
|
|
775
|
+
* callers in UserNotificationRuleService therefore re-read it from the
|
|
776
|
+
* database first, and pass what the database said.
|
|
777
|
+
*
|
|
778
|
+
* A method row that does not exist is rejected rather than ignored. "Not
|
|
779
|
+
* found" and "not yours" are the same answer here — in both cases the caller
|
|
780
|
+
* named a row they have no business naming — and a guard whose failure mode
|
|
781
|
+
* is `undefined === undefined` is a guard that opens on the one input it was
|
|
782
|
+
* written to reject.
|
|
783
|
+
*/
|
|
784
|
+
@CaptureSpan()
|
|
785
|
+
public async assertNotificationMethodsBelongToUser(data: {
|
|
786
|
+
ownerUserId: ObjectID | undefined;
|
|
787
|
+
references: Array<NotificationMethodReference>;
|
|
788
|
+
}): Promise<void> {
|
|
789
|
+
if (data.references.length === 0) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (!data.ownerUserId) {
|
|
794
|
+
/*
|
|
795
|
+
* An unowned rule is not a safe rule. Its userId decides whose pages
|
|
796
|
+
* select it, so a row that names an address but no owner is a row nobody
|
|
797
|
+
* can be shown and nobody can audit. Reject rather than skip the check.
|
|
798
|
+
*/
|
|
799
|
+
throw new BadDataException(
|
|
800
|
+
"A notification rule that names a notification method must have an owner.",
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const descriptorsByColumn: Map<string, NotificationMethodDescriptor> =
|
|
805
|
+
new Map<string, NotificationMethodDescriptor>();
|
|
806
|
+
|
|
807
|
+
for (const descriptor of this.getNotificationMethodDescriptors()) {
|
|
808
|
+
descriptorsByColumn.set(descriptor.idColumn, descriptor);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
for (const reference of data.references) {
|
|
812
|
+
const descriptor: NotificationMethodDescriptor | undefined =
|
|
813
|
+
descriptorsByColumn.get(reference.idColumn);
|
|
814
|
+
|
|
815
|
+
if (!descriptor) {
|
|
816
|
+
/*
|
|
817
|
+
* Unreachable while references only ever come from
|
|
818
|
+
* collectNotificationMethodReferences, which builds them from this same
|
|
819
|
+
* table. Refusing an unknown column rather than skipping it keeps that
|
|
820
|
+
* true if the two ever drift.
|
|
821
|
+
*/
|
|
822
|
+
throw new BadDataException(
|
|
823
|
+
`Unknown notification method column ${reference.idColumn}.`,
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const methodOwnerUserId: ObjectID | undefined =
|
|
828
|
+
await descriptor.findOwnerUserId(reference.methodId);
|
|
829
|
+
|
|
830
|
+
if (!methodOwnerUserId) {
|
|
831
|
+
throw new BadDataException(
|
|
832
|
+
`The ${descriptor.label} notification method referenced by this rule does not exist.`,
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (methodOwnerUserId.toString() !== data.ownerUserId.toString()) {
|
|
837
|
+
/*
|
|
838
|
+
* The spelling is named at the end rather than woven into the sentence
|
|
839
|
+
* so that the wording support runbooks and tests match on stays intact.
|
|
840
|
+
* It earns its place because the two spellings are one column: told only
|
|
841
|
+
* "your Email method is wrong", a caller looking at a payload whose
|
|
842
|
+
* `userEmailId` is perfectly correct has no way to see that the relation
|
|
843
|
+
* slot beside it is the problem.
|
|
844
|
+
*/
|
|
845
|
+
throw new BadDataException(
|
|
846
|
+
`The ${descriptor.label} notification method referenced by this rule belongs to a different user. A notification rule can only use notification methods owned by the user the rule belongs to. The offending value was sent in ${reference.suppliedColumn}.`,
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* R6 — record and announce a change one person made to another person's
|
|
854
|
+
* paging configuration.
|
|
855
|
+
*
|
|
856
|
+
* Both halves are keyed on `props.userId` — the actor the SERVER resolved
|
|
857
|
+
* from the session — versus the row's PERSISTED userId, read back after the
|
|
858
|
+
* write. Nothing here reads the request body: the body is the thing under
|
|
859
|
+
* suspicion, and an audit line that an attacker can author is worse than no
|
|
860
|
+
* audit line, because it looks like evidence.
|
|
861
|
+
*
|
|
862
|
+
* Nothing in here may break the write. The row is already committed by the
|
|
863
|
+
* time this runs; throwing now would report a failure for a change that
|
|
864
|
+
* happened, and the caller would be entitled to believe it did not. So the
|
|
865
|
+
* whole body is a try/catch, and the mail is fire-and-forget on top of that.
|
|
866
|
+
*/
|
|
867
|
+
@CaptureSpan()
|
|
868
|
+
public async recordAdminRuleChange(data: {
|
|
869
|
+
action: AuditLogAction;
|
|
870
|
+
actorUserId: ObjectID;
|
|
871
|
+
ownerUserId: ObjectID;
|
|
872
|
+
projectId: ObjectID | undefined;
|
|
873
|
+
/*
|
|
874
|
+
* `null` as well as `undefined`, because BaseModel.id is a getter over
|
|
875
|
+
* `_id` and returns null for a row that has none. Both mean the same thing
|
|
876
|
+
* here — no row to point the audit entry at — and typing only one of them
|
|
877
|
+
* makes every call site cast.
|
|
878
|
+
*/
|
|
879
|
+
ruleId: ObjectID | null | undefined;
|
|
880
|
+
/*
|
|
881
|
+
* The row as it stood before the write. Absent on create. On delete this is
|
|
882
|
+
* the whole of the record that is left: the row is gone, so this snapshot
|
|
883
|
+
* is the only description of what the project just lost.
|
|
884
|
+
*/
|
|
885
|
+
before?: UserNotificationRule | undefined;
|
|
886
|
+
/** The row as it stands after the write. Absent on update and delete. */
|
|
887
|
+
after?: UserNotificationRule | undefined;
|
|
888
|
+
/** The patch, for the update diff. Absent on create and delete. */
|
|
889
|
+
updatedFields?: JSONObject | undefined;
|
|
890
|
+
/*
|
|
891
|
+
* Whether to mail the owner. The audit entry is per ROW because that is
|
|
892
|
+
* what an investigator needs to reconstruct; the mail is per PERSON,
|
|
893
|
+
* because one request that touches twenty of somebody's rules is still one
|
|
894
|
+
* thing that happened to them, and twenty identical mails is how a warning
|
|
895
|
+
* becomes a filter rule.
|
|
896
|
+
*/
|
|
897
|
+
notifyOwner: boolean;
|
|
898
|
+
props: DatabaseCommonInteractionProps;
|
|
899
|
+
}): Promise<void> {
|
|
900
|
+
try {
|
|
901
|
+
/*
|
|
902
|
+
* The dependable half of the trail. AuditLogService below only writes on
|
|
903
|
+
* enterprise builds with audit logs switched on for the project, which is
|
|
904
|
+
* a minority of installs — and "an admin edited somebody else's paging"
|
|
905
|
+
* is exactly the event an operator needs to be able to find on the other
|
|
906
|
+
* ones too.
|
|
907
|
+
*/
|
|
908
|
+
logger.info(
|
|
909
|
+
`On-call notification rule ${data.action.toLowerCase()}d for user ${data.ownerUserId.toString()} by a different user ${data.actorUserId.toString()}.`,
|
|
910
|
+
{
|
|
911
|
+
projectId: data.projectId?.toString(),
|
|
912
|
+
userId: data.ownerUserId.toString(),
|
|
913
|
+
actorUserId: data.actorUserId.toString(),
|
|
914
|
+
userNotificationRuleId: data.ruleId?.toString(),
|
|
915
|
+
action: data.action,
|
|
916
|
+
} as LogAttributes,
|
|
917
|
+
);
|
|
918
|
+
|
|
919
|
+
await this.writeAuditLog(data);
|
|
920
|
+
|
|
921
|
+
if (!data.notifyOwner) {
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/*
|
|
926
|
+
* Fire-and-forget by construction: the owner's mail involves three more
|
|
927
|
+
* reads and an SMTP round trip, none of which the writing request should
|
|
928
|
+
* wait on and none of which may surface as a failed write.
|
|
929
|
+
*/
|
|
930
|
+
this.notifyOwnerOfAdminChange({
|
|
931
|
+
action: data.action,
|
|
932
|
+
actorUserId: data.actorUserId,
|
|
933
|
+
ownerUserId: data.ownerUserId,
|
|
934
|
+
projectId: data.projectId,
|
|
935
|
+
}).catch((error: Error): void => {
|
|
936
|
+
logger.error(
|
|
937
|
+
`UserNotificationRuleAdminService: failed to tell user ${data.ownerUserId.toString()} that an admin changed their on-call notification rules.`,
|
|
938
|
+
);
|
|
939
|
+
logger.error(error);
|
|
940
|
+
});
|
|
941
|
+
} catch (error) {
|
|
942
|
+
logger.error(
|
|
943
|
+
"UserNotificationRuleAdminService: failed to record an administrative notification rule change.",
|
|
944
|
+
);
|
|
945
|
+
logger.error(error);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/*
|
|
950
|
+
* The AuditLog analytics row, written directly rather than through
|
|
951
|
+
* DatabaseService's own audit hook.
|
|
952
|
+
*
|
|
953
|
+
* DatabaseService writes one automatically for models decorated with
|
|
954
|
+
* @EnableAuditLog, and UserNotificationRule is not one of them — so without
|
|
955
|
+
* this call an administrative edit leaves no row at all. The decorator is
|
|
956
|
+
* checked anyway so that adding it later produces one entry rather than two:
|
|
957
|
+
* a duplicated audit record is not merely noise, it makes a reader count two
|
|
958
|
+
* changes where one happened.
|
|
959
|
+
*
|
|
960
|
+
* Required lazily for the same reason DatabaseService requires it lazily —
|
|
961
|
+
* AuditLogService depends on ProjectService and UserService, both of which
|
|
962
|
+
* extend DatabaseService, so a top-level import can leave the base class
|
|
963
|
+
* undefined at class-extension time.
|
|
964
|
+
*/
|
|
965
|
+
private async writeAuditLog(data: {
|
|
966
|
+
action: AuditLogAction;
|
|
967
|
+
ruleId: ObjectID | null | undefined;
|
|
968
|
+
before?: UserNotificationRule | undefined;
|
|
969
|
+
after?: UserNotificationRule | undefined;
|
|
970
|
+
updatedFields?: JSONObject | undefined;
|
|
971
|
+
props: DatabaseCommonInteractionProps;
|
|
972
|
+
}): Promise<void> {
|
|
973
|
+
const model: UserNotificationRule = new UserNotificationRule();
|
|
974
|
+
|
|
975
|
+
if (model.enableAuditLogOn) {
|
|
976
|
+
// DatabaseService already writes this row; a second one would double-count.
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
const auditLogService: typeof AuditLogServiceType =
|
|
981
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
|
982
|
+
require("./AuditLogService").default;
|
|
983
|
+
|
|
984
|
+
if (data.action === AuditLogAction.Create && data.after) {
|
|
985
|
+
await auditLogService.recordCreate({
|
|
986
|
+
model: model,
|
|
987
|
+
createdItem: data.after,
|
|
988
|
+
props: data.props,
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
if (data.action === AuditLogAction.Update && data.before && data.ruleId) {
|
|
995
|
+
await auditLogService.recordUpdate({
|
|
996
|
+
model: model,
|
|
997
|
+
before: data.before,
|
|
998
|
+
updatedFields: data.updatedFields || {},
|
|
999
|
+
itemId: data.ruleId,
|
|
1000
|
+
props: data.props,
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/*
|
|
1007
|
+
* Delete is the one action whose audit entry cannot be reconstructed after
|
|
1008
|
+
* the fact. An edited row can still be read; a deleted one cannot, so the
|
|
1009
|
+
* snapshot captured before the write is the entire record of what the
|
|
1010
|
+
* responder's paging used to be.
|
|
1011
|
+
*/
|
|
1012
|
+
if (data.action === AuditLogAction.Delete && data.before && data.ruleId) {
|
|
1013
|
+
await auditLogService.recordDelete({
|
|
1014
|
+
model: model,
|
|
1015
|
+
deletedItem: data.before,
|
|
1016
|
+
itemId: data.ruleId,
|
|
1017
|
+
props: data.props,
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
/*
|
|
1023
|
+
* Tell the person whose pages just changed.
|
|
1024
|
+
*
|
|
1025
|
+
* This is the part of the phase that makes admin repair safe to hand out
|
|
1026
|
+
* rather than merely possible: an admin fixing a colleague's rules is a
|
|
1027
|
+
* normal, welcome thing, and an attacker redirecting them is not, and from
|
|
1028
|
+
* the server's side those two are the same request. The only reliable way to
|
|
1029
|
+
* tell them apart is to ask the one person who always knows which it was.
|
|
1030
|
+
*/
|
|
1031
|
+
private async notifyOwnerOfAdminChange(data: {
|
|
1032
|
+
action: AuditLogAction;
|
|
1033
|
+
actorUserId: ObjectID;
|
|
1034
|
+
ownerUserId: ObjectID;
|
|
1035
|
+
projectId: ObjectID | undefined;
|
|
1036
|
+
}): Promise<void> {
|
|
1037
|
+
const owner: User | null = await UserService.findOneById({
|
|
1038
|
+
id: data.ownerUserId,
|
|
1039
|
+
select: {
|
|
1040
|
+
_id: true,
|
|
1041
|
+
name: true,
|
|
1042
|
+
email: true,
|
|
1043
|
+
},
|
|
1044
|
+
props: {
|
|
1045
|
+
isRoot: true,
|
|
1046
|
+
},
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
if (!owner || !owner.email) {
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
const actor: User | null = await UserService.findOneById({
|
|
1054
|
+
id: data.actorUserId,
|
|
1055
|
+
select: {
|
|
1056
|
+
_id: true,
|
|
1057
|
+
name: true,
|
|
1058
|
+
email: true,
|
|
1059
|
+
},
|
|
1060
|
+
props: {
|
|
1061
|
+
isRoot: true,
|
|
1062
|
+
},
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
/*
|
|
1066
|
+
* Naming the actor by email as well as by name is deliberate. Display names
|
|
1067
|
+
* are not unique and are user-editable, so "Alex changed your rules" is not
|
|
1068
|
+
* something the reader can act on; an address is.
|
|
1069
|
+
*/
|
|
1070
|
+
const actorDescription: string = actor
|
|
1071
|
+
? `${actor.name?.toString() || "A project administrator"} (${
|
|
1072
|
+
actor.email?.toString() || data.actorUserId.toString()
|
|
1073
|
+
})`
|
|
1074
|
+
: `A project administrator (${data.actorUserId.toString()})`;
|
|
1075
|
+
|
|
1076
|
+
let projectName: string = "your project";
|
|
1077
|
+
|
|
1078
|
+
if (data.projectId) {
|
|
1079
|
+
const project: Project | null = await ProjectService.findOneById({
|
|
1080
|
+
id: data.projectId,
|
|
1081
|
+
select: {
|
|
1082
|
+
_id: true,
|
|
1083
|
+
name: true,
|
|
1084
|
+
},
|
|
1085
|
+
props: {
|
|
1086
|
+
isRoot: true,
|
|
1087
|
+
},
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
if (project?.name) {
|
|
1091
|
+
projectName = project.name;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
let verb: string = "changed";
|
|
1096
|
+
|
|
1097
|
+
if (data.action === AuditLogAction.Create) {
|
|
1098
|
+
verb = "added";
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (data.action === AuditLogAction.Delete) {
|
|
1102
|
+
verb = "deleted";
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
const subject: string = `Your on-call notification rules were ${verb} by an administrator`;
|
|
1106
|
+
|
|
1107
|
+
const settingsLink: string = data.projectId
|
|
1108
|
+
? (
|
|
1109
|
+
await this.getNotificationRulesLinkInDashboard(data.projectId)
|
|
1110
|
+
).toString()
|
|
1111
|
+
: "";
|
|
1112
|
+
|
|
1113
|
+
/*
|
|
1114
|
+
* Deletion gets its own sentence rather than the generic one. "Somebody
|
|
1115
|
+
* changed your rules" is a prompt to go and look; "you may no longer be
|
|
1116
|
+
* paged" is the consequence, and it is the only one of the three actions
|
|
1117
|
+
* that can leave a responder silently unreachable — which is the failure
|
|
1118
|
+
* this whole epic exists to stop happening unnoticed.
|
|
1119
|
+
*/
|
|
1120
|
+
const consequence: string =
|
|
1121
|
+
data.action === AuditLogAction.Delete
|
|
1122
|
+
? "Your notification rules decide how OneUptime reaches you when you are paged. With them removed you may no longer be notified when you are on call, so add new rules if this was not what you expected, and tell your project owners."
|
|
1123
|
+
: "Your notification rules decide how OneUptime reaches you when you are paged. If you were not expecting this change, review your rules now and tell your project owners.";
|
|
1124
|
+
|
|
1125
|
+
const message: string = `${this.escapeHtml(actorDescription)} ${verb} your on-call notification rules in ${this.escapeHtml(
|
|
1126
|
+
projectName,
|
|
1127
|
+
)}.<br/><br/>${consequence}${
|
|
1128
|
+
settingsLink
|
|
1129
|
+
? `<br/><br/><a href="${this.escapeHtml(settingsLink)}">Review your notification rules</a>`
|
|
1130
|
+
: ""
|
|
1131
|
+
}`;
|
|
1132
|
+
|
|
1133
|
+
const vars: Dictionary<string> = {
|
|
1134
|
+
subject: subject,
|
|
1135
|
+
message: message,
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
await MailService.sendMail(
|
|
1139
|
+
{
|
|
1140
|
+
toEmail: owner.email,
|
|
1141
|
+
templateType: EmailTemplateType.SimpleMessage,
|
|
1142
|
+
vars: vars,
|
|
1143
|
+
subject: subject,
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
projectId: data.projectId,
|
|
1147
|
+
userId: owner.id!,
|
|
1148
|
+
},
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
private async getNotificationRulesLinkInDashboard(
|
|
1153
|
+
projectId: ObjectID,
|
|
1154
|
+
): Promise<URL> {
|
|
1155
|
+
const dashboardUrl: URL = await DatabaseConfig.getDashboardUrl();
|
|
1156
|
+
|
|
1157
|
+
/*
|
|
1158
|
+
* Spelled out rather than imported: Common/Server cannot reach the
|
|
1159
|
+
* Dashboard's RouteMap, the same reason
|
|
1160
|
+
* OnCallNotificationAlertingService.getNotificationRulesLinkInDashboard
|
|
1161
|
+
* duplicates its path segments.
|
|
1162
|
+
*/
|
|
1163
|
+
return URL.fromString(dashboardUrl.toString()).addRoute(
|
|
1164
|
+
`/${projectId.toString()}/user-settings/notification-methods`,
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/*
|
|
1169
|
+
* SimpleMessage.hbs renders `message` through a triple-stache InfoBlock
|
|
1170
|
+
* partial, so nothing between here and the recipient's inbox escapes
|
|
1171
|
+
* anything. Project names and user names are attacker-influenced free text.
|
|
1172
|
+
*/
|
|
1173
|
+
private escapeHtml(value: string): string {
|
|
1174
|
+
return value
|
|
1175
|
+
.replace(/&/g, "&")
|
|
1176
|
+
.replace(/</g, "<")
|
|
1177
|
+
.replace(/>/g, ">")
|
|
1178
|
+
.replace(/"/g, """)
|
|
1179
|
+
.replace(/'/g, "'");
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
export default new UserNotificationRuleAdminService();
|