added promstats backend with charts
This commit is contained in:
parent
e8f09f7777
commit
2c2807d15c
10 changed files with 308 additions and 373 deletions
|
@ -2,5 +2,5 @@
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
compatibilityDate: '2024-11-01',
|
compatibilityDate: '2024-11-01',
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: true },
|
||||||
modules: ['@nuxtjs/tailwindcss'],
|
modules: ['@nuxtjs/tailwindcss', 'nuxt-charts'],
|
||||||
})
|
})
|
10
package.json
10
package.json
|
@ -11,16 +11,18 @@
|
||||||
"password_gen": "node .password_hash_gen/generator.js"
|
"password_gen": "node .password_hash_gen/generator.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nuxt": "^3.15.4",
|
|
||||||
"vue": "latest",
|
|
||||||
"vue-router": "latest",
|
|
||||||
"@nuxtjs/tailwindcss": "^6.13.1",
|
"@nuxtjs/tailwindcss": "^6.13.1",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"bcryptjs": "^3.0.0",
|
"bcryptjs": "^3.0.0",
|
||||||
"daisyui": "^4.12.23",
|
"daisyui": "^4.12.23",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"systeminformation": "^5.25.11"
|
"nuxt": "^3.15.4",
|
||||||
|
"nuxt-charts": "^0.1.10",
|
||||||
|
"promstats": "^1.0.1",
|
||||||
|
"systeminformation": "^5.25.11",
|
||||||
|
"vue": "latest",
|
||||||
|
"vue-router": "latest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jsonwebtoken": "^9.0.8"
|
"@types/jsonwebtoken": "^9.0.8"
|
||||||
|
|
574
pages/index.vue
574
pages/index.vue
|
@ -1,248 +1,116 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { VM } from '~/types/VM';
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import { reactive, ref } from 'vue';
|
import axios from "axios";
|
||||||
import axios from 'axios';
|
|
||||||
import type {networkInterface} from "~/types/networkInterface";
|
|
||||||
import type {serviceInterface} from "~/types/serviceInterface";
|
|
||||||
import {checkAuth} from "~/util/auth";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const startVm = async (vm: any) => {
|
|
||||||
|
const POLL_INTERVAL_MS = 30000
|
||||||
|
const MAX_POINTS = 10
|
||||||
|
|
||||||
|
interface AreaChartItem {
|
||||||
|
date: string
|
||||||
|
value: number
|
||||||
|
pinned: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const cpuLoadData = ref<AreaChartItem[]>([])
|
||||||
|
const cpuClockData = ref<AreaChartItem[]>([])
|
||||||
|
const cpuTempData = ref<AreaChartItem[]>([])
|
||||||
|
const ramLoadData = ref<AreaChartItem[]>([])
|
||||||
|
|
||||||
|
|
||||||
|
let intervalId: number | undefined
|
||||||
|
let isUpdating = false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function updateMetrics() {
|
||||||
|
if (isUpdating) return
|
||||||
|
isUpdating = true
|
||||||
try {
|
try {
|
||||||
const response = await axios.post('/api/controlVM', {
|
|
||||||
action: 'start',
|
|
||||||
token: useCookie('token').value,
|
|
||||||
vm: vm
|
const token = useCookie('token').value
|
||||||
});
|
const response = await axios.post('/api/getMetrics', { token })
|
||||||
console.log(response.data);
|
|
||||||
if(response.data.status == 'success') {
|
const load = response.data.cpu?.load || 0
|
||||||
vmInfo.vms.forEach(vm_list => {
|
const curClock = response.data.cpu?.currentClock || 0
|
||||||
if(vm.name == vm_list.name) {
|
const maxClock = response.data.cpu?.maxClock || 0
|
||||||
vm_list.state = "on";
|
|
||||||
}
|
|
||||||
})
|
const cpuTemp = response.data.cpu?.temp || 0
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error starting VM: ${vm.name}`, error);
|
|
||||||
|
const ramLoad = response.data.ram?.used || 0
|
||||||
|
const ramTotal = response.data.ram?.total || 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const now = new Date().toLocaleTimeString()
|
||||||
|
cpuLoadData.value = [...cpuLoadData.value, { date: now, value: load, pinned: 100 }].slice(-MAX_POINTS)
|
||||||
|
cpuClockData.value = [...cpuClockData.value, { date: now, value: curClock, pinned: maxClock }].slice(-MAX_POINTS)
|
||||||
|
cpuTempData.value = [...cpuTempData.value, { date: now, value: cpuTemp, pinned: 100 }].slice(-MAX_POINTS)
|
||||||
|
ramLoadData.value = [...ramLoadData.value, { date: now, value: ramLoad, pinned: ramTotal }].slice(-MAX_POINTS)
|
||||||
|
} finally {
|
||||||
|
isUpdating = false
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const shutdownVm = async (vm: any) => {
|
onMounted(() => {
|
||||||
try {
|
const empty1 = Array(MAX_POINTS).fill({ date: '', value: 0, pinned: 100 })
|
||||||
const response = await axios.post('/api/controlVM', {
|
//cpuLoadData.value = [...empty1]
|
||||||
action: 'shutdown',
|
updateMetrics()
|
||||||
force: settings.force_shutdown,
|
setTimeout(() => {
|
||||||
token: useCookie('token').value,
|
updateMetrics(); // Second call after 2 seconds
|
||||||
vm: vm
|
}, 1010);
|
||||||
});
|
intervalId = window.setInterval(updateMetrics, POLL_INTERVAL_MS)
|
||||||
console.log(response.data);
|
|
||||||
if(response.data.status == 'success') {
|
|
||||||
vmInfo.vms.forEach(vm_list => {
|
|
||||||
if(vm.name == vm_list.name) {
|
|
||||||
vm_list.state = "off";
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error shutting down VM: ${vm.name}`, error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const settings = reactive({
|
|
||||||
ignoreCache: false,
|
|
||||||
enable_services: false,
|
|
||||||
enable_qemu_controls: false,
|
|
||||||
force_shutdown: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const vmInfo = reactive({
|
|
||||||
isLoaded: false,
|
|
||||||
vms: [] as VM[],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const osInfo = reactive({
|
onUnmounted(() => {
|
||||||
isLoaded: false,
|
if (intervalId) clearInterval(intervalId)
|
||||||
name: '',
|
|
||||||
version: '',
|
|
||||||
kernel: '',
|
|
||||||
architecture: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
const serviceInfo = reactive({
|
|
||||||
isLoaded: false,
|
|
||||||
services: [] as serviceInterface[],
|
|
||||||
});
|
|
||||||
|
|
||||||
const cpuInfo = reactive({
|
|
||||||
isLoaded: false,
|
|
||||||
manufacturer: '',
|
|
||||||
model: '',
|
|
||||||
cores: 0,
|
|
||||||
speed: 0,
|
|
||||||
mainTemp: 0,
|
|
||||||
maxTemp: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
const memoryInfo = reactive({
|
|
||||||
isLoaded: false,
|
|
||||||
total: 0,
|
|
||||||
free: 0,
|
|
||||||
used: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
const networkInfo = reactive({
|
|
||||||
isLoaded: true,
|
|
||||||
interfacesList:[] as networkInterface[]
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const fetchServiceInfo = async () => {
|
|
||||||
try{
|
|
||||||
//let services = await $fetch('/api/getServices')
|
|
||||||
const response = await axios.post('/api/getServices', {
|
|
||||||
token: useCookie('token').value
|
|
||||||
});
|
|
||||||
let services = response.data;
|
|
||||||
services?.forEach((interface_obj: serviceInterface) => {
|
|
||||||
serviceInfo.services.push(interface_obj)
|
|
||||||
});
|
|
||||||
serviceInfo.isLoaded = true;
|
|
||||||
}catch(error){
|
|
||||||
console.error(`Error fetchOsInfo: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchNetworkInfo = async () => {
|
|
||||||
try{
|
|
||||||
const response = await axios.post('/api/getNetworkInterfaces', {
|
|
||||||
token: useCookie('token').value
|
|
||||||
});
|
|
||||||
let networkInfoFetch = response.data;
|
|
||||||
|
|
||||||
networkInfoFetch?.forEach((interface_obj: networkInterface) => {
|
|
||||||
networkInfo.interfacesList.push(interface_obj)
|
|
||||||
});
|
|
||||||
|
|
||||||
networkInfo.isLoaded = true
|
|
||||||
}catch(error){
|
|
||||||
console.error(`Error fetchOsInfo: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchOsInfo = async () => {
|
|
||||||
try{
|
|
||||||
const response = await axios.post('/api/getSystem', {
|
|
||||||
token: useCookie('token').value
|
|
||||||
});
|
|
||||||
let systemInfoFetch = response.data;
|
|
||||||
|
|
||||||
console.log(systemInfoFetch)
|
|
||||||
osInfo.name = systemInfoFetch?.platform || 'N/A'
|
|
||||||
osInfo.version = systemInfoFetch?.distro || 'N/A'
|
|
||||||
osInfo.kernel = systemInfoFetch?.kernel || 'N/A'
|
|
||||||
osInfo.architecture = systemInfoFetch?.arch || 'N/A'
|
|
||||||
osInfo.isLoaded = true
|
|
||||||
}catch(error){
|
|
||||||
console.error(`Error fetchOsInfo: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchCpuTemp = async () => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post('/api/getCpu', {
|
|
||||||
token: useCookie('token').value
|
|
||||||
});
|
|
||||||
let cpuInfoFetch = response.data;
|
|
||||||
console.log(cpuInfoFetch)
|
|
||||||
cpuInfo.manufacturer = cpuInfoFetch?.info.manufacturer || 'N/A'
|
|
||||||
cpuInfo.model = cpuInfoFetch?.info.brand || 'N/A'
|
|
||||||
cpuInfo.cores = cpuInfoFetch?.info.cores || 0
|
|
||||||
cpuInfo.speed = cpuInfoFetch?.info.speed || 0
|
|
||||||
cpuInfo.mainTemp = cpuInfoFetch?.temps.main || 0
|
|
||||||
cpuInfo.maxTemp = cpuInfoFetch?.temps.max || 0
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching CPU temperature:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchMemoryInfo = async () => {
|
|
||||||
try{
|
|
||||||
const response = await axios.post('/api/getMemory', {
|
|
||||||
token: useCookie('token').value
|
|
||||||
});
|
|
||||||
let memoryInfoFetch = response.data;
|
|
||||||
console.log(memoryInfoFetch)
|
|
||||||
let ram_cache = settings.ignoreCache ? (memoryInfoFetch?.cached ?? 0) : 0;
|
|
||||||
if(memoryInfoFetch?.total != null)
|
|
||||||
memoryInfo.total = Math.round( memoryInfoFetch?.total/ (1024 * 1024 * 1024)) || 0
|
|
||||||
if(memoryInfoFetch?.free != null)
|
|
||||||
memoryInfo.free = Math.round( (memoryInfoFetch?.free + ram_cache) / (1024 * 1024 * 1024)) || 0
|
|
||||||
if(memoryInfoFetch?.used != null)
|
|
||||||
memoryInfo.used = Math.round( (memoryInfoFetch?.used - ram_cache) / (1024 * 1024 * 1024)) || 0
|
|
||||||
memoryInfo.isLoaded = true
|
|
||||||
}catch(error){
|
|
||||||
console.error(`Error fetchOsInfo: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchVMs = async () => {
|
|
||||||
try{
|
|
||||||
const response = await axios.post('/api/getVMs', {
|
|
||||||
token: useCookie('token').value
|
|
||||||
});
|
|
||||||
let vmInfoFetch = response.data;
|
|
||||||
console.log(vmInfoFetch)
|
|
||||||
vmInfoFetch?.forEach((vm: VM) => {
|
|
||||||
vmInfo.vms.push(vm)
|
|
||||||
})
|
|
||||||
vmInfo.isLoaded = true
|
|
||||||
}catch(error){
|
|
||||||
console.error(`Error fetchOsInfo: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const fetchSettings = async () => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post('/api/getSettings', {
|
|
||||||
token: useCookie('token').value
|
|
||||||
});
|
|
||||||
let settingsFetch = response.data;
|
|
||||||
console.log(settingsFetch)
|
|
||||||
settings.ignoreCache = settingsFetch?.ignoreCache || false
|
|
||||||
settings.enable_qemu_controls = settingsFetch?.enable_qemu_controls || false
|
|
||||||
settings.enable_services = settingsFetch?.enable_services || false
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching CPU temperature:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let intervalId: NodeJS.Timeout;
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
let isAuthed = await checkAuth(useRouter())
|
|
||||||
if(isAuthed){
|
|
||||||
await fetchSettings()
|
|
||||||
if(settings.enable_qemu_controls) await fetchVMs()
|
|
||||||
await fetchOsInfo()
|
|
||||||
await fetchCpuTemp()
|
|
||||||
cpuInfo.isLoaded = true
|
|
||||||
await fetchMemoryInfo()
|
|
||||||
await fetchNetworkInfo()
|
|
||||||
if(settings.enable_services) await fetchServiceInfo()
|
|
||||||
intervalId = setInterval(fetchCpuTemp, 7000);
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
onBeforeUnmount(()=>{
|
|
||||||
clearInterval(intervalId);
|
|
||||||
})
|
|
||||||
|
const chartDataCpuUsage = computed(() => cpuLoadData.value)
|
||||||
|
const chartCategoriesCpuUsage = computed(() => ({
|
||||||
|
value: { name: 'CPU Load (%)', color: '#863bf6' },
|
||||||
|
pinned: { name: 'Limit (100%)', color: 'rgba(0,0,0,0.01)' },
|
||||||
|
}))
|
||||||
|
const xFormatterCpuUsage = (i: number) => chartDataCpuUsage.value[i]?.date || ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const chartDataCpuClock = computed(() => cpuClockData.value)
|
||||||
|
const chartCategoriesCpuClock = computed(() => ({
|
||||||
|
value: { name: 'CPU Clock (Ghz)', color: '#9cf63b' },
|
||||||
|
pinned: { name: 'CPU Max Clock (Ghz)', color: 'rgba(0,0,0,0.01)' },
|
||||||
|
}))
|
||||||
|
const xFormatterCpuClock = (i: number) => chartDataCpuClock.value[i]?.date || ''
|
||||||
|
|
||||||
|
const chartDataCpuTemp = computed(() => cpuTempData.value)
|
||||||
|
const chartCategoriesCpuTemp = computed(() => ({
|
||||||
|
value: { name: 'CPU Temp (°C)', color: '#f63b3b' },
|
||||||
|
pinned: { name: 'Max Temp (°C)', color: 'rgba(0,0,0,0.01)' },
|
||||||
|
}))
|
||||||
|
const xFormatterCpuTemp = (i: number) => chartDataCpuTemp.value[i]?.date || ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const chartDataRamUsage = computed(() => ramLoadData.value)
|
||||||
|
const chartCategoriesRamUsage = computed(() => ({
|
||||||
|
value: { name: 'RAM Used (Gb)', color: '#f63b6d' },
|
||||||
|
pinned: { name: 'RAM Total (Gb)', color: 'rgba(0,0,0,0.01)' },
|
||||||
|
}))
|
||||||
|
const xFormatterRamUsage = (i: number) => chartDataRamUsage.value[i]?.date || ''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -252,111 +120,167 @@ onBeforeUnmount(()=>{
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col items-center justify-center py-16 px-6">
|
<div class="flex flex-col items-center justify-center py-16 px-6">
|
||||||
|
|
||||||
<div class="grid md:grid-cols-3 gap-6 w-full max-w-5xl mb-8">
|
<div class="grid md:grid-cols-3 gap-6 w-full max-w-5xl mb-8">
|
||||||
|
|
||||||
|
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
||||||
<div class="card bg-base-100 shadow-2xl p-6 opacity-0 transition-opacity duration-500 ease-in-out"
|
|
||||||
:class="{'opacity-100': osInfo.isLoaded}">
|
|
||||||
<h2 class="text-xl font-bold text-center">OS Info</h2>
|
|
||||||
<div class="mt-4 text-sm">
|
|
||||||
<p><strong>Operating System:</strong> {{ osInfo.name }}</p>
|
|
||||||
<p><strong>Version:</strong> {{ osInfo.version }}</p>
|
|
||||||
<p><strong>Kernel:</strong> {{ osInfo.kernel }}</p>
|
|
||||||
<p><strong>Architecture:</strong> {{ osInfo.architecture }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="card bg-base-100 shadow-2xl p-6 opacity-0 transition-opacity duration-500 ease-in-out"
|
|
||||||
:class="{'opacity-100': cpuInfo.isLoaded}">
|
|
||||||
<h2 class="text-xl font-bold text-center">CPU Info</h2>
|
<h2 class="text-xl font-bold text-center">CPU Info</h2>
|
||||||
<div class="mt-4 text-sm">
|
<div class="mt-4 text-sm">
|
||||||
<p><strong>Manufacturer:</strong> {{ cpuInfo.manufacturer }}</p>
|
<p><strong>Manufacturer:</strong> cpuInfo.manufacturer</p>
|
||||||
<p><strong>Model:</strong> {{ cpuInfo.model }}</p>
|
<p><strong>Model:</strong> cpuInfo.model</p>
|
||||||
<p><strong>Core Count:</strong> {{ cpuInfo.cores }}</p>
|
<p><strong>Core Count:</strong> cpuInfo.cores</p>
|
||||||
<p><strong>Speed:</strong> {{ cpuInfo.speed }} GHz</p>
|
<p><strong>Speed:</strong> puInfo.speed GHz</p>
|
||||||
<p><strong>Main Temp:</strong> {{ cpuInfo.mainTemp }} °C</p>
|
<p><strong>Main Temp:</strong> cpuInfo.mainTemp °C</p>
|
||||||
<p><strong>Max Temp:</strong> {{ cpuInfo.maxTemp }} °C</p>
|
<p><strong>Max Temp:</strong> cpuInfo.maxTemp °C</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card bg-base-100 shadow-2xl p-6 opacity-0 transition-opacity duration-500 ease-in-out"
|
|
||||||
:class="{'opacity-100': memoryInfo.isLoaded}">
|
|
||||||
<h2 class="text-xl font-bold text-center">Memory Info</h2>
|
|
||||||
<div class="mt-4 text-sm">
|
|
||||||
<p><strong>Total Memory:</strong> {{ memoryInfo.total }} GB</p>
|
|
||||||
<p><strong>Free Memory:</strong> {{ memoryInfo.free }} GB</p>
|
|
||||||
<p><strong>Used Memory:</strong> {{ memoryInfo.used }} GB</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-for="ifs in networkInfo.interfacesList" class="card bg-base-100 shadow-2xl p-6">
|
|
||||||
<h2 :class="{
|
|
||||||
'text-green-500': ifs.state === 'up',
|
|
||||||
'text-red-500': ifs.state === 'down',
|
|
||||||
'text-yellow-500': ifs.state === 'unknown'
|
|
||||||
}"
|
|
||||||
class="text-xl font-bold text-center">Interface: {{ ifs.name }}</h2>
|
|
||||||
<div class="mt-4 text-sm">
|
|
||||||
<p><strong>Interface Name:</strong> {{ ifs.name }}</p>
|
|
||||||
<p><strong>IPv4:</strong> {{ ifs.ip4 }} / {{ ifs.ip4subnet }}</p>
|
|
||||||
<p><strong>IPv6:</strong> {{ ifs.ip6 }}</p>
|
|
||||||
<p><strong>State:</strong> {{ ifs.state }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h1 v-if="settings.enable_qemu_controls" class="text-4xl font-bold text-center mb-6">QEMU Virtual Machines</h1>
|
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
||||||
<div v-if="settings.enable_qemu_controls" class="form-control flex flex-row gap-2 mb-6">
|
<h2 class="text-xl font-bold text-center">CPU Info</h2>
|
||||||
<label class="label cursor-pointer flex items-center gap-2">
|
<div class="mt-4 text-sm">
|
||||||
<span class="label-text">Force Shutdown</span>
|
<p><strong>Manufacturer:</strong> cpuInfo.manufacturer</p>
|
||||||
<input
|
<p><strong>Model:</strong> cpuInfo.model</p>
|
||||||
type="checkbox"
|
<p><strong>Core Count:</strong> cpuInfo.cores</p>
|
||||||
class="checkbox checkbox-primary"
|
<p><strong>Speed:</strong> puInfo.speed GHz</p>
|
||||||
v-model="settings.force_shutdown"
|
<p><strong>Main Temp:</strong> cpuInfo.mainTemp °C</p>
|
||||||
|
<p><strong>Max Temp:</strong> cpuInfo.maxTemp °C</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
||||||
|
<h2 class="text-xl font-bold text-center">CPU Info</h2>
|
||||||
|
<div class="mt-4 text-sm">
|
||||||
|
<p><strong>Manufacturer:</strong> cpuInfo.manufacturer</p>
|
||||||
|
<p><strong>Model:</strong> cpuInfo.model</p>
|
||||||
|
<p><strong>Core Count:</strong> cpuInfo.cores</p>
|
||||||
|
<p><strong>Speed:</strong> puInfo.speed GHz</p>
|
||||||
|
<p><strong>Main Temp:</strong> cpuInfo.mainTemp °C</p>
|
||||||
|
<p><strong>Max Temp:</strong> cpuInfo.maxTemp °C</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
||||||
|
<h2 class="text-xl font-bold text-center">CPU Usage</h2>
|
||||||
|
<LineChart
|
||||||
|
:data="chartDataCpuUsage"
|
||||||
|
data-key-x="date"
|
||||||
|
data-key-y="value"
|
||||||
|
:height="250"
|
||||||
|
:categories="chartCategoriesCpuUsage"
|
||||||
|
:x-formatter="xFormatterCpuUsage"
|
||||||
|
:curve-type="CurveType.MonotoneX"
|
||||||
|
:legend-position="LegendPosition.Top"
|
||||||
|
:hide-legend="true"
|
||||||
|
:y-min="0"
|
||||||
|
:y-max="100"
|
||||||
|
|
||||||
|
:x-num-ticks='5'
|
||||||
|
:y-num-ticks='5'
|
||||||
|
|
||||||
|
:x-grid-line='false'
|
||||||
|
:y-grid-line='false'
|
||||||
|
|
||||||
|
:x-domain-line='true'
|
||||||
|
:y-domain-line='true'
|
||||||
|
|
||||||
|
|
||||||
|
:x-tick-line='false'
|
||||||
|
:y-tick-line='true'
|
||||||
/>
|
/>
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div v-if="settings.enable_qemu_controls" class="grid md:grid-cols-3 gap-6 w-full max-w-5xl mb-8">
|
|
||||||
<div
|
|
||||||
v-for="vm in vmInfo.vms"
|
|
||||||
class="card bg-base-100 shadow-2xl p-6"
|
|
||||||
>
|
|
||||||
<h2 :class="vm.state === 'on' ? 'text-green-500' : 'text-red-500'" class="text-xl font-bold text-center">
|
|
||||||
{{ vm.name }}
|
|
||||||
</h2>
|
|
||||||
<div class="mt-2 text-sm">
|
|
||||||
<p><strong>OS:</strong> {{ vm.os }}</p>
|
|
||||||
<p><strong>CPU(s):</strong> {{ vm.vCpuCount }}</p>
|
|
||||||
<p><strong>Max Memory:</strong> {{ vm.maxMemory }} MB</p>
|
|
||||||
<p><strong>Autostart:</strong> {{ vm.autostart ? 'Enabled' : 'Disabled' }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-control mt-4 flex flex-row gap-2">
|
|
||||||
<button @click="startVm(vm)" class="btn btn-primary w-1/2">Start</button>
|
|
||||||
<button
|
|
||||||
@click="shutdownVm(vm)"
|
|
||||||
:class="settings.force_shutdown ? 'btn btn-error w-1/2' : 'btn btn-warning w-1/2'"
|
|
||||||
class="w-1/2">
|
|
||||||
{{ settings.force_shutdown ? 'Kill' : 'Shutdown' }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
||||||
|
<h2 class="text-xl font-bold text-center">CPU Clock</h2>
|
||||||
|
<LineChart
|
||||||
|
:data="chartDataCpuClock"
|
||||||
|
data-key-x="date"
|
||||||
|
data-key-y="value"
|
||||||
|
:height="250"
|
||||||
|
:categories="chartCategoriesCpuClock"
|
||||||
|
:x-formatter="xFormatterCpuClock"
|
||||||
|
:curve-type="CurveType.MonotoneX"
|
||||||
|
:legend-position="LegendPosition.Top"
|
||||||
|
:hide-legend="true"
|
||||||
|
:y-min="0"
|
||||||
|
:y-max="100"
|
||||||
|
|
||||||
|
:x-num-ticks='5'
|
||||||
|
:y-num-ticks='5'
|
||||||
|
|
||||||
|
:x-grid-line='false'
|
||||||
|
:y-grid-line='false'
|
||||||
|
|
||||||
|
:x-domain-line='true'
|
||||||
|
:y-domain-line='true'
|
||||||
|
|
||||||
|
|
||||||
<h1 v-if="serviceInfo.isLoaded" class="text-4xl font-bold text-center mb-6">Services</h1>
|
:x-tick-line='false'
|
||||||
<div v-if="serviceInfo.isLoaded" class="grid md:grid-cols-3 gap-6 w-full max-w-5xl">
|
:y-tick-line='true'
|
||||||
<div
|
/>
|
||||||
v-for="service in serviceInfo.services"
|
|
||||||
class="card bg-base-100 shadow-2xl p-6">
|
|
||||||
<h2 :class="service.state === true ? 'text-green-500' : 'text-red-500'" class="text-xl font-bold text-center">
|
|
||||||
{{ service.name }}
|
|
||||||
</h2>
|
|
||||||
<div class="mt-2 text-sm">
|
|
||||||
<p><strong>Name:</strong> {{ service.name }}</p>
|
|
||||||
<p><strong>State:</strong> {{ service.state ? "Running" : "Not Running" }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
||||||
|
<h2 class="text-xl font-bold text-center">CPU Temp</h2>
|
||||||
|
<LineChart
|
||||||
|
:data="chartDataCpuTemp"
|
||||||
|
data-key-x="date"
|
||||||
|
data-key-y="value"
|
||||||
|
:height="250"
|
||||||
|
:categories="chartCategoriesCpuTemp"
|
||||||
|
:x-formatter="xFormatterCpuTemp"
|
||||||
|
:curve-type="CurveType.MonotoneX"
|
||||||
|
:legend-position="LegendPosition.Top"
|
||||||
|
:hide-legend="true"
|
||||||
|
:y-min="0"
|
||||||
|
:y-max="100"
|
||||||
|
|
||||||
|
:x-num-ticks='5'
|
||||||
|
:y-num-ticks='5'
|
||||||
|
|
||||||
|
:x-grid-line='false'
|
||||||
|
:y-grid-line='false'
|
||||||
|
|
||||||
|
:x-domain-line='true'
|
||||||
|
:y-domain-line='true'
|
||||||
|
|
||||||
|
|
||||||
|
:x-tick-line='false'
|
||||||
|
:y-tick-line='true'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
||||||
|
<h2 class="text-xl font-bold text-center">Ram Usage</h2>
|
||||||
|
<LineChart
|
||||||
|
:data="chartDataRamUsage"
|
||||||
|
data-key-x="date"
|
||||||
|
data-key-y="value"
|
||||||
|
:height="250"
|
||||||
|
:categories="chartCategoriesRamUsage"
|
||||||
|
:x-formatter="xFormatterRamUsage"
|
||||||
|
:curve-type="CurveType.MonotoneX"
|
||||||
|
:legend-position="LegendPosition.Top"
|
||||||
|
:hide-legend="true"
|
||||||
|
:y-min="0"
|
||||||
|
:y-max="100"
|
||||||
|
|
||||||
|
:x-num-ticks='5'
|
||||||
|
:y-num-ticks='5'
|
||||||
|
|
||||||
|
:x-grid-line='false'
|
||||||
|
:y-grid-line='false'
|
||||||
|
|
||||||
|
:x-domain-line='true'
|
||||||
|
:y-domain-line='true'
|
||||||
|
|
||||||
|
|
||||||
|
:x-tick-line='false'
|
||||||
|
:y-tick-line='true'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -24,5 +24,5 @@ export const settings = reactive({
|
||||||
"libvirt",
|
"libvirt",
|
||||||
"frp"
|
"frp"
|
||||||
],
|
],
|
||||||
password_hash: ""
|
password_hash: "$2b$10$UO1FyIPODvf.VpGwHBvlq.RfVWOSJYRp/Hh88L306P/fNcXTG9WAC"
|
||||||
});
|
});
|
|
@ -1,22 +0,0 @@
|
||||||
import { defineEventHandler, getCookie, createError } from 'h3';
|
|
||||||
import si from 'systeminformation';
|
|
||||||
import {checkValidJwtToken} from "~/core/command_auth";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
const body = await readBody(event);
|
|
||||||
const { token } = body;
|
|
||||||
checkValidJwtToken(token)
|
|
||||||
|
|
||||||
const cpuData = await si.cpu();
|
|
||||||
const cpuTemp = await si.cpuTemperature();
|
|
||||||
|
|
||||||
return {
|
|
||||||
info: cpuData, // `info` is the key, `cpuData` is the value
|
|
||||||
temps: cpuTemp // `temps` is the key, `cpuTemp` is the value
|
|
||||||
};
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching CPU info:', error);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,17 +0,0 @@
|
||||||
import si from 'systeminformation';
|
|
||||||
import {checkValidJwtToken} from "~/core/command_auth";
|
|
||||||
import { defineEventHandler, getCookie, createError } from 'h3';
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
|
|
||||||
const body = await readBody(event);
|
|
||||||
const { token } = body;
|
|
||||||
checkValidJwtToken(token)
|
|
||||||
const memoryData = await si.mem();
|
|
||||||
|
|
||||||
return memoryData;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching CPU info:', error);
|
|
||||||
}
|
|
||||||
});
|
|
26
server/api/getMetrics.ts
Normal file
26
server/api/getMetrics.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { defineEventHandler, getCookie, createError } from 'h3';
|
||||||
|
import si from 'systeminformation';
|
||||||
|
import {checkValidJwtToken} from "~/core/command_auth";
|
||||||
|
import {CPU, RAM} from "promstats";
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
//console.log("metrics called")
|
||||||
|
return {
|
||||||
|
cpu: {
|
||||||
|
load: await CPU.getCpuLoad(),
|
||||||
|
maxClock: await CPU.getMaxCpuClockGHz(),
|
||||||
|
minClock: await CPU.getMinCpuClockGHz(),
|
||||||
|
currentClock: await CPU.getCurrentCpuClockGHz(),
|
||||||
|
temp: await CPU.getAverageTemp(),
|
||||||
|
},
|
||||||
|
ram:{
|
||||||
|
used: await RAM.getUsedMemory(),
|
||||||
|
total: await RAM.getTotalMemory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching CPU info:', error);
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,14 +1,21 @@
|
||||||
import si from 'systeminformation';
|
import si from 'systeminformation';
|
||||||
import {checkValidJwtToken} from "~/core/command_auth";
|
import {checkValidJwtToken} from "~/core/command_auth";
|
||||||
import { defineEventHandler, getCookie, createError } from 'h3';
|
import { defineEventHandler, getCookie, createError } from 'h3';
|
||||||
|
import {System} from "promstats";
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
try {
|
try {
|
||||||
const body = await readBody(event);
|
const body = await readBody(event);
|
||||||
const { token } = body;
|
const { token } = body;
|
||||||
checkValidJwtToken(token)
|
checkValidJwtToken(token)
|
||||||
const systemData = await si.osInfo();
|
const osName = await System.getOsName();
|
||||||
|
const osVersion = await System.getKernelVersion();
|
||||||
|
const osArch = await System.getMachineArchitecture()
|
||||||
|
|
||||||
return systemData;
|
return {
|
||||||
|
name: osName,
|
||||||
|
version: osVersion,
|
||||||
|
arch: osArch,
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching CPU info:', error);
|
console.error('Error fetching CPU info:', error);
|
||||||
|
|
|
@ -4,9 +4,11 @@ import * as crypto from 'crypto';
|
||||||
import {jwt_globals} from "~/core/globals";
|
import {jwt_globals} from "~/core/globals";
|
||||||
import Logger from "~/core/logger";
|
import Logger from "~/core/logger";
|
||||||
import {settings} from "~/panel.config";
|
import {settings} from "~/panel.config";
|
||||||
|
import {setUrlEndpoint} from "promstats";
|
||||||
|
|
||||||
export default defineNitroPlugin((nitroApp) => {
|
export default defineNitroPlugin((nitroApp) => {
|
||||||
Logger.info("Running init...");
|
Logger.info("Running init...");
|
||||||
|
setUrlEndpoint("http://192.168.178.128:9595")
|
||||||
if(settings.password_hash == ""){
|
if(settings.password_hash == ""){
|
||||||
throw new Error("The password hash is missing. Please use \"npm run password_gen <password>\" to set the password and then \"npm run build\" rebuild the server files");
|
throw new Error("The password hash is missing. Please use \"npm run password_gen <password>\" to set the password and then \"npm run build\" rebuild the server files");
|
||||||
}
|
}
|
||||||
|
|
13
types/MetricsResponse.ts
Normal file
13
types/MetricsResponse.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
interface MetricsResponse {
|
||||||
|
cpu: {
|
||||||
|
load: number
|
||||||
|
maxClock: number
|
||||||
|
minClock: number
|
||||||
|
currentClock: number
|
||||||
|
temp: number
|
||||||
|
}
|
||||||
|
ram: {
|
||||||
|
used: number
|
||||||
|
total: number
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue