From 29e24e0de96c20a598e41a8a500a8be5848664c4 Mon Sep 17 00:00:00 2001 From: c Date: Sun, 8 Jan 2023 17:19:07 +0000 Subject: [PATCH 01/13] Feature - Added Optional Google Analytics tag for Status Page. --- db/patch-add-google-analytics-status-page-tag.sql | 4 ++++ server/database.js | 1 + server/model/status_page.js | 2 ++ .../socket-handlers/status-page-socket-handler.js | 1 + src/pages/StatusPage.vue | 15 +++++++++++++++ 5 files changed, 23 insertions(+) create mode 100644 db/patch-add-google-analytics-status-page-tag.sql diff --git a/db/patch-add-google-analytics-status-page-tag.sql b/db/patch-add-google-analytics-status-page-tag.sql new file mode 100644 index 00000000..15305ae2 --- /dev/null +++ b/db/patch-add-google-analytics-status-page-tag.sql @@ -0,0 +1,4 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; +ALTER TABLE status_page ADD google_analytics_tag_id TEXT; +COMMIT; diff --git a/server/database.js b/server/database.js index 06b81153..52c701fb 100644 --- a/server/database.js +++ b/server/database.js @@ -69,6 +69,7 @@ class Database { "patch-ping-packet-size.sql": true, "patch-maintenance-table2.sql": true, "patch-add-gamedig-monitor.sql": true, + "patch-add-google-analytics-status-page-tag.sql": true }; /** diff --git a/server/model/status_page.js b/server/model/status_page.js index 0dabf5ab..8dbf1a8c 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -225,6 +225,7 @@ class StatusPage extends BeanModel { customCSS: this.custom_css, footerText: this.footer_text, showPoweredBy: !!this.show_powered_by, + googleAnalyticsId: this.google_analytics_tag_id }; } @@ -245,6 +246,7 @@ class StatusPage extends BeanModel { customCSS: this.custom_css, footerText: this.footer_text, showPoweredBy: !!this.show_powered_by, + googleAnalyticsId: this.google_analytics_tag_id }; } diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js index 16d6ee73..717aba9c 100644 --- a/server/socket-handlers/status-page-socket-handler.js +++ b/server/socket-handlers/status-page-socket-handler.js @@ -163,6 +163,7 @@ module.exports.statusPageSocketHandler = (socket) => { statusPage.custom_css = config.customCSS; statusPage.show_powered_by = config.showPoweredBy; statusPage.modified_date = R.isoDateTime(); + statusPage.google_analytics_tag_id = config.googleAnalyticsId; await R.store(statusPage); diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 6cecf668..40a8e7c9 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -64,6 +64,12 @@ + +
+ + +
+
{{ $t("Custom CSS") }}
@@ -294,6 +300,15 @@ {{ config.customCSS }} + + + + + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '{{ config.googleAnalyticsId }}'); +
From 99c0b8cb710a41f9cb09c26a3caa497daf48e635 Mon Sep 17 00:00:00 2001 From: c Date: Tue, 10 Jan 2023 20:25:45 +0000 Subject: [PATCH 02/13] Feature - Google Analytics - Addressing PR Comments. --- server/database.js | 2 +- server/model/status_page.js | 11 +++++++++-- server/modules/google-analytics.js | 11 +++++++++++ src/languages/en.js | 0 src/pages/StatusPage.vue | 9 --------- 5 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 server/modules/google-analytics.js create mode 100644 src/languages/en.js diff --git a/server/database.js b/server/database.js index 52c701fb..19c09a00 100644 --- a/server/database.js +++ b/server/database.js @@ -69,7 +69,7 @@ class Database { "patch-ping-packet-size.sql": true, "patch-maintenance-table2.sql": true, "patch-add-gamedig-monitor.sql": true, - "patch-add-google-analytics-status-page-tag.sql": true + "patch-add-google-analytics-status-page-tag.sql": true, }; /** diff --git a/server/model/status_page.js b/server/model/status_page.js index 8dbf1a8c..2d90b639 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -4,6 +4,7 @@ const cheerio = require("cheerio"); const { UptimeKumaServer } = require("../uptime-kuma-server"); const jsesc = require("jsesc"); const Maintenance = require("./maintenance"); +const googleAnalytics = require("../modules/google-analytics"); class StatusPage extends BeanModel { @@ -53,6 +54,12 @@ class StatusPage extends BeanModel { const head = $("head"); + await StatusPage.getStatusPageData(statusPage).then( (page) => { + if (page.config?.googleAnalyticsId) { + head.append($(googleAnalytics.getGoogleAnalyticsScript(page.config.googleAnalyticsId))); + } + }); + // OG Meta Tags head.append(``); head.append(``); @@ -225,7 +232,7 @@ class StatusPage extends BeanModel { customCSS: this.custom_css, footerText: this.footer_text, showPoweredBy: !!this.show_powered_by, - googleAnalyticsId: this.google_analytics_tag_id + googleAnalyticsId: this.google_analytics_tag_id, }; } @@ -246,7 +253,7 @@ class StatusPage extends BeanModel { customCSS: this.custom_css, footerText: this.footer_text, showPoweredBy: !!this.show_powered_by, - googleAnalyticsId: this.google_analytics_tag_id + googleAnalyticsId: this.google_analytics_tag_id, }; } diff --git a/server/modules/google-analytics.js b/server/modules/google-analytics.js new file mode 100644 index 00000000..55820a85 --- /dev/null +++ b/server/modules/google-analytics.js @@ -0,0 +1,11 @@ +let GoogleAnalytics = (() => { + function getGoogleAnalyticsScript(tagId) { + return "" + + ""; + } + return { + getGoogleAnalyticsScript: getGoogleAnalyticsScript + }; +})(); + +module.exports = GoogleAnalytics; diff --git a/src/languages/en.js b/src/languages/en.js new file mode 100644 index 00000000..e69de29b diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 40a8e7c9..41aa7993 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -300,15 +300,6 @@ {{ config.customCSS }} - - - - - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - gtag('config', '{{ config.googleAnalyticsId }}'); - From 2b3a3895b399c606e65aca83e5ebc4376ba86b02 Mon Sep 17 00:00:00 2001 From: c Date: Wed, 11 Jan 2023 21:44:31 +0000 Subject: [PATCH 03/13] Feature - Google Analytics - Use Regex to validate UA as per https://support.google.com/analytics/answer/9310895 --- server/modules/google-analytics.js | 7 ++++++- server/socket-handlers/status-page-socket-handler.js | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/server/modules/google-analytics.js b/server/modules/google-analytics.js index 55820a85..0a40076d 100644 --- a/server/modules/google-analytics.js +++ b/server/modules/google-analytics.js @@ -3,8 +3,13 @@ let GoogleAnalytics = (() => { return "" + ""; } + function isValidTag(tagInput) { + const re = /^\w{1,2}-\d{8}$/g; + return tagInput.match(re) != null; + } return { - getGoogleAnalyticsScript: getGoogleAnalyticsScript + getGoogleAnalyticsScript: getGoogleAnalyticsScript, + isValidTag: isValidTag }; })(); diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js index 717aba9c..359ed15b 100644 --- a/server/socket-handlers/status-page-socket-handler.js +++ b/server/socket-handlers/status-page-socket-handler.js @@ -7,6 +7,7 @@ const Database = require("../database"); const apicache = require("../modules/apicache"); const StatusPage = require("../model/status_page"); const { UptimeKumaServer } = require("../uptime-kuma-server"); +const googleAnalytics = require("../modules/google-analytics"); /** * Socket handlers for status page @@ -163,7 +164,7 @@ module.exports.statusPageSocketHandler = (socket) => { statusPage.custom_css = config.customCSS; statusPage.show_powered_by = config.showPoweredBy; statusPage.modified_date = R.isoDateTime(); - statusPage.google_analytics_tag_id = config.googleAnalyticsId; + statusPage.google_analytics_tag_id = googleAnalytics.isValidTag(config.googleAnalyticsId) ? config.googleAnalyticsId : ""; await R.store(statusPage); From fb2999706c74de3c891ad8cd554fd1d82d895586 Mon Sep 17 00:00:00 2001 From: c Date: Thu, 12 Jan 2023 00:02:11 +0000 Subject: [PATCH 04/13] Feature - Google Analytics - Added JSDoc to Google Analytics functions. --- server/modules/google-analytics.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/modules/google-analytics.js b/server/modules/google-analytics.js index 0a40076d..2c10e584 100644 --- a/server/modules/google-analytics.js +++ b/server/modules/google-analytics.js @@ -1,8 +1,22 @@ let GoogleAnalytics = (() => { + /** + * Returns a string that represents the javascript that is required to insert the Google Analytics scripts + * into a webpage. + * @param tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script. + * @returns {string} + */ function getGoogleAnalyticsScript(tagId) { return "" + ""; } + + /** + * Returns true if the tag conforms to the format of 1-2 Letters followed by a dash and 8 numbers. + * This should take care of the following property tag formats: + * UA-########, G-########, AW-########, DC-######## + * @param {String} tagInput Google UA/G/AW/DC Property ID + * @returns {boolean} + */ function isValidTag(tagInput) { const re = /^\w{1,2}-\d{8}$/g; return tagInput.match(re) != null; From 3ff0cbe3116185afb316e3b14db33e701c52765b Mon Sep 17 00:00:00 2001 From: c Date: Thu, 12 Jan 2023 13:17:26 +0000 Subject: [PATCH 05/13] Feature - Google Analytics - Simplified Module & Escaped the Script to prevent XXS. --- server/model/status_page.js | 5 ++- server/modules/google-analytics.js | 53 ++++++++++++++---------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/server/model/status_page.js b/server/model/status_page.js index 2d90b639..a65a7da1 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -56,7 +56,10 @@ class StatusPage extends BeanModel { await StatusPage.getStatusPageData(statusPage).then( (page) => { if (page.config?.googleAnalyticsId) { - head.append($(googleAnalytics.getGoogleAnalyticsScript(page.config.googleAnalyticsId))); + let escapedGoogleAnalyticsScript = jsesc(googleAnalytics.getGoogleAnalyticsScript(page.config.googleAnalyticsId), { + "isScriptContext": true + }); + head.append($(escapedGoogleAnalyticsScript)); } }); diff --git a/server/modules/google-analytics.js b/server/modules/google-analytics.js index 2c10e584..4e0c95b2 100644 --- a/server/modules/google-analytics.js +++ b/server/modules/google-analytics.js @@ -1,30 +1,27 @@ -let GoogleAnalytics = (() => { - /** - * Returns a string that represents the javascript that is required to insert the Google Analytics scripts - * into a webpage. - * @param tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script. - * @returns {string} - */ - function getGoogleAnalyticsScript(tagId) { - return "" + - ""; - } +/** + * Returns true if the tag conforms to the format of 1-2 Letters followed by a dash and 8 numbers. + * This should take care of the following property tag formats: + * UA-########, G-########, AW-########, DC-######## + * @param {String} tagInput Google UA/G/AW/DC Property ID + * @returns {boolean} + */ +function isValidTag(tagInput) { + const re = /^\w{1,2}-\d{8}$/g; + return tagInput.match(re) != null; +} - /** - * Returns true if the tag conforms to the format of 1-2 Letters followed by a dash and 8 numbers. - * This should take care of the following property tag formats: - * UA-########, G-########, AW-########, DC-######## - * @param {String} tagInput Google UA/G/AW/DC Property ID - * @returns {boolean} - */ - function isValidTag(tagInput) { - const re = /^\w{1,2}-\d{8}$/g; - return tagInput.match(re) != null; - } - return { - getGoogleAnalyticsScript: getGoogleAnalyticsScript, - isValidTag: isValidTag - }; -})(); +/** + * Returns a string that represents the javascript that is required to insert the Google Analytics scripts + * into a webpage. + * @param tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script. + * @returns {string} + */ +function getGoogleAnalyticsScript(tagId) { + return "" + + ""; +} -module.exports = GoogleAnalytics; +module.exports = { + getGoogleAnalyticsScript, + isValidTag, +}; From c08d8a5eaffc5b707ddb9a59d1125c44cb425c53 Mon Sep 17 00:00:00 2001 From: c Date: Sun, 22 Jan 2023 16:59:09 +0000 Subject: [PATCH 06/13] Google Analytics - Simplified retrieving Tag ID from Status Page. --- server/model/status_page.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/server/model/status_page.js b/server/model/status_page.js index a65a7da1..5b1c1240 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -54,14 +54,12 @@ class StatusPage extends BeanModel { const head = $("head"); - await StatusPage.getStatusPageData(statusPage).then( (page) => { - if (page.config?.googleAnalyticsId) { - let escapedGoogleAnalyticsScript = jsesc(googleAnalytics.getGoogleAnalyticsScript(page.config.googleAnalyticsId), { - "isScriptContext": true - }); - head.append($(escapedGoogleAnalyticsScript)); - } - }); + if (statusPage.googleAnalyticsTagId) { + let escapedGoogleAnalyticsScript = jsesc(googleAnalytics.getGoogleAnalyticsScript(statusPage.googleAnalyticsTagId), { + "isScriptContext": true + }); + head.append($(escapedGoogleAnalyticsScript)); + } // OG Meta Tags head.append(``); From 5a94a3fe3c27d4b51469a71bab990d7232b70a3c Mon Sep 17 00:00:00 2001 From: c Date: Tue, 24 Jan 2023 14:09:56 +0000 Subject: [PATCH 07/13] Google Analytics - Moved string to updated file. --- src/lang/en.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lang/en.json b/src/lang/en.json index 231fe29f..df8288d2 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -690,5 +690,6 @@ "onebotSafetyTips": "For safety, must set access token", "PushDeer Key": "PushDeer Key", "wayToGetClickSendSMSToken": "You can get API Username and API Key from {0} .", - "Custom Monitor Type": "Custom Monitor Type" + "Custom Monitor Type": "Custom Monitor Type", + "Google Analytics ID": "Google Analytics ID" } From 3afe8013ca330545d194a8ccbbcb86721b4c2b67 Mon Sep 17 00:00:00 2001 From: c Date: Tue, 31 Jan 2023 13:17:43 +0000 Subject: [PATCH 08/13] Feature - Google Analytics - Change TEXT type to VARCHAR. --- db/patch-add-google-analytics-status-page-tag.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/patch-add-google-analytics-status-page-tag.sql b/db/patch-add-google-analytics-status-page-tag.sql index 15305ae2..5de6ff37 100644 --- a/db/patch-add-google-analytics-status-page-tag.sql +++ b/db/patch-add-google-analytics-status-page-tag.sql @@ -1,4 +1,4 @@ -- You should not modify if this have pushed to Github, unless it does serious wrong with the db. BEGIN TRANSACTION; -ALTER TABLE status_page ADD google_analytics_tag_id TEXT; +ALTER TABLE status_page ADD google_analytics_tag_id VARCHAR; COMMIT; From 913bb611d589f70778ee80f6f0521bdb243e4652 Mon Sep 17 00:00:00 2001 From: c Date: Tue, 31 Jan 2023 13:18:02 +0000 Subject: [PATCH 09/13] Feature - Google Analytics - Removed regex to validate a Google Analytics tag. --- server/modules/google-analytics.js | 13 ------------- .../socket-handlers/status-page-socket-handler.js | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/server/modules/google-analytics.js b/server/modules/google-analytics.js index 4e0c95b2..8b909b42 100644 --- a/server/modules/google-analytics.js +++ b/server/modules/google-analytics.js @@ -1,15 +1,3 @@ -/** - * Returns true if the tag conforms to the format of 1-2 Letters followed by a dash and 8 numbers. - * This should take care of the following property tag formats: - * UA-########, G-########, AW-########, DC-######## - * @param {String} tagInput Google UA/G/AW/DC Property ID - * @returns {boolean} - */ -function isValidTag(tagInput) { - const re = /^\w{1,2}-\d{8}$/g; - return tagInput.match(re) != null; -} - /** * Returns a string that represents the javascript that is required to insert the Google Analytics scripts * into a webpage. @@ -23,5 +11,4 @@ function getGoogleAnalyticsScript(tagId) { module.exports = { getGoogleAnalyticsScript, - isValidTag, }; diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js index 359ed15b..c973d7ed 100644 --- a/server/socket-handlers/status-page-socket-handler.js +++ b/server/socket-handlers/status-page-socket-handler.js @@ -164,7 +164,7 @@ module.exports.statusPageSocketHandler = (socket) => { statusPage.custom_css = config.customCSS; statusPage.show_powered_by = config.showPoweredBy; statusPage.modified_date = R.isoDateTime(); - statusPage.google_analytics_tag_id = googleAnalytics.isValidTag(config.googleAnalyticsId) ? config.googleAnalyticsId : ""; + statusPage.google_analytics_tag_id = config.googleAnalyticsId; await R.store(statusPage); From f153082184bc7f220d23ae93acd38f123b028d32 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 31 Jan 2023 14:23:40 +0800 Subject: [PATCH 10/13] Drop en.js --- src/languages/en.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/languages/en.js diff --git a/src/languages/en.js b/src/languages/en.js deleted file mode 100644 index e69de29b..00000000 From a823ed8ccc4d85230a3dd90e6f78fc4b9259c8fe Mon Sep 17 00:00:00 2001 From: c Date: Fri, 3 Feb 2023 11:49:25 +0000 Subject: [PATCH 11/13] Feature - Google Analytics - Removed unused import. --- server/socket-handlers/status-page-socket-handler.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js index c973d7ed..717aba9c 100644 --- a/server/socket-handlers/status-page-socket-handler.js +++ b/server/socket-handlers/status-page-socket-handler.js @@ -7,7 +7,6 @@ const Database = require("../database"); const apicache = require("../modules/apicache"); const StatusPage = require("../model/status_page"); const { UptimeKumaServer } = require("../uptime-kuma-server"); -const googleAnalytics = require("../modules/google-analytics"); /** * Socket handlers for status page From 5f2affb38ccace2c41d637b2ce587b770270204a Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 4 Feb 2023 16:58:39 +0800 Subject: [PATCH 12/13] Relocate and fix jsesc issue --- server/google-analytics.js | 19 +++++++++++++++++++ server/model/status_page.js | 6 ++---- server/modules/google-analytics.js | 14 -------------- 3 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 server/google-analytics.js delete mode 100644 server/modules/google-analytics.js diff --git a/server/google-analytics.js b/server/google-analytics.js new file mode 100644 index 00000000..b98ca862 --- /dev/null +++ b/server/google-analytics.js @@ -0,0 +1,19 @@ +const jsesc = require("jsesc"); + +/** + * Returns a string that represents the javascript that is required to insert the Google Analytics scripts + * into a webpage. + * @param tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script. + * @returns {string} + */ +function getGoogleAnalyticsScript(tagId) { + let escapedTagId = jsesc(tagId, { isScriptContext: true }); + return ` + + + `; +} + +module.exports = { + getGoogleAnalyticsScript, +}; diff --git a/server/model/status_page.js b/server/model/status_page.js index 5b1c1240..d7185a2e 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -4,7 +4,7 @@ const cheerio = require("cheerio"); const { UptimeKumaServer } = require("../uptime-kuma-server"); const jsesc = require("jsesc"); const Maintenance = require("./maintenance"); -const googleAnalytics = require("../modules/google-analytics"); +const googleAnalytics = require("../google-analytics"); class StatusPage extends BeanModel { @@ -55,9 +55,7 @@ class StatusPage extends BeanModel { const head = $("head"); if (statusPage.googleAnalyticsTagId) { - let escapedGoogleAnalyticsScript = jsesc(googleAnalytics.getGoogleAnalyticsScript(statusPage.googleAnalyticsTagId), { - "isScriptContext": true - }); + let escapedGoogleAnalyticsScript = googleAnalytics.getGoogleAnalyticsScript(statusPage.googleAnalyticsTagId); head.append($(escapedGoogleAnalyticsScript)); } diff --git a/server/modules/google-analytics.js b/server/modules/google-analytics.js deleted file mode 100644 index 8b909b42..00000000 --- a/server/modules/google-analytics.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Returns a string that represents the javascript that is required to insert the Google Analytics scripts - * into a webpage. - * @param tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script. - * @returns {string} - */ -function getGoogleAnalyticsScript(tagId) { - return "" + - ""; -} - -module.exports = { - getGoogleAnalyticsScript, -}; From afadfe32d5b2a43940bf5b22e5985b206d4a808b Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 4 Feb 2023 17:03:00 +0800 Subject: [PATCH 13/13] Trim --- server/google-analytics.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/google-analytics.js b/server/google-analytics.js index b98ca862..fc9fbec8 100644 --- a/server/google-analytics.js +++ b/server/google-analytics.js @@ -8,6 +8,11 @@ const jsesc = require("jsesc"); */ function getGoogleAnalyticsScript(tagId) { let escapedTagId = jsesc(tagId, { isScriptContext: true }); + + if (escapedTagId) { + escapedTagId = escapedTagId.trim(); + } + return `