Skip to content

Commit

Permalink
feat: monitoring UI (wip)
Browse files Browse the repository at this point in the history
  • Loading branch information
andre8244 committed Sep 19, 2024
1 parent 06f1ff5 commit 612eca6
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 162 deletions.
1 change: 1 addition & 0 deletions public/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -1807,6 +1807,7 @@
"host_traffic": "Host traffic",
"host": "Host",
"connections": "Connections",
"wan_events": "WAN events",
"wan_name_events": "{name} events",
"wan_name_traffic": "{name} traffic",
"blocked_threats": "Blocked threats",
Expand Down
48 changes: 42 additions & 6 deletions src/components/standalone/monitoring/ConnectivityMonitor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,32 @@

<script setup lang="ts">
import { ubusCall } from '@/lib/standalone/ubus'
import { getAxiosErrorMessage, NeCard } from '@nethesis/vue-components'
import {
getAxiosErrorMessage,
NeCard,
NeEmptyState,
sortByProperty
} from '@nethesis/vue-components'
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import WanEventsCard from './connectivity/WanEventsCard.vue'
import SingleWanTrafficCard from './connectivity/SingleWanTrafficCard.vue'
import { isEmpty } from 'lodash-es'

export type Wan = {
iface: string
device: string
}

export type WanEvent = {
time: number
status: number
}

const { t } = useI18n()

const wans = ref<Wan[]>([])
const mwanEvents = ref<Record<string, any[]> | undefined>(undefined)
const mwanEvents = ref<Record<string, any[]>>({})

let loading = ref({
listWans: false,
Expand Down Expand Up @@ -62,7 +73,20 @@ async function getMwanReport() {

try {
const res = await ubusCall('ns.report', 'mwan-report')
mwanEvents.value = res.data.events_by_wan
mwanEvents.value = {}

for (const [wanName, eventsList] of Object.entries(res.data.events_by_wan) as [
string,
any[]
][]) {
const events = eventsList.map((event: number[]) => {
return {
time: event[0],
status: event[1]
}
})
mwanEvents.value[wanName] = events.sort(sortByProperty('time')).reverse()
}
} catch (err: any) {
console.error(err)
error.value.getMwanReport = t('error.cannot_retrieve_mwan_report')
Expand All @@ -75,7 +99,7 @@ async function getMwanReport() {

<template>
<div>
<div class="grid grid-cols-1 gap-x-6 gap-y-6 lg:grid-cols-2">
<div class="grid grid-cols-1 gap-x-6 gap-y-6 xl:grid-cols-2">
<!-- connections -->
<!-- <NeCard ////
:title="t('standalone.real_time_monitor.connections')"
Expand All @@ -87,15 +111,27 @@ async function getMwanReport() {
{{ wans }} ////
</NeCard> -->
<!-- wan events -->
{{ mwanEvents }} ////
<NeCard
v-if="isEmpty(mwanEvents)"
:title="t('standalone.real_time_monitor.wan_events')"
class="col-span-1"
>
<NeEmptyState
:title="t('standalone.real_time_monitor.no_events')"
:icon="['fas', 'table']"
class="bg-white dark:bg-gray-950"
/>
</NeCard>
<WanEventsCard
v-else
v-for="(events, wanName) in mwanEvents"
:key="wanName"
:wan="wanName"
:wanEvents="events"
class="col-span-1"
/>
<!-- wans traffic -->
<SingleWanTrafficCard v-for="wan in wans" :key="wan.device" :wan="wan" />
<SingleWanTrafficCard v-for="wan in wans" :key="wan.device" :wan="wan" class="col-span-1" />
</div>
</div>
</template>
3 changes: 3 additions & 0 deletions src/components/standalone/monitoring/SecurityMonitor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,11 @@ async function getAttackReport() {
:errorDescription="error.getMalwareReportDescription"
class="row-span-2 sm:col-span-2 md:col-span-3 xl:col-span-6 3xl:col-span-10"
>
{{ blockedPacketsChartLabels }} ////
<BlockedPacketsChart
:labels="blockedPacketsChartLabels"
:datasets="blockedPacketsChartDatasets"
height="25vh"
/>
</NeCard>
<!-- blocked ip addresses -->
Expand Down Expand Up @@ -470,6 +472,7 @@ async function getAttackReport() {
<BlockedIpsPerHourChart
:labels="blockedIpsPerHourChartLabels"
:datasets="blockedIpsPerHourChartDatasets"
height="25vh"
isHorizontal
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/standalone/monitoring/TrafficMonitor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ const {
{{ t('standalone.real_time_monitor.recent_traffic') }}
</NeHeading>
<NeCard class="sm:col-span-2 md:col-span-4 3xl:col-span-6">
<RecentTrafficChart :labels="hoursLabels" :datasets="hoursDatasets" />
<RecentTrafficChart :labels="hoursLabels" :datasets="hoursDatasets" height="25vh" />
</NeCard>
</div>
</div>
Expand Down
191 changes: 72 additions & 119 deletions src/components/standalone/monitoring/connectivity/WanEventsCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,152 +14,105 @@ import {
NeTableCell,
NePaginator,
useItemPagination,
NeButton,
NeTooltip,
NeEmptyState
NeEmptyState,
formatDateLoc
} from '@nethesis/vue-components'
import { computed, ref } from 'vue'
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import type { WanEvent } from '../ConnectivityMonitor.vue'

//// review

//// copy table from NatHelpersTable -->

const props = defineProps<{
wan: string
wanEvents: any[]
wanEvents: WanEvent[]
}>()

const { t } = useI18n()

const pageSize = ref(5)
const { currentPage, paginatedItems } = useItemPagination(() => events, {
const { currentPage, paginatedItems } = useItemPagination(() => props.wanEvents, {
itemsPerPage: pageSize
})

const events = computed(() => {
return props.wanEvents.map((event) => {
return {
time: event[0],
message: event[1]
}
})
})
</script>

<template>
<div>
<NeCard
:title="
<NeCard
:title="
t('standalone.real_time_monitor.wan_name_events', {
name: props.wan
})
"
>
<NeTable
:ariaLabel="
t('standalone.real_time_monitor.wan_name_events', {
name: props.wan
})
"
cardBreakpoint="sm"
>
<NeTable
:ariaLabel="
t('standalone.real_time_monitor.wan_name_events', {
name: props.wan
})
"
cardBreakpoint="sm"
>
<NeTableHead>
<NeTableHeadCell>{{ t('standalone.real_time_monitor.timestamp') }}</NeTableHeadCell>
<NeTableHeadCell>{{ t('standalone.real_time_monitor.event') }}</NeTableHeadCell>
</NeTableHead>
<NeTableBody>
<!-- empty state -->
<NeTableRow v-if="!events.length">
<NeTableCell colspan="2">
<NeEmptyState
:title="t('standalone.real_time_monitor.no_events')"
:icon="['fas', 'table']"
class="bg-white dark:bg-gray-950"
<NeTableHead>
<NeTableHeadCell>{{ t('standalone.real_time_monitor.timestamp') }}</NeTableHeadCell>
<NeTableHeadCell>{{ t('standalone.real_time_monitor.event') }}</NeTableHeadCell>
</NeTableHead>
<NeTableBody>
<!-- empty state -->
<NeTableRow v-if="!wanEvents.length">
<NeTableCell colspan="2">
<NeEmptyState
:title="t('standalone.real_time_monitor.no_events')"
:icon="['fas', 'table']"
class="bg-white dark:bg-gray-950"
/>
</NeTableCell>
</NeTableRow>
<NeTableRow v-else v-for="item in paginatedItems" :key="item.ip">
<NeTableCell :data-label="t('standalone.real_time_monitor.timestamp')">
{{ formatDateLoc(new Date(Number(item.time) * 1000), 'PPpp') || '-' }}
</NeTableCell>
<NeTableCell :data-label="t('standalone.real_time_monitor.event')">
<div class="flex items-center gap-2">
<font-awesome-icon
:icon="['fas', item.status ? 'circle-check' : 'circle-xmark']"
:class="[
'h-4 w-4',
item.status
? 'text-green-600 dark:text-green-400'
: 'text-rose-600 dark:text-rose-400'
]"
aria-hidden="true"
/>
</NeTableCell>
</NeTableRow>
<NeTableRow v-else v-for="item in paginatedItems" :key="item.ip">
<NeTableCell :data-label="t('standalone.real_time_monitor.timestamp')">
{{ item.time }}
</NeTableCell>
<NeTableCell :data-label="t('standalone.real_time_monitor.event')">
<div class="flex items-center gap-2">
<font-awesome-icon
:icon="['fas', item.message ? 'circle-check' : 'circle-xmark']"
class="h-4 w-4"
aria-hidden="true"
/>
{{ item.enabled ? t('common.enabled') : t('common.disabled') }}
</div>
</NeTableCell>
<NeTableCell :data-label="t('standalone.nat_helpers.loaded_on_kernel')">
<div class="flex items-center gap-2">
<span>
{{
item.loaded
? t('standalone.nat_helpers.loaded')
: t('standalone.nat_helpers.not_loaded')
}}
</span>
<!-- enabled/loaded inconsistency warning -->
<NeTooltip v-if="(item.enabled && !item.loaded) || (!item.enabled && item.loaded)">
<template #trigger>
<font-awesome-icon
:icon="['fas', 'triangle-exclamation']"
class="h-4 w-4 text-amber-500"
/>
</template>
<template #content>
<p>
{{
item.enabled && !item.loaded
? t('standalone.nat_helpers.enabled_but_not_loaded_tooltip')
: t('standalone.nat_helpers.disabled_but_loaded_tooltip')
}}
</p>
</template>
</NeTooltip>
</div>
</NeTableCell>
<NeTableCell :data-label="t('common.actions')">
<div class="-ml-2.5 flex xl:ml-0 xl:justify-end">
<NeButton kind="tertiary" @click="emit('editNatHelper', item)">
<template #prefix>
<font-awesome-icon
:icon="['fas', 'pen-to-square']"
class="h-4 w-4"
aria-hidden="true"
/>
</template>
{{ t('common.edit') }}
</NeButton>
</div>
</NeTableCell>
</NeTableRow>
</NeTableBody>
<template #paginator>
<NePaginator
:current-page="currentPage"
:total-rows="events.length"
:page-size="pageSize"
:page-sizes="[5, 10]"
:nav-pagination-label="t('ne_table.pagination')"
:next-label="t('ne_table.go_to_next_page')"
:previous-label="t('ne_table.go_to_previous_page')"
:range-of-total-label="t('ne_table.of')"
:page-size-label="t('ne_table.show')"
@select-page="
{{
item.status
? t('standalone.interfaces_and_devices.up')
: t('standalone.interfaces_and_devices.down')
}}
</div>
</NeTableCell>
</NeTableRow>
</NeTableBody>
<template #paginator>
<NePaginator
:current-page="currentPage"
:total-rows="wanEvents.length"
:page-size="pageSize"
:page-sizes="[5, 10]"
:nav-pagination-label="t('ne_table.pagination')"
:next-label="t('ne_table.go_to_next_page')"
:previous-label="t('ne_table.go_to_previous_page')"
:range-of-total-label="t('ne_table.of')"
:page-size-label="t('ne_table.show')"
@select-page="
(page: number) => {
currentPage = page
}"
@selectPageSize="
@selectPageSize="
(size: number) => {
pageSize = size
}"
/>
</template>
</NeTable>
</NeCard>
</div>
/>
</template>
</NeTable>
</NeCard>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,16 @@ import 'chartjs-adapter-date-fns'
import { Line } from 'vue-chartjs'
import { computed } from 'vue'
import { useThemeStore } from '@/stores/theme'
import { byteFormat1024 } from '@nethesis/vue-components'
import { GRAY_200, GRAY_700, GRAY_800 } from '@/lib/color'
import { padStart } from 'lodash-es'

//// review

const themeStore = useThemeStore()

const props = defineProps({
labels: {
type: Array,
required: true
},
datasets: {
type: Array,
required: true
}
})
const props = defineProps<{
labels: string[]
datasets: any[]
height?: string
}>()

const options: any = {
// turn off animations and data parsing for performance
Expand Down Expand Up @@ -97,7 +89,7 @@ const chartData: any = computed(() => {

const chartStyle = computed(() => {
return {
'max-height': '25vh',
height: props.height || '',
width: '100%',
position: 'relative'
}
Expand Down
Loading

0 comments on commit 612eca6

Please sign in to comment.