@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,393 @@
1
+ import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
2
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
3
+ import LIMIT_MAX from "../../../Types/Database/LimitMax";
4
+ import BadDataException from "../../../Types/Exception/BadDataException";
5
+ import NotAuthorizedException from "../../../Types/Exception/NotAuthorizedException";
6
+ import { JSONObject } from "../../../Types/JSON";
7
+ import StatusPageSubscriberNotificationStatus from "../../../Types/StatusPage/StatusPageSubscriberNotificationStatus";
8
+ import SubscriberNotificationResend from "../../../Types/StatusPage/SubscriberNotificationResend";
9
+ import DatabaseService from "../../Services/DatabaseService";
10
+ import DatabaseRequestType from "../../Types/BaseDatabase/DatabaseRequestType";
11
+ import Query from "../../Types/Database/Query";
12
+ import Select from "../../Types/Database/Select";
13
+ import UpdateBy from "../../Types/Database/UpdateBy";
14
+ import ColumnPermissions from "../../Types/Database/Permissions/ColumnPermission";
15
+ import ModelPermission from "../../Types/Database/Permissions/Index";
16
+ import TablePermission from "../../Types/Database/Permissions/TablePermission";
17
+
18
+ /*
19
+ * Who may send a status page subscriber notification again, and when.
20
+ *
21
+ * Sending one again is a write: its status goes back to Pending, and the
22
+ * job that sends it picks it up. So the update's own permission check - the
23
+ * table's update access control and each written column's - already decides
24
+ * who may. For a public note that is not enough: the note's edit permission
25
+ * is granted on its own, and editing a note does not make someone its
26
+ * poster. Sending a note's notification again tells every subscriber of the
27
+ * incident's status pages what the note says, which is what posting it did,
28
+ * so it also needs the permission to post a note that notifies subscribers.
29
+ * The same goes for telling subscribers about an edit (the note's 'updated'
30
+ * notification): an editor can change the text first, so with the edit
31
+ * permission alone it would be a way to message every subscriber.
32
+ *
33
+ * When: never while the notification is being sent. The send settles its
34
+ * own status when it finishes, so a Pending written over a running send
35
+ * either lets a second run send it at the same time - the same subscribers
36
+ * messaged twice, each run then overwriting the other's status - or is
37
+ * overwritten and lost without a word. That holds for every notification a
38
+ * user can send again (assertNotQueuedWhileBeingSent).
39
+ *
40
+ * These checks run in the services' onBeforeUpdate, before the update's own
41
+ * check (DatabaseService runs that after the hooks), so each one checks
42
+ * permissions before it reads anything: a caller who may not send a
43
+ * notification again is told only that, never what state the notification
44
+ * is in. Rows are then read with the caller's own permissions, so nothing
45
+ * they cannot read decides the answer either.
46
+ *
47
+ * Root callers - the workers and other internal writes - are trusted and
48
+ * never checked. Master admins skip the permission checks, as they do
49
+ * everywhere, but not the checks on the notification's state.
50
+ */
51
+ export default class SubscriberNotificationResendAccess {
52
+ /*
53
+ * The columns of a public note that sending its 'posted' notification
54
+ * again writes.
55
+ */
56
+ private static readonly PUBLIC_NOTE_RESEND_COLUMNS: JSONObject = {
57
+ subscriberNotificationStatusOnNoteCreated:
58
+ StatusPageSubscriberNotificationStatus.Pending,
59
+ subscriberNotificationStatusMessage: "",
60
+ };
61
+
62
+ /*
63
+ * The columns of a public note that telling subscribers about an edit
64
+ * writes.
65
+ */
66
+ private static readonly PUBLIC_NOTE_UPDATE_NOTIFICATION_COLUMNS: JSONObject =
67
+ {
68
+ subscriberNotificationStatusOnNoteUpdated:
69
+ StatusPageSubscriberNotificationStatus.Pending,
70
+ subscriberNotificationStatusMessageOnNoteUpdated: "",
71
+ };
72
+
73
+ public static isPermissionChecked(
74
+ props: DatabaseCommonInteractionProps,
75
+ ): boolean {
76
+ return !props.isRoot && !props.isMasterAdmin;
77
+ }
78
+
79
+ /*
80
+ * Throws `refusal` unless the caller may write these columns of the model:
81
+ * the table's update access control, then each column's. A request that
82
+ * fails for any other reason - an expired session, an unpaid plan - keeps
83
+ * its own error.
84
+ */
85
+ public static assertCallerMayUpdateColumns<
86
+ TBaseModel extends BaseModel,
87
+ >(data: {
88
+ modelType: { new (): TBaseModel };
89
+ columns: JSONObject;
90
+ props: DatabaseCommonInteractionProps;
91
+ refusal: string;
92
+ }): void {
93
+ if (!this.isPermissionChecked(data.props)) {
94
+ return;
95
+ }
96
+
97
+ this.withRefusal(data.refusal, () => {
98
+ TablePermission.checkTableLevelPermissions(
99
+ data.modelType,
100
+ data.props,
101
+ DatabaseRequestType.Update,
102
+ );
103
+
104
+ ColumnPermissions.checkDataColumnPermissions(
105
+ data.modelType,
106
+ data.columns as unknown as TBaseModel,
107
+ data.props,
108
+ DatabaseRequestType.Update,
109
+ );
110
+ });
111
+ }
112
+
113
+ /*
114
+ * Throws `refusal` unless the caller may post a new note of this type
115
+ * that notifies subscribers: the create check a post with Notify Status
116
+ * Page Subscribers ticked goes through, on a note carrying only that
117
+ * choice.
118
+ */
119
+ public static assertCallerMayPostNotifyingNote<
120
+ TNote extends BaseModel,
121
+ >(data: {
122
+ modelType: { new (): TNote };
123
+ props: DatabaseCommonInteractionProps;
124
+ refusal: string;
125
+ }): void {
126
+ if (!this.isPermissionChecked(data.props)) {
127
+ return;
128
+ }
129
+
130
+ const probe: TNote = new data.modelType();
131
+ (probe as unknown as JSONObject)[
132
+ "shouldStatusPageSubscribersBeNotifiedOnNoteCreated"
133
+ ] = true;
134
+
135
+ this.withRefusal(data.refusal, () => {
136
+ ModelPermission.checkCreatePermissions(data.modelType, probe, data.props);
137
+ });
138
+ }
139
+
140
+ /*
141
+ * A user's or an API key's request to send a public note's 'posted'
142
+ * notification again - an update writing Pending into
143
+ * subscriberNotificationStatusOnNoteCreated, which is what the dashboard's
144
+ * Retry and Resend do. Shared by the incident, incident episode and
145
+ * scheduled maintenance public notes.
146
+ *
147
+ * - The caller must be able to edit the note's notification status and to
148
+ * post a note that notifies subscribers (see the header).
149
+ * - Every note the update matches must be one whose notification can be
150
+ * sent again (SubscriberNotificationResend.getPublicNoteResendRefusal):
151
+ * not a note posted without notifying subscribers, which the job never
152
+ * sends, and not one being sent right now, whose send would overwrite
153
+ * the request when it finishes.
154
+ *
155
+ * An update that does not ask for it is not looked at.
156
+ */
157
+ public static async assertPublicNoteResendAllowed<
158
+ TNote extends BaseModel,
159
+ >(data: {
160
+ modelType: { new (): TNote };
161
+ service: DatabaseService<TNote>;
162
+ updateBy: UpdateBy<TNote>;
163
+ }): Promise<void> {
164
+ const updateBy: UpdateBy<TNote> = data.updateBy;
165
+
166
+ if (
167
+ updateBy.props.isRoot ||
168
+ !SubscriberNotificationResend.isPublicNoteResendRequested(
169
+ updateBy.data as {
170
+ subscriberNotificationStatusOnNoteCreated?: unknown;
171
+ },
172
+ )
173
+ ) {
174
+ return;
175
+ }
176
+
177
+ this.assertCallerMayUpdateColumns({
178
+ modelType: data.modelType,
179
+ columns: this.PUBLIC_NOTE_RESEND_COLUMNS,
180
+ props: updateBy.props,
181
+ refusal: SubscriberNotificationResend.noPermissionToResendNoteMessage,
182
+ });
183
+
184
+ this.assertCallerMayPostNotifyingNote({
185
+ modelType: data.modelType,
186
+ props: updateBy.props,
187
+ refusal: SubscriberNotificationResend.noPermissionToResendNoteMessage,
188
+ });
189
+
190
+ const notes: Array<TNote> = await data.service.findBy({
191
+ query: updateBy.query as Query<TNote>,
192
+ select: {
193
+ _id: true,
194
+ subscriberNotificationStatusOnNoteCreated: true,
195
+ shouldStatusPageSubscribersBeNotifiedOnNoteCreated: true,
196
+ } as unknown as Select<TNote>,
197
+ limit: LIMIT_MAX,
198
+ skip: 0,
199
+ props: updateBy.props,
200
+ });
201
+
202
+ for (const note of notes) {
203
+ const record: JSONObject = note as unknown as JSONObject;
204
+
205
+ const refusal: string | null =
206
+ SubscriberNotificationResend.getPublicNoteResendRefusal({
207
+ status: record["subscriberNotificationStatusOnNoteCreated"] as
208
+ | StatusPageSubscriberNotificationStatus
209
+ | undefined,
210
+ shouldStatusPageSubscribersBeNotifiedOnNoteCreated: record[
211
+ "shouldStatusPageSubscribersBeNotifiedOnNoteCreated"
212
+ ] as boolean | undefined,
213
+ });
214
+
215
+ if (refusal) {
216
+ throw new BadDataException(refusal);
217
+ }
218
+ }
219
+ }
220
+
221
+ /*
222
+ * A user's or an API key's request to tell subscribers about an edit to a
223
+ * public note: the edit carries the notify-on-edit misc data prop
224
+ * (SubscriberUpdateNotification, which the service has turned into a
225
+ * Pending by now), or writes Pending into
226
+ * subscriberNotificationStatusOnNoteUpdated itself, as the dashboard's
227
+ * Retry of a failed update does. Shared by the incident, incident episode
228
+ * and scheduled maintenance public notes.
229
+ *
230
+ * - The caller must be able to write the update notification's status and
231
+ * to post a note that notifies subscribers (see the header).
232
+ * - It is refused while the update notification is being sent
233
+ * (assertNotQueuedWhileBeingSent): the edit can be saved without it.
234
+ *
235
+ * An update that does not ask for it is not looked at.
236
+ */
237
+ public static async assertPublicNoteUpdateNotificationAllowed<
238
+ TNote extends BaseModel,
239
+ >(data: {
240
+ modelType: { new (): TNote };
241
+ service: DatabaseService<TNote>;
242
+ updateBy: UpdateBy<TNote>;
243
+ }): Promise<void> {
244
+ const updateBy: UpdateBy<TNote> = data.updateBy;
245
+
246
+ if (
247
+ updateBy.props.isRoot ||
248
+ (updateBy.data as JSONObject)[
249
+ "subscriberNotificationStatusOnNoteUpdated"
250
+ ] !== StatusPageSubscriberNotificationStatus.Pending
251
+ ) {
252
+ return;
253
+ }
254
+
255
+ this.assertCallerMayUpdateColumns({
256
+ modelType: data.modelType,
257
+ columns: this.PUBLIC_NOTE_UPDATE_NOTIFICATION_COLUMNS,
258
+ props: updateBy.props,
259
+ refusal:
260
+ SubscriberNotificationResend.noPermissionToNotifyAboutEditMessage,
261
+ });
262
+
263
+ this.assertCallerMayPostNotifyingNote({
264
+ modelType: data.modelType,
265
+ props: updateBy.props,
266
+ refusal:
267
+ SubscriberNotificationResend.noPermissionToNotifyAboutEditMessage,
268
+ });
269
+
270
+ await this.assertNotQueuedWhileBeingSent({
271
+ modelType: data.modelType,
272
+ service: data.service,
273
+ updateBy: updateBy,
274
+ statusColumns: ["subscriberNotificationStatusOnNoteUpdated"],
275
+ refusal: SubscriberNotificationResend.updateBeingSentMessage,
276
+ });
277
+ }
278
+
279
+ /*
280
+ * A user's or an API key's update writing Pending into a notification's
281
+ * status column - Retry, Resend, or the API route of sending it again -
282
+ * while that notification is being sent (InProgress). Refused with
283
+ * `refusal` (SubscriberNotificationResend.beingSentMessage by default):
284
+ * see the header for why.
285
+ *
286
+ * Only the columns the update writes Pending into are looked at, so an
287
+ * edit that leaves the notification alone is never refused, and Pending
288
+ * written over Pending (queued already) changes nothing and is let
289
+ * through.
290
+ *
291
+ * Permission first: a caller who may not write those columns is left to
292
+ * the update's own check, which refuses them anyway, and never learns the
293
+ * notification's state from this one. The rows are then read with the
294
+ * caller's own permissions, so nothing they cannot read decides the
295
+ * answer. Root callers - the workers - are never checked.
296
+ */
297
+ public static async assertNotQueuedWhileBeingSent<
298
+ TBaseModel extends BaseModel,
299
+ >(data: {
300
+ modelType: { new (): TBaseModel };
301
+ service: DatabaseService<TBaseModel>;
302
+ updateBy: UpdateBy<TBaseModel>;
303
+ statusColumns: Array<string>;
304
+ refusal?: string | undefined;
305
+ }): Promise<void> {
306
+ const updateBy: UpdateBy<TBaseModel> = data.updateBy;
307
+
308
+ if (updateBy.props.isRoot) {
309
+ return;
310
+ }
311
+
312
+ const written: JSONObject = (updateBy.data || {}) as JSONObject;
313
+
314
+ const queuedColumns: Array<string> = data.statusColumns.filter(
315
+ (column: string): boolean => {
316
+ return (
317
+ written[column] === StatusPageSubscriberNotificationStatus.Pending
318
+ );
319
+ },
320
+ );
321
+
322
+ if (queuedColumns.length === 0) {
323
+ return;
324
+ }
325
+
326
+ const pendingColumns: JSONObject = {};
327
+ const select: JSONObject = { _id: true };
328
+
329
+ for (const column of queuedColumns) {
330
+ pendingColumns[column] = StatusPageSubscriberNotificationStatus.Pending;
331
+ select[column] = true;
332
+ }
333
+
334
+ try {
335
+ this.assertCallerMayUpdateColumns({
336
+ modelType: data.modelType,
337
+ columns: pendingColumns,
338
+ props: updateBy.props,
339
+ refusal: "",
340
+ });
341
+ } catch (err) {
342
+ if (err instanceof NotAuthorizedException) {
343
+ return;
344
+ }
345
+
346
+ throw err;
347
+ }
348
+
349
+ const rows: Array<TBaseModel> = await data.service.findBy({
350
+ query: updateBy.query as Query<TBaseModel>,
351
+ select: select as unknown as Select<TBaseModel>,
352
+ limit: LIMIT_MAX,
353
+ skip: 0,
354
+ props: updateBy.props,
355
+ });
356
+
357
+ for (const row of rows) {
358
+ const record: JSONObject = row as unknown as JSONObject;
359
+
360
+ for (const column of queuedColumns) {
361
+ if (
362
+ record[column] === StatusPageSubscriberNotificationStatus.InProgress
363
+ ) {
364
+ throw new BadDataException(
365
+ data.refusal || SubscriberNotificationResend.beingSentMessage,
366
+ );
367
+ }
368
+ }
369
+ }
370
+ }
371
+
372
+ /*
373
+ * Runs a permission check and turns its refusal into `refusal`, which says
374
+ * what the caller was trying to do. A column refusal is a BadDataException
375
+ * ("User is not allowed to update on ... column"), a table refusal a
376
+ * NotAuthorizedException; both become a NotAuthorizedException. Anything
377
+ * else - an anonymous caller, an unpaid plan - passes through as it is.
378
+ */
379
+ private static withRefusal(refusal: string, check: () => void): void {
380
+ try {
381
+ check();
382
+ } catch (err) {
383
+ if (
384
+ err instanceof NotAuthorizedException ||
385
+ err instanceof BadDataException
386
+ ) {
387
+ throw new NotAuthorizedException(refusal);
388
+ }
389
+
390
+ throw err;
391
+ }
392
+ }
393
+ }
@@ -0,0 +1,246 @@
1
+ import OneUptimeDate from "../../../Types/Date";
2
+ import ObjectID from "../../../Types/ObjectID";
3
+ import Semaphore, {
4
+ SemaphoreLockTimeoutError,
5
+ SemaphorePermit,
6
+ } from "../../Infrastructure/Semaphore";
7
+ import logger from "../Logger";
8
+
9
+ /*
10
+ * How many runs of one subscriber job may be sending at once, across every
11
+ * worker.
12
+ *
13
+ * The incident and episode subscriber jobs run every minute on the shared
14
+ * Worker queue, and each run awaits the notifications it claims - minutes,
15
+ * for a big or slow send (SubscriberNotificationTiming). The runs overlap by
16
+ * design, and nothing bounded how many: with a backlog of slow sends - a
17
+ * tenant whose mail server answers slowly, a large outage with many notes and
18
+ * state changes on big pages - every job could keep a run going every minute
19
+ * for the length of its timeout, some 25-30 runs per job, nine jobs, all
20
+ * holding slots of the one Worker consumer (WORKER_CONCURRENCY, 100 by
21
+ * default) that on-call escalation, monitors and every other cron share.
22
+ * Exactly during an outage, subscriber email could starve paging.
23
+ *
24
+ * So a run takes a permit first: at most MAX_CONCURRENT_RUNS_PER_JOB runs of
25
+ * a job hold one at a time, nine jobs times that in all. A run that finds
26
+ * them all taken returns at once, leaving its notifications Pending for the
27
+ * runs that follow; a run's claim is atomic (SubscriberNotificationClaim), so
28
+ * which run sends a notification never matters. A permit is refreshed while
29
+ * its run lives and lapses PERMIT_TTL_IN_MS after a worker dies holding it,
30
+ * so a crash frees its slot within minutes, not at the job timeout.
31
+ *
32
+ * If Redis cannot be reached for the permit, the run goes ahead without one:
33
+ * the queue that started it runs on Redis too, so that is a blip, and sending
34
+ * the notifications matters more than bounding them for its length.
35
+ *
36
+ * One project cannot take them all. The permits are shared by every project
37
+ * on the server, and a run sends one notification at a time, so a tenant
38
+ * whose own mail server hangs - each message waiting out its timeout - or who
39
+ * posts a burst of notes on a large page would otherwise hold every run of
40
+ * the job, and every other tenant's notifications of that kind would wait
41
+ * behind it. So a run also takes one of the project's slots for the job
42
+ * before it claims each notification (takeProjectSlot): at most
43
+ * MAX_CONCURRENT_SENDS_PER_PROJECT of one project's notifications of a job
44
+ * are being sent at once, and a run that finds the project's slots taken
45
+ * leaves that notification Pending and goes on to the next project's.
46
+ *
47
+ * Both numbers can be raised for a larger fleet:
48
+ * SUBSCRIBER_NOTIFICATION_MAX_CONCURRENT_RUNS_PER_JOB and
49
+ * SUBSCRIBER_NOTIFICATION_MAX_CONCURRENT_SENDS_PER_PROJECT.
50
+ */
51
+
52
+ // A slot one project holds while one of its notifications is being sent.
53
+ export interface SubscriberNotificationProjectSlot {
54
+ release: () => Promise<void>;
55
+ }
56
+
57
+ const parsePositiveIntFromEnv: (envKey: string, fallback: number) => number = (
58
+ envKey: string,
59
+ fallback: number,
60
+ ): number => {
61
+ const rawValue: string | undefined = process.env[envKey];
62
+
63
+ if (!rawValue) {
64
+ return fallback;
65
+ }
66
+
67
+ const parsedValue: number = parseInt(rawValue, 10);
68
+
69
+ if (!Number.isFinite(parsedValue) || parsedValue <= 0) {
70
+ return fallback;
71
+ }
72
+
73
+ return parsedValue;
74
+ };
75
+
76
+ // Held when there is nothing to bound: no project, or no Redis.
77
+ const NO_SLOT: SubscriberNotificationProjectSlot = {
78
+ release: async (): Promise<void> => {},
79
+ };
80
+
81
+ export default class SubscriberNotificationRunLimit {
82
+ public static readonly MAX_CONCURRENT_RUNS_PER_JOB: number =
83
+ parsePositiveIntFromEnv(
84
+ "SUBSCRIBER_NOTIFICATION_MAX_CONCURRENT_RUNS_PER_JOB",
85
+ 4,
86
+ );
87
+
88
+ /*
89
+ * Fewer than the runs of a job, so a project whose sends are slow always
90
+ * leaves some of them to everyone else.
91
+ */
92
+ public static readonly MAX_CONCURRENT_SENDS_PER_PROJECT: number = Math.max(
93
+ 1,
94
+ Math.min(
95
+ parsePositiveIntFromEnv(
96
+ "SUBSCRIBER_NOTIFICATION_MAX_CONCURRENT_SENDS_PER_PROJECT",
97
+ 2,
98
+ ),
99
+ SubscriberNotificationRunLimit.MAX_CONCURRENT_RUNS_PER_JOB - 1,
100
+ ),
101
+ );
102
+
103
+ // The per-project slots' Redis namespace; the key is the job and the project.
104
+ public static readonly PROJECT_NAMESPACE: string =
105
+ "subscriber-notification-project";
106
+
107
+ public static readonly PERMIT_TTL_IN_MS: number =
108
+ OneUptimeDate.convertMinutesToMilliseconds(2);
109
+
110
+ // The permits' Redis namespace; the key is the job's name.
111
+ public static readonly NAMESPACE: string = "subscriber-notification-run";
112
+
113
+ /*
114
+ * The job's run, run only while it holds one of the job's permits. For
115
+ * RunCron: RunCron("Job:Name", options, limit("Job:Name", async () => {})).
116
+ */
117
+ public static limit(
118
+ jobName: string,
119
+ run: () => Promise<void>,
120
+ ): () => Promise<void> {
121
+ return async (): Promise<void> => {
122
+ await SubscriberNotificationRunLimit.runWithinLimit(jobName, run);
123
+ };
124
+ }
125
+
126
+ /*
127
+ * One of the project's slots for this job, taken before a run claims one
128
+ * of the project's notifications; release it once that notification has
129
+ * settled. Null when the project already has
130
+ * MAX_CONCURRENT_SENDS_PER_PROJECT of this job's notifications being sent:
131
+ * leave this one Pending for a later run, and go on to the next. Never
132
+ * throws: without a project, or without Redis, the send goes ahead
133
+ * unbounded, as a run does without its permit.
134
+ */
135
+ public static async takeProjectSlot(data: {
136
+ jobName: string;
137
+ projectId: ObjectID | string | undefined | null;
138
+ }): Promise<SubscriberNotificationProjectSlot | null> {
139
+ const projectId: string = data.projectId?.toString().trim() || "";
140
+
141
+ if (!projectId) {
142
+ return NO_SLOT;
143
+ }
144
+
145
+ let permit: SemaphorePermit;
146
+
147
+ try {
148
+ permit = await Semaphore.acquirePermit({
149
+ key: `${data.jobName}:${projectId.toLowerCase()}`,
150
+ namespace: SubscriberNotificationRunLimit.PROJECT_NAMESPACE,
151
+ limit: SubscriberNotificationRunLimit.MAX_CONCURRENT_SENDS_PER_PROJECT,
152
+ lockTimeout: SubscriberNotificationRunLimit.PERMIT_TTL_IN_MS,
153
+ acquireAttemptsLimit: 1,
154
+ acquireTimeout: 5000,
155
+ retryInterval: 50,
156
+ onLockLost: (err: Error): void => {
157
+ logger.warn(
158
+ `${data.jobName}: lost the slot of project ${projectId} while sending: ${err.message}`,
159
+ );
160
+ },
161
+ });
162
+ } catch (err) {
163
+ if (err instanceof SemaphoreLockTimeoutError) {
164
+ logger.debug(
165
+ `${data.jobName}: project ${projectId} already has ${SubscriberNotificationRunLimit.MAX_CONCURRENT_SENDS_PER_PROJECT} notification(s) being sent; leaving this one Pending.`,
166
+ );
167
+ return null;
168
+ }
169
+
170
+ logger.warn(
171
+ `${data.jobName}: could not take a slot for project ${projectId} (${err instanceof Error ? err.message : String(err)}); sending without one.`,
172
+ );
173
+ return NO_SLOT;
174
+ }
175
+
176
+ return {
177
+ release: async (): Promise<void> => {
178
+ try {
179
+ await Semaphore.releasePermit(permit);
180
+ } catch (err) {
181
+ // It lapses on its own after PERMIT_TTL_IN_MS.
182
+ logger.warn(
183
+ `${data.jobName}: could not release the slot of project ${projectId}: ${err instanceof Error ? err.message : String(err)}`,
184
+ );
185
+ }
186
+ },
187
+ };
188
+ }
189
+
190
+ public static async runWithinLimit(
191
+ jobName: string,
192
+ run: () => Promise<void>,
193
+ ): Promise<void> {
194
+ let permit: SemaphorePermit | null = null;
195
+
196
+ try {
197
+ permit = await Semaphore.acquirePermit({
198
+ key: jobName,
199
+ namespace: SubscriberNotificationRunLimit.NAMESPACE,
200
+ limit: SubscriberNotificationRunLimit.MAX_CONCURRENT_RUNS_PER_JOB,
201
+ lockTimeout: SubscriberNotificationRunLimit.PERMIT_TTL_IN_MS,
202
+ // One look: a full set of permits means other runs are sending.
203
+ acquireAttemptsLimit: 1,
204
+ acquireTimeout: 5000,
205
+ retryInterval: 50,
206
+ onLockLost: (err: Error): void => {
207
+ /*
208
+ * Redis lost the permit (it could not be refreshed). The run goes
209
+ * on - its claims keep its notifications its own - and another
210
+ * run may take the freed slot meanwhile.
211
+ */
212
+ logger.warn(
213
+ `${jobName}: lost its run permit while sending: ${err.message}`,
214
+ );
215
+ },
216
+ });
217
+ } catch (err) {
218
+ if (err instanceof SemaphoreLockTimeoutError) {
219
+ logger.debug(
220
+ `${jobName}: ${SubscriberNotificationRunLimit.MAX_CONCURRENT_RUNS_PER_JOB} runs are already sending; leaving the Pending notifications to the runs that follow.`,
221
+ );
222
+ return;
223
+ }
224
+
225
+ logger.warn(
226
+ `${jobName}: could not take a run permit (${err instanceof Error ? err.message : String(err)}); running without one.`,
227
+ );
228
+ permit = null;
229
+ }
230
+
231
+ try {
232
+ await run();
233
+ } finally {
234
+ if (permit) {
235
+ try {
236
+ await Semaphore.releasePermit(permit);
237
+ } catch (err) {
238
+ // It lapses on its own after PERMIT_TTL_IN_MS.
239
+ logger.warn(
240
+ `${jobName}: could not release its run permit: ${err instanceof Error ? err.message : String(err)}`,
241
+ );
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }