implement uptime

This commit is contained in:
LouisLam 2021-07-01 13:11:16 +08:00
parent 82afddcfd6
commit 84c21b71c0
7 changed files with 145 additions and 49 deletions

View file

@ -20,7 +20,7 @@ class Monitor extends BeanModel {
id: this.id, id: this.id,
name: this.name, name: this.name,
url: this.url, url: this.url,
upRate: this.upRate, weight: this.weight,
active: this.active, active: this.active,
type: this.type, type: this.type,
interval: this.interval, interval: this.interval,
@ -91,15 +91,20 @@ class Monitor extends BeanModel {
static async sendStats(io, monitorID, userID) { static async sendStats(io, monitorID, userID) {
Monitor.sendAvgPing(24, io, monitorID, userID); Monitor.sendAvgPing(24, io, monitorID, userID);
//Monitor.sendUptime(24, io, this.id); Monitor.sendUptime(24, io, monitorID, userID);
//Monitor.sendUptime(24 * 30, io, this.id); Monitor.sendUptime(24 * 30, io, monitorID, userID);
} }
/**
*
* @param duration : int Hours
*/
static async sendAvgPing(duration, io, monitorID, userID) { static async sendAvgPing(duration, io, monitorID, userID) {
let avgPing = parseInt(await R.getCell(` let avgPing = parseInt(await R.getCell(`
SELECT AVG(ping) SELECT AVG(ping)
FROM heartbeat FROM heartbeat
WHERE time > DATE('now', ? || ' hours') WHERE time > DATE('now', ? || ' hours')
AND ping IS NOT NULL
AND monitor_id = ? `, [ AND monitor_id = ? `, [
-duration, -duration,
monitorID monitorID
@ -108,8 +113,25 @@ class Monitor extends BeanModel {
io.to(userID).emit("avgPing", monitorID, avgPing); io.to(userID).emit("avgPing", monitorID, avgPing);
} }
sendUptime(duration) { /**
*
* @param duration : int Hours
*/
static async sendUptime(duration, io, monitorID, userID) {
let downtime = parseInt(await R.getCell(`
SELECT SUM(duration)
FROM heartbeat
WHERE time > DATE('now', ? || ' hours')
AND status = 0
AND monitor_id = ? `, [
-duration,
monitorID
]));
let sec = duration * 3600;
let uptime = (sec - downtime) / sec;
io.to(userID).emit("uptime", monitorID, duration, uptime);
} }
} }

View file

@ -340,7 +340,7 @@ async function afterLogin(socket, user) {
async function getMonitorJSONList(userID) { async function getMonitorJSONList(userID) {
let result = {}; let result = {};
let monitorList = await R.find("monitor", " user_id = ? ORDER BY active DESC, name ASC ", [ let monitorList = await R.find("monitor", " user_id = ? ", [
userID userID
]) ])

View file

@ -18,9 +18,6 @@ export default {
computed: { computed: {
displayText() { displayText() {
console.log(dayjs.tz.guess())
return this.value return this.value
}, },
} }

61
src/components/Uptime.vue Normal file
View file

@ -0,0 +1,61 @@
<template>
<span :class="className">{{ uptime }}</span>
</template>
<script>
export default {
props: {
monitor : Object,
type: String,
pill: {
Boolean,
default: false,
},
},
computed: {
uptime() {
let key = this.monitor.id + "_" + this.type;
if (this.$root.uptimeList[key]) {
return Math.round(this.$root.uptimeList[key] * 10000) / 100 + "%";
} else {
return "N/A"
}
},
color() {
if (this.lastHeartBeat.status === 0) {
return "danger"
} else if (this.lastHeartBeat.status === 1) {
return "primary"
} else {
return "secondary"
}
},
lastHeartBeat() {
if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) {
return this.$root.lastHeartbeatList[this.monitor.id]
} else {
return { status: -1 }
}
},
className() {
if (this.pill) {
return `badge rounded-pill bg-${this.color}`;
} else {
return "";
}
},
},
}
</script>
<style scoped>
</style>

View file

@ -17,18 +17,11 @@ export default {
}, },
allowLoginDialog: false, // Allowed to show login dialog, but "loggedIn" have to be true too. This exists because prevent the login dialog show 0.1s in first before the socket server auth-ed. allowLoginDialog: false, // Allowed to show login dialog, but "loggedIn" have to be true too. This exists because prevent the login dialog show 0.1s in first before the socket server auth-ed.
loggedIn: false, loggedIn: false,
monitorList: [ monitorList: { },
heartbeatList: { },
], importantHeartbeatList: { },
heartbeatList: { avgPingList: { },
uptimeList: { }
},
importantHeartbeatList: {
},
avgPingList: {
}
} }
}, },
@ -85,6 +78,10 @@ export default {
this.avgPingList[monitorID] = data this.avgPingList[monitorID] = data
}); });
socket.on('uptime', (monitorID, type, data) => {
this.uptimeList[`${monitorID}_${type}`] = data
});
socket.on('importantHeartbeatList', (monitorID, data) => { socket.on('importantHeartbeatList', (monitorID, data) => {
if (! (monitorID in this.importantHeartbeatList)) { if (! (monitorID in this.importantHeartbeatList)) {
this.importantHeartbeatList[monitorID] = data; this.importantHeartbeatList[monitorID] = data;

View file

@ -11,18 +11,18 @@
<span v-if="$root.monitorList.length === 0">No Monitors, please <router-link to="/add">add one</router-link>.</span> <span v-if="$root.monitorList.length === 0">No Monitors, please <router-link to="/add">add one</router-link>.</span>
<router-link :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }" v-for="item in $root.monitorList"> <router-link :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }" v-for="item in sortedMonitorList">
<div class="row"> <div class="row">
<div class="col-6"> <div class="col-6 col-md-8 small-padding">
<div class="info"> <div class="info">
<span class="badge rounded-pill bg-primary">{{ item.upRate }}%</span> <Uptime :monitor="item" type="24" :pill="true" />
{{ item.name }} {{ item.name }}
</div> </div>
</div> </div>
<div class="col-6"> <div class="col-6 col-md-4">
<HeartbeatBar size="small" :monitor-id="item.id" /> <HeartbeatBar size="small" :monitor-id="item.id" />
</div> </div>
</div> </div>
@ -42,15 +42,49 @@
<script> <script>
import HeartbeatBar from "../components/HeartbeatBar.vue"; import HeartbeatBar from "../components/HeartbeatBar.vue";
import Uptime from "../components/Uptime.vue";
export default { export default {
components: { components: {
Uptime,
HeartbeatBar HeartbeatBar
}, },
data() { data() {
return { return {
} }
}, },
computed: {
sortedMonitorList() {
let result = Object.values(this.$root.monitorList);
result.sort((m1, m2) => {
if (m1.active !== m2.active) {
if (m1.active === 0) {
return 1;
}
if (m2.active === 0) {
return -1;
}
}
if (m1.weight !== m2.weight) {
if (m1.weight > m2.weight) {
return -1;
}
if (m1.weight < m2.weight) {
return 1;
}
}
return m1.name.localeCompare(m2.name);
})
return result;
},
},
methods: { methods: {
monitorURL(id) { monitorURL(id) {
return "/dashboard/" + id; return "/dashboard/" + id;
@ -98,29 +132,13 @@ export default {
} }
} }
.hp-bar { .badge {
white-space: nowrap; min-width: 58px;
margin-top: 4px; }
text-align: right;
div { .small-padding {
display: inline-block; padding-left: 5px !important;
background-color: $primary; padding-right: 5px !important;
width: 0.35rem;
height: 1rem;
margin: 0.15rem;
border-radius: 50rem;
transition: all ease-in-out 0.15s;
&.empty {
background-color: aliceblue;
}
&:hover {
opacity: 0.8;
transform: scale(1.5);
}
}
} }
</style> </style>

View file

@ -36,12 +36,12 @@
<div class="col"> <div class="col">
<h4>Uptime</h4> <h4>Uptime</h4>
<p>(24-hour)</p> <p>(24-hour)</p>
<span class="num"></span> <span class="num"><Uptime :monitor="monitor" type="24" /></span>
</div> </div>
<div class="col"> <div class="col">
<h4>Uptime</h4> <h4>Uptime</h4>
<p>(30-day)</p> <p>(30-day)</p>
<span class="num"></span> <span class="num"><Uptime :monitor="monitor" type="720" /></span>
</div> </div>
</div> </div>
</div> </div>
@ -86,9 +86,11 @@ import HeartbeatBar from "../components/HeartbeatBar.vue";
import Status from "../components/Status.vue"; import Status from "../components/Status.vue";
import Datetime from "../components/Datetime.vue"; import Datetime from "../components/Datetime.vue";
import CountUp from "../components/CountUp.vue"; import CountUp from "../components/CountUp.vue";
import Uptime from "../components/Uptime.vue";
export default { export default {
components: { components: {
Uptime,
CountUp, CountUp,
Datetime, Datetime,
HeartbeatBar, HeartbeatBar,
@ -233,7 +235,6 @@ table {
font-size: 14px; font-size: 14px;
tr { tr {
--bs-table-accent-bg: white;
transition: all ease-in-out 0.2ms; transition: all ease-in-out 0.2ms;
} }
} }