mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2025-12-22 10:47:08 +00:00
format
This commit is contained in:
10
server/.github/pull_request_template.md
vendored
10
server/.github/pull_request_template.md
vendored
@@ -1,23 +1,25 @@
|
||||
**(Please remove this line only before submitting your PR. Ensure that all relevant items are checked before submission.)**
|
||||
**(Please remove this line only before submitting your PR. Ensure that all relevant items are checked before submission.)**
|
||||
|
||||
## Describe your changes
|
||||
|
||||
Briefly describe the changes you made and their purpose.
|
||||
Briefly describe the changes you made and their purpose.
|
||||
|
||||
## Write your issue number after "Fixes "
|
||||
|
||||
Fixes #123
|
||||
Fixes #123
|
||||
|
||||
## Please ensure all items are checked off before requesting a review. "Checked off" means you need to add an "x" character between brackets so they turn into checkmarks.
|
||||
|
||||
- [ ] (Do not skip this or your PR will be closed) I deployed the application locally.
|
||||
- [ ] (Do not skip this or your PR will be closed) I have performed a self-review and testing of my code.
|
||||
- [ ] I have included the issue # in the PR.
|
||||
- [ ] I have added i18n support to visible strings (instead of `<div>Add</div>`, use):
|
||||
- [ ] I have added i18n support to visible strings (instead of `<div>Add</div>`, use):
|
||||
|
||||
```Javascript
|
||||
const { t } = useTranslation();
|
||||
<div>{t('add')}</div>
|
||||
```
|
||||
|
||||
- [ ] The issue I am working on is assigned to me.
|
||||
- [ ] I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application).
|
||||
- [ ] My PR is granular and targeted to one specific feature.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<p align="center"><strong>The backend service for Checkmate, an open source uptime and infrastructure monitoring application</strong></p>
|
||||
|
||||
This directory contains the **backend** of Checkmate, which handles data processing, storage, and API services for the Checkmate monitoring tool. The backend is responsible for managing uptime checks, handling real-time alerts, and storing historical monitoring data.
|
||||
This directory contains the **backend** of Checkmate, which handles data processing, storage, and API services for the Checkmate monitoring tool. The backend is responsible for managing uptime checks, handling real-time alerts, and storing historical monitoring data.
|
||||
|
||||
Checkmate's backend is designed to be lightweight and scalable, ensuring reliable performance even with a high number of active monitors.
|
||||
|
||||
|
||||
@@ -4,171 +4,171 @@ import { handleError, handleValidationError } from "./controllerUtils.js";
|
||||
const SERVICE_NAME = "NotificationController";
|
||||
|
||||
const NOTIFICATION_TYPES = {
|
||||
WEBHOOK: 'webhook',
|
||||
TELEGRAM: 'telegram'
|
||||
WEBHOOK: "webhook",
|
||||
TELEGRAM: "telegram",
|
||||
};
|
||||
|
||||
const PLATFORMS = {
|
||||
SLACK: 'slack',
|
||||
DISCORD: 'discord',
|
||||
TELEGRAM: 'telegram'
|
||||
SLACK: "slack",
|
||||
DISCORD: "discord",
|
||||
TELEGRAM: "telegram",
|
||||
};
|
||||
|
||||
class NotificationController {
|
||||
constructor(notificationService, stringService, statusService) {
|
||||
this.notificationService = notificationService;
|
||||
this.stringService = stringService;
|
||||
this.statusService = statusService;
|
||||
this.triggerNotification = this.triggerNotification.bind(this);
|
||||
this.testWebhook = this.testWebhook.bind(this);
|
||||
}
|
||||
constructor(notificationService, stringService, statusService) {
|
||||
this.notificationService = notificationService;
|
||||
this.stringService = stringService;
|
||||
this.statusService = statusService;
|
||||
this.triggerNotification = this.triggerNotification.bind(this);
|
||||
this.testWebhook = this.testWebhook.bind(this);
|
||||
}
|
||||
|
||||
async triggerNotification(req, res, next) {
|
||||
try {
|
||||
await triggerNotificationBodyValidation.validateAsync(req.body, {
|
||||
abortEarly: false,
|
||||
stripUnknown: true,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
async triggerNotification(req, res, next) {
|
||||
try {
|
||||
await triggerNotificationBodyValidation.validateAsync(req.body, {
|
||||
abortEarly: false,
|
||||
stripUnknown: true,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { monitorId, type, platform, config, status = false } = req.body;
|
||||
try {
|
||||
const { monitorId, type, platform, config, status = false } = req.body;
|
||||
|
||||
// Create a simplified networkResponse similar to what would come from monitoring
|
||||
const networkResponse = {
|
||||
monitorId,
|
||||
status
|
||||
};
|
||||
|
||||
// Use the statusService to get monitor details and handle status change logic
|
||||
// This returns { monitor, statusChanged, prevStatus } exactly like your job queue uses
|
||||
const statusResult = await this.statusService.updateStatus(networkResponse);
|
||||
|
||||
if (type === NOTIFICATION_TYPES.WEBHOOK) {
|
||||
const notification = {
|
||||
type,
|
||||
platform,
|
||||
config,
|
||||
};
|
||||
// Create a simplified networkResponse similar to what would come from monitoring
|
||||
const networkResponse = {
|
||||
monitorId,
|
||||
status,
|
||||
};
|
||||
|
||||
await this.notificationService.sendWebhookNotification(
|
||||
statusResult, // Contains monitor, statusChanged, and prevStatus
|
||||
notification
|
||||
);
|
||||
}
|
||||
// Use the statusService to get monitor details and handle status change logic
|
||||
// This returns { monitor, statusChanged, prevStatus } exactly like your job queue uses
|
||||
const statusResult = await this.statusService.updateStatus(networkResponse);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.webhookSendSuccess,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "triggerNotification"));
|
||||
}
|
||||
}
|
||||
if (type === NOTIFICATION_TYPES.WEBHOOK) {
|
||||
const notification = {
|
||||
type,
|
||||
platform,
|
||||
config,
|
||||
};
|
||||
|
||||
createTestNetworkResponse() {
|
||||
return {
|
||||
monitor: {
|
||||
_id: "test-monitor-id",
|
||||
name: "Test Monitor",
|
||||
url: "https://example.com"
|
||||
},
|
||||
status: true,
|
||||
statusChanged: true,
|
||||
prevStatus: false,
|
||||
};
|
||||
}
|
||||
await this.notificationService.sendWebhookNotification(
|
||||
statusResult, // Contains monitor, statusChanged, and prevStatus
|
||||
notification
|
||||
);
|
||||
}
|
||||
|
||||
handleTelegramTest(botToken, chatId) {
|
||||
if (!botToken || !chatId) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: {
|
||||
msg: this.stringService.telegramRequiresBotTokenAndChatId,
|
||||
status: 400
|
||||
}
|
||||
};
|
||||
}
|
||||
return res.success({
|
||||
msg: this.stringService.webhookSendSuccess,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "triggerNotification"));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
notification: {
|
||||
type: NOTIFICATION_TYPES.WEBHOOK,
|
||||
platform: PLATFORMS.TELEGRAM,
|
||||
config: { botToken, chatId }
|
||||
}
|
||||
};
|
||||
}
|
||||
createTestNetworkResponse() {
|
||||
return {
|
||||
monitor: {
|
||||
_id: "test-monitor-id",
|
||||
name: "Test Monitor",
|
||||
url: "https://example.com",
|
||||
},
|
||||
status: true,
|
||||
statusChanged: true,
|
||||
prevStatus: false,
|
||||
};
|
||||
}
|
||||
|
||||
handleWebhookTest(webhookUrl, platform) {
|
||||
if (webhookUrl === null) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: {
|
||||
msg: this.stringService.webhookUrlRequired,
|
||||
status: 400
|
||||
}
|
||||
};
|
||||
}
|
||||
handleTelegramTest(botToken, chatId) {
|
||||
if (!botToken || !chatId) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: {
|
||||
msg: this.stringService.telegramRequiresBotTokenAndChatId,
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
notification: {
|
||||
type: NOTIFICATION_TYPES.WEBHOOK,
|
||||
platform: platform,
|
||||
config: { webhookUrl }
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
isValid: true,
|
||||
notification: {
|
||||
type: NOTIFICATION_TYPES.WEBHOOK,
|
||||
platform: PLATFORMS.TELEGRAM,
|
||||
config: { botToken, chatId },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async testWebhook(req, res, next) {
|
||||
try {
|
||||
const { webhookUrl, platform, botToken, chatId } = req.body;
|
||||
handleWebhookTest(webhookUrl, platform) {
|
||||
if (webhookUrl === null) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: {
|
||||
msg: this.stringService.webhookUrlRequired,
|
||||
status: 400,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (platform === null) {
|
||||
return res.error({
|
||||
msg: this.stringService.platformRequired,
|
||||
status: 400
|
||||
});
|
||||
}
|
||||
|
||||
// Platform-specific handling
|
||||
const platformHandlers = {
|
||||
[PLATFORMS.TELEGRAM]: () => this.handleTelegramTest(botToken, chatId),
|
||||
// Default handler for webhook-based platforms (Slack, Discord, etc.)
|
||||
default: () => this.handleWebhookTest(webhookUrl, platform)
|
||||
};
|
||||
|
||||
const handler = platformHandlers[platform] || platformHandlers.default;
|
||||
const handlerResult = handler();
|
||||
|
||||
if (!handlerResult.isValid) {
|
||||
return res.error(handlerResult.error);
|
||||
}
|
||||
|
||||
const networkResponse = this.createTestNetworkResponse();
|
||||
|
||||
const result = await this.notificationService.sendWebhookNotification(
|
||||
networkResponse,
|
||||
handlerResult.notification
|
||||
);
|
||||
|
||||
if (result && result !== false) {
|
||||
return res.success({
|
||||
msg: this.stringService.webhookSendSuccess,
|
||||
});
|
||||
} else {
|
||||
return res.error({
|
||||
msg: this.stringService.testNotificationFailed,
|
||||
status: 400
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "testWebhook"));
|
||||
}
|
||||
}
|
||||
return {
|
||||
isValid: true,
|
||||
notification: {
|
||||
type: NOTIFICATION_TYPES.WEBHOOK,
|
||||
platform: platform,
|
||||
config: { webhookUrl },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async testWebhook(req, res, next) {
|
||||
try {
|
||||
const { webhookUrl, platform, botToken, chatId } = req.body;
|
||||
|
||||
if (platform === null) {
|
||||
return res.error({
|
||||
msg: this.stringService.platformRequired,
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
// Platform-specific handling
|
||||
const platformHandlers = {
|
||||
[PLATFORMS.TELEGRAM]: () => this.handleTelegramTest(botToken, chatId),
|
||||
// Default handler for webhook-based platforms (Slack, Discord, etc.)
|
||||
default: () => this.handleWebhookTest(webhookUrl, platform),
|
||||
};
|
||||
|
||||
const handler = platformHandlers[platform] || platformHandlers.default;
|
||||
const handlerResult = handler();
|
||||
|
||||
if (!handlerResult.isValid) {
|
||||
return res.error(handlerResult.error);
|
||||
}
|
||||
|
||||
const networkResponse = this.createTestNetworkResponse();
|
||||
|
||||
const result = await this.notificationService.sendWebhookNotification(
|
||||
networkResponse,
|
||||
handlerResult.notification
|
||||
);
|
||||
|
||||
if (result && result !== false) {
|
||||
return res.success({
|
||||
msg: this.stringService.webhookSendSuccess,
|
||||
});
|
||||
} else {
|
||||
return res.error({
|
||||
msg: this.stringService.testNotificationFailed,
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "testWebhook"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NotificationController;
|
||||
export default NotificationController;
|
||||
|
||||
@@ -20,9 +20,9 @@ const NotificationSchema = mongoose.Schema(
|
||||
type: String,
|
||||
enum: ["email", "sms", "webhook"],
|
||||
},
|
||||
platform: {
|
||||
platform: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
config: {
|
||||
type: configSchema,
|
||||
default: () => ({}),
|
||||
|
||||
@@ -38,7 +38,7 @@ const updateStatusPage = async (statusPageData, image) => {
|
||||
data: image.buffer,
|
||||
contentType: image.mimetype,
|
||||
};
|
||||
}else{
|
||||
} else {
|
||||
statusPageData.logo = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,29 +2,23 @@ import { Router } from "express";
|
||||
import { verifyJWT } from "../middleware/verifyJWT.js";
|
||||
|
||||
class NotificationRoutes {
|
||||
constructor(notificationController) {
|
||||
this.router = Router();
|
||||
this.notificationController = notificationController;
|
||||
this.initializeRoutes();
|
||||
}
|
||||
constructor(notificationController) {
|
||||
this.router = Router();
|
||||
this.notificationController = notificationController;
|
||||
this.initializeRoutes();
|
||||
}
|
||||
|
||||
initializeRoutes() {
|
||||
this.router.use(verifyJWT);
|
||||
|
||||
this.router.post(
|
||||
"/trigger",
|
||||
this.notificationController.triggerNotification
|
||||
);
|
||||
|
||||
this.router.post(
|
||||
"/test-webhook",
|
||||
this.notificationController.testWebhook
|
||||
);
|
||||
}
|
||||
initializeRoutes() {
|
||||
this.router.use(verifyJWT);
|
||||
|
||||
getRouter() {
|
||||
return this.router;
|
||||
}
|
||||
this.router.post("/trigger", this.notificationController.triggerNotification);
|
||||
|
||||
this.router.post("/test-webhook", this.notificationController.testWebhook);
|
||||
}
|
||||
|
||||
getRouter() {
|
||||
return this.router;
|
||||
}
|
||||
}
|
||||
|
||||
export default NotificationRoutes;
|
||||
export default NotificationRoutes;
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
<mjml>
|
||||
<mj-body>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text font-size="20px" font-family="Helvetica Neue">
|
||||
Hello!
|
||||
</mj-text>
|
||||
<mj-text font-size="16px" font-family="Helvetica Neue">
|
||||
This is a test email from the Monitoring System.
|
||||
</mj-text>
|
||||
<mj-text font-size="14px" font-family="Helvetica Neue">
|
||||
If you're receiving this, your email configuration is working correctly.
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
<mj-body>
|
||||
<mj-section>
|
||||
<mj-column>
|
||||
<mj-text
|
||||
font-size="20px"
|
||||
font-family="Helvetica Neue"
|
||||
>
|
||||
Hello!
|
||||
</mj-text>
|
||||
<mj-text
|
||||
font-size="16px"
|
||||
font-family="Helvetica Neue"
|
||||
>
|
||||
This is a test email from the Monitoring System.
|
||||
</mj-text>
|
||||
<mj-text
|
||||
font-size="14px"
|
||||
font-family="Helvetica Neue"
|
||||
>
|
||||
If you're receiving this, your email configuration is working correctly.
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
</mjml>
|
||||
|
||||
Reference in New Issue
Block a user