@oneuptime/common 12.0.1 → 12.0.3

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 (385) hide show
  1. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
  2. package/Models/DatabaseModels/Host.ts +12 -4
  3. package/Models/DatabaseModels/Index.ts +2 -0
  4. package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
  5. package/Models/DatabaseModels/KubernetesResource.ts +8 -7
  6. package/Models/DatabaseModels/LogDropFilter.ts +9 -20
  7. package/Models/DatabaseModels/PodmanResource.ts +6 -6
  8. package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
  9. package/Models/DatabaseModels/StatusPage.ts +95 -0
  10. package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
  11. package/Models/DatabaseModels/StatusPageResource.ts +73 -0
  12. package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
  13. package/Server/API/BaseAPI.ts +13 -2
  14. package/Server/API/DashboardAPI.ts +39 -0
  15. package/Server/API/TeamMemberAPI.ts +85 -0
  16. package/Server/EnvironmentConfig.ts +31 -1
  17. package/Server/Infrastructure/GlobalCache.ts +115 -0
  18. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.ts +84 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  25. package/Server/Middleware/ProjectAuthorization.ts +66 -4
  26. package/Server/Services/AlertStateTimelineService.ts +20 -0
  27. package/Server/Services/BillingService.ts +600 -51
  28. package/Server/Services/CephClusterService.ts +27 -45
  29. package/Server/Services/CephResourceService.ts +43 -4
  30. package/Server/Services/CloudResourceService.ts +24 -42
  31. package/Server/Services/DatabaseService.ts +198 -6
  32. package/Server/Services/DockerHostService.ts +20 -38
  33. package/Server/Services/DockerResourceService.ts +62 -8
  34. package/Server/Services/DockerSwarmClusterService.ts +28 -46
  35. package/Server/Services/DockerSwarmResourceService.ts +53 -4
  36. package/Server/Services/HostService.ts +47 -57
  37. package/Server/Services/IncidentStateTimelineService.ts +20 -0
  38. package/Server/Services/Index.ts +2 -0
  39. package/Server/Services/IoTDeviceService.ts +1 -17
  40. package/Server/Services/IoTFleetService.ts +20 -38
  41. package/Server/Services/KubernetesClusterService.ts +18 -36
  42. package/Server/Services/KubernetesContainerService.ts +50 -9
  43. package/Server/Services/KubernetesResourceService.ts +53 -7
  44. package/Server/Services/LabelService.ts +153 -1
  45. package/Server/Services/MetricTypeService.ts +84 -0
  46. package/Server/Services/MonitorService.ts +146 -14
  47. package/Server/Services/MonitorStatusTimelineService.ts +29 -0
  48. package/Server/Services/MonitorTemplateService.ts +65 -0
  49. package/Server/Services/PodmanHostService.ts +20 -38
  50. package/Server/Services/PodmanResourceService.ts +62 -8
  51. package/Server/Services/ProjectService.ts +165 -35
  52. package/Server/Services/ProxmoxClusterService.ts +24 -42
  53. package/Server/Services/ProxmoxResourceService.ts +43 -4
  54. package/Server/Services/RumApplicationService.ts +20 -38
  55. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
  56. package/Server/Services/ServerlessFunctionService.ts +25 -43
  57. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
  58. package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
  59. package/Server/Services/ServiceService.ts +299 -73
  60. package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
  61. package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
  62. package/Server/Services/StatusPageService.ts +95 -36
  63. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
  64. package/Server/Services/StatusPageSubscriberService.ts +3 -0
  65. package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
  66. package/Server/Services/TelemetryEntityService.ts +1 -0
  67. package/Server/Types/Database/JSONColumnQuery.ts +621 -0
  68. package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
  69. package/Server/Types/Database/QueryHelper.ts +17 -32
  70. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
  71. package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
  72. package/Server/Utils/Monitor/MonitorResource.ts +63 -36
  73. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
  74. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
  75. package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
  76. package/Server/Utils/SingleFlight.ts +83 -0
  77. package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
  78. package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
  79. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
  80. package/Server/Utils/Telemetry/Telemetry.ts +351 -122
  81. package/Tests/App/Dashboard/UsersTableGroupsByPerson.test.tsx +448 -0
  82. package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
  83. package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
  84. package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
  85. package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
  86. package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +704 -0
  87. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
  88. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
  89. package/Tests/Server/Infrastructure/GlobalCache.test.ts +226 -0
  90. package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
  91. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
  92. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
  93. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
  94. package/Tests/Server/Services/BillingService.test.ts +78 -3
  95. package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
  96. package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
  97. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
  98. package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
  99. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
  100. package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
  101. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +658 -0
  102. package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
  103. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
  104. package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
  105. package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
  106. package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
  107. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
  108. package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +650 -0
  109. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
  110. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +636 -0
  111. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
  112. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
  113. package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
  114. package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -0
  115. package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
  116. package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
  117. package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
  118. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +29 -13
  119. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
  120. package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
  121. package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
  122. package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -0
  123. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
  124. package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
  125. package/Tests/Server/Utils/Database/TruncateColumnValue.test.ts +179 -0
  126. package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
  127. package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
  128. package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
  129. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
  130. package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
  131. package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
  132. package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
  133. package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
  134. package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
  135. package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
  136. package/Tests/Types/Decimal.test.ts +37 -0
  137. package/Tests/Types/Events/Recurring.test.ts +157 -0
  138. package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
  139. package/Tests/Types/Port.test.ts +38 -0
  140. package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
  141. package/Tests/UI/Components/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
  142. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
  143. package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
  144. package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
  145. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
  146. package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
  147. package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
  148. package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
  149. package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
  150. package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
  151. package/Types/CustomField/CustomFieldDefinition.ts +24 -0
  152. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  153. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  154. package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
  155. package/Types/Dashboard/DashboardTemplates.ts +397 -0
  156. package/Types/Database/BigIntColumnTransformer.ts +74 -0
  157. package/Types/Database/DatabaseProperty.ts +26 -0
  158. package/Types/Database/NumericColumnValue.ts +128 -0
  159. package/Types/Date.ts +112 -0
  160. package/Types/Decimal.ts +16 -4
  161. package/Types/Events/Recurring.ts +99 -1
  162. package/Types/Permission.ts +48 -0
  163. package/Types/Port.ts +16 -4
  164. package/Types/StatusPage/StatusPageReport.ts +11 -0
  165. package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
  166. package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
  167. package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
  168. package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
  169. package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
  170. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
  171. package/UI/Utils/TeamMembersByUser.ts +302 -0
  172. package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
  173. package/Utils/Dashboard/Components/Index.ts +7 -0
  174. package/Utils/StatusPage/ReportPeriod.ts +236 -0
  175. package/Utils/Telemetry/HostIpAddresses.ts +98 -0
  176. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
  177. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  178. package/build/dist/Models/DatabaseModels/Host.js +12 -4
  179. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  180. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  181. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  182. package/build/dist/Models/DatabaseModels/KubernetesContainer.js +4 -4
  183. package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
  184. package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
  185. package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
  187. package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/PodmanResource.js +6 -6
  189. package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
  190. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
  191. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
  193. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
  195. package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
  196. package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
  197. package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
  199. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
  200. package/build/dist/Server/API/BaseAPI.js +11 -1
  201. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  202. package/build/dist/Server/API/DashboardAPI.js +39 -0
  203. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  204. package/build/dist/Server/API/TeamMemberAPI.js +57 -2
  205. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  206. package/build/dist/Server/EnvironmentConfig.js +27 -1
  207. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  208. package/build/dist/Server/Infrastructure/GlobalCache.js +91 -0
  209. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  210. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
  211. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  212. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js +45 -0
  213. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
  214. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
  215. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
  216. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
  217. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
  218. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
  219. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
  220. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
  221. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
  222. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  223. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  224. package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
  225. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  226. package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
  227. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  228. package/build/dist/Server/Services/BillingService.js +466 -36
  229. package/build/dist/Server/Services/BillingService.js.map +1 -1
  230. package/build/dist/Server/Services/CephClusterService.js +26 -39
  231. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  232. package/build/dist/Server/Services/CephResourceService.js +28 -3
  233. package/build/dist/Server/Services/CephResourceService.js.map +1 -1
  234. package/build/dist/Server/Services/CloudResourceService.js +23 -36
  235. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  236. package/build/dist/Server/Services/DatabaseService.js +155 -2
  237. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  238. package/build/dist/Server/Services/DockerHostService.js +19 -32
  239. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  240. package/build/dist/Server/Services/DockerResourceService.js +34 -4
  241. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  242. package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -40
  243. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  244. package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
  245. package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
  246. package/build/dist/Server/Services/HostService.js +58 -64
  247. package/build/dist/Server/Services/HostService.js.map +1 -1
  248. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  249. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  250. package/build/dist/Server/Services/Index.js +2 -0
  251. package/build/dist/Server/Services/Index.js.map +1 -1
  252. package/build/dist/Server/Services/IoTDeviceService.js +1 -8
  253. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  254. package/build/dist/Server/Services/IoTFleetService.js +19 -32
  255. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  256. package/build/dist/Server/Services/KubernetesClusterService.js +17 -30
  257. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  258. package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
  259. package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
  260. package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
  261. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  262. package/build/dist/Server/Services/LabelService.js +127 -0
  263. package/build/dist/Server/Services/LabelService.js.map +1 -1
  264. package/build/dist/Server/Services/MetricTypeService.js +74 -0
  265. package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
  266. package/build/dist/Server/Services/MonitorService.js +128 -9
  267. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  268. package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
  269. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  270. package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
  271. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  272. package/build/dist/Server/Services/PodmanHostService.js +19 -32
  273. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  274. package/build/dist/Server/Services/PodmanResourceService.js +34 -4
  275. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  276. package/build/dist/Server/Services/ProjectService.js +135 -21
  277. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  278. package/build/dist/Server/Services/ProxmoxClusterService.js +23 -36
  279. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  280. package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
  281. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  282. package/build/dist/Server/Services/RumApplicationService.js +19 -32
  283. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  284. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
  285. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  286. package/build/dist/Server/Services/ServerlessFunctionService.js +24 -37
  287. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  288. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
  289. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
  290. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
  291. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  292. package/build/dist/Server/Services/ServiceService.js +236 -61
  293. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  294. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
  295. package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
  296. package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
  297. package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
  298. package/build/dist/Server/Services/StatusPageService.js +69 -46
  299. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  300. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
  301. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  302. package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
  303. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  304. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
  305. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  306. package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
  307. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  308. package/build/dist/Server/Types/Database/JSONColumnQuery.js +402 -0
  309. package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
  310. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
  311. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
  312. package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
  313. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  314. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
  315. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  316. package/build/dist/Server/Utils/Database/TruncateColumnValue.js +32 -0
  317. package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
  318. package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
  319. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  320. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
  321. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  322. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
  323. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
  324. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
  325. package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
  326. package/build/dist/Server/Utils/SingleFlight.js +76 -0
  327. package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
  328. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
  329. package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
  330. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
  331. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  332. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
  333. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
  334. package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
  335. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  336. package/build/dist/Types/CustomField/CustomFieldDefinition.js +2 -0
  337. package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
  338. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  339. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  340. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  341. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  342. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
  343. package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
  344. package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
  345. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  346. package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
  347. package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
  348. package/build/dist/Types/Database/DatabaseProperty.js +25 -0
  349. package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
  350. package/build/dist/Types/Database/NumericColumnValue.js +97 -0
  351. package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
  352. package/build/dist/Types/Date.js +68 -0
  353. package/build/dist/Types/Date.js.map +1 -1
  354. package/build/dist/Types/Decimal.js +14 -3
  355. package/build/dist/Types/Decimal.js.map +1 -1
  356. package/build/dist/Types/Events/Recurring.js +63 -0
  357. package/build/dist/Types/Events/Recurring.js.map +1 -1
  358. package/build/dist/Types/Permission.js +42 -0
  359. package/build/dist/Types/Permission.js.map +1 -1
  360. package/build/dist/Types/Port.js +15 -5
  361. package/build/dist/Types/Port.js.map +1 -1
  362. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
  363. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
  364. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
  365. package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
  366. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
  367. package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
  368. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  369. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
  370. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  371. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
  372. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
  373. package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
  374. package/build/dist/UI/Utils/TeamMembersByUser.js.map +1 -0
  375. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
  376. package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
  377. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  378. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  379. package/build/dist/Utils/StatusPage/ReportPeriod.js +131 -0
  380. package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
  381. package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
  382. package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
  383. package/jest.config.json +2 -0
  384. package/package.json +1 -1
  385. package/tsconfig.json +12 -1
@@ -7,6 +7,7 @@ import Typeof from "../../../Types/Typeof";
7
7
  import { FindOperator, Raw } from "typeorm";
8
8
  import { FindWhereProperty } from "../../../Types/BaseDatabase/Query";
9
9
  import CaptureSpan from "../../Utils/Telemetry/CaptureSpan";
10
+ import buildJSONColumnQuery, { JSONColumnQuery } from "./JSONColumnQuery";
10
11
 
11
12
  export default class QueryHelper {
12
13
  @CaptureSpan()
@@ -668,40 +669,25 @@ export default class QueryHelper {
668
669
  ) as FindWhereProperty<any>;
669
670
  }
670
671
 
672
+ /**
673
+ * Filter on the keys of a jsonb column — `customFields` above all, whose
674
+ * keys are the names a project gave its custom fields.
675
+ *
676
+ * The per-key predicates (equality, "is any of", contains, numeric
677
+ * comparison, is-empty, and their negations) live in JSONColumnQuery. Two
678
+ * things this used to get wrong and no longer does: the key was spliced
679
+ * straight into the SQL string, and every value was compared with `=`, so an
680
+ * operator such as `Includes` reached Postgres stringified.
681
+ */
671
682
  @CaptureSpan()
672
683
  public static queryJson(value: JSONObject): FindWhereProperty<any> {
673
- // seed random text
674
- const values: JSONObject = {};
675
-
676
- let queryText: string = "";
677
-
678
- if (typeof value === Typeof.String) {
679
- value = JSON.parse(value.toString());
680
- }
681
-
682
684
  if (value instanceof FindOperator) {
683
685
  return value;
684
686
  }
685
687
 
686
- const hasValue: boolean = value && Object.keys(value).length > 0;
687
-
688
- for (const key in value) {
689
- const temp: string = Text.generateRandomText(10);
690
-
691
- values[temp] = value[key];
692
-
693
- if (!queryText) {
694
- queryText = `(COLUMN_NAME_ALIAS->>'${key}' = :${temp as string}`;
695
- } else {
696
- queryText += ` AND COLUMN_NAME_ALIAS->>'${key}' = :${temp as string}`;
697
- }
698
- }
699
-
700
- if (hasValue) {
701
- queryText += ")";
702
- } else {
703
- queryText = "(COLUMN_NAME_ALIAS IS NULL OR COLUMN_NAME_ALIAS = '{}')";
704
- }
688
+ const jsonQuery: JSONColumnQuery = buildJSONColumnQuery({
689
+ value: value as JSONObject,
690
+ });
705
691
 
706
692
  return Raw((alias: string) => {
707
693
  /*
@@ -709,16 +695,15 @@ export default class QueryHelper {
709
695
  * we need to convert this to "tableName"."columnName"
710
696
  */
711
697
 
712
- alias = alias
698
+ const columnReference: string = alias
713
699
  .split(".")
714
700
  .map((item: string) => {
715
701
  return `"${item}"`;
716
702
  })
717
703
  .join(".");
718
704
 
719
- queryText = Text.replaceAll(queryText, "COLUMN_NAME_ALIAS", `${alias}`);
720
- return queryText;
721
- }, values);
705
+ return jsonQuery.toSql(columnReference);
706
+ }, jsonQuery.parameters);
722
707
  }
723
708
 
724
709
  @CaptureSpan()
@@ -3,6 +3,7 @@ import Query from "../../Types/Database/Query";
3
3
  import QueryHelper from "../../Types/Database/QueryHelper";
4
4
  import Select from "../../Types/Database/Select";
5
5
  import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
6
+ import Dictionary from "../../../Types/Dictionary";
6
7
  import BadDataException from "../../../Types/Exception/BadDataException";
7
8
  import ObjectID from "../../../Types/ObjectID";
8
9
  import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
@@ -20,12 +21,23 @@ import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/
20
21
  * FKs are ON DELETE NO ACTION, so Postgres raises 23503 and the API answers
21
22
  * "This item cannot be deleted because Incident records still reference it."
22
23
  *
23
- * The check below is deliberately narrow, for the same reason
24
- * MonitorStepsProjectValidator is: it only rejects an id that really is
25
- * another project's record. An id that matches no row at all is left alone
26
- * refusing it would block users from saving their way out of a record that
27
- * already references something deleted, and the not-null/foreign-key checks
28
- * report that case on their own.
24
+ * An id that matches NO row is rejected too (issue #3039). The REST API used to
25
+ * take any uuid for these columns; the write then either blew up deep inside
26
+ * Postgres as a raw 23503 (an opaque 500 rather than "you picked a bad id"), or
27
+ * for ids that live in a JSON blob with no foreign key behind it, like
28
+ * monitorSteps saved happily and only failed much later, inside the probe
29
+ * worker, as
30
+ * insert or update on table "MonitorStatusTimeline" violates foreign key
31
+ * constraint
32
+ * with nothing surfaced to the caller who typed the bad id.
33
+ *
34
+ * Rejecting a missing id is safe for the columns validated here because every
35
+ * one of them is a real foreign key and deletes in this codebase are hard
36
+ * deletes (DatabaseService._deleteBy), so a *stored* id can never dangle: the
37
+ * ON DELETE NO ACTION constraint blocks the delete while a row still points at
38
+ * it. A missing id therefore only ever comes from the payload being validated.
39
+ * Callers whose reference is NOT foreign-key backed (monitorSteps ids, say) can
40
+ * opt a single reference out with `mustExist: false`.
29
41
  */
30
42
 
31
43
  export interface ProjectScopedReference {
@@ -34,6 +46,13 @@ export interface ProjectScopedReference {
34
46
  id: ObjectID | string | undefined | null;
35
47
  // The service that owns the referenced model, e.g. IncidentSeverityService.
36
48
  service: DatabaseService<DatabaseBaseModel>;
49
+ /*
50
+ * Defaults to true: an id that matches no record is rejected. Set false for a
51
+ * reference that is allowed to dangle — one with no foreign key behind it,
52
+ * where refusing would block a user from saving their way out of a record
53
+ * that already points at something deleted.
54
+ */
55
+ mustExist?: boolean | undefined;
37
56
  }
38
57
 
39
58
  interface ForeignReference {
@@ -41,6 +60,33 @@ interface ForeignReference {
41
60
  name: string;
42
61
  }
43
62
 
63
+ interface MissingReference {
64
+ modelName: string;
65
+ id: string;
66
+ }
67
+
68
+ // What the caller asked about, keyed by id inside one service's lookup.
69
+ interface RequestedReference {
70
+ modelName: string;
71
+ mustExist: boolean;
72
+ // The id exactly as the caller wrote it, so the message echoes their input.
73
+ id: string;
74
+ }
75
+
76
+ /*
77
+ * Ids are matched case-insensitively.
78
+ *
79
+ * ObjectID keeps whatever case it was handed (its validation regex is
80
+ * case-insensitive) and Postgres compares `uuid` values by parsed value, so a
81
+ * payload carrying an UPPERCASE uuid selects the row fine — but reads the id
82
+ * back lower-cased, because that is how Postgres renders `uuid`. Comparing the
83
+ * two verbatim would find no match and report a record that plainly exists as
84
+ * missing.
85
+ */
86
+ function normalizeId(id: string): string {
87
+ return id.trim().toLowerCase();
88
+ }
89
+
44
90
  /*
45
91
  * The same reference reaches a service hook in several shapes: the id column
46
92
  * (`incidentSeverityId`), a relation object (`incidentSeverity: { _id }`), an
@@ -92,11 +138,11 @@ export default class ProjectScopedReferenceValidator {
92
138
  */
93
139
  const idsByService: Map<
94
140
  DatabaseService<DatabaseBaseModel>,
95
- Map<string, string>
141
+ Map<string, RequestedReference>
96
142
  > = new Map();
97
143
 
98
144
  for (const reference of data.references) {
99
- const id: string = reference.id?.toString() || "";
145
+ const id: string = reference.id?.toString().trim() || "";
100
146
 
101
147
  if (!id) {
102
148
  continue;
@@ -106,7 +152,25 @@ export default class ProjectScopedReferenceValidator {
106
152
  idsByService.set(reference.service, new Map());
107
153
  }
108
154
 
109
- idsByService.get(reference.service)!.set(id, reference.modelName);
155
+ const requested: Map<string, RequestedReference> = idsByService.get(
156
+ reference.service,
157
+ )!;
158
+
159
+ const key: string = normalizeId(id);
160
+
161
+ const existing: RequestedReference | undefined = requested.get(key);
162
+
163
+ requested.set(key, {
164
+ modelName: reference.modelName,
165
+ id: existing?.id || id,
166
+ /*
167
+ * The same id can arrive twice for one service (e.g. two monitor
168
+ * criteria pointing at the same status). If ANY of them requires the
169
+ * record to exist, the strictest wins.
170
+ */
171
+ mustExist:
172
+ (existing?.mustExist ?? false) || (reference.mustExist ?? true),
173
+ });
110
174
  }
111
175
 
112
176
  if (idsByService.size === 0) {
@@ -114,21 +178,38 @@ export default class ProjectScopedReferenceValidator {
114
178
  }
115
179
 
116
180
  const foreignReferences: Array<ForeignReference> = [];
181
+ const missingReferences: Array<MissingReference> = [];
182
+
183
+ for (const [service, requestedById] of idsByService) {
184
+ /*
185
+ * Read the tenant and name columns off the model rather than assuming
186
+ * `projectId` / `name`. Not every referenced model is project scoped —
187
+ * a monitor criteria can name owner *users*, and User has no tenant
188
+ * column at all. Selecting a column a model does not have throws, and
189
+ * comparing an absent projectId against the write's project would
190
+ * report every such record as belonging to "a different project".
191
+ */
192
+ const referencedModel: DatabaseBaseModel = service.getModel();
193
+ const tenantColumnName: string | null = referencedModel.getTenantColumn();
194
+ const hasNameColumn: boolean = referencedModel.hasColumn("name");
195
+
196
+ const select: Select<DatabaseBaseModel> = {
197
+ _id: true,
198
+ } as Select<DatabaseBaseModel>;
199
+
200
+ if (hasNameColumn) {
201
+ (select as Dictionary<boolean>)["name"] = true;
202
+ }
203
+
204
+ if (tenantColumnName) {
205
+ (select as Dictionary<boolean>)[tenantColumnName] = true;
206
+ }
117
207
 
118
- for (const [service, idToModelName] of idsByService) {
119
208
  const records: Array<DatabaseBaseModel> = await service.findBy({
120
209
  query: {
121
- _id: QueryHelper.any(Array.from(idToModelName.keys())),
210
+ _id: QueryHelper.any(Array.from(requestedById.keys())),
122
211
  },
123
- /*
124
- * `name` and `projectId` are not on DatabaseBaseModel, but every model
125
- * passed here is project scoped and named, so the cast is safe.
126
- */
127
- select: {
128
- _id: true,
129
- name: true,
130
- projectId: true,
131
- } as Select<DatabaseBaseModel>,
212
+ select: select,
132
213
  limit: LIMIT_PER_PROJECT,
133
214
  skip: 0,
134
215
  props: {
@@ -136,37 +217,84 @@ export default class ProjectScopedReferenceValidator {
136
217
  },
137
218
  });
138
219
 
220
+ const foundIds: Set<string> = new Set();
221
+
139
222
  for (const record of records) {
223
+ const id: string = normalizeId(record._id?.toString() || "");
224
+
225
+ foundIds.add(id);
226
+
227
+ if (!tenantColumnName) {
228
+ // Global record (e.g. User) — there is no project to compare against.
229
+ continue;
230
+ }
231
+
140
232
  const recordProjectId: ObjectID | undefined =
141
- record.getValue<ObjectID>("projectId") || undefined;
233
+ record.getValue<ObjectID>(tenantColumnName) || undefined;
142
234
 
143
235
  if (recordProjectId?.toString() === data.projectId.toString()) {
144
236
  continue;
145
237
  }
146
238
 
147
- const id: string = record._id?.toString() || "";
148
-
149
239
  foreignReferences.push({
150
- modelName: idToModelName.get(id) || "Record",
151
- name: record.getValue<string>("name")?.toString() || id,
240
+ modelName: requestedById.get(id)?.modelName || "Record",
241
+ name:
242
+ (hasNameColumn && record.getValue<string>("name")?.toString()) ||
243
+ id,
244
+ });
245
+ }
246
+
247
+ for (const [id, requested] of requestedById) {
248
+ if (foundIds.has(id) || !requested.mustExist) {
249
+ continue;
250
+ }
251
+
252
+ missingReferences.push({
253
+ modelName: requested.modelName,
254
+ // Echo the id as the caller wrote it, not the normalized key.
255
+ id: requested.id,
152
256
  });
153
257
  }
154
258
  }
155
259
 
156
- if (foreignReferences.length === 0) {
157
- return;
260
+ /*
261
+ * Both problems are reported in one go so a caller fixing a payload with
262
+ * several bad ids does not have to discover them one round-trip at a time.
263
+ * The wording of each clause is kept verbatim from when they were separate
264
+ * messages — support runbooks and tests match on it.
265
+ */
266
+ const clauses: Array<string> = [];
267
+
268
+ if (foreignReferences.length > 0) {
269
+ const described: string = foreignReferences
270
+ .map((reference: ForeignReference) => {
271
+ return `${reference.modelName} "${reference.name}"`;
272
+ })
273
+ .join(", ");
274
+
275
+ clauses.push(
276
+ `references records that belong to a different project: ${described}. Please pick values from this project and try again.`,
277
+ );
278
+ }
279
+
280
+ if (missingReferences.length > 0) {
281
+ const described: string = missingReferences
282
+ .map((reference: MissingReference) => {
283
+ return `${reference.modelName} "${reference.id}"`;
284
+ })
285
+ .join(", ");
286
+
287
+ clauses.push(
288
+ `references records that do not exist: ${described}. Please pick values that exist in this project and try again.`,
289
+ );
158
290
  }
159
291
 
160
- const described: string = foreignReferences
161
- .map((reference: ForeignReference) => {
162
- return `${reference.modelName} "${reference.name}"`;
163
- })
164
- .join(", ");
292
+ if (clauses.length === 0) {
293
+ return;
294
+ }
165
295
 
166
296
  throw new BadDataException(
167
- `This ${
168
- data.subject || "request"
169
- } references records that belong to a different project: ${described}. Please pick values from this project and try again.`,
297
+ `This ${data.subject || "request"} ${clauses.join(" It also ")}`,
170
298
  );
171
299
  }
172
300
 
@@ -178,10 +306,13 @@ export default class ProjectScopedReferenceValidator {
178
306
  * there fails the whole ingest job for that monitor, so those callers ask
179
307
  * this instead and fall back to their project's own default.
180
308
  *
181
- * Note the deliberate difference from validateReferencesBelongToProject: an
182
- * id that matches NO record is unusable here (false), while the write guard
183
- * lets it pass so a record already pointing at something deleted stays
184
- * saveable.
309
+ * Note the deliberate difference from validateReferencesBelongToProject:
310
+ * that one refuses the write and names what is wrong, which is right when
311
+ * someone is typing an id in. This one answers a question and never throws,
312
+ * which is right when a worker is reading an id that was typed in long ago —
313
+ * an id monitorSteps is allowed to keep holding (the write guard exempts what
314
+ * a record already stores, since none of those ids has a foreign key behind
315
+ * it and there is no migration to clean them up).
185
316
  */
186
317
  public static async isUsableInProject(data: {
187
318
  projectId: ObjectID | undefined;
@@ -0,0 +1,84 @@
1
+ import ColumnLength from "../../../Types/Database/ColumnLength";
2
+
3
+ /*
4
+ * ------------------------------------------------------------------
5
+ * TruncateColumnValue
6
+ *
7
+ * Per-value clamps for bounded varchar columns written by the bulk
8
+ * inventory upsert paths (Kubernetes / Docker / Docker Swarm / Podman /
9
+ * Proxmox / Ceph / IoT).
10
+ *
11
+ * Those services hand-build `INSERT ... ON CONFLICT` statements and
12
+ * flush them in chunks of 500 rows through
13
+ * `getRepository().manager.query(...)`, which bypasses the length
14
+ * validation DatabaseService applies to ordinary CRUD writes. A single
15
+ * value past its column limit raises 22001 ("value too long for type
16
+ * character varying(N)") and aborts the WHOLE multi-row INSERT, so one
17
+ * malformed resource silently drops up to 499 healthy inventory rows
18
+ * with it. Clamping per value keeps the blast radius at one field.
19
+ *
20
+ * Truncation is the last line of defense, not the primary fix: columns
21
+ * that legitimately carry long values (image references, Kubernetes
22
+ * DNS-subdomain names) should be widened in the model + a migration so
23
+ * these clamps effectively never fire.
24
+ * ------------------------------------------------------------------
25
+ */
26
+
27
+ export function truncateToLength(value: string, maxLength: number): string;
28
+ export function truncateToLength(
29
+ value: string | null | undefined,
30
+ maxLength: number,
31
+ ): string | null;
32
+ export function truncateToLength(
33
+ value: string | null | undefined,
34
+ maxLength: number,
35
+ ): string | null {
36
+ if (value === null || value === undefined) {
37
+ return null;
38
+ }
39
+
40
+ if (value.length <= maxLength) {
41
+ return value;
42
+ }
43
+
44
+ const clipped: string = value.substring(0, maxLength);
45
+
46
+ /*
47
+ * JS string length counts UTF-16 code units, so a cut at maxLength can
48
+ * land in the middle of a surrogate pair (any astral-plane character:
49
+ * emoji, rare CJK). The orphaned high surrogate is not valid UTF-8 —
50
+ * Node encodes it as U+FFFD on the way to the driver — so drop it.
51
+ * A well-formed string never ends a character on a high surrogate,
52
+ * which makes this check unambiguous. Postgres counts characters, not
53
+ * code units, so clipping by code units is always conservative: the
54
+ * result can only be shorter than the column bound, never longer.
55
+ */
56
+ const lastCodeUnit: number = clipped.charCodeAt(clipped.length - 1);
57
+ if (lastCodeUnit >= 0xd800 && lastCodeUnit <= 0xdbff) {
58
+ return clipped.substring(0, clipped.length - 1);
59
+ }
60
+
61
+ return clipped;
62
+ }
63
+
64
+ /** Clamp to ColumnLength.ShortText — varchar(100). */
65
+ export function truncateShortText(value: string): string;
66
+ export function truncateShortText(
67
+ value: string | null | undefined,
68
+ ): string | null;
69
+ export function truncateShortText(
70
+ value: string | null | undefined,
71
+ ): string | null {
72
+ return truncateToLength(value, ColumnLength.ShortText);
73
+ }
74
+
75
+ /** Clamp to ColumnLength.LongText — varchar(500). */
76
+ export function truncateLongText(value: string): string;
77
+ export function truncateLongText(
78
+ value: string | null | undefined,
79
+ ): string | null;
80
+ export function truncateLongText(
81
+ value: string | null | undefined,
82
+ ): string | null {
83
+ return truncateToLength(value, ColumnLength.LongText);
84
+ }
@@ -50,6 +50,7 @@ import CaptureSpan from "../Telemetry/CaptureSpan";
50
50
  import ExceptionMessages from "../../../Types/Exception/ExceptionMessages";
51
51
  import MonitorEvaluationSummary from "../../../Types/Monitor/MonitorEvaluationSummary";
52
52
  import MonitorStatusService from "../../Services/MonitorStatusService";
53
+ import ProjectScopedReferenceValidator from "../Database/ProjectScopedReferenceValidator";
53
54
  import { ProbeConnectionStatus } from "../../../Models/DatabaseModels/Probe";
54
55
  import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
55
56
 
@@ -1063,48 +1064,74 @@ export default class MonitorResourceUtil {
1063
1064
  */
1064
1065
  let revertedToDefaultStatus: boolean = true;
1065
1066
 
1066
- try {
1067
- await MonitorStatusTimelineService.create({
1068
- data: monitorStatusTimeline,
1069
- props: {
1070
- isRoot: true,
1071
- },
1067
+ /*
1068
+ * The stored default status can be a status that no longer exists, or
1069
+ * one from another project: monitorSteps is a JSON blob with no
1070
+ * foreign key behind it, and before issue #3039 was fixed the API
1071
+ * accepted any uuid for it. Writing it raised the
1072
+ * "violates foreign key constraint" error from the probe/telemetry
1073
+ * worker and failed the whole ingest run for this monitor, losing the
1074
+ * monitor log and payload persisted below. Skip the revert and log,
1075
+ * the same way the criteria path in Utils/Monitor/MonitorStatusTimeline
1076
+ * does. New writes are rejected up front by
1077
+ * MonitorStepsProjectValidator.
1078
+ */
1079
+ const isDefaultStatusUsable: boolean =
1080
+ await ProjectScopedReferenceValidator.isUsableInProject({
1081
+ projectId: monitor.projectId!,
1082
+ id: monitorSteps.data.defaultMonitorStatusId,
1083
+ service: MonitorStatusService,
1072
1084
  });
1073
- logger.debug(
1074
- `${dataToProcess.monitorId.toString()} - Monitor status updated to default.`,
1085
+
1086
+ if (!isDefaultStatusUsable) {
1087
+ logger.error(
1088
+ `${dataToProcess.monitorId.toString()} - The default monitor status ${monitorSteps.data.defaultMonitorStatusId.toString()} does not exist in project ${monitor.projectId?.toString()}. Skipping revert to default status. Please pick a default monitor status that exists in this project.`,
1075
1089
  );
1076
- } catch (err) {
1077
- /*
1078
- * Idempotent concurrency race (see MonitorStatusTimeline.ts): a
1079
- * concurrent result already moved the monitor to this default status,
1080
- * so onBeforeCreate's dedupe check throws this exact BadDataException.
1081
- * Treat as a no-op at debug level rather than failing the job. Match the
1082
- * exact message so unrelated BadDataExceptions still propagate.
1083
- */
1084
- if (
1085
- err instanceof BadDataException &&
1086
- err.message === MONITOR_STATUS_SAME_AS_PREVIOUS_ERROR_MESSAGE
1087
- ) {
1090
+ revertedToDefaultStatus = false;
1091
+ } else {
1092
+ try {
1093
+ await MonitorStatusTimelineService.create({
1094
+ data: monitorStatusTimeline,
1095
+ props: {
1096
+ isRoot: true,
1097
+ },
1098
+ });
1088
1099
  logger.debug(
1089
- `${dataToProcess.monitorId.toString()} - Monitor status already at default; skipping duplicate status timeline (concurrent race).`,
1100
+ `${dataToProcess.monitorId.toString()} - Monitor status updated to default.`,
1090
1101
  );
1091
- } else if (
1092
- err instanceof ServerException &&
1093
- err.message === MONITOR_STATUS_TIMELINE_LOCK_ERROR_MESSAGE
1094
- ) {
1102
+ } catch (err) {
1095
1103
  /*
1096
- * The per-monitor timeline mutex could not be acquired (fail-closed
1097
- * create, see MonitorStatusTimelineService). Skipping is recoverable:
1098
- * the next probe result re-evaluates the same criteria and recreates
1099
- * the revert-to-default, while failing here would abort the whole
1100
- * ingest run and lose the monitor log for this probe result.
1104
+ * Idempotent concurrency race (see MonitorStatusTimeline.ts): a
1105
+ * concurrent result already moved the monitor to this default status,
1106
+ * so onBeforeCreate's dedupe check throws this exact BadDataException.
1107
+ * Treat as a no-op at debug level rather than failing the job. Match the
1108
+ * exact message so unrelated BadDataExceptions still propagate.
1101
1109
  */
1102
- logger.error(
1103
- `${dataToProcess.monitorId.toString()} - Could not acquire the monitor status timeline lock; skipping revert to default status. It will be retried on the next probe result.`,
1104
- );
1105
- revertedToDefaultStatus = false;
1106
- } else {
1107
- throw err;
1110
+ if (
1111
+ err instanceof BadDataException &&
1112
+ err.message === MONITOR_STATUS_SAME_AS_PREVIOUS_ERROR_MESSAGE
1113
+ ) {
1114
+ logger.debug(
1115
+ `${dataToProcess.monitorId.toString()} - Monitor status already at default; skipping duplicate status timeline (concurrent race).`,
1116
+ );
1117
+ } else if (
1118
+ err instanceof ServerException &&
1119
+ err.message === MONITOR_STATUS_TIMELINE_LOCK_ERROR_MESSAGE
1120
+ ) {
1121
+ /*
1122
+ * The per-monitor timeline mutex could not be acquired (fail-closed
1123
+ * create, see MonitorStatusTimelineService). Skipping is recoverable:
1124
+ * the next probe result re-evaluates the same criteria and recreates
1125
+ * the revert-to-default, while failing here would abort the whole
1126
+ * ingest run and lose the monitor log for this probe result.
1127
+ */
1128
+ logger.error(
1129
+ `${dataToProcess.monitorId.toString()} - Could not acquire the monitor status timeline lock; skipping revert to default status. It will be retried on the next probe result.`,
1130
+ );
1131
+ revertedToDefaultStatus = false;
1132
+ } else {
1133
+ throw err;
1134
+ }
1108
1135
  }
1109
1136
  }
1110
1137
 
@@ -9,7 +9,9 @@ import MonitorStatusTimelineService, {
9
9
  MONITOR_STATUS_SAME_AS_PREVIOUS_ERROR_MESSAGE,
10
10
  MONITOR_STATUS_TIMELINE_LOCK_ERROR_MESSAGE,
11
11
  } from "../../Services/MonitorStatusTimelineService";
12
+ import MonitorStatusService from "../../Services/MonitorStatusService";
12
13
  import ServerException from "../../../Types/Exception/ServerException";
14
+ import ProjectScopedReferenceValidator from "../Database/ProjectScopedReferenceValidator";
13
15
  import logger, { LogAttributes } from "../Logger";
14
16
  import CaptureSpan from "../Telemetry/CaptureSpan";
15
17
  import DataToProcess from "./DataToProcess";
@@ -112,6 +114,40 @@ export default class MonitorStatusTimelineUtil {
112
114
  throw new BadDataException("Monitor status is not defined.");
113
115
  }
114
116
 
117
+ /*
118
+ * The criteria can name a status that no longer exists, or that belongs to
119
+ * another project: monitorSteps is a JSON blob with no foreign key behind
120
+ * it, so deleting a monitor status does not rewrite the criteria that
121
+ * point at it, and before issue #3039 was fixed the API accepted any uuid
122
+ * here in the first place. Writing it anyway is what raised
123
+ * insert or update on table "MonitorStatusTimeline" violates foreign key
124
+ * constraint
125
+ * inside the probe/telemetry worker, failing the whole ingest run for this
126
+ * monitor - no status change, and no monitor log or payload persisted
127
+ * either, because those come after this. Skip the status change and log,
128
+ * the same way MonitorIncident and MonitorAlert handle an unusable
129
+ * severity. New writes are rejected up front by
130
+ * MonitorStepsProjectValidator, so this only ever fires for monitors saved
131
+ * before that guard existed.
132
+ */
133
+ const isMonitorStatusUsable: boolean =
134
+ await ProjectScopedReferenceValidator.isUsableInProject({
135
+ projectId: input.monitor.projectId!,
136
+ id: monitorStatusId,
137
+ service: MonitorStatusService,
138
+ });
139
+
140
+ if (!isMonitorStatusUsable) {
141
+ logger.error(
142
+ `${input.monitor.id?.toString()} - Criteria "${
143
+ input.criteriaInstance.data?.name
144
+ }" changes the monitor status to ${monitorStatusId.toString()}, which does not exist in project ${input.monitor.projectId?.toString()}. Skipping the status change. Please pick a monitor status that exists in this project.`,
145
+ monitorLogAttributes,
146
+ );
147
+
148
+ return null;
149
+ }
150
+
115
151
  //change monitor status.
116
152
 
117
153
  // get last status of this monitor.