@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
@@ -354,6 +354,13 @@ const FormField: <T extends GenericObject>(
354
354
  : (props.field.defaultValue as boolean)) || false;
355
355
  }
356
356
 
357
+ const storedInputValue: unknown =
358
+ props.currentValues?.[props.fieldName as keyof FormValues<T>];
359
+ const inputInitialValue: unknown =
360
+ storedInputValue === undefined
361
+ ? props.field.defaultValue
362
+ : storedInputValue;
363
+
357
364
  return (
358
365
  <div className="sm:col-span-4 mt-0 mb-2" key={props.fieldName}>
359
366
  {/*** Do not display label on checkbox because checkbox can display its own label */}
@@ -964,10 +971,9 @@ const FormField: <T extends GenericObject>(
964
971
  props.setFieldTouched(props.fieldName, true);
965
972
  }}
966
973
  initialValue={
967
- props.currentValues &&
968
- (props.currentValues as any)[props.fieldName]
969
- ? (props.currentValues as any)[props.fieldName]
970
- : props.field.defaultValue || ""
974
+ inputInitialValue instanceof Date
975
+ ? inputInitialValue
976
+ : String(inputInitialValue ?? "")
971
977
  }
972
978
  placeholder={translatedPlaceholder || ""}
973
979
  />
@@ -209,6 +209,13 @@ const FormSummary: <T extends GenericObject>(
209
209
  description: field.description || "",
210
210
  getElement: (field.getSummaryElement ||
211
211
  defaultSummaryElement) as any,
212
+ /*
213
+ * A dropdown's form value is only the chosen ID. The options
214
+ * ModelForm already fetched are what turn it back into a
215
+ * name — without them a single select prints nothing and a
216
+ * multi select prints raw IDs.
217
+ */
218
+ dropdownOptions: field.dropdownOptions,
212
219
  sideLink: field.sideLink,
213
220
  key: (Object.keys(field.field || {})[0]?.toString() ||
214
221
  "") as keyof T,
@@ -44,6 +44,13 @@ export interface CategoryCheckboxProps {
44
44
  options: Array<CategoryCheckboxOption>;
45
45
  }
46
46
 
47
+ export interface FormFieldCollapsibleSection<TEntity> {
48
+ id: string;
49
+ title: string;
50
+ description?: string | undefined;
51
+ isConfigured: (values: FormValues<TEntity>) => boolean;
52
+ }
53
+
47
54
  export default interface Field<TEntity> {
48
55
  name?: string; // form field name, should be unique in thr form. If not provided, the field will be auto generated.
49
56
  title?: string;
@@ -178,4 +185,7 @@ export default interface Field<TEntity> {
178
185
  */
179
186
  sectionTitle?: string | undefined;
180
187
  sectionDescription?: string | ReactElement | undefined;
188
+
189
+ // Consecutive visible fields with the same id share one collapsible section.
190
+ collapsibleSection?: FormFieldCollapsibleSection<TEntity> | undefined;
181
191
  }
@@ -79,6 +79,7 @@ SyntaxHighlighter.registerLanguage("php", php);
79
79
  SyntaxHighlighter.registerLanguage("graphql", graphql);
80
80
  SyntaxHighlighter.registerLanguage("http", http);
81
81
  import DOMPurify from "dompurify";
82
+ import SessionAwareImage from "./SessionAwareImage";
82
83
  import OneUptimeDate from "../../../Types/Date";
83
84
  import { Theme, useTheme } from "../../Utils/Theme";
84
85
  import {
@@ -598,7 +599,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
598
599
  ) : null;
599
600
  }
600
601
  return (
601
- <img
602
+ <SessionAwareImage
602
603
  className="max-w-full h-auto rounded-md border border-gray-200 my-3"
603
604
  loading="lazy"
604
605
  alt={alt}
@@ -0,0 +1,122 @@
1
+ import UserUtil from "../../Utils/User";
2
+ import React, {
3
+ FunctionComponent,
4
+ ReactElement,
5
+ SyntheticEvent,
6
+ useCallback,
7
+ useEffect,
8
+ useRef,
9
+ useState,
10
+ } from "react";
11
+
12
+ /*
13
+ * Private inline images (pasted into notes, postmortems, runbooks) are served
14
+ * from this route, and only to a request that carries a session.
15
+ */
16
+ export const PRIVATE_IMAGE_ROUTE_SEGMENT: string = "/image/access-token/";
17
+
18
+ export const isPrivateImageUrl: (src: unknown) => boolean = (
19
+ src: unknown,
20
+ ): boolean => {
21
+ return typeof src === "string" && src.includes(PRIVATE_IMAGE_ROUTE_SEGMENT);
22
+ };
23
+
24
+ export const withCacheBuster: (src: string, value: string) => string = (
25
+ src: string,
26
+ value: string,
27
+ ): string => {
28
+ return `${src}${src.includes("?") ? "&" : "?"}sessionRetry=${encodeURIComponent(value)}`;
29
+ };
30
+
31
+ export type RefreshSessionFunction = () => Promise<boolean>;
32
+
33
+ /*
34
+ * Which session an image may refresh is the app's decision, not this
35
+ * component's: the same markdown renders on status pages and public
36
+ * dashboards, where a failed dashboard refresh would log the reader out and
37
+ * send them to the dashboard's login page - and on a status page served from
38
+ * the OneUptime host that shares the dashboard's storage, "signed in" in local
39
+ * storage does not tell the two apart. So nothing is refreshed unless the app
40
+ * has registered its refresh here (the Dashboard and Admin Dashboard do, at
41
+ * startup) or the caller passes one.
42
+ */
43
+ let appRefreshSession: RefreshSessionFunction | null = null;
44
+
45
+ export const enablePrivateImageSessionRefresh: (
46
+ refreshSession: RefreshSessionFunction | null,
47
+ ) => void = (refreshSession: RefreshSessionFunction | null): void => {
48
+ appRefreshSession = refreshSession;
49
+ };
50
+
51
+ export interface ComponentProps
52
+ extends React.ImgHTMLAttributes<HTMLImageElement> {
53
+ refreshSession?: RefreshSessionFunction | undefined;
54
+ isSignedIn?: (() => boolean) | undefined;
55
+ }
56
+
57
+ /*
58
+ * An <img> cannot refresh an expired session the way a request through the API
59
+ * class does: once the access-token cookie lapses, a private image comes back
60
+ * 404 and stays broken until something else happens to refresh the session.
61
+ * So on the first failure of a private image, refresh once and load it again -
62
+ * where the app has opted in (see enablePrivateImageSessionRefresh) and the
63
+ * user is signed in.
64
+ */
65
+ const SessionAwareImage: FunctionComponent<ComponentProps> = ({
66
+ refreshSession,
67
+ isSignedIn,
68
+ onError,
69
+ src,
70
+ ...imageProps
71
+ }: ComponentProps): ReactElement => {
72
+ const [currentSrc, setCurrentSrc] = useState<string | undefined>(src);
73
+ const hasRetriedRef: React.MutableRefObject<boolean> = useRef(false);
74
+
75
+ // A new source is a new image, with its own one retry.
76
+ useEffect(() => {
77
+ setCurrentSrc(src);
78
+ hasRetriedRef.current = false;
79
+ }, [src]);
80
+
81
+ const handleError: (event: SyntheticEvent<HTMLImageElement>) => void =
82
+ useCallback(
83
+ (event: SyntheticEvent<HTMLImageElement>): void => {
84
+ const signedIn: boolean = isSignedIn
85
+ ? isSignedIn()
86
+ : UserUtil.isLoggedIn();
87
+
88
+ const refresh: RefreshSessionFunction | null =
89
+ refreshSession || appRefreshSession;
90
+
91
+ if (
92
+ !refresh ||
93
+ hasRetriedRef.current ||
94
+ !isPrivateImageUrl(src) ||
95
+ !signedIn
96
+ ) {
97
+ onError?.(event);
98
+ return;
99
+ }
100
+
101
+ hasRetriedRef.current = true;
102
+
103
+ void refresh()
104
+ .then((refreshed: boolean) => {
105
+ if (refreshed && typeof src === "string") {
106
+ setCurrentSrc(withCacheBuster(src, Date.now().toString()));
107
+ return;
108
+ }
109
+
110
+ onError?.(event);
111
+ })
112
+ .catch(() => {
113
+ onError?.(event);
114
+ });
115
+ },
116
+ [src, onError, refreshSession, isSignedIn],
117
+ );
118
+
119
+ return <img {...imageProps} src={currentSrc} onError={handleError} />;
120
+ };
121
+
122
+ export default SessionAwareImage;
@@ -323,6 +323,18 @@ export interface BaseTableProps<
323
323
 
324
324
  onFilterApplied?: ((isFilterApplied: boolean) => void) | undefined;
325
325
 
326
+ /**
327
+ * Called with the query the rows are fetched with - the caller's `query`,
328
+ * the column filters and the search box, merged exactly as the list request
329
+ * merges them - on mount and whenever that set of matching rows changes.
330
+ *
331
+ * For a surface that summarizes the same rows the table lists (a volume
332
+ * chart above it, say): reading the filters any other way would let the
333
+ * summary and the list quietly disagree about what "these events" means.
334
+ * Sort and page are not part of it; they re-order the same matching set.
335
+ */
336
+ onQueryChange?: ((query: Query<TBaseModel>) => void) | undefined;
337
+
326
338
  formSummary?: FormSummaryConfig | undefined;
327
339
 
328
340
  onAdvancedFiltersToggle?:
@@ -2029,6 +2041,27 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
2029
2041
  clearBulkSelectionState();
2030
2042
  }, [effectiveQueryKey]);
2031
2043
 
2044
+ type BuildEffectiveQueryFunction = () => Query<TBaseModel>;
2045
+
2046
+ // The one merge the list request and `onQueryChange` both read.
2047
+ const buildEffectiveQuery: BuildEffectiveQueryFunction =
2048
+ (): Query<TBaseModel> => {
2049
+ return {
2050
+ ...props.query,
2051
+ ...query,
2052
+ ...buildSearchQueryFragment(),
2053
+ };
2054
+ };
2055
+
2056
+ /*
2057
+ * Keyed on effectiveQueryKey rather than on the query object, which is a
2058
+ * fresh literal every render: a caller that stores what it is handed here
2059
+ * re-renders, and must not be handed the same query again for it.
2060
+ */
2061
+ useEffect(() => {
2062
+ props.onQueryChange?.(buildEffectiveQuery());
2063
+ }, [effectiveQueryKey]);
2064
+
2032
2065
  const fetchItems: PromiseVoidFunction = async (): Promise<void> => {
2033
2066
  setError("");
2034
2067
  setIsLoading(true);
@@ -2047,11 +2080,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
2047
2080
  modelType: props.modelType as
2048
2081
  | DatabaseBaseModelType
2049
2082
  | AnalyticsBaseModelType,
2050
- query: {
2051
- ...props.query,
2052
- ...query,
2053
- ...buildSearchQueryFragment(),
2054
- },
2083
+ query: buildEffectiveQuery(),
2055
2084
  groupBy: {
2056
2085
  ...props.groupBy,
2057
2086
  },
@@ -6,7 +6,7 @@ import MoreMenuItem from "../MoreMenu/MoreMenuItem";
6
6
  import ModelAPI from "../../Utils/ModelAPI/ModelAPI";
7
7
  import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
8
8
  import SortOrder from "../../../Types/BaseDatabase/SortOrder";
9
- import API from "../../../Utils/API";
9
+ import API from "../../Utils/API/API";
10
10
  import MoreMenuSection from "../MoreMenu/MoreMenuSection";
11
11
  import { ButtonStyleType } from "../Button/Button";
12
12
  import IconProp from "../../../Types/Icon/IconProp";
@@ -118,8 +118,15 @@ const TelemetryHistogram: FunctionComponent<TelemetryHistogramProps> = (
118
118
  const activeSeries: Array<HistogramSeriesOption> = useMemo(() => {
119
119
  const present: Set<string> = new Set<string>();
120
120
 
121
+ /*
122
+ * A zero-count bucket holds a slot on the time axis (so a quiet stretch
123
+ * reads as a gap rather than being squeezed out); it is not a sighting
124
+ * of its series, which draws nothing and must not be legended.
125
+ */
121
126
  for (const bucket of props.buckets) {
122
- present.add(bucket.series);
127
+ if (bucket.count > 0) {
128
+ present.add(bucket.series);
129
+ }
123
130
  }
124
131
 
125
132
  return props.series.filter((option: HistogramSeriesOption): boolean => {
@@ -15,6 +15,13 @@ export interface ComponentProps {
15
15
  richContent?: ReactElement | undefined;
16
16
  // Large collections can wait to create a tooltip until first opened.
17
17
  lazy?: boolean | undefined;
18
+ /*
19
+ * Rich tooltips are interactive by default, and Tippy mounts an interactive
20
+ * tooltip inside the trigger's parent - where an overflow-clipped ancestor
21
+ * (a scrolling grid, say) cuts it off. Pass false for rich content that has
22
+ * nothing to click: it then mounts on <body> and hides on mouse-out.
23
+ */
24
+ interactive?: boolean | undefined;
18
25
  }
19
26
 
20
27
  interface PopupProps extends ComponentProps {
@@ -61,7 +68,7 @@ const TooltipPopup: FunctionComponent<PopupProps> = (
61
68
  {...(props.reference
62
69
  ? { reference: props.reference, showOnCreate: true }
63
70
  : { children: props.children })}
64
- interactive={isRich}
71
+ interactive={props.interactive ?? isRich}
65
72
  trigger="mouseenter focus"
66
73
  hideOnClick={false}
67
74
  maxWidth={isRich ? 380 : 350}
@@ -22,8 +22,25 @@ import {
22
22
  import API, { AuthRetryContext } from "../../../Utils/API";
23
23
  import { IDENTITY_URL } from "../../Config";
24
24
 
25
+ /*
26
+ * One in-flight refresh per session, shared by every caller in this tab: the
27
+ * dashboard's BaseAPI, any subclass that inherits its refresh, and callers of
28
+ * refreshSession() that are not going through a request at all (raw fetch,
29
+ * navigations, the realtime socket). Keyed by session name rather than kept on
30
+ * `this`, because a static field read through `this` gives every subclass its
31
+ * own slot, and two slots for one session means two refreshes racing with the
32
+ * same rotating refresh token.
33
+ */
34
+ const inFlightSessionRefreshes: Map<string, Promise<boolean>> = new Map();
35
+
25
36
  class BaseAPI extends API {
26
- private static refreshPromise: Promise<boolean> | null = null;
37
+ /*
38
+ * A refresh another tab finished this recently is one this tab can reuse:
39
+ * tabs share cookies, so its new access token is already ours. Comfortably
40
+ * longer than a refresh round trip, far shorter than the token lifetime.
41
+ */
42
+ public static readonly RECENT_SESSION_REFRESH_WINDOW_IN_MS: number =
43
+ 15 * 1000;
27
44
 
28
45
  public constructor(protocol: Protocol, hostname: Hostname, route?: Route) {
29
46
  super(protocol, hostname, route);
@@ -117,6 +134,14 @@ class BaseAPI extends API {
117
134
 
118
135
  this.logoutUser();
119
136
 
137
+ /*
138
+ * Already there: a forced navigation would reload the login page, whose
139
+ * requests would fail the same way and reload it again.
140
+ */
141
+ if (Navigation.getCurrentRoute().toString() === loginRoute.toString()) {
142
+ return error;
143
+ }
144
+
120
145
  if (Navigation.getQueryStringByName("token")) {
121
146
  Navigation.navigate(loginRoute.addRouteParam("sso", "true"), {
122
147
  forceNavigate: true,
@@ -140,14 +165,79 @@ class BaseAPI extends API {
140
165
  return error;
141
166
  }
142
167
 
168
+ /*
169
+ * A refresh that got no answer at all (network down, identity unreachable)
170
+ * rejects here rather than resolving false: the request that asked for it
171
+ * then fails with that transport error, instead of logging the user out
172
+ * over a blip.
173
+ */
143
174
  protected static override async tryRefreshAuth(
144
175
  _context: AuthRetryContext,
145
176
  ): Promise<boolean> {
146
- if (!this.refreshPromise) {
147
- this.refreshPromise = (async () => {
148
- const refreshUrl: URL = URL.fromString(
149
- IDENTITY_URL.toString(),
150
- ).addRoute("/refresh-token");
177
+ return await this.refreshSessionOrThrow();
178
+ }
179
+
180
+ /*
181
+ * Where this client's session is refreshed, or null when it has no session
182
+ * that can be (a public dashboard, a status page with no id yet).
183
+ */
184
+ protected static getRefreshSessionUrl(): URL | null {
185
+ return URL.fromString(IDENTITY_URL.toString()).addRoute("/refresh-token");
186
+ }
187
+
188
+ // Names the session for the in-tab single flight, the cross-tab lock and the "recently refreshed" marker.
189
+ protected static getSessionName(): string {
190
+ return "dashboard";
191
+ }
192
+
193
+ /*
194
+ * Refresh the session now, and say whether it worked.
195
+ *
196
+ * Requests made through this class already do this for themselves when they
197
+ * come back 401. This is for the paths that cannot: a raw fetch() (binary
198
+ * responses), a navigation or new tab pointed at an authenticated route, the
199
+ * realtime socket's handshake. Those call it before (or after) and carry on.
200
+ *
201
+ * Concurrent callers share one refresh, in this tab and across tabs. The
202
+ * server rotates the refresh token on every use, and a token that has
203
+ * already been rotated no longer matches a session: that refresh gets a 401
204
+ * and a response that clears every session cookie, including the ones the
205
+ * winning refresh just set. So two tabs that both notice an expired session
206
+ * and both refresh would log each other out. The refresh therefore runs
207
+ * under a cross-tab lock, and a tab that gets the lock just after another
208
+ * tab refreshed reuses that result instead of spending the stale token.
209
+ */
210
+ public static async refreshSession(): Promise<boolean> {
211
+ try {
212
+ return await this.refreshSessionOrThrow();
213
+ } catch {
214
+ // No answer at all; callers outside a request just carry on.
215
+ return false;
216
+ }
217
+ }
218
+
219
+ private static async refreshSessionOrThrow(): Promise<boolean> {
220
+ const refreshUrl: URL | null = this.getRefreshSessionUrl();
221
+
222
+ if (!refreshUrl) {
223
+ return false;
224
+ }
225
+
226
+ const sessionName: string = this.getSessionName();
227
+
228
+ const inFlight: Promise<boolean> | undefined =
229
+ inFlightSessionRefreshes.get(sessionName);
230
+
231
+ if (inFlight) {
232
+ return await inFlight;
233
+ }
234
+
235
+ const refresh: Promise<boolean> = this.runWithSessionLock(
236
+ sessionName,
237
+ async (): Promise<boolean> => {
238
+ if (this.wasSessionRefreshedRecently(sessionName)) {
239
+ return true;
240
+ }
151
241
 
152
242
  const result: HTTPResponse<JSONObject> | HTTPErrorResponse =
153
243
  await super.fetch<JSONObject>({
@@ -156,20 +246,91 @@ class BaseAPI extends API {
156
246
  options: {
157
247
  skipAuthRefresh: true,
158
248
  hasAttemptedAuthRefresh: true,
249
+ /*
250
+ * A refresh that fails because the identity service is
251
+ * restarting is not a dead session, and treating it as one logs
252
+ * the user out. Retry what a retry can fix; a 401 is final.
253
+ */
254
+ retries: 2,
255
+ exponentialBackoff: true,
256
+ retryOnlyOnRetryableErrors: true,
159
257
  },
160
258
  });
161
259
 
162
- if (result instanceof HTTPResponse && result.isSuccess()) {
163
- return true;
260
+ const refreshed: boolean =
261
+ result instanceof HTTPResponse && result.isSuccess();
262
+
263
+ if (refreshed) {
264
+ this.markSessionRefreshed(sessionName);
164
265
  }
165
266
 
267
+ return refreshed;
268
+ },
269
+ ).finally(() => {
270
+ inFlightSessionRefreshes.delete(sessionName);
271
+ });
272
+
273
+ inFlightSessionRefreshes.set(sessionName, refresh);
274
+
275
+ return await refresh;
276
+ }
277
+
278
+ private static getSessionRefreshedAtKey(sessionName: string): string {
279
+ return `session-refreshed-at:${sessionName}`;
280
+ }
281
+
282
+ private static wasSessionRefreshedRecently(sessionName: string): boolean {
283
+ try {
284
+ const refreshedAt: number = Number(
285
+ LocalStorage.getItem(this.getSessionRefreshedAtKey(sessionName)),
286
+ );
287
+
288
+ if (!refreshedAt) {
166
289
  return false;
167
- })().finally(() => {
168
- this.refreshPromise = null;
169
- });
290
+ }
291
+
292
+ const ageInMs: number = Date.now() - refreshedAt;
293
+
294
+ return (
295
+ ageInMs >= 0 && ageInMs < BaseAPI.RECENT_SESSION_REFRESH_WINDOW_IN_MS
296
+ );
297
+ } catch {
298
+ // Storage can be unavailable (privacy modes); then every tab refreshes.
299
+ return false;
300
+ }
301
+ }
302
+
303
+ private static markSessionRefreshed(sessionName: string): void {
304
+ try {
305
+ LocalStorage.setItem(
306
+ this.getSessionRefreshedAtKey(sessionName),
307
+ Date.now().toString(),
308
+ );
309
+ } catch {
310
+ // Best effort, as above.
311
+ }
312
+ }
313
+
314
+ /*
315
+ * Web Locks serialise the refresh across every tab of this origin. Where
316
+ * they are unavailable the refresh simply runs, which is what happened
317
+ * before this lock existed.
318
+ */
319
+ private static async runWithSessionLock(
320
+ sessionName: string,
321
+ work: () => Promise<boolean>,
322
+ ): Promise<boolean> {
323
+ const locks: LockManager | undefined =
324
+ typeof navigator !== "undefined" ? navigator.locks : undefined;
325
+
326
+ if (!locks || typeof locks.request !== "function") {
327
+ return await work();
170
328
  }
171
329
 
172
- return await this.refreshPromise;
330
+ return await locks.request(
331
+ `oneuptime-session-refresh:${sessionName}`,
332
+ work,
333
+ );
173
334
  }
174
335
 
175
336
  protected static getLoginRoute(): Route {
@@ -2,10 +2,21 @@ import URL from "../../../Types/API/URL";
2
2
  import type { RequestOptions as CoreRequestOptions } from "../../../Utils/API";
3
3
  import Dictionary from "../../../Types/Dictionary";
4
4
  import { JSONObject } from "../../../Types/JSON";
5
+ import type BaseAPI from "./API";
5
6
 
6
7
  export default interface RequestOptions {
7
8
  requestHeaders?: Dictionary<string> | undefined;
8
9
  overrideRequestUrl?: URL | undefined;
10
+ /*
11
+ * The client that sends THIS request instead of the model API's default
12
+ * (the dashboard client, or a subclass's getApiClient()). Set by callers
13
+ * that route a shared model request to another app's endpoint - a public
14
+ * dashboard widget's /public-dashboard-api/resource-list read - so that
15
+ * endpoint's 401/403 is handled by that app's client (its master-password
16
+ * page), never by the dashboard's refresh-token + User.logout +
17
+ * /accounts/login.
18
+ */
19
+ apiClient?: typeof BaseAPI | undefined;
9
20
  apiRequestOptions?: CoreRequestOptions | undefined;
10
21
  /*
11
22
  * Extra top-level fields merged into a list request body before the
@@ -28,6 +28,24 @@ export type ListResult<TAnalyticsBaseModel extends AnalyticsBaseModel> =
28
28
  BaseListResult<TAnalyticsBaseModel>;
29
29
 
30
30
  export default class ModelAPI {
31
+ /**
32
+ * The API client used for analytics model requests: the authenticated
33
+ * dashboard client.
34
+ */
35
+ protected static getApiClient(): typeof API {
36
+ return API;
37
+ }
38
+
39
+ /*
40
+ * The client for ONE request: a client the caller routed this request
41
+ * through (RequestOptions.apiClient) wins; otherwise this class's client.
42
+ */
43
+ protected static getApiClientForRequest(
44
+ requestOptions?: RequestOptions | undefined,
45
+ ): typeof API {
46
+ return requestOptions?.apiClient || this.getApiClient();
47
+ }
48
+
31
49
  public static async create<
32
50
  TAnalyticsBaseModel extends AnalyticsBaseModel,
33
51
  >(data: {
@@ -103,7 +121,7 @@ export default class ModelAPI {
103
121
  | JSONArray
104
122
  | TAnalyticsBaseModel
105
123
  | Array<TAnalyticsBaseModel>
106
- > = await API.fetch<
124
+ > = await this.getApiClientForRequest(requestOptions).fetch<
107
125
  JSONObject | JSONArray | TAnalyticsBaseModel | Array<TAnalyticsBaseModel>
108
126
  >({
109
127
  method: HTTPMethod.PUT,
@@ -155,7 +173,9 @@ export default class ModelAPI {
155
173
  }
156
174
 
157
175
  const apiResult: HTTPErrorResponse | HTTPResponse<TAnalyticsBaseModel> =
158
- await API.fetch<TAnalyticsBaseModel>({
176
+ await this.getApiClientForRequest(
177
+ requestOptions,
178
+ ).fetch<TAnalyticsBaseModel>({
159
179
  method: httpMethod,
160
180
  url: apiUrl,
161
181
  data: {
@@ -221,7 +241,7 @@ export default class ModelAPI {
221
241
  const headers: Dictionary<string> = this.getCommonHeaders(requestOptions);
222
242
 
223
243
  const result: HTTPResponse<JSONArray> | HTTPErrorResponse =
224
- await API.fetch<JSONArray>({
244
+ await this.getApiClientForRequest(requestOptions).fetch<JSONArray>({
225
245
  method: HTTPMethod.POST,
226
246
  url: apiUrl,
227
247
  data: {
@@ -288,7 +308,7 @@ export default class ModelAPI {
288
308
  const headers: Dictionary<string> = this.getCommonHeaders(requestOptions);
289
309
 
290
310
  const result: HTTPResponse<JSONArray> | HTTPErrorResponse =
291
- await API.fetch<JSONArray>({
311
+ await this.getApiClientForRequest(requestOptions).fetch<JSONArray>({
292
312
  method: HTTPMethod.POST,
293
313
  url: apiUrl,
294
314
  data: {
@@ -355,7 +375,7 @@ export default class ModelAPI {
355
375
  const headers: Dictionary<string> = this.getCommonHeaders(requestOptions);
356
376
 
357
377
  const result: HTTPResponse<JSONObject> | HTTPErrorResponse =
358
- await API.fetch<JSONObject>({
378
+ await this.getApiClientForRequest(requestOptions).fetch<JSONObject>({
359
379
  method: HTTPMethod.POST,
360
380
  url: apiUrl,
361
381
  data: {
@@ -440,7 +460,9 @@ export default class ModelAPI {
440
460
  requestOptions?: RequestOptions | undefined,
441
461
  ): Promise<TAnalyticsBaseModel | null> {
442
462
  const result: HTTPResponse<TAnalyticsBaseModel> | HTTPErrorResponse =
443
- await API.fetch<TAnalyticsBaseModel>({
463
+ await this.getApiClientForRequest(
464
+ requestOptions,
465
+ ).fetch<TAnalyticsBaseModel>({
444
466
  method: HTTPMethod.POST,
445
467
  url: apiUrl,
446
468
  data: {
@@ -488,7 +510,9 @@ export default class ModelAPI {
488
510
  }
489
511
 
490
512
  const result: HTTPResponse<TAnalyticsBaseModel> | HTTPErrorResponse =
491
- await API.fetch<TAnalyticsBaseModel>({
513
+ await this.getApiClientForRequest(
514
+ requestOptions,
515
+ ).fetch<TAnalyticsBaseModel>({
492
516
  method: HTTPMethod.DELETE,
493
517
  url: apiUrl,
494
518
  headers: this.getCommonHeaders(requestOptions),