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

View File

@ -3,7 +3,25 @@ from asyncio.subprocess import Process
import psutil import psutil
class SystemStatus: 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: class CPU:
def __init__(self): def __init__(self):
@ -11,8 +29,8 @@ class CPU:
self.cpu_percent_per_cpu: list[float] = [] self.cpu_percent_per_cpu: list[float] = []
def getData(self): def getData(self):
try: try:
self.cpu_percent = psutil.cpu_percent(interval=0.5, percpu=True) self.cpu_percent_per_cpu = psutil.cpu_percent(interval=0.5, percpu=True)
self.cpu_percent_per_cpu = psutil.cpu_percent(interval=0.5) self.cpu_percent = psutil.cpu_percent(interval=0.5)
except Exception as e: except Exception as e:
pass pass
def toJson(self): def toJson(self):
@ -21,22 +39,37 @@ class CPU:
class Memory: class Memory:
def __init__(self, memoryType: str): def __init__(self, memoryType: str):
self.__memoryType = memoryType self.__memoryType__ = memoryType
self.total = 0 self.total = 0
self.available = 0 self.available = 0
self.percent = 0 self.percent = 0
def getData(self): def getData(self):
if self.__memoryType == "virtual": try:
if self.__memoryType__ == "virtual":
memory = psutil.virtual_memory() memory = psutil.virtual_memory()
else: else:
memory = psutil.swap_memory() memory = psutil.swap_memory()
self.total = memory.total self.total = memory.total
self.available = memory.available self.available = memory.available
self.percent = memory.percent self.percent = memory.percent
except Exception as e:
pass
def toJson(self): def toJson(self):
self.getData() self.getData()
return self.__dict__ 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: class Disk:
def __init__(self, mountPoint: str): def __init__(self, mountPoint: str):
self.total = 0 self.total = 0
@ -45,11 +78,14 @@ class Disk:
self.percent = 0 self.percent = 0
self.mountPoint = mountPoint self.mountPoint = mountPoint
def getData(self): def getData(self):
try:
disk = psutil.disk_usage(self.mountPoint) disk = psutil.disk_usage(self.mountPoint)
self.total = disk.total self.total = disk.total
self.free = disk.free self.free = disk.free
self.used = disk.used self.used = disk.used
self.percent = disk.percent self.percent = disk.percent
except Exception as e:
pass
def toJson(self): def toJson(self):
self.getData() self.getData()
return self.__dict__ return self.__dict__
@ -58,9 +94,12 @@ class NetworkInterfaces:
def __init__(self): def __init__(self):
self.interfaces = {} self.interfaces = {}
def getData(self): def getData(self):
try:
network = psutil.net_io_counters(pernic=True, nowrap=True) network = psutil.net_io_counters(pernic=True, nowrap=True)
for i in network.keys(): for i in network.keys():
self.interfaces[i] = network[i]._asdict() self.interfaces[i] = network[i]._asdict()
except Exception as e:
pass
def toJson(self): def toJson(self):
self.getData() self.getData()
return self.interfaces return self.interfaces
@ -78,19 +117,21 @@ class Processes:
self.CPU_Top_10_Processes: list[Processes.Process] = [] self.CPU_Top_10_Processes: list[Processes.Process] = []
self.Memory_Top_10_Processes: list[Processes.Process] = [] self.Memory_Top_10_Processes: list[Processes.Process] = []
def getData(self): def getData(self):
while True:
try:
processes = list(psutil.process_iter()) processes = list(psutil.process_iter())
self.CPU_Top_10_Processes = sorted(list(map(lambda x : self.CPU_Top_10_Processes = sorted(
Processes.Process(x.name(), " ".join(x.cmdline()), x.pid, x.cpu_percent()), processes)), 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] key=lambda x : x.percent, reverse=True)[:20]
self.Memory_Top_10_Processes = sorted(list(map(lambda x : self.Memory_Top_10_Processes = sorted(
Processes.Process(x.name(), " ".join(x.cmdline()), x.pid, x.memory_percent()), processes)), 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] key=lambda x : x.percent, reverse=True)[:20]
break
except Exception as e:
continue
def toJson(self): def toJson(self):
self.getData() self.getData()
return { return {
"cpu_top_10": self.CPU_Top_10_Processes, "cpu_top_10": self.CPU_Top_10_Processes,
"memory_top_10": self.Memory_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> <samp>{{process.command ? process.command : process.name}}</samp>
</small> </small>
<small class="ms-auto"> <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> </small>
</div> </div>
</template> </template>

View File

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

View File

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

View File

@ -12,6 +12,36 @@ const data = computed(() => {
return dashboardStore.SystemStatus return dashboardStore.SystemStatus
}) })
let interval = null; 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(() => { onMounted(() => {
getData() getData()
@ -24,11 +54,97 @@ onBeforeUnmount(() => {
clearInterval(interval) clearInterval(interval)
}) })
const historicalChartTimestamp = ref([])
const historicalCpuUsage = ref([])
const historicalVirtualMemoryUsage = ref([])
const historicalSwapMemoryUsage = ref([])
let i = 0.1
const getData = () => { const getData = () => {
fetchGet("/api/systemStatus", {}, (res) => { fetchGet("/api/systemStatus", {}, (res) => {
historicalChartTimestamp.value.push(dayjs().format("HH:mm:ss A"))
dashboardStore.SystemStatus = res.data 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> </script>
<template> <template>
@ -36,8 +152,8 @@ const getData = () => {
<div class="col-sm-6"> <div class="col-sm-6">
<div class="card rounded-3 h-100 shadow"> <div class="card rounded-3 h-100 shadow">
<div class="card-body p-4"> <div class="card-body p-4">
<div class="row"> <div class="d-flex flex-column gap-3">
<div class="col-sm-12 d-flex flex-column gap-3"> <div class="d-flex flex-column gap-3" style="height: 130px">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<h3 class="text-muted mb-0"> <h3 class="text-muted mb-0">
<i class="bi bi-cpu-fill me-2"></i> <i class="bi bi-cpu-fill me-2"></i>
@ -45,42 +161,58 @@ const getData = () => {
</h3> </h3>
<h3 class="ms-auto mb-0"> <h3 class="ms-auto mb-0">
<span v-if="data"> <span v-if="data">
{{ data.cpu.cpu_percent }}% {{ data.CPU.cpu_percent }}%
</span> </span>
<span v-else class="spinner-border"></span> <span v-else class="spinner-border"></span>
</h3> </h3>
</div> </div>
<div class="progress" role="progressbar" style="height: 10px"> <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>
<div class="d-flex gap-1"> <div class="d-flex gap-1">
<CpuCore <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" :square="true"
:key="count" :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" :core_number="count" :percentage="cpu"
></CpuCore> ></CpuCore>
</div> </div>
<h5 class="mb-0">Processes</h5> </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"> <div class="position-relative">
<TransitionGroup name="process"> <TransitionGroup name="process">
<Process <Process
:key="p.pid" :key="p.pid"
:cpu="true" :cpu="true"
:process="p" v-for="p in data?.process.cpu_top_10"></Process> :process="p" v-for="p in data?.Processes.cpu_top_10"></Process>
</TransitionGroup> </TransitionGroup>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="card rounded-3 h-100 shadow"> <div class="card rounded-3 h-100 shadow">
<div class="card-body p-4"> <div class="card-body p-4">
<div class="row"> <div class="d-flex flex-column gap-3">
<div class="col-sm-12 d-flex flex-column gap-3"> <div class="d-flex flex-column gap-3" style="height: 130px">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<h3 class="text-muted"> <h3 class="text-muted">
<i class="bi bi-memory me-2"></i> <i class="bi bi-memory me-2"></i>
@ -88,27 +220,44 @@ const getData = () => {
</h3> </h3>
<h3 class="ms-auto"> <h3 class="ms-auto">
<span v-if="data"> <span v-if="data">
{{ data.memory.virtual_memory.percent }}% {{ data?.Memory.VirtualMemory.percent }}%
</span> </span>
<span v-else class="spinner-border"></span> <span v-else class="spinner-border"></span>
</h3> </h3>
</div> </div>
<div class="progress" role="progressbar" style="height: 10px"> <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>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<h6 class="mb-0">Swap Memory</h6> <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>
<div class="progress" role="progressbar" style="height: 10px"> <div class="progress" role="progressbar" style="height: 10px">
<div class="progress-bar bg-info-subtle" :style="{width: `${data?.memory.swap_memory.percent}%` }"></div> <div class="progress-bar bg-info-subtle" :style="{width: `${data?.Memory.SwapMemory.percent}%` }"></div>
</div> </div>
<h5 class="mb-0">Processes</h5> </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"> <div class="position-relative">
<TransitionGroup name="process"> <TransitionGroup name="process">
<Process <Process
:key="p.pid" :key="p.pid"
:process="p" v-for="p in data?.process.memory_top_10"> :process="p" v-for="p in data?.Processes.memory_top_10">
</Process> </Process>
</TransitionGroup> </TransitionGroup>
</div> </div>
@ -116,7 +265,6 @@ const getData = () => {
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="col-sm-12"> <div class="col-sm-12">
<div class="card rounded-3 h-100 shadow"> <div class="card rounded-3 h-100 shadow">
<div class="card-body p-4 d-flex gap-3 flex-column"> <div class="card-body p-4 d-flex gap-3 flex-column">
@ -127,13 +275,13 @@ const getData = () => {
</h3> </h3>
<h3 class="ms-auto mb-0"> <h3 class="ms-auto mb-0">
<span v-if="data"> <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>
<span v-else class="spinner-border"></span> <span v-else class="spinner-border"></span>
</h3> </h3>
</div> </div>
<div v-if="data" class="row g-3"> <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"> class="col-sm-6 fadeIn">
<div class="d-flex mb-2"> <div class="d-flex mb-2">
<h6 class="mb-0"> <h6 class="mb-0">
@ -142,22 +290,22 @@ const getData = () => {
<h6 class="mb-0 ms-auto d-flex gap-2"> <h6 class="mb-0 ms-auto d-flex gap-2">
<span class="text-info"> <span class="text-info">
<i class="bi bi-arrow-down"></i> <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>
<span class="text-warning"> <span class="text-warning">
<i class="bi bi-arrow-up"></i> <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> </span>
</h6> </h6>
</div> </div>
<div class="progress" role="progressbar" style="height: 10px"> <div class="progress" role="progressbar" style="height: 10px">
<div class="progress-bar bg-info" <div class="progress-bar bg-info"
v-if="data.network[key].byte_recv > 0" v-if="data.NetworkInterfaces[key].byte_recv > 0"
:style="{width: `${(data.network[key].byte_recv / (data.network[key].byte_sent + data.network[key].byte_recv)) * 100}%` }"></div> :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" <div class="progress-bar bg-warning"
v-if="data.network[key].byte_sent > 0" v-if="data.NetworkInterfaces[key].byte_sent > 0"
:style="{width: `${(data.network[key].byte_sent / (data.network[key].byte_sent + data.network[key].byte_recv)) * 100}%` }"></div> :style="{width: `${(data.NetworkInterfaces[key].bytes_sent / (data.NetworkInterfaces[key].byte_sent + data.NetworkInterfaces[key].byte_recv)) * 100}%` }"></div>
</div> </div>
</div> </div>
</div> </div>
@ -174,28 +322,28 @@ const getData = () => {
</h3> </h3>
<h3 class="ms-auto mb-0"> <h3 class="ms-auto mb-0">
<span v-if="data"> <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>
<span v-else class="spinner-border"></span> <span v-else class="spinner-border"></span>
</h3> </h3>
</div> </div>
<div class="row g-3"> <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"> v-if="data">
<div class="d-flex mb-2"> <div class="d-flex mb-2">
<h6 class="mb-0"> <h6 class="mb-0">
<samp>{{key}}</samp> <samp>{{disk.mountPoint}}</samp>
</h6> </h6>
<h6 class="mb-0 ms-auto d-flex gap-2"> <h6 class="mb-0 ms-auto d-flex gap-2">
<span class="text-success"> <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> </span>
</h6> </h6>
</div> </div>
<div class="progress" role="progressbar" style="height: 20px"> <div class="progress" role="progressbar" style="height: 20px">
<div class="progress-bar bg-success" <div class="progress-bar bg-success"
:style="{width: `${data.disk[key].percent}%`}"> :style="{width: `${disk.percent}%`}">
{{ data.disk[key].percent }}% {{ disk.percent }}%
</div> </div>
</div> </div>
</div> </div>
@ -217,7 +365,7 @@ const getData = () => {
.process-enter-from, .process-enter-from,
.process-leave-to { .process-leave-to {
opacity: 0; opacity: 0;
transform: translateY(30px); transform: scale(0.9);
} }
.process-leave-active { .process-leave-active {