479 lines
No EOL
13 KiB
Vue
479 lines
No EOL
13 KiB
Vue
<template>
|
|
<div class="flex flex-col items-center justify-center py-16 px-6">
|
|
<h1 class="text-4xl font-bold text-center mb-6">Minecraft Servers</h1>
|
|
<div class="grid md:grid-cols-3 gap-6 w-full max-w-5xl mb-8">
|
|
<div v-for="server in settings.servers" class="card bg-base-100 shadow-2xl p-6">
|
|
<h2 class="text-xl font-bold text-center cursor-pointer" @click="showServerLogDialog(server)">
|
|
{{ server.name }}
|
|
</h2>
|
|
|
|
<div class="mt-2 text-sm">
|
|
<p><strong>Version:</strong> {{ server.version }}</p>
|
|
<p><strong>Memory:</strong> {{ server.maxMemory }} GB</p>
|
|
</div>
|
|
|
|
<div class="form-control mt-4 flex flex-row gap-2">
|
|
<button @click="startServer(server)" class="btn btn-primary w-1/3">Start</button>
|
|
<button @click="showEditServerDialog(server)" class="btn btn-warning w-1/3">Edit</button>
|
|
<button @click="stopServer(server)" class="btn btn-error w-1/3">Stop</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="card bg-base-100 shadow-2xl p-6 cursor-pointer" onclick="addServerModal.showModal()">
|
|
<h2 class="text-xl font-bold text-center">
|
|
Add Server
|
|
</h2>
|
|
<div class="mt-2 text-sm flex justify-center">
|
|
<Icon name="material-symbols:add" class="icon" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<dialog id="addServerModal" class="modal">
|
|
<div class="modal-box">
|
|
<h3 class="text-lg font-bold">Add a new Server</h3>
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Server Name</span>
|
|
</div>
|
|
<input v-model="addDialog.newServer.name" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Version</span>
|
|
</div>
|
|
<input v-model="addDialog.newServer.version" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Jar File Upload</span>
|
|
</div>
|
|
<input
|
|
type="file"
|
|
accept=".jar"
|
|
class="file-input file-input-bordered w-full max-w-xs"
|
|
@change="onFileChange"
|
|
/>
|
|
</label>
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">JVM Max Memory</span>
|
|
</div>
|
|
<input v-model="addDialog.newServer.maxMemory" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
|
|
<div class="modal-action">
|
|
<form method="dialog">
|
|
<button class="btn" @click="addServer">Add</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<input type="checkbox" id="serverLogModal" class="modal-toggle" />
|
|
<div class="modal" role="dialog">
|
|
<div class="modal-box w-11/12 max-w-5xl">
|
|
<h3 class="text-lg font-bold text-center">{{ serverLogDialog.selectedServer.name }}</h3>
|
|
<div class="mockup-code w-full">
|
|
<pre v-for="str in serverLogDialog.logs" data-prefix="$"><code>{{str}}</code></pre>
|
|
</div>
|
|
<div class="modal-action flex justify-between space-x-4">
|
|
<!-- Start Button -->
|
|
<button @click="startServer(serverLogDialog.selectedServer)" class="btn btn-primary">Start</button>
|
|
|
|
<!-- Stop Button -->
|
|
<button @click="stopServer(serverLogDialog.selectedServer)" class="btn btn-error">Stop</button>
|
|
</div>
|
|
</div>
|
|
<label class="modal-backdrop" for="serverLogModal">Close</label>
|
|
</div>
|
|
|
|
|
|
|
|
<dialog id="editServerModal" class="modal">
|
|
<div class="modal-box">
|
|
<h3 class="text-lg font-bold">Edit Server: {{ editServerDialog.selectedServer.name }}</h3>
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<!-- First row -->
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Difficulty</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['difficulty']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Server Port</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['server-port']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
<!-- Second row (adding more elements) -->
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Command Blocks</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['enable-command-block']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Gamemode</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['gamemode']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
<!-- Third row -->
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Slots</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['max-players']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">PVP</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['pvp']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Render Distance</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['view-distance']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Simulation Distance</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['simulation-distance']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">Server Banner</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['motd']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
<label class="form-control w-full max-w-xs">
|
|
<div class="label">
|
|
<span class="label-text">PVP</span>
|
|
</div>
|
|
<input v-model="editServerDialog.properties['pvp']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
</label>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="modal-action flex justify-between space-x-4">
|
|
<!-- Save Button -->
|
|
<form method="dialog">
|
|
<button class="btn btn-primary" @click="editServer">Save</button>
|
|
</form>
|
|
|
|
<!-- Delete Button -->
|
|
<form method="dialog">
|
|
<button class="btn btn-error" @click="deleteServer(editServerDialog.selectedServer)">Delete</button>
|
|
</form>
|
|
</div>
|
|
|
|
</div>
|
|
</dialog>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
import type {MinecraftServer} from "~/types/MinecraftServer";
|
|
import axios from 'axios';
|
|
import {parse, stringify} from 'ini'
|
|
|
|
const settings = reactive({
|
|
servers: [] as MinecraftServer[],
|
|
})
|
|
|
|
const addDialog = reactive({
|
|
newServer: {} as MinecraftServer,
|
|
uploadData: '' as string
|
|
})
|
|
|
|
const serverLogDialog = reactive({
|
|
selectedServer: {} as MinecraftServer,
|
|
logs: [] as string[],
|
|
})
|
|
|
|
const editServerDialog = reactive({
|
|
selectedServer: {} as MinecraftServer,
|
|
properties: {} as { [p: string]: any }
|
|
})
|
|
|
|
|
|
const getServers = async () => {
|
|
try{
|
|
settings.servers = await $fetch('/api/getServers')
|
|
settings.servers.forEach((server) => {
|
|
console.log(server.name)
|
|
})
|
|
}catch(error){
|
|
console.error(`Error fetch: ${error}`);
|
|
}
|
|
}
|
|
|
|
const showServerLogDialog = async (server: MinecraftServer) => {
|
|
serverLogDialog.selectedServer = server
|
|
// @ts-ignore
|
|
serverLogModal.checked = true
|
|
}
|
|
|
|
const getLogs = async () => {
|
|
|
|
|
|
// @ts-ignore
|
|
if(!serverLogModal.checked){
|
|
if(serverLogDialog.logs.length != 0){
|
|
serverLogDialog.logs.length = 0;
|
|
}
|
|
return
|
|
}
|
|
|
|
if(!serverLogDialog.selectedServer)
|
|
return
|
|
|
|
try{
|
|
const response = await axios.post('/api/getLogs', {
|
|
name: serverLogDialog.selectedServer.name
|
|
});
|
|
const strRet = response.data.logs;
|
|
const stringArray = strRet.split('\n');
|
|
serverLogDialog.logs.length = 0;
|
|
stringArray.slice(-20).forEach((element: string) => {
|
|
serverLogDialog.logs.push(element);
|
|
})
|
|
}catch(error){
|
|
console.error(`Error fetch: ${error}`);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
const showEditServerDialog = async (server: MinecraftServer) => {
|
|
editServerDialog.selectedServer = server
|
|
await getProperties()
|
|
// @ts-ignore
|
|
editServerModal.showModal()
|
|
}
|
|
|
|
const getProperties = async () => {
|
|
// @ts-ignore
|
|
editServerModal.showModal()
|
|
|
|
try{
|
|
const response = await axios.post('/api/getProperties', {
|
|
name: editServerDialog.selectedServer.name
|
|
});
|
|
console.log(response.data.properties)
|
|
|
|
editServerDialog.properties = parse(response.data.properties)
|
|
|
|
}catch(error){
|
|
console.error(`Error fetch: ${error}`);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const editServer = async () => {
|
|
try {
|
|
const response = await axios.post('/api/setProperties', {
|
|
name: editServerDialog.selectedServer.name,
|
|
properties: stringify(editServerDialog.properties)
|
|
});
|
|
console.log(response.data);
|
|
} catch (error) {
|
|
console.error(`Error `, error);
|
|
}
|
|
}
|
|
|
|
|
|
const deleteServer = async (server: MinecraftServer) => {
|
|
try {
|
|
const response = await axios.post('/api/deleteServer', {
|
|
name: server.name
|
|
});
|
|
console.log(response.data);
|
|
await getServers()
|
|
} catch (error) {
|
|
console.error(`Error `, error);
|
|
}
|
|
}
|
|
|
|
const addServer = async (server: MinecraftServer) => {
|
|
try {
|
|
const checksum = await calculateChecksum(addDialog.uploadData)
|
|
const response = await axios.post('/api/addServer', {
|
|
newServer: addDialog.newServer,
|
|
fileUpload:{
|
|
data: addDialog.uploadData,
|
|
checksum: checksum
|
|
}
|
|
});
|
|
console.log(response.data);
|
|
await getServers()
|
|
} catch (error) {
|
|
console.error(`Error `, error);
|
|
}
|
|
}
|
|
|
|
|
|
const onFileChange = async (event: any) => {
|
|
const file = event.target.files[0];
|
|
addDialog.newServer.jarName = file.name;
|
|
|
|
if (file) {
|
|
const reader = new FileReader();
|
|
const readFile = new Promise<ArrayBuffer>((resolve, reject) => {
|
|
reader.onload = (e) => {
|
|
resolve(e.target?.result as ArrayBuffer);
|
|
};
|
|
reader.onerror = (error) => {
|
|
reject(error);
|
|
};
|
|
});
|
|
reader.readAsArrayBuffer(file);
|
|
|
|
try {
|
|
const result = await readFile;
|
|
addDialog.uploadData = arrayBufferToBase64(result);
|
|
} catch (error) {
|
|
console.error("Error reading file:", error);
|
|
}
|
|
}
|
|
};
|
|
|
|
const arrayBufferToBase64 = (buffer: ArrayBuffer): string => {
|
|
const uint8Array = new Uint8Array(buffer);
|
|
let binaryString = '';
|
|
|
|
uint8Array.forEach((byte) => {
|
|
binaryString += String.fromCharCode(byte);
|
|
});
|
|
|
|
return btoa(binaryString);
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const calculateChecksum = async (data: any) => {
|
|
try {
|
|
const buffer = data instanceof ArrayBuffer ? data : new TextEncoder().encode(data);
|
|
|
|
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
|
|
|
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
const hexString = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
|
|
|
|
console.log('Checksum (SHA-256):', hexString);
|
|
return hexString;
|
|
} catch (error) {
|
|
console.error('Error calculating checksum:', error);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
const startServer = async (server: MinecraftServer) => {
|
|
try {
|
|
const response = await axios.post('/api/startServer', {
|
|
name: server.name
|
|
});
|
|
console.log(response.data);
|
|
} catch (error) {
|
|
console.error(`Error `, error);
|
|
}
|
|
};
|
|
|
|
const stopServer = async (server: MinecraftServer) => {
|
|
try {
|
|
const response = await axios.post('/api/stopServer', {
|
|
name: server.name
|
|
});
|
|
console.log(response.data);
|
|
} catch (error) {
|
|
console.error(`Error `, error);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
let intervalId: NodeJS.Timeout;
|
|
|
|
|
|
onMounted(async()=>{
|
|
await getServers()
|
|
intervalId = setInterval(getLogs, 500);
|
|
})
|
|
|
|
onBeforeUnmount(()=>{
|
|
clearInterval(intervalId);
|
|
})
|
|
|
|
|
|
definePageMeta({
|
|
middleware: 'auth'
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
<style>
|
|
.icon {
|
|
width: 5rem; /* You can adjust this value based on your needs */
|
|
height: 5rem; /* Set both width and height */
|
|
object-fit: contain; /* Keep the aspect ratio of the icon */
|
|
}
|
|
</style> |