@oneuptime/common 12.0.28 → 12.0.30

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 (367) hide show
  1. package/Models/DatabaseModels/DashboardDomain.ts +43 -0
  2. package/Models/DatabaseModels/Index.ts +11 -0
  3. package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
  4. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +43 -0
  5. package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +662 -0
  6. package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +584 -0
  7. package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
  8. package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +546 -0
  9. package/Models/DatabaseModels/UserOnCallShiftReminder.ts +231 -0
  10. package/Models/DatabaseModels/UserOnCallShiftReminderLog.ts +331 -0
  11. package/Server/API/DashboardDomainAPI.ts +74 -0
  12. package/Server/API/MicrosoftTeamsAPI.ts +81 -180
  13. package/Server/API/OnCallCalendarAPI.ts +2194 -0
  14. package/Server/API/StatusPageDomainAPI.ts +74 -0
  15. package/Server/EnvironmentConfig.ts +138 -0
  16. package/Server/Infrastructure/OnCallCalendarFeedCache.ts +1050 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.ts +253 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  21. package/Server/Middleware/OnCallCalendarFeedRateLimit.ts +487 -0
  22. package/Server/Middleware/ProjectAuthorization.ts +6 -1
  23. package/Server/Services/DashboardDomainService.ts +131 -0
  24. package/Server/Services/Index.ts +12 -0
  25. package/Server/Services/LogPipelineProcessorService.ts +88 -0
  26. package/Server/Services/MonitorTemplateService.ts +70 -11
  27. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +32 -0
  28. package/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.ts +260 -0
  29. package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +70 -3
  30. package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +138 -3
  31. package/Server/Services/OnCallDutyPolicyScheduleService.ts +1465 -134
  32. package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +167 -31
  33. package/Server/Services/ProjectOnCallCalendarFeedService.ts +192 -0
  34. package/Server/Services/StatusPageDomainService.ts +163 -3
  35. package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
  36. package/Server/Services/TeamMemberService.ts +489 -1
  37. package/Server/Services/TeamService.ts +50 -0
  38. package/Server/Services/UserNotificationSettingService.ts +44 -1
  39. package/Server/Services/UserOnCallCalendarFeedService.ts +159 -0
  40. package/Server/Services/UserOnCallShiftReminderLogService.ts +151 -0
  41. package/Server/Services/UserOnCallShiftReminderService.ts +112 -0
  42. package/Server/Types/Domain.ts +17 -0
  43. package/Server/Utils/APIKey/AccessPermission.ts +24 -0
  44. package/Server/Utils/Greenlock/Greenlock.ts +156 -58
  45. package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
  46. package/Server/Utils/OnCall/CalendarFeedToken.ts +241 -0
  47. package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1488 -0
  48. package/Server/Utils/OnCall/OnCallCalendarFeedUrls.ts +230 -0
  49. package/Server/Utils/OnCall/OnCallShiftChangeListeners.ts +227 -0
  50. package/Server/Utils/OnCall/OnCallShiftMaterializer.ts +1127 -0
  51. package/Server/Utils/OnCall/OnCallShiftReminderListener.ts +174 -0
  52. package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +2584 -0
  53. package/Server/Utils/Response.ts +134 -0
  54. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
  55. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
  56. package/Server/Utils/StartServer.ts +10 -0
  57. package/Server/Utils/Telemetry/AppMetrics.ts +51 -0
  58. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
  59. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
  60. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
  61. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
  62. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
  63. package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
  64. package/Tests/App/Dashboard/OnCallCalendarFeedLinks.test.tsx +229 -0
  65. package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +489 -0
  66. package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +149 -0
  67. package/Tests/App/Dashboard/OnCallCalendarFeedUtil.test.ts +855 -0
  68. package/Tests/App/Dashboard/OnCallPersonalCalendarFeed.test.tsx +1209 -0
  69. package/Tests/App/Dashboard/OnCallSharedCalendarFeedCard.test.tsx +1019 -0
  70. package/Tests/App/Dashboard/OnCallShiftRemindersAndUpcomingShifts.test.tsx +655 -0
  71. package/Tests/App/Dashboard/UserSettingsSetupChecklistModel.test.ts +73 -0
  72. package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
  73. package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
  74. package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
  75. package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
  76. package/Tests/Models/OnCallCalendarFeedModels.test.ts +642 -0
  77. package/Tests/Models/OnCallDutyPolicyScheduleCalendarFeed.test.ts +288 -0
  78. package/Tests/Models/OnCallDutyPolicyScheduleShiftConfigVersion.test.ts +88 -0
  79. package/Tests/Models/UserOnCallShiftReminderModels.test.ts +368 -0
  80. package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
  81. package/Tests/Server/API/Helpers.ts +8 -0
  82. package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
  83. package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
  84. package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
  85. package/Tests/Server/API/OnCallCalendarAPI.test.ts +4461 -0
  86. package/Tests/Server/EnvironmentConfigOnCallCalendarFeed.test.ts +357 -0
  87. package/Tests/Server/Infrastructure/OnCallCalendarFeedCache.test.ts +1589 -0
  88. package/Tests/Server/Infrastructure/Postgres/AddOnCallCalendarFeedsMigration.test.ts +369 -0
  89. package/Tests/Server/Middleware/OnCallCalendarFeedRateLimit.test.ts +1365 -0
  90. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
  91. package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
  92. package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
  93. package/Tests/Server/Services/FeedRetentionConsistency.test.ts +70 -0
  94. package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
  95. package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
  96. package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
  97. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
  98. package/Tests/Server/Services/OnCallCalendarFeedServices.test.ts +1103 -0
  99. package/Tests/Server/Services/OnCallDutyPolicyScheduleResolver.test.ts +1037 -0
  100. package/Tests/Server/Services/OnCallShiftConfigPropagation.test.ts +1270 -0
  101. package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
  102. package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
  103. package/Tests/Server/Services/TeamDeleteMemberCleanup.test.ts +156 -0
  104. package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +755 -0
  105. package/Tests/Server/Services/UserNotificationSettingShiftReminderDefaults.test.ts +267 -0
  106. package/Tests/Server/Services/UserOnCallShiftReminderServices.test.ts +583 -0
  107. package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
  108. package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
  109. package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +254 -0
  110. package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
  111. package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
  112. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
  113. package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
  114. package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
  115. package/Tests/Server/Utils/OnCall/CalendarFeedToken.test.ts +467 -0
  116. package/Tests/Server/Utils/OnCall/OnCallCalendarFeedRenderer.test.ts +2142 -0
  117. package/Tests/Server/Utils/OnCall/OnCallCalendarFeedUrls.test.ts +278 -0
  118. package/Tests/Server/Utils/OnCall/OnCallResolverTestHarness.ts +440 -0
  119. package/Tests/Server/Utils/OnCall/OnCallShiftChangeListeners.test.ts +314 -0
  120. package/Tests/Server/Utils/OnCall/OnCallShiftMaterializer.test.ts +1502 -0
  121. package/Tests/Server/Utils/OnCall/OnCallShiftReminderListener.test.ts +491 -0
  122. package/Tests/Server/Utils/OnCall/OnCallShiftReminderRunner.test.ts +3088 -0
  123. package/Tests/Server/Utils/OnCall/OnCallShiftReminderTestHarness.ts +910 -0
  124. package/Tests/Server/Utils/ResponseSendCalendarResponse.test.ts +561 -0
  125. package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
  126. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
  127. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
  128. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
  129. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
  130. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
  131. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
  132. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
  133. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
  134. package/Tests/Server/Utils/StartServerEncryptionSecretWarning.test.ts +64 -0
  135. package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +29 -0
  136. package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
  137. package/Tests/Types/API/HTTPResponse.test.ts +175 -0
  138. package/Tests/Types/API/Route.test.ts +36 -4
  139. package/Tests/Types/Calendar/ICalendar.test.ts +488 -0
  140. package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
  141. package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
  142. package/Tests/Types/DateLocalTimeString.test.ts +259 -0
  143. package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
  144. package/Tests/Types/NotificationSetting/NotificationSettingEventType.test.ts +53 -0
  145. package/Tests/Types/OnCallDutyPolicy/CalendarFeedTestFixtures.ts +310 -0
  146. package/Tests/Types/OnCallDutyPolicy/CalendarFeedWindow.test.ts +205 -0
  147. package/Tests/Types/OnCallDutyPolicy/LayerUtilLayerMetaAndIterationCap.test.ts +458 -0
  148. package/Tests/Types/OnCallDutyPolicy/MaterializedShift.test.ts +197 -0
  149. package/Tests/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.test.ts +1986 -0
  150. package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtilGroupKey.test.ts +678 -0
  151. package/Tests/Types/OnCallDutyPolicy/ShiftSeamUtil.test.ts +417 -0
  152. package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
  153. package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
  154. package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
  155. package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
  156. package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
  157. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
  158. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
  159. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  160. package/Tests/UI/Utils/Navigation.test.ts +82 -0
  161. package/Tests/Utils/Array.test.ts +100 -0
  162. package/Tests/Utils/CertificateReissue.test.ts +225 -0
  163. package/Tests/Utils/Grok/Grok.test.ts +492 -0
  164. package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
  165. package/Types/API/Route.ts +20 -5
  166. package/Types/Calendar/ICalendar.ts +442 -0
  167. package/Types/Date.ts +143 -13
  168. package/Types/Email/EmailTemplateType.ts +2 -0
  169. package/Types/NotificationSetting/NotificationSettingEventType.ts +4 -0
  170. package/Types/OnCallDutyPolicy/CalendarFeedWindow.ts +140 -0
  171. package/Types/OnCallDutyPolicy/Layer.ts +190 -35
  172. package/Types/OnCallDutyPolicy/MaterializedShift.ts +274 -0
  173. package/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.ts +1415 -0
  174. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +159 -4
  175. package/Types/OnCallDutyPolicy/ShiftSeamUtil.ts +106 -0
  176. package/Types/Rum/SessionReplay.ts +62 -1
  177. package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
  178. package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
  179. package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
  180. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
  181. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
  182. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
  183. package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
  184. package/UI/Utils/Navigation.ts +42 -0
  185. package/Utils/Array.ts +37 -0
  186. package/Utils/CertificateReissue.ts +128 -0
  187. package/Utils/Grok/Grok.ts +461 -0
  188. package/Utils/Grok/GrokPatterns.ts +118 -0
  189. package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
  190. package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
  191. package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/Index.js +11 -0
  193. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
  195. package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +44 -0
  197. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +699 -0
  199. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -0
  200. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +620 -0
  201. package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -0
  202. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
  203. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +585 -0
  205. package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -0
  206. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js +247 -0
  207. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminder.js.map +1 -0
  208. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js +355 -0
  209. package/build/dist/Models/DatabaseModels/UserOnCallShiftReminderLog.js.map +1 -0
  210. package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
  211. package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
  212. package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
  213. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  214. package/build/dist/Server/API/OnCallCalendarAPI.js +1371 -0
  215. package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -0
  216. package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
  217. package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
  218. package/build/dist/Server/EnvironmentConfig.js +102 -0
  219. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  220. package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js +663 -0
  221. package/build/dist/Server/Infrastructure/OnCallCalendarFeedCache.js.map +1 -0
  222. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
  223. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
  224. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
  225. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js.map +1 -0
  226. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js +103 -0
  227. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790400000000-AddOnCallCalendarFeeds.js.map +1 -0
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  230. package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js +342 -0
  231. package/build/dist/Server/Middleware/OnCallCalendarFeedRateLimit.js.map +1 -0
  232. package/build/dist/Server/Middleware/ProjectAuthorization.js +6 -1
  233. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  234. package/build/dist/Server/Services/DashboardDomainService.js +104 -0
  235. package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
  236. package/build/dist/Server/Services/Index.js +12 -0
  237. package/build/dist/Server/Services/Index.js.map +1 -1
  238. package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
  239. package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
  240. package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
  241. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  242. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +20 -0
  243. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  244. package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js +219 -0
  245. package/build/dist/Server/Services/OnCallDutyPolicyScheduleCalendarFeedService.js.map +1 -0
  246. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +47 -3
  247. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
  248. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +112 -3
  249. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
  250. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +955 -9
  251. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  252. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +93 -6
  253. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
  254. package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js +157 -0
  255. package/build/dist/Server/Services/ProjectOnCallCalendarFeedService.js.map +1 -0
  256. package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
  257. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  258. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
  259. package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
  260. package/build/dist/Server/Services/TeamMemberService.js +396 -1
  261. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  262. package/build/dist/Server/Services/TeamService.js +43 -0
  263. package/build/dist/Server/Services/TeamService.js.map +1 -1
  264. package/build/dist/Server/Services/UserNotificationSettingService.js +26 -1
  265. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  266. package/build/dist/Server/Services/UserOnCallCalendarFeedService.js +140 -0
  267. package/build/dist/Server/Services/UserOnCallCalendarFeedService.js.map +1 -0
  268. package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js +116 -0
  269. package/build/dist/Server/Services/UserOnCallShiftReminderLogService.js.map +1 -0
  270. package/build/dist/Server/Services/UserOnCallShiftReminderService.js +92 -0
  271. package/build/dist/Server/Services/UserOnCallShiftReminderService.js.map +1 -0
  272. package/build/dist/Server/Types/Domain.js +16 -0
  273. package/build/dist/Server/Types/Domain.js.map +1 -1
  274. package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
  275. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  276. package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
  277. package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
  278. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
  279. package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
  280. package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js +161 -0
  281. package/build/dist/Server/Utils/OnCall/CalendarFeedToken.js.map +1 -0
  282. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js +983 -0
  283. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedRenderer.js.map +1 -0
  284. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js +155 -0
  285. package/build/dist/Server/Utils/OnCall/OnCallCalendarFeedUrls.js.map +1 -0
  286. package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js +145 -0
  287. package/build/dist/Server/Utils/OnCall/OnCallShiftChangeListeners.js.map +1 -0
  288. package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js +752 -0
  289. package/build/dist/Server/Utils/OnCall/OnCallShiftMaterializer.js.map +1 -0
  290. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js +92 -0
  291. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderListener.js.map +1 -0
  292. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1786 -0
  293. package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -0
  294. package/build/dist/Server/Utils/Response.js +97 -0
  295. package/build/dist/Server/Utils/Response.js.map +1 -1
  296. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
  297. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  298. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
  299. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  300. package/build/dist/Server/Utils/StartServer.js +9 -1
  301. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  302. package/build/dist/Server/Utils/Telemetry/AppMetrics.js +41 -0
  303. package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
  304. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
  305. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  306. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
  307. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  308. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
  309. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
  310. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
  311. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  312. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
  313. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  314. package/build/dist/Types/API/Route.js +16 -5
  315. package/build/dist/Types/API/Route.js.map +1 -1
  316. package/build/dist/Types/Calendar/ICalendar.js +290 -0
  317. package/build/dist/Types/Calendar/ICalendar.js.map +1 -0
  318. package/build/dist/Types/Date.js +115 -13
  319. package/build/dist/Types/Date.js.map +1 -1
  320. package/build/dist/Types/Email/EmailTemplateType.js +2 -0
  321. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  322. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +3 -0
  323. package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
  324. package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js +97 -0
  325. package/build/dist/Types/OnCallDutyPolicy/CalendarFeedWindow.js.map +1 -0
  326. package/build/dist/Types/OnCallDutyPolicy/Layer.js +113 -19
  327. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  328. package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js +134 -0
  329. package/build/dist/Types/OnCallDutyPolicy/MaterializedShift.js.map +1 -0
  330. package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js +800 -0
  331. package/build/dist/Types/OnCallDutyPolicy/OnCallCalendarFeedUtil.js.map +1 -0
  332. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +97 -3
  333. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -1
  334. package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js +77 -0
  335. package/build/dist/Types/OnCallDutyPolicy/ShiftSeamUtil.js.map +1 -0
  336. package/build/dist/Types/Rum/SessionReplay.js +38 -0
  337. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  338. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
  339. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
  340. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
  341. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  342. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
  343. package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
  344. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
  345. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  346. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
  347. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  348. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
  349. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  350. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
  351. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
  352. package/build/dist/UI/Utils/Navigation.js +30 -0
  353. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  354. package/build/dist/Utils/Array.js +25 -0
  355. package/build/dist/Utils/Array.js.map +1 -1
  356. package/build/dist/Utils/CertificateReissue.js +97 -0
  357. package/build/dist/Utils/CertificateReissue.js.map +1 -0
  358. package/build/dist/Utils/Grok/Grok.js +324 -0
  359. package/build/dist/Utils/Grok/Grok.js.map +1 -0
  360. package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
  361. package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
  362. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
  363. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
  364. package/package.json +1 -1
  365. package/UI/Utils/JsonWebToken.ts +0 -14
  366. package/build/dist/UI/Utils/JsonWebToken.js +0 -10
  367. package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
@@ -18,12 +18,124 @@ import Timezone from "../../Types/Timezone";
18
18
  import DeleteBy from "../Types/Database/DeleteBy";
19
19
  import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
20
20
  import logger from "../Utils/Logger";
21
+ import { OnCallShiftChangeReason } from "../Utils/OnCall/OnCallShiftChangeListeners";
22
+
23
+ /*
24
+ * The (project, overridden user, substitute) triple an override names; the
25
+ * hooks capture it before an update / delete so the schedules of the OLD
26
+ * users can be refreshed and re-versioned once the row has changed.
27
+ */
28
+ interface OverrideUserPair {
29
+ projectId: ObjectID;
30
+ overrideUserId: ObjectID;
31
+ routeAlertsToUserId?: ObjectID | undefined;
32
+ }
33
+
34
+ /*
35
+ * The subset of OnCallDutyPolicyScheduleService the override hooks call.
36
+ * Lazily required (see refreshRostersForOverrideUser) because that service
37
+ * imports this one.
38
+ */
39
+ interface ScheduleServiceForOverrides {
40
+ refreshRostersForUserInProject: (d: {
41
+ projectId: ObjectID;
42
+ userId: ObjectID;
43
+ }) => Promise<void>;
44
+ getScheduleIdsForUsersInProject: (d: {
45
+ projectId: ObjectID;
46
+ userIds: Array<ObjectID>;
47
+ }) => Promise<Array<ObjectID>>;
48
+ propagateShiftConfigChange: (d: {
49
+ scheduleIds: Array<ObjectID>;
50
+ projectId?: ObjectID | null | undefined;
51
+ userIds?: Array<ObjectID> | undefined;
52
+ reason: OnCallShiftChangeReason;
53
+ }) => Promise<void>;
54
+ }
21
55
 
22
56
  export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
23
57
  public constructor() {
24
58
  super(OnCallDutyPolicyUserOverride);
25
59
  }
26
60
 
61
+ private getScheduleService(): ScheduleServiceForOverrides {
62
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
63
+ return require("./OnCallDutyPolicyScheduleService").default;
64
+ }
65
+
66
+ /**
67
+ * An override changes who is on call on every schedule that contains the
68
+ * overridden user, and it changes the SUBSTITUTE's personal calendar too.
69
+ * Find those schedules with the same layer-user query the roster refresh
70
+ * uses (for every overridden user, old and new), then bump their
71
+ * shiftConfigVersion, drop the feed caches — the substitutes' personal
72
+ * caches included — and let the shift-change listeners re-plan reminders.
73
+ * One call per project; best-effort, never throws into the CRUD path.
74
+ */
75
+ private async propagateShiftChangeForOverrides(
76
+ pairs: Array<OverrideUserPair>,
77
+ ): Promise<void> {
78
+ const byProject: Map<
79
+ string,
80
+ {
81
+ projectId: ObjectID;
82
+ overrideUserIds: Array<ObjectID>;
83
+ allUserIds: Array<ObjectID>;
84
+ }
85
+ > = new Map();
86
+
87
+ for (const pair of pairs) {
88
+ if (!pair.projectId || !pair.overrideUserId) {
89
+ continue;
90
+ }
91
+
92
+ const key: string = pair.projectId.toString();
93
+ const entry: {
94
+ projectId: ObjectID;
95
+ overrideUserIds: Array<ObjectID>;
96
+ allUserIds: Array<ObjectID>;
97
+ } = byProject.get(key) || {
98
+ projectId: pair.projectId,
99
+ overrideUserIds: [],
100
+ allUserIds: [],
101
+ };
102
+
103
+ entry.overrideUserIds.push(pair.overrideUserId);
104
+ entry.allUserIds.push(pair.overrideUserId);
105
+
106
+ if (pair.routeAlertsToUserId) {
107
+ entry.allUserIds.push(pair.routeAlertsToUserId);
108
+ }
109
+
110
+ byProject.set(key, entry);
111
+ }
112
+
113
+ for (const entry of byProject.values()) {
114
+ try {
115
+ const scheduleService: ScheduleServiceForOverrides =
116
+ this.getScheduleService();
117
+
118
+ const scheduleIds: Array<ObjectID> =
119
+ await scheduleService.getScheduleIdsForUsersInProject({
120
+ projectId: entry.projectId,
121
+ userIds: entry.overrideUserIds,
122
+ });
123
+
124
+ await scheduleService.propagateShiftConfigChange({
125
+ scheduleIds,
126
+ projectId: entry.projectId,
127
+ userIds: entry.allUserIds,
128
+ reason: OnCallShiftChangeReason.OverrideChanged,
129
+ });
130
+ } catch (err) {
131
+ logger.error(
132
+ "Error propagating a user override change to the calendar feeds (best-effort).",
133
+ );
134
+ logger.error(err);
135
+ }
136
+ }
137
+ }
138
+
27
139
  /**
28
140
  * Re-resolve the persisted roster of every schedule in the project that
29
141
  * contains the override user, so a created / updated / deleted override is
@@ -41,13 +153,8 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
41
153
  }
42
154
 
43
155
  try {
44
- const scheduleService: {
45
- refreshRostersForUserInProject: (d: {
46
- projectId: ObjectID;
47
- userId: ObjectID;
48
- }) => Promise<void>;
49
- // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
50
- } = require("./OnCallDutyPolicyScheduleService").default;
156
+ const scheduleService: ScheduleServiceForOverrides =
157
+ this.getScheduleService();
51
158
 
52
159
  await scheduleService.refreshRostersForUserInProject({
53
160
  projectId: data.projectId,
@@ -190,6 +297,16 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
190
297
  overrideUserId,
191
298
  });
192
299
 
300
+ if (projectId && overrideUserId) {
301
+ await this.propagateShiftChangeForOverrides([
302
+ {
303
+ projectId,
304
+ overrideUserId,
305
+ routeAlertsToUserId: routeAlertsToUserId || undefined,
306
+ },
307
+ ]);
308
+ }
309
+
193
310
  return createdItem;
194
311
  }
195
312
 
@@ -207,10 +324,7 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
207
324
  * schedules by the NEW user only (audit M2). Mirrors the onBeforeDelete
208
325
  * carry-forward pattern used by the delete path.
209
326
  */
210
- const previous: Array<{
211
- projectId: ObjectID;
212
- overrideUserId: ObjectID;
213
- }> = [];
327
+ const previous: Array<OverrideUserPair> = [];
214
328
 
215
329
  try {
216
330
  const rows: Array<OnCallDutyPolicyUserOverride> = await this.findBy({
@@ -218,6 +332,7 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
218
332
  select: {
219
333
  projectId: true,
220
334
  overrideUserId: true,
335
+ routeAlertsToUserId: true,
221
336
  },
222
337
  props: {
223
338
  isRoot: true,
@@ -231,8 +346,14 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
231
346
  row.projectId || row.project?.id;
232
347
  const overrideUserId: ObjectID | undefined | null =
233
348
  row.overrideUserId || row.overrideUser?.id;
349
+ const routeAlertsToUserId: ObjectID | undefined | null =
350
+ row.routeAlertsToUserId || row.routeAlertsToUser?.id;
234
351
  if (projectId && overrideUserId) {
235
- previous.push({ projectId, overrideUserId });
352
+ previous.push({
353
+ projectId,
354
+ overrideUserId,
355
+ routeAlertsToUserId: routeAlertsToUserId || undefined,
356
+ });
236
357
  }
237
358
  }
238
359
  } catch (err) {
@@ -279,11 +400,11 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
279
400
  };
280
401
 
281
402
  // OLD override users captured before the update.
282
- const previous: Array<{ projectId: ObjectID; overrideUserId: ObjectID }> =
283
- (onUpdate.carryForward as Array<{
284
- projectId: ObjectID;
285
- overrideUserId: ObjectID;
286
- }>) || [];
403
+ const previous: Array<OverrideUserPair> =
404
+ (onUpdate.carryForward as Array<OverrideUserPair>) || [];
405
+
406
+ // Everyone the edit touched, old and new, for the feed / reminder pass.
407
+ const affectedPairs: Array<OverrideUserPair> = [...previous];
287
408
 
288
409
  for (const entry of previous) {
289
410
  await refreshPair(entry.projectId, entry.overrideUserId);
@@ -296,6 +417,7 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
296
417
  select: {
297
418
  projectId: true,
298
419
  overrideUserId: true,
420
+ routeAlertsToUserId: true,
299
421
  },
300
422
  props: {
301
423
  isRoot: true,
@@ -306,12 +428,26 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
306
428
  continue;
307
429
  }
308
430
 
309
- await refreshPair(
310
- item.projectId || item.project?.id,
311
- item.overrideUserId || item.overrideUser?.id,
312
- );
431
+ const newProjectId: ObjectID | undefined | null =
432
+ item.projectId || item.project?.id;
433
+ const newOverrideUserId: ObjectID | undefined | null =
434
+ item.overrideUserId || item.overrideUser?.id;
435
+ const newRouteAlertsToUserId: ObjectID | undefined | null =
436
+ item.routeAlertsToUserId || item.routeAlertsToUser?.id;
437
+
438
+ await refreshPair(newProjectId, newOverrideUserId);
439
+
440
+ if (newProjectId && newOverrideUserId) {
441
+ affectedPairs.push({
442
+ projectId: newProjectId,
443
+ overrideUserId: newOverrideUserId,
444
+ routeAlertsToUserId: newRouteAlertsToUserId || undefined,
445
+ });
446
+ }
313
447
  }
314
448
 
449
+ await this.propagateShiftChangeForOverrides(affectedPairs);
450
+
315
451
  return onUpdate;
316
452
  }
317
453
 
@@ -326,11 +462,8 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
326
462
  * than waiting for the next natural handoff (audit F4). The affected
327
463
  * project/user pairs were captured in onBeforeDelete (the rows are gone now).
328
464
  */
329
- const affected: Array<{ projectId: ObjectID; overrideUserId: ObjectID }> =
330
- (onDelete.carryForward as Array<{
331
- projectId: ObjectID;
332
- overrideUserId: ObjectID;
333
- }>) || [];
465
+ const affected: Array<OverrideUserPair> =
466
+ (onDelete.carryForward as Array<OverrideUserPair>) || [];
334
467
 
335
468
  for (const entry of affected) {
336
469
  await this.refreshRostersForOverrideUser({
@@ -339,6 +472,8 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
339
472
  });
340
473
  }
341
474
 
475
+ await this.propagateShiftChangeForOverrides(affected);
476
+
342
477
  return onDelete;
343
478
  }
344
479
 
@@ -368,10 +503,7 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
368
503
  * onDeleteSuccess can refresh those schedules' rosters AFTER the rows are
369
504
  * gone (audit F4). Collected for global overrides too (no policy id).
370
505
  */
371
- const affectedRosters: Array<{
372
- projectId: ObjectID;
373
- overrideUserId: ObjectID;
374
- }> = [];
506
+ const affectedRosters: Array<OverrideUserPair> = [];
375
507
 
376
508
  for (const item of itemsToDelete) {
377
509
  const onCallDutyPolicyId: ObjectID | undefined | null =
@@ -387,7 +519,11 @@ export class Service extends DatabaseService<OnCallDutyPolicyUserOverride> {
387
519
  item.routeAlertsToUserId || item.routeAlertsToUser?.id;
388
520
 
389
521
  if (projectId && overrideUserId) {
390
- affectedRosters.push({ projectId, overrideUserId });
522
+ affectedRosters.push({
523
+ projectId,
524
+ overrideUserId,
525
+ routeAlertsToUserId: routeAlertsToUserId || undefined,
526
+ });
391
527
  }
392
528
 
393
529
  if (
@@ -0,0 +1,192 @@
1
+ import CreateBy from "../Types/Database/CreateBy";
2
+ import UpdateBy from "../Types/Database/UpdateBy";
3
+ import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
4
+ import DatabaseService from "./DatabaseService";
5
+ import {
6
+ MAX_MINIMUM_GAP_MINUTES,
7
+ MIN_MINIMUM_GAP_MINUTES,
8
+ } from "./OnCallDutyPolicyScheduleCalendarFeedService";
9
+ import CalendarFeedToken, {
10
+ CalendarFeedRotation,
11
+ } from "../Utils/OnCall/CalendarFeedToken";
12
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
13
+ import Model from "../../Models/DatabaseModels/ProjectOnCallCalendarFeed";
14
+ import BadDataException from "../../Types/Exception/BadDataException";
15
+ import NotFoundException from "../../Types/Exception/NotFoundException";
16
+ import CalendarFeedWindow from "../../Types/OnCallDutyPolicy/CalendarFeedWindow";
17
+ import ObjectID from "../../Types/ObjectID";
18
+ import PositiveNumber from "../../Types/PositiveNumber";
19
+
20
+ /*
21
+ * The project-wide shared calendar feed: at most one row per project.
22
+ *
23
+ * The same guarantees as OnCallDutyPolicyScheduleCalendarFeedService minus
24
+ * the schedule check: one feed per project (unique index + a readable
25
+ * message), the token minted here on every create whatever the request
26
+ * carried, window and gap settings bounded on every write, root-only
27
+ * rotation with the 30-day grace hash.
28
+ */
29
+ export class Service extends DatabaseService<Model> {
30
+ public constructor() {
31
+ super(Model);
32
+ }
33
+
34
+ @CaptureSpan()
35
+ protected override async onBeforeCreate(
36
+ createBy: CreateBy<Model>,
37
+ ): Promise<OnCreate<Model>> {
38
+ const projectId: ObjectID | undefined =
39
+ createBy.data.projectId || createBy.props.tenantId;
40
+
41
+ if (!projectId) {
42
+ throw new BadDataException("projectId is required");
43
+ }
44
+
45
+ createBy.data.projectId = projectId;
46
+
47
+ const existing: PositiveNumber = await this.countBy({
48
+ query: {
49
+ projectId: projectId,
50
+ },
51
+ props: {
52
+ isRoot: true,
53
+ },
54
+ });
55
+
56
+ if (existing.toNumber() > 0) {
57
+ throw new BadDataException(
58
+ "This project already has a shared calendar feed. Regenerate or edit the existing one instead.",
59
+ );
60
+ }
61
+
62
+ // A request body never chooses the secret; only root may pre-mint one.
63
+ CalendarFeedToken.applyTokenColumnsOnCreate(createBy.data, {
64
+ trustSuppliedToken: createBy.props.isRoot === true,
65
+ });
66
+
67
+ createBy.data.pastDays = CalendarFeedWindow.clampPastDays(
68
+ createBy.data.pastDays,
69
+ );
70
+ createBy.data.futureDays = CalendarFeedWindow.clampFutureDays(
71
+ createBy.data.futureDays,
72
+ );
73
+
74
+ if (createBy.data.minimumGapMinutes !== undefined) {
75
+ createBy.data.minimumGapMinutes = Service.validateMinimumGapMinutes(
76
+ createBy.data.minimumGapMinutes,
77
+ );
78
+ }
79
+
80
+ return { createBy, carryForward: null };
81
+ }
82
+
83
+ @CaptureSpan()
84
+ protected override async onBeforeUpdate(
85
+ updateBy: UpdateBy<Model>,
86
+ ): Promise<OnUpdate<Model>> {
87
+ if (updateBy.data.pastDays !== undefined) {
88
+ updateBy.data.pastDays = CalendarFeedWindow.clampPastDays(
89
+ updateBy.data.pastDays,
90
+ );
91
+ }
92
+
93
+ if (updateBy.data.futureDays !== undefined) {
94
+ updateBy.data.futureDays = CalendarFeedWindow.clampFutureDays(
95
+ updateBy.data.futureDays,
96
+ );
97
+ }
98
+
99
+ if (updateBy.data.minimumGapMinutes !== undefined) {
100
+ updateBy.data.minimumGapMinutes = Service.validateMinimumGapMinutes(
101
+ updateBy.data.minimumGapMinutes,
102
+ );
103
+ }
104
+
105
+ return { updateBy, carryForward: null };
106
+ }
107
+
108
+ public static validateMinimumGapMinutes(value: unknown): number {
109
+ const parsed: number =
110
+ typeof value === "string" && value.trim() !== ""
111
+ ? Number(value)
112
+ : (value as number);
113
+
114
+ if (
115
+ typeof parsed !== "number" ||
116
+ !Number.isInteger(parsed) ||
117
+ parsed < MIN_MINIMUM_GAP_MINUTES ||
118
+ parsed > MAX_MINIMUM_GAP_MINUTES
119
+ ) {
120
+ throw new BadDataException(
121
+ `Minimum gap must be a whole number of minutes between ${MIN_MINIMUM_GAP_MINUTES} and ${MAX_MINIMUM_GAP_MINUTES}.`,
122
+ );
123
+ }
124
+
125
+ return parsed;
126
+ }
127
+
128
+ /**
129
+ * Rotate the token of an existing feed. Returns the new plaintext token
130
+ * ONCE. Root write; the caller purges caches.
131
+ */
132
+ @CaptureSpan()
133
+ public async rotateTokenById(data: {
134
+ id: ObjectID;
135
+ }): Promise<CalendarFeedRotation> {
136
+ const existing: Model | null = await this.findOneBy({
137
+ query: { _id: data.id },
138
+ select: { _id: true, tokenHash: true },
139
+ props: { isRoot: true, ignoreHooks: true },
140
+ });
141
+
142
+ if (!existing) {
143
+ throw new NotFoundException("Calendar feed not found.");
144
+ }
145
+
146
+ const rotation: CalendarFeedRotation = CalendarFeedToken.buildRotation({
147
+ currentTokenHash: existing.tokenHash,
148
+ });
149
+
150
+ await this.updateOneById({
151
+ id: data.id,
152
+ data: CalendarFeedToken.toRotationUpdateData(rotation),
153
+ props: { isRoot: true },
154
+ });
155
+
156
+ return rotation;
157
+ }
158
+
159
+ /**
160
+ * Rotate the project's feed when a member leaves, if it is enabled and
161
+ * opted in. Returns the rotated id (at most one) for cache purging.
162
+ */
163
+ @CaptureSpan()
164
+ public async rotateFeedsForMemberLeave(data: {
165
+ projectId: ObjectID;
166
+ }): Promise<Array<ObjectID>> {
167
+ const feed: Model | null = await this.findOneBy({
168
+ query: {
169
+ projectId: data.projectId,
170
+ isEnabled: true,
171
+ rotateWhenMemberLeaves: true,
172
+ },
173
+ select: {
174
+ _id: true,
175
+ },
176
+ props: {
177
+ isRoot: true,
178
+ ignoreHooks: true,
179
+ },
180
+ });
181
+
182
+ if (!feed || !feed.id) {
183
+ return [];
184
+ }
185
+
186
+ await this.rotateTokenById({ id: feed.id });
187
+
188
+ return [feed.id];
189
+ }
190
+ }
191
+
192
+ export default new Service();
@@ -20,10 +20,24 @@ import Telemetry, { Span } from "../Utils/Telemetry";
20
20
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
21
21
  import { StatusPageCNameRecord } from "../EnvironmentConfig";
22
22
  import Domain from "../Types/Domain";
23
+ import ArrayUtil from "../../Utils/Array";
24
+ import OneUptimeDate from "../../Types/Date";
25
+ import CertificateReissueUtil from "../../Utils/CertificateReissue";
26
+ import TooManyRequestsException from "../../Types/Exception/TooManyRequestsException";
27
+ import QueryHelper from "../Types/Database/QueryHelper";
23
28
 
24
29
  const STATUS_PAGE_DOMAIN_EGRESS_LABEL: string = "Status page domain";
25
30
 
26
31
  export class Service extends DatabaseService<StatusPageDomain> {
32
+ /*
33
+ * How many status pages the provisioning sweep checks at once. Each check is
34
+ * one bounded request out to a customer domain, so this is about not letting
35
+ * the sweep's length grow with the size of the fleet, rather than about local
36
+ * work. Kept modest so a sweep cannot look like a burst of traffic to any
37
+ * shared infrastructure sitting in front of those domains.
38
+ */
39
+ public static readonly SSL_PROVISIONING_CHECK_CONCURRENCY: number = 10;
40
+
27
41
  public constructor() {
28
42
  super(StatusPageDomain);
29
43
  }
@@ -248,6 +262,133 @@ export class Service extends DatabaseService<StatusPageDomain> {
248
262
  });
249
263
  }
250
264
 
265
+ /*
266
+ * Reissue this domain's Let's Encrypt certificate because a customer asked
267
+ * for it, rather than because it is close to expiring.
268
+ *
269
+ * Every order spends from a Let's Encrypt account that is shared by the
270
+ * whole installation - including by the cron that keeps everybody else's
271
+ * certificates alive - so this is throttled to one request per domain per
272
+ * CertificateReissueUtil.COOLDOWN_IN_HOURS.
273
+ *
274
+ * Throws with a customer-readable reason for every refusal, so the API layer
275
+ * can hand the message straight back without classifying the failure:
276
+ * BadDataException for a domain that is not eligible at all, and
277
+ * TooManyRequestsException (429) for one that is simply too soon.
278
+ */
279
+ @CaptureSpan()
280
+ public async reissueCert(domainId: ObjectID): Promise<void> {
281
+ const now: Date = OneUptimeDate.getCurrentDate();
282
+
283
+ const statusPageDomain: StatusPageDomain | null = await this.findOneBy({
284
+ query: {
285
+ _id: domainId.toString(),
286
+ },
287
+ select: {
288
+ _id: true,
289
+ fullDomain: true,
290
+ isCnameVerified: true,
291
+ isSslOrdered: true,
292
+ isCustomCertificate: true,
293
+ certificateReissueRequestedAt: true,
294
+ },
295
+ props: {
296
+ isRoot: true,
297
+ },
298
+ });
299
+
300
+ if (!statusPageDomain) {
301
+ throw new BadDataException("Domain not found.");
302
+ }
303
+
304
+ if (!statusPageDomain.fullDomain) {
305
+ throw new BadDataException(
306
+ "Unable to reissue certificate because domain is null",
307
+ );
308
+ }
309
+
310
+ /*
311
+ * Nothing to reissue: the certificate on a custom-certificate domain is
312
+ * the one the customer uploaded, and we have no way to obtain another.
313
+ */
314
+ if (statusPageDomain.isCustomCertificate) {
315
+ throw new BadDataException(
316
+ "This domain uses a certificate you uploaded yourself, so there is no Let's Encrypt certificate to reissue. Please edit this domain and upload a new certificate instead.",
317
+ );
318
+ }
319
+
320
+ if (!statusPageDomain.isCnameVerified) {
321
+ throw new BadDataException(
322
+ "CNAME is not verified. Please verify CNAME first before you reissue the SSL certificate.",
323
+ );
324
+ }
325
+
326
+ /*
327
+ * A domain that never ordered a certificate has nothing to reissue - the
328
+ * dashboard shows "Order Free SSL" for it, which is the correct button.
329
+ */
330
+ if (!statusPageDomain.isSslOrdered) {
331
+ throw new BadDataException(
332
+ "No SSL certificate has been ordered for this domain yet. Please order one first.",
333
+ );
334
+ }
335
+
336
+ /*
337
+ * Claim the cooldown before ordering anything, and claim it by putting the
338
+ * cooldown condition in the WHERE clause rather than in an `if` above the
339
+ * write. Two clicks that arrive together both read a row that is not
340
+ * cooling down; only the write can decide which one of them gets to spend
341
+ * the CA's allowance.
342
+ *
343
+ * The stamp goes down BEFORE the order and is never rolled back on
344
+ * failure. A rejected order still costs a validation attempt against the
345
+ * shared account, and a domain whose order fails is exactly the domain
346
+ * somebody presses again - so a failed attempt has to start the clock the
347
+ * same way a successful one does.
348
+ */
349
+ const claimedRowCount: number = await this.updateOneBy({
350
+ query: {
351
+ _id: domainId.toString(),
352
+ certificateReissueRequestedAt: QueryHelper.lessThanEqualToOrNull(
353
+ CertificateReissueUtil.getCooldownCutoff(now),
354
+ ),
355
+ },
356
+ data: {
357
+ certificateReissueRequestedAt: now,
358
+ },
359
+ props: {
360
+ isRoot: true,
361
+ },
362
+ });
363
+
364
+ if (claimedRowCount === 0) {
365
+ if (statusPageDomain.certificateReissueRequestedAt) {
366
+ throw new TooManyRequestsException(
367
+ CertificateReissueUtil.getCooldownMessage(
368
+ statusPageDomain.certificateReissueRequestedAt,
369
+ now,
370
+ ),
371
+ );
372
+ }
373
+
374
+ /*
375
+ * The row matched the cooldown a moment ago and does not now: another
376
+ * request claimed it in between, or the domain was deleted. Either way
377
+ * the caller must not order.
378
+ */
379
+ throw new BadDataException(
380
+ "Could not start a certificate reissue for this domain. Please refresh the page and try again.",
381
+ );
382
+ }
383
+
384
+ logger.debug(
385
+ "Reissuing SSL certificate for domain: " + statusPageDomain.fullDomain,
386
+ { fullDomain: statusPageDomain.fullDomain } as LogAttributes,
387
+ );
388
+
389
+ await this.orderCert(statusPageDomain);
390
+ }
391
+
251
392
  @CaptureSpan()
252
393
  public async updateSslProvisioningStatusForAllDomains(): Promise<void> {
253
394
  const domains: Array<StatusPageDomain> = await this.findBy({
@@ -257,6 +398,7 @@ export class Service extends DatabaseService<StatusPageDomain> {
257
398
  },
258
399
  select: {
259
400
  _id: true,
401
+ fullDomain: true,
260
402
  },
261
403
  limit: LIMIT_MAX,
262
404
  skip: 0,
@@ -265,9 +407,27 @@ export class Service extends DatabaseService<StatusPageDomain> {
265
407
  },
266
408
  });
267
409
 
268
- for (const domain of domains) {
269
- await this.updateSslProvisioningStatus(domain);
270
- }
410
+ /*
411
+ * Each domain costs a request out to the customer's status page, so walking
412
+ * the fleet one at a time makes the sweep as long as the fleet is slow.
413
+ * That length is the delay before an expired certificate is noticed and
414
+ * reordered, so it is the outage a customer sees when renewal has not
415
+ * already happened - it must stay well inside the sweep's own interval.
416
+ */
417
+ await ArrayUtil.forEachWithConcurrency(
418
+ domains,
419
+ Service.SSL_PROVISIONING_CHECK_CONCURRENCY,
420
+ async (domain: StatusPageDomain): Promise<void> => {
421
+ try {
422
+ await this.updateSslProvisioningStatus(domain);
423
+ } catch (err) {
424
+ // one unreachable domain must not end the sweep for the rest.
425
+ logger.error(err, {
426
+ fullDomain: domain.fullDomain,
427
+ } as LogAttributes);
428
+ }
429
+ },
430
+ );
271
431
  }
272
432
 
273
433
  private async isSSLProvisioned(