@oneuptime/common 14.0.6 → 14.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/Incident.ts +267 -1
- package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
- package/Models/DatabaseModels/InventoryItem.ts +23 -12
- package/Models/DatabaseModels/StatusPage.ts +55 -0
- package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
- package/Server/API/IncidentAPI.ts +162 -0
- package/Server/API/StatusPageAPI.ts +487 -171
- package/Server/API/StatusPageSubscriberAPI.ts +63 -15
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.ts +217 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
- package/Server/Infrastructure/Semaphore.ts +10 -0
- package/Server/Middleware/IdentityRateLimit.ts +108 -3
- package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
- package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
- package/Server/Services/AlertService.ts +32 -36
- package/Server/Services/DatabaseService.ts +93 -10
- package/Server/Services/IncidentAlertService.ts +609 -2
- package/Server/Services/IncidentCustomFieldService.ts +276 -1
- package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
- package/Server/Services/IncidentEpisodeService.ts +15 -0
- package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
- package/Server/Services/IncidentPublicNoteService.ts +30 -1
- package/Server/Services/IncidentService.ts +1663 -103
- package/Server/Services/IncidentStateTimelineService.ts +13 -0
- package/Server/Services/IncidentTemplateService.ts +109 -0
- package/Server/Services/InventoryItemService.ts +38 -0
- package/Server/Services/IoTDeviceCredentialService.ts +13 -4
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
- package/Server/Services/ScheduledMaintenanceService.ts +118 -64
- package/Server/Services/SmsService.ts +10 -0
- package/Server/Services/StatusPageService.ts +65 -8
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
- package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
- package/Server/Services/StatusPageSubscriberService.ts +1161 -64
- package/Server/Services/UserNotificationRuleService.ts +55 -53
- package/Server/Services/WorkspaceNotificationSummaryService.ts +38 -20
- package/Server/Types/Markdown.ts +153 -6
- package/Server/Types/Workflow/Components/Email.ts +61 -2
- package/Server/Utils/AffectedResources/LinkedAffectedResources.ts +576 -0
- package/Server/Utils/Alert/AlertStateChangeAuthorization.ts +129 -0
- package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
- package/Server/Utils/DataSource/EgressGuard.ts +66 -21
- package/Server/Utils/Inventory/InventoryOverviewAggregation.ts +111 -0
- package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
- package/Server/Utils/Response.ts +31 -0
- package/Server/Utils/SSRFProtection.ts +76 -16
- package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
- package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
- package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
- package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
- package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
- package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
- package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
- package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
- package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
- package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
- package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
- package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
- package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
- package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
- package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
- package/Server/Utils/StatusPageResource.ts +51 -20
- package/Server/Utils/Topology/TopologyConcurrencyLimiter.ts +229 -0
- package/Server/Utils/Topology/TopologyQueries.ts +1350 -0
- package/Server/Utils/Topology/TopologyRequest.ts +463 -0
- package/Server/Utils/Topology/TopologyResponseCache.ts +183 -0
- package/Server/Utils/Topology/TopologySql.ts +1024 -0
- package/Server/Utils/VM/VMAPI.ts +1 -0
- package/Server/Utils/VM/VMRunner.ts +10 -0
- package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
- package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
- package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
- package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
- package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
- package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
- package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
- package/Tests/App/Dashboard/AcknowledgeAlertsOnDeclare.test.ts +369 -0
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +1 -1
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +510 -123
- package/Tests/App/Dashboard/AlertsTableAffectedResources.test.tsx +490 -0
- package/Tests/App/Dashboard/ChangeAlertStateDeclareIncident.test.tsx +886 -0
- package/Tests/App/Dashboard/CreateReviewStepRoleSummary.test.tsx +1250 -0
- package/Tests/App/Dashboard/CreateReviewStepSelectionSummary.test.tsx +738 -0
- package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
- package/Tests/App/Dashboard/DeclareIncidentFromAlert.test.ts +417 -0
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +1948 -229
- package/Tests/App/Dashboard/EntityDetailPanelLifecycle.test.tsx +493 -0
- package/Tests/App/Dashboard/EntityDetailPanelNavigation.test.tsx +439 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
- package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
- package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +534 -6
- package/Tests/App/Dashboard/EventStatusPanel.test.tsx +798 -2
- package/Tests/App/Dashboard/FetchStateAndSeverityElements.test.tsx +669 -0
- package/Tests/App/Dashboard/FetchUsers.test.tsx +230 -0
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
- package/Tests/App/Dashboard/IncidentCreateAcknowledgeAlerts.test.tsx +1611 -0
- package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
- package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
- package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
- package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
- package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
- package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
- package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
- package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
- package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +1365 -57
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +964 -23
- package/Tests/App/Dashboard/InventoryRelationships.test.tsx +885 -0
- package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
- package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +201 -15
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +508 -36
- package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
- package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
- package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
- package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
- package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
- package/Tests/App/Dashboard/TopologyDataLifecycle.test.tsx +583 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +982 -218
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +818 -114
- package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
- package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
- package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
- package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
- package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
- package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
- package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
- package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
- package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
- package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
- package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
- package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
- package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
- package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
- package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
- package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +90 -21
- package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
- package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
- package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
- package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
- package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
- package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
- package/Tests/Server/Services/DomainService.test.ts +525 -0
- package/Tests/Server/Services/IncidentAlertAcknowledgeOnDeclare.test.ts +2630 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +1625 -1
- package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
- package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
- package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
- package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
- package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
- package/Tests/Server/Services/IncidentDeclareAcknowledgeAlerts.test.ts +2189 -0
- package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
- package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
- package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
- package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
- package/Tests/Server/Services/InventoryItemServiceOverviewCounts.test.ts +345 -0
- package/Tests/Server/Services/InventoryOverviewPostgres.test.ts +254 -0
- package/Tests/Server/Services/IoTDeviceCredentialService.test.ts +819 -0
- package/Tests/Server/Services/LinkedAffectedResourcesCreatedFeed.test.ts +420 -0
- package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
- package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
- package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +92 -36
- package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
- package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
- package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
- package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
- package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
- package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
- package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
- package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
- package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
- package/Tests/Server/Utils/AffectedResources/LinkedAffectedResources.test.ts +858 -0
- package/Tests/Server/Utils/Alert/AlertStateChangeAuthorization.test.ts +1172 -0
- package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
- package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
- package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
- package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
- package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
- package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
- package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
- package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
- package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
- package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
- package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
- package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
- package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
- package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
- package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigration.test.ts +386 -0
- package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigrationPostgres.test.ts +430 -0
- package/Tests/Server/Utils/Topology/TopologyActivityParityPostgres.test.ts +279 -0
- package/Tests/Server/Utils/Topology/TopologyConcurrencyLimiter.test.ts +534 -0
- package/Tests/Server/Utils/Topology/TopologyEstateGenerator.ts +624 -0
- package/Tests/Server/Utils/Topology/TopologyJsonResponse.test.ts +90 -0
- package/Tests/Server/Utils/Topology/TopologyQueries.test.ts +1634 -0
- package/Tests/Server/Utils/Topology/TopologyQueriesDifferentialPostgres.test.ts +683 -0
- package/Tests/Server/Utils/Topology/TopologyQueriesPostgres.test.ts +2624 -0
- package/Tests/Server/Utils/Topology/TopologyReference.ts +1018 -0
- package/Tests/Server/Utils/Topology/TopologyRequest.test.ts +643 -0
- package/Tests/Server/Utils/Topology/TopologyResponseCache.test.ts +305 -0
- package/Tests/Server/Utils/Topology/TopologySql.test.ts +973 -0
- package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
- package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
- package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
- package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
- package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
- package/Tests/Types/SafeHtml.test.ts +102 -0
- package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
- package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
- package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
- package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
- package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
- package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
- package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
- package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
- package/Tests/Types/Topology/TopologyTypeRules.test.ts +176 -0
- package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
- package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
- package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
- package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
- package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +302 -0
- package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
- package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
- package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
- package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
- package/Types/CustomField/CustomFieldOrder.ts +55 -0
- package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
- package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
- package/Types/CustomField/CustomFieldType.ts +14 -0
- package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
- package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
- package/Types/Incident/IncidentAlertLink.ts +19 -0
- package/Types/SafeHtml.ts +93 -0
- package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
- package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
- package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
- package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
- package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
- package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
- package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
- package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
- package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
- package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
- package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
- package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
- package/Types/Telemetry/InventoryLiveness.ts +26 -0
- package/Types/Topology/TopologyApi.ts +428 -0
- package/Types/Topology/TopologyTypeRules.ts +191 -0
- package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
- package/UI/Components/Forms/ModelForm.tsx +70 -8
- package/UI/Components/Forms/Validation.ts +1 -1
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
- package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
- package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
- package/UI/Components/ModelTable/ModelTable.tsx +6 -7
- package/UI/Utils/SensitiveUrlToken.ts +10 -5
- package/Utils/API.ts +247 -2
- package/Utils/Incident/IncidentFromAlerts.ts +78 -0
- package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
- package/Utils/Markdown/MarkdownEscape.ts +65 -0
- package/Utils/ProbeNetworkFailureUtil.ts +673 -0
- package/build/dist/Models/DatabaseModels/Incident.js +267 -1
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js +20 -11
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +100 -1
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +417 -176
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
- package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js +165 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
- package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
- package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
- package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
- package/build/dist/Server/Services/AlertService.js +21 -33
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +56 -7
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentAlertService.js +457 -3
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
- package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +1148 -42
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +31 -0
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceCredentialService.js +12 -5
- package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +84 -57
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/SmsService.js +2 -22
- package/build/dist/Server/Services/SmsService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +47 -9
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +55 -51
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +42 -25
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +110 -6
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
- package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
- package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js +405 -0
- package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js.map +1 -0
- package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js +112 -0
- package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js.map +1 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js +81 -0
- package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js.map +1 -0
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
- package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +30 -0
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +48 -6
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
- package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
- package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
- package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
- package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
- package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageResource.js +39 -7
- package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
- package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js +178 -0
- package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyQueries.js +884 -0
- package/build/dist/Server/Utils/Topology/TopologyQueries.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyRequest.js +275 -0
- package/build/dist/Server/Utils/Topology/TopologyRequest.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologyResponseCache.js +146 -0
- package/build/dist/Server/Utils/Topology/TopologyResponseCache.js.map +1 -0
- package/build/dist/Server/Utils/Topology/TopologySql.js +688 -0
- package/build/dist/Server/Utils/Topology/TopologySql.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
- package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
- package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
- package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
- package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
- package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js +17 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -1
- package/build/dist/Types/SafeHtml.js +84 -0
- package/build/dist/Types/SafeHtml.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
- package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
- package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
- package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
- package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
- package/build/dist/Types/Telemetry/InventoryLiveness.js +23 -0
- package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -1
- package/build/dist/Types/Topology/TopologyApi.js +55 -0
- package/build/dist/Types/Topology/TopologyApi.js.map +1 -0
- package/build/dist/Types/Topology/TopologyTypeRules.js +165 -0
- package/build/dist/Types/Topology/TopologyTypeRules.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +1 -1
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
- package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
- package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
- package/build/dist/Utils/API.js +177 -1
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +38 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
- package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
- package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
- package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { generateCustomFieldVariableKey } from "../../../../Types/CustomField/CustomFieldVariableKey";
|
|
2
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Incident custom field settings for declaring incidents and for subscriber
|
|
6
|
+
* emails (OneUptime/oneuptime#4035):
|
|
7
|
+
*
|
|
8
|
+
* - isRequiredOnCreate, sortOrder, showOnCreate and
|
|
9
|
+
* includeInSubscriberNotifications: plain settings. The three flags start
|
|
10
|
+
* off, and no field has an order, so every existing field behaves as it did.
|
|
11
|
+
* - variableKey: the key templates reach a field by, {{customFields.<key>}}.
|
|
12
|
+
* Unique per project (the index), made from the name when a field is
|
|
13
|
+
* created and never changed afterwards. Nullable only because the table
|
|
14
|
+
* already has rows; the backfill below gives each of them a key.
|
|
15
|
+
*
|
|
16
|
+
* The backfill runs here, in the same transaction as the columns, rather than
|
|
17
|
+
* in a batched data migration like the subscriber tokens: definition tables
|
|
18
|
+
* hold a few dozen rows a project, and a key has to exist before anything
|
|
19
|
+
* lists a field's template variable. Keys are made by the same generator the
|
|
20
|
+
* service uses for a new field, so an existing field gets the key it would
|
|
21
|
+
* have got had it been created today. Within a project the oldest field wins
|
|
22
|
+
* the plain key and later ones get _2, _3, ...
|
|
23
|
+
*
|
|
24
|
+
* down() drops the columns and the index; the keys cannot be recovered, and
|
|
25
|
+
* templates that use them stop filling in.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// Rows written per UPDATE: two parameters each, well under Postgres' limit.
|
|
29
|
+
const BACKFILL_BATCH_SIZE: number = 500;
|
|
30
|
+
|
|
31
|
+
type BackfillQueryRunner = Pick<QueryRunner, "query">;
|
|
32
|
+
|
|
33
|
+
interface FieldRow {
|
|
34
|
+
_id: string;
|
|
35
|
+
projectId: string;
|
|
36
|
+
name: string | null;
|
|
37
|
+
variableKey: string | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Give every incident custom field without a template key one. Idempotent:
|
|
42
|
+
* a key already there is kept, and counted as taken for the others.
|
|
43
|
+
* Returns how many fields it gave a key.
|
|
44
|
+
*/
|
|
45
|
+
export const backfillIncidentCustomFieldVariableKeys: (
|
|
46
|
+
queryRunner: BackfillQueryRunner,
|
|
47
|
+
) => Promise<number> = async (
|
|
48
|
+
queryRunner: BackfillQueryRunner,
|
|
49
|
+
): Promise<number> => {
|
|
50
|
+
const rows: Array<FieldRow> = await queryRunner.query(
|
|
51
|
+
`SELECT "_id", "projectId", "name", "variableKey" FROM "IncidentCustomField" ORDER BY "projectId" ASC, "createdAt" ASC, "_id" ASC`,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const takenByProject: Map<string, Set<string>> = new Map<
|
|
55
|
+
string,
|
|
56
|
+
Set<string>
|
|
57
|
+
>();
|
|
58
|
+
|
|
59
|
+
for (const row of rows) {
|
|
60
|
+
const taken: Set<string> =
|
|
61
|
+
takenByProject.get(row.projectId) || new Set<string>();
|
|
62
|
+
|
|
63
|
+
if (row.variableKey) {
|
|
64
|
+
taken.add(row.variableKey);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
takenByProject.set(row.projectId, taken);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const assignments: Array<{ id: string; variableKey: string }> = [];
|
|
71
|
+
|
|
72
|
+
for (const row of rows) {
|
|
73
|
+
if (row.variableKey) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const taken: Set<string> = takenByProject.get(row.projectId)!;
|
|
78
|
+
|
|
79
|
+
const variableKey: string = generateCustomFieldVariableKey({
|
|
80
|
+
name: row.name || "",
|
|
81
|
+
existingKeys: taken,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
taken.add(variableKey);
|
|
85
|
+
assignments.push({ id: row._id, variableKey: variableKey });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (
|
|
89
|
+
let start: number = 0;
|
|
90
|
+
start < assignments.length;
|
|
91
|
+
start += BACKFILL_BATCH_SIZE
|
|
92
|
+
) {
|
|
93
|
+
const batch: Array<{ id: string; variableKey: string }> = assignments.slice(
|
|
94
|
+
start,
|
|
95
|
+
start + BACKFILL_BATCH_SIZE,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const values: Array<string> = [];
|
|
99
|
+
const parameters: Array<string> = [];
|
|
100
|
+
|
|
101
|
+
for (const assignment of batch) {
|
|
102
|
+
parameters.push(assignment.id, assignment.variableKey);
|
|
103
|
+
values.push(
|
|
104
|
+
`($${parameters.length - 1}::uuid, $${parameters.length}::varchar)`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await queryRunner.query(
|
|
109
|
+
`UPDATE "IncidentCustomField" AS "field" SET "variableKey" = "keys"."variableKey" FROM (VALUES ${values.join(
|
|
110
|
+
", ",
|
|
111
|
+
)}) AS "keys"("_id", "variableKey") WHERE "field"."_id" = "keys"."_id" AND "field"."variableKey" IS NULL`,
|
|
112
|
+
parameters,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return assignments.length;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export class AddIncidentCustomFieldCreateAndNotificationSettings1795800000000
|
|
120
|
+
implements MigrationInterface
|
|
121
|
+
{
|
|
122
|
+
public name: string =
|
|
123
|
+
"AddIncidentCustomFieldCreateAndNotificationSettings1795800000000";
|
|
124
|
+
|
|
125
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
126
|
+
await queryRunner.query(
|
|
127
|
+
`ALTER TABLE "IncidentCustomField" ADD "isRequiredOnCreate" boolean NOT NULL DEFAULT false`,
|
|
128
|
+
);
|
|
129
|
+
await queryRunner.query(
|
|
130
|
+
`ALTER TABLE "IncidentCustomField" ADD "sortOrder" integer`,
|
|
131
|
+
);
|
|
132
|
+
await queryRunner.query(
|
|
133
|
+
`ALTER TABLE "IncidentCustomField" ADD "showOnCreate" boolean NOT NULL DEFAULT false`,
|
|
134
|
+
);
|
|
135
|
+
await queryRunner.query(
|
|
136
|
+
`ALTER TABLE "IncidentCustomField" ADD "includeInSubscriberNotifications" boolean NOT NULL DEFAULT false`,
|
|
137
|
+
);
|
|
138
|
+
await queryRunner.query(
|
|
139
|
+
`ALTER TABLE "IncidentCustomField" ADD "variableKey" character varying(100)`,
|
|
140
|
+
);
|
|
141
|
+
await queryRunner.query(
|
|
142
|
+
`CREATE UNIQUE INDEX "IDX_0a49bcb055baad4356af439169" ON "IncidentCustomField" ("projectId", "variableKey") `,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
await backfillIncidentCustomFieldVariableKeys(queryRunner);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
149
|
+
await queryRunner.query(
|
|
150
|
+
`DROP INDEX "public"."IDX_0a49bcb055baad4356af439169"`,
|
|
151
|
+
);
|
|
152
|
+
await queryRunner.query(
|
|
153
|
+
`ALTER TABLE "IncidentCustomField" DROP COLUMN "variableKey"`,
|
|
154
|
+
);
|
|
155
|
+
await queryRunner.query(
|
|
156
|
+
`ALTER TABLE "IncidentCustomField" DROP COLUMN "includeInSubscriberNotifications"`,
|
|
157
|
+
);
|
|
158
|
+
await queryRunner.query(
|
|
159
|
+
`ALTER TABLE "IncidentCustomField" DROP COLUMN "showOnCreate"`,
|
|
160
|
+
);
|
|
161
|
+
await queryRunner.query(
|
|
162
|
+
`ALTER TABLE "IncidentCustomField" DROP COLUMN "sortOrder"`,
|
|
163
|
+
);
|
|
164
|
+
await queryRunner.query(
|
|
165
|
+
`ALTER TABLE "IncidentCustomField" DROP COLUMN "isRequiredOnCreate"`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* When a subscriber job claimed the 'incident created', 'postmortem' and
|
|
5
|
+
* 'episode created' notifications (SubscriberNotificationClaim), for the
|
|
6
|
+
* sweeper (StatusPageSubscriber:TimeoutStuckNotifications) to time an
|
|
7
|
+
* interrupted send from. It used updatedAt, which other code writes on a
|
|
8
|
+
* schedule while an incident or episode is open - the owners' reminders,
|
|
9
|
+
* state changes, incidents joining an episode - so a send cut off by a
|
|
10
|
+
* redeploy could stay In progress until the incident went quiet.
|
|
11
|
+
*
|
|
12
|
+
* Nullable timestamps with no default: adding them rewrites nothing. A row
|
|
13
|
+
* claimed before they existed has none, and the sweeper times it from
|
|
14
|
+
* updatedAt, as before.
|
|
15
|
+
*/
|
|
16
|
+
export class AddSubscriberNotificationClaimedAt1795900000000
|
|
17
|
+
implements MigrationInterface
|
|
18
|
+
{
|
|
19
|
+
public name: string = "AddSubscriberNotificationClaimedAt1795900000000";
|
|
20
|
+
|
|
21
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
22
|
+
await queryRunner.query(
|
|
23
|
+
`ALTER TABLE "IncidentEpisode" ADD "subscriberNotificationClaimedAtOnEpisodeCreated" TIMESTAMP WITH TIME ZONE`,
|
|
24
|
+
);
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`ALTER TABLE "Incident" ADD "subscriberNotificationClaimedAtOnIncidentCreated" TIMESTAMP WITH TIME ZONE`,
|
|
27
|
+
);
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`ALTER TABLE "Incident" ADD "subscriberNotificationClaimedAtOnPostmortemPublished" TIMESTAMP WITH TIME ZONE`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "Incident" DROP COLUMN "subscriberNotificationClaimedAtOnPostmortemPublished"`,
|
|
36
|
+
);
|
|
37
|
+
await queryRunner.query(
|
|
38
|
+
`ALTER TABLE "Incident" DROP COLUMN "subscriberNotificationClaimedAtOnIncidentCreated"`,
|
|
39
|
+
);
|
|
40
|
+
await queryRunner.query(
|
|
41
|
+
`ALTER TABLE "IncidentEpisode" DROP COLUMN "subscriberNotificationClaimedAtOnEpisodeCreated"`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -12,6 +12,14 @@ import { AddSessionReplaySameOriginTracePropagation1794800000000 } from "./17948
|
|
|
12
12
|
import { AddIncidentAlert1794900000000 } from "./1794900000000-AddIncidentAlert";
|
|
13
13
|
import { AddDatabaseServerTables1795000000000 } from "./1795000000000-AddDatabaseServerTables";
|
|
14
14
|
import { AddUserProjectSsoConsent1795100000000 } from "./1795100000000-AddUserProjectSsoConsent";
|
|
15
|
+
import { AddInventoryItemProjectEntityKeyIndex1795200000000 } from "./1795200000000-AddInventoryItemProjectEntityKeyIndex";
|
|
16
|
+
import { SkipHiddenIncidentCreatedNotifications1795300000000 } from "./1795300000000-SkipHiddenIncidentCreatedNotifications";
|
|
17
|
+
import { AddIncidentStatusPageScope1795400000000 } from "./1795400000000-AddIncidentStatusPageScope";
|
|
18
|
+
import { AddIncidentTemplateStatusPageScopeFlag1795500000000 } from "./1795500000000-AddIncidentTemplateStatusPageScopeFlag";
|
|
19
|
+
import { AddStatusPageSubscriberUnsubscribeToken1795600000000 } from "./1795600000000-AddStatusPageSubscriberUnsubscribeToken";
|
|
20
|
+
import { AddStatusPageSubscriberIsAddedByTeam1795700000000 } from "./1795700000000-AddStatusPageSubscriberIsAddedByTeam";
|
|
21
|
+
import { AddIncidentCustomFieldCreateAndNotificationSettings1795800000000 } from "./1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings";
|
|
22
|
+
import { AddSubscriberNotificationClaimedAt1795900000000 } from "./1795900000000-AddSubscriberNotificationClaimedAt";
|
|
15
23
|
import { AddConfigurableRuleCriteria1792500000000 } from "./1792500000000-AddConfigurableRuleCriteria";
|
|
16
24
|
import { AddGoogleSecOpsDiagnostics1792300000000 } from "./1792300000000-AddGoogleSecOpsDiagnostics";
|
|
17
25
|
import { AddIncomingCallPolicyPhoneNumbers1792600000000 } from "./1792600000000-AddIncomingCallPolicyPhoneNumbers";
|
|
@@ -1224,4 +1232,12 @@ export default [
|
|
|
1224
1232
|
AddIncidentAlert1794900000000,
|
|
1225
1233
|
AddDatabaseServerTables1795000000000,
|
|
1226
1234
|
AddUserProjectSsoConsent1795100000000,
|
|
1235
|
+
AddInventoryItemProjectEntityKeyIndex1795200000000,
|
|
1236
|
+
SkipHiddenIncidentCreatedNotifications1795300000000,
|
|
1237
|
+
AddIncidentStatusPageScope1795400000000,
|
|
1238
|
+
AddIncidentTemplateStatusPageScopeFlag1795500000000,
|
|
1239
|
+
AddStatusPageSubscriberUnsubscribeToken1795600000000,
|
|
1240
|
+
AddStatusPageSubscriberIsAddedByTeam1795700000000,
|
|
1241
|
+
AddIncidentCustomFieldCreateAndNotificationSettings1795800000000,
|
|
1242
|
+
AddSubscriberNotificationClaimedAt1795900000000,
|
|
1227
1243
|
];
|
|
@@ -81,6 +81,12 @@ export default class Semaphore {
|
|
|
81
81
|
acquireTimeout?: number | undefined;
|
|
82
82
|
acquireAttemptsLimit?: number | undefined;
|
|
83
83
|
retryInterval?: number | undefined;
|
|
84
|
+
/*
|
|
85
|
+
* Called when a held permit could not be refreshed and has lapsed. By
|
|
86
|
+
* default redis-semaphore throws from its refresh timer, which nothing
|
|
87
|
+
* can catch; a holder that runs for long should pass one.
|
|
88
|
+
*/
|
|
89
|
+
onLockLost?: ((err: Error) => void) | undefined;
|
|
84
90
|
}): Promise<SemaphorePermit> {
|
|
85
91
|
if (!Number.isInteger(data.limit) || data.limit <= 0) {
|
|
86
92
|
throw new Error("Semaphore permit limit must be a positive integer");
|
|
@@ -108,6 +114,10 @@ export default class Semaphore {
|
|
|
108
114
|
lockOptions.retryInterval = data.retryInterval;
|
|
109
115
|
}
|
|
110
116
|
|
|
117
|
+
if (data.onLockLost) {
|
|
118
|
+
lockOptions.onLockLost = data.onLockLost;
|
|
119
|
+
}
|
|
120
|
+
|
|
111
121
|
const permit: SemaphorePermit = new RedisSemaphore(
|
|
112
122
|
client,
|
|
113
123
|
`${data.namespace}-${data.key}`,
|
|
@@ -74,6 +74,17 @@ import TooManyRequestsException from "../../Types/Exception/TooManyRequestsExcep
|
|
|
74
74
|
* DatabaseNotConnectedException when Redis is down. Logging in is already
|
|
75
75
|
* impossible in that state; the difference this makes is a 503 that says to
|
|
76
76
|
* come back shortly instead of a 500.
|
|
77
|
+
*
|
|
78
|
+
* ONE ROUTE HERE SENDS MAIL RATHER THAN CHECKING A CREDENTIAL
|
|
79
|
+
*
|
|
80
|
+
* POST /resend-verification-email (Authentication.ts) is limited by this
|
|
81
|
+
* middleware too, on a bucket of its own. It is not a guessing oracle against
|
|
82
|
+
* a stored credential -- the credential it takes is server-minted and signed,
|
|
83
|
+
* or a random UUID from an emailed link -- but every request that gets past its
|
|
84
|
+
* checks makes the server send an email, and an anonymous route that sends
|
|
85
|
+
* mail needs a ceiling in front of its database reads and its caps. It shares
|
|
86
|
+
* the fail-closed choice for the same reason: its own cooldown fence lives in
|
|
87
|
+
* Redis, so with Redis down it could not bound anything either.
|
|
77
88
|
*/
|
|
78
89
|
|
|
79
90
|
/*
|
|
@@ -144,6 +155,15 @@ export enum IdentityRateLimitBucket {
|
|
|
144
155
|
|
|
145
156
|
// Discoverable passkey requests have no account identifier until verified.
|
|
146
157
|
Passkey = "passkey",
|
|
158
|
+
|
|
159
|
+
/*
|
|
160
|
+
* POST /resend-verification-email -- an unverified account asking for its
|
|
161
|
+
* verification link again. Mail, not guessing: see
|
|
162
|
+
* VERIFICATION_EMAIL_RESEND_BUCKET. Its own counter so that a user mashing
|
|
163
|
+
* "resend" cannot spend the sign-in attempts they are about to need, and so
|
|
164
|
+
* that sign-in noise cannot refuse the resend.
|
|
165
|
+
*/
|
|
166
|
+
VerificationEmailResend = "verification-email-resend",
|
|
147
167
|
}
|
|
148
168
|
|
|
149
169
|
export enum IdentityRateLimitScope {
|
|
@@ -413,6 +433,55 @@ const PASSKEY_BUCKET: BucketConfig = {
|
|
|
413
433
|
perIpLimit: PASSKEY_RATE_LIMIT,
|
|
414
434
|
};
|
|
415
435
|
|
|
436
|
+
/*
|
|
437
|
+
* Verification email resend budget.
|
|
438
|
+
*
|
|
439
|
+
* Shaped like the passkey bucket, and for the same reason: the request carries
|
|
440
|
+
* no email address (it carries a signed resend token or a verification link
|
|
441
|
+
* token instead), so resolveAccountKey answers "none" and the account counter
|
|
442
|
+
* is keyed on the client address alone. Both counters are therefore the same
|
|
443
|
+
* per-address budget and are given the same number; a caller who adds an
|
|
444
|
+
* `email` field to rotate the account key still meets the address ceiling.
|
|
445
|
+
*
|
|
446
|
+
* This is NOT what bounds mail to any one inbox. The route's own caps do that
|
|
447
|
+
* per account -- a cooldown between sends, a per-hour cap, and a small fixed
|
|
448
|
+
* number of sends per credential (VerificationEmailResendPolicy) -- and those
|
|
449
|
+
* hold however many addresses a caller has. What this bounds is how much work
|
|
450
|
+
* one address can make the route do before those caps are even consulted:
|
|
451
|
+
* token verification, a verification-token lookup, a user lookup and a Redis
|
|
452
|
+
* read per request.
|
|
453
|
+
*
|
|
454
|
+
* Twenty a quarter hour is far past an honest user, who presses the button a
|
|
455
|
+
* handful of times at most because the page itself counts the cooldown down,
|
|
456
|
+
* and still generous to an office behind one NAT where several people are
|
|
457
|
+
* finishing signup at once.
|
|
458
|
+
*/
|
|
459
|
+
const VERIFICATION_EMAIL_RESEND_RATE_LIMIT: number = parsePositiveIntFromEnv(
|
|
460
|
+
"IDENTITY_VERIFICATION_EMAIL_RESEND_RATE_LIMIT_PER_IP_PER_WINDOW",
|
|
461
|
+
20,
|
|
462
|
+
);
|
|
463
|
+
const VERIFICATION_EMAIL_RESEND_BUCKET: BucketConfig = {
|
|
464
|
+
windowSeconds: parsePositiveIntFromEnv(
|
|
465
|
+
"IDENTITY_VERIFICATION_EMAIL_RESEND_RATE_LIMIT_WINDOW_SECONDS",
|
|
466
|
+
15 * 60,
|
|
467
|
+
),
|
|
468
|
+
perAccountLimit: VERIFICATION_EMAIL_RESEND_RATE_LIMIT,
|
|
469
|
+
perIpLimit: VERIFICATION_EMAIL_RESEND_RATE_LIMIT,
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
/*
|
|
473
|
+
* What a refused caller is told. See getRefusalMessages for why the sign-in
|
|
474
|
+
* routes all share one pair and the resend route has its own.
|
|
475
|
+
*/
|
|
476
|
+
export const IDENTITY_RATE_LIMITED_MESSAGE: string =
|
|
477
|
+
"Too many sign-in attempts. Please try again later.";
|
|
478
|
+
export const IDENTITY_COUNTER_UNAVAILABLE_MESSAGE: string =
|
|
479
|
+
"Unable to sign you in right now. Please try again shortly.";
|
|
480
|
+
export const VERIFICATION_EMAIL_RESEND_RATE_LIMITED_MESSAGE: string =
|
|
481
|
+
"Too many requests for a new verification email. Please try again later.";
|
|
482
|
+
export const VERIFICATION_EMAIL_RESEND_UNAVAILABLE_MESSAGE: string =
|
|
483
|
+
"Unable to send a verification email right now. Please try again shortly.";
|
|
484
|
+
|
|
416
485
|
const KEY_PREFIX: string = "identity:rl:";
|
|
417
486
|
|
|
418
487
|
/*
|
|
@@ -574,9 +643,44 @@ export default class IdentityRateLimit {
|
|
|
574
643
|
return STATUS_PAGE_LOGIN_BUCKET;
|
|
575
644
|
}
|
|
576
645
|
|
|
646
|
+
if (bucket === IdentityRateLimitBucket.VerificationEmailResend) {
|
|
647
|
+
return VERIFICATION_EMAIL_RESEND_BUCKET;
|
|
648
|
+
}
|
|
649
|
+
|
|
577
650
|
return LOGIN_BUCKET;
|
|
578
651
|
}
|
|
579
652
|
|
|
653
|
+
/*
|
|
654
|
+
* What a refused request is told, for the 429 and for the fail-closed 503.
|
|
655
|
+
*
|
|
656
|
+
* Every sign-in bucket gets the same pair, byte for byte, whichever counter
|
|
657
|
+
* fired and whichever route was hit -- see the note in getMiddleware on why
|
|
658
|
+
* a sign-in limiter must not answer differently for different accounts.
|
|
659
|
+
*
|
|
660
|
+
* The resend route gets its own pair because the sign-in wording is simply
|
|
661
|
+
* wrong there: somebody who pressed "resend verification email" has made no
|
|
662
|
+
* sign-in attempt, and telling them they made too many would send them
|
|
663
|
+
* looking for a problem they do not have. The pair is still uniform WITHIN
|
|
664
|
+
* the route: the same text for every caller, token and account, so it
|
|
665
|
+
* discloses nothing the sign-in pair does not.
|
|
666
|
+
*/
|
|
667
|
+
public static getRefusalMessages(bucket: IdentityRateLimitBucket): {
|
|
668
|
+
rateLimited: string;
|
|
669
|
+
unavailable: string;
|
|
670
|
+
} {
|
|
671
|
+
if (bucket === IdentityRateLimitBucket.VerificationEmailResend) {
|
|
672
|
+
return {
|
|
673
|
+
rateLimited: VERIFICATION_EMAIL_RESEND_RATE_LIMITED_MESSAGE,
|
|
674
|
+
unavailable: VERIFICATION_EMAIL_RESEND_UNAVAILABLE_MESSAGE,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
return {
|
|
679
|
+
rateLimited: IDENTITY_RATE_LIMITED_MESSAGE,
|
|
680
|
+
unavailable: IDENTITY_COUNTER_UNAVAILABLE_MESSAGE,
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
|
|
580
684
|
/*
|
|
581
685
|
* Increment both counters for this request and decide.
|
|
582
686
|
*
|
|
@@ -773,13 +877,14 @@ export default class IdentityRateLimit {
|
|
|
773
877
|
* account exists. These routes are careful not to say which half of a
|
|
774
878
|
* credential was wrong; a limiter that answered differently for a real
|
|
775
879
|
* address than for an invented one would hand back the account
|
|
776
|
-
* enumeration the handlers withhold.
|
|
880
|
+
* enumeration the handlers withhold. (The resend route has wording of
|
|
881
|
+
* its own, but it is just as uniform; see getRefusalMessages.)
|
|
777
882
|
*/
|
|
778
883
|
return Response.sendErrorResponse(
|
|
779
884
|
req,
|
|
780
885
|
res,
|
|
781
886
|
new TooManyRequestsException(
|
|
782
|
-
|
|
887
|
+
IdentityRateLimit.getRefusalMessages(bucket).rateLimited,
|
|
783
888
|
),
|
|
784
889
|
);
|
|
785
890
|
}
|
|
@@ -800,7 +905,7 @@ export default class IdentityRateLimit {
|
|
|
800
905
|
req,
|
|
801
906
|
res,
|
|
802
907
|
new ServiceUnavailableException(
|
|
803
|
-
|
|
908
|
+
IdentityRateLimit.getRefusalMessages(bucket).unavailable,
|
|
804
909
|
),
|
|
805
910
|
);
|
|
806
911
|
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import Redis, { ClientType } from "../Infrastructure/Redis";
|
|
2
|
+
import {
|
|
3
|
+
ExpressRequest,
|
|
4
|
+
ExpressResponse,
|
|
5
|
+
NextFunction,
|
|
6
|
+
} from "../Utils/Express";
|
|
7
|
+
import logger from "../Utils/Logger";
|
|
8
|
+
import Response from "../Utils/Response";
|
|
9
|
+
import TooManyRequestsException from "../../Types/Exception/TooManyRequestsException";
|
|
10
|
+
import SubscriberNotificationTestSendRateLimit from "./SubscriberNotificationTestSendRateLimit";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* Request limiting for "Preview notification" (POST /preview on the
|
|
14
|
+
* subscriber notification preview router).
|
|
15
|
+
*
|
|
16
|
+
* A preview sends nothing, but it is not free: it renders the incident's
|
|
17
|
+
* description and custom fields once, then an email for every status page
|
|
18
|
+
* the caller can read, and returns all of that HTML in one answer. Any
|
|
19
|
+
* project member holding one of the audience roles may call it, as often as
|
|
20
|
+
* they like, so this bounds how much rendering one person can ask the App
|
|
21
|
+
* server for. The request itself is bounded too
|
|
22
|
+
* (SubscriberNotificationPreviewBuilder.parseRequest caps the text and the
|
|
23
|
+
* custom field values).
|
|
24
|
+
*
|
|
25
|
+
* The limit is loose: someone writing an incident or a note and previewing
|
|
26
|
+
* it after each change is nowhere near it. The counter is per user, across
|
|
27
|
+
* every project and incident, like the test email's
|
|
28
|
+
* (SubscriberNotificationTestSendRateLimit, whose user key this shares).
|
|
29
|
+
*
|
|
30
|
+
* THIS FAILS OPEN
|
|
31
|
+
*
|
|
32
|
+
* If Redis is unreachable the preview is served. That is the opposite of the
|
|
33
|
+
* test email's choice, and the difference is what is at stake: a test email
|
|
34
|
+
* is real mail, and this counter would be the only bound on it; a preview
|
|
35
|
+
* sends nothing, its request is size-capped, and refusing it would take the
|
|
36
|
+
* preview away from everyone for as long as Redis is down.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
export enum SubscriberNotificationPreviewRateLimitOutcome {
|
|
40
|
+
Allowed = "allowed",
|
|
41
|
+
RateLimited = "rate-limited",
|
|
42
|
+
|
|
43
|
+
// Redis is unreachable: served, since this fails open.
|
|
44
|
+
CounterUnavailable = "counter-unavailable",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface SubscriberNotificationPreviewRateLimitDecision {
|
|
48
|
+
outcome: SubscriberNotificationPreviewRateLimitOutcome;
|
|
49
|
+
retryAfterSeconds?: number | undefined;
|
|
50
|
+
// True only on the request that first crossed the line in this window.
|
|
51
|
+
isFirstRejectionInWindow?: boolean | undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SubscriberNotificationPreviewRateLimitConfig {
|
|
55
|
+
windowSeconds: number;
|
|
56
|
+
perUserLimit: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const KEY_PREFIX: string = "subnotifpreview:rl:";
|
|
60
|
+
|
|
61
|
+
// Counter keys outlive their window by one full window.
|
|
62
|
+
const TTL_MULTIPLIER: number = 2;
|
|
63
|
+
|
|
64
|
+
// How often the "counter unavailable" condition may be logged.
|
|
65
|
+
const COUNTER_UNAVAILABLE_LOG_INTERVAL_MS: number = 60 * 1000;
|
|
66
|
+
|
|
67
|
+
export default class SubscriberNotificationPreviewRateLimit {
|
|
68
|
+
// Sixty previews every ten minutes: one every ten seconds, sustained.
|
|
69
|
+
private static readonly config: SubscriberNotificationPreviewRateLimitConfig =
|
|
70
|
+
{
|
|
71
|
+
windowSeconds: 10 * 60,
|
|
72
|
+
perUserLimit: 60,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
private static counterUnavailableLastLoggedAt: number = 0;
|
|
76
|
+
|
|
77
|
+
// The limit in force, for tests and for operators' diagnostics.
|
|
78
|
+
public static getConfig(): SubscriberNotificationPreviewRateLimitConfig {
|
|
79
|
+
return { ...SubscriberNotificationPreviewRateLimit.config };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/*
|
|
83
|
+
* Count this request against the user and decide. A rejected request is
|
|
84
|
+
* counted too, so a caller who keeps trying keeps their window pinned.
|
|
85
|
+
*/
|
|
86
|
+
public static async consume(data: {
|
|
87
|
+
userKey: string;
|
|
88
|
+
}): Promise<SubscriberNotificationPreviewRateLimitDecision> {
|
|
89
|
+
const client: ClientType | null = Redis.getClient();
|
|
90
|
+
|
|
91
|
+
if (!client || !Redis.isConnected()) {
|
|
92
|
+
return {
|
|
93
|
+
outcome:
|
|
94
|
+
SubscriberNotificationPreviewRateLimitOutcome.CounterUnavailable,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const config: SubscriberNotificationPreviewRateLimitConfig =
|
|
99
|
+
SubscriberNotificationPreviewRateLimit.config;
|
|
100
|
+
const windowMs: number = config.windowSeconds * 1000;
|
|
101
|
+
const windowIndex: number = Math.floor(Date.now() / windowMs);
|
|
102
|
+
const counterKey: string = `${KEY_PREFIX}u:${data.userKey}:${windowIndex}`;
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const count: number = await client.incr(counterKey);
|
|
106
|
+
|
|
107
|
+
if (typeof count !== "number") {
|
|
108
|
+
throw new Error("Rate limit counter returned a non-numeric value");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Set only by the request that created the key, so the window is fixed.
|
|
112
|
+
if (count === 1) {
|
|
113
|
+
await client.expire(counterKey, config.windowSeconds * TTL_MULTIPLIER);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (count > config.perUserLimit) {
|
|
117
|
+
return {
|
|
118
|
+
outcome: SubscriberNotificationPreviewRateLimitOutcome.RateLimited,
|
|
119
|
+
retryAfterSeconds:
|
|
120
|
+
SubscriberNotificationTestSendRateLimit.getSecondsUntilWindowEnd(
|
|
121
|
+
config.windowSeconds,
|
|
122
|
+
),
|
|
123
|
+
isFirstRejectionInWindow: count === config.perUserLimit + 1,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
outcome: SubscriberNotificationPreviewRateLimitOutcome.Allowed,
|
|
129
|
+
};
|
|
130
|
+
} catch (err) {
|
|
131
|
+
if (
|
|
132
|
+
SubscriberNotificationPreviewRateLimit.shouldLogCounterUnavailable()
|
|
133
|
+
) {
|
|
134
|
+
logger.warn(
|
|
135
|
+
`SubscriberNotificationPreviewRateLimit: counter failed for user ${data.userKey}`,
|
|
136
|
+
);
|
|
137
|
+
logger.warn(err);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
outcome:
|
|
142
|
+
SubscriberNotificationPreviewRateLimitOutcome.CounterUnavailable,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/*
|
|
148
|
+
* Express middleware. Register after UserMiddleware (the counter is per
|
|
149
|
+
* user) and before the handler, so a flood is refused before it renders
|
|
150
|
+
* anything.
|
|
151
|
+
*/
|
|
152
|
+
public static getMiddleware(): (
|
|
153
|
+
req: ExpressRequest,
|
|
154
|
+
res: ExpressResponse,
|
|
155
|
+
next: NextFunction,
|
|
156
|
+
) => Promise<void> {
|
|
157
|
+
return async (
|
|
158
|
+
req: ExpressRequest,
|
|
159
|
+
res: ExpressResponse,
|
|
160
|
+
next: NextFunction,
|
|
161
|
+
): Promise<void> => {
|
|
162
|
+
const userKey: string =
|
|
163
|
+
SubscriberNotificationTestSendRateLimit.resolveUserKey(req);
|
|
164
|
+
|
|
165
|
+
const decision: SubscriberNotificationPreviewRateLimitDecision =
|
|
166
|
+
await SubscriberNotificationPreviewRateLimit.consume({
|
|
167
|
+
userKey: userKey,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (
|
|
171
|
+
decision.outcome ===
|
|
172
|
+
SubscriberNotificationPreviewRateLimitOutcome.RateLimited
|
|
173
|
+
) {
|
|
174
|
+
if (decision.retryAfterSeconds) {
|
|
175
|
+
const setHeader: unknown = (
|
|
176
|
+
res as unknown as Record<string, unknown>
|
|
177
|
+
)["setHeader"];
|
|
178
|
+
|
|
179
|
+
if (typeof setHeader === "function") {
|
|
180
|
+
(setHeader as (name: string, value: string) => void).call(
|
|
181
|
+
res,
|
|
182
|
+
"Retry-After",
|
|
183
|
+
String(decision.retryAfterSeconds),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (decision.isFirstRejectionInWindow) {
|
|
189
|
+
logger.warn(
|
|
190
|
+
`SubscriberNotificationPreviewRateLimit: rejected previews from user ${userKey} for the rest of this window`,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return Response.sendErrorResponse(
|
|
195
|
+
req,
|
|
196
|
+
res,
|
|
197
|
+
new TooManyRequestsException(
|
|
198
|
+
"You have previewed notifications too many times in a short while. Please try again in a few minutes.",
|
|
199
|
+
),
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (
|
|
204
|
+
decision.outcome ===
|
|
205
|
+
SubscriberNotificationPreviewRateLimitOutcome.CounterUnavailable &&
|
|
206
|
+
SubscriberNotificationPreviewRateLimit.shouldLogCounterUnavailable()
|
|
207
|
+
) {
|
|
208
|
+
// Fails open: see the note at the top of this file.
|
|
209
|
+
logger.warn(
|
|
210
|
+
"SubscriberNotificationPreviewRateLimit: rate limit counter unavailable, serving previews without a limit",
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return next();
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/*
|
|
219
|
+
* A Redis outage means every request takes the unavailable path; one log
|
|
220
|
+
* line per interval makes the condition visible without burying the rest.
|
|
221
|
+
*/
|
|
222
|
+
private static shouldLogCounterUnavailable(): boolean {
|
|
223
|
+
const now: number = Date.now();
|
|
224
|
+
|
|
225
|
+
if (
|
|
226
|
+
now -
|
|
227
|
+
SubscriberNotificationPreviewRateLimit.counterUnavailableLastLoggedAt <
|
|
228
|
+
COUNTER_UNAVAILABLE_LOG_INTERVAL_MS
|
|
229
|
+
) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
SubscriberNotificationPreviewRateLimit.counterUnavailableLastLoggedAt = now;
|
|
234
|
+
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
}
|