mirror of
https://github.com/louislam/uptime-kuma.git
synced 2026-03-03 00:37:01 +00:00
fix(domain-expiry): only enable domain expiry notification when TLD has RDAP support (#6884)
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
const { parse: parseTld } = require("tldts");
|
||||
const rdapDnsData = require("../../server/model/rdap-dns.json");
|
||||
|
||||
const TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD = {
|
||||
http: "url",
|
||||
keyword: "url",
|
||||
"json-query": "url",
|
||||
"real-browser": "url",
|
||||
"websocket-upgrade": "url",
|
||||
port: "hostname",
|
||||
ping: "hostname",
|
||||
"grpc-keyword": "grpc_url",
|
||||
dns: "hostname",
|
||||
smtp: "hostname",
|
||||
snmp: "hostname",
|
||||
gamedig: "hostname",
|
||||
steam: "hostname",
|
||||
mqtt: "hostname",
|
||||
radius: "hostname",
|
||||
"tailscale-ping": "hostname",
|
||||
"sip-options": "hostname",
|
||||
};
|
||||
|
||||
/**
|
||||
* Build set of root TLDs that have RDAP support
|
||||
* @returns {Set<string>} Set of supported root TLDs
|
||||
*/
|
||||
function getSupportedTlds() {
|
||||
const supported = new Set();
|
||||
const services = rdapDnsData["services"] ?? [];
|
||||
for (const [tlds] of services) {
|
||||
for (const tld of tlds) {
|
||||
supported.add(tld);
|
||||
}
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a target URL/hostname has RDAP support
|
||||
* @param {string} target URL or hostname
|
||||
* @param {Set<string>} supportedTlds Set of supported root TLDs
|
||||
* @returns {boolean} Whether the target's TLD has RDAP support
|
||||
*/
|
||||
function hasRdapSupport(target, supportedTlds) {
|
||||
if (!target || typeof target !== "string") {
|
||||
return false;
|
||||
}
|
||||
const tld = parseTld(target);
|
||||
if (!tld.publicSuffix || !tld.isIcann) {
|
||||
return false;
|
||||
}
|
||||
const rootTld = tld.publicSuffix.split(".").pop();
|
||||
return supportedTlds.has(rootTld);
|
||||
}
|
||||
|
||||
exports.up = async function (knex) {
|
||||
const supportedTlds = getSupportedTlds();
|
||||
|
||||
const monitors = await knex("monitor")
|
||||
.where("domain_expiry_notification", 1)
|
||||
.select("id", "type", "url", "hostname", "grpc_url");
|
||||
|
||||
const idsToDisable = [];
|
||||
for (const monitor of monitors) {
|
||||
const targetField = TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD[monitor.type];
|
||||
if (!targetField || !hasRdapSupport(monitor[targetField], supportedTlds)) {
|
||||
idsToDisable.push(monitor.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (idsToDisable.length > 0) {
|
||||
await knex("monitor").whereIn("id", idsToDisable).update("domain_expiry_notification", 0);
|
||||
}
|
||||
|
||||
await knex.schema.alterTable("monitor", function (table) {
|
||||
table.boolean("domain_expiry_notification").defaultTo(0).alter();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = async function (knex) {
|
||||
await knex.schema.alterTable("monitor", function (table) {
|
||||
table.boolean("domain_expiry_notification").defaultTo(1).alter();
|
||||
});
|
||||
};
|
||||
@@ -2761,7 +2761,7 @@ const monitorDefaults = {
|
||||
ignoreTls: false,
|
||||
upsideDown: false,
|
||||
expiryNotification: false,
|
||||
domainExpiryNotification: true,
|
||||
domainExpiryNotification: false,
|
||||
maxredirects: 10,
|
||||
accepted_statuscodes: ["200-299"],
|
||||
saveResponse: false,
|
||||
@@ -3199,7 +3199,11 @@ message HealthCheckResponse {
|
||||
|
||||
this.checkMonitorDebounce = setTimeout(() => {
|
||||
this.$root.getSocket().emit("checkMointor", data, (res) => {
|
||||
const wasSupported = this.hasDomain;
|
||||
this.hasDomain = !!res?.ok;
|
||||
if (this.hasDomain !== wasSupported) {
|
||||
this.monitor.domainExpiryNotification = this.hasDomain;
|
||||
}
|
||||
this.domainExpiryUnsupportedReason = res.msgi18n ? this.$t(res.msg, res.meta) : res.msg;
|
||||
});
|
||||
}, 500);
|
||||
|
||||
105
test/e2e/specs/domain-expiry-notification.spec.js
Normal file
105
test/e2e/specs/domain-expiry-notification.spec.js
Normal file
@@ -0,0 +1,105 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { login, restoreSqliteSnapshot, screenshot } from "../util-test";
|
||||
|
||||
test.describe("Domain Expiry Notification", () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await restoreSqliteSnapshot(page);
|
||||
});
|
||||
|
||||
test("supported TLD auto-enables checkbox", async ({ page }, testInfo) => {
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
const monitorTypeSelect = page.getByTestId("monitor-type-select");
|
||||
await monitorTypeSelect.selectOption("http");
|
||||
|
||||
await page.getByTestId("url-input").fill("https://example.com");
|
||||
|
||||
const checkbox = page.getByLabel("Domain Name Expiry Notification");
|
||||
await expect(checkbox).toBeChecked();
|
||||
await expect(checkbox).toBeEnabled();
|
||||
|
||||
await screenshot(testInfo, page);
|
||||
});
|
||||
|
||||
test("unsupported TLD leaves checkbox disabled", async ({ page }, testInfo) => {
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
const monitorTypeSelect = page.getByTestId("monitor-type-select");
|
||||
await monitorTypeSelect.selectOption("http");
|
||||
|
||||
await page.getByTestId("url-input").fill("https://example.co");
|
||||
|
||||
const checkbox = page.getByLabel("Domain Name Expiry Notification");
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
await expect(checkbox).toBeDisabled();
|
||||
|
||||
await screenshot(testInfo, page);
|
||||
});
|
||||
|
||||
test("switching from supported to unsupported TLD disables checkbox", async ({ page }, testInfo) => {
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
const monitorTypeSelect = page.getByTestId("monitor-type-select");
|
||||
await monitorTypeSelect.selectOption("http");
|
||||
|
||||
const urlInput = page.getByTestId("url-input");
|
||||
const checkbox = page.getByLabel("Domain Name Expiry Notification");
|
||||
|
||||
await urlInput.fill("https://example.com");
|
||||
await expect(checkbox).toBeChecked();
|
||||
|
||||
await urlInput.fill("https://example.co");
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
await expect(checkbox).toBeDisabled();
|
||||
|
||||
await screenshot(testInfo, page);
|
||||
});
|
||||
|
||||
test("switching from unsupported to supported TLD enables checkbox", async ({ page }, testInfo) => {
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
const monitorTypeSelect = page.getByTestId("monitor-type-select");
|
||||
await monitorTypeSelect.selectOption("http");
|
||||
|
||||
const urlInput = page.getByTestId("url-input");
|
||||
const checkbox = page.getByLabel("Domain Name Expiry Notification");
|
||||
|
||||
await urlInput.fill("https://example.co");
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
|
||||
await urlInput.fill("https://example.com");
|
||||
await expect(checkbox).toBeChecked();
|
||||
await expect(checkbox).toBeEnabled();
|
||||
|
||||
await screenshot(testInfo, page);
|
||||
});
|
||||
|
||||
test("manual uncheck preserved when URL changes within same TLD", async ({ page }, testInfo) => {
|
||||
await page.goto("./add");
|
||||
await login(page);
|
||||
|
||||
const monitorTypeSelect = page.getByTestId("monitor-type-select");
|
||||
await monitorTypeSelect.selectOption("http");
|
||||
|
||||
const urlInput = page.getByTestId("url-input");
|
||||
const checkbox = page.getByLabel("Domain Name Expiry Notification");
|
||||
|
||||
await urlInput.fill("https://example.com");
|
||||
await expect(checkbox).toBeChecked();
|
||||
|
||||
await checkbox.uncheck();
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
|
||||
await urlInput.fill("https://example.com/different-path");
|
||||
// Wait for debounce to fire and verify checkbox stays unchecked
|
||||
await page.waitForTimeout(600);
|
||||
await expect(checkbox).not.toBeChecked();
|
||||
await expect(checkbox).toBeEnabled();
|
||||
|
||||
await screenshot(testInfo, page);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user