mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-07-21 08:34:03 +02:00
Add support for DoT, DoH, and TCP DNS lookups
This commit is contained in:
parent
2b5f57f92d
commit
8d483a8f02
10 changed files with 213 additions and 46 deletions
|
@ -86,6 +86,8 @@ async function createTables() {
|
||||||
table.text("accepted_statuscodes_json").notNullable().defaultTo("[\"200-299\"]");
|
table.text("accepted_statuscodes_json").notNullable().defaultTo("[\"200-299\"]");
|
||||||
table.string("dns_resolve_type", 5);
|
table.string("dns_resolve_type", 5);
|
||||||
table.string("dns_resolve_server", 255);
|
table.string("dns_resolve_server", 255);
|
||||||
|
table.string("dns_transport", 3);
|
||||||
|
table.string("doh_query_path", 255);
|
||||||
table.string("dns_last_result", 255);
|
table.string("dns_last_result", 255);
|
||||||
table.integer("retry_interval").notNullable().defaultTo(0);
|
table.integer("retry_interval").notNullable().defaultTo(0);
|
||||||
table.string("push_token", 20).defaultTo(null);
|
table.string("push_token", 20).defaultTo(null);
|
||||||
|
|
14
db/knex_migrations/2025-22-02-0000-dns-trasnsport.js
Normal file
14
db/knex_migrations/2025-22-02-0000-dns-trasnsport.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
exports.up = function (knex) {
|
||||||
|
return knex.schema
|
||||||
|
.alterTable("monitor", function (table) {
|
||||||
|
table.string("dns_transport");
|
||||||
|
table.string("doh_query_path");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function (knex) {
|
||||||
|
return knex.schema.alterTable("monitor", function (table) {
|
||||||
|
table.dropColumn("dns_transport");
|
||||||
|
table.dropColumn("doh_query_path");
|
||||||
|
});
|
||||||
|
};
|
|
@ -85,6 +85,7 @@
|
||||||
"croner": "~8.1.0",
|
"croner": "~8.1.0",
|
||||||
"dayjs": "~1.11.5",
|
"dayjs": "~1.11.5",
|
||||||
"dev-null": "^0.1.1",
|
"dev-null": "^0.1.1",
|
||||||
|
"dns2": "song940/node-dns",
|
||||||
"dotenv": "~16.0.3",
|
"dotenv": "~16.0.3",
|
||||||
"express": "~4.21.0",
|
"express": "~4.21.0",
|
||||||
"express-basic-auth": "~1.2.1",
|
"express-basic-auth": "~1.2.1",
|
||||||
|
@ -98,6 +99,7 @@
|
||||||
"http-proxy-agent": "~7.0.2",
|
"http-proxy-agent": "~7.0.2",
|
||||||
"https-proxy-agent": "~7.0.6",
|
"https-proxy-agent": "~7.0.6",
|
||||||
"iconv-lite": "~0.6.3",
|
"iconv-lite": "~0.6.3",
|
||||||
|
"ip-address": "~10.0.1",
|
||||||
"isomorphic-ws": "^5.0.0",
|
"isomorphic-ws": "^5.0.0",
|
||||||
"jsesc": "~3.0.2",
|
"jsesc": "~3.0.2",
|
||||||
"jsonata": "^2.0.3",
|
"jsonata": "^2.0.3",
|
||||||
|
@ -168,7 +170,6 @@
|
||||||
"cronstrue": "~2.24.0",
|
"cronstrue": "~2.24.0",
|
||||||
"cross-env": "~7.0.3",
|
"cross-env": "~7.0.3",
|
||||||
"delay": "^5.0.0",
|
"delay": "^5.0.0",
|
||||||
"dns2": "~2.0.1",
|
|
||||||
"dompurify": "~3.1.7",
|
"dompurify": "~3.1.7",
|
||||||
"eslint": "~8.14.0",
|
"eslint": "~8.14.0",
|
||||||
"eslint-plugin-jsdoc": "~46.4.6",
|
"eslint-plugin-jsdoc": "~46.4.6",
|
||||||
|
|
|
@ -118,6 +118,8 @@ class Monitor extends BeanModel {
|
||||||
accepted_statuscodes: this.getAcceptedStatuscodes(),
|
accepted_statuscodes: this.getAcceptedStatuscodes(),
|
||||||
dns_resolve_type: this.dns_resolve_type,
|
dns_resolve_type: this.dns_resolve_type,
|
||||||
dns_resolve_server: this.dns_resolve_server,
|
dns_resolve_server: this.dns_resolve_server,
|
||||||
|
dns_transport: this.dns_transport,
|
||||||
|
doh_query_path: this.doh_query_path,
|
||||||
dns_last_result: this.dns_last_result,
|
dns_last_result: this.dns_last_result,
|
||||||
docker_container: this.docker_container,
|
docker_container: this.docker_container,
|
||||||
docker_host: this.docker_host,
|
docker_host: this.docker_host,
|
||||||
|
|
|
@ -24,50 +24,90 @@ class DnsMonitorType extends MonitorType {
|
||||||
let startTime = dayjs().valueOf();
|
let startTime = dayjs().valueOf();
|
||||||
let dnsMessage = "";
|
let dnsMessage = "";
|
||||||
|
|
||||||
let dnsRes = await dnsResolve(monitor.hostname, monitor.dns_resolve_server, monitor.port, monitor.dns_resolve_type);
|
let dnsRes = await dnsResolve(monitor.hostname, monitor.dns_resolve_server, monitor.port, monitor.dns_resolve_type, monitor.dns_transport, monitor.doh_query_path);
|
||||||
heartbeat.ping = dayjs().valueOf() - startTime;
|
heartbeat.ping = dayjs().valueOf() - startTime;
|
||||||
|
|
||||||
const conditions = ConditionExpressionGroup.fromMonitor(monitor);
|
const conditions = ConditionExpressionGroup.fromMonitor(monitor);
|
||||||
let conditionsResult = true;
|
let conditionsResult = true;
|
||||||
const handleConditions = (data) => conditions ? evaluateExpressionGroup(conditions, data) : true;
|
const handleConditions = (data) => conditions ? evaluateExpressionGroup(conditions, data) : true;
|
||||||
|
|
||||||
|
let records = [];
|
||||||
switch (monitor.dns_resolve_type) {
|
switch (monitor.dns_resolve_type) {
|
||||||
case "A":
|
case "A":
|
||||||
case "AAAA":
|
case "AAAA":
|
||||||
case "TXT":
|
records = dnsRes.answers.map(record => record.address);
|
||||||
|
dnsMessage = `Records: ${records.join(" | ")}`;
|
||||||
|
conditionsResult = records.some(record => handleConditions({ record }));
|
||||||
|
break;
|
||||||
|
|
||||||
case "PTR":
|
case "PTR":
|
||||||
dnsMessage = `Records: ${dnsRes.join(" | ")}`;
|
records = dnsRes.answers.map(record => record.domain);
|
||||||
conditionsResult = dnsRes.some(record => handleConditions({ record }));
|
dnsMessage = `Records: ${records.join(" | ")}`;
|
||||||
|
conditionsResult = records.some(record => handleConditions({ record }));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "TXT":
|
||||||
|
records = dnsRes.answers.map(record => record.data);
|
||||||
|
dnsMessage = `Records: ${records.join(" | ")}`;
|
||||||
|
conditionsResult = records.some(record => handleConditions({ record }));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "CNAME":
|
case "CNAME":
|
||||||
dnsMessage = dnsRes[0];
|
records.push(dnsRes.answers[0].domain);
|
||||||
conditionsResult = handleConditions({ record: dnsRes[0] });
|
dnsMessage = records[0];
|
||||||
|
conditionsResult = handleConditions({ record: records[0] });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "CAA":
|
case "CAA":
|
||||||
dnsMessage = dnsRes[0].issue;
|
// dns2 library currently has not implemented decoding CAA response
|
||||||
conditionsResult = handleConditions({ record: dnsRes[0].issue });
|
//records.push(dnsRes.answers[0].issue);
|
||||||
|
records.push("CAA issue placeholder");
|
||||||
|
dnsMessage = records[0];
|
||||||
|
conditionsResult = handleConditions({ record: records[0] });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "MX":
|
case "MX":
|
||||||
dnsMessage = dnsRes.map(record => `Hostname: ${record.exchange} - Priority: ${record.priority}`).join(" | ");
|
records = dnsRes.answers.map(record => record.exchange);
|
||||||
conditionsResult = dnsRes.some(record => handleConditions({ record: record.exchange }));
|
dnsMessage = dnsRes.answers.map(record => `Hostname: ${record.exchange} ; Priority: ${record.priority}`).join(" | ");
|
||||||
|
conditionsResult = records.some(record => handleConditions({ record }));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "NS":
|
case "NS":
|
||||||
dnsMessage = `Servers: ${dnsRes.join(" | ")}`;
|
records = dnsRes.answers.map(record => record.ns);
|
||||||
conditionsResult = dnsRes.some(record => handleConditions({ record }));
|
dnsMessage = `Servers: ${records.join(" | ")}`;
|
||||||
|
conditionsResult = records.some(record => handleConditions({ record }));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "SOA":
|
case "SOA": {
|
||||||
dnsMessage = `NS-Name: ${dnsRes.nsname} | Hostmaster: ${dnsRes.hostmaster} | Serial: ${dnsRes.serial} | Refresh: ${dnsRes.refresh} | Retry: ${dnsRes.retry} | Expire: ${dnsRes.expire} | MinTTL: ${dnsRes.minttl}`;
|
records.push(dnsRes.answers[0].primary);
|
||||||
conditionsResult = handleConditions({ record: dnsRes.nsname });
|
dnsMessage = Object.entries({
|
||||||
|
"Primary-NS": dnsRes.answers[0].primary,
|
||||||
|
"Hostmaster": dnsRes.answers[0].admin,
|
||||||
|
"Serial": dnsRes.answers[0].serial,
|
||||||
|
"Refresh": dnsRes.answers[0].refresh,
|
||||||
|
"Retry": dnsRes.answers[0].retry,
|
||||||
|
"Expire": dnsRes.answers[0].expiration,
|
||||||
|
"MinTTL": dnsRes.answers[0].minimum,
|
||||||
|
}).map(([name, value]) => {
|
||||||
|
return `${name}: ${value}`;
|
||||||
|
}).join("; ");
|
||||||
|
conditionsResult = handleConditions({ record: records[0] });
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "SRV":
|
case "SRV":
|
||||||
dnsMessage = dnsRes.map(record => `Name: ${record.name} | Port: ${record.port} | Priority: ${record.priority} | Weight: ${record.weight}`).join(" | ");
|
records = dnsRes.answers.map(record => record.target);
|
||||||
conditionsResult = dnsRes.some(record => handleConditions({ record: record.name }));
|
dnsMessage = dnsRes.answers.map((record) => {
|
||||||
|
return Object.entries({
|
||||||
|
"Target": record.target,
|
||||||
|
"Port": record.port,
|
||||||
|
"Priority": record.priority,
|
||||||
|
"Weight": record.weight,
|
||||||
|
}).map(([name, value]) => {
|
||||||
|
return `${name}: ${value}`;
|
||||||
|
}).join("; ");
|
||||||
|
}).join(" | ");
|
||||||
|
conditionsResult = records.some(record => handleConditions({ record }));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -826,6 +826,8 @@ let needSetup = false;
|
||||||
bean.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
|
bean.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
|
||||||
bean.dns_resolve_type = monitor.dns_resolve_type;
|
bean.dns_resolve_type = monitor.dns_resolve_type;
|
||||||
bean.dns_resolve_server = monitor.dns_resolve_server;
|
bean.dns_resolve_server = monitor.dns_resolve_server;
|
||||||
|
bean.dns_transport = monitor.dns_transport;
|
||||||
|
bean.doh_query_path = monitor.doh_query_path;
|
||||||
bean.pushToken = monitor.pushToken;
|
bean.pushToken = monitor.pushToken;
|
||||||
bean.docker_container = monitor.docker_container;
|
bean.docker_container = monitor.docker_container;
|
||||||
bean.docker_host = monitor.docker_host;
|
bean.docker_host = monitor.docker_host;
|
||||||
|
|
|
@ -3,7 +3,9 @@ const ping = require("@louislam/ping");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
const { log, genSecret, badgeConstants } = require("../src/util");
|
const { log, genSecret, badgeConstants } = require("../src/util");
|
||||||
const passwordHash = require("./password-hash");
|
const passwordHash = require("./password-hash");
|
||||||
const { Resolver } = require("dns");
|
const { UDPClient, TCPClient, DOHClient } = require("dns2");
|
||||||
|
const { isIP, isIPv4, isIPv6 } = require("node:net");
|
||||||
|
const { Address6 } = require("ip-address");
|
||||||
const iconv = require("iconv-lite");
|
const iconv = require("iconv-lite");
|
||||||
const chardet = require("chardet");
|
const chardet = require("chardet");
|
||||||
const chroma = require("chroma-js");
|
const chroma = require("chroma-js");
|
||||||
|
@ -286,33 +288,63 @@ exports.httpNtlm = function (options, ntlmOptions) {
|
||||||
* @param {string} resolverServer The DNS server to use
|
* @param {string} resolverServer The DNS server to use
|
||||||
* @param {string} resolverPort Port the DNS server is listening on
|
* @param {string} resolverPort Port the DNS server is listening on
|
||||||
* @param {string} rrtype The type of record to request
|
* @param {string} rrtype The type of record to request
|
||||||
|
* @param {string} transport The transport method to use
|
||||||
|
* @param {string} dohQuery The query path used only for DoH
|
||||||
* @returns {Promise<(string[] | object[] | object)>} DNS response
|
* @returns {Promise<(string[] | object[] | object)>} DNS response
|
||||||
*/
|
*/
|
||||||
exports.dnsResolve = function (hostname, resolverServer, resolverPort, rrtype) {
|
exports.dnsResolve = function (hostname, resolverServer, resolverPort, rrtype, transport, dohQuery) {
|
||||||
const resolver = new Resolver();
|
// Parse IPv4 and IPv6 addresses to determine address family and
|
||||||
// Remove brackets from IPv6 addresses so we can re-add them to
|
// add square brackets to IPv6 addresses, following RFC 3986 syntax
|
||||||
// prevent issues with ::1:5300 (::1 port 5300)
|
|
||||||
resolverServer = resolverServer.replace("[", "").replace("]", "");
|
resolverServer = resolverServer.replace("[", "").replace("]", "");
|
||||||
resolver.setServers([ `[${resolverServer}]:${resolverPort}` ]);
|
const addressFamily = isIP(resolverServer);
|
||||||
return new Promise((resolve, reject) => {
|
if (addressFamily === 6) {
|
||||||
|
resolverServer = `[${resolverServer}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If performing reverse (PTR) record lookup, ensure hostname
|
||||||
|
// syntax follows RFC 1034 / RFC 3596
|
||||||
if (rrtype === "PTR") {
|
if (rrtype === "PTR") {
|
||||||
resolver.reverse(hostname, (err, records) => {
|
if (isIPv4(hostname)) {
|
||||||
if (err) {
|
let octets = hostname.split(".");
|
||||||
reject(err);
|
octets.reverse();
|
||||||
} else {
|
hostname = octets.join(".") + ".in-addr.arpa";
|
||||||
resolve(records);
|
} else if (isIPv6(hostname)) {
|
||||||
|
let address = new Address6(hostname);
|
||||||
|
hostname = address.reverseForm();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transport method determines which client type to use
|
||||||
|
let resolver;
|
||||||
|
switch (transport.toUpperCase()) {
|
||||||
|
case "TCP":
|
||||||
|
resolver = TCPClient({
|
||||||
|
dns: resolverServer,
|
||||||
|
protocol: "tcp:",
|
||||||
|
port: resolverPort,
|
||||||
});
|
});
|
||||||
} else {
|
break;
|
||||||
resolver.resolve(hostname, rrtype, (err, records) => {
|
case "DOT":
|
||||||
if (err) {
|
resolver = TCPClient({
|
||||||
reject(err);
|
dns: resolverServer,
|
||||||
} else {
|
protocol: "tls:",
|
||||||
resolve(records);
|
port: resolverPort,
|
||||||
}
|
});
|
||||||
|
break;
|
||||||
|
case "DOH":
|
||||||
|
resolver = DOHClient({
|
||||||
|
dns: `https://${resolverServer}:${resolverPort}/${dohQuery}`,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
resolver = UDPClient({
|
||||||
|
dns: resolverServer,
|
||||||
|
port: resolverPort,
|
||||||
|
socketType: "udp" + String(addressFamily),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
return resolver(hostname, rrtype);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -571,9 +571,11 @@
|
||||||
"deleteMonitorMsg": "Are you sure want to delete this monitor?",
|
"deleteMonitorMsg": "Are you sure want to delete this monitor?",
|
||||||
"deleteMaintenanceMsg": "Are you sure want to delete this maintenance?",
|
"deleteMaintenanceMsg": "Are you sure want to delete this maintenance?",
|
||||||
"deleteNotificationMsg": "Are you sure want to delete this notification for all monitors?",
|
"deleteNotificationMsg": "Are you sure want to delete this notification for all monitors?",
|
||||||
"dnsPortDescription": "DNS server port. Defaults to 53. You can change the port at any time.",
|
"dnsPortDescription": "DNS server port. Defaults to 53. Alternative ports are 443 for DoH and 853 for DoT.",
|
||||||
"resolverserverDescription": "Cloudflare is the default server. You can change the resolver server anytime.",
|
"resolverserverDescription": "Cloudflare is the default server. You can change the resolver server anytime.",
|
||||||
"rrtypeDescription": "Select the RR type you want to monitor",
|
"rrtypeDescription": "Select the RR type you want to monitor",
|
||||||
|
"dnsTransportDescription": "Select the transport method for querying the DNS server.",
|
||||||
|
"dohQueryPathDescription": "Set the query path to use for DNS wireformat. Must contain \"{query}\".",
|
||||||
"pauseMonitorMsg": "Are you sure want to pause?",
|
"pauseMonitorMsg": "Are you sure want to pause?",
|
||||||
"enableDefaultNotificationDescription": "This notification will be enabled by default for new monitors. You can still disable the notification separately for each monitor.",
|
"enableDefaultNotificationDescription": "This notification will be enabled by default for new monitors. You can still disable the notification separately for each monitor.",
|
||||||
"clearEventsMsg": "Are you sure want to delete all events for this monitor?",
|
"clearEventsMsg": "Are you sure want to delete all events for this monitor?",
|
||||||
|
|
|
@ -367,12 +367,42 @@
|
||||||
<template v-if="monitor.type === 'dns'">
|
<template v-if="monitor.type === 'dns'">
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="dns_resolve_server" class="form-label">{{ $t("Resolver Server") }}</label>
|
<label for="dns_resolve_server" class="form-label">{{ $t("Resolver Server") }}</label>
|
||||||
<input id="dns_resolve_server" v-model="monitor.dns_resolve_server" type="text" class="form-control" :pattern="ipRegex" required>
|
<input id="dns_resolve_server" v-model="monitor.dns_resolve_server" type="text" class="form-control" :pattern="ipOrHostnameRegex" required>
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
{{ $t("resolverserverDescription") }}
|
{{ $t("resolverserverDescription") }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="doh_query_path" class="form-label">{{ $t("Query Path") }}</label>
|
||||||
|
<input id="doh_query_path" v-model="monitor.doh_query_path" type="text" class="form-control" :pattern="urlQueryRegex">
|
||||||
|
<div class="form-text">
|
||||||
|
{{ $t("dohQueryPathDescription") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="dns_transport" class="form-label">{{ $t("Transport Method") }}</label>
|
||||||
|
<!-- :allow-empty="false" is not working, set a default value instead https://github.com/shentao/vue-multiselect/issues/336 -->
|
||||||
|
<VueMultiselect
|
||||||
|
id="dns_transport"
|
||||||
|
v-model="monitor.dns_transport"
|
||||||
|
:options="dnsTransportOptions"
|
||||||
|
:multiple="false"
|
||||||
|
:close-on-select="true"
|
||||||
|
:clear-on-select="false"
|
||||||
|
:preserve-search="false"
|
||||||
|
:placeholder="$t('Select the transport method')"
|
||||||
|
:preselect-first="false"
|
||||||
|
:max-height="500"
|
||||||
|
:taggable="false"
|
||||||
|
data-testid="resolve-type-select"
|
||||||
|
></VueMultiselect>
|
||||||
|
<div class="form-text">
|
||||||
|
{{ $t("dnsTransportDescription") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Port -->
|
<!-- Port -->
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="port" class="form-label">{{ $t("Port") }}</label>
|
<label for="port" class="form-label">{{ $t("Port") }}</label>
|
||||||
|
@ -1061,7 +1091,7 @@ import RemoteBrowserDialog from "../components/RemoteBrowserDialog.vue";
|
||||||
import ProxyDialog from "../components/ProxyDialog.vue";
|
import ProxyDialog from "../components/ProxyDialog.vue";
|
||||||
import TagsManager from "../components/TagsManager.vue";
|
import TagsManager from "../components/TagsManager.vue";
|
||||||
import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, sleep } from "../util.ts";
|
import { genSecret, isDev, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, sleep } from "../util.ts";
|
||||||
import { hostNameRegexPattern } from "../util-frontend";
|
import { hostNameRegexPattern, urlPathRegexPattern } from "../util-frontend";
|
||||||
import HiddenInput from "../components/HiddenInput.vue";
|
import HiddenInput from "../components/HiddenInput.vue";
|
||||||
import EditMonitorConditions from "../components/EditMonitorConditions.vue";
|
import EditMonitorConditions from "../components/EditMonitorConditions.vue";
|
||||||
|
|
||||||
|
@ -1088,6 +1118,8 @@ const monitorDefaults = {
|
||||||
accepted_statuscodes: [ "200-299" ],
|
accepted_statuscodes: [ "200-299" ],
|
||||||
dns_resolve_type: "A",
|
dns_resolve_type: "A",
|
||||||
dns_resolve_server: "1.1.1.1",
|
dns_resolve_server: "1.1.1.1",
|
||||||
|
dns_transport: "UDP",
|
||||||
|
doh_query_path: "dns-query?dns={query}",
|
||||||
docker_container: "",
|
docker_container: "",
|
||||||
docker_host: null,
|
docker_host: null,
|
||||||
proxyId: null,
|
proxyId: null,
|
||||||
|
@ -1140,6 +1172,7 @@ export default {
|
||||||
},
|
},
|
||||||
acceptedStatusCodeOptions: [],
|
acceptedStatusCodeOptions: [],
|
||||||
dnsresolvetypeOptions: [],
|
dnsresolvetypeOptions: [],
|
||||||
|
dnsTransportOptions: [],
|
||||||
kafkaSaslMechanismOptions: [],
|
kafkaSaslMechanismOptions: [],
|
||||||
ipOrHostnameRegexPattern: hostNameRegexPattern(),
|
ipOrHostnameRegexPattern: hostNameRegexPattern(),
|
||||||
mqttIpOrHostnameRegexPattern: hostNameRegexPattern(true),
|
mqttIpOrHostnameRegexPattern: hostNameRegexPattern(true),
|
||||||
|
@ -1166,6 +1199,24 @@ export default {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ipOrHostnameRegex() {
|
||||||
|
|
||||||
|
// Permit either IP address or hostname (127.0.0.1, dns.example.com)
|
||||||
|
if (! isDev) {
|
||||||
|
return this.ipOrHostnameRegexPattern;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
urlQueryRegex() {
|
||||||
|
|
||||||
|
// Permit only URL paths with a query parameter ( {query} )
|
||||||
|
if (! isDev) {
|
||||||
|
return this.queryRegexPattern;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
pageName() {
|
pageName() {
|
||||||
let name = "Add New Monitor";
|
let name = "Add New Monitor";
|
||||||
if (this.isClone) {
|
if (this.isClone) {
|
||||||
|
@ -1539,6 +1590,13 @@ message HealthCheckResponse {
|
||||||
"TXT",
|
"TXT",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let dnsTransportOptions = [
|
||||||
|
"UDP",
|
||||||
|
"TCP",
|
||||||
|
"DoH",
|
||||||
|
"DoT",
|
||||||
|
]
|
||||||
|
|
||||||
let kafkaSaslMechanismOptions = [
|
let kafkaSaslMechanismOptions = [
|
||||||
"None",
|
"None",
|
||||||
"plain",
|
"plain",
|
||||||
|
@ -1553,6 +1611,7 @@ message HealthCheckResponse {
|
||||||
|
|
||||||
this.acceptedStatusCodeOptions = acceptedStatusCodeOptions;
|
this.acceptedStatusCodeOptions = acceptedStatusCodeOptions;
|
||||||
this.dnsresolvetypeOptions = dnsresolvetypeOptions;
|
this.dnsresolvetypeOptions = dnsresolvetypeOptions;
|
||||||
|
this.dnsTransportOptions = dnsTransportOptions;
|
||||||
this.kafkaSaslMechanismOptions = kafkaSaslMechanismOptions;
|
this.kafkaSaslMechanismOptions = kafkaSaslMechanismOptions;
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -109,7 +109,7 @@ export function getDevContainerServerHostname() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regex pattern fr identifying hostnames and IP addresses
|
* Regex pattern for identifying hostnames and IP addresses
|
||||||
* @param {boolean} mqtt whether or not the regex should take into
|
* @param {boolean} mqtt whether or not the regex should take into
|
||||||
* account the fact that it is an mqtt uri
|
* account the fact that it is an mqtt uri
|
||||||
* @returns {RegExp} The requested regex
|
* @returns {RegExp} The requested regex
|
||||||
|
@ -125,6 +125,19 @@ export function hostNameRegexPattern(mqtt = false) {
|
||||||
return `${ipRegexPattern}|${hostNameRegexPattern}`;
|
return `${ipRegexPattern}|${hostNameRegexPattern}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regex patterns for validating URL paths
|
||||||
|
* @returns {RegExp} The requested regex
|
||||||
|
*/
|
||||||
|
export function urlPathRegexPattern() {
|
||||||
|
// Ensures a URL path follows query string format
|
||||||
|
const queryStringRegexPattern = "^/?(([a-zA-Z0-9\\-_%])+/)*[a-zA-Z0-9\\-_%]*\\?([a-zA-Z0-9\\-_%]+=[a-zA-Z0-9\\-_%]*&?)+$";
|
||||||
|
// Only checks for valid URL path containing "{query}"
|
||||||
|
const queryRegexPattern = "^[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=]*{query}[a-zA-Z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=]*$";
|
||||||
|
|
||||||
|
return `${queryStringRegexPattern}|${queryRegexPattern}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the tag color options
|
* Get the tag color options
|
||||||
* Shared between components
|
* Shared between components
|
||||||
|
|
Loading…
Add table
Reference in a new issue