From e6c2e34b788e769b2bbb4f46311cc60215050e20 Mon Sep 17 00:00:00 2001 From: mohadeseh safari Date: Tue, 27 May 2025 19:54:31 -0400 Subject: [PATCH 1/2] fix(maintenance): prevent placeholder flash when maintenance items exist (#2286) --- client/src/Pages/Maintenance/index.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/Pages/Maintenance/index.jsx b/client/src/Pages/Maintenance/index.jsx index 41337da2a..eccc95b50 100644 --- a/client/src/Pages/Maintenance/index.jsx +++ b/client/src/Pages/Maintenance/index.jsx @@ -25,6 +25,7 @@ const Maintenance = () => { const [sort, setSort] = useState({}); const [updateTrigger, setUpdateTrigger] = useState(false); const [networkError, setNetworkError] = useState(false); + const [isDataFetched, setIsDataFetched] = useState(false); const handleActionMenuDelete = () => { setUpdateTrigger((prev) => !prev); @@ -42,6 +43,8 @@ const Maintenance = () => { setMaintenanceWindowCount(maintenanceWindowCount); } catch (error) { setNetworkError(true); + } finally { + setIsDataFetched(true); } }; fetchMaintenanceWindows(); @@ -61,7 +64,8 @@ const Maintenance = () => { ); } - if (maintenanceWindows.length === 0) { + // Only show the fallback if we've fetched data and there are no maintenance windows + if (isDataFetched && maintenanceWindows.length === 0) { return ( Date: Thu, 29 May 2025 17:09:47 -0700 Subject: [PATCH 2/2] add config for webhook --- server/service/notificationService.js | 201 ++++++++++++++------------ 1 file changed, 108 insertions(+), 93 deletions(-) diff --git a/server/service/notificationService.js b/server/service/notificationService.js index e4f343c80..ed74ff539 100755 --- a/server/service/notificationService.js +++ b/server/service/notificationService.js @@ -1,11 +1,12 @@ const SERVICE_NAME = "NotificationService"; const TELEGRAM_API_BASE_URL = "https://api.telegram.org/bot"; -const PLATFORM_TYPES = ["telegram", "slack", "discord"]; +const PLATFORM_TYPES = ["telegram", "slack", "discord", "webhook"]; const MESSAGE_FORMATTERS = { telegram: (messageText, chatId) => ({ chat_id: chatId, text: messageText }), slack: (messageText) => ({ text: messageText }), discord: (messageText) => ({ content: messageText }), + webhook: (messageText) => ({ text: messageText }), }; class NotificationService { @@ -42,49 +43,57 @@ class NotificationService { formatNotificationMessage(monitor, status, platform, chatId, code, timestamp) { // Format timestamp using the local system timezone const formatTime = (timestamp) => { - const date = new Date(timestamp); - - // Get timezone abbreviation and format the date - const timeZoneAbbr = date.toLocaleTimeString('en-US', { timeZoneName: 'short' }) - .split(' ').pop(); - - // Format the date with readable format - return date.toLocaleString('en-US', { - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false - }).replace(/(\d+)\/(\d+)\/(\d+),\s/, '$3-$1-$2 ') + ' ' + timeZoneAbbr; + const date = new Date(timestamp); + + // Get timezone abbreviation and format the date + const timeZoneAbbr = date + .toLocaleTimeString("en-US", { timeZoneName: "short" }) + .split(" ") + .pop(); + + // Format the date with readable format + return ( + date + .toLocaleString("en-US", { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: false, + }) + .replace(/(\d+)\/(\d+)\/(\d+),\s/, "$3-$1-$2 ") + + " " + + timeZoneAbbr + ); }; - + // Get formatted time - const formattedTime = timestamp - ? formatTime(timestamp) - : formatTime(new Date().getTime()); - + const formattedTime = timestamp + ? formatTime(timestamp) + : formatTime(new Date().getTime()); + // Create different messages based on status with extra spacing let messageText; if (status === true) { messageText = this.stringService.monitorUpAlert - .replace("{monitorName}", monitor.name) - .replace("{time}", formattedTime) - .replace("{code}", code || 'Unknown'); + .replace("{monitorName}", monitor.name) + .replace("{time}", formattedTime) + .replace("{code}", code || "Unknown"); } else { messageText = this.stringService.monitorDownAlert - .replace("{monitorName}", monitor.name) - .replace("{time}", formattedTime) - .replace("{code}", code || 'Unknown'); + .replace("{monitorName}", monitor.name) + .replace("{time}", formattedTime) + .replace("{code}", code || "Unknown"); } - + if (!PLATFORM_TYPES.includes(platform)) { - return undefined; + return undefined; } - + return MESSAGE_FORMATTERS[platform](messageText, chatId); - } + } /** * Sends a webhook notification to a specified platform. @@ -105,56 +114,56 @@ class NotificationService { const { monitor, status, code } = networkResponse; const { platform } = notification; const { webhookUrl, botToken, chatId } = notification.config; - + // Early return if platform is not supported if (!PLATFORM_TYPES.includes(platform)) { - this.logger.warn({ - message: this.stringService.getWebhookUnsupportedPlatform(platform), - service: this.SERVICE_NAME, - method: "sendWebhookNotification", - details: { platform } - }); - return false; + this.logger.warn({ + message: this.stringService.getWebhookUnsupportedPlatform(platform), + service: this.SERVICE_NAME, + method: "sendWebhookNotification", + details: { platform }, + }); + return false; } - + // Early return for telegram if required fields are missing if (platform === "telegram" && (!botToken || !chatId)) { - this.logger.warn({ - message: "Missing required fields for Telegram notification", - service: this.SERVICE_NAME, - method: "sendWebhookNotification", - details: { platform } - }); - return false; + this.logger.warn({ + message: "Missing required fields for Telegram notification", + service: this.SERVICE_NAME, + method: "sendWebhookNotification", + details: { platform }, + }); + return false; } - + let url = webhookUrl; if (platform === "telegram") { - url = `${TELEGRAM_API_BASE_URL}${botToken}/sendMessage`; + url = `${TELEGRAM_API_BASE_URL}${botToken}/sendMessage`; } - + const message = this.formatNotificationMessage( - monitor, - status, - platform, - chatId, - code, // Pass the code field directly - networkResponse.timestamp + monitor, + status, + platform, + chatId, + code, // Pass the code field directly + networkResponse.timestamp ); - + try { - const response = await this.networkService.requestWebhook(platform, url, message); - return response.status; + const response = await this.networkService.requestWebhook(platform, url, message); + return response.status; } catch (error) { - this.logger.error({ - message: this.stringService.getWebhookSendError(platform), - service: this.SERVICE_NAME, - method: "sendWebhookNotification", - stack: error.stack, - }); - return false; + this.logger.error({ + message: this.stringService.getWebhookSendError(platform), + service: this.SERVICE_NAME, + method: "sendWebhookNotification", + stack: error.stack, + }); + return false; } - } + } /** * Sends an email notification for hardware infrastructure alerts @@ -197,33 +206,33 @@ class NotificationService { async handleStatusNotifications(networkResponse) { try { - // If status hasn't changed, we're done - if (networkResponse.statusChanged === false) return false; - // if prevStatus is undefined, monitor is resuming, we're done - if (networkResponse.prevStatus === undefined) return false; - - const notifications = await this.db.getNotificationsByMonitorId( - networkResponse.monitorId - ); - - for (const notification of notifications) { - if (notification.type === "email") { - await this.sendEmail(networkResponse, notification.address); - } else if (notification.type === "webhook") { - await this.sendWebhookNotification(networkResponse, notification); + // If status hasn't changed, we're done + if (networkResponse.statusChanged === false) return false; + // if prevStatus is undefined, monitor is resuming, we're done + if (networkResponse.prevStatus === undefined) return false; + + const notifications = await this.db.getNotificationsByMonitorId( + networkResponse.monitorId + ); + + for (const notification of notifications) { + if (notification.type === "email") { + await this.sendEmail(networkResponse, notification.address); + } else if (notification.type === "webhook") { + await this.sendWebhookNotification(networkResponse, notification); + } + // Handle other types of notifications here } - // Handle other types of notifications here - } - return true; + return true; } catch (error) { - this.logger.error({ - message: error.message, - service: this.SERVICE_NAME, - method: "handleNotifications", - stack: error.stack, - }); + this.logger.error({ + message: error.message, + service: this.SERVICE_NAME, + method: "handleNotifications", + stack: error.stack, + }); } - } + } /** * Handles status change notifications for a monitor * @@ -257,7 +266,13 @@ class NotificationService { const alerts = { cpu: cpuThreshold !== -1 && cpuUsage > cpuThreshold ? true : false, memory: memoryThreshold !== -1 && memoryUsage > memoryThreshold ? true : false, - disk: disk?.some(d => diskThreshold !== -1 && typeof d?.usage_percent === "number" && d?.usage_percent > diskThreshold) ?? false, + disk: + disk?.some( + (d) => + diskThreshold !== -1 && + typeof d?.usage_percent === "number" && + d?.usage_percent > diskThreshold + ) ?? false, }; const notifications = await this.db.getNotificationsByMonitorId(