@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.
Files changed (556) hide show
  1. package/Models/DatabaseModels/Incident.ts +267 -1
  2. package/Models/DatabaseModels/IncidentCustomField.ts +202 -0
  3. package/Models/DatabaseModels/IncidentEpisode.ts +30 -0
  4. package/Models/DatabaseModels/IncidentTemplate.ts +126 -1
  5. package/Models/DatabaseModels/InventoryItem.ts +23 -12
  6. package/Models/DatabaseModels/StatusPage.ts +55 -0
  7. package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
  8. package/Server/API/IncidentAPI.ts +162 -0
  9. package/Server/API/StatusPageAPI.ts +487 -171
  10. package/Server/API/StatusPageSubscriberAPI.ts +63 -15
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.ts +217 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
  20. package/Server/Infrastructure/Semaphore.ts +10 -0
  21. package/Server/Middleware/IdentityRateLimit.ts +108 -3
  22. package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
  23. package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
  24. package/Server/Services/AlertService.ts +32 -36
  25. package/Server/Services/DatabaseService.ts +93 -10
  26. package/Server/Services/IncidentAlertService.ts +609 -2
  27. package/Server/Services/IncidentCustomFieldService.ts +276 -1
  28. package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
  29. package/Server/Services/IncidentEpisodeService.ts +15 -0
  30. package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
  31. package/Server/Services/IncidentPublicNoteService.ts +30 -1
  32. package/Server/Services/IncidentService.ts +1663 -103
  33. package/Server/Services/IncidentStateTimelineService.ts +13 -0
  34. package/Server/Services/IncidentTemplateService.ts +109 -0
  35. package/Server/Services/InventoryItemService.ts +38 -0
  36. package/Server/Services/IoTDeviceCredentialService.ts +13 -4
  37. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
  38. package/Server/Services/ScheduledMaintenanceService.ts +118 -64
  39. package/Server/Services/SmsService.ts +10 -0
  40. package/Server/Services/StatusPageService.ts +65 -8
  41. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
  42. package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
  43. package/Server/Services/StatusPageSubscriberService.ts +1161 -64
  44. package/Server/Services/UserNotificationRuleService.ts +55 -53
  45. package/Server/Services/WorkspaceNotificationSummaryService.ts +38 -20
  46. package/Server/Types/Markdown.ts +153 -6
  47. package/Server/Types/Workflow/Components/Email.ts +61 -2
  48. package/Server/Utils/AffectedResources/LinkedAffectedResources.ts +576 -0
  49. package/Server/Utils/Alert/AlertStateChangeAuthorization.ts +129 -0
  50. package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
  51. package/Server/Utils/DataSource/EgressGuard.ts +66 -21
  52. package/Server/Utils/Inventory/InventoryOverviewAggregation.ts +111 -0
  53. package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
  54. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
  55. package/Server/Utils/Response.ts +31 -0
  56. package/Server/Utils/SSRFProtection.ts +76 -16
  57. package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
  58. package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
  59. package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
  60. package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
  61. package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
  62. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
  63. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
  64. package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
  65. package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
  66. package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
  67. package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
  68. package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
  69. package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
  70. package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
  71. package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
  72. package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
  73. package/Server/Utils/StatusPageResource.ts +51 -20
  74. package/Server/Utils/Topology/TopologyConcurrencyLimiter.ts +229 -0
  75. package/Server/Utils/Topology/TopologyQueries.ts +1350 -0
  76. package/Server/Utils/Topology/TopologyRequest.ts +463 -0
  77. package/Server/Utils/Topology/TopologyResponseCache.ts +183 -0
  78. package/Server/Utils/Topology/TopologySql.ts +1024 -0
  79. package/Server/Utils/VM/VMAPI.ts +1 -0
  80. package/Server/Utils/VM/VMRunner.ts +10 -0
  81. package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
  82. package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
  83. package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
  84. package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
  85. package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
  86. package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
  87. package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
  88. package/Tests/App/Dashboard/AcknowledgeAlertsOnDeclare.test.ts +369 -0
  89. package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +1 -1
  90. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +510 -123
  91. package/Tests/App/Dashboard/AlertsTableAffectedResources.test.tsx +490 -0
  92. package/Tests/App/Dashboard/ChangeAlertStateDeclareIncident.test.tsx +886 -0
  93. package/Tests/App/Dashboard/CreateReviewStepRoleSummary.test.tsx +1250 -0
  94. package/Tests/App/Dashboard/CreateReviewStepSelectionSummary.test.tsx +738 -0
  95. package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
  96. package/Tests/App/Dashboard/DeclareIncidentFromAlert.test.ts +417 -0
  97. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +1948 -229
  98. package/Tests/App/Dashboard/EntityDetailPanelLifecycle.test.tsx +493 -0
  99. package/Tests/App/Dashboard/EntityDetailPanelNavigation.test.tsx +439 -0
  100. package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
  101. package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
  102. package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
  103. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +534 -6
  104. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +798 -2
  105. package/Tests/App/Dashboard/FetchStateAndSeverityElements.test.tsx +669 -0
  106. package/Tests/App/Dashboard/FetchUsers.test.tsx +230 -0
  107. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
  108. package/Tests/App/Dashboard/IncidentCreateAcknowledgeAlerts.test.tsx +1611 -0
  109. package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
  110. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
  111. package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
  112. package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
  113. package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
  114. package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
  115. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
  116. package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
  117. package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
  118. package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
  119. package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
  120. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +1365 -57
  121. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +964 -23
  122. package/Tests/App/Dashboard/InventoryRelationships.test.tsx +885 -0
  123. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
  124. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +201 -15
  125. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +508 -36
  126. package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
  127. package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
  128. package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
  129. package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
  130. package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
  131. package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
  132. package/Tests/App/Dashboard/TopologyDataLifecycle.test.tsx +583 -0
  133. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +982 -218
  134. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +818 -114
  135. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
  136. package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
  137. package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
  138. package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
  139. package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
  140. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
  141. package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
  142. package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
  143. package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
  144. package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
  145. package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
  146. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
  147. package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
  148. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
  149. package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
  150. package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
  151. package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
  152. package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
  153. package/Tests/Server/Infrastructure/Postgres/RenameInventoryItemMigration.test.ts +90 -21
  154. package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
  155. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
  156. package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
  157. package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
  158. package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
  159. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
  160. package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
  161. package/Tests/Server/Services/DomainService.test.ts +525 -0
  162. package/Tests/Server/Services/IncidentAlertAcknowledgeOnDeclare.test.ts +2630 -0
  163. package/Tests/Server/Services/IncidentAlertPostgres.test.ts +1625 -1
  164. package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
  165. package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
  166. package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
  167. package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
  168. package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
  169. package/Tests/Server/Services/IncidentDeclareAcknowledgeAlerts.test.ts +2189 -0
  170. package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
  171. package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
  172. package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
  173. package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
  174. package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
  175. package/Tests/Server/Services/InventoryItemServiceOverviewCounts.test.ts +345 -0
  176. package/Tests/Server/Services/InventoryOverviewPostgres.test.ts +254 -0
  177. package/Tests/Server/Services/IoTDeviceCredentialService.test.ts +819 -0
  178. package/Tests/Server/Services/LinkedAffectedResourcesCreatedFeed.test.ts +420 -0
  179. package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
  180. package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
  181. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
  182. package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +92 -36
  183. package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
  184. package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
  185. package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
  186. package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
  187. package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
  188. package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
  189. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
  190. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
  191. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
  192. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
  193. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
  194. package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
  195. package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
  196. package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
  197. package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
  198. package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
  199. package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
  200. package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
  201. package/Tests/Server/Utils/AffectedResources/LinkedAffectedResources.test.ts +858 -0
  202. package/Tests/Server/Utils/Alert/AlertStateChangeAuthorization.test.ts +1172 -0
  203. package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
  204. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
  205. package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
  206. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
  207. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
  208. package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
  209. package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
  210. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
  211. package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
  212. package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
  213. package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
  214. package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
  215. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
  216. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
  217. package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
  218. package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
  219. package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
  220. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
  221. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
  222. package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
  223. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
  224. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
  225. package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
  226. package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
  227. package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
  228. package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
  229. package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
  230. package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
  231. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigration.test.ts +386 -0
  232. package/Tests/Server/Utils/Topology/InventoryItemEntityKeyIndexMigrationPostgres.test.ts +430 -0
  233. package/Tests/Server/Utils/Topology/TopologyActivityParityPostgres.test.ts +279 -0
  234. package/Tests/Server/Utils/Topology/TopologyConcurrencyLimiter.test.ts +534 -0
  235. package/Tests/Server/Utils/Topology/TopologyEstateGenerator.ts +624 -0
  236. package/Tests/Server/Utils/Topology/TopologyJsonResponse.test.ts +90 -0
  237. package/Tests/Server/Utils/Topology/TopologyQueries.test.ts +1634 -0
  238. package/Tests/Server/Utils/Topology/TopologyQueriesDifferentialPostgres.test.ts +683 -0
  239. package/Tests/Server/Utils/Topology/TopologyQueriesPostgres.test.ts +2624 -0
  240. package/Tests/Server/Utils/Topology/TopologyReference.ts +1018 -0
  241. package/Tests/Server/Utils/Topology/TopologyRequest.test.ts +643 -0
  242. package/Tests/Server/Utils/Topology/TopologyResponseCache.test.ts +305 -0
  243. package/Tests/Server/Utils/Topology/TopologySql.test.ts +973 -0
  244. package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
  245. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
  246. package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
  247. package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
  248. package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
  249. package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
  250. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
  251. package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
  252. package/Tests/Types/SafeHtml.test.ts +102 -0
  253. package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
  254. package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
  255. package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
  256. package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
  257. package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
  258. package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
  259. package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
  260. package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
  261. package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
  262. package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
  263. package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
  264. package/Tests/Types/Topology/TopologyTypeRules.test.ts +176 -0
  265. package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
  266. package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
  267. package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
  268. package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
  269. package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
  270. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
  271. package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
  272. package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +302 -0
  273. package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
  274. package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
  275. package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
  276. package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
  277. package/Types/CustomField/CustomFieldOrder.ts +55 -0
  278. package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
  279. package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
  280. package/Types/CustomField/CustomFieldType.ts +14 -0
  281. package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
  282. package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
  283. package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
  284. package/Types/Incident/IncidentAlertLink.ts +19 -0
  285. package/Types/SafeHtml.ts +93 -0
  286. package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
  287. package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
  288. package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
  289. package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
  290. package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
  291. package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
  292. package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
  293. package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
  294. package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
  295. package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
  296. package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
  297. package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
  298. package/Types/Telemetry/InventoryLiveness.ts +26 -0
  299. package/Types/Topology/TopologyApi.ts +428 -0
  300. package/Types/Topology/TopologyTypeRules.ts +191 -0
  301. package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
  302. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
  303. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
  304. package/UI/Components/Forms/ModelForm.tsx +70 -8
  305. package/UI/Components/Forms/Validation.ts +1 -1
  306. package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
  307. package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
  308. package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
  309. package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
  310. package/UI/Components/ModelTable/ModelTable.tsx +6 -7
  311. package/UI/Utils/SensitiveUrlToken.ts +10 -5
  312. package/Utils/API.ts +247 -2
  313. package/Utils/Incident/IncidentFromAlerts.ts +78 -0
  314. package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
  315. package/Utils/Markdown/MarkdownEscape.ts +65 -0
  316. package/Utils/ProbeNetworkFailureUtil.ts +673 -0
  317. package/build/dist/Models/DatabaseModels/Incident.js +267 -1
  318. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  319. package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
  320. package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
  321. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
  322. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  323. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
  324. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  325. package/build/dist/Models/DatabaseModels/InventoryItem.js +20 -11
  326. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
  327. package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
  328. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  329. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
  330. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  331. package/build/dist/Server/API/IncidentAPI.js +100 -1
  332. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  333. package/build/dist/Server/API/StatusPageAPI.js +417 -176
  334. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  335. package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
  336. package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
  337. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js +165 -0
  338. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795200000000-AddInventoryItemProjectEntityKeyIndex.js.map +1 -0
  339. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
  340. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
  341. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
  342. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
  343. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
  344. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
  345. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
  346. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
  347. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
  348. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
  349. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
  350. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
  351. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
  352. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
  353. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
  354. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  355. package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
  356. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  357. package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
  358. package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
  359. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
  360. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
  361. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
  362. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
  363. package/build/dist/Server/Services/AlertService.js +21 -33
  364. package/build/dist/Server/Services/AlertService.js.map +1 -1
  365. package/build/dist/Server/Services/DatabaseService.js +56 -7
  366. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  367. package/build/dist/Server/Services/IncidentAlertService.js +457 -3
  368. package/build/dist/Server/Services/IncidentAlertService.js.map +1 -1
  369. package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
  370. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  371. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
  372. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  373. package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
  374. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  375. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
  376. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  377. package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
  378. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  379. package/build/dist/Server/Services/IncidentService.js +1148 -42
  380. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  381. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  382. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  383. package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
  384. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  385. package/build/dist/Server/Services/InventoryItemService.js +31 -0
  386. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  387. package/build/dist/Server/Services/IoTDeviceCredentialService.js +12 -5
  388. package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -1
  389. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
  390. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  391. package/build/dist/Server/Services/ScheduledMaintenanceService.js +84 -57
  392. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  393. package/build/dist/Server/Services/SmsService.js +2 -22
  394. package/build/dist/Server/Services/SmsService.js.map +1 -1
  395. package/build/dist/Server/Services/StatusPageService.js +47 -9
  396. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  397. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
  398. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  399. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
  400. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
  401. package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
  402. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  403. package/build/dist/Server/Services/UserNotificationRuleService.js +55 -51
  404. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  405. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +42 -25
  406. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
  407. package/build/dist/Server/Types/Markdown.js +110 -6
  408. package/build/dist/Server/Types/Markdown.js.map +1 -1
  409. package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
  410. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  411. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js +405 -0
  412. package/build/dist/Server/Utils/AffectedResources/LinkedAffectedResources.js.map +1 -0
  413. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js +112 -0
  414. package/build/dist/Server/Utils/Alert/AlertStateChangeAuthorization.js.map +1 -0
  415. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
  416. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
  417. package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
  418. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  419. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js +81 -0
  420. package/build/dist/Server/Utils/Inventory/InventoryOverviewAggregation.js.map +1 -0
  421. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
  422. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
  423. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
  424. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  425. package/build/dist/Server/Utils/Response.js +30 -0
  426. package/build/dist/Server/Utils/Response.js.map +1 -1
  427. package/build/dist/Server/Utils/SSRFProtection.js +48 -6
  428. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  429. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
  430. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
  431. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
  432. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
  433. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
  434. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
  435. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
  436. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
  437. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
  438. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
  439. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
  440. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
  441. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
  442. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
  443. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
  444. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
  445. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
  446. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
  447. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
  448. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
  449. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
  450. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
  451. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
  452. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
  453. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
  454. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
  455. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
  456. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
  457. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
  458. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
  459. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
  460. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
  461. package/build/dist/Server/Utils/StatusPageResource.js +39 -7
  462. package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
  463. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js +178 -0
  464. package/build/dist/Server/Utils/Topology/TopologyConcurrencyLimiter.js.map +1 -0
  465. package/build/dist/Server/Utils/Topology/TopologyQueries.js +884 -0
  466. package/build/dist/Server/Utils/Topology/TopologyQueries.js.map +1 -0
  467. package/build/dist/Server/Utils/Topology/TopologyRequest.js +275 -0
  468. package/build/dist/Server/Utils/Topology/TopologyRequest.js.map +1 -0
  469. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js +146 -0
  470. package/build/dist/Server/Utils/Topology/TopologyResponseCache.js.map +1 -0
  471. package/build/dist/Server/Utils/Topology/TopologySql.js +688 -0
  472. package/build/dist/Server/Utils/Topology/TopologySql.js.map +1 -0
  473. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  474. package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
  475. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  476. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
  477. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
  478. package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
  479. package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
  480. package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
  481. package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
  482. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
  483. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
  484. package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
  485. package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
  486. package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
  487. package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
  488. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
  489. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
  490. package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
  491. package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
  492. package/build/dist/Types/Incident/IncidentAlertLink.js +17 -0
  493. package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -1
  494. package/build/dist/Types/SafeHtml.js +84 -0
  495. package/build/dist/Types/SafeHtml.js.map +1 -0
  496. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
  497. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
  498. package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
  499. package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
  500. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
  501. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
  502. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
  503. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
  504. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
  505. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
  506. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
  507. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
  508. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
  509. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
  510. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
  511. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
  512. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
  513. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
  514. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
  515. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
  516. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
  517. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
  518. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
  519. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
  520. package/build/dist/Types/Telemetry/InventoryLiveness.js +23 -0
  521. package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -1
  522. package/build/dist/Types/Topology/TopologyApi.js +55 -0
  523. package/build/dist/Types/Topology/TopologyApi.js.map +1 -0
  524. package/build/dist/Types/Topology/TopologyTypeRules.js +165 -0
  525. package/build/dist/Types/Topology/TopologyTypeRules.js.map +1 -0
  526. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
  527. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
  528. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
  529. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
  530. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
  531. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  532. package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
  533. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  534. package/build/dist/UI/Components/Forms/Validation.js +1 -1
  535. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  536. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  537. package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
  538. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
  539. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  540. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
  541. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  542. package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
  543. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  544. package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
  545. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
  546. package/build/dist/Utils/API.js +177 -1
  547. package/build/dist/Utils/API.js.map +1 -1
  548. package/build/dist/Utils/Incident/IncidentFromAlerts.js +38 -0
  549. package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -1
  550. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
  551. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
  552. package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
  553. package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
  554. package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
  555. package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
  556. package/package.json +1 -1
@@ -105,6 +105,9 @@ import StatusPageSubscriberNotificationEventType from "../../Types/StatusPage/St
105
105
  import StatusPageSubscriberNotificationMethod from "../../Types/StatusPage/StatusPageSubscriberNotificationMethod";
106
106
  import StatusPageSubscriberNotificationTemplateService, { Service as StatusPageSubscriberNotificationTemplateServiceClass, } from "../Services/StatusPageSubscriberNotificationTemplateService";
107
107
  import { canServeStatusPageCustomizations } from "../Utils/StatusPageCustomizationAccess";
108
+ import IncidentStatusPageScope, { INCIDENT_SCOPE_SELECT, } from "../Utils/StatusPage/IncidentStatusPageScope";
109
+ import { StatusPageSubscriberUnsubscribeSource } from "../Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice";
110
+ import StatusPageSubscriberUnsubscribe from "../../Types/StatusPage/StatusPageSubscriberUnsubscribe";
108
111
  /*
109
112
  * A manage-subscription request is unauthenticated, and one Slack or Microsoft
110
113
  * Teams workspace name can match several subscriptions. Cap how many of them a
@@ -176,7 +179,7 @@ class StatusPageAPI extends BaseAPI {
176
179
  }
177
180
  }
178
181
  constructor() {
179
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9;
182
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10;
180
183
  super(StatusPage, StatusPageService);
181
184
  // get title, description of the page. This is used for SEO.
182
185
  this.router.get(`${(_a = new this.entityType().getCrudApiPath()) === null || _a === void 0 ? void 0 : _a.toString()}/seo/:statusPageIdOrDomain`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res) => {
@@ -522,9 +525,65 @@ class StatusPageAPI extends BaseAPI {
522
525
  });
523
526
  return Response.sendEmptySuccessResponse(req, res);
524
527
  });
528
+ /*
529
+ * The unsubscribe link in every subscriber notification opens the status
530
+ * page's unsubscribe page ({statusPageUrl}/unsubscribe/{id}-{token}, see
531
+ * Common/Types/StatusPage/StatusPageSubscriberUnsubscribe), which reads
532
+ * what the link belongs to with GET and cancels it with POST.
533
+ *
534
+ * - GET changes nothing. Mail scanners and link previewers fetch every
535
+ * link in a message; they must not unsubscribe anybody.
536
+ * - POST unsubscribes, and is idempotent. It answers whatever its body is
537
+ * - the page sends none, and an RFC 8058 one-click request sends
538
+ * "List-Unsubscribe=One-Click" - because holding the token is the whole
539
+ * authorisation.
540
+ *
541
+ * Deliberately no read-access check: a private status page's subscribers
542
+ * are exactly the people who may not be able to sign in to it, and the
543
+ * token proves the request came from the subscription's own inbox (or
544
+ * phone, or channel). Nothing but the subscription's own contact is ever
545
+ * returned - no page content - so a private page stays private.
546
+ *
547
+ * Every bad link answers the same Invalid, so the endpoint cannot be used
548
+ * to learn which subscriptions exist; see
549
+ * StatusPageSubscriberService.getUnsubscribeLinkDetails.
550
+ */
551
+ const unsubscribeApiPath = `${(_m = new this.entityType()
552
+ .getCrudApiPath()) === null || _m === void 0 ? void 0 : _m.toString()}/unsubscribe/:statusPageId/:subscriberId/:token`;
553
+ this.router.get(unsubscribeApiPath, async (req, res, next) => {
554
+ try {
555
+ const details = await StatusPageSubscriberService.getUnsubscribeLinkDetails({
556
+ statusPageId: req.params["statusPageId"],
557
+ subscriberId: req.params["subscriberId"],
558
+ token: req.params["token"],
559
+ });
560
+ // A token-bearing answer: never let a shared cache keep it.
561
+ Response.setNoCacheHeaders(res);
562
+ return Response.sendJsonObjectResponse(req, res, details);
563
+ }
564
+ catch (err) {
565
+ next(err);
566
+ }
567
+ });
568
+ this.router.post(unsubscribeApiPath, async (req, res, next) => {
569
+ try {
570
+ const state = await StatusPageSubscriberService.unsubscribeWithLink({
571
+ statusPageId: req.params["statusPageId"],
572
+ subscriberId: req.params["subscriberId"],
573
+ token: req.params["token"],
574
+ });
575
+ Response.setNoCacheHeaders(res);
576
+ return Response.sendJsonObjectResponse(req, res, {
577
+ state: state,
578
+ });
579
+ }
580
+ catch (err) {
581
+ next(err);
582
+ }
583
+ });
525
584
  // CNAME verification api
526
- this.router.get(`${(_m = new this.entityType()
527
- .getCrudApiPath()) === null || _m === void 0 ? void 0 : _m.toString()}/cname-verification/:token`, async (req, res) => {
585
+ this.router.get(`${(_o = new this.entityType()
586
+ .getCrudApiPath()) === null || _o === void 0 ? void 0 : _o.toString()}/cname-verification/:token`, async (req, res) => {
528
587
  const host = req.get("host");
529
588
  if (!host) {
530
589
  throw new BadDataException("Host not found");
@@ -549,8 +608,8 @@ class StatusPageAPI extends BaseAPI {
549
608
  return Response.sendEmptySuccessResponse(req, res);
550
609
  });
551
610
  // ACME Challenge Validation.
552
- this.router.get(`${(_o = new this.entityType()
553
- .getCrudApiPath()) === null || _o === void 0 ? void 0 : _o.toString()}/.well-known/acme-challenge/:token`, async (req, res) => {
611
+ this.router.get(`${(_p = new this.entityType()
612
+ .getCrudApiPath()) === null || _p === void 0 ? void 0 : _p.toString()}/.well-known/acme-challenge/:token`, async (req, res) => {
554
613
  logger.debug(`ACME challenge validation request received for token: ${req.params["token"]} from host: ${req.headers["host"]}`, getLogAttributesFromRequest(req));
555
614
  const challenge = await AcmeChallengeService.findOneBy({
556
615
  query: {
@@ -574,7 +633,7 @@ class StatusPageAPI extends BaseAPI {
574
633
  * is gated like editing the status page: an authenticated member of the
575
634
  * project that owns it who could update this particular page.
576
635
  */
577
- this.router.post(`${(_p = new this.entityType().getCrudApiPath()) === null || _p === void 0 ? void 0 : _p.toString()}/test-email-report`, UserMiddleware.getUserMiddleware, UserMiddleware.requireUserAuthentication, async (req, res, next) => {
636
+ this.router.post(`${(_q = new this.entityType().getCrudApiPath()) === null || _q === void 0 ? void 0 : _q.toString()}/test-email-report`, UserMiddleware.getUserMiddleware, UserMiddleware.requireUserAuthentication, async (req, res, next) => {
578
637
  try {
579
638
  /*
580
639
  * This route acts on one page in one project, and the permission
@@ -627,7 +686,7 @@ class StatusPageAPI extends BaseAPI {
627
686
  next(err);
628
687
  }
629
688
  });
630
- this.router.post(`${(_q = new this.entityType().getCrudApiPath()) === null || _q === void 0 ? void 0 : _q.toString()}/domain`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
689
+ this.router.post(`${(_r = new this.entityType().getCrudApiPath()) === null || _r === void 0 ? void 0 : _r.toString()}/domain`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
631
690
  try {
632
691
  if (!req.body["domain"]) {
633
692
  throw new BadDataException("domain is required in request body");
@@ -659,8 +718,8 @@ class StatusPageAPI extends BaseAPI {
659
718
  next(err);
660
719
  }
661
720
  });
662
- this.router.post(`${(_r = new this.entityType()
663
- .getCrudApiPath()) === null || _r === void 0 ? void 0 : _r.toString()}/master-page/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
721
+ this.router.post(`${(_s = new this.entityType()
722
+ .getCrudApiPath()) === null || _s === void 0 ? void 0 : _s.toString()}/master-page/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
664
723
  try {
665
724
  const statusPageIdParam = req.params["statusPageId"];
666
725
  ObjectID.validateUUID(statusPageIdParam);
@@ -831,8 +890,8 @@ class StatusPageAPI extends BaseAPI {
831
890
  * a refused attempt costs neither a session lookup nor a hash. See
832
891
  * PublicDashboardRateLimit for the budget and why it fails closed.
833
892
  */
834
- this.router.post(`${(_s = new this.entityType()
835
- .getCrudApiPath()) === null || _s === void 0 ? void 0 : _s.toString()}/master-password/:statusPageId`, PublicDashboardRateLimit.getMiddleware(PublicDashboardRateLimitBucket.StatusPageMasterPassword), UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
893
+ this.router.post(`${(_t = new this.entityType()
894
+ .getCrudApiPath()) === null || _t === void 0 ? void 0 : _t.toString()}/master-password/:statusPageId`, PublicDashboardRateLimit.getMiddleware(PublicDashboardRateLimitBucket.StatusPageMasterPassword), UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
836
895
  try {
837
896
  if (!req.params["statusPageId"]) {
838
897
  throw new BadDataException("Status Page ID not found");
@@ -883,7 +942,7 @@ class StatusPageAPI extends BaseAPI {
883
942
  next(err);
884
943
  }
885
944
  });
886
- this.router.post(`${(_t = new this.entityType().getCrudApiPath()) === null || _t === void 0 ? void 0 : _t.toString()}/sso/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
945
+ this.router.post(`${(_u = new this.entityType().getCrudApiPath()) === null || _u === void 0 ? void 0 : _u.toString()}/sso/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
887
946
  try {
888
947
  const objectId = new ObjectID(req.params["statusPageId"]);
889
948
  /*
@@ -915,7 +974,7 @@ class StatusPageAPI extends BaseAPI {
915
974
  next(err);
916
975
  }
917
976
  });
918
- this.router.post(`${(_u = new this.entityType().getCrudApiPath()) === null || _u === void 0 ? void 0 : _u.toString()}/oidc/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
977
+ this.router.post(`${(_v = new this.entityType().getCrudApiPath()) === null || _v === void 0 ? void 0 : _v.toString()}/oidc/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
919
978
  try {
920
979
  const objectId = new ObjectID(req.params["statusPageId"]);
921
980
  /*
@@ -947,8 +1006,8 @@ class StatusPageAPI extends BaseAPI {
947
1006
  }
948
1007
  });
949
1008
  // Get all status page resources for subscriber to subscribe to.
950
- this.router.post(`${(_v = new this.entityType()
951
- .getCrudApiPath()) === null || _v === void 0 ? void 0 : _v.toString()}/resources/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1009
+ this.router.post(`${(_w = new this.entityType()
1010
+ .getCrudApiPath()) === null || _w === void 0 ? void 0 : _w.toString()}/resources/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
952
1011
  try {
953
1012
  const statusPageId = new ObjectID(req.params["statusPageId"]);
954
1013
  await this.checkHasReadAccess({
@@ -981,8 +1040,8 @@ class StatusPageAPI extends BaseAPI {
981
1040
  next(err);
982
1041
  }
983
1042
  });
984
- this.router.post(`${(_w = new this.entityType()
985
- .getCrudApiPath()) === null || _w === void 0 ? void 0 : _w.toString()}/uptime/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1043
+ this.router.post(`${(_x = new this.entityType()
1044
+ .getCrudApiPath()) === null || _x === void 0 ? void 0 : _x.toString()}/uptime/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
986
1045
  var _a, _b;
987
1046
  try {
988
1047
  // This reosurce ID can be of a status page resource OR a status page group.
@@ -1299,8 +1358,8 @@ class StatusPageAPI extends BaseAPI {
1299
1358
  next(err);
1300
1359
  }
1301
1360
  };
1302
- const overviewApiPath = `${(_x = new this.entityType()
1303
- .getCrudApiPath()) === null || _x === void 0 ? void 0 : _x.toString()}/overview/:statusPageIdOrDomain`;
1361
+ const overviewApiPath = `${(_y = new this.entityType()
1362
+ .getCrudApiPath()) === null || _y === void 0 ? void 0 : _y.toString()}/overview/:statusPageIdOrDomain`;
1304
1363
  this.router.post(overviewApiPath, UserMiddleware.getPublicRouteUserMiddleware, overviewHandler);
1305
1364
  /*
1306
1365
  * GET variant of the overview endpoint. Same middleware and same handler
@@ -1308,8 +1367,8 @@ class StatusPageAPI extends BaseAPI {
1308
1367
  * unauthenticated callers exactly as they are on POST.
1309
1368
  */
1310
1369
  this.router.get(overviewApiPath, UserMiddleware.getPublicRouteUserMiddleware, overviewHandler);
1311
- this.router.put(`${(_y = new this.entityType()
1312
- .getCrudApiPath()) === null || _y === void 0 ? void 0 : _y.toString()}/update-subscription/:statusPageId/:subscriberId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1370
+ this.router.put(`${(_z = new this.entityType()
1371
+ .getCrudApiPath()) === null || _z === void 0 ? void 0 : _z.toString()}/update-subscription/:statusPageId/:subscriberId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1313
1372
  try {
1314
1373
  await this.subscribeToStatusPage(req);
1315
1374
  return Response.sendEmptySuccessResponse(req, res);
@@ -1318,8 +1377,8 @@ class StatusPageAPI extends BaseAPI {
1318
1377
  next(err);
1319
1378
  }
1320
1379
  });
1321
- this.router.post(`${(_z = new this.entityType()
1322
- .getCrudApiPath()) === null || _z === void 0 ? void 0 : _z.toString()}/get-subscription/:statusPageId/:subscriberId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1380
+ this.router.post(`${(_0 = new this.entityType()
1381
+ .getCrudApiPath()) === null || _0 === void 0 ? void 0 : _0.toString()}/get-subscription/:statusPageId/:subscriberId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1323
1382
  try {
1324
1383
  const subscriber = await this.getSubscriber(req);
1325
1384
  return Response.sendEntityResponse(req, res, subscriber, StatusPageSubscriber);
@@ -1328,8 +1387,8 @@ class StatusPageAPI extends BaseAPI {
1328
1387
  next(err);
1329
1388
  }
1330
1389
  });
1331
- this.router.post(`${(_0 = new this.entityType()
1332
- .getCrudApiPath()) === null || _0 === void 0 ? void 0 : _0.toString()}/subscribe/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1390
+ this.router.post(`${(_1 = new this.entityType()
1391
+ .getCrudApiPath()) === null || _1 === void 0 ? void 0 : _1.toString()}/subscribe/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1333
1392
  try {
1334
1393
  await this.subscribeToStatusPage(req);
1335
1394
  return Response.sendEmptySuccessResponse(req, res);
@@ -1338,8 +1397,8 @@ class StatusPageAPI extends BaseAPI {
1338
1397
  next(err);
1339
1398
  }
1340
1399
  });
1341
- this.router.post(`${(_1 = new this.entityType()
1342
- .getCrudApiPath()) === null || _1 === void 0 ? void 0 : _1.toString()}/manage-subscription/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1400
+ this.router.post(`${(_2 = new this.entityType()
1401
+ .getCrudApiPath()) === null || _2 === void 0 ? void 0 : _2.toString()}/manage-subscription/:statusPageId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1343
1402
  try {
1344
1403
  await this.manageExistingSubscription(req);
1345
1404
  return Response.sendEmptySuccessResponse(req, res);
@@ -1364,8 +1423,8 @@ class StatusPageAPI extends BaseAPI {
1364
1423
  next(err);
1365
1424
  }
1366
1425
  };
1367
- const incidentsListApiPath = `${(_2 = new this.entityType()
1368
- .getCrudApiPath()) === null || _2 === void 0 ? void 0 : _2.toString()}/incidents/:statusPageIdOrDomain`;
1426
+ const incidentsListApiPath = `${(_3 = new this.entityType()
1427
+ .getCrudApiPath()) === null || _3 === void 0 ? void 0 : _3.toString()}/incidents/:statusPageIdOrDomain`;
1369
1428
  this.router.post(incidentsListApiPath, UserMiddleware.getPublicRouteUserMiddleware, incidentsListHandler);
1370
1429
  this.router.get(incidentsListApiPath, UserMiddleware.getPublicRouteUserMiddleware, incidentsListHandler);
1371
1430
  /*
@@ -1385,8 +1444,8 @@ class StatusPageAPI extends BaseAPI {
1385
1444
  next(err);
1386
1445
  }
1387
1446
  };
1388
- const scheduledMaintenanceEventsListApiPath = `${(_3 = new this.entityType()
1389
- .getCrudApiPath()) === null || _3 === void 0 ? void 0 : _3.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain`;
1447
+ const scheduledMaintenanceEventsListApiPath = `${(_4 = new this.entityType()
1448
+ .getCrudApiPath()) === null || _4 === void 0 ? void 0 : _4.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain`;
1390
1449
  this.router.post(scheduledMaintenanceEventsListApiPath, UserMiddleware.getPublicRouteUserMiddleware, scheduledMaintenanceEventsListHandler);
1391
1450
  this.router.get(scheduledMaintenanceEventsListApiPath, UserMiddleware.getPublicRouteUserMiddleware, scheduledMaintenanceEventsListHandler);
1392
1451
  /*
@@ -1405,12 +1464,12 @@ class StatusPageAPI extends BaseAPI {
1405
1464
  next(err);
1406
1465
  }
1407
1466
  };
1408
- const announcementsListApiPath = `${(_4 = new this.entityType()
1409
- .getCrudApiPath()) === null || _4 === void 0 ? void 0 : _4.toString()}/announcements/:statusPageIdOrDomain`;
1467
+ const announcementsListApiPath = `${(_5 = new this.entityType()
1468
+ .getCrudApiPath()) === null || _5 === void 0 ? void 0 : _5.toString()}/announcements/:statusPageIdOrDomain`;
1410
1469
  this.router.post(announcementsListApiPath, UserMiddleware.getPublicRouteUserMiddleware, announcementsListHandler);
1411
1470
  this.router.get(announcementsListApiPath, UserMiddleware.getPublicRouteUserMiddleware, announcementsListHandler);
1412
- this.router.post(`${(_5 = new this.entityType()
1413
- .getCrudApiPath()) === null || _5 === void 0 ? void 0 : _5.toString()}/incidents/:statusPageIdOrDomain/:incidentId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1471
+ this.router.post(`${(_6 = new this.entityType()
1472
+ .getCrudApiPath()) === null || _6 === void 0 ? void 0 : _6.toString()}/incidents/:statusPageIdOrDomain/:incidentId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1414
1473
  try {
1415
1474
  const objectId = await resolveStatusPageIdOrThrow(req.params["statusPageIdOrDomain"]);
1416
1475
  const incidentId = new ObjectID(req.params["incidentId"]);
@@ -1421,8 +1480,8 @@ class StatusPageAPI extends BaseAPI {
1421
1480
  next(err);
1422
1481
  }
1423
1482
  });
1424
- this.router.post(`${(_6 = new this.entityType()
1425
- .getCrudApiPath()) === null || _6 === void 0 ? void 0 : _6.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain/:scheduledMaintenanceId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1483
+ this.router.post(`${(_7 = new this.entityType()
1484
+ .getCrudApiPath()) === null || _7 === void 0 ? void 0 : _7.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain/:scheduledMaintenanceId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1426
1485
  try {
1427
1486
  const objectId = await resolveStatusPageIdOrThrow(req.params["statusPageIdOrDomain"]);
1428
1487
  const scheduledMaintenanceId = new ObjectID(req.params["scheduledMaintenanceId"]);
@@ -1433,8 +1492,8 @@ class StatusPageAPI extends BaseAPI {
1433
1492
  next(err);
1434
1493
  }
1435
1494
  });
1436
- this.router.post(`${(_7 = new this.entityType()
1437
- .getCrudApiPath()) === null || _7 === void 0 ? void 0 : _7.toString()}/announcements/:statusPageIdOrDomain/:announcementId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1495
+ this.router.post(`${(_8 = new this.entityType()
1496
+ .getCrudApiPath()) === null || _8 === void 0 ? void 0 : _8.toString()}/announcements/:statusPageIdOrDomain/:announcementId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1438
1497
  try {
1439
1498
  const objectId = await resolveStatusPageIdOrThrow(req.params["statusPageIdOrDomain"]);
1440
1499
  const announcementId = new ObjectID(req.params["announcementId"]);
@@ -1446,8 +1505,8 @@ class StatusPageAPI extends BaseAPI {
1446
1505
  }
1447
1506
  });
1448
1507
  // Episodes endpoints
1449
- this.router.post(`${(_8 = new this.entityType()
1450
- .getCrudApiPath()) === null || _8 === void 0 ? void 0 : _8.toString()}/episodes/:statusPageIdOrDomain`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1508
+ this.router.post(`${(_9 = new this.entityType()
1509
+ .getCrudApiPath()) === null || _9 === void 0 ? void 0 : _9.toString()}/episodes/:statusPageIdOrDomain`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1451
1510
  try {
1452
1511
  const objectId = await resolveStatusPageIdOrThrow(req.params["statusPageIdOrDomain"]);
1453
1512
  const response = await this.getEpisodes(objectId, null, req);
@@ -1457,8 +1516,8 @@ class StatusPageAPI extends BaseAPI {
1457
1516
  next(err);
1458
1517
  }
1459
1518
  });
1460
- this.router.post(`${(_9 = new this.entityType()
1461
- .getCrudApiPath()) === null || _9 === void 0 ? void 0 : _9.toString()}/episodes/:statusPageIdOrDomain/:episodeId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1519
+ this.router.post(`${(_10 = new this.entityType()
1520
+ .getCrudApiPath()) === null || _10 === void 0 ? void 0 : _10.toString()}/episodes/:statusPageIdOrDomain/:episodeId`, UserMiddleware.getPublicRouteUserMiddleware, async (req, res, next) => {
1462
1521
  try {
1463
1522
  const objectId = await resolveStatusPageIdOrThrow(req.params["statusPageIdOrDomain"]);
1464
1523
  const episodeId = new ObjectID(req.params["episodeId"]);
@@ -1932,23 +1991,43 @@ class StatusPageAPI extends BaseAPI {
1932
1991
  */
1933
1992
  let lookupQuery;
1934
1993
  let lookupSelect;
1994
+ /*
1995
+ * The unsubscribe token is read too: the message carries the
1996
+ * subscription's unsubscribe link as well as its manage link.
1997
+ */
1935
1998
  if (email) {
1936
1999
  lookupQuery = { subscriberEmail: email };
1937
- lookupSelect = { _id: true, subscriberEmail: true };
2000
+ lookupSelect = {
2001
+ _id: true,
2002
+ subscriberEmail: true,
2003
+ unsubscribeToken: true,
2004
+ };
1938
2005
  }
1939
2006
  else if (phone) {
1940
2007
  lookupQuery = { subscriberPhone: phone };
1941
- lookupSelect = { _id: true, subscriberPhone: true };
2008
+ lookupSelect = {
2009
+ _id: true,
2010
+ subscriberPhone: true,
2011
+ unsubscribeToken: true,
2012
+ };
1942
2013
  }
1943
2014
  else if (slackWorkspaceName) {
1944
2015
  lookupQuery = { slackWorkspaceName: slackWorkspaceName };
1945
- lookupSelect = { _id: true, slackIncomingWebhookUrl: true };
2016
+ lookupSelect = {
2017
+ _id: true,
2018
+ slackIncomingWebhookUrl: true,
2019
+ unsubscribeToken: true,
2020
+ };
1946
2021
  }
1947
2022
  else {
1948
2023
  lookupQuery = {
1949
2024
  microsoftTeamsWorkspaceName: microsoftTeamsWorkspaceName,
1950
2025
  };
1951
- lookupSelect = { _id: true, microsoftTeamsIncomingWebhookUrl: true };
2026
+ lookupSelect = {
2027
+ _id: true,
2028
+ microsoftTeamsIncomingWebhookUrl: true,
2029
+ unsubscribeToken: true,
2030
+ };
1952
2031
  }
1953
2032
  logger.debug(`Looking up subscribers by email: ${email}, phone: ${phone}, Slack workspace: ${slackWorkspaceName}, or Microsoft Teams workspace: ${microsoftTeamsWorkspaceName}`, getLogAttributesFromRequest(req));
1954
2033
  /*
@@ -1993,6 +2072,12 @@ class StatusPageAPI extends BaseAPI {
1993
2072
  async sendManageSubscriptionLinks(data) {
1994
2073
  var _a, _b;
1995
2074
  const { statusPageId, subscribers, req } = data;
2075
+ /*
2076
+ * Their unsubscribe links need their tokens, as every other sender's do:
2077
+ * a subscriber the backfill has not reached yet is given one first,
2078
+ * rather than sent a link to the "out of date" page.
2079
+ */
2080
+ await StatusPageSubscriberService.ensureUnsubscribeTokens(subscribers);
1996
2081
  const statusPageURL = await StatusPageService.getStatusPageURL(statusPageId);
1997
2082
  const statusPages = await StatusPageSubscriberService.getStatusPagesToSendNotification([
1998
2083
  statusPageId,
@@ -2031,16 +2116,19 @@ class StatusPageAPI extends BaseAPI {
2031
2116
  const subscriberPhone = statusPageSubscriber.subscriberPhone;
2032
2117
  const slackIncomingWebhookUrl = statusPageSubscriber.slackIncomingWebhookUrl;
2033
2118
  const microsoftTeamsIncomingWebhookUrl = statusPageSubscriber.microsoftTeamsIncomingWebhookUrl;
2034
- const manageUrlink = StatusPageSubscriberService.getUnsubscribeLink(URL.fromString(statusPageURL), statusPageSubscriber.id).toString();
2119
+ const manageUrlink = StatusPageSubscriberService.getManageSubscriptionLink(URL.fromString(statusPageURL), statusPageSubscriber.id).toString();
2035
2120
  /*
2036
- * The manage link is the subscriber's update-subscription page, which
2037
- * is the same URL every other sender passes as unsubscribeUrl, so a
2038
- * template may use either name for it.
2121
+ * The visitor asked to manage their subscription, so the message
2122
+ * links to its Update Subscription page. It also hands templates the
2123
+ * unsubscribe link every other sender passes as unsubscribeUrl: that
2124
+ * one works without signing in, which the manage page on a private
2125
+ * status page does not.
2039
2126
  */
2127
+ const unsubscribeUrl = StatusPageSubscriberService.getUnsubscribeLink(URL.fromString(statusPageURL), statusPageSubscriber).toString();
2040
2128
  const manageTemplateVariables = {
2041
2129
  statusPageName: statusPageNameStr,
2042
2130
  statusPageUrl: statusPageURL,
2043
- unsubscribeUrl: manageUrlink,
2131
+ unsubscribeUrl: unsubscribeUrl,
2044
2132
  manageSubscriptionUrl: manageUrlink,
2045
2133
  };
2046
2134
  const defaultChatMessage = `You have selected to manage your subscription for the status page: ${statusPageNameStr}. You can manage your subscription here: ${manageUrlink}`;
@@ -2049,7 +2137,8 @@ class StatusPageAPI extends BaseAPI {
2049
2137
  const httpProtocol = await DatabaseConfig.getHttpProtocol();
2050
2138
  const statusPageIdString = ((_a = statusPage.id) === null || _a === void 0 ? void 0 : _a.toString()) || ((_b = statusPage._id) === null || _b === void 0 ? void 0 : _b.toString()) || null;
2051
2139
  if ((manageEmailTemplate === null || manageEmailTemplate === void 0 ? void 0 : manageEmailTemplate.templateBody) && statusPage.smtpConfig) {
2052
- const compiledBody = StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(manageEmailTemplate.templateBody, manageTemplateVariables);
2140
+ // The body is HTML, so the (plain-text) values are escaped into it.
2141
+ const compiledBody = StatusPageSubscriberNotificationTemplateServiceClass.compileEmailBodyTemplate(manageEmailTemplate.templateBody, manageTemplateVariables);
2053
2142
  const compiledSubject = manageEmailTemplate.emailSubject
2054
2143
  ? StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(manageEmailTemplate.emailSubject, manageTemplateVariables)
2055
2144
  : "Manage your Subscription for " + statusPageNameStr;
@@ -2098,8 +2187,19 @@ class StatusPageAPI extends BaseAPI {
2098
2187
  if (subscriberPhone) {
2099
2188
  let smsMessage;
2100
2189
  if ((manageSmsTemplate === null || manageSmsTemplate === void 0 ? void 0 : manageSmsTemplate.templateBody) && statusPage.callSmsConfig) {
2190
+ /*
2191
+ * On a public status page an SMS carries the shorter manage link
2192
+ * where other messages carry the unsubscribe link: an SMS is
2193
+ * billed by the segment (see
2194
+ * StatusPageSubscriberUnsubscribe.buildSmsLink).
2195
+ */
2101
2196
  smsMessage =
2102
- StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(manageSmsTemplate.templateBody, manageTemplateVariables);
2197
+ StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(manageSmsTemplate.templateBody, Object.assign(Object.assign({}, manageTemplateVariables), { unsubscribeUrl: StatusPageSubscriberUnsubscribe.buildSmsLink({
2198
+ isPublicStatusPage: statusPage.isPublicStatusPage,
2199
+ statusPageUrl: statusPageURL,
2200
+ subscriberId: statusPageSubscriber.id,
2201
+ unsubscribeUrl: unsubscribeUrl,
2202
+ }) }));
2103
2203
  }
2104
2204
  else {
2105
2205
  smsMessage = defaultChatMessage;
@@ -2335,31 +2435,26 @@ class StatusPageAPI extends BaseAPI {
2335
2435
  statusPageSubscriber.statusPageEventTypes = req.body.data["statusPageEventTypes"];
2336
2436
  }
2337
2437
  if (isUpdate) {
2338
- // check isUnsubscribed is set to false.
2339
2438
  logger.debug(`Updating subscriber with ID: ${statusPageSubscriber.id}`, getLogAttributesFromRequest(req));
2340
- statusPageSubscriber.isUnsubscribed = Boolean(req.body.data["isUnsubscribed"]);
2439
+ const wantsToUnsubscribe = Boolean(req.body.data["isUnsubscribed"]);
2341
2440
  await StatusPageSubscriberService.updateOneById({
2342
2441
  id: statusPageSubscriber.id,
2343
- data: {
2344
- statusPageResources: statusPageSubscriber.statusPageResources,
2345
- isSubscribedToAllResources: statusPageSubscriber.isSubscribedToAllResources,
2346
- statusPageEventTypes: statusPageSubscriber.statusPageEventTypes,
2347
- isSubscribedToAllEventTypes: statusPageSubscriber.isSubscribedToAllEventTypes,
2348
- isUnsubscribed: statusPageSubscriber.isUnsubscribed,
2349
- },
2442
+ data: Object.assign({ statusPageResources: statusPageSubscriber.statusPageResources, isSubscribedToAllResources: statusPageSubscriber.isSubscribedToAllResources, statusPageEventTypes: statusPageSubscriber.statusPageEventTypes, isSubscribedToAllEventTypes: statusPageSubscriber.isSubscribedToAllEventTypes }, (wantsToUnsubscribe ? {} : { isUnsubscribed: false })),
2350
2443
  props: {
2351
2444
  isRoot: true,
2352
2445
  },
2353
2446
  });
2447
+ if (wantsToUnsubscribe) {
2448
+ await StatusPageSubscriberService.unsubscribe({
2449
+ subscriberId: statusPageSubscriber.id,
2450
+ source: StatusPageSubscriberUnsubscribeSource.ManageSubscriptionPage,
2451
+ });
2452
+ }
2354
2453
  }
2355
2454
  else {
2356
2455
  logger.debug(`Creating new subscriber: ${JSON.stringify(statusPageSubscriber)}`, getLogAttributesFromRequest(req));
2357
- await StatusPageSubscriberService.create({
2358
- data: statusPageSubscriber,
2359
- props: {
2360
- isRoot: true,
2361
- },
2362
- });
2456
+ // A sign-up, not a subscriber the team added (Is Added By Team stays off).
2457
+ await StatusPageSubscriberService.createFromStatusPageSignUp(statusPageSubscriber);
2363
2458
  }
2364
2459
  logger.debug(`Subscription process completed for status page with ID: ${objectId}`, getLogAttributesFromRequest(req));
2365
2460
  }
@@ -2410,6 +2505,12 @@ class StatusPageAPI extends BaseAPI {
2410
2505
  }
2411
2506
  return statusPageSubscriber;
2412
2507
  }
2508
+ /*
2509
+ * Every incident a public status page is sent goes through here - the
2510
+ * overview's active and timeline incidents, and the incident list and
2511
+ * detail - so it is where the page's JSON, and the overview cache, are made
2512
+ * safe to serve.
2513
+ */
2413
2514
  serializeIncidentsForStatusPage(incidents) {
2414
2515
  return incidents.map((incident) => {
2415
2516
  const incidentJson = BaseModel.toJSON(incident, Incident);
@@ -2419,9 +2520,32 @@ class StatusPageAPI extends BaseAPI {
2419
2520
  delete incidentJson["postmortemPostedAt"];
2420
2521
  delete incidentJson["postmortemAttachments"];
2421
2522
  }
2523
+ /*
2524
+ * Which status pages an incident is limited to names the other
2525
+ * audiences it is shown to. The display queries never select these
2526
+ * columns (IncidentStatusPageScope), so this is a second line of
2527
+ * defence against a select that one day does.
2528
+ */
2529
+ IncidentStatusPageScope.removeScopeColumns(incidentJson);
2422
2530
  return incidentJson;
2423
2531
  });
2424
2532
  }
2533
+ /*
2534
+ * An episode's member incidents, read by id to work out which of the
2535
+ * page's monitors the episode affects, keeping only those this page shows
2536
+ * as far as scope goes. The read selects the scope columns for that
2537
+ * (INCIDENT_SCOPE_SELECT); they are removed again before the incidents are
2538
+ * used, so they cannot reach the page's JSON.
2539
+ */
2540
+ keepMemberIncidentsInScope(data) {
2541
+ return data.incidents
2542
+ .filter((incident) => {
2543
+ return IncidentStatusPageScope.isIncidentInScope(incident, data.statusPage);
2544
+ })
2545
+ .map((incident) => {
2546
+ return IncidentStatusPageScope.removeScopeColumns(incident);
2547
+ });
2548
+ }
2425
2549
  async getIncidents(statusPageId, incidentId, req) {
2426
2550
  await this.checkHasReadAccess({
2427
2551
  statusPageId: statusPageId,
@@ -2437,6 +2561,8 @@ class StatusPageAPI extends BaseAPI {
2437
2561
  showIncidentHistoryInDays: true,
2438
2562
  showIncidentLabelsOnStatusPage: true,
2439
2563
  showIncidentsOnStatusPage: true,
2564
+ // Read by IncidentStatusPageScope to decide which incidents it shows.
2565
+ onlyShowScopedIncidents: true,
2440
2566
  },
2441
2567
  props: {
2442
2568
  isRoot: true,
@@ -2510,7 +2636,15 @@ class StatusPageAPI extends BaseAPI {
2510
2636
  } });
2511
2637
  }
2512
2638
  if (monitorsOnStatusPage.length > 0) {
2513
- incidents = await IncidentService.findBy({
2639
+ /*
2640
+ * Through IncidentStatusPageScope, like every incident read here that
2641
+ * decides what the page shows: an incident limited to other status
2642
+ * pages is left out, and so is an unlimited one when this page only
2643
+ * shows incidents limited to it. The scope is applied in SQL, before
2644
+ * LIMIT_PER_PROJECT cuts the list, not filtered out afterwards.
2645
+ */
2646
+ incidents = await IncidentStatusPageScope.findIncidentsForStatusPage({
2647
+ statusPage: statusPage,
2514
2648
  query: incidentQuery,
2515
2649
  select: selectIncidents,
2516
2650
  sort: {
@@ -2532,24 +2666,26 @@ class StatusPageAPI extends BaseAPI {
2532
2666
  });
2533
2667
  // If there is no particular incident id to fetch then fetch active incidents.
2534
2668
  if (!incidentId) {
2535
- activeIncidents = await IncidentService.findBy({
2536
- query: {
2537
- monitors: monitorsOnStatusPage,
2538
- isVisibleOnStatusPage: true,
2539
- currentIncidentStateId: QueryHelper.any(unresolvbedIncidentStateIds),
2540
- projectId: statusPage.projectId,
2541
- },
2542
- select: selectIncidents,
2543
- sort: {
2544
- declaredAt: SortOrder.Descending,
2545
- createdAt: SortOrder.Descending,
2546
- },
2547
- skip: 0,
2548
- limit: LIMIT_PER_PROJECT,
2549
- props: {
2550
- isRoot: true,
2551
- },
2552
- });
2669
+ activeIncidents =
2670
+ await IncidentStatusPageScope.findIncidentsForStatusPage({
2671
+ statusPage: statusPage,
2672
+ query: {
2673
+ monitors: monitorsOnStatusPage,
2674
+ isVisibleOnStatusPage: true,
2675
+ currentIncidentStateId: QueryHelper.any(unresolvbedIncidentStateIds),
2676
+ projectId: statusPage.projectId,
2677
+ },
2678
+ select: selectIncidents,
2679
+ sort: {
2680
+ declaredAt: SortOrder.Descending,
2681
+ createdAt: SortOrder.Descending,
2682
+ },
2683
+ skip: 0,
2684
+ limit: LIMIT_PER_PROJECT,
2685
+ props: {
2686
+ isRoot: true,
2687
+ },
2688
+ });
2553
2689
  }
2554
2690
  incidents = [...activeIncidents, ...incidents];
2555
2691
  // get distinct by id.
@@ -2652,6 +2788,8 @@ class StatusPageAPI extends BaseAPI {
2652
2788
  showEpisodeHistoryInDays: true,
2653
2789
  showEpisodesOnStatusPage: true,
2654
2790
  showEpisodeLabelsOnStatusPage: true,
2791
+ // Read by IncidentStatusPageScope to decide which incidents it shows.
2792
+ onlyShowScopedIncidents: true,
2655
2793
  },
2656
2794
  props: {
2657
2795
  isRoot: true,
@@ -2688,11 +2826,22 @@ class StatusPageAPI extends BaseAPI {
2688
2826
  };
2689
2827
  let incidents = [];
2690
2828
  if (monitorsOnStatusPage.length > 0) {
2691
- incidents = await IncidentService.findBy({
2829
+ /*
2830
+ * An episode is listed through the incidents in it that this page
2831
+ * shows, so an incident limited to other status pages (or an unlimited
2832
+ * one, on a page that only shows incidents limited to it) does not
2833
+ * bring its episode here. Newest first, so that when LIMIT_PER_PROJECT
2834
+ * cuts the list it keeps the recent episodes.
2835
+ */
2836
+ incidents = await IncidentStatusPageScope.findIncidentsForStatusPage({
2837
+ statusPage: statusPage,
2692
2838
  query: incidentQuery,
2693
2839
  select: {
2694
2840
  _id: true,
2695
2841
  },
2842
+ sort: {
2843
+ createdAt: SortOrder.Descending,
2844
+ },
2696
2845
  skip: 0,
2697
2846
  limit: LIMIT_PER_PROJECT,
2698
2847
  props: {
@@ -2758,6 +2907,42 @@ class StatusPageAPI extends BaseAPI {
2758
2907
  isRoot: true,
2759
2908
  },
2760
2909
  });
2910
+ /*
2911
+ * An episode is this page's to show only through an incident in it
2912
+ * that the page shows: one on a monitor the page lists, and in scope
2913
+ * for the page. Without this check any visible episode of the project
2914
+ * could be read from any of its status pages by id - including an
2915
+ * episode whose incidents are all limited to other status pages. Like
2916
+ * the list, it does not ask whether the incidents themselves are
2917
+ * visible on status pages, and unlike the list, it has no history
2918
+ * window: a link to an older episode keeps working.
2919
+ */
2920
+ const episodeMemberIncidentIds = episodeMembersForSpecificEpisode
2921
+ .map((member) => {
2922
+ return member.incidentId;
2923
+ })
2924
+ .filter((id) => {
2925
+ return Boolean(id);
2926
+ });
2927
+ const incidentShownOnStatusPage = monitorsOnStatusPage.length > 0 && episodeMemberIncidentIds.length > 0
2928
+ ? await IncidentStatusPageScope.findOneIncidentForStatusPage({
2929
+ statusPage: statusPage,
2930
+ query: {
2931
+ _id: QueryHelper.any(episodeMemberIncidentIds),
2932
+ monitors: monitorsOnStatusPage,
2933
+ projectId: statusPage.projectId,
2934
+ },
2935
+ select: {
2936
+ _id: true,
2937
+ },
2938
+ props: {
2939
+ isRoot: true,
2940
+ },
2941
+ })
2942
+ : null;
2943
+ if (!incidentShownOnStatusPage) {
2944
+ throw new NotFoundException("Episode not found");
2945
+ }
2761
2946
  // Merge with existing episode members
2762
2947
  for (const member of episodeMembersForSpecificEpisode) {
2763
2948
  if (!episodeMembers.some((m) => {
@@ -2863,25 +3048,30 @@ class StatusPageAPI extends BaseAPI {
2863
3048
  memberIncidentIds.push(member.incidentId);
2864
3049
  }
2865
3050
  }
2866
- // Fetch incidents with their monitors
3051
+ /*
3052
+ * Fetch incidents with their monitors. Read by id, so the scope is
3053
+ * applied here, in memory: a member incident limited to other status
3054
+ * pages does not tell this page which of its monitors the episode
3055
+ * affects. (An episode viewed by id brings all of its members.)
3056
+ */
2867
3057
  let memberIncidents = [];
2868
3058
  if (memberIncidentIds.length > 0) {
2869
- memberIncidents = await IncidentService.findBy({
2870
- query: {
2871
- _id: QueryHelper.any(memberIncidentIds),
2872
- projectId: statusPage.projectId,
2873
- },
2874
- select: {
2875
- _id: true,
2876
- monitors: {
2877
- _id: true,
3059
+ memberIncidents = this.keepMemberIncidentsInScope({
3060
+ incidents: await IncidentService.findBy({
3061
+ query: {
3062
+ _id: QueryHelper.any(memberIncidentIds),
3063
+ projectId: statusPage.projectId,
2878
3064
  },
2879
- },
2880
- skip: 0,
2881
- limit: LIMIT_PER_PROJECT,
2882
- props: {
2883
- isRoot: true,
2884
- },
3065
+ select: Object.assign({ _id: true, monitors: {
3066
+ _id: true,
3067
+ } }, INCIDENT_SCOPE_SELECT),
3068
+ skip: 0,
3069
+ limit: LIMIT_PER_PROJECT,
3070
+ props: {
3071
+ isRoot: true,
3072
+ },
3073
+ }),
3074
+ statusPage: statusPage,
2885
3075
  });
2886
3076
  }
2887
3077
  // Build a map of incident ID -> monitors
@@ -3038,6 +3228,11 @@ class StatusPageAPI extends BaseAPI {
3038
3228
  showEpisodesOnStatusPage: true,
3039
3229
  showScheduledMaintenanceEventsOnStatusPage: true,
3040
3230
  showUptimeHistoryInDays: true,
3231
+ /*
3232
+ * Read by IncidentStatusPageScope to decide which incidents the
3233
+ * overview shows. Not public: the overview removes it again.
3234
+ */
3235
+ onlyShowScopedIncidents: true,
3041
3236
  },
3042
3237
  props: {
3043
3238
  isRoot: true,
@@ -3386,24 +3581,27 @@ class StatusPageAPI extends BaseAPI {
3386
3581
  return state.id;
3387
3582
  });
3388
3583
  if (statusPage.showIncidentsOnStatusPage) {
3389
- activeIncidents = await IncidentService.findBy({
3390
- query: {
3391
- monitors: monitorsOnStatusPage,
3392
- currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
3393
- isVisibleOnStatusPage: true,
3394
- projectId: statusPage.projectId,
3395
- },
3396
- select: select,
3397
- sort: {
3398
- declaredAt: SortOrder.Descending,
3399
- createdAt: SortOrder.Descending,
3400
- },
3401
- skip: 0,
3402
- limit: LIMIT_PER_PROJECT,
3403
- props: {
3404
- isRoot: true,
3405
- },
3406
- });
3584
+ // Only the incidents in scope for this page; see getIncidents.
3585
+ activeIncidents =
3586
+ await IncidentStatusPageScope.findIncidentsForStatusPage({
3587
+ statusPage: statusPage,
3588
+ query: {
3589
+ monitors: monitorsOnStatusPage,
3590
+ currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
3591
+ isVisibleOnStatusPage: true,
3592
+ projectId: statusPage.projectId,
3593
+ },
3594
+ select: select,
3595
+ sort: {
3596
+ declaredAt: SortOrder.Descending,
3597
+ createdAt: SortOrder.Descending,
3598
+ },
3599
+ skip: 0,
3600
+ limit: LIMIT_PER_PROJECT,
3601
+ props: {
3602
+ isRoot: true,
3603
+ },
3604
+ });
3407
3605
  }
3408
3606
  }
3409
3607
  const incidentsOnStatusPage = activeIncidents.map((incident) => {
@@ -3503,8 +3701,15 @@ class StatusPageAPI extends BaseAPI {
3503
3701
  hasActiveEpisodes = activeEpisodeCount.toNumber() > 0;
3504
3702
  }
3505
3703
  if (hasActiveEpisodes) {
3506
- // First, get incidents that have monitors on status page
3507
- const incidentsForEpisodes = await IncidentService.findBy({
3704
+ /*
3705
+ * First, get incidents that have monitors on status page, and are in
3706
+ * scope for it: an episode is shown through the incidents in it that
3707
+ * the page shows (see getEpisodes). Newest first, so that when
3708
+ * LIMIT_PER_PROJECT cuts the list it keeps the incidents most likely
3709
+ * to be in an active episode.
3710
+ */
3711
+ const incidentsForEpisodes = await IncidentStatusPageScope.findIncidentsForStatusPage({
3712
+ statusPage: statusPage,
3508
3713
  query: {
3509
3714
  monitors: monitorsOnStatusPage,
3510
3715
  isVisibleOnStatusPage: true,
@@ -3513,6 +3718,9 @@ class StatusPageAPI extends BaseAPI {
3513
3718
  select: {
3514
3719
  _id: true,
3515
3720
  },
3721
+ sort: {
3722
+ createdAt: SortOrder.Descending,
3723
+ },
3516
3724
  skip: 0,
3517
3725
  limit: LIMIT_PER_PROJECT,
3518
3726
  props: {
@@ -3617,26 +3825,31 @@ class StatusPageAPI extends BaseAPI {
3617
3825
  memberIncidentIds.push(member.incidentId);
3618
3826
  }
3619
3827
  }
3620
- // Fetch incidents with monitors
3828
+ /*
3829
+ * Fetch incidents with monitors. Read by id, so the scope is
3830
+ * applied in memory, as in getEpisodes - these members came from
3831
+ * incidents in scope, and this keeps it that way should that
3832
+ * change.
3833
+ */
3621
3834
  let memberIncidents = [];
3622
3835
  if (memberIncidentIds.length > 0) {
3623
- memberIncidents = await IncidentService.findBy({
3624
- query: {
3625
- _id: QueryHelper.any(memberIncidentIds),
3626
- isVisibleOnStatusPage: true,
3627
- projectId: statusPage.projectId,
3628
- },
3629
- select: {
3630
- _id: true,
3631
- monitors: {
3632
- _id: true,
3836
+ memberIncidents = this.keepMemberIncidentsInScope({
3837
+ incidents: await IncidentService.findBy({
3838
+ query: {
3839
+ _id: QueryHelper.any(memberIncidentIds),
3840
+ isVisibleOnStatusPage: true,
3841
+ projectId: statusPage.projectId,
3633
3842
  },
3634
- },
3635
- skip: 0,
3636
- limit: LIMIT_PER_PROJECT,
3637
- props: {
3638
- isRoot: true,
3639
- },
3843
+ select: Object.assign({ _id: true, monitors: {
3844
+ _id: true,
3845
+ } }, INCIDENT_SCOPE_SELECT),
3846
+ skip: 0,
3847
+ limit: LIMIT_PER_PROJECT,
3848
+ props: {
3849
+ isRoot: true,
3850
+ },
3851
+ }),
3852
+ statusPage: statusPage,
3640
3853
  });
3641
3854
  }
3642
3855
  // Build incident -> monitors map
@@ -3937,39 +4150,47 @@ class StatusPageAPI extends BaseAPI {
3937
4150
  let timelineIncidents = [];
3938
4151
  if (monitorsOnStatusPage.length > 0 &&
3939
4152
  statusPage.showIncidentsOnStatusPage) {
3940
- timelineIncidents = await IncidentService.findBy({
3941
- query: {
3942
- monitors: monitorsOnStatusPage,
3943
- declaredAt: QueryHelper.inBetween(startDate, endDate),
3944
- isVisibleOnStatusPage: true,
3945
- projectId: statusPage.projectId,
3946
- },
3947
- select: {
3948
- _id: true,
3949
- title: true,
3950
- declaredAt: true,
3951
- incidentSeverity: {
3952
- name: true,
3953
- color: true,
4153
+ /*
4154
+ * Only the incidents in scope for this page; see getIncidents. The
4155
+ * uptime bars stay as they are - a monitor's status is shared by every
4156
+ * page that lists it - but an incident limited to other pages is not
4157
+ * named in their tooltips.
4158
+ */
4159
+ timelineIncidents =
4160
+ await IncidentStatusPageScope.findIncidentsForStatusPage({
4161
+ statusPage: statusPage,
4162
+ query: {
4163
+ monitors: monitorsOnStatusPage,
4164
+ declaredAt: QueryHelper.inBetween(startDate, endDate),
4165
+ isVisibleOnStatusPage: true,
4166
+ projectId: statusPage.projectId,
3954
4167
  },
3955
- currentIncidentState: {
4168
+ select: {
3956
4169
  _id: true,
3957
- name: true,
3958
- color: true,
4170
+ title: true,
4171
+ declaredAt: true,
4172
+ incidentSeverity: {
4173
+ name: true,
4174
+ color: true,
4175
+ },
4176
+ currentIncidentState: {
4177
+ _id: true,
4178
+ name: true,
4179
+ color: true,
4180
+ },
4181
+ monitors: {
4182
+ _id: true,
4183
+ },
3959
4184
  },
3960
- monitors: {
3961
- _id: true,
4185
+ sort: {
4186
+ declaredAt: SortOrder.Descending,
3962
4187
  },
3963
- },
3964
- sort: {
3965
- declaredAt: SortOrder.Descending,
3966
- },
3967
- skip: 0,
3968
- limit: LIMIT_PER_PROJECT,
3969
- props: {
3970
- isRoot: true,
3971
- },
3972
- });
4188
+ skip: 0,
4189
+ limit: LIMIT_PER_PROJECT,
4190
+ props: {
4191
+ isRoot: true,
4192
+ },
4193
+ });
3973
4194
  }
3974
4195
  const overallStatus = StatusPageService.getOverallMonitorStatus({
3975
4196
  statusPageResources,
@@ -3999,12 +4220,17 @@ class StatusPageAPI extends BaseAPI {
3999
4220
  statusPage: (() => {
4000
4221
  const statusPageJson = BaseModel.toJSONObject(statusPage, StatusPage);
4001
4222
  delete statusPageJson["projectId"];
4223
+ /*
4224
+ * Read only to decide which incidents the overview shows; the page
4225
+ * itself has no use for it.
4226
+ */
4227
+ delete statusPageJson["onlyShowScopedIncidents"];
4002
4228
  return statusPageJson;
4003
4229
  })(),
4004
4230
  scheduledMaintenanceStateTimelines: BaseModel.toJSONArray(scheduledMaintenanceStateTimelines, ScheduledMaintenanceStateTimeline),
4005
4231
  monitorGroupCurrentStatuses: JSONFunctions.serialize(monitorGroupCurrentStatuses),
4006
4232
  monitorsInGroup: JSONFunctions.serialize(monitorsInGroup),
4007
- timelineIncidents: BaseModel.toJSONArray(timelineIncidents, Incident),
4233
+ timelineIncidents: this.serializeIncidentsForStatusPage(timelineIncidents),
4008
4234
  };
4009
4235
  return response;
4010
4236
  }
@@ -4214,6 +4440,8 @@ class StatusPageAPI extends BaseAPI {
4214
4440
  _id: true,
4215
4441
  projectId: true,
4216
4442
  showIncidentsOnStatusPage: true,
4443
+ // Read by IncidentStatusPageScope to decide which incidents it shows.
4444
+ onlyShowScopedIncidents: true,
4217
4445
  },
4218
4446
  props: {
4219
4447
  isRoot: true,
@@ -4230,7 +4458,9 @@ class StatusPageAPI extends BaseAPI {
4230
4458
  if (!monitorsOnStatusPage || monitorsOnStatusPage.length === 0) {
4231
4459
  throw new NotFoundException("Attachment not found");
4232
4460
  }
4233
- const incident = await IncidentService.findOneBy({
4461
+ // Only an incident this page shows: on its monitors and in its scope.
4462
+ const incident = await IncidentStatusPageScope.findOneIncidentForStatusPage({
4463
+ statusPage: statusPage,
4234
4464
  query: {
4235
4465
  _id: incidentId.toString(),
4236
4466
  projectId: statusPage.projectId,
@@ -4304,6 +4534,8 @@ class StatusPageAPI extends BaseAPI {
4304
4534
  _id: true,
4305
4535
  projectId: true,
4306
4536
  showIncidentsOnStatusPage: true,
4537
+ // Read by IncidentStatusPageScope to decide which incidents it shows.
4538
+ onlyShowScopedIncidents: true,
4307
4539
  },
4308
4540
  props: {
4309
4541
  isRoot: true,
@@ -4321,7 +4553,9 @@ class StatusPageAPI extends BaseAPI {
4321
4553
  if (!monitorsOnStatusPage || monitorsOnStatusPage.length === 0) {
4322
4554
  throw new NotFoundException("Attachment not found");
4323
4555
  }
4324
- const incident = await IncidentService.findOneBy({
4556
+ // Only an incident this page shows: on its monitors and in its scope.
4557
+ const incident = await IncidentStatusPageScope.findOneIncidentForStatusPage({
4558
+ statusPage: statusPage,
4325
4559
  query: {
4326
4560
  _id: incidentId.toString(),
4327
4561
  projectId: statusPage.projectId,
@@ -4407,6 +4641,8 @@ class StatusPageAPI extends BaseAPI {
4407
4641
  _id: true,
4408
4642
  projectId: true,
4409
4643
  showEpisodesOnStatusPage: true,
4644
+ // Read by IncidentStatusPageScope to decide which incidents it shows.
4645
+ onlyShowScopedIncidents: true,
4410
4646
  },
4411
4647
  props: {
4412
4648
  isRoot: true,
@@ -4449,8 +4685,13 @@ class StatusPageAPI extends BaseAPI {
4449
4685
  .filter((id) => {
4450
4686
  return Boolean(id);
4451
4687
  });
4452
- // Check if any of the incidents are linked to monitors on the status page
4453
- const incident = await IncidentService.findOneBy({
4688
+ /*
4689
+ * Check if any of the incidents are linked to monitors on the status
4690
+ * page, and in scope for it: an episode whose incidents are all limited
4691
+ * to other status pages is not this page's to serve.
4692
+ */
4693
+ const incident = await IncidentStatusPageScope.findOneIncidentForStatusPage({
4694
+ statusPage: statusPage,
4454
4695
  query: {
4455
4696
  _id: QueryHelper.any(incidentIds),
4456
4697
  projectId: statusPage.projectId,