@oneuptime/common 12.0.31 → 12.0.33

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 (747) hide show
  1. package/Models/AnalyticsModels/Span.ts +14 -0
  2. package/Models/DatabaseModels/AlertState.ts +0 -2
  3. package/Models/DatabaseModels/Domain.ts +1 -1
  4. package/Models/DatabaseModels/EnterpriseLicense.ts +56 -0
  5. package/Models/DatabaseModels/Incident.ts +1 -1
  6. package/Models/DatabaseModels/IncidentTemplate.ts +1 -1
  7. package/Models/DatabaseModels/Index.ts +12 -0
  8. package/Models/DatabaseModels/MetricType.ts +0 -2
  9. package/Models/DatabaseModels/Monitor.ts +76 -0
  10. package/Models/DatabaseModels/MonitorStatusTimeline.ts +0 -2
  11. package/Models/DatabaseModels/MonitorTest.ts +0 -2
  12. package/Models/DatabaseModels/NetworkAlertPolicy.ts +690 -0
  13. package/Models/DatabaseModels/NetworkDevice.ts +297 -3
  14. package/Models/DatabaseModels/NetworkDeviceOidTemplate.ts +3 -1
  15. package/Models/DatabaseModels/NetworkDeviceRole.ts +648 -0
  16. package/Models/DatabaseModels/NetworkSite.ts +201 -2
  17. package/Models/DatabaseModels/NetworkSiteType.ts +102 -3
  18. package/Models/DatabaseModels/NetworkSnmpCredentialProfile.ts +841 -0
  19. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -1
  20. package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -1
  21. package/Models/DatabaseModels/StatusPageAnnouncement.ts +0 -2
  22. package/Models/DatabaseModels/StatusPageAnnouncementTemplate.ts +0 -2
  23. package/Models/DatabaseModels/StatusPagePrivateUser.ts +0 -2
  24. package/Models/DatabaseModels/StatusPageResource.ts +0 -2
  25. package/Models/DatabaseModels/StatusPageSubscriber.ts +0 -2
  26. package/Models/DatabaseModels/TelemetryException.ts +119 -0
  27. package/Models/DatabaseModels/UserNotificationEmailRollupBatch.ts +334 -0
  28. package/Models/DatabaseModels/UserNotificationEmailRollupItem.ts +370 -0
  29. package/Models/DatabaseModels/UserNotificationEmailRollupSetting.ts +282 -0
  30. package/Server/API/EnterpriseLicenseAPI.ts +391 -64
  31. package/Server/API/LlmProviderAPI.ts +137 -22
  32. package/Server/EnvironmentConfig.ts +14 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.ts +55 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.ts +72 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.ts +31 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.ts +124 -0
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.ts +132 -0
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.ts +79 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.ts +191 -0
  40. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  41. package/Server/Infrastructure/QueueWorker.ts +38 -8
  42. package/Server/Middleware/BearerTokenAuthorization.ts +12 -0
  43. package/Server/Middleware/ProjectAuthorization.ts +11 -0
  44. package/Server/Middleware/SCIMAuthorization.ts +12 -0
  45. package/Server/Middleware/TelemetryIngest.ts +11 -0
  46. package/Server/Services/AIBillingService.ts +101 -27
  47. package/Server/Services/DatabaseService.ts +39 -9
  48. package/Server/Services/EnterpriseLicenseInstanceService.ts +197 -0
  49. package/Server/Services/EnterpriseLicenseService.ts +55 -0
  50. package/Server/Services/Index.ts +12 -0
  51. package/Server/Services/MonitorService.ts +143 -21
  52. package/Server/Services/MonitorStatusTimelineService.ts +150 -8
  53. package/Server/Services/MonitorTemplateService.ts +217 -1
  54. package/Server/Services/NetworkAlertPolicyEngineService.ts +2159 -0
  55. package/Server/Services/NetworkAlertPolicyService.ts +944 -0
  56. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +9 -10
  57. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +60 -15
  58. package/Server/Services/NetworkDeviceRoleService.ts +90 -0
  59. package/Server/Services/NetworkDeviceService.ts +1032 -73
  60. package/Server/Services/NetworkSiteService.ts +1502 -129
  61. package/Server/Services/NetworkSiteTypeService.ts +1216 -0
  62. package/Server/Services/NetworkSnmpCredentialProfileService.ts +238 -0
  63. package/Server/Services/NotificationService.ts +118 -28
  64. package/Server/Services/ProjectService.ts +186 -44
  65. package/Server/Services/StatusPagePrivateUserService.ts +103 -3
  66. package/Server/Services/UserNotificationEmailRollupBatchService.ts +78 -0
  67. package/Server/Services/UserNotificationEmailRollupItemService.ts +79 -0
  68. package/Server/Services/UserNotificationEmailRollupSettingService.ts +113 -0
  69. package/Server/Services/UserNotificationSettingService.ts +62 -31
  70. package/Server/Services/UserService.ts +95 -0
  71. package/Server/Types/Database/QueryHelper.ts +2 -2
  72. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +14 -3
  73. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +10 -0
  74. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +10 -0
  75. package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +62 -1
  76. package/Server/Utils/AI/Toolbox/AlertTools.ts +15 -1
  77. package/Server/Utils/Billing/BillingFailureNoticeThrottle.ts +80 -0
  78. package/Server/Utils/DataSource/HttpFetch.ts +9 -2
  79. package/Server/Utils/EmailRollup/EmailRollupConstants.ts +98 -0
  80. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +872 -0
  81. package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +781 -0
  82. package/Server/Utils/EmailRollup/EmailRollupWriter.ts +368 -0
  83. package/Server/Utils/LLM/LLMService.ts +78 -0
  84. package/Server/Utils/LogRedaction.ts +8 -0
  85. package/Server/Utils/Logger.ts +79 -1
  86. package/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.ts +190 -0
  87. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +9 -2
  88. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +61 -1
  89. package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +14 -0
  90. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +49 -0
  91. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +255 -4
  92. package/Server/Utils/Monitor/MonitorMetricUtil.ts +41 -0
  93. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +43 -0
  94. package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +499 -19
  95. package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +66 -10
  96. package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +238 -44
  97. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +146 -39
  98. package/Server/Utils/NetworkDevice/DeviceHealthAggregation.ts +31 -18
  99. package/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.ts +213 -0
  100. package/Server/Utils/OutboundUserAgent.ts +152 -0
  101. package/Server/Utils/StartServer.ts +51 -19
  102. package/Server/Utils/Telemetry/CaptureSpan.ts +32 -8
  103. package/Server/Utils/Telemetry/ErrorClassResolver.ts +120 -0
  104. package/Server/Utils/Telemetry/SpanUtil.ts +33 -0
  105. package/Server/Utils/Telemetry.ts +198 -51
  106. package/Server/Views/Partials/SensitiveUrlToken.ejs +140 -0
  107. package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +158 -27
  108. package/Tests/App/Dashboard/AskAiSuggestedPrompts.test.ts +279 -0
  109. package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +35 -8
  110. package/Tests/App/Dashboard/DashboardCommandPalette.test.tsx +107 -3
  111. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +517 -0
  112. package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +26 -0
  113. package/Tests/App/Dashboard/DashboardWideChartZoom.test.tsx +401 -0
  114. package/Tests/App/Dashboard/DeviceStatusHero.test.tsx +588 -0
  115. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +18 -9
  116. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +632 -0
  117. package/Tests/App/Dashboard/MonitorRecommendationCreateProgress.test.tsx +682 -0
  118. package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +318 -0
  119. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +4 -2
  120. package/Tests/App/Dashboard/NetworkDeviceBulkDelete.test.tsx +357 -0
  121. package/Tests/App/Dashboard/NetworkDeviceCreateFormMonitorBinding.test.tsx +676 -0
  122. package/Tests/App/Dashboard/NetworkDeviceCreateFormPingMonitor.test.tsx +791 -0
  123. package/Tests/App/Dashboard/NetworkDeviceCreateFormProbeAndSnmp.test.tsx +833 -0
  124. package/Tests/App/Dashboard/NetworkDeviceCriteriaFilter.test.ts +161 -0
  125. package/Tests/App/Dashboard/NetworkDeviceSettingsMonitoringSection.test.tsx +458 -0
  126. package/Tests/App/Dashboard/NetworkSideMenu.test.tsx +626 -0
  127. package/Tests/App/Dashboard/PingMonitorProvisioning.test.ts +368 -0
  128. package/Tests/App/Dashboard/RecommendationCard.test.tsx +796 -0
  129. package/Tests/App/Dashboard/RecommendationToolbar.test.tsx +519 -0
  130. package/Tests/App/Dashboard/RecommendationsList.test.tsx +661 -0
  131. package/Tests/App/Dashboard/SideMenuHarness.tsx +11 -3
  132. package/Tests/App/Dashboard/UseBulkCreatePingMonitors.test.tsx +899 -0
  133. package/Tests/App/Dashboard/UseBulkSnmpCredentialProfileActions.test.tsx +732 -0
  134. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +256 -0
  135. package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +11 -1
  136. package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +9 -4
  137. package/Tests/Models/DatabaseModels/ModelRegistryInvariants.test.ts +115 -41
  138. package/Tests/Models/NetworkSiteHierarchy.test.ts +41 -4
  139. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +951 -7
  140. package/Tests/Server/API/LlmProviderConnectionTest.test.ts +522 -0
  141. package/Tests/Server/Infrastructure/Postgres/NetworkDeviceRoleTableMigration.test.ts +476 -0
  142. package/Tests/Server/Infrastructure/Postgres/NetworkSnmpCredentialProfilesAndAlertPoliciesMigration.test.ts +854 -0
  143. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +18 -0
  144. package/Tests/Server/Infrastructure/Postgres/UserNotificationEmailRollupTableMigration.test.ts +535 -0
  145. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +560 -0
  146. package/Tests/Server/Services/BillingRechargeOwnerEmailGuards.test.ts +783 -0
  147. package/Tests/Server/Services/DatabaseServiceGenerateSlug.test.ts +215 -0
  148. package/Tests/Server/Services/EnterpriseLicenseInstanceServiceDeletionUsage.test.ts +707 -0
  149. package/Tests/Server/Services/EnterpriseLicenseServiceUsageAggregationLock.test.ts +329 -0
  150. package/Tests/Server/Services/MonitorServiceDeleteNetworkDeviceCleanup.test.ts +294 -0
  151. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +802 -0
  152. package/Tests/Server/Services/MonitorStatusTimelineService.test.ts +72 -2
  153. package/Tests/Server/Services/NetworkAlertPolicyEngineHooks.test.ts +752 -0
  154. package/Tests/Server/Services/NetworkAlertPolicyEngineService.test.ts +1849 -0
  155. package/Tests/Server/Services/NetworkAlertPolicyModel.test.ts +1793 -0
  156. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +47 -4
  157. package/Tests/Server/Services/NetworkDeviceAutoImportRuleService.test.ts +97 -12
  158. package/Tests/Server/Services/NetworkDeviceAutoMonitorLifecycle.test.ts +148 -3
  159. package/Tests/Server/Services/NetworkDeviceMonitorBindingUpdateGuard.test.ts +514 -0
  160. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +447 -58
  161. package/Tests/Server/Services/NetworkDeviceMonitoringMethodTransition.test.ts +739 -0
  162. package/Tests/Server/Services/NetworkDevicePollingTenancy.test.ts +501 -0
  163. package/Tests/Server/Services/NetworkDeviceRoleModel.test.ts +672 -0
  164. package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +1 -1
  165. package/Tests/Server/Services/NetworkDeviceSiteDefaultProbe.test.ts +432 -0
  166. package/Tests/Server/Services/NetworkSiteMonitoringDefaults.test.ts +498 -0
  167. package/Tests/Server/Services/NetworkSiteRollupPolicyAndMaintenance.test.ts +337 -55
  168. package/Tests/Server/Services/NetworkSiteService.test.ts +1868 -131
  169. package/Tests/Server/Services/NetworkSiteTypeService.test.ts +1109 -0
  170. package/Tests/Server/Services/NetworkSnmpCredentialProfileModel.test.ts +926 -0
  171. package/Tests/Server/Services/NetworkSnmpCredentialProfileService.test.ts +796 -0
  172. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +67 -4
  173. package/Tests/Server/Services/ProjectServiceNetworkSiteTypeDefaults.test.ts +214 -0
  174. package/Tests/Server/Services/ScheduledMaintenanceSubscriberEndTime.test.ts +369 -0
  175. package/Tests/Server/Services/StatusPagePrivateUserEmailChangePasswordResetExpiry.test.ts +269 -0
  176. package/Tests/Server/Services/UserEmailChangePasswordResetExpiry.test.ts +472 -0
  177. package/Tests/Server/Services/UserNotificationEmailRollupModels.test.ts +569 -0
  178. package/Tests/Server/Services/UserNotificationEmailRollupSettingModel.test.ts +314 -0
  179. package/Tests/Server/Services/UserNotificationEmailRollupSettingService.test.ts +409 -0
  180. package/Tests/Server/Services/UserNotificationSettingMonitorDefaults.test.ts +248 -0
  181. package/Tests/Server/Services/UserNotificationSettingRollupRouting.test.ts +856 -0
  182. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +145 -3
  183. package/Tests/Server/Types/Database/Permissions/DashboardAccessPermission.test.ts +321 -0
  184. package/Tests/Server/Types/Database/QueryHelperOperators.test.ts +17 -0
  185. package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +124 -0
  186. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +208 -0
  187. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +235 -0
  188. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +253 -0
  189. package/Tests/Server/Utils/BasicCron.test.ts +283 -0
  190. package/Tests/Server/Utils/Captcha.test.ts +422 -0
  191. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +96 -2
  192. package/Tests/Server/Utils/Database/MigrationFailureLog.test.ts +466 -0
  193. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1096 -0
  194. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerClaim.test.ts +466 -0
  195. package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +1428 -0
  196. package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +752 -0
  197. package/Tests/Server/Utils/EmailRollup/EmailRollupWriter.test.ts +864 -0
  198. package/Tests/Server/Utils/IncomingCallPhoneNumber.test.ts +174 -0
  199. package/Tests/Server/Utils/LoggerFaultDemotion.test.ts +346 -0
  200. package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +633 -0
  201. package/Tests/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.test.ts +196 -0
  202. package/Tests/Server/Utils/Monitor/DatabaseMetricWrite.test.ts +367 -0
  203. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +100 -0
  204. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +5 -0
  205. package/Tests/Server/Utils/Monitor/NetworkDeviceHydrationUtil.test.ts +512 -31
  206. package/Tests/Server/Utils/Monitor/NetworkDeviceMetricUtil.test.ts +252 -0
  207. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +479 -42
  208. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +351 -5
  209. package/Tests/Server/Utils/NetworkDevice/DeviceHealthAggregation.test.ts +136 -12
  210. package/Tests/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.test.ts +306 -0
  211. package/Tests/Server/Utils/OutboundUserAgent.test.ts +266 -0
  212. package/Tests/Server/Utils/SecurityEvent/ThreatIntel/TaxiiClientUserAgent.test.ts +141 -0
  213. package/Tests/Server/Utils/Telemetry/ErrorClassResolver.test.ts +298 -0
  214. package/Tests/Server/Utils/Telemetry.test.ts +456 -66
  215. package/Tests/Types/Monitor/DatabaseMetricCatalog.test.ts +388 -0
  216. package/Tests/Types/Monitor/MonitorStepDatabaseMonitor.test.ts +243 -0
  217. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +1 -0
  218. package/Tests/Types/Monitor/MonitorType.test.ts +1 -0
  219. package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +16 -7
  220. package/Tests/Types/Monitor/NetworkDeviceAlertPack.test.ts +60 -0
  221. package/Tests/Types/NetworkDevice/DefaultNetworkDeviceRole.test.ts +417 -0
  222. package/Tests/Types/NetworkDevice/NetworkAlertPolicyScope.test.ts +655 -0
  223. package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +115 -26
  224. package/Tests/Types/NetworkSite/DefaultNetworkSiteType.test.ts +39 -0
  225. package/Tests/Types/NotificationSetting/NotificationEmailRollupPolicy.test.ts +299 -0
  226. package/Tests/Types/Telemetry/ErrorClass.test.ts +483 -0
  227. package/Tests/UI/Components/BasicRadioButtons.test.tsx +336 -0
  228. package/Tests/UI/Components/Charts/ChartClickDoubleClickDisambiguation.test.tsx +221 -0
  229. package/Tests/UI/Components/Charts/ChartDoubleClickReset.test.tsx +220 -0
  230. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +751 -0
  231. package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +475 -43
  232. package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +89 -0
  233. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +15 -0
  234. package/Tests/UI/Components/NavBarCommandKGating.test.tsx +103 -8
  235. package/Tests/UI/Components/ProgressBar.test.tsx +38 -2
  236. package/Tests/UI/Components/SideMenuItem.test.tsx +256 -4
  237. package/Tests/UI/Components/SideMenuSection.test.tsx +310 -0
  238. package/Tests/UI/Components/SideOverSubmitState.test.tsx +335 -0
  239. package/Tests/UI/Utils/PageScrollLock.test.tsx +377 -0
  240. package/Tests/UI/Utils/PermissionGate.test.ts +78 -0
  241. package/Tests/UI/Utils/SensitiveUrlToken.test.ts +140 -0
  242. package/Tests/UI/Utils/TableColumnsToCsv.test.ts +30 -0
  243. package/Tests/UI/Utils/UseDashboardTimeRangeZoom.test.tsx +187 -0
  244. package/Tests/Utils/Dashboard/DashboardResourceListSharedArgs.test.ts +302 -0
  245. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +242 -0
  246. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +69 -0
  247. package/Tests/Utils/EnterpriseLicenseSync.test.ts +7 -1
  248. package/Tests/Utils/EnterpriseLicenseUsage.test.ts +433 -2
  249. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +280 -1
  250. package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +1 -0
  251. package/Tests/Utils/Monitor/NetworkDeviceRoleCatalog.test.ts +981 -0
  252. package/Tests/Utils/Monitor/NetworkTopologyRoleCatalog.test.ts +865 -0
  253. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +38 -0
  254. package/Tests/Utils/NetworkDevice/DeviceHealthStateUtil.test.ts +284 -23
  255. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +177 -0
  256. package/Tests/Utils/NetworkDevice/DeviceRoleKeyUtil.test.ts +538 -0
  257. package/Tests/Utils/NetworkDevice/MonitoringMethodThreadedClassifiers.test.ts +992 -0
  258. package/Tests/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.test.ts +427 -0
  259. package/Tests/Utils/NetworkDevice/ReachabilityStampConsistency.test.ts +185 -0
  260. package/Tests/Utils/NetworkDevice/SnmpCredentialUtil.test.ts +268 -0
  261. package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +55 -5
  262. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +98 -21
  263. package/Tests/Utils/NetworkDiscovery/DiscoveryImportEligibility.test.ts +98 -20
  264. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +8 -7
  265. package/Tests/Utils/NetworkDiscovery/PingMonitorBuilderForAddress.test.ts +274 -0
  266. package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +230 -51
  267. package/Tests/Utils/NetworkSite/TypeHierarchyUtil.test.ts +209 -0
  268. package/Tests/Utils/Slo/SloEvaluation.test.ts +171 -0
  269. package/Tests/Utils/Slug.test.ts +61 -1
  270. package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +145 -0
  271. package/Tests/jest.setup.ts +19 -0
  272. package/Types/AI/ExceptionAIClassification.ts +10 -24
  273. package/Types/Email/EmailTemplateType.ts +2 -0
  274. package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +5 -0
  275. package/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.ts +13 -0
  276. package/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.ts +6 -0
  277. package/Types/Exception/ApiException.ts +10 -0
  278. package/Types/Exception/BadDataException.ts +11 -0
  279. package/Types/Exception/BadRequestException.ts +11 -0
  280. package/Types/Exception/Exception.ts +65 -0
  281. package/Types/Exception/ForbiddenException.ts +10 -0
  282. package/Types/Exception/NotAuthenticatedException.ts +10 -0
  283. package/Types/Exception/NotAuthorizedException.ts +10 -0
  284. package/Types/Exception/NotFoundException.ts +11 -0
  285. package/Types/Exception/PayloadTooLargeException.ts +11 -0
  286. package/Types/Exception/PaymentRequiredException.ts +10 -0
  287. package/Types/Exception/ServiceUnavailableException.ts +10 -0
  288. package/Types/Exception/SsoAuthorizationException.ts +10 -0
  289. package/Types/Exception/TenantNotFoundException.ts +11 -0
  290. package/Types/Exception/TimeoutException.ts +10 -0
  291. package/Types/Exception/TooManyRequestsException.ts +10 -0
  292. package/Types/Exception/UnableToReachServer.ts +10 -0
  293. package/Types/Exception/WebsiteRequestException.ts +10 -0
  294. package/Types/Monitor/CriteriaFilter.ts +60 -2
  295. package/Types/Monitor/DatabaseMetricCatalog.ts +693 -0
  296. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +83 -0
  297. package/Types/Monitor/MonitorCriteriaInstance.ts +76 -0
  298. package/Types/Monitor/MonitorMetricType.ts +71 -0
  299. package/Types/Monitor/MonitorStep.ts +76 -0
  300. package/Types/Monitor/MonitorStepDatabaseMonitor.ts +171 -0
  301. package/Types/Monitor/MonitorStepSqlMonitor.ts +2 -20
  302. package/Types/Monitor/MonitorType.ts +47 -1
  303. package/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.ts +22 -1
  304. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +61 -0
  305. package/Types/Monitor/SqlConnectionConfig.ts +31 -0
  306. package/Types/NetworkDevice/DefaultNetworkDeviceRole.ts +145 -0
  307. package/Types/NetworkDevice/NetworkAlertPolicyScope.ts +343 -0
  308. package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +42 -22
  309. package/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.ts +30 -0
  310. package/Types/NotificationSetting/NotificationEmailRollupCategory.ts +255 -0
  311. package/Types/NotificationSetting/NotificationEmailRollupPolicy.ts +104 -0
  312. package/Types/Permission.ts +135 -0
  313. package/Types/Probe/ProbeMonitorResponse.ts +16 -0
  314. package/Types/Telemetry/ErrorClass.ts +274 -0
  315. package/Types/Telemetry/UnitOfWork.ts +59 -0
  316. package/UI/Components/Charts/Area/AreaChart.tsx +7 -0
  317. package/UI/Components/Charts/Bar/BarChart.tsx +6 -0
  318. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +9 -1
  319. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +97 -35
  320. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +14 -0
  321. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +97 -35
  322. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +13 -0
  323. package/UI/Components/Charts/Line/LineChart.tsx +7 -0
  324. package/UI/Components/CommandPalette/CommandPalette.tsx +2 -8
  325. package/UI/Components/EditionLabel/EditionLabel.tsx +4 -0
  326. package/UI/Components/Modal/Modal.tsx +2 -8
  327. package/UI/Components/ModelTable/BaseModelTable.tsx +50 -15
  328. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +44 -0
  329. package/UI/Components/Navbar/NavBar.tsx +62 -13
  330. package/UI/Components/Navbar/NavBarMenuModal.tsx +7 -2
  331. package/UI/Components/ProgressBar/ProgressBar.tsx +12 -1
  332. package/UI/Components/RadioButtons/BasicRadioButtons.tsx +37 -7
  333. package/UI/Components/SideMenu/CountModelSideMenuItem.tsx +2 -0
  334. package/UI/Components/SideMenu/SideMenu.tsx +135 -7
  335. package/UI/Components/SideMenu/SideMenuItem.tsx +80 -85
  336. package/UI/Components/SideMenu/SideMenuSection.tsx +69 -25
  337. package/UI/Components/SideOver/SideOver.tsx +21 -20
  338. package/UI/Utils/PageScrollLock.ts +79 -3
  339. package/UI/Utils/PermissionGate.ts +61 -0
  340. package/UI/Utils/SensitiveUrlToken.ts +112 -0
  341. package/UI/Utils/TestLLMProvider.ts +11 -0
  342. package/UI/Utils/UseDashboardTimeRangeZoom.ts +77 -0
  343. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +118 -0
  344. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +16 -0
  345. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +191 -11
  346. package/Utils/Monitor/MonitorMetricType.ts +153 -1
  347. package/Utils/Monitor/NetworkDeviceRoleCatalog.ts +232 -0
  348. package/Utils/Monitor/NetworkDeviceRoleUtil.ts +2 -1
  349. package/Utils/Monitor/NetworkTopologyUtil.ts +139 -36
  350. package/Utils/NetworkDevice/DeviceHealthStateUtil.ts +65 -14
  351. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +43 -14
  352. package/Utils/NetworkDevice/DeviceRoleKeyUtil.ts +137 -0
  353. package/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.ts +247 -0
  354. package/Utils/NetworkDevice/SnmpCredentialUtil.ts +68 -0
  355. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +15 -7
  356. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +52 -17
  357. package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +55 -23
  358. package/Utils/NetworkDiscovery/PingMonitorBuilder.ts +115 -13
  359. package/Utils/NetworkSite/SiteStatusRollupUtil.ts +66 -20
  360. package/Utils/NetworkSite/TypeHierarchyUtil.ts +313 -0
  361. package/Utils/Rules/RulePatternMatchUtil.ts +1 -1
  362. package/Utils/Slug.ts +34 -1
  363. package/build/dist/Models/AnalyticsModels/Span.js +14 -0
  364. package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
  365. package/build/dist/Models/DatabaseModels/AlertState.js +0 -2
  366. package/build/dist/Models/DatabaseModels/AlertState.js.map +1 -1
  367. package/build/dist/Models/DatabaseModels/Domain.js +1 -1
  368. package/build/dist/Models/DatabaseModels/Domain.js.map +1 -1
  369. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +59 -0
  370. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  371. package/build/dist/Models/DatabaseModels/Incident.js +1 -1
  372. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  373. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +1 -1
  374. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  375. package/build/dist/Models/DatabaseModels/Index.js +12 -0
  376. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  377. package/build/dist/Models/DatabaseModels/MetricType.js +0 -2
  378. package/build/dist/Models/DatabaseModels/MetricType.js.map +1 -1
  379. package/build/dist/Models/DatabaseModels/Monitor.js +75 -0
  380. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  381. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +0 -2
  382. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
  383. package/build/dist/Models/DatabaseModels/MonitorTest.js +0 -2
  384. package/build/dist/Models/DatabaseModels/MonitorTest.js.map +1 -1
  385. package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js +713 -0
  386. package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js.map +1 -0
  387. package/build/dist/Models/DatabaseModels/NetworkDevice.js +304 -4
  388. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  389. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js +3 -1
  390. package/build/dist/Models/DatabaseModels/NetworkDeviceOidTemplate.js.map +1 -1
  391. package/build/dist/Models/DatabaseModels/NetworkDeviceRole.js +677 -0
  392. package/build/dist/Models/DatabaseModels/NetworkDeviceRole.js.map +1 -0
  393. package/build/dist/Models/DatabaseModels/NetworkSite.js +200 -2
  394. package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
  395. package/build/dist/Models/DatabaseModels/NetworkSiteType.js +102 -3
  396. package/build/dist/Models/DatabaseModels/NetworkSiteType.js.map +1 -1
  397. package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js +878 -0
  398. package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js.map +1 -0
  399. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -1
  400. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  401. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -1
  402. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
  403. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +0 -2
  404. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  405. package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js +0 -2
  406. package/build/dist/Models/DatabaseModels/StatusPageAnnouncementTemplate.js.map +1 -1
  407. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +0 -2
  408. package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
  409. package/build/dist/Models/DatabaseModels/StatusPageResource.js +0 -2
  410. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  411. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +0 -2
  412. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  413. package/build/dist/Models/DatabaseModels/TelemetryException.js +123 -0
  414. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  415. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js +363 -0
  416. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js.map +1 -0
  417. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js +402 -0
  418. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js.map +1 -0
  419. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js +296 -0
  420. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js.map +1 -0
  421. package/build/dist/Server/API/EnterpriseLicenseAPI.js +273 -60
  422. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  423. package/build/dist/Server/API/LlmProviderAPI.js +108 -13
  424. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  425. package/build/dist/Server/EnvironmentConfig.js +12 -0
  426. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  427. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js +24 -0
  428. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js.map +1 -0
  429. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.js +39 -0
  430. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790800000000-AddNetworkDeviceRoleTable.js.map +1 -0
  431. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js +16 -0
  432. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js.map +1 -0
  433. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js +111 -0
  434. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js.map +1 -0
  435. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js +77 -0
  436. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js.map +1 -0
  437. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js +50 -0
  438. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js.map +1 -0
  439. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js +83 -0
  440. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js.map +1 -0
  441. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  442. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  443. package/build/dist/Server/Infrastructure/QueueWorker.js +33 -5
  444. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  445. package/build/dist/Server/Middleware/BearerTokenAuthorization.js +12 -0
  446. package/build/dist/Server/Middleware/BearerTokenAuthorization.js.map +1 -1
  447. package/build/dist/Server/Middleware/ProjectAuthorization.js +11 -0
  448. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  449. package/build/dist/Server/Middleware/SCIMAuthorization.js +12 -0
  450. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +1 -1
  451. package/build/dist/Server/Middleware/TelemetryIngest.js +11 -0
  452. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  453. package/build/dist/Server/Services/AIBillingService.js +70 -16
  454. package/build/dist/Server/Services/AIBillingService.js.map +1 -1
  455. package/build/dist/Server/Services/DatabaseService.js +31 -5
  456. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  457. package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js +167 -0
  458. package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js.map +1 -1
  459. package/build/dist/Server/Services/EnterpriseLicenseService.js +39 -0
  460. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  461. package/build/dist/Server/Services/Index.js +12 -0
  462. package/build/dist/Server/Services/Index.js.map +1 -1
  463. package/build/dist/Server/Services/MonitorService.js +119 -21
  464. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  465. package/build/dist/Server/Services/MonitorStatusTimelineService.js +127 -8
  466. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  467. package/build/dist/Server/Services/MonitorTemplateService.js +162 -0
  468. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  469. package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js +1683 -0
  470. package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js.map +1 -0
  471. package/build/dist/Server/Services/NetworkAlertPolicyService.js +732 -0
  472. package/build/dist/Server/Services/NetworkAlertPolicyService.js.map +1 -0
  473. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +10 -9
  474. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  475. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +54 -11
  476. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  477. package/build/dist/Server/Services/NetworkDeviceRoleService.js +70 -0
  478. package/build/dist/Server/Services/NetworkDeviceRoleService.js.map +1 -0
  479. package/build/dist/Server/Services/NetworkDeviceService.js +841 -70
  480. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  481. package/build/dist/Server/Services/NetworkSiteService.js +1145 -107
  482. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  483. package/build/dist/Server/Services/NetworkSiteTypeService.js +863 -0
  484. package/build/dist/Server/Services/NetworkSiteTypeService.js.map +1 -1
  485. package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js +217 -0
  486. package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js.map +1 -0
  487. package/build/dist/Server/Services/NotificationService.js +80 -17
  488. package/build/dist/Server/Services/NotificationService.js.map +1 -1
  489. package/build/dist/Server/Services/ProjectService.js +131 -30
  490. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  491. package/build/dist/Server/Services/StatusPagePrivateUserService.js +92 -2
  492. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  493. package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js +76 -0
  494. package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js.map +1 -0
  495. package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js +77 -0
  496. package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js.map +1 -0
  497. package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js +111 -0
  498. package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js.map +1 -0
  499. package/build/dist/Server/Services/UserNotificationSettingService.js +46 -21
  500. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  501. package/build/dist/Server/Services/UserService.js +90 -0
  502. package/build/dist/Server/Services/UserService.js.map +1 -1
  503. package/build/dist/Server/Types/Database/QueryHelper.js +1 -1
  504. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  505. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +14 -3
  506. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  507. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +10 -0
  508. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  509. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +10 -0
  510. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  511. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +54 -1
  512. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
  513. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +18 -4
  514. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
  515. package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js +59 -0
  516. package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js.map +1 -0
  517. package/build/dist/Server/Utils/DataSource/HttpFetch.js +2 -1
  518. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
  519. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js +91 -0
  520. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js.map +1 -0
  521. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +691 -0
  522. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -0
  523. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +497 -0
  524. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -0
  525. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js +257 -0
  526. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js.map +1 -0
  527. package/build/dist/Server/Utils/LLM/LLMService.js +64 -1
  528. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  529. package/build/dist/Server/Utils/LogRedaction.js +8 -0
  530. package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
  531. package/build/dist/Server/Utils/Logger.js +63 -1
  532. package/build/dist/Server/Utils/Logger.js.map +1 -1
  533. package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js +141 -0
  534. package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js.map +1 -0
  535. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  536. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  537. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +51 -1
  538. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  539. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +7 -0
  540. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
  541. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +37 -0
  542. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  543. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +158 -6
  544. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  545. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
  546. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  547. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +32 -0
  548. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  549. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +330 -21
  550. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -1
  551. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +55 -10
  552. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
  553. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +178 -49
  554. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
  555. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +119 -29
  556. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  557. package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js +30 -17
  558. package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js.map +1 -1
  559. package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js +158 -0
  560. package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js.map +1 -0
  561. package/build/dist/Server/Utils/OutboundUserAgent.js +123 -0
  562. package/build/dist/Server/Utils/OutboundUserAgent.js.map +1 -0
  563. package/build/dist/Server/Utils/StartServer.js +38 -14
  564. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  565. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +28 -8
  566. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
  567. package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js +95 -0
  568. package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js.map +1 -0
  569. package/build/dist/Server/Utils/Telemetry/SpanUtil.js +29 -0
  570. package/build/dist/Server/Utils/Telemetry/SpanUtil.js.map +1 -1
  571. package/build/dist/Server/Utils/Telemetry.js +162 -46
  572. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  573. package/build/dist/Types/AI/ExceptionAIClassification.js +10 -24
  574. package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -1
  575. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  576. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  577. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js +2 -0
  578. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js.map +1 -0
  579. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js +7 -0
  580. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js.map +1 -0
  581. package/build/dist/Types/Exception/ApiException.js +9 -0
  582. package/build/dist/Types/Exception/ApiException.js.map +1 -1
  583. package/build/dist/Types/Exception/BadDataException.js +10 -0
  584. package/build/dist/Types/Exception/BadDataException.js.map +1 -1
  585. package/build/dist/Types/Exception/BadRequestException.js +10 -0
  586. package/build/dist/Types/Exception/BadRequestException.js.map +1 -1
  587. package/build/dist/Types/Exception/Exception.js +56 -0
  588. package/build/dist/Types/Exception/Exception.js.map +1 -1
  589. package/build/dist/Types/Exception/ForbiddenException.js +9 -0
  590. package/build/dist/Types/Exception/ForbiddenException.js.map +1 -1
  591. package/build/dist/Types/Exception/NotAuthenticatedException.js +9 -0
  592. package/build/dist/Types/Exception/NotAuthenticatedException.js.map +1 -1
  593. package/build/dist/Types/Exception/NotAuthorizedException.js +9 -0
  594. package/build/dist/Types/Exception/NotAuthorizedException.js.map +1 -1
  595. package/build/dist/Types/Exception/NotFoundException.js +10 -0
  596. package/build/dist/Types/Exception/NotFoundException.js.map +1 -1
  597. package/build/dist/Types/Exception/PayloadTooLargeException.js +10 -0
  598. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -1
  599. package/build/dist/Types/Exception/PaymentRequiredException.js +9 -0
  600. package/build/dist/Types/Exception/PaymentRequiredException.js.map +1 -1
  601. package/build/dist/Types/Exception/ServiceUnavailableException.js +9 -0
  602. package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -1
  603. package/build/dist/Types/Exception/SsoAuthorizationException.js +9 -0
  604. package/build/dist/Types/Exception/SsoAuthorizationException.js.map +1 -1
  605. package/build/dist/Types/Exception/TenantNotFoundException.js +10 -0
  606. package/build/dist/Types/Exception/TenantNotFoundException.js.map +1 -1
  607. package/build/dist/Types/Exception/TimeoutException.js +9 -0
  608. package/build/dist/Types/Exception/TimeoutException.js.map +1 -1
  609. package/build/dist/Types/Exception/TooManyRequestsException.js +9 -0
  610. package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -1
  611. package/build/dist/Types/Exception/UnableToReachServer.js +9 -0
  612. package/build/dist/Types/Exception/UnableToReachServer.js.map +1 -1
  613. package/build/dist/Types/Exception/WebsiteRequestException.js +9 -0
  614. package/build/dist/Types/Exception/WebsiteRequestException.js.map +1 -1
  615. package/build/dist/Types/Monitor/CriteriaFilter.js +45 -2
  616. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  617. package/build/dist/Types/Monitor/DatabaseMetricCatalog.js +594 -0
  618. package/build/dist/Types/Monitor/DatabaseMetricCatalog.js.map +1 -0
  619. package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js +21 -0
  620. package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js.map +1 -0
  621. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +70 -0
  622. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  623. package/build/dist/Types/Monitor/MonitorMetricType.js +61 -0
  624. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  625. package/build/dist/Types/Monitor/MonitorStep.js +54 -0
  626. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  627. package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js +122 -0
  628. package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js.map +1 -0
  629. package/build/dist/Types/Monitor/MonitorStepSqlMonitor.js.map +1 -1
  630. package/build/dist/Types/Monitor/MonitorType.js +45 -1
  631. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  632. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js +21 -1
  633. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js.map +1 -1
  634. package/build/dist/Types/Monitor/SqlConnectionConfig.js +2 -0
  635. package/build/dist/Types/Monitor/SqlConnectionConfig.js.map +1 -0
  636. package/build/dist/Types/NetworkDevice/DefaultNetworkDeviceRole.js +105 -0
  637. package/build/dist/Types/NetworkDevice/DefaultNetworkDeviceRole.js.map +1 -0
  638. package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js +172 -0
  639. package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js.map +1 -0
  640. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +40 -22
  641. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -1
  642. package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js +26 -0
  643. package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js.map +1 -0
  644. package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js +186 -0
  645. package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js.map +1 -0
  646. package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js +87 -0
  647. package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js.map +1 -0
  648. package/build/dist/Types/Permission.js +120 -0
  649. package/build/dist/Types/Permission.js.map +1 -1
  650. package/build/dist/Types/Telemetry/ErrorClass.js +216 -0
  651. package/build/dist/Types/Telemetry/ErrorClass.js.map +1 -0
  652. package/build/dist/Types/Telemetry/UnitOfWork.js +59 -0
  653. package/build/dist/Types/Telemetry/UnitOfWork.js.map +1 -0
  654. package/build/dist/UI/Components/Charts/Area/AreaChart.js +1 -1
  655. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  656. package/build/dist/UI/Components/Charts/Bar/BarChart.js +1 -1
  657. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  658. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +6 -1
  659. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  660. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +79 -31
  661. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  662. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +7 -1
  663. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  664. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +79 -31
  665. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  666. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +14 -0
  667. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -0
  668. package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
  669. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  670. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +2 -7
  671. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
  672. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +3 -0
  673. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  674. package/build/dist/UI/Components/Modal/Modal.js +2 -7
  675. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  676. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +39 -13
  677. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  678. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +39 -0
  679. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  680. package/build/dist/UI/Components/Navbar/NavBar.js +44 -9
  681. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  682. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
  683. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  684. package/build/dist/UI/Components/ProgressBar/ProgressBar.js +12 -1
  685. package/build/dist/UI/Components/ProgressBar/ProgressBar.js.map +1 -1
  686. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +6 -6
  687. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
  688. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js +1 -1
  689. package/build/dist/UI/Components/SideMenu/CountModelSideMenuItem.js.map +1 -1
  690. package/build/dist/UI/Components/SideMenu/SideMenu.js +76 -11
  691. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  692. package/build/dist/UI/Components/SideMenu/SideMenuItem.js +50 -54
  693. package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
  694. package/build/dist/UI/Components/SideMenu/SideMenuSection.js +32 -9
  695. package/build/dist/UI/Components/SideMenu/SideMenuSection.js.map +1 -1
  696. package/build/dist/UI/Components/SideOver/SideOver.js +5 -20
  697. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  698. package/build/dist/UI/Utils/PageScrollLock.js +31 -3
  699. package/build/dist/UI/Utils/PageScrollLock.js.map +1 -1
  700. package/build/dist/UI/Utils/PermissionGate.js +42 -1
  701. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  702. package/build/dist/UI/Utils/SensitiveUrlToken.js +97 -0
  703. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -0
  704. package/build/dist/UI/Utils/TestLLMProvider.js +4 -5
  705. package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -1
  706. package/build/dist/UI/Utils/UseDashboardTimeRangeZoom.js +42 -0
  707. package/build/dist/UI/Utils/UseDashboardTimeRangeZoom.js.map +1 -0
  708. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +74 -0
  709. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -0
  710. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +15 -0
  711. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -1
  712. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +111 -9
  713. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  714. package/build/dist/Utils/Monitor/MonitorMetricType.js +109 -1
  715. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  716. package/build/dist/Utils/Monitor/NetworkDeviceRoleCatalog.js +126 -0
  717. package/build/dist/Utils/Monitor/NetworkDeviceRoleCatalog.js.map +1 -0
  718. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +2 -1
  719. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
  720. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +75 -35
  721. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  722. package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js +27 -7
  723. package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js.map +1 -1
  724. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +7 -5
  725. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  726. package/build/dist/Utils/NetworkDevice/DeviceRoleKeyUtil.js +114 -0
  727. package/build/dist/Utils/NetworkDevice/DeviceRoleKeyUtil.js.map +1 -0
  728. package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js +180 -0
  729. package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js.map +1 -0
  730. package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js +12 -0
  731. package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js.map +1 -0
  732. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +15 -5
  733. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
  734. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +44 -15
  735. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
  736. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +52 -23
  737. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -1
  738. package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js +85 -13
  739. package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js.map +1 -1
  740. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +30 -15
  741. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
  742. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js +191 -0
  743. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js.map +1 -0
  744. package/build/dist/Utils/Rules/RulePatternMatchUtil.js +1 -1
  745. package/build/dist/Utils/Slug.js +31 -1
  746. package/build/dist/Utils/Slug.js.map +1 -1
  747. package/package.json +2 -2
@@ -15,6 +15,9 @@ import MonitorService from "./MonitorService";
15
15
  import MonitorStatusService from "./MonitorStatusService";
16
16
  import NetworkDeviceService from "./NetworkDeviceService";
17
17
  import NetworkSiteStatusTimelineService from "./NetworkSiteStatusTimelineService";
18
+ import NetworkSiteTypeService from "./NetworkSiteTypeService";
19
+ import NetworkSnmpCredentialProfileService from "./NetworkSnmpCredentialProfileService";
20
+ import ProbeService from "./ProbeService";
18
21
  import Model from "../../Models/DatabaseModels/NetworkSite";
19
22
  import Alert from "../../Models/DatabaseModels/Alert";
20
23
  import AlertStateTimeline from "../../Models/DatabaseModels/AlertStateTimeline";
@@ -25,11 +28,13 @@ import QueryHelper from "../Types/Database/QueryHelper";
25
28
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
26
29
  import NetworkDeviceHydrationUtil from "../Utils/Monitor/NetworkDeviceHydrationUtil";
27
30
  import logger from "../Utils/Logger";
31
+ import { NetworkDeviceMonitoringMethodUtil } from "../../Types/NetworkDevice/NetworkDeviceMonitoringMethod";
28
32
  import LIMIT_MAX, { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
29
33
  import BadDataException from "../../Types/Exception/BadDataException";
30
34
  import MonitorType from "../../Types/Monitor/MonitorType";
31
35
  import ObjectID from "../../Types/ObjectID";
32
36
  import OneUptimeDate from "../../Types/Date";
37
+ import PositiveNumber from "../../Types/PositiveNumber";
33
38
  import Text from "../../Types/Text";
34
39
  import { Raw } from "typeorm";
35
40
  import RelationIdUtil from "../Utils/Database/RelationIdUtil";
@@ -37,6 +42,7 @@ import MaterializedPathUtil from "../../Utils/NetworkSite/MaterializedPathUtil";
37
42
  import SiteStatusRollupUtil from "../../Utils/NetworkSite/SiteStatusRollupUtil";
38
43
  import { parseSiteHealthRollupPolicy } from "../../Types/NetworkSite/SiteHealthRollupPolicy";
39
44
  import NetworkSiteMaintenanceSuppression from "../Utils/NetworkSite/NetworkSiteMaintenanceSuppression";
45
+ import NetworkSiteHierarchyLock from "../Utils/NetworkSite/NetworkSiteHierarchyLock";
40
46
  import AggregateResultUtil from "../Types/Database/AggregateResultUtil";
41
47
  import { deviceRollupStateForGroup, } from "../Utils/NetworkDevice/DeviceHealthAggregation";
42
48
  /*
@@ -47,10 +53,354 @@ import { deviceRollupStateForGroup, } from "../Utils/NetworkDevice/DeviceHealthA
47
53
  * path rebase entirely. See RelationIdUtil.
48
54
  */
49
55
  const PARENT_SITE_KEYS = ["parentSiteId", "parentSite"];
56
+ /*
57
+ * Like parentSite, the site's type can be written either as its FK or as the
58
+ * serialised relation produced by dashboard forms. Hierarchy validation must
59
+ * inspect both spellings because TypeORM has not resolved the relation when
60
+ * the before hooks run.
61
+ */
62
+ const NETWORK_SITE_TYPE_KEYS = [
63
+ "networkSiteTypeId",
64
+ "networkSiteType",
65
+ ];
66
+ const PROJECT_KEYS = ["projectId", "project"];
67
+ /*
68
+ * Both spellings of the site's two monitoring defaults: the probe that polls
69
+ * devices here, and the SNMP credentials they are walked with.
70
+ *
71
+ * Both are references that a device INHERITS - the probe copied onto the
72
+ * device at write (see NetworkDeviceService), the credentials read live at
73
+ * poll time (see NetworkDeviceHydrationUtil.resolveSnmpCredentials). So a
74
+ * cross-project value here is not a cosmetic error on one row: it reaches
75
+ * every device in the subtree. Watching only the FK spelling would leave the
76
+ * dashboard's site form - which posts the relation - unguarded, which is the
77
+ * bug RelationIdUtil exists to document.
78
+ */
79
+ const PROBE_KEYS = ["probeId", "probe"];
80
+ const SNMP_CREDENTIAL_PROFILE_KEYS = [
81
+ "snmpCredentialProfileId",
82
+ "snmpCredentialProfile",
83
+ ];
84
+ const MATERIALIZED_HIERARCHY_KEYS = [
85
+ "materializedPath",
86
+ "depth",
87
+ ];
88
+ /*
89
+ * Type edits have to inspect every direct child. Keep each read bounded and
90
+ * page until exhaustion so a very wide site cannot hide invalid edges beyond
91
+ * DatabaseService's single-query limit.
92
+ */
93
+ const DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE = 1000;
94
+ /*
95
+ * Rebase large subtrees in bounded pages. Each successful rewrite removes the
96
+ * row from the old path prefix, so every page must start at offset zero.
97
+ */
98
+ const SUBTREE_REBASE_PAGE_SIZE = 1000;
99
+ /*
100
+ * Model instances initialise optional fields to undefined. Those properties
101
+ * are omissions, not requests to clear a relation; null is the explicit
102
+ * clear value and must still run validation.
103
+ */
104
+ function isRelationWritten(data, keys) {
105
+ return keys.some((key) => {
106
+ return key in data && data[key] !== undefined;
107
+ });
108
+ }
109
+ function normalizeId(id) {
110
+ return id.toString().toLowerCase();
111
+ }
112
+ function sameId(left, right) {
113
+ return normalizeId(left) === normalizeId(right);
114
+ }
115
+ /*
116
+ * RelationIdUtil intentionally returns null for anything it cannot resolve.
117
+ * In a hierarchy hook, however, an unresolvable NON-null value cannot mean
118
+ * "clear": TypeORM may still interpret a raw expression or relation-shaped
119
+ * object during the eventual write. Require each supplied spelling to be an
120
+ * explicit clear or a concrete ID before checking cross-spelling agreement.
121
+ */
122
+ function readStrictRelationId(data) {
123
+ for (const key of data.keys) {
124
+ const value = data.payload[key];
125
+ if (typeof value === "function") {
126
+ throw new BadDataException(`${key} cannot be set to a raw SQL expression because the network site hierarchy must be validated against an actual ID.`);
127
+ }
128
+ const isExplicitClear = value === null || value === "";
129
+ if (value !== undefined &&
130
+ !isExplicitClear &&
131
+ !RelationIdUtil.read(data.payload, [key])) {
132
+ throw new BadDataException(`${key} must contain a valid ${data.relationTitle} ID.`);
133
+ }
134
+ }
135
+ return RelationIdUtil.readConsistent(data.payload, data.keys, data.relationTitle);
136
+ }
50
137
  export class Service extends DatabaseService {
51
138
  constructor() {
52
139
  super(Model);
53
140
  }
141
+ /*
142
+ * Parent/type validation and the eventual write must observe one serialized
143
+ * project hierarchy. The same distributed lock is used by
144
+ * NetworkSiteTypeService, because changing either table can invalidate the
145
+ * other. It surrounds the whole DatabaseService mutation so success-hook
146
+ * subtree maintenance is protected too and finally always releases it.
147
+ */
148
+ async create(createBy) {
149
+ if (createBy.props.ignoreHooks) {
150
+ return await super.create(createBy);
151
+ }
152
+ const rawData = createBy.data;
153
+ const projectId = createBy.props.tenantId ||
154
+ RelationIdUtil.readConsistent(rawData, ["projectId", "project"], "Project") ||
155
+ null;
156
+ return await NetworkSiteHierarchyLock.runExclusive({
157
+ projectIds: projectId ? [projectId] : [],
158
+ operation: async () => {
159
+ return await super.create(createBy);
160
+ },
161
+ });
162
+ }
163
+ async updateOneBy(updateOneBy) {
164
+ if (updateOneBy.props.ignoreHooks ||
165
+ !this.updateTouchesHierarchy(updateOneBy.data)) {
166
+ return await super.updateOneBy(updateOneBy);
167
+ }
168
+ const projectIds = await this.findMutationProjectIds({
169
+ query: updateOneBy.query,
170
+ props: updateOneBy.props,
171
+ limit: 1,
172
+ skip: 0,
173
+ isDelete: false,
174
+ });
175
+ return await NetworkSiteHierarchyLock.runExclusive({
176
+ projectIds,
177
+ operation: async () => {
178
+ return await super.updateOneBy(updateOneBy);
179
+ },
180
+ });
181
+ }
182
+ async updateBy(updateBy) {
183
+ if (updateBy.props.ignoreHooks ||
184
+ !this.updateTouchesHierarchy(updateBy.data)) {
185
+ return await super.updateBy(updateBy);
186
+ }
187
+ const projectIds = await this.findMutationProjectIds({
188
+ query: updateBy.query,
189
+ props: updateBy.props,
190
+ limit: this.positiveNumberValue(updateBy.limit, LIMIT_MAX),
191
+ skip: this.positiveNumberValue(updateBy.skip, 0),
192
+ isDelete: false,
193
+ });
194
+ return await NetworkSiteHierarchyLock.runExclusive({
195
+ projectIds,
196
+ operation: async () => {
197
+ return await super.updateBy(updateBy);
198
+ },
199
+ });
200
+ }
201
+ async deleteOneBy(deleteOneBy) {
202
+ if (deleteOneBy.props.ignoreHooks) {
203
+ return await super.deleteOneBy(deleteOneBy);
204
+ }
205
+ const projectIds = await this.findMutationProjectIds({
206
+ query: deleteOneBy.query,
207
+ props: deleteOneBy.props,
208
+ limit: 1,
209
+ skip: 0,
210
+ isDelete: true,
211
+ });
212
+ return await NetworkSiteHierarchyLock.runExclusive({
213
+ projectIds,
214
+ operation: async () => {
215
+ return await super.deleteOneBy(deleteOneBy);
216
+ },
217
+ });
218
+ }
219
+ async deleteBy(deleteBy) {
220
+ if (deleteBy.props.ignoreHooks) {
221
+ return await super.deleteBy(deleteBy);
222
+ }
223
+ const projectIds = await this.findMutationProjectIds({
224
+ query: deleteBy.query,
225
+ props: deleteBy.props,
226
+ limit: this.positiveNumberValue(deleteBy.limit, LIMIT_MAX),
227
+ skip: this.positiveNumberValue(deleteBy.skip, 0),
228
+ isDelete: true,
229
+ });
230
+ return await NetworkSiteHierarchyLock.runExclusive({
231
+ projectIds,
232
+ operation: async () => {
233
+ return await super.deleteBy(deleteBy);
234
+ },
235
+ });
236
+ }
237
+ async hardDeleteBy(deleteBy) {
238
+ if (deleteBy.props.ignoreHooks) {
239
+ return await super.hardDeleteBy(deleteBy);
240
+ }
241
+ /*
242
+ * The generic retention cron intentionally queries every tenant at once
243
+ * by deletedAt. Resolve that open-ended query to a closed set of leaf IDs
244
+ * before locking and deleting. Deleting leaves only means a limit can
245
+ * never split a parent from a surviving child; the cron's next iteration
246
+ * naturally works upward through the tree.
247
+ */
248
+ if (!NetworkSiteHierarchyLock.isSafeRootMutationScope({
249
+ query: deleteBy.query,
250
+ props: deleteBy.props,
251
+ tenantScopeIsClosed: true,
252
+ })) {
253
+ return await this.hardDeleteClosedLeafBatch(deleteBy);
254
+ }
255
+ const projectIds = await this.findMutationProjectIds({
256
+ query: deleteBy.query,
257
+ props: deleteBy.props,
258
+ limit: this.positiveNumberValue(deleteBy.limit, LIMIT_MAX),
259
+ skip: this.positiveNumberValue(deleteBy.skip, 0),
260
+ isDelete: true,
261
+ });
262
+ return await NetworkSiteHierarchyLock.runExclusive({
263
+ projectIds,
264
+ operation: async () => {
265
+ return await super.hardDeleteBy(deleteBy);
266
+ },
267
+ });
268
+ }
269
+ updateTouchesHierarchy(data) {
270
+ const record = data;
271
+ return (isRelationWritten(record, PARENT_SITE_KEYS) ||
272
+ isRelationWritten(record, NETWORK_SITE_TYPE_KEYS) ||
273
+ isRelationWritten(record, PROJECT_KEYS) ||
274
+ isRelationWritten(record, MATERIALIZED_HIERARCHY_KEYS));
275
+ }
276
+ positiveNumberValue(value, fallback) {
277
+ if (value instanceof PositiveNumber) {
278
+ return value.toNumber();
279
+ }
280
+ return value !== null && value !== void 0 ? value : fallback;
281
+ }
282
+ async hardDeleteClosedLeafBatch(deleteBy) {
283
+ const requestedLimit = this.positiveNumberValue(deleteBy.limit, LIMIT_MAX);
284
+ if (requestedLimit <= 0) {
285
+ return 0;
286
+ }
287
+ const scanPageSize = Math.min(DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE, requestedLimit);
288
+ const leafSites = [];
289
+ let scanSkip = this.positiveNumberValue(deleteBy.skip, 0);
290
+ while (leafSites.length < requestedLimit) {
291
+ const candidates = await this.findBy({
292
+ query: deleteBy.query,
293
+ select: {
294
+ _id: true,
295
+ projectId: true,
296
+ },
297
+ sort: { _id: SortOrder.Ascending },
298
+ limit: scanPageSize,
299
+ skip: scanSkip,
300
+ props: { isRoot: true },
301
+ });
302
+ if (candidates.length === 0) {
303
+ break;
304
+ }
305
+ const candidateIds = candidates
306
+ .map((candidate) => {
307
+ return candidate.id || null;
308
+ })
309
+ .filter((id) => {
310
+ return Boolean(id);
311
+ });
312
+ const parentIdsWithChildren = new Set();
313
+ for (let parentOffset = 0; parentOffset < candidateIds.length; parentOffset += DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE) {
314
+ const parentIdBatch = candidateIds.slice(parentOffset, parentOffset + DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE);
315
+ let childSkip = 0;
316
+ while (parentIdBatch.length > 0) {
317
+ const children = await this.findBy({
318
+ query: {
319
+ parentSiteId: QueryHelper.any(parentIdBatch),
320
+ },
321
+ select: { parentSiteId: true },
322
+ sort: { _id: SortOrder.Ascending },
323
+ limit: DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE,
324
+ skip: childSkip,
325
+ props: { isRoot: true },
326
+ });
327
+ for (const child of children) {
328
+ if (child.parentSiteId) {
329
+ parentIdsWithChildren.add(normalizeId(child.parentSiteId));
330
+ }
331
+ }
332
+ if (children.length < DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE) {
333
+ break;
334
+ }
335
+ childSkip += children.length;
336
+ }
337
+ }
338
+ for (const candidate of candidates) {
339
+ if (candidate.id &&
340
+ candidate.projectId &&
341
+ !parentIdsWithChildren.has(normalizeId(candidate.id))) {
342
+ leafSites.push(candidate);
343
+ if (leafSites.length === requestedLimit) {
344
+ break;
345
+ }
346
+ }
347
+ }
348
+ scanSkip += candidates.length;
349
+ if (candidates.length < scanPageSize) {
350
+ break;
351
+ }
352
+ }
353
+ if (leafSites.length === 0) {
354
+ return 0;
355
+ }
356
+ const leafIds = leafSites.map((site) => {
357
+ return site.id;
358
+ });
359
+ return await NetworkSiteHierarchyLock.runExclusive({
360
+ projectIds: leafSites.map((site) => {
361
+ return site.projectId;
362
+ }),
363
+ operation: async () => {
364
+ return await super.hardDeleteBy(Object.assign(Object.assign({}, deleteBy), { query: Object.assign(Object.assign({}, deleteBy.query), { _id: QueryHelper.any(leafIds) }), limit: leafIds.length, skip: 0 }));
365
+ },
366
+ });
367
+ }
368
+ async findMutationProjectIds(data) {
369
+ NetworkSiteHierarchyLock.assertSafeRootMutationScope({
370
+ query: data.query,
371
+ props: data.props,
372
+ tenantScopeIsClosed: data.isDelete,
373
+ });
374
+ const sites = await this.findBy({
375
+ query: data.isDelete
376
+ ? this.scopeDeleteQueryToCallerTenant(data.query, data.props)
377
+ : this.scopeQueryToCallerTenant(data.query, data.props),
378
+ select: { projectId: true },
379
+ limit: data.limit,
380
+ skip: data.skip,
381
+ props: { isRoot: true },
382
+ });
383
+ const projectIds = sites
384
+ .map((site) => {
385
+ return site.projectId;
386
+ })
387
+ .filter((projectId) => {
388
+ return Boolean(projectId);
389
+ });
390
+ projectIds.push(...NetworkSiteHierarchyLock.getExplicitProjectIds(data.query));
391
+ if (projectIds.length === 0 && data.props.tenantId) {
392
+ projectIds.push(data.props.tenantId);
393
+ }
394
+ const seenProjectIds = new Set();
395
+ return projectIds.filter((projectId) => {
396
+ const normalizedProjectId = normalizeId(projectId);
397
+ if (seenProjectIds.has(normalizedProjectId)) {
398
+ return false;
399
+ }
400
+ seenProjectIds.add(normalizedProjectId);
401
+ return true;
402
+ });
403
+ }
54
404
  /**
55
405
  * How many sites sit under each rolled-up MonitorStatus, counted in the
56
406
  * database.
@@ -102,7 +452,18 @@ export class Service extends DatabaseService {
102
452
  * never see, let alone rewrite, a row outside the caller's project.
103
453
  */
104
454
  scopeQueryToCallerTenant(query, props) {
105
- if (props.isRoot || !props.tenantId) {
455
+ if (props.isRoot || props.isMasterAdmin || !props.tenantId) {
456
+ return query;
457
+ }
458
+ return Object.assign(Object.assign({}, query), { projectId: props.tenantId });
459
+ }
460
+ /*
461
+ * Root deletes are also tenant-scoped by DeletePermission when tenantId is
462
+ * present. Mirror that exact rule in the preflight read so limit/skip guard
463
+ * the same rows the eventual delete can select.
464
+ */
465
+ scopeDeleteQueryToCallerTenant(query, props) {
466
+ if (!props.tenantId || props.isMultiTenantRequest) {
106
467
  return query;
107
468
  }
108
469
  return Object.assign(Object.assign({}, query), { projectId: props.tenantId });
@@ -148,10 +509,10 @@ export class Service extends DatabaseService {
148
509
  /*
149
510
  * A stored path is trustworthy only when it agrees with parentSiteId: it
150
511
  * must end with the site's own id, and the segment before it must be the
151
- * parent (nothing before it for a root). A delete that nulls parentSiteId,
152
- * or a half-applied move, leaves the two disagreeing - and a stale path
153
- * silently corrupts every prefix query built from it, so treat it as
154
- * missing and let the caller rebuild.
512
+ * parent (nothing before it for a root). A legacy delete that nullified
513
+ * parentSiteId, or a half-applied move, leaves the two disagreeing - and a
514
+ * stale path silently corrupts every prefix query built from it, so treat
515
+ * it as missing and let the caller rebuild.
155
516
  */
156
517
  isPathConsistent(site) {
157
518
  if (!site.id) {
@@ -170,32 +531,329 @@ export class Service extends DatabaseService {
170
531
  : null;
171
532
  return parentSegment === parentId;
172
533
  }
534
+ /*
535
+ * Resolve a site type with tenant information and its configured direct
536
+ * parent type. The lookup deliberately runs as root: hook validation occurs
537
+ * before DatabaseService applies the caller's permission-scoped query, so
538
+ * the service must see the referenced row and perform the project check
539
+ * explicitly rather than confuse "foreign" with "missing".
540
+ */
541
+ async getNetworkSiteTypeForHierarchy(networkSiteTypeId, cache) {
542
+ const key = normalizeId(networkSiteTypeId);
543
+ const cached = cache.get(key);
544
+ if (cached) {
545
+ return cached;
546
+ }
547
+ const networkSiteType = await NetworkSiteTypeService.findOneById({
548
+ id: networkSiteTypeId,
549
+ select: {
550
+ _id: true,
551
+ projectId: true,
552
+ parentNetworkSiteTypeId: true,
553
+ },
554
+ props: {
555
+ isRoot: true,
556
+ },
557
+ });
558
+ if (!networkSiteType) {
559
+ throw new BadDataException("Network site type not found.");
560
+ }
561
+ cache.set(key, networkSiteType);
562
+ return networkSiteType;
563
+ }
564
+ /*
565
+ * Assert one proposed site edge against the type hierarchy. Untyped legacy
566
+ * root rows remain readable/editable, but as soon as a site has a type its
567
+ * placement is exact: a root type has no parent, and a non-root type has a
568
+ * parent site whose type is precisely the configured direct parent type.
569
+ */
570
+ async validateSiteTypeEdge(data) {
571
+ if (!data.networkSiteTypeId) {
572
+ if (data.parentSite) {
573
+ throw new BadDataException("A network site with a parent must have a network site type.");
574
+ }
575
+ return;
576
+ }
577
+ const networkSiteType = await this.getNetworkSiteTypeForHierarchy(data.networkSiteTypeId, data.typeCache);
578
+ if (data.projectId &&
579
+ networkSiteType.projectId &&
580
+ !sameId(networkSiteType.projectId, data.projectId)) {
581
+ throw new BadDataException("Network site type must belong to the same project.");
582
+ }
583
+ const requiredParentTypeId = networkSiteType.parentNetworkSiteTypeId || null;
584
+ if (!requiredParentTypeId) {
585
+ if (data.parentSite) {
586
+ throw new BadDataException("A site with a root network site type cannot have a parent site.");
587
+ }
588
+ return;
589
+ }
590
+ if (!data.parentSite) {
591
+ throw new BadDataException("This network site type requires a parent site.");
592
+ }
593
+ if (!data.parentSite.networkSiteTypeId ||
594
+ !sameId(data.parentSite.networkSiteTypeId, requiredParentTypeId)) {
595
+ throw new BadDataException("Parent site must use the configured parent network site type.");
596
+ }
597
+ }
598
+ /*
599
+ * Changing a site's type also changes what every direct child's type must
600
+ * point at. Validate those reverse edges before the update so one edit
601
+ * cannot strand a previously valid subtree.
602
+ */
603
+ async validateDirectChildrenForTypeChange(data) {
604
+ if (!data.site.id || !data.site.projectId) {
605
+ return;
606
+ }
607
+ const proposedParent = {
608
+ id: data.site.id,
609
+ projectId: data.site.projectId,
610
+ networkSiteTypeId: data.proposedNetworkSiteTypeId || undefined,
611
+ };
612
+ let skip = 0;
613
+ while (true) {
614
+ const children = await this.findBy({
615
+ query: {
616
+ projectId: data.site.projectId,
617
+ parentSiteId: data.site.id,
618
+ },
619
+ select: {
620
+ _id: true,
621
+ projectId: true,
622
+ networkSiteTypeId: true,
623
+ },
624
+ sort: {
625
+ _id: SortOrder.Ascending,
626
+ },
627
+ limit: DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE,
628
+ skip: skip,
629
+ props: {
630
+ isRoot: true,
631
+ },
632
+ });
633
+ for (const child of children) {
634
+ await this.validateSiteTypeEdge({
635
+ networkSiteTypeId: child.networkSiteTypeId || null,
636
+ parentSite: proposedParent,
637
+ projectId: child.projectId || data.site.projectId,
638
+ typeCache: data.typeCache,
639
+ });
640
+ }
641
+ if (children.length < DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE) {
642
+ break;
643
+ }
644
+ skip += children.length;
645
+ }
646
+ }
647
+ /*
648
+ * The FK behind `probeId` only requires the Probe row to exist. It does
649
+ * NOT require it to belong to this project, and probe ids reach the server
650
+ * from the browser (the site form's dropdown posts one), so without this
651
+ * check an operator could name another project's probe as this site's
652
+ * default - and NetworkDeviceService would then copy it onto every device
653
+ * created into the site, handing that project's probe a target list inside
654
+ * this one.
655
+ *
656
+ * A GLOBAL probe has no project and is attachable anywhere, which is why
657
+ * this delegates to ProbeService rather than comparing projectIds: the
658
+ * same predicate that decides which probes a monitor may use decides which
659
+ * probe a site may default to.
660
+ */
661
+ async assertProbeIsAttachableToProject(data) {
662
+ if (!data.projectId) {
663
+ return;
664
+ }
665
+ const isAttachable = await ProbeService.isProbeAttachableToProject({
666
+ probeId: data.probeId,
667
+ projectId: data.projectId,
668
+ });
669
+ if (!isAttachable) {
670
+ throw new BadDataException("Probe not found or it does not belong to this project.");
671
+ }
672
+ }
673
+ /*
674
+ * Same hole as the probe above, and the consequence is worse: a site's
675
+ * credential profile is read LIVE at poll time for every device in the
676
+ * site that has no credentials of its own
677
+ * (NetworkDeviceHydrationUtil.resolveSnmpCredentials), so a cross-project
678
+ * reference here would put another project's community string on this
679
+ * project's probe's wire. The resolver drops such a reference as a
680
+ * backstop; this is the half that stops it being written at all.
681
+ */
682
+ async assertSnmpCredentialProfileBelongsToProject(data) {
683
+ if (!data.projectId) {
684
+ return;
685
+ }
686
+ const profile = await NetworkSnmpCredentialProfileService.findOneById({
687
+ id: data.snmpCredentialProfileId,
688
+ select: {
689
+ _id: true,
690
+ projectId: true,
691
+ },
692
+ props: {
693
+ isRoot: true,
694
+ },
695
+ });
696
+ if (!profile) {
697
+ throw new BadDataException("SNMP Credential Profile not found.");
698
+ }
699
+ if (profile.projectId && !sameId(profile.projectId, data.projectId)) {
700
+ throw new BadDataException("SNMP Credential Profile must belong to the same project.");
701
+ }
702
+ }
703
+ /*
704
+ * The site's default probe, inherited: this site's own `probeId`, or the
705
+ * nearest ancestor that has one.
706
+ *
707
+ * Copy-at-write, not read-through — NetworkDeviceService calls this once,
708
+ * when a device is created into or moved to a site with no probe of its
709
+ * own, and stamps the answer on the device. That is what makes editing a
710
+ * site's default a decision about FUTURE devices only: nothing re-reads
711
+ * this, so no site edit can silently re-point a fleet that is already
712
+ * polling.
713
+ *
714
+ * Nearest ancestor wins, so a Region's probe covers every Market under it
715
+ * while a Market that names its own overrides it for its own subtree.
716
+ * `getAncestorIds` returns root-first, hence the reverse walk.
717
+ */
718
+ async resolveDefaultProbeIdForSite(siteId) {
719
+ const site = await this.findOneById({
720
+ id: siteId,
721
+ select: {
722
+ _id: true,
723
+ probeId: true,
724
+ parentSiteId: true,
725
+ },
726
+ props: {
727
+ isRoot: true,
728
+ },
729
+ });
730
+ if (!site) {
731
+ return null;
732
+ }
733
+ if (site.probeId) {
734
+ return site.probeId;
735
+ }
736
+ /*
737
+ * A root site has no ancestors, so there is nothing above it to inherit
738
+ * from. Short-circuited here rather than left to getAncestorIds because
739
+ * that call is not free: it resolves — and, for a row whose path is
740
+ * missing or stale, REBUILDS AND PERSISTS — the materialized path. This
741
+ * runs on the device create path, where most sites are roots and no
742
+ * device write should be paying for hierarchy maintenance it cannot use.
743
+ */
744
+ if (!site.parentSiteId) {
745
+ return null;
746
+ }
747
+ const ancestorIds = await this.getAncestorIds(siteId);
748
+ if (ancestorIds.length === 0) {
749
+ return null;
750
+ }
751
+ const ancestors = await this.findBy({
752
+ query: {
753
+ _id: QueryHelper.any(ancestorIds.map((ancestorId) => {
754
+ return ancestorId.toString();
755
+ })),
756
+ },
757
+ select: {
758
+ _id: true,
759
+ probeId: true,
760
+ },
761
+ limit: LIMIT_MAX,
762
+ skip: 0,
763
+ props: {
764
+ isRoot: true,
765
+ },
766
+ });
767
+ const probeIdBySiteId = new Map();
768
+ for (const ancestor of ancestors) {
769
+ if (ancestor.id && ancestor.probeId) {
770
+ probeIdBySiteId.set(ancestor.id.toString(), ancestor.probeId);
771
+ }
772
+ }
773
+ for (let index = ancestorIds.length - 1; index >= 0; index--) {
774
+ const probeId = probeIdBySiteId.get(ancestorIds[index].toString());
775
+ if (probeId) {
776
+ return probeId;
777
+ }
778
+ }
779
+ return null;
780
+ }
173
781
  async onBeforeCreate(createBy) {
174
782
  let parentPath = null;
783
+ let parentSite = null;
784
+ const rawData = createBy.data;
785
+ const projectId = createBy.props.tenantId ||
786
+ RelationIdUtil.readConsistent(rawData, ["projectId", "project"], "Project") ||
787
+ undefined;
175
788
  /*
176
789
  * Both spellings: the dashboard's site form posts the `parentSite`
177
790
  * relation, not the `parentSiteId` column. See RelationIdUtil.
178
791
  */
179
- const parentSiteId = RelationIdUtil.read(createBy.data, PARENT_SITE_KEYS);
792
+ const parentSiteId = readStrictRelationId({
793
+ payload: rawData,
794
+ keys: PARENT_SITE_KEYS,
795
+ relationTitle: "Parent Site",
796
+ });
797
+ const networkSiteTypeId = readStrictRelationId({
798
+ payload: rawData,
799
+ keys: NETWORK_SITE_TYPE_KEYS,
800
+ relationTitle: "Network Site Type",
801
+ });
180
802
  if (parentSiteId) {
181
- const parent = await this.findOneById({
803
+ parentSite = await this.findOneById({
182
804
  id: parentSiteId,
183
805
  select: {
184
806
  _id: true,
185
807
  projectId: true,
808
+ networkSiteTypeId: true,
186
809
  },
187
810
  props: {
188
811
  isRoot: true,
189
812
  },
190
813
  });
191
- if (!parent) {
814
+ if (!parentSite) {
192
815
  throw new BadDataException("Parent site not found.");
193
816
  }
194
- if (createBy.data.projectId &&
195
- parent.projectId &&
196
- parent.projectId.toString() !== createBy.data.projectId.toString()) {
817
+ if (projectId &&
818
+ parentSite.projectId &&
819
+ !sameId(parentSite.projectId, projectId)) {
197
820
  throw new BadDataException("Parent site must belong to the same project.");
198
821
  }
822
+ }
823
+ await this.validateSiteTypeEdge({
824
+ networkSiteTypeId: networkSiteTypeId,
825
+ parentSite: parentSite,
826
+ projectId: projectId,
827
+ typeCache: new Map(),
828
+ });
829
+ /*
830
+ * The monitoring defaults are tenant-checked on the way in, in both
831
+ * spellings. See the constants for why a bad value here is not confined
832
+ * to this row.
833
+ */
834
+ const probeId = readStrictRelationId({
835
+ payload: rawData,
836
+ keys: PROBE_KEYS,
837
+ relationTitle: "Probe",
838
+ });
839
+ if (probeId) {
840
+ await this.assertProbeIsAttachableToProject({
841
+ probeId: probeId,
842
+ projectId: projectId,
843
+ });
844
+ }
845
+ const snmpCredentialProfileId = readStrictRelationId({
846
+ payload: rawData,
847
+ keys: SNMP_CREDENTIAL_PROFILE_KEYS,
848
+ relationTitle: "SNMP Credential Profile",
849
+ });
850
+ if (snmpCredentialProfileId) {
851
+ await this.assertSnmpCredentialProfileBelongsToProject({
852
+ snmpCredentialProfileId: snmpCredentialProfileId,
853
+ projectId: projectId,
854
+ });
855
+ }
856
+ if (parentSiteId) {
199
857
  parentPath = await this.getMaterializedPathForSite(parentSiteId);
200
858
  }
201
859
  return {
@@ -224,25 +882,85 @@ export class Service extends DatabaseService {
224
882
  return createdItem;
225
883
  }
226
884
  async onBeforeUpdate(updateBy) {
227
- const dataKeys = Object.keys(updateBy.data || {});
228
- if (!RelationIdUtil.isWritten(dataKeys, PARENT_SITE_KEYS)) {
885
+ const rawData = updateBy.data;
886
+ const touchesParent = isRelationWritten(rawData, PARENT_SITE_KEYS);
887
+ const touchesNetworkSiteType = isRelationWritten(rawData, NETWORK_SITE_TYPE_KEYS);
888
+ const touchesProject = isRelationWritten(rawData, PROJECT_KEYS);
889
+ const touchesMaterializedHierarchy = isRelationWritten(rawData, MATERIALIZED_HIERARCHY_KEYS);
890
+ /*
891
+ * These two have to be named in the early return, and that is the whole
892
+ * reason they are here. Setting a site's default probe or credential
893
+ * profile touches neither the parent nor the type, so a tenancy guard
894
+ * placed below the return would be dead code on exactly the write it
895
+ * exists for — the shape the site settings form actually posts.
896
+ */
897
+ const touchesProbe = isRelationWritten(rawData, PROBE_KEYS);
898
+ const touchesSnmpCredentialProfile = isRelationWritten(rawData, SNMP_CREDENTIAL_PROFILE_KEYS);
899
+ if (!touchesParent &&
900
+ !touchesNetworkSiteType &&
901
+ !touchesProject &&
902
+ !touchesMaterializedHierarchy &&
903
+ !touchesProbe &&
904
+ !touchesSnmpCredentialProfile) {
229
905
  return { updateBy, carryForward: null };
230
906
  }
231
- const newParentId = RelationIdUtil.read(updateBy.data, PARENT_SITE_KEYS);
907
+ if (touchesMaterializedHierarchy) {
908
+ throw new BadDataException("materializedPath and depth are managed by the Network Site hierarchy and cannot be updated directly.");
909
+ }
910
+ const proposedProjectId = touchesProject
911
+ ? readStrictRelationId({
912
+ payload: rawData,
913
+ keys: PROJECT_KEYS,
914
+ relationTitle: "Project",
915
+ })
916
+ : null;
917
+ if (touchesProject && !proposedProjectId) {
918
+ throw new BadDataException("A Network Site cannot be moved to another project.");
919
+ }
920
+ const newParentId = touchesParent
921
+ ? readStrictRelationId({
922
+ payload: rawData,
923
+ keys: PARENT_SITE_KEYS,
924
+ relationTitle: "Parent Site",
925
+ })
926
+ : null;
927
+ const newNetworkSiteTypeId = touchesNetworkSiteType
928
+ ? readStrictRelationId({
929
+ payload: rawData,
930
+ keys: NETWORK_SITE_TYPE_KEYS,
931
+ relationTitle: "Network Site Type",
932
+ })
933
+ : null;
934
+ const updateLimit = updateBy.limit instanceof PositiveNumber
935
+ ? updateBy.limit.toNumber()
936
+ : updateBy.limit || LIMIT_MAX;
937
+ const updateSkip = updateBy.skip instanceof PositiveNumber
938
+ ? updateBy.skip.toNumber()
939
+ : updateBy.skip || 0;
232
940
  const previousItems = await this.findBy({
233
941
  query: this.scopeQueryToCallerTenant(updateBy.query, updateBy.props),
234
942
  select: {
235
943
  _id: true,
236
944
  projectId: true,
237
945
  parentSiteId: true,
946
+ networkSiteTypeId: true,
238
947
  materializedPath: true,
239
948
  },
240
- limit: LIMIT_MAX,
241
- skip: 0,
949
+ limit: updateLimit,
950
+ skip: updateSkip,
242
951
  props: {
243
952
  isRoot: true,
244
953
  },
245
954
  });
955
+ if (touchesProject) {
956
+ for (const item of previousItems) {
957
+ if (!item.projectId ||
958
+ !proposedProjectId ||
959
+ !sameId(item.projectId, proposedProjectId)) {
960
+ throw new BadDataException("A Network Site cannot be moved to another project.");
961
+ }
962
+ }
963
+ }
246
964
  /*
247
965
  * Same-project assertion for EVERY parentSiteId write, including the
248
966
  * detach case (parentSiteId: null) which has no parent to compare
@@ -252,33 +970,84 @@ export class Service extends DatabaseService {
252
970
  if (updateBy.props.tenantId) {
253
971
  for (const item of previousItems) {
254
972
  if (item.projectId &&
255
- item.projectId.toString() !== updateBy.props.tenantId.toString()) {
973
+ !sameId(item.projectId, updateBy.props.tenantId)) {
256
974
  throw new BadDataException("Network site must belong to the same project.");
257
975
  }
258
976
  }
259
977
  }
978
+ /*
979
+ * One check per DISTINCT project in the matched set, because a single
980
+ * updateBy can span more than one project when a root caller issues it,
981
+ * and the payload names one probe / one profile for all of them. Reading
982
+ * the ids here (rather than above the previousItems read) keeps a
983
+ * conflicting-spelling payload refused on every write shape.
984
+ */
985
+ if (touchesProbe || touchesSnmpCredentialProfile) {
986
+ const newProbeId = touchesProbe
987
+ ? readStrictRelationId({
988
+ payload: rawData,
989
+ keys: PROBE_KEYS,
990
+ relationTitle: "Probe",
991
+ })
992
+ : null;
993
+ const newSnmpCredentialProfileId = touchesSnmpCredentialProfile
994
+ ? readStrictRelationId({
995
+ payload: rawData,
996
+ keys: SNMP_CREDENTIAL_PROFILE_KEYS,
997
+ relationTitle: "SNMP Credential Profile",
998
+ })
999
+ : null;
1000
+ // A clear (null) points at nothing and has nothing to check.
1001
+ if (newProbeId || newSnmpCredentialProfileId) {
1002
+ const checkedProjectIds = new Set();
1003
+ for (const item of previousItems) {
1004
+ if (!item.projectId ||
1005
+ checkedProjectIds.has(normalizeId(item.projectId))) {
1006
+ continue;
1007
+ }
1008
+ checkedProjectIds.add(normalizeId(item.projectId));
1009
+ if (newProbeId) {
1010
+ await this.assertProbeIsAttachableToProject({
1011
+ probeId: newProbeId,
1012
+ projectId: item.projectId,
1013
+ });
1014
+ }
1015
+ if (newSnmpCredentialProfileId) {
1016
+ await this.assertSnmpCredentialProfileBelongsToProject({
1017
+ snmpCredentialProfileId: newSnmpCredentialProfileId,
1018
+ projectId: item.projectId,
1019
+ });
1020
+ }
1021
+ }
1022
+ }
1023
+ }
1024
+ if (!touchesParent && !touchesNetworkSiteType) {
1025
+ return { updateBy, carryForward: null };
1026
+ }
260
1027
  let newParentPath = null;
261
- if (newParentId) {
262
- const parent = await this.findOneById({
1028
+ let newParent = null;
1029
+ if (touchesParent && newParentId) {
1030
+ newParent = await this.findOneById({
263
1031
  id: newParentId,
264
1032
  select: {
265
1033
  _id: true,
266
1034
  projectId: true,
1035
+ networkSiteTypeId: true,
267
1036
  },
268
1037
  props: {
269
1038
  isRoot: true,
270
1039
  },
271
1040
  });
272
- if (!parent) {
1041
+ if (!newParent) {
273
1042
  throw new BadDataException("Parent site not found.");
274
1043
  }
275
1044
  for (const item of previousItems) {
276
- if (item.id && item.id.toString() === newParentId.toString()) {
1045
+ if (item.id && sameId(item.id, newParentId)) {
277
1046
  throw new BadDataException("A site cannot be its own parent.");
278
1047
  }
279
1048
  if (item.projectId &&
280
- parent.projectId &&
281
- item.projectId.toString() !== parent.projectId.toString()) {
1049
+ newParent.projectId &&
1050
+ !sameId(item.projectId, newParent.projectId)) {
282
1051
  throw new BadDataException("Parent site must belong to the same project.");
283
1052
  }
284
1053
  }
@@ -290,6 +1059,66 @@ export class Service extends DatabaseService {
290
1059
  }
291
1060
  }
292
1061
  }
1062
+ const typeCache = new Map();
1063
+ const existingParents = new Map();
1064
+ for (const item of previousItems) {
1065
+ let proposedParent = null;
1066
+ if (touchesParent) {
1067
+ proposedParent = newParent;
1068
+ }
1069
+ else if (item.parentSiteId) {
1070
+ const currentParentId = normalizeId(item.parentSiteId);
1071
+ proposedParent = existingParents.get(currentParentId) || null;
1072
+ if (!proposedParent) {
1073
+ proposedParent = await this.findOneById({
1074
+ id: item.parentSiteId,
1075
+ select: {
1076
+ _id: true,
1077
+ projectId: true,
1078
+ networkSiteTypeId: true,
1079
+ },
1080
+ props: {
1081
+ isRoot: true,
1082
+ },
1083
+ });
1084
+ if (!proposedParent) {
1085
+ throw new BadDataException("Parent site not found.");
1086
+ }
1087
+ existingParents.set(currentParentId, proposedParent);
1088
+ }
1089
+ if (item.projectId &&
1090
+ proposedParent.projectId &&
1091
+ !sameId(item.projectId, proposedParent.projectId)) {
1092
+ throw new BadDataException("Parent site must belong to the same project.");
1093
+ }
1094
+ }
1095
+ const proposedNetworkSiteTypeId = touchesNetworkSiteType
1096
+ ? newNetworkSiteTypeId
1097
+ : item.networkSiteTypeId || null;
1098
+ await this.validateSiteTypeEdge({
1099
+ networkSiteTypeId: proposedNetworkSiteTypeId,
1100
+ parentSite: proposedParent,
1101
+ projectId: item.projectId,
1102
+ typeCache: typeCache,
1103
+ });
1104
+ const previousNetworkSiteTypeId = item.networkSiteTypeId
1105
+ ? normalizeId(item.networkSiteTypeId)
1106
+ : null;
1107
+ const proposedNetworkSiteTypeIdString = proposedNetworkSiteTypeId
1108
+ ? normalizeId(proposedNetworkSiteTypeId)
1109
+ : null;
1110
+ if (touchesNetworkSiteType &&
1111
+ previousNetworkSiteTypeId !== proposedNetworkSiteTypeIdString) {
1112
+ await this.validateDirectChildrenForTypeChange({
1113
+ site: item,
1114
+ proposedNetworkSiteTypeId: proposedNetworkSiteTypeId,
1115
+ typeCache: typeCache,
1116
+ });
1117
+ }
1118
+ }
1119
+ if (!touchesParent) {
1120
+ return { updateBy, carryForward: null };
1121
+ }
293
1122
  const carryForward = {
294
1123
  previousItems: previousItems,
295
1124
  newParentId: newParentId,
@@ -339,7 +1168,19 @@ export class Service extends DatabaseService {
339
1168
  const updatedIds = new Set(updatedItemIds.map((id) => {
340
1169
  return id.toString();
341
1170
  }));
342
- for (const previousItem of parentChange.previousItems) {
1171
+ /*
1172
+ * A bulk update can include both an ancestor and one of its descendants.
1173
+ * Move the deepest roots first; otherwise rebasing the ancestor changes
1174
+ * the descendant branch away from the latter's old prefix before its own
1175
+ * subtree has been processed.
1176
+ */
1177
+ const previousItemsDeepestFirst = [
1178
+ ...parentChange.previousItems,
1179
+ ].sort((left, right) => {
1180
+ return (MaterializedPathUtil.segmentsOf(right.materializedPath).length -
1181
+ MaterializedPathUtil.segmentsOf(left.materializedPath).length);
1182
+ });
1183
+ for (const previousItem of previousItemsDeepestFirst) {
343
1184
  if (!previousItem.id || !updatedIds.has(previousItem.id.toString())) {
344
1185
  continue;
345
1186
  }
@@ -358,35 +1199,43 @@ export class Service extends DatabaseService {
358
1199
  });
359
1200
  // ...then its entire subtree in one prefix query.
360
1201
  if (oldPath) {
361
- const descendants = await this.findBy({
362
- query: {
363
- projectId: previousItem.projectId,
364
- materializedPath: this.pathStartsWith(oldPath),
365
- },
366
- select: {
367
- _id: true,
368
- materializedPath: true,
369
- },
370
- limit: LIMIT_MAX,
371
- skip: 0,
372
- props: {
373
- isRoot: true,
374
- },
375
- });
376
- for (const descendant of descendants) {
377
- if (!descendant.id ||
378
- !descendant.materializedPath ||
379
- descendant.id.toString() === previousItem.id.toString()) {
380
- continue;
381
- }
382
- const rebasedPath = MaterializedPathUtil.rebasePaths(oldPath, newPath, [descendant.materializedPath])[0];
383
- await this.updateColumnsByIdWithoutHooks({
384
- id: descendant.id,
385
- data: {
386
- materializedPath: rebasedPath,
387
- depth: MaterializedPathUtil.depthOf(rebasedPath),
1202
+ while (true) {
1203
+ const descendants = await this.findBy({
1204
+ query: {
1205
+ projectId: previousItem.projectId,
1206
+ materializedPath: this.pathStartsWith(oldPath),
1207
+ },
1208
+ select: {
1209
+ _id: true,
1210
+ materializedPath: true,
1211
+ },
1212
+ sort: {
1213
+ _id: SortOrder.Ascending,
1214
+ },
1215
+ limit: SUBTREE_REBASE_PAGE_SIZE,
1216
+ skip: 0,
1217
+ props: {
1218
+ isRoot: true,
388
1219
  },
389
1220
  });
1221
+ for (const descendant of descendants) {
1222
+ if (!descendant.id ||
1223
+ !descendant.materializedPath ||
1224
+ descendant.id.toString() === previousItem.id.toString()) {
1225
+ continue;
1226
+ }
1227
+ const rebasedPath = MaterializedPathUtil.rebasePaths(oldPath, newPath, [descendant.materializedPath])[0];
1228
+ await this.updateColumnsByIdWithoutHooks({
1229
+ id: descendant.id,
1230
+ data: {
1231
+ materializedPath: rebasedPath,
1232
+ depth: MaterializedPathUtil.depthOf(rebasedPath),
1233
+ },
1234
+ });
1235
+ }
1236
+ if (descendants.length < SUBTREE_REBASE_PAGE_SIZE) {
1237
+ break;
1238
+ }
390
1239
  }
391
1240
  }
392
1241
  /*
@@ -417,35 +1266,97 @@ export class Service extends DatabaseService {
417
1266
  * checked.
418
1267
  */
419
1268
  async onBeforeDelete(deleteBy) {
1269
+ const deleteLimit = deleteBy.limit instanceof PositiveNumber
1270
+ ? deleteBy.limit.toNumber()
1271
+ : deleteBy.limit || LIMIT_MAX;
1272
+ const deleteSkip = deleteBy.skip instanceof PositiveNumber
1273
+ ? deleteBy.skip.toNumber()
1274
+ : deleteBy.skip || 0;
420
1275
  const sitesToDelete = await this.findBy({
421
- query: this.scopeQueryToCallerTenant(deleteBy.query, deleteBy.props),
1276
+ query: this.scopeDeleteQueryToCallerTenant(deleteBy.query, deleteBy.props),
422
1277
  select: {
423
1278
  _id: true,
424
1279
  projectId: true,
425
1280
  parentSiteId: true,
426
1281
  materializedPath: true,
427
1282
  },
428
- limit: LIMIT_MAX,
429
- skip: 0,
1283
+ limit: deleteLimit,
1284
+ skip: deleteSkip,
430
1285
  props: {
431
1286
  isRoot: true,
432
1287
  },
433
1288
  });
1289
+ const deletingSiteIds = new Set(sitesToDelete
1290
+ .map((site) => {
1291
+ return site.id ? normalizeId(site.id) : null;
1292
+ })
1293
+ .filter((siteId) => {
1294
+ return Boolean(siteId);
1295
+ }));
1296
+ const deletingSiteIdList = Array.from(deletingSiteIds);
1297
+ const deletingProjectIdList = Array.from(new Set(sitesToDelete
1298
+ .map((site) => {
1299
+ return site.projectId ? normalizeId(site.projectId) : null;
1300
+ })
1301
+ .filter((projectId) => {
1302
+ return Boolean(projectId);
1303
+ })));
1304
+ /*
1305
+ * A valid child type explicitly names the deleted site's type as its
1306
+ * required direct parent type. Legacy SET NULL/orphan-repair behaviour
1307
+ * therefore cannot produce a valid edge: promoting the child to root or
1308
+ * attaching it to the grandparent both violate its type. Reject the
1309
+ * delete unless every direct child is part of this same bulk delete.
1310
+ *
1311
+ * Parent ids and result rows are both batched so neither a wide delete nor
1312
+ * a wide site is silently truncated at a service query limit.
1313
+ */
1314
+ for (let parentIdOffset = 0; parentIdOffset < deletingSiteIdList.length; parentIdOffset += DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE) {
1315
+ const parentIdBatch = deletingSiteIdList.slice(parentIdOffset, parentIdOffset + DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE);
1316
+ let childSkip = 0;
1317
+ while (true) {
1318
+ const directChildren = await this.findBy({
1319
+ query: {
1320
+ projectId: QueryHelper.any(deletingProjectIdList),
1321
+ parentSiteId: QueryHelper.any(parentIdBatch),
1322
+ },
1323
+ select: {
1324
+ _id: true,
1325
+ parentSiteId: true,
1326
+ },
1327
+ sort: {
1328
+ _id: SortOrder.Ascending,
1329
+ },
1330
+ limit: DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE,
1331
+ skip: childSkip,
1332
+ props: {
1333
+ isRoot: true,
1334
+ },
1335
+ });
1336
+ const hasSurvivingChild = directChildren.some((child) => {
1337
+ return !child.id || !deletingSiteIds.has(normalizeId(child.id));
1338
+ });
1339
+ if (hasSurvivingChild) {
1340
+ throw new BadDataException("A network site with child sites cannot be deleted. Move or delete its child sites first.");
1341
+ }
1342
+ if (directChildren.length < DIRECT_CHILD_TYPE_VALIDATION_PAGE_SIZE) {
1343
+ break;
1344
+ }
1345
+ childSkip += directChildren.length;
1346
+ }
1347
+ }
434
1348
  const carryForward = {
435
1349
  sitesToDelete: sitesToDelete,
436
1350
  };
437
1351
  return { deleteBy, carryForward };
438
1352
  }
439
1353
  /*
440
- * parentSiteId is declared onDelete: "SET NULL", so Postgres detaches the
441
- * deleted site's direct children but leaves their materializedPath - and
442
- * their whole subtree's - routed through a row that no longer exists. That
443
- * strands the subtree: the children endpoint (which reads parentSiteId)
444
- * shows them at the root while the rollup engine (which reads
445
- * materializedPath) still folds them into the dead ancestor's chain, double
446
- * counting every outage. Re-attach the orphans to the deleted site's own
447
- * parent - NULL when it was a root, which is exactly what the FK did - and
448
- * rebase the subtree paths so both readers agree again.
1354
+ * New schemas use a non-nullifying foreign key and onBeforeDelete rejects
1355
+ * surviving children, so this repair is normally a no-op. Keep it as a
1356
+ * defensive bridge for a database that has not applied the FK migration
1357
+ * yet, or for a legacy write already in flight during an upgrade: reattach
1358
+ * any detached children and rebase their subtree paths so parentSiteId and
1359
+ * materializedPath agree.
449
1360
  */
450
1361
  async onDeleteSuccess(onDelete, itemIdsBeforeDelete) {
451
1362
  const carryForward = onDelete.carryForward || null;
@@ -493,47 +1404,55 @@ export class Service extends DatabaseService {
493
1404
  ? await this.getMaterializedPathForSite(deletedSite.parentSiteId)
494
1405
  : null;
495
1406
  const deletedDepth = MaterializedPathUtil.segmentsOf(oldPath).length;
496
- const descendants = await this.findBy({
497
- query: {
498
- projectId: deletedSite.projectId,
499
- materializedPath: this.pathStartsWith(oldPath),
500
- },
501
- select: {
502
- _id: true,
503
- materializedPath: true,
504
- },
505
- limit: LIMIT_MAX,
506
- skip: 0,
507
- props: {
508
- isRoot: true,
509
- },
510
- });
511
- for (const descendant of descendants) {
512
- if (!descendant.id ||
513
- !descendant.materializedPath ||
514
- descendant.id.toString() === deletedSite.id.toString()) {
515
- continue;
516
- }
517
- const tailSegments = MaterializedPathUtil.segmentsOf(descendant.materializedPath).slice(deletedDepth);
518
- if (tailSegments.length === 0) {
519
- continue;
520
- }
521
- let rebasedPath = parentPath;
522
- for (const segment of tailSegments) {
523
- rebasedPath = MaterializedPathUtil.buildPath(rebasedPath, segment);
1407
+ while (true) {
1408
+ const descendants = await this.findBy({
1409
+ query: {
1410
+ projectId: deletedSite.projectId,
1411
+ materializedPath: this.pathStartsWith(oldPath),
1412
+ },
1413
+ select: {
1414
+ _id: true,
1415
+ materializedPath: true,
1416
+ },
1417
+ sort: {
1418
+ _id: SortOrder.Ascending,
1419
+ },
1420
+ limit: SUBTREE_REBASE_PAGE_SIZE,
1421
+ skip: 0,
1422
+ props: {
1423
+ isRoot: true,
1424
+ },
1425
+ });
1426
+ for (const descendant of descendants) {
1427
+ if (!descendant.id ||
1428
+ !descendant.materializedPath ||
1429
+ descendant.id.toString() === deletedSite.id.toString()) {
1430
+ continue;
1431
+ }
1432
+ const tailSegments = MaterializedPathUtil.segmentsOf(descendant.materializedPath).slice(deletedDepth);
1433
+ if (tailSegments.length === 0) {
1434
+ continue;
1435
+ }
1436
+ let rebasedPath = parentPath;
1437
+ for (const segment of tailSegments) {
1438
+ rebasedPath = MaterializedPathUtil.buildPath(rebasedPath, segment);
1439
+ }
1440
+ const updateData = {
1441
+ materializedPath: rebasedPath,
1442
+ depth: MaterializedPathUtil.depthOf(rebasedPath),
1443
+ };
1444
+ // A direct child is the one the FK just detached - give it a parent back.
1445
+ if (tailSegments.length === 1) {
1446
+ updateData["parentSiteId"] = deletedSite.parentSiteId || null;
1447
+ }
1448
+ await this.updateColumnsByIdWithoutHooks({
1449
+ id: descendant.id,
1450
+ data: updateData,
1451
+ });
524
1452
  }
525
- const data = {
526
- materializedPath: rebasedPath,
527
- depth: MaterializedPathUtil.depthOf(rebasedPath),
528
- };
529
- // A direct child is the one the FK just detached - give it a parent back.
530
- if (tailSegments.length === 1) {
531
- data["parentSiteId"] = deletedSite.parentSiteId || null;
1453
+ if (descendants.length < SUBTREE_REBASE_PAGE_SIZE) {
1454
+ break;
532
1455
  }
533
- await this.updateColumnsByIdWithoutHooks({
534
- id: descendant.id,
535
- data: data,
536
- });
537
1456
  }
538
1457
  /*
539
1458
  * The deleted site's own devices were detached by their FK too, so the
@@ -1243,8 +2162,11 @@ export class Service extends DatabaseService {
1243
2162
  * ------------------------------------------------------------------
1244
2163
  */
1245
2164
  /*
1246
- * Called by MonitorService.changeMonitorStatus after a status persists.
1247
- * Resolves which NetworkDevices these monitors report on, stamps those
2165
+ * Called after a monitor's current status persists - from
2166
+ * MonitorStatusTimelineService.onCreateSuccess / onDeleteSuccess (the one
2167
+ * path every status change, probe-driven or manual, passes through) and
2168
+ * from MonitorService.refreshMonitorCurrentStatus when a repair moves the
2169
+ * id. Resolves which NetworkDevices these monitors report on, stamps those
1248
2170
  * devices' currentMonitorStatusId, then recomputes the rollup for every
1249
2171
  * affected site chain. Never throws - a rollup failure must never break a
1250
2172
  * monitor status change.
@@ -1258,6 +2180,26 @@ export class Service extends DatabaseService {
1258
2180
  * monitor-backed path (monitoringMethod "Monitor") for gear that does
1259
2181
  * not speak SNMP, so ANY monitor type qualifies — a Ping monitor on an
1260
2182
  * access point is the whole point of it.
2183
+ *
2184
+ * What gets stamped depends on how the device is monitored:
2185
+ *
2186
+ * - every device gets currentMonitorStatusId, which the pill, the site
2187
+ * rollup and the topology node read.
2188
+ * - a MONITOR-BACKED device also gets isReachable, derived from the
2189
+ * status row (`!isOfflineState`, the same offline-end reading
2190
+ * DeviceReachabilityUtil uses). Nothing polls such a device, so the
2191
+ * column is NULL forever otherwise - and the device list's summary
2192
+ * tiles and its Status filter count and filter on isReachable in SQL,
2193
+ * which is why a bound ping-only device sat under "Pending" there while
2194
+ * its own pill said Up. The server keeps the two in sync so the list
2195
+ * agrees with itself.
2196
+ * - an SNMP device's isReachable is left alone: the walk owns it
2197
+ * (NetworkInventoryUtil.updateFromWalk), and a Network Device monitor
2198
+ * going Degraded must not overwrite what the probe actually found.
2199
+ *
2200
+ * The status row is read at most once per call, and only when at least one
2201
+ * collected device is monitor-backed, so an all-SNMP estate costs no extra
2202
+ * query.
1261
2203
  */
1262
2204
  async onMonitorStatusChanged(data) {
1263
2205
  try {
@@ -1306,6 +2248,8 @@ export class Service extends DatabaseService {
1306
2248
  select: {
1307
2249
  _id: true,
1308
2250
  siteId: true,
2251
+ // Decides whether isReachable is stamped alongside the status.
2252
+ monitoringMethod: true,
1309
2253
  },
1310
2254
  limit: LIMIT_MAX,
1311
2255
  skip: 0,
@@ -1322,6 +2266,8 @@ export class Service extends DatabaseService {
1322
2266
  select: {
1323
2267
  _id: true,
1324
2268
  siteId: true,
2269
+ // Decides whether isReachable is stamped alongside the status.
2270
+ monitoringMethod: true,
1325
2271
  },
1326
2272
  limit: LIMIT_MAX,
1327
2273
  skip: 0,
@@ -1333,15 +2279,65 @@ export class Service extends DatabaseService {
1333
2279
  if (devices.length === 0) {
1334
2280
  return;
1335
2281
  }
2282
+ /*
2283
+ * Resolve the status row lazily - once, and only if a monitor-backed
2284
+ * device is in the set - because it is only needed to derive
2285
+ * isReachable, and the SNMP-only case must stay one query cheaper.
2286
+ *
2287
+ * `undefined` afterwards means "do not touch isReachable": either no
2288
+ * device needs it, or the row could not be found in this project (a
2289
+ * status deleted between the timeline write and this call, or one
2290
+ * from another tenant). The id is still stamped in that case so the
2291
+ * pill keeps moving; a stale reachability is the lesser harm next to
2292
+ * a device that stops reporting altogether.
2293
+ */
2294
+ const hasMonitorBackedDevice = devices.some((device) => {
2295
+ return NetworkDeviceMonitoringMethodUtil.isMonitorBacked(device.monitoringMethod);
2296
+ });
2297
+ let monitorBackedIsReachable = undefined;
2298
+ if (hasMonitorBackedDevice) {
2299
+ const status = await MonitorStatusService.findOneBy({
2300
+ query: {
2301
+ _id: data.monitorStatusId,
2302
+ projectId: data.projectId,
2303
+ },
2304
+ select: {
2305
+ _id: true,
2306
+ isOfflineState: true,
2307
+ },
2308
+ props: {
2309
+ isRoot: true,
2310
+ },
2311
+ });
2312
+ if (status) {
2313
+ monitorBackedIsReachable = !status.isOfflineState;
2314
+ }
2315
+ else {
2316
+ logger.warn(`NetworkSiteService.onMonitorStatusChanged: monitor status ${data.monitorStatusId.toString()} was not found in project ${data.projectId.toString()}; stamping the status id on the bound network devices but leaving isReachable unchanged.`, {
2317
+ projectId: data.projectId.toString(),
2318
+ monitorStatusId: data.monitorStatusId.toString(),
2319
+ });
2320
+ }
2321
+ }
1336
2322
  for (const device of devices) {
1337
2323
  if (!device.id) {
1338
2324
  continue;
1339
2325
  }
2326
+ const stamp = {
2327
+ currentMonitorStatusId: data.monitorStatusId,
2328
+ };
2329
+ /*
2330
+ * Only a monitor-backed device gets isReachable from here. The key
2331
+ * is left OUT of the payload for an SNMP device rather than set to
2332
+ * undefined, so the walk's verdict is provably untouched.
2333
+ */
2334
+ if (monitorBackedIsReachable !== undefined &&
2335
+ NetworkDeviceMonitoringMethodUtil.isMonitorBacked(device.monitoringMethod)) {
2336
+ stamp.isReachable = monitorBackedIsReachable;
2337
+ }
1340
2338
  await NetworkDeviceService.updateColumnsByIdWithoutHooks({
1341
2339
  id: device.id,
1342
- data: {
1343
- currentMonitorStatusId: data.monitorStatusId,
1344
- },
2340
+ data: stamp,
1345
2341
  });
1346
2342
  }
1347
2343
  const distinctSiteIds = new Map();
@@ -1362,12 +2358,54 @@ export class Service extends DatabaseService {
1362
2358
  }
1363
2359
  }
1364
2360
  }
2361
+ __decorate([
2362
+ CaptureSpan(),
2363
+ __metadata("design:type", Function),
2364
+ __metadata("design:paramtypes", [Object]),
2365
+ __metadata("design:returntype", Promise)
2366
+ ], Service.prototype, "create", null);
2367
+ __decorate([
2368
+ CaptureSpan(),
2369
+ __metadata("design:type", Function),
2370
+ __metadata("design:paramtypes", [Object]),
2371
+ __metadata("design:returntype", Promise)
2372
+ ], Service.prototype, "updateOneBy", null);
2373
+ __decorate([
2374
+ CaptureSpan(),
2375
+ __metadata("design:type", Function),
2376
+ __metadata("design:paramtypes", [Object]),
2377
+ __metadata("design:returntype", Promise)
2378
+ ], Service.prototype, "updateBy", null);
2379
+ __decorate([
2380
+ CaptureSpan(),
2381
+ __metadata("design:type", Function),
2382
+ __metadata("design:paramtypes", [Object]),
2383
+ __metadata("design:returntype", Promise)
2384
+ ], Service.prototype, "deleteOneBy", null);
2385
+ __decorate([
2386
+ CaptureSpan(),
2387
+ __metadata("design:type", Function),
2388
+ __metadata("design:paramtypes", [Object]),
2389
+ __metadata("design:returntype", Promise)
2390
+ ], Service.prototype, "deleteBy", null);
2391
+ __decorate([
2392
+ CaptureSpan(),
2393
+ __metadata("design:type", Function),
2394
+ __metadata("design:paramtypes", [Object]),
2395
+ __metadata("design:returntype", Promise)
2396
+ ], Service.prototype, "hardDeleteBy", null);
1365
2397
  __decorate([
1366
2398
  CaptureSpan(),
1367
2399
  __metadata("design:type", Function),
1368
2400
  __metadata("design:paramtypes", [Object]),
1369
2401
  __metadata("design:returntype", Promise)
1370
2402
  ], Service.prototype, "getStatusCounts", null);
2403
+ __decorate([
2404
+ CaptureSpan(),
2405
+ __metadata("design:type", Function),
2406
+ __metadata("design:paramtypes", [ObjectID]),
2407
+ __metadata("design:returntype", Promise)
2408
+ ], Service.prototype, "resolveDefaultProbeIdForSite", null);
1371
2409
  __decorate([
1372
2410
  CaptureSpan(),
1373
2411
  __metadata("design:type", Function),