@oneuptime/common 13.0.7 → 13.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
  2. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
  3. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
  4. package/Server/API/AIBillingAPI.ts +1 -0
  5. package/Server/API/AIInsightAPI.ts +1 -4
  6. package/Server/API/AIInvestigationAPI.ts +1 -4
  7. package/Server/API/AIReadinessAPI.ts +4 -5
  8. package/Server/API/AlertAPI.ts +1 -0
  9. package/Server/API/AutoRemediationAPI.ts +1 -4
  10. package/Server/API/BaseAPI.ts +13 -0
  11. package/Server/API/BaseAnalyticsAPI.ts +80 -9
  12. package/Server/API/BillingAPI.ts +2 -0
  13. package/Server/API/BillingInvoiceAPI.ts +1 -0
  14. package/Server/API/BillingPaymentMethodAPI.ts +2 -0
  15. package/Server/API/CodeFixRunAPI.ts +1 -3
  16. package/Server/API/CommonAPI.ts +68 -4
  17. package/Server/API/DashboardAPI.ts +12 -12
  18. package/Server/API/GlobalConfigAPI.ts +17 -0
  19. package/Server/API/IncidentAPI.ts +2 -0
  20. package/Server/API/IncidentEpisodeAPI.ts +1 -0
  21. package/Server/API/NotificationAPI.ts +1 -0
  22. package/Server/API/OnCallCalendarAPI.ts +4 -5
  23. package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
  24. package/Server/API/OnCallReadinessAPI.ts +4 -1
  25. package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
  26. package/Server/API/ProjectAPI.ts +1 -0
  27. package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
  28. package/Server/API/StatusPageAPI.ts +161 -32
  29. package/Server/API/TeamMemberAPI.ts +3 -0
  30. package/Server/API/UserCallAPI.ts +2 -0
  31. package/Server/API/UserEmailAPI.ts +2 -0
  32. package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
  33. package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
  34. package/Server/API/UserSlackAPI.ts +1 -0
  35. package/Server/API/UserSmsAPI.ts +2 -0
  36. package/Server/API/UserTotpAuthAPI.ts +1 -0
  37. package/Server/API/UserWebAuthnAPI.ts +2 -0
  38. package/Server/API/UserWebhookAPI.ts +1 -0
  39. package/Server/API/UserWhatsAppAPI.ts +2 -0
  40. package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
  41. package/Server/Middleware/UserAuthorization.ts +79 -5
  42. package/Server/Services/ApiKeyPermissionService.ts +101 -4
  43. package/Server/Services/DatabaseService.ts +245 -0
  44. package/Server/Services/DetectionRuleService.ts +5 -4
  45. package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
  46. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
  47. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
  48. package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
  49. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
  50. package/Server/Services/PayAsYouGoBillingService.ts +23 -28
  51. package/Server/Services/ProjectService.ts +18 -0
  52. package/Server/Services/RumSessionPinService.ts +274 -22
  53. package/Server/Services/TeamPermissionService.ts +120 -26
  54. package/Server/Services/ThreatIntelFeedService.ts +6 -3
  55. package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
  56. package/Server/Types/Database/Permissions/Index.ts +75 -11
  57. package/Server/Types/Database/Permissions/TablePermission.ts +24 -28
  58. package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
  59. package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
  60. package/Server/Utils/MasterAdminPageAccess.ts +61 -0
  61. package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
  62. package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
  63. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
  64. package/Server/Utils/Realtime.ts +163 -45
  65. package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
  66. package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
  67. package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
  68. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
  69. package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
  70. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
  71. package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
  72. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
  73. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
  74. package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
  75. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
  76. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
  77. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
  78. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
  79. package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
  80. package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
  81. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
  82. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
  83. package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
  84. package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
  85. package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
  86. package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
  87. package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
  88. package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
  89. package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +95 -68
  90. package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
  91. package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +28 -3
  92. package/Tests/App/Dashboard/SecurityEventsExplorer.test.tsx +761 -0
  93. package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
  94. package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
  95. package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
  96. package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
  97. package/Tests/App/Dashboard/SecurityEventsVolumeChart.test.tsx +321 -0
  98. package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
  99. package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
  100. package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
  101. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
  102. package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
  103. package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
  104. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
  105. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
  106. package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
  107. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
  108. package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
  109. package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
  110. package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
  111. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
  112. package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
  113. package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
  114. package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
  115. package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
  116. package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
  117. package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
  118. package/Tests/Server/API/GlobalConfigLicense.test.ts +140 -0
  119. package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
  120. package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
  121. package/Tests/Server/API/OnCallReadinessAPI.test.ts +175 -17
  122. package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
  123. package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
  124. package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
  125. package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
  126. package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
  127. package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
  128. package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
  129. package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
  130. package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
  131. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
  132. package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
  133. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
  134. package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
  135. package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
  136. package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
  137. package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
  138. package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
  139. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
  140. package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
  141. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
  142. package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
  143. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
  144. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
  145. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
  146. package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
  147. package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
  148. package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
  149. package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
  150. package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
  151. package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
  152. package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
  153. package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
  154. package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
  155. package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
  156. package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
  157. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
  158. package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
  159. package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
  160. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
  161. package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
  162. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
  163. package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
  164. package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
  165. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
  166. package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
  167. package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
  168. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
  169. package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
  170. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
  171. package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
  172. package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
  173. package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
  174. package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
  175. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
  176. package/Tests/Server/Utils/Realtime.test.ts +502 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
  178. package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
  179. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
  181. package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
  182. package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
  183. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
  184. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
  185. package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
  186. package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
  187. package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
  188. package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
  189. package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
  190. package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
  191. package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
  192. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
  193. package/Tests/Types/Call/CallProvider.test.ts +528 -0
  194. package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
  195. package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
  196. package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
  197. package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
  198. package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
  199. package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
  200. package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
  201. package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +89 -1
  202. package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
  203. package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
  204. package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
  205. package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
  206. package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
  207. package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
  208. package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
  209. package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
  210. package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
  211. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
  212. package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
  213. package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
  214. package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
  215. package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
  216. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  217. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  218. package/Tests/UI/Utils/Cookie.test.ts +440 -0
  219. package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
  220. package/Tests/UI/Utils/Navigation.test.ts +33 -0
  221. package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
  222. package/Tests/UI/Utils/Realtime.test.ts +759 -0
  223. package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
  224. package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
  225. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
  226. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
  227. package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
  228. package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
  229. package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
  230. package/Types/Realtime/EventName.ts +11 -0
  231. package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
  232. package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
  233. package/Types/SecurityEvent/SigmaRule.ts +51 -0
  234. package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
  235. package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
  236. package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
  237. package/UI/Components/Dropdown/Dropdown.tsx +6 -1
  238. package/UI/Components/EditionLabel/EditionLabel.tsx +12 -0
  239. package/UI/Components/EventItem/EventItem.tsx +12 -1
  240. package/UI/Components/Forms/BasicForm.tsx +151 -126
  241. package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
  242. package/UI/Components/Forms/Fields/FormField.tsx +10 -4
  243. package/UI/Components/Forms/FormSummary.tsx +7 -0
  244. package/UI/Components/Forms/Types/Field.ts +10 -0
  245. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
  246. package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
  247. package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
  248. package/UI/Components/ModelTable/TableView.tsx +1 -1
  249. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
  250. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  251. package/UI/Utils/API/API.ts +173 -12
  252. package/UI/Utils/API/RequestOptions.ts +11 -0
  253. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
  254. package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
  255. package/UI/Utils/Navigation.ts +9 -0
  256. package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
  257. package/UI/Utils/Realtime.ts +176 -30
  258. package/Utils/API.ts +14 -5
  259. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
  260. package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
  261. package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
  262. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
  263. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
  264. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
  265. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
  266. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
  267. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
  268. package/build/dist/Server/API/AIBillingAPI.js +1 -1
  269. package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
  270. package/build/dist/Server/API/AIInsightAPI.js +1 -4
  271. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  272. package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
  273. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  274. package/build/dist/Server/API/AIReadinessAPI.js +1 -3
  275. package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
  276. package/build/dist/Server/API/AlertAPI.js +1 -1
  277. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  278. package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
  279. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  280. package/build/dist/Server/API/BaseAPI.js +12 -0
  281. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  282. package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
  283. package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
  284. package/build/dist/Server/API/BillingAPI.js +2 -2
  285. package/build/dist/Server/API/BillingAPI.js.map +1 -1
  286. package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
  287. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  288. package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
  289. package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
  290. package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
  291. package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
  292. package/build/dist/Server/API/CommonAPI.js +50 -5
  293. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  294. package/build/dist/Server/API/DashboardAPI.js +12 -12
  295. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  296. package/build/dist/Server/API/GlobalConfigAPI.js +12 -0
  297. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  298. package/build/dist/Server/API/IncidentAPI.js +2 -2
  299. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  300. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  301. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  302. package/build/dist/Server/API/NotificationAPI.js +1 -1
  303. package/build/dist/Server/API/NotificationAPI.js.map +1 -1
  304. package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
  305. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
  306. package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
  307. package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
  308. package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
  309. package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
  310. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
  311. package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
  312. package/build/dist/Server/API/ProjectAPI.js +1 -1
  313. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  314. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  315. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  316. package/build/dist/Server/API/StatusPageAPI.js +125 -33
  317. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  318. package/build/dist/Server/API/TeamMemberAPI.js +3 -3
  319. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  320. package/build/dist/Server/API/UserCallAPI.js +2 -2
  321. package/build/dist/Server/API/UserCallAPI.js.map +1 -1
  322. package/build/dist/Server/API/UserEmailAPI.js +2 -2
  323. package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
  324. package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
  325. package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
  326. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
  327. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
  328. package/build/dist/Server/API/UserSlackAPI.js +1 -1
  329. package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
  330. package/build/dist/Server/API/UserSmsAPI.js +2 -2
  331. package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
  332. package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
  333. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
  334. package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
  335. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
  336. package/build/dist/Server/API/UserWebhookAPI.js +1 -1
  337. package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
  338. package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
  339. package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
  340. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -2
  341. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  342. package/build/dist/Server/Middleware/UserAuthorization.js +64 -4
  343. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  344. package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
  345. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  346. package/build/dist/Server/Services/DatabaseService.js +171 -0
  347. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  348. package/build/dist/Server/Services/DetectionRuleService.js +4 -4
  349. package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
  350. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
  351. package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
  352. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
  353. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  354. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
  355. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  356. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
  357. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
  358. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
  359. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  360. package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
  361. package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
  362. package/build/dist/Server/Services/ProjectService.js +17 -0
  363. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  364. package/build/dist/Server/Services/RumSessionPinService.js +180 -16
  365. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  366. package/build/dist/Server/Services/TeamPermissionService.js +89 -27
  367. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  368. package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
  369. package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
  370. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
  371. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  372. package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
  373. package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
  374. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +14 -13
  375. package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
  376. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
  377. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
  378. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
  379. package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
  380. package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
  381. package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
  382. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
  383. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  384. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
  385. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  386. package/build/dist/Server/Utils/Realtime.js +126 -37
  387. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  388. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
  389. package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
  390. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
  391. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
  392. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
  393. package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
  394. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
  395. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
  396. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
  397. package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
  398. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
  399. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  400. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
  401. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
  402. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
  403. package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
  404. package/build/dist/Types/Realtime/EventName.js +10 -0
  405. package/build/dist/Types/Realtime/EventName.js.map +1 -1
  406. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
  407. package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
  408. package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
  409. package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
  410. package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
  411. package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
  412. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
  413. package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
  414. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
  415. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
  416. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
  417. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
  418. package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
  419. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  420. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +11 -0
  421. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  422. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  423. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  424. package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
  425. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  426. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
  427. package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
  428. package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
  429. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  430. package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
  431. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  432. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
  433. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  434. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
  435. package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
  436. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
  437. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  438. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  439. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  440. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
  441. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  442. package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
  443. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  444. package/build/dist/UI/Utils/API/API.js +143 -19
  445. package/build/dist/UI/Utils/API/API.js.map +1 -1
  446. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
  447. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  448. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
  449. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  450. package/build/dist/UI/Utils/Navigation.js +8 -0
  451. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  452. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
  453. package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
  454. package/build/dist/UI/Utils/Realtime.js +120 -15
  455. package/build/dist/UI/Utils/Realtime.js.map +1 -1
  456. package/build/dist/Utils/API.js +13 -5
  457. package/build/dist/Utils/API.js.map +1 -1
  458. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
  459. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  460. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
  461. package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
  462. package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
  463. package/package.json +1 -1
@@ -47,6 +47,16 @@ export default class ModelAPI {
47
47
  return API;
48
48
  }
49
49
 
50
+ /*
51
+ * The client for ONE request: a client the caller routed this request
52
+ * through (RequestOptions.apiClient) wins; otherwise this class's client.
53
+ */
54
+ protected static getApiClientForRequest(
55
+ requestOptions?: RequestOptions | undefined,
56
+ ): typeof API {
57
+ return requestOptions?.apiClient || this.getApiClient();
58
+ }
59
+
50
60
  public static async create<TBaseModel extends BaseModel>(data: {
51
61
  model: TBaseModel;
52
62
  modelType: { new (): TBaseModel };
@@ -109,7 +119,7 @@ export default class ModelAPI {
109
119
 
110
120
  const result: HTTPResponse<
111
121
  JSONObject | JSONArray | TBaseModel | Array<TBaseModel>
112
- > = await this.getApiClient().fetch<
122
+ > = await this.getApiClientForRequest(data.requestOptions).fetch<
113
123
  JSONObject | JSONArray | TBaseModel | Array<TBaseModel>
114
124
  >({
115
125
  method: HTTPMethod.PUT,
@@ -163,7 +173,7 @@ export default class ModelAPI {
163
173
  }
164
174
 
165
175
  const apiResult: HTTPErrorResponse | HTTPResponse<TBaseModel> =
166
- await this.getApiClient().fetch<TBaseModel>({
176
+ await this.getApiClientForRequest(data.requestOptions).fetch<TBaseModel>({
167
177
  method: httpMethod,
168
178
  url: apiUrl,
169
179
  data: {
@@ -241,7 +251,7 @@ export default class ModelAPI {
241
251
  }
242
252
 
243
253
  const result: HTTPResponse<JSONArray> | HTTPErrorResponse =
244
- await this.getApiClient().fetch<JSONArray>({
254
+ await this.getApiClientForRequest(data.requestOptions).fetch<JSONArray>({
245
255
  method: HTTPMethod.POST,
246
256
  url: apiUrl,
247
257
  data: {
@@ -315,7 +325,7 @@ export default class ModelAPI {
315
325
  }
316
326
 
317
327
  const result: HTTPResponse<JSONObject> | HTTPErrorResponse =
318
- await this.getApiClient().fetch<JSONObject>({
328
+ await this.getApiClientForRequest(data.requestOptions).fetch<JSONObject>({
319
329
  method: HTTPMethod.POST,
320
330
  url: apiUrl,
321
331
  data: {
@@ -343,10 +353,21 @@ export default class ModelAPI {
343
353
  ): Dictionary<string> {
344
354
  let headers: Dictionary<string> = {};
345
355
 
356
+ /*
357
+ * A request routed through another app's client (a public dashboard
358
+ * widget's /public-dashboard-api read) is not scoped to the viewer's own
359
+ * project: that client sends its own tenant header. The viewer's
360
+ * last-opened dashboard project would override it, and a stale or
361
+ * foreign one turns the public read into a 405 (tenant not found) that
362
+ * the public page treats as a login event.
363
+ */
364
+ const isRoutedToAnotherClient: boolean = Boolean(requestOptions?.apiClient);
365
+
346
366
  if (
347
- !requestOptions ||
348
- !requestOptions.isMultiTenantRequest ||
349
- Object.keys(requestOptions).length === 0
367
+ !isRoutedToAnotherClient &&
368
+ (!requestOptions ||
369
+ !requestOptions.isMultiTenantRequest ||
370
+ Object.keys(requestOptions).length === 0)
350
371
  ) {
351
372
  /*
352
373
  * Read the tenant id via getCurrentProjectId() first - a plain string
@@ -427,7 +448,7 @@ export default class ModelAPI {
427
448
  requestOptions?: RequestOptions | undefined;
428
449
  }): Promise<TBaseModel | null> {
429
450
  const result: HTTPResponse<TBaseModel> | HTTPErrorResponse =
430
- await this.getApiClient().fetch<TBaseModel>({
451
+ await this.getApiClientForRequest(data.requestOptions).fetch<TBaseModel>({
431
452
  method: HTTPMethod.POST,
432
453
  url: data.apiUrl,
433
454
  data: {
@@ -476,7 +497,7 @@ export default class ModelAPI {
476
497
  }
477
498
 
478
499
  const result: HTTPResponse<TBaseModel> | HTTPErrorResponse =
479
- await this.getApiClient().fetch<TBaseModel>({
500
+ await this.getApiClientForRequest(data.requestOptions).fetch<TBaseModel>({
480
501
  method: HTTPMethod.DELETE,
481
502
  url: apiUrl,
482
503
  headers: this.getCommonHeaders(data.requestOptions),
@@ -174,6 +174,15 @@ abstract class Navigation {
174
174
  }
175
175
 
176
176
  public static getCurrentRoute(): Route {
177
+ /*
178
+ * The router hands us its location once it has rendered. An API error can
179
+ * be handled before that (or outside a router altogether), and the address
180
+ * bar is the same answer then.
181
+ */
182
+ if (!this.location) {
183
+ return new Route(window.location.pathname);
184
+ }
185
+
177
186
  return new Route(this.location.pathname);
178
187
  }
179
188
 
@@ -0,0 +1,154 @@
1
+ import BaseAPI from "./API/API";
2
+
3
+ /*
4
+ * Opening an authenticated URL in a new tab, with the session refreshed first.
5
+ *
6
+ * A link to an authenticated API route (an attachment, a pprof export) is a
7
+ * plain navigation. It carries the session cookies but none of the API
8
+ * class's recovery: a request made through BaseAPI that comes back 401
9
+ * refreshes the session and replays itself, while a navigation simply shows
10
+ * the 401. The access cookie expires with the 15-minute access token, so on a
11
+ * page left open that long every such link opened a bare "Authentication
12
+ * required" page although the refresh token was still good. Refreshing before
13
+ * navigating makes the new tab arrive with a live access cookie. It costs
14
+ * nothing extra when another request or another tab has just refreshed:
15
+ * refreshSession() reuses that result.
16
+ */
17
+
18
+ export type RefreshSessionFunction = () => Promise<boolean>;
19
+
20
+ /*
21
+ * The part of a click event the link handler reads. React.MouseEvent
22
+ * satisfies it, and so does a DOM MouseEvent, so this module needs neither.
23
+ */
24
+ export interface AuthenticatedLinkClickEvent {
25
+ button: number;
26
+ metaKey: boolean;
27
+ ctrlKey: boolean;
28
+ shiftKey: boolean;
29
+ altKey: boolean;
30
+ defaultPrevented: boolean;
31
+ preventDefault: () => void;
32
+ }
33
+
34
+ /*
35
+ * The dashboard's session. A client with a session of its own (the status
36
+ * page) passes its API class's refresh instead.
37
+ */
38
+ const refreshDashboardSession: RefreshSessionFunction =
39
+ async (): Promise<boolean> => {
40
+ return await BaseAPI.refreshSession();
41
+ };
42
+
43
+ /*
44
+ * Point a tab this click already opened at the URL, once the session has been
45
+ * refreshed. A refresh that did not work closes the tab instead: a refused one
46
+ * means the session has ended and this page's API client is already taking
47
+ * the user to the login page, and one that got no answer means the server
48
+ * cannot be reached, so the tab would only show an error either way.
49
+ */
50
+ async function navigateTabAfterRefresh(
51
+ tab: Window,
52
+ url: string,
53
+ refreshSession: RefreshSessionFunction,
54
+ ): Promise<void> {
55
+ /*
56
+ * Cut loose from this page, as rel="noopener" would have done. A blank tab
57
+ * opened WITH the "noopener" feature could not be pointed anywhere
58
+ * afterwards, because window.open returns null for it.
59
+ */
60
+ tab.opener = null;
61
+
62
+ let refreshed: boolean = false;
63
+
64
+ try {
65
+ refreshed = await refreshSession();
66
+ } catch {
67
+ refreshed = false;
68
+ }
69
+
70
+ // Closed while the refresh ran: the user changed their mind.
71
+ if (tab.closed) {
72
+ return;
73
+ }
74
+
75
+ if (!refreshed) {
76
+ tab.close();
77
+ return;
78
+ }
79
+
80
+ tab.location.href = url;
81
+ }
82
+
83
+ export default async function openAuthenticatedUrl(
84
+ url: string,
85
+ refreshSession: RefreshSessionFunction = refreshDashboardSession,
86
+ ): Promise<void> {
87
+ /*
88
+ * Popup blockers let window.open through only while the click is still
89
+ * being handled; once the handler has awaited something, Safari and Firefox
90
+ * no longer count the new tab as the user's doing and block it. So the tab
91
+ * is opened now, blank, and pointed at the URL once the refresh has settled.
92
+ */
93
+ const newTab: Window | null = window.open("", "_blank");
94
+
95
+ if (!newTab) {
96
+ /*
97
+ * Blocked. Ask for the URL itself, still inside the click: without a tab
98
+ * to hold, there is nothing to refresh the session for.
99
+ */
100
+ window.open(url, "_blank", "noopener,noreferrer");
101
+ return;
102
+ }
103
+
104
+ await navigateTabAfterRefresh(newTab, url, refreshSession);
105
+ }
106
+
107
+ /*
108
+ * The onClick for an <a href={url} target="_blank"> that points at an
109
+ * authenticated route. The href stays on the element so middle-click, "copy
110
+ * link address" and the context menu keep working exactly as before (those
111
+ * paths get no refresh); a plain click opens the URL in a tab of its own once
112
+ * the session is refreshed.
113
+ */
114
+ export function handleAuthenticatedLinkClick(
115
+ event: AuthenticatedLinkClickEvent,
116
+ url: string,
117
+ refreshSession?: RefreshSessionFunction,
118
+ ): void {
119
+ /*
120
+ * Modified clicks stay the browser's: cmd/ctrl-click opens a background
121
+ * tab, shift-click a new window, alt-click downloads. Turning all of them
122
+ * into a foreground tab would be a surprise.
123
+ */
124
+ if (
125
+ event.defaultPrevented ||
126
+ event.button !== 0 ||
127
+ event.metaKey ||
128
+ event.ctrlKey ||
129
+ event.shiftKey ||
130
+ event.altKey
131
+ ) {
132
+ return;
133
+ }
134
+
135
+ /*
136
+ * Open the tab before deciding to take the click over. Where window.open
137
+ * is refused (a popup-blocking extension, some in-app browsers) the anchor's
138
+ * own navigation may still be allowed, so the click is left to it - no
139
+ * refresh, but a link that opens, as it always did.
140
+ */
141
+ const newTab: Window | null = window.open("", "_blank");
142
+
143
+ if (!newTab) {
144
+ return;
145
+ }
146
+
147
+ event.preventDefault();
148
+
149
+ void navigateTabAfterRefresh(
150
+ newTab,
151
+ url,
152
+ refreshSession || refreshDashboardSession,
153
+ );
154
+ }
@@ -1,4 +1,5 @@
1
1
  import { HOST, HTTP_PROTOCOL } from "../Config";
2
+ import API from "./API/API";
2
3
  import AnalyticsBaseModel from "../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
3
4
  import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
4
5
  import { RealtimeRoute } from "../../ServiceRoute";
@@ -21,14 +22,55 @@ export interface ListenToModelEvent<
21
22
  modelId?: ObjectID | undefined;
22
23
  }
23
24
 
25
+ /*
26
+ * One subscription a caller holds: what the server has to be asked for (again
27
+ * after every reconnect) and the handler listening on its room.
28
+ */
29
+ interface RealtimeSubscription {
30
+ request: ListenToModelEventJSON;
31
+ roomId: string;
32
+ handler: (data: JSONObject) => void;
33
+ }
34
+
24
35
  export default abstract class Realtime {
36
+ /*
37
+ * The least time between two refresh-and-reconnects. A reconnect with a
38
+ * freshly refreshed session that the server still refuses (a cookie that
39
+ * does not reach the socket, say) must not turn into a reconnect loop; the
40
+ * next refusal after this long tries again. Far shorter than the 15-minute
41
+ * access token, so a session that genuinely expires again is still renewed.
42
+ */
43
+ public static readonly AUTHENTICATION_RECOVERY_COOLDOWN_IN_MS: number =
44
+ 60 * 1000;
45
+
25
46
  private static socket: Socket;
26
47
 
48
+ /*
49
+ * Every subscription still wanted, by identity: two callers asking for the
50
+ * same room are two entries, and each one's stop function removes only its
51
+ * own. The server forgets a socket's rooms when the connection drops, so
52
+ * this is what gets re-sent on every (re)connect.
53
+ */
54
+ private static subscriptions: Set<RealtimeSubscription> = new Set();
55
+
56
+ private static isRecoveringAuthentication: boolean = false;
57
+
58
+ private static lastAuthenticationRecoveryAt: number | null = null;
59
+
27
60
  public static init(): void {
28
61
  const socket: Socket = SocketIO(new URL(HTTP_PROTOCOL, HOST).toString(), {
29
62
  path: RealtimeRoute.toString(),
30
63
  });
31
64
 
65
+ // Initial connect and every reconnect: the new connection is in no rooms.
66
+ socket.on("connect", (): void => {
67
+ this.resubscribeAll();
68
+ });
69
+
70
+ socket.on(EventName.AuthenticationRequired, (): void => {
71
+ this.onAuthenticationRequired();
72
+ });
73
+
32
74
  this.socket = socket;
33
75
  }
34
76
 
@@ -55,27 +97,22 @@ export default abstract class Realtime {
55
97
  tenantId: listenToModelEvent.tenantId.toString(),
56
98
  };
57
99
 
58
- this.emit(EventName.ListenToModalEvent, listenToModelEventJSON as any);
59
-
60
100
  const roomId: string = RealtimeUtil.getRoomId(
61
101
  listenToModelEvent.tenantId,
62
102
  listenToModelEvent.modelType.name,
63
103
  listenToModelEvent.eventType,
64
104
  );
65
105
 
66
- this.socket.on(roomId, (data: JSONObject) => {
67
- const id: ObjectID = ObjectID.fromString(
68
- data["modelId"]?.toString() as string,
69
- );
70
- onEvent(id);
71
- });
72
-
73
- // Stop listening to the event.
74
- const stopListening: () => void = (): void => {
75
- this.socket.off(roomId);
76
- };
77
-
78
- return stopListening;
106
+ return this.subscribe(
107
+ listenToModelEventJSON,
108
+ roomId,
109
+ (data: JSONObject): void => {
110
+ const id: ObjectID = ObjectID.fromString(
111
+ data["modelId"]?.toString() as string,
112
+ );
113
+ onEvent(id);
114
+ },
115
+ );
79
116
  }
80
117
 
81
118
  public static listenToAnalyticsModelEvent<Model extends AnalyticsBaseModel>(
@@ -93,8 +130,6 @@ export default abstract class Realtime {
93
130
  tenantId: listenToModelEvent.tenantId.toString(),
94
131
  };
95
132
 
96
- this.emit(EventName.ListenToModalEvent, listenToModelEventJSON as any);
97
-
98
133
  let roomId: string = RealtimeUtil.getRoomId(
99
134
  listenToModelEvent.tenantId,
100
135
  listenToModelEvent.modelType.name,
@@ -110,28 +145,139 @@ export default abstract class Realtime {
110
145
  );
111
146
  }
112
147
 
113
- this.socket.on(roomId, (model: JSONObject) => {
114
- onEvent(
115
- AnalyticsBaseModel.fromJSON(
116
- model,
117
- listenToModelEvent.modelType,
118
- ) as Model,
119
- );
120
- });
148
+ return this.subscribe(
149
+ listenToModelEventJSON,
150
+ roomId,
151
+ (model: JSONObject): void => {
152
+ onEvent(
153
+ AnalyticsBaseModel.fromJSON(
154
+ model,
155
+ listenToModelEvent.modelType,
156
+ ) as Model,
157
+ );
158
+ },
159
+ );
160
+ }
161
+
162
+ public static emit(eventName: string, data: JSONObject): void {
163
+ if (!this.socket) {
164
+ this.init();
165
+ }
166
+
167
+ this.socket.emit(eventName, data);
168
+ }
169
+
170
+ private static subscribe(
171
+ request: ListenToModelEventJSON,
172
+ roomId: string,
173
+ handler: (data: JSONObject) => void,
174
+ ): () => void {
175
+ const subscription: RealtimeSubscription = {
176
+ request: request,
177
+ roomId: roomId,
178
+ handler: handler,
179
+ };
180
+
181
+ this.subscriptions.add(subscription);
182
+
183
+ this.socket.on(roomId, handler);
184
+
185
+ /*
186
+ * Not connected yet (or between a disconnect and a reconnect): the
187
+ * 'connect' handler sends every registered subscription, this one
188
+ * included. Emitting now as well would have socket.io buffer this one and
189
+ * send it a second time on connect.
190
+ */
191
+ if (this.socket.connected) {
192
+ this.socket.emit(EventName.ListenToModalEvent, request);
193
+ }
121
194
 
122
195
  // Stop listening to the event.
123
196
  const stopListening: () => void = (): void => {
124
- this.socket.off(roomId);
197
+ this.subscriptions.delete(subscription);
198
+
199
+ /*
200
+ * Only this subscription's handler. Removing every listener on the room
201
+ * would also silence any other subscription to the same room.
202
+ */
203
+ this.socket.off(roomId, handler);
125
204
  };
126
205
 
127
206
  return stopListening;
128
207
  }
129
208
 
130
- public static emit(eventName: string, data: JSONObject): void {
131
- if (!this.socket) {
132
- this.init();
209
+ private static resubscribeAll(): void {
210
+ // The server's join is idempotent, so identical requests go once.
211
+ const sentRequests: Set<string> = new Set();
212
+
213
+ for (const subscription of this.subscriptions) {
214
+ const requestKey: string = JSON.stringify(subscription.request);
215
+
216
+ if (sentRequests.has(requestKey)) {
217
+ continue;
218
+ }
219
+
220
+ sentRequests.add(requestKey);
221
+
222
+ this.socket.emit(EventName.ListenToModalEvent, subscription.request);
133
223
  }
224
+ }
134
225
 
135
- this.socket.emit(eventName, data);
226
+ /*
227
+ * The server refused a subscription because this socket's handshake carries
228
+ * no access token, or an expired one. The cookie is read once, when the
229
+ * socket connects, so an HTTP request refreshing the session does nothing
230
+ * for the socket: refresh (or reuse a refresh another request or tab just
231
+ * did), then reconnect so the new handshake carries the new cookie. The
232
+ * reconnect's 'connect' re-sends every subscription, including the refused
233
+ * one.
234
+ *
235
+ * A burst of refusals (a page subscribing to several rooms at once) is one
236
+ * recovery: the first starts it, the rest arrive while it is in flight or
237
+ * inside the cool-down.
238
+ */
239
+ private static onAuthenticationRequired(): void {
240
+ if (this.isRecoveringAuthentication) {
241
+ return;
242
+ }
243
+
244
+ if (
245
+ this.lastAuthenticationRecoveryAt !== null &&
246
+ Date.now() - this.lastAuthenticationRecoveryAt <
247
+ this.AUTHENTICATION_RECOVERY_COOLDOWN_IN_MS
248
+ ) {
249
+ return;
250
+ }
251
+
252
+ this.isRecoveringAuthentication = true;
253
+ this.lastAuthenticationRecoveryAt = Date.now();
254
+
255
+ this.recoverAuthentication().catch(() => {
256
+ // recoverAuthentication settles every outcome itself.
257
+ });
258
+ }
259
+
260
+ private static async recoverAuthentication(): Promise<void> {
261
+ try {
262
+ /*
263
+ * Without a new cookie a reconnect would only be refused again. When
264
+ * the refresh failed because the session is over, the refresh request
265
+ * has already sent the user to the login page.
266
+ */
267
+ const refreshed: boolean = await API.refreshSession();
268
+
269
+ if (!refreshed) {
270
+ return;
271
+ }
272
+
273
+ this.socket.disconnect();
274
+ this.socket.connect();
275
+ } catch {
276
+ // A refresh that could not complete (offline, say) did not happen.
277
+ } finally {
278
+ this.isRecoveringAuthentication = false;
279
+ // The cool-down runs from the end of the attempt, however long it took.
280
+ this.lastAuthenticationRecoveryAt = Date.now();
281
+ }
136
282
  }
137
283
  }
package/Utils/API.ts CHANGED
@@ -247,34 +247,43 @@ export default class API {
247
247
  return false;
248
248
  }
249
249
 
250
+ /*
251
+ * The class this instance was built from, so `new BaseAPI(...).get()` gets
252
+ * BaseAPI's session refresh, error handling and headers rather than the
253
+ * base class's no-op versions.
254
+ */
255
+ private getStaticClient(): typeof API {
256
+ return this.constructor as typeof API;
257
+ }
258
+
250
259
  public async get<
251
260
  T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>,
252
261
  >(options: APIRequestOptions): Promise<HTTPResponse<T> | HTTPErrorResponse> {
253
- return await API.get<T>(options);
262
+ return await this.getStaticClient().get<T>(options);
254
263
  }
255
264
 
256
265
  public async delete<
257
266
  T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>,
258
267
  >(options: APIRequestOptions): Promise<HTTPResponse<T> | HTTPErrorResponse> {
259
- return await API.delete<T>(options);
268
+ return await this.getStaticClient().delete<T>(options);
260
269
  }
261
270
 
262
271
  public async head<
263
272
  T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>,
264
273
  >(options: APIRequestOptions): Promise<HTTPResponse<T> | HTTPErrorResponse> {
265
- return await API.head<T>(options);
274
+ return await this.getStaticClient().head<T>(options);
266
275
  }
267
276
 
268
277
  public async put<
269
278
  T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>,
270
279
  >(options: APIRequestOptions): Promise<HTTPResponse<T> | HTTPErrorResponse> {
271
- return await API.put<T>(options);
280
+ return await this.getStaticClient().put<T>(options);
272
281
  }
273
282
 
274
283
  public async patch<
275
284
  T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>,
276
285
  >(options: APIRequestOptions): Promise<HTTPResponse<T> | HTTPErrorResponse> {
277
- return await API.patch<T>(options);
286
+ return await this.getStaticClient().patch<T>(options);
278
287
  }
279
288
 
280
289
  public static handleError(