initial commit
This commit is contained in:
commit
6dc118c8c7
24 changed files with 682 additions and 0 deletions
49
app.vue
Normal file
49
app.vue
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<div class="min-h-screen flex flex-col">
|
||||||
|
<!-- Navbar -->
|
||||||
|
<nav class="navbar bg-base-100 shadow-lg px-4">
|
||||||
|
<div class="flex-1">
|
||||||
|
<NuxtLink to="/" class="btn btn-ghost text-xl">Server Panel</NuxtLink>
|
||||||
|
</div>
|
||||||
|
<div class="hidden lg:flex">
|
||||||
|
<ul class="menu menu-horizontal px-1">
|
||||||
|
<li><NuxtLink to="/">Dashboard</NuxtLink></li>
|
||||||
|
<li><NuxtLink to="/settings">Settings</NuxtLink></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="lg:hidden">
|
||||||
|
<button @click="toggleMenu" class="btn btn-square btn-ghost">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block w-6 h-6 stroke-current">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<!-- Mobile Menu -->
|
||||||
|
<div v-if="menuOpen" class="absolute top-16 left-0 w-full bg-base-100 shadow-lg lg:hidden z-50">
|
||||||
|
<ul class="menu menu-vertical p-4">
|
||||||
|
<li><NuxtLink to="/" @click="toggleMenu">Dashboard</NuxtLink></li>
|
||||||
|
<li><NuxtLink to="/settings" @click="toggleMenu">Settings</NuxtLink></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<main class="flex-1 p-4">
|
||||||
|
<NuxtPage />
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="footer p-4 bg-neutral text-neutral-content text-center">
|
||||||
|
<a href="https://github.com/WeeXnes"> ServerPanel by WeeXnes</a>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const menuOpen = ref(false)
|
||||||
|
const toggleMenu = () => {
|
||||||
|
menuOpen.value = !menuOpen.value
|
||||||
|
}
|
||||||
|
</script>
|
6
nuxt.config.ts
Normal file
6
nuxt.config.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
compatibilityDate: '2024-11-01',
|
||||||
|
devtools: { enabled: true },
|
||||||
|
modules: ['@nuxtjs/tailwindcss'],
|
||||||
|
})
|
27
package.json
Normal file
27
package.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "nuxt-app",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "nuxt build",
|
||||||
|
"dev": "nuxt dev",
|
||||||
|
"generate": "nuxt generate",
|
||||||
|
"preview": "nuxt preview",
|
||||||
|
"postinstall": "nuxt prepare"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"nuxt": "^3.15.4",
|
||||||
|
"vue": "latest",
|
||||||
|
"vue-router": "latest",
|
||||||
|
"@nuxtjs/tailwindcss": "^6.13.1",
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"bcryptjs": "^3.0.0",
|
||||||
|
"daisyui": "^4.12.23",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"systeminformation": "^5.25.11"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jsonwebtoken": "^9.0.8"
|
||||||
|
}
|
||||||
|
}
|
280
pages/index.vue
Normal file
280
pages/index.vue
Normal file
|
@ -0,0 +1,280 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { VM } from '~/types/VM';
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import type {networkInterface} from "~/types/networkInterface";
|
||||||
|
import type {serviceInterface} from "~/types/serviceInterface";
|
||||||
|
|
||||||
|
const { data: virtualMachines } = await useAsyncData<VM[]>('vms', () => $fetch('/api/getVMs'));
|
||||||
|
|
||||||
|
const ignoreCache = true;
|
||||||
|
|
||||||
|
|
||||||
|
const startVm = async (vm: any) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/api/controlVM', {
|
||||||
|
action: 'start',
|
||||||
|
vm: vm
|
||||||
|
});
|
||||||
|
console.log(response.data);
|
||||||
|
if(response.data.status == 'success') {
|
||||||
|
virtualMachines.value?.forEach(vm_list => {
|
||||||
|
if(vm.name == vm_list.name) {
|
||||||
|
vm_list.state = "on";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error starting VM: ${vm.name}`, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const shutdownVm = async (vm: any) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('/api/controlVM', {
|
||||||
|
action: 'shutdown',
|
||||||
|
vm: vm
|
||||||
|
});
|
||||||
|
console.log(response.data);
|
||||||
|
if(response.data.status == 'success') {
|
||||||
|
virtualMachines.value?.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 osInfo = reactive({
|
||||||
|
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')
|
||||||
|
services?.forEach((interface_obj) => {
|
||||||
|
serviceInfo.services.push(interface_obj)
|
||||||
|
});
|
||||||
|
serviceInfo.isLoaded = true;
|
||||||
|
}catch(error){
|
||||||
|
console.error(`Error fetchOsInfo: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchNetworkInfo = async () => {
|
||||||
|
try{
|
||||||
|
let networkInfoFetch = await $fetch('/api/getNetworkInterfaces')
|
||||||
|
networkInfoFetch?.forEach((interface_obj) => {
|
||||||
|
networkInfo.interfacesList.push(interface_obj)
|
||||||
|
});
|
||||||
|
|
||||||
|
networkInfo.isLoaded = true
|
||||||
|
}catch(error){
|
||||||
|
console.error(`Error fetchOsInfo: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchOsInfo = async () => {
|
||||||
|
try{
|
||||||
|
let systemInfoFetch = await $fetch('/api/getSystem')
|
||||||
|
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 {
|
||||||
|
let cpuInfoFetch = await $fetch('/api/getCpu')
|
||||||
|
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{
|
||||||
|
let memoryInfoFetch = await $fetch('/api/getMemory')
|
||||||
|
console.log(memoryInfoFetch)
|
||||||
|
let ram_cache = 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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await fetchOsInfo()
|
||||||
|
await fetchCpuTemp()
|
||||||
|
cpuInfo.isLoaded = true
|
||||||
|
await fetchMemoryInfo()
|
||||||
|
await fetchNetworkInfo()
|
||||||
|
await fetchServiceInfo()
|
||||||
|
const intervalId = setInterval(fetchCpuTemp, 7000);
|
||||||
|
onUnmounted(() => {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<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="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>
|
||||||
|
<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> {{ cpuInfo.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 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 class="text-4xl font-bold text-center mb-6">QEMU Virtual Machines</h1>
|
||||||
|
<div class="grid md:grid-cols-3 gap-6 w-full max-w-5xl mb-8">
|
||||||
|
<div
|
||||||
|
v-for="vm in virtualMachines"
|
||||||
|
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="btn btn-error w-1/2">Shutdown</button>
|
||||||
|
</div>
|
||||||
|
</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 class="form-control mt-4 flex flex-row gap-2">
|
||||||
|
<button class="btn btn-primary w-1/2">Start</button>
|
||||||
|
<button class="btn btn-error w-1/2">Stop</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
65
pages/login.vue
Normal file
65
pages/login.vue
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<template>
|
||||||
|
<div class="flex items-center justify-center py-16">
|
||||||
|
<div class="card w-96 bg-base-100 shadow-2xl p-6">
|
||||||
|
<h2 class="text-2xl font-bold text-center">Login</h2>
|
||||||
|
|
||||||
|
<form @submit.prevent="handleLogin" class="mt-4">
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">E-Mail</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
v-model="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="Enter your email"
|
||||||
|
class="input input-bordered"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control mt-2">
|
||||||
|
<label class="label">
|
||||||
|
<span class="label-text">Password</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
v-model="password"
|
||||||
|
type="password"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
class="input input-bordered"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control mt-4">
|
||||||
|
<button type="submit" class="btn btn-primary w-full">Login</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="mt-4 text-center text-sm">
|
||||||
|
<p>Don't have an account? <a href="/register" class="text-blue-500 hover:underline">Register here</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async handleLogin() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
16
pages/settings.vue
Normal file
16
pages/settings.vue
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { VM } from '../types/VM';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h1>hola</h1>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
21
panel.config.ts
Normal file
21
panel.config.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { reactive } from "vue";
|
||||||
|
|
||||||
|
export const settings = reactive({
|
||||||
|
ignoreCache: false,
|
||||||
|
enable_services: false,
|
||||||
|
enable_qemu_controls: true,
|
||||||
|
qemu_vms: [
|
||||||
|
{
|
||||||
|
name: "Gameserver",
|
||||||
|
os: "Ubuntu 24.04"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Ubuntu_VM1",
|
||||||
|
os: "Ubuntu 24.04"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
systemctl_services:[
|
||||||
|
"libvirt",
|
||||||
|
"frp"
|
||||||
|
]
|
||||||
|
});
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
1
public/robots.txt
Normal file
1
public/robots.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
27
server/api/controlVM.ts
Normal file
27
server/api/controlVM.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody(event);
|
||||||
|
const { action, vm } = body;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const command = action === 'start'
|
||||||
|
? `virsh start ${vm.name}`
|
||||||
|
: `virsh shutdown ${vm.name}`;
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
exec(command, (error, stdout, stderr) => {
|
||||||
|
if (error || stderr) {
|
||||||
|
reject(`Error: ${stderr || error?.message}`);
|
||||||
|
}
|
||||||
|
resolve(stdout);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return { status: 'success', message: `VM ${action} successful`, vm };
|
||||||
|
} catch (error) {
|
||||||
|
return { status: 'error', message: `Failed to ${action} VM`, error: error };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
16
server/api/getCpu.ts
Normal file
16
server/api/getCpu.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import si from 'systeminformation';
|
||||||
|
|
||||||
|
export default defineEventHandler(async () => {
|
||||||
|
try {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
12
server/api/getMemory.ts
Normal file
12
server/api/getMemory.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import si from 'systeminformation';
|
||||||
|
|
||||||
|
export default defineEventHandler(async () => {
|
||||||
|
try {
|
||||||
|
const memoryData = await si.mem();
|
||||||
|
|
||||||
|
return memoryData;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching CPU info:', error);
|
||||||
|
}
|
||||||
|
});
|
45
server/api/getNetworkInterfaces.ts
Normal file
45
server/api/getNetworkInterfaces.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
import si from 'systeminformation';
|
||||||
|
import {VM} from "~/types/VM";
|
||||||
|
import {networkInterface} from "~/types/networkInterface";
|
||||||
|
|
||||||
|
export default defineEventHandler(async () => {
|
||||||
|
try {
|
||||||
|
const cpuData = await si.cpu();
|
||||||
|
const cpuTemp = await si.cpuTemperature();
|
||||||
|
const osInfo = await si.osInfo();
|
||||||
|
const network = await si.networkInterfaces();
|
||||||
|
|
||||||
|
const interfaces: networkInterface[] = [];
|
||||||
|
if (Array.isArray(network)) {
|
||||||
|
network.forEach((interface_obj) => {
|
||||||
|
interfaces.push({
|
||||||
|
name: interface_obj.ifaceName,
|
||||||
|
ip4: interface_obj.ip4,
|
||||||
|
ip6: interface_obj.ip6,
|
||||||
|
ip4subnet: interface_obj.ip4subnet,
|
||||||
|
ip6subnet: interface_obj.ip6subnet,
|
||||||
|
state: interface_obj.operstate as "up" | "down" | "unknown"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(network.ifaceName + " is reachable at " + network.ip4);
|
||||||
|
interfaces.push({
|
||||||
|
name: network.ifaceName,
|
||||||
|
ip4: network.ip4,
|
||||||
|
ip6: network.ip6,
|
||||||
|
ip4subnet: network.ip4subnet,
|
||||||
|
ip6subnet: network.ip6subnet,
|
||||||
|
state: network.operstate as "up" | "down" | "unknown"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaces.forEach(obj => {
|
||||||
|
console.log(obj.name + " is " + obj.state);
|
||||||
|
})
|
||||||
|
return interfaces;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching CPU info:', error);
|
||||||
|
}
|
||||||
|
});
|
30
server/api/getServices.ts
Normal file
30
server/api/getServices.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
import si from 'systeminformation';
|
||||||
|
import {VM} from "~/types/VM";
|
||||||
|
import {serviceInterface} from "~/types/serviceInterface";
|
||||||
|
import {settings} from "~/panel.config";
|
||||||
|
|
||||||
|
export default defineEventHandler(async () => {
|
||||||
|
try {
|
||||||
|
const services = await si.services(settings.systemctl_services.join(', '));
|
||||||
|
|
||||||
|
|
||||||
|
const interfaces: serviceInterface[] = [];
|
||||||
|
if (Array.isArray(services)) {
|
||||||
|
console.log(`services is array`);
|
||||||
|
services.forEach((interface_obj) => {
|
||||||
|
interfaces.push({
|
||||||
|
name: interface_obj.name,
|
||||||
|
state: interface_obj.running
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
interfaces.push(services);
|
||||||
|
}
|
||||||
|
|
||||||
|
return interfaces;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching CPU info:', error);
|
||||||
|
}
|
||||||
|
});
|
10
server/api/getSettings.ts
Normal file
10
server/api/getSettings.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import si from 'systeminformation';
|
||||||
|
import {settings} from "~/panel.config";
|
||||||
|
|
||||||
|
export default defineEventHandler(async () => {
|
||||||
|
try {
|
||||||
|
return settings
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching CPU info:', error);
|
||||||
|
}
|
||||||
|
});
|
12
server/api/getSystem.ts
Normal file
12
server/api/getSystem.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import si from 'systeminformation';
|
||||||
|
|
||||||
|
export default defineEventHandler(async () => {
|
||||||
|
try {
|
||||||
|
const systemData = await si.osInfo();
|
||||||
|
|
||||||
|
return systemData;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching CPU info:', error);
|
||||||
|
}
|
||||||
|
});
|
28
server/api/getVMs.ts
Normal file
28
server/api/getVMs.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
import {VM} from "~/types/VM";
|
||||||
|
import {settings} from "~/panel.config";
|
||||||
|
|
||||||
|
export default defineEventHandler(() => {
|
||||||
|
|
||||||
|
|
||||||
|
let vmNames = ["Gameserver", "Ubuntu_VM1"]
|
||||||
|
const virtualMachines: VM[] = [];
|
||||||
|
settings.qemu_vms.forEach(vm => {
|
||||||
|
const vCpuCount = parseInt(execSync(`LANG=C virsh dominfo ${vm.name} | grep 'CPU(s)' | awk '{print $2}'`).toString().trim());
|
||||||
|
const maxMemory = parseInt(execSync(`LANG=C virsh dominfo ${vm.name} | grep 'Max memory' | awk '{print $3}'`).toString().trim()) / 1024;
|
||||||
|
const autostartValue = execSync(`LANG=C virsh dominfo ${vm.name} | grep 'Autostart' | awk '{print $2}'`).toString().trim();
|
||||||
|
const autostart = autostartValue === "enable";
|
||||||
|
const stateValue = execSync(`LANG=C virsh dominfo ${vm.name} | grep 'State' | awk '{print $2, $3}'`).toString().trim();
|
||||||
|
const state: 'on' | 'off' = stateValue === "running" ? 'on' : 'off';
|
||||||
|
virtualMachines.push({
|
||||||
|
name: vm.name,
|
||||||
|
os: vm.os,
|
||||||
|
vCpuCount: vCpuCount,
|
||||||
|
maxMemory: maxMemory,
|
||||||
|
autostart: autostart,
|
||||||
|
state: state
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return virtualMachines;
|
||||||
|
});
|
7
server/plugins/init.ts
Normal file
7
server/plugins/init.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { defineNitroPlugin } from "#imports";
|
||||||
|
|
||||||
|
|
||||||
|
export default defineNitroPlugin((nitroApp) => {
|
||||||
|
console.log("Loading Server Settings");
|
||||||
|
|
||||||
|
});
|
3
server/tsconfig.json
Normal file
3
server/tsconfig.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../.nuxt/tsconfig.server.json"
|
||||||
|
}
|
3
tailwind.config.js
Normal file
3
tailwind.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: [require('daisyui')],
|
||||||
|
};
|
4
tsconfig.json
Normal file
4
tsconfig.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
// https://nuxt.com/docs/guide/concepts/typescript
|
||||||
|
"extends": "./.nuxt/tsconfig.json"
|
||||||
|
}
|
8
types/VM.ts
Normal file
8
types/VM.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export interface VM {
|
||||||
|
name: string;
|
||||||
|
os: string;
|
||||||
|
vCpuCount: number;
|
||||||
|
maxMemory: number;
|
||||||
|
autostart: boolean;
|
||||||
|
state: "on" | "off";
|
||||||
|
}
|
8
types/networkInterface.ts
Normal file
8
types/networkInterface.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export interface networkInterface {
|
||||||
|
name: string;
|
||||||
|
ip4: string;
|
||||||
|
ip6: string;
|
||||||
|
ip4subnet: string;
|
||||||
|
ip6subnet: string;
|
||||||
|
state: "up" | "down" | "unknown";
|
||||||
|
}
|
4
types/serviceInterface.ts
Normal file
4
types/serviceInterface.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export interface serviceInterface {
|
||||||
|
name: string;
|
||||||
|
state: boolean;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue