mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-23 14:54:05 +00:00
SNMP Initial Commits
This commit introduces a new SNMP monitor feature to the application, allowing users to monitor devices using SNMP (Simple Network Management Protocol).
This commit is contained in:
parent
bab427f715
commit
d92003e172
7 changed files with 714 additions and 647 deletions
10
db/knex_migrations/2024-04-26-0000-snmp-monitor.js
Normal file
10
db/knex_migrations/2024-04-26-0000-snmp-monitor.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
exports.up = function (knex) {
|
||||
return knex.schema
|
||||
.alterTable("monitor", function (table) {
|
||||
table.string("snmp_community_string", 255).defaultTo("public"); // Add community_string column
|
||||
table.string("snmp_oid").notNullable(); // Add oid column
|
||||
table.enum("snmp_version", ["1", "2c", "3"]).defaultTo("2c"); // Add snmp_version column with enum values
|
||||
table.float("snmp_control_value").notNullable(); // Add control_value column as float
|
||||
table.string("snmp_condition").notNullable(); // Add oid column
|
||||
});
|
||||
};
|
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -72,6 +72,7 @@
|
|||
"redbean-node": "~0.3.0",
|
||||
"redis": "~4.5.1",
|
||||
"semver": "~7.5.4",
|
||||
"snmp-native": "^1.2.0",
|
||||
"socket.io": "~4.6.1",
|
||||
"socket.io-client": "~4.6.1",
|
||||
"socks-proxy-agent": "6.1.1",
|
||||
|
@ -12609,6 +12610,11 @@
|
|||
"npm": ">= 3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/snmp-native": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/snmp-native/-/snmp-native-1.2.0.tgz",
|
||||
"integrity": "sha512-JIyuLX3bQmuAI4gHztHSQd3M/M2hqgLhiHBZYEk8YnYRJ2ooxqwON4gUQfgp/WCZVDca4tIX3vFJgv6lz5iY+g=="
|
||||
},
|
||||
"node_modules/socket.io": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.2.tgz",
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
"redbean-node": "~0.3.0",
|
||||
"redis": "~4.5.1",
|
||||
"semver": "~7.5.4",
|
||||
"snmp-native": "^1.2.0",
|
||||
"socket.io": "~4.6.1",
|
||||
"socket.io-client": "~4.6.1",
|
||||
"socks-proxy-agent": "6.1.1",
|
||||
|
|
69
server/monitor-types/snmp.js
Normal file
69
server/monitor-types/snmp.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
const { MonitorType } = require("./monitor-type");
|
||||
const { UP, DOWN } = require("../../src/util");
|
||||
const snmp = require("snmp-native");
|
||||
|
||||
class SNMPMonitorType extends MonitorType {
|
||||
name = "snmp";
|
||||
|
||||
/**
|
||||
* Checks the SNMP value against the condition and control value.
|
||||
* @param {object} monitor The monitor object associated with the check.
|
||||
* @param {object} heartbeat The heartbeat object to update.
|
||||
* @param {object} _server Unused server object.
|
||||
*/
|
||||
async check(monitor, heartbeat, _server) {
|
||||
try {
|
||||
const session = new snmp.Session({ host: monitor.ipAddress, community: monitor.snmpCommunityString, version: monitor.snmpVersion });
|
||||
|
||||
session.get({ oid: monitor.snmpOid }, (err, varbinds) => {
|
||||
if (err) {
|
||||
heartbeat.status = DOWN;
|
||||
heartbeat.msg = `Error: ${err.message}`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Assuming only one varbind is returned
|
||||
const value = varbinds[0].value;
|
||||
|
||||
// Convert value to appropriate type based on SNMP type (assuming it's integer or string for simplicity)
|
||||
const numericValue = parseInt(value);
|
||||
const stringValue = value.toString();
|
||||
|
||||
// Check against condition and control value
|
||||
switch (monitor.snmpCondition) {
|
||||
case '>':
|
||||
heartbeat.status = numericValue > monitor.snmpControlValue ? UP : DOWN;
|
||||
break;
|
||||
case '>=':
|
||||
heartbeat.status = numericValue >= monitor.snmpControlValue ? UP : DOWN;
|
||||
break;
|
||||
case '<':
|
||||
heartbeat.status = numericValue < monitor.snmpControlValue ? UP : DOWN;
|
||||
break;
|
||||
case '<=':
|
||||
heartbeat.status = numericValue <= monitor.snmpControlValue ? UP : DOWN;
|
||||
break;
|
||||
case '==':
|
||||
heartbeat.status = value === monitor.snmpControlValue ? UP : DOWN;
|
||||
break;
|
||||
case 'contains':
|
||||
heartbeat.status = stringValue.includes(monitor.snmpControlValue) ? UP : DOWN;
|
||||
break;
|
||||
default:
|
||||
heartbeat.status = DOWN;
|
||||
heartbeat.msg = `Invalid condition: ${monitor.snmpCondition}`;
|
||||
}
|
||||
|
||||
session.close();
|
||||
});
|
||||
} catch (err) {
|
||||
heartbeat.status = DOWN;
|
||||
heartbeat.msg = `Error: ${err.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
SNMPMonitorType,
|
||||
};
|
|
@ -113,6 +113,7 @@ class UptimeKumaServer {
|
|||
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
|
||||
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
|
||||
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
|
||||
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
|
||||
|
||||
// Allow all CORS origins (polling) in development
|
||||
let cors = undefined;
|
||||
|
@ -516,3 +517,4 @@ const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor
|
|||
const { TailscalePing } = require("./monitor-types/tailscale-ping");
|
||||
const { DnsMonitorType } = require("./monitor-types/dns");
|
||||
const { MqttMonitorType } = require("./monitor-types/mqtt");
|
||||
const { SNMPMonitorType } = require("./monitor-types/snmp");
|
||||
|
|
BIN
src/pages/.EditMonitor.vue.swp
Normal file
BIN
src/pages/.EditMonitor.vue.swp
Normal file
Binary file not shown.
|
@ -24,6 +24,9 @@
|
|||
<option value="ping">
|
||||
Ping
|
||||
</option>
|
||||
<option value="snmp">
|
||||
SNMP
|
||||
</option>
|
||||
<option value="keyword">
|
||||
HTTP(s) - {{ $t("Keyword") }}
|
||||
</option>
|
||||
|
@ -100,19 +103,19 @@
|
|||
</div>
|
||||
|
||||
<!-- URL -->
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'real-browser' " class="my-3">
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'real-browser'" 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>
|
||||
|
||||
<!-- gRPC URL -->
|
||||
<div v-if="monitor.type === 'grpc-keyword' " class="my-3">
|
||||
<div v-if="monitor.type === 'grpc-keyword'" class="my-3">
|
||||
<label for="grpc-url" class="form-label">{{ $t("URL") }}</label>
|
||||
<input id="grpc-url" v-model="monitor.grpcUrl" type="text" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<!-- Push URL -->
|
||||
<div v-if="monitor.type === 'push' " class="my-3">
|
||||
<div v-if="monitor.type === 'push'" class="my-3">
|
||||
<label for="push-url" class="form-label">{{ $t("PushUrl") }}</label>
|
||||
<CopyableInput id="push-url" v-model="pushURL" type="url" disabled="disabled" />
|
||||
<div class="form-text">
|
||||
|
@ -159,12 +162,7 @@
|
|||
|
||||
<div v-if="remoteBrowsersToggle">
|
||||
<label for="remote-browser" class="form-label">{{ $t("Remote Browser") }}</label>
|
||||
<ActionSelect
|
||||
v-model="monitor.remote_browser"
|
||||
:options="remoteBrowsersOptions"
|
||||
icon="plus"
|
||||
:action="() => $refs.remoteBrowserDialog.show()"
|
||||
/>
|
||||
<ActionSelect v-model="monitor.remote_browser" :options="remoteBrowsersOptions" icon="plus" :action="() => $refs.remoteBrowserDialog.show()" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -198,22 +196,7 @@
|
|||
<!-- Kafka Brokers List -->
|
||||
<div class="my-3">
|
||||
<label for="kafkaProducerBrokers" class="form-label">{{ $t("Kafka Brokers") }}</label>
|
||||
<VueMultiselect
|
||||
id="kafkaProducerBrokers"
|
||||
v-model="monitor.kafkaProducerBrokers"
|
||||
:multiple="true"
|
||||
:options="[]"
|
||||
:placeholder="$t('Enter the list of brokers')"
|
||||
:tag-placeholder="$t('Press Enter to add broker')"
|
||||
:max-height="500"
|
||||
:taggable="true"
|
||||
:show-no-options="false"
|
||||
:close-on-select="false"
|
||||
:clear-on-select="false"
|
||||
:preserve-search="false"
|
||||
:preselect-first="false"
|
||||
@tag="addKafkaProducerBroker"
|
||||
></VueMultiselect>
|
||||
<VueMultiselect id="kafkaProducerBrokers" v-model="monitor.kafkaProducerBrokers" :multiple="true" :options="[]" :placeholder="$t('Enter the list of brokers')" :tag-placeholder="$t('Press Enter to add broker')" :max-height="500" :taggable="true" :show-no-options="false" :close-on-select="false" :clear-on-select="false" :preserve-search="false" :preselect-first="false" @tag="addKafkaProducerBroker"></VueMultiselect>
|
||||
</div>
|
||||
|
||||
<!-- Kafka Topic Name -->
|
||||
|
@ -246,19 +229,65 @@
|
|||
</template>
|
||||
|
||||
<!-- Hostname -->
|
||||
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius / Tailscale Ping only -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'gamedig' ||monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'tailscale-ping'" class="my-3">
|
||||
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius / Tailscale Ping / SNMP only -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'tailscale-ping' || monitor.type === 'snmp'" class="my-3">
|
||||
<label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
|
||||
<input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${monitor.type === 'mqtt' ? mqttIpOrHostnameRegexPattern : ipOrHostnameRegexPattern}`" required>
|
||||
</div>
|
||||
|
||||
<!-- Port -->
|
||||
<!-- For TCP Port / Steam / MQTT / Radius Type -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius'" class="my-3">
|
||||
<!-- For TCP Port / Steam / MQTT / Radius Type / SNMP -->
|
||||
<div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'snmp'" class="my-3">
|
||||
<label for="port" class="form-label">{{ $t("Port") }}</label>
|
||||
<input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
|
||||
</div>
|
||||
|
||||
<!-- SNMP Monitor Type -->
|
||||
<div v-if="monitor.type === 'snmp'" class="my-3">
|
||||
<label for="snmp_community_string" class="form-label">{{ $t("Community String") }}</label>
|
||||
<input id="snmp_community_string" v-model="monitor.snmpCommunityString" type="text" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div v-if="monitor.type === 'snmp'" class="my-3">
|
||||
<label for="snmp_oid" class="form-label">{{ $t("OID (Object Identifier)") }}</label>
|
||||
<input id="snmp_oid" v-model="monitor.snmpOid" type="text" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div v-if="monitor.type === 'snmp'" class="my-3">
|
||||
<div class="d-flex align-items-start">
|
||||
<div>
|
||||
<label for="snmp_condition" class="form-label">{{ $t("Condition") }}</label>
|
||||
<select id="snmp_condition" v-model="monitor.snmpCondition" class="form-select me-3">
|
||||
<option value=">">></option>
|
||||
<option value=">=">>=</option>
|
||||
<option value="<"><</option>
|
||||
<option value="<="><=</option>
|
||||
<option value="==">==</option>
|
||||
<option value="contains">contains</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label for="snmp_control_value" class="form-label">{{ $t("Control Value") }}</label>
|
||||
<input id="snmp_control_value" v-model="monitor.snmpControlValue" type="number" class="form-control" required step=".01">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="monitor.type === 'snmp'" class="my-3">
|
||||
<label for="snmp_version" class="form-label">{{ $t("SNMP Version") }}</label>
|
||||
<select id="snmp_version" v-model="monitor.snmpVersion" class="form-select">
|
||||
<option value="1">
|
||||
SNMPv1
|
||||
</option>
|
||||
<option value="2c">
|
||||
SNMPv2c
|
||||
</option>
|
||||
<option value="3">
|
||||
SNMPv3
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- DNS Resolver Server -->
|
||||
<!-- For DNS Type -->
|
||||
<template v-if="monitor.type === 'dns'">
|
||||
|
@ -283,19 +312,7 @@
|
|||
<label for="dns_resolve_type" class="form-label">{{ $t("Resource Record Type") }}</label>
|
||||
|
||||
<!-- :allow-empty="false" is not working, set a default value instead https://github.com/shentao/vue-multiselect/issues/336 -->
|
||||
<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="$t('Pick a RR-Type...')"
|
||||
:preselect-first="false"
|
||||
:max-height="500"
|
||||
:taggable="false"
|
||||
></VueMultiselect>
|
||||
<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="$t('Pick a RR-Type...')" :preselect-first="false" :max-height="500" :taggable="false"></VueMultiselect>
|
||||
|
||||
<div class="form-text">
|
||||
{{ $t("rrtypeDescription") }}
|
||||
|
@ -315,16 +332,7 @@
|
|||
<div v-if="monitor.type === 'docker'" class="my-3">
|
||||
<div class="mb-3">
|
||||
<label for="docker-host" class="form-label">{{ $t("Docker Host") }}</label>
|
||||
<ActionSelect
|
||||
id="docker-host"
|
||||
v-model="monitor.docker_host"
|
||||
:action-aria-label="$t('openModalTo', $t('Setup Docker Host'))"
|
||||
:options="dockerHostOptionsList"
|
||||
:disabled="$root.dockerHostList == null || $root.dockerHostList.length === 0"
|
||||
:icon="'plus'"
|
||||
:action="() => $refs.dockerHostDialog.show()"
|
||||
:required="true"
|
||||
/>
|
||||
<ActionSelect id="docker-host" v-model="monitor.docker_host" :action-aria-label="$t('openModalTo', $t('Setup Docker Host'))" :options="dockerHostOptionsList" :disabled="$root.dockerHostList == null || $root.dockerHostList.length === 0" :icon="'plus'" :action="() => $refs.dockerHostDialog.show()" :required="true" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -394,19 +402,19 @@
|
|||
<div class="my-3">
|
||||
<label for="radius_secret" class="form-label">{{ $t("RadiusSecret") }}</label>
|
||||
<input id="radius_secret" v-model="monitor.radiusSecret" type="password" class="form-control" required />
|
||||
<div class="form-text"> {{ $t( "RadiusSecretDescription") }} </div>
|
||||
<div class="form-text"> {{ $t("RadiusSecretDescription") }} </div>
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="radius_called_station_id" class="form-label">{{ $t("RadiusCalledStationId") }}</label>
|
||||
<input id="radius_called_station_id" v-model="monitor.radiusCalledStationId" type="text" class="form-control" required />
|
||||
<div class="form-text"> {{ $t( "RadiusCalledStationIdDescription") }} </div>
|
||||
<div class="form-text"> {{ $t("RadiusCalledStationIdDescription") }} </div>
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="radius_calling_station_id" class="form-label">{{ $t("RadiusCallingStationId") }}</label>
|
||||
<input id="radius_calling_station_id" v-model="monitor.radiusCallingStationId" type="text" class="form-control" required />
|
||||
<div class="form-text"> {{ $t( "RadiusCallingStationIdDescription") }} </div>
|
||||
<div class="form-text"> {{ $t("RadiusCallingStationIdDescription") }} </div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -430,13 +438,13 @@
|
|||
<template v-if="monitor.type === 'sqlserver' || monitor.type === 'postgres' || monitor.type === 'mysql'">
|
||||
<div class="my-3">
|
||||
<label for="sqlQuery" class="form-label">{{ $t("Query") }}</label>
|
||||
<textarea id="sqlQuery" v-model="monitor.databaseQuery" class="form-control" :placeholder="$t('Example:', [ 'SELECT 1' ])"></textarea>
|
||||
<textarea id="sqlQuery" v-model="monitor.databaseQuery" class="form-control" :placeholder="$t('Example:', ['SELECT 1'])"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Interval -->
|
||||
<div class="my-3">
|
||||
<label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [ monitor.interval ]) }})</label>
|
||||
<label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [monitor.interval]) }})</label>
|
||||
<input id="interval" v-model="monitor.interval" type="number" class="form-control" required :min="minInterval" step="1" :max="maxInterval" @blur="finishUpdateInterval">
|
||||
</div>
|
||||
|
||||
|
@ -451,21 +459,21 @@
|
|||
<div class="my-3">
|
||||
<label for="retry-interval" class="form-label">
|
||||
{{ $t("Heartbeat Retry Interval") }}
|
||||
<span>({{ $t("retryCheckEverySecond", [ monitor.retryInterval ]) }})</span>
|
||||
<span>({{ $t("retryCheckEverySecond", [monitor.retryInterval]) }})</span>
|
||||
</label>
|
||||
<input id="retry-interval" v-model="monitor.retryInterval" type="number" class="form-control" required :min="minInterval" step="1">
|
||||
</div>
|
||||
|
||||
<!-- Timeout: HTTP / Keyword only -->
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query'" class="my-3">
|
||||
<label for="timeout" class="form-label">{{ $t("Request Timeout") }} ({{ $t("timeoutAfter", [ monitor.timeout || clampTimeout(monitor.interval) ]) }})</label>
|
||||
<label for="timeout" class="form-label">{{ $t("Request Timeout") }} ({{ $t("timeoutAfter", [monitor.timeout || clampTimeout(monitor.interval)]) }})</label>
|
||||
<input id="timeout" v-model="monitor.timeout" type="number" class="form-control" required min="0" step="0.1">
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="resend-interval" class="form-label">
|
||||
{{ $t("Resend Notification if Down X times consecutively") }}
|
||||
<span v-if="monitor.resendInterval > 0">({{ $t("resendEveryXTimes", [ monitor.resendInterval ]) }})</span>
|
||||
<span v-if="monitor.resendInterval > 0">({{ $t("resendEveryXTimes", [monitor.resendInterval]) }})</span>
|
||||
<span v-else>({{ $t("resendDisabled") }})</span>
|
||||
</label>
|
||||
<input id="resend-interval" v-model="monitor.resendInterval" type="number" class="form-control" required min="0" step="1">
|
||||
|
@ -473,7 +481,7 @@
|
|||
|
||||
<h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
|
||||
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' " class="my-3 form-check">
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query'" class="my-3 form-check">
|
||||
<input id="expiry-notification" v-model="monitor.expiryNotification" class="form-check-input" type="checkbox">
|
||||
<label class="form-check-label" for="expiry-notification">
|
||||
{{ $t("Certificate Expiry Notification") }}
|
||||
|
@ -482,7 +490,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' " class="my-3 form-check">
|
||||
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query'" 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") }}
|
||||
|
@ -516,7 +524,7 @@
|
|||
</div>
|
||||
|
||||
<!-- HTTP / Keyword only -->
|
||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'grpc-keyword' ">
|
||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'grpc-keyword'">
|
||||
<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">
|
||||
|
@ -528,19 +536,7 @@
|
|||
<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="$t('Pick Accepted Status Codes...')"
|
||||
:preselect-first="false"
|
||||
:max-height="600"
|
||||
:taggable="true"
|
||||
></VueMultiselect>
|
||||
<VueMultiselect id="acceptedStatusCodes" v-model="monitor.accepted_statuscodes" :options="acceptedStatusCodeOptions" :multiple="true" :close-on-select="false" :clear-on-select="false" :preserve-search="true" :placeholder="$t('Pick Accepted Status Codes...')" :preselect-first="false" :max-height="600" :taggable="true"></VueMultiselect>
|
||||
|
||||
<div class="form-text">
|
||||
{{ $t("acceptedStatusCodesDescription") }}
|
||||
|
@ -551,15 +547,7 @@
|
|||
<!-- Parent Monitor -->
|
||||
<div class="my-3">
|
||||
<label for="monitorGroupSelector" class="form-label">{{ $t("Monitor Group") }}</label>
|
||||
<ActionSelect
|
||||
id="monitorGroupSelector"
|
||||
v-model="monitor.parent"
|
||||
:action-aria-label="$t('openModalTo', 'setup a new monitor group')"
|
||||
:options="parentMonitorOptionsList"
|
||||
:disabled="sortedGroupMonitorList.length === 0 && draftGroupName == null"
|
||||
:icon="'plus'"
|
||||
:action="() => $refs.createGroupDialog.show()"
|
||||
/>
|
||||
<ActionSelect id="monitorGroupSelector" v-model="monitor.parent" :action-aria-label="$t('openModalTo', 'setup a new monitor group')" :options="parentMonitorOptionsList" :disabled="sortedGroupMonitorList.length === 0 && draftGroupName == null" :icon="'plus'" :action="() => $refs.createGroupDialog.show()" />
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
|
@ -583,9 +571,9 @@
|
|||
</p>
|
||||
|
||||
<div v-for="notification in $root.notificationList" :key="notification.id" class="form-check form-switch my-3">
|
||||
<input :id=" 'notification' + notification.id" v-model="monitor.notificationIDList[notification.id]" class="form-check-input" type="checkbox">
|
||||
<input :id="'notification' + notification.id" v-model="monitor.notificationIDList[notification.id]" class="form-check-input" type="checkbox">
|
||||
|
||||
<label class="form-check-label" :for=" 'notification' + notification.id">
|
||||
<label class="form-check-label" :for="'notification' + notification.id">
|
||||
{{ notification.name }}
|
||||
<a href="#" @click="$refs.notificationDialog.show(notification.id)">{{ $t("Edit") }}</a>
|
||||
</label>
|
||||
|
@ -633,19 +621,7 @@
|
|||
<label class="form-label" for="kafkaProducerSaslMechanism">
|
||||
{{ $t("Mechanism") }}
|
||||
</label>
|
||||
<VueMultiselect
|
||||
id="kafkaProducerSaslMechanism"
|
||||
v-model="monitor.kafkaProducerSaslOptions.mechanism"
|
||||
:options="kafkaSaslMechanismOptions"
|
||||
:multiple="false"
|
||||
:clear-on-select="false"
|
||||
:preserve-search="false"
|
||||
:placeholder="$t('Pick a SASL Mechanism...')"
|
||||
:preselect-first="false"
|
||||
:max-height="500"
|
||||
:allow-empty="false"
|
||||
:taggable="false"
|
||||
></VueMultiselect>
|
||||
<VueMultiselect id="kafkaProducerSaslMechanism" v-model="monitor.kafkaProducerSaslOptions.mechanism" :options="kafkaSaslMechanismOptions" :multiple="false" :clear-on-select="false" :preserve-search="false" :placeholder="$t('Pick a SASL Mechanism...')" :preselect-first="false" :max-height="500" :allow-empty="false" :taggable="false"></VueMultiselect>
|
||||
</div>
|
||||
<div v-if="monitor.kafkaProducerSaslOptions.mechanism !== 'None'">
|
||||
<div v-if="monitor.kafkaProducerSaslOptions.mechanism !== 'aws'" class="my-3">
|
||||
|
@ -676,7 +652,7 @@
|
|||
</template>
|
||||
|
||||
<!-- HTTP Options -->
|
||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' ">
|
||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query'">
|
||||
<h2 class="mt-5 mb-2">{{ $t("HTTP Options") }}</h2>
|
||||
|
||||
<!-- Method -->
|
||||
|
@ -753,8 +729,8 @@
|
|||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<template v-if="monitor.authMethod && monitor.authMethod !== null ">
|
||||
<template v-if="monitor.authMethod === 'mtls' ">
|
||||
<template v-if="monitor.authMethod && monitor.authMethod !== null">
|
||||
<template v-if="monitor.authMethod === 'mtls'">
|
||||
<div class="my-3">
|
||||
<label for="tls-cert" class="form-label">{{ $t("Cert") }}</label>
|
||||
<textarea id="tls-cert" v-model="monitor.tlsCert" class="form-control" :placeholder="$t('Cert body')" required></textarea>
|
||||
|
@ -768,7 +744,7 @@
|
|||
<textarea id="tls-ca" v-model="monitor.tlsCa" class="form-control" :placeholder="$t('Server CA')"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="monitor.authMethod === 'oauth2-cc' ">
|
||||
<template v-else-if="monitor.authMethod === 'oauth2-cc'">
|
||||
<div class="my-3">
|
||||
<label for="oauth_auth_method" class="form-label">{{ $t("Authentication Method") }}</label>
|
||||
<select id="oauth_auth_method" v-model="monitor.oauth_auth_method" class="form-select">
|
||||
|
@ -809,7 +785,7 @@
|
|||
<label for="basicauth-pass" class="form-label">{{ $t("Password") }}</label>
|
||||
<input id="basicauth-pass" v-model="monitor.basic_auth_pass" type="password" autocomplete="new-password" class="form-control" :placeholder="$t('Password')">
|
||||
</div>
|
||||
<template v-if="monitor.authMethod === 'ntlm' ">
|
||||
<template v-if="monitor.authMethod === 'ntlm'">
|
||||
<div class="my-3">
|
||||
<label for="ntlm-domain" class="form-label">{{ $t("Domain") }}</label>
|
||||
<input id="ntlm-domain" v-model="monitor.authDomain" type="text" class="form-control" :placeholder="$t('Domain')">
|
||||
|
@ -825,7 +801,7 @@
|
|||
</template>
|
||||
|
||||
<!-- gRPC Options -->
|
||||
<template v-if="monitor.type === 'grpc-keyword' ">
|
||||
<template v-if="monitor.type === 'grpc-keyword'">
|
||||
<!-- Proto service enable TLS -->
|
||||
<h2 class="mt-5 mb-2">{{ $t("GRPC Options") }}</h2>
|
||||
<div class="my-3 form-check">
|
||||
|
@ -926,7 +902,7 @@ const monitorDefaults = {
|
|||
packetSize: 56,
|
||||
expiryNotification: false,
|
||||
maxredirects: 10,
|
||||
accepted_statuscodes: [ "200-299" ],
|
||||
accepted_statuscodes: ["200-299"],
|
||||
dns_resolve_type: "A",
|
||||
dns_resolve_server: "1.1.1.1",
|
||||
docker_container: "",
|
||||
|
@ -947,7 +923,10 @@ const monitorDefaults = {
|
|||
kafkaProducerSsl: false,
|
||||
kafkaProducerAllowAutoTopicCreation: false,
|
||||
gamedigGivenPortOnly: true,
|
||||
remote_browser: null
|
||||
remote_browser: null,
|
||||
port: 161,
|
||||
communityString: 'public',
|
||||
oid: '1.3.6.1.2.1.1.1.0',
|
||||
};
|
||||
|
||||
export default {
|
||||
|
@ -996,7 +975,7 @@ export default {
|
|||
ipRegex() {
|
||||
|
||||
// Allow to test with simple dns server with port (127.0.0.1:5300)
|
||||
if (! isDev) {
|
||||
if (!isDev) {
|
||||
return this.ipRegexPattern;
|
||||
}
|
||||
return null;
|
||||
|
@ -1053,15 +1032,15 @@ export default {
|
|||
},
|
||||
|
||||
protoServicePlaceholder() {
|
||||
return this.$t("Example:", [ "Health" ]);
|
||||
return this.$t("Example:", ["Health"]);
|
||||
},
|
||||
|
||||
protoMethodPlaceholder() {
|
||||
return this.$t("Example:", [ "check" ]);
|
||||
return this.$t("Example:", ["check"]);
|
||||
},
|
||||
|
||||
protoBufDataPlaceholder() {
|
||||
return this.$t("Example:", [ `
|
||||
return this.$t("Example:", [`
|
||||
syntax = "proto3";
|
||||
|
||||
package grpc.health.v1;
|
||||
|
@ -1088,7 +1067,7 @@ message HealthCheckResponse {
|
|||
},
|
||||
bodyPlaceholder() {
|
||||
if (this.monitor && this.monitor.httpBodyEncoding && this.monitor.httpBodyEncoding === "xml") {
|
||||
return this.$t("Example:", [ `
|
||||
return this.$t("Example:", [`
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soap:Body>
|
||||
|
@ -1097,16 +1076,16 @@ message HealthCheckResponse {
|
|||
</soap:Envelope>` ]);
|
||||
}
|
||||
if (this.monitor && this.monitor.httpBodyEncoding === "form") {
|
||||
return this.$t("Example:", [ "key1=value1&key2=value2" ]);
|
||||
return this.$t("Example:", ["key1=value1&key2=value2"]);
|
||||
}
|
||||
return this.$t("Example:", [ `
|
||||
return this.$t("Example:", [`
|
||||
{
|
||||
"key": "value"
|
||||
}` ]);
|
||||
},
|
||||
|
||||
headersPlaceholder() {
|
||||
return this.$t("Example:", [ `
|
||||
return this.$t("Example:", [`
|
||||
{
|
||||
"HeaderName": "HeaderValue"
|
||||
}` ]);
|
||||
|
@ -1251,7 +1230,7 @@ message HealthCheckResponse {
|
|||
|
||||
"monitor.type"() {
|
||||
if (this.monitor.type === "push") {
|
||||
if (! this.monitor.pushToken) {
|
||||
if (!this.monitor.pushToken) {
|
||||
// ideally this would require checking if the generated token is already used
|
||||
// it's very unlikely to get a collision though (62^32 ~ 2.27265788 * 10^57 unique tokens)
|
||||
this.monitor.pushToken = genSecret(pushTokenLength);
|
||||
|
@ -1259,7 +1238,7 @@ message HealthCheckResponse {
|
|||
}
|
||||
|
||||
// Set default port for DNS if not already defined
|
||||
if (! this.monitor.port || this.monitor.port === "53" || this.monitor.port === "1812") {
|
||||
if (!this.monitor.port || this.monitor.port === "53" || this.monitor.port === "1812") {
|
||||
if (this.monitor.type === "dns") {
|
||||
this.monitor.port = "53";
|
||||
} else if (this.monitor.type === "radius") {
|
||||
|
@ -1399,7 +1378,7 @@ message HealthCheckResponse {
|
|||
this.monitor.pathName = undefined;
|
||||
this.monitor.screenshot = undefined;
|
||||
|
||||
this.monitor.name = this.$t("cloneOf", [ this.monitor.name ]);
|
||||
this.monitor.name = this.$t("cloneOf", [this.monitor.name]);
|
||||
this.$refs.tagsManager.newTags = this.monitor.tags.map((monitorTag) => {
|
||||
return {
|
||||
id: monitorTag.tag_id,
|
||||
|
@ -1486,7 +1465,7 @@ message HealthCheckResponse {
|
|||
this.monitor.body = JSON.stringify(JSON.parse(this.monitor.body), null, 4);
|
||||
}
|
||||
|
||||
const monitorTypesWithEncodingAllowed = [ "http", "keyword", "json-query" ];
|
||||
const monitorTypesWithEncodingAllowed = ["http", "keyword", "json-query"];
|
||||
if (this.monitor.type && !monitorTypesWithEncodingAllowed.includes(this.monitor.type)) {
|
||||
this.monitor.httpBodyEncoding = null;
|
||||
}
|
||||
|
@ -1564,7 +1543,7 @@ message HealthCheckResponse {
|
|||
|
||||
async startParentGroupMonitor() {
|
||||
await sleep(2000);
|
||||
await this.$root.getSocket().emit("resumeMonitor", this.monitor.parent, () => {});
|
||||
await this.$root.getSocket().emit("resumeMonitor", this.monitor.parent, () => { });
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1610,7 +1589,7 @@ message HealthCheckResponse {
|
|||
// Clamp timeout
|
||||
clampTimeout(timeout) {
|
||||
// limit to 80% of interval, narrowly avoiding epsilon bug
|
||||
const maxTimeout = ~~(this.monitor.interval * 8 ) / 10;
|
||||
const maxTimeout = ~~(this.monitor.interval * 8) / 10;
|
||||
const clamped = Math.max(0, Math.min(timeout, maxTimeout));
|
||||
|
||||
// 0 will be treated as 80% of interval
|
||||
|
@ -1630,9 +1609,9 @@ message HealthCheckResponse {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../assets/vars.scss";
|
||||
@import "../assets/vars.scss";
|
||||
|
||||
textarea {
|
||||
textarea {
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue