@oneuptime/common 11.7.0 → 11.7.2

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 (264) hide show
  1. package/Models/DatabaseModels/DashboardDomain.ts +1 -0
  2. package/Models/DatabaseModels/Incident.ts +2 -0
  3. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  4. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  5. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  6. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  7. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  8. package/Models/DatabaseModels/Monitor.ts +1 -0
  9. package/Models/DatabaseModels/MonitorStatusTimeline.ts +1 -0
  10. package/Models/DatabaseModels/NetworkSite.ts +1 -0
  11. package/Models/DatabaseModels/NetworkSiteStatusTimeline.ts +1 -0
  12. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -0
  13. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +2 -0
  14. package/Models/DatabaseModels/Probe.ts +13 -13
  15. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  16. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  17. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  18. package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -0
  19. package/Models/DatabaseModels/StatusPage.ts +1 -0
  20. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  21. package/Models/DatabaseModels/StatusPageDomain.ts +1 -0
  22. package/Models/DatabaseModels/WorkspaceNotificationSummary.ts +1 -0
  23. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +11 -0
  24. package/Server/API/BaseAPI.ts +17 -1
  25. package/Server/API/CommonAPI.ts +32 -0
  26. package/Server/API/MicrosoftTeamsAPI.ts +99 -2
  27. package/Server/API/ProbeAPI.ts +2 -0
  28. package/Server/API/SlackAPI.ts +62 -0
  29. package/Server/API/StatusPageAPI.ts +157 -192
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.ts +89 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.ts +61 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.ts +280 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  34. package/Server/Services/DatabaseService.ts +11 -3
  35. package/Server/Services/MonitorGroupService.ts +145 -0
  36. package/Server/Services/MonitorService.ts +231 -30
  37. package/Server/Services/ProbeService.ts +319 -16
  38. package/Server/Services/StatusPageService.ts +72 -42
  39. package/Server/Services/WorkspaceNotificationRuleService.ts +232 -2
  40. package/Server/Utils/Database/PostgresErrorTranslator.ts +131 -0
  41. package/Server/Utils/Monitor/MonitorResource.ts +88 -35
  42. package/Server/Utils/Monitor/MonitorStatusTimeline.ts +21 -1
  43. package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +158 -0
  44. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +706 -6
  45. package/Tests/Models/DatabaseModels/ProbeColumnAccessControl.test.ts +103 -0
  46. package/Tests/Server/API/BaseAPIUpdateNoOp.test.ts +135 -0
  47. package/Tests/Server/API/CommonAPIAuthGuard.test.ts +317 -0
  48. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +228 -0
  49. package/Tests/Server/API/ProbeAPI.test.ts +6 -0
  50. package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +167 -0
  51. package/Tests/Server/Services/AddHotQueryIndexesMigration.test.ts +255 -0
  52. package/Tests/Server/Services/AddHotQueryIndexesSecondPassMigration.test.ts +258 -0
  53. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +1 -1
  54. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +121 -0
  55. package/Tests/Server/Services/HeartbeatWriteFastPathSafety.test.ts +200 -0
  56. package/Tests/Server/Services/HookFreeWriteSafetySecondPass.test.ts +181 -0
  57. package/Tests/Server/Services/MonitorGroupServiceBatchedStatuses.test.ts +513 -0
  58. package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +492 -0
  59. package/Tests/Server/Services/MonitorServiceProbeSelection.test.ts +401 -0
  60. package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +453 -0
  61. package/Tests/Server/Services/ProbeAuthKeyCacheAndHeartbeat.test.ts +679 -0
  62. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +3 -3
  63. package/Tests/Server/Services/StatusPageDomainResolutionCache.test.ts +245 -0
  64. package/Tests/Server/Services/StatusPageOverviewTimelineWindow.test.ts +283 -0
  65. package/Tests/Server/Services/StatusPageServiceMcp.test.ts +17 -1
  66. package/Tests/Server/Services/StatusPageServiceMonitorGroupBatching.test.ts +331 -0
  67. package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +12 -12
  68. package/Tests/Server/Services/WorkspaceNotificationRuleChats.test.ts +1390 -0
  69. package/Tests/Server/Utils/AI/Insights/InsightFixRouting.test.ts +4 -4
  70. package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +2 -2
  71. package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +1 -3
  72. package/Tests/Server/Utils/AI/Insights/InsightStoreHardening.test.ts +1 -3
  73. package/Tests/Server/Utils/AI/Insights/InsightTriage.test.ts +1 -1
  74. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +5 -5
  75. package/Tests/Server/Utils/Alert/AlertPrivacyFilter.test.ts +187 -0
  76. package/Tests/Server/Utils/AlertEpisode/AlertEpisodePrivacyFilter.test.ts +170 -0
  77. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +125 -0
  78. package/Tests/Server/Utils/Incident/IncidentPrivacyFilter.test.ts +264 -0
  79. package/Tests/Server/Utils/IncidentEpisode/IncidentEpisodePrivacyFilter.test.ts +175 -0
  80. package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +181 -0
  81. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +1 -1
  82. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +2 -2
  83. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +341 -0
  84. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +2175 -0
  85. package/Tests/Server/Utils/Workspace/SlackChannelsList.test.ts +585 -0
  86. package/Tests/Types/Billing/MeteredPlan.test.ts +46 -0
  87. package/Tests/Types/DateUserTimezone.test.ts +205 -0
  88. package/Tests/Types/DateUserTimezoneOverridesBrowser.test.ts +73 -0
  89. package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +219 -1
  90. package/Tests/UI/Components/BulkUpdateForm.test.tsx +174 -0
  91. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +315 -0
  92. package/Tests/UI/Components/ModelDetailSelect.test.tsx +229 -0
  93. package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1258 -0
  94. package/Tests/UI/Components/ModelTableExportFromColumns.test.ts +153 -0
  95. package/Tests/UI/Components/MonacoLoader.test.ts +67 -0
  96. package/Tests/UI/Components/MonacoRuntime.test.ts +121 -0
  97. package/Tests/UI/Components/TableBulkCsvExport.test.tsx +212 -3
  98. package/Tests/UI/Components/TimePicker/TimePicker.test.tsx +11 -0
  99. package/Tests/UI/Utils/Project.test.ts +809 -0
  100. package/Tests/UI/Utils/TableColumnsToCsv.test.ts +360 -0
  101. package/Tests/Utils/Alerts/AlertMetricType.test.ts +143 -0
  102. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +219 -0
  103. package/Tests/Utils/Incident/IncidentMetricType.test.ts +174 -0
  104. package/Tests/Utils/Monitor/MonitorProbeSelectionUtil.test.ts +145 -0
  105. package/Tests/Utils/StatusPage/ResourceUptime.test.ts +263 -0
  106. package/Types/Date.ts +119 -50
  107. package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +4 -0
  108. package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +25 -10
  109. package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
  110. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +63 -8
  111. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  112. package/UI/Components/CodeEditor/MonacoLoader.ts +36 -0
  113. package/UI/Components/Forms/Fields/FormField.tsx +13 -3
  114. package/UI/Components/Forms/ModelForm.tsx +41 -18
  115. package/UI/Components/Input/Input.tsx +6 -1
  116. package/UI/Components/ModelDetail/CardModelDetail.tsx +23 -1
  117. package/UI/Components/ModelDetail/ModelDetail.tsx +46 -1
  118. package/UI/Components/ModelTable/BaseModelTable.tsx +300 -30
  119. package/UI/Components/ModelTable/Column.ts +12 -0
  120. package/UI/Components/ModelTable/ExportFromColumns.ts +57 -0
  121. package/UI/Components/Navbar/NavBar.tsx +66 -1
  122. package/UI/Components/Table/Table.tsx +52 -13
  123. package/UI/Components/Table/Types/Column.ts +18 -0
  124. package/UI/Components/TimePicker/TimePicker.tsx +8 -4
  125. package/UI/Utils/Project.ts +214 -18
  126. package/UI/Utils/TableColumnsToCsv.ts +86 -6
  127. package/UI/Utils/User.ts +19 -0
  128. package/UI/esbuild-config.js +34 -0
  129. package/Utils/Monitor/MonitorProbeSelectionUtil.ts +72 -0
  130. package/build/dist/Models/DatabaseModels/DashboardDomain.js +1 -0
  131. package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
  132. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  133. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  134. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  135. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  136. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  137. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  138. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  139. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  140. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  141. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  142. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  143. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  144. package/build/dist/Models/DatabaseModels/Monitor.js +1 -0
  145. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  146. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
  147. package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
  148. package/build/dist/Models/DatabaseModels/NetworkSite.js +1 -0
  149. package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
  150. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js +1 -0
  151. package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js.map +1 -1
  152. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -0
  153. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  154. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +2 -0
  155. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  156. package/build/dist/Models/DatabaseModels/Probe.js +13 -13
  157. package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
  158. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  159. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  160. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  161. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  162. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  163. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  164. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
  165. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
  166. package/build/dist/Models/DatabaseModels/StatusPage.js +1 -0
  167. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  168. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  169. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  170. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +1 -0
  171. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  172. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js +1 -0
  173. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js.map +1 -1
  174. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  175. package/build/dist/Server/API/BaseAPI.js +12 -1
  176. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  177. package/build/dist/Server/API/CommonAPI.js +21 -0
  178. package/build/dist/Server/API/CommonAPI.js.map +1 -1
  179. package/build/dist/Server/API/MicrosoftTeamsAPI.js +72 -2
  180. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  181. package/build/dist/Server/API/ProbeAPI.js +2 -0
  182. package/build/dist/Server/API/ProbeAPI.js.map +1 -1
  183. package/build/dist/Server/API/SlackAPI.js +44 -0
  184. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  185. package/build/dist/Server/API/StatusPageAPI.js +98 -152
  186. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  187. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js +36 -0
  188. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js.map +1 -0
  189. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js +26 -0
  190. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js.map +1 -0
  191. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js +254 -0
  192. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js.map +1 -0
  193. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  194. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  195. package/build/dist/Server/Services/DatabaseService.js +10 -2
  196. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  197. package/build/dist/Server/Services/MonitorGroupService.js +118 -0
  198. package/build/dist/Server/Services/MonitorGroupService.js.map +1 -1
  199. package/build/dist/Server/Services/MonitorService.js +198 -24
  200. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  201. package/build/dist/Server/Services/ProbeService.js +238 -9
  202. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  203. package/build/dist/Server/Services/StatusPageService.js +46 -39
  204. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  205. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +178 -3
  206. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  207. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +80 -0
  208. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -0
  209. package/build/dist/Server/Utils/Monitor/MonitorResource.js +67 -29
  210. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  211. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +31 -14
  212. package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
  213. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +117 -0
  214. package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -0
  215. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +549 -7
  216. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  217. package/build/dist/Types/Date.js +106 -35
  218. package/build/dist/Types/Date.js.map +1 -1
  219. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +17 -7
  220. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
  221. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +37 -11
  222. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  223. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +2 -0
  224. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  225. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +34 -0
  226. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +1 -0
  227. package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -3
  228. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  229. package/build/dist/UI/Components/Forms/ModelForm.js +37 -17
  230. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  231. package/build/dist/UI/Components/Input/Input.js +6 -1
  232. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  233. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +22 -1
  234. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  235. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +31 -1
  236. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  237. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +234 -21
  238. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  239. package/build/dist/UI/Components/ModelTable/ExportFromColumns.js +30 -0
  240. package/build/dist/UI/Components/ModelTable/ExportFromColumns.js.map +1 -0
  241. package/build/dist/UI/Components/Navbar/NavBar.js +39 -1
  242. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  243. package/build/dist/UI/Components/Table/Table.js +31 -11
  244. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  245. package/build/dist/UI/Components/TimePicker/TimePicker.js +4 -4
  246. package/build/dist/UI/Components/TimePicker/TimePicker.js.map +1 -1
  247. package/build/dist/UI/Utils/Project.js +161 -15
  248. package/build/dist/UI/Utils/Project.js.map +1 -1
  249. package/build/dist/UI/Utils/TableColumnsToCsv.js +67 -6
  250. package/build/dist/UI/Utils/TableColumnsToCsv.js.map +1 -1
  251. package/build/dist/UI/Utils/User.js +19 -0
  252. package/build/dist/UI/Utils/User.js.map +1 -1
  253. package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js +52 -0
  254. package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js.map +1 -0
  255. package/package.json +4 -3
  256. package/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.ts +0 -79
  257. package/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.ts +0 -41
  258. package/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.ts +0 -17
  259. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js +0 -34
  260. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js.map +0 -1
  261. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js +0 -22
  262. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js.map +0 -1
  263. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js +0 -12
  264. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js.map +0 -1
@@ -67,6 +67,21 @@ export interface ComponentProps<T extends GenericObject> {
67
67
  onActionStart?: (() => void) | undefined;
68
68
  onActionEnd?: (() => void) | undefined;
69
69
  itemToString?: ((item: T) => string) | undefined;
70
+ /*
71
+ * Selecting everything is a multi-request fetch that can fail. Surfacing it
72
+ * here - next to the button the user pressed - keeps the message alive; the
73
+ * table body's own error is wiped by the next refresh.
74
+ */
75
+ errorMessage?: string | undefined;
76
+ isSelectingAllItems?: boolean | undefined;
77
+ /*
78
+ * Set when the selection hit the per-selection ceiling and there are more
79
+ * matching rows than were selected. `totalMatchingItemsCount` is how many
80
+ * rows actually matched, so the warning can name real numbers instead of
81
+ * inferring truncation from the selected count.
82
+ */
83
+ isSelectionTruncated?: boolean | undefined;
84
+ totalMatchingItemsCount?: number | undefined;
70
85
  }
71
86
 
72
87
  const isDangerStyle: (style: ButtonStyleType) => boolean = (
@@ -317,9 +332,16 @@ const BulkUpdateForm: <T extends GenericObject>(
317
332
  menuChildren.push(renderMenuItem(button, safeButtons.length + index));
318
333
  });
319
334
 
320
- const showLimitWarning: boolean =
321
- props.isAllItemsSelected &&
322
- props.selectedItems.length === LIMIT_PER_PROJECT;
335
+ /*
336
+ * Driven by what the fetch actually reported rather than by
337
+ * `selectedItems.length === LIMIT_PER_PROJECT`, which both cried wolf on a
338
+ * project holding exactly the ceiling and stayed silent whenever a partial
339
+ * selection came back one row short of it.
340
+ */
341
+ const showLimitWarning: boolean = Boolean(props.isSelectionTruncated);
342
+
343
+ const totalMatchingItemsCount: number =
344
+ props.totalMatchingItemsCount || props.selectedItems.length;
323
345
 
324
346
  return (
325
347
  <div>
@@ -345,13 +367,33 @@ const BulkUpdateForm: <T extends GenericObject>(
345
367
  {!props.isAllItemsSelected && (
346
368
  <button
347
369
  type="button"
370
+ disabled={props.isSelectingAllItems}
348
371
  onClick={() => {
372
+ if (props.isSelectingAllItems) {
373
+ // the fetch is paged, so it must not be re-entrant.
374
+ return;
375
+ }
349
376
  props.onSelectAllClick();
350
377
  }}
351
- className="inline-flex items-center gap-1.5 rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 shadow-sm hover:bg-indigo-50 hover:border-indigo-300 hover:text-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-all duration-150 cursor-pointer select-none"
378
+ className={`inline-flex items-center gap-1.5 rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-all duration-150 select-none ${
379
+ props.isSelectingAllItems
380
+ ? "opacity-50 cursor-not-allowed"
381
+ : "cursor-pointer hover:bg-indigo-50 hover:border-indigo-300 hover:text-indigo-700"
382
+ }`}
352
383
  >
353
- <Icon icon={IconProp.CheckCircle} className="h-3.5 w-3.5" />
354
- <span>Select All {props.pluralLabel}</span>
384
+ <Icon
385
+ icon={
386
+ props.isSelectingAllItems
387
+ ? IconProp.Spinner
388
+ : IconProp.CheckCircle
389
+ }
390
+ className="h-3.5 w-3.5"
391
+ />
392
+ <span>
393
+ {props.isSelectingAllItems
394
+ ? `Selecting All ${props.pluralLabel}...`
395
+ : `Select All ${props.pluralLabel}`}
396
+ </span>
355
397
  </button>
356
398
  )}
357
399
 
@@ -393,8 +435,21 @@ const BulkUpdateForm: <T extends GenericObject>(
393
435
 
394
436
  {showLimitWarning && (
395
437
  <div className="mt-2 text-xs text-gray-500">
396
- You can only select {LIMIT_PER_PROJECT} {props.pluralLabel} at a
397
- time. This is for performance reasons.
438
+ Selected {props.selectedItems.length.toLocaleString()} of{" "}
439
+ {totalMatchingItemsCount.toLocaleString()} matching{" "}
440
+ {props.pluralLabel}. You can only select{" "}
441
+ {LIMIT_PER_PROJECT.toLocaleString()} {props.pluralLabel} at a
442
+ time, for performance reasons, so bulk actions will apply to the
443
+ selected {props.selectedItems.length.toLocaleString()} only.
444
+ </div>
445
+ )}
446
+
447
+ {props.errorMessage && (
448
+ <div
449
+ role="alert"
450
+ className="mt-2 rounded-lg bg-red-50 p-3 text-sm text-red-800"
451
+ >
452
+ {props.errorMessage}
398
453
  </div>
399
454
  )}
400
455
  </div>
@@ -1,4 +1,5 @@
1
1
  import Editor from "@monaco-editor/react";
2
+ import configureMonacoLoader from "./MonacoLoader";
2
3
  import CodeType from "../../../Types/Code/CodeType";
3
4
  import MarkdownUtil from "../../Utils/Markdown";
4
5
  import { Theme, useTheme } from "../../Utils/Theme";
@@ -10,6 +11,8 @@ import React, {
10
11
  useState,
11
12
  } from "react";
12
13
 
14
+ configureMonacoLoader();
15
+
13
16
  export interface ComponentProps {
14
17
  initialValue?: undefined | string;
15
18
  onClick?: undefined | (() => void);
@@ -0,0 +1,36 @@
1
+ import { loader } from "@monaco-editor/react";
2
+
3
+ /**
4
+ * Points Monaco at the copy of its runtime that ships with the app.
5
+ *
6
+ * @monaco-editor/loader otherwise fetches it from cdn.jsdelivr.net at runtime,
7
+ * which never resolves on an air-gapped install and leaves every code editor
8
+ * stuck on "Loading...". The path is baked in by the build, which is the only
9
+ * place that knows where this service is mounted.
10
+ *
11
+ * The path has to be absolute. Monaco loads its language services in a worker
12
+ * spun up from a blob: URL, and a blob worker has no base to resolve a
13
+ * root-relative path against - it fetches "/dashboard/assets/monaco/vs/..." and
14
+ * throws "Failed to parse URL". The editor still renders, so the only visible
15
+ * symptom is that JSON/TypeScript validation and IntelliSense quietly stop
16
+ * working. The CDN default never hit this because it was already absolute.
17
+ *
18
+ * The monaco-editor version in Common/package.json is pinned exactly, because
19
+ * the loader resolves init() with whatever the AMD module `vs/editor/editor.main`
20
+ * exports and that shape is not stable across releases - 0.53 and 0.54 nest the
21
+ * API under `exports.m`, which leaves `monaco.editor` undefined and makes every
22
+ * editor throw instead of mount. MonacoRuntime.test.ts guards the pin.
23
+ */
24
+ export default function configureMonacoLoader(): void {
25
+ const assetPath: string | undefined = process.env["MONACO_ASSET_PATH"];
26
+
27
+ if (!assetPath) {
28
+ return;
29
+ }
30
+
31
+ loader.config({
32
+ paths: {
33
+ vs: new URL(assetPath, window.location.origin).href,
34
+ },
35
+ });
36
+ }
@@ -291,9 +291,19 @@ const FormField: <T extends GenericObject>(
291
291
  fieldDescription = "";
292
292
  }
293
293
 
294
- fieldDescription +=
295
- " This is in your local timezone - " +
296
- OneUptimeDate.getCurrentTimezoneString();
294
+ /*
295
+ * Name the zone in full. The abbreviation on its own ("EDT") is not
296
+ * enough to tell whether the field is following the timezone picked in
297
+ * User Settings or the one the browser reports.
298
+ */
299
+ fieldDescription = (
300
+ fieldDescription +
301
+ " This is in your timezone - " +
302
+ OneUptimeDate.getCurrentTimezoneString() +
303
+ " (" +
304
+ OneUptimeDate.getCurrentTimezone().toString() +
305
+ ")."
306
+ ).trim();
297
307
  }
298
308
 
299
309
  type GetFieldDescriptionFunction = () => ReactElement | string;
@@ -641,27 +641,50 @@ const ModelForm: <TBaseModel extends BaseModel>(
641
641
  return fields;
642
642
  };
643
643
 
644
+ /*
645
+ * Keyed on the id (as a string - ObjectID identity changes every render)
646
+ * so a form that mounts before its id is known still fetches once it
647
+ * arrives. It used to run only on mount, which left an Update form showing
648
+ * blank defaults: submitting that silently wrote empty values - and false
649
+ * for every Toggle, since BasicForm defaults untouched toggles to false -
650
+ * over the real record.
651
+ */
644
652
  useAsyncEffect(async () => {
645
- if (
646
- props.modelIdToEdit &&
647
- props.formType === FormType.Update &&
648
- !props.doNotFetchExistingModel
649
- ) {
650
- // get item.
651
- setLoading(true);
652
- setIsFetching(true);
653
- setError("");
654
- try {
655
- await fetchItem();
656
- } catch (err) {
657
- setError(API.getFriendlyMessage(err));
658
- props.onError?.(API.getFriendlyMessage(err));
659
- }
653
+ if (props.formType !== FormType.Update || props.doNotFetchExistingModel) {
654
+ return;
655
+ }
656
+
657
+ if (!props.modelIdToEdit) {
658
+ /*
659
+ * Without an id there is nothing to prefill and nothing to update, so
660
+ * say so rather than rendering an empty form that looks editable.
661
+ *
662
+ * Deliberately not props.onError: ModelFormModal unmounts the form on
663
+ * that, and this state is recoverable - the effect re-runs and clears
664
+ * the message as soon as the id arrives.
665
+ */
666
+ setError(
667
+ `Cannot edit this ${(
668
+ model.singularName || "item"
669
+ ).toLowerCase()} yet because it is still loading. Please try again in a moment.`,
670
+ );
671
+ return;
672
+ }
660
673
 
661
- setLoading(false);
662
- setIsFetching(false);
674
+ // get item.
675
+ setLoading(true);
676
+ setIsFetching(true);
677
+ setError("");
678
+ try {
679
+ await fetchItem();
680
+ } catch (err) {
681
+ setError(API.getFriendlyMessage(err));
682
+ props.onError?.(API.getFriendlyMessage(err));
663
683
  }
664
- }, []);
684
+
685
+ setLoading(false);
686
+ setIsFetching(false);
687
+ }, [props.modelIdToEdit?.toString()]);
665
688
 
666
689
  type GetMiscDataPropsFunction = (
667
690
  values: FormValues<JSONObject>,
@@ -174,7 +174,12 @@ const Input: FunctionComponent<ComponentProps> = (
174
174
  props.type === InputType.DATETIME_LOCAL) &&
175
175
  value
176
176
  ) {
177
- const date: Date = OneUptimeDate.fromString(value);
177
+ /*
178
+ * The input hands back a bare wall-clock with no offset. Resolve
179
+ * it in the user's configured timezone rather than letting the
180
+ * browser assume its own zone.
181
+ */
182
+ const date: Date = OneUptimeDate.fromDateTimeLocalString(value);
178
183
  const dateString: string = OneUptimeDate.toString(date);
179
184
  setValue(dateString);
180
185
  if (props.onChange) {
@@ -12,6 +12,8 @@ import Fields from "../Forms/Types/Fields";
12
12
  import { FormStep } from "../Forms/Types/FormStep";
13
13
  import { ModalWidth } from "../Modal/Modal";
14
14
  import ModelFormModal from "../ModelFormModal/ModelFormModal";
15
+ import { ToastType } from "../Toast/Toast";
16
+ import { ShowToastNotification } from "../Toast/ToastInit";
15
17
  import ModelDetail, { ComponentProps as ModeDetailProps } from "./ModelDetail";
16
18
  import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
17
19
  import IconProp from "../../../Types/Icon/IconProp";
@@ -165,6 +167,16 @@ const CardModelDetail: <TBaseModel extends BaseModel>(
165
167
  onSuccess={(item: TBaseModel) => {
166
168
  setShowModal(false);
167
169
  setRefresher(!refresher);
170
+ /*
171
+ * The modal just disappears otherwise, which reads as "nothing
172
+ * happened" - especially on cards that do not display every field
173
+ * the edit form exposes.
174
+ */
175
+ ShowToastNotification({
176
+ title: "Changes saved",
177
+ description: `${model.singularName || "Item"} updated successfully.`,
178
+ type: ToastType.SUCCESS,
179
+ });
168
180
  if (props.onSaveSuccess) {
169
181
  props.onSaveSuccess(item);
170
182
  }
@@ -180,7 +192,17 @@ const CardModelDetail: <TBaseModel extends BaseModel>(
180
192
  steps: props.formSteps || [],
181
193
  createOrUpdateApiUrl: props.createOrUpdateApiUrl,
182
194
  }}
183
- modelIdToEdit={item?.id || undefined}
195
+ /*
196
+ * Prefer the id the caller already handed us. Deriving it purely
197
+ * from the loaded item meant that opening the modal before (or
198
+ * without) a successful detail fetch produced an Update form with
199
+ * no id: ModelForm silently skipped its fetch, rendered defaults,
200
+ * and BasicForm coerced every untouched Toggle to false - so
201
+ * saving quietly wrote blank values over the real record.
202
+ */
203
+ modelIdToEdit={
204
+ props.modelDetailProps.modelId || item?.id || undefined
205
+ }
184
206
  />
185
207
  ) : (
186
208
  <></>
@@ -51,15 +51,55 @@ const ModelDetail: <TBaseModel extends BaseModel>(
51
51
  JSONObject | undefined
52
52
  >(undefined);
53
53
 
54
+ type HasPermissionToReadFieldFunction = (fieldName: string) => boolean;
55
+
56
+ /*
57
+ * The server rejects the WHOLE get-item request when the select names a
58
+ * single column the caller cannot read (SelectPermission.checkSelectPermission
59
+ * throws NotAuthorizedException), which blanks the entire card instead of
60
+ * hiding one row. setDetailFields() below already drops unreadable fields
61
+ * from the render, so the fetch has to drop them too - the same thing
62
+ * BaseModelTable does for its selectMoreFields.
63
+ */
64
+ const hasPermissionToReadField: HasPermissionToReadFieldFunction = (
65
+ fieldName: string,
66
+ ): boolean => {
67
+ if (User.isMasterAdmin()) {
68
+ return true;
69
+ }
70
+
71
+ const accessControl: Dictionary<ColumnAccessControl> =
72
+ new props.modelType().getColumnAccessControlForAllColumns() || {};
73
+
74
+ const fieldPermissions: Array<Permission> =
75
+ accessControl[fieldName]?.read || [];
76
+
77
+ return PermissionHelper.doesPermissionsIntersect(
78
+ PermissionUtil.getAllPermissions(),
79
+ fieldPermissions,
80
+ );
81
+ };
82
+
54
83
  type GetSelectFields = () => Select<TBaseModel>;
55
84
 
56
85
  const getSelectFields: GetSelectFields = (): Select<TBaseModel> => {
57
86
  const select: Select<TBaseModel> = {};
87
+
88
+ /*
89
+ * _id is never a declared field on most cards, but CardModelDetail needs
90
+ * it to know which record its edit modal is updating. It is exempt from
91
+ * select permission checks server-side, so it is always safe to ask for.
92
+ */
93
+ (select as Dictionary<boolean>)["_id"] = true;
94
+
58
95
  for (const field of props.fields) {
59
96
  if (!field.field) {
60
97
  continue;
61
98
  }
62
99
  for (const key of Object.keys(field.field)) {
100
+ if (!hasPermissionToReadField(key)) {
101
+ continue;
102
+ }
63
103
  select[key as keyof TBaseModel] = true;
64
104
  }
65
105
  }
@@ -70,7 +110,8 @@ const ModelDetail: <TBaseModel extends BaseModel>(
70
110
  typeof field === "string" &&
71
111
  field &&
72
112
  props.selectMoreFields &&
73
- (props.selectMoreFields as Select<TBaseModel>)[keyofField]
113
+ (props.selectMoreFields as Select<TBaseModel>)[keyofField] &&
114
+ hasPermissionToReadField(field)
74
115
  ) {
75
116
  select[keyofField] = props.selectMoreFields[keyofField] as JSONObject;
76
117
  }
@@ -91,6 +132,10 @@ const ModelDetail: <TBaseModel extends BaseModel>(
91
132
  continue;
92
133
  }
93
134
  for (const key of Object.keys(field.field)) {
135
+ if (!hasPermissionToReadField(key)) {
136
+ continue;
137
+ }
138
+
94
139
  if (model.isFileColumn(key)) {
95
140
  (relationSelect as JSONObject)[key] = {
96
141
  file: true,