mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-28 01:04:05 +00:00
[status page] minor
This commit is contained in:
parent
9b8f01cfc6
commit
a6fdd272a6
3 changed files with 63 additions and 39 deletions
|
@ -1,16 +1,16 @@
|
|||
const https = require("https");
|
||||
const dayjs = require("dayjs");
|
||||
const utc = require("dayjs/plugin/utc")
|
||||
let timezone = require("dayjs/plugin/timezone")
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
const utc = require("dayjs/plugin/utc");
|
||||
let timezone = require("dayjs/plugin/timezone");
|
||||
dayjs.extend(utc);
|
||||
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 { R } = require("redbean-node");
|
||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||
const { Notification } = require("../notification")
|
||||
const { Notification } = require("../notification");
|
||||
const version = require("../../package.json").version;
|
||||
|
||||
/**
|
||||
|
@ -39,7 +39,7 @@ class Monitor extends BeanModel {
|
|||
|
||||
let list = await R.find("monitor_notification", " monitor_id = ? ", [
|
||||
this.id,
|
||||
])
|
||||
]);
|
||||
|
||||
for (let bean of list) {
|
||||
notificationIDList[bean.notification_id] = true;
|
||||
|
@ -77,7 +77,7 @@ class Monitor extends BeanModel {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
getIgnoreTls() {
|
||||
return Boolean(this.ignoreTls)
|
||||
return Boolean(this.ignoreTls);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,12 +107,12 @@ class Monitor extends BeanModel {
|
|||
if (! previousBeat) {
|
||||
previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
|
||||
this.id,
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
const isFirstBeat = !previousBeat;
|
||||
|
||||
let bean = R.dispense("heartbeat")
|
||||
let bean = R.dispense("heartbeat");
|
||||
bean.monitor_id = this.id;
|
||||
bean.time = R.isoDateTime(dayjs.utc());
|
||||
bean.status = DOWN;
|
||||
|
@ -148,7 +148,7 @@ class Monitor extends BeanModel {
|
|||
return checkStatusCode(status, this.getAcceptedStatuscodes());
|
||||
},
|
||||
});
|
||||
bean.msg = `${res.status} - ${res.statusText}`
|
||||
bean.msg = `${res.status} - ${res.statusText}`;
|
||||
bean.ping = dayjs().valueOf() - startTime;
|
||||
|
||||
// Check certificate if https is used
|
||||
|
@ -158,12 +158,12 @@ class Monitor extends BeanModel {
|
|||
tlsInfo = await this.updateTlsInfo(checkCertificate(res));
|
||||
} catch (e) {
|
||||
if (e.message !== "No TLS certificate in response") {
|
||||
console.error(e.message)
|
||||
console.error(e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms")
|
||||
debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms");
|
||||
|
||||
if (this.type === "http") {
|
||||
bean.status = UP;
|
||||
|
@ -173,26 +173,26 @@ class Monitor extends BeanModel {
|
|||
|
||||
// Convert to string for object/array
|
||||
if (typeof data !== "string") {
|
||||
data = JSON.stringify(data)
|
||||
data = JSON.stringify(data);
|
||||
}
|
||||
|
||||
if (data.includes(this.keyword)) {
|
||||
bean.msg += ", keyword is found"
|
||||
bean.msg += ", keyword is found";
|
||||
bean.status = UP;
|
||||
} else {
|
||||
throw new Error(bean.msg + ", but keyword is not found")
|
||||
throw new Error(bean.msg + ", but keyword is not found");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if (this.type === "port") {
|
||||
bean.ping = await tcping(this.hostname, this.port);
|
||||
bean.msg = ""
|
||||
bean.msg = "";
|
||||
bean.status = UP;
|
||||
|
||||
} else if (this.type === "ping") {
|
||||
bean.ping = await ping(this.hostname);
|
||||
bean.msg = ""
|
||||
bean.msg = "";
|
||||
bean.status = UP;
|
||||
} else if (this.type === "dns") {
|
||||
let startTime = dayjs().valueOf();
|
||||
|
@ -212,7 +212,7 @@ class Monitor extends BeanModel {
|
|||
dnsRes.forEach(record => {
|
||||
dnsMessage += `Hostname: ${record.exchange} - Priority: ${record.priority} | `;
|
||||
});
|
||||
dnsMessage = dnsMessage.slice(0, -2)
|
||||
dnsMessage = dnsMessage.slice(0, -2);
|
||||
} else if (this.dns_resolve_type == "NS") {
|
||||
dnsMessage += "Servers: ";
|
||||
dnsMessage += dnsRes.join(" | ");
|
||||
|
@ -222,7 +222,7 @@ class Monitor extends BeanModel {
|
|||
dnsRes.forEach(record => {
|
||||
dnsMessage += `Name: ${record.name} | Port: ${record.port} | Priority: ${record.priority} | Weight: ${record.weight} | `;
|
||||
});
|
||||
dnsMessage = dnsMessage.slice(0, -2)
|
||||
dnsMessage = dnsMessage.slice(0, -2);
|
||||
}
|
||||
|
||||
if (this.dnsLastResult !== dnsMessage) {
|
||||
|
@ -285,20 +285,20 @@ class Monitor extends BeanModel {
|
|||
if (!isFirstBeat || bean.status === DOWN) {
|
||||
let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [
|
||||
this.id,
|
||||
])
|
||||
]);
|
||||
|
||||
let text;
|
||||
if (bean.status === UP) {
|
||||
text = "✅ Up"
|
||||
text = "✅ Up";
|
||||
} else {
|
||||
text = "🔴 Down"
|
||||
text = "🔴 Down";
|
||||
}
|
||||
|
||||
let msg = `[${this.name}] [${text}] ${bean.msg}`;
|
||||
|
||||
for (let notification of notificationList) {
|
||||
try {
|
||||
await Notification.send(JSON.parse(notification.config), msg, await this.toJSON(), bean.toJSON())
|
||||
await Notification.send(JSON.parse(notification.config), msg, await this.toJSON(), bean.toJSON());
|
||||
} catch (e) {
|
||||
console.error("Cannot send notification to " + notification.name);
|
||||
console.log(e);
|
||||
|
@ -313,18 +313,18 @@ class Monitor extends BeanModel {
|
|||
let beatInterval = this.interval;
|
||||
|
||||
if (bean.status === UP) {
|
||||
console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`)
|
||||
console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}`);
|
||||
} else if (bean.status === PENDING) {
|
||||
if (this.retryInterval !== this.interval) {
|
||||
beatInterval = this.retryInterval;
|
||||
}
|
||||
console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`)
|
||||
console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}`);
|
||||
} else {
|
||||
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`)
|
||||
console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type}`);
|
||||
}
|
||||
|
||||
io.to(this.user_id).emit("heartbeat", bean.toJSON());
|
||||
Monitor.sendStats(io, this.id, this.user_id)
|
||||
Monitor.sendStats(io, this.id, this.user_id);
|
||||
|
||||
await R.store(bean);
|
||||
prometheus.update(bean, tlsInfo);
|
||||
|
@ -335,7 +335,7 @@ class Monitor extends BeanModel {
|
|||
this.heartbeatInterval = setTimeout(beat, beatInterval * 1000);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
beat();
|
||||
}
|
||||
|
@ -481,7 +481,7 @@ class Monitor extends BeanModel {
|
|||
} else {
|
||||
// Handle new monitor with only one beat, because the beat's duration = 0
|
||||
let status = parseInt(await R.getCell("SELECT `status` FROM heartbeat WHERE monitor_id = ?", [ monitorID ]));
|
||||
console.log("here???" + status);
|
||||
|
||||
if (status === UP) {
|
||||
uptime = 1;
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
:animation="100"
|
||||
>
|
||||
<template #item="group">
|
||||
<div>
|
||||
<div class="mb-5 ">
|
||||
<!-- Group Title -->
|
||||
<h2 class="mt-5 group-title">
|
||||
<h2 class="group-title">
|
||||
<font-awesome-icon v-if="editMode && showGroupDrag" icon="arrows-alt-v" class="action drag me-3" />
|
||||
<font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeGroup(group.index)" />
|
||||
<Editable v-model="group.element.name" :contenteditable="editMode" tag="span" />
|
||||
|
@ -76,7 +76,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showGroupDrag() {
|
||||
|
@ -95,7 +95,7 @@ export default {
|
|||
this.$root.publicGroupList[groupIndex].monitorList.splice(index, 1);
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -184,7 +184,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import VueMultiselect from "vue-multiselect"
|
||||
import VueMultiselect from "vue-multiselect";
|
||||
import axios from "axios";
|
||||
import PublicGroupList from "../components/PublicGroupList.vue";
|
||||
import ImageCropUpload from "vue-image-crop-upload";
|
||||
|
@ -194,6 +194,8 @@ const toast = useToast();
|
|||
|
||||
const leavePageMsg = "Do you really want to leave? you have unsaved changes!";
|
||||
|
||||
let feedInterval;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PublicGroupList,
|
||||
|
@ -222,9 +224,10 @@ export default {
|
|||
config: {},
|
||||
selectedMonitor: null,
|
||||
incident: null,
|
||||
previousIncident: null,
|
||||
showImageCropUpload: false,
|
||||
imgDataUrl: "/icon.svg",
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
|
@ -264,7 +267,7 @@ export default {
|
|||
if (this.editMode) {
|
||||
return {
|
||||
"edit-mode": true,
|
||||
}
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
|
@ -359,6 +362,16 @@ export default {
|
|||
axios.get("/api/status-page/monitor-list").then((res) => {
|
||||
this.monitorList = res.data;
|
||||
});
|
||||
|
||||
// 5mins a loop
|
||||
feedInterval = setInterval(() => {
|
||||
// If editMode, it will use the data from websocket.
|
||||
if (! this.editMode) {
|
||||
axios.get("/api/status-page/heartbeat").then((res) => {
|
||||
// TODO
|
||||
});
|
||||
}
|
||||
}, 5 * 60 * 1000);
|
||||
},
|
||||
methods: {
|
||||
|
||||
|
@ -385,7 +398,7 @@ export default {
|
|||
this.$root.publicGroupList.push({
|
||||
name: groupName,
|
||||
monitorList: [],
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
discard() {
|
||||
|
@ -411,6 +424,11 @@ export default {
|
|||
|
||||
createIncident() {
|
||||
this.enableEditIncidentMode = true;
|
||||
|
||||
if (this.incident) {
|
||||
this.previousIncident = this.incident;
|
||||
}
|
||||
|
||||
this.incident = {
|
||||
title: "",
|
||||
content: "",
|
||||
|
@ -442,19 +460,25 @@ export default {
|
|||
*/
|
||||
editIncident() {
|
||||
this.enableEditIncidentMode = true;
|
||||
this.previousIncident = Object.assign({}, this.incident);
|
||||
},
|
||||
|
||||
cancelIncident() {
|
||||
this.enableEditIncidentMode = false;
|
||||
|
||||
if (this.previousIncident) {
|
||||
this.incident = this.previousIncident;
|
||||
this.previousIncident = null;
|
||||
}
|
||||
},
|
||||
|
||||
unpinIncident() {
|
||||
this.$root.getSocket().emit("unpinIncident", () => {
|
||||
this.incident = null;
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
Loading…
Reference in a new issue