Moved Header into separate component, added ConnectionMonitor
Some checks failed
continuous-integration/drone Build is failing
Some checks failed
continuous-integration/drone Build is failing
This commit is contained in:
parent
4452819aed
commit
9fb5d34118
85
src/components/Header/ConnectionMonitor.vue
Normal file
85
src/components/Header/ConnectionMonitor.vue
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<v-tooltip :text="`Last ping from server received at ${secondsFromLastPing} seconds ago`" location="bottom">
|
||||||
|
<template v-slot:activator="{ props }">
|
||||||
|
<v-icon :color="(secondsFromLastPing > pingThreshold || secondsFromLastPing === null) ? 'red' : 'green'" v-bind="props" class="mr-6">mdi-server</v-icon>
|
||||||
|
</template>
|
||||||
|
</v-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { VTooltip } from 'vuetify/components/VTooltip'
|
||||||
|
import { VIcon } from 'vuetify/components/VIcon'
|
||||||
|
import {mapGetters} from "vuex";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ConnectionMonitor",
|
||||||
|
components: {
|
||||||
|
VTooltip,
|
||||||
|
VIcon
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pingThreshold: 10,
|
||||||
|
pingError: false,
|
||||||
|
secondsFromLastPing: null,
|
||||||
|
setupStreamInterval: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'lastPingReceived'
|
||||||
|
])
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
// window.setInterval(() => {
|
||||||
|
// this.monitorServerConnection()
|
||||||
|
// }, 10000)
|
||||||
|
window.setInterval(() => {
|
||||||
|
this.secondsFromLastPing = Math.floor(((new Date()).getTime() - this.lastPingReceived) / 1000)
|
||||||
|
this.pingError = this.secondsFromLastPing > this.pingThreshold
|
||||||
|
}, 1000)
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
pingError(error) {
|
||||||
|
if (error) {
|
||||||
|
this.$toast.error(`Connection with server lost, attempting to reconnect...`, {
|
||||||
|
position: "top-right",
|
||||||
|
duration: false,
|
||||||
|
});
|
||||||
|
this.setupStreamInterval = window.setInterval(() => {
|
||||||
|
this.$emit('setupStream')
|
||||||
|
}, 3000)
|
||||||
|
} else {
|
||||||
|
window.clearTimeout(this.setupStreamInterval);
|
||||||
|
this.$toast.clear();
|
||||||
|
this.$toast.success(`Connection with server reestablished`, {
|
||||||
|
position: "top-right",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
monitorServerConnection () {
|
||||||
|
if (this.secondsFromLastPing > this.pingThreshold) {
|
||||||
|
this.pingError = true;
|
||||||
|
this.$toast.error(`Connection with server lost, attempting to reconnect...`, {
|
||||||
|
position: "top-right",
|
||||||
|
});
|
||||||
|
this.setupStream();
|
||||||
|
} else {
|
||||||
|
if (this.pingError) {
|
||||||
|
this.$toast.success(`Connection with server reestablished`, {
|
||||||
|
position: "top-right",
|
||||||
|
});
|
||||||
|
setTimeout(this.$toast.clear, 3000);
|
||||||
|
}
|
||||||
|
this.pingError = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
58
src/components/Header/Header.vue
Normal file
58
src/components/Header/Header.vue
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<template>
|
||||||
|
<v-row no-gutters>
|
||||||
|
<v-col cols="12" sm="5" class="d-flex justify-start flex-wrap" >
|
||||||
|
<Datepicker class="ma-2" :refresh="refreshFilterState" />
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="2" class="d-flex justify-center flex-wrap">
|
||||||
|
<h1 class="app-title"> Logmower </h1>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="12" sm="5" class="d-flex justify-end flex-wrap align-center">
|
||||||
|
<ConnectionMonitor @setup-stream="setupStream" />
|
||||||
|
<v-btn
|
||||||
|
color="blue-grey"
|
||||||
|
class="ma-2"
|
||||||
|
:prepend-icon="streaming ? 'mdi-pause' :'mdi-play'"
|
||||||
|
@click="toggleFilterQueryStreaming"
|
||||||
|
>
|
||||||
|
Stream new lines
|
||||||
|
</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { VRow, VCol } from 'vuetify/components/VGrid'
|
||||||
|
import { VBtn } from 'vuetify/components/VBtn'
|
||||||
|
import Datepicker from "../Grid/Main/Filter/Datepicker.vue";
|
||||||
|
import {mapActions, mapGetters} from "vuex";
|
||||||
|
import ConnectionMonitor from "./ConnectionMonitor.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
ConnectionMonitor,
|
||||||
|
Datepicker,
|
||||||
|
VRow,
|
||||||
|
VCol,
|
||||||
|
VBtn
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
refreshFilterState: Function
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters([
|
||||||
|
'streaming'
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
toggleFilterQueryStreaming: 'toggleFilterQueryStreaming',
|
||||||
|
}),
|
||||||
|
setupStream () {
|
||||||
|
this.$emit('setupStream')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -1,23 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="height: 100%; width: 100%;" v-resize="onResize">
|
<div style="height: 100%; width: 100%;" v-resize="onResize">
|
||||||
<v-row no-gutters>
|
<Header :refresh-filter-state="refreshFilterState" @setup-stream="setupStream" />
|
||||||
<v-col cols="12" sm="5" class="d-flex justify-start flex-wrap">
|
|
||||||
<Datepicker class="ma-2" :refresh="refreshFilterState" />
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="2" class="d-flex justify-center flex-wrap">
|
|
||||||
<h1 class="app-title"> Logmower </h1>
|
|
||||||
</v-col>
|
|
||||||
<v-col cols="12" sm="5" class="d-flex justify-end flex-wrap">
|
|
||||||
<v-btn
|
|
||||||
color="blue-grey"
|
|
||||||
class="ma-2"
|
|
||||||
:prepend-icon="streaming ? 'mdi-pause' :'mdi-play'"
|
|
||||||
@click="toggleFilterQueryStreaming"
|
|
||||||
>
|
|
||||||
Stream new lines
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
<ag-grid-vue
|
<ag-grid-vue
|
||||||
style="width: 100%; height: calc(100% - 52px);"
|
style="width: 100%; height: calc(100% - 52px);"
|
||||||
class="ag-theme-material"
|
class="ag-theme-material"
|
||||||
@ -44,8 +27,6 @@
|
|||||||
import { AgGridVue } from "ag-grid-vue3";
|
import { AgGridVue } from "ag-grid-vue3";
|
||||||
import "ag-grid-community/styles//ag-grid.css";
|
import "ag-grid-community/styles//ag-grid.css";
|
||||||
import "ag-grid-community/styles//ag-theme-material.css";
|
import "ag-grid-community/styles//ag-theme-material.css";
|
||||||
import { VBtn } from 'vuetify/components/VBtn'
|
|
||||||
import { VRow, VCol } from 'vuetify/components/VGrid'
|
|
||||||
import { Resize } from 'vuetify/directives';
|
import { Resize } from 'vuetify/directives';
|
||||||
import ExamineLogModal from "./Modal/ExamineLogModal.vue";
|
import ExamineLogModal from "./Modal/ExamineLogModal.vue";
|
||||||
import ComboboxFilter from "./Grid/Main/Filter/ComboboxFilter.js";
|
import ComboboxFilter from "./Grid/Main/Filter/ComboboxFilter.js";
|
||||||
@ -54,19 +35,16 @@ import flattenObj from "../helpers/flattenObj";
|
|||||||
import parseEventData from "../helpers/parseEventData";
|
import parseEventData from "../helpers/parseEventData";
|
||||||
import {mapActions, mapGetters} from 'vuex';
|
import {mapActions, mapGetters} from 'vuex';
|
||||||
import config from "./Grid/Main/config";
|
import config from "./Grid/Main/config";
|
||||||
import Datepicker from "./Grid/Main/Filter/Datepicker.vue";
|
|
||||||
import loadingOverlay from "./Grid/Main/loadingOverlay";
|
import loadingOverlay from "./Grid/Main/loadingOverlay";
|
||||||
|
import Header from "./Header/Header.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Datepicker,
|
Header,
|
||||||
ExamineLogModal,
|
ExamineLogModal,
|
||||||
AgGridVue,
|
AgGridVue,
|
||||||
ComboboxFilter,
|
ComboboxFilter,
|
||||||
MessageWithLevelRenderer,
|
MessageWithLevelRenderer,
|
||||||
VBtn,
|
|
||||||
VRow,
|
|
||||||
VCol,
|
|
||||||
loadingOverlay
|
loadingOverlay
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
@ -110,7 +88,7 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
setFilterOptions: 'setFilterOptions',
|
setFilterOptions: 'setFilterOptions',
|
||||||
setFilterQuery: 'setFilterQuery',
|
setFilterQuery: 'setFilterQuery',
|
||||||
toggleFilterQueryStreaming: 'toggleFilterQueryStreaming',
|
setLastPingReceived: 'setLastPingReceived',
|
||||||
}),
|
}),
|
||||||
onResize () {
|
onResize () {
|
||||||
if (this.gridApi) {
|
if (this.gridApi) {
|
||||||
@ -129,6 +107,7 @@ export default {
|
|||||||
if (url.searchParams.keys().next()) {
|
if (url.searchParams.keys().next()) {
|
||||||
let es = new EventSource(url.toString());
|
let es = new EventSource(url.toString());
|
||||||
es.onmessage = (e) => this.handleReceiveMessage(e)
|
es.onmessage = (e) => this.handleReceiveMessage(e)
|
||||||
|
es.addEventListener("ping", (e) => this.handleReceivePing())
|
||||||
es.addEventListener("filters", (e) => this.handleReceiveFilters(e))
|
es.addEventListener("filters", (e) => this.handleReceiveFilters(e))
|
||||||
es.addEventListener("timeout", (e) => this.handleReceiveTimeout(e))
|
es.addEventListener("timeout", (e) => this.handleReceiveTimeout(e))
|
||||||
es.addEventListener("completed", (e) => this.handleAllReceived(e))
|
es.addEventListener("completed", (e) => this.handleAllReceived(e))
|
||||||
@ -247,6 +226,9 @@ export default {
|
|||||||
handleAllReceived () {
|
handleAllReceived () {
|
||||||
this.gridApi.hideOverlay();
|
this.gridApi.hideOverlay();
|
||||||
},
|
},
|
||||||
|
handleReceivePing () {
|
||||||
|
this.setLastPingReceived()
|
||||||
|
},
|
||||||
doesExternalFilterPass(node) {
|
doesExternalFilterPass(node) {
|
||||||
if (node.data && this.filterQuery.from && this.filterQuery.to) {
|
if (node.data && this.filterQuery.from && this.filterQuery.to) {
|
||||||
let ts = new Date(node.data['@timestamp']).getTime()
|
let ts = new Date(node.data['@timestamp']).getTime()
|
||||||
|
@ -4,6 +4,7 @@ const store = createStore({
|
|||||||
state: {
|
state: {
|
||||||
filterOptions: {},
|
filterOptions: {},
|
||||||
filterQuery: {},
|
filterQuery: {},
|
||||||
|
lastPingReceived: null,
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
streaming (state) {
|
streaming (state) {
|
||||||
@ -11,7 +12,10 @@ const store = createStore({
|
|||||||
},
|
},
|
||||||
filterQuery (state) {
|
filterQuery (state) {
|
||||||
return state.filterQuery
|
return state.filterQuery
|
||||||
}
|
},
|
||||||
|
lastPingReceived (state) {
|
||||||
|
return state.lastPingReceived
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setFilterOptions(context, payload) {
|
setFilterOptions(context, payload) {
|
||||||
@ -30,6 +34,9 @@ const store = createStore({
|
|||||||
query.streaming = false
|
query.streaming = false
|
||||||
commit("SET_FILTER_QUERY", query);
|
commit("SET_FILTER_QUERY", query);
|
||||||
},
|
},
|
||||||
|
setLastPingReceived(context) {
|
||||||
|
context.commit("SET_LAST_PING_RECEIVED");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
SET_FILTER_OPTIONS(state, payload) {
|
SET_FILTER_OPTIONS(state, payload) {
|
||||||
@ -55,6 +62,9 @@ const store = createStore({
|
|||||||
|
|
||||||
state.filterQuery = query
|
state.filterQuery = query
|
||||||
},
|
},
|
||||||
|
SET_LAST_PING_RECEIVED(state) {
|
||||||
|
state.lastPingReceived = (new Date()).getTime()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,5 +22,10 @@ export default {
|
|||||||
vuetify: {
|
vuetify: {
|
||||||
// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader
|
// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
exclude: [
|
||||||
|
"@meforma/vue-toaster"
|
||||||
|
]
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user