Start working on replacing SSR chart by chart.js

This commit is contained in:
TwiN 2021-11-09 00:09:35 -05:00
parent c4ef56511d
commit ed4fa94fc2
4 changed files with 94 additions and 2 deletions

View File

@ -1,6 +1,7 @@
package handler package handler
import ( import (
"encoding/json"
"log" "log"
"math" "math"
"net/http" "net/http"
@ -117,3 +118,41 @@ func ResponseTimeChart(writer http.ResponseWriter, r *http.Request) {
return return
} }
} }
func ResponseTime(writer http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
duration := vars["duration"]
var from time.Time
switch duration {
case "7d":
from = time.Now().Truncate(time.Hour).Add(-24 * 7 * time.Hour)
case "24h":
from = time.Now().Truncate(time.Hour).Add(-24 * time.Hour)
default:
http.Error(writer, "Durations supported: 7d, 24h", http.StatusBadRequest)
return
}
hourlyAverageResponseTime, err := store.Get().GetHourlyAverageResponseTimeByKey(vars["key"], from, time.Now())
if err != nil {
if err == common.ErrEndpointNotFound {
http.Error(writer, err.Error(), http.StatusNotFound)
} else if err == common.ErrInvalidTimeRange {
http.Error(writer, err.Error(), http.StatusBadRequest)
} else {
http.Error(writer, err.Error(), http.StatusInternalServerError)
}
return
}
if len(hourlyAverageResponseTime) == 0 {
http.Error(writer, "", http.StatusNoContent)
return
}
data, err := json.Marshal(hourlyAverageResponseTime)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return
}
writer.Header().Add("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK)
_, _ = writer.Write(data)
}

View File

@ -23,6 +23,7 @@ func CreateRouter(staticFolder string, securityConfig *security.Config, uiConfig
router.HandleFunc("/api/v1/endpoints/{key}/uptimes/{duration}/badge.svg", UptimeBadge).Methods("GET") router.HandleFunc("/api/v1/endpoints/{key}/uptimes/{duration}/badge.svg", UptimeBadge).Methods("GET")
router.HandleFunc("/api/v1/endpoints/{key}/response-times/{duration}/badge.svg", ResponseTimeBadge).Methods("GET") router.HandleFunc("/api/v1/endpoints/{key}/response-times/{duration}/badge.svg", ResponseTimeBadge).Methods("GET")
router.HandleFunc("/api/v1/endpoints/{key}/response-times/{duration}/chart.svg", ResponseTimeChart).Methods("GET") router.HandleFunc("/api/v1/endpoints/{key}/response-times/{duration}/chart.svg", ResponseTimeChart).Methods("GET")
router.HandleFunc("/api/v1/endpoints/{key}/response-times/{duration}", ResponseTime).Methods("GET")
// XXX: Remove the lines between this and the next XXX comment in v4.0.0 // XXX: Remove the lines between this and the next XXX comment in v4.0.0
router.HandleFunc("/api/v1/services/statuses", secureIfNecessary(securityConfig, EndpointStatuses)).Methods("GET") // No GzipHandler for this one, because we cache the content as Gzipped already router.HandleFunc("/api/v1/services/statuses", secureIfNecessary(securityConfig, EndpointStatuses)).Methods("GET") // No GzipHandler for this one, because we cache the content as Gzipped already
router.HandleFunc("/api/v1/services/{key}/statuses", secureIfNecessary(securityConfig, GzipHandlerFunc(EndpointStatus))).Methods("GET") router.HandleFunc("/api/v1/services/{key}/statuses", secureIfNecessary(securityConfig, GzipHandlerFunc(EndpointStatus))).Methods("GET")

View File

@ -10,6 +10,7 @@
"dependencies": { "dependencies": {
"core-js": "^3.19.1", "core-js": "^3.19.1",
"vue": "3.2.21", "vue": "3.2.21",
"vue-chart-3": "^0.5.11",
"vue-router": "^4.0.11" "vue-router": "^4.0.11"
}, },
"devDependencies": { "devDependencies": {

View File

@ -37,6 +37,9 @@
<div v-if="endpointStatus && endpointStatus.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> <h1 class="text-xl xl:text-3xl font-mono text-gray-400">RESPONSE TIME</h1>
<hr/> <hr/>
<LineChart :chartData="chartData" :options="chartOptions" />
<img :src="generateResponseTimeChartImageURL()" alt="response time chart" class="mt-6" /> <img :src="generateResponseTimeChartImageURL()" alt="response time chart" class="mt-6" />
<div class="flex space-x-4 text-center text-2xl mt-6 relative bottom-2 mb-10"> <div class="flex space-x-4 text-center text-2xl mt-6 relative bottom-2 mb-10">
<div class="flex-1"> <div class="flex-1">
@ -92,17 +95,24 @@ import {SERVER_URL} from "@/main.js";
import {helper} from "@/mixins/helper.js"; import {helper} from "@/mixins/helper.js";
import Pagination from "@/components/Pagination"; import Pagination from "@/components/Pagination";
import { LineChart } from 'vue-chart-3';
import { Chart, CategoryScale, LineController, LinearScale, LineElement, PointElement, TimeScale, Title, Tooltip} from 'chart.js';
Chart.register(LineController, CategoryScale, LinearScale, LineElement, PointElement, TimeScale, Title, Tooltip);
export default { export default {
name: 'Details', name: 'Details',
components: { components: {
Pagination, Pagination,
Endpoint, Endpoint,
Settings, Settings,
LineChart,
}, },
emits: ['showTooltip'], emits: ['showTooltip'],
mixins: [helper], mixins: [helper],
methods: { methods: {
fetchData() { fetchData() {
// XXX: This should probably be called every 15 minutes or so
//console.log("[Details][fetchData] Fetching data"); //console.log("[Details][fetchData] Fetching data");
fetch(`${this.serverUrl}/api/v1/endpoints/${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(response => response.json())
@ -140,8 +150,39 @@ export default {
} }
this.events = events; this.events = events;
} }
this.fetchUptimeChartData();
}); });
}, },
fetchUptimeChartData() {
fetch(`${this.serverUrl}/api/v1/endpoints/${this.$route.params.key}/response-times/24h`).then(response => {
response.json().then(data => {
let chart = {
labels: [],
datasets: [
{
label: 'Average response time (ms)',
data: [],
borderColor: 'rgb(75, 192, 192)',
},
]
};
let latest = null;
for (const [key] of Object.entries(data)) {
latest = key;
}
for (let i = 24; i >= 0; i--) {
let date = new Date((latest*1000)-(i*3600000));
chart.labels.push(date.toLocaleTimeString().replaceAll(":00", ""));
if (data[date.getTime()/1000]) {
chart.datasets[0].data.push(data[date.getTime()/1000]);
} else {
chart.datasets[0].data.push(0);
}
}
this.chartData = chart;
})
});
},
generateUptimeBadgeImageURL(duration) { generateUptimeBadgeImageURL(duration) {
return `${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/uptimes/${duration}/badge.svg`; return `${this.serverUrl}/api/v1/endpoints/${this.endpointStatus.key}/uptimes/${duration}/badge.svg`;
}, },
@ -182,8 +223,18 @@ export default {
serverUrl: SERVER_URL === '.' ? '..' : SERVER_URL, serverUrl: SERVER_URL === '.' ? '..' : SERVER_URL,
currentPage: 1, currentPage: 1,
showAverageResponseTime: true, showAverageResponseTime: true,
chartLabels: [], chartData: {labels: [], datasets: [{data: []}]},
chartValues: [], chartOptions: {
scales: {
y: {
min: 0,
title: {
display: true,
text: 'Average response time (ms)'
}
}
}
}
} }
}, },
created() { created() {