Add events to service detail page

This commit is contained in:
TwinProduction
2021-01-28 22:44:31 -05:00
parent 119b80edc0
commit fbb5d48bf7
9 changed files with 166 additions and 32 deletions

View File

@ -2,11 +2,11 @@
<div class="container container-xs relative mx-auto rounded shadow-xl border my-3 p-5 text-left" id="global">
<div class="mb-2">
<div class="flex flex-wrap">
<div class="w-2/3 text-left my-auto">
<div class="w-3/4 text-left my-auto">
<div class="title text-5xl font-light">Health Status</div>
</div>
<div class="w-1/3 flex justify-end">
<img src="./assets/logo.png" alt="Gatus" style="min-width: 50px; max-width: 200px; width: 20%;"/>
<div class="w-1/4 flex justify-end">
<img src="./assets/logo.png" alt="Gatus" class="object-scale-down" style="max-width: 100px; min-width: 50px; min-height:50px;"/>
</div>
</div>
</div>
@ -42,9 +42,11 @@ export default {
<style>
html {
height: 100%;
}
html, body {
background-color: #f7f9fb;
height: 100%;
}
#global, #results {

View File

@ -2,7 +2,7 @@
<div class='service container px-3 py-3 border-l border-r border-t rounded-none' v-if="data && data.results && data.results.length">
<div class='flex flex-wrap mb-2'>
<div class='w-3/4'>
<router-link :to="generatePath()" class="font-bold transition duration-200 ease-in-out hover:text-blue-900">{{ data.name }}</router-link> <span class='text-gray-500 font-light'>- {{ data.results[data.results.length - 1].hostname }}</span>
<router-link :to="generatePath()" class="inline-block font-bold transform hover:scale-110 transition duration-100 ease-in-out hover:text-blue-800" title="View detailed service health">{{ data.name }}</router-link> <span class='text-gray-500 font-light'>| {{ data.results[data.results.length - 1].hostname }}</span>
</div>
<div class='w-1/4 text-right'>
<span class='font-light status-min-max-ms'>
@ -35,6 +35,8 @@
<script>
import {helper} from "@/mixins/helper";
export default {
name: 'Service',
props: {
@ -42,6 +44,7 @@ export default {
data: Object,
},
emits: ['showTooltip'],
mixins: [helper],
methods: {
updateMinAndMaxResponseTimes() {
let minResponseTime = null;
@ -62,18 +65,6 @@ export default {
this.maxResponseTime = maxResponseTime;
}
},
generatePrettyTimeAgo(t) {
let differenceInMs = new Date().getTime() - new Date(t).getTime();
if (differenceInMs > 3600000) {
let hours = (differenceInMs/3600000).toFixed(0);
return hours + " hour" + (hours !== "1" ? "s" : "") + " ago";
}
if (differenceInMs > 60000) {
let minutes = (differenceInMs/60000).toFixed(0);
return minutes + " minute" + (minutes !== "1" ? "s" : "") + " ago";
}
return (differenceInMs/1000).toFixed(0) + " seconds ago";
},
generatePath() {
if (!this.data) {
return "/";

View File

@ -0,0 +1,16 @@
export const helper = {
methods: {
generatePrettyTimeAgo(t) {
let differenceInMs = new Date().getTime() - new Date(t).getTime();
if (differenceInMs > 3600000) {
let hours = (differenceInMs / 3600000).toFixed(0);
return hours + " hour" + (hours !== "1" ? "s" : "") + " ago";
}
if (differenceInMs > 60000) {
let minutes = (differenceInMs / 60000).toFixed(0);
return minutes + " minute" + (minutes !== "1" ? "s" : "") + " ago";
}
return (differenceInMs / 1000).toFixed(0) + " seconds ago";
},
}
}

View File

@ -1,6 +1,6 @@
<template>
<router-link to="/" class="absolute top-2 left-2 inline-block px-2 py-0 text-lg text-black transition bg-gray-100 rounded shadow ripple hover:shadow-lg hover:bg-gray-200 focus:outline-none">
&#8592;
&larr;
</router-link>
<div class="container mx-auto">
<slot v-if="serviceStatus">
@ -8,7 +8,7 @@
<hr class="mb-4" />
<Service :data="serviceStatus" :maximumNumberOfResults="20" @showTooltip="showTooltip" />
</slot>
<div v-if="serviceStatus.uptime" class="mt-5">
<div v-if="serviceStatus.uptime" class="mt-12">
<h1 class="text-3xl text-monospace text-gray-400">UPTIME</h1>
<hr />
<div class="flex space-x-4 text-center text-2xl mt-5">
@ -25,20 +25,44 @@
<h2 class="text-sm text-gray-400">Last hour</h2>
</div>
</div>
<h3 class="text-xl text-monospace text-gray-400">BADGES</h3>
<hr />
<div class="flex space-x-4 text-center text-2xl mt-5">
<hr class="mt-1"/>
<h3 class="text-xl text-monospace text-gray-400 mt-1 text-right">BADGES</h3>
<div class="flex space-x-4 text-center text-2xl mt-6 relative bottom-12">
<div class="flex-1">
<img :src="generateBadgeImageURL('7d')" alt="7d uptime badge" class="mx-auto" />
</div>
<div class="flex-1">
<img :src="generateBadgeImageURL('24h')" alt="7d uptime badge" class="mx-auto" />
<img :src="generateBadgeImageURL('24h')" alt="24h uptime badge" class="mx-auto" />
</div>
<div class="flex-1">
<img :src="generateBadgeImageURL('1h')" alt="7d uptime badge" class="mx-auto" />
<img :src="generateBadgeImageURL('1h')" alt="1h uptime badge" class="mx-auto" />
</div>
</div>
</div>
<div>
<h1 class="text-3xl text-monospace text-gray-400 mt-4">EVENTS</h1>
<hr class="mb-4" />
<div>
<slot v-for="event in events" :key="event">
<div class="p-3 my-4">
<h2 class="text-lg">
<span v-if="event.type === 'HEALTHY'" class="border border-green-600 rounded-full px-1 text-green-700 opacity-75 bg-green-100 mr-2"><span class="relative bottom-0.5">🡡</span></span>
<span v-else-if="event.type === 'UNHEALTHY'" class="border border-red-500 rounded-full px-1 text-red-700 opacity-75 bg-red-100 mr-2">🡣</span>
<span v-else-if="event.type === 'START'" class="mr-2"></span>
{{ event.fancyText }}
</h2>
<div class="flex mt-1 text-sm text-gray-400">
<div class="flex-1 text-left pl-10">
{{ new Date(event.timestamp).toISOString() }}
</div>
<div class="flex-1 text-right">
{{ event.fancyTimeAgo }}
</div>
</div>
</div>
</slot>
</div>
</div>
</div>
<Settings @refreshData="fetchData"/>
</template>
@ -48,6 +72,7 @@
import Settings from '@/components/Settings.vue'
import Service from '@/components/Service.vue';
import {SERVER_URL} from "@/main.js";
import {helper} from "@/mixins/helper.js";
export default {
name: 'Details',
@ -56,6 +81,7 @@ export default {
Settings,
},
emits: ['showTooltip'],
mixins: [helper],
methods: {
fetchData() {
console.log("[Details][fetchData] Fetching data");
@ -63,8 +89,34 @@ export default {
.then(response => response.json())
.then(data => {
if (JSON.stringify(this.serviceStatus) !== JSON.stringify(data)) {
console.log(data);
this.serviceStatus = data;
this.serviceStatus = data.serviceStatus;
let events = [];
for (let i = data.events.length-1; i >= 0; i--) {
let event = data.events[i];
if (i === data.events.length-1) {
if (event.type === "UNHEALTHY") {
event.fancyText = "Service is unhealthy";
} else {
event.fancyText = "Service is healthy";
}
} else {
let nextEvent = data.events[i+1];
if (event.type === "HEALTHY") {
event.fancyText = "Service became healthy again";
} else if (event.type === "UNHEALTHY") {
if (nextEvent) {
event.fancyText = "Service was unhealthy for " + this.prettifyTimeDifference(nextEvent.timestamp, event.timestamp);
} else {
event.fancyText = "Service became unhealthy";
}
} else if (event.type === "START") {
event.fancyText = "Monitoring started";
}
}
event.fancyTimeAgo = this.generatePrettyTimeAgo(event.timestamp);
events.push(event);
}
this.events = events;
}
});
},
@ -77,13 +129,18 @@ export default {
}
return (uptime * 100).toFixed(2) + "%"
},
prettifyTimeDifference(start, end) {
let minutes = Math.ceil((new Date(start) - new Date(end))/1000/60);
return minutes + (minutes === 1 ? " minute" : " minutes");
},
showTooltip(result, event) {
this.$emit('showTooltip', result, event);
}
},
data() {
return {
serviceStatus: {}
serviceStatus: {},
events: []
}
},
created() {

4
web/app/vue.config.js Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
filenameHashing: false,
productionSourceMap: false
}