@oneuptime/common 12.0.6 → 12.0.7

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 (424) hide show
  1. package/Models/DatabaseModels/AIInsight.ts +1 -1
  2. package/Models/DatabaseModels/DatabaseBaseModel/FileModel.ts +2 -3
  3. package/Models/DatabaseModels/DeletedProject.ts +595 -0
  4. package/Models/DatabaseModels/Index.ts +2 -0
  5. package/Models/DatabaseModels/Monitor.ts +111 -0
  6. package/Models/DatabaseModels/Project.ts +3 -3
  7. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -1
  8. package/Server/API/FileAPI.ts +15 -2
  9. package/Server/API/MicrosoftTeamsAPI.ts +6 -1
  10. package/Server/API/ProjectAPI.ts +214 -0
  11. package/Server/API/StatusPageAPI.ts +39 -6
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.ts +50 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.ts +67 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.ts +39 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  16. package/Server/Services/AIAgentService.ts +20 -0
  17. package/Server/Services/DashboardDomainService.ts +73 -28
  18. package/Server/Services/DatabaseService.ts +1 -0
  19. package/Server/Services/DeletedProjectService.ts +138 -0
  20. package/Server/Services/FileService.ts +61 -0
  21. package/Server/Services/IncidentEpisodePublicNoteService.ts +18 -0
  22. package/Server/Services/IncidentPublicNoteService.ts +18 -0
  23. package/Server/Services/Index.ts +2 -0
  24. package/Server/Services/MonitorService.ts +311 -0
  25. package/Server/Services/ProbeService.ts +20 -0
  26. package/Server/Services/ProjectService.ts +297 -1
  27. package/Server/Services/PushNotificationService.ts +71 -0
  28. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +18 -0
  29. package/Server/Services/StatusPageAnnouncementService.ts +53 -0
  30. package/Server/Services/StatusPageDomainService.ts +73 -28
  31. package/Server/Services/TeamMemberService.ts +117 -12
  32. package/Server/Services/TelemetryIngestionKeyService.ts +1 -0
  33. package/Server/Services/UserWebhookService.ts +34 -109
  34. package/Server/Services/WorkspaceNotificationRuleService.ts +69 -0
  35. package/Server/Services/WorkspaceProjectAuthTokenService.ts +37 -1
  36. package/Server/Types/Database/DeleteById.ts +2 -0
  37. package/Server/Types/Database/DeleteOneBy.ts +6 -0
  38. package/Server/Types/Domain.ts +41 -0
  39. package/Server/Types/Workflow/Components/API/Delete.ts +2 -1
  40. package/Server/Types/Workflow/Components/API/Get.ts +2 -1
  41. package/Server/Types/Workflow/Components/API/Patch.ts +2 -1
  42. package/Server/Types/Workflow/Components/API/Post.ts +2 -1
  43. package/Server/Types/Workflow/Components/API/Put.ts +2 -1
  44. package/Server/Types/Workflow/Components/API/Utils.ts +41 -2
  45. package/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.ts +25 -19
  46. package/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.ts +6 -5
  47. package/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.ts +32 -14
  48. package/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.ts +31 -13
  49. package/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.ts +29 -20
  50. package/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.ts +28 -21
  51. package/Server/Types/Workflow/Components/BaseModel/LogComponentError.ts +89 -0
  52. package/Server/Types/Workflow/Components/BaseModel/ModelArguments.ts +208 -0
  53. package/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.ts +11 -0
  54. package/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.ts +41 -18
  55. package/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.ts +45 -19
  56. package/Server/Types/Workflow/Components/Discord/SendMessageToChannel.ts +16 -9
  57. package/Server/Types/Workflow/Components/IncomingWebhookUtils.ts +66 -0
  58. package/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.ts +15 -9
  59. package/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts +17 -0
  60. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
  61. package/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.ts +171 -0
  62. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +3 -3
  63. package/Server/Utils/AI/SRE/Insights/FixRouting.ts +3 -2
  64. package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +1 -1
  65. package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +6 -6
  66. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +43 -5
  67. package/Server/Utils/DataSource/EgressGuard.ts +176 -16
  68. package/Server/Utils/DataSource/HttpFetch.ts +6 -45
  69. package/Server/Utils/Execute.ts +8 -0
  70. package/Server/Utils/InlineImageAccessTokenSync.ts +23 -0
  71. package/Server/Utils/LLM/LLMService.ts +57 -12
  72. package/Server/Utils/Monitor/MonitorAlert.ts +40 -0
  73. package/Server/Utils/Monitor/MonitorDependencySuppression.ts +192 -0
  74. package/Server/Utils/Monitor/MonitorIncident.ts +40 -0
  75. package/Server/Utils/Monitor/MonitorResource.ts +36 -0
  76. package/Server/Utils/Response.ts +142 -6
  77. package/Server/Utils/SSRFProtection.ts +273 -60
  78. package/Server/Utils/StartServer.ts +8 -0
  79. package/Server/Utils/VM/VMRunner.ts +88 -1
  80. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +555 -49
  81. package/Server/Utils/Workspace/Slack/Slack.ts +25 -2
  82. package/Server/Utils/Workspace/WorkspaceBase.ts +6 -0
  83. package/Tests/App/Dashboard/DangerZone.test.tsx +334 -0
  84. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +539 -0
  85. package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +678 -0
  86. package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +706 -0
  87. package/Tests/App/Dashboard/DashboardVariablesModal.test.tsx +508 -0
  88. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +470 -0
  89. package/Tests/App/StatusPage/StatusPageDetailPagesNotFound.test.tsx +308 -0
  90. package/Tests/Models/DatabaseModels/DeletedProjectModel.test.ts +251 -0
  91. package/Tests/Models/DatabaseModels/SavedViewJSONColumnTuples.test.ts +115 -0
  92. package/Tests/Server/API/FileAPIAccessControl.test.ts +316 -0
  93. package/Tests/Server/API/ProjectAPI.test.ts +299 -1
  94. package/Tests/Server/API/ProjectAPIAdjustBalance.test.ts +459 -0
  95. package/Tests/Server/API/ProjectAPIAdjustBalanceBillingDisabled.test.ts +177 -0
  96. package/Tests/Server/Services/DatabaseServiceDeletionReason.test.ts +123 -0
  97. package/Tests/Server/Services/DeletedProjectService.test.ts +415 -0
  98. package/Tests/Server/Services/DeletedProjectServiceSelfHosted.test.ts +70 -0
  99. package/Tests/Server/Services/DomainSubdomainHooks.test.ts +94 -0
  100. package/Tests/Server/Services/FileIsPublicBooleanMigration.test.ts +244 -0
  101. package/Tests/Server/Services/FileServiceMimeType.test.ts +89 -0
  102. package/Tests/Server/Services/MonitorServiceDependencyValidation.test.ts +702 -0
  103. package/Tests/Server/Services/ProjectServiceAdjustBalance.test.ts +615 -0
  104. package/Tests/Server/Services/ProjectServiceDeleteAudit.test.ts +406 -0
  105. package/Tests/Server/Services/PublishTimeImageVisibility.test.ts +450 -0
  106. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +669 -0
  107. package/Tests/Server/Services/UserWebhookSSRF.test.ts +174 -0
  108. package/Tests/Server/Services/WebPushEndpointAllowlist.test.ts +133 -0
  109. package/Tests/Server/Services/WorkspaceProjectAuthTokenRepoint.test.ts +618 -0
  110. package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +26 -1
  111. package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +215 -0
  112. package/Tests/Server/Types/Workflow/Components/BaseModelDatabaseComponents.test.ts +742 -0
  113. package/Tests/Server/Types/Workflow/Components/ChatWebhookComponents.test.ts +256 -0
  114. package/Tests/Server/Types/Workflow/Components/ModelArguments.test.ts +371 -0
  115. package/Tests/Server/Utils/AI/CodeAgentCommandGuard.test.ts +204 -0
  116. package/Tests/Server/Utils/AI/CodeAgentWriteLimits.test.ts +213 -0
  117. package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +52 -5
  118. package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +3 -0
  119. package/Tests/Server/Utils/AI/LLMServiceEgressGuard.test.ts +315 -0
  120. package/Tests/Server/Utils/AI/LLMServiceModelCompatibility.test.ts +3 -0
  121. package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +17 -5
  122. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +14 -1
  123. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +35 -0
  124. package/Tests/Server/Utils/AI/StubLLMEgressGuard.ts +25 -0
  125. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +62 -7
  126. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +69 -0
  127. package/Tests/Server/Utils/DataSource/EgressGuardPinning.test.ts +330 -0
  128. package/Tests/Server/Utils/GitHubCreatePullRequestWithToken.test.ts +393 -0
  129. package/Tests/Server/Utils/InlineImageAccessTokenSync.test.ts +202 -0
  130. package/Tests/Server/Utils/Monitor/MonitorDependencySuppression.test.ts +811 -0
  131. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +604 -0
  132. package/Tests/Server/Utils/ResponseSendFileResponse.test.ts +192 -0
  133. package/Tests/Server/Utils/SSRFProtectionBypasses.test.ts +406 -0
  134. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +194 -0
  135. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +1169 -0
  136. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +2 -0
  137. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +1605 -0
  138. package/Tests/Server/Utils/Workspace/MicrosoftTeamsTenantResolution.test.ts +1218 -0
  139. package/Tests/Types/AI/AIChatMessageStatus.test.ts +79 -0
  140. package/Tests/Types/AI/AIInsightStatus.test.ts +40 -0
  141. package/Tests/Types/API/HostnameAuthorityParsing.test.ts +157 -0
  142. package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +27 -0
  143. package/Tests/Types/DataSource/DataSourceLimits.test.ts +71 -0
  144. package/Tests/Types/DataSource/DataSourceType.test.ts +181 -0
  145. package/Tests/Types/DomainSubdomainValidation.test.ts +90 -0
  146. package/Tests/Types/JSONFunctions.test.ts +252 -1
  147. package/Tests/Types/Monitor/SnmpMonitor/SnmpAuthProtocol.test.ts +61 -0
  148. package/Tests/Types/Monitor/SnmpMonitor/SnmpPrivProtocol.test.ts +62 -0
  149. package/Tests/Types/Monitor/SnmpMonitor/SnmpSecurityLevel.test.ts +68 -0
  150. package/Tests/Types/Monitor/SnmpMonitor/SnmpVersion.test.ts +51 -0
  151. package/Tests/UI/Components/404.test.tsx +1 -1
  152. package/Tests/UI/Components/Alert.test.tsx +1 -1
  153. package/Tests/UI/Components/Badge.test.tsx +1 -1
  154. package/Tests/UI/Components/BasicForm.test.tsx +39 -10
  155. package/Tests/UI/Components/Button.test.tsx +1 -1
  156. package/Tests/UI/Components/Card.test.tsx +1 -1
  157. package/Tests/UI/Components/Charts/AxisTickColor.test.tsx +1 -1
  158. package/Tests/UI/Components/ColorViewer.test.tsx +1 -1
  159. package/Tests/UI/Components/ComponentsModal.test.tsx +7 -1
  160. package/Tests/UI/Components/DictionaryOfStrings.test.tsx +1 -1
  161. package/Tests/UI/Components/Dropdown.test.tsx +1 -1
  162. package/Tests/UI/Components/FilePicker.test.tsx +1 -1
  163. package/Tests/UI/Components/HiddenText.test.tsx +1 -1
  164. package/Tests/UI/Components/Input.test.tsx +1 -1
  165. package/Tests/UI/Components/Loader.test.tsx +1 -1
  166. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +1 -1
  167. package/Tests/UI/Components/MasterPage.test.tsx +1 -1
  168. package/Tests/UI/Components/ModalPortalDismissal.test.tsx +489 -0
  169. package/Tests/UI/Components/ModelDelete.test.tsx +214 -0
  170. package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1 -1
  171. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +1 -1
  172. package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +1 -1
  173. package/Tests/UI/Components/ModelTable/BaseModelTableUrlState.test.tsx +1 -1
  174. package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1 -1
  175. package/Tests/UI/Components/NavBar.test.tsx +1 -1
  176. package/Tests/UI/Components/OperatorSelectorKeyboard.test.tsx +1 -1
  177. package/Tests/UI/Components/Pagination.test.tsx +1 -1
  178. package/Tests/UI/Components/Pill.test.tsx +1 -1
  179. package/Tests/UI/Components/Probe.test.tsx +1 -1
  180. package/Tests/UI/Components/ProgressBar.test.tsx +1 -1
  181. package/Tests/UI/Components/RadioButtons.test.tsx +1 -1
  182. package/Tests/UI/Components/SideMenuItem.test.tsx +1 -1
  183. package/Tests/UI/Components/SideOver.test.tsx +500 -6
  184. package/Tests/UI/Components/Tabs.test.tsx +1 -1
  185. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +1 -1
  186. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +1 -1
  187. package/Tests/UI/Components/TextArea.test.tsx +1 -1
  188. package/Tests/UI/Components/Toast.test.tsx +1 -1
  189. package/Tests/UI/Components/Toggle.test.tsx +1 -1
  190. package/Tests/UI/Components/TopSection.test.tsx +1 -1
  191. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +1 -1
  192. package/Tests/UI/Rum/ReplayScrubber.test.tsx +1 -1
  193. package/Tests/UI/Rum/ReplayStage.test.tsx +1 -1
  194. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +1 -1
  195. package/Tests/UI/Utils/LocalStorageArrayRoundTrip.test.ts +72 -0
  196. package/Tests/Utils/Dashboard/HtmlWidgetDocument.test.ts +19 -2
  197. package/Tests/Utils/Dashboard/ModelQueryVariableInterpolation.test.ts +44 -0
  198. package/Tests/Utils/Dashboard/VariableInterpolation.test.ts +288 -5
  199. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +42 -0
  200. package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +27 -0
  201. package/Tests/Utils/LegacySerializedArray.test.ts +139 -0
  202. package/Tests/Utils/Monitor/MonitorDependencyRule.test.ts +190 -0
  203. package/Tests/Utils/Realtime.test.ts +78 -0
  204. package/Tests/Utils/Telemetry/SavedViewFilters.test.ts +246 -0
  205. package/Tests/__mocks__/i18next-browser-languagedetector.js +21 -0
  206. package/Types/AI/FixPullRequestCiStatus.ts +1 -1
  207. package/Types/AI/FixVerificationStatus.ts +2 -2
  208. package/Types/API/Hostname.ts +92 -4
  209. package/Types/API/URL.ts +43 -25
  210. package/Types/Billing/BalanceAdjustmentType.ts +14 -0
  211. package/Types/Billing/ProjectBalanceType.ts +13 -0
  212. package/Types/Domain.ts +26 -0
  213. package/Types/File/MimeType.ts +11 -0
  214. package/Types/JSONFunctions.ts +15 -0
  215. package/Types/Telemetry/TelemetrySavedViewState.ts +9 -1
  216. package/Types/Workflow/Components/BaseModel.ts +92 -28
  217. package/Typings/Index.d.ts +12 -0
  218. package/UI/Components/GanttChart/ChartContainer.tsx +4 -3
  219. package/UI/Components/Modal/ConfirmModal.tsx +10 -5
  220. package/UI/Components/Modal/Modal.tsx +27 -47
  221. package/UI/Components/ModelDelete/ModelDelete.tsx +23 -6
  222. package/UI/Components/SideOver/SideOver.tsx +195 -68
  223. package/UI/Utils/PageScrollLock.ts +66 -0
  224. package/Utils/Dashboard/HtmlWidgetDocument.ts +4 -2
  225. package/Utils/Dashboard/VariableInterpolation.ts +19 -1
  226. package/Utils/LegacySerializedArray.ts +60 -0
  227. package/Utils/Monitor/MonitorDependencyRule.ts +88 -0
  228. package/Utils/Telemetry/SavedViewFilters.ts +88 -0
  229. package/build/dist/Models/DatabaseModels/AIInsight.js +1 -1
  230. package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
  231. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js +2 -3
  232. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/FileModel.js.map +1 -1
  233. package/build/dist/Models/DatabaseModels/DeletedProject.js +640 -0
  234. package/build/dist/Models/DatabaseModels/DeletedProject.js.map +1 -0
  235. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  236. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  237. package/build/dist/Models/DatabaseModels/Monitor.js +106 -0
  238. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  239. package/build/dist/Models/DatabaseModels/Project.js +3 -3
  240. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  241. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  242. package/build/dist/Server/API/FileAPI.js +14 -2
  243. package/build/dist/Server/API/FileAPI.js.map +1 -1
  244. package/build/dist/Server/API/MicrosoftTeamsAPI.js +6 -1
  245. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  246. package/build/dist/Server/API/ProjectAPI.js +157 -4
  247. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  248. package/build/dist/Server/API/StatusPageAPI.js +27 -3
  249. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  250. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js +34 -0
  251. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786446545142-MigrationName.js.map +1 -0
  252. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js +30 -0
  253. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786449497966-AddMonitorDependency.js.map +1 -0
  254. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js +20 -0
  255. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786461136170-AddDeletedProjectTable.js.map +1 -0
  256. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  257. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  258. package/build/dist/Server/Services/AIAgentService.js +17 -0
  259. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  260. package/build/dist/Server/Services/DashboardDomainService.js +55 -18
  261. package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
  262. package/build/dist/Server/Services/DatabaseService.js +1 -0
  263. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  264. package/build/dist/Server/Services/DeletedProjectService.js +124 -0
  265. package/build/dist/Server/Services/DeletedProjectService.js.map +1 -0
  266. package/build/dist/Server/Services/FileService.js +52 -0
  267. package/build/dist/Server/Services/FileService.js.map +1 -1
  268. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +10 -1
  269. package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
  270. package/build/dist/Server/Services/IncidentPublicNoteService.js +10 -1
  271. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  272. package/build/dist/Server/Services/Index.js +2 -0
  273. package/build/dist/Server/Services/Index.js.map +1 -1
  274. package/build/dist/Server/Services/MonitorService.js +232 -0
  275. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  276. package/build/dist/Server/Services/ProbeService.js +17 -0
  277. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  278. package/build/dist/Server/Services/ProjectService.js +228 -6
  279. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  280. package/build/dist/Server/Services/PushNotificationService.js +56 -0
  281. package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
  282. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +10 -1
  283. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  284. package/build/dist/Server/Services/StatusPageAnnouncementService.js +54 -0
  285. package/build/dist/Server/Services/StatusPageAnnouncementService.js.map +1 -1
  286. package/build/dist/Server/Services/StatusPageDomainService.js +55 -18
  287. package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
  288. package/build/dist/Server/Services/TeamMemberService.js +97 -6
  289. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  290. package/build/dist/Server/Services/TelemetryIngestionKeyService.js +1 -0
  291. package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
  292. package/build/dist/Server/Services/UserWebhookService.js +31 -87
  293. package/build/dist/Server/Services/UserWebhookService.js.map +1 -1
  294. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +65 -0
  295. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  296. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +23 -0
  297. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  298. package/build/dist/Server/Types/Domain.js +37 -0
  299. package/build/dist/Server/Types/Domain.js.map +1 -1
  300. package/build/dist/Server/Types/Workflow/Components/API/Delete.js +2 -1
  301. package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
  302. package/build/dist/Server/Types/Workflow/Components/API/Get.js +2 -1
  303. package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
  304. package/build/dist/Server/Types/Workflow/Components/API/Patch.js +2 -1
  305. package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
  306. package/build/dist/Server/Types/Workflow/Components/API/Post.js +2 -1
  307. package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
  308. package/build/dist/Server/Types/Workflow/Components/API/Put.js +2 -1
  309. package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
  310. package/build/dist/Server/Types/Workflow/Components/API/Utils.js +35 -2
  311. package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
  312. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js +15 -11
  313. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateManyBaseModel.js.map +1 -1
  314. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js +2 -3
  315. package/build/dist/Server/Types/Workflow/Components/BaseModel/CreateOneBaseModel.js.map +1 -1
  316. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js +21 -10
  317. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteManyBaseModel.js.map +1 -1
  318. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js +21 -10
  319. package/build/dist/Server/Types/Workflow/Components/BaseModel/DeleteOneBaseModel.js.map +1 -1
  320. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js +18 -13
  321. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindManyBaseModel.js.map +1 -1
  322. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js +18 -13
  323. package/build/dist/Server/Types/Workflow/Components/BaseModel/FindOneBaseModel.js.map +1 -1
  324. package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js +43 -0
  325. package/build/dist/Server/Types/Workflow/Components/BaseModel/LogComponentError.js.map +1 -0
  326. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js +116 -0
  327. package/build/dist/Server/Types/Workflow/Components/BaseModel/ModelArguments.js.map +1 -0
  328. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js +7 -0
  329. package/build/dist/Server/Types/Workflow/Components/BaseModel/OnTriggerBaseModel.js.map +1 -1
  330. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js +24 -13
  331. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateManyBaseModel.js.map +1 -1
  332. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js +27 -13
  333. package/build/dist/Server/Types/Workflow/Components/BaseModel/UpdateOneBaseModel.js.map +1 -1
  334. package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js +14 -6
  335. package/build/dist/Server/Types/Workflow/Components/Discord/SendMessageToChannel.js.map +1 -1
  336. package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js +42 -0
  337. package/build/dist/Server/Types/Workflow/Components/IncomingWebhookUtils.js.map +1 -0
  338. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js +15 -6
  339. package/build/dist/Server/Types/Workflow/Components/MicrosoftTeams/SendMessageToChannel.js.map +1 -1
  340. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js +10 -2
  341. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js.map +1 -1
  342. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
  343. package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js +141 -0
  344. package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js.map +1 -1
  345. package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -1
  346. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +1 -1
  347. package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +6 -6
  348. package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js.map +1 -1
  349. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +39 -5
  350. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  351. package/build/dist/Server/Utils/DataSource/EgressGuard.js +94 -10
  352. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  353. package/build/dist/Server/Utils/DataSource/HttpFetch.js +1 -19
  354. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
  355. package/build/dist/Server/Utils/Execute.js +2 -2
  356. package/build/dist/Server/Utils/Execute.js.map +1 -1
  357. package/build/dist/Server/Utils/InlineImageAccessTokenSync.js +13 -0
  358. package/build/dist/Server/Utils/InlineImageAccessTokenSync.js.map +1 -1
  359. package/build/dist/Server/Utils/LLM/LLMService.js +43 -12
  360. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  361. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +44 -24
  362. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  363. package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js +158 -0
  364. package/build/dist/Server/Utils/Monitor/MonitorDependencySuppression.js.map +1 -0
  365. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +50 -30
  366. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  367. package/build/dist/Server/Utils/Monitor/MonitorResource.js +32 -0
  368. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  369. package/build/dist/Server/Utils/Response.js +112 -5
  370. package/build/dist/Server/Utils/Response.js.map +1 -1
  371. package/build/dist/Server/Utils/SSRFProtection.js +219 -53
  372. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  373. package/build/dist/Server/Utils/StartServer.js +7 -0
  374. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  375. package/build/dist/Server/Utils/VM/VMRunner.js +73 -1
  376. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  377. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +445 -43
  378. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  379. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +18 -1
  380. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  381. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  382. package/build/dist/Types/AI/FixPullRequestCiStatus.js +1 -1
  383. package/build/dist/Types/AI/FixVerificationStatus.js +2 -2
  384. package/build/dist/Types/API/Hostname.js +73 -4
  385. package/build/dist/Types/API/Hostname.js.map +1 -1
  386. package/build/dist/Types/API/URL.js +42 -24
  387. package/build/dist/Types/API/URL.js.map +1 -1
  388. package/build/dist/Types/Billing/BalanceAdjustmentType.js +15 -0
  389. package/build/dist/Types/Billing/BalanceAdjustmentType.js.map +1 -0
  390. package/build/dist/Types/Billing/ProjectBalanceType.js +14 -0
  391. package/build/dist/Types/Billing/ProjectBalanceType.js.map +1 -0
  392. package/build/dist/Types/Domain.js +22 -0
  393. package/build/dist/Types/Domain.js.map +1 -1
  394. package/build/dist/Types/File/MimeType.js +11 -0
  395. package/build/dist/Types/File/MimeType.js.map +1 -1
  396. package/build/dist/Types/JSONFunctions.js +14 -0
  397. package/build/dist/Types/JSONFunctions.js.map +1 -1
  398. package/build/dist/Types/Workflow/Components/BaseModel.js +83 -28
  399. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  400. package/build/dist/UI/Components/GanttChart/ChartContainer.js +4 -3
  401. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +1 -1
  402. package/build/dist/UI/Components/Modal/ConfirmModal.js +3 -1
  403. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  404. package/build/dist/UI/Components/Modal/Modal.js +24 -34
  405. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  406. package/build/dist/UI/Components/ModelDelete/ModelDelete.js +11 -6
  407. package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
  408. package/build/dist/UI/Components/SideOver/SideOver.js +105 -30
  409. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  410. package/build/dist/UI/Utils/PageScrollLock.js +52 -0
  411. package/build/dist/UI/Utils/PageScrollLock.js.map +1 -0
  412. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js +4 -2
  413. package/build/dist/Utils/Dashboard/HtmlWidgetDocument.js.map +1 -1
  414. package/build/dist/Utils/Dashboard/VariableInterpolation.js +19 -1
  415. package/build/dist/Utils/Dashboard/VariableInterpolation.js.map +1 -1
  416. package/build/dist/Utils/LegacySerializedArray.js +51 -0
  417. package/build/dist/Utils/LegacySerializedArray.js.map +1 -0
  418. package/build/dist/Utils/Monitor/MonitorDependencyRule.js +47 -0
  419. package/build/dist/Utils/Monitor/MonitorDependencyRule.js.map +1 -0
  420. package/build/dist/Utils/Telemetry/SavedViewFilters.js +52 -0
  421. package/build/dist/Utils/Telemetry/SavedViewFilters.js.map +1 -0
  422. package/jest.config.json +3 -0
  423. package/package.json +1 -1
  424. package/tsconfig.json +3 -1
@@ -0,0 +1,123 @@
1
+ import DatabaseService from "../../../Server/Services/DatabaseService";
2
+ import ModelPermission from "../../../Server/Types/Database/Permissions/Index";
3
+ import DeleteOneBy from "../../../Server/Types/Database/DeleteOneBy";
4
+ import Probe from "../../../Models/DatabaseModels/Probe";
5
+ import User from "../../../Models/DatabaseModels/User";
6
+ import ObjectID from "../../../Types/ObjectID";
7
+ import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
8
+ import { getJestSpyOn } from "../../Spy";
9
+
10
+ /*
11
+ * "Why are you deleting this?" is asked in the confirmation dialog, so the
12
+ * answer exists only for the length of the request that deletes the row. There
13
+ * is nowhere to persist it on the way down - the row is about to be gone - so
14
+ * it rides on the delete call itself and the hooks are the last place that can
15
+ * record it. This pins the one hop where it could quietly be dropped:
16
+ * deleteOneById rebuilding the delete as a query.
17
+ */
18
+
19
+ const PROBE_ID: ObjectID = new ObjectID("550e8400-e29b-41d4-a716-446655440031");
20
+
21
+ describe("DatabaseService.deleteOneById and the deletion reason", () => {
22
+ let service: DatabaseService<Probe>;
23
+ let deleteOneByCalls: Array<DeleteOneBy<Probe>> = [];
24
+
25
+ beforeEach(() => {
26
+ jest.restoreAllMocks();
27
+
28
+ service = new DatabaseService<Probe>(Probe);
29
+ deleteOneByCalls = [];
30
+
31
+ getJestSpyOn(
32
+ ModelPermission,
33
+ "checkDeletePermissionByModel",
34
+ ).mockResolvedValue(undefined as never);
35
+
36
+ getJestSpyOn(service, "deleteOneBy").mockImplementation((async (
37
+ deleteOneBy: DeleteOneBy<Probe>,
38
+ ) => {
39
+ deleteOneByCalls.push(deleteOneBy);
40
+ return 1;
41
+ }) as never);
42
+ });
43
+
44
+ afterEach(() => {
45
+ jest.restoreAllMocks();
46
+ });
47
+
48
+ it("passes the reason on to the delete, where the hooks can see it", async () => {
49
+ await service.deleteOneById({
50
+ id: PROBE_ID,
51
+ deletionReason: "No longer needed",
52
+ props: { isRoot: true },
53
+ });
54
+
55
+ expect(deleteOneByCalls).toHaveLength(1);
56
+ expect(deleteOneByCalls[0]?.deletionReason).toBe("No longer needed");
57
+ });
58
+
59
+ it("deletes the row the id names", async () => {
60
+ await service.deleteOneById({
61
+ id: PROBE_ID,
62
+ deletionReason: "No longer needed",
63
+ props: { isRoot: true },
64
+ });
65
+
66
+ expect(deleteOneByCalls[0]?.query).toEqual({ _id: PROBE_ID.toString() });
67
+ });
68
+
69
+ it("leaves the reason unset when the caller did not give one", async () => {
70
+ await service.deleteOneById({
71
+ id: PROBE_ID,
72
+ props: { isRoot: true },
73
+ });
74
+
75
+ expect(deleteOneByCalls[0]?.deletionReason).toBeUndefined();
76
+ });
77
+
78
+ it("still carries the acting user alongside the reason", async () => {
79
+ const actor: User = new User();
80
+ actor._id = "550e8400-e29b-41d4-a716-446655440032";
81
+
82
+ await service.deleteOneById({
83
+ id: PROBE_ID,
84
+ deletedByUser: actor,
85
+ deletionReason: "Retiring this probe",
86
+ props: { isRoot: true },
87
+ });
88
+
89
+ expect(deleteOneByCalls[0]?.deletedByUser).toBe(actor);
90
+ expect(deleteOneByCalls[0]?.deletionReason).toBe("Retiring this probe");
91
+ });
92
+
93
+ /*
94
+ * The reason travels with the request, not the row: nothing about it may
95
+ * change who is allowed to delete what.
96
+ */
97
+ it("still checks delete permission before anything else", async () => {
98
+ await service.deleteOneById({
99
+ id: PROBE_ID,
100
+ deletionReason: "No longer needed",
101
+ props: { isRoot: false },
102
+ });
103
+
104
+ expect(ModelPermission.checkDeletePermissionByModel).toHaveBeenCalled();
105
+ });
106
+
107
+ it("does not delete anything when permission is refused", async () => {
108
+ getJestSpyOn(
109
+ ModelPermission,
110
+ "checkDeletePermissionByModel",
111
+ ).mockRejectedValue(new Error("not allowed") as never);
112
+
113
+ await expect(
114
+ service.deleteOneById({
115
+ id: PROBE_ID,
116
+ deletionReason: "No longer needed",
117
+ props: { isRoot: false },
118
+ }),
119
+ ).rejects.toThrow("not allowed");
120
+
121
+ expect(deleteOneByCalls).toHaveLength(0);
122
+ });
123
+ });
@@ -0,0 +1,415 @@
1
+ import DeletedProjectService from "../../../Server/Services/DeletedProjectService";
2
+ import UserService from "../../../Server/Services/UserService";
3
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
4
+ import DeletedProject from "../../../Models/DatabaseModels/DeletedProject";
5
+ import Project from "../../../Models/DatabaseModels/Project";
6
+ import User from "../../../Models/DatabaseModels/User";
7
+ import { PlanType } from "../../../Types/Billing/SubscriptionPlan";
8
+ import SubscriptionStatus from "../../../Types/Billing/SubscriptionStatus";
9
+ import Email from "../../../Types/Email";
10
+ import Name from "../../../Types/Name";
11
+ import ObjectID from "../../../Types/ObjectID";
12
+ import Phone from "../../../Types/Phone";
13
+ import CompanySize from "../../../Types/Company/CompanySize";
14
+ import JobRole from "../../../Types/Company/JobRole";
15
+ import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
16
+ import { getJestSpyOn } from "../../Spy";
17
+
18
+ /*
19
+ * A project is hard deleted, so the snapshot this service writes is the only
20
+ * thing left of it. Anything it drops on the floor - the reason the customer
21
+ * gave, the address to reach them on, what they were paying - cannot be
22
+ * recovered afterwards, which is what every case below is about.
23
+ *
24
+ * Billing is on for this file: the audit trail exists so OneUptime can follow
25
+ * up with a SaaS customer. IsBillingEnabled is read at import time, so the
26
+ * self-hosted case needs its own module registry and lives in
27
+ * DeletedProjectServiceSelfHosted.test.ts.
28
+ */
29
+ jest.mock("../../../Server/EnvironmentConfig", () => {
30
+ return {
31
+ ...jest.requireActual("../../../Server/EnvironmentConfig"),
32
+ IsBillingEnabled: true,
33
+ };
34
+ });
35
+
36
+ const PROJECT_ID: ObjectID = new ObjectID(
37
+ "11111111-1111-4111-8111-111111111111",
38
+ );
39
+ const CREATOR_USER_ID: ObjectID = new ObjectID(
40
+ "22222222-2222-4222-8222-222222222222",
41
+ );
42
+ const OTHER_USER_ID: ObjectID = new ObjectID(
43
+ "33333333-3333-4333-8333-333333333333",
44
+ );
45
+
46
+ type MakeCreatorFunction = () => User;
47
+
48
+ const makeCreator: MakeCreatorFunction = (): User => {
49
+ const user: User = new User();
50
+ user._id = CREATOR_USER_ID.toString();
51
+ user.name = new Name("Jane Creator");
52
+ user.email = new Email("jane@acme.com");
53
+ user.companyName = "Acme Inc.";
54
+ user.companyPhoneNumber = new Phone("+15555550100");
55
+ user.companySize = CompanySize.ElevenToFifty;
56
+ user.jobRole = JobRole.EngineeringManager;
57
+
58
+ return user;
59
+ };
60
+
61
+ type MakeProjectFunction = (overrides?: Partial<Project>) => Project;
62
+
63
+ const makeProject: MakeProjectFunction = (
64
+ overrides?: Partial<Project>,
65
+ ): Project => {
66
+ const project: Project = new Project();
67
+ project._id = PROJECT_ID.toString();
68
+ project.name = "Acme Monitoring";
69
+ project.slug = "acme-monitoring";
70
+ project.createdAt = new Date("2024-01-01T00:00:00.000Z");
71
+ project.planName = PlanType.Growth;
72
+ project.paymentProviderPlanId = "price_growth";
73
+ project.paymentProviderCustomerId = "cus_123";
74
+ project.paymentProviderSubscriptionId = "sub_123";
75
+ project.paymentProviderSubscriptionStatus = SubscriptionStatus.Active;
76
+ project.paymentProviderSubscriptionSeats = 7;
77
+ project.trialEndsAt = new Date("2024-02-01T00:00:00.000Z");
78
+ project.currentActiveMonitorsCount = 12;
79
+ project.isBlocked = false;
80
+ project.createdByUserId = CREATOR_USER_ID;
81
+ project.createdByUser = makeCreator();
82
+
83
+ return Object.assign(project, overrides || {});
84
+ };
85
+
86
+ describe("DeletedProjectService.logProjectDeletion", () => {
87
+ let created: Array<CreateBy<DeletedProject>> = [];
88
+
89
+ beforeEach(() => {
90
+ created = [];
91
+
92
+ getJestSpyOn(DeletedProjectService, "create").mockImplementation((async (
93
+ createBy: CreateBy<DeletedProject>,
94
+ ) => {
95
+ created.push(createBy);
96
+ return createBy.data;
97
+ }) as never);
98
+
99
+ getJestSpyOn(UserService, "findOneById").mockResolvedValue(null as never);
100
+ });
101
+
102
+ afterEach(() => {
103
+ jest.restoreAllMocks();
104
+ });
105
+
106
+ it("writes one row for the deleted project", async () => {
107
+ await DeletedProjectService.logProjectDeletion({
108
+ project: makeProject(),
109
+ deletedByUserId: CREATOR_USER_ID,
110
+ deletionReason: "We moved to a single project",
111
+ });
112
+
113
+ expect(created).toHaveLength(1);
114
+ expect(created[0]?.data.projectId?.toString()).toBe(PROJECT_ID.toString());
115
+ expect(created[0]?.data.projectName).toBe("Acme Monitoring");
116
+ });
117
+
118
+ /*
119
+ * The table has empty access control arrays, so nothing but a root write can
120
+ * ever land a row in it.
121
+ */
122
+ it("writes as root, because no user permission can write this table", async () => {
123
+ await DeletedProjectService.logProjectDeletion({
124
+ project: makeProject(),
125
+ deletedByUserId: CREATOR_USER_ID,
126
+ });
127
+
128
+ expect(created[0]?.props).toEqual({ isRoot: true });
129
+ });
130
+
131
+ it("keeps the reason the customer typed, which is the whole point", async () => {
132
+ await DeletedProjectService.logProjectDeletion({
133
+ project: makeProject(),
134
+ deletedByUserId: CREATOR_USER_ID,
135
+ deletionReason: "Too expensive for our team size",
136
+ });
137
+
138
+ expect(created[0]?.data.deletionReason).toBe(
139
+ "Too expensive for our team size",
140
+ );
141
+ });
142
+
143
+ it("still records the deletion when no reason was given", async () => {
144
+ await DeletedProjectService.logProjectDeletion({
145
+ project: makeProject(),
146
+ deletedByUserId: CREATOR_USER_ID,
147
+ });
148
+
149
+ expect(created).toHaveLength(1);
150
+ expect(created[0]?.data.deletionReason).toBeUndefined();
151
+ });
152
+
153
+ it("copies the project snapshot rather than relating to the deleted row", async () => {
154
+ await DeletedProjectService.logProjectDeletion({
155
+ project: makeProject(),
156
+ deletedByUserId: CREATOR_USER_ID,
157
+ });
158
+
159
+ const row: DeletedProject = created[0]!.data;
160
+
161
+ expect(row.projectSlug).toBe("acme-monitoring");
162
+ expect(row.projectCreatedAt).toEqual(new Date("2024-01-01T00:00:00.000Z"));
163
+ expect(row.planName).toBe("Growth");
164
+ expect(row.paymentProviderPlanId).toBe("price_growth");
165
+ expect(row.paymentProviderCustomerId).toBe("cus_123");
166
+ expect(row.paymentProviderSubscriptionId).toBe("sub_123");
167
+ expect(row.paymentProviderSubscriptionStatus).toBe(
168
+ SubscriptionStatus.Active,
169
+ );
170
+ expect(row.paymentProviderSubscriptionSeats).toBe(7);
171
+ expect(row.trialEndsAt).toEqual(new Date("2024-02-01T00:00:00.000Z"));
172
+ expect(row.activeMonitorCount).toBe(12);
173
+ expect(row.wasProjectBlocked).toBe(false);
174
+ });
175
+
176
+ it("stamps the time of the deletion", async () => {
177
+ const before: number = Date.now();
178
+
179
+ await DeletedProjectService.logProjectDeletion({
180
+ project: makeProject(),
181
+ deletedByUserId: CREATOR_USER_ID,
182
+ });
183
+
184
+ const deletedAt: Date | undefined = created[0]?.data.projectDeletedAt;
185
+
186
+ expect(deletedAt).toBeInstanceOf(Date);
187
+ expect(deletedAt!.getTime()).toBeGreaterThanOrEqual(before);
188
+ });
189
+
190
+ it("records that a blocked project was deleted, which is a different story", async () => {
191
+ await DeletedProjectService.logProjectDeletion({
192
+ project: makeProject({ isBlocked: true }),
193
+ deletedByUserId: CREATOR_USER_ID,
194
+ });
195
+
196
+ expect(created[0]?.data.wasProjectBlocked).toBe(true);
197
+ });
198
+
199
+ describe("who to reach out to", () => {
200
+ it("records the creator of the project", async () => {
201
+ await DeletedProjectService.logProjectDeletion({
202
+ project: makeProject(),
203
+ deletedByUserId: CREATOR_USER_ID,
204
+ });
205
+
206
+ const row: DeletedProject = created[0]!.data;
207
+
208
+ expect(row.projectCreatedByUserId?.toString()).toBe(
209
+ CREATOR_USER_ID.toString(),
210
+ );
211
+ expect(row.projectCreatedByUserName).toBe("Jane Creator");
212
+ expect(row.projectCreatedByUserEmail?.toString()).toBe("jane@acme.com");
213
+ });
214
+
215
+ /*
216
+ * The common case: the owner deletes their own project. Looking the same
217
+ * user up again would be a wasted query on a path that already did one.
218
+ */
219
+ it("does not re-query the user when the creator deleted their own project", async () => {
220
+ await DeletedProjectService.logProjectDeletion({
221
+ project: makeProject(),
222
+ deletedByUserId: CREATOR_USER_ID,
223
+ });
224
+
225
+ expect(UserService.findOneById).not.toHaveBeenCalled();
226
+ expect(created[0]?.data.projectDeletedByUserName).toBe("Jane Creator");
227
+ expect(created[0]?.data.projectDeletedByUserEmail?.toString()).toBe(
228
+ "jane@acme.com",
229
+ );
230
+ });
231
+
232
+ it("looks up the deleting user when it was not the creator", async () => {
233
+ const other: User = new User();
234
+ other._id = OTHER_USER_ID.toString();
235
+ other.name = new Name("Sam Admin");
236
+ other.email = new Email("sam@acme.com");
237
+
238
+ getJestSpyOn(UserService, "findOneById").mockResolvedValue(
239
+ other as never,
240
+ );
241
+
242
+ await DeletedProjectService.logProjectDeletion({
243
+ project: makeProject(),
244
+ deletedByUserId: OTHER_USER_ID,
245
+ });
246
+
247
+ expect(UserService.findOneById).toHaveBeenCalled();
248
+
249
+ const row: DeletedProject = created[0]!.data;
250
+
251
+ expect(row.projectDeletedByUserId?.toString()).toBe(
252
+ OTHER_USER_ID.toString(),
253
+ );
254
+ expect(row.projectDeletedByUserName).toBe("Sam Admin");
255
+ expect(row.projectDeletedByUserEmail?.toString()).toBe("sam@acme.com");
256
+ // The creator is still recorded separately.
257
+ expect(row.projectCreatedByUserName).toBe("Jane Creator");
258
+ });
259
+
260
+ /*
261
+ * API-key and root deletes have no acting user. The row is still worth
262
+ * writing - the project and its billing history are the interesting part.
263
+ */
264
+ it("records the deletion when no user is attached to it", async () => {
265
+ await DeletedProjectService.logProjectDeletion({
266
+ project: makeProject(),
267
+ });
268
+
269
+ expect(UserService.findOneById).not.toHaveBeenCalled();
270
+ expect(created).toHaveLength(1);
271
+ expect(created[0]?.data.projectDeletedByUserId).toBeUndefined();
272
+ expect(created[0]?.data.projectDeletedByUserName).toBeUndefined();
273
+ });
274
+
275
+ it("survives the deleting user no longer being findable", async () => {
276
+ getJestSpyOn(UserService, "findOneById").mockResolvedValue(null as never);
277
+
278
+ await DeletedProjectService.logProjectDeletion({
279
+ project: makeProject(),
280
+ deletedByUserId: OTHER_USER_ID,
281
+ });
282
+
283
+ expect(created).toHaveLength(1);
284
+ expect(created[0]?.data.projectDeletedByUserId?.toString()).toBe(
285
+ OTHER_USER_ID.toString(),
286
+ );
287
+ expect(created[0]?.data.projectDeletedByUserName).toBeUndefined();
288
+ });
289
+
290
+ it("records the deletion for a project whose creator is gone", async () => {
291
+ const project: Project = makeProject();
292
+ delete project.createdByUser;
293
+ delete project.createdByUserId;
294
+
295
+ await DeletedProjectService.logProjectDeletion({
296
+ project: project,
297
+ deletionReason: "No longer needed",
298
+ });
299
+
300
+ expect(created).toHaveLength(1);
301
+ expect(created[0]?.data.projectCreatedByUserName).toBeUndefined();
302
+ expect(created[0]?.data.deletionReason).toBe("No longer needed");
303
+ });
304
+ });
305
+
306
+ describe("company details", () => {
307
+ it("takes the company from the user who created the project", async () => {
308
+ await DeletedProjectService.logProjectDeletion({
309
+ project: makeProject(),
310
+ deletedByUserId: CREATOR_USER_ID,
311
+ });
312
+
313
+ const row: DeletedProject = created[0]!.data;
314
+
315
+ expect(row.companyName).toBe("Acme Inc.");
316
+ expect(row.companyPhoneNumber?.toString()).toBe("+15555550100");
317
+ expect(row.companySize).toBe(CompanySize.ElevenToFifty);
318
+ expect(row.jobRole).toBe(JobRole.EngineeringManager);
319
+ });
320
+
321
+ /*
322
+ * Company details are collected at signup, so an invited teammate often
323
+ * has none. Falling back keeps a usable answer on the row.
324
+ */
325
+ it("falls back to the deleting user when the creator has no company on file", async () => {
326
+ const project: Project = makeProject();
327
+ project.createdByUser = new User();
328
+ project.createdByUser._id = CREATOR_USER_ID.toString();
329
+ project.createdByUser.name = new Name("Jane Creator");
330
+
331
+ const deleter: User = new User();
332
+ deleter._id = OTHER_USER_ID.toString();
333
+ deleter.companyName = "Acme Subsidiary";
334
+ deleter.companyPhoneNumber = new Phone("+15555550199");
335
+ deleter.companySize = CompanySize.FiveHundredAndMore;
336
+ deleter.jobRole = JobRole.CTO;
337
+
338
+ getJestSpyOn(UserService, "findOneById").mockResolvedValue(
339
+ deleter as never,
340
+ );
341
+
342
+ await DeletedProjectService.logProjectDeletion({
343
+ project: project,
344
+ deletedByUserId: OTHER_USER_ID,
345
+ });
346
+
347
+ const row: DeletedProject = created[0]!.data;
348
+
349
+ expect(row.companyName).toBe("Acme Subsidiary");
350
+ expect(row.companyPhoneNumber?.toString()).toBe("+15555550199");
351
+ expect(row.companySize).toBe(CompanySize.FiveHundredAndMore);
352
+ expect(row.jobRole).toBe(JobRole.CTO);
353
+ });
354
+
355
+ it("prefers the creator's company over the deleting user's", async () => {
356
+ const deleter: User = new User();
357
+ deleter._id = OTHER_USER_ID.toString();
358
+ deleter.companyName = "Somewhere Else";
359
+
360
+ getJestSpyOn(UserService, "findOneById").mockResolvedValue(
361
+ deleter as never,
362
+ );
363
+
364
+ await DeletedProjectService.logProjectDeletion({
365
+ project: makeProject(),
366
+ deletedByUserId: OTHER_USER_ID,
367
+ });
368
+
369
+ expect(created[0]?.data.companyName).toBe("Acme Inc.");
370
+ });
371
+
372
+ it("writes the row even when nobody filled in a company", async () => {
373
+ const project: Project = makeProject();
374
+ project.createdByUser = new User();
375
+ project.createdByUser._id = CREATOR_USER_ID.toString();
376
+
377
+ await DeletedProjectService.logProjectDeletion({
378
+ project: project,
379
+ deletedByUserId: CREATOR_USER_ID,
380
+ });
381
+
382
+ expect(created).toHaveLength(1);
383
+ expect(created[0]?.data.companyName).toBeUndefined();
384
+ });
385
+ });
386
+
387
+ /*
388
+ * The project id is what makes the row identifiable at all. A project
389
+ * without one cannot have been in the database, so there is nothing to log.
390
+ */
391
+ it("does not write a row for a project with no id", async () => {
392
+ const project: Project = makeProject();
393
+ delete project._id;
394
+
395
+ const result: DeletedProject | null =
396
+ await DeletedProjectService.logProjectDeletion({
397
+ project: project,
398
+ deletedByUserId: CREATOR_USER_ID,
399
+ });
400
+
401
+ expect(result).toBeNull();
402
+ expect(created).toHaveLength(0);
403
+ });
404
+
405
+ it("returns the row it wrote", async () => {
406
+ const result: DeletedProject | null =
407
+ await DeletedProjectService.logProjectDeletion({
408
+ project: makeProject(),
409
+ deletedByUserId: CREATOR_USER_ID,
410
+ });
411
+
412
+ expect(result).not.toBeNull();
413
+ expect(result?.projectName).toBe("Acme Monitoring");
414
+ });
415
+ });
@@ -0,0 +1,70 @@
1
+ import DeletedProjectService from "../../../Server/Services/DeletedProjectService";
2
+ import UserService from "../../../Server/Services/UserService";
3
+ import DeletedProject from "../../../Models/DatabaseModels/DeletedProject";
4
+ import Project from "../../../Models/DatabaseModels/Project";
5
+ import ObjectID from "../../../Types/ObjectID";
6
+ import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
7
+ import { getJestSpyOn } from "../../Spy";
8
+
9
+ /*
10
+ * The deletion audit trail is for reaching out to SaaS customers who left.
11
+ * A self-hosted install has nobody to reach out to, and the operator deleting
12
+ * their own project has not agreed to OneUptime keeping a copy of who they
13
+ * are - so on IsBillingEnabled=false nothing is recorded at all.
14
+ *
15
+ * The flag is read at module scope, so this is a separate file from the
16
+ * billing-on suite rather than a case inside it.
17
+ */
18
+ jest.mock("../../../Server/EnvironmentConfig", () => {
19
+ return {
20
+ ...jest.requireActual("../../../Server/EnvironmentConfig"),
21
+ IsBillingEnabled: false,
22
+ };
23
+ });
24
+
25
+ const PROJECT_ID: ObjectID = new ObjectID(
26
+ "11111111-1111-4111-8111-111111111111",
27
+ );
28
+ const USER_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
29
+
30
+ describe("DeletedProjectService.logProjectDeletion on a self-hosted install", () => {
31
+ beforeEach(() => {
32
+ getJestSpyOn(DeletedProjectService, "create").mockResolvedValue(
33
+ new DeletedProject() as never,
34
+ );
35
+ getJestSpyOn(UserService, "findOneById").mockResolvedValue(null as never);
36
+ });
37
+
38
+ afterEach(() => {
39
+ jest.restoreAllMocks();
40
+ });
41
+
42
+ it("does not record anything", async () => {
43
+ const project: Project = new Project();
44
+ project._id = PROJECT_ID.toString();
45
+ project.name = "Self Hosted Project";
46
+
47
+ const result: DeletedProject | null =
48
+ await DeletedProjectService.logProjectDeletion({
49
+ project: project,
50
+ deletedByUserId: USER_ID,
51
+ deletionReason: "Shutting down the instance",
52
+ });
53
+
54
+ expect(result).toBeNull();
55
+ expect(DeletedProjectService.create).not.toHaveBeenCalled();
56
+ });
57
+
58
+ it("does not go looking up the user either", async () => {
59
+ const project: Project = new Project();
60
+ project._id = PROJECT_ID.toString();
61
+ project.name = "Self Hosted Project";
62
+
63
+ await DeletedProjectService.logProjectDeletion({
64
+ project: project,
65
+ deletedByUserId: USER_ID,
66
+ });
67
+
68
+ expect(UserService.findOneById).not.toHaveBeenCalled();
69
+ });
70
+ });
@@ -0,0 +1,94 @@
1
+ import StatusPageDomainService from "../../../Server/Services/StatusPageDomainService";
2
+ import DashboardDomainService from "../../../Server/Services/DashboardDomainService";
3
+ import BadDataException from "../../../Types/Exception/BadDataException";
4
+ import { describe, expect, test } from "@jest/globals";
5
+
6
+ /*
7
+ * Subdomain validation is only a control if it is actually wired into BOTH
8
+ * hooks. subdomain is ProjectMember-updatable, so create-time validation on
9
+ * its own leaves the value editable straight afterwards. These tests call the
10
+ * hooks directly — no database is touched, because the update hook only
11
+ * inspects the payload.
12
+ */
13
+
14
+ interface SubdomainUpdate {
15
+ data: { subdomain?: string | undefined };
16
+ }
17
+
18
+ type CallOnBeforeUpdate = (
19
+ service: unknown,
20
+ update: SubdomainUpdate,
21
+ ) => Promise<SubdomainUpdate>;
22
+
23
+ const callOnBeforeUpdate: CallOnBeforeUpdate = async (
24
+ service: unknown,
25
+ update: SubdomainUpdate,
26
+ ): Promise<SubdomainUpdate> => {
27
+ const result: { updateBy: SubdomainUpdate } = await (
28
+ service as {
29
+ onBeforeUpdate: (u: SubdomainUpdate) => Promise<{
30
+ updateBy: SubdomainUpdate;
31
+ }>;
32
+ }
33
+ ).onBeforeUpdate(update);
34
+
35
+ return result.updateBy;
36
+ };
37
+
38
+ const services: Array<[string, unknown]> = [
39
+ ["StatusPageDomainService", StatusPageDomainService],
40
+ ["DashboardDomainService", DashboardDomainService],
41
+ ];
42
+
43
+ describe.each(services)(
44
+ "%s.onBeforeUpdate",
45
+ (_name: string, service: unknown) => {
46
+ test("rejects a subdomain carrying a path and a fragment", async () => {
47
+ await expect(
48
+ callOnBeforeUpdate(service, {
49
+ data: { subdomain: "169.254.169.254/latest/meta-data/#" },
50
+ }),
51
+ ).rejects.toThrow(BadDataException);
52
+ });
53
+
54
+ test("rejects a subdomain carrying a scheme", async () => {
55
+ await expect(
56
+ callOnBeforeUpdate(service, {
57
+ data: { subdomain: "http://169.254.169.254" },
58
+ }),
59
+ ).rejects.toThrow(BadDataException);
60
+ });
61
+
62
+ test("rejects a subdomain carrying a port", async () => {
63
+ await expect(
64
+ callOnBeforeUpdate(service, {
65
+ data: { subdomain: "169.254.169.254:80" },
66
+ }),
67
+ ).rejects.toThrow(BadDataException);
68
+ });
69
+
70
+ test("accepts and normalizes an ordinary subdomain", async () => {
71
+ const updated: SubdomainUpdate = await callOnBeforeUpdate(service, {
72
+ data: { subdomain: " STATUS " },
73
+ });
74
+
75
+ expect(updated.data.subdomain).toBe("status");
76
+ });
77
+
78
+ test("treats @ as the apex", async () => {
79
+ const updated: SubdomainUpdate = await callOnBeforeUpdate(service, {
80
+ data: { subdomain: "@" },
81
+ });
82
+
83
+ expect(updated.data.subdomain).toBe("");
84
+ });
85
+
86
+ test("leaves an update that does not touch the subdomain alone", async () => {
87
+ const updated: SubdomainUpdate = await callOnBeforeUpdate(service, {
88
+ data: {},
89
+ });
90
+
91
+ expect(updated.data.subdomain).toBeUndefined();
92
+ });
93
+ },
94
+ );