@oneuptime/common 14.0.7 → 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 (440) 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/StatusPage.ts +55 -0
  6. package/Models/DatabaseModels/StatusPageSubscriber.ts +128 -0
  7. package/Server/API/IncidentAPI.ts +162 -0
  8. package/Server/API/StatusPageAPI.ts +487 -171
  9. package/Server/API/StatusPageSubscriberAPI.ts +63 -15
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.ts +49 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.ts +108 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.ts +33 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.ts +51 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.ts +168 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.ts +44 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  18. package/Server/Infrastructure/Semaphore.ts +10 -0
  19. package/Server/Middleware/IdentityRateLimit.ts +108 -3
  20. package/Server/Middleware/SubscriberNotificationPreviewRateLimit.ts +237 -0
  21. package/Server/Middleware/SubscriberNotificationTestSendRateLimit.ts +304 -0
  22. package/Server/Services/DatabaseService.ts +93 -10
  23. package/Server/Services/IncidentCustomFieldService.ts +276 -1
  24. package/Server/Services/IncidentEpisodePublicNoteService.ts +28 -0
  25. package/Server/Services/IncidentEpisodeService.ts +15 -0
  26. package/Server/Services/IncidentEpisodeStateTimelineService.ts +27 -1
  27. package/Server/Services/IncidentPublicNoteService.ts +30 -1
  28. package/Server/Services/IncidentService.ts +1535 -65
  29. package/Server/Services/IncidentStateTimelineService.ts +13 -0
  30. package/Server/Services/IncidentTemplateService.ts +109 -0
  31. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +30 -1
  32. package/Server/Services/ScheduledMaintenanceService.ts +68 -18
  33. package/Server/Services/SmsService.ts +10 -0
  34. package/Server/Services/StatusPageService.ts +65 -8
  35. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +299 -37
  36. package/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.ts +129 -0
  37. package/Server/Services/StatusPageSubscriberService.ts +1161 -64
  38. package/Server/Types/Markdown.ts +153 -6
  39. package/Server/Types/Workflow/Components/Email.ts +61 -2
  40. package/Server/Utils/CustomField/CustomFieldRename.ts +378 -0
  41. package/Server/Utils/DataSource/EgressGuard.ts +66 -21
  42. package/Server/Utils/Mail/PinnedSmtpSocket.ts +130 -0
  43. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +10 -0
  44. package/Server/Utils/SSRFProtection.ts +76 -16
  45. package/Server/Utils/StatusPage/IncidentStatusPageScope.ts +948 -0
  46. package/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.ts +656 -0
  47. package/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.ts +805 -0
  48. package/Server/Utils/StatusPage/StatusPageExclusion.ts +18 -0
  49. package/Server/Utils/StatusPage/StatusPageReadAccess.ts +125 -0
  50. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.ts +151 -0
  51. package/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.ts +70 -0
  52. package/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.ts +421 -0
  53. package/Server/Utils/StatusPage/SubscriberNotificationClaim.ts +107 -0
  54. package/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.ts +1049 -0
  55. package/Server/Utils/StatusPage/SubscriberNotificationFanOut.ts +180 -0
  56. package/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.ts +839 -0
  57. package/Server/Utils/StatusPage/SubscriberNotificationResendAccess.ts +393 -0
  58. package/Server/Utils/StatusPage/SubscriberNotificationRunLimit.ts +246 -0
  59. package/Server/Utils/StatusPage/SubscriberNotificationTiming.ts +133 -0
  60. package/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.ts +215 -0
  61. package/Server/Utils/StatusPageResource.ts +51 -20
  62. package/Server/Utils/VM/VMAPI.ts +1 -0
  63. package/Server/Utils/VM/VMRunner.ts +10 -0
  64. package/Server/Utils/Workflow/OAuth2TokenClient.ts +10 -0
  65. package/Server/Views/Partials/SensitiveUrlToken.ejs +8 -5
  66. package/Tests/App/Accounts/RegisterVerifyEmail.test.tsx +748 -0
  67. package/Tests/App/Accounts/ResendVerificationEmail.test.tsx +1222 -0
  68. package/Tests/App/Accounts/VerificationEmailResendUtils.test.ts +797 -0
  69. package/Tests/App/Accounts/VerifyEmailPending.test.tsx +538 -0
  70. package/Tests/App/Accounts/VerifyEmailResend.test.tsx +779 -0
  71. package/Tests/App/Dashboard/CustomFieldsSettingsIncidentOptions.test.tsx +357 -0
  72. package/Tests/App/Dashboard/EventNotes.test.tsx +776 -0
  73. package/Tests/App/Dashboard/EventNotesPages.test.tsx +119 -0
  74. package/Tests/App/Dashboard/EventNotesUtil.test.ts +116 -17
  75. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +242 -5
  76. package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +91 -1
  77. package/Tests/App/Dashboard/IncidentCreateDetailsStep.test.tsx +643 -0
  78. package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +8 -1
  79. package/Tests/App/Dashboard/IncidentCreateStatusPageScope.test.tsx +601 -0
  80. package/Tests/App/Dashboard/IncidentCustomFieldTemplateVariables.test.tsx +386 -0
  81. package/Tests/App/Dashboard/IncidentNoteTemplatePlaceholders.test.tsx +124 -0
  82. package/Tests/App/Dashboard/IncidentNoteTemplateVariablesLoader.test.ts +305 -0
  83. package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +112 -0
  84. package/Tests/App/Dashboard/IncidentSettingsRenotifyOnPublish.test.tsx +674 -0
  85. package/Tests/App/Dashboard/IncidentSettingsStatusPageScope.test.tsx +1051 -0
  86. package/Tests/App/Dashboard/IncidentTemplateCustomFields.test.tsx +384 -0
  87. package/Tests/App/Dashboard/IncidentTemplateStatusPageScopeCard.test.tsx +215 -0
  88. package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +113 -1
  89. package/Tests/App/Dashboard/SubscriberAudienceSummary.test.tsx +512 -0
  90. package/Tests/App/Dashboard/SubscriberNotificationPreviewModal.test.tsx +837 -0
  91. package/Tests/App/Dashboard/SubscriberNotificationStatusLabel.test.tsx +88 -0
  92. package/Tests/App/Dashboard/SubscriberNotificationStatusResend.test.tsx +473 -0
  93. package/Tests/App/Dashboard/SubscriberTemplateLivePreview.test.tsx +196 -0
  94. package/Tests/App/Dashboard/TeamAddedSubscribersUnsubscribedNotice.test.tsx +248 -0
  95. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +64 -3
  96. package/Tests/App/StatusPage/StatusPageUnsubscribePage.test.tsx +412 -0
  97. package/Tests/Models/IncidentCustomFieldApiSchemaContract.test.ts +217 -0
  98. package/Tests/Models/IncidentCustomFieldCreateSettingsColumns.test.ts +252 -0
  99. package/Tests/Models/StatusPageSubscriberUnsubscribeColumns.test.ts +218 -0
  100. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +7 -0
  101. package/Tests/Server/API/IncidentSubscriberAudienceAPI.test.ts +1408 -0
  102. package/Tests/Server/API/StatusPageIncidentScope.test.ts +1868 -0
  103. package/Tests/Server/API/StatusPageIncidentScopePostgres.test.ts +790 -0
  104. package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +10 -1
  105. package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +206 -18
  106. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +14 -2
  107. package/Tests/Server/API/StatusPageSubscriberCreateResponse.test.ts +217 -0
  108. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +103 -2
  109. package/Tests/Server/API/StatusPageUnsubscribeAPI.test.ts +396 -0
  110. package/Tests/Server/Infrastructure/Postgres/AddIncidentStatusPageScopeMigration.test.ts +395 -0
  111. package/Tests/Server/Infrastructure/Postgres/AddIncidentTemplateStatusPageScopeFlagMigration.test.ts +150 -0
  112. package/Tests/Server/Infrastructure/Postgres/AddSubscriberNotificationClaimedAtMigration.test.ts +156 -0
  113. package/Tests/Server/Infrastructure/Postgres/SkipHiddenIncidentCreatedNotificationsMigration.test.ts +410 -0
  114. package/Tests/Server/Middleware/IdentityRateLimit.test.ts +551 -1
  115. package/Tests/Server/Middleware/SubscriberNotificationPreviewRateLimit.test.ts +238 -0
  116. package/Tests/Server/Middleware/SubscriberNotificationTestSendRateLimit.test.ts +245 -0
  117. package/Tests/Server/Services/AddStatusPageSubscriberUnsubscribeTokenMigration.test.ts +152 -0
  118. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +221 -36
  119. package/Tests/Server/Services/DatabaseServiceCompareAndSetColumns.test.ts +191 -0
  120. package/Tests/Server/Services/IncidentCreatedRenotifyOnPublish.test.ts +681 -0
  121. package/Tests/Server/Services/IncidentCreatedResend.test.ts +775 -0
  122. package/Tests/Server/Services/IncidentCustomFieldRenamePostgres.test.ts +713 -0
  123. package/Tests/Server/Services/IncidentCustomFieldService.test.ts +751 -0
  124. package/Tests/Server/Services/IncidentCustomFieldValueValidation.test.ts +556 -0
  125. package/Tests/Server/Services/IncidentStatusPageScope.test.ts +2971 -0
  126. package/Tests/Server/Services/IncidentStatusPageScopeFeed.test.ts +131 -0
  127. package/Tests/Server/Services/IncidentStatusPageScopePostgres.test.ts +625 -0
  128. package/Tests/Server/Services/IncidentTemplateCustomFieldsCopy.test.ts +311 -0
  129. package/Tests/Server/Services/IncidentTemplateStatusPageScope.test.ts +363 -0
  130. package/Tests/Server/Services/PublicNoteEditFeedEventType.test.ts +213 -0
  131. package/Tests/Server/Services/PublicNoteResendAccess.test.ts +994 -0
  132. package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +289 -9
  133. package/Tests/Server/Services/SmsServiceFailIfNotSent.test.ts +67 -0
  134. package/Tests/Server/Services/StatusPageReportIncidentCounts.test.ts +610 -0
  135. package/Tests/Server/Services/StatusPageSubscriberAudienceCounts.test.ts +201 -0
  136. package/Tests/Server/Services/StatusPageSubscriberAudienceCountsPostgres.test.ts +271 -0
  137. package/Tests/Server/Services/StatusPageSubscriberConfirmationTemplateEscaping.test.ts +200 -0
  138. package/Tests/Server/Services/StatusPageSubscriberNotificationLogRedaction.test.ts +54 -0
  139. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +37 -0
  140. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateIncidentRecordAccess.test.ts +772 -0
  141. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookup.test.ts +506 -0
  142. package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateLookupPostgres.test.ts +261 -0
  143. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +21 -5
  144. package/Tests/Server/Services/StatusPageSubscriberUnsubscribe.test.ts +1618 -0
  145. package/Tests/Server/Services/StatusPageSubscriberUnsubscribeLinkSenders.test.ts +373 -0
  146. package/Tests/Server/Services/StatusPageSubscriberUnsubscribePostgres.test.ts +388 -0
  147. package/Tests/Server/Services/SubscriberNotificationInFlightRequeue.test.ts +500 -0
  148. package/Tests/Server/Types/MarkdownEmailLinks.test.ts +320 -0
  149. package/Tests/Server/Types/Workflow/Components/Email.test.ts +491 -1
  150. package/Tests/Server/Types/Workflow/Components/EmailSmtpPinning.test.ts +398 -0
  151. package/Tests/Server/Utils/CustomField/CustomFieldRename.test.ts +586 -0
  152. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +246 -10
  153. package/Tests/Server/Utils/DataSource/EgressGuardDeploymentConfig.test.ts +226 -0
  154. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +131 -0
  155. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +4 -0
  156. package/Tests/Server/Utils/EgressPolicyEnvironment.ts +45 -0
  157. package/Tests/Server/Utils/Mail/PinnedSmtpSocket.test.ts +409 -0
  158. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +23 -0
  159. package/Tests/Server/Utils/SSRFProtection.test.ts +4 -0
  160. package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +4 -0
  161. package/Tests/Server/Utils/SSRFProtectionPrivateNetwork.test.ts +4 -0
  162. package/Tests/Server/Utils/SSRFProtectionResolutionDetail.test.ts +314 -0
  163. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScope.test.ts +1699 -0
  164. package/Tests/Server/Utils/StatusPage/IncidentStatusPageScopeQueriesPostgres.test.ts +508 -0
  165. package/Tests/Server/Utils/StatusPage/IncidentSubscriberAudienceCallerPermission.test.ts +246 -0
  166. package/Tests/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.test.ts +1348 -0
  167. package/Tests/Server/Utils/StatusPage/StatusPageReadAccess.test.ts +221 -0
  168. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.test.ts +132 -0
  169. package/Tests/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.test.ts +155 -0
  170. package/Tests/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.test.ts +708 -0
  171. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaim.test.ts +255 -0
  172. package/Tests/Server/Utils/StatusPage/SubscriberNotificationClaimPostgres.test.ts +528 -0
  173. package/Tests/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.test.ts +1238 -0
  174. package/Tests/Server/Utils/StatusPage/SubscriberNotificationFanOut.test.ts +472 -0
  175. package/Tests/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.test.ts +1168 -0
  176. package/Tests/Server/Utils/StatusPage/SubscriberNotificationRunLimit.test.ts +275 -0
  177. package/Tests/Server/Utils/StatusPage/SubscriberNotificationTiming.test.ts +131 -0
  178. package/Tests/Server/Utils/StatusPageResource.test.ts +98 -0
  179. package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +17 -0
  180. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +75 -0
  181. package/Tests/Types/CustomField/CustomFieldSavedViews.test.ts +247 -0
  182. package/Tests/Types/CustomField/CustomFieldTemplateValues.test.ts +186 -0
  183. package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +48 -0
  184. package/Tests/Types/CustomField/CustomFieldValueFormat.test.ts +215 -0
  185. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +343 -0
  186. package/Tests/Types/CustomField/CustomFieldVariableKey.test.ts +364 -0
  187. package/Tests/Types/SafeHtml.test.ts +102 -0
  188. package/Tests/Types/StatusPage/IncidentCreatedRenotify.test.ts +419 -0
  189. package/Tests/Types/StatusPage/IncidentCreatedResend.test.ts +118 -0
  190. package/Tests/Types/StatusPage/IncidentScopeAddedPagesNotification.test.ts +558 -0
  191. package/Tests/Types/StatusPage/IncidentSubscriberAudience.test.ts +284 -0
  192. package/Tests/Types/StatusPage/StatusPageSubscriberUnsubscribe.test.ts +341 -0
  193. package/Tests/Types/StatusPage/SubscriberNotificationPreview.test.ts +196 -0
  194. package/Tests/Types/StatusPage/SubscriberNotificationResend.test.ts +201 -0
  195. package/Tests/Types/StatusPage/SubscriberNotificationTemplateCompiler.test.ts +277 -0
  196. package/Tests/Types/StatusPage/SubscriberNotificationTemplatePreview.test.ts +179 -0
  197. package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +324 -0
  198. package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +60 -13
  199. package/Tests/UI/Components/CustomFields/CustomFieldFormFields.test.ts +432 -0
  200. package/Tests/UI/Components/CustomFields/CustomFieldModelFormFields.test.tsx +685 -0
  201. package/Tests/UI/Components/CustomFields/CustomFieldsDetailOrder.test.tsx +469 -0
  202. package/Tests/UI/Components/Forms/ModelFormCustomFieldInputs.test.tsx +325 -0
  203. package/Tests/UI/Components/Forms/ModelFormDropdownOptionsRefused.test.tsx +358 -0
  204. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +22 -0
  205. package/Tests/UI/Monitor/NetworkPathView.test.tsx +289 -0
  206. package/Tests/Utils/Incident/IncidentNoteTemplateVariables.test.ts +623 -0
  207. package/Tests/Utils/Markdown/MarkdownEscape.test.ts +89 -0
  208. package/Tests/Utils/ProbeNetworkFailureUtil.test.ts +992 -0
  209. package/Tests/Utils/RequestFailedDetails.test.ts +575 -0
  210. package/Types/CustomField/CustomFieldOrder.ts +55 -0
  211. package/Types/CustomField/CustomFieldSavedViews.ts +225 -0
  212. package/Types/CustomField/CustomFieldTemplateMerge.ts +71 -0
  213. package/Types/CustomField/CustomFieldType.ts +14 -0
  214. package/Types/CustomField/CustomFieldValueFormat.ts +161 -0
  215. package/Types/CustomField/CustomFieldValueValidator.ts +488 -0
  216. package/Types/CustomField/CustomFieldVariableKey.ts +211 -0
  217. package/Types/SafeHtml.ts +93 -0
  218. package/Types/StatusPage/IncidentCreatedRenotify.ts +224 -0
  219. package/Types/StatusPage/IncidentCreatedResend.ts +108 -0
  220. package/Types/StatusPage/IncidentScopeAddedPagesNotification.ts +343 -0
  221. package/Types/StatusPage/IncidentSubscriberAudience.ts +332 -0
  222. package/Types/StatusPage/StatusPageSubscriberUnsubscribe.ts +405 -0
  223. package/Types/StatusPage/SubscriberNotificationInterruption.ts +32 -0
  224. package/Types/StatusPage/SubscriberNotificationPreview.ts +322 -0
  225. package/Types/StatusPage/SubscriberNotificationResend.ts +132 -0
  226. package/Types/StatusPage/SubscriberNotificationTemplateCompiler.ts +180 -0
  227. package/Types/StatusPage/SubscriberNotificationTemplatePreview.ts +301 -0
  228. package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +217 -4
  229. package/Types/StatusPage/SubscriberUpdateNotification.ts +32 -11
  230. package/UI/Components/CustomFields/CustomFieldFormFields.ts +317 -0
  231. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +395 -0
  232. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +95 -55
  233. package/UI/Components/Forms/ModelForm.tsx +70 -8
  234. package/UI/Components/Forms/Validation.ts +1 -1
  235. package/UI/Components/ModelFormModal/ModelFormModal.tsx +2 -4
  236. package/UI/Components/ModelTable/AnalyticsModelTable.tsx +2 -6
  237. package/UI/Components/ModelTable/BaseModelTable.tsx +9 -8
  238. package/UI/Components/ModelTable/CustomFieldColumns.tsx +14 -1
  239. package/UI/Components/ModelTable/ModelTable.tsx +6 -7
  240. package/UI/Utils/SensitiveUrlToken.ts +10 -5
  241. package/Utils/API.ts +247 -2
  242. package/Utils/Incident/IncidentNoteTemplateVariables.ts +304 -0
  243. package/Utils/Markdown/MarkdownEscape.ts +65 -0
  244. package/Utils/ProbeNetworkFailureUtil.ts +673 -0
  245. package/build/dist/Models/DatabaseModels/Incident.js +267 -1
  246. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  247. package/build/dist/Models/DatabaseModels/IncidentCustomField.js +207 -1
  248. package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
  249. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +31 -0
  250. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  251. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +124 -1
  252. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  253. package/build/dist/Models/DatabaseModels/StatusPage.js +56 -0
  254. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  255. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +131 -0
  256. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  257. package/build/dist/Server/API/IncidentAPI.js +100 -1
  258. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  259. package/build/dist/Server/API/StatusPageAPI.js +417 -176
  260. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  261. package/build/dist/Server/API/StatusPageSubscriberAPI.js +52 -13
  262. package/build/dist/Server/API/StatusPageSubscriberAPI.js.map +1 -1
  263. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js +41 -0
  264. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795300000000-SkipHiddenIncidentCreatedNotifications.js.map +1 -0
  265. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js +57 -0
  266. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795400000000-AddIncidentStatusPageScope.js.map +1 -0
  267. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js +23 -0
  268. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795500000000-AddIncidentTemplateStatusPageScopeFlag.js.map +1 -0
  269. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js +40 -0
  270. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795600000000-AddStatusPageSubscriberUnsubscribeToken.js.map +1 -0
  271. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js +26 -0
  272. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795700000000-AddStatusPageSubscriberIsAddedByTeam.js.map +1 -0
  273. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js +89 -0
  274. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795800000000-AddIncidentCustomFieldCreateAndNotificationSettings.js.map +1 -0
  275. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js +29 -0
  276. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795900000000-AddSubscriberNotificationClaimedAt.js.map +1 -0
  277. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  278. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  279. package/build/dist/Server/Infrastructure/Semaphore.js +3 -0
  280. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  281. package/build/dist/Server/Middleware/IdentityRateLimit.js +89 -3
  282. package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -1
  283. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js +149 -0
  284. package/build/dist/Server/Middleware/SubscriberNotificationPreviewRateLimit.js.map +1 -0
  285. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js +190 -0
  286. package/build/dist/Server/Middleware/SubscriberNotificationTestSendRateLimit.js.map +1 -0
  287. package/build/dist/Server/Services/DatabaseService.js +56 -7
  288. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  289. package/build/dist/Server/Services/IncidentCustomFieldService.js +204 -8
  290. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  291. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
  292. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  293. package/build/dist/Server/Services/IncidentEpisodeService.js +14 -0
  294. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  295. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js +26 -0
  296. package/build/dist/Server/Services/IncidentEpisodeStateTimelineService.js.map +1 -1
  297. package/build/dist/Server/Services/IncidentPublicNoteService.js +26 -1
  298. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  299. package/build/dist/Server/Services/IncidentService.js +1052 -0
  300. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  301. package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
  302. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  303. package/build/dist/Server/Services/IncidentTemplateService.js +83 -0
  304. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  305. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +26 -1
  306. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  307. package/build/dist/Server/Services/ScheduledMaintenanceService.js +47 -17
  308. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  309. package/build/dist/Server/Services/SmsService.js +2 -22
  310. package/build/dist/Server/Services/SmsService.js.map +1 -1
  311. package/build/dist/Server/Services/StatusPageService.js +47 -9
  312. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  313. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +237 -32
  314. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  315. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js +109 -0
  316. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateStatusPageService.js.map +1 -1
  317. package/build/dist/Server/Services/StatusPageSubscriberService.js +925 -99
  318. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  319. package/build/dist/Server/Types/Markdown.js +110 -6
  320. package/build/dist/Server/Types/Markdown.js.map +1 -1
  321. package/build/dist/Server/Types/Workflow/Components/Email.js +55 -6
  322. package/build/dist/Server/Types/Workflow/Components/Email.js.map +1 -1
  323. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js +211 -0
  324. package/build/dist/Server/Utils/CustomField/CustomFieldRename.js.map +1 -0
  325. package/build/dist/Server/Utils/DataSource/EgressGuard.js +41 -12
  326. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  327. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js +101 -0
  328. package/build/dist/Server/Utils/Mail/PinnedSmtpSocket.js.map +1 -0
  329. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
  330. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  331. package/build/dist/Server/Utils/SSRFProtection.js +48 -6
  332. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  333. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js +631 -0
  334. package/build/dist/Server/Utils/StatusPage/IncidentStatusPageScope.js.map +1 -0
  335. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js +403 -0
  336. package/build/dist/Server/Utils/StatusPage/IncidentSubscriberAudienceBuilder.js.map +1 -0
  337. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js +451 -0
  338. package/build/dist/Server/Utils/StatusPage/IncidentTemplateVariableBuilder.js.map +1 -0
  339. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js +13 -0
  340. package/build/dist/Server/Utils/StatusPage/StatusPageExclusion.js.map +1 -0
  341. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js +91 -0
  342. package/build/dist/Server/Utils/StatusPage/StatusPageReadAccess.js.map +1 -0
  343. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js +101 -0
  344. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeNotice.js.map +1 -0
  345. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js +47 -0
  346. package/build/dist/Server/Utils/StatusPage/StatusPageSubscriberUnsubscribeToken.js.map +1 -0
  347. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js +273 -0
  348. package/build/dist/Server/Utils/StatusPage/SubscriberIncidentEmailBuilder.js.map +1 -0
  349. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js +73 -0
  350. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationClaim.js.map +1 -0
  351. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js +722 -0
  352. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationDeliveryRecord.js.map +1 -0
  353. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js +102 -0
  354. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationFanOut.js.map +1 -0
  355. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js +527 -0
  356. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationPreviewBuilder.js.map +1 -0
  357. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js +278 -0
  358. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationResendAccess.js.map +1 -0
  359. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js +137 -0
  360. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationRunLimit.js.map +1 -0
  361. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js +103 -0
  362. package/build/dist/Server/Utils/StatusPage/SubscriberNotificationTiming.js.map +1 -0
  363. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js +114 -0
  364. package/build/dist/Server/Utils/StatusPage/SubscriberTemplateIncidentRecordAccess.js.map +1 -0
  365. package/build/dist/Server/Utils/StatusPageResource.js +39 -7
  366. package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
  367. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  368. package/build/dist/Server/Utils/VM/VMRunner.js +1 -0
  369. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  370. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +3 -0
  371. package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -1
  372. package/build/dist/Types/CustomField/CustomFieldOrder.js +36 -0
  373. package/build/dist/Types/CustomField/CustomFieldOrder.js.map +1 -0
  374. package/build/dist/Types/CustomField/CustomFieldSavedViews.js +135 -0
  375. package/build/dist/Types/CustomField/CustomFieldSavedViews.js.map +1 -0
  376. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js +27 -0
  377. package/build/dist/Types/CustomField/CustomFieldTemplateMerge.js.map +1 -0
  378. package/build/dist/Types/CustomField/CustomFieldType.js +12 -0
  379. package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
  380. package/build/dist/Types/CustomField/CustomFieldValueFormat.js +126 -0
  381. package/build/dist/Types/CustomField/CustomFieldValueFormat.js.map +1 -0
  382. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +280 -0
  383. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -0
  384. package/build/dist/Types/CustomField/CustomFieldVariableKey.js +150 -0
  385. package/build/dist/Types/CustomField/CustomFieldVariableKey.js.map +1 -0
  386. package/build/dist/Types/SafeHtml.js +84 -0
  387. package/build/dist/Types/SafeHtml.js.map +1 -0
  388. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js +117 -0
  389. package/build/dist/Types/StatusPage/IncidentCreatedRenotify.js.map +1 -0
  390. package/build/dist/Types/StatusPage/IncidentCreatedResend.js +86 -0
  391. package/build/dist/Types/StatusPage/IncidentCreatedResend.js.map +1 -0
  392. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js +246 -0
  393. package/build/dist/Types/StatusPage/IncidentScopeAddedPagesNotification.js.map +1 -0
  394. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js +183 -0
  395. package/build/dist/Types/StatusPage/IncidentSubscriberAudience.js.map +1 -0
  396. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js +277 -0
  397. package/build/dist/Types/StatusPage/StatusPageSubscriberUnsubscribe.js.map +1 -0
  398. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js +27 -0
  399. package/build/dist/Types/StatusPage/SubscriberNotificationInterruption.js.map +1 -0
  400. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js +190 -0
  401. package/build/dist/Types/StatusPage/SubscriberNotificationPreview.js.map +1 -0
  402. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js +102 -0
  403. package/build/dist/Types/StatusPage/SubscriberNotificationResend.js.map +1 -0
  404. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js +97 -0
  405. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateCompiler.js.map +1 -0
  406. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js +175 -0
  407. package/build/dist/Types/StatusPage/SubscriberNotificationTemplatePreview.js.map +1 -0
  408. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +144 -4
  409. package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -1
  410. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +29 -9
  411. package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -1
  412. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js +169 -0
  413. package/build/dist/UI/Components/CustomFields/CustomFieldFormFields.js.map +1 -0
  414. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +212 -0
  415. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -0
  416. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +64 -49
  417. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  418. package/build/dist/UI/Components/Forms/ModelForm.js +39 -5
  419. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  420. package/build/dist/UI/Components/Forms/Validation.js +1 -1
  421. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  422. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  423. package/build/dist/UI/Components/ModelTable/AnalyticsModelTable.js.map +1 -1
  424. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +2 -2
  425. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  426. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +13 -1
  427. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  428. package/build/dist/UI/Components/ModelTable/ModelTable.js +1 -1
  429. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  430. package/build/dist/UI/Utils/SensitiveUrlToken.js +10 -5
  431. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -1
  432. package/build/dist/Utils/API.js +177 -1
  433. package/build/dist/Utils/API.js.map +1 -1
  434. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js +155 -0
  435. package/build/dist/Utils/Incident/IncidentNoteTemplateVariables.js.map +1 -0
  436. package/build/dist/Utils/Markdown/MarkdownEscape.js +48 -0
  437. package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -1
  438. package/build/dist/Utils/ProbeNetworkFailureUtil.js +460 -0
  439. package/build/dist/Utils/ProbeNetworkFailureUtil.js.map +1 -0
  440. package/package.json +1 -1
@@ -0,0 +1,656 @@
1
+ import Incident from "../../../Models/DatabaseModels/Incident";
2
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
3
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
4
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
5
+ import DatabaseCommonInteractionPropsUtil, {
6
+ PermissionType,
7
+ } from "../../../Types/BaseDatabase/DatabaseCommonInteractionPropsUtil";
8
+ import Permission, { UserPermission } from "../../../Types/Permission";
9
+ import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
10
+ import Dictionary from "../../../Types/Dictionary";
11
+ import BadDataException from "../../../Types/Exception/BadDataException";
12
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
13
+ import NotFoundException from "../../../Types/Exception/NotFoundException";
14
+ import ObjectID from "../../../Types/ObjectID";
15
+ import IncidentScopeAddedPagesNotification from "../../../Types/StatusPage/IncidentScopeAddedPagesNotification";
16
+ import IncidentSubscriberAudience, {
17
+ IncidentSubscriberAudienceCounts,
18
+ IncidentSubscriberAudienceExcludedStatusPage,
19
+ IncidentSubscriberAudienceExclusionReason,
20
+ IncidentSubscriberAudienceNamedStatusPage,
21
+ IncidentSubscriberAudienceResult,
22
+ IncidentSubscriberAudienceStatusPage,
23
+ } from "../../../Types/StatusPage/IncidentSubscriberAudience";
24
+ import IncidentService from "../../Services/IncidentService";
25
+ import MonitorService from "../../Services/MonitorService";
26
+ import StatusPageService from "../../Services/StatusPageService";
27
+ import StatusPageSubscriberService from "../../Services/StatusPageSubscriberService";
28
+ import QueryHelper from "../../Types/Database/QueryHelper";
29
+ import IncidentStatusPageScope, {
30
+ ExcludedStatusPage,
31
+ ResolvedIncidentStatusPages,
32
+ StatusPageExclusionReason,
33
+ } from "./IncidentStatusPageScope";
34
+
35
+ /*
36
+ * Who an incident's status page notifications would reach (see
37
+ * Common/Types/StatusPage/IncidentSubscriberAudience), for
38
+ * POST /incident/subscriber-audience.
39
+ *
40
+ * Which pages an incident reaches is IncidentStatusPageScope's call, the same
41
+ * as for the subscriber jobs: through its monitors, narrowed to the pages it
42
+ * is limited to, without the pages that only show scoped incidents when it is
43
+ * not scoped. On top of that, a page that does not show incidents at all is
44
+ * skipped by the jobs, so it is reported as left out here too.
45
+ *
46
+ * What the caller learns is bounded by what they may read:
47
+ *
48
+ * - the incident is read with the caller's own permissions (labels, private
49
+ * incidents), and must be in the caller's project;
50
+ * - monitors and status pages named for an incident being declared must be
51
+ * the project's; another project's id is refused, not looked up;
52
+ * - a status page the caller cannot read (status pages are label-scoped, and
53
+ * incident roles do not read them at all) is not named here and its
54
+ * subscribers are not counted - the pages that will be notified that the
55
+ * caller cannot see come back as one number;
56
+ * - subscribers are counted, never read out. No address leaves the server.
57
+ *
58
+ * What that protects is which pages list an incident's monitors, looking
59
+ * ahead: this summary (and the notification preview built on it) is open to
60
+ * anyone who may declare an incident or post a note, before anything is
61
+ * sent. The pages an incident is limited to are part of the incident, like
62
+ * its monitors and like a scheduled maintenance event's status pages: anyone
63
+ * who can read the incident sees their names (the Status Page Scope card,
64
+ * and the incident feed, which names the pages added to and removed from
65
+ * the scope), whether or not they can read those status pages themselves.
66
+ *
67
+ * So is what a send reached, once it has gone out: the notification's
68
+ * status message and its feed item name every page it went to, with what
69
+ * was sent and failed on each (SubscriberNotificationDeliveryRecord), and
70
+ * everyone who can read the incident - or the note - reads them. That is
71
+ * the record of what the incident told whom, which is its purpose; the docs
72
+ * say so (One Status Page per Audience, Permissions). Only the look-ahead
73
+ * here keeps to the pages the caller can read.
74
+ */
75
+
76
+ export type IncidentSubscriberAudienceRequest =
77
+ | {
78
+ projectId: ObjectID;
79
+ props: DatabaseCommonInteractionProps;
80
+ incidentId: ObjectID;
81
+ /*
82
+ * Who a Retry of the incident's 'created' notification reaches: the
83
+ * pages it was already sent to in full (its record,
84
+ * Incident.statusPagesNotifiedOnCreation) are not sent it again, so
85
+ * they are reported as left out, with why, rather than as notified.
86
+ */
87
+ excludeStatusPagesNotifiedOnCreation?: boolean | undefined;
88
+ }
89
+ | {
90
+ projectId: ObjectID;
91
+ props: DatabaseCommonInteractionProps;
92
+ monitorIds: Array<ObjectID>;
93
+ statusPageIds: Array<ObjectID>;
94
+ };
95
+
96
+ /*
97
+ * The audience with the status pages behind it, for a caller that goes on to
98
+ * build what those pages are sent (the notification preview): the pages the
99
+ * incident reaches, as the subscriber jobs load them, and the resources each
100
+ * lists. Never serialized: it holds every page, readable or not, and each
101
+ * page's mail and SMS settings.
102
+ */
103
+ export interface IncidentSubscriberAudienceWithStatusPages {
104
+ audience: IncidentSubscriberAudienceResult;
105
+ resolved: ResolvedIncidentStatusPages;
106
+ }
107
+
108
+ // What decides the audience, once the request is resolved.
109
+ interface AudienceInputs {
110
+ resolved: ResolvedIncidentStatusPages;
111
+ hasMonitors: boolean;
112
+ // The pages the incident is limited to, lower-cased; empty when unscoped.
113
+ scopedStatusPageIds: Array<string>;
114
+ isHiddenFromStatusPages: boolean;
115
+ /*
116
+ * The pages a Retry of the 'created' notification skips, lower-cased;
117
+ * empty unless the request asked (excludeStatusPagesNotifiedOnCreation).
118
+ */
119
+ alreadyNotifiedStatusPageIds: Array<string>;
120
+ }
121
+
122
+ export default class IncidentSubscriberAudienceBuilder {
123
+ /*
124
+ * The roles that may see who an incident's notifications would reach, and
125
+ * preview what they would be sent: the ones that may declare an incident,
126
+ * edit one, or post a public note on one - the places the audience is
127
+ * shown. Which incident, monitors and status pages an answer covers is
128
+ * then bounded by what the caller may read.
129
+ */
130
+ public static readonly PERMISSIONS: ReadonlyArray<Permission> = [
131
+ Permission.ProjectOwner,
132
+ Permission.ProjectAdmin,
133
+ Permission.ProjectMember,
134
+ Permission.IncidentAdmin,
135
+ Permission.IncidentMember,
136
+ Permission.CreateProjectIncident,
137
+ Permission.EditProjectIncident,
138
+ Permission.CreateIncidentPublicNote,
139
+ ];
140
+
141
+ /*
142
+ * Refuses a caller who holds none of PERMISSIONS in the project. Read
143
+ * through getUserPermissions(Allow): the tenant permission list holds
144
+ * grants and denials together, and a team's block entry for one of these
145
+ * must not count as a grant of it.
146
+ */
147
+ public static assertCallerMaySeeAudience(
148
+ props: DatabaseCommonInteractionProps,
149
+ ): void {
150
+ if (props.isMasterAdmin) {
151
+ return;
152
+ }
153
+
154
+ const permissions: Array<Permission> =
155
+ DatabaseCommonInteractionPropsUtil.getUserPermissions(
156
+ props,
157
+ PermissionType.Allow,
158
+ ).map((userPermission: UserPermission) => {
159
+ return userPermission.permission;
160
+ });
161
+
162
+ const hasPermission: boolean = permissions.some((p: Permission) => {
163
+ return this.PERMISSIONS.includes(p);
164
+ });
165
+
166
+ if (!hasPermission) {
167
+ throw new NotAuthorizedException(
168
+ "You do not have permission to see who this incident's status page notifications would reach. You need one of these permissions: Project Owner, Project Admin, Project Member, Incident Admin, Incident Member, Create Incident, Edit Incident, Create Incident Public Note.",
169
+ );
170
+ }
171
+ }
172
+
173
+ public static async build(
174
+ request: IncidentSubscriberAudienceRequest,
175
+ ): Promise<IncidentSubscriberAudienceResult> {
176
+ return (await this.buildWithStatusPages(request)).audience;
177
+ }
178
+
179
+ // The audience, and the status pages it was worked out from.
180
+ public static async buildWithStatusPages(
181
+ request: IncidentSubscriberAudienceRequest,
182
+ ): Promise<IncidentSubscriberAudienceWithStatusPages> {
183
+ const inputs: AudienceInputs =
184
+ "incidentId" in request
185
+ ? await this.getInputsForIncident(request)
186
+ : await this.getInputsForDraft(request);
187
+
188
+ const audience: IncidentSubscriberAudienceResult = await this.buildAudience(
189
+ {
190
+ projectId: request.projectId,
191
+ props: request.props,
192
+ inputs: inputs,
193
+ },
194
+ );
195
+
196
+ return {
197
+ audience: audience,
198
+ resolved: inputs.resolved,
199
+ };
200
+ }
201
+
202
+ // An incident that exists: read with the caller's permissions.
203
+ private static async getInputsForIncident(request: {
204
+ projectId: ObjectID;
205
+ props: DatabaseCommonInteractionProps;
206
+ incidentId: ObjectID;
207
+ excludeStatusPagesNotifiedOnCreation?: boolean | undefined;
208
+ }): Promise<AudienceInputs> {
209
+ const incident: Incident | null = await IncidentService.findOneBy({
210
+ query: {
211
+ _id: request.incidentId,
212
+ projectId: request.projectId,
213
+ },
214
+ select: {
215
+ _id: true,
216
+ isVisibleOnStatusPage: true,
217
+ isPrivate: true,
218
+ monitors: {
219
+ _id: true,
220
+ },
221
+ },
222
+ props: request.props,
223
+ });
224
+
225
+ if (!incident) {
226
+ throw new NotFoundException("Incident not found");
227
+ }
228
+
229
+ /*
230
+ * The scope is read as root, now that the caller has been shown to read
231
+ * the incident: its status page ids only decide which pages below are
232
+ * reported as listing none of its monitors, and those are named only if
233
+ * the caller can read them. The record of pages already sent the
234
+ * 'created' notification is read the same way, for a Retry's audience:
235
+ * it only moves pages from notified to left out.
236
+ */
237
+ const incidentWithScope: Incident | null = await IncidentService.findOneBy({
238
+ query: {
239
+ _id: request.incidentId,
240
+ projectId: request.projectId,
241
+ },
242
+ select: {
243
+ _id: true,
244
+ isScopedToStatusPages: true,
245
+ statusPages: {
246
+ _id: true,
247
+ },
248
+ ...(request.excludeStatusPagesNotifiedOnCreation
249
+ ? { statusPagesNotifiedOnCreation: true }
250
+ : {}),
251
+ },
252
+ props: {
253
+ isRoot: true,
254
+ },
255
+ });
256
+
257
+ const resolved: ResolvedIncidentStatusPages =
258
+ await IncidentStatusPageScope.resolvePagesForIncidents({
259
+ incidents: [incident],
260
+ });
261
+
262
+ return {
263
+ resolved: resolved,
264
+ hasMonitors: (incident.monitors || []).length > 0,
265
+ scopedStatusPageIds:
266
+ incidentWithScope?.isScopedToStatusPages === true
267
+ ? this.normalizeIds(incidentWithScope.statusPages)
268
+ : [],
269
+ /*
270
+ * The jobs send nothing for an incident hidden from status pages.
271
+ * Private incidents are always hidden (IncidentService forces it), but
272
+ * a row that says private is treated as hidden either way.
273
+ */
274
+ isHiddenFromStatusPages:
275
+ incident.isVisibleOnStatusPage !== true || incident.isPrivate === true,
276
+ alreadyNotifiedStatusPageIds: request.excludeStatusPagesNotifiedOnCreation
277
+ ? IncidentScopeAddedPagesNotification.normalizeStatusPageIds(
278
+ incidentWithScope?.statusPagesNotifiedOnCreation,
279
+ )
280
+ : [],
281
+ };
282
+ }
283
+
284
+ /*
285
+ * An incident being declared: the monitors and status pages picked on the
286
+ * form. Both lists must be the project's.
287
+ */
288
+ private static async getInputsForDraft(request: {
289
+ projectId: ObjectID;
290
+ props: DatabaseCommonInteractionProps;
291
+ monitorIds: Array<ObjectID>;
292
+ statusPageIds: Array<ObjectID>;
293
+ }): Promise<AudienceInputs> {
294
+ const monitorIds: Array<string> = this.normalizeIds(request.monitorIds);
295
+ const statusPageIds: Array<string> = this.normalizeIds(
296
+ request.statusPageIds,
297
+ );
298
+
299
+ await Promise.all([
300
+ this.assertBelongToProject({
301
+ ids: monitorIds,
302
+ projectId: request.projectId,
303
+ find: async (ids: Array<string>): Promise<Array<{ _id?: string }>> => {
304
+ return MonitorService.findBy({
305
+ query: {
306
+ _id: QueryHelper.any(ids),
307
+ projectId: request.projectId,
308
+ },
309
+ select: {
310
+ _id: true,
311
+ },
312
+ limit: ids.length,
313
+ skip: 0,
314
+ props: {
315
+ isRoot: true,
316
+ },
317
+ });
318
+ },
319
+ message:
320
+ "One or more of these monitors were not found in this project.",
321
+ }),
322
+ this.assertBelongToProject({
323
+ ids: statusPageIds,
324
+ projectId: request.projectId,
325
+ find: async (ids: Array<string>): Promise<Array<{ _id?: string }>> => {
326
+ return StatusPageService.findBy({
327
+ query: {
328
+ _id: QueryHelper.any(ids),
329
+ projectId: request.projectId,
330
+ },
331
+ select: {
332
+ _id: true,
333
+ },
334
+ limit: ids.length,
335
+ skip: 0,
336
+ props: {
337
+ isRoot: true,
338
+ },
339
+ });
340
+ },
341
+ message:
342
+ "One or more of these status pages were not found in this project.",
343
+ }),
344
+ ]);
345
+
346
+ const resolved: ResolvedIncidentStatusPages =
347
+ await IncidentStatusPageScope.resolvePagesForDraftIncident({
348
+ monitorIds: monitorIds.map((id: string): ObjectID => {
349
+ return new ObjectID(id);
350
+ }),
351
+ statusPageIds: statusPageIds,
352
+ });
353
+
354
+ return {
355
+ resolved: resolved,
356
+ hasMonitors: monitorIds.length > 0,
357
+ scopedStatusPageIds: statusPageIds,
358
+ // The form knows whether the incident will be private; the page says so.
359
+ isHiddenFromStatusPages: false,
360
+ // Nothing has been sent about an incident not yet declared.
361
+ alreadyNotifiedStatusPageIds: [],
362
+ };
363
+ }
364
+
365
+ private static async assertBelongToProject(data: {
366
+ ids: Array<string>;
367
+ projectId: ObjectID;
368
+ find: (ids: Array<string>) => Promise<Array<{ _id?: string }>>;
369
+ message: string;
370
+ }): Promise<void> {
371
+ if (data.ids.length === 0) {
372
+ return;
373
+ }
374
+
375
+ const found: Array<string> = this.normalizeIds(await data.find(data.ids));
376
+
377
+ const isEveryIdFound: boolean = data.ids.every((id: string): boolean => {
378
+ return found.includes(id);
379
+ });
380
+
381
+ if (!isEveryIdFound) {
382
+ throw new BadDataException(data.message);
383
+ }
384
+ }
385
+
386
+ private static async buildAudience(data: {
387
+ projectId: ObjectID;
388
+ props: DatabaseCommonInteractionProps;
389
+ inputs: AudienceInputs;
390
+ }): Promise<IncidentSubscriberAudienceResult> {
391
+ const projectId: string = data.projectId.toString().toLowerCase();
392
+
393
+ // Another project's page cannot be reached; if one were, it is not told.
394
+ const isProjectPage: (statusPage: StatusPage) => boolean = (
395
+ statusPage: StatusPage,
396
+ ): boolean => {
397
+ return (
398
+ !statusPage.projectId ||
399
+ statusPage.projectId.toString().toLowerCase() === projectId
400
+ );
401
+ };
402
+
403
+ const notifiedPages: Array<StatusPage> = [];
404
+ const excludedPages: Array<{
405
+ statusPage: StatusPage;
406
+ reason: IncidentSubscriberAudienceExclusionReason;
407
+ }> = [];
408
+
409
+ for (const statusPage of data.inputs.resolved.statusPages) {
410
+ if (!isProjectPage(statusPage)) {
411
+ continue;
412
+ }
413
+
414
+ // The jobs skip a page that does not show incidents.
415
+ if (statusPage.showIncidentsOnStatusPage === false) {
416
+ excludedPages.push({
417
+ statusPage: statusPage,
418
+ reason: IncidentSubscriberAudienceExclusionReason.HidesIncidents,
419
+ });
420
+ continue;
421
+ }
422
+
423
+ // A Retry of the 'created' notification resumes after these.
424
+ if (
425
+ data.inputs.alreadyNotifiedStatusPageIds.includes(
426
+ this.getId(statusPage),
427
+ )
428
+ ) {
429
+ excludedPages.push({
430
+ statusPage: statusPage,
431
+ reason: IncidentSubscriberAudienceExclusionReason.AlreadyNotified,
432
+ });
433
+ continue;
434
+ }
435
+
436
+ notifiedPages.push(statusPage);
437
+ }
438
+
439
+ for (const excluded of data.inputs.resolved.excludedStatusPages) {
440
+ if (!isProjectPage(excluded.statusPage)) {
441
+ continue;
442
+ }
443
+
444
+ excludedPages.push({
445
+ statusPage: excluded.statusPage,
446
+ reason: this.toExclusionReason(excluded),
447
+ });
448
+ }
449
+
450
+ excludedPages.sort(
451
+ (
452
+ a: { statusPage: StatusPage },
453
+ b: { statusPage: StatusPage },
454
+ ): number => {
455
+ return IncidentStatusPageScope.compareStatusPagesByName(
456
+ a.statusPage,
457
+ b.statusPage,
458
+ );
459
+ },
460
+ );
461
+
462
+ const reachedIds: Array<string> = [
463
+ ...notifiedPages,
464
+ ...excludedPages.map((excluded: { statusPage: StatusPage }) => {
465
+ return excluded.statusPage;
466
+ }),
467
+ ]
468
+ .map((statusPage: StatusPage): string => {
469
+ return this.getId(statusPage);
470
+ })
471
+ .filter((id: string): boolean => {
472
+ return Boolean(id);
473
+ });
474
+
475
+ /*
476
+ * Pages the incident is limited to that its monitors do not reach. With
477
+ * no monitor at all nothing is reached, which the summary says on its
478
+ * own, so they are not listed then.
479
+ */
480
+ const selectedNotListingMonitorIds: Array<string> = data.inputs.hasMonitors
481
+ ? data.inputs.scopedStatusPageIds.filter((id: string): boolean => {
482
+ return !reachedIds.includes(id);
483
+ })
484
+ : [];
485
+
486
+ const readableNames: Dictionary<string> = await this.getReadableNames({
487
+ projectId: data.projectId,
488
+ props: data.props,
489
+ statusPageIds: [...reachedIds, ...selectedNotListingMonitorIds],
490
+ });
491
+
492
+ const isReadable: (id: string) => boolean = (id: string): boolean => {
493
+ return Object.prototype.hasOwnProperty.call(readableNames, id);
494
+ };
495
+
496
+ const readableNotifiedPages: Array<StatusPage> = notifiedPages.filter(
497
+ (statusPage: StatusPage): boolean => {
498
+ return isReadable(this.getId(statusPage));
499
+ },
500
+ );
501
+
502
+ const counts: Dictionary<IncidentSubscriberAudienceCounts> =
503
+ readableNotifiedPages.length > 0
504
+ ? await StatusPageSubscriberService.countActiveSubscribersByChannel({
505
+ projectId: data.projectId,
506
+ statusPageIds: readableNotifiedPages.map(
507
+ (statusPage: StatusPage): ObjectID => {
508
+ return new ObjectID(this.getId(statusPage));
509
+ },
510
+ ),
511
+ })
512
+ : {};
513
+
514
+ const statusPages: Array<IncidentSubscriberAudienceStatusPage> =
515
+ readableNotifiedPages.map(
516
+ (statusPage: StatusPage): IncidentSubscriberAudienceStatusPage => {
517
+ const id: string = this.getId(statusPage);
518
+
519
+ return {
520
+ statusPageId: id,
521
+ name: readableNames[id] || "",
522
+ subscriberCounts: {
523
+ ...(counts[id] || IncidentSubscriberAudience.getEmptyCounts()),
524
+ },
525
+ };
526
+ },
527
+ );
528
+
529
+ const excludedStatusPages: Array<IncidentSubscriberAudienceExcludedStatusPage> =
530
+ excludedPages
531
+ .filter((excluded: { statusPage: StatusPage }): boolean => {
532
+ return isReadable(this.getId(excluded.statusPage));
533
+ })
534
+ .map(
535
+ (excluded: {
536
+ statusPage: StatusPage;
537
+ reason: IncidentSubscriberAudienceExclusionReason;
538
+ }): IncidentSubscriberAudienceExcludedStatusPage => {
539
+ const id: string = this.getId(excluded.statusPage);
540
+
541
+ return {
542
+ statusPageId: id,
543
+ name: readableNames[id] || "",
544
+ reason: excluded.reason,
545
+ };
546
+ },
547
+ );
548
+
549
+ const selectedStatusPagesNotListingMonitors: Array<IncidentSubscriberAudienceNamedStatusPage> =
550
+ selectedNotListingMonitorIds
551
+ .filter(isReadable)
552
+ .map((id: string): IncidentSubscriberAudienceNamedStatusPage => {
553
+ return {
554
+ statusPageId: id,
555
+ name: readableNames[id] || "",
556
+ };
557
+ })
558
+ .sort(
559
+ (
560
+ a: IncidentSubscriberAudienceNamedStatusPage,
561
+ b: IncidentSubscriberAudienceNamedStatusPage,
562
+ ): number => {
563
+ return a.name.localeCompare(b.name, "en", {
564
+ sensitivity: "base",
565
+ numeric: true,
566
+ });
567
+ },
568
+ );
569
+
570
+ return {
571
+ hasMonitors: data.inputs.hasMonitors,
572
+ isScoped: data.inputs.resolved.isScoped,
573
+ isHiddenFromStatusPages: data.inputs.isHiddenFromStatusPages,
574
+ statusPages: statusPages,
575
+ hiddenStatusPageCount: notifiedPages.length - statusPages.length,
576
+ excludedStatusPages: excludedStatusPages,
577
+ selectedStatusPagesNotListingMonitors:
578
+ selectedStatusPagesNotListingMonitors,
579
+ };
580
+ }
581
+
582
+ /*
583
+ * The names of the status pages the caller may read, keyed by lower-cased
584
+ * id, read with the caller's own permissions - and so through the labels
585
+ * status page access is restricted by. A caller with no status page read
586
+ * access at all can read none of them.
587
+ */
588
+ private static async getReadableNames(data: {
589
+ projectId: ObjectID;
590
+ props: DatabaseCommonInteractionProps;
591
+ statusPageIds: Array<string>;
592
+ }): Promise<Dictionary<string>> {
593
+ const names: Dictionary<string> = {};
594
+
595
+ const statusPageIds: Array<string> = this.normalizeIds(data.statusPageIds);
596
+
597
+ if (statusPageIds.length === 0) {
598
+ return names;
599
+ }
600
+
601
+ let statusPages: Array<StatusPage> = [];
602
+
603
+ try {
604
+ statusPages = await StatusPageService.findBy({
605
+ query: {
606
+ _id: QueryHelper.any(statusPageIds),
607
+ projectId: data.projectId,
608
+ },
609
+ select: {
610
+ _id: true,
611
+ name: true,
612
+ },
613
+ limit: Math.min(statusPageIds.length, LIMIT_PER_PROJECT),
614
+ skip: 0,
615
+ props: data.props,
616
+ });
617
+ } catch (err) {
618
+ if (err instanceof NotAuthorizedException) {
619
+ return names;
620
+ }
621
+
622
+ throw err;
623
+ }
624
+
625
+ for (const statusPage of statusPages) {
626
+ const id: string = this.getId(statusPage);
627
+
628
+ if (id && statusPageIds.includes(id)) {
629
+ names[id] = statusPage.name?.trim() || "Untitled status page";
630
+ }
631
+ }
632
+
633
+ return names;
634
+ }
635
+
636
+ private static toExclusionReason(
637
+ excluded: ExcludedStatusPage,
638
+ ): IncidentSubscriberAudienceExclusionReason {
639
+ return excluded.reason ===
640
+ StatusPageExclusionReason.OnlyShowsScopedIncidents
641
+ ? IncidentSubscriberAudienceExclusionReason.OnlyShowsScopedIncidents
642
+ : IncidentSubscriberAudienceExclusionReason.OutsideIncidentScope;
643
+ }
644
+
645
+ private static getId(model: StatusPage | Monitor): string {
646
+ return (model._id || model.id?.toString() || "").toString().toLowerCase();
647
+ }
648
+
649
+ /*
650
+ * Ids in any shape they arrive in - an ObjectID, a string, a model or a
651
+ * {_id} - lower-cased as the database returns them, without duplicates.
652
+ */
653
+ private static normalizeIds(value: unknown): Array<string> {
654
+ return IncidentScopeAddedPagesNotification.normalizeStatusPageIds(value);
655
+ }
656
+ }