feat(public URL): Add a public URL field and use it for the status page

This commit is contained in:
Ionys 2024-12-13 09:40:21 +01:00
parent efdffca06c
commit 2488e6402e
8 changed files with 48 additions and 5 deletions

View file

@ -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");
});
};

View file

@ -53,7 +53,7 @@ class Monitor extends BeanModel {
}; };
if (this.sendUrl) { if (this.sendUrl) {
obj.url = this.url; obj.url = this.publicUrl ?? this.url; // Use publicUrl if available, otherwise use url
} }
if (showTags) { if (showTags) {
@ -91,6 +91,7 @@ class Monitor extends BeanModel {
id: this.id, id: this.id,
name: this.name, name: this.name,
description: this.description, description: this.description,
publicUrl: this.publicUrl,
path, path,
pathName, pathName,
parent: this.parent, parent: this.parent,

View file

@ -790,6 +790,7 @@ let needSetup = false;
bean.parent = monitor.parent; bean.parent = monitor.parent;
bean.type = monitor.type; bean.type = monitor.type;
bean.url = monitor.url; bean.url = monitor.url;
bean.publicUrl = monitor.publicUrl;
bean.method = monitor.method; bean.method = monitor.method;
bean.body = monitor.body; bean.body = monitor.body;
bean.headers = monitor.headers; bean.headers = monitor.headers;

View file

@ -10,7 +10,7 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="my-3 form-check"> <div class="my-3 form-check">
<input id="show-clickable-link" v-model="monitor.isClickAble" class="form-check-input" type="checkbox" @click="toggleLink(monitor.group_index, monitor.monitor_index)" /> <input id="show-clickable-link" v-model="monitor.isClickAble" class="form-check-input" type="checkbox" @click="toggleLink(monitor.group_index, monitor.monitor_index)" data-testid="show-clickable-link" />
<label class="form-check-label" for="show-clickable-link"> <label class="form-check-label" for="show-clickable-link">
{{ $t("Show Clickable Link") }} {{ $t("Show Clickable Link") }}
</label> </label>
@ -29,7 +29,7 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-danger" data-bs-dismiss="modal"> <button type="submit" class="btn btn-danger" data-bs-dismiss="modal" data-testid="monitor-settings-close">
{{ $t("Close") }} {{ $t("Close") }}
</button> </button>
</div> </div>

View file

@ -59,6 +59,7 @@
:class="{'link-active': true, 'btn-link': true}" :class="{'link-active': true, 'btn-link': true}"
icon="cog" class="action me-3" icon="cog" class="action me-3"
@click="$refs.monitorSettingDialog.show(group, monitor)" @click="$refs.monitorSettingDialog.show(group, monitor)"
data-testid="monitor-setting"
/> />
</span> </span>
</div> </div>

View file

@ -1051,5 +1051,7 @@
"RabbitMQ Password": "RabbitMQ Password", "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}.", "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", "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."
} }

View file

@ -719,6 +719,16 @@
<input id="description" v-model="monitor.description" type="text" class="form-control"> <input id="description" v-model="monitor.description" type="text" class="form-control">
</div> </div>
<!-- Public URL -->
<div class="my-3">
<label for="publicUrl" class="form-label">{{ $t("Public URL") }}</label>
<input id="publicUrl" v-model="monitor.publicUrl" type="url" class="form-control" data-testid="public-url-input">
<div class="form-text">
{{ $t("publicUrlDescription") }}
</div>
</div>
<div class="my-3"> <div class="my-3">
<tags-manager ref="tagsManager" :pre-selected-tags="monitor.tags"></tags-manager> <tags-manager ref="tagsManager" :pre-selected-tags="monitor.tags"></tags-manager>
</div> </div>
@ -1727,6 +1737,10 @@ message HealthCheckResponse {
this.monitor.url = this.monitor.url.trim(); this.monitor.url = this.monitor.url.trim();
} }
if (this.monitor.publicUrl) {
this.monitor.publicUrl = this.monitor.publicUrl.trim();
}
let createdNewParent = false; let createdNewParent = false;
if (this.draftGroupName && this.monitor.parent === -1) { if (this.draftGroupName && this.monitor.parent === -1) {

View file

@ -12,6 +12,8 @@ test.describe("Status Page", () => {
const monitorName = "Monitor for Status Page"; const monitorName = "Monitor for Status Page";
const tagName = "Client"; const tagName = "Client";
const tagValue = "Acme Inc"; const tagValue = "Acme Inc";
const monitorUrl = "https://www.example.com/status";
const monitorPublicUrl = "https://www.example.com";
// Status Page // Status Page
const footerText = "This is footer text."; const footerText = "This is footer text.";
@ -30,13 +32,14 @@ test.describe("Status Page", () => {
await expect(page.getByTestId("monitor-type-select")).toBeVisible(); await expect(page.getByTestId("monitor-type-select")).toBeVisible();
await page.getByTestId("monitor-type-select").selectOption("http"); await page.getByTestId("monitor-type-select").selectOption("http");
await page.getByTestId("friendly-name-input").fill(monitorName); 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("add-tag-button").click();
await page.getByTestId("tag-name-input").fill(tagName); await page.getByTestId("tag-name-input").fill(tagName);
await page.getByTestId("tag-value-input").fill(tagValue); await page.getByTestId("tag-value-input").fill(tagValue);
await page.getByTestId("tag-color-select").click(); // Vue-Multiselect component 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-color-select").getByRole("option", { name: "Orange" }).click();
await page.getByTestId("tag-submit-button").click(); await page.getByTestId("tag-submit-button").click();
await page.getByTestId("public-url-input").fill(monitorPublicUrl);
await page.getByTestId("save-button").click(); await page.getByTestId("save-button").click();
await page.waitForURL("/dashboard/*"); // wait for the monitor to be created 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 page.getByTestId("monitor-select").getByRole("option", { name: monitorName }).click();
await expect(page.getByTestId("monitor")).toHaveCount(1); await expect(page.getByTestId("monitor")).toHaveCount(1);
await expect(page.getByTestId("monitor-name")).toContainText(monitorName); 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 // Save the changes
await screenshot(testInfo, page); await screenshot(testInfo, page);
@ -94,6 +103,8 @@ test.describe("Status Page", () => {
await expect(page.getByTestId("footer-text")).toContainText(footerText); await expect(page.getByTestId("footer-text")).toContainText(footerText);
await expect(page.getByTestId("powered-by")).toHaveCount(0); 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:"); await expect(page.getByTestId("update-countdown-text")).toContainText("00:");
const updateCountdown = Number((await page.getByTestId("update-countdown-text").textContent()).match(/(\d+):(\d+)/)[2]); 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 expect(updateCountdown).toBeGreaterThanOrEqual(refreshInterval); // cant be certain when the timer will start, so ensure it's within expected range