Rename Service to Endpoint (#192)
* Add clarifications in comments * #191: Rename Service to Endpoint
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class='service px-3 py-3 border-l border-r border-t rounded-none hover:bg-gray-100 dark:hover:bg-gray-700 dark:border-gray-500' v-if="data">
|
||||
<div class='endpoint px-3 py-3 border-l border-r border-t rounded-none hover:bg-gray-100 dark:hover:bg-gray-700 dark:border-gray-500' v-if="data">
|
||||
<div class='flex flex-wrap mb-2'>
|
||||
<div class='w-3/4'>
|
||||
<router-link :to="generatePath()" class="font-bold hover:text-blue-800 hover:underline dark:hover:text-blue-400" title="View detailed service health">
|
||||
<router-link :to="generatePath()" class="font-bold hover:text-blue-800 hover:underline dark:hover:text-blue-400" title="View detailed endpoint health">
|
||||
{{ data.name }}
|
||||
</router-link>
|
||||
<span v-if="data.results && data.results.length && data.results[data.results.length - 1].hostname" class='text-gray-500 font-light'> | {{ data.results[data.results.length - 1].hostname }}</span>
|
||||
@ -60,7 +60,7 @@
|
||||
import {helper} from "@/mixins/helper";
|
||||
|
||||
export default {
|
||||
name: 'Service',
|
||||
name: 'Endpoint',
|
||||
props: {
|
||||
maximumNumberOfResults: Number,
|
||||
data: Object,
|
||||
@ -97,7 +97,7 @@ export default {
|
||||
if (!this.data) {
|
||||
return '/';
|
||||
}
|
||||
return `/services/${this.data.key}`;
|
||||
return `/endpoints/${this.data.key}`;
|
||||
},
|
||||
showTooltip(result, event) {
|
||||
this.$emit('showTooltip', result, event);
|
||||
@ -126,12 +126,12 @@ export default {
|
||||
|
||||
|
||||
<style>
|
||||
.service:first-child {
|
||||
.endpoint:first-child {
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
|
||||
.service:last-child {
|
||||
.endpoint:last-child {
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-width: 3px;
|
@ -1,21 +1,21 @@
|
||||
<template>
|
||||
<div :class="services.length === 0 ? 'mt-3' : 'mt-4'">
|
||||
<div :class="endpoints.length === 0 ? 'mt-3' : 'mt-4'">
|
||||
<slot v-if="name !== 'undefined'">
|
||||
<div class="service-group pt-2 border dark:bg-gray-800 dark:border-gray-500" @click="toggleGroup">
|
||||
<div class="endpoint-group pt-2 border dark:bg-gray-800 dark:border-gray-500" @click="toggleGroup">
|
||||
<h5 class='font-mono text-gray-400 text-xl font-medium pb-2 px-3 dark:text-gray-200 dark:hover:text-gray-500 dark:border-gray-500'>
|
||||
<span v-if="healthy" class='text-green-600'>✓</span>
|
||||
<span v-else class='text-yellow-400'>~</span>
|
||||
{{ name }}
|
||||
<span class='float-right service-group-arrow'>
|
||||
<span class='float-right endpoint-group-arrow'>
|
||||
{{ collapsed ? '▼' : '▲' }}
|
||||
</span>
|
||||
</h5>
|
||||
</div>
|
||||
</slot>
|
||||
<div v-if="!collapsed" :class="name === 'undefined' ? '' : 'service-group-content'">
|
||||
<slot v-for="service in services" :key="service">
|
||||
<Service
|
||||
:data="service"
|
||||
<div v-if="!collapsed" :class="name === 'undefined' ? '' : 'endpoint-group-content'">
|
||||
<slot v-for="(endpoint, idx) in endpoints" :key="idx">
|
||||
<Endpoint
|
||||
:data="endpoint"
|
||||
:maximumNumberOfResults="20"
|
||||
@showTooltip="showTooltip"
|
||||
@toggleShowAverageResponseTime="toggleShowAverageResponseTime" :showAverageResponseTime="showAverageResponseTime"
|
||||
@ -27,26 +27,26 @@
|
||||
|
||||
|
||||
<script>
|
||||
import Service from './Service.vue';
|
||||
import Endpoint from './Endpoint.vue';
|
||||
|
||||
export default {
|
||||
name: 'ServiceGroup',
|
||||
name: 'EndpointGroup',
|
||||
components: {
|
||||
Service
|
||||
Endpoint
|
||||
},
|
||||
props: {
|
||||
name: String,
|
||||
services: Array,
|
||||
endpoints: Array,
|
||||
showAverageResponseTime: Boolean
|
||||
},
|
||||
emits: ['showTooltip', 'toggleShowAverageResponseTime'],
|
||||
methods: {
|
||||
healthCheck() {
|
||||
if (this.services) {
|
||||
for (let i in this.services) {
|
||||
for (let j in this.services[i].results) {
|
||||
if (!this.services[i].results[j].success) {
|
||||
// Set the service group to unhealthy (only if it's currently healthy)
|
||||
if (this.endpoints) {
|
||||
for (let i in this.endpoints) {
|
||||
for (let j in this.endpoints[i].results) {
|
||||
if (!this.endpoints[i].results[j].success) {
|
||||
// Set the endpoint group to unhealthy (only if it's currently healthy)
|
||||
if (this.healthy) {
|
||||
this.healthy = false;
|
||||
}
|
||||
@ -55,14 +55,14 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set the service group to healthy (only if it's currently unhealthy)
|
||||
// Set the endpoint group to healthy (only if it's currently unhealthy)
|
||||
if (!this.healthy) {
|
||||
this.healthy = true;
|
||||
}
|
||||
},
|
||||
toggleGroup() {
|
||||
this.collapsed = !this.collapsed;
|
||||
sessionStorage.setItem(`gatus:service-group:${this.name}:collapsed`, this.collapsed);
|
||||
sessionStorage.setItem(`gatus:endpoint-group:${this.name}:collapsed`, this.collapsed);
|
||||
},
|
||||
showTooltip(result, event) {
|
||||
this.$emit('showTooltip', result, event);
|
||||
@ -72,7 +72,7 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
services: function () {
|
||||
endpoints: function () {
|
||||
this.healthCheck();
|
||||
}
|
||||
},
|
||||
@ -82,7 +82,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
healthy: true,
|
||||
collapsed: sessionStorage.getItem(`gatus:service-group:${this.name}:collapsed`) === "true"
|
||||
collapsed: sessionStorage.getItem(`gatus:endpoint-group:${this.name}:collapsed`) === "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,12 +90,12 @@ export default {
|
||||
|
||||
|
||||
<style>
|
||||
.service-group {
|
||||
.endpoint-group {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.service-group h5:hover {
|
||||
.endpoint-group h5:hover {
|
||||
color: #1b1e21;
|
||||
}
|
||||
</style>
|
74
web/app/src/components/Endpoints.vue
Normal file
74
web/app/src/components/Endpoints.vue
Normal file
@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div id="results">
|
||||
<slot v-for="endpointGroup in endpointGroups" :key="endpointGroup">
|
||||
<EndpointGroup :endpoints="endpointGroup.endpoints" :name="endpointGroup.name" @showTooltip="showTooltip" @toggleShowAverageResponseTime="toggleShowAverageResponseTime" :showAverageResponseTime="showAverageResponseTime" />
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import EndpointGroup from './EndpointGroup.vue';
|
||||
|
||||
export default {
|
||||
name: 'Endpoints',
|
||||
components: {
|
||||
EndpointGroup
|
||||
},
|
||||
props: {
|
||||
showStatusOnHover: Boolean,
|
||||
endpointStatuses: Object,
|
||||
showAverageResponseTime: Boolean
|
||||
},
|
||||
emits: ['showTooltip', 'toggleShowAverageResponseTime'],
|
||||
methods: {
|
||||
process() {
|
||||
let outputByGroup = {};
|
||||
for (let endpointStatusIndex in this.endpointStatuses) {
|
||||
let endpointStatus = this.endpointStatuses[endpointStatusIndex];
|
||||
// create an empty entry if this group is new
|
||||
if (!outputByGroup[endpointStatus.group] || outputByGroup[endpointStatus.group].length === 0) {
|
||||
outputByGroup[endpointStatus.group] = [];
|
||||
}
|
||||
outputByGroup[endpointStatus.group].push(endpointStatus);
|
||||
}
|
||||
let endpointGroups = [];
|
||||
for (let name in outputByGroup) {
|
||||
if (name !== 'undefined') {
|
||||
endpointGroups.push({name: name, endpoints: outputByGroup[name]})
|
||||
}
|
||||
}
|
||||
// Add all endpoints that don't have a group at the end
|
||||
if (outputByGroup['undefined']) {
|
||||
endpointGroups.push({name: 'undefined', endpoints: outputByGroup['undefined']})
|
||||
}
|
||||
this.endpointGroups = endpointGroups;
|
||||
},
|
||||
showTooltip(result, event) {
|
||||
this.$emit('showTooltip', result, event);
|
||||
},
|
||||
toggleShowAverageResponseTime() {
|
||||
this.$emit('toggleShowAverageResponseTime');
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
endpointStatuses: function () {
|
||||
this.process();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
userClickedStatus: false,
|
||||
endpointGroups: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
.endpoint-group-content > div:nth-child(1) {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
</style>
|
@ -1,74 +0,0 @@
|
||||
<template>
|
||||
<div id="results">
|
||||
<slot v-for="serviceGroup in serviceGroups" :key="serviceGroup">
|
||||
<ServiceGroup :services="serviceGroup.services" :name="serviceGroup.name" @showTooltip="showTooltip" @toggleShowAverageResponseTime="toggleShowAverageResponseTime" :showAverageResponseTime="showAverageResponseTime" />
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
import ServiceGroup from './ServiceGroup.vue';
|
||||
|
||||
export default {
|
||||
name: 'Services',
|
||||
components: {
|
||||
ServiceGroup
|
||||
},
|
||||
props: {
|
||||
showStatusOnHover: Boolean,
|
||||
serviceStatuses: Object,
|
||||
showAverageResponseTime: Boolean
|
||||
},
|
||||
emits: ['showTooltip', 'toggleShowAverageResponseTime'],
|
||||
methods: {
|
||||
process() {
|
||||
let outputByGroup = {};
|
||||
for (let serviceStatusIndex in this.serviceStatuses) {
|
||||
let serviceStatus = this.serviceStatuses[serviceStatusIndex];
|
||||
// create an empty entry if this group is new
|
||||
if (!outputByGroup[serviceStatus.group] || outputByGroup[serviceStatus.group].length === 0) {
|
||||
outputByGroup[serviceStatus.group] = [];
|
||||
}
|
||||
outputByGroup[serviceStatus.group].push(serviceStatus);
|
||||
}
|
||||
let serviceGroups = [];
|
||||
for (let name in outputByGroup) {
|
||||
if (name !== 'undefined') {
|
||||
serviceGroups.push({name: name, services: outputByGroup[name]})
|
||||
}
|
||||
}
|
||||
// Add all services that don't have a group at the end
|
||||
if (outputByGroup['undefined']) {
|
||||
serviceGroups.push({name: 'undefined', services: outputByGroup['undefined']})
|
||||
}
|
||||
this.serviceGroups = serviceGroups;
|
||||
},
|
||||
showTooltip(result, event) {
|
||||
this.$emit('showTooltip', result, event);
|
||||
},
|
||||
toggleShowAverageResponseTime() {
|
||||
this.$emit('toggleShowAverageResponseTime');
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
serviceStatuses: function () {
|
||||
this.process();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
userClickedStatus: false,
|
||||
serviceGroups: []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
.service-group-content > div:nth-child(1) {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
</style>
|
@ -26,7 +26,7 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Services',
|
||||
name: 'Endpoints',
|
||||
props: {
|
||||
event: Event,
|
||||
result: Object
|
||||
|
@ -3,21 +3,25 @@ import Home from '@/views/Home'
|
||||
import Details from "@/views/Details";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/services/:key',
|
||||
name: 'Details',
|
||||
component: Details,
|
||||
},
|
||||
]
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/endpoints/:key',
|
||||
name: 'Details',
|
||||
component: Details,
|
||||
},
|
||||
{ // XXX: Remove in v4.0.0
|
||||
path: '/services/:key',
|
||||
redirect: {name: 'Details'}
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(process.env.BASE_URL),
|
||||
routes
|
||||
})
|
||||
history: createWebHistory(process.env.BASE_URL),
|
||||
routes
|
||||
});
|
||||
|
||||
export default router
|
||||
export default router;
|
||||
|
@ -4,11 +4,11 @@
|
||||
←
|
||||
</router-link>
|
||||
<div>
|
||||
<slot v-if="serviceStatus">
|
||||
<slot v-if="endpointStatus">
|
||||
<h1 class="text-xl xl:text-3xl font-mono text-gray-400">RECENT CHECKS</h1>
|
||||
<hr class="mb-4"/>
|
||||
<Service
|
||||
:data="serviceStatus"
|
||||
<Endpoint
|
||||
:data="endpointStatus"
|
||||
:maximumNumberOfResults="20"
|
||||
@showTooltip="showTooltip"
|
||||
@toggleShowAverageResponseTime="toggleShowAverageResponseTime"
|
||||
@ -16,7 +16,7 @@
|
||||
/>
|
||||
<Pagination @page="changePage"/>
|
||||
</slot>
|
||||
<div v-if="serviceStatus && serviceStatus.key" class="mt-12">
|
||||
<div v-if="endpointStatus && endpointStatus.key" class="mt-12">
|
||||
<h1 class="text-xl xl:text-3xl font-mono text-gray-400">UPTIME</h1>
|
||||
<hr/>
|
||||
<div class="flex space-x-4 text-center text-2xl mt-6 relative bottom-2 mb-10">
|
||||
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="serviceStatus && serviceStatus.key" class="mt-12">
|
||||
<div v-if="endpointStatus && endpointStatus.key" class="mt-12">
|
||||
<h1 class="text-xl xl:text-3xl font-mono text-gray-400">RESPONSE TIME</h1>
|
||||
<hr/>
|
||||
<img :src="generateResponseTimeChartImageURL()" alt="response time chart" class="mt-6" />
|
||||
@ -53,7 +53,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="serviceStatus && serviceStatus.key">
|
||||
<div v-if="endpointStatus && endpointStatus.key">
|
||||
<h1 class="text-xl xl:text-3xl font-mono text-gray-400 mt-4">EVENTS</h1>
|
||||
<hr class="mb-4"/>
|
||||
<div>
|
||||
@ -87,7 +87,7 @@
|
||||
|
||||
<script>
|
||||
import Settings from '@/components/Settings.vue'
|
||||
import Service from '@/components/Service.vue';
|
||||
import Endpoint from '@/components/Endpoint.vue';
|
||||
import {SERVER_URL} from "@/main.js";
|
||||
import {helper} from "@/mixins/helper.js";
|
||||
import Pagination from "@/components/Pagination";
|
||||
@ -96,7 +96,7 @@ export default {
|
||||
name: 'Details',
|
||||
components: {
|
||||
Pagination,
|
||||
Service,
|
||||
Endpoint,
|
||||
Settings,
|
||||
},
|
||||
emits: ['showTooltip'],
|
||||
@ -104,32 +104,32 @@ export default {
|
||||
methods: {
|
||||
fetchData() {
|
||||
//console.log("[Details][fetchData] Fetching data");
|
||||
fetch(`${this.serverUrl}/api/v1/services/${this.$route.params.key}/statuses?page=${this.currentPage}`)
|
||||
fetch(`${this.serverUrl}/api/v1/endpoints/${this.$route.params.key}/statuses?page=${this.currentPage}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (JSON.stringify(this.serviceStatus) !== JSON.stringify(data)) {
|
||||
this.serviceStatus = data;
|
||||
if (JSON.stringify(this.endpointStatus) !== JSON.stringify(data)) {
|
||||
this.endpointStatus = data;
|
||||
this.uptime = data.uptime;
|
||||
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';
|
||||
event.fancyText = 'Endpoint is unhealthy';
|
||||
} else if (event.type === 'HEALTHY') {
|
||||
event.fancyText = 'Service is healthy';
|
||||
event.fancyText = 'Endpoint is healthy';
|
||||
} else if (event.type === 'START') {
|
||||
event.fancyText = 'Monitoring started';
|
||||
}
|
||||
} else {
|
||||
let nextEvent = data.events[i + 1];
|
||||
if (event.type === 'HEALTHY') {
|
||||
event.fancyText = 'Service became healthy';
|
||||
event.fancyText = 'Endpoint became healthy';
|
||||
} else if (event.type === 'UNHEALTHY') {
|
||||
if (nextEvent) {
|
||||
event.fancyText = 'Service was unhealthy for ' + this.prettifyTimeDifference(nextEvent.timestamp, event.timestamp);
|
||||
event.fancyText = 'Endpoint was unhealthy for ' + this.prettifyTimeDifference(nextEvent.timestamp, event.timestamp);
|
||||
} else {
|
||||
event.fancyText = 'Service became unhealthy';
|
||||
event.fancyText = 'Endpoint became unhealthy';
|
||||
}
|
||||
} else if (event.type === 'START') {
|
||||
event.fancyText = 'Monitoring started';
|
||||
@ -143,13 +143,13 @@ export default {
|
||||
});
|
||||
},
|
||||
generateUptimeBadgeImageURL(duration) {
|
||||
return `${this.serverUrl}/api/v1/services/${this.serviceStatus.key}/uptimes/${duration}/badge.svg`;
|
||||
return `${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/uptimes/${duration}/badge.svg`;
|
||||
},
|
||||
generateResponseTimeBadgeImageURL(duration) {
|
||||
return `${this.serverUrl}/api/v1/services/${this.serviceStatus.key}/response-times/${duration}/badge.svg`;
|
||||
return `${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/response-times/${duration}/badge.svg`;
|
||||
},
|
||||
generateResponseTimeChartImageURL() {
|
||||
return `${this.serverUrl}/api/v1/services/${this.serviceStatus.key}/response-times/24h/chart.svg`;
|
||||
return `${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/response-times/24h/chart.svg`;
|
||||
},
|
||||
prettifyUptime(uptime) {
|
||||
if (!uptime) {
|
||||
@ -174,7 +174,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
serviceStatus: {},
|
||||
endpointStatus: {},
|
||||
uptime: {},
|
||||
events: [],
|
||||
hourlyAverageResponseTime: {},
|
||||
@ -193,7 +193,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.service {
|
||||
.endpoint {
|
||||
border-radius: 3px;
|
||||
border-bottom-width: 3px;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<Services
|
||||
:serviceStatuses="serviceStatuses"
|
||||
<Endpoints
|
||||
:endpointStatuses="endpointStatuses"
|
||||
:showStatusOnHover="true"
|
||||
@showTooltip="showTooltip"
|
||||
@toggleShowAverageResponseTime="toggleShowAverageResponseTime" :showAverageResponseTime="showAverageResponseTime"
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
<script>
|
||||
import Settings from '@/components/Settings.vue'
|
||||
import Services from '@/components/Services.vue';
|
||||
import Endpoints from '@/components/Endpoints.vue';
|
||||
import Pagination from "@/components/Pagination";
|
||||
import {SERVER_URL} from "@/main.js";
|
||||
|
||||
@ -19,18 +19,18 @@ export default {
|
||||
name: 'Home',
|
||||
components: {
|
||||
Pagination,
|
||||
Services,
|
||||
Endpoints,
|
||||
Settings,
|
||||
},
|
||||
emits: ['showTooltip', 'toggleShowAverageResponseTime'],
|
||||
methods: {
|
||||
fetchData() {
|
||||
//console.log("[Home][fetchData] Fetching data");
|
||||
fetch(`${SERVER_URL}/api/v1/services/statuses?page=${this.currentPage}`)
|
||||
fetch(`${SERVER_URL}/api/v1/endpoints/statuses?page=${this.currentPage}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (JSON.stringify(this.serviceStatuses) !== JSON.stringify(data)) {
|
||||
this.serviceStatuses = data;
|
||||
if (JSON.stringify(this.endpointStatuses) !== JSON.stringify(data)) {
|
||||
this.endpointStatuses = data;
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -47,7 +47,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
serviceStatuses: [],
|
||||
endpointStatuses: [],
|
||||
currentPage: 1,
|
||||
showAverageResponseTime: true
|
||||
}
|
||||
|
Reference in New Issue
Block a user