From 186a2c6ea22589ca28f655eba01526ceb74ec2d9 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Fri, 15 May 2026 02:29:31 -0500 Subject: [PATCH 1/5] audit log template tweak --- src/data/AuditLogTemplates.json | 2 +- src/layouts/config.js | 7 + src/pages/cipp/advanced/worker-health.js | 442 +++++++++++++++++++++++ 3 files changed, 450 insertions(+), 1 deletion(-) create mode 100644 src/pages/cipp/advanced/worker-health.js diff --git a/src/data/AuditLogTemplates.json b/src/data/AuditLogTemplates.json index 1762fb2eb7bb..63df852bd318 100644 --- a/src/data/AuditLogTemplates.json +++ b/src/data/AuditLogTemplates.json @@ -450,7 +450,7 @@ { "Property": { "value": "String", "label": "SecuredAccessPassData" }, "Operator": { "value": "like", "label": "Like" }, - "Input": { "value": "*" } + "Input": { "value": "[*]" } } ] } diff --git a/src/layouts/config.js b/src/layouts/config.js index a9df0957241d..987ab9d8f984 100644 --- a/src/layouts/config.js +++ b/src/layouts/config.js @@ -1116,6 +1116,13 @@ export const nativeMenuItems = [ permissions: ['CIPP.SuperAdmin.*'], scope: 'global', }, + { + title: 'Worker Health', + path: '/cipp/advanced/worker-health', + roles: ['superadmin'], + permissions: ['CIPP.SuperAdmin.*'], + scope: 'global', + }, ], }, ], diff --git a/src/pages/cipp/advanced/worker-health.js b/src/pages/cipp/advanced/worker-health.js new file mode 100644 index 000000000000..fd23de33c031 --- /dev/null +++ b/src/pages/cipp/advanced/worker-health.js @@ -0,0 +1,442 @@ +import { useMemo } from "react"; +import Head from "next/head"; +import { + Box, + Button, + Card, + CardContent, + CardHeader, + Chip, + CircularProgress, + Container, + LinearProgress, + Stack, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + Typography, +} from "@mui/material"; +import { + Memory, + Speed, + PlayArrow, + HourglassEmpty, + CheckCircle, + Warning, + Cancel, + Delete, + LowPriority, + DeleteSweep, +} from "@mui/icons-material"; +import { Grid } from "@mui/system"; +import { Layout as DashboardLayout } from "../../../layouts/index.js"; +import { CippInfoBar } from "../../../components/CippCards/CippInfoBar"; +import { CippPropertyListCard } from "../../../components/CippCards/CippPropertyListCard"; +import { CippDataTable } from "../../../components/CippTable/CippDataTable"; +import { ApiGetCall, ApiPostCall } from "../../../api/ApiCall"; + +const formatDuration = (ms) => { + if (ms === 0 || ms == null) return "—"; + if (ms < 1000) return `${ms}ms`; + if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; + return `${(ms / 60000).toFixed(1)}m`; +}; + +const formatUptime = (seconds) => { + if (!seconds) return "—"; + const h = Math.floor(seconds / 3600); + const m = Math.floor((seconds % 3600) / 60); + if (h > 0) return `${h}h ${m}m`; + return `${m}m`; +}; + +const WorkerStatusChip = ({ isBusy, currentFunction }) => { + if (isBusy) { + return ( + } + sx={{ maxWidth: 200 }} + /> + ); + } + return } />; +}; + +const UtilizationBar = ({ value }) => ( + + + 80 ? "error" : value > 50 ? "warning" : "primary"} + sx={{ height: 8, borderRadius: 4 }} + /> + + + {value}% + + +); + +const WorkerTable = ({ workers, title }) => { + if (!workers || workers.length === 0) return null; + + return ( + + + + + + + + Worker + Status + Invocations + Utilization + Avg + Min + Max + Last + Faults + + + + {workers.map((w) => ( + + + + W{w.WorkerId} + + + + + + {w.TotalInvocations?.toLocaleString() ?? 0} + + + + {formatDuration(w.AvgDurationMs)} + {formatDuration(w.MinDurationMs)} + {formatDuration(w.MaxDurationMs)} + {formatDuration(w.LastDurationMs)} + + {w.TotalFaults > 0 ? ( + + ) : ( + "0" + )} + + + ))} + +
+
+
+
+ ); +}; + +const Page = () => { + const healthQuery = ApiGetCall({ + url: "/api/ListWorkerHealth", + data: { Action: "Snapshot" }, + queryKey: "WorkerHealth", + refetchInterval: 5000, + }); + + const jobAction = ApiPostCall({ + relatedQueryKeys: ["WorkerHealthJobs", "WorkerHealth"], + }); + + const snapshot = healthQuery.data?.Results; + + const infoBarData = useMemo(() => { + if (!snapshot) return []; + const http = snapshot.HttpPool || {}; + const bg = snapshot.BgPool || {}; + const jobs = snapshot.Jobs || {}; + const limiter = snapshot.Limiter || {}; + + return [ + { + icon: , + name: "HTTP Workers", + data: `${http.BusyCount ?? 0} / ${http.PoolSize ?? 0} busy`, + color: http.BusyCount >= http.PoolSize ? "error" : "primary", + }, + { + icon: , + name: "BG Workers", + data: `${bg.BusyCount ?? 0} / ${bg.PoolSize ?? 0} busy`, + color: bg.BusyCount >= bg.PoolSize ? "error" : "primary", + }, + { + icon: jobs.Running > 0 ? : , + name: "Job Queue", + data: `${jobs.Running ?? 0} running, ${jobs.Queued ?? 0} queued`, + color: jobs.Queued > 10 ? "warning" : "primary", + }, + { + icon: limiter.IsHttpThrottled ? : , + name: "BG Limiter", + data: limiter.IsHttpThrottled + ? "HTTP Throttled" + : `${limiter.Active ?? 0} / ${limiter.CurrentMax ?? 0} active`, + color: limiter.IsHttpThrottled ? "error" : "primary", + }, + ]; + }, [snapshot]); + + const httpPoolItems = useMemo(() => { + if (!snapshot?.HttpPool) return []; + const p = snapshot.HttpPool; + return [ + { label: "Pool Size", value: p.PoolSize }, + { label: "Available", value: p.Available }, + { label: "Busy", value: p.BusyCount }, + { label: "Total Invocations", value: p.TotalInvocations?.toLocaleString() ?? 0 }, + { label: "Total Busy Time", value: formatDuration(p.TotalBusyMs) }, + { label: "Avg Utilization", value: `${p.AvgUtilizationPct ?? 0}%` }, + { label: "Avg Duration", value: formatDuration(p.AvgDurationMs) }, + { label: "Total Faults", value: p.TotalFaults ?? 0 }, + ]; + }, [snapshot]); + + const bgPoolItems = useMemo(() => { + if (!snapshot?.BgPool) return []; + const p = snapshot.BgPool; + return [ + { label: "Pool Size", value: p.PoolSize }, + { label: "Available", value: p.Available }, + { label: "Busy", value: p.BusyCount }, + { label: "Total Invocations", value: p.TotalInvocations?.toLocaleString() ?? 0 }, + { label: "Total Busy Time", value: formatDuration(p.TotalBusyMs) }, + { label: "Avg Utilization", value: `${p.AvgUtilizationPct ?? 0}%` }, + { label: "Avg Duration", value: formatDuration(p.AvgDurationMs) }, + { label: "Total Faults", value: p.TotalFaults ?? 0 }, + ]; + }, [snapshot]); + + const limiterItems = useMemo(() => { + if (!snapshot?.Limiter) return []; + const l = snapshot.Limiter; + return [ + { label: "Base Concurrency", value: l.BaseConcurrency }, + { label: "Ceiling Concurrency", value: l.CeilingConcurrency }, + { label: "Current Max", value: l.CurrentMax }, + { label: "Active", value: l.Active }, + { label: "Waiting", value: l.Waiting }, + { + label: "HTTP Throttled", + value: l.IsHttpThrottled ? "Yes" : "No", + }, + ]; + }, [snapshot]); + + const jobItems = useMemo(() => { + if (!snapshot?.Jobs) return []; + const j = snapshot.Jobs; + return [ + { label: "Running", value: j.Running }, + { label: "Queued", value: j.Queued }, + { label: "Completed", value: j.Completed?.toLocaleString() ?? 0 }, + { label: "Failed", value: j.Failed }, + { label: "Total Processed", value: j.TotalProcessed?.toLocaleString() ?? 0 }, + { label: "Max Concurrency", value: j.MaxConcurrency }, + { label: "Active Concurrency", value: j.ActiveConcurrency }, + ]; + }, [snapshot]); + + const jobSimpleColumns = ["Name", "RunName", "Priority", "Status", "WaitSeconds", "DurationSeconds"]; + + const jobActions = useMemo( + () => [ + { + label: "Cancel Job", + icon: , + color: "error.main", + noConfirm: true, + customFunction: (row) => { + jobAction.mutate({ + url: "/api/ListWorkerHealth", + data: { Action: "CancelJob", JobId: row.Id }, + }); + }, + condition: (row) => row.Status === "Queued", + }, + { + label: "Change Priority", + icon: , + fields: [ + { + type: "textField", + name: "Priority", + label: "New Priority (0 = highest)", + }, + ], + url: "/api/ListWorkerHealth", + data: { Action: "ChangePriority" }, + dataFunction: (row, formData) => ({ + Action: "ChangePriority", + JobId: row.Id, + Priority: parseInt(formData.Priority, 10), + }), + confirmText: "Change", + condition: (row) => row.Status === "Queued", + relatedQueryKeys: ["WorkerHealthJobs", "WorkerHealth"], + }, + { + label: "Cancel Run", + icon: , + color: "error.main", + noConfirm: true, + customFunction: (row) => { + if (row.RunName) { + jobAction.mutate({ + url: "/api/ListWorkerHealth", + data: { Action: "CancelRun", RunName: row.RunName }, + }); + } + }, + condition: (row) => row.Status === "Queued" && row.RunName, + }, + { + label: "Delete", + icon: , + noConfirm: true, + customFunction: (row) => { + jobAction.mutate({ + url: "/api/ListWorkerHealth", + data: { Action: "DeleteJob", JobId: row.Id }, + }); + }, + condition: (row) => row.Status !== "Queued" && row.Status !== "Running", + }, + ], + [jobAction] + ); + + const jobFilters = useMemo( + () => [ + { + filterName: "Queued", + value: [{ id: "Status", value: "Queued" }], + }, + { + filterName: "Running", + value: [{ id: "Status", value: "Running" }], + }, + { + filterName: "Failed", + value: [{ id: "Status", value: "Failed" }], + }, + ], + [] + ); + + return ( + <> + + Worker Health | CIPP + + + + + + Worker Health + + {healthQuery.isFetching && } + {snapshot && ( + + Uptime: {formatUptime(snapshot.UptimeSeconds)} | Auto-refreshing every 5s + + )} + + + + + + + + + + + + + + + + + + + + + + + + + + + } + color="warning" + onClick={() => + jobAction.mutate({ + url: "/api/ListWorkerHealth", + data: { Action: "PurgeCompleted" }, + }) + } + > + Purge Completed + + } + /> + + + + + ); +}; + +Page.getLayout = (page) => {page}; + +export default Page; From 9d5ce40275098a4a442d247fe42541958d08ba88 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Mon, 18 May 2026 07:27:51 -0400 Subject: [PATCH 2/5] Org auto expanding archive property usage --- src/components/CippCards/CippExchangeInfoCard.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/CippCards/CippExchangeInfoCard.jsx b/src/components/CippCards/CippExchangeInfoCard.jsx index 6a00f53c0248..8bb2737f76c3 100644 --- a/src/components/CippCards/CippExchangeInfoCard.jsx +++ b/src/components/CippCards/CippExchangeInfoCard.jsx @@ -247,7 +247,9 @@ export const CippExchangeInfoCard = (props) => { <> - Auto Expanding Archive: + {exchangeData?.AutoExpandingArchiveScope === 'Organization' + ? 'Auto Expanding Archive: (org)' + : 'Auto Expanding Archive:'} {getCippFormatting( From 6db7e7760fc02807df809897fcda40b48a8b365b Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Mon, 18 May 2026 14:37:01 -0400 Subject: [PATCH 3/5] Delete .claude directory Signed-off-by: Zacgoose <107489668+Zacgoose@users.noreply.github.com> --- .claude/worktrees/blissful-golick-d405ab | 1 - 1 file changed, 1 deletion(-) delete mode 160000 .claude/worktrees/blissful-golick-d405ab diff --git a/.claude/worktrees/blissful-golick-d405ab b/.claude/worktrees/blissful-golick-d405ab deleted file mode 160000 index 0710355e2ada..000000000000 --- a/.claude/worktrees/blissful-golick-d405ab +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0710355e2adac37fffe4c7eef48d6f2c3a04993d From 1e7aef11995feb42e9872ec4aefac39fc7ba67c5 Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 19 May 2026 08:19:29 -0400 Subject: [PATCH 4/5] Update alerts.json --- src/data/alerts.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/alerts.json b/src/data/alerts.json index 9bce965852ab..ac8a28fa6cc0 100644 --- a/src/data/alerts.json +++ b/src/data/alerts.json @@ -125,7 +125,7 @@ "inputType": "textField", "inputLabel": "Enter quota percentage", "inputName": "QuotaUsedQuota", - "recommendedRunInterval": "4h" + "recommendedRunInterval": "1d" }, { "name": "SharePointQuota", @@ -134,7 +134,7 @@ "inputType": "textField", "inputLabel": "Enter quota percentage", "inputName": "SharePointQuota", - "recommendedRunInterval": "4h" + "recommendedRunInterval": "1d" }, { "name": "OneDriveQuota", @@ -143,7 +143,7 @@ "inputType": "textField", "inputLabel": "Enter quota percentage (default: 90)", "inputName": "OneDriveQuota", - "recommendedRunInterval": "4h" + "recommendedRunInterval": "1d" }, { "name": "ExpiringLicenses", From fc246a54ee6c1720f9d442f4cf22568b53add85d Mon Sep 17 00:00:00 2001 From: Zacgoose <107489668+Zacgoose@users.noreply.github.com> Date: Tue, 19 May 2026 10:02:02 -0400 Subject: [PATCH 5/5] update default value for standard --- src/data/standards.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/standards.json b/src/data/standards.json index 4c61c04cf250..16a6406db1fa 100644 --- a/src/data/standards.json +++ b/src/data/standards.json @@ -4100,7 +4100,7 @@ "type": "number", "name": "standards.IntuneComplianceSettings.deviceComplianceCheckinThresholdDays", "label": "Compliance status validity period (days)", - "defaultValue": 130, + "defaultValue": 120, "validators": { "min": { "value": 1, "message": "Minimum value is 1" }, "max": { "value": 120, "message": "Maximum value is 120" }