diff --git a/client/src/Components/v2/design-elements/StatusBox.tsx b/client/src/Components/v2/design-elements/StatusBox.tsx
index bf7979cca..a83ebcf5d 100644
--- a/client/src/Components/v2/design-elements/StatusBox.tsx
+++ b/client/src/Components/v2/design-elements/StatusBox.tsx
@@ -105,6 +105,29 @@ export const PausedStatusBox = ({ n }: { n: number }) => {
/>
);
};
+export const MaintenanceStatusBox = ({ n }: { n: number }) => {
+ const theme = useTheme();
+ const { t } = useTranslation();
+ return (
+
+ );
+};
+export const InitializingStatusBox = ({ n }: { n: number }) => {
+ const theme = useTheme();
+ const { t } = useTranslation();
+ return (
+
+ );
+};
+
export const TotalChecksBox = ({ n }: { n: number }) => {
const theme = useTheme();
const { t } = useTranslation();
@@ -138,15 +161,3 @@ export const UpChecksBox = ({ n }: { n: number }) => {
/>
);
};
-
-export const InitializingStatusBox = ({ n }: { n: number }) => {
- const theme = useTheme();
- const { t } = useTranslation();
- return (
-
- );
-};
diff --git a/client/src/Components/v2/design-elements/StatusLabel.tsx b/client/src/Components/v2/design-elements/StatusLabel.tsx
index d7212ddff..cf404421e 100644
--- a/client/src/Components/v2/design-elements/StatusLabel.tsx
+++ b/client/src/Components/v2/design-elements/StatusLabel.tsx
@@ -11,33 +11,24 @@ import { useTranslation } from "react-i18next";
export const ValueTypes = ["positive", "negative", "neutral"] as const;
export type ValueType = (typeof ValueTypes)[number];
-export const StatusLabel = ({
- status,
- isActive,
- sx,
-}: {
- status: MonitorStatus;
- isActive?: boolean;
- sx?: SxProps;
-}) => {
+export const StatusLabel = ({ status, sx }: { status: MonitorStatus; sx?: SxProps }) => {
const { t } = useTranslation();
const theme = useTheme();
const palette = getStatusPalette(status);
- const determineStatus = (
- isActive: boolean | undefined,
- status: MonitorStatus
- ): string => {
- if (isActive === false) {
+ const determineStatus = (status: MonitorStatus): string => {
+ if (status === "up") {
+ return t("pages.common.monitors.status.up");
+ } else if (status === "down") {
+ return t("pages.common.monitors.status.down");
+ } else if (status === "maintenance") {
+ return t("pages.common.monitors.status.maintenance");
+ } else if (status === "paused") {
return t("pages.common.monitors.status.paused");
+ } else if (status === "initializing") {
+ return t("pages.common.monitors.status.initializing");
}
- if (status === true) {
- return t("pages.common.monitors.status.up");
- }
- if (status === false) {
- return t("pages.common.monitors.status.down");
- }
return t("pages.common.monitors.status.initializing");
};
@@ -64,9 +55,7 @@ export const StatusLabel = ({
borderRadius="50%"
marginRight="5px"
/>
-
- {determineStatus(isActive, status)}
-
+ {determineStatus(status)}
);
};
diff --git a/client/src/Components/v2/monitors/HeaderMonitorsSummary.tsx b/client/src/Components/v2/monitors/HeaderMonitorsSummary.tsx
new file mode 100644
index 000000000..b2a93a807
--- /dev/null
+++ b/client/src/Components/v2/monitors/HeaderMonitorsSummary.tsx
@@ -0,0 +1,29 @@
+import {
+ UpStatusBox,
+ DownStatusBox,
+ PausedStatusBox,
+ InitializingStatusBox,
+} from "@/Components/v2/design-elements";
+import Stack from "@mui/material/Stack";
+
+import type { MonitorsSummary } from "@/Types/Monitor";
+import { useTheme } from "@mui/material";
+
+interface MonitorsSummaryProps {
+ summary: MonitorsSummary | null;
+}
+
+export const HeaderMonitorsSummary = ({ summary }: MonitorsSummaryProps) => {
+ const theme = useTheme();
+ return (
+
+
+
+
+
+
+ );
+};
diff --git a/client/src/Components/v2/monitors/index.tsx b/client/src/Components/v2/monitors/index.tsx
index e2856d6a9..9496395f6 100644
--- a/client/src/Components/v2/monitors/index.tsx
+++ b/client/src/Components/v2/monitors/index.tsx
@@ -11,3 +11,4 @@ export * from "./charts/PiePageSpeedLegend";
export * from "./charts/HistogramPageSpeedDetails";
export * from "./charts/HistogramPageSpeedDetailsTooltip";
export * from "./charts/HistogramInfrastructure";
+export * from "./HeaderMonitorsSummary";
diff --git a/client/src/Pages/Checks/Components/ChecksTable.tsx b/client/src/Pages/Checks/Components/ChecksTable.tsx
index 6464be25c..bb1920ab2 100644
--- a/client/src/Pages/Checks/Components/ChecksTable.tsx
+++ b/client/src/Pages/Checks/Components/ChecksTable.tsx
@@ -6,7 +6,7 @@ import {
} from "@/Components/v2/design-elements";
import Box from "@mui/material/Box";
import type { Header } from "@/Components/v2/design-elements/Table";
-import type { Monitor, MonitorStatus } from "@/Types/Monitor";
+import type { Monitor } from "@/Types/Monitor";
import { useTranslation } from "react-i18next";
import { formatDateWithTz } from "@/Utils/TimeUtils";
@@ -52,7 +52,7 @@ export const ChecksTable = ({
id: "status",
content: "Status",
render: (row) => {
- return ;
+ return ;
},
},
{
diff --git a/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable.tsx b/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable.tsx
index ca236f45f..b2bcadc1a 100644
--- a/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable.tsx
+++ b/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable.tsx
@@ -195,12 +195,7 @@ export const InfraMonitorsTable = ({
),
render: (row) => {
- return (
-
- );
+ return ;
},
},
{
diff --git a/client/src/Pages/Infrastructure/Monitors/index.tsx b/client/src/Pages/Infrastructure/Monitors/index.tsx
index 1d38dbdd3..01de1c09c 100644
--- a/client/src/Pages/Infrastructure/Monitors/index.tsx
+++ b/client/src/Pages/Infrastructure/Monitors/index.tsx
@@ -1,13 +1,8 @@
import Stack from "@mui/material/Stack";
import useMediaQuery from "@mui/material/useMediaQuery";
-import {
- MonitorBasePageWithStates,
- UpStatusBox,
- DownStatusBox,
- PausedStatusBox,
-} from "@/Components/v2/design-elements";
+import { MonitorBasePageWithStates } from "@/Components/v2/design-elements";
import { HeaderCreate } from "@/Components/v2/common";
-import { ControlsFilter } from "@/Components/v2/monitors";
+import { ControlsFilter, HeaderMonitorsSummary } from "@/Components/v2/monitors";
import { TextField, Dialog } from "@/Components/v2/inputs";
import { useGet, useDelete } from "@/Hooks/UseApi";
@@ -99,7 +94,7 @@ const InfrastructureMonitors = () => {
{ refreshInterval: 5000, keepPreviousData: true }
);
- const { summary, count } = monitorsWithChecksData ?? {};
+ const { summary, count } = monitorsWithChecksData ?? { summary: null, count: 0 };
const isLoading = monitorsWithChecksLoading;
// Check if any filters are active
@@ -138,14 +133,7 @@ const InfrastructureMonitors = () => {
isLoading={isLoading}
isAdmin={isAdmin}
/>
-
-
-
-
-
+
{
const { t } = useTranslation();
- const theme = useTheme();
const isAdmin = useIsAdmin();
const { deleteFn, loading: isDeleting } = useDelete();
@@ -48,8 +43,8 @@ const PageSpeedMonitorsPage = () => {
const [rowsPerPage, setRowsPerPage] = useState(10);
const monitors = monitorsData?.monitors;
- const monitorsCount = monitorsData?.count;
- const summary = monitorsData?.summary;
+ const monitorsCount = monitorsData?.count ?? 0;
+ const summary = monitorsData?.summary ?? null;
const isLoading = monitorsIsLoading || settingsIsLoading;
@@ -80,14 +75,7 @@ const PageSpeedMonitorsPage = () => {
isLoading={isLoading}
isAdmin={isAdmin}
/>
-
-
-
-
-
+
{
)}
{monitors?.map((monitor) => {
- const status = determineState(monitor);
return (
{
)}
-
+
{statusPage.showCharts !== false && (
diff --git a/client/src/Pages/StatusPage/Status/Components/StatusBar.tsx b/client/src/Pages/StatusPage/Status/Components/StatusBar.tsx
index 8b3343249..b5c52b32b 100644
--- a/client/src/Pages/StatusPage/Status/Components/StatusBar.tsx
+++ b/client/src/Pages/StatusPage/Status/Components/StatusBar.tsx
@@ -12,16 +12,16 @@ const getMonitorStatus = (monitors: Monitor[], theme: Theme, t: Function) => {
icon: ,
};
- if (monitors.every((monitor) => monitor.status === true)) {
+ if (monitors.every((monitor) => monitor.status === "up")) {
monitorsStatus.msg = t("pages.statusPages.statusBar.allUp");
monitorsStatus.color = theme.palette.success.main;
monitorsStatus.icon = ;
return monitorsStatus;
- } else if (monitors.every((monitor) => monitor.status === false)) {
+ } else if (monitors.every((monitor) => monitor.status === "down")) {
monitorsStatus.msg = t("pages.statusPages.statusBar.allDown");
monitorsStatus.color = theme.palette.error.main;
return monitorsStatus;
- } else if (monitors.some((monitor) => monitor.status === false)) {
+ } else if (monitors.some((monitor) => monitor.status === "down")) {
monitorsStatus.msg = t("pages.statusPages.statusBar.degraded");
monitorsStatus.color = theme.palette.warning.main;
return monitorsStatus;
diff --git a/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx b/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx
index 2cfdc1f15..ceb8e1a28 100644
--- a/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx
+++ b/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx
@@ -15,7 +15,7 @@ const getHeaders = (t: Function, uiTimezone: string) => {
id: "status",
content: t("common.table.headers.status"),
render: (row) => {
- return ;
+ return ;
},
},
{
diff --git a/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx b/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx
index 94c619672..75738e6ad 100644
--- a/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx
+++ b/client/src/Pages/Uptime/Monitors/Components/UptimeMonitorsTable.tsx
@@ -186,12 +186,7 @@ export const MonitorTable = ({
),
render: (row) => {
- return (
-
- );
+ return ;
},
},
{
diff --git a/client/src/Pages/Uptime/Monitors/index.tsx b/client/src/Pages/Uptime/Monitors/index.tsx
index 702bbd928..9e36ccc86 100644
--- a/client/src/Pages/Uptime/Monitors/index.tsx
+++ b/client/src/Pages/Uptime/Monitors/index.tsx
@@ -1,10 +1,5 @@
-import { ControlsFilter } from "@/Components/v2/monitors";
-import {
- MonitorBasePageWithStates,
- UpStatusBox,
- DownStatusBox,
- PausedStatusBox,
-} from "@/Components/v2/design-elements";
+import { ControlsFilter, HeaderMonitorsSummary } from "@/Components/v2/monitors";
+import { MonitorBasePageWithStates } from "@/Components/v2/design-elements";
import { TextField, Dialog } from "@/Components/v2/inputs";
import Stack from "@mui/material/Stack";
import { MonitorTable } from "@/Pages/Uptime/Monitors/Components/UptimeMonitorsTable";
@@ -99,7 +94,11 @@ const UptimeMonitorsPage = () => {
{ refreshInterval: 5000, keepPreviousData: true }
);
- const { monitors: monitorsWithChecks, summary, count } = monitorsWithChecksData ?? {};
+ const {
+ monitors: monitorsWithChecks,
+ summary,
+ count,
+ } = monitorsWithChecksData ?? { monitors: null, summary: null, count: 0 };
// Delete hook
const { deleteFn, loading: isDeleting } = useDelete();
@@ -150,14 +149,8 @@ const UptimeMonitorsPage = () => {
isLoading={isLoading}
isAdmin={isAdmin}
/>
-
-
-
-
-
+
+
{
- if (typeof monitor === "undefined") return "pending";
- if (monitor?.isActive === false) return "paused";
- if (monitor?.status === undefined) return "pending";
- return monitor?.status == true ? "up" : "down";
-};
-
export const getMonitorPath = (type: MonitorType): string => {
const pathMap: Record = {
http: "uptime",
@@ -24,10 +17,10 @@ export const getMonitorPath = (type: MonitorType): string => {
};
export const getStatusPalette = (status: MonitorStatus): PaletteKey => {
- if (status === true) {
+ if (status === "up") {
return "success";
}
- if (status === false) {
+ if (status === "down") {
return "error";
}
return "warning";
@@ -43,11 +36,11 @@ export const getValuePalette = (value: ValueType): PaletteKey => {
};
export const getStatusColor = (status: MonitorStatus, theme: any): string => {
- if (status === true) {
+ if (status === "up") {
return theme.palette.success.light;
}
- if (status === false) {
+ if (status === "down") {
return theme.palette.error.light;
}
@@ -101,9 +94,9 @@ export const getStatusPageHeaderConfig = (
return { paletteKey: "error", message: "No monitors available" };
}
- const allUp = monitors.every((monitor) => monitor.status === true);
- const anyDown = monitors.some((monitor) => monitor.status === false);
- const allDown = monitors.every((monitor) => monitor.status === false);
+ const allUp = monitors.every((monitor) => monitor.status === "up");
+ const anyDown = monitors.some((monitor) => monitor.status === "down");
+ const allDown = monitors.every((monitor) => monitor.status === "down");
if (allUp)
return {
diff --git a/client/src/locales/en.json b/client/src/locales/en.json
index d2a066c11..7eb833308 100644
--- a/client/src/locales/en.json
+++ b/client/src/locales/en.json
@@ -775,6 +775,7 @@
"status": {
"down": "down",
"initializing": "initializing",
+ "maintenance": "maintenance",
"paused": "paused",
"total": "total",
"up": "up"
diff --git a/server/src/config/services.ts b/server/src/config/services.ts
index 4437e9176..9bf18ff1d 100644
--- a/server/src/config/services.ts
+++ b/server/src/config/services.ts
@@ -206,6 +206,7 @@ export const initializeServices = async ({
buffer: bufferService,
incidentService,
maintenanceWindowsRepository,
+ monitorsRepository,
});
const superSimpleQueue = await SuperSimpleQueue.create({
diff --git a/server/src/db/models/Monitor.ts b/server/src/db/models/Monitor.ts
index d4cc9a872..9111ce47f 100644
--- a/server/src/db/models/Monitor.ts
+++ b/server/src/db/models/Monitor.ts
@@ -1,9 +1,6 @@
import { Schema, model, Types, type UpdateQuery } from "mongoose";
import type { Monitor, MonitorMatchMethod, MonitorThresholds, CheckSnapshot } from "@/types/monitor.js";
-import { MonitorTypes } from "@/types/monitor.js";
-import Check from "./Check.js";
-import MonitorStats from "./MonitorStats.js";
-import StatusPage from "./StatusPage.js";
+import { MonitorTypes, MonitorStatuses } from "@/types/monitor.js";
type CheckSnapshotDocument = Omit & { createdAt: Date };
@@ -68,8 +65,9 @@ const MonitorSchema = new Schema(
type: String,
},
status: {
- type: Boolean,
- default: undefined,
+ type: String,
+ enum: MonitorStatuses,
+ default: "initializing",
},
statusWindow: {
type: [Boolean],
diff --git a/server/src/repositories/monitors/MongoMonitorsRepository.ts b/server/src/repositories/monitors/MongoMonitorsRepository.ts
index 9b30e5ac2..65d93421e 100644
--- a/server/src/repositories/monitors/MongoMonitorsRepository.ts
+++ b/server/src/repositories/monitors/MongoMonitorsRepository.ts
@@ -55,7 +55,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
query.isActive = filter === "true";
break;
case "status":
- query.status = filter === "true";
+ query.status = filter;
break;
case "type":
query.type = filter;
@@ -181,7 +181,13 @@ class MongoMonitorsRepository implements IMonitorsRepository {
{
$set: {
isActive: { $not: "$isActive" },
- status: "$$REMOVE",
+ status: {
+ $cond: {
+ if: { $eq: ["$status", "paused"] },
+ then: "initializing",
+ else: "paused",
+ },
+ },
},
},
],
@@ -223,17 +229,27 @@ class MongoMonitorsRepository implements IMonitorsRepository {
totalMonitors: { $sum: 1 },
upMonitors: {
$sum: {
- $cond: [{ $eq: ["$status", true] }, 1, 0],
+ $cond: [{ $eq: ["$status", "up"] }, 1, 0],
},
},
downMonitors: {
$sum: {
- $cond: [{ $eq: ["$status", false] }, 1, 0],
+ $cond: [{ $eq: ["$status", "down"] }, 1, 0],
},
},
pausedMonitors: {
$sum: {
- $cond: [{ $eq: ["$isActive", false] }, 1, 0],
+ $cond: [{ $eq: ["$status", "paused"] }, 1, 0],
+ },
+ },
+ initializingMonitors: {
+ $sum: {
+ $cond: [{ $eq: ["$status", "initializing"] }, 1, 0],
+ },
+ },
+ maintenanceMonitors: {
+ $sum: {
+ $cond: [{ $eq: ["$status", "maintenance"] }, 1, 0],
},
},
},
@@ -242,7 +258,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
];
const [summary] = await MonitorModel.aggregate(pipeline);
- return summary ?? { totalMonitors: 0, upMonitors: 0, downMonitors: 0, pausedMonitors: 0 };
+ return summary ?? { totalMonitors: 0, upMonitors: 0, downMonitors: 0, pausedMonitors: 0, initializingMonitors: 0, maintenanceMonitors: 0 };
};
findGroupsByTeamId = async (teamId: string): Promise => {
@@ -284,7 +300,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
teamId: toStringId(doc.teamId),
name: doc.name,
description: doc.description ?? undefined,
- status: doc.status ?? undefined,
+ status: doc.status ?? "initializing",
statusWindow: doc.statusWindow ?? [],
statusWindowSize: doc.statusWindowSize,
statusWindowThreshold: doc.statusWindowThreshold,
@@ -331,45 +347,13 @@ class MongoMonitorsRepository implements IMonitorsRepository {
const notificationIds = (doc.notifications ?? []).map((notification: unknown) => toStringId(notification));
- const checks: Check[] = (doc.checks ?? []).map((check: any) => ({
- id: toStringId(check._id),
- metadata: {
- monitorId: toStringId(check.metadata?.monitorId),
- teamId: toStringId(check.metadata?.teamId),
- type: check.metadata?.type,
- },
- status: check.status,
- responseTime: check.responseTime,
- timings: check.timings,
- statusCode: check.statusCode,
- message: check.message,
- ack: check.ack,
- ackAt: check.ackAt ?? null,
- expiry: toDateString(check.expiry),
- cpu: check.cpu,
- memory: check.memory,
- disk: check.disk,
- host: check.host,
- errors: check.errors,
- capture: check.capture,
- net: check.net,
- accessibility: check.accessibility,
- bestPractices: check.bestPractices,
- seo: check.seo,
- performance: check.performance,
- audits: check.audits,
- __v: check.__v,
- createdAt: toDateString(check.createdAt),
- updatedAt: toDateString(check.updatedAt),
- }));
-
return {
id: toStringId(doc._id),
userId: toStringId(doc.userId),
teamId: toStringId(doc.teamId),
name: doc.name,
description: doc.description ?? undefined,
- status: doc.status ?? undefined,
+ status: doc.status ?? "initializing",
statusWindow: doc.statusWindow ?? [],
statusWindowSize: doc.statusWindowSize,
statusWindowThreshold: doc.statusWindowThreshold,
diff --git a/server/src/service/business/incidentService.ts b/server/src/service/business/incidentService.ts
index 8116c893a..881f98f56 100644
--- a/server/src/service/business/incidentService.ts
+++ b/server/src/service/business/incidentService.ts
@@ -46,7 +46,7 @@ class IncidentService {
handleIncident = async (monitor: Monitor, code: number): Promise => {
const activeIncident = await this.incidentsRepository.findActiveByMonitorId(monitor.id, monitor.teamId);
// Monitor is down, create an incident
- if (monitor.status === false) {
+ if (monitor.status === "down") {
if (activeIncident) {
return activeIncident;
} else {
diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts
index cf3824d60..d5d275e59 100644
--- a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts
+++ b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts
@@ -3,7 +3,7 @@ import type { Monitor } from "@/types/monitor.js";
import { AppError } from "@/utils/AppError.js";
import { INetworkService, INotificationsService, IStatusService } from "@/service/index.js";
import IncidentService from "@/service/business/incidentService.js";
-import { IMaintenanceWindowsRepository } from "@/repositories/index.js";
+import { IMaintenanceWindowsRepository, IMonitorsRepository } from "@/repositories/index.js";
class SuperSimpleQueueHelper {
static SERVICE_NAME = SERVICE_NAME;
@@ -16,6 +16,7 @@ class SuperSimpleQueueHelper {
private buffer: any;
private incidentService: IncidentService;
private maintenanceWindowsRepository: IMaintenanceWindowsRepository;
+ private monitorsRepository: IMonitorsRepository;
constructor({
logger,
@@ -26,6 +27,7 @@ class SuperSimpleQueueHelper {
buffer,
incidentService,
maintenanceWindowsRepository,
+ monitorsRepository,
}: {
logger: any;
networkService: INetworkService;
@@ -35,6 +37,7 @@ class SuperSimpleQueueHelper {
buffer: any;
incidentService: IncidentService;
maintenanceWindowsRepository: IMaintenanceWindowsRepository;
+ monitorsRepository: IMonitorsRepository;
}) {
this.logger = logger;
this.networkService = networkService;
@@ -44,6 +47,7 @@ class SuperSimpleQueueHelper {
this.notificationsService = notificationsService;
this.incidentService = incidentService;
this.maintenanceWindowsRepository = maintenanceWindowsRepository;
+ this.monitorsRepository = monitorsRepository;
}
get serviceName() {
@@ -59,7 +63,8 @@ class SuperSimpleQueueHelper {
throw new AppError({ message: "No monitor id", service: SERVICE_NAME, method: "getMonitorJob" });
}
- // Step 1. Check for maintenacne window, if found, skip the check
+ // Step 1. Check for maintenance window, if found, skip the check
+
const maintenanceWindowActive = await this.isInMaintenanceWindow(monitorId, teamId);
if (maintenanceWindowActive) {
this.logger.debug({
@@ -67,6 +72,9 @@ class SuperSimpleQueueHelper {
service: SERVICE_NAME,
method: "getMonitorJob",
});
+ if (monitor.status !== "maintenance") {
+ await this.monitorsRepository.updateById(monitorId, teamId, { status: "maintenance" });
+ }
return;
}
diff --git a/server/src/service/infrastructure/notificationProviders/utils.ts b/server/src/service/infrastructure/notificationProviders/utils.ts
index f71933425..2fe22e834 100644
--- a/server/src/service/infrastructure/notificationProviders/utils.ts
+++ b/server/src/service/infrastructure/notificationProviders/utils.ts
@@ -163,7 +163,7 @@ export const buildHardwareEmail = async (emailService: any, monitor: Monitor, al
};
export const buildEmail = async (emailService: any, monitor: Monitor): Promise => {
- const template = monitor.status === true ? "serverIsUpTemplate" : "serverIsDownTemplate";
+ const template = monitor.status === "up" ? "serverIsUpTemplate" : "serverIsDownTemplate";
const context = { monitor: monitor.name, url: monitor.url };
const html = await emailService.buildEmail(template, context);
return html;
diff --git a/server/src/service/infrastructure/notificationsService.ts b/server/src/service/infrastructure/notificationsService.ts
index 78b1dc8ab..531014a7a 100644
--- a/server/src/service/infrastructure/notificationsService.ts
+++ b/server/src/service/infrastructure/notificationsService.ts
@@ -1,7 +1,8 @@
-import type { HardwareStatusPayload, Monitor, MonitorStatusResponse, Notification } from "@/types/index.js";
+import type { HardwareStatusPayload, Monitor, MonitorStatusResponse, Notification, MonitorStatus } from "@/types/index.js";
import { shouldSendHardwareAlert } from "@/service/infrastructure/notificationProviders/utils.js";
import { IMonitorsRepository, INotificationsRepository } from "@/repositories/index.js";
import { INotificationProvider } from "./notificationProviders/INotificationProvider.js";
+
export interface INotificationsService {
createNotification: (notificationData: Partial) => Promise;
findById: (id: string, teamId: string) => Promise;
@@ -11,7 +12,7 @@ export interface INotificationsService {
handleNotifications: (
monitor: Monitor,
monitorStatusResponse: MonitorStatusResponse,
- prevStatus: boolean | undefined,
+ prevStatus: MonitorStatus,
statusChanged: boolean
) => Promise;
@@ -228,12 +229,7 @@ export class NotificationsService implements INotificationsService {
return await this.emailProvider.sendAlert(notification, syntheticMonitor, baseStatus);
};
- handleNotifications = async (
- monitor: Monitor,
- monitorStatusResponse: MonitorStatusResponse,
- prevStatus: boolean | undefined,
- statusChanged: boolean
- ) => {
+ handleNotifications = async (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse, prevStatus: MonitorStatus, statusChanged: boolean) => {
const { type } = monitor;
const payload = monitorStatusResponse.payload as HardwareStatusPayload;
// If this is a non-hardeware type monitor and status did not change, we're done
diff --git a/server/src/service/infrastructure/statusService.ts b/server/src/service/infrastructure/statusService.ts
index 0e1011278..506ad5396 100755
--- a/server/src/service/infrastructure/statusService.ts
+++ b/server/src/service/infrastructure/statusService.ts
@@ -2,7 +2,6 @@ import { IMonitorsRepository } from "@/repositories/index.js";
import MonitorStats from "../../db/models/MonitorStats.js";
import { CheckModel } from "@/db/models/index.js";
import type {
- CheckErrorInfo,
Monitor,
MonitorStatusResponse,
StatusChangeResult,
@@ -15,7 +14,6 @@ const SERVICE_NAME = "StatusService";
export interface IStatusService {
updateRunningStats({ monitor, networkResponse }: { monitor: Monitor; networkResponse: any }): Promise;
- getStatusString(status: boolean | undefined): string;
handleIncidentForCheck(check: any, monitor: Monitor, action: any, errorContext?: string): Promise;
updateMonitorStatus(
statusResponse: MonitorStatusResponse,
@@ -39,7 +37,7 @@ export class StatusService implements IStatusService {
return StatusService.SERVICE_NAME;
}
- async updateRunningStats({ monitor, networkResponse }: { monitor: Monitor; networkResponse: any }) {
+ async updateRunningStats({ monitor, networkResponse }: { monitor: Monitor; networkResponse: MonitorStatusResponse }) {
try {
const monitorId = monitor.id;
const { responseTime, status } = networkResponse;
@@ -60,7 +58,7 @@ export class StatusService implements IStatusService {
// Update stats
// Last response time
- stats.lastResponseTime = responseTime;
+ stats.lastResponseTime = responseTime ?? 0;
// Avg response time:
let avgResponseTime = stats.avgResponseTime;
@@ -111,12 +109,6 @@ export class StatusService implements IStatusService {
}
}
- getStatusString = (status: boolean | undefined) => {
- if (status === true) return "up";
- if (status === false) return "down";
- return "unknown";
- };
-
handleIncidentForCheck = async (check: Check, monitor: Monitor, action: any, errorContext = "incident handling") => {
try {
let savedCheck;
@@ -208,9 +200,7 @@ export class StatusService implements IStatusService {
monitor.recentChecks.shift();
}
- if (monitor.status === undefined || monitor.status === null) {
- monitor.status = status;
- }
+ monitor.status = status === true ? "up" : "down";
const prevStatus = monitor.status;
let newStatus = monitor.status;
@@ -233,13 +223,13 @@ export class StatusService implements IStatusService {
const failureRate = (failures / monitor.statusWindow.length) * 100;
// If threshold has been met and the monitor is not already down, mark down:
- if (failureRate >= monitor.statusWindowThreshold && monitor.status !== false) {
- newStatus = false;
+ if (failureRate >= monitor.statusWindowThreshold && monitor.status !== "down") {
+ newStatus = "down";
statusChanged = true;
}
// If the failure rate is below the threshold and the monitor is down, recover:
- else if (failureRate < monitor.statusWindowThreshold && monitor.status === false) {
- newStatus = true;
+ else if (failureRate < monitor.statusWindowThreshold && monitor.status === "down") {
+ newStatus = "up";
statusChanged = true;
}
diff --git a/server/src/types/monitor.ts b/server/src/types/monitor.ts
index f6c0f83a7..384e14fe0 100644
--- a/server/src/types/monitor.ts
+++ b/server/src/types/monitor.ts
@@ -1,10 +1,12 @@
-import type { Check } from "@/types/check.js";
import type { CheckSnapshot } from "@/types/check.js";
export type { CheckSnapshot } from "@/types/check.js";
export const MonitorTypes = ["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "unknown"] as const;
export type MonitorType = (typeof MonitorTypes)[number];
+export const MonitorStatuses = ["up", "down", "paused", "initializing", "maintenance"] as const;
+export type MonitorStatus = (typeof MonitorStatuses)[number];
+
export interface MonitorThresholds {
usage_cpu?: number;
usage_memory?: number;
@@ -20,7 +22,7 @@ export interface Monitor {
teamId: string;
name: string;
description?: string;
- status?: boolean;
+ status: MonitorStatus;
statusWindow: boolean[];
statusWindowSize: number;
statusWindowThreshold: number;
@@ -56,6 +58,8 @@ export interface MonitorsSummary {
upMonitors: number;
downMonitors: number;
pausedMonitors: number;
+ initializingMonitors: number;
+ maintenanceMonitors: number;
}
export interface MonitorsWithChecksByTeamIdResult {
diff --git a/server/src/types/network.ts b/server/src/types/network.ts
index 6bed40316..7fbfb79fe 100644
--- a/server/src/types/network.ts
+++ b/server/src/types/network.ts
@@ -11,6 +11,7 @@ import type {
Monitor,
MonitorMatchMethod,
MonitorType,
+ MonitorStatus,
} from "@/types/index.js";
export interface MonitorStatusResponse {
@@ -106,7 +107,7 @@ export interface MonitorPayloadMap {
export type StatusChangeResult = {
monitor: Monitor;
statusChanged: boolean;
- prevStatus: boolean | undefined;
+ prevStatus: MonitorStatus;
code: number;
timestamp: number;
};