From 491741be2305994a2a8deb776f84dea65ae96861 Mon Sep 17 00:00:00 2001 From: Dharun Ashokkumar Date: Tue, 20 Jan 2026 09:52:35 +0530 Subject: [PATCH 1/5] feat: add google sheets notification provider logs monitor events to google spreadsheet via apps script webhook --- .../notification-providers/google-sheets.js | 62 ++++++++++ server/notification.js | 2 + src/components/NotificationDialog.vue | 4 +- src/components/notifications/GoogleSheets.vue | 116 ++++++++++++++++++ src/components/notifications/index.js | 2 + 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 server/notification-providers/google-sheets.js create mode 100644 src/components/notifications/GoogleSheets.vue diff --git a/server/notification-providers/google-sheets.js b/server/notification-providers/google-sheets.js new file mode 100644 index 000000000..416c5ba8a --- /dev/null +++ b/server/notification-providers/google-sheets.js @@ -0,0 +1,62 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class GoogleSheets extends NotificationProvider { + name = "GoogleSheets"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + // Prepare the data to be logged + const timestamp = new Date().toISOString(); + let status = "N/A"; + let monitorName = "N/A"; + let monitorUrl = "N/A"; + let responseTime = "N/A"; + let statusCode = "N/A"; + + if (monitorJSON) { + monitorName = monitorJSON.name || "N/A"; + monitorUrl = this.extractAddress(monitorJSON) || "N/A"; + } + + if (heartbeatJSON) { + status = heartbeatJSON.status === DOWN ? "DOWN" : heartbeatJSON.status === UP ? "UP" : "UNKNOWN"; + responseTime = heartbeatJSON.ping || "N/A"; + statusCode = heartbeatJSON.status || "N/A"; + } + + // Send data to Google Apps Script webhook + const webhookUrl = notification.googleSheetsWebhookUrl; + + const config = this.getAxiosConfigWithProxy({ + headers: { + "Content-Type": "application/json" + } + }); + + const data = { + timestamp: timestamp, + status: status, + monitorName: monitorName, + monitorUrl: monitorUrl, + message: msg, + responseTime: responseTime, + statusCode: statusCode + }; + + await axios.post(webhookUrl, data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = GoogleSheets; diff --git a/server/notification.js b/server/notification.js index de429388c..a1a6fec3c 100644 --- a/server/notification.js +++ b/server/notification.js @@ -17,6 +17,7 @@ const Feishu = require("./notification-providers/feishu"); const Notifery = require("./notification-providers/notifery"); const FreeMobile = require("./notification-providers/freemobile"); const GoogleChat = require("./notification-providers/google-chat"); +const GoogleSheets = require("./notification-providers/google-sheets"); const Gorush = require("./notification-providers/gorush"); const Gotify = require("./notification-providers/gotify"); const GrafanaOncall = require("./notification-providers/grafana-oncall"); @@ -117,6 +118,7 @@ class Notification { new Feishu(), new FreeMobile(), new GoogleChat(), + new GoogleSheets(), new Gorush(), new Gotify(), new GrafanaOncall(), diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 6d915255a..1c8bd68cb 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -305,7 +305,9 @@ export default { }; // Other Integrations - let other = {}; + let other = { + GoogleSheets: "Google Sheets", + }; // Regional - Not supported in most regions or documentation is not in English let regional = { diff --git a/src/components/notifications/GoogleSheets.vue b/src/components/notifications/GoogleSheets.vue new file mode 100644 index 000000000..1d21cbdeb --- /dev/null +++ b/src/components/notifications/GoogleSheets.vue @@ -0,0 +1,116 @@ + + + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 1a8f6dba3..6a7c3e71b 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -15,6 +15,7 @@ import Elks from "./46elks.vue"; import Feishu from "./Feishu.vue"; import FreeMobile from "./FreeMobile.vue"; import GoogleChat from "./GoogleChat.vue"; +import GoogleSheets from "./GoogleSheets.vue"; import Gorush from "./Gorush.vue"; import Gotify from "./Gotify.vue"; import GrafanaOncall from "./GrafanaOncall.vue"; @@ -105,6 +106,7 @@ const NotificationFormList = { Feishu: Feishu, FreeMobile: FreeMobile, GoogleChat: GoogleChat, + GoogleSheets: GoogleSheets, gorush: Gorush, gotify: Gotify, GrafanaOncall: GrafanaOncall, From 44923b01415b633642fc51d62b5e6afedcdddd69 Mon Sep 17 00:00:00 2001 From: Dharun Ashokkumar Date: Tue, 20 Jan 2026 10:02:20 +0530 Subject: [PATCH 2/5] fix: address copilot review suggestions use proper statusCode field and add error handling for clipboard --- server/notification-providers/google-sheets.js | 14 +++++++------- src/components/notifications/GoogleSheets.vue | 8 ++++++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/server/notification-providers/google-sheets.js b/server/notification-providers/google-sheets.js index 416c5ba8a..8c8ec1581 100644 --- a/server/notification-providers/google-sheets.js +++ b/server/notification-providers/google-sheets.js @@ -28,7 +28,7 @@ class GoogleSheets extends NotificationProvider { if (heartbeatJSON) { status = heartbeatJSON.status === DOWN ? "DOWN" : heartbeatJSON.status === UP ? "UP" : "UNKNOWN"; responseTime = heartbeatJSON.ping || "N/A"; - statusCode = heartbeatJSON.status || "N/A"; + statusCode = heartbeatJSON.statusCode || "N/A"; } // Send data to Google Apps Script webhook @@ -41,13 +41,13 @@ class GoogleSheets extends NotificationProvider { }); const data = { - timestamp: timestamp, - status: status, - monitorName: monitorName, - monitorUrl: monitorUrl, + timestamp, + status, + monitorName, + monitorUrl, message: msg, - responseTime: responseTime, - statusCode: statusCode + responseTime, + statusCode }; await axios.post(webhookUrl, data, config); diff --git a/src/components/notifications/GoogleSheets.vue b/src/components/notifications/GoogleSheets.vue index 1d21cbdeb..2f167b3ce 100644 --- a/src/components/notifications/GoogleSheets.vue +++ b/src/components/notifications/GoogleSheets.vue @@ -108,8 +108,12 @@ export default { return ContentService.createTextOutput(JSON.stringify({result: 'success'})) .setMimeType(ContentService.MimeType.JSON); }`; - navigator.clipboard.writeText(scriptCode); - alert(this.$t("Copied to clipboard!")); + try { + navigator.clipboard.writeText(scriptCode); + alert(this.$t("Copied to clipboard!")); + } catch (error) { + alert(this.$t("Failed to copy to clipboard")); + } } } }; From bcaf554896a92f43cc2245d6f7320c6292bdd91b Mon Sep 17 00:00:00 2001 From: Dharun Ashokkumar Date: Tue, 20 Jan 2026 21:17:58 +0530 Subject: [PATCH 3/5] fix: address code review feedback Use HiddenInput for webhook URL, replace button with ToggleSection, inline webhook variable, and move script to constant to avoid duplication --- .../notification-providers/google-sheets.js | 4 +- src/components/notifications/GoogleSheets.vue | 102 +++++++----------- 2 files changed, 41 insertions(+), 65 deletions(-) diff --git a/server/notification-providers/google-sheets.js b/server/notification-providers/google-sheets.js index 8c8ec1581..fc74a6b55 100644 --- a/server/notification-providers/google-sheets.js +++ b/server/notification-providers/google-sheets.js @@ -32,8 +32,6 @@ class GoogleSheets extends NotificationProvider { } // Send data to Google Apps Script webhook - const webhookUrl = notification.googleSheetsWebhookUrl; - const config = this.getAxiosConfigWithProxy({ headers: { "Content-Type": "application/json" @@ -50,7 +48,7 @@ class GoogleSheets extends NotificationProvider { statusCode }; - await axios.post(webhookUrl, data, config); + await axios.post(notification.googleSheetsWebhookUrl, data, config); return okMsg; } catch (error) { diff --git a/src/components/notifications/GoogleSheets.vue b/src/components/notifications/GoogleSheets.vue index 2f167b3ce..f702a94b4 100644 --- a/src/components/notifications/GoogleSheets.vue +++ b/src/components/notifications/GoogleSheets.vue @@ -1,13 +1,12 @@