mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-21 09:09:09 -05:00
Merge develop into codecanvas-diagram-1761668096103 to sync with upstream
This commit is contained in:
@@ -223,18 +223,54 @@ const CreateNotifications = () => {
|
||||
<Typography component="p">{t(DESCRIPTION_MAP[type])}</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(12)}>
|
||||
<TextInput
|
||||
label={t(LABEL_MAP[type])}
|
||||
name="address"
|
||||
placeholder={t(PLACEHOLDER_MAP[type])}
|
||||
value={notification.address}
|
||||
onChange={onChange}
|
||||
error={Boolean(errors.address)}
|
||||
helperText={errors["address"]}
|
||||
/>
|
||||
{type === "matrix" ? (
|
||||
<>
|
||||
<TextInput
|
||||
label={t("createNotifications.matrixSettings.homeserverLabel")}
|
||||
name="homeserverUrl"
|
||||
placeholder={t(
|
||||
"createNotifications.matrixSettings.homeserverPlaceholder"
|
||||
)}
|
||||
value={notification.homeserverUrl || ""}
|
||||
onChange={onChange}
|
||||
error={Boolean(errors.homeserverUrl)}
|
||||
helperText={errors["homeserverUrl"]}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("createNotifications.matrixSettings.roomIdLabel")}
|
||||
name="roomId"
|
||||
placeholder={t("createNotifications.matrixSettings.roomIdPlaceholder")}
|
||||
value={notification.roomId || ""}
|
||||
onChange={onChange}
|
||||
error={Boolean(errors.roomId)}
|
||||
helperText={errors["roomId"]}
|
||||
/>
|
||||
<TextInput
|
||||
label={t("createNotifications.matrixSettings.accessTokenLabel")}
|
||||
name="accessToken"
|
||||
type="password"
|
||||
placeholder={t(
|
||||
"createNotifications.matrixSettings.accessTokenPlaceholder"
|
||||
)}
|
||||
value={notification.accessToken || ""}
|
||||
onChange={onChange}
|
||||
error={Boolean(errors.accessToken)}
|
||||
helperText={errors["accessToken"]}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<TextInput
|
||||
label={t(LABEL_MAP[type])}
|
||||
name="address"
|
||||
placeholder={t(PLACEHOLDER_MAP[type])}
|
||||
value={notification.address}
|
||||
onChange={onChange}
|
||||
error={Boolean(errors.address)}
|
||||
helperText={errors["address"]}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
|
||||
</ConfigBox>{" "}
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-end"
|
||||
|
||||
@@ -4,6 +4,7 @@ export const NOTIFICATION_TYPES = [
|
||||
{ _id: 3, name: "PagerDuty", value: "pager_duty" },
|
||||
{ _id: 4, name: "Webhook", value: "webhook" },
|
||||
{ _id: 5, name: "Discord", value: "discord" },
|
||||
{ _id: 6, name: "Matrix", value: "matrix" },
|
||||
];
|
||||
|
||||
export const TITLE_MAP = {
|
||||
@@ -12,6 +13,7 @@ export const TITLE_MAP = {
|
||||
pager_duty: "createNotifications.pagerdutySettings.title",
|
||||
webhook: "createNotifications.webhookSettings.title",
|
||||
discord: "createNotifications.discordSettings.title",
|
||||
matrix: "createNotifications.matrixSettings.title",
|
||||
};
|
||||
|
||||
export const DESCRIPTION_MAP = {
|
||||
@@ -20,6 +22,7 @@ export const DESCRIPTION_MAP = {
|
||||
pager_duty: "createNotifications.pagerdutySettings.description",
|
||||
webhook: "createNotifications.webhookSettings.description",
|
||||
discord: "createNotifications.discordSettings.description",
|
||||
matrix: "createNotifications.matrixSettings.description",
|
||||
};
|
||||
|
||||
export const LABEL_MAP = {
|
||||
@@ -28,6 +31,7 @@ export const LABEL_MAP = {
|
||||
pager_duty: "createNotifications.pagerdutySettings.integrationKeyLabel",
|
||||
webhook: "createNotifications.webhookSettings.webhookLabel",
|
||||
discord: "createNotifications.discordSettings.webhookLabel",
|
||||
matrix: "createNotifications.matrixSettings.homeserverLabel",
|
||||
};
|
||||
|
||||
export const PLACEHOLDER_MAP = {
|
||||
@@ -36,4 +40,5 @@ export const PLACEHOLDER_MAP = {
|
||||
pager_duty: "createNotifications.pagerdutySettings.integrationKeyPlaceholder",
|
||||
webhook: "createNotifications.webhookSettings.webhookPlaceholder",
|
||||
discord: "createNotifications.discordSettings.webhookPlaceholder",
|
||||
matrix: "createNotifications.matrixSettings.homeserverPlaceholder",
|
||||
};
|
||||
|
||||
@@ -458,7 +458,7 @@ const notificationValidation = joi.object({
|
||||
|
||||
type: joi
|
||||
.string()
|
||||
.valid("email", "webhook", "slack", "discord", "pager_duty")
|
||||
.valid("email", "webhook", "slack", "discord", "pager_duty", "matrix")
|
||||
.required()
|
||||
.messages({
|
||||
"string.empty": "Notification type is required",
|
||||
@@ -495,8 +495,40 @@ const notificationValidation = joi.object({
|
||||
"string.uri": "Please enter a valid Webhook URL",
|
||||
}),
|
||||
},
|
||||
{
|
||||
is: "matrix",
|
||||
then: joi.string().allow("").optional(),
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
||||
homeserverUrl: joi.when("type", {
|
||||
is: "matrix",
|
||||
then: joi.string().uri().required().messages({
|
||||
"string.empty": "Homeserver URL cannot be empty",
|
||||
"any.required": "Homeserver URL is required",
|
||||
"string.uri": "Please enter a valid Homeserver URL",
|
||||
}),
|
||||
otherwise: joi.string().allow("").optional(),
|
||||
}),
|
||||
|
||||
roomId: joi.when("type", {
|
||||
is: "matrix",
|
||||
then: joi.string().required().messages({
|
||||
"string.empty": "Room ID cannot be empty",
|
||||
"any.required": "Room ID is required",
|
||||
}),
|
||||
otherwise: joi.string().allow("").optional(),
|
||||
}),
|
||||
|
||||
accessToken: joi.when("type", {
|
||||
is: "matrix",
|
||||
then: joi.string().required().messages({
|
||||
"string.empty": "Access Token cannot be empty",
|
||||
"any.required": "Access Token is required",
|
||||
}),
|
||||
otherwise: joi.string().allow("").optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
const editUserValidation = joi.object({
|
||||
|
||||
@@ -7,7 +7,7 @@ const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
export default defineConfig(({}) => {
|
||||
let version = "3.2.0";
|
||||
let version = "3.2.1";
|
||||
|
||||
return {
|
||||
base: "/",
|
||||
|
||||
Generated
+1267
-1049
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -51,7 +51,7 @@
|
||||
"ping": "0.4.4",
|
||||
"sharp": "0.33.5",
|
||||
"ssl-checker": "2.0.10",
|
||||
"super-simple-scheduler": "1.4.1",
|
||||
"super-simple-scheduler": "1.4.4",
|
||||
"swagger-ui-express": "5.0.1",
|
||||
"winston": "^3.13.0"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ const NotificationSchema = mongoose.Schema(
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: ["email", "slack", "discord", "webhook", "pager_duty"],
|
||||
enum: ["email", "slack", "discord", "webhook", "pager_duty", "matrix"],
|
||||
},
|
||||
notificationName: {
|
||||
type: String,
|
||||
@@ -28,6 +28,16 @@ const NotificationSchema = mongoose.Schema(
|
||||
phone: {
|
||||
type: String,
|
||||
},
|
||||
// Matrix-specific fields
|
||||
homeserverUrl: {
|
||||
type: String,
|
||||
},
|
||||
roomId: {
|
||||
type: String,
|
||||
},
|
||||
accessToken: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
"checkmate": "Checkmate",
|
||||
"url": "URL",
|
||||
"unknown": "Unknown",
|
||||
"uptimeAlert": "Uptime Alert: One of your monitors is back online",
|
||||
"downtimeAlert": "Downtime Alert: One of your monitors went offline"
|
||||
"uptimeAlert": "Uptime Alert: Your monitor {monitorName} is back online",
|
||||
"downtimeAlert": "Downtime Alert: Your monitor {monitorName} went offline"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Router } from "express";
|
||||
import MaintenanceWindow from "../../db/v1/models/MaintenanceWindow.js";
|
||||
|
||||
class MaintenanceWindowRoutes {
|
||||
constructor(maintenanceWindowController) {
|
||||
this.router = Router();
|
||||
|
||||
@@ -529,6 +529,42 @@ class NetworkService {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async requestMatrix({ homeserverUrl, accessToken, roomId, message }) {
|
||||
try {
|
||||
const url = `${homeserverUrl}/_matrix/client/v3/rooms/${roomId}/send/m.room.message?access_token=${accessToken}`;
|
||||
const body = {
|
||||
msgtype: "m.text",
|
||||
body: message,
|
||||
format: "org.matrix.custom.html",
|
||||
formatted_body: message,
|
||||
};
|
||||
const response = await this.axios.post(url, body, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
status: true,
|
||||
code: response.status,
|
||||
message: "Successfully sent Matrix notification",
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.warn({
|
||||
message: error.message,
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestMatrix",
|
||||
});
|
||||
|
||||
return {
|
||||
status: false,
|
||||
code: error.response?.status || this.NETWORK_ERROR,
|
||||
message: "Failed to send Matrix notification",
|
||||
payload: error.response?.data,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NetworkService;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
const SERVICE_NAME = "Matrix";
|
||||
|
||||
class Matrix {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor({ networkService, logger }) {
|
||||
this.networkService = networkService;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
get serviceName() {
|
||||
return Matrix.SERVICE_NAME;
|
||||
}
|
||||
|
||||
async send({ friendlyName, homeserverUrl, accessToken, roomId, message, monitorName }) {
|
||||
const title = `Checkmate status for ${monitorName}`;
|
||||
const formattedMessage = `## ${title}\n${message}`;
|
||||
try {
|
||||
await this.networkService.requestMatrix({
|
||||
homeserverUrl,
|
||||
accessToken,
|
||||
roomId,
|
||||
message: formattedMessage,
|
||||
});
|
||||
this.logger.info(`Successfully sent Matrix notification for ${friendlyName}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to send Matrix notification for ${friendlyName}: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Matrix;
|
||||
@@ -1,4 +1,5 @@
|
||||
const SERVICE_NAME = "NotificationService";
|
||||
import Matrix from "./notificationProviders/matrix.js";
|
||||
|
||||
class NotificationService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
@@ -46,6 +47,14 @@ class NotificationService {
|
||||
|
||||
return response;
|
||||
}
|
||||
if (type === "matrix") {
|
||||
const { friendlyName, homeserverUrl, accessToken, roomId } = notification;
|
||||
const monitorName = subject;
|
||||
const message = content;
|
||||
const matrix = new Matrix({ networkService: this.networkService, logger: this.logger });
|
||||
const success = await matrix.send({ friendlyName, homeserverUrl, accessToken, roomId, message, monitorName });
|
||||
return success;
|
||||
}
|
||||
};
|
||||
|
||||
async handleNotifications(networkResponse) {
|
||||
|
||||
@@ -74,7 +74,9 @@ class NotificationUtils {
|
||||
let discordMessageText = {
|
||||
embeds: [
|
||||
{
|
||||
title: status ? dn.uptimeAlert : dn.downtimeAlert,
|
||||
title: status
|
||||
? dn.uptimeAlert.replace("{monitorName}", monitor?.name ?? dn.unknown)
|
||||
: dn.downtimeAlert.replace("{monitorName}", monitor?.name ?? dn.unknown),
|
||||
color: status ? 5763719 : 15548997,
|
||||
|
||||
fields: [
|
||||
|
||||
@@ -381,7 +381,7 @@ class MonitorService implements IMonitorService {
|
||||
// Get monitor stats
|
||||
const monitorStats = await MonitorStats.findOne({
|
||||
monitorId: monitor._id,
|
||||
}).lean();
|
||||
});
|
||||
|
||||
if (!monitorStats) {
|
||||
throw new ApiError("Monitor stats not found", 404);
|
||||
|
||||
@@ -575,10 +575,10 @@ const createNotificationBodyValidation = joi.object({
|
||||
"any.required": "Notification name is required",
|
||||
}),
|
||||
|
||||
type: joi.string().valid("email", "webhook", "slack", "discord", "pager_duty").required().messages({
|
||||
type: joi.string().valid("email", "webhook", "slack", "discord", "pager_duty", "matrix").required().messages({
|
||||
"string.empty": "Notification type is required",
|
||||
"any.required": "Notification type is required",
|
||||
"any.only": "Notification type must be email, webhook, or pager_duty",
|
||||
"any.only": "Notification type must be email, webhook, slack, discord, pager_duty, or matrix",
|
||||
}),
|
||||
|
||||
address: joi.when("type", {
|
||||
@@ -606,8 +606,40 @@ const createNotificationBodyValidation = joi.object({
|
||||
"string.uri": "Please enter a valid Webhook URL",
|
||||
}),
|
||||
},
|
||||
{
|
||||
is: "matrix",
|
||||
then: joi.string().allow("").optional(),
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
||||
homeserverUrl: joi.when("type", {
|
||||
is: "matrix",
|
||||
then: joi.string().uri().required().messages({
|
||||
"string.empty": "Homeserver URL cannot be empty",
|
||||
"any.required": "Homeserver URL is required",
|
||||
"string.uri": "Please enter a valid Homeserver URL",
|
||||
}),
|
||||
otherwise: joi.string().allow("").optional(),
|
||||
}),
|
||||
|
||||
roomId: joi.when("type", {
|
||||
is: "matrix",
|
||||
then: joi.string().required().messages({
|
||||
"string.empty": "Room ID cannot be empty",
|
||||
"any.required": "Room ID is required",
|
||||
}),
|
||||
otherwise: joi.string().allow("").optional(),
|
||||
}),
|
||||
|
||||
accessToken: joi.when("type", {
|
||||
is: "matrix",
|
||||
then: joi.string().required().messages({
|
||||
"string.empty": "Access Token cannot be empty",
|
||||
"any.required": "Access Token is required",
|
||||
}),
|
||||
otherwise: joi.string().allow("").optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
//****************************************
|
||||
|
||||
Reference in New Issue
Block a user