Refactored system status into a class. Added charts

This commit is contained in:
Donald Zou 2025-01-24 19:19:17 +08:00
parent 84069ee882
commit e603af5f24
6 changed files with 352 additions and 163 deletions

View File

@ -2948,74 +2948,77 @@ def API_Email_PreviewBody():
except Exception as e:
return ResponseObject(False, message=str(e))
from modules.SystemStatus import SystemStatus
SystemStatus = SystemStatus()
@app.get(f'{APP_PREFIX}/api/systemStatus')
def API_SystemStatus():
cpu_percpu = psutil.cpu_percent(interval=0.5, percpu=True)
cpu = psutil.cpu_percent(interval=0.5)
memory = psutil.virtual_memory()
swap_memory = psutil.swap_memory()
disks = psutil.disk_partitions()
network = psutil.net_io_counters(pernic=True, nowrap=True)
status = {
"cpu": {
"cpu_percent": cpu,
"cpu_percent_per_cpu": cpu_percpu,
},
"memory": {
"virtual_memory": {
"total": memory.total,
"available": memory.available,
"percent": memory.percent
},
"swap_memory": {
"total": swap_memory.total,
"used": swap_memory.used,
"percent": swap_memory.percent
}
},
"disk": {},
"network": {},
"process": {
"cpu_top_10": [],
"memory_top_10": []
}
}
for d in disks:
detail = psutil.disk_usage(d.mountpoint)
status['disk'][d.mountpoint] = {
"total": detail.total,
"used": detail.used,
"free": detail.free,
"percent": detail.percent
}
for i in network.keys():
status["network"][i] = {
"byte_sent": network[i].bytes_sent,
"byte_recv": network[i].bytes_recv
}
while True:
try:
processes = list(psutil.process_iter())
status["process"]["cpu_top_10"] = sorted(list(map(lambda x : {
"name": x.name(),
"command": " ".join(x.cmdline()),
"pid": x.pid,
"cpu_percent": x.cpu_percent()
}, processes)), key=lambda x : x['cpu_percent'], reverse=True)[:10]
status["process"]["memory_top_10"] = sorted(list(map(lambda x : {
"name": x.name(),
"command": " ".join(x.cmdline()),
"pid": x.pid,
"memory_percent": x.memory_percent()
}, processes)), key=lambda x : x['memory_percent'], reverse=True)[:10]
break
except Exception as e:
continue
return ResponseObject(data=status)
# cpu_percpu = psutil.cpu_percent(interval=0.5, percpu=True)
# cpu = psutil.cpu_percent(interval=0.5)
# memory = psutil.virtual_memory()
# swap_memory = psutil.swap_memory()
# disks = psutil.disk_partitions()
# network = psutil.net_io_counters(pernic=True, nowrap=True)
#
#
# status = {
# "cpu": {
# "cpu_percent": cpu,
# "cpu_percent_per_cpu": cpu_percpu,
# },
# "memory": {
# "virtual_memory": {
# "total": memory.total,
# "available": memory.available,
# "percent": memory.percent
# },
# "swap_memory": {
# "total": swap_memory.total,
# "used": swap_memory.used,
# "percent": swap_memory.percent
# }
# },
# "disk": {},
# "network": {},
# "process": {
# "cpu_top_10": [],
# "memory_top_10": []
# },
# "SystemStatus": systemStatus
# }
# for d in disks:
# detail = psutil.disk_usage(d.mountpoint)
# status['disk'][d.mountpoint] = {
# "total": detail.total,
# "used": detail.used,
# "free": detail.free,
# "percent": detail.percent
# }
# for i in network.keys():
# status["network"][i] = {
# "byte_sent": network[i].bytes_sent,
# "byte_recv": network[i].bytes_recv
# }
#
# while True:
# try:
# processes = list(psutil.process_iter())
# status["process"]["cpu_top_10"] = sorted(list(map(lambda x : {
# "name": x.name(),
# "command": " ".join(x.cmdline()),
# "pid": x.pid,
# "cpu_percent": x.cpu_percent()
# }, processes)), key=lambda x : x['cpu_percent'], reverse=True)[:10]
# status["process"]["memory_top_10"] = sorted(list(map(lambda x : {
# "name": x.name(),
# "command": " ".join(x.cmdline()),
# "pid": x.pid,
# "memory_percent": x.memory_percent()
# }, processes)), key=lambda x : x['memory_percent'], reverse=True)[:10]
# break
# except Exception as e:
# continue
return ResponseObject(data=SystemStatus)
@app.get(f'{APP_PREFIX}/api/protocolsEnabled')
def API_ProtocolsEnabled():

View File

@ -3,7 +3,25 @@ from asyncio.subprocess import Process
import psutil
class SystemStatus:
pass
def __init__(self):
self.CPU = CPU()
self.MemoryVirtual = Memory('virtual')
self.MemorySwap = Memory('swap')
self.Disks = Disks()
self.NetworkInterfaces = NetworkInterfaces()
self.Processes = Processes()
def toJson(self):
return {
"CPU": self.CPU,
"Memory": {
"VirtualMemory": self.MemoryVirtual,
"SwapMemory": self.MemorySwap
},
"Disks": self.Disks,
"NetworkInterfaces": self.NetworkInterfaces,
"Processes": self.Processes
}
class CPU:
def __init__(self):
@ -11,8 +29,8 @@ class CPU:
self.cpu_percent_per_cpu: list[float] = []
def getData(self):
try:
self.cpu_percent = psutil.cpu_percent(interval=0.5, percpu=True)
self.cpu_percent_per_cpu = psutil.cpu_percent(interval=0.5)
self.cpu_percent_per_cpu = psutil.cpu_percent(interval=0.5, percpu=True)
self.cpu_percent = psutil.cpu_percent(interval=0.5)
except Exception as e:
pass
def toJson(self):
@ -21,22 +39,37 @@ class CPU:
class Memory:
def __init__(self, memoryType: str):
self.__memoryType = memoryType
self.__memoryType__ = memoryType
self.total = 0
self.available = 0
self.percent = 0
def getData(self):
if self.__memoryType == "virtual":
memory = psutil.virtual_memory()
else:
memory = psutil.swap_memory()
self.total = memory.total
self.available = memory.available
self.percent = memory.percent
try:
if self.__memoryType__ == "virtual":
memory = psutil.virtual_memory()
else:
memory = psutil.swap_memory()
self.total = memory.total
self.available = memory.available
self.percent = memory.percent
except Exception as e:
pass
def toJson(self):
self.getData()
return self.__dict__
class Disks:
def __init__(self):
self.disks : list[Disk] = []
def getData(self):
try:
self.disks = list(map(lambda x : Disk(x.mountpoint), psutil.disk_partitions()))
except Exception as e:
pass
def toJson(self):
self.getData()
return self.disks
class Disk:
def __init__(self, mountPoint: str):
self.total = 0
@ -45,11 +78,14 @@ class Disk:
self.percent = 0
self.mountPoint = mountPoint
def getData(self):
disk = psutil.disk_usage(self.mountPoint)
self.total = disk.total
self.free = disk.free
self.used = disk.used
self.percent = disk.percent
try:
disk = psutil.disk_usage(self.mountPoint)
self.total = disk.total
self.free = disk.free
self.used = disk.used
self.percent = disk.percent
except Exception as e:
pass
def toJson(self):
self.getData()
return self.__dict__
@ -58,9 +94,12 @@ class NetworkInterfaces:
def __init__(self):
self.interfaces = {}
def getData(self):
network = psutil.net_io_counters(pernic=True, nowrap=True)
for i in network.keys():
self.interfaces[i] = network[i]._asdict()
try:
network = psutil.net_io_counters(pernic=True, nowrap=True)
for i in network.keys():
self.interfaces[i] = network[i]._asdict()
except Exception as e:
pass
def toJson(self):
self.getData()
return self.interfaces
@ -78,19 +117,21 @@ class Processes:
self.CPU_Top_10_Processes: list[Processes.Process] = []
self.Memory_Top_10_Processes: list[Processes.Process] = []
def getData(self):
processes = list(psutil.process_iter())
self.CPU_Top_10_Processes = sorted(list(map(lambda x :
Processes.Process(x.name(), " ".join(x.cmdline()), x.pid, x.cpu_percent()), processes)),
key=lambda x : x.percent, reverse=True)[:10]
self.Memory_Top_10_Processes = sorted(list(map(lambda x :
Processes.Process(x.name(), " ".join(x.cmdline()), x.pid, x.memory_percent()), processes)),
key=lambda x : x.percent, reverse=True)[:10]
while True:
try:
processes = list(psutil.process_iter())
self.CPU_Top_10_Processes = sorted(
list(map(lambda x : Processes.Process(x.name(), " ".join(x.cmdline()), x.pid, x.cpu_percent()), processes)),
key=lambda x : x.percent, reverse=True)[:20]
self.Memory_Top_10_Processes = sorted(
list(map(lambda x : Processes.Process(x.name(), " ".join(x.cmdline()), x.pid, x.memory_percent()), processes)),
key=lambda x : x.percent, reverse=True)[:20]
break
except Exception as e:
continue
def toJson(self):
self.getData()
return {
"cpu_top_10": self.CPU_Top_10_Processes,
"memory_top_10": self.Memory_Top_10_Processes
}
p = Processes()
print(p.toJson())

View File

@ -10,7 +10,7 @@ const props = defineProps(["process", "cpu"])
<samp>{{process.command ? process.command : process.name}}</samp>
</small>
<small class="ms-auto">
{{cpu ? process.cpu_percent : (Math.round((process.memory_percent + Number.EPSILON) * 10) / 10)}}%
{{(Math.round((process.percent + Number.EPSILON) * 10) / 10)}}%
</small>
</div>
</template>

View File

@ -2,8 +2,7 @@
import {computed, ref} from "vue";
const props = defineProps({
mount: String,
percentage: Number,
mount: Object,
align: Boolean,
square: Boolean
})
@ -19,7 +18,7 @@ const squareHeight = computed(() => {
<div class="flex-grow-1 square rounded-3 border position-relative"
@mouseenter="show = true"
@mouseleave="show = false"
:style="{'background-color': `rgb(25 135 84 / ${percentage}%)`}">
:style="{'background-color': `rgb(25 135 84 / ${mount.percent}%)`}">
<Transition name="zoomReversed">
<div
v-if="show"
@ -29,10 +28,10 @@ const squareHeight = computed(() => {
:class="[align ? 'end-0':'start-0']"
>
<small class="text-muted me-2">
<samp>{{mount}}</samp>
<samp>{{mount.mountPoint}}</samp>
</small>
<small class="fw-bold">
{{percentage}}%
{{mount.percent}}%
</small>
</div>
</Transition>

View File

@ -7,8 +7,6 @@ import StorageMount from "@/components/systemStatusComponents/storageMount.vue";
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
const dashboardStore = DashboardConfigurationStore()
// const data = ref(undefined)
let interval = null;
onMounted(() => {
@ -43,19 +41,19 @@ const data = computed(() => {
</h6>
<h6 class="ms-auto">
<span v-if="data">
{{ data.cpu.cpu_percent }}%
{{ data.CPU.cpu_percent }}%
</span>
<span v-else class="spinner-border spinner-border-sm"></span>
</h6>
</div>
<div class="progress" role="progressbar" style="height: 6px">
<div class="progress-bar" :style="{width: `${data?.cpu.cpu_percent}%` }"></div>
<div class="progress-bar" :style="{width: `${data?.CPU.cpu_percent}%` }"></div>
</div>
<div class="d-flex mt-2 gap-1">
<CpuCore
v-for="(cpu, count) in data?.cpu.cpu_percent_per_cpu"
v-for="(cpu, count) in data?.CPU.cpu_percent_per_cpu"
:key="count"
:align="(count + 1) > Math.round(data?.cpu.cpu_percent_per_cpu.length / 2)"
:align="(count + 1) > Math.round(data?.CPU.cpu_percent_per_cpu.length / 2)"
:core_number="count" :percentage="cpu"
></CpuCore>
</div>
@ -68,20 +66,20 @@ const data = computed(() => {
</h6>
<h6 class="ms-auto">
<span v-if="data">
{{ data?.disk['/'].percent }}%
{{ data?.Disks.find(x => x.mountPoint === '/').percent }}%
</span>
<span v-else class="spinner-border spinner-border-sm"></span>
</h6>
</div>
<div class="progress" role="progressbar" style="height: 6px">
<div class="progress-bar bg-success" :style="{width: `${data?.disk['/'].percent}%` }"></div>
<div class="progress-bar bg-success" :style="{width: `${data?.Disks.find(x => x.mountPoint === '/').percent}%` }"></div>
</div>
<div class="d-flex mt-2 gap-1">
<StorageMount v-for="(disk, count) in Object.keys(data?.disk)"
<StorageMount v-for="(disk, count) in data?.Disks"
v-if="data"
:key="count"
:align="(count + 1) > Math.round(Object.keys(data?.disk).length / 2)"
:mount="disk" :percentage="data?.disk[disk].percent"
:key="disk.mountPoint"
:align="(count + 1) > Math.round(data?.Disks.length / 2)"
:mount="disk"
></StorageMount>
</div>
</div>
@ -93,13 +91,13 @@ const data = computed(() => {
</h6>
<h6 class="ms-auto">
<span v-if="data">
{{ data?.memory.virtual_memory.percent }}%
{{ data?.Memory.VirtualMemory.percent }}%
</span>
<span v-else class="spinner-border spinner-border-sm"></span>
</h6>
</div>
<div class="progress" role="progressbar" style="height: 6px">
<div class="progress-bar bg-info" :style="{width: `${data?.memory.virtual_memory.percent}%` }"></div>
<div class="progress-bar bg-info" :style="{width: `${data?.Memory.VirtualMemory.percent}%` }"></div>
</div>
</div>
<div class="col-md-6 col-sm-12 col-xl-3">
@ -110,13 +108,13 @@ const data = computed(() => {
</h6>
<h6 class="ms-auto">
<span v-if="data">
{{ data?.memory.swap_memory.percent }}%
{{ data?.Memory.SwapMemory.percent }}%
</span>
<span v-else class="spinner-border spinner-border-sm"></span>
</h6>
</div>
<div class="progress" role="progressbar" style="height: 6px">
<div class="progress-bar bg-warning" :style="{width: `${data?.memory.swap_memory.percent}%` }"></div>
<div class="progress-bar bg-warning" :style="{width: `$ data?.Memory.SwapMemory.percent}%` }"></div>
</div>
</div>
</div>

View File

@ -12,6 +12,36 @@ const data = computed(() => {
return dashboardStore.SystemStatus
})
let interval = null;
import {
Chart,
LineElement,
BarElement,
BarController,
LineController,
LinearScale,
Legend,
Title,
Tooltip,
CategoryScale,
PointElement,
Filler
} from 'chart.js';
import {Line} from "vue-chartjs";
import dayjs from "dayjs";
import {GetLocale} from "@/utilities/locale.js";
Chart.register(
LineElement,
BarElement,
BarController,
LineController,
LinearScale,
Legend,
Title,
Tooltip,
CategoryScale,
PointElement,
Filler
);
onMounted(() => {
getData()
@ -24,11 +54,97 @@ onBeforeUnmount(() => {
clearInterval(interval)
})
const historicalChartTimestamp = ref([])
const historicalCpuUsage = ref([])
const historicalVirtualMemoryUsage = ref([])
const historicalSwapMemoryUsage = ref([])
let i = 0.1
const getData = () => {
fetchGet("/api/systemStatus", {}, (res) => {
historicalChartTimestamp.value.push(dayjs().format("HH:mm:ss A"))
dashboardStore.SystemStatus = res.data
historicalCpuUsage.value.push(res.data.CPU.cpu_percent)
historicalVirtualMemoryUsage.value.push(res.data.Memory.VirtualMemory.percent)
historicalSwapMemoryUsage.value.push(res.data.Memory.SwapMemory.percent)
})
}
const chartOption = computed(() => {
return {
responsive: true,
plugins: {
legend: {
display: true
},
tooltip: {
callbacks: {
label: (tooltipItem) => {
return `${tooltipItem.formattedValue}%`
}
}
}
},
scales: {
x: {
ticks: {
display: false,
},
grid: {
display: false
},
},
y:{
ticks: {
callback: (val, index) => {
return `${val}%`
}
},
grid: {
display: false
},
}
}
}
})
const cpuHistoricalChartData = computed(() => {
return {
labels: [...historicalChartTimestamp.value],
datasets: [
{
label: GetLocale('CPU Usage'),
data: [...historicalCpuUsage.value],
fill: 'start',
backgroundColor: '#0d6efd50',
borderColor: '#0d6efd',
tension: 0
}
]
}
})
const memoryHistoricalChartData = computed(() => {
return {
labels: [...historicalChartTimestamp.value],
datasets: [
{
label: GetLocale('Memory Usage'),
data: [...historicalVirtualMemoryUsage.value],
fill: 1,
borderColor: '#0dcaf0',
backgroundColor: '#0dcaf050',
tension: 0
},
{
label: GetLocale('Swap Memory Usage'),
data: [...historicalSwapMemoryUsage.value],
fill: 'start',
backgroundColor: '#ffc10750',
borderColor: '#ffc107',
tension: 0
}
]
}
})
</script>
<template>
@ -36,8 +152,8 @@ const getData = () => {
<div class="col-sm-6">
<div class="card rounded-3 h-100 shadow">
<div class="card-body p-4">
<div class="row">
<div class="col-sm-12 d-flex flex-column gap-3">
<div class="d-flex flex-column gap-3">
<div class="d-flex flex-column gap-3" style="height: 130px">
<div class="d-flex align-items-center">
<h3 class="text-muted mb-0">
<i class="bi bi-cpu-fill me-2"></i>
@ -45,32 +161,48 @@ const getData = () => {
</h3>
<h3 class="ms-auto mb-0">
<span v-if="data">
{{ data.cpu.cpu_percent }}%
{{ data.CPU.cpu_percent }}%
</span>
<span v-else class="spinner-border"></span>
</h3>
</div>
<div class="progress" role="progressbar" style="height: 10px">
<div class="progress-bar" :style="{width: `${data?.cpu.cpu_percent}%` }"></div>
<div class="progress-bar" :style="{width: `${data?.CPU.cpu_percent}%` }"></div>
</div>
<div class="d-flex gap-1">
<CpuCore
v-for="(cpu, count) in data?.cpu.cpu_percent_per_cpu"
v-for="(cpu, count) in data?.CPU.cpu_percent_per_cpu"
:square="true"
:key="count"
:align="(count + 1) > Math.round(data?.cpu.cpu_percent_per_cpu.length / 2)"
:align="(count + 1) > Math.round(data?.CPU.cpu_percent_per_cpu.length / 2)"
:core_number="count" :percentage="cpu"
></CpuCore>
</div>
<h5 class="mb-0">Processes</h5>
<div class="position-relative">
<TransitionGroup name="process">
<Process
:key="p.pid"
:cpu="true"
:process="p" v-for="p in data?.process.cpu_top_10"></Process>
</TransitionGroup>
</div>
</div>
<Line
:options="chartOption"
:data="cpuHistoricalChartData"
style="width: 100%; height: 200px; max-height: 200px"
></Line>
<div class="d-flex align-items-center">
<h5 class="mb-0">
<LocaleText t="Processes"></LocaleText>
</h5>
<h6 class="mb-0 ms-auto text-muted">
<small>
<LocaleText t="CPU Usage"></LocaleText>
</small>
</h6>
</div>
<hr class="my-1">
<div class="position-relative">
<TransitionGroup name="process">
<Process
:key="p.pid"
:cpu="true"
:process="p" v-for="p in data?.Processes.cpu_top_10"></Process>
</TransitionGroup>
</div>
</div>
</div>
@ -79,8 +211,8 @@ const getData = () => {
<div class="col-sm-6">
<div class="card rounded-3 h-100 shadow">
<div class="card-body p-4">
<div class="row">
<div class="col-sm-12 d-flex flex-column gap-3">
<div class="d-flex flex-column gap-3">
<div class="d-flex flex-column gap-3" style="height: 130px">
<div class="d-flex align-items-center">
<h3 class="text-muted">
<i class="bi bi-memory me-2"></i>
@ -88,31 +220,47 @@ const getData = () => {
</h3>
<h3 class="ms-auto">
<span v-if="data">
{{ data.memory.virtual_memory.percent }}%
{{ data?.Memory.VirtualMemory.percent }}%
</span>
<span v-else class="spinner-border"></span>
</h3>
</div>
<div class="progress" role="progressbar" style="height: 10px">
<div class="progress-bar bg-info" :style="{width: `${data?.memory.virtual_memory.percent}%` }"></div>
<div class="progress-bar bg-info" :style="{width: `${data?.Memory.VirtualMemory.percent}%` }"></div>
</div>
<div class="d-flex align-items-center">
<h6 class="mb-0">Swap Memory</h6>
<h6 class="mb-0 ms-auto">{{data?.memory.swap_memory.percent}}%</h6>
<h6 class="mb-0 ms-auto">{{data?.Memory.SwapMemory.percent}}%</h6>
</div>
<div class="progress" role="progressbar" style="height: 10px">
<div class="progress-bar bg-info-subtle" :style="{width: `${data?.memory.swap_memory.percent}%` }"></div>
</div>
<h5 class="mb-0">Processes</h5>
<div class="position-relative">
<TransitionGroup name="process">
<Process
:key="p.pid"
:process="p" v-for="p in data?.process.memory_top_10">
</Process>
</TransitionGroup>
<div class="progress-bar bg-info-subtle" :style="{width: `${data?.Memory.SwapMemory.percent}%` }"></div>
</div>
</div>
<Line
:options="chartOption"
:data="memoryHistoricalChartData"
style="width: 100%; height: 200px; max-height: 200px"
></Line>
<div class="d-flex align-items-center">
<h5 class="mb-0">
<LocaleText t="Processes"></LocaleText>
</h5>
<h6 class="mb-0 ms-auto text-muted">
<small>
<LocaleText t="Memory Usage"></LocaleText>
</small>
</h6>
</div>
<hr class="my-1">
<div class="position-relative">
<TransitionGroup name="process">
<Process
:key="p.pid"
:process="p" v-for="p in data?.Processes.memory_top_10">
</Process>
</TransitionGroup>
</div>
</div>
</div>
</div>
@ -127,13 +275,13 @@ const getData = () => {
</h3>
<h3 class="ms-auto mb-0">
<span v-if="data">
<LocaleText :t="Object.keys(data.network).length + ' Interface' + (Object.keys(data.network).length > 1 ? 's':'')"></LocaleText>
<LocaleText :t="Object.keys(data.NetworkInterfaces).length + ' Interface' + (Object.keys(data.NetworkInterfaces).length > 1 ? 's':'')"></LocaleText>
</span>
<span v-else class="spinner-border"></span>
</h3>
</div>
<div v-if="data" class="row g-3">
<div v-for="(key, index) in Object.keys(data.network).sort()"
<div v-for="(key, index) in Object.keys(data.NetworkInterfaces).sort()"
class="col-sm-6 fadeIn">
<div class="d-flex mb-2">
<h6 class="mb-0">
@ -142,22 +290,22 @@ const getData = () => {
<h6 class="mb-0 ms-auto d-flex gap-2">
<span class="text-info">
<i class="bi bi-arrow-down"></i>
{{ Math.round((data.network[key].byte_recv / 1024000000 + Number.EPSILON) * 10000) / 10000}} GB
{{ Math.round((data.NetworkInterfaces[key].bytes_recv / 1024000000 + Number.EPSILON) * 10000) / 10000}} GB
</span>
<span class="text-warning">
<i class="bi bi-arrow-up"></i>
{{ Math.round((data.network[key].byte_sent / 1024000000 + Number.EPSILON) * 10000) / 10000}} GB
{{ Math.round((data.NetworkInterfaces[key].bytes_sent / 1024000000 + Number.EPSILON) * 10000) / 10000}} GB
</span>
</h6>
</div>
<div class="progress" role="progressbar" style="height: 10px">
<div class="progress-bar bg-info"
v-if="data.network[key].byte_recv > 0"
:style="{width: `${(data.network[key].byte_recv / (data.network[key].byte_sent + data.network[key].byte_recv)) * 100}%` }"></div>
v-if="data.NetworkInterfaces[key].byte_recv > 0"
:style="{width: `${(data.NetworkInterfaces[key].bytes_recv / (data.NetworkInterfaces[key].byte_sent + data.NetworkInterfaces[key].byte_recv)) * 100}%` }"></div>
<div class="progress-bar bg-warning"
v-if="data.network[key].byte_sent > 0"
:style="{width: `${(data.network[key].byte_sent / (data.network[key].byte_sent + data.network[key].byte_recv)) * 100}%` }"></div>
v-if="data.NetworkInterfaces[key].byte_sent > 0"
:style="{width: `${(data.NetworkInterfaces[key].bytes_sent / (data.NetworkInterfaces[key].byte_sent + data.NetworkInterfaces[key].byte_recv)) * 100}%` }"></div>
</div>
</div>
</div>
@ -174,28 +322,28 @@ const getData = () => {
</h3>
<h3 class="ms-auto mb-0">
<span v-if="data">
<LocaleText :t="Object.keys(data.disk).length + ' Partition' + (Object.keys(data.disk).length > 1 ? 's':'')"></LocaleText>
<LocaleText :t="data.Disks.length + ' Partition' + (data.Disks.length > 1 ? 's':'')"></LocaleText>
</span>
<span v-else class="spinner-border"></span>
</h3>
</div>
<div class="row g-3">
<div v-for="(key, index) in Object.keys(data.disk).sort()" class="col-sm-6 fadeIn"
<div v-for="disk in data.Disks" class="col-sm-6 fadeIn"
v-if="data">
<div class="d-flex mb-2">
<h6 class="mb-0">
<samp>{{key}}</samp>
<samp>{{disk.mountPoint}}</samp>
</h6>
<h6 class="mb-0 ms-auto d-flex gap-2">
<span class="text-success">
{{ Math.round((data.disk[key].used / 1024000000 + Number.EPSILON) * 100) / 100}} / {{ Math.round((data.disk[key].total / 1024000000 + Number.EPSILON) * 100) / 100}} GB Used
{{ Math.round((disk.used / 1024000000 + Number.EPSILON) * 100) / 100}} / {{ Math.round((disk.total / 1024000000 + Number.EPSILON) * 100) / 100}} GB Used
</span>
</h6>
</div>
<div class="progress" role="progressbar" style="height: 20px">
<div class="progress-bar bg-success"
:style="{width: `${data.disk[key].percent}%`}">
{{ data.disk[key].percent }}%
:style="{width: `${disk.percent}%`}">
{{ disk.percent }}%
</div>
</div>
</div>
@ -217,7 +365,7 @@ const getData = () => {
.process-enter-from,
.process-leave-to {
opacity: 0;
transform: translateY(30px);
transform: scale(0.9);
}
.process-leave-active {