Files
Checkmate/client/src/Pages/Infrastructure/Details/index.jsx
T
gorkem-bwl e9da602af6 feat: Add Proxmox LXC container monitoring support
## Summary
Add support for monitoring Proxmox LXC containers through the Capture agent's
/api/v1/metrics/proxmox endpoint. This feature is opt-in via a new setting
in the infrastructure monitor configuration.

## Backend Changes
- Add enableProxmoxContainers field to Monitor schema and types
- Update networkService to conditionally fetch Proxmox metrics
- Handle HTTP 207 multi-status responses and merge errors
- Add containers field to Check schema for storing container data
- Update validation schemas for the new setting

## Frontend Changes
- Add ContainerStats component with charts and stat boxes
- Add Containers tab to infrastructure monitor details (conditional)
- Add enableProxmoxContainers switch in monitor create/edit form
- Fix chart colors for disk I/O and network I/O to be distinct
- Add dotColor2 prop to InfrastructureTooltip for multi-series charts
- Add translation keys to all 16 locale files

## How It Works
1. User enables "Proxmox container monitoring" in monitor settings
2. Checkmate appends /proxmox to the metrics URL when fetching
3. Container data is displayed in a dedicated tab with:
   - CPU, memory, disk, swap, and uptime stat boxes
   - Interactive charts for CPU, memory, disk I/O, network I/O
4. Errors from Capture are merged and logged appropriately
2026-01-16 19:07:49 -05:00

159 lines
4.3 KiB
React

// Components
import { Stack, Typography, Tab } from "@mui/material";
import Breadcrumbs from "@/Components/v1/Breadcrumbs/index.jsx";
import MonitorDetailsControlHeader from "@/Components/v1/MonitorDetailsControlHeader/index.jsx";
import MonitorTimeFrameHeader from "@/Components/v1/MonitorTimeFrameHeader/index.jsx";
import StatusBoxes from "./Components/StatusBoxes/index.jsx";
import GaugeBoxes from "./Components/GaugeBoxes/index.jsx";
import AreaChartBoxes from "./Components/AreaChartBoxes/index.jsx";
import GenericFallback from "@/Components/v1/GenericFallback/index.jsx";
import NetworkStats from "./Components/NetworkStats/index.jsx";
import ContainerStats from "./Components/ContainerStats/index.jsx";
import CustomTabList from "@/Components/v1/Tab/index.jsx";
import TabContext from "@mui/lab/TabContext";
// Utils
import { useTheme } from "@emotion/react";
import { useIsAdmin } from "@/Hooks/useIsAdmin.js";
import { useFetchHardwareMonitorById } from "../../../Hooks/monitorHooks.js";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
// Constants
const BREADCRUMBS = [
{ name: "infrastructure monitors", path: "/infrastructure" },
{ name: "details", path: "" },
];
const InfrastructureDetails = () => {
// Local state
const [dateRange, setDateRange] = useState("recent");
const [trigger, setTrigger] = useState(false);
const [tab, setTab] = useState("details");
// Utils
const theme = useTheme();
const { monitorId } = useParams();
const { t } = useTranslation();
const isAdmin = useIsAdmin();
const [monitor, isLoading, networkError] = useFetchHardwareMonitorById({
monitorId,
dateRange,
updateTrigger: trigger,
});
const triggerUpdate = () => {
setTrigger(!trigger);
};
if (networkError === true) {
return (
<GenericFallback>
<Typography
variant="h1"
marginY={theme.spacing(4)}
color={theme.palette.primary.contrastTextTertiary}
>
{t("common.toasts.networkError")}
</Typography>
<Typography>{t("common.toasts.checkConnection")}</Typography>
</GenericFallback>
);
}
if (!isLoading && monitor?.stats?.checks?.length === 0) {
return (
<Stack gap={theme.spacing(10)}>
<Breadcrumbs list={BREADCRUMBS} />
<MonitorDetailsControlHeader
path={"infrastructure"}
isLoading={isLoading}
isAdmin={isAdmin}
monitor={monitor}
triggerUpdate={triggerUpdate}
/>
<GenericFallback>
<Typography>{t("distributedUptimeDetailsNoMonitorHistory")}</Typography>
</GenericFallback>
</Stack>
);
}
return (
<Stack gap={theme.spacing(10)}>
<Breadcrumbs list={BREADCRUMBS} />
<MonitorDetailsControlHeader
path={"infrastructure"}
isLoading={isLoading}
isAdmin={isAdmin}
monitor={monitor}
triggerUpdate={triggerUpdate}
/>
<TabContext value={tab}>
<CustomTabList
value={tab}
onChange={(e, v) => setTab(v)}
>
<Tab
label={t("details")}
value="details"
/>
<Tab
label={t("network")}
value="network"
/>
{monitor?.enableProxmoxContainers && (
<Tab
label={t("containers")}
value="containers"
/>
)}
</CustomTabList>
{tab === "details" && (
<>
<StatusBoxes
shouldRender={!isLoading}
monitor={monitor}
/>
<GaugeBoxes
isLoading={isLoading}
monitor={monitor}
/>
<MonitorTimeFrameHeader
isLoading={isLoading}
dateRange={dateRange}
setDateRange={setDateRange}
/>
<AreaChartBoxes
shouldRender={!isLoading}
monitor={monitor}
dateRange={dateRange}
/>
</>
)}
{tab === "network" && (
<NetworkStats
net={monitor?.stats?.aggregateData?.latestCheck?.net || []}
isLoading={isLoading}
checks={monitor?.stats?.checks}
dateRange={dateRange}
setDateRange={setDateRange}
/>
)}
{tab === "containers" && monitor?.enableProxmoxContainers && (
<ContainerStats
containers={monitor?.stats?.aggregateData?.latestCheck?.containers || []}
isLoading={isLoading}
checks={monitor?.stats?.checks}
dateRange={dateRange}
setDateRange={setDateRange}
/>
)}
</TabContext>
</Stack>
);
};
export default InfrastructureDetails;