mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-05-19 21:52:35 +02:00
Add a public URL field for monitors and uses it on the status page (#5435)
Some checks are pending
Auto Test / auto-test (18, ARM64) (push) Blocked by required conditions
Auto Test / auto-test (18, macos-latest) (push) Blocked by required conditions
Auto Test / auto-test (18, ubuntu-latest) (push) Blocked by required conditions
Auto Test / auto-test (18, windows-latest) (push) Blocked by required conditions
Auto Test / auto-test (20, ARM64) (push) Blocked by required conditions
Auto Test / auto-test (20, macos-latest) (push) Blocked by required conditions
Auto Test / auto-test (20, ubuntu-latest) (push) Blocked by required conditions
Auto Test / auto-test (20, windows-latest) (push) Blocked by required conditions
Auto Test / armv7-simple-test (18, ARMv7) (push) Waiting to run
Auto Test / armv7-simple-test (20, ARMv7) (push) Waiting to run
Auto Test / check-linters (push) Waiting to run
Auto Test / e2e-test (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Merge Conflict Labeler / Labeling (push) Waiting to run
validate / json-yaml-validate (push) Waiting to run
validate / validate (push) Waiting to run
Some checks are pending
Auto Test / auto-test (18, ARM64) (push) Blocked by required conditions
Auto Test / auto-test (18, macos-latest) (push) Blocked by required conditions
Auto Test / auto-test (18, ubuntu-latest) (push) Blocked by required conditions
Auto Test / auto-test (18, windows-latest) (push) Blocked by required conditions
Auto Test / auto-test (20, ARM64) (push) Blocked by required conditions
Auto Test / auto-test (20, macos-latest) (push) Blocked by required conditions
Auto Test / auto-test (20, ubuntu-latest) (push) Blocked by required conditions
Auto Test / auto-test (20, windows-latest) (push) Blocked by required conditions
Auto Test / armv7-simple-test (18, ARMv7) (push) Waiting to run
Auto Test / armv7-simple-test (20, ARMv7) (push) Waiting to run
Auto Test / check-linters (push) Waiting to run
Auto Test / e2e-test (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Merge Conflict Labeler / Labeling (push) Waiting to run
validate / json-yaml-validate (push) Waiting to run
validate / validate (push) Waiting to run
Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com>
This commit is contained in:
parent
86b3ef9c86
commit
2b3f49a266
8 changed files with 58 additions and 5 deletions
13
db/knex_migrations/2025-05-09-0000-add-custom-url.js
Normal file
13
db/knex_migrations/2025-05-09-0000-add-custom-url.js
Normal file
|
@ -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");
|
||||||
|
});
|
||||||
|
};
|
|
@ -33,7 +33,7 @@ class Group extends BeanModel {
|
||||||
*/
|
*/
|
||||||
async getMonitorList() {
|
async getMonitorList() {
|
||||||
return R.convertToBeans("monitor", await R.getAll(`
|
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
|
WHERE monitor.id = monitor_group.monitor_id
|
||||||
AND group_id = ?
|
AND group_id = ?
|
||||||
ORDER BY monitor_group.weight
|
ORDER BY monitor_group.weight
|
||||||
|
|
|
@ -53,7 +53,7 @@ class Monitor extends BeanModel {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.sendUrl) {
|
if (this.sendUrl) {
|
||||||
obj.url = this.url;
|
obj.url = this.customUrl ?? this.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showTags) {
|
if (showTags) {
|
||||||
|
|
|
@ -211,6 +211,10 @@ module.exports.statusPageSocketHandler = (socket) => {
|
||||||
relationBean.send_url = monitor.sendUrl;
|
relationBean.send_url = monitor.sendUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (monitor.url !== undefined) {
|
||||||
|
relationBean.custom_url = monitor.url;
|
||||||
|
}
|
||||||
|
|
||||||
await R.store(relationBean);
|
await R.store(relationBean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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" data-testid="show-clickable-link" @click="toggleLink(monitor.group_index, monitor.monitor_index)" />
|
||||||
<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>
|
||||||
|
@ -19,6 +19,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Custom URL -->
|
||||||
|
<template v-if="monitor.isClickAble">
|
||||||
|
<label for="customUrl" class="form-label">{{ $t("Custom URL") }}</label>
|
||||||
|
<input id="customUrl" :value="monitor.url" type="url" class="form-control" data-testid="custom-url-input" @input="e => changeUrl(monitor.group_index, monitor.monitor_index, e.target!.value)">
|
||||||
|
|
||||||
|
<div class="form-text mb-3">
|
||||||
|
{{ $t("customUrlDescription") }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary btn-add-group me-2"
|
class="btn btn-primary btn-add-group me-2"
|
||||||
@click="$refs.badgeGeneratorDialog.show(monitor.id, monitor.name)"
|
@click="$refs.badgeGeneratorDialog.show(monitor.id, monitor.name)"
|
||||||
|
@ -29,7 +39,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>
|
||||||
|
@ -78,6 +88,7 @@ export default {
|
||||||
monitor_index: monitor.index,
|
monitor_index: monitor.index,
|
||||||
group_index: group.index,
|
group_index: group.index,
|
||||||
isClickAble: this.showLink(monitor),
|
isClickAble: this.showLink(monitor),
|
||||||
|
url: monitor.element.url,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.MonitorSettingDialog.show();
|
this.MonitorSettingDialog.show();
|
||||||
|
@ -110,6 +121,17 @@ export default {
|
||||||
}
|
}
|
||||||
return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://" && !this.editMode;
|
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;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
v-if="editMode"
|
v-if="editMode"
|
||||||
: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"
|
||||||
|
data-testid="monitor-settings"
|
||||||
@click="$refs.monitorSettingDialog.show(group, monitor)"
|
@click="$refs.monitorSettingDialog.show(group, monitor)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -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}.",
|
"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",
|
||||||
|
"Custom URL": "Custom URL",
|
||||||
|
"customUrlDescription": "Will be used as the clickable URL instead of the monitor's one.",
|
||||||
"OneChatAccessToken": "OneChat Access Token",
|
"OneChatAccessToken": "OneChat Access Token",
|
||||||
"OneChatUserIdOrGroupId": "OneChat User ID or Group ID",
|
"OneChatUserIdOrGroupId": "OneChat User ID or Group ID",
|
||||||
"OneChatBotId": "OneChat Bot ID",
|
"OneChatBotId": "OneChat Bot ID",
|
||||||
|
|
|
@ -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 monitorCustomUrl = "https://www.example.com";
|
||||||
|
|
||||||
// Status Page
|
// Status Page
|
||||||
const footerText = "This is footer text.";
|
const footerText = "This is footer text.";
|
||||||
|
@ -30,7 +32,7 @@ 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);
|
||||||
|
@ -79,6 +81,13 @@ 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("custom-url-input").fill(monitorCustomUrl);
|
||||||
|
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", monitorCustomUrl);
|
||||||
|
|
||||||
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
|
||||||
|
|
Loading…
Add table
Reference in a new issue