@oneuptime/common 12.0.13 → 12.0.14

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 (376) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +14 -0
  2. package/Models/DatabaseModels/UserCall.ts +94 -3
  3. package/Models/DatabaseModels/UserEmail.ts +94 -3
  4. package/Models/DatabaseModels/UserIncomingCallNumber.ts +94 -3
  5. package/Models/DatabaseModels/UserPush.ts +37 -0
  6. package/Models/DatabaseModels/UserSMS.ts +94 -3
  7. package/Models/DatabaseModels/UserWhatsApp.ts +93 -2
  8. package/Server/API/BaseAPI.ts +79 -14
  9. package/Server/API/UserCallAPI.ts +53 -45
  10. package/Server/API/UserEmailAPI.ts +53 -44
  11. package/Server/API/UserIncomingCallNumberAPI.ts +48 -38
  12. package/Server/API/UserNotificationMethodAdminAPI.ts +481 -0
  13. package/Server/API/UserPushAPI.ts +126 -2
  14. package/Server/API/UserSmsAPI.ts +53 -44
  15. package/Server/API/UserWhatsAppAPI.ts +53 -50
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.ts +154 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.ts +17 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.ts +45 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/VerificationCodeRateLimit.ts +586 -0
  21. package/Server/Services/AnalyticsDatabaseService.ts +169 -0
  22. package/Server/Services/DatabaseService.ts +101 -0
  23. package/Server/Services/LogAggregationService.ts +5 -0
  24. package/Server/Services/PushNotificationService.ts +127 -12
  25. package/Server/Services/TeamMemberService.ts +53 -13
  26. package/Server/Services/UserCallService.ts +55 -24
  27. package/Server/Services/UserEmailService.ts +83 -17
  28. package/Server/Services/UserIncomingCallNumberService.ts +49 -85
  29. package/Server/Services/UserNotificationMethodAdminService.ts +1527 -0
  30. package/Server/Services/UserNotificationRuleService.ts +26 -0
  31. package/Server/Services/UserPushService.ts +89 -0
  32. package/Server/Services/UserSmsService.ts +50 -17
  33. package/Server/Services/UserWhatsAppService.ts +48 -15
  34. package/Server/Types/Database/QueryHelper.ts +30 -0
  35. package/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.ts +59 -0
  36. package/Server/Utils/ChannelVerification.ts +510 -0
  37. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +13 -16
  38. package/Server/Utils/Monitor/MonitorAlert.ts +24 -21
  39. package/Server/Utils/Monitor/MonitorIncident.ts +21 -18
  40. package/Server/Utils/Monitor/MonitorResourceContext.ts +107 -0
  41. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +442 -0
  42. package/Server/Utils/Monitor/SeriesResourceLabels.ts +3 -3
  43. package/Server/Utils/Monitor/SeriesResourceLinker.ts +96 -28
  44. package/Server/Utils/TotpAuth.ts +137 -9
  45. package/Server/Utils/UserRegistrationToken.ts +182 -0
  46. package/Server/Utils/VerificationCode.ts +134 -0
  47. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +173 -94
  48. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +1151 -0
  49. package/Tests/App/Dashboard/AdminUserOnCallPages.test.tsx +905 -0
  50. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +338 -0
  51. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1 -1
  52. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +28 -0
  53. package/Tests/Models/UserPushCriticalAlertColumn.test.ts +172 -0
  54. package/Tests/Server/API/BaseAPIGetListParallel.test.ts +519 -0
  55. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +543 -0
  56. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +108 -18
  57. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +963 -0
  58. package/Tests/Server/API/UserPushCriticalAlertsApi.test.ts +114 -0
  59. package/Tests/Server/API/UserSmsApi.test.ts +107 -14
  60. package/Tests/Server/API/UserTotpAuthAPI.test.ts +427 -0
  61. package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +315 -0
  62. package/Tests/Server/Middleware/VerificationCodeRateLimit.test.ts +775 -0
  63. package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +10 -62
  64. package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +33 -13
  65. package/Tests/Server/Services/AnalyticsDatabaseSortKeyBoundary.test.ts +658 -0
  66. package/Tests/Server/Services/CriticalOnCallAlertDelivery.test.ts +458 -0
  67. package/Tests/Server/Services/CriticalPushAlertPayload.test.ts +301 -0
  68. package/Tests/Server/Services/DatabaseServiceAtomicIncrement.test.ts +143 -0
  69. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +1 -0
  70. package/Tests/Server/Services/LogAggregationScanMemory.test.ts +421 -0
  71. package/Tests/Server/Services/LogAggregationService.test.ts +2 -2
  72. package/Tests/Server/Services/NotificationChannelCodeIssuance.test.ts +401 -0
  73. package/Tests/Server/Services/PushNotificationDeliveryOptions.test.ts +204 -0
  74. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +21 -1
  75. package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +290 -0
  76. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +948 -0
  77. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +1 -0
  78. package/Tests/Server/Services/UserPushCriticalAlertToggle.test.ts +255 -0
  79. package/Tests/Server/Services/UserTotpAuthEnrolment.test.ts +218 -0
  80. package/Tests/Server/TestingUtils/AuthenticatorApp.ts +248 -0
  81. package/Tests/Server/Types/Database/Permissions/NotificationChannelVerificationColumns.test.ts +135 -0
  82. package/Tests/Server/Types/Database/Permissions/OnCallScheduleRelationSelect.test.ts +504 -0
  83. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAnyOf.test.ts +91 -0
  84. package/Tests/Server/Types/Workflow/Components/JsonToText.test.ts +384 -0
  85. package/Tests/Server/Types/Workflow/Components/MergeJson.test.ts +403 -0
  86. package/Tests/Server/Utils/Browser.test.ts +162 -0
  87. package/Tests/Server/Utils/ChannelVerification.test.ts +689 -0
  88. package/Tests/Server/Utils/Express.test.ts +371 -0
  89. package/Tests/Server/Utils/Monitor/Criteria/CustomCodeMonitorCriteria.test.ts +707 -0
  90. package/Tests/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.test.ts +698 -0
  91. package/Tests/Server/Utils/Monitor/Criteria/IncomingEmailBodyCriteria.test.ts +243 -0
  92. package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestBodyCriteria.test.ts +278 -0
  93. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +160 -20
  94. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +13 -8
  95. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +162 -18
  96. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +349 -0
  97. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +690 -0
  98. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +7 -2
  99. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +92 -18
  100. package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +267 -0
  101. package/Tests/Server/Utils/Telemetry/OneuptimeLabel.test.ts +439 -0
  102. package/Tests/Server/Utils/TotpAuth.test.ts +719 -0
  103. package/Tests/Server/Utils/UserRegistrationToken.test.ts +322 -0
  104. package/Tests/Server/Utils/VerificationCode.test.ts +296 -0
  105. package/Tests/Types/AutoRemediation/AutoRemediationSuggestionStatus.test.ts +192 -0
  106. package/Tests/Types/Call/CallRequest.test.ts +189 -0
  107. package/Tests/Types/Dashboard/DashboardSize.test.ts +226 -0
  108. package/Tests/Types/Incident/IncidentSlaStatus.test.ts +191 -0
  109. package/Tests/Types/Metrics/MetricDashboardMetricType.test.ts +278 -0
  110. package/Tests/Types/Metrics/MetricPipelineRuleType.test.ts +517 -0
  111. package/Tests/Types/Monitor/IncomingMonitorDefaultCriteria.test.ts +470 -0
  112. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +6 -4
  113. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +262 -5
  114. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +1 -1
  115. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +1 -1
  116. package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +905 -0
  117. package/Tests/Types/Service/ServiceLanguage.test.ts +255 -0
  118. package/Tests/Types/TextRandomGeneration.test.ts +211 -0
  119. package/Tests/UI/Components/Button.test.tsx +175 -1
  120. package/Tests/UI/Components/CodeBlockLanguages.test.tsx +236 -0
  121. package/Tests/UI/Components/CommandPalette/CommandPalette.test.tsx +568 -0
  122. package/Tests/UI/Components/CommandPalette/CommandPaletteKeyboard.test.tsx +239 -0
  123. package/Tests/UI/Components/CommandPalette/CommandPaletteProviders.test.tsx +400 -0
  124. package/Tests/UI/Components/CommandPalette/PaletteFilter.test.ts +234 -0
  125. package/Tests/UI/Components/CommandPalette/RecentCommands.test.ts +97 -0
  126. package/Tests/UI/Components/CountModelSideMenuItemGuard.test.tsx +198 -0
  127. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +1 -1
  128. package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +267 -0
  129. package/Tests/UI/Components/List.test.tsx +9 -2
  130. package/Tests/UI/Components/ListLoadingStates.test.tsx +173 -0
  131. package/Tests/UI/Components/MarkdownLazy.test.tsx +113 -0
  132. package/Tests/UI/Components/MarkdownMermaidRetry.test.tsx +110 -0
  133. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +8 -0
  134. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +692 -0
  135. package/Tests/UI/Components/MoreMenuMotion.test.tsx +118 -0
  136. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +178 -0
  137. package/Tests/UI/Components/OrderedStatesList.test.tsx +19 -2
  138. package/Tests/UI/Components/SideOverMotion.test.tsx +109 -0
  139. package/Tests/UI/Components/Skeleton.test.tsx +71 -0
  140. package/Tests/UI/Components/TableLoadingStates.test.tsx +279 -0
  141. package/Tests/UI/Components/Tabs.test.tsx +24 -0
  142. package/Tests/UI/Components/Toast.test.tsx +130 -7
  143. package/Tests/UI/Components/ToastStacking.test.tsx +198 -0
  144. package/Tests/UI/Utils/Dropdown.test.ts +423 -0
  145. package/Tests/UI/Utils/ModelAPITenantHeader.test.ts +122 -0
  146. package/Tests/UI/Utils/ModelListCache.test.ts +338 -0
  147. package/Tests/UI/Utils/ProjectListCacheInvalidation.test.ts +225 -0
  148. package/Tests/Utils/API.test.ts +16 -2
  149. package/Tests/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.test.ts +347 -0
  150. package/Tests/Utils/Dashboard/Components/DashboardListSharedArgs.test.ts +259 -0
  151. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +466 -0
  152. package/Tests/Utils/Rum/UrlScrubberSegments.test.ts +362 -0
  153. package/Types/Email/EmailTemplateType.ts +1 -0
  154. package/Types/Monitor/MonitorCriteriaInstance.ts +41 -20
  155. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +131 -8
  156. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +48 -8
  157. package/Types/Monitor/ServiceAlertTemplates.ts +1352 -0
  158. package/Types/Permission.ts +35 -0
  159. package/Types/PushNotification/AndroidNotificationChannel.ts +26 -0
  160. package/Types/PushNotification/PushNotificationMessage.ts +15 -0
  161. package/Types/PushNotification/PushNotificationRequest.ts +8 -16
  162. package/Types/Service/ServiceLanguage.ts +144 -0
  163. package/Types/Text.ts +111 -19
  164. package/UI/Components/Button/Button.tsx +27 -14
  165. package/UI/Components/CodeBlock/CodeBlock.tsx +54 -7
  166. package/UI/Components/CodeBlock/LanguageRegistry.ts +103 -0
  167. package/UI/Components/CommandPalette/CommandPalette.tsx +790 -0
  168. package/UI/Components/CommandPalette/PaletteFilter.ts +196 -0
  169. package/UI/Components/CommandPalette/PaletteRow.tsx +173 -0
  170. package/UI/Components/CommandPalette/RecentCommands.ts +57 -0
  171. package/UI/Components/CommandPalette/Types.ts +49 -0
  172. package/UI/Components/CommandPalette/UseProviderSearch.ts +129 -0
  173. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  174. package/UI/Components/Feed/FeedItem.tsx +1 -1
  175. package/UI/Components/List/List.tsx +37 -5
  176. package/UI/Components/List/ListSkeleton.tsx +54 -0
  177. package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +6 -1
  178. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +71 -26
  179. package/UI/Components/Modal/Modal.tsx +2 -2
  180. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  181. package/UI/Components/ModelTable/useCustomFieldColumns.ts +11 -5
  182. package/UI/Components/MoreMenu/MoreMenu.tsx +39 -1
  183. package/UI/Components/Navbar/NavBar.tsx +14 -1
  184. package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +50 -5
  185. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +40 -1
  186. package/UI/Components/SideOver/SideOver.tsx +37 -2
  187. package/UI/Components/Skeleton/Skeleton.tsx +53 -0
  188. package/UI/Components/Table/Table.tsx +74 -44
  189. package/UI/Components/Table/TableSkeletonRows.tsx +141 -0
  190. package/UI/Components/Tabs/Tab.tsx +1 -1
  191. package/UI/Components/Toast/Toast.tsx +118 -53
  192. package/UI/Components/Toast/ToastInit.tsx +82 -32
  193. package/UI/Styles/Theme.css +40 -0
  194. package/UI/Utils/ModelAPI/ModelAPI.ts +20 -10
  195. package/UI/Utils/ModelListCache.ts +188 -0
  196. package/UI/Utils/Project.ts +29 -0
  197. package/Utils/API.ts +6 -1
  198. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +14 -0
  199. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/UserCall.js +101 -3
  201. package/build/dist/Models/DatabaseModels/UserCall.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/UserEmail.js +101 -3
  203. package/build/dist/Models/DatabaseModels/UserEmail.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js +101 -3
  205. package/build/dist/Models/DatabaseModels/UserIncomingCallNumber.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/UserPush.js +38 -0
  207. package/build/dist/Models/DatabaseModels/UserPush.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/UserSMS.js +101 -3
  209. package/build/dist/Models/DatabaseModels/UserSMS.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/UserWhatsApp.js +100 -2
  211. package/build/dist/Models/DatabaseModels/UserWhatsApp.js.map +1 -1
  212. package/build/dist/Server/API/BaseAPI.js +69 -13
  213. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  214. package/build/dist/Server/API/UserCallAPI.js +42 -38
  215. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  216. package/build/dist/Server/API/UserEmailAPI.js +42 -38
  217. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  218. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +37 -32
  219. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  220. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js +341 -0
  221. package/build/dist/Server/API/UserNotificationMethodAdminAPI.js.map +1 -0
  222. package/build/dist/Server/API/UserPushAPI.js +95 -2
  223. package/build/dist/Server/API/UserPushAPI.js.map +1 -1
  224. package/build/dist/Server/API/UserSmsAPI.js +42 -38
  225. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  226. package/build/dist/Server/API/UserWhatsAppAPI.js +42 -40
  227. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js +89 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787142779538-MigrationName.js.map +1 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js +12 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787156982416-MigrationName.js.map +1 -0
  232. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js +40 -0
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787700000000-FixTotpOtpUrlAlgorithm.js.map +1 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  236. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js +393 -0
  237. package/build/dist/Server/Middleware/VerificationCodeRateLimit.js.map +1 -0
  238. package/build/dist/Server/Services/AnalyticsDatabaseService.js +136 -0
  239. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  240. package/build/dist/Server/Services/DatabaseService.js +74 -0
  241. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  242. package/build/dist/Server/Services/LogAggregationService.js +5 -0
  243. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  244. package/build/dist/Server/Services/PushNotificationService.js +69 -30
  245. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  246. package/build/dist/Server/Services/TeamMemberService.js +43 -7
  247. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  248. package/build/dist/Server/Services/UserCallService.js +54 -22
  249. package/build/dist/Server/Services/UserCallService.js.map +1 -1
  250. package/build/dist/Server/Services/UserEmailService.js +78 -17
  251. package/build/dist/Server/Services/UserEmailService.js.map +1 -1
  252. package/build/dist/Server/Services/UserIncomingCallNumberService.js +45 -74
  253. package/build/dist/Server/Services/UserIncomingCallNumberService.js.map +1 -1
  254. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1117 -0
  255. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -0
  256. package/build/dist/Server/Services/UserNotificationRuleService.js +20 -0
  257. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  258. package/build/dist/Server/Services/UserPushService.js +77 -0
  259. package/build/dist/Server/Services/UserPushService.js.map +1 -1
  260. package/build/dist/Server/Services/UserSmsService.js +50 -17
  261. package/build/dist/Server/Services/UserSmsService.js.map +1 -1
  262. package/build/dist/Server/Services/UserWhatsAppService.js +48 -15
  263. package/build/dist/Server/Services/UserWhatsAppService.js.map +1 -1
  264. package/build/dist/Server/Types/Database/QueryHelper.js +27 -0
  265. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  266. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js +46 -0
  267. package/build/dist/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.js.map +1 -1
  268. package/build/dist/Server/Utils/ChannelVerification.js +348 -0
  269. package/build/dist/Server/Utils/ChannelVerification.js.map +1 -0
  270. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +13 -12
  271. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
  272. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +20 -17
  273. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  274. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +17 -14
  275. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  276. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +95 -0
  277. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -0
  278. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +313 -0
  279. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -0
  280. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +3 -3
  281. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +67 -26
  282. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  283. package/build/dist/Server/Utils/TotpAuth.js +127 -8
  284. package/build/dist/Server/Utils/TotpAuth.js.map +1 -1
  285. package/build/dist/Server/Utils/UserRegistrationToken.js +170 -0
  286. package/build/dist/Server/Utils/UserRegistrationToken.js.map +1 -0
  287. package/build/dist/Server/Utils/VerificationCode.js +120 -0
  288. package/build/dist/Server/Utils/VerificationCode.js.map +1 -0
  289. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  290. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  291. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +42 -21
  292. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  293. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +77 -5
  294. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  295. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +18 -6
  296. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  297. package/build/dist/Types/Monitor/ServiceAlertTemplates.js +1028 -0
  298. package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -0
  299. package/build/dist/Types/Permission.js +33 -0
  300. package/build/dist/Types/Permission.js.map +1 -1
  301. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js +27 -0
  302. package/build/dist/Types/PushNotification/AndroidNotificationChannel.js.map +1 -0
  303. package/build/dist/Types/Service/ServiceLanguage.js +96 -0
  304. package/build/dist/Types/Service/ServiceLanguage.js.map +1 -0
  305. package/build/dist/Types/Text.js +82 -17
  306. package/build/dist/Types/Text.js.map +1 -1
  307. package/build/dist/UI/Components/Button/Button.js +22 -14
  308. package/build/dist/UI/Components/Button/Button.js.map +1 -1
  309. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +35 -3
  310. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  311. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js +57 -0
  312. package/build/dist/UI/Components/CodeBlock/LanguageRegistry.js.map +1 -0
  313. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +439 -0
  314. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -0
  315. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js +135 -0
  316. package/build/dist/UI/Components/CommandPalette/PaletteFilter.js.map +1 -0
  317. package/build/dist/UI/Components/CommandPalette/PaletteRow.js +83 -0
  318. package/build/dist/UI/Components/CommandPalette/PaletteRow.js.map +1 -0
  319. package/build/dist/UI/Components/CommandPalette/RecentCommands.js +47 -0
  320. package/build/dist/UI/Components/CommandPalette/RecentCommands.js.map +1 -0
  321. package/build/dist/UI/Components/CommandPalette/Types.js +2 -0
  322. package/build/dist/UI/Components/CommandPalette/Types.js.map +1 -0
  323. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js +88 -0
  324. package/build/dist/UI/Components/CommandPalette/UseProviderSearch.js.map +1 -0
  325. package/build/dist/UI/Components/Dropdown/Dropdown.js +6 -1
  326. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  327. package/build/dist/UI/Components/Feed/FeedItem.js +1 -1
  328. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  329. package/build/dist/UI/Components/List/List.js +19 -5
  330. package/build/dist/UI/Components/List/List.js.map +1 -1
  331. package/build/dist/UI/Components/List/ListSkeleton.js +21 -0
  332. package/build/dist/UI/Components/List/ListSkeleton.js.map +1 -0
  333. package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
  334. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +52 -25
  335. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  336. package/build/dist/UI/Components/Modal/Modal.js +2 -2
  337. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  338. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  339. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +9 -2
  340. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  341. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +32 -1
  342. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  343. package/build/dist/UI/Components/Navbar/NavBar.js +5 -1
  344. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  345. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +29 -5
  346. package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
  347. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +23 -1
  348. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  349. package/build/dist/UI/Components/SideOver/SideOver.js +23 -2
  350. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  351. package/build/dist/UI/Components/Skeleton/Skeleton.js +23 -0
  352. package/build/dist/UI/Components/Skeleton/Skeleton.js.map +1 -0
  353. package/build/dist/UI/Components/Table/Table.js +35 -24
  354. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  355. package/build/dist/UI/Components/Table/TableSkeletonRows.js +52 -0
  356. package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -0
  357. package/build/dist/UI/Components/Tabs/Tab.js +1 -1
  358. package/build/dist/UI/Components/Tabs/Tab.js.map +1 -1
  359. package/build/dist/UI/Components/Toast/Toast.js +77 -23
  360. package/build/dist/UI/Components/Toast/Toast.js.map +1 -1
  361. package/build/dist/UI/Components/Toast/ToastInit.js +37 -15
  362. package/build/dist/UI/Components/Toast/ToastInit.js.map +1 -1
  363. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -9
  364. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  365. package/build/dist/UI/Utils/ModelListCache.js +126 -0
  366. package/build/dist/UI/Utils/ModelListCache.js.map +1 -0
  367. package/build/dist/UI/Utils/Project.js +24 -1
  368. package/build/dist/UI/Utils/Project.js.map +1 -1
  369. package/build/dist/Utils/API.js +6 -1
  370. package/build/dist/Utils/API.js.map +1 -1
  371. package/jest.config.json +1 -1
  372. package/package.json +2 -3
  373. package/Server/Utils/Monitor/MonitorClusterContext.ts +0 -182
  374. package/Tests/__mocks__/otpauth.js +0 -30
  375. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +0 -141
  376. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +0 -1
@@ -0,0 +1,1117 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import DatabaseConfig from "../DatabaseConfig";
11
+ import BaseService from "./BaseService";
12
+ import MailService from "./MailService";
13
+ import OnCallReadinessService, { MaskedIdentifierKind, ReadinessMethodType, maskIdentifier, } from "./OnCallReadinessService";
14
+ import ProjectService from "./ProjectService";
15
+ import TeamMemberService from "./TeamMemberService";
16
+ import UserCallService from "./UserCallService";
17
+ import UserEmailService from "./UserEmailService";
18
+ import UserNotificationRuleService from "./UserNotificationRuleService";
19
+ import UserPushService from "./UserPushService";
20
+ import UserService from "./UserService";
21
+ import UserSmsService from "./UserSmsService";
22
+ import UserTelegramService from "./UserTelegramService";
23
+ import UserWebhookService from "./UserWebhookService";
24
+ import UserWhatsAppService from "./UserWhatsAppService";
25
+ import logger from "../Utils/Logger";
26
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
27
+ import URL from "../../Types/API/URL";
28
+ import AuditLogAction from "../../Types/AuditLog/AuditLogAction";
29
+ import SortOrder from "../../Types/BaseDatabase/SortOrder";
30
+ import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
31
+ import Email from "../../Types/Email";
32
+ import EmailTemplateType from "../../Types/Email/EmailTemplateType";
33
+ import BadDataException from "../../Types/Exception/BadDataException";
34
+ import Phone from "../../Types/Phone";
35
+ import UserCall from "../../Models/DatabaseModels/UserCall";
36
+ import UserEmail from "../../Models/DatabaseModels/UserEmail";
37
+ import UserPush from "../../Models/DatabaseModels/UserPush";
38
+ import UserSMS from "../../Models/DatabaseModels/UserSMS";
39
+ import UserTelegram from "../../Models/DatabaseModels/UserTelegram";
40
+ import UserWebhook from "../../Models/DatabaseModels/UserWebhook";
41
+ import UserWhatsApp from "../../Models/DatabaseModels/UserWhatsApp";
42
+ /**
43
+ * The four channels an administrator may CREATE. See the header for why the
44
+ * other three are absent; this enum is the single place that decides, and both
45
+ * the API's validation and the service's own dispatch read it.
46
+ */
47
+ export var AdminAddableChannel;
48
+ (function (AdminAddableChannel) {
49
+ AdminAddableChannel["Email"] = "Email";
50
+ AdminAddableChannel["SMS"] = "SMS";
51
+ AdminAddableChannel["Call"] = "Call";
52
+ AdminAddableChannel["WhatsApp"] = "WhatsApp";
53
+ })(AdminAddableChannel || (AdminAddableChannel = {}));
54
+ export class UserNotificationMethodAdminService extends BaseService {
55
+ /*
56
+ * The seven channels. Every one of them is listable and deletable; four are
57
+ * creatable. A channel missing from this table is a channel this service
58
+ * cannot see at all, which is why the list is exhaustive rather than
59
+ * restricted to the interesting ones — an admin looking at a responder whose
60
+ * only method is a webhook must be told the webhook is there, or they will
61
+ * conclude the person has nothing and go and add a phone number that
62
+ * duplicates a working channel.
63
+ */
64
+ getChannelDescriptors() {
65
+ return [
66
+ {
67
+ methodType: ReadinessMethodType.Email,
68
+ isAdminAddable: true,
69
+ modelType: UserEmail,
70
+ list: async (data) => {
71
+ const rows = await UserEmailService.findBy({
72
+ query: { projectId: data.projectId, userId: data.userId },
73
+ select: {
74
+ _id: true,
75
+ email: true,
76
+ isVerified: true,
77
+ createdAt: true,
78
+ },
79
+ sort: { createdAt: SortOrder.Ascending },
80
+ skip: 0,
81
+ limit: LIMIT_PER_PROJECT,
82
+ props: { isRoot: true },
83
+ });
84
+ return rows.map((row) => {
85
+ var _a;
86
+ return this.toView({
87
+ row: row,
88
+ methodType: ReadinessMethodType.Email,
89
+ isAdminAddable: true,
90
+ identifier: (_a = row.email) === null || _a === void 0 ? void 0 : _a.toString(),
91
+ kind: MaskedIdentifierKind.Email,
92
+ isVerified: row.isVerified,
93
+ });
94
+ });
95
+ },
96
+ findOwnedRow: async (data) => {
97
+ var _a;
98
+ const row = await UserEmailService.findOneBy({
99
+ query: {
100
+ _id: data.methodId,
101
+ projectId: data.projectId,
102
+ userId: data.userId,
103
+ },
104
+ select: { _id: true, email: true, isVerified: true },
105
+ props: { isRoot: true },
106
+ });
107
+ return row
108
+ ? this.toView({
109
+ row: row,
110
+ methodType: ReadinessMethodType.Email,
111
+ isAdminAddable: true,
112
+ identifier: (_a = row.email) === null || _a === void 0 ? void 0 : _a.toString(),
113
+ kind: MaskedIdentifierKind.Email,
114
+ isVerified: row.isVerified,
115
+ })
116
+ : null;
117
+ },
118
+ deleteRow: async (data) => {
119
+ await UserEmailService.deleteOneById({
120
+ id: data.methodId,
121
+ props: { isRoot: true },
122
+ });
123
+ },
124
+ create: async (data) => {
125
+ var _a;
126
+ const model = new UserEmail();
127
+ model.projectId = data.projectId;
128
+ model.userId = data.userId;
129
+ model.email = new Email(data.value);
130
+ const created = await UserEmailService.create({
131
+ data: model,
132
+ props: { isRoot: true },
133
+ });
134
+ return this.toView({
135
+ row: created,
136
+ methodType: ReadinessMethodType.Email,
137
+ isAdminAddable: true,
138
+ identifier: (_a = created.email) === null || _a === void 0 ? void 0 : _a.toString(),
139
+ kind: MaskedIdentifierKind.Email,
140
+ isVerified: created.isVerified,
141
+ });
142
+ },
143
+ resendVerificationCode: async (methodId) => {
144
+ await UserEmailService.resendVerificationCode(methodId);
145
+ },
146
+ },
147
+ {
148
+ methodType: ReadinessMethodType.SMS,
149
+ isAdminAddable: true,
150
+ modelType: UserSMS,
151
+ list: async (data) => {
152
+ const rows = await UserSmsService.findBy({
153
+ query: { projectId: data.projectId, userId: data.userId },
154
+ select: {
155
+ _id: true,
156
+ phone: true,
157
+ isVerified: true,
158
+ createdAt: true,
159
+ },
160
+ sort: { createdAt: SortOrder.Ascending },
161
+ skip: 0,
162
+ limit: LIMIT_PER_PROJECT,
163
+ props: { isRoot: true },
164
+ });
165
+ return rows.map((row) => {
166
+ var _a;
167
+ return this.toView({
168
+ row: row,
169
+ methodType: ReadinessMethodType.SMS,
170
+ isAdminAddable: true,
171
+ identifier: (_a = row.phone) === null || _a === void 0 ? void 0 : _a.toString(),
172
+ kind: MaskedIdentifierKind.Phone,
173
+ isVerified: row.isVerified,
174
+ });
175
+ });
176
+ },
177
+ findOwnedRow: async (data) => {
178
+ var _a;
179
+ const row = await UserSmsService.findOneBy({
180
+ query: {
181
+ _id: data.methodId,
182
+ projectId: data.projectId,
183
+ userId: data.userId,
184
+ },
185
+ select: { _id: true, phone: true, isVerified: true },
186
+ props: { isRoot: true },
187
+ });
188
+ return row
189
+ ? this.toView({
190
+ row: row,
191
+ methodType: ReadinessMethodType.SMS,
192
+ isAdminAddable: true,
193
+ identifier: (_a = row.phone) === null || _a === void 0 ? void 0 : _a.toString(),
194
+ kind: MaskedIdentifierKind.Phone,
195
+ isVerified: row.isVerified,
196
+ })
197
+ : null;
198
+ },
199
+ deleteRow: async (data) => {
200
+ await UserSmsService.deleteOneById({
201
+ id: data.methodId,
202
+ props: { isRoot: true },
203
+ });
204
+ },
205
+ create: async (data) => {
206
+ var _a;
207
+ const model = new UserSMS();
208
+ model.projectId = data.projectId;
209
+ model.userId = data.userId;
210
+ model.phone = new Phone(data.value);
211
+ const created = await UserSmsService.create({
212
+ data: model,
213
+ props: { isRoot: true },
214
+ });
215
+ return this.toView({
216
+ row: created,
217
+ methodType: ReadinessMethodType.SMS,
218
+ isAdminAddable: true,
219
+ identifier: (_a = created.phone) === null || _a === void 0 ? void 0 : _a.toString(),
220
+ kind: MaskedIdentifierKind.Phone,
221
+ isVerified: created.isVerified,
222
+ });
223
+ },
224
+ resendVerificationCode: async (methodId) => {
225
+ await UserSmsService.resendVerificationCode(methodId);
226
+ },
227
+ },
228
+ {
229
+ methodType: ReadinessMethodType.Call,
230
+ isAdminAddable: true,
231
+ modelType: UserCall,
232
+ list: async (data) => {
233
+ const rows = await UserCallService.findBy({
234
+ query: { projectId: data.projectId, userId: data.userId },
235
+ select: {
236
+ _id: true,
237
+ phone: true,
238
+ isVerified: true,
239
+ createdAt: true,
240
+ },
241
+ sort: { createdAt: SortOrder.Ascending },
242
+ skip: 0,
243
+ limit: LIMIT_PER_PROJECT,
244
+ props: { isRoot: true },
245
+ });
246
+ return rows.map((row) => {
247
+ var _a;
248
+ return this.toView({
249
+ row: row,
250
+ methodType: ReadinessMethodType.Call,
251
+ isAdminAddable: true,
252
+ identifier: (_a = row.phone) === null || _a === void 0 ? void 0 : _a.toString(),
253
+ kind: MaskedIdentifierKind.Phone,
254
+ isVerified: row.isVerified,
255
+ });
256
+ });
257
+ },
258
+ findOwnedRow: async (data) => {
259
+ var _a;
260
+ const row = await UserCallService.findOneBy({
261
+ query: {
262
+ _id: data.methodId,
263
+ projectId: data.projectId,
264
+ userId: data.userId,
265
+ },
266
+ select: { _id: true, phone: true, isVerified: true },
267
+ props: { isRoot: true },
268
+ });
269
+ return row
270
+ ? this.toView({
271
+ row: row,
272
+ methodType: ReadinessMethodType.Call,
273
+ isAdminAddable: true,
274
+ identifier: (_a = row.phone) === null || _a === void 0 ? void 0 : _a.toString(),
275
+ kind: MaskedIdentifierKind.Phone,
276
+ isVerified: row.isVerified,
277
+ })
278
+ : null;
279
+ },
280
+ deleteRow: async (data) => {
281
+ await UserCallService.deleteOneById({
282
+ id: data.methodId,
283
+ props: { isRoot: true },
284
+ });
285
+ },
286
+ create: async (data) => {
287
+ var _a;
288
+ const model = new UserCall();
289
+ model.projectId = data.projectId;
290
+ model.userId = data.userId;
291
+ model.phone = new Phone(data.value);
292
+ const created = await UserCallService.create({
293
+ data: model,
294
+ props: { isRoot: true },
295
+ });
296
+ return this.toView({
297
+ row: created,
298
+ methodType: ReadinessMethodType.Call,
299
+ isAdminAddable: true,
300
+ identifier: (_a = created.phone) === null || _a === void 0 ? void 0 : _a.toString(),
301
+ kind: MaskedIdentifierKind.Phone,
302
+ isVerified: created.isVerified,
303
+ });
304
+ },
305
+ resendVerificationCode: async (methodId) => {
306
+ await UserCallService.resendVerificationCode(methodId);
307
+ },
308
+ },
309
+ {
310
+ methodType: ReadinessMethodType.WhatsApp,
311
+ isAdminAddable: true,
312
+ modelType: UserWhatsApp,
313
+ list: async (data) => {
314
+ const rows = await UserWhatsAppService.findBy({
315
+ query: { projectId: data.projectId, userId: data.userId },
316
+ select: {
317
+ _id: true,
318
+ phone: true,
319
+ isVerified: true,
320
+ createdAt: true,
321
+ },
322
+ sort: { createdAt: SortOrder.Ascending },
323
+ skip: 0,
324
+ limit: LIMIT_PER_PROJECT,
325
+ props: { isRoot: true },
326
+ });
327
+ return rows.map((row) => {
328
+ var _a;
329
+ return this.toView({
330
+ row: row,
331
+ methodType: ReadinessMethodType.WhatsApp,
332
+ isAdminAddable: true,
333
+ identifier: (_a = row.phone) === null || _a === void 0 ? void 0 : _a.toString(),
334
+ kind: MaskedIdentifierKind.Phone,
335
+ isVerified: row.isVerified,
336
+ });
337
+ });
338
+ },
339
+ findOwnedRow: async (data) => {
340
+ var _a;
341
+ const row = await UserWhatsAppService.findOneBy({
342
+ query: {
343
+ _id: data.methodId,
344
+ projectId: data.projectId,
345
+ userId: data.userId,
346
+ },
347
+ select: { _id: true, phone: true, isVerified: true },
348
+ props: { isRoot: true },
349
+ });
350
+ return row
351
+ ? this.toView({
352
+ row: row,
353
+ methodType: ReadinessMethodType.WhatsApp,
354
+ isAdminAddable: true,
355
+ identifier: (_a = row.phone) === null || _a === void 0 ? void 0 : _a.toString(),
356
+ kind: MaskedIdentifierKind.Phone,
357
+ isVerified: row.isVerified,
358
+ })
359
+ : null;
360
+ },
361
+ deleteRow: async (data) => {
362
+ await UserWhatsAppService.deleteOneById({
363
+ id: data.methodId,
364
+ props: { isRoot: true },
365
+ });
366
+ },
367
+ create: async (data) => {
368
+ var _a;
369
+ const model = new UserWhatsApp();
370
+ model.projectId = data.projectId;
371
+ model.userId = data.userId;
372
+ model.phone = new Phone(data.value);
373
+ const created = await UserWhatsAppService.create({
374
+ data: model,
375
+ props: { isRoot: true },
376
+ });
377
+ return this.toView({
378
+ row: created,
379
+ methodType: ReadinessMethodType.WhatsApp,
380
+ isAdminAddable: true,
381
+ identifier: (_a = created.phone) === null || _a === void 0 ? void 0 : _a.toString(),
382
+ kind: MaskedIdentifierKind.Phone,
383
+ isVerified: created.isVerified,
384
+ });
385
+ },
386
+ resendVerificationCode: async (methodId) => {
387
+ await UserWhatsAppService.resendVerificationCode(methodId);
388
+ },
389
+ },
390
+ /*
391
+ * The three read-and-remove-only channels. They carry no `create`, which
392
+ * is what the API's "this channel cannot be added by an administrator"
393
+ * refusal is derived from — the refusal is a consequence of the table
394
+ * rather than a second list that could drift out of step with it.
395
+ */
396
+ {
397
+ methodType: ReadinessMethodType.Push,
398
+ isAdminAddable: false,
399
+ modelType: UserPush,
400
+ list: async (data) => {
401
+ const rows = await UserPushService.findBy({
402
+ query: { projectId: data.projectId, userId: data.userId },
403
+ select: {
404
+ _id: true,
405
+ deviceName: true,
406
+ isVerified: true,
407
+ createdAt: true,
408
+ },
409
+ sort: { createdAt: SortOrder.Ascending },
410
+ skip: 0,
411
+ limit: LIMIT_PER_PROJECT,
412
+ props: { isRoot: true },
413
+ });
414
+ /*
415
+ * `deviceName` and never `deviceToken`. The token is the addressable
416
+ * secret behind a push method — anyone holding it can push to that
417
+ * device — and this select is the only thing standing between it and
418
+ * a response body.
419
+ */
420
+ return rows.map((row) => {
421
+ return this.toView({
422
+ row: row,
423
+ methodType: ReadinessMethodType.Push,
424
+ isAdminAddable: false,
425
+ identifier: row.deviceName,
426
+ kind: MaskedIdentifierKind.Handle,
427
+ isVerified: row.isVerified,
428
+ });
429
+ });
430
+ },
431
+ findOwnedRow: async (data) => {
432
+ const row = await UserPushService.findOneBy({
433
+ query: {
434
+ _id: data.methodId,
435
+ projectId: data.projectId,
436
+ userId: data.userId,
437
+ },
438
+ select: { _id: true, deviceName: true, isVerified: true },
439
+ props: { isRoot: true },
440
+ });
441
+ return row
442
+ ? this.toView({
443
+ row: row,
444
+ methodType: ReadinessMethodType.Push,
445
+ isAdminAddable: false,
446
+ identifier: row.deviceName,
447
+ kind: MaskedIdentifierKind.Handle,
448
+ isVerified: row.isVerified,
449
+ })
450
+ : null;
451
+ },
452
+ deleteRow: async (data) => {
453
+ await UserPushService.deleteOneById({
454
+ id: data.methodId,
455
+ props: { isRoot: true },
456
+ });
457
+ },
458
+ },
459
+ {
460
+ methodType: ReadinessMethodType.Telegram,
461
+ isAdminAddable: false,
462
+ modelType: UserTelegram,
463
+ list: async (data) => {
464
+ const rows = await UserTelegramService.findBy({
465
+ query: { projectId: data.projectId, userId: data.userId },
466
+ /*
467
+ * The handle only — never telegramChatId, which is the addressable
468
+ * target a bot sends to. Same rule OnCallReadinessService follows.
469
+ */
470
+ select: {
471
+ _id: true,
472
+ telegramUserHandle: true,
473
+ isVerified: true,
474
+ createdAt: true,
475
+ },
476
+ sort: { createdAt: SortOrder.Ascending },
477
+ skip: 0,
478
+ limit: LIMIT_PER_PROJECT,
479
+ props: { isRoot: true },
480
+ });
481
+ return rows.map((row) => {
482
+ return this.toView({
483
+ row: row,
484
+ methodType: ReadinessMethodType.Telegram,
485
+ isAdminAddable: false,
486
+ identifier: row.telegramUserHandle,
487
+ kind: MaskedIdentifierKind.Handle,
488
+ isVerified: row.isVerified,
489
+ });
490
+ });
491
+ },
492
+ findOwnedRow: async (data) => {
493
+ const row = await UserTelegramService.findOneBy({
494
+ query: {
495
+ _id: data.methodId,
496
+ projectId: data.projectId,
497
+ userId: data.userId,
498
+ },
499
+ select: {
500
+ _id: true,
501
+ telegramUserHandle: true,
502
+ isVerified: true,
503
+ },
504
+ props: { isRoot: true },
505
+ });
506
+ return row
507
+ ? this.toView({
508
+ row: row,
509
+ methodType: ReadinessMethodType.Telegram,
510
+ isAdminAddable: false,
511
+ identifier: row.telegramUserHandle,
512
+ kind: MaskedIdentifierKind.Handle,
513
+ isVerified: row.isVerified,
514
+ })
515
+ : null;
516
+ },
517
+ deleteRow: async (data) => {
518
+ await UserTelegramService.deleteOneById({
519
+ id: data.methodId,
520
+ props: { isRoot: true },
521
+ });
522
+ },
523
+ },
524
+ {
525
+ methodType: ReadinessMethodType.Webhook,
526
+ isAdminAddable: false,
527
+ modelType: UserWebhook,
528
+ list: async (data) => {
529
+ const rows = await UserWebhookService.findBy({
530
+ query: { projectId: data.projectId, userId: data.userId },
531
+ /*
532
+ * `name` ONLY. UserWebhook.webhookUrl is a bearer credential —
533
+ * anyone holding a Slack/Discord/Teams hook url can post as the
534
+ * integration — so it never leaves the server on this path.
535
+ */
536
+ select: { _id: true, name: true, createdAt: true },
537
+ sort: { createdAt: SortOrder.Ascending },
538
+ skip: 0,
539
+ limit: LIMIT_PER_PROJECT,
540
+ props: { isRoot: true },
541
+ });
542
+ return rows.map((row) => {
543
+ return this.toView({
544
+ row: row,
545
+ methodType: ReadinessMethodType.Webhook,
546
+ isAdminAddable: false,
547
+ identifier: row.name,
548
+ kind: MaskedIdentifierKind.Handle,
549
+ /*
550
+ * UserWebhook has no isVerified column: a webhook is live from
551
+ * the moment it is written. Reported as verified because that is
552
+ * what it behaves like, and reporting it as unverified would put
553
+ * a "needs setup" badge on a channel that is already delivering.
554
+ */
555
+ isVerified: true,
556
+ });
557
+ });
558
+ },
559
+ findOwnedRow: async (data) => {
560
+ const row = await UserWebhookService.findOneBy({
561
+ query: {
562
+ _id: data.methodId,
563
+ projectId: data.projectId,
564
+ userId: data.userId,
565
+ },
566
+ select: { _id: true, name: true },
567
+ props: { isRoot: true },
568
+ });
569
+ return row
570
+ ? this.toView({
571
+ row: row,
572
+ methodType: ReadinessMethodType.Webhook,
573
+ isAdminAddable: false,
574
+ identifier: row.name,
575
+ kind: MaskedIdentifierKind.Handle,
576
+ isVerified: true,
577
+ })
578
+ : null;
579
+ },
580
+ deleteRow: async (data) => {
581
+ await UserWebhookService.deleteOneById({
582
+ id: data.methodId,
583
+ props: { isRoot: true },
584
+ });
585
+ },
586
+ },
587
+ ];
588
+ }
589
+ /*
590
+ * The ONLY place a raw identifier is turned into something this service will
591
+ * return. Every descriptor above routes through it, so there is no code path
592
+ * on which an unmasked value can reach a view object by accident — reaching
593
+ * one would mean deliberately constructing the interface by hand.
594
+ */
595
+ toView(data) {
596
+ return {
597
+ methodId: data.row.id,
598
+ methodType: data.methodType,
599
+ maskedIdentifier: maskIdentifier(data.identifier, data.kind),
600
+ isVerified: Boolean(data.isVerified),
601
+ isAdminAddable: data.isAdminAddable,
602
+ createdAt: data.row.createdAt,
603
+ };
604
+ }
605
+ getChannelDescriptor(methodType) {
606
+ return this.getChannelDescriptors().find((descriptor) => {
607
+ return descriptor.methodType === methodType;
608
+ });
609
+ }
610
+ /**
611
+ * Every notification method this user has in this project, masked.
612
+ *
613
+ * Ordered by channel and then by age, so the list an admin reads is stable
614
+ * between visits — a list that reshuffles is a list somebody misreads when
615
+ * they come back to remove the row they saw a moment ago.
616
+ */
617
+ async listMethodsForUser(data) {
618
+ const methods = [];
619
+ for (const descriptor of this.getChannelDescriptors()) {
620
+ methods.push(...(await descriptor.list({
621
+ projectId: data.projectId,
622
+ userId: data.userId,
623
+ })));
624
+ }
625
+ return methods;
626
+ }
627
+ /**
628
+ * Membership, read straight from TeamMember with root props and no cache.
629
+ *
630
+ * Holding an administrative permission is a claim about a PROJECT, so it can
631
+ * only ever authorise writing for users of that project. Without this, one
632
+ * throwaway project where the caller is an admin would license adding a phone
633
+ * number to any user id in the installation.
634
+ *
635
+ * TeamMemberService.getTeamIdsForUser would answer the same question with one
636
+ * fewer query, but it memoises for 60 seconds — and a security decision that
637
+ * keeps saying "yes" for a minute after somebody was removed from the project
638
+ * is not a security decision.
639
+ *
640
+ * `hasAcceptedInvitation` is part of the test for the same reason
641
+ * UserNotificationRuleAdminService includes it: a pending invitation is an
642
+ * admin-created row for an arbitrary email address, so treating one as
643
+ * membership would hand back most of what this guard takes away.
644
+ */
645
+ async assertTargetUserIsProjectMember(data) {
646
+ const membership = await TeamMemberService.findOneBy({
647
+ query: {
648
+ userId: data.targetUserId,
649
+ projectId: data.projectId,
650
+ hasAcceptedInvitation: true,
651
+ },
652
+ select: { _id: true },
653
+ props: { isRoot: true },
654
+ });
655
+ if (!membership) {
656
+ throw new BadDataException("This user is not a member of this project, so their notification methods cannot be managed here.");
657
+ }
658
+ }
659
+ /**
660
+ * Add a notification method to another member's account.
661
+ *
662
+ * The row is written with the TARGET's userId and with isVerified untouched,
663
+ * which is what makes the method service mail or message a verification code
664
+ * to the address itself. Nothing here can mark it verified, and neither can
665
+ * the administrator afterwards: the verify endpoints compare the row's owner
666
+ * against the signed-in caller.
667
+ *
668
+ * `value` is validated by the type it becomes — Email and Phone both throw
669
+ * BadDataException from their constructors — so a malformed number is
670
+ * refused before any row is written rather than stored and discovered later
671
+ * by a page that fails to deliver.
672
+ */
673
+ async addMethodForUser(data) {
674
+ await this.assertTargetUserIsProjectMember({
675
+ targetUserId: data.targetUserId,
676
+ projectId: data.projectId,
677
+ });
678
+ const descriptor = this.getChannelDescriptor(data.methodType);
679
+ if (!descriptor || !descriptor.create) {
680
+ /*
681
+ * One sentence for "no such channel" and for "that channel exists but an
682
+ * administrator may not create it", because the two are the same answer
683
+ * to the caller and naming which one it was would describe the shape of
684
+ * the feature to somebody probing it.
685
+ */
686
+ throw new BadDataException(`A project administrator can only add Email, SMS, Call and WhatsApp notification methods for another user. Push, Telegram and webhook methods have to be added by the person who owns the device.`);
687
+ }
688
+ const trimmed = (data.value || "").trim();
689
+ if (!trimmed) {
690
+ throw new BadDataException(`A ${descriptor.methodType} notification method needs a value.`);
691
+ }
692
+ /*
693
+ * Adding the same address twice is refused rather than deduplicated. Two
694
+ * identical rows are not merely untidy: they show up as two entries an
695
+ * admin has to tell apart on a masked list, each with its own verification
696
+ * state, and a rule pointed at the unverified one looks correct and pages
697
+ * nobody.
698
+ */
699
+ const existing = await descriptor.list({
700
+ projectId: data.projectId,
701
+ userId: data.targetUserId,
702
+ });
703
+ const candidateMask = maskIdentifier(trimmed, descriptor.methodType === ReadinessMethodType.Email
704
+ ? MaskedIdentifierKind.Email
705
+ : MaskedIdentifierKind.Phone);
706
+ /*
707
+ * Compared on the MASK, deliberately. The raw values are on the server and
708
+ * could be compared directly, but a duplicate check that reports "this
709
+ * already exists" for a value the admin cannot see is only useful if the
710
+ * thing it matched is the thing they are looking at on screen — and what
711
+ * they are looking at is the mask.
712
+ */
713
+ const isDuplicate = existing.some((method) => {
714
+ return method.maskedIdentifier === candidateMask;
715
+ });
716
+ if (isDuplicate) {
717
+ throw new BadDataException(`This user already has a ${descriptor.methodType} notification method that looks like ${candidateMask}.`);
718
+ }
719
+ const created = await descriptor.create({
720
+ projectId: data.projectId,
721
+ userId: data.targetUserId,
722
+ value: trimmed,
723
+ });
724
+ await this.recordAdminMethodChange({
725
+ action: AuditLogAction.Create,
726
+ actorUserId: data.actorUserId,
727
+ ownerUserId: data.targetUserId,
728
+ projectId: data.projectId,
729
+ methodType: descriptor.methodType,
730
+ modelType: descriptor.modelType,
731
+ methodId: created.methodId,
732
+ isVerified: created.isVerified,
733
+ maskedIdentifier: created.maskedIdentifier,
734
+ props: data.props,
735
+ });
736
+ return created;
737
+ }
738
+ /**
739
+ * What removing this method would cost the person who owns it.
740
+ *
741
+ * Every method foreign key on UserNotificationRule is onDelete: "CASCADE",
742
+ * and each method service deletes the same rows itself in onBeforeDelete, so
743
+ * removing one phone number takes every rule that pointed at it with it. An
744
+ * administrator has even less reason to expect that than the owner does —
745
+ * they are usually tidying up a number they know is dead — so the numbers are
746
+ * put in front of them before they confirm.
747
+ *
748
+ * The row is resolved through the ownership check first, so a caller cannot
749
+ * use this preview to learn anything about a method id belonging to another
750
+ * user or another project.
751
+ */
752
+ async getDeletionPreview(data) {
753
+ const descriptor = await this.resolveOwnedMethodDescriptor(data);
754
+ const impact = await UserNotificationRuleService.getNotificationMethodDeletionImpact({
755
+ projectId: data.projectId,
756
+ methodType: descriptor.methodType,
757
+ methodId: data.methodId,
758
+ });
759
+ return {
760
+ rulesDeletedCount: impact.rulesDeletedCount,
761
+ coverageLostCount: impact.coverageLost.length,
762
+ verifiedMethodCountAfterDeletion: impact.verifiedMethodCountAfterDeletion,
763
+ reachability: impact.reachability,
764
+ isFallbackEnabled: impact.isFallbackEnabled,
765
+ isTruncated: impact.isTruncated,
766
+ };
767
+ }
768
+ /**
769
+ * Remove one of another member's notification methods.
770
+ *
771
+ * Allowed on all seven channels: a device that has been handed back, a number
772
+ * that has been reassigned or a webhook pointing at a decommissioned endpoint
773
+ * are all things an administrator has to be able to clear up, frequently
774
+ * without the owner around to do it.
775
+ *
776
+ * It is also the one operation here that can make somebody LESS reachable, so
777
+ * it is the one that always mails them — including when it leaves them with
778
+ * nothing.
779
+ */
780
+ async deleteMethodForUser(data) {
781
+ await this.assertTargetUserIsProjectMember({
782
+ targetUserId: data.targetUserId,
783
+ projectId: data.projectId,
784
+ });
785
+ const descriptor = await this.resolveOwnedMethodDescriptor(data);
786
+ /*
787
+ * Read before the delete, because afterwards there is nothing to read. The
788
+ * mask is the whole description of what the project just lost, and it is
789
+ * the only thing the owner's mail can name.
790
+ */
791
+ const before = await descriptor.findOwnedRow({
792
+ methodId: data.methodId,
793
+ projectId: data.projectId,
794
+ userId: data.targetUserId,
795
+ });
796
+ await descriptor.deleteRow({ methodId: data.methodId });
797
+ await this.recordAdminMethodChange({
798
+ action: AuditLogAction.Delete,
799
+ actorUserId: data.actorUserId,
800
+ ownerUserId: data.targetUserId,
801
+ projectId: data.projectId,
802
+ methodType: descriptor.methodType,
803
+ modelType: descriptor.modelType,
804
+ methodId: data.methodId,
805
+ isVerified: Boolean(before === null || before === void 0 ? void 0 : before.isVerified),
806
+ maskedIdentifier: (before === null || before === void 0 ? void 0 : before.maskedIdentifier) || "",
807
+ props: data.props,
808
+ });
809
+ }
810
+ /**
811
+ * Send the verification code again, to the device.
812
+ *
813
+ * This is the administrator's most useful lever after adding a method, and it
814
+ * discloses nothing: the code goes to the address on the row, which is the
815
+ * one place the admin cannot read. It is how "I added your work mobile,
816
+ * please confirm it" becomes something they can nudge rather than re-type.
817
+ */
818
+ async resendVerificationCodeForUser(data) {
819
+ await this.assertTargetUserIsProjectMember({
820
+ targetUserId: data.targetUserId,
821
+ projectId: data.projectId,
822
+ });
823
+ const descriptor = await this.resolveOwnedMethodDescriptor(data);
824
+ if (!descriptor.resendVerificationCode) {
825
+ throw new BadDataException(`${descriptor.methodType} notification methods do not use a verification code.`);
826
+ }
827
+ await descriptor.resendVerificationCode(data.methodId);
828
+ }
829
+ /*
830
+ * "Does this method exist, is it on a channel we know, and does it belong to
831
+ * THIS user in THIS project?" — asked once, by every write path.
832
+ *
833
+ * A row that does not exist and a row belonging to somebody else get the same
834
+ * refusal. In both cases the caller named a row they have no business naming,
835
+ * and a distinguishable answer turns this into a probe for which method ids
836
+ * are real.
837
+ */
838
+ async resolveOwnedMethodDescriptor(data) {
839
+ const descriptor = this.getChannelDescriptor(data.methodType);
840
+ if (!descriptor) {
841
+ throw new BadDataException("This notification method could not be found for this user.");
842
+ }
843
+ const row = await descriptor.findOwnedRow({
844
+ methodId: data.methodId,
845
+ projectId: data.projectId,
846
+ userId: data.targetUserId,
847
+ });
848
+ if (!row) {
849
+ throw new BadDataException("This notification method could not be found for this user.");
850
+ }
851
+ return descriptor;
852
+ }
853
+ /*
854
+ * The trail, and the mail. Nothing in here may break the write: the row is
855
+ * already committed by the time this runs, so throwing now would report a
856
+ * failure for a change that happened and the caller would be entitled to
857
+ * believe it did not.
858
+ */
859
+ async recordAdminMethodChange(data) {
860
+ var _a;
861
+ try {
862
+ /*
863
+ * The dependable half. AuditLogService writes only on enterprise builds
864
+ * with audit logs switched on for the project, and "an administrator
865
+ * added a phone number to somebody's account" is exactly the event an
866
+ * operator needs to be able to find on the other installs too.
867
+ */
868
+ logger.info(`Notification method ${data.action.toLowerCase()}d for user ${data.ownerUserId.toString()} by ${data.actorUserId ? data.actorUserId.toString() : "an unknown actor"}.`, {
869
+ projectId: data.projectId.toString(),
870
+ userId: data.ownerUserId.toString(),
871
+ actorUserId: (_a = data.actorUserId) === null || _a === void 0 ? void 0 : _a.toString(),
872
+ methodType: data.methodType,
873
+ action: data.action,
874
+ });
875
+ await this.writeAuditLog({
876
+ action: data.action,
877
+ modelType: data.modelType,
878
+ methodId: data.methodId,
879
+ ownerUserId: data.ownerUserId,
880
+ projectId: data.projectId,
881
+ isVerified: data.isVerified,
882
+ props: data.props,
883
+ });
884
+ /*
885
+ * The owner is not mailed about their own action. A person managing their
886
+ * own methods from this page — an owner looking at their own row — has
887
+ * just done the thing the mail would be warning them about.
888
+ */
889
+ if (data.actorUserId &&
890
+ data.actorUserId.toString() === data.ownerUserId.toString()) {
891
+ return;
892
+ }
893
+ /*
894
+ * The readiness cache holds this user's method list for 60 seconds, and
895
+ * the page the admin is looking at re-reads readiness the moment this
896
+ * returns. Left alone it would redraw the responder exactly as they were
897
+ * before the change, which reads as "the add did not work".
898
+ */
899
+ OnCallReadinessService.clearCache();
900
+ /*
901
+ * Fire-and-forget by construction: the owner's mail involves three more
902
+ * reads and an SMTP round trip, none of which the writing request should
903
+ * wait on and none of which may surface as a failed write.
904
+ */
905
+ this.notifyOwnerOfAdminChange(data).catch((error) => {
906
+ logger.error(`UserNotificationMethodAdminService: failed to tell user ${data.ownerUserId.toString()} that an administrator changed their notification methods.`);
907
+ logger.error(error);
908
+ });
909
+ }
910
+ catch (error) {
911
+ logger.error("UserNotificationMethodAdminService: failed to record an administrative notification method change.");
912
+ logger.error(error);
913
+ }
914
+ }
915
+ /**
916
+ * The AuditLog row, written from a BLANK model rather than from the row that
917
+ * changed. That is the whole subtlety of this method.
918
+ *
919
+ * AuditLogService snapshots every column it is handed and redacts only the
920
+ * ones whose column-level `read` list is EMPTY. On UserEmail the address is
921
+ * `read: [Permission.CurrentUser]`, not empty — so handing it the real row
922
+ * would write the raw email address, phone number or webhook name into an
923
+ * analytics table that every project administrator can query. For a removal
924
+ * that address is one the acting admin never saw, which would make the audit
925
+ * trail leak precisely what the page it records was built to hide.
926
+ *
927
+ * So the snapshot carries the three facts that make the entry useful and
928
+ * nothing else: which project, whose account, and whether the method was
929
+ * live. `buildSnapshotChanges` skips undefined columns, so everything else
930
+ * simply is not in the row. WHAT was added or removed is in the message
931
+ * logged above it, masked; WHO did it comes from `props` by way of
932
+ * resolveActor.
933
+ *
934
+ * AuditLogService is required lazily for the same reason DatabaseService
935
+ * requires it lazily: it depends on ProjectService and UserService, both of
936
+ * which extend DatabaseService, so a top-level import can leave the base
937
+ * class undefined at class-extension time.
938
+ */
939
+ async writeAuditLog(data) {
940
+ const auditLogService =
941
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
942
+ require("./AuditLogService").default;
943
+ if (!auditLogService) {
944
+ return;
945
+ }
946
+ const snapshot = new data.modelType();
947
+ /*
948
+ * Assigned through an index rather than through typed properties because
949
+ * `snapshot` is a BaseModel here — the three columns are declared on each
950
+ * of the seven concrete classes, not on the base, and casting to one of
951
+ * them would be claiming a type this method deliberately does not know.
952
+ */
953
+ const columns = snapshot;
954
+ if (data.methodId) {
955
+ columns["_id"] = data.methodId.toString();
956
+ }
957
+ columns["projectId"] = data.projectId;
958
+ columns["userId"] = data.ownerUserId;
959
+ columns["isVerified"] = data.isVerified;
960
+ if (data.action === AuditLogAction.Create) {
961
+ await auditLogService.recordCreate({
962
+ model: new data.modelType(),
963
+ createdItem: snapshot,
964
+ props: data.props,
965
+ });
966
+ return;
967
+ }
968
+ /*
969
+ * A deleted method cannot be re-read, so this snapshot is the whole of what
970
+ * the project has left of it. It is also the reason the id is set above: an
971
+ * entry with no resourceId cannot be tied back to the rules that went with
972
+ * it.
973
+ */
974
+ if (data.methodId) {
975
+ await auditLogService.recordDelete({
976
+ model: new data.modelType(),
977
+ deletedItem: snapshot,
978
+ itemId: data.methodId,
979
+ props: data.props,
980
+ });
981
+ }
982
+ }
983
+ /*
984
+ * Tell the person whose account just changed.
985
+ *
986
+ * This is what makes the capability safe to hand out rather than merely
987
+ * possible. An admin adding a colleague's work mobile and an attacker adding
988
+ * a number of their own are the same request from the server's side; the only
989
+ * party who always knows which it was is the account holder.
990
+ */
991
+ async notifyOwnerOfAdminChange(data) {
992
+ var _a, _b;
993
+ const owner = await UserService.findOneById({
994
+ id: data.ownerUserId,
995
+ select: { _id: true, name: true, email: true },
996
+ props: { isRoot: true },
997
+ });
998
+ if (!owner || !owner.email) {
999
+ return;
1000
+ }
1001
+ let actorDescription = "A project administrator";
1002
+ if (data.actorUserId) {
1003
+ const actor = await UserService.findOneById({
1004
+ id: data.actorUserId,
1005
+ select: { _id: true, name: true, email: true },
1006
+ props: { isRoot: true },
1007
+ });
1008
+ /*
1009
+ * Named by email as well as by name, deliberately. Display names are not
1010
+ * unique and are user-editable, so "Alex added a phone number" is not
1011
+ * something the reader can act on; an address is.
1012
+ */
1013
+ actorDescription = actor
1014
+ ? `${((_a = actor.name) === null || _a === void 0 ? void 0 : _a.toString()) || "A project administrator"} (${((_b = actor.email) === null || _b === void 0 ? void 0 : _b.toString()) || data.actorUserId.toString()})`
1015
+ : `A project administrator (${data.actorUserId.toString()})`;
1016
+ }
1017
+ let projectName = "your project";
1018
+ const project = await ProjectService.findOneById({
1019
+ id: data.projectId,
1020
+ select: { _id: true, name: true },
1021
+ props: { isRoot: true },
1022
+ });
1023
+ if (project === null || project === void 0 ? void 0 : project.name) {
1024
+ projectName = project.name;
1025
+ }
1026
+ const isAdd = data.action === AuditLogAction.Create;
1027
+ const subject = isAdd
1028
+ ? "A notification method was added to your account"
1029
+ : "A notification method was removed from your account";
1030
+ /*
1031
+ * The two consequences are genuinely different and are worth separate
1032
+ * sentences. An addition is inert until the owner verifies it, and saying
1033
+ * so is what turns "somebody touched my account" into an instruction. A
1034
+ * removal has already happened and may have taken rules with it, which is
1035
+ * the only one of the two that can leave a responder silently unreachable.
1036
+ */
1037
+ const consequence = isAdd
1038
+ ? "It cannot be used to notify you until you verify it, and only you can do that — a verification code has been sent to the address or device itself. If you were not expecting this, do not verify it, remove it from your notification methods, and tell your project owners."
1039
+ : "Any notification rules that used it were removed with it, so you may no longer be paged the way you were. Check your notification methods and rules now, and tell your project owners if you were not expecting this.";
1040
+ const message = `${this.escapeHtml(actorDescription)} ${isAdd ? "added" : "removed"} a ${this.escapeHtml(data.methodType)} notification method (${this.escapeHtml(data.maskedIdentifier)}) ${isAdd ? "to" : "from"} your account in ${this.escapeHtml(projectName)}.<br/><br/>${consequence}`;
1041
+ const settingsLink = (await this.getNotificationMethodsLinkInDashboard(data.projectId)).toString();
1042
+ const vars = {
1043
+ subject: subject,
1044
+ message: `${message}<br/><br/><a href="${this.escapeHtml(settingsLink)}">Review your notification methods</a>`,
1045
+ };
1046
+ await MailService.sendMail({
1047
+ toEmail: owner.email,
1048
+ templateType: EmailTemplateType.SimpleMessage,
1049
+ vars: vars,
1050
+ subject: subject,
1051
+ }, {
1052
+ projectId: data.projectId,
1053
+ userId: owner.id,
1054
+ });
1055
+ }
1056
+ async getNotificationMethodsLinkInDashboard(projectId) {
1057
+ const dashboardUrl = await DatabaseConfig.getDashboardUrl();
1058
+ /*
1059
+ * Spelled out rather than imported: Common/Server cannot reach the
1060
+ * Dashboard's RouteMap, the same reason
1061
+ * UserNotificationRuleAdminService.getNotificationRulesLinkInDashboard
1062
+ * duplicates its path segments.
1063
+ */
1064
+ return URL.fromString(dashboardUrl.toString()).addRoute(`/${projectId.toString()}/user-settings/notification-methods`);
1065
+ }
1066
+ /*
1067
+ * SimpleMessage.hbs renders `message` through a triple-stache InfoBlock
1068
+ * partial, so nothing between here and the recipient's inbox escapes
1069
+ * anything. Project names and user names are attacker-influenced free text.
1070
+ */
1071
+ escapeHtml(value) {
1072
+ return value
1073
+ .replace(/&/g, "&amp;")
1074
+ .replace(/</g, "&lt;")
1075
+ .replace(/>/g, "&gt;")
1076
+ .replace(/"/g, "&quot;")
1077
+ .replace(/'/g, "&#39;");
1078
+ }
1079
+ }
1080
+ __decorate([
1081
+ CaptureSpan(),
1082
+ __metadata("design:type", Function),
1083
+ __metadata("design:paramtypes", [Object]),
1084
+ __metadata("design:returntype", Promise)
1085
+ ], UserNotificationMethodAdminService.prototype, "listMethodsForUser", null);
1086
+ __decorate([
1087
+ CaptureSpan(),
1088
+ __metadata("design:type", Function),
1089
+ __metadata("design:paramtypes", [Object]),
1090
+ __metadata("design:returntype", Promise)
1091
+ ], UserNotificationMethodAdminService.prototype, "assertTargetUserIsProjectMember", null);
1092
+ __decorate([
1093
+ CaptureSpan(),
1094
+ __metadata("design:type", Function),
1095
+ __metadata("design:paramtypes", [Object]),
1096
+ __metadata("design:returntype", Promise)
1097
+ ], UserNotificationMethodAdminService.prototype, "addMethodForUser", null);
1098
+ __decorate([
1099
+ CaptureSpan(),
1100
+ __metadata("design:type", Function),
1101
+ __metadata("design:paramtypes", [Object]),
1102
+ __metadata("design:returntype", Promise)
1103
+ ], UserNotificationMethodAdminService.prototype, "getDeletionPreview", null);
1104
+ __decorate([
1105
+ CaptureSpan(),
1106
+ __metadata("design:type", Function),
1107
+ __metadata("design:paramtypes", [Object]),
1108
+ __metadata("design:returntype", Promise)
1109
+ ], UserNotificationMethodAdminService.prototype, "deleteMethodForUser", null);
1110
+ __decorate([
1111
+ CaptureSpan(),
1112
+ __metadata("design:type", Function),
1113
+ __metadata("design:paramtypes", [Object]),
1114
+ __metadata("design:returntype", Promise)
1115
+ ], UserNotificationMethodAdminService.prototype, "resendVerificationCodeForUser", null);
1116
+ export default new UserNotificationMethodAdminService();
1117
+ //# sourceMappingURL=UserNotificationMethodAdminService.js.map