mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-27 16:54:04 +00:00
implement uptime
This commit is contained in:
parent
82afddcfd6
commit
84c21b71c0
7 changed files with 145 additions and 49 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
|
@ -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
61
src/components/Uptime.vue
Normal 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>
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue