mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-15 06:09:44 -06:00
Merge pull request #1548 from bluewave-labs/fix/be/uptime-duration
fix: be/uptime duration
This commit is contained in:
@@ -4,7 +4,7 @@ import { ResponsiveContainer, BarChart, XAxis, Bar, Cell } from "recharts";
|
||||
import PropTypes from "prop-types";
|
||||
import CustomLabels from "./CustomLabels";
|
||||
|
||||
const DownBarChart = memo(({ stats, type, onBarHover }) => {
|
||||
const DownBarChart = memo(({ monitor, type, onBarHover }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const [chartHovered, setChartHovered] = useState(false);
|
||||
@@ -19,7 +19,7 @@ const DownBarChart = memo(({ stats, type, onBarHover }) => {
|
||||
<BarChart
|
||||
width="100%"
|
||||
height="100%"
|
||||
data={stats.downChecks}
|
||||
data={monitor.groupedDownChecks}
|
||||
onMouseEnter={() => {
|
||||
setChartHovered(true);
|
||||
onBarHover({ time: null, totalChecks: 0 });
|
||||
@@ -40,8 +40,10 @@ const DownBarChart = memo(({ stats, type, onBarHover }) => {
|
||||
y={0}
|
||||
width="100%"
|
||||
height="100%"
|
||||
firstDataPoint={stats.downChecks?.[0] ?? {}}
|
||||
lastDataPoint={stats.downChecks?.[stats.downChecks.length - 1] ?? {}}
|
||||
firstDataPoint={monitor.groupedDownChecks?.[0] ?? {}}
|
||||
lastDataPoint={
|
||||
monitor.groupedDownChecks?.[monitor.groupedDownChecks.length - 1] ?? {}
|
||||
}
|
||||
type={type}
|
||||
/>
|
||||
}
|
||||
@@ -51,7 +53,7 @@ const DownBarChart = memo(({ stats, type, onBarHover }) => {
|
||||
maxBarSize={7}
|
||||
background={{ fill: "transparent" }}
|
||||
>
|
||||
{stats.downChecks.map((entry, index) => (
|
||||
{monitor.groupedDownChecks.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${entry.time}`}
|
||||
fill={
|
||||
@@ -79,9 +81,8 @@ const DownBarChart = memo(({ stats, type, onBarHover }) => {
|
||||
|
||||
DownBarChart.displayName = "DownBarChart";
|
||||
DownBarChart.propTypes = {
|
||||
stats: PropTypes.shape({
|
||||
downChecks: PropTypes.arrayOf(PropTypes.object),
|
||||
downChecksAggregate: PropTypes.object,
|
||||
monitor: PropTypes.shape({
|
||||
groupedDownChecks: PropTypes.arrayOf(PropTypes.object),
|
||||
}),
|
||||
type: PropTypes.string,
|
||||
onBarHover: PropTypes.func,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ResponsiveContainer, BarChart, XAxis, Bar, Cell } from "recharts";
|
||||
import PropTypes from "prop-types";
|
||||
import CustomLabels from "./CustomLabels";
|
||||
|
||||
const UpBarChart = memo(({ stats, type, onBarHover }) => {
|
||||
const UpBarChart = memo(({ monitor, type, onBarHover }) => {
|
||||
const theme = useTheme();
|
||||
const [chartHovered, setChartHovered] = useState(false);
|
||||
const [hoveredBarIndex, setHoveredBarIndex] = useState(null);
|
||||
@@ -26,7 +26,7 @@ const UpBarChart = memo(({ stats, type, onBarHover }) => {
|
||||
<BarChart
|
||||
width="100%"
|
||||
height="100%"
|
||||
data={stats.upChecks}
|
||||
data={monitor.groupedUpChecks}
|
||||
onMouseEnter={() => {
|
||||
setChartHovered(true);
|
||||
onBarHover({ time: null, totalChecks: 0, avgResponseTime: 0 });
|
||||
@@ -47,8 +47,8 @@ const UpBarChart = memo(({ stats, type, onBarHover }) => {
|
||||
y={0}
|
||||
width="100%"
|
||||
height="100%"
|
||||
firstDataPoint={stats.upChecks[0]}
|
||||
lastDataPoint={stats.upChecks[stats.upChecks.length - 1]}
|
||||
firstDataPoint={monitor.groupedUpChecks[0]}
|
||||
lastDataPoint={monitor.groupedUpChecks[monitor.groupedUpChecks.length - 1]}
|
||||
type={type}
|
||||
/>
|
||||
}
|
||||
@@ -58,7 +58,7 @@ const UpBarChart = memo(({ stats, type, onBarHover }) => {
|
||||
maxBarSize={7}
|
||||
background={{ fill: "transparent" }}
|
||||
>
|
||||
{stats.upChecks.map((entry, index) => {
|
||||
{monitor.groupedUpChecks.map((entry, index) => {
|
||||
let { main, light } = getColorRange(entry.avgResponseTime);
|
||||
return (
|
||||
<Cell
|
||||
@@ -90,9 +90,8 @@ UpBarChart.displayName = "UpBarChart";
|
||||
|
||||
// Validate props using PropTypes
|
||||
UpBarChart.propTypes = {
|
||||
stats: PropTypes.shape({
|
||||
upChecks: PropTypes.array,
|
||||
upChecksAggregate: PropTypes.object,
|
||||
monitor: PropTypes.shape({
|
||||
groupedUpChecks: PropTypes.array,
|
||||
}),
|
||||
type: PropTypes.string,
|
||||
onBarHover: PropTypes.func,
|
||||
|
||||
@@ -221,17 +221,17 @@ const DetailsPage = () => {
|
||||
<StatBox
|
||||
sx={statusStyles[determineState(monitor)]}
|
||||
heading={"active for"}
|
||||
subHeading={splitDuration(monitor?.stats?.timeSinceLastFalseCheck)}
|
||||
subHeading={splitDuration(monitor?.uptimeStreak)}
|
||||
/>
|
||||
<StatBox
|
||||
heading="last check"
|
||||
subHeading={splitDuration(monitor?.stats?.timeSinceLastCheck)}
|
||||
subHeading={splitDuration(monitor?.timeSinceLastCheck)}
|
||||
/>
|
||||
<StatBox
|
||||
heading="last response time"
|
||||
subHeading={
|
||||
<>
|
||||
{monitor?.stats?.latestResponseTime}
|
||||
{monitor?.latestResponseTime}
|
||||
<Typography component="span">{"ms"}</Typography>
|
||||
</>
|
||||
}
|
||||
@@ -308,7 +308,7 @@ const DetailsPage = () => {
|
||||
<Typography component="span">
|
||||
{hoveredUptimeData !== null
|
||||
? hoveredUptimeData.totalChecks
|
||||
: (monitor.stats?.upChecks?.reduce((count, checkGroup) => {
|
||||
: (monitor?.groupedUpChecks?.reduce((count, checkGroup) => {
|
||||
return count + checkGroup.totalChecks;
|
||||
}, 0) ?? 0)}
|
||||
</Typography>
|
||||
@@ -338,8 +338,8 @@ const DetailsPage = () => {
|
||||
{hoveredUptimeData !== null
|
||||
? Math.floor(hoveredUptimeData?.avgResponseTime ?? 0)
|
||||
: Math.floor(
|
||||
((monitor?.stats?.upChecksAggregate?.totalChecks ?? 0) /
|
||||
(monitor?.stats?.totalChecks ?? 1)) *
|
||||
((monitor?.upChecks?.totalChecks ?? 0) /
|
||||
(monitor?.totalChecks ?? 1)) *
|
||||
100
|
||||
)}
|
||||
<Typography component="span">
|
||||
@@ -349,7 +349,7 @@ const DetailsPage = () => {
|
||||
</Box>
|
||||
</Stack>
|
||||
<UpBarChart
|
||||
stats={monitor?.stats}
|
||||
monitor={monitor}
|
||||
type={dateRange}
|
||||
onBarHover={setHoveredUptimeData}
|
||||
/>
|
||||
@@ -366,7 +366,7 @@ const DetailsPage = () => {
|
||||
<Typography component="span">
|
||||
{hoveredIncidentsData !== null
|
||||
? hoveredIncidentsData.totalChecks
|
||||
: (monitor.stats?.downChecks?.reduce((count, checkGroup) => {
|
||||
: (monitor?.groupedDownChecks?.reduce((count, checkGroup) => {
|
||||
return count + checkGroup.totalChecks;
|
||||
}, 0) ?? 0)}
|
||||
</Typography>
|
||||
@@ -388,7 +388,7 @@ const DetailsPage = () => {
|
||||
)}
|
||||
</Box>
|
||||
<DownBarChart
|
||||
stats={monitor?.stats}
|
||||
monitor={monitor}
|
||||
type={dateRange}
|
||||
onBarHover={setHoveredIncidentsData}
|
||||
/>
|
||||
@@ -400,9 +400,7 @@ const DetailsPage = () => {
|
||||
</IconBox>
|
||||
<Typography component="h2">Average Response Time</Typography>
|
||||
</Stack>
|
||||
<ResponseGaugeChart
|
||||
avgResponseTime={monitor?.stats?.groupAggregate?.avgResponseTime ?? 0}
|
||||
/>
|
||||
<ResponseGaugeChart avgResponseTime={monitor.avgResponseTime ?? 0} />
|
||||
</ChartBox>
|
||||
<ChartBox sx={{ padding: 0 }}>
|
||||
<Stack
|
||||
@@ -415,7 +413,7 @@ const DetailsPage = () => {
|
||||
<Typography component="h2">Response Times</Typography>
|
||||
</Stack>
|
||||
<MonitorDetailsAreaChart
|
||||
checks={monitor?.stats?.groupChecks ?? []}
|
||||
checks={monitor.groupedChecks ?? []}
|
||||
dateRange={dateRange}
|
||||
/>
|
||||
</ChartBox>
|
||||
|
||||
@@ -340,31 +340,21 @@ const getUptimeDetailsById = async (req) => {
|
||||
};
|
||||
|
||||
const dateString = formatLookup[dateRange];
|
||||
const monitorData = await Check.aggregate(
|
||||
const results = await Check.aggregate(
|
||||
buildUptimeDetailsPipeline(monitor, dates, dateString)
|
||||
);
|
||||
|
||||
const monitorData = results[0];
|
||||
const normalizedGroupChecks = NormalizeDataUptimeDetails(
|
||||
monitorData[0].groupChecks,
|
||||
monitorData.groupedChecks,
|
||||
10,
|
||||
100
|
||||
);
|
||||
|
||||
const monitorStats = {
|
||||
...monitor.toObject(),
|
||||
stats: {
|
||||
avgResponseTime: monitorData[0].avgResponseTime,
|
||||
totalChecks: monitorData[0].totalChecks,
|
||||
timeSinceLastCheck: monitorData[0].timeSinceLastCheck,
|
||||
timeSinceLastFalseCheck: monitorData[0].timeSinceLastFalseCheck,
|
||||
latestResponseTime: monitorData[0].latestResponseTime,
|
||||
groupChecks: normalizedGroupChecks,
|
||||
groupAggregate: monitorData[0].groupAggregate,
|
||||
upChecksAggregate: monitorData[0].upChecksAggregate,
|
||||
upChecks: monitorData[0].upChecks,
|
||||
downChecksAggregate: monitorData[0].downChecksAggregate,
|
||||
downChecks: monitorData[0].downChecks,
|
||||
},
|
||||
...monitorData,
|
||||
groupedChecks: normalizedGroupChecks,
|
||||
};
|
||||
|
||||
return monitorStats;
|
||||
|
||||
@@ -19,9 +19,6 @@ const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
avgResponseTime: {
|
||||
$avg: "$responseTime",
|
||||
},
|
||||
firstCheck: {
|
||||
$first: "$$ROOT",
|
||||
},
|
||||
lastCheck: {
|
||||
$last: "$$ROOT",
|
||||
},
|
||||
@@ -31,27 +28,52 @@ const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
uptimeDuration: [
|
||||
{
|
||||
$match: {
|
||||
status: false,
|
||||
},
|
||||
},
|
||||
uptimeStreak: [
|
||||
{
|
||||
$sort: {
|
||||
createdAt: 1,
|
||||
createdAt: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
lastFalseCheck: {
|
||||
$last: "$$ROOT",
|
||||
checks: { $push: "$$ROOT" },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
streak: {
|
||||
$reduce: {
|
||||
input: "$checks",
|
||||
initialValue: { checks: [], foundFalse: false },
|
||||
in: {
|
||||
$cond: [
|
||||
{
|
||||
$and: [
|
||||
{ $not: "$$value.foundFalse" }, // stop reducing if a false check has been found
|
||||
{ $eq: ["$$this.status", true] }, // continue reducing if current check true
|
||||
],
|
||||
},
|
||||
// true case
|
||||
{
|
||||
checks: { $concatArrays: ["$$value.checks", ["$$this"]] },
|
||||
foundFalse: false, // Add the check to the streak
|
||||
},
|
||||
// false case
|
||||
{
|
||||
checks: "$$value.checks",
|
||||
foundFalse: true, // Mark that we found a false
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
groupChecks: [
|
||||
// For the response time chart, should return checks for date window
|
||||
// Grouped by: {day: hour}, {week: day}, {month: day}
|
||||
groupedChecks: [
|
||||
{
|
||||
$match: {
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
@@ -79,7 +101,8 @@ const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
groupAggregate: [
|
||||
// Average response time for the date window
|
||||
groupAvgResponseTime: [
|
||||
{
|
||||
$match: {
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
@@ -94,10 +117,12 @@ const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
upChecksAggregate: [
|
||||
// All UpChecks for the date window
|
||||
upChecks: [
|
||||
{
|
||||
$match: {
|
||||
status: true,
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -112,7 +137,8 @@ const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
upChecks: [
|
||||
// Up checks grouped by: {day: hour}, {week: day}, {month: day}
|
||||
groupedUpChecks: [
|
||||
{
|
||||
$match: {
|
||||
status: true,
|
||||
@@ -139,10 +165,12 @@ const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
$sort: { _id: 1 },
|
||||
},
|
||||
],
|
||||
downChecksAggregate: [
|
||||
// All down checks for the date window
|
||||
downChecks: [
|
||||
{
|
||||
$match: {
|
||||
status: false,
|
||||
createdAt: { $gte: dates.start, $lte: dates.end },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -157,7 +185,8 @@ const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
downChecks: [
|
||||
// Down checks grouped by: {day: hour}, {week: day}, {month: day} for the date window
|
||||
groupedDownChecks: [
|
||||
{
|
||||
$match: {
|
||||
status: false,
|
||||
@@ -188,6 +217,20 @@ const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
uptimeStreak: {
|
||||
$cond: [
|
||||
{ $eq: [{ $size: { $first: "$uptimeStreak.streak.checks" } }, 0] },
|
||||
0,
|
||||
{
|
||||
$subtract: [
|
||||
new Date(),
|
||||
{
|
||||
$last: { $first: "$uptimeStreak.streak.checks.createdAt" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
avgResponseTime: {
|
||||
$arrayElemAt: ["$aggregateData.avgResponseTime", 0],
|
||||
},
|
||||
@@ -217,51 +260,18 @@ const buildUptimeDetailsPipeline = (monitor, dates, dateString) => {
|
||||
},
|
||||
},
|
||||
},
|
||||
timeSinceLastFalseCheck: {
|
||||
$let: {
|
||||
vars: {
|
||||
lastFalseCheck: {
|
||||
$arrayElemAt: ["$uptimeDuration.lastFalseCheck", 0],
|
||||
},
|
||||
firstCheck: {
|
||||
$arrayElemAt: ["$aggregateData.firstCheck", 0],
|
||||
},
|
||||
},
|
||||
in: {
|
||||
$cond: [
|
||||
{
|
||||
$ifNull: ["$$lastFalseCheck", false],
|
||||
},
|
||||
{
|
||||
$subtract: [new Date(), "$$lastFalseCheck.createdAt"],
|
||||
},
|
||||
{
|
||||
$cond: [
|
||||
{
|
||||
$ifNull: ["$$firstCheck", false],
|
||||
},
|
||||
{
|
||||
$subtract: [new Date(), "$$firstCheck.createdAt"],
|
||||
},
|
||||
0,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
groupedChecks: "$groupedChecks",
|
||||
groupedAvgResponseTime: {
|
||||
$arrayElemAt: ["$groupAvgResponseTime", 0],
|
||||
},
|
||||
groupChecks: "$groupChecks",
|
||||
groupAggregate: {
|
||||
$arrayElemAt: ["$groupAggregate", 0],
|
||||
upChecks: {
|
||||
$arrayElemAt: ["$upChecks", 0],
|
||||
},
|
||||
upChecksAggregate: {
|
||||
$arrayElemAt: ["$upChecksAggregate", 0],
|
||||
groupedUpChecks: "$groupedUpChecks",
|
||||
downChecks: {
|
||||
$arrayElemAt: ["$downChecks", 0],
|
||||
},
|
||||
upChecks: "$upChecks",
|
||||
downChecksAggregate: {
|
||||
$arrayElemAt: ["$downChecksAggregate", 0],
|
||||
},
|
||||
downChecks: "$downChecks",
|
||||
groupedDownChecks: "$groupedDownChecks",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user