@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,421 @@
1
+ import Incident from "../../../Models/DatabaseModels/Incident";
2
+ import StatusPage from "../../../Models/DatabaseModels/StatusPage";
3
+ import StatusPageSubscriberNotificationTemplate from "../../../Models/DatabaseModels/StatusPageSubscriberNotificationTemplate";
4
+ import { StatusPageApiRoute } from "../../../ServiceRoute";
5
+ import Hostname from "../../../Types/API/Hostname";
6
+ import Protocol from "../../../Types/API/Protocol";
7
+ import URL from "../../../Types/API/URL";
8
+ import OneUptimeDate from "../../../Types/Date";
9
+ import { EmailEnvelope } from "../../../Types/Email/EmailMessage";
10
+ import EmailTemplateType from "../../../Types/Email/EmailTemplateType";
11
+ import BadDataException from "../../../Types/Exception/BadDataException";
12
+ import { JSONObject } from "../../../Types/JSON";
13
+ import ObjectID from "../../../Types/ObjectID";
14
+ import StatusPageSubscriberNotificationEventType from "../../../Types/StatusPage/StatusPageSubscriberNotificationEventType";
15
+ import StatusPageSubscriberNotificationMethod from "../../../Types/StatusPage/StatusPageSubscriberNotificationMethod";
16
+ import {
17
+ SubscriberEmailTemplateChoice,
18
+ SubscriberEmailTemplateChoiceReason,
19
+ } from "../../../Types/StatusPage/SubscriberNotificationPreview";
20
+ import { Service as StatusPageServiceType } from "../../Services/StatusPageService";
21
+ import StatusPageSubscriberNotificationTemplateService, {
22
+ Service as StatusPageSubscriberNotificationTemplateServiceClass,
23
+ SubscriberNotificationEmailBodyTemplateVariables,
24
+ } from "../../Services/StatusPageSubscriberNotificationTemplateService";
25
+ import IncidentTemplateVariableBuilder, {
26
+ IncidentStatusPageTemplateVariables,
27
+ IncidentTemplateVariables,
28
+ } from "./IncidentTemplateVariableBuilder";
29
+
30
+ /*
31
+ * The email a status page's subscribers get about an incident - its subject
32
+ * and its body, from the page's custom template or the default one - for the
33
+ * 'incident created' and public note (posted and updated) notifications.
34
+ *
35
+ * The one code path for it. The subscriber jobs send what this builds
36
+ * (Workers/Jobs/Incident/SendNotificationToSubscribers and
37
+ * Workers/Jobs/IncidentPublicNote/SendNotificationToSubscribers), and "Preview
38
+ * notification" and "Send test to me" show and send the same thing
39
+ * (Notification/API/SubscriberNotificationPreview), rendered by the same
40
+ * mailer (Notification MailService.render). A preview therefore cannot show
41
+ * one email while subscribers are sent another.
42
+ *
43
+ * It builds on what the jobs already share, rather than repeating it:
44
+ *
45
+ * - IncidentTemplateVariableBuilder for the values, in each channel's
46
+ * format (buildTemplateVariables reads them for an event), and
47
+ * - SubscriberNotificationTemplateCompiler, through the template service,
48
+ * for a custom template: compileEmailBodyTemplate for the body, which
49
+ * escapes every plain value, and compileTemplate for the subject.
50
+ *
51
+ * The page's custom email template is used only when it has a body and the
52
+ * page sends through its own SMTP server, as the jobs have always done;
53
+ * templateChoice says which email the page's subscribers get, and why.
54
+ *
55
+ * Building an email has no side effects. Sending one does: the custom field
56
+ * values it carries are recorded for the incident feed, and a Rich text
57
+ * field's inline images are made public so the recipient can load them. So a
58
+ * job calls recordSending before each message goes out, and a preview never
59
+ * does - previewing an email must not publish anything.
60
+ */
61
+
62
+ // Which notification the email is for.
63
+ export enum SubscriberIncidentEmailEvent {
64
+ IncidentCreated = "IncidentCreated",
65
+ IncidentPublicNoteCreated = "IncidentPublicNoteCreated",
66
+ IncidentPublicNoteUpdated = "IncidentPublicNoteUpdated",
67
+ }
68
+
69
+ interface SubscriberIncidentEmailCopy {
70
+ // The custom templates this event's email is looked up by.
71
+ templateEventType: StatusPageSubscriberNotificationEventType;
72
+ // The default email's template.
73
+ defaultTemplateType: EmailTemplateType;
74
+ // The default email's subject, before the incident title.
75
+ defaultSubjectPrefix: string;
76
+ // A custom template with no subject of its own: before the incident title.
77
+ customTemplateSubjectPrefix: string;
78
+ }
79
+
80
+ const EMAIL_COPY: Record<
81
+ SubscriberIncidentEmailEvent,
82
+ SubscriberIncidentEmailCopy
83
+ > = {
84
+ [SubscriberIncidentEmailEvent.IncidentCreated]: {
85
+ templateEventType:
86
+ StatusPageSubscriberNotificationEventType.SubscriberIncidentCreated,
87
+ defaultTemplateType: EmailTemplateType.SubscriberIncidentCreated,
88
+ defaultSubjectPrefix: "[Incident] ",
89
+ customTemplateSubjectPrefix: "[Incident] ",
90
+ },
91
+ [SubscriberIncidentEmailEvent.IncidentPublicNoteCreated]: {
92
+ templateEventType:
93
+ StatusPageSubscriberNotificationEventType.SubscriberIncidentNoteCreated,
94
+ defaultTemplateType: EmailTemplateType.SubscriberIncidentNoteCreated,
95
+ defaultSubjectPrefix: "[Update Incident] ",
96
+ customTemplateSubjectPrefix: "[Incident Update] ",
97
+ },
98
+ [SubscriberIncidentEmailEvent.IncidentPublicNoteUpdated]: {
99
+ templateEventType:
100
+ StatusPageSubscriberNotificationEventType.SubscriberIncidentNoteUpdated,
101
+ defaultTemplateType: EmailTemplateType.SubscriberIncidentNoteUpdated,
102
+ defaultSubjectPrefix: "[Incident Note Updated] ",
103
+ customTemplateSubjectPrefix: "[Incident Note Updated] ",
104
+ },
105
+ };
106
+
107
+ // One subscriber's email, ready for the mailer.
108
+ export interface SubscriberIncidentEmail {
109
+ // The subject as it is sent: final text, never compiled again.
110
+ subject: string;
111
+ /*
112
+ * Everything the mailer takes but the recipient: the template and its
113
+ * variables, and the subject. MailService.sendMail sends it (with toEmail
114
+ * added) and Notification MailService.render renders it.
115
+ */
116
+ envelope: EmailEnvelope;
117
+ }
118
+
119
+ // One status page's email, before it is addressed to anyone.
120
+ export interface SubscriberIncidentStatusPageEmail {
121
+ // Which email the page's subscribers get, and why.
122
+ templateChoice: SubscriberEmailTemplateChoice;
123
+ /*
124
+ * The email one subscriber gets: the page's email, with the subscriber's
125
+ * own unsubscribe link. Pure: it can be called for a preview.
126
+ */
127
+ forSubscriber: (data: { unsubscribeUrl: string }) => SubscriberIncidentEmail;
128
+ /*
129
+ * An email is about to go out: records the custom field values it carries
130
+ * (for the incident feed) and makes their inline images public. Awaited
131
+ * before each send. Never called for a preview. Never throws.
132
+ */
133
+ recordSending: () => Promise<void>;
134
+ }
135
+
136
+ export default class SubscriberIncidentEmailBuilder {
137
+ /**
138
+ * The values every message of this event is filled with, on every
139
+ * channel, read once per send (IncidentTemplateVariableBuilder).
140
+ *
141
+ * The incident needs projectId, title, incidentSeverity.name, labels.name
142
+ * and customFields; a public note's also currentIncidentState.name.
143
+ * statusPages are the pages the send reaches, which name
144
+ * {{affectedStatusPages}}. A public note's event takes the note and when
145
+ * it says it was posted - an update reads it fresh from the row, and only a
146
+ * note with none falls back to now.
147
+ */
148
+ public static async buildTemplateVariables(data: {
149
+ event: SubscriberIncidentEmailEvent;
150
+ incident: Incident;
151
+ statusPages: Array<StatusPage>;
152
+ note?:
153
+ | {
154
+ text: string | null | undefined;
155
+ postedAt: Date | null | undefined;
156
+ }
157
+ | undefined;
158
+ }): Promise<IncidentTemplateVariables> {
159
+ if (data.event === SubscriberIncidentEmailEvent.IncidentCreated) {
160
+ return IncidentTemplateVariableBuilder.build({
161
+ incident: data.incident,
162
+ statusPages: data.statusPages,
163
+ markdownVariables: {
164
+ incidentDescription: data.incident.description,
165
+ },
166
+ });
167
+ }
168
+
169
+ return IncidentTemplateVariableBuilder.build({
170
+ incident: data.incident,
171
+ statusPages: data.statusPages,
172
+ markdownVariables: {
173
+ note: data.note?.text,
174
+ },
175
+ textVariables: {
176
+ incidentState: data.incident.currentIncidentState?.name || "",
177
+ postedAt: OneUptimeDate.getDateAsUserFriendlyFormattedString(
178
+ data.note?.postedAt || OneUptimeDate.getCurrentDate(),
179
+ ),
180
+ },
181
+ });
182
+ }
183
+
184
+ /*
185
+ * The incident on the status page, or the status page itself for an
186
+ * incident that has no id yet (one being previewed before it is declared).
187
+ */
188
+ public static getDetailsUrl(data: {
189
+ statusPageUrl: string;
190
+ incidentId: ObjectID | null | undefined;
191
+ }): string {
192
+ return data.incidentId && data.statusPageUrl
193
+ ? URL.fromString(data.statusPageUrl)
194
+ .addRoute(`/incidents/${data.incidentId.toString()}`)
195
+ .toString()
196
+ : data.statusPageUrl;
197
+ }
198
+
199
+ // The status page's logo, for the default email, or "" when it has none.
200
+ public static getLogoUrl(data: {
201
+ statusPage: StatusPage;
202
+ host: Hostname;
203
+ httpProtocol: Protocol;
204
+ }): string {
205
+ const statusPageId: string | null =
206
+ data.statusPage.id?.toString() || data.statusPage._id?.toString() || null;
207
+
208
+ return data.statusPage.logoFileId && statusPageId
209
+ ? new URL(data.httpProtocol, data.host)
210
+ .addRoute(StatusPageApiRoute)
211
+ .addRoute(`/logo/${statusPageId}`)
212
+ .toString()
213
+ : "";
214
+ }
215
+
216
+ /*
217
+ * Which email a page's subscribers get: its custom template only when the
218
+ * template has a body and the page sends through its own SMTP server.
219
+ */
220
+ public static chooseTemplate(data: {
221
+ emailTemplate: StatusPageSubscriberNotificationTemplate | null;
222
+ statusPage: StatusPage;
223
+ }): SubscriberEmailTemplateChoice {
224
+ const customTemplateName: string | undefined =
225
+ data.emailTemplate?.templateName || undefined;
226
+
227
+ if (!data.emailTemplate) {
228
+ return {
229
+ usesCustomTemplate: false,
230
+ reason: SubscriberEmailTemplateChoiceReason.NoCustomTemplate,
231
+ };
232
+ }
233
+
234
+ if (!data.emailTemplate.templateBody) {
235
+ return {
236
+ usesCustomTemplate: false,
237
+ reason: SubscriberEmailTemplateChoiceReason.CustomTemplateIsEmpty,
238
+ customTemplateName: customTemplateName,
239
+ };
240
+ }
241
+
242
+ if (!data.statusPage.smtpConfig) {
243
+ return {
244
+ usesCustomTemplate: false,
245
+ reason:
246
+ SubscriberEmailTemplateChoiceReason.CustomTemplateNeedsCustomSmtp,
247
+ customTemplateName: customTemplateName,
248
+ };
249
+ }
250
+
251
+ return {
252
+ usesCustomTemplate: true,
253
+ reason: SubscriberEmailTemplateChoiceReason.CustomTemplate,
254
+ customTemplateName: customTemplateName,
255
+ };
256
+ }
257
+
258
+ /**
259
+ * One status page's email for this event. Looks up the page's custom
260
+ * email template for the event; everything else comes in: the values
261
+ * (buildTemplateVariables, then forStatusPage for this page), the page's
262
+ * URL and the incident's details URL on it (getDetailsUrl), and the
263
+ * instance's host and protocol for the logo.
264
+ *
265
+ * The page needs what StatusPageSubscriberService.
266
+ * getStatusPagesToSendNotification reads: its names, logoFileId,
267
+ * isPublicStatusPage, smtpConfig and the footer text columns.
268
+ */
269
+ public static async forStatusPage(data: {
270
+ event: SubscriberIncidentEmailEvent;
271
+ incident: Incident;
272
+ incidentTemplateVariables: IncidentTemplateVariables;
273
+ statusPage: StatusPage;
274
+ statusPageUrl: string;
275
+ detailsUrl: string;
276
+ pageTemplateVariables: IncidentStatusPageTemplateVariables;
277
+ host: Hostname;
278
+ httpProtocol: Protocol;
279
+ }): Promise<SubscriberIncidentStatusPageEmail> {
280
+ const copy: SubscriberIncidentEmailCopy = EMAIL_COPY[data.event];
281
+ const statusPage: StatusPage = data.statusPage;
282
+ const incidentTemplateVariables: IncidentTemplateVariables =
283
+ data.incidentTemplateVariables;
284
+ const pageTemplateVariables: IncidentStatusPageTemplateVariables =
285
+ data.pageTemplateVariables;
286
+ const incidentTitle: string = data.incident.title || "";
287
+
288
+ if (!statusPage.id) {
289
+ throw new BadDataException(
290
+ "Cannot build a status page's subscriber email without its id.",
291
+ );
292
+ }
293
+
294
+ const emailTemplate: StatusPageSubscriberNotificationTemplate | null =
295
+ await StatusPageSubscriberNotificationTemplateService.getTemplateForStatusPage(
296
+ {
297
+ statusPageId: statusPage.id,
298
+ eventType: copy.templateEventType,
299
+ notificationMethod: StatusPageSubscriberNotificationMethod.Email,
300
+ },
301
+ );
302
+
303
+ const templateChoice: SubscriberEmailTemplateChoice = this.chooseTemplate({
304
+ emailTemplate: emailTemplate,
305
+ statusPage: statusPage,
306
+ });
307
+
308
+ if (templateChoice.usesCustomTemplate) {
309
+ return {
310
+ templateChoice: templateChoice,
311
+ forSubscriber: (subscriber: {
312
+ unsubscribeUrl: string;
313
+ }): SubscriberIncidentEmail => {
314
+ /*
315
+ * The body is HTML (BlankTemplate wraps nothing around it), so its
316
+ * plain values are escaped; the subject is text, and gets them as
317
+ * written.
318
+ */
319
+ const emailBodyTemplateVariables: SubscriberNotificationEmailBodyTemplateVariables =
320
+ {
321
+ ...pageTemplateVariables.emailBody,
322
+ unsubscribeUrl: subscriber.unsubscribeUrl,
323
+ };
324
+ const plainTextTemplateVariables: Record<string, string> = {
325
+ ...pageTemplateVariables.plainText,
326
+ unsubscribeUrl: subscriber.unsubscribeUrl,
327
+ };
328
+
329
+ const compiledBody: string =
330
+ StatusPageSubscriberNotificationTemplateServiceClass.compileEmailBodyTemplate(
331
+ emailTemplate!.templateBody!,
332
+ emailBodyTemplateVariables,
333
+ );
334
+ const compiledSubject: string = emailTemplate!.emailSubject
335
+ ? StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(
336
+ emailTemplate!.emailSubject,
337
+ plainTextTemplateVariables,
338
+ )
339
+ : copy.customTemplateSubjectPrefix + incidentTitle;
340
+
341
+ return {
342
+ subject: compiledSubject,
343
+ envelope: {
344
+ templateType: EmailTemplateType.BlankTemplate,
345
+ vars: {
346
+ body: compiledBody,
347
+ },
348
+ subject: compiledSubject,
349
+ isSubjectLiteral: true,
350
+ },
351
+ };
352
+ },
353
+ recordSending: async (): Promise<void> => {
354
+ await incidentTemplateVariables.recordFieldsUsedBy([
355
+ emailTemplate!.templateBody,
356
+ emailTemplate!.emailSubject,
357
+ ]);
358
+ },
359
+ };
360
+ }
361
+
362
+ const subject: string = copy.defaultSubjectPrefix + incidentTitle;
363
+
364
+ // What the default email reads that does not vary per subscriber.
365
+ const pageVars: JSONObject = {
366
+ statusPageName:
367
+ IncidentTemplateVariableBuilder.getStatusPageName(statusPage),
368
+ statusPageUrl: data.statusPageUrl,
369
+ detailsUrl: data.detailsUrl,
370
+ logoUrl: this.getLogoUrl({
371
+ statusPage: statusPage,
372
+ host: data.host,
373
+ httpProtocol: data.httpProtocol,
374
+ }),
375
+ isPublicStatusPage: statusPage.isPublicStatusPage ? "true" : "false",
376
+ // Every name escaped, "<br/>" between groups: for the raw slot.
377
+ resourcesAffected: pageTemplateVariables.resourcesAffectedHtml,
378
+ incidentSeverity: data.incident.incidentSeverity?.name || " - ",
379
+ incidentTitle: incidentTitle,
380
+ // The fields marked "Include in Subscriber Notifications".
381
+ customFieldRows:
382
+ pageTemplateVariables.customFieldRows as unknown as JSONObject,
383
+ subscriberEmailNotificationFooterText:
384
+ StatusPageServiceType.getSubscriberEmailFooterText(statusPage),
385
+ };
386
+
387
+ // The event's own content, rendered from Markdown once per send.
388
+ if (data.event === SubscriberIncidentEmailEvent.IncidentCreated) {
389
+ pageVars["incidentDescription"] =
390
+ incidentTemplateVariables.getMarkdownVariable(
391
+ "incidentDescription",
392
+ ).html;
393
+ } else {
394
+ pageVars["note"] =
395
+ incidentTemplateVariables.getMarkdownVariable("note").html;
396
+ }
397
+
398
+ return {
399
+ templateChoice: templateChoice,
400
+ forSubscriber: (subscriber: {
401
+ unsubscribeUrl: string;
402
+ }): SubscriberIncidentEmail => {
403
+ return {
404
+ subject: subject,
405
+ envelope: {
406
+ templateType: copy.defaultTemplateType,
407
+ vars: {
408
+ ...pageVars,
409
+ unsubscribeUrl: subscriber.unsubscribeUrl,
410
+ } as EmailEnvelope["vars"],
411
+ subject: subject,
412
+ isSubjectLiteral: true,
413
+ },
414
+ };
415
+ },
416
+ recordSending: async (): Promise<void> => {
417
+ await incidentTemplateVariables.recordIncludedFieldsSent();
418
+ },
419
+ };
420
+ }
421
+ }
@@ -0,0 +1,107 @@
1
+ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
2
+ import PartialEntity from "../../../Types/Database/PartialEntity";
3
+ import OneUptimeDate from "../../../Types/Date";
4
+ import ObjectID from "../../../Types/ObjectID";
5
+ import StatusPageSubscriberNotificationStatus from "../../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
6
+ import DatabaseService from "../../Services/DatabaseService";
7
+
8
+ /*
9
+ * The two writes that move a subscriber notification in and out of
10
+ * InProgress without racing anyone: a job claiming a Pending notification to
11
+ * send it, and the sweeper failing one whose send was interrupted.
12
+ *
13
+ * Why a claim. The subscriber jobs run every minute, and a run can now take
14
+ * as long as its sends (SubscriberNotificationTiming), so the next run starts
15
+ * while the last is still going - on another worker slot, or another worker.
16
+ * Each run reads the Pending notifications when it starts. Without a claim,
17
+ * a run still working through its list would send a notification a later run
18
+ * had already sent, or pick up one that was re-queued since it read it and
19
+ * send it with what it read before (for the incident created notification,
20
+ * an old record of the pages already told, so those pages would be told
21
+ * again).
22
+ *
23
+ * A claim is one conditional UPDATE: Pending to InProgress, only if the row
24
+ * is still Pending and its version is still the one the job read. Every
25
+ * write through the services bumps the version, so a notification that was
26
+ * sent, re-queued or edited since is left for a run that reads it afresh.
27
+ * Exactly one run wins a notification. The claim is a hook-free write: it
28
+ * fires none of the row's 'on update' workflows, realtime events or audit
29
+ * log entries (the status the send settles on does). It stamps updatedAt,
30
+ * and, for a notification with one, its claimed-at column: the sweeper times
31
+ * an InProgress row from the claimed-at column where there is one, since
32
+ * other code writes those rows (an open incident or episode) all the time,
33
+ * and from updatedAt otherwise.
34
+ */
35
+ export default class SubscriberNotificationClaim {
36
+ /*
37
+ * Claim a Pending notification for this run. True when this run is now the
38
+ * one sending it; false when it is no longer Pending, or changed since the
39
+ * job read it (another run has it, or will read it afresh).
40
+ *
41
+ * `version` is the row's version as the job read it. A row read without
42
+ * one is claimed on its status alone.
43
+ *
44
+ * `claimedAtColumn` is stamped with the time of the claim, for the
45
+ * sweeper (see Incident.subscriberNotificationClaimedAtOnIncidentCreated).
46
+ *
47
+ * `alsoSet` settles other columns of the row in the same write, only if
48
+ * they still hold `expected`: the public note jobs use it to skip a note's
49
+ * update notification that the 'posted' notification they are claiming
50
+ * already covers (see the note jobs).
51
+ */
52
+ public static async claim<TBaseModel extends BaseModel>(data: {
53
+ service: DatabaseService<TBaseModel>;
54
+ id: ObjectID;
55
+ statusColumn: keyof TBaseModel & string;
56
+ version: number | undefined | null;
57
+ claimedAtColumn?: (keyof TBaseModel & string) | undefined;
58
+ alsoSet?:
59
+ | {
60
+ data: PartialEntity<TBaseModel>;
61
+ expected: PartialEntity<TBaseModel>;
62
+ }
63
+ | undefined;
64
+ }): Promise<boolean> {
65
+ return await data.service.compareAndSetColumnsByIdWithoutHooks({
66
+ id: data.id,
67
+ data: {
68
+ ...(data.alsoSet?.data || {}),
69
+ [data.statusColumn]: StatusPageSubscriberNotificationStatus.InProgress,
70
+ ...(data.claimedAtColumn
71
+ ? { [data.claimedAtColumn]: OneUptimeDate.getCurrentDate() }
72
+ : {}),
73
+ } as unknown as PartialEntity<TBaseModel>,
74
+ expectedData: {
75
+ ...(data.alsoSet?.expected || {}),
76
+ [data.statusColumn]: StatusPageSubscriberNotificationStatus.Pending,
77
+ ...(typeof data.version === "number" ? { version: data.version } : {}),
78
+ } as unknown as PartialEntity<TBaseModel>,
79
+ });
80
+ }
81
+
82
+ /*
83
+ * Fail a notification whose send was interrupted, with the reason, if it
84
+ * is still InProgress at the version the sweeper read. False when its send
85
+ * settled, or it was sent again, in the meantime.
86
+ */
87
+ public static async failInterrupted<TBaseModel extends BaseModel>(data: {
88
+ service: DatabaseService<TBaseModel>;
89
+ id: ObjectID;
90
+ statusColumn: keyof TBaseModel & string;
91
+ messageColumn: keyof TBaseModel & string;
92
+ message: string;
93
+ version: number | undefined | null;
94
+ }): Promise<boolean> {
95
+ return await data.service.compareAndSetColumnsByIdWithoutHooks({
96
+ id: data.id,
97
+ data: {
98
+ [data.statusColumn]: StatusPageSubscriberNotificationStatus.Failed,
99
+ [data.messageColumn]: data.message,
100
+ } as unknown as PartialEntity<TBaseModel>,
101
+ expectedData: {
102
+ [data.statusColumn]: StatusPageSubscriberNotificationStatus.InProgress,
103
+ ...(typeof data.version === "number" ? { version: data.version } : {}),
104
+ } as unknown as PartialEntity<TBaseModel>,
105
+ });
106
+ }
107
+ }