diff --git a/db/knex_migrations/2024-12-12-2255-add-public-url.js b/db/knex_migrations/2024-12-12-2255-add-public-url.js new file mode 100644 index 000000000..0cd97240d --- /dev/null +++ b/db/knex_migrations/2024-12-12-2255-add-public-url.js @@ -0,0 +1,13 @@ +// Add column publicUrl to monitor table +exports.up = function (knex) { + return knex.schema + .alterTable("monitor", function (table) { + table.text("publicUrl", "text"); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("publicUrl"); + }); +}; diff --git a/server/model/monitor.js b/server/model/monitor.js index 3ad8cfafc..cb82bb85c 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.publicUrl ?? this.url; // Use publicUrl if available, otherwise use url } if (showTags) { @@ -91,6 +91,7 @@ class Monitor extends BeanModel { id: this.id, name: this.name, description: this.description, + publicUrl: this.publicUrl, path, pathName, parent: this.parent, diff --git a/server/server.js b/server/server.js index ec5ad49f6..15f062a6f 100644 --- a/server/server.js +++ b/server/server.js @@ -790,6 +790,7 @@ let needSetup = false; bean.parent = monitor.parent; bean.type = monitor.type; bean.url = monitor.url; + bean.publicUrl = monitor.publicUrl; bean.method = monitor.method; bean.body = monitor.body; bean.headers = monitor.headers; diff --git a/src/components/MonitorSettingDialog.vue b/src/components/MonitorSettingDialog.vue index e6b2cd1ef..d0ff80b00 100644 --- a/src/components/MonitorSettingDialog.vue +++ b/src/components/MonitorSettingDialog.vue @@ -10,7 +10,7 @@ diff --git a/src/lang/en.json b/src/lang/en.json index e215f1031..b0f801db7 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -1051,5 +1051,7 @@ "RabbitMQ Password": "RabbitMQ Password", "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" + "Separate multiple email addresses with commas": "Separate multiple email addresses with commas", + "Public URL": "Public URL", + "publicUrlDescription": "The public URL of the monitor. Can be displayed on the status page as the monitor link." } diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index a83f91cab..d656c95ac 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -719,6 +719,16 @@ + +
+ + + +
+ {{ $t("publicUrlDescription") }} +
+
+
@@ -1727,6 +1737,10 @@ message HealthCheckResponse { this.monitor.url = this.monitor.url.trim(); } + if (this.monitor.publicUrl) { + this.monitor.publicUrl = this.monitor.publicUrl.trim(); + } + let createdNewParent = false; if (this.draftGroupName && this.monitor.parent === -1) { diff --git a/test/e2e/specs/status-page.spec.js b/test/e2e/specs/status-page.spec.js index f525dfc6f..18aadba52 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 monitorPublicUrl = "https://www.example.com"; // Status Page const footerText = "This is footer text."; @@ -30,13 +32,14 @@ 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); await page.getByTestId("tag-color-select").click(); // Vue-Multiselect component await page.getByTestId("tag-color-select").getByRole("option", { name: "Orange" }).click(); await page.getByTestId("tag-submit-button").click(); + await page.getByTestId("public-url-input").fill(monitorPublicUrl); await page.getByTestId("save-button").click(); await page.waitForURL("/dashboard/*"); // wait for the monitor to be created @@ -79,6 +82,12 @@ 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("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", monitorPublicUrl); + 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