Compare commits
No commits in common. "development" and "master" have entirely different histories.
developmen
...
master
10 changed files with 363 additions and 298 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', 'nuxt-charts'],
|
modules: ['@nuxtjs/tailwindcss'],
|
||||||
})
|
})
|
10
package.json
10
package.json
|
@ -11,18 +11,16 @@
|
||||||
"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",
|
||||||
"nuxt": "^3.15.4",
|
"systeminformation": "^5.25.11"
|
||||||
"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"
|
||||||
|
|
554
pages/index.vue
554
pages/index.vue
|
@ -1,116 +1,248 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
import type { VM } from '~/types/VM';
|
||||||
import axios from "axios";
|
import { reactive, ref } from 'vue';
|
||||||
|
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,
|
||||||
const token = useCookie('token').value
|
vm: vm
|
||||||
const response = await axios.post('/api/getMetrics', { token })
|
});
|
||||||
|
console.log(response.data);
|
||||||
const load = response.data.cpu?.load || 0
|
if(response.data.status == 'success') {
|
||||||
const curClock = response.data.cpu?.currentClock || 0
|
vmInfo.vms.forEach(vm_list => {
|
||||||
const maxClock = response.data.cpu?.maxClock || 0
|
if(vm.name == vm_list.name) {
|
||||||
|
vm_list.state = "on";
|
||||||
|
|
||||||
const cpuTemp = response.data.cpu?.temp || 0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error starting VM: ${vm.name}`, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
const shutdownVm = async (vm: any) => {
|
||||||
const empty1 = Array(MAX_POINTS).fill({ date: '', value: 0, pinned: 100 })
|
try {
|
||||||
//cpuLoadData.value = [...empty1]
|
const response = await axios.post('/api/controlVM', {
|
||||||
updateMetrics()
|
action: 'shutdown',
|
||||||
setTimeout(() => {
|
force: settings.force_shutdown,
|
||||||
updateMetrics(); // Second call after 2 seconds
|
token: useCookie('token').value,
|
||||||
}, 1010);
|
vm: vm
|
||||||
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[],
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
const osInfo = reactive({
|
||||||
if (intervalId) clearInterval(intervalId)
|
isLoaded: false,
|
||||||
|
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 || ''
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,167 +252,111 @@ const xFormatterRamUsage = (i: number) => chartDataRamUsage.value[i]?.date || ''
|
||||||
|
|
||||||
<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> puInfo.speed GHz</p>
|
<p><strong>Speed:</strong> {{ cpuInfo.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"
|
||||||
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
:class="{'opacity-100': memoryInfo.isLoaded}">
|
||||||
<h2 class="text-xl font-bold text-center">CPU Info</h2>
|
<h2 class="text-xl font-bold text-center">Memory Info</h2>
|
||||||
<div class="mt-4 text-sm">
|
<div class="mt-4 text-sm">
|
||||||
<p><strong>Manufacturer:</strong> cpuInfo.manufacturer</p>
|
<p><strong>Total Memory:</strong> {{ memoryInfo.total }} GB</p>
|
||||||
<p><strong>Model:</strong> cpuInfo.model</p>
|
<p><strong>Free Memory:</strong> {{ memoryInfo.free }} GB</p>
|
||||||
<p><strong>Core Count:</strong> cpuInfo.cores</p>
|
<p><strong>Used Memory:</strong> {{ memoryInfo.used }} GB</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>
|
</div>
|
||||||
|
<div v-for="ifs in networkInfo.interfacesList" class="card bg-base-100 shadow-2xl p-6">
|
||||||
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
<h2 :class="{
|
||||||
<h2 class="text-xl font-bold text-center">CPU Info</h2>
|
'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">
|
<div class="mt-4 text-sm">
|
||||||
<p><strong>Manufacturer:</strong> cpuInfo.manufacturer</p>
|
<p><strong>Interface Name:</strong> {{ ifs.name }}</p>
|
||||||
<p><strong>Model:</strong> cpuInfo.model</p>
|
<p><strong>IPv4:</strong> {{ ifs.ip4 }} / {{ ifs.ip4subnet }}</p>
|
||||||
<p><strong>Core Count:</strong> cpuInfo.cores</p>
|
<p><strong>IPv6:</strong> {{ ifs.ip6 }}</p>
|
||||||
<p><strong>Speed:</strong> puInfo.speed GHz</p>
|
<p><strong>State:</strong> {{ ifs.state }}</p>
|
||||||
<p><strong>Main Temp:</strong> cpuInfo.mainTemp °C</p>
|
</div>
|
||||||
<p><strong>Max Temp:</strong> cpuInfo.maxTemp °C</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h1 v-if="settings.enable_qemu_controls" class="text-4xl font-bold text-center mb-6">QEMU Virtual Machines</h1>
|
||||||
|
<div v-if="settings.enable_qemu_controls" class="form-control flex flex-row gap-2 mb-6">
|
||||||
|
<label class="label cursor-pointer flex items-center gap-2">
|
||||||
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
<span class="label-text">Force Shutdown</span>
|
||||||
<h2 class="text-xl font-bold text-center">CPU Usage</h2>
|
<input
|
||||||
<LineChart
|
type="checkbox"
|
||||||
:data="chartDataCpuUsage"
|
class="checkbox checkbox-primary"
|
||||||
data-key-x="date"
|
v-model="settings.force_shutdown"
|
||||||
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>
|
||||||
|
|
||||||
<div class="card bg-base-100 shadow-2xl p-6 transition-opacity duration-500 ease-in-out">
|
<div class="form-control mt-4 flex flex-row gap-2">
|
||||||
<h2 class="text-xl font-bold text-center">CPU Clock</h2>
|
<button @click="startVm(vm)" class="btn btn-primary w-1/2">Start</button>
|
||||||
<LineChart
|
<button
|
||||||
:data="chartDataCpuClock"
|
@click="shutdownVm(vm)"
|
||||||
data-key-x="date"
|
:class="settings.force_shutdown ? 'btn btn-error w-1/2' : 'btn btn-warning w-1/2'"
|
||||||
data-key-y="value"
|
class="w-1/2">
|
||||||
:height="250"
|
{{ settings.force_shutdown ? 'Kill' : 'Shutdown' }}
|
||||||
:categories="chartCategoriesCpuClock"
|
</button>
|
||||||
: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'
|
|
||||||
|
|
||||||
|
|
||||||
:x-tick-line='false'
|
|
||||||
:y-tick-line='true'
|
|
||||||
/>
|
|
||||||
</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>
|
||||||
<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>
|
||||||
|
|
||||||
|
|
||||||
|
<h1 v-if="serviceInfo.isLoaded" class="text-4xl font-bold text-center mb-6">Services</h1>
|
||||||
|
<div v-if="serviceInfo.isLoaded" class="grid md:grid-cols-3 gap-6 w-full max-w-5xl">
|
||||||
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -24,5 +24,5 @@ export const settings = reactive({
|
||||||
"libvirt",
|
"libvirt",
|
||||||
"frp"
|
"frp"
|
||||||
],
|
],
|
||||||
password_hash: "$2b$10$UO1FyIPODvf.VpGwHBvlq.RfVWOSJYRp/Hh88L306P/fNcXTG9WAC"
|
password_hash: ""
|
||||||
});
|
});
|
22
server/api/getCpu.ts
Normal file
22
server/api/getCpu.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
17
server/api/getMemory.ts
Normal file
17
server/api/getMemory.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,26 +0,0 @@
|
||||||
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,21 +1,14 @@
|
||||||
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 osName = await System.getOsName();
|
const systemData = await si.osInfo();
|
||||||
const osVersion = await System.getKernelVersion();
|
|
||||||
const osArch = await System.getMachineArchitecture()
|
|
||||||
|
|
||||||
return {
|
return systemData;
|
||||||
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,11 +4,9 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
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