@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
@@ -298,20 +298,85 @@ export default class BaseAPI<
298
298
  const databaseProps: DatabaseCommonInteractionProps =
299
299
  await CommonAPI.getDatabaseCommonInteractionProps(req);
300
300
 
301
- const list: Array<BaseModel> = await this.service.findBy({
302
- query,
303
- select,
304
- skip: skip,
305
- limit: limit,
306
- groupBy: groupBy,
307
- sort: sort,
308
- props: databaseProps,
309
- });
310
-
311
- const count: PositiveNumber = await this.service.countBy({
312
- query,
313
- props: databaseProps,
314
- });
301
+ /*
302
+ * findBy and countBy are independent reads, so run them in parallel to
303
+ * cut list latency where the count is costly. The one thing that makes
304
+ * the sequential order load-bearing is a service-level onBeforeFind
305
+ * override that mutates the shared query/props objects in place (e.g.
306
+ * ProjectService elevates props.isRoot and pins query._id for users with
307
+ * no projects; the incident/alert privacy filters rewrite query keys):
308
+ * countBy historically ran after findBy and inherited those mutations.
309
+ * Services on the stock hook cannot mutate anything (and the permission
310
+ * pipeline copies the query before touching it, ReadPermission.ts:33),
311
+ * so only they run in parallel; hook-overriding services keep the
312
+ * sequential order and its exact semantics.
313
+ */
314
+ const usesStockOnBeforeFindHook: boolean =
315
+ (this.service as unknown as { onBeforeFind: unknown }).onBeforeFind ===
316
+ (DatabaseService.prototype as unknown as { onBeforeFind: unknown })
317
+ .onBeforeFind;
318
+
319
+ let list: Array<BaseModel>;
320
+ let count: PositiveNumber;
321
+
322
+ if (usesStockOnBeforeFindHook) {
323
+ const listPromise: Promise<Array<BaseModel>> = this.service.findBy({
324
+ query,
325
+ select,
326
+ skip: skip,
327
+ limit: limit,
328
+ groupBy: groupBy,
329
+ sort: sort,
330
+ props: databaseProps,
331
+ });
332
+
333
+ /*
334
+ * Shallow copies so a countBy override that rewrites top-level query
335
+ * keys (the established privacy-filter pattern) can never race the
336
+ * concurrent findBy's reads of the shared objects.
337
+ */
338
+ const countPromise: Promise<PositiveNumber> = this.service.countBy({
339
+ query: { ...query },
340
+ props: { ...databaseProps },
341
+ });
342
+
343
+ /*
344
+ * allSettled instead of Promise.all: a findBy rejection must not leave
345
+ * countPromise as an unhandled rejection, and findBy's error keeps
346
+ * priority when both fail - the same failure the sequential code
347
+ * surfaced.
348
+ */
349
+ const [listResult, countResult]: [
350
+ PromiseSettledResult<Array<BaseModel>>,
351
+ PromiseSettledResult<PositiveNumber>,
352
+ ] = await Promise.allSettled([listPromise, countPromise]);
353
+
354
+ if (listResult.status === "rejected") {
355
+ throw listResult.reason;
356
+ }
357
+
358
+ if (countResult.status === "rejected") {
359
+ throw countResult.reason;
360
+ }
361
+
362
+ list = listResult.value;
363
+ count = countResult.value;
364
+ } else {
365
+ list = await this.service.findBy({
366
+ query,
367
+ select,
368
+ skip: skip,
369
+ limit: limit,
370
+ groupBy: groupBy,
371
+ sort: sort,
372
+ props: databaseProps,
373
+ });
374
+
375
+ count = await this.service.countBy({
376
+ query,
377
+ props: databaseProps,
378
+ });
379
+ }
315
380
 
316
381
  return Response.sendEntityArrayResponse(
317
382
  req,
@@ -1,22 +1,44 @@
1
1
  import UserMiddleware from "../Middleware/UserAuthorization";
2
+ import VerificationCodeRateLimit, {
3
+ VerificationCodeRateLimitBucket,
4
+ } from "../Middleware/VerificationCodeRateLimit";
2
5
  import UserCallService, {
3
6
  Service as UserCallServiceType,
4
7
  } from "../Services/UserCallService";
5
- import UserNotificationRuleService from "../Services/UserNotificationRuleService";
6
8
  import {
7
9
  ExpressRequest,
8
10
  ExpressResponse,
9
11
  NextFunction,
10
12
  OneUptimeRequest,
11
13
  } from "../Utils/Express";
14
+ import ChannelVerification, {
15
+ ChannelVerificationOutcome,
16
+ ChannelVerificationResult,
17
+ } from "../Utils/ChannelVerification";
12
18
  import Response from "../Utils/Response";
13
- import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
14
19
  import BaseAPI from "./BaseAPI";
15
20
  import BadDataException from "../../Types/Exception/BadDataException";
16
21
  import ObjectID from "../../Types/ObjectID";
17
22
  import UserCall from "../../Models/DatabaseModels/UserCall";
18
- import UserSMS from "../../Models/DatabaseModels/UserSMS";
23
+ import UserNotificationRuleService from "../Services/UserNotificationRuleService";
24
+ import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
19
25
 
26
+ /*
27
+ * The verify and resend routes for this notification channel.
28
+ *
29
+ * Neither the checking of a submitted code nor the issuing of a new one lives
30
+ * here, on purpose. The state machine is shared by all five channels
31
+ * (Common/Server/Utils/ChannelVerification.ts) so that a control added for one
32
+ * of them cannot quietly go missing on another — which is exactly what went
33
+ * wrong before: five hand-written copies of "compare the column, set
34
+ * isVerified", none of which expired the code, counted attempts, or refused a
35
+ * caller running the comparison a million times against somebody else's phone
36
+ * number.
37
+ *
38
+ * What is left here is the HTTP shape: reject malformed bodies, run the rate
39
+ * limiter before anything costs a database read, and turn the outcome into a
40
+ * response.
41
+ */
20
42
  export default class UserCallAPI extends BaseAPI<
21
43
  UserCall,
22
44
  UserCallServiceType
@@ -27,6 +49,9 @@ export default class UserCallAPI extends BaseAPI<
27
49
  this.router.post(
28
50
  `/user-call/verify`,
29
51
  UserMiddleware.getUserMiddleware,
52
+ VerificationCodeRateLimit.getMiddleware(
53
+ VerificationCodeRateLimitBucket.Verify,
54
+ ),
30
55
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
31
56
  try {
32
57
  req = req as OneUptimeRequest;
@@ -47,33 +72,10 @@ export default class UserCallAPI extends BaseAPI<
47
72
  );
48
73
  }
49
74
 
50
- // Check if the code matches and verify the phone number.
51
- const item: UserSMS | null = await this.service.findOneById({
52
- id: req.body["itemId"],
53
- props: {
54
- isRoot: true,
55
- },
56
- select: {
57
- userId: true,
58
- projectId: true,
59
- verificationCode: true,
60
- },
61
- });
75
+ const userId: ObjectID | undefined = (req as OneUptimeRequest)
76
+ ?.userAuthorization?.userId;
62
77
 
63
- if (!item) {
64
- return Response.sendErrorResponse(
65
- req,
66
- res,
67
- new BadDataException("Item not found"),
68
- );
69
- }
70
-
71
- //check user id
72
-
73
- if (
74
- item.userId?.toString() !==
75
- (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
76
- ) {
78
+ if (!userId) {
77
79
  return Response.sendErrorResponse(
78
80
  req,
79
81
  res,
@@ -81,32 +83,30 @@ export default class UserCallAPI extends BaseAPI<
81
83
  );
82
84
  }
83
85
 
84
- if (item.verificationCode !== req.body["code"]) {
86
+ const result: ChannelVerificationResult =
87
+ await ChannelVerification.verifyCode({
88
+ service: this.service,
89
+ itemId: new ObjectID(req.body["itemId"].toString()),
90
+ userId: userId,
91
+ code: req.body["code"].toString(),
92
+ });
93
+
94
+ if (result.outcome !== ChannelVerificationOutcome.Verified) {
85
95
  return Response.sendErrorResponse(
86
96
  req,
87
97
  res,
88
- new BadDataException("Invalid code"),
98
+ ChannelVerification.getFailureException(result.outcome),
89
99
  );
90
100
  }
91
101
 
92
- await this.service.updateOneById({
93
- id: item.id!,
94
- props: {
95
- isRoot: true,
96
- },
97
- data: {
98
- isVerified: true,
99
- },
100
- });
101
-
102
- // Create default notification rules for this verified call number
102
+ /* Create default notification rules for this verified call number */
103
103
  try {
104
104
  await UserNotificationRuleService.addDefaultNotificationRulesForVerifiedMethod(
105
105
  {
106
- projectId: new ObjectID(item.projectId!.toString()),
107
- userId: new ObjectID(item.userId!.toString()),
106
+ projectId: new ObjectID(result.projectId!.toString()),
107
+ userId: new ObjectID(result.userId!.toString()),
108
108
  notificationMethod: {
109
- userCallId: item.id!,
109
+ userCallId: result.itemId!,
110
110
  },
111
111
  },
112
112
  );
@@ -127,6 +127,9 @@ export default class UserCallAPI extends BaseAPI<
127
127
  this.router.post(
128
128
  `/user-call/resend-verification-code`,
129
129
  UserMiddleware.getUserMiddleware,
130
+ VerificationCodeRateLimit.getMiddleware(
131
+ VerificationCodeRateLimitBucket.Resend,
132
+ ),
130
133
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
131
134
  try {
132
135
  req = req as OneUptimeRequest;
@@ -157,6 +160,11 @@ export default class UserCallAPI extends BaseAPI<
157
160
  );
158
161
  }
159
162
 
163
+ /*
164
+ * A caller may only ask for a code to be sent to a row they own.
165
+ * Without this the resend route is a way to make somebody else's
166
+ * device ring on demand.
167
+ */
160
168
  if (
161
169
  item.userId?.toString() !==
162
170
  (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
@@ -1,21 +1,44 @@
1
1
  import UserMiddleware from "../Middleware/UserAuthorization";
2
+ import VerificationCodeRateLimit, {
3
+ VerificationCodeRateLimitBucket,
4
+ } from "../Middleware/VerificationCodeRateLimit";
2
5
  import UserEmailService, {
3
6
  Service as UserEmailServiceType,
4
7
  } from "../Services/UserEmailService";
5
- import UserNotificationRuleService from "../Services/UserNotificationRuleService";
6
8
  import {
7
9
  ExpressRequest,
8
10
  ExpressResponse,
9
11
  NextFunction,
10
12
  OneUptimeRequest,
11
13
  } from "../Utils/Express";
14
+ import ChannelVerification, {
15
+ ChannelVerificationOutcome,
16
+ ChannelVerificationResult,
17
+ } from "../Utils/ChannelVerification";
12
18
  import Response from "../Utils/Response";
13
- import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
14
19
  import BaseAPI from "./BaseAPI";
15
20
  import BadDataException from "../../Types/Exception/BadDataException";
16
21
  import ObjectID from "../../Types/ObjectID";
17
22
  import UserEmail from "../../Models/DatabaseModels/UserEmail";
23
+ import UserNotificationRuleService from "../Services/UserNotificationRuleService";
24
+ import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
18
25
 
26
+ /*
27
+ * The verify and resend routes for this notification channel.
28
+ *
29
+ * Neither the checking of a submitted code nor the issuing of a new one lives
30
+ * here, on purpose. The state machine is shared by all five channels
31
+ * (Common/Server/Utils/ChannelVerification.ts) so that a control added for one
32
+ * of them cannot quietly go missing on another — which is exactly what went
33
+ * wrong before: five hand-written copies of "compare the column, set
34
+ * isVerified", none of which expired the code, counted attempts, or refused a
35
+ * caller running the comparison a million times against somebody else's phone
36
+ * number.
37
+ *
38
+ * What is left here is the HTTP shape: reject malformed bodies, run the rate
39
+ * limiter before anything costs a database read, and turn the outcome into a
40
+ * response.
41
+ */
19
42
  export default class UserEmailAPI extends BaseAPI<
20
43
  UserEmail,
21
44
  UserEmailServiceType
@@ -26,6 +49,9 @@ export default class UserEmailAPI extends BaseAPI<
26
49
  this.router.post(
27
50
  `${new this.entityType().getCrudApiPath()?.toString()}/verify`,
28
51
  UserMiddleware.getUserMiddleware,
52
+ VerificationCodeRateLimit.getMiddleware(
53
+ VerificationCodeRateLimitBucket.Verify,
54
+ ),
29
55
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
30
56
  try {
31
57
  req = req as OneUptimeRequest;
@@ -46,33 +72,10 @@ export default class UserEmailAPI extends BaseAPI<
46
72
  );
47
73
  }
48
74
 
49
- // Check if the code matches and verify the email.
50
- const item: UserEmail | null = await this.service.findOneById({
51
- id: req.body["itemId"],
52
- props: {
53
- isRoot: true,
54
- },
55
- select: {
56
- userId: true,
57
- projectId: true,
58
- verificationCode: true,
59
- },
60
- });
61
-
62
- if (!item) {
63
- return Response.sendErrorResponse(
64
- req,
65
- res,
66
- new BadDataException("Item not found"),
67
- );
68
- }
69
-
70
- //check user id
75
+ const userId: ObjectID | undefined = (req as OneUptimeRequest)
76
+ ?.userAuthorization?.userId;
71
77
 
72
- if (
73
- item.userId?.toString() !==
74
- (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
75
- ) {
78
+ if (!userId) {
76
79
  return Response.sendErrorResponse(
77
80
  req,
78
81
  res,
@@ -80,32 +83,30 @@ export default class UserEmailAPI extends BaseAPI<
80
83
  );
81
84
  }
82
85
 
83
- if (item.verificationCode !== req.body["code"]) {
86
+ const result: ChannelVerificationResult =
87
+ await ChannelVerification.verifyCode({
88
+ service: this.service,
89
+ itemId: new ObjectID(req.body["itemId"].toString()),
90
+ userId: userId,
91
+ code: req.body["code"].toString(),
92
+ });
93
+
94
+ if (result.outcome !== ChannelVerificationOutcome.Verified) {
84
95
  return Response.sendErrorResponse(
85
96
  req,
86
97
  res,
87
- new BadDataException("Invalid code"),
98
+ ChannelVerification.getFailureException(result.outcome),
88
99
  );
89
100
  }
90
101
 
91
- await this.service.updateOneById({
92
- id: item.id!,
93
- props: {
94
- isRoot: true,
95
- },
96
- data: {
97
- isVerified: true,
98
- },
99
- });
100
-
101
- // Create default notification rules for this verified email
102
+ /* Create default notification rules for this verified email */
102
103
  try {
103
104
  await UserNotificationRuleService.addDefaultNotificationRulesForVerifiedMethod(
104
105
  {
105
- projectId: new ObjectID(item.projectId!.toString()),
106
- userId: new ObjectID(item.userId!.toString()),
106
+ projectId: new ObjectID(result.projectId!.toString()),
107
+ userId: new ObjectID(result.userId!.toString()),
107
108
  notificationMethod: {
108
- userEmailId: item.id!,
109
+ userEmailId: result.itemId!,
109
110
  },
110
111
  },
111
112
  );
@@ -128,6 +129,9 @@ export default class UserEmailAPI extends BaseAPI<
128
129
  .getCrudApiPath()
129
130
  ?.toString()}/resend-verification-code`,
130
131
  UserMiddleware.getUserMiddleware,
132
+ VerificationCodeRateLimit.getMiddleware(
133
+ VerificationCodeRateLimitBucket.Resend,
134
+ ),
131
135
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
132
136
  try {
133
137
  req = req as OneUptimeRequest;
@@ -158,6 +162,11 @@ export default class UserEmailAPI extends BaseAPI<
158
162
  );
159
163
  }
160
164
 
165
+ /*
166
+ * A caller may only ask for a code to be sent to a row they own.
167
+ * Without this the resend route is a way to make somebody else's
168
+ * device ring on demand.
169
+ */
161
170
  if (
162
171
  item.userId?.toString() !==
163
172
  (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
@@ -1,4 +1,7 @@
1
1
  import UserMiddleware from "../Middleware/UserAuthorization";
2
+ import VerificationCodeRateLimit, {
3
+ VerificationCodeRateLimitBucket,
4
+ } from "../Middleware/VerificationCodeRateLimit";
2
5
  import UserIncomingCallNumberService, {
3
6
  Service as UserIncomingCallNumberServiceType,
4
7
  } from "../Services/UserIncomingCallNumberService";
@@ -8,11 +11,32 @@ import {
8
11
  NextFunction,
9
12
  OneUptimeRequest,
10
13
  } from "../Utils/Express";
14
+ import ChannelVerification, {
15
+ ChannelVerificationOutcome,
16
+ ChannelVerificationResult,
17
+ } from "../Utils/ChannelVerification";
11
18
  import Response from "../Utils/Response";
12
19
  import BaseAPI from "./BaseAPI";
13
20
  import BadDataException from "../../Types/Exception/BadDataException";
21
+ import ObjectID from "../../Types/ObjectID";
14
22
  import UserIncomingCallNumber from "../../Models/DatabaseModels/UserIncomingCallNumber";
15
23
 
24
+ /*
25
+ * The verify and resend routes for this notification channel.
26
+ *
27
+ * Neither the checking of a submitted code nor the issuing of a new one lives
28
+ * here, on purpose. The state machine is shared by all five channels
29
+ * (Common/Server/Utils/ChannelVerification.ts) so that a control added for one
30
+ * of them cannot quietly go missing on another — which is exactly what went
31
+ * wrong before: five hand-written copies of "compare the column, set
32
+ * isVerified", none of which expired the code, counted attempts, or refused a
33
+ * caller running the comparison a million times against somebody else's phone
34
+ * number.
35
+ *
36
+ * What is left here is the HTTP shape: reject malformed bodies, run the rate
37
+ * limiter before anything costs a database read, and turn the outcome into a
38
+ * response.
39
+ */
16
40
  export default class UserIncomingCallNumberAPI extends BaseAPI<
17
41
  UserIncomingCallNumber,
18
42
  UserIncomingCallNumberServiceType
@@ -23,6 +47,9 @@ export default class UserIncomingCallNumberAPI extends BaseAPI<
23
47
  this.router.post(
24
48
  `/user-incoming-call-number/verify`,
25
49
  UserMiddleware.getUserMiddleware,
50
+ VerificationCodeRateLimit.getMiddleware(
51
+ VerificationCodeRateLimitBucket.Verify,
52
+ ),
26
53
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
27
54
  try {
28
55
  req = req as OneUptimeRequest;
@@ -43,32 +70,10 @@ export default class UserIncomingCallNumberAPI extends BaseAPI<
43
70
  );
44
71
  }
45
72
 
46
- // Check if the code matches and verify the phone number
47
- const item: UserIncomingCallNumber | null =
48
- await this.service.findOneById({
49
- id: req.body["itemId"],
50
- props: {
51
- isRoot: true,
52
- },
53
- select: {
54
- userId: true,
55
- verificationCode: true,
56
- },
57
- });
58
-
59
- if (!item) {
60
- return Response.sendErrorResponse(
61
- req,
62
- res,
63
- new BadDataException("Item not found"),
64
- );
65
- }
73
+ const userId: ObjectID | undefined = (req as OneUptimeRequest)
74
+ ?.userAuthorization?.userId;
66
75
 
67
- // Check user ID
68
- if (
69
- item.userId?.toString() !==
70
- (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()
71
- ) {
76
+ if (!userId) {
72
77
  return Response.sendErrorResponse(
73
78
  req,
74
79
  res,
@@ -76,24 +81,22 @@ export default class UserIncomingCallNumberAPI extends BaseAPI<
76
81
  );
77
82
  }
78
83
 
79
- if (item.verificationCode !== req.body["code"]) {
84
+ const result: ChannelVerificationResult =
85
+ await ChannelVerification.verifyCode({
86
+ service: this.service,
87
+ itemId: new ObjectID(req.body["itemId"].toString()),
88
+ userId: userId,
89
+ code: req.body["code"].toString(),
90
+ });
91
+
92
+ if (result.outcome !== ChannelVerificationOutcome.Verified) {
80
93
  return Response.sendErrorResponse(
81
94
  req,
82
95
  res,
83
- new BadDataException("Invalid code"),
96
+ ChannelVerification.getFailureException(result.outcome),
84
97
  );
85
98
  }
86
99
 
87
- await this.service.updateOneById({
88
- id: item.id!,
89
- props: {
90
- isRoot: true,
91
- },
92
- data: {
93
- isVerified: true,
94
- },
95
- });
96
-
97
100
  return Response.sendEmptySuccessResponse(req, res);
98
101
  } catch (err) {
99
102
  return next(err);
@@ -104,6 +107,9 @@ export default class UserIncomingCallNumberAPI extends BaseAPI<
104
107
  this.router.post(
105
108
  `/user-incoming-call-number/resend-verification-code`,
106
109
  UserMiddleware.getUserMiddleware,
110
+ VerificationCodeRateLimit.getMiddleware(
111
+ VerificationCodeRateLimitBucket.Resend,
112
+ ),
107
113
  async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
108
114
  try {
109
115
  req = req as OneUptimeRequest;
@@ -135,7 +141,11 @@ export default class UserIncomingCallNumberAPI extends BaseAPI<
135
141
  );
136
142
  }
137
143
 
138
- // Check user ID
144
+ /*
145
+ * A caller may only ask for a code to be sent to a row they own.
146
+ * Without this the resend route is a way to make somebody else's
147
+ * device ring on demand.
148
+ */
139
149
  if (
140
150
  item.userId?.toString() !==
141
151
  (req as OneUptimeRequest)?.userAuthorization?.userId?.toString()