mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-06-06 13:12:33 +02:00
feat(monitor-checks): add checks with specific types
This commit is contained in:
parent
3fb4bf9abe
commit
20e3c5061e
10 changed files with 364 additions and 8111 deletions
|
@ -8,7 +8,7 @@ create table monitor_checks
|
|||
constraint monitor_checks_pk
|
||||
primary key autoincrement,
|
||||
type VARCHAR(50) not null,
|
||||
value TEXTt,
|
||||
value TEXT,
|
||||
monitor_id INTEGER not null
|
||||
constraint monitor_checks_monitor_id_fk
|
||||
references monitor
|
||||
|
|
8126
package-lock.json
generated
8126
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -57,6 +57,7 @@
|
|||
"form-data": "^4.0.0",
|
||||
"http-graceful-shutdown": "^3.1.4",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"nodemailer": "^6.6.3",
|
||||
"notp": "^2.0.3",
|
||||
"password-hash": "^1.2.2",
|
||||
|
|
|
@ -7,10 +7,11 @@ dayjs.extend(timezone)
|
|||
const axios = require("axios");
|
||||
const { Prometheus } = require("../prometheus");
|
||||
const { debug, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
|
||||
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom } = require("../util-server");
|
||||
const { tcping, ping, dnsResolve, checkCertificate, getTotalClientInRoom } = require("../util-server");
|
||||
const { R } = require("redbean-node");
|
||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||
const { Notification } = require("../notification")
|
||||
const validateMonitorChecks = require("./validate-monitor-checks");
|
||||
const version = require("../../package.json").version;
|
||||
|
||||
/**
|
||||
|
@ -43,15 +44,14 @@ class Monitor extends BeanModel {
|
|||
active: this.active,
|
||||
type: this.type,
|
||||
interval: this.interval,
|
||||
keyword: this.keyword,
|
||||
ignoreTls: this.getIgnoreTls(),
|
||||
upsideDown: this.isUpsideDown(),
|
||||
maxredirects: this.maxredirects,
|
||||
accepted_statuscodes: this.getAcceptedStatuscodes(),
|
||||
dns_resolve_type: this.dns_resolve_type,
|
||||
dns_resolve_server: this.dns_resolve_server,
|
||||
dns_last_result: this.dns_last_result,
|
||||
notificationIDList,
|
||||
checks: this.checks,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -71,10 +71,6 @@ class Monitor extends BeanModel {
|
|||
return Boolean(this.upsideDown);
|
||||
}
|
||||
|
||||
getAcceptedStatuscodes() {
|
||||
return JSON.parse(this.accepted_statuscodes_json);
|
||||
}
|
||||
|
||||
start(io) {
|
||||
let previousBeat = null;
|
||||
let retries = 0;
|
||||
|
@ -112,7 +108,7 @@ class Monitor extends BeanModel {
|
|||
}
|
||||
|
||||
try {
|
||||
if (this.type === "http" || this.type === "keyword") {
|
||||
if (this.type === "http") {
|
||||
// Do not do any queries/high loading things before the "bean.ping"
|
||||
let startTime = dayjs().valueOf();
|
||||
|
||||
|
@ -127,9 +123,6 @@ class Monitor extends BeanModel {
|
|||
rejectUnauthorized: ! this.getIgnoreTls(),
|
||||
}),
|
||||
maxRedirects: this.maxredirects,
|
||||
validateStatus: (status) => {
|
||||
return checkStatusCode(status, this.getAcceptedStatuscodes());
|
||||
},
|
||||
});
|
||||
bean.msg = `${res.status} - ${res.statusText}`
|
||||
bean.ping = dayjs().valueOf() - startTime;
|
||||
|
@ -148,6 +141,8 @@ class Monitor extends BeanModel {
|
|||
|
||||
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms")
|
||||
|
||||
|
||||
validateMonitorChecks(res, this.checks, bean);
|
||||
if (this.type === "http") {
|
||||
bean.status = UP;
|
||||
} else {
|
||||
|
|
102
server/model/validate-monitor-checks.js
Normal file
102
server/model/validate-monitor-checks.js
Normal file
|
@ -0,0 +1,102 @@
|
|||
const { checkStatusCode } = require("../util-server");
|
||||
const { UP } = require("../../src/util");
|
||||
const get = require("lodash.get");
|
||||
|
||||
function validateMonitorChecks(res, checks, bean) {
|
||||
const responseText = typeof data === "string" ? res.data : JSON.stringify(res.data);
|
||||
let checkObj;
|
||||
|
||||
this.checks.forEach(check => {
|
||||
switch (check.type) {
|
||||
case "HTTP_STATUS_CODE_SHOULD_EQUAL":
|
||||
if (checkStatusCode(res.status, check.value)) {
|
||||
bean.msg += `, status matches '${check.value}'`
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(bean.msg + ", but status code dit not match " + check.value)
|
||||
}
|
||||
break;
|
||||
|
||||
case "RESPONSE_SHOULD_CONTAIN_TEXT":
|
||||
if (responseText.includes(check.value)) {
|
||||
bean.msg += `, response contains '${check.value}'`
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(bean.msg + ", but response does not contain '" + check.value + "'");
|
||||
}
|
||||
break;
|
||||
|
||||
case "RESPONSE_SHOULD_NOT_CONTAIN_TEXT":
|
||||
if (!responseText.includes(check.value)) {
|
||||
bean.msg += `, response does not contain '${check.value}'`
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(bean.msg + ", but response does contain '" + check.value + "'");
|
||||
}
|
||||
break;
|
||||
|
||||
case "RESPONSE_SHOULD_MATCH_REGEX":
|
||||
if (responseText.test(new RegExp(check.value))) {
|
||||
bean.msg += `, regex '${check.value}' matches`
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(bean.msg + ", but response does not match regex: '" + check.value + "'");
|
||||
}
|
||||
break;
|
||||
|
||||
case "RESPONSE_SHOULD_NOT_MATCH_REGEX":
|
||||
if (!responseText.test(new RegExp(check.value))) {
|
||||
bean.msg += `, regex '${check.value}' does not matches`
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(bean.msg + ", but response does match regex: '" + check.value + "'");
|
||||
}
|
||||
break;
|
||||
|
||||
case "RESPONSE_SELECTOR_SHOULD_EQUAL":
|
||||
checkObj = JSON.parse(check.value);
|
||||
if (get(res, checkObj.selector) === checkObj.value) {
|
||||
bean.msg += `, response selector equals '${checkObj.value}'`
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(`${bean.msg}, but response selector '${checkObj.selector}' does not equal '${checkObj.value}'`);
|
||||
}
|
||||
break;
|
||||
|
||||
case "RESPONSE_SELECTOR_SHOULD_NOT_EQUAL":
|
||||
checkObj = JSON.parse(check.value);
|
||||
if (get(res, checkObj.selector) !== checkObj.value) {
|
||||
bean.msg += `, response selector does not equal '${checkObj.value}'`
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(`${bean.msg}, but response selector '${checkObj.selector}' does equal '${checkObj.value}'`);
|
||||
}
|
||||
break;
|
||||
|
||||
case "RESPONSE_SELECTOR_SHOULD_MATCH_REGEX":
|
||||
checkObj = JSON.parse(check.value);
|
||||
if (get(res, checkObj.selector).test(new RegExp(checkObj.value))) {
|
||||
bean.msg += `, response selector matches regex '${checkObj.value}'`
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(`${bean.msg}, but response selector '${checkObj.selector}' does not match regex '${checkObj.value}'`);
|
||||
}
|
||||
break;
|
||||
|
||||
case "RESPONSE_SELECTOR_SHOULD_NOT_MATCH_REGEX":
|
||||
checkObj = JSON.parse(check.value);
|
||||
if (!get(res, checkObj.selector).test(new RegExp(checkObj.value))) {
|
||||
bean.msg += `, response selector does not match regex '${checkObj.value}'`
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(`${bean.msg}, but response selector '${checkObj.selector}' does match regex '${checkObj.value}'`);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`${bean.msg}, encountered unknown monitor_check.type`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = validateMonitorChecks;
|
|
@ -1,7 +1,7 @@
|
|||
console.log("Welcome to Uptime Kuma");
|
||||
console.log("Node Env: " + process.env.NODE_ENV);
|
||||
|
||||
const { sleep, debug, TimeLogger, getRandomInt } = require("../src/util");
|
||||
const { sleep, debug, getRandomInt } = require("../src/util");
|
||||
|
||||
console.log("Importing Node libraries")
|
||||
const fs = require("fs");
|
||||
|
@ -437,8 +437,8 @@ let indexHTML = fs.readFileSync("./dist/index.html").toString();
|
|||
let notificationIDList = monitor.notificationIDList;
|
||||
delete monitor.notificationIDList;
|
||||
|
||||
monitor.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
|
||||
delete monitor.accepted_statuscodes;
|
||||
monitor.checks_json = JSON.stringify(monitor.checks);
|
||||
delete monitor.checks;
|
||||
|
||||
bean.import(monitor)
|
||||
bean.user_id = socket.userID
|
||||
|
@ -481,13 +481,12 @@ let indexHTML = fs.readFileSync("./dist/index.html").toString();
|
|||
bean.hostname = monitor.hostname;
|
||||
bean.maxretries = monitor.maxretries;
|
||||
bean.port = monitor.port;
|
||||
bean.keyword = monitor.keyword;
|
||||
bean.ignoreTls = monitor.ignoreTls;
|
||||
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;
|
||||
bean.checks_json = JSON.stringify(monitor.checks);
|
||||
|
||||
await R.store(bean)
|
||||
|
||||
|
@ -776,11 +775,10 @@ let indexHTML = fs.readFileSync("./dist/index.html").toString();
|
|||
hostname: monitorList[i].hostname,
|
||||
maxretries: monitorList[i].maxretries,
|
||||
port: monitorList[i].port,
|
||||
keyword: monitorList[i].keyword,
|
||||
ignoreTls: monitorList[i].ignoreTls,
|
||||
upsideDown: monitorList[i].upsideDown,
|
||||
maxredirects: monitorList[i].maxredirects,
|
||||
accepted_statuscodes: monitorList[i].accepted_statuscodes,
|
||||
checks: monitorList[i].checks,
|
||||
dns_resolve_type: monitorList[i].dns_resolve_type,
|
||||
dns_resolve_server: monitorList[i].dns_resolve_server,
|
||||
notificationIDList: {},
|
||||
|
@ -791,7 +789,7 @@ let indexHTML = fs.readFileSync("./dist/index.html").toString();
|
|||
let notificationIDList = monitor.notificationIDList;
|
||||
delete monitor.notificationIDList;
|
||||
|
||||
monitor.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
|
||||
monitor.checks_json = JSON.stringify(monitor.checks);
|
||||
delete monitor.accepted_statuscodes;
|
||||
|
||||
bean.import(monitor)
|
||||
|
|
141
src/components/MonitorCheckEditor.vue
Normal file
141
src/components/MonitorCheckEditor.vue
Normal file
|
@ -0,0 +1,141 @@
|
|||
<template>
|
||||
<div class="monitor-check mb-4">
|
||||
<div>
|
||||
<select id="type" v-model="monitorCheck.type" :class="{'form-select': true, 'mb-1': !!monitorCheck.type}">
|
||||
<option value="HTTP_STATUS_CODE_SHOULD_EQUAL">
|
||||
{{ $t("HTTP status code should equal") }}
|
||||
</option>
|
||||
<option value="RESPONSE_SHOULD_CONTAIN_TEXT">
|
||||
{{ $t("Response should contain text") }}
|
||||
</option>
|
||||
<option value="RESPONSE_SHOULD_NOT_CONTAIN_TEXT">
|
||||
{{ $t("Response should not contain text") }}
|
||||
</option>
|
||||
<option value="RESPONSE_SHOULD_MATCH_REGEX">
|
||||
{{ $t("Response should match regex") }}
|
||||
</option>
|
||||
<option value="RESPONSE_SHOULD_NOT_MATCH_REGEX">
|
||||
{{ $t("Response should not match regex") }}
|
||||
</option>
|
||||
<option value="RESPONSE_SELECTOR_SHOULD_EQUAL">
|
||||
{{ $t("Response selector should equal") }}
|
||||
</option>
|
||||
<option value="RESPONSE_SELECTOR_SHOULD_NOT_EQUAL">
|
||||
{{ $t("Response selector should not equal") }}
|
||||
</option>
|
||||
<option value="RESPONSE_SELECTOR_SHOULD_MATCH_REGEX">
|
||||
{{ $t("Response selector should match regex") }}
|
||||
</option>
|
||||
<option value="RESPONSE_SELECTOR_SHOULD_NOT_MATCH_REGEX">
|
||||
{{ $t("Response selector should not match regex") }}
|
||||
</option>
|
||||
</select>
|
||||
<div v-if="monitorCheck.type === 'HTTP_STATUS_CODE_SHOULD_EQUAL'">
|
||||
<VueMultiselect
|
||||
id="acceptedStatusCodes"
|
||||
v-model="monitorCheck.value"
|
||||
:options="acceptedStatusCodeOptions"
|
||||
:multiple="true"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="false"
|
||||
:preserve-search="true"
|
||||
placeholder="Pick Accepted Status Codes..."
|
||||
:preselect-first="false"
|
||||
:max-height="600"
|
||||
:taggable="true"
|
||||
></VueMultiselect>
|
||||
</div>
|
||||
<div v-if="monitorCheck.type === 'RESPONSE_SHOULD_CONTAIN_TEXT' || monitorCheck.type === 'RESPONSE_SHOULD_NOT_CONTAIN_TEXT'">
|
||||
<input v-model="monitorCheck.value" type="text" class="form-control" required :placeholder="$t('Value')">
|
||||
</div>
|
||||
<div v-if="monitorCheck.type === 'RESPONSE_SHOULD_MATCH_REGEX' || monitorCheck.type === 'RESPONSE_SHOULD_NOT_MATCH_REGEX'">
|
||||
<input v-model="monitorCheck.value" type="text" class="form-control" required
|
||||
:placeholder="$t('Regexp, Example: [a-z0-9.]+@gmail\.com')">
|
||||
</div>
|
||||
<div
|
||||
v-if="monitorCheck.type === 'RESPONSE_SELECTOR_SHOULD_EQUAL' || monitorCheck.type === 'RESPONSE_SELECTOR_SHOULD_NOT_EQUAL'">
|
||||
<input v-model="monitorCheck.value.selector" type="text" class="form-control mb-1" required
|
||||
:placeholder="$t('Selector, Example: customer.address.street')">
|
||||
<input v-model="monitorCheck.value.value" type="text" class="form-control" required :placeholder="$t('Value, Example: First street')">
|
||||
</div>
|
||||
<div
|
||||
v-if="monitorCheck.type === 'RESPONSE_SELECTOR_SHOULD_MATCH_REGEX' || monitorCheck.type === 'RESPONSE_SELECTOR_SHOULD_NOT_MATCH_REGEX'">
|
||||
<input v-model="monitorCheck.value.selector" type="text" class="form-control mb-1" required
|
||||
:placeholder="$t('Selector, Example: customer.contactInfo.email')">
|
||||
<input v-model="monitorCheck.value.value" type="text" class="form-control" required
|
||||
:placeholder="$t('Regexp, Example: [a-z0-9.]+@gmail\.com')">
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-outline-danger" type="button" @click="deleteMonitorCheck">
|
||||
<font-awesome-icon icon="times" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VueMultiselect,
|
||||
},
|
||||
props: {
|
||||
monitorCheck: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
acceptedStatusCodeOptions: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let acceptedStatusCodeOptions = [
|
||||
"100-199",
|
||||
"200-299",
|
||||
"300-399",
|
||||
"400-499",
|
||||
"500-599",
|
||||
];
|
||||
|
||||
for (let i = 100; i <= 999; i++) {
|
||||
acceptedStatusCodeOptions.push(i.toString());
|
||||
}
|
||||
|
||||
this.acceptedStatusCodeOptions = acceptedStatusCodeOptions;
|
||||
},
|
||||
methods: {
|
||||
deleteMonitorCheck() {
|
||||
this.$emit('delete');
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/vars.scss";
|
||||
|
||||
.monitor-check {
|
||||
display: flex;
|
||||
|
||||
input,
|
||||
select {
|
||||
border-radius: 19px 0 0 19px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-left: 0.25rem;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
border-radius: 0 19px 19px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.monitor-check {
|
||||
.multiselect__tags {
|
||||
border-radius: 19px 0 0 19px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,10 +1,10 @@
|
|||
import { library } from "@fortawesome/fontawesome-svg-core"
|
||||
import { faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp, faEye, faEyeSlash } from "@fortawesome/free-solid-svg-icons"
|
||||
import { faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp, faEye, faEyeSlash, faTimes } from "@fortawesome/free-solid-svg-icons"
|
||||
//import { fa } from '@fortawesome/free-regular-svg-icons'
|
||||
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"
|
||||
|
||||
// Add Free Font Awesome Icons here
|
||||
// https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free
|
||||
library.add(faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp, faEye, faEyeSlash);
|
||||
library.add(faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp, faEye, faEyeSlash, faTimes);
|
||||
|
||||
export { FontAwesomeIcon }
|
||||
|
|
|
@ -3,13 +3,9 @@
|
|||
<div v-if="monitor">
|
||||
<h1> {{ monitor.name }}</h1>
|
||||
<p class="url">
|
||||
<a v-if="monitor.type === 'http' || monitor.type === 'keyword' " :href="monitor.url" target="_blank">{{ monitor.url }}</a>
|
||||
<a v-if="monitor.type === 'http'" :href="monitor.url" target="_blank">{{ monitor.url }}</a>
|
||||
<span v-if="monitor.type === 'port'">TCP Ping {{ monitor.hostname }}:{{ monitor.port }}</span>
|
||||
<span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
|
||||
<span v-if="monitor.type === 'keyword'">
|
||||
<br>
|
||||
<span>{{ $t("Keyword") }}:</span> <span class="keyword">{{ monitor.keyword }}</span>
|
||||
</span>
|
||||
<span v-if="monitor.type === 'dns'">[{{ monitor.dns_resolve_type }}] {{ monitor.hostname }}
|
||||
<br>
|
||||
<span>{{ $t("Last Result") }}:</span> <span class="keyword">{{ monitor.dns_last_result }}</span>
|
||||
|
|
|
@ -12,19 +12,16 @@
|
|||
<label for="type" class="form-label">{{ $t("Monitor Type") }}</label>
|
||||
<select id="type" v-model="monitor.type" class="form-select">
|
||||
<option value="http">
|
||||
HTTP(s)
|
||||
{{ $t("HTTP(s)") }}
|
||||
</option>
|
||||
<option value="port">
|
||||
TCP Port
|
||||
{{ $t("TCP Port") }}
|
||||
</option>
|
||||
<option value="ping">
|
||||
Ping
|
||||
</option>
|
||||
<option value="keyword">
|
||||
HTTP(s) - {{ $t("Keyword") }}
|
||||
{{ $t("Ping") }}
|
||||
</option>
|
||||
<option value="dns">
|
||||
DNS
|
||||
{{ $t("DNS") }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -34,19 +31,11 @@
|
|||
<input id="name" v-model="monitor.name" type="text" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3">
|
||||
<div v-if="monitor.type === 'http'" class="my-3">
|
||||
<label for="url" class="form-label">{{ $t("URL") }}</label>
|
||||
<input id="url" v-model="monitor.url" type="url" class="form-control" pattern="https?://.+" required>
|
||||
</div>
|
||||
|
||||
<div v-if="monitor.type === 'keyword' " class="my-3">
|
||||
<label for="keyword" class="form-label">{{ $t("Keyword") }}</label>
|
||||
<input id="keyword" v-model="monitor.keyword" type="text" class="form-control" required>
|
||||
<div class="form-text">
|
||||
{{ $t("keywordDescription") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TCP Port / Ping / DNS only -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' " class="my-3">
|
||||
<label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
|
||||
|
@ -108,7 +97,7 @@
|
|||
|
||||
<h2 class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
|
||||
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3 form-check">
|
||||
<div v-if="monitor.type === 'http'" class="my-3 form-check">
|
||||
<input id="ignore-tls" v-model="monitor.ignoreTls" class="form-check-input" type="checkbox" value="">
|
||||
<label class="form-check-label" for="ignore-tls">
|
||||
{{ $t("ignoreTLSError") }}
|
||||
|
@ -125,8 +114,8 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- HTTP / Keyword only -->
|
||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' ">
|
||||
<!-- HTTP only -->
|
||||
<template v-if="monitor.type === 'http'">
|
||||
<div class="my-3">
|
||||
<label for="maxRedirects" class="form-label">{{ $t("Max. Redirects") }}</label>
|
||||
<input id="maxRedirects" v-model="monitor.maxredirects" type="number" class="form-control" required min="0" step="1">
|
||||
|
@ -135,26 +124,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<h2 class="mt-5 mb-2">{{ $t("Checks") }}</h2>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="acceptedStatusCodes" class="form-label">{{ $t("Accepted Status Codes") }}</label>
|
||||
|
||||
<VueMultiselect
|
||||
id="acceptedStatusCodes"
|
||||
v-model="monitor.accepted_statuscodes"
|
||||
:options="acceptedStatusCodeOptions"
|
||||
:multiple="true"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="false"
|
||||
:preserve-search="true"
|
||||
placeholder="Pick Accepted Status Codes..."
|
||||
:preselect-first="false"
|
||||
:max-height="600"
|
||||
:taggable="true"
|
||||
></VueMultiselect>
|
||||
|
||||
<div class="form-text">
|
||||
{{ $t("acceptedStatusCodesDescription") }}
|
||||
<div v-for="(monitorCheck, index) in monitor.checks" :key="index" class="mb-3">
|
||||
<MonitorCheckEditor :monitorCheck="monitorCheck" :index="index" @delete="deleteMonitorCheck(index)"></MonitorCheckEditor>
|
||||
</div>
|
||||
<button class="btn btn-light" type="button" @click="addMonitorCheck()">{{ $t("Add check") }}</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -197,6 +173,7 @@
|
|||
|
||||
<script>
|
||||
import NotificationDialog from "../components/NotificationDialog.vue";
|
||||
import MonitorCheckEditor from "../components/MonitorCheckEditor.vue";
|
||||
import { useToast } from "vue-toastification"
|
||||
import VueMultiselect from "vue-multiselect"
|
||||
import { isDev } from "../util.ts";
|
||||
|
@ -205,6 +182,7 @@ const toast = useToast()
|
|||
export default {
|
||||
components: {
|
||||
NotificationDialog,
|
||||
MonitorCheckEditor,
|
||||
VueMultiselect,
|
||||
},
|
||||
|
||||
|
@ -314,7 +292,17 @@ export default {
|
|||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
addMonitorCheck() {
|
||||
this.monitor.checks = [...(this.monitor.checks || []), {
|
||||
type: null,
|
||||
value: '',
|
||||
}];
|
||||
},
|
||||
|
||||
deleteMonitorCheck(index) {
|
||||
this.monitor.checks = this.monitor.checks.splice(index, 1);
|
||||
},
|
||||
|
||||
submit() {
|
||||
|
|
Loading…
Add table
Reference in a new issue