get system status with sse

This commit is contained in:
jb-alvarado 2024-04-25 14:49:57 +02:00
parent 52411f61ef
commit 5f203a809b
16 changed files with 114 additions and 51 deletions

View File

@ -28,4 +28,7 @@
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"cSpell.words": [
"nuxt"
],
}

View File

@ -0,0 +1,12 @@
<template>
<div class="flex items-center ps-4 pe-1">
<div
class="w-2 h-2 rounded-full"
:class="indexStore.sseConnected ? 'bg-success shadow shadow-success' : 'bg-error shadow shadow-error'"
:title="indexStore.sseConnected ? $t('socketConnected') : $t('socketDisconnected')"
/>
</div>
</template>
<script setup lang="ts">
const indexStore = useIndex()
</script>

View File

@ -3,6 +3,7 @@
<NuxtLink class="navbar-brand min-w-[46px] p-2" href="/">
<img src="~/assets/images/ffplayout-small.png" class="img-fluid" alt="Logo" width="30" height="30" />
</NuxtLink>
<EventStatus class="z-10"/>
<div class="navbar-end w-1/5 grow">
<label class="swap swap-rotate me-2 md:hidden">
<input type="checkbox" :checked="indexStore.darkMode" @change="toggleDarkTheme" />

View File

@ -1,5 +1,6 @@
<template>
<div class="grid grid-cols-1 xs:grid-cols-2 border-4 rounded-md border-primary text-left shadow max-w-[960px]">
<!-- <div v-if="data">{{ systemStatus(data) }}</div> -->
<div class="grid grid-cols-1 xs:grid-cols-2 border-4 rounded-md border-primary text-left shadow min-w-[728px] max-w-[960px]">
<div class="p-4 bg-base-100">
<span class="text-3xl">{{ sysStat.system.name }} {{ sysStat.system.version }}</span>
<span v-if="sysStat.system.kernel">
@ -92,51 +93,67 @@ const { fileSize } = stringFormatter()
const authStore = useAuth()
const configStore = useConfig()
const timer = ref()
const sysStat = ref({
cpu: { cores: 0.0, usage: 0.0 },
const indexStore = useIndex()
const streamUrl = ref(
`/data/event/${configStore.configGui[configStore.configID].id}?endpoint=system&uuid=${authStore.uuid}`
)
// 'http://127.0.0.1:8787/data/event/1?endpoint=system&uuid=f2f8c29b-712a-48c5-8919-b535d3a05a3a'
const { status, data, error, close } = useEventSource(streamUrl, [], {
autoReconnect: {
retries: -1,
delay: 1000,
onFailed() {
indexStore.sseConnected = false
},
},
})
const errorCounter = ref(0)
const defaultStat = {
cpu: { cores: 1, usage: 0.0 },
load: { one: 0.0, five: 0.0, fifteen: 0.0 },
memory: { total: 0.0, used: 0.0, free: 0.0 },
network: { name: '', current_in: 0.0, current_out: 0.0, total_in: 0.0, total_out: 0.0 },
network: { name: '...', current_in: 0.0, current_out: 0.0, total_in: 0.0, total_out: 0.0 },
storage: { path: '', total: 0.0, used: 0.0 },
swap: { total: 0.0, used: 0.0, free: 0.0 },
system: { name: '', kernel: '', version: '', ffp_version: '' },
} as SystemStatistics)
system: { name: '...', kernel: '...', version: '...', ffp_version: '...' },
} as SystemStatistics
onMounted(() => {
status()
})
const sysStat = ref(defaultStat)
onBeforeUnmount(() => {
if (timer.value) {
clearTimeout(timer.value)
close()
indexStore.sseConnected = false
})
watch([status, error], async () => {
if (status.value === 'OPEN') {
indexStore.sseConnected = true
errorCounter.value = 0
} else {
indexStore.sseConnected = false
errorCounter.value += 1
sysStat.value = defaultStat
if (errorCounter.value > 15) {
await authStore.obtainUuid()
streamUrl.value = `/data/event/${configStore.configGui[configStore.configID].id}?endpoint=system&uuid=${
authStore.uuid
}`
errorCounter.value = 0
}
}
})
async function status() {
systemStatus()
async function setStatus(resolve: any) {
/*
recursive function as a endless loop
*/
systemStatus()
timer.value = setTimeout(() => setStatus(resolve), 1000)
watch([data], () => {
if (data.value) {
try {
sysStat.value = JSON.parse(data.value)
} catch (_) {
indexStore.sseConnected = true
}
}
return new Promise((resolve) => setStatus(resolve))
}
async function systemStatus() {
const channel = configStore.configGui[configStore.configID].id
if (!document?.hidden) {
await $fetch<SystemStatistics>(`/api/system/${channel}`, {
method: 'GET',
headers: { ...configStore.contentType, ...authStore.authHeader },
}).then((stat) => {
sysStat.value = stat
})
}
}
})
</script>

View File

@ -28,16 +28,16 @@ const props = defineProps({
},
})
onMounted(() => {
const statusCode = props.error?.statusCode || 400
// onMounted(() => {
// const statusCode = props.error?.statusCode || 400
if (statusCode >= 400) {
setTimeout(() => {
reloadNuxtApp({
path: localePath({ name: 'index' }),
ttl: 1000,
})
}, 3000)
}
})
// if (statusCode >= 400) {
// setTimeout(() => {
// reloadNuxtApp({
// path: localePath({ name: 'index' }),
// ttl: 1000,
// })
// }, 3000)
// }
// })
</script>

View File

@ -1,6 +1,8 @@
export default {
ok: 'Ok',
cancel: 'Abbrechen',
socketConnected: 'Event Stream verbunden',
socketDisconnected: 'Event Stream nicht verbunden',
alert: {
wrongLogin: 'Falsche Anmeldedaten!',
},

View File

@ -1,6 +1,8 @@
export default {
ok: 'Ok',
cancel: 'Cancel',
socketConnected: 'Event stream connected',
socketDisconnected: 'Event stream disconnected',
alert: {
wrongLogin: 'Incorrect login data!',
},

View File

@ -1,6 +1,8 @@
export default {
ok: 'Ok',
cancel: 'Cancelar',
socketConnected: 'Event stream conectado',
socketDisconnected: 'Event stream desconectado',
alert: {
wrongLogin: 'Dados incorretos!',
},

View File

@ -9,6 +9,7 @@ export default defineNuxtConfig({
devProxy: {
'/api': { target: 'http://127.0.0.1:8787/api' },
'/auth': { target: 'http://127.0.0.1:8787/auth' },
'/data': { target: 'http://127.0.0.1:8787/data' },
'/live': { target: 'http://127.0.0.1:8787/live' },
'/file': { target: 'http://127.0.0.1:8787/file' },
},

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{
"name": "ffplayout-frontend",
"version": "0.8.1",
"version": "0.9.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ffplayout-frontend",
"version": "0.8.1",
"version": "0.9.0",
"hasInstallScript": true,
"dependencies": {
"@nuxtjs/color-mode": "^3.4.0",

View File

@ -1,6 +1,6 @@
{
"name": "ffplayout-frontend",
"version": "0.8.1",
"version": "0.9.0",
"description": "Web GUI for ffplayout",
"author": "Jonathan Baecker",
"private": true,

View File

@ -1,6 +1,9 @@
<template>
<div class="w-full min-h-screen xs:h-full flex justify-center items-center">
<div v-if="authStore.isLogin" class="flex flex-wrap justify-center text-center w-full max-w-[1024px] p-5">
<div class="absolute top-4 left-1">
<EventStatus />
</div>
<SystemStats v-if="configStore.configGui.length > 0" />
<div class="flex flex-wrap justify-center gap-1 md:gap-0 md:join mt-5">
<NuxtLink :to="localePath({ name: 'player' })" class="btn join-item btn-primary px-2">

View File

@ -7,6 +7,7 @@ export const useAuth = defineStore('auth', {
jwtToken: '',
authHeader: {},
role: '',
uuid: null as null | string,
}),
getters: {},
@ -56,6 +57,19 @@ export const useAuth = defineStore('auth', {
return code
},
async obtainUuid() {
await $fetch<DataAuth>('/api/generate-uuid', {
method: 'POST',
headers: this.authHeader,
})
.then((response) => {
this.uuid = response.uuid
})
.catch(() => {
this.uuid = null
})
},
inspectToken() {
const token = useCookie('token').value

View File

@ -23,6 +23,7 @@ export const useConfig = defineStore('config', {
authStore.inspectToken()
if (authStore.isLogin) {
await authStore.obtainUuid()
await this.getGuiConfig()
await this.getPlayoutConfig()
await this.getUserConfig()

View File

@ -6,6 +6,7 @@ export const useIndex = defineStore('index', {
showAlert: false,
alertVariant: 'success',
alertMsg: '',
sseConnected: false,
}),
getters: {},

4
types/index.d.ts vendored
View File

@ -17,6 +17,10 @@ declare global {
}
}
interface DataAuth {
uuid: string
}
interface GuiConfig {
id: number
config_path: string