mirror of https://github.com/louislam/uptime-kuma
				
				
				
			Added DNS Monitor Type
							parent
							
								
									2912ca1248
								
							
						
					
					
						commit
						c79be19ec3
					
				| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
 | 
			
		||||
PRAGMA foreign_keys = off;
 | 
			
		||||
 | 
			
		||||
BEGIN TRANSACTION;
 | 
			
		||||
 | 
			
		||||
create table monitor_dg_tmp (
 | 
			
		||||
	id INTEGER not null primary key autoincrement,
 | 
			
		||||
	name VARCHAR(150),
 | 
			
		||||
	active BOOLEAN default 1 not null,
 | 
			
		||||
	user_id INTEGER references user on update cascade on delete
 | 
			
		||||
	set
 | 
			
		||||
		null,
 | 
			
		||||
		interval INTEGER default 20 not null,
 | 
			
		||||
		url TEXT,
 | 
			
		||||
		type VARCHAR(20),
 | 
			
		||||
		weight INTEGER default 2000,
 | 
			
		||||
		hostname VARCHAR(255),
 | 
			
		||||
		port INTEGER,
 | 
			
		||||
		created_date DATETIME default (DATETIME('now')) not null,
 | 
			
		||||
		keyword VARCHAR(255),
 | 
			
		||||
		maxretries INTEGER NOT NULL DEFAULT 0,
 | 
			
		||||
		ignore_tls BOOLEAN default 0 not null,
 | 
			
		||||
		upside_down BOOLEAN default 0 not null,
 | 
			
		||||
    maxredirects INTEGER default 10 not null,
 | 
			
		||||
    accepted_statuscodes_json TEXT default '["200-299"]' not null,
 | 
			
		||||
    		dns_resolve_type VARCHAR(5),
 | 
			
		||||
    		dns_resolve_server VARCHAR(255)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
insert into
 | 
			
		||||
	monitor_dg_tmp(
 | 
			
		||||
		id,
 | 
			
		||||
		name,
 | 
			
		||||
		active,
 | 
			
		||||
		user_id,
 | 
			
		||||
		interval,
 | 
			
		||||
		url,
 | 
			
		||||
		type,
 | 
			
		||||
		weight,
 | 
			
		||||
		hostname,
 | 
			
		||||
		port,
 | 
			
		||||
    created_date,
 | 
			
		||||
		keyword,
 | 
			
		||||
		maxretries,
 | 
			
		||||
		ignore_tls,
 | 
			
		||||
		upside_down,
 | 
			
		||||
		maxredirects,
 | 
			
		||||
		accepted_statuscodes_json
 | 
			
		||||
	)
 | 
			
		||||
select
 | 
			
		||||
	id,
 | 
			
		||||
	name,
 | 
			
		||||
	active,
 | 
			
		||||
	user_id,
 | 
			
		||||
	interval,
 | 
			
		||||
	url,
 | 
			
		||||
	type,
 | 
			
		||||
	weight,
 | 
			
		||||
	hostname,
 | 
			
		||||
	port,
 | 
			
		||||
  created_date,
 | 
			
		||||
	keyword,
 | 
			
		||||
	maxretries,
 | 
			
		||||
	ignore_tls,
 | 
			
		||||
	upside_down,
 | 
			
		||||
	maxredirects,
 | 
			
		||||
	accepted_statuscodes_json
 | 
			
		||||
from
 | 
			
		||||
	monitor;
 | 
			
		||||
 | 
			
		||||
drop table monitor;
 | 
			
		||||
 | 
			
		||||
alter table
 | 
			
		||||
	monitor_dg_tmp rename to monitor;
 | 
			
		||||
 | 
			
		||||
create index user_id on monitor (user_id);
 | 
			
		||||
 | 
			
		||||
COMMIT;
 | 
			
		||||
 | 
			
		||||
PRAGMA foreign_keys = on;
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ class Database {
 | 
			
		|||
 | 
			
		||||
    static templatePath = "./db/kuma.db"
 | 
			
		||||
    static path = "./data/kuma.db";
 | 
			
		||||
    static latestVersion = 6;
 | 
			
		||||
    static latestVersion = 7;
 | 
			
		||||
    static noReject = true;
 | 
			
		||||
    static sqliteInstance = null;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ dayjs.extend(timezone)
 | 
			
		|||
const axios = require("axios");
 | 
			
		||||
const { Prometheus } = require("../prometheus");
 | 
			
		||||
const { debug, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
 | 
			
		||||
const { tcping, ping, checkCertificate, checkStatusCode } = require("../util-server");
 | 
			
		||||
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode } = require("../util-server");
 | 
			
		||||
const { R } = require("redbean-node");
 | 
			
		||||
const { BeanModel } = require("redbean-node/dist/bean-model");
 | 
			
		||||
const { Notification } = require("../notification")
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +48,8 @@ class Monitor extends BeanModel {
 | 
			
		|||
            upsideDown: this.isUpsideDown(),
 | 
			
		||||
            maxredirects: this.maxredirects,
 | 
			
		||||
            accepted_statuscodes: this.getAcceptedStatuscodes(),
 | 
			
		||||
            dns_resolve_type: this.dns_resolve_type,
 | 
			
		||||
            dns_resolve_server: this.dns_resolve_server,
 | 
			
		||||
            notificationIDList,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -175,6 +177,44 @@ class Monitor extends BeanModel {
 | 
			
		|||
                    bean.ping = await ping(this.hostname);
 | 
			
		||||
                    bean.msg = ""
 | 
			
		||||
                    bean.status = UP;
 | 
			
		||||
                } else if (this.type === "dns") {
 | 
			
		||||
                    let startTime = dayjs().valueOf();
 | 
			
		||||
 | 
			
		||||
                    var dnsRes = await dnsResolve(this.hostname, this.dns_resolve_server, this.dns_resolve_type);
 | 
			
		||||
 | 
			
		||||
                    var dnsMessage = "";
 | 
			
		||||
 | 
			
		||||
                    if (this.dns_resolve_type == 'A' || this.dns_resolve_type == 'AAAA' || this.dns_resolve_type == 'CNAME' || this.dns_resolve_type == 'PTR') {
 | 
			
		||||
                        var dnsMessage = dnsRes[0];
 | 
			
		||||
                    } else if (this.dns_resolve_type == 'CAA') {
 | 
			
		||||
                        var dnsMessage = dnsRes[0].issue;
 | 
			
		||||
                    } else if (this.dns_resolve_type == 'MX') {
 | 
			
		||||
                        dnsRes.forEach(record => {
 | 
			
		||||
                            dnsMessage += `Server: ${record.exchange} - Priority: ${record.priority} | `;
 | 
			
		||||
                        });
 | 
			
		||||
                        var dnsMessage = dnsMessage.slice(0, -2)
 | 
			
		||||
                    } else if (this.dns_resolve_type == 'NS') { 
 | 
			
		||||
                        dnsRes.forEach(record => {
 | 
			
		||||
                            dnsMessage += `Server: ${record} | `;
 | 
			
		||||
                        });
 | 
			
		||||
                        var dnsMessage = dnsMessage.slice(0, -2)
 | 
			
		||||
                    } else if (this.dns_resolve_type == 'SOA') {
 | 
			
		||||
                        dnsMessage += `NS-Name: ${dnsRes.nsname} | Hostmaster: ${dnsRes.hostmaster} | Serial: ${dnsRes.serial} | Refresh: ${dnsRes.refresh} | Retry: ${dnsRes.retry} | Expire: ${dnsRes.expire} | MinTTL: ${dnsRes.minttl}`;
 | 
			
		||||
                    } else if (this.dns_resolve_type == 'SRV') {
 | 
			
		||||
                        dnsRes.forEach(record => {
 | 
			
		||||
                            dnsMessage += `Name: ${record.name} | Port: ${record.port} | Priority: ${record.priority} | Weight: ${record.weight} | `;
 | 
			
		||||
                        });
 | 
			
		||||
                        var dnsMessage = dnsMessage.slice(0, -2)
 | 
			
		||||
                    } else if (this.dns_resolve_type == 'TXT') {
 | 
			
		||||
                        dnsRes.forEach(record => {
 | 
			
		||||
                            dnsMessage += `Record: ${record} | `;
 | 
			
		||||
                        });
 | 
			
		||||
                        var dnsMessage = dnsMessage.slice(0, -2)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    bean.msg = dnsMessage;
 | 
			
		||||
                    bean.ping = dayjs().valueOf() - startTime;
 | 
			
		||||
                    bean.status = UP;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (this.isUpsideDown()) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -293,6 +293,8 @@ let indexHTML = fs.readFileSync("./dist/index.html").toString();
 | 
			
		|||
                bean.upsideDown = monitor.upsideDown;
 | 
			
		||||
                bean.maxredirects = monitor.maxredirects;
 | 
			
		||||
                bean.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
 | 
			
		||||
                bean.dns_resolve_type = monitor.dns_resolve_type;
 | 
			
		||||
                bean.dns_resolve_server = monitor.dns_resolve_server;
 | 
			
		||||
 | 
			
		||||
                await R.store(bean)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ const { R } = require("redbean-node");
 | 
			
		|||
const { debug } = require("../src/util");
 | 
			
		||||
const passwordHash = require("./password-hash");
 | 
			
		||||
const dayjs = require("dayjs");
 | 
			
		||||
const { Resolver } = require('dns');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Init or reset JWT secret
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +77,30 @@ exports.pingAsync = function (hostname, ipv6 = false) {
 | 
			
		|||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.dnsResolve = function (hostname, resolver_server, rrtype) {
 | 
			
		||||
    const resolver = new Resolver();
 | 
			
		||||
    resolver.setServers([resolver_server]);
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
        if (rrtype == 'PTR') {
 | 
			
		||||
            resolver.reverse(hostname, (err, records) => {
 | 
			
		||||
                if (err) {
 | 
			
		||||
                    reject(err);
 | 
			
		||||
                } else {
 | 
			
		||||
                    resolve(records);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            resolver.resolve(hostname, rrtype, (err, records) => {
 | 
			
		||||
                if (err) {
 | 
			
		||||
                    reject(err);
 | 
			
		||||
                } else {
 | 
			
		||||
                    resolve(records);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
exports.setting = async function (key) {
 | 
			
		||||
    let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [
 | 
			
		||||
        key,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,9 @@
 | 
			
		|||
                                    <option value="keyword">
 | 
			
		||||
                                        HTTP(s) - Keyword
 | 
			
		||||
                                    </option>
 | 
			
		||||
                                    <option value="dns">
 | 
			
		||||
                                        DNS
 | 
			
		||||
                                    </option>
 | 
			
		||||
                                </select>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +57,41 @@
 | 
			
		|||
                                <input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div v-if="monitor.type === 'dns'" class="my-3">
 | 
			
		||||
                                <label for="hostname" class="form-label">Hostname</label>
 | 
			
		||||
                                <input id="hostname" v-model="monitor.hostname" type="text" class="form-control" required>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div v-if="monitor.type === 'dns'" class="my-3">
 | 
			
		||||
                                <label for="dns_resolve_server" class="form-label">Resolver Server</label>
 | 
			
		||||
                                <input id="dns_resolve_server" v-model="monitor.dns_resolve_server" type="text" class="form-control" :pattern="this.ipRegex" required>
 | 
			
		||||
                                <div class="form-text">
 | 
			
		||||
                                    Cloudflare is the default server, you can change the resolver server anytime.
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div v-if="monitor.type === 'dns'" class="my-3">
 | 
			
		||||
                                <label for="dns_resolve_type" class="form-label">Resource Record Type</label>
 | 
			
		||||
 | 
			
		||||
                                <VueMultiselect
 | 
			
		||||
                                    id="dns_resolve_type"
 | 
			
		||||
                                    v-model="monitor.dns_resolve_type"
 | 
			
		||||
                                    :options="dnsresolvetypeOptions"
 | 
			
		||||
                                    :multiple="false"
 | 
			
		||||
                                    :close-on-select="true"
 | 
			
		||||
                                    :clear-on-select="false"
 | 
			
		||||
                                    :preserve-search="false"
 | 
			
		||||
                                    placeholder="Pick a RR-Type..."
 | 
			
		||||
                                    :preselect-first="false"
 | 
			
		||||
                                    :max-height="500"
 | 
			
		||||
                                    :taggable="false"
 | 
			
		||||
                                ></VueMultiselect>
 | 
			
		||||
 | 
			
		||||
                                <div class="form-text">
 | 
			
		||||
                                    Select the RR-Type you want to monitor
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
 | 
			
		||||
                            <div class="my-3">
 | 
			
		||||
                                <label for="interval" class="form-label">Heartbeat Interval (Every {{ monitor.interval }} seconds)</label>
 | 
			
		||||
                                <input id="interval" v-model="monitor.interval" type="number" class="form-control" required min="20" step="1">
 | 
			
		||||
| 
						 | 
				
			
			@ -170,6 +208,8 @@ export default {
 | 
			
		|||
                notificationIDList: {},
 | 
			
		||||
            },
 | 
			
		||||
            acceptedStatusCodeOptions: [],
 | 
			
		||||
            dnsresolvetypeOptions: [],
 | 
			
		||||
            ipRegex: "((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))",
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -200,11 +240,25 @@ export default {
 | 
			
		|||
            "500-599",
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        let dnsresolvetypeOptions = [
 | 
			
		||||
            "A",
 | 
			
		||||
            "AAAA",
 | 
			
		||||
            "CAA",
 | 
			
		||||
            "CNAME",
 | 
			
		||||
            "MX",
 | 
			
		||||
            "NS",
 | 
			
		||||
            "PTR",
 | 
			
		||||
            "SOA",
 | 
			
		||||
            "SRV",
 | 
			
		||||
            "TXT",
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        for (let i = 100; i <= 999; i++) {
 | 
			
		||||
            acceptedStatusCodeOptions.push(i.toString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.acceptedStatusCodeOptions = acceptedStatusCodeOptions;
 | 
			
		||||
        this.dnsresolvetypeOptions = dnsresolvetypeOptions;
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
        init() {
 | 
			
		||||
| 
						 | 
				
			
			@ -221,6 +275,7 @@ export default {
 | 
			
		|||
                    upsideDown: false,
 | 
			
		||||
                    maxredirects: 10,
 | 
			
		||||
                    accepted_statuscodes: ["200-299"],
 | 
			
		||||
                    dns_resolve_server: "1.1.1.1",
 | 
			
		||||
                }
 | 
			
		||||
            } else if (this.isEdit) {
 | 
			
		||||
                this.$root.getSocket().emit("getMonitor", this.$route.params.id, (res) => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue