uptime-kuma/src/pages/Details.vue

795 lines
28 KiB
Vue
Raw Normal View History

2021-06-25 13:55:49 +00:00
<template>
2021-08-19 18:37:59 +00:00
<transition name="slide-fade" appear>
<div v-if="monitor">
2023-01-28 13:53:40 +00:00
<router-link v-if="group !== ''" :to="monitorURL(monitor.parent)"> {{ group }}</router-link>
2021-08-19 18:37:59 +00:00
<h1> {{ monitor.name }}</h1>
<p v-if="monitor.description">{{ monitor.description }}</p>
<div class="tags">
<Tag v-for="tag in monitor.tags" :key="tag.id" :item="tag" :size="'sm'" />
</div>
2021-08-19 18:37:59 +00:00
<p class="url">
✨ feat: json-query monitor added (#3253) * ✨ feat: json-query monitor added Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: import warning error Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: br tag and remove comment Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: supporting compare string with other types Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: switch to a better lib for json query Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: better description on json query and using `v-html` in jsonQueryDescription element to fix `a` tags Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: result variable in error message Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: typos in json query description Co-authored-by: Frank Elsinga <frank@elsinga.de> * 📝 docs: `HTTP(s) Json Query` added to monitor list in `README.md` Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: needed white space in `README.md` Co-authored-by: Frank Elsinga <frank@elsinga.de> * Nostr dm notifications (#3051) * Add nostr DM notification provider * require crypto for node 18 compatibility * remove whitespace Co-authored-by: Frank Elsinga <frank@elsinga.de> * move closer to where it is used * simplify success or failure logic * don't clobber the non-alert msg * Update server/notification-providers/nostr.js Co-authored-by: Frank Elsinga <frank@elsinga.de> * polyfills required for node <= 18 * resolve linter warnings * missing comma --------- Co-authored-by: Frank Elsinga <frank@elsinga.de> * Drop nostr * Rebuild package-lock.json * Lint --------- Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> Co-authored-by: Frank Elsinga <frank@elsinga.de> Co-authored-by: zappityzap <128872140+zappityzap@users.noreply.github.com> Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
2023-07-13 15:37:26 +00:00
<a v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'mp-health' " :href="monitor.url" target="_blank" rel="noopener noreferrer">{{ filterPassword(monitor.url) }}</a>
2023-04-27 07:34:01 +00:00
<span v-if="monitor.type === 'port'">TCP Port {{ monitor.hostname }}:{{ monitor.port }}</span>
2021-08-19 18:37:59 +00:00
<span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
<span v-if="monitor.type === 'keyword'">
<br>
2023-04-06 00:10:21 +00:00
<span>{{ $t("Keyword") }}: </span>
<span class="keyword">{{ monitor.keyword }}</span>
<span v-if="monitor.invertKeyword" alt="Inverted keyword" class="keyword-inverted"> </span>
2021-08-19 18:37:59 +00:00
</span>
✨ feat: json-query monitor added (#3253) * ✨ feat: json-query monitor added Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: import warning error Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: br tag and remove comment Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: supporting compare string with other types Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: switch to a better lib for json query Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: better description on json query and using `v-html` in jsonQueryDescription element to fix `a` tags Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: result variable in error message Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: typos in json query description Co-authored-by: Frank Elsinga <frank@elsinga.de> * 📝 docs: `HTTP(s) Json Query` added to monitor list in `README.md` Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: needed white space in `README.md` Co-authored-by: Frank Elsinga <frank@elsinga.de> * Nostr dm notifications (#3051) * Add nostr DM notification provider * require crypto for node 18 compatibility * remove whitespace Co-authored-by: Frank Elsinga <frank@elsinga.de> * move closer to where it is used * simplify success or failure logic * don't clobber the non-alert msg * Update server/notification-providers/nostr.js Co-authored-by: Frank Elsinga <frank@elsinga.de> * polyfills required for node <= 18 * resolve linter warnings * missing comma --------- Co-authored-by: Frank Elsinga <frank@elsinga.de> * Drop nostr * Rebuild package-lock.json * Lint --------- Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> Co-authored-by: Frank Elsinga <frank@elsinga.de> Co-authored-by: zappityzap <128872140+zappityzap@users.noreply.github.com> Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
2023-07-13 15:37:26 +00:00
<span v-if="monitor.type === 'json-query'">
<br>
<span>{{ $t("Json Query") }}:</span> <span class="keyword">{{ monitor.jsonPath }}</span>
<br>
<span>{{ $t("Expected Value") }}:</span> <span class="keyword">{{ monitor.expectedValue }}</span>
</span>
2021-08-29 01:57:26 +00:00
<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>
</span>
<span v-if="monitor.type === 'docker'">Docker container: {{ monitor.docker_container }}</span>
<span v-if="monitor.type === 'gamedig'">Gamedig - {{ monitor.game }}: {{ monitor.hostname }}:{{ monitor.port }}</span>
2023-04-18 19:20:35 +00:00
<span v-if="monitor.type === 'grpc-keyword'">gRPC - {{ filterPassword(monitor.grpcUrl) }}
<br>
<span>{{ $t("Keyword") }}:</span> <span class="keyword">{{ monitor.keyword }}</span>
</span>
2023-04-18 19:20:35 +00:00
<span v-if="monitor.type === 'mongodb'">{{ filterPassword(monitor.databaseConnectionString) }}</span>
<span v-if="monitor.type === 'mqtt'">MQTT: {{ monitor.hostname }}:{{ monitor.port }}/{{ monitor.mqttTopic }}</span>
2023-04-18 19:20:35 +00:00
<span v-if="monitor.type === 'mysql'">{{ filterPassword(monitor.databaseConnectionString) }}</span>
<span v-if="monitor.type === 'postgres'">{{ filterPassword(monitor.databaseConnectionString) }}</span>
<span v-if="monitor.type === 'push'">Push: <a :href="pushURL" target="_blank" rel="noopener noreferrer">{{ pushURL }}</a></span>
2023-04-18 19:20:35 +00:00
<span v-if="monitor.type === 'radius'">Radius: {{ filterPassword(monitor.hostname) }}</span>
<span v-if="monitor.type === 'redis'">{{ filterPassword(monitor.databaseConnectionString) }}</span>
<span v-if="monitor.type === 'sqlserver'">SQL Server: {{ filterPassword(monitor.databaseConnectionString) }}</span>
<span v-if="monitor.type === 'steam'">Steam Game Server: {{ monitor.hostname }}:{{ monitor.port }}</span>
2021-08-19 18:37:59 +00:00
</p>
2021-06-25 13:55:49 +00:00
2021-08-19 18:37:59 +00:00
<div class="functions">
2022-10-15 13:01:48 +00:00
<div class="btn-group" role="group">
<button v-if="monitor.active" class="btn btn-normal" @click="pauseDialog">
<font-awesome-icon icon="pause" /> {{ $t("Pause") }}
</button>
<button v-if="! monitor.active" class="btn btn-primary" :disabled="monitor.forceInactive" @click="resumeMonitor">
2022-10-15 13:01:48 +00:00
<font-awesome-icon icon="play" /> {{ $t("Resume") }}
</button>
<router-link :to=" '/edit/' + monitor.id " class="btn btn-normal">
<font-awesome-icon icon="edit" /> {{ $t("Edit") }}
</router-link>
<router-link :to=" '/clone/' + monitor.id " class="btn btn-normal">
<font-awesome-icon icon="clone" /> {{ $t("Clone") }}
</router-link>
2022-10-15 13:01:48 +00:00
<button class="btn btn-danger" @click="deleteDialog">
<font-awesome-icon icon="trash" /> {{ $t("Delete") }}
</button>
</div>
2021-06-25 13:55:49 +00:00
</div>
2021-08-19 18:37:59 +00:00
<div class="shadow-box">
<div class="row">
<div class="col-md-8">
<HeartbeatBar :monitor-id="monitor.id" />
<span class="word">{{ $t("checkEverySecond", [ monitor.interval ]) }}</span>
2021-08-19 18:37:59 +00:00
</div>
<div class="col-md-4 text-center">
2021-10-18 16:19:26 +00:00
<span class="badge rounded-pill" :class=" 'bg-' + status.color " style="font-size: 30px;">{{ status.text }}</span>
2021-08-19 18:37:59 +00:00
</div>
</div>
2021-06-30 13:04:58 +00:00
</div>
2021-07-26 14:53:07 +00:00
<!-- Push Examples -->
<div v-if="monitor.type === 'push'" class="shadow-box big-padding">
<a href="#" @click="pushMonitor.showPushExamples = !pushMonitor.showPushExamples">{{ $t("pushViewCode") }}</a>
<transition name="slide-fade" appear>
<div v-if="pushMonitor.showPushExamples" class="mt-3">
<select id="push-current-example" v-model="pushMonitor.currentExample" class="form-select">
<optgroup :label="$t('programmingLanguages')">
<option value="csharp">C#</option>
<option value="go">Go</option>
<option value="java">Java</option>
<option value="javascript-fetch">JavaScript (fetch)</option>
<option value="php">PHP</option>
<option value="python">Python</option>
<option value="typescript-fetch">TypeScript (fetch)</option>
</optgroup>
<optgroup :label="$t('pushOthers')">
<option value="bash-curl">Bash (curl)</option>
<option value="powershell">PowerShell</option>
<option value="docker">Docker</option>
</optgroup>
</select>
<prism-editor v-model="pushMonitor.code" class="css-editor mt-3" :highlight="pushExampleHighlighter" line-numbers readonly></prism-editor>
</div>
</transition>
</div>
2023-06-27 07:54:33 +00:00
<!-- Stats -->
2021-08-19 18:37:59 +00:00
<div class="shadow-box big-padding text-center stats">
<div class="row">
<div v-if="monitor.type !== 'group'" class="col-12 col-sm col row d-flex align-items-center d-sm-block">
2023-04-11 13:16:45 +00:00
<h4 class="col-4 col-sm-12">{{ pingTitle() }}</h4>
<p class="col-4 col-sm-12 mb-0 mb-sm-2">({{ $t("Current") }})</p>
<span class="col-4 col-sm-12 num">
2021-08-19 18:37:59 +00:00
<a href="#" @click.prevent="showPingChartBox = !showPingChartBox">
<CountUp :value="ping" />
</a>
</span>
</div>
<div v-if="monitor.type !== 'group'" class="col-12 col-sm col row d-flex align-items-center d-sm-block">
2023-04-11 13:16:45 +00:00
<h4 class="col-4 col-sm-12">{{ pingTitle(true) }}</h4>
<p class="col-4 col-sm-12 mb-0 mb-sm-2">(24{{ $t("-hour") }})</p>
<span class="col-4 col-sm-12 num">
<CountUp :value="avgPing" />
</span>
2021-08-19 18:37:59 +00:00
</div>
<!-- Uptime (24-hour) -->
2023-04-11 13:16:45 +00:00
<div class="col-12 col-sm col row d-flex align-items-center d-sm-block">
<h4 class="col-4 col-sm-12">{{ $t("Uptime") }}</h4>
<p class="col-4 col-sm-12 mb-0 mb-sm-2">(24{{ $t("-hour") }})</p>
<span class="col-4 col-sm-12 num">
<Uptime :monitor="monitor" type="24" />
</span>
2021-08-19 18:37:59 +00:00
</div>
<!-- Uptime (30-day) -->
2023-04-11 13:16:45 +00:00
<div class="col-12 col-sm col row d-flex align-items-center d-sm-block">
<h4 class="col-4 col-sm-12">{{ $t("Uptime") }}</h4>
<p class="col-4 col-sm-12 mb-0 mb-sm-2">(30{{ $t("-day") }})</p>
<span class="col-4 col-sm-12 num">
<Uptime :monitor="monitor" type="720" />
</span>
2021-08-19 18:37:59 +00:00
</div>
<!-- Uptime (1-year) -->
<div class="col-12 col-sm col row d-flex align-items-center d-sm-block">
<h4 class="col-4 col-sm-12">{{ $t("Uptime") }}</h4>
<p class="col-4 col-sm-12 mb-0 mb-sm-2">(1{{ $t("-year") }})</p>
<span class="col-4 col-sm-12 num">
<Uptime :monitor="monitor" type="1y" />
</span>
</div>
2023-04-11 13:16:45 +00:00
<div v-if="tlsInfo" class="col-12 col-sm col row d-flex align-items-center d-sm-block">
<h4 class="col-4 col-sm-12">{{ $t("Cert Exp.") }}</h4>
<p class="col-4 col-sm-12 mb-0 mb-sm-2">(<Datetime :value="tlsInfo.certInfo.validTo" date-only />)</p>
<span class="col-4 col-sm-12 num">
2022-05-30 09:53:32 +00:00
<a href="#" @click.prevent="toggleCertInfoBox = !toggleCertInfoBox">{{ tlsInfo.certInfo.daysRemaining }} {{ $tc("day", tlsInfo.certInfo.daysRemaining) }}</a>
2021-08-19 18:37:59 +00:00
</span>
</div>
</div>
2021-07-26 14:53:07 +00:00
</div>
2021-06-30 13:04:58 +00:00
<!-- Cert Info Box -->
2021-08-19 18:37:59 +00:00
<transition name="slide-fade" appear>
<div v-if="showCertInfoBox" class="shadow-box big-padding text-center">
<div class="row">
<div class="col">
2021-10-01 10:44:32 +00:00
<certificate-info :certInfo="tlsInfo.certInfo" :valid="tlsInfo.valid" />
2021-08-19 18:37:59 +00:00
</div>
</div>
</div>
</transition>
<!-- Ping Chart -->
2021-08-19 18:37:59 +00:00
<div v-if="showPingChartBox" class="shadow-box big-padding text-center ping-chart-wrapper">
<div class="row">
<div class="col">
<PingChart :monitor-id="monitor.id" />
</div>
</div>
2021-08-10 11:34:47 +00:00
</div>
2023-06-27 07:54:33 +00:00
<!-- Screenshot -->
<div v-if="monitor.type === 'real-browser'" class="shadow-box">
<div class="row">
<div class="col-md-6">
<img :src="screenshotURL" alt style="width: 100%;">
</div>
</div>
</div>
<div class="shadow-box table-shadow-box">
<div class="dropdown dropdown-clear-data">
<button class="btn btn-sm btn-outline-danger dropdown-toggle" type="button" data-bs-toggle="dropdown">
<font-awesome-icon icon="trash" /> {{ $t("Clear Data") }}
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<button type="button" class="dropdown-item" @click="clearEventsDialog">
{{ $t("Events") }}
</button>
</li>
<li>
<button type="button" class="dropdown-item" @click="clearHeartbeatsDialog">
{{ $t("Heartbeats") }}
</button>
</li>
</ul>
</div>
2021-08-19 18:37:59 +00:00
<table class="table table-borderless table-hover">
<thead>
<tr>
<th>{{ $t("Status") }}</th>
<th>{{ $t("DateTime") }}</th>
<th>{{ $t("Message") }}</th>
</tr>
2021-08-19 18:37:59 +00:00
</thead>
<tbody>
2023-04-11 13:16:45 +00:00
<tr v-for="(beat, index) in displayedRecords" :key="index" style="padding: 10px;">
2021-08-19 18:37:59 +00:00
<td><Status :status="beat.status" /></td>
2021-08-22 23:22:55 +00:00
<td :class="{ 'border-0':! beat.msg}"><Datetime :value="beat.time" /></td>
<td class="border-0">{{ beat.msg }}</td>
</tr>
2021-08-19 18:37:59 +00:00
<tr v-if="importantHeartBeatListLength === 0">
2021-08-19 18:37:59 +00:00
<td colspan="3">
{{ $t("No important events") }}
2021-07-27 17:47:13 +00:00
</td>
</tr>
</tbody>
</table>
2021-08-19 18:37:59 +00:00
<div class="d-flex justify-content-center kuma_pagination">
<pagination
v-model="page"
:records="importantHeartBeatListLength"
2021-08-19 18:37:59 +00:00
:per-page="perPage"
2021-09-26 15:20:12 +00:00
:options="paginationConfig"
2021-08-19 18:37:59 +00:00
/>
</div>
</div>
2021-06-30 13:04:58 +00:00
<Confirm ref="confirmPause" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="pauseMonitor">
{{ $t("pauseMonitorMsg") }}
2021-08-19 18:37:59 +00:00
</Confirm>
2021-07-18 01:04:40 +00:00
2021-08-26 00:43:26 +00:00
<Confirm ref="confirmDelete" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="deleteMonitor">
{{ $t("deleteMonitorMsg") }}
2021-08-19 18:37:59 +00:00
</Confirm>
<Confirm ref="confirmClearEvents" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="clearEvents">
{{ $t("clearEventsMsg") }}
</Confirm>
<Confirm ref="confirmClearHeartbeats" btn-style="btn-danger" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="clearHeartbeats">
{{ $t("clearHeartbeatsMsg") }}
</Confirm>
2021-07-18 01:04:40 +00:00
</div>
2021-08-19 18:37:59 +00:00
</transition>
2021-06-25 13:55:49 +00:00
</template>
<script>
2021-08-11 16:47:58 +00:00
import { defineAsyncComponent } from "vue";
2021-10-01 10:44:32 +00:00
import { useToast } from "vue-toastification";
const toast = useToast();
2021-06-25 13:55:49 +00:00
import Confirm from "../components/Confirm.vue";
2021-06-25 19:03:06 +00:00
import HeartbeatBar from "../components/HeartbeatBar.vue";
2021-06-30 13:04:58 +00:00
import Status from "../components/Status.vue";
import Datetime from "../components/Datetime.vue";
import CountUp from "../components/CountUp.vue";
2021-07-01 05:11:16 +00:00
import Uptime from "../components/Uptime.vue";
2021-07-18 01:04:40 +00:00
import Pagination from "v-pagination-3";
2021-08-11 16:47:58 +00:00
const PingChart = defineAsyncComponent(() => import("../components/PingChart.vue"));
import Tag from "../components/Tag.vue";
2021-10-01 10:44:32 +00:00
import CertificateInfo from "../components/CertificateInfo.vue";
2023-01-28 13:53:40 +00:00
import { getMonitorRelativeURL } from "../util.ts";
2023-04-18 19:20:35 +00:00
import { URL } from "whatwg-url";
2023-06-27 07:54:33 +00:00
import { getResBaseURL } from "../util-frontend";
import { highlight, languages } from "prismjs/components/prism-core";
import "prismjs/components/prism-clike";
import "prismjs/components/prism-javascript";
import "prismjs/components/prism-css";
import { PrismEditor } from "vue-prism-editor";
import "vue-prism-editor/dist/prismeditor.min.css";
2021-06-25 13:55:49 +00:00
export default {
components: {
2021-07-01 05:11:16 +00:00
Uptime,
2021-06-30 13:04:58 +00:00
CountUp,
Datetime,
2021-06-25 19:03:06 +00:00
HeartbeatBar,
2021-06-30 13:04:58 +00:00
Confirm,
Status,
2021-07-18 01:04:40 +00:00
Pagination,
2021-08-10 11:34:47 +00:00
PingChart,
Tag,
2021-10-01 10:44:32 +00:00
CertificateInfo,
PrismEditor,
2021-06-25 13:55:49 +00:00
},
data() {
return {
2021-07-18 01:04:40 +00:00
page: 1,
perPage: 25,
heartBeatList: [],
2021-07-26 14:53:07 +00:00
toggleCertInfoBox: false,
2021-08-11 12:59:33 +00:00
showPingChartBox: true,
2021-09-26 15:20:12 +00:00
paginationConfig: {
2021-10-29 11:37:38 +00:00
hideCount: true,
chunksNavigation: "scroll",
},
2023-06-27 07:54:33 +00:00
cacheTime: Date.now(),
importantHeartBeatListLength: 0,
displayedRecords: [],
pushMonitor: {
showPushExamples: false,
currentExample: "javascript-fetch",
code: "",
},
2021-10-01 10:44:32 +00:00
};
2021-06-25 13:55:49 +00:00
},
computed: {
monitor() {
2021-10-01 10:44:32 +00:00
let id = this.$route.params.id;
2021-06-27 08:10:55 +00:00
return this.$root.monitorList[id];
},
2021-06-25 13:55:49 +00:00
2021-06-27 08:10:55 +00:00
lastHeartBeat() {
2023-06-27 07:54:33 +00:00
// Also trigger screenshot refresh here
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
this.cacheTime = Date.now();
2021-06-27 08:10:55 +00:00
if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) {
2021-10-01 10:44:32 +00:00
return this.$root.lastHeartbeatList[this.monitor.id];
2021-07-27 17:47:13 +00:00
}
return {
status: -1,
2021-10-01 10:44:32 +00:00
};
2021-06-25 13:55:49 +00:00
},
2021-06-27 08:10:55 +00:00
2021-06-30 13:04:58 +00:00
ping() {
2021-07-12 03:20:18 +00:00
if (this.lastHeartBeat.ping || this.lastHeartBeat.ping === 0) {
2021-06-30 13:04:58 +00:00
return this.lastHeartBeat.ping;
}
2021-07-27 17:47:13 +00:00
2021-10-01 10:44:32 +00:00
return this.$t("notAvailableShort");
2021-06-30 13:04:58 +00:00
},
avgPing() {
2021-07-12 03:20:18 +00:00
if (this.$root.avgPingList[this.monitor.id] || this.$root.avgPingList[this.monitor.id] === 0) {
2021-06-30 13:04:58 +00:00
return this.$root.avgPingList[this.monitor.id];
}
2021-07-27 17:47:13 +00:00
2021-10-01 10:44:32 +00:00
return this.$t("notAvailableShort");
2021-06-30 13:04:58 +00:00
},
2021-06-27 08:10:55 +00:00
status() {
if (this.$root.statusList[this.monitor.id]) {
2021-10-01 10:44:32 +00:00
return this.$root.statusList[this.monitor.id];
2021-06-27 08:10:55 +00:00
}
2021-07-27 17:47:13 +00:00
2021-10-01 10:44:32 +00:00
return { };
2021-07-18 01:04:40 +00:00
},
2021-06-27 08:10:55 +00:00
2021-10-01 10:44:32 +00:00
tlsInfo() {
// Add: this.$root.tlsInfoList[this.monitor.id].certInfo
// Fix: TypeError: Cannot read properties of undefined (reading 'validTo')
// Reason: TLS Info object format is changed in 1.8.0, if for some reason, it cannot connect to the site after update to 1.8.0, the object is still in the old format.
if (this.$root.tlsInfoList[this.monitor.id] && this.$root.tlsInfoList[this.monitor.id].certInfo) {
2021-10-01 10:44:32 +00:00
return this.$root.tlsInfoList[this.monitor.id];
}
2021-07-27 17:47:13 +00:00
2021-10-01 10:44:32 +00:00
return null;
},
2021-07-26 14:53:07 +00:00
showCertInfoBox() {
2021-10-01 10:44:32 +00:00
return this.tlsInfo != null && this.toggleCertInfoBox;
2021-07-26 14:53:07 +00:00
},
group() {
if (!this.monitor.pathName.includes("/")) {
return "";
}
return this.monitor.pathName.substr(0, this.monitor.pathName.lastIndexOf("/"));
2023-05-31 19:19:46 +00:00
},
pushURL() {
return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?status=up&msg=OK&ping=";
},
2023-06-27 07:54:33 +00:00
screenshotURL() {
return getResBaseURL() + this.monitor.screenshot + "?time=" + this.cacheTime;
}
2021-07-27 17:47:13 +00:00
},
watch: {
page(to) {
this.getImportantHeartbeatListPaged();
},
monitor(to) {
this.getImportantHeartbeatListLength();
},
"monitor.type"() {
if (this.monitor && this.monitor.type === "push") {
this.loadPushExample();
}
},
"pushMonitor.currentExample"() {
this.loadPushExample();
},
},
2021-07-27 17:47:13 +00:00
mounted() {
this.getImportantHeartbeatListLength();
2021-07-27 17:47:13 +00:00
this.$root.emitter.on("newImportantHeartbeat", this.onNewImportantHeartbeat);
if (this.monitor && this.monitor.type === "push") {
if (this.lastHeartBeat.status === -1) {
this.pushMonitor.showPushExamples = true;
}
this.loadPushExample();
}
2021-06-25 13:55:49 +00:00
},
beforeUnmount() {
this.$root.emitter.off("newImportantHeartbeat", this.onNewImportantHeartbeat);
},
2021-06-25 13:55:49 +00:00
methods: {
2023-06-27 07:54:33 +00:00
getResBaseURL,
/**
* Request a test notification be sent for this monitor
* @returns {void}
*/
2021-06-30 13:04:58 +00:00
testNotification() {
2021-10-01 10:44:32 +00:00
this.$root.getSocket().emit("testNotification", this.monitor.id);
2023-10-14 11:00:27 +00:00
this.$root.toastSuccess("Test notification is requested.");
2021-06-30 13:04:58 +00:00
},
/**
* Show dialog to confirm pause
* @returns {void}
*/
2021-06-25 13:55:49 +00:00
pauseDialog() {
this.$refs.confirmPause.show();
},
2021-06-30 13:04:58 +00:00
/**
* Resume this monitor
* @returns {void}
*/
2021-06-25 13:55:49 +00:00
resumeMonitor() {
this.$root.getSocket().emit("resumeMonitor", this.monitor.id, (res) => {
2021-10-01 10:44:32 +00:00
this.$root.toastRes(res);
});
2021-06-25 13:55:49 +00:00
},
2021-06-30 13:04:58 +00:00
/**
* Request that this monitor is paused
* @returns {void}
*/
2021-06-25 13:55:49 +00:00
pauseMonitor() {
this.$root.getSocket().emit("pauseMonitor", this.monitor.id, (res) => {
2021-10-01 10:44:32 +00:00
this.$root.toastRes(res);
});
2021-06-25 13:55:49 +00:00
},
2021-06-30 13:04:58 +00:00
/**
* Show dialog to confirm deletion
* @returns {void}
*/
2021-06-25 13:55:49 +00:00
deleteDialog() {
this.$refs.confirmDelete.show();
},
2021-06-30 13:04:58 +00:00
/**
* Show dialog to confirm clearing events
* @returns {void}
*/
clearEventsDialog() {
this.$refs.confirmClearEvents.show();
},
/**
* Show dialog to confirm clearing heartbeats
* @returns {void}
*/
clearHeartbeatsDialog() {
this.$refs.confirmClearHeartbeats.show();
},
/**
* Request that this monitor is deleted
* @returns {void}
*/
2021-06-25 13:55:49 +00:00
deleteMonitor() {
this.$root.deleteMonitor(this.monitor.id, (res) => {
2023-10-14 11:00:27 +00:00
this.$root.toastRes(res);
2021-06-25 13:55:49 +00:00
if (res.ok) {
2021-10-01 10:44:32 +00:00
this.$router.push("/dashboard");
2021-06-25 13:55:49 +00:00
}
2021-10-01 10:44:32 +00:00
});
2021-07-27 17:47:13 +00:00
},
2021-06-30 13:04:58 +00:00
/**
* Request that this monitors events are cleared
* @returns {void}
*/
clearEvents() {
this.$root.clearEvents(this.monitor.id, (res) => {
if (res.ok) {
this.getImportantHeartbeatListLength();
} else {
toast.error(res.msg);
}
2021-10-01 10:44:32 +00:00
});
},
/**
* Request that this monitors heartbeats are cleared
* @returns {void}
*/
clearHeartbeats() {
this.$root.clearHeartbeats(this.monitor.id, (res) => {
if (! res.ok) {
toast.error(res.msg);
}
2021-10-01 10:44:32 +00:00
});
},
/**
* Return the correct title for the ping stat
* @param {boolean} average Is the statistic an average?
* @returns {string} Title formatted dependant on monitor type
*/
pingTitle(average = false) {
2021-10-01 10:44:32 +00:00
let translationPrefix = "";
if (average) {
2021-10-01 10:44:32 +00:00
translationPrefix = "Avg. ";
}
✨ feat: json-query monitor added (#3253) * ✨ feat: json-query monitor added Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: import warning error Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: br tag and remove comment Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: supporting compare string with other types Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: switch to a better lib for json query Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: better description on json query and using `v-html` in jsonQueryDescription element to fix `a` tags Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: result variable in error message Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: typos in json query description Co-authored-by: Frank Elsinga <frank@elsinga.de> * 📝 docs: `HTTP(s) Json Query` added to monitor list in `README.md` Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> * 🐛 fix: needed white space in `README.md` Co-authored-by: Frank Elsinga <frank@elsinga.de> * Nostr dm notifications (#3051) * Add nostr DM notification provider * require crypto for node 18 compatibility * remove whitespace Co-authored-by: Frank Elsinga <frank@elsinga.de> * move closer to where it is used * simplify success or failure logic * don't clobber the non-alert msg * Update server/notification-providers/nostr.js Co-authored-by: Frank Elsinga <frank@elsinga.de> * polyfills required for node <= 18 * resolve linter warnings * missing comma --------- Co-authored-by: Frank Elsinga <frank@elsinga.de> * Drop nostr * Rebuild package-lock.json * Lint --------- Signed-off-by: Muhammed Hussein Karimi <info@karimi.dev> Co-authored-by: Frank Elsinga <frank@elsinga.de> Co-authored-by: zappityzap <128872140+zappityzap@users.noreply.github.com> Co-authored-by: Louis Lam <louislam@users.noreply.github.com>
2023-07-13 15:37:26 +00:00
if (this.monitor.type === "http" || this.monitor.type === "keyword" || this.monitor.type === "json-query") {
return this.$t(translationPrefix + "Response");
}
return this.$t(translationPrefix + "Ping");
},
2023-01-28 13:53:40 +00:00
/**
* Get URL of monitor
* @param {number} id ID of monitor
* @returns {string} Relative URL of monitor
*/
monitorURL(id) {
return getMonitorRelativeURL(id);
},
2023-05-31 19:19:46 +00:00
/**
* Filter and hide password in URL for display
* @param {string} urlString URL to censor
* @returns {string} Censored URL
*/
2023-04-18 19:20:35 +00:00
filterPassword(urlString) {
try {
let parsedUrl = new URL(urlString);
if (parsedUrl.password !== "") {
parsedUrl.password = "******";
}
return parsedUrl.toString();
} catch (e) {
// Handle SQL Server
return urlString.replaceAll(/Password=(.+);/ig, "Password=******;");
}
},
/**
* Retrieves the length of the important heartbeat list for this monitor.
* @returns {void}
*/
getImportantHeartbeatListLength() {
if (this.monitor) {
this.$root.getSocket().emit("monitorImportantHeartbeatListCount", this.monitor.id, (res) => {
if (res.ok) {
this.importantHeartBeatListLength = res.count;
this.getImportantHeartbeatListPaged();
}
});
}
},
/**
* Retrieves the important heartbeat list for the current page.
* @returns {void}
*/
getImportantHeartbeatListPaged() {
if (this.monitor) {
const offset = (this.page - 1) * this.perPage;
this.$root.getSocket().emit("monitorImportantHeartbeatListPaged", this.monitor.id, offset, this.perPage, (res) => {
if (res.ok) {
this.displayedRecords = res.data;
}
});
}
},
/**
* Updates the displayed records when a new important heartbeat arrives.
* @param {object} heartbeat - The heartbeat object received.
* @returns {void}
*/
onNewImportantHeartbeat(heartbeat) {
if (heartbeat.monitorID === this.monitor?.id) {
if (this.page === 1) {
this.displayedRecords.unshift(heartbeat);
if (this.displayedRecords.length > this.perPage) {
this.displayedRecords.pop();
}
this.importantHeartBeatListLength += 1;
}
}
},
/**
* Highlight the example code
* @param {string} code Code
* @returns {string} Highlighted code
*/
pushExampleHighlighter(code) {
return highlight(code, languages.js);
},
loadPushExample() {
this.pushMonitor.code = "";
this.$root.getSocket().emit("getPushExample", this.pushMonitor.currentExample, (res) => {
let code = res.code
.replace("60", this.monitor.interval)
.replace("https://example.com/api/push/key?status=up&msg=OK&ping=", this.pushURL);
this.pushMonitor.code = code;
});
}
2021-07-27 17:47:13 +00:00
},
2021-10-01 10:44:32 +00:00
};
2021-06-25 13:55:49 +00:00
</script>
<style lang="scss" scoped>
@import "../assets/vars.scss";
2021-08-18 04:22:45 +00:00
@media (max-width: 767px) {
.badge {
margin-top: 14px;
}
}
2021-08-18 04:22:45 +00:00
@media (max-width: 550px) {
.functions {
text-align: center;
}
2021-08-18 04:22:45 +00:00
.ping-chart-wrapper {
padding: 10px !important;
}
.dropdown-clear-data {
margin-bottom: 10px;
}
}
2021-08-18 04:22:45 +00:00
@media (max-width: 400px) {
.btn {
display: inline-flex;
flex-direction: column;
align-items: center;
padding-top: 10px;
2023-04-11 13:16:45 +00:00
font-size: 0.9em;
}
a.btn {
padding-left: 25px;
padding-right: 25px;
}
.dropdown-clear-data {
button {
display: block;
padding-top: 4px;
}
}
}
2021-06-27 08:10:55 +00:00
.url {
2021-06-25 13:55:49 +00:00
color: $primary;
margin-bottom: 20px;
2021-06-27 08:10:55 +00:00
font-weight: bold;
a {
color: $primary;
}
2021-06-25 13:55:49 +00:00
}
.shadow-box {
padding: 20px;
margin-top: 25px;
}
2021-06-27 08:10:55 +00:00
.word {
2021-08-24 15:38:25 +00:00
color: #aaa;
2021-06-27 08:10:55 +00:00
font-size: 14px;
}
2021-06-30 13:04:58 +00:00
table {
font-size: 14px;
tr {
transition: all ease-in-out 0.2ms;
}
}
.stats p {
font-size: 13px;
2021-08-24 15:38:25 +00:00
color: #aaa;
2021-06-30 13:04:58 +00:00
}
2021-07-26 14:53:07 +00:00
.stats {
padding: 10px;
.col {
margin: 20px 0;
}
}
2023-04-11 13:16:45 +00:00
@media (max-width: 550px) {
.stats {
.col {
margin: 10px 0 !important;
}
2023-04-11 13:27:03 +00:00
2023-04-11 13:16:45 +00:00
h4 {
font-size: 1.1rem;
}
}
}
.keyword {
color: black;
}
.dropdown-clear-data {
float: right;
ul {
width: 100%;
min-width: unset;
padding-left: 0;
}
}
2021-08-24 15:38:25 +00:00
.dark {
.keyword {
color: $dark-font-color;
}
2023-04-06 00:10:21 +00:00
.keyword-inverted {
color: $dark-font-color;
}
.dropdown-clear-data {
ul {
background-color: $dark-bg;
border-color: $dark-bg2;
border-width: 2px;
li button {
color: $dark-font-color;
}
li button:hover {
background-color: $dark-bg2;
}
}
}
}
2021-08-29 18:22:49 +00:00
.tags {
margin-bottom: 0.5rem;
}
.tags > div:first-child {
margin-left: 0 !important;
}
2021-06-25 13:55:49 +00:00
</style>