From 2b3f49a26653495964248b91a6631486f13abe29 Mon Sep 17 00:00:00 2001 From: Ionys <9364594+Ionys320@users.noreply.github.com> Date: Sat, 10 May 2025 19:05:37 +0200 Subject: [PATCH] Add a public URL field for monitors and uses it on the status page (#5435) Co-authored-by: Adam Stachowicz --- .../2025-05-09-0000-add-custom-url.js | 13 ++++++++++ server/model/group.js | 2 +- server/model/monitor.js | 2 +- .../status-page-socket-handler.js | 4 +++ src/components/MonitorSettingDialog.vue | 26 +++++++++++++++++-- src/components/PublicGroupList.vue | 1 + src/lang/en.json | 2 ++ test/e2e/specs/status-page.spec.js | 13 +++++++++- 8 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 db/knex_migrations/2025-05-09-0000-add-custom-url.js diff --git a/db/knex_migrations/2025-05-09-0000-add-custom-url.js b/db/knex_migrations/2025-05-09-0000-add-custom-url.js new file mode 100644 index 000000000..b3465c87f --- /dev/null +++ b/db/knex_migrations/2025-05-09-0000-add-custom-url.js @@ -0,0 +1,13 @@ +// Add column custom_url to monitor_group table +exports.up = function (knex) { + return knex.schema + .alterTable("monitor_group", function (table) { + table.text("custom_url", "text"); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor_group", function (table) { + table.dropColumn("custom_url"); + }); +}; diff --git a/server/model/group.js b/server/model/group.js index bd2c30189..16c482759 100644 --- a/server/model/group.js +++ b/server/model/group.js @@ -33,7 +33,7 @@ class Group extends BeanModel { */ async getMonitorList() { return R.convertToBeans("monitor", await R.getAll(` - SELECT monitor.*, monitor_group.send_url FROM monitor, monitor_group + SELECT monitor.*, monitor_group.send_url, monitor_group.custom_url FROM monitor, monitor_group WHERE monitor.id = monitor_group.monitor_id AND group_id = ? ORDER BY monitor_group.weight diff --git a/server/model/monitor.js b/server/model/monitor.js index 58decbcba..85293bbdc 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -53,7 +53,7 @@ class Monitor extends BeanModel { }; if (this.sendUrl) { - obj.url = this.url; + obj.url = this.customUrl ?? this.url; } if (showTags) { diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js index 1114d81fd..952ec2fa7 100644 --- a/server/socket-handlers/status-page-socket-handler.js +++ b/server/socket-handlers/status-page-socket-handler.js @@ -211,6 +211,10 @@ module.exports.statusPageSocketHandler = (socket) => { relationBean.send_url = monitor.sendUrl; } + if (monitor.url !== undefined) { + relationBean.custom_url = monitor.url; + } + await R.store(relationBean); } diff --git a/src/components/MonitorSettingDialog.vue b/src/components/MonitorSettingDialog.vue index e6b2cd1ef..8723c4862 100644 --- a/src/components/MonitorSettingDialog.vue +++ b/src/components/MonitorSettingDialog.vue @@ -10,7 +10,7 @@ + + + @@ -78,6 +88,7 @@ export default { monitor_index: monitor.index, group_index: group.index, isClickAble: this.showLink(monitor), + url: monitor.element.url, }; this.MonitorSettingDialog.show(); @@ -110,6 +121,17 @@ export default { } return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://" && !this.editMode; }, + + /** + * Toggle the value of sendUrl + * @param {number} groupIndex Index of group monitor is member of + * @param {number} index Index of monitor within group + * @param {string} value The new value of the url + * @returns {void} + */ + changeUrl(groupIndex, index, value) { + this.$root.publicGroupList[groupIndex].monitorList[index].url = value; + }, }, }; diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue index 38aca2957..cb97ecdcd 100644 --- a/src/components/PublicGroupList.vue +++ b/src/components/PublicGroupList.vue @@ -58,6 +58,7 @@ v-if="editMode" :class="{'link-active': true, 'btn-link': true}" icon="cog" class="action me-3" + data-testid="monitor-settings" @click="$refs.monitorSettingDialog.show(group, monitor)" /> diff --git a/src/lang/en.json b/src/lang/en.json index 23832f48d..42ec5d9f6 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -1074,6 +1074,8 @@ "rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.", "SendGrid API Key": "SendGrid API Key", "Separate multiple email addresses with commas": "Separate multiple email addresses with commas", + "Custom URL": "Custom URL", + "customUrlDescription": "Will be used as the clickable URL instead of the monitor's one.", "OneChatAccessToken": "OneChat Access Token", "OneChatUserIdOrGroupId": "OneChat User ID or Group ID", "OneChatBotId": "OneChat Bot ID", diff --git a/test/e2e/specs/status-page.spec.js b/test/e2e/specs/status-page.spec.js index f525dfc6f..0231aa225 100644 --- a/test/e2e/specs/status-page.spec.js +++ b/test/e2e/specs/status-page.spec.js @@ -12,6 +12,8 @@ test.describe("Status Page", () => { const monitorName = "Monitor for Status Page"; const tagName = "Client"; const tagValue = "Acme Inc"; + const monitorUrl = "https://www.example.com/status"; + const monitorCustomUrl = "https://www.example.com"; // Status Page const footerText = "This is footer text."; @@ -30,7 +32,7 @@ test.describe("Status Page", () => { await expect(page.getByTestId("monitor-type-select")).toBeVisible(); await page.getByTestId("monitor-type-select").selectOption("http"); await page.getByTestId("friendly-name-input").fill(monitorName); - await page.getByTestId("url-input").fill("https://www.example.com/"); + await page.getByTestId("url-input").fill(monitorUrl); await page.getByTestId("add-tag-button").click(); await page.getByTestId("tag-name-input").fill(tagName); await page.getByTestId("tag-value-input").fill(tagValue); @@ -79,6 +81,13 @@ test.describe("Status Page", () => { await page.getByTestId("monitor-select").getByRole("option", { name: monitorName }).click(); await expect(page.getByTestId("monitor")).toHaveCount(1); await expect(page.getByTestId("monitor-name")).toContainText(monitorName); + await expect(page.getByTestId("monitor-name")).not.toHaveAttribute("href"); + + // Set public url on + await page.getByTestId("monitor-settings").click(); + await page.getByTestId("show-clickable-link").check(); + await page.getByTestId("custom-url-input").fill(monitorCustomUrl); + await page.getByTestId("monitor-settings-close").click(); // Save the changes await screenshot(testInfo, page); @@ -94,6 +103,8 @@ test.describe("Status Page", () => { await expect(page.getByTestId("footer-text")).toContainText(footerText); await expect(page.getByTestId("powered-by")).toHaveCount(0); + await expect(page.getByTestId("monitor-name")).toHaveAttribute("href", monitorCustomUrl); + await expect(page.getByTestId("update-countdown-text")).toContainText("00:"); const updateCountdown = Number((await page.getByTestId("update-countdown-text").textContent()).match(/(\d+):(\d+)/)[2]); expect(updateCountdown).toBeGreaterThanOrEqual(refreshInterval); // cant be certain when the timer will start, so ensure it's within expected range