Merge pull request #3219 from bluewave-labs/feat/v2-infra-details
Feat/v2 infra details
This commit is contained in:
24 files changed
+1068
-54
No files matched your search
@@ -0,0 +1,64 @@
|
||||
import MuiTabs from "@mui/material/Tabs";
|
||||
import type { TabsProps } from "@mui/material/Tabs";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
interface CustomTabsProps extends TabsProps {}
|
||||
|
||||
export const Tabs = (props: CustomTabsProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<MuiTabs
|
||||
sx={{
|
||||
minHeight: 34,
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
"& .MuiTabs-indicator": {
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
height: 2,
|
||||
bottom: 0,
|
||||
},
|
||||
"& .MuiTabs-flexContainer": {
|
||||
gap: theme.spacing(16),
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{props.children}
|
||||
</MuiTabs>
|
||||
);
|
||||
};
|
||||
|
||||
import MuiTab from "@mui/material/Tab";
|
||||
import type { TabProps } from "@mui/material/Tab";
|
||||
interface CustomTabProps extends TabProps {}
|
||||
|
||||
export const Tab = (props: CustomTabProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<MuiTab
|
||||
disableRipple
|
||||
iconPosition="start"
|
||||
sx={{
|
||||
textTransform: "none",
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
minHeight: 34,
|
||||
padding: theme.spacing(1, 0),
|
||||
paddingBottom: 0,
|
||||
minWidth: "auto",
|
||||
alignItems: "flex-start",
|
||||
color: theme.palette.text.secondary,
|
||||
"&.Mui-selected": {
|
||||
color: theme.palette.primary.main,
|
||||
fontWeight: 600,
|
||||
},
|
||||
"&:hover": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
"& .MuiTab-iconWrapper": {
|
||||
marginRight: theme.spacing(2),
|
||||
marginBottom: 0,
|
||||
},
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -16,3 +16,4 @@ export * from "./Tooltip";
|
||||
export * from "./StatBox";
|
||||
export * from "./BaseChart";
|
||||
export * from "./Gauge";
|
||||
export * from "./Tabs";
|
||||
@@ -0,0 +1,206 @@
|
||||
import { BaseChart } from "@/Components/v2/design-elements";
|
||||
import {
|
||||
AreaChart,
|
||||
Area,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
ResponsiveContainer,
|
||||
} from "recharts";
|
||||
import { Fragment, useId } from "react";
|
||||
import { XTick } from "@/Components/v2/monitors";
|
||||
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import type { HardwareCheckStats } from "@/Types/Monitor";
|
||||
|
||||
const AREA_COLORS = [
|
||||
// Blues
|
||||
"#3182bd", // Deep blue
|
||||
"#6baed6", // Medium blue
|
||||
"#9ecae1", // Light blue
|
||||
|
||||
// Greens
|
||||
"#74c476", // Soft green
|
||||
"#a1d99b", // Light green
|
||||
"#c7e9c0", // Pale green
|
||||
|
||||
// Oranges
|
||||
"#fdae6b", // Warm orange
|
||||
"#fdd0a2", // Light orange
|
||||
"#feedde", // Pale orange
|
||||
|
||||
// Purples
|
||||
"#9467bd", // Lavender
|
||||
"#a55194", // Deep magenta
|
||||
"#c994c7", // Soft magenta
|
||||
|
||||
// Reds
|
||||
"#ff9896", // Soft red
|
||||
"#de2d26", // Deep red
|
||||
"#fc9272", // Medium red
|
||||
|
||||
// Cyans/Teals
|
||||
"#17becf", // Cyan
|
||||
"#7fcdbb", // Teal
|
||||
"#a1dab4", // Light teal
|
||||
|
||||
// Yellows
|
||||
"#fec44f", // Mustard
|
||||
"#fee391", // Light yellow
|
||||
"#ffffd4", // Pale yellow
|
||||
|
||||
// Additional colors
|
||||
"#e377c2", // Soft pink
|
||||
"#bcbd22", // Olive
|
||||
"#2ca02c", // Vibrant green
|
||||
];
|
||||
|
||||
const createGradient = ({
|
||||
id,
|
||||
startColor,
|
||||
endColor,
|
||||
startOpacity = 0.8,
|
||||
endOpacity = 0,
|
||||
direction = "vertical",
|
||||
}: {
|
||||
id: string;
|
||||
startColor: string;
|
||||
endColor: string;
|
||||
startOpacity?: number;
|
||||
endOpacity?: number;
|
||||
direction?: "vertical" | "horizontal";
|
||||
}) => (
|
||||
<defs>
|
||||
<linearGradient
|
||||
id={id}
|
||||
x1={direction === "vertical" ? "0" : "0"}
|
||||
y1={direction === "vertical" ? "0" : "0"}
|
||||
x2={direction === "vertical" ? "0" : "1"}
|
||||
y2={direction === "vertical" ? "1" : "0"}
|
||||
>
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={startColor}
|
||||
stopOpacity={startOpacity}
|
||||
/>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={endColor}
|
||||
stopOpacity={endOpacity}
|
||||
/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
);
|
||||
|
||||
export const HistogramInfrastructure = ({
|
||||
dateRange,
|
||||
title,
|
||||
type,
|
||||
idx: _idx,
|
||||
checks,
|
||||
xKey,
|
||||
yDomain,
|
||||
dataKeys,
|
||||
gradient = false,
|
||||
gradientDirection = "vertical",
|
||||
gradientStartColor,
|
||||
gradientEndColor,
|
||||
strokeColor,
|
||||
fillColor,
|
||||
yAxisFormatter,
|
||||
}: {
|
||||
dateRange: string;
|
||||
title: string;
|
||||
type: string;
|
||||
idx: number | null;
|
||||
checks: HardwareCheckStats[];
|
||||
xKey: string;
|
||||
yDomain?: number[];
|
||||
dataKeys: string[];
|
||||
gradient?: boolean;
|
||||
gradientDirection?: "vertical" | "horizontal";
|
||||
gradientStartColor?: string;
|
||||
gradientEndColor?: string;
|
||||
strokeColor: string;
|
||||
fillColor?: string;
|
||||
yAxisFormatter?: (value: number) => string;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const uniqueId = useId();
|
||||
let data = checks.reverse();
|
||||
|
||||
let avgTemps: { bucketDate: string; avg_temp: number | null }[] = [];
|
||||
let tempYDomain: number[] = [];
|
||||
if (type === "temp") {
|
||||
avgTemps = data.map((check) => {
|
||||
const temps = check.avgTemperature || [];
|
||||
if (temps.length === 0) return { bucketDate: check.bucketDate, avg_temp: null };
|
||||
const totalTemp = temps.reduce((sum, temp) => sum + (temp || 0), 0);
|
||||
const avgTemp = totalTemp / temps.length;
|
||||
return { bucketDate: check.bucketDate, avg_temp: avgTemp };
|
||||
});
|
||||
|
||||
const maxTemp: number = avgTemps.reduce((max, item) => {
|
||||
return item.avg_temp && item.avg_temp > max ? item.avg_temp : max;
|
||||
}, 0);
|
||||
|
||||
tempYDomain = [0, Math.ceil((maxTemp * 1.3) / 10) * 10];
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseChart
|
||||
icon={null}
|
||||
title={title}
|
||||
>
|
||||
<ResponsiveContainer
|
||||
width="100%"
|
||||
height={200}
|
||||
>
|
||||
<AreaChart data={type === "temp" ? avgTemps : data}>
|
||||
<XAxis
|
||||
dataKey={xKey}
|
||||
tick={(props) => (
|
||||
<XTick
|
||||
{...props}
|
||||
range={dateRange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<YAxis
|
||||
domain={type === "temp" ? tempYDomain : yDomain}
|
||||
tickFormatter={yAxisFormatter}
|
||||
/>
|
||||
|
||||
<CartesianGrid
|
||||
stroke={theme.palette.divider}
|
||||
strokeWidth={1}
|
||||
strokeOpacity={1}
|
||||
fill="transparent"
|
||||
vertical={false}
|
||||
/>
|
||||
{dataKeys?.map((dataKey, index) => {
|
||||
const gradientId = `gradient-${uniqueId}-${index}`;
|
||||
return (
|
||||
<Fragment key={`${dataKey}-${index}`}>
|
||||
{gradient === true &&
|
||||
createGradient({
|
||||
id: gradientId,
|
||||
startColor: gradientStartColor || AREA_COLORS[index],
|
||||
endColor: gradientEndColor || "transparent",
|
||||
direction: gradientDirection,
|
||||
})}
|
||||
<Area
|
||||
key={dataKey}
|
||||
type="monotone"
|
||||
dataKey={dataKey}
|
||||
stroke={strokeColor || AREA_COLORS[index]}
|
||||
fill={gradient === true ? `url(#${gradientId})` : fillColor}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</BaseChart>
|
||||
);
|
||||
};
|
||||
@@ -9,3 +9,4 @@ export * from "./charts/PiePageSpeed";
|
||||
export * from "./charts/PiePageSpeedLegend";
|
||||
export * from "./charts/HistogramPageSpeedDetails";
|
||||
export * from "./charts/HistogramPageSpeedDetailsTooltip";
|
||||
export * from "./charts/HistogramInfrastructure";
|
||||
@@ -0,0 +1,104 @@
|
||||
import Grid from "@mui/material/Grid2";
|
||||
import { HistogramInfrastructure } from "@/Components/v2/monitors";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { HardwareCheckStats } from "@/Types/Monitor";
|
||||
import { useMemo } from "react";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
const formatPercent = (value: number) => `${(value * 100).toFixed(0)}%`;
|
||||
const formatTemp = (value: number) => `${value.toFixed(0)}°C`;
|
||||
|
||||
const getChartConfigs = (theme: any, checks: HardwareCheckStats[], t: any) => {
|
||||
return [
|
||||
{
|
||||
title: t("pages.infrastructure.charts.labels.memory"),
|
||||
type: "memory",
|
||||
dataKeys: ["avgMemoryUsage"],
|
||||
strokeColor: theme.palette.primary.main,
|
||||
gradientStartColor: theme.palette.primary.main,
|
||||
yDomain: [0, 1],
|
||||
yAxisFormatter: formatPercent,
|
||||
idx: null,
|
||||
},
|
||||
{
|
||||
title: t("pages.infrastructure.charts.labels.cpu"),
|
||||
type: "cpu",
|
||||
dataKeys: ["avgCpuUsage"],
|
||||
strokeColor: theme.palette.success.main,
|
||||
gradientStartColor: theme.palette.success.main,
|
||||
yDomain: [0, 1],
|
||||
yAxisFormatter: formatPercent,
|
||||
idx: null,
|
||||
},
|
||||
{
|
||||
title: t("pages.infrastructure.charts.labels.temp"),
|
||||
type: "temp",
|
||||
dataKeys: ["avg_temp"],
|
||||
strokeColor: theme.palette.error.main,
|
||||
gradientStartColor: theme.palette.error.main,
|
||||
yDomain: [0, 150],
|
||||
yAxisFormatter: formatTemp,
|
||||
idx: null,
|
||||
},
|
||||
...(checks[0]?.disks?.map((_, idx) => ({
|
||||
title: t("pages.infrastructure.charts.labels.disk", { idx }),
|
||||
type: "disk",
|
||||
dataKeys: [`disks[${idx}].usagePercent`],
|
||||
strokeColor: theme.palette.warning.main,
|
||||
gradientStartColor: theme.palette.warning.main,
|
||||
yDomain: [0, 1],
|
||||
yAxisFormatter: formatPercent,
|
||||
idx,
|
||||
})) || []),
|
||||
];
|
||||
};
|
||||
|
||||
export const InfraDetailsCharts = ({
|
||||
checks,
|
||||
dateRange,
|
||||
}: {
|
||||
checks: HardwareCheckStats[];
|
||||
dateRange: string;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
||||
const { t } = useTranslation();
|
||||
const chartConfigs = useMemo(
|
||||
() => getChartConfigs(theme, checks, t),
|
||||
[theme, checks, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
spacing={theme.spacing(8)}
|
||||
>
|
||||
{chartConfigs.map((config) => {
|
||||
return (
|
||||
<Grid
|
||||
size={isSmall ? 12 : 6}
|
||||
key={`${config.type}-${config.idx ?? ""}`}
|
||||
>
|
||||
<HistogramInfrastructure
|
||||
dateRange={dateRange}
|
||||
title={config.title}
|
||||
type={config.type}
|
||||
idx={config.idx}
|
||||
key={`${config.type}-${config.idx ?? ""}`}
|
||||
checks={checks}
|
||||
xKey="bucketDate"
|
||||
yDomain={config.yDomain}
|
||||
dataKeys={config.dataKeys}
|
||||
gradient={true}
|
||||
gradientStartColor={config.gradientStartColor}
|
||||
gradientEndColor="#ffffff"
|
||||
strokeColor={config.strokeColor}
|
||||
yAxisFormatter={config.yAxisFormatter}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
import Grid from "@mui/material/Grid2";
|
||||
import { HistogramInfrastructure } from "@/Components/v2/monitors";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { HardwareCheckStats } from "@/Types/Monitor";
|
||||
import { useMemo } from "react";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
|
||||
const formatBytesToMB = (value: number) => `${(value / (1024 * 1024)).toFixed(2)} MB`;
|
||||
|
||||
interface ChartConfig {
|
||||
title: string;
|
||||
type: string;
|
||||
dataKeys: string[];
|
||||
strokeColor: string;
|
||||
gradientStartColor: string;
|
||||
idx: number | null;
|
||||
interfaceName?: string;
|
||||
}
|
||||
|
||||
const getChartConfigs = (
|
||||
theme: any,
|
||||
checks: HardwareCheckStats[],
|
||||
t: any
|
||||
): ChartConfig[] => {
|
||||
const configs: ChartConfig[] = [];
|
||||
|
||||
const netInterfaces = checks[0]?.net || [];
|
||||
|
||||
netInterfaces.forEach((iface, idx) => {
|
||||
configs.push(
|
||||
{
|
||||
title: t("pages.infrastructure.charts.labels.netBytesSent", {
|
||||
name: iface.name,
|
||||
}),
|
||||
type: "netBytesSent",
|
||||
dataKeys: [`net[${idx}].bytesSentPerSecond`],
|
||||
strokeColor: theme.palette.primary.main,
|
||||
gradientStartColor: theme.palette.primary.main,
|
||||
idx,
|
||||
interfaceName: iface.name,
|
||||
},
|
||||
{
|
||||
title: t("pages.infrastructure.charts.labels.netBytesRecv", {
|
||||
name: iface.name,
|
||||
}),
|
||||
type: "netBytesRecv",
|
||||
dataKeys: [`net[${idx}].deltaBytesRecv`],
|
||||
strokeColor: theme.palette.success.main,
|
||||
gradientStartColor: theme.palette.success.main,
|
||||
idx,
|
||||
interfaceName: iface.name,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return configs;
|
||||
};
|
||||
|
||||
export const InfraNetworkCharts = ({
|
||||
checks,
|
||||
dateRange,
|
||||
}: {
|
||||
checks: HardwareCheckStats[];
|
||||
dateRange: string;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
||||
const { t } = useTranslation();
|
||||
const chartConfigs = useMemo(
|
||||
() => getChartConfigs(theme, checks, t),
|
||||
[theme, checks, t]
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
spacing={theme.spacing(8)}
|
||||
>
|
||||
{chartConfigs.map((config) => {
|
||||
return (
|
||||
<Grid
|
||||
size={isSmall ? 12 : 6}
|
||||
key={`${config.type}-${config.interfaceName ?? config.idx ?? ""}`}
|
||||
>
|
||||
<HistogramInfrastructure
|
||||
dateRange={dateRange}
|
||||
title={config.title}
|
||||
type={config.type}
|
||||
idx={config.idx}
|
||||
checks={checks}
|
||||
xKey="bucketDate"
|
||||
dataKeys={config.dataKeys}
|
||||
gradient={true}
|
||||
gradientStartColor={config.gradientStartColor}
|
||||
gradientEndColor="#ffffff"
|
||||
strokeColor={config.strokeColor}
|
||||
yAxisFormatter={formatBytesToMB}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { BaseChart, Gauge } from "@/Components/v2/design-elements";
|
||||
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getGbs, getFrequency } from "@/Utils/InfraUtils";
|
||||
import { useTheme } from "@mui/material";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
import type { CheckSnapshot } from "@/Types/Check";
|
||||
|
||||
const InfraDetailGauge = ({
|
||||
title,
|
||||
progress,
|
||||
upperLabel,
|
||||
upperValue,
|
||||
lowerLabel,
|
||||
lowerValue,
|
||||
}: {
|
||||
title: string;
|
||||
progress: number;
|
||||
upperLabel?: string;
|
||||
upperValue?: string | number;
|
||||
lowerLabel?: string;
|
||||
lowerValue?: string | number;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<BaseChart
|
||||
icon={null}
|
||||
title={title}
|
||||
maxWidth={225}
|
||||
>
|
||||
<Stack
|
||||
alignItems={"center"}
|
||||
mb={theme.spacing(4)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Gauge progress={progress} />
|
||||
</Stack>
|
||||
<Stack
|
||||
direction={"row"}
|
||||
justifyContent={"space-between"}
|
||||
>
|
||||
<Typography>{upperLabel}</Typography>
|
||||
<Typography>{upperValue}</Typography>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction={"row"}
|
||||
justifyContent={"space-between"}
|
||||
>
|
||||
<Typography>{lowerLabel}</Typography>
|
||||
<Typography>{lowerValue}</Typography>
|
||||
</Stack>
|
||||
</BaseChart>
|
||||
);
|
||||
};
|
||||
|
||||
export const InfraDetailsGauges = ({
|
||||
snapshot,
|
||||
}: {
|
||||
snapshot: CheckSnapshot | undefined;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
|
||||
|
||||
if (!snapshot) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction={isSmall ? "column" : "row"}
|
||||
spacing={theme.spacing(8)}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<InfraDetailGauge
|
||||
title={t("pages.infrastructure.gauges.memory.title")}
|
||||
progress={(snapshot?.memory?.usage_percent || 0) * 100}
|
||||
upperLabel={t("pages.infrastructure.gauges.memory.upperLabel")}
|
||||
upperValue={getGbs(snapshot?.memory?.used_bytes || 0)}
|
||||
lowerLabel={t("pages.infrastructure.gauges.memory.lowerLabel")}
|
||||
lowerValue={getGbs(snapshot?.memory?.total_bytes || 0)}
|
||||
/>
|
||||
<InfraDetailGauge
|
||||
title={t("pages.infrastructure.gauges.cpu.title")}
|
||||
progress={(snapshot?.cpu?.usage_percent || 0) * 100}
|
||||
upperLabel={t("pages.infrastructure.gauges.cpu.upperLabel")}
|
||||
upperValue={getFrequency(snapshot?.cpu?.current_frequency || 0)}
|
||||
lowerLabel={t("pages.infrastructure.gauges.cpu.lowerLabel")}
|
||||
lowerValue={getFrequency(snapshot?.cpu?.frequency || 0)}
|
||||
/>
|
||||
{snapshot?.disk?.map((disk, idx) => {
|
||||
return (
|
||||
<InfraDetailGauge
|
||||
key={disk?.device || 0 + idx}
|
||||
// title={`Disk ${idx} usage`}
|
||||
title={t("pages.infrastructure.gauges.disk.title", { idx })}
|
||||
progress={(disk.usage_percent || 0) * 100}
|
||||
upperLabel={t("pages.infrastructure.gauges.disk.upperLabel")}
|
||||
upperValue={getGbs(disk?.used_bytes || 0)}
|
||||
lowerLabel={t("pages.infrastructure.gauges.disk.lowerLabel")}
|
||||
lowerValue={getGbs(disk?.total_bytes || 0)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { StatBox } from "@/Components/v2/design-elements";
|
||||
|
||||
import { useTheme } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { Monitor } from "@/Types/Monitor";
|
||||
import {
|
||||
getAvgTemp,
|
||||
getCores,
|
||||
getFrequency,
|
||||
getGbs,
|
||||
getDiskTotalGbs,
|
||||
getOsAndPlatform,
|
||||
} from "@/Utils/InfraUtils";
|
||||
|
||||
export const StatusBoxes = ({ monitor }: { monitor: Monitor }) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
const latestCheck = monitor?.recentChecks?.[0];
|
||||
// Get data from latest check
|
||||
const physicalCores = getCores(latestCheck?.cpu?.physical_core);
|
||||
const logicalCores = getCores(latestCheck?.cpu?.logical_core);
|
||||
const cpuFrequency = getFrequency(latestCheck?.cpu?.frequency);
|
||||
const cpuTemps = latestCheck?.cpu?.temperature ?? [];
|
||||
const cpuTemperature = getAvgTemp(cpuTemps);
|
||||
const memoryTotalBytes = getGbs(latestCheck?.memory?.total_bytes);
|
||||
const diskTotalBytes = getDiskTotalGbs(latestCheck?.disk);
|
||||
const os = getOsAndPlatform(latestCheck?.host);
|
||||
|
||||
const platform = latestCheck?.host?.platform ?? undefined;
|
||||
const osPlatform =
|
||||
typeof os === "undefined" && typeof platform === "undefined"
|
||||
? undefined
|
||||
: `${os} ${platform}`;
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.spacing(8)}
|
||||
flexWrap={"wrap"}
|
||||
>
|
||||
<StatBox
|
||||
title={t("pages.infrastructure.statBoxes.cpuPhysical")}
|
||||
subtitle={physicalCores.toString()}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.infrastructure.statBoxes.cpuLogical")}
|
||||
subtitle={logicalCores.toString()}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.infrastructure.statBoxes.cpuFrequency")}
|
||||
subtitle={cpuFrequency}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.infrastructure.statBoxes.avgCpuTemperature")}
|
||||
subtitle={cpuTemperature}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.infrastructure.statBoxes.memory")}
|
||||
subtitle={memoryTotalBytes.toString()}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.infrastructure.statBoxes.disk")}
|
||||
subtitle={diskTotalBytes.toString()}
|
||||
/>
|
||||
<StatBox
|
||||
title={t("pages.infrastructure.statBoxes.os")}
|
||||
subtitle={osPlatform || "N/A"}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { InfraNetworkCharts } from "./ChartsNetwork";
|
||||
|
||||
import { useTheme } from "@mui/material";
|
||||
import type { HardwareStats } from "@/Types/Monitor";
|
||||
|
||||
export const TabNetwork = ({
|
||||
stats,
|
||||
dateRange,
|
||||
}: {
|
||||
stats: HardwareStats | undefined;
|
||||
dateRange: string;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
if (!stats) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const checks = stats?.checks || [];
|
||||
return (
|
||||
<Stack gap={theme.spacing(8)}>
|
||||
<InfraNetworkCharts
|
||||
checks={checks}
|
||||
dateRange={dateRange}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import Stack from "@mui/material/Stack";
|
||||
import { InfraDetailsGauges } from "@/Pages/Infrastructure/Details/Components/Gauges";
|
||||
import { StatusBoxes } from "@/Pages/Infrastructure/Details/Components/StatusBoxes";
|
||||
import { InfraDetailsCharts } from "@/Pages/Infrastructure/Details/Components/Charts";
|
||||
|
||||
import { useTheme } from "@mui/material";
|
||||
import type { HardwareStats, Monitor } from "@/Types/Monitor";
|
||||
|
||||
export const TabOverview = ({
|
||||
monitor,
|
||||
stats,
|
||||
dateRange,
|
||||
}: {
|
||||
monitor: Monitor | undefined;
|
||||
stats: HardwareStats | undefined;
|
||||
dateRange: string;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
if (!monitor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const checks = stats?.checks || [];
|
||||
return (
|
||||
<Stack gap={theme.spacing(8)}>
|
||||
<StatusBoxes monitor={monitor} />
|
||||
<InfraDetailsGauges snapshot={monitor.recentChecks?.[0]} />
|
||||
<InfraDetailsCharts
|
||||
checks={checks}
|
||||
dateRange={dateRange}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
import { BasePage, Tab, Tabs } from "@/Components/v2/design-elements";
|
||||
import { HeaderMonitorControls, HeaderTimeRange } from "@/Components/v2/common";
|
||||
import { MonitorStatBoxes } from "@/Components/v2/monitors";
|
||||
import { TabNetwork } from "@/Pages/Infrastructure/Details/Components/TabNetwork";
|
||||
import { TabOverview } from "@/Pages/Infrastructure/Details/Components/TabOverview";
|
||||
|
||||
import { useMemo, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useGet } from "@/Hooks/UseApi";
|
||||
import type { HardwareDetailsResponse } from "@/Types/Monitor";
|
||||
import { useIsAdmin } from "@/Hooks/useIsAdmin";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const InfrastructureDetails = () => {
|
||||
const { t } = useTranslation();
|
||||
const isAdmin = useIsAdmin();
|
||||
|
||||
const { monitorId } = useParams<{ monitorId: string }>();
|
||||
|
||||
const [dateRange, setDateRange] = useState<string>("recent");
|
||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||
|
||||
const monitorDetailsUrl = useMemo(() => {
|
||||
if (!monitorId) {
|
||||
return null;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("dateRange", dateRange);
|
||||
return `/monitors/hardware/details/${monitorId}?${params.toString()}`;
|
||||
}, [monitorId, dateRange]);
|
||||
|
||||
const {
|
||||
data: monitorDetailsData,
|
||||
isLoading: monitorIsLoading,
|
||||
refetch: refetchMonitor,
|
||||
} = useGet<HardwareDetailsResponse>(
|
||||
monitorDetailsUrl,
|
||||
{},
|
||||
{ refreshInterval: 10000, keepPreviousData: true }
|
||||
);
|
||||
|
||||
const monitor = monitorDetailsData?.monitor;
|
||||
const monitorStats = monitorDetailsData?.monitorStats ?? null;
|
||||
const stats = monitorDetailsData?.stats;
|
||||
|
||||
return (
|
||||
<BasePage>
|
||||
<HeaderMonitorControls
|
||||
path="hardware"
|
||||
monitor={monitor}
|
||||
isAdmin={isAdmin}
|
||||
refetch={refetchMonitor}
|
||||
/>
|
||||
<MonitorStatBoxes
|
||||
monitor={monitor}
|
||||
monitorStats={monitorStats}
|
||||
/>
|
||||
<HeaderTimeRange
|
||||
isLoading={monitorIsLoading}
|
||||
hasDateRange={true}
|
||||
dateRange={dateRange}
|
||||
setDateRange={setDateRange}
|
||||
/>
|
||||
<Tabs
|
||||
value={selectedTab}
|
||||
onChange={(_e, value) => {
|
||||
setSelectedTab(value);
|
||||
}}
|
||||
>
|
||||
<Tab label={t("pages.infrastructure.tabs.labels.overview")} />
|
||||
<Tab label={t("pages.infrastructure.tabs.labels.network")} />
|
||||
</Tabs>
|
||||
{selectedTab === 0 && (
|
||||
<TabOverview
|
||||
monitor={monitor}
|
||||
stats={stats}
|
||||
dateRange={dateRange}
|
||||
/>
|
||||
)}
|
||||
{selectedTab === 1 && (
|
||||
<TabNetwork
|
||||
stats={stats}
|
||||
dateRange={dateRange}
|
||||
/>
|
||||
)}
|
||||
</BasePage>
|
||||
);
|
||||
};
|
||||
|
||||
export default InfrastructureDetails;
|
||||
File renamed without changes.
@@ -8,6 +8,7 @@ import {
|
||||
} from "@/Components/v2/monitors";
|
||||
import { TrendingUp, AlertTriangle } from "lucide-react";
|
||||
import { ChecksTable } from "@/Pages/Uptime/Details/Components/ChecksTable";
|
||||
import { MonitorStatBoxes } from "@/Components/v2/monitors";
|
||||
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useIsAdmin } from "@/Hooks/useIsAdmin";
|
||||
@@ -18,7 +19,6 @@ import { useGet } from "@/Hooks/UseApi";
|
||||
import type { MonitorDetailsResponse } from "@/Types/Monitor";
|
||||
import type { ChecksResponse } from "@/Types/Check";
|
||||
import type { RootState } from "@/Types/state";
|
||||
import { MonitorStatBoxes } from "@/Components/v2/monitors";
|
||||
import { formatDateWithTz } from "@/Utils/timeUtilsLegacy";
|
||||
import { t } from "i18next";
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import PageSpeedCreate from "../Pages/PageSpeed/Create/index.jsx";
|
||||
// Infrastructure
|
||||
import Infrastructure from "../Pages/Infrastructure/Monitors";
|
||||
import InfrastructureCreate from "../Pages/Infrastructure/Create/index.jsx";
|
||||
import InfrastructureDetails from "../Pages/Infrastructure/Details/index.jsx";
|
||||
import InfrastructureDetails from "../Pages/Infrastructure/Details/index";
|
||||
|
||||
// Server Status
|
||||
import ServerUnreachable from "../Pages/ServerUnreachable.jsx";
|
||||
@@ -165,7 +165,13 @@ const Routes = () => {
|
||||
/>
|
||||
<Route
|
||||
path="infrastructure/:monitorId"
|
||||
element={<InfrastructureDetails />}
|
||||
element={
|
||||
<>
|
||||
<ThemeProvider theme={v2theme}>
|
||||
<InfrastructureDetails />
|
||||
</ThemeProvider>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="checks/:monitorId?"
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface CheckCpuInfo {
|
||||
physical_core?: number;
|
||||
logical_core?: number;
|
||||
frequency?: number;
|
||||
current_frequency?: number;
|
||||
temperature?: number[];
|
||||
free_percent?: number;
|
||||
usage_percent?: number;
|
||||
@@ -32,6 +33,7 @@ export interface CheckHostInfo {
|
||||
os?: string;
|
||||
platform?: string;
|
||||
kernel_version?: string;
|
||||
pretty_name?: string;
|
||||
}
|
||||
|
||||
export interface CheckCaptureInfo {
|
||||
@@ -42,11 +44,18 @@ export interface CheckCaptureInfo {
|
||||
export interface CheckDiskInfo {
|
||||
device?: string;
|
||||
mountpoint?: string;
|
||||
read_speed_bytes?: number;
|
||||
write_speed_bytes?: number;
|
||||
total_bytes?: number;
|
||||
free_bytes?: number;
|
||||
used_bytes?: number;
|
||||
usage_percent?: number;
|
||||
total_inodes?: number;
|
||||
free_inodes?: number;
|
||||
used_inodes?: number;
|
||||
inodes_usage_percent?: number;
|
||||
read_bytes?: number;
|
||||
write_bytes?: number;
|
||||
read_time?: number;
|
||||
write_time?: number;
|
||||
}
|
||||
|
||||
export interface CheckErrorInfo {
|
||||
@@ -183,7 +192,7 @@ export interface HardwareChecksResult {
|
||||
totalChecks: number;
|
||||
};
|
||||
checks: Array<{
|
||||
_id: string;
|
||||
bucketDate: string;
|
||||
avgCpuUsage: number;
|
||||
avgMemoryUsage: number;
|
||||
avgTemperature: number[];
|
||||
|
||||
@@ -107,3 +107,51 @@ export interface PageSpeedDetailsResponse {
|
||||
monitor: MonitorWithChecks;
|
||||
monitorStats: MonitorStats | null;
|
||||
}
|
||||
|
||||
export interface HardwareDiskStats {
|
||||
name: string;
|
||||
readSpeed: number;
|
||||
writeSpeed: number;
|
||||
totalBytes: number;
|
||||
freeBytes: number;
|
||||
usagePercent: number;
|
||||
}
|
||||
|
||||
export interface HardwareNetStats {
|
||||
name: string;
|
||||
bytesSentPerSecond: number;
|
||||
deltaBytesRecv: number;
|
||||
deltaPacketsSent: number;
|
||||
deltaPacketsRecv: number;
|
||||
deltaErrIn: number;
|
||||
deltaErrOut: number;
|
||||
deltaDropIn: number;
|
||||
deltaDropOut: number;
|
||||
deltaFifoIn: number;
|
||||
deltaFifoOut: number;
|
||||
}
|
||||
|
||||
export interface HardwareCheckStats {
|
||||
bucketDate: string;
|
||||
avgCpuUsage: number;
|
||||
avgMemoryUsage: number;
|
||||
avgTemperature: number[];
|
||||
disks: HardwareDiskStats[];
|
||||
net: HardwareNetStats[];
|
||||
}
|
||||
|
||||
export interface HardwareStats {
|
||||
aggregateData: {
|
||||
totalChecks: number;
|
||||
};
|
||||
upChecks: {
|
||||
totalChecks: number;
|
||||
};
|
||||
checks: HardwareCheckStats[];
|
||||
}
|
||||
|
||||
export interface HardwareDetailsResponse {
|
||||
monitor: Monitor;
|
||||
stats: HardwareStats;
|
||||
monitorStats: MonitorStats | null;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import type { CheckDiskInfo, CheckHostInfo } from "@/Types/Check";
|
||||
|
||||
export const getFrequency = (frequency: number | undefined): string => {
|
||||
if (!frequency) return "N/A";
|
||||
const ghz = (frequency / 1000).toFixed(2);
|
||||
return `${ghz} GHz`;
|
||||
};
|
||||
|
||||
export const getCores = (cores: number | undefined) => {
|
||||
if (!cores) return "N/A";
|
||||
if (cores === 1) return `${cores} core`;
|
||||
return `${cores} cores`;
|
||||
};
|
||||
|
||||
export const getAvgTemp = (temps: number[]): string => {
|
||||
if (!temps || temps.length === 0) return "N/A";
|
||||
const avgTemp = temps.reduce((a, b) => a + b, 0) / temps.length;
|
||||
return `${avgTemp?.toFixed(2)} °C`;
|
||||
};
|
||||
|
||||
export const getGbs = (bytes: number | undefined): string => {
|
||||
if (!bytes) {
|
||||
return "N/A";
|
||||
}
|
||||
if (bytes === 0) {
|
||||
return "0 GB";
|
||||
}
|
||||
|
||||
const GB = bytes / (1024 * 1024 * 1024);
|
||||
const MB = bytes / (1024 * 1024);
|
||||
|
||||
if (GB >= 1) {
|
||||
return GB.toFixed(2) + " GB";
|
||||
} else {
|
||||
return MB.toFixed(2) + " MB";
|
||||
}
|
||||
};
|
||||
|
||||
export const getDiskTotalGbs = (disk?: Partial<CheckDiskInfo>[]): string => {
|
||||
if (!disk) {
|
||||
return getGbs(0);
|
||||
}
|
||||
const totalBytes = disk?.reduce((acc, disk) => acc + (disk.total_bytes || 0), 0) || 0;
|
||||
return getGbs(totalBytes);
|
||||
};
|
||||
|
||||
export const getOsAndPlatform = (hostInfo: CheckHostInfo | undefined): string => {
|
||||
if (!hostInfo) {
|
||||
return "N/A";
|
||||
}
|
||||
const os = hostInfo?.pretty_name || hostInfo?.os || "N/A";
|
||||
const platform = hostInfo?.platform || "N/A";
|
||||
return `${os} (${platform})`;
|
||||
};
|
||||
@@ -284,6 +284,48 @@
|
||||
"disk": "Disk",
|
||||
"memory": "Memory"
|
||||
}
|
||||
},
|
||||
"tabs": {
|
||||
"labels": {
|
||||
"overview": "Overview",
|
||||
"network": "Network"
|
||||
}
|
||||
},
|
||||
"statBoxes": {
|
||||
"cpuPhysical": "CPU (Physical)",
|
||||
"cpuLogical": "CPU (Logical)",
|
||||
"cpuFrequency": "CPU Frequency",
|
||||
"avgCpuTemperature": "Average CPU Temperature",
|
||||
"memory": "Memory",
|
||||
"disk": "Disk",
|
||||
"os": "OS"
|
||||
},
|
||||
"gauges": {
|
||||
"cpu": {
|
||||
"lowerLabel": "Max frequency",
|
||||
"title": "CPU usage",
|
||||
"upperLabel": "Current frequency"
|
||||
},
|
||||
"disk": {
|
||||
"lowerLabel": "Free",
|
||||
"title": "Disk {{idx}} usage",
|
||||
"upperLabel": "Used"
|
||||
},
|
||||
"memory": {
|
||||
"lowerLabel": "Free",
|
||||
"title": "Memory usage",
|
||||
"upperLabel": "Used"
|
||||
}
|
||||
},
|
||||
"charts": {
|
||||
"labels": {
|
||||
"memory": "Memory usage",
|
||||
"cpu": "CPU usage",
|
||||
"temp": "Temp",
|
||||
"disk": "Disk usage",
|
||||
"netBytesSent": "{{name}} - Bytes Sent",
|
||||
"netBytesRecv": "{{name}} - Bytes Received"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -93,7 +93,7 @@ class MonitorController {
|
||||
const dateRange = optionalString(req?.query?.dateRange, "dateRange") || "recent";
|
||||
const teamId = requireTeamId(req?.user?.teamId);
|
||||
|
||||
const monitor = await this.monitorService.getHardwareDetailsById({
|
||||
const data = await this.monitorService.getHardwareDetailsById({
|
||||
teamId,
|
||||
monitorId,
|
||||
dateRange,
|
||||
@@ -102,7 +102,7 @@ class MonitorController {
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: "Hardware details retrieved successfully",
|
||||
data: monitor,
|
||||
data: data,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
|
||||
@@ -71,6 +71,7 @@ const cpuSchema = new Schema<CheckCpuInfo>(
|
||||
physical_core: { type: Number, default: 0 },
|
||||
logical_core: { type: Number, default: 0 },
|
||||
frequency: { type: Number, default: 0 },
|
||||
current_frequency: { type: Number, default: 0 },
|
||||
temperature: { type: [Number], default: [] },
|
||||
free_percent: { type: Number, default: 0 },
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
@@ -92,11 +93,18 @@ const diskSchema = new Schema<CheckDiskInfo>(
|
||||
{
|
||||
device: { type: String, default: "" },
|
||||
mountpoint: { type: String, default: "" },
|
||||
read_speed_bytes: { type: Number, default: 0 },
|
||||
write_speed_bytes: { type: Number, default: 0 },
|
||||
total_bytes: { type: Number, default: 0 },
|
||||
free_bytes: { type: Number, default: 0 },
|
||||
used_bytes: { type: Number, default: 0 },
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
total_inodes: { type: Number, default: 0 },
|
||||
free_inodes: { type: Number, default: 0 },
|
||||
used_inodes: { type: Number, default: 0 },
|
||||
inodes_usage_percent: { type: Number, default: 0 },
|
||||
read_bytes: { type: Number, default: 0 },
|
||||
write_bytes: { type: Number, default: 0 },
|
||||
read_time: { type: Number, default: 0 },
|
||||
write_time: { type: Number, default: 0 },
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
@@ -106,6 +114,7 @@ const hostSchema = new Schema<CheckHostInfo>(
|
||||
os: { type: String, default: "" },
|
||||
platform: { type: String, default: "" },
|
||||
kernel_version: { type: String, default: "" },
|
||||
pretty_name: { type: String, default: "" },
|
||||
},
|
||||
{ _id: false }
|
||||
);
|
||||
|
||||
@@ -117,11 +117,18 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
(disks ?? []).map((disk) => ({
|
||||
device: disk?.device ?? "",
|
||||
mountpoint: disk?.mountpoint ?? "",
|
||||
read_speed_bytes: disk?.read_speed_bytes ?? 0,
|
||||
write_speed_bytes: disk?.write_speed_bytes ?? 0,
|
||||
total_bytes: disk?.total_bytes ?? 0,
|
||||
free_bytes: disk?.free_bytes ?? 0,
|
||||
used_bytes: disk?.used_bytes ?? 0,
|
||||
usage_percent: disk?.usage_percent ?? 0,
|
||||
total_inodes: disk?.total_inodes ?? 0,
|
||||
free_inodes: disk?.free_inodes ?? 0,
|
||||
used_inodes: disk?.used_inodes ?? 0,
|
||||
inodes_usage_percent: disk?.inodes_usage_percent ?? 0,
|
||||
read_bytes: disk?.read_bytes ?? 0,
|
||||
write_bytes: disk?.write_bytes ?? 0,
|
||||
read_time: disk?.read_time ?? 0,
|
||||
write_time: disk?.write_time ?? 0,
|
||||
}));
|
||||
|
||||
const mapErrors = (errors?: CheckErrorInfo[]): CheckErrorInfo[] =>
|
||||
@@ -522,7 +529,7 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
};
|
||||
|
||||
const checks = (hardwareMetrics ?? []).map((metric) => ({
|
||||
_id: metric._id,
|
||||
bucketDate: metric._id,
|
||||
avgCpuUsage: metric.avgCpuUsage ?? 0,
|
||||
avgMemoryUsage: metric.avgMemoryUsage ?? 0,
|
||||
avgTemperature: metric.avgTemperature ?? [],
|
||||
@@ -628,8 +635,8 @@ class MongoChecksRepository implements IChecksRepository {
|
||||
as: "dIdx",
|
||||
in: {
|
||||
name: { $concat: ["disk", { $toString: "$$dIdx" }] },
|
||||
readSpeed: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.read_speed_bytes", "$$dIdx"] } } } },
|
||||
writeSpeed: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.write_speed_bytes", "$$dIdx"] } } } },
|
||||
readSpeed: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.read_bytes", "$$dIdx"] } } } },
|
||||
writeSpeed: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.write_bytes", "$$dIdx"] } } } },
|
||||
totalBytes: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.total_bytes", "$$dIdx"] } } } },
|
||||
freeBytes: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.free_bytes", "$$dIdx"] } } } },
|
||||
usagePercent: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.usage_percent", "$$dIdx"] } } } },
|
||||
|
||||
@@ -257,9 +257,12 @@ export class MonitorService implements IMonitorService {
|
||||
checks: checksData.checks,
|
||||
};
|
||||
|
||||
const monitorStats = await this.monitorStatsRepository.findByMonitorId(monitor.id);
|
||||
|
||||
return {
|
||||
...monitor,
|
||||
monitor,
|
||||
stats,
|
||||
monitorStats,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface CheckCpuInfo {
|
||||
physical_core?: number;
|
||||
logical_core?: number;
|
||||
frequency?: number;
|
||||
current_frequency?: number;
|
||||
temperature?: number[];
|
||||
free_percent?: number;
|
||||
usage_percent?: number;
|
||||
@@ -28,6 +29,7 @@ export interface CheckHostInfo {
|
||||
os?: string;
|
||||
platform?: string;
|
||||
kernel_version?: string;
|
||||
pretty_name?: string;
|
||||
}
|
||||
|
||||
export interface CheckCaptureInfo {
|
||||
@@ -38,11 +40,18 @@ export interface CheckCaptureInfo {
|
||||
export interface CheckDiskInfo {
|
||||
device?: string;
|
||||
mountpoint?: string;
|
||||
read_speed_bytes?: number;
|
||||
write_speed_bytes?: number;
|
||||
total_bytes?: number;
|
||||
free_bytes?: number;
|
||||
used_bytes?: number;
|
||||
usage_percent?: number;
|
||||
total_inodes?: number;
|
||||
free_inodes?: number;
|
||||
used_inodes?: number;
|
||||
inodes_usage_percent?: number;
|
||||
read_bytes?: number;
|
||||
write_bytes?: number;
|
||||
read_time?: number;
|
||||
write_time?: number;
|
||||
}
|
||||
|
||||
export interface CheckErrorInfo {
|
||||
@@ -127,7 +136,7 @@ export interface HardwareChecksResult {
|
||||
totalChecks: number;
|
||||
};
|
||||
checks: Array<{
|
||||
_id: string;
|
||||
bucketDate: string;
|
||||
avgCpuUsage: number;
|
||||
avgMemoryUsage: number;
|
||||
avgTemperature: number[];
|
||||
|
||||
+45
-35
@@ -75,42 +75,52 @@ export interface UptimeDetailsResult {
|
||||
monitorStats: import("./monitorStats.js").MonitorStats | null;
|
||||
}
|
||||
|
||||
export interface HardwareDetailsResult extends Monitor {
|
||||
stats: {
|
||||
aggregateData: {
|
||||
totalChecks: number;
|
||||
};
|
||||
upChecks: {
|
||||
totalChecks: number;
|
||||
};
|
||||
checks: Array<{
|
||||
_id: string;
|
||||
avgCpuUsage: number;
|
||||
avgMemoryUsage: number;
|
||||
avgTemperature: number[];
|
||||
disks: Array<{
|
||||
name: string;
|
||||
readSpeed: number;
|
||||
writeSpeed: number;
|
||||
totalBytes: number;
|
||||
freeBytes: number;
|
||||
usagePercent: number;
|
||||
}>;
|
||||
net: Array<{
|
||||
name: string;
|
||||
bytesSentPerSecond: number;
|
||||
deltaBytesRecv: number;
|
||||
deltaPacketsSent: number;
|
||||
deltaPacketsRecv: number;
|
||||
deltaErrIn: number;
|
||||
deltaErrOut: number;
|
||||
deltaDropIn: number;
|
||||
deltaDropOut: number;
|
||||
deltaFifoIn: number;
|
||||
deltaFifoOut: number;
|
||||
}>;
|
||||
}>;
|
||||
export interface HardwareDiskStats {
|
||||
name: string;
|
||||
readSpeed: number;
|
||||
writeSpeed: number;
|
||||
totalBytes: number;
|
||||
freeBytes: number;
|
||||
usagePercent: number;
|
||||
}
|
||||
|
||||
export interface HardwareNetStats {
|
||||
name: string;
|
||||
bytesSentPerSecond: number;
|
||||
deltaBytesRecv: number;
|
||||
deltaPacketsSent: number;
|
||||
deltaPacketsRecv: number;
|
||||
deltaErrIn: number;
|
||||
deltaErrOut: number;
|
||||
deltaDropIn: number;
|
||||
deltaDropOut: number;
|
||||
deltaFifoIn: number;
|
||||
deltaFifoOut: number;
|
||||
}
|
||||
|
||||
export interface HardwareCheckStats {
|
||||
bucketDate: string;
|
||||
avgCpuUsage: number;
|
||||
avgMemoryUsage: number;
|
||||
avgTemperature: number[];
|
||||
disks: HardwareDiskStats[];
|
||||
net: HardwareNetStats[];
|
||||
}
|
||||
|
||||
export interface HardwareStats {
|
||||
aggregateData: {
|
||||
totalChecks: number;
|
||||
};
|
||||
upChecks: {
|
||||
totalChecks: number;
|
||||
};
|
||||
checks: HardwareCheckStats[];
|
||||
}
|
||||
|
||||
export interface HardwareDetailsResult {
|
||||
monitor: Monitor;
|
||||
stats: HardwareStats;
|
||||
monitorStats: import("./monitorStats.js").MonitorStats | null;
|
||||
}
|
||||
|
||||
export interface PageSpeedDetailsResult {
|
||||
|
||||
Reference in new issue
Block a user