@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
@@ -34,6 +34,7 @@ import AIIncidentPostmortemRunner from "../Utils/AI/SRE/IncidentPostmortemRunner
34
34
  import InvestigationGrader from "../Utils/AI/SRE/InvestigationGrader";
35
35
  import { IncidentFeedEventType } from "../../Models/DatabaseModels/IncidentFeed";
36
36
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
37
+ import SubscriberNotificationResendAccess from "../Utils/StatusPage/SubscriberNotificationResendAccess";
37
38
  import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
38
39
  import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
39
40
  import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
@@ -748,6 +749,18 @@ ${createdItem.rootCause}`,
748
749
  protected override async onBeforeUpdate(
749
750
  updateBy: UpdateBy<IncidentStateTimeline>,
750
751
  ): Promise<OnUpdate<IncidentStateTimeline>> {
752
+ /*
753
+ * Retry - a user's Pending - over a state change notification that is
754
+ * being sent would let a second run send it alongside, or be overwritten
755
+ * when the send settles (SubscriberNotificationResendAccess).
756
+ */
757
+ await SubscriberNotificationResendAccess.assertNotQueuedWhileBeingSent({
758
+ modelType: IncidentStateTimeline,
759
+ service: this,
760
+ updateBy: updateBy,
761
+ statusColumns: ["subscriberNotificationStatus"],
762
+ });
763
+
751
764
  const incidentIds: Array<ObjectID> =
752
765
  await this.getIncidentIdsForTimelineQuery(updateBy);
753
766
 
@@ -10,6 +10,7 @@ import LabelService from "./LabelService";
10
10
  import MonitorService from "./MonitorService";
11
11
  import MonitorStatusService from "./MonitorStatusService";
12
12
  import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
13
+ import StatusPageService from "./StatusPageService";
13
14
  import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
14
15
  import LIMIT_MAX from "../../Types/Database/LimitMax";
15
16
  import Dictionary from "../../Types/Dictionary";
@@ -28,6 +29,8 @@ import Query from "../Types/Database/Query";
28
29
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
29
30
  import OwnerRuleAssignment from "../Utils/Rules/OwnerRuleAssignment";
30
31
  import QueryDeepPartialEntity from "../../Types/Database/PartialEntity";
32
+ import StatusPageReadAccess from "../Utils/StatusPage/StatusPageReadAccess";
33
+ import IncidentScopeAddedPagesNotification from "../../Types/StatusPage/IncidentScopeAddedPagesNotification";
31
34
 
32
35
  export class Service extends DatabaseService<Model> {
33
36
  public constructor() {
@@ -52,6 +55,12 @@ export class Service extends DatabaseService<Model> {
52
55
  const projectId: ObjectID | undefined =
53
56
  createBy.props.tenantId || createBy.data.projectId;
54
57
 
58
+ // Derived from the list, whatever the caller sent (see the column).
59
+ createBy.data.isScopedToStatusPages =
60
+ IncidentScopeAddedPagesNotification.normalizeStatusPageIds(
61
+ createBy.data.statusPages,
62
+ ).length > 0;
63
+
55
64
  await ProjectScopedReferenceValidator.validateReferencesBelongToProject({
56
65
  projectId: projectId,
57
66
  subject: "incident template",
@@ -64,6 +73,19 @@ export class Service extends DatabaseService<Model> {
64
73
  ],
65
74
  });
66
75
 
76
+ /*
77
+ * A template's status pages are picked for everyone who declares from
78
+ * it, so picking them needs the same read access as picking an
79
+ * incident's (see StatusPageReadAccess).
80
+ */
81
+ await StatusPageReadAccess.assertCallerCanPickStatusPages({
82
+ statusPageIds: IncidentScopeAddedPagesNotification.normalizeStatusPageIds(
83
+ createBy.data.statusPages,
84
+ ),
85
+ props: createBy.props,
86
+ subject: "incident template",
87
+ });
88
+
67
89
  return { createBy, carryForward: null };
68
90
  }
69
91
 
@@ -71,6 +93,22 @@ export class Service extends DatabaseService<Model> {
71
93
  protected override async onBeforeUpdate(
72
94
  updateBy: UpdateBy<Model>,
73
95
  ): Promise<OnUpdate<Model>> {
96
+ /*
97
+ * The scope flag follows a write to the list and nothing else: a client
98
+ * value is dropped, and deleting the template's pages (join rows
99
+ * cascading away) never clears it.
100
+ */
101
+ const data: Dictionary<unknown> = updateBy.data as Dictionary<unknown>;
102
+
103
+ delete data["isScopedToStatusPages"];
104
+
105
+ if (data["statusPages"] !== undefined) {
106
+ data["isScopedToStatusPages"] =
107
+ IncidentScopeAddedPagesNotification.normalizeStatusPageIds(
108
+ data["statusPages"],
109
+ ).length > 0;
110
+ }
111
+
74
112
  const references: Array<ProjectScopedReference> =
75
113
  this.getProjectScopedReferences(updateBy.data);
76
114
 
@@ -131,9 +169,75 @@ export class Service extends DatabaseService<Model> {
131
169
  });
132
170
  }
133
171
 
172
+ // Once the pages are known to be the project's.
173
+ await this.assertCallerCanReadAddedStatusPages(updateBy);
174
+
134
175
  return { updateBy, carryForward: null };
135
176
  }
136
177
 
178
+ /*
179
+ * The status pages an update adds to a template must be pages the caller
180
+ * can read (see StatusPageReadAccess). Pages the template already holds are
181
+ * not checked, so saving a template limited to a page the editor cannot
182
+ * see does not fail. The templates are read as root, limited to the
183
+ * caller's project: the update's own tenant filter is only added after
184
+ * this hook.
185
+ */
186
+ private async assertCallerCanReadAddedStatusPages(
187
+ updateBy: UpdateBy<Model>,
188
+ ): Promise<void> {
189
+ const requested: unknown = (updateBy.data as Dictionary<unknown>)[
190
+ "statusPages"
191
+ ];
192
+
193
+ if (updateBy.props.isRoot || requested === undefined) {
194
+ return;
195
+ }
196
+
197
+ const requestedIds: Array<string> =
198
+ IncidentScopeAddedPagesNotification.normalizeStatusPageIds(requested);
199
+
200
+ if (requestedIds.length === 0) {
201
+ return;
202
+ }
203
+
204
+ const templates: Array<Model> = await this.findBy({
205
+ query: updateBy.props.tenantId
206
+ ? { ...updateBy.query, projectId: updateBy.props.tenantId }
207
+ : updateBy.query,
208
+ select: {
209
+ _id: true,
210
+ statusPages: {
211
+ _id: true,
212
+ },
213
+ },
214
+ limit: LIMIT_MAX,
215
+ skip: 0,
216
+ props: {
217
+ isRoot: true,
218
+ },
219
+ });
220
+
221
+ const addedIds: Array<string> = [];
222
+
223
+ for (const template of templates) {
224
+ for (const id of IncidentScopeAddedPagesNotification.getScopeChange({
225
+ before: template.statusPages,
226
+ after: requestedIds,
227
+ }).addedStatusPageIds) {
228
+ if (!addedIds.includes(id)) {
229
+ addedIds.push(id);
230
+ }
231
+ }
232
+ }
233
+
234
+ await StatusPageReadAccess.assertCallerCanPickStatusPages({
235
+ statusPageIds: addedIds,
236
+ props: updateBy.props,
237
+ subject: "incident template",
238
+ });
239
+ }
240
+
137
241
  /*
138
242
  * The many-to-many lists whose ids must belong to the template's project.
139
243
  * Built per call rather than at module load: these services sit in an
@@ -157,6 +261,11 @@ export class Service extends DatabaseService<Model> {
157
261
  modelName: "On-Call Policy",
158
262
  service: OnCallDutyPolicyService,
159
263
  },
264
+ {
265
+ column: "statusPages",
266
+ modelName: "Status Page",
267
+ service: StatusPageService,
268
+ },
160
269
  ...getAffectedResourceRelations(this.getModel()),
161
270
  ];
162
271
  }
@@ -19,6 +19,7 @@ import Query from "../Types/Database/Query";
19
19
  import File from "../../Models/DatabaseModels/File";
20
20
  import FileAttachmentMarkdownUtil from "../Utils/FileAttachmentMarkdownUtil";
21
21
  import { syncIsPublicForMarkdownImages } from "../Utils/InlineImageAccessTokenSync";
22
+ import SubscriberNotificationResendAccess from "../Utils/StatusPage/SubscriberNotificationResendAccess";
22
23
 
23
24
  export class Service extends DatabaseService<Model> {
24
25
  public constructor() {
@@ -122,11 +123,24 @@ export class Service extends DatabaseService<Model> {
122
123
  * An edit tells subscribers nothing unless the editor asked for it on this
123
124
  * edit (see SubscriberUpdateNotification). When they did, queue the update
124
125
  * notification; the ScheduledMaintenancePublicNote worker job sends it.
126
+ *
127
+ * Sending the note's 'posted' notification again - its status written back
128
+ * to Pending, as the dashboard's Retry does - needs the permission to post
129
+ * a note that notifies subscribers, and a note whose notification can go
130
+ * out again (see SubscriberNotificationResendAccess). So does telling
131
+ * subscribers about an edit, which is also refused while that update
132
+ * notification is being sent.
125
133
  */
126
134
  @CaptureSpan()
127
135
  protected override async onBeforeUpdate(
128
136
  updateBy: UpdateBy<Model>,
129
137
  ): Promise<OnUpdate<Model>> {
138
+ await SubscriberNotificationResendAccess.assertPublicNoteResendAllowed({
139
+ modelType: Model,
140
+ service: this,
141
+ updateBy: updateBy,
142
+ });
143
+
130
144
  if (SubscriberUpdateNotification.isRequested(updateBy.miscDataProps)) {
131
145
  updateBy.data.subscriberNotificationStatusOnNoteUpdated =
132
146
  StatusPageSubscriberNotificationStatus.Pending;
@@ -134,6 +148,20 @@ export class Service extends DatabaseService<Model> {
134
148
  SubscriberUpdateNotification.queuedMessage;
135
149
  }
136
150
 
151
+ /*
152
+ * Telling subscribers about the edit - asked for above, or written as
153
+ * Pending directly (the dashboard's Retry of a failed update) - needs
154
+ * the permission to post a note that notifies subscribers too, and is
155
+ * refused while the update notification is being sent.
156
+ */
157
+ await SubscriberNotificationResendAccess.assertPublicNoteUpdateNotificationAllowed(
158
+ {
159
+ modelType: Model,
160
+ service: this,
161
+ updateBy: updateBy,
162
+ },
163
+ );
164
+
137
165
  return {
138
166
  updateBy: updateBy,
139
167
  carryForward: null,
@@ -246,8 +274,9 @@ ${(createdItem.note || "") + attachmentsMarkdown}
246
274
  {
247
275
  scheduledMaintenanceId: updatedItem.scheduledMaintenanceId!,
248
276
  projectId: updatedItem.projectId!,
277
+ // An edit to a public note is a public note event, not a private one.
249
278
  scheduledMaintenanceFeedEventType:
250
- ScheduledMaintenanceFeedEventType.PrivateNote,
279
+ ScheduledMaintenanceFeedEventType.PublicNote,
251
280
  displayColor: Blue500,
252
281
  userId: userId || undefined,
253
282
 
@@ -60,6 +60,7 @@ import SmsService from "../../Server/Services/SmsService";
60
60
  import StatusPageResourceService from "../../Server/Services/StatusPageResourceService";
61
61
  import StatusPageService from "../../Server/Services/StatusPageService";
62
62
  import StatusPageSubscriberService from "../../Server/Services/StatusPageSubscriberService";
63
+ import StatusPageSubscriberUnsubscribe from "../../Types/StatusPage/StatusPageSubscriberUnsubscribe";
63
64
  import QueryHelper from "../../Server/Types/Database/QueryHelper";
64
65
  import Markdown, { MarkdownContentType } from "../../Server/Types/Markdown";
65
66
  import logger, { LogAttributes } from "../../Server/Utils/Logger";
@@ -90,6 +91,8 @@ import ProjectService from "./ProjectService";
90
91
  import StatusPageSubscriberNotificationTemplateService, {
91
92
  Service as StatusPageSubscriberNotificationTemplateServiceClass,
92
93
  } from "./StatusPageSubscriberNotificationTemplateService";
94
+ import StatusPageResourceUtil from "../Utils/StatusPageResource";
95
+ import SafeHtml from "../../Types/SafeHtml";
93
96
  import StatusPageSubscriberNotificationTemplate from "../../Models/DatabaseModels/StatusPageSubscriberNotificationTemplate";
94
97
  import StatusPageSubscriberNotificationEventType from "../../Types/StatusPage/StatusPageSubscriberNotificationEventType";
95
98
  import StatusPageSubscriberNotificationMethod from "../../Types/StatusPage/StatusPageSubscriberNotificationMethod";
@@ -208,12 +211,20 @@ export class Service extends DatabaseService<Model> {
208
211
 
209
212
  // Send email to Email subscribers.
210
213
 
214
+ /*
215
+ * The resources are read without their groups, so both forms are the
216
+ * names joined by commas. The HTML one, for email bodies, has every
217
+ * name escaped; SMS, Slack, subjects and webhooks get the names as
218
+ * written.
219
+ */
211
220
  const resourcesAffected: string =
212
- statusPageToResources[statuspage._id!]
213
- ?.map((r: StatusPageResource) => {
214
- return r.displayName;
215
- })
216
- .join(", ") || "";
221
+ StatusPageResourceUtil.getResourcesGroupedByGroupNameAsPlainText(
222
+ statusPageToResources[statuspage._id!] || [],
223
+ );
224
+ const resourcesAffectedHtml: string =
225
+ StatusPageResourceUtil.getResourcesGroupedByGroupName(
226
+ statusPageToResources[statuspage._id!] || [],
227
+ );
217
228
 
218
229
  // Fetch custom templates for each notification method
219
230
  const [
@@ -270,7 +281,7 @@ export class Service extends DatabaseService<Model> {
270
281
  const unsubscribeUrl: string =
271
282
  StatusPageSubscriberService.getUnsubscribeLink(
272
283
  URL.fromString(statusPageURL),
273
- subscriber.id!,
284
+ subscriber,
274
285
  ).toString();
275
286
 
276
287
  // Template variables for custom templates, as Markdown (Slack)
@@ -298,6 +309,19 @@ export class Service extends DatabaseService<Model> {
298
309
  };
299
310
 
300
311
  if (subscriber.subscriberPhone) {
312
+ /*
313
+ * On a public status page the SMS keeps the shorter manage link,
314
+ * which works there without signing in: an SMS is billed by the
315
+ * segment (see StatusPageSubscriberUnsubscribe.buildSmsLink).
316
+ */
317
+ const smsUnsubscribeUrl: string =
318
+ StatusPageSubscriberUnsubscribe.buildSmsLink({
319
+ isPublicStatusPage: statuspage.isPublicStatusPage,
320
+ statusPageUrl: statusPageURL,
321
+ subscriberId: subscriber.id!,
322
+ unsubscribeUrl: unsubscribeUrl,
323
+ });
324
+
301
325
  let smsMessage: string;
302
326
 
303
327
  if (
@@ -309,11 +333,14 @@ export class Service extends DatabaseService<Model> {
309
333
  smsMessage =
310
334
  StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(
311
335
  smsTemplate.templateBody,
312
- plainTextTemplateVariables,
336
+ {
337
+ ...plainTextTemplateVariables,
338
+ unsubscribeUrl: smsUnsubscribeUrl,
339
+ },
313
340
  );
314
341
  } else {
315
342
  // Use default template
316
- smsMessage = `Scheduled Maintenance: ${event.title || ""} on ${statusPageName}.${resourcesAffected ? ` Impact: ${resourcesAffected}.` : ""} Details: ${scheduledEventDetailsUrl}. Unsub: ${unsubscribeUrl}`;
343
+ smsMessage = `Scheduled Maintenance: ${event.title || ""} on ${statusPageName}.${resourcesAffected ? ` Impact: ${resourcesAffected}.` : ""} Details: ${scheduledEventDetailsUrl}. Unsub: ${smsUnsubscribeUrl}`;
317
344
  }
318
345
 
319
346
  const sms: SMS = {
@@ -408,7 +435,20 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
408
435
  const statusPageIdString: string | null =
409
436
  statuspage.id?.toString() || statuspage._id?.toString() || null;
410
437
 
411
- // Prepare email variables
438
+ const scheduledAtHtml: string =
439
+ OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
440
+ date: event.startsAt!,
441
+ timezones: statuspage.subscriberTimezones || [],
442
+ use12HourFormat: true,
443
+ });
444
+
445
+ /*
446
+ * The default template's variables. resourcesAffected,
447
+ * scheduledAt and eventDescription are HTML (the template puts
448
+ * them in its raw-HTML slot); the footer is HTML the status
449
+ * page's admins wrote, as they write the template. Everything
450
+ * else is plain text, which the template escapes.
451
+ */
412
452
  const emailVars: Record<string, string> = {
413
453
  statusPageName: statusPageName,
414
454
  statusPageUrl: statusPageURL,
@@ -425,13 +465,8 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
425
465
  : "false",
426
466
  subscriberEmailNotificationFooterText:
427
467
  statuspage.subscriberEmailNotificationFooterText || "",
428
- resourcesAffected: resourcesAffected,
429
- scheduledAt:
430
- OneUptimeDate.getDateAsFormattedHTMLInMultipleTimezones({
431
- date: event.startsAt!,
432
- timezones: statuspage.subscriberTimezones || [],
433
- use12HourFormat: true,
434
- }),
468
+ resourcesAffected: resourcesAffectedHtml,
469
+ scheduledAt: scheduledAtHtml,
435
470
  eventTitle: event.title || "",
436
471
  eventDescription: eventDescriptionHtml,
437
472
  unsubscribeUrl: unsubscribeUrl,
@@ -448,14 +483,29 @@ ${resourcesAffected ? `**Resources Affected:** ${resourcesAffected}` : ""}
448
483
  * is configured. The body is HTML, so the description is too.
449
484
  * The subject is plain text, including the email-only
450
485
  * variables that are HTML in the body.
486
+ *
487
+ * In the body, the plain values are escaped
488
+ * (compileEmailBodyTemplate) and only the ones wrapped in
489
+ * SafeHtml below go in as HTML.
451
490
  */
452
491
  const customEmailBody: string =
453
- StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(
492
+ StatusPageSubscriberNotificationTemplateServiceClass.compileEmailBodyTemplate(
454
493
  emailTemplate.templateBody,
455
494
  {
456
495
  ...templateVariables,
457
496
  ...emailVars,
458
- scheduledMaintenanceDescription: eventDescriptionHtml,
497
+ resourcesAffected: SafeHtml.fromTrustedHtml(
498
+ resourcesAffectedHtml,
499
+ ),
500
+ scheduledAt: SafeHtml.fromTrustedHtml(scheduledAtHtml),
501
+ eventDescription:
502
+ SafeHtml.fromTrustedHtml(eventDescriptionHtml),
503
+ scheduledMaintenanceDescription:
504
+ SafeHtml.fromTrustedHtml(eventDescriptionHtml),
505
+ subscriberEmailNotificationFooterText:
506
+ SafeHtml.fromTrustedHtml(
507
+ emailVars["subscriberEmailNotificationFooterText"],
508
+ ),
459
509
  },
460
510
  );
461
511
  const customEmailSubject: string = emailTemplate.emailSubject
@@ -41,6 +41,15 @@ export class SmsService extends BaseService {
41
41
  onCallDutyPolicyExecutionLogTimelineId?: ObjectID | undefined;
42
42
  onCallScheduleId?: ObjectID | undefined;
43
43
  teamId?: ObjectID | undefined;
44
+ /*
45
+ * Answer with an error, not success, when the Notification service
46
+ * deliberately does not send the SMS: the project has SMS notifications
47
+ * turned off, or too little SMS balance. The status page subscriber
48
+ * sends that count what they delivered pass it
49
+ * (SubscriberNotificationDeliveryRecord), so such an SMS counts as
50
+ * failed rather than sent.
51
+ */
52
+ failIfNotSent?: boolean | undefined;
44
53
  },
45
54
  ): Promise<HTTPResponse<EmptyResponseData>> {
46
55
  const body: JSONObject = {
@@ -74,6 +83,7 @@ export class SmsService extends BaseService {
74
83
  options.onCallDutyPolicyExecutionLogTimelineId?.toString(),
75
84
  onCallScheduleId: options.onCallScheduleId?.toString(),
76
85
  teamId: options.teamId?.toString(),
86
+ ...(options.failIfNotSent ? { failIfNotSent: true } : {}),
77
87
  };
78
88
 
79
89
  return await API.post<EmptyResponseData>({
@@ -63,7 +63,8 @@ import MonitorGroupResource from "../../Models/DatabaseModels/MonitorGroupResour
63
63
  import MonitorGroupService from "./MonitorGroupService";
64
64
  import QueryHelper from "../Types/Database/QueryHelper";
65
65
  import OneUptimeDate from "../../Types/Date";
66
- import IncidentService from "./IncidentService";
66
+ import IncidentStatusPageScope from "../Utils/StatusPage/IncidentStatusPageScope";
67
+ import Select from "../Types/Database/Select";
67
68
  import MonitorStatusTimeline from "../../Models/DatabaseModels/MonitorStatusTimeline";
68
69
  import MonitorStatusTimelineService, {
69
70
  MergedDowntimeTotals,
@@ -122,6 +123,17 @@ export const STATUS_PAGE_SSO_REQUIREMENT_COLUMNS: ReadonlyArray<string> = [
122
123
  "requireSsoForLogin",
123
124
  ];
124
125
 
126
+ /*
127
+ * What a report's incident counts read of the status page: which project's
128
+ * incidents to count, and whether the page only shows incidents limited to
129
+ * it (IncidentStatusPageScope decides from that which incidents count).
130
+ */
131
+ export const INCIDENT_COUNT_STATUS_PAGE_SELECT: Select<StatusPage> = {
132
+ _id: true,
133
+ projectId: true,
134
+ onlyShowScopedIncidents: true,
135
+ };
136
+
125
137
  export class Service extends DatabaseService<StatusPage> {
126
138
  /*
127
139
  * Caches the resolved status page URL per statusPageId. `getStatusPageURL`
@@ -1393,7 +1405,7 @@ export class Service extends DatabaseService<StatusPage> {
1393
1405
  const unsubscribeUrl: string =
1394
1406
  StatusPageSubscriberService.getUnsubscribeLink(
1395
1407
  URL.fromString(statusPageURL),
1396
- subscriber.id!,
1408
+ subscriber,
1397
1409
  ).toString();
1398
1410
 
1399
1411
  if (subscriber.subscriberEmail) {
@@ -1432,6 +1444,8 @@ export class Service extends DatabaseService<StatusPage> {
1432
1444
  },
1433
1445
  select: {
1434
1446
  downtimeMonitorStatuses: true,
1447
+ // What the incident counts below are scoped by.
1448
+ ...INCIDENT_COUNT_STATUS_PAGE_SELECT,
1435
1449
  },
1436
1450
  });
1437
1451
 
@@ -1499,7 +1513,7 @@ export class Service extends DatabaseService<StatusPage> {
1499
1513
  }
1500
1514
 
1501
1515
  const incidentCount: number = await this.getIncidentCountOnStatusPage({
1502
- statusPageId: data.statusPageId,
1516
+ statusPage: statusPage,
1503
1517
  startDate: startDate,
1504
1518
  endDate: endDate,
1505
1519
  });
@@ -1661,6 +1675,7 @@ export class Service extends DatabaseService<StatusPage> {
1661
1675
  reportItem: {
1662
1676
  resourceName: resource.displayName || "",
1663
1677
  totalIncidentCount: await this.getIncidentCountByMonitorIds({
1678
+ statusPage: statusPage,
1664
1679
  monitorIds: monitorIdsForThisResource,
1665
1680
  startDate: startDate,
1666
1681
  endDate: endDate,
@@ -1710,6 +1725,7 @@ export class Service extends DatabaseService<StatusPage> {
1710
1725
 
1711
1726
  const groupMetricsByGroupId: Dictionary<StatusPageReportGroupMetrics> =
1712
1727
  await this.getReportGroupMetrics({
1728
+ statusPage: statusPage,
1713
1729
  statusPageGroups: statusPageGroups,
1714
1730
  entries: entries,
1715
1731
  monitorIdsByResourceIndex: monitorIdsByResourceIndex,
@@ -1817,6 +1833,11 @@ export class Service extends DatabaseService<StatusPage> {
1817
1833
  */
1818
1834
  @CaptureSpan()
1819
1835
  public async getReportGroupMetrics(data: {
1836
+ /*
1837
+ * The status page the report is for, which the incident counts are
1838
+ * scoped by: loaded with INCIDENT_COUNT_STATUS_PAGE_SELECT.
1839
+ */
1840
+ statusPage: StatusPage;
1820
1841
  statusPageGroups: Array<StatusPageGroup>;
1821
1842
  entries: Array<StatusPageReportResourceEntry>;
1822
1843
  monitorIdsByResourceIndex: Array<Array<ObjectID>>;
@@ -1913,6 +1934,7 @@ export class Service extends DatabaseService<StatusPage> {
1913
1934
  totalIncidentCount =
1914
1935
  monitorIds.length > 0
1915
1936
  ? await this.getIncidentCountByMonitorIds({
1937
+ statusPage: data.statusPage,
1916
1938
  monitorIds: monitorIds,
1917
1939
  startDate: data.reportWindow.startDate,
1918
1940
  endDate: data.reportWindow.endDate,
@@ -1963,13 +1985,41 @@ export class Service extends DatabaseService<StatusPage> {
1963
1985
  });
1964
1986
  }
1965
1987
 
1988
+ /*
1989
+ * How many incidents the status page shows on these monitors, created in
1990
+ * the window: the report's count for a resource, a group or the page.
1991
+ *
1992
+ * It counts what the page shows, through IncidentStatusPageScope - an
1993
+ * incident limited to other status pages is not counted, nor is an
1994
+ * unlimited one on a page that only shows incidents limited to it - and
1995
+ * only the project's incidents visible on status pages. (Before the scope,
1996
+ * this counted every incident on the monitors, hidden ones and all.)
1997
+ *
1998
+ * The status page must be loaded with INCIDENT_COUNT_STATUS_PAGE_SELECT. It
1999
+ * is passed in rather than read here because a report counts once for each
2000
+ * resource and group.
2001
+ */
1966
2002
  @CaptureSpan()
1967
2003
  public async getIncidentCountByMonitorIds(data: {
2004
+ statusPage: StatusPage;
1968
2005
  monitorIds: Array<ObjectID>;
1969
2006
  startDate: Date;
1970
2007
  endDate: Date;
1971
2008
  }): Promise<number> {
1972
- const incidentCount: PositiveNumber = await IncidentService.countBy({
2009
+ // No monitor, so no incident on one to count.
2010
+ if (data.monitorIds.length === 0) {
2011
+ return 0;
2012
+ }
2013
+
2014
+ if (!data.statusPage.projectId) {
2015
+ throw new BadDataException(
2016
+ "Cannot count the incidents on a status page without its project.",
2017
+ );
2018
+ }
2019
+
2020
+ return await IncidentStatusPageScope.countIncidentsForStatusPage({
2021
+ statusPage: data.statusPage,
2022
+ projectId: data.statusPage.projectId,
1973
2023
  query: {
1974
2024
  monitors: data.monitorIds as any,
1975
2025
  createdAt: QueryHelper.inBetween(data.startDate, data.endDate),
@@ -1978,24 +2028,31 @@ export class Service extends DatabaseService<StatusPage> {
1978
2028
  isRoot: true,
1979
2029
  },
1980
2030
  });
1981
-
1982
- return incidentCount.toNumber();
1983
2031
  }
1984
2032
 
2033
+ // How many incidents the status page shows, over all of its monitors.
1985
2034
  @CaptureSpan()
1986
2035
  public async getIncidentCountOnStatusPage(data: {
1987
- statusPageId: ObjectID;
2036
+ // Loaded with INCIDENT_COUNT_STATUS_PAGE_SELECT.
2037
+ statusPage: StatusPage;
1988
2038
  startDate: Date;
1989
2039
  endDate: Date;
1990
2040
  }): Promise<number> {
2041
+ if (!data.statusPage.id) {
2042
+ throw new BadDataException(
2043
+ "Cannot count the incidents on a status page without its id.",
2044
+ );
2045
+ }
2046
+
1991
2047
  const monitorsOnStatusPage: {
1992
2048
  monitorsOnStatusPage: Array<ObjectID>;
1993
2049
  monitorsInGroup: Dictionary<Array<ObjectID>>;
1994
2050
  } = await this.getMonitorIdsOnStatusPage({
1995
- statusPageId: data.statusPageId,
2051
+ statusPageId: data.statusPage.id,
1996
2052
  });
1997
2053
 
1998
2054
  return this.getIncidentCountByMonitorIds({
2055
+ statusPage: data.statusPage,
1999
2056
  monitorIds: monitorsOnStatusPage.monitorsOnStatusPage,
2000
2057
  startDate: data.startDate,
2001
2058
  endDate: data.endDate,