Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: monitoring UI #364

Merged
merged 2 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 89 additions & 31 deletions public/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@
"more_info": "More info",
"data_updated_every_seconds": "Data updated every {seconds} seconds",
"actions": "Actions",
"go_to_page": "Go to {page}"
"go_to_page": "Go to {page}",
"download": "Download",
"upload": "Upload",
"no_data_available": "No data available",
"type": "Type"
},
"error": {
"generic_error": "Something went wrong",
Expand Down Expand Up @@ -200,6 +204,7 @@
"cannot_delete_rule": "Cannot delete rule",
"upload_file_migration": "Uploaded file is not a valid migration archive",
"cannot_migrate": "Can't execute migration",
"cannot_retrieve_ovpn_tunnels": "Cannot retrieve OpenVPN tunnels",
"cannot_retrieve_server_tunnels": "Cannot retrieve server tunnels",
"cannot_retrieve_client_tunnels": "Cannot retrieve client tunnels",
"cannot_duplicate_tunnel": "Cannot duplicate tunnel",
Expand Down Expand Up @@ -357,7 +362,15 @@
"start_reserved": "First IP of network is reserved",
"cannot_retrieve_object_suggestions": "Cannot retrieve object suggestions",
"cannot_retrieve_nat_helpers": "Cannot retrieve NAT helpers",
"cannot_save_nat_helper": "Cannot save NAT helper"
"cannot_save_nat_helper": "Cannot save NAT helper",
"cannot_retrieve_mwan_report": "Cannot retrieve MultiWAN report",
"cannot_retrieve_malware_report": "Cannot retrieve malware report",
"cannot_retrieve_attack_report": "Cannot retrieve attack report",
"cannot_retrieve_ovpn_traffic_by_hour": "Cannot retrieve OpenVPN traffic by hour",
"cannot_retrieve_vpn_client_sessions": "Cannot retrieve VPN client sessions",
"cannot_retrieve_connected_client_by_hour": "Cannot retrieve connected clients by hour",
"cannot_retrieve_client_traffic_by_hour": "Cannot retrieve client traffic by hour",
"cannot_retrieve_talkers_list": "Cannot retrieve talkers list"
},
"ne_text_input": {
"show_password": "Show password",
Expand Down Expand Up @@ -430,15 +443,6 @@
"hotspot": "Hotspot",
"thread_shield_ip": "Threat Shield IP",
"known_hosts": "Known hosts",
"real_time_traffic": "Real time traffic",
"host": "Host",
"traffic": "Traffic",
"today_top_hosts": "Top hosts",
"today_top_applications": "Top applications",
"today_top_protocols": "Top protocols",
"today_top_hosts_description": "Hosts that generated most traffic today",
"today_top_applications_description": "Applications that generated most traffic today",
"today_top_protocols_description": "Protocols that generated most traffic today",
"wan_traffic": "WAN traffic",
"wan_interface": "WAN interface",
"download": "Download",
Expand All @@ -448,9 +452,6 @@
"root_usage": "Root",
"storage_usage": "Data",
"tmpfs_usage": "Tmpfs",
"no_hosts": "No hosts",
"no_applications": "No applications",
"no_protocols": "No protocols",
"openvpn_rw": "OpenVPN RW",
"usage_free_of_total": "{free} free of {total}",
"default_hostname_warning": "Using the default hostname 'NethSec' is not recommended. Change it in {systemSettingsLink} and apply the change.",
Expand Down Expand Up @@ -1443,7 +1444,7 @@
"blocklist_description": "Threat shield keeps you safe by blocking attacks from known malicious IP addresses. These addresses are compiled into blocklists, each with a clear name that tells you its purpose and who maintains it. The confidence score is a value from 1 to 10 that indicates the quality of the list. A higher number means a lower chance of false positives. This score is not available for 'Community' lists.",
"blocklist_subscription_description": "Nethesis and Yoroi blocklists are available only if the unit has a valid subscription that includes Threat shield service.",
"threat_shield_enabled": "Threat shield enabled",
"threat_shield_disabled": "Threat shield disabled",
"threat_shield_disabled": "Threat shield is disabled",
"name": "Name",
"type": "Type",
"confidence": "Confidence",
Expand Down Expand Up @@ -1783,22 +1784,79 @@
"search_error": "Invalid regular expression.",
"search_tooltip": "The search uses regular expression format."
},
"report": {
"title": "Report",
"tabs": {
"real_time_report": "Real time report",
"ping_latency_monitor": "Ping latency monitor"
},
"real_time_report": {
"description": "The monitoring tool offers in-depth analysis of system and application performance, presenting detailed metrics and visualizations. These metrics are stored in RAM and reset upon machine reboot, except when the unit is linked to a remote controller. In such cases, metrics are saved in the controller and persist through reboots.",
"open_report": "Open report",
"cannot_open_report_from_controller": "Cannot open report from a controlled unit. You can open the report by accessing the unit UI directly, or by accessing the controller UI, going to 'Unit manager' page, clicking the three dots menu of the unit and selecting 'Open metrics'."
},
"ping_latency_monitor": {
"description": "Set up the monitoring tool to assess round-trip time and packet loss by sending ping messages to network hosts. This tool is used for monitoring network connectivity quality. You can add one or more hosts to monitor. It's also possible to add IP addresses in a VPN to assess tunnel quality.",
"add_host": "Add host",
"host_to_monitor": "Hosts to monitor"
}
"monitoring": {
"title": "Monitoring"
},
"real_time_monitor": {
"title": "Real time monitor",
"description": "In-depth analysis of system and application performance, detailed metrics and visualizations. Monitoring data are stored in RAM and reset upon unit reboot. If the unit is linked to a remote controller, metrics are saved on the controller too and persist through unit reboots.",
"open_report": "Open Netdata",
"cannot_open_grafana_message": "Grafana monitoring is available only after connecting the unit to a controller",
"cannot_open_report_from_controller": "Cannot open report from a controlled unit. You can open the report by accessing the unit UI directly, or by accessing the controller UI, going to 'Unit manager' page, clicking the three dots menu of the unit and selecting 'Open metrics'.",
"traffic": "Traffic",
"connectivity": "Connectivity",
"vpn": "VPN",
"security": "Security",
"today_top_local_hosts": "Local hosts",
"no_hosts": "No hosts",
"today_top_applications": "Applications",
"today_top_remote_hosts": "Remote hosts",
"no_applications": "No applications",
"today_top_protocols": "Protocols",
"no_protocols": "No protocols",
"today_total_traffic": "Today total traffic",
"instant_traffic": "Instant traffic",
"today_traffic": "Today traffic",
"recent_traffic": "Recent traffic",
"host": "Host",
"hosts": "Hosts",
"application": "Application",
"applications": "Applications",
"protocol": "Protocol",
"protocols": "Protocols",
"connections": "Connections",
"wan_events": "WAN events",
"wan_name_events": "{name} events",
"interface_name_traffic": "{name} traffic",
"blocked_threats": "Blocked threats",
"blocked_packets_by_hour": "Blocked packets by hour",
"malware_by_direction": "Malware by direction",
"malware_by_category": "Malware by category",
"most_blocked_ip_addresses": "Most blocked IP addresses",
"blocked_ip_addresses_by_hour": "Blocked IP addresses by hour",
"times_blocked": "Times blocked",
"blocked_ip_addresses": "Blocked IP addresses",
"timestamp": "Timestamp",
"event": "Event",
"remote_access_vpn": "Remote access VPN",
"site_to_site_vpn": "Site-to-Site VPN",
"connected_clients": "Connected clients",
"total_clients_traffic_by_hour": "Total clients traffic by hour",
"client_sessions": "Client sessions",
"account": "Account",
"connected_since": "Connected since",
"no_sessions": "No sessions",
"connected_clients_by_hour": "Connected clients by hour",
"client_traffic_by_hour": "Client traffic by hour",
"connected_tunnels": "Connected tunnels",
"configured_tunnels": "Configured tunnels",
"tunnel_ovpn_client": "OpenVPN client tunnel",
"tunnel_ovpn_server": "OpenVPN server tunnel",
"tunnel_ipsec": "IPsec tunnel",
"device": "Device",
"interface": "Interface",
"threat_shield_disabled_message": "No statistics are available because Threat shield is disabled.",
"online": "Online",
"offline": "Offline",
"no_events_message": "No events, all good",
"view_all_on_grafana": "View all on Grafana",
"no_vpn_network_configured": "No VPN network configured"
},
"ping_latency_monitor": {
"title": "Ping latency monitor",
"description": "Set up the monitoring tool to assess round-trip time and packet loss by sending ping messages to network hosts. This tool is used for monitoring network connectivity quality. You can add one or more hosts to monitor. It's also possible to add IP addresses in a VPN to assess tunnel quality.",
"add_host": "Add host",
"host_to_monitor": "Hosts to monitor"
},
"account_management": {
"title": "Account settings for '{name}'",
Expand Down
103 changes: 103 additions & 0 deletions src/components/charts/BasicBarChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->

<script setup lang="ts">
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
BarElement,
CategoryScale,
LinearScale
} from 'chart.js'
import { Bar } from 'vue-chartjs'
import { computed } from 'vue'
import { useThemeStore } from '@/stores/theme'
import { GRAY_200, GRAY_700, GRAY_800 } from '@/lib/color'

const themeStore = useThemeStore()

const props = withDefaults(
defineProps<{
labels: string[]
datasets: any[]
height?: string
isHorizontal?: boolean
showLegend?: boolean
showValuesOnBars?: boolean
}>(),
{ isHorizontal: false, showLegend: false, showValuesOnBars: true }
)

const options: any = {
indexAxis: props.isHorizontal ? 'y' : 'x',
scales: {
x: {
ticks: {
color: themeStore.isLight ? GRAY_700 : GRAY_200
},
grid: {
color: themeStore.isLight ? GRAY_200 : GRAY_800
}
},
y: {
ticks: {
color: themeStore.isLight ? GRAY_700 : GRAY_200
},
grid: {
color: themeStore.isLight ? GRAY_200 : GRAY_800
}
}
},
plugins: {
legend: {
display: props.showLegend
}
},
animation:
props.showValuesOnBars && props.isHorizontal
? {
duration: 1,
onComplete: function ({ chart }: any) {
const ctx = chart.ctx

chart.config.data.datasets.forEach(function (dataset: any, i: number) {
const meta = chart.getDatasetMeta(i)

meta.data.forEach(function (bar: any, index: number) {
const data = dataset.data[index]

if (data) {
ctx.fillStyle = themeStore.isLight ? GRAY_700 : GRAY_200
ctx.fillText(Number(data).toLocaleString(), bar.x + 5, bar.y + 5)
}
})
})
}
}
: {}
}

const chartData: any = computed(() => {
return { labels: props.labels, datasets: props.datasets }
})

const chartStyle = computed(() => {
return {
height: props.height || '',
width: '100%',
position: 'relative'
}
})

ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend)
</script>

<template>
<div>
<Bar :data="chartData" :options="options" :style="chartStyle" />
</div>
</template>
85 changes: 85 additions & 0 deletions src/components/charts/BasicPieChart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->

<script setup lang="ts">
import 'chartjs-adapter-date-fns'
import { computed } from 'vue'
import { useThemeStore } from '@/stores/theme'
import { GRAY_200, GRAY_700 } from '@/lib/color'
import { Pie } from 'vue-chartjs'
import { Chart as ChartJS, registerables } from 'chart.js'

const themeStore = useThemeStore()

const props = defineProps<{
labels: string[]
datasets: any[]
height?: string
}>()

const options: any = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
// show dataset value and sort by pie slice
generateLabels: function (chart: any) {
const data = chart.data
if (data.labels.length && data.datasets.length) {
// Create an array of label-value pairs
const labelValuePairs = data.labels.map((label: string, i: number) => {
const value = data.datasets[0].data[i]
return { label, value, index: i }
})

// Sort the array by value in descending order
labelValuePairs.sort((a: any, b: any) => b.value - a.value)

// Return the sorted labels
return labelValuePairs.map(
({ label, value, index }: { label: string; value: number; index: number }) => {
const meta = chart.getDatasetMeta(0)
const ds = data.datasets[0]
const arc = meta.data[index]
const custom = (arc && arc.custom) || {}
return {
text: `${label}: ${value?.toLocaleString()}`,
fontColor: themeStore.isLight ? GRAY_700 : GRAY_200,
fillStyle: custom.backgroundColor || ds.backgroundColor[index],
hidden: isNaN(ds.data[index]) || meta.data[index].hidden,
index: index
}
}
)
}
return []
}
}
}
}
}

const chartData: any = computed(() => {
return { labels: props.labels, datasets: props.datasets }
})

const chartStyle = computed(() => {
return {
height: props.height || '',
width: '100%',
position: 'relative'
}
})

ChartJS.register(...registerables)
</script>

<template>
<div>
<Pie :data="chartData" :options="options" :style="chartStyle" />
</div>
</template>
12 changes: 12 additions & 0 deletions src/components/charts/SimpleStat.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!--
Copyright (C) 2024 Nethesis S.r.l.
SPDX-License-Identifier: GPL-3.0-or-later
-->

<template>
<div
class="inline-block w-full text-center text-2xl font-medium text-indigo-700 dark:text-indigo-500 2xl:text-3xl"
>
<slot />
</div>
</template>
Loading