@oneuptime/common 14.0.6 → 14.0.8

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