servers are running and log is shown in web ui
This commit is contained in:
parent
26108eab5e
commit
2e55a6d566
8 changed files with 159 additions and 79 deletions
|
@ -1,11 +1,12 @@
|
||||||
import type {MinecraftServer} from "~/types/MinecraftServer";
|
import type {MinecraftServer} from "~/types/MinecraftServer";
|
||||||
import type {SettingsJsonFile} from "~/types/SettingsJsonFile";
|
import type {SettingsJsonFile} from "~/types/SettingsJsonFile";
|
||||||
|
import * as os from 'os';
|
||||||
import {saveJsonFile} from "~/util/jsonLoader";
|
import {saveJsonFile} from "~/util/jsonLoader";
|
||||||
|
|
||||||
export namespace environment{
|
export namespace environment{
|
||||||
export namespace paths{
|
export namespace paths{
|
||||||
export let data: string = "/home/weexnes/.mcservermanager";
|
export let data: string = `${os.homedir()}/.mcservermanager`;
|
||||||
export let servers: string = "/home/weexnes/.mcservermanager/servers";
|
export let servers: string = `${os.homedir()}/.mcservermanager/servers`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"nuxt": "^3.15.4",
|
"nuxt": "^3.15.4",
|
||||||
"vue": "latest",
|
"vue": "latest",
|
||||||
"vue-router": "latest",
|
"vue-router": "latest",
|
||||||
"execa": "^9.5.2"
|
"execa": "^9.5.2",
|
||||||
|
"ini": "^5.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
128
pages/index.vue
128
pages/index.vue
|
@ -2,16 +2,21 @@
|
||||||
<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">
|
||||||
<h1 class="text-4xl font-bold text-center mb-6">Minecraft Servers</h1>
|
<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 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" @click="editServerDialog(server)">
|
<div v-for="server in settings.servers" class="card bg-base-100 shadow-2xl p-6">
|
||||||
<h2 class="text-xl font-bold text-center">
|
<h2 class="text-xl font-bold text-center cursor-pointer" @click="editServerDialog(server)">
|
||||||
{{ server.name }}
|
{{ server.name }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="mt-2 text-sm">
|
<div class="mt-2 text-sm">
|
||||||
<p><strong>Version:</strong> {{ server.version }}</p>
|
<p><strong>Version:</strong> {{ server.version }}</p>
|
||||||
<p><strong>Port</strong> {{ server.port }}</p>
|
<p><strong>Port</strong> {{ server.port }}</p>
|
||||||
<p><strong>Slots:</strong> {{ server.maxPlayers }}</p>
|
<p><strong>Memory:</strong> {{ server.minMemory }}-{{ server.maxMemory }}GB</p>
|
||||||
<p><strong>Max Memory:</strong> {{ server.maxMemory }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control mt-4 flex flex-row gap-2">
|
||||||
|
<button @click="startServer(server)" class="btn btn-primary w-1/2">Start</button>
|
||||||
|
<button @click="stopServer(server)" class="btn btn-error w-1/2">Stop</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card bg-base-100 shadow-2xl p-6 cursor-pointer" onclick="addServerModal.showModal()">
|
<div class="card bg-base-100 shadow-2xl p-6 cursor-pointer" onclick="addServerModal.showModal()">
|
||||||
|
@ -41,12 +46,6 @@
|
||||||
</div>
|
</div>
|
||||||
<input v-model="addDialog.newServer.port" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
<input v-model="addDialog.newServer.port" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
||||||
</label>
|
</label>
|
||||||
<label class="form-control w-full max-w-xs">
|
|
||||||
<div class="label">
|
|
||||||
<span class="label-text">Max Players</span>
|
|
||||||
</div>
|
|
||||||
<input v-model="addDialog.newServer.maxPlayers" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
||||||
</label>
|
|
||||||
<label class="form-control w-full max-w-xs">
|
<label class="form-control w-full max-w-xs">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span class="label-text">Version</span>
|
<span class="label-text">Version</span>
|
||||||
|
@ -59,34 +58,6 @@
|
||||||
</div>
|
</div>
|
||||||
<input v-model="addDialog.newServer.jar_url" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
<input v-model="addDialog.newServer.jar_url" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
||||||
</label>
|
</label>
|
||||||
<label class="form-control w-full max-w-xs">
|
|
||||||
<div class="label">
|
|
||||||
<span class="label-text"></span>
|
|
||||||
</div>
|
|
||||||
<div class="dropdown dropdown-bottom dropdown-center">
|
|
||||||
<div tabindex="0" role="button" class="btn m-1">Difficuly: {{addDialog.newServer.difficulty}}</div>
|
|
||||||
<ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-1 w-52 p-2 shadow-sm">
|
|
||||||
<li @click="setNewServerDifficulty('peaceful')"><a>Peaceful</a></li>
|
|
||||||
<li @click="setNewServerDifficulty('easy')"><a>Easy</a></li>
|
|
||||||
<li @click="setNewServerDifficulty('normal')"><a>Normal</a></li>
|
|
||||||
<li @click="setNewServerDifficulty('hard')"><a>Hard</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</label>
|
|
||||||
<label class="form-control w-full max-w-xs">
|
|
||||||
<div class="label">
|
|
||||||
<span class="label-text">Spawn Protection</span>
|
|
||||||
</div>
|
|
||||||
<input v-model="addDialog.newServer.spawnProtection" 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="addDialog.newServer.viewDistance" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
|
|
||||||
<label class="form-control w-full max-w-xs">
|
<label class="form-control w-full max-w-xs">
|
||||||
|
@ -113,19 +84,21 @@
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<dialog id="editServerModal" class="modal">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<input type="checkbox" id="editServerModal" class="modal-toggle" />
|
||||||
|
<div class="modal" role="dialog">
|
||||||
<div class="modal-box w-11/12 max-w-5xl">
|
<div class="modal-box w-11/12 max-w-5xl">
|
||||||
<h3 class="text-lg font-bold">{{ editDialog.editedServer.name }}</h3>
|
<h3 class="text-lg font-bold text-center">{{ editDialog.editedServer.name }}</h3>
|
||||||
<p class="py-4"> blablabla </p>
|
<div class="mockup-code w-full">
|
||||||
<div class="modal-action">
|
<pre v-for="str in testrec.logs" data-prefix="$"><code>{{str}}</code></pre>
|
||||||
<form method="dialog">
|
|
||||||
<!-- if there is a button, it will close the modal -->
|
|
||||||
<button @click="deleteServer" class="btn">Delete</button>
|
|
||||||
<button @click="startServer" class="btn">Save Changes</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
<label class="modal-backdrop" for="editServerModal">Close</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
@ -139,6 +112,10 @@ import type {MinecraftServer} from "~/types/MinecraftServer";
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
|
|
||||||
|
const testrec = reactive({
|
||||||
|
logs: [] as string[],
|
||||||
|
})
|
||||||
|
|
||||||
const settings = reactive({
|
const settings = reactive({
|
||||||
servers: [] as MinecraftServer[],
|
servers: [] as MinecraftServer[],
|
||||||
})
|
})
|
||||||
|
@ -164,16 +141,41 @@ const getServers = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const getLogs = async () => {
|
||||||
|
// @ts-ignore
|
||||||
|
if(!editServerModal.checked)
|
||||||
|
return
|
||||||
|
|
||||||
|
try{
|
||||||
|
const response = await axios.post('/api/getLogs', {
|
||||||
|
name: editDialog.editedServer.name
|
||||||
|
});
|
||||||
|
//console.log(response)
|
||||||
|
const strRet: string = response.data.logs;
|
||||||
|
const stringArray = strRet.split('\n');
|
||||||
|
//console.log(stringArray);
|
||||||
|
testrec.logs.length = 0;
|
||||||
|
stringArray.slice(-20).forEach(element => {
|
||||||
|
testrec.logs.push(element);
|
||||||
|
})
|
||||||
|
}catch(error){
|
||||||
|
console.error(`Error fetch: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const editServerDialog = async (server: MinecraftServer) => {
|
const editServerDialog = async (server: MinecraftServer) => {
|
||||||
editDialog.editedServer = server
|
editDialog.editedServer = server
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
editServerModal.showModal()
|
//editServerModal.showModal()
|
||||||
|
editServerModal.checked = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const addServer = async () => {
|
const addServer = async () => {
|
||||||
if(!settings.servers.some(server => server.name === addDialog.newServer.name)){
|
if(!settings.servers.some(server => server.name === addDialog.newServer.name)){
|
||||||
addDialog.newServer.logs = ["Server Created via Webui"]
|
|
||||||
settings.servers.push(addDialog.newServer)
|
settings.servers.push(addDialog.newServer)
|
||||||
await syncServers()
|
await syncServers()
|
||||||
}
|
}
|
||||||
|
@ -185,9 +187,7 @@ const deleteServer = async () => {
|
||||||
await syncServers()
|
await syncServers()
|
||||||
}
|
}
|
||||||
|
|
||||||
const setNewServerDifficulty = async (newDiff: "peaceful" | "easy" | "normal" | "hard") => {
|
|
||||||
addDialog.newServer.difficulty = newDiff;
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncServers = async () => {
|
const syncServers = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -201,10 +201,21 @@ const syncServers = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const startServer = async () => {
|
const startServer = async (server: MinecraftServer) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post('/api/startServer', {
|
const response = await axios.post('/api/startServer', {
|
||||||
name: editDialog.editedServer.name
|
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);
|
console.log(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -214,12 +225,19 @@ const startServer = async () => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let intervalId: NodeJS.Timeout;
|
||||||
|
|
||||||
|
|
||||||
onMounted(async()=>{
|
onMounted(async()=>{
|
||||||
await getServers()
|
await getServers()
|
||||||
|
intervalId = setInterval(getLogs, 500);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(()=>{
|
||||||
|
clearInterval(intervalId);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
33
server/api/getLogs.ts
Normal file
33
server/api/getLogs.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import {environment} from "~/core/globals";
|
||||||
|
import { defineEventHandler, getCookie, createError } from 'h3';
|
||||||
|
import {saveJsonFile} from "~/util/jsonLoader";
|
||||||
|
import {MinecraftServer} from "~/types/MinecraftServer";
|
||||||
|
import {execa} from "execa";
|
||||||
|
import * as fs from 'fs/promises';
|
||||||
|
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody(event);
|
||||||
|
const server_name: string = body.name
|
||||||
|
|
||||||
|
const server = environment.settings.servers.find(server => server.name == server_name);
|
||||||
|
|
||||||
|
if(!server) {
|
||||||
|
return { message: "Server with name " + server_name + " does not exist" };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const workingDir = environment.paths.servers + "/" + server.name;
|
||||||
|
const logsPath = environment.paths.servers + "/" + server.name + "/logs/latest.log";
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const data = await fs.readFile(logsPath, 'utf-8');
|
||||||
|
return {
|
||||||
|
message: `Logs for ${server_name}`,
|
||||||
|
logs: data
|
||||||
|
};
|
||||||
|
} catch (error: any) {
|
||||||
|
return { message: "Failed to get logs for " + server_name, error: error.message };
|
||||||
|
}
|
||||||
|
});
|
|
@ -14,6 +14,11 @@ export default defineEventHandler(async (event) => {
|
||||||
return { message: "Server with name " + server_name + " does not exist" };
|
return { message: "Server with name " + server_name + " does not exist" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (server.process) {
|
||||||
|
return { message: "Minecraft server is already running." };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const workingDir = environment.paths.servers + "/" + server.name;
|
const workingDir = environment.paths.servers + "/" + server.name;
|
||||||
const jarPath = environment.paths.servers + "/" + server.name + "/server.jar";
|
const jarPath = environment.paths.servers + "/" + server.name + "/server.jar";
|
||||||
|
|
||||||
|
@ -33,9 +38,10 @@ export default defineEventHandler(async (event) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
server.process.on('exit', (code: string) => {
|
server.process.on('exit', (code: string) => {
|
||||||
console.log(`Minecraft server process exited with code ${code}`);
|
console.log(`Server exit signal from startServer with code ${code}`);
|
||||||
server.process = null;
|
server.process = null;
|
||||||
});
|
})
|
||||||
|
|
||||||
|
|
||||||
return { message: "Minecraft server started." };
|
return { message: "Minecraft server started." };
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
38
server/api/stopServer.ts
Normal file
38
server/api/stopServer.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { environment } from "~/core/globals";
|
||||||
|
import { execa } from "execa";
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody(event);
|
||||||
|
const server_name: string = body.name;
|
||||||
|
|
||||||
|
const server = environment.settings.servers.find(server => server.name == server_name);
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
return { message: "Server with name " + server_name + " does not exist" };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!server.process) {
|
||||||
|
return { message: "Minecraft server is not running." };
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(server.process)
|
||||||
|
|
||||||
|
console.log("currently contains: " + server.process)
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Send "stop" command to the server process stdin
|
||||||
|
server.process.stdin.write('stop\n');
|
||||||
|
|
||||||
|
// Optionally, listen for the process exit or cleanup
|
||||||
|
server.process.on('exit', (code: string) => {
|
||||||
|
console.log(`Server exit signal from stopServer with code ${code}`);
|
||||||
|
server.process = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return { message: "Minecraft server is stopping." };
|
||||||
|
} catch (error: any) {
|
||||||
|
return { message: "Failed to stop Minecraft server.", error: error.message };
|
||||||
|
}
|
||||||
|
});
|
|
@ -3,25 +3,12 @@ import type {ResultPromise} from "execa";
|
||||||
export interface MinecraftServer {
|
export interface MinecraftServer {
|
||||||
name: string;
|
name: string;
|
||||||
port: number;
|
port: number;
|
||||||
maxPlayers: number;
|
|
||||||
version: string;
|
version: string;
|
||||||
jar_url: string;
|
jar_url: string;
|
||||||
|
|
||||||
isRunning: boolean;
|
|
||||||
currentPlayers: number;
|
|
||||||
|
|
||||||
|
|
||||||
difficulty: "peaceful" | "easy" | "normal" | "hard";
|
|
||||||
spawnProtection: number;
|
|
||||||
viewDistance: number;
|
|
||||||
|
|
||||||
|
|
||||||
minMemory: number;
|
minMemory: number;
|
||||||
maxMemory: number;
|
maxMemory: number;
|
||||||
|
|
||||||
logs: string[];
|
|
||||||
|
|
||||||
|
|
||||||
process: any;
|
process: any;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,6 @@ export function loadJsonFile(){
|
||||||
environment.settings = JSON.parse(jsonStr);
|
environment.settings = JSON.parse(jsonStr);
|
||||||
|
|
||||||
checkAllServerDirectories()
|
checkAllServerDirectories()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error reading or parsing JSON file:', error);
|
console.error('Error reading or parsing JSON file:', error);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +22,7 @@ export function saveJsonFile() {
|
||||||
const jsonSettings = JSON.stringify(environment.settings, null, 2);
|
const jsonSettings = JSON.stringify(environment.settings, null, 2);
|
||||||
fs.writeFileSync(environment.files.settings, jsonSettings);
|
fs.writeFileSync(environment.files.settings, jsonSettings);
|
||||||
console.log(`Settings saved to ` + environment.files.settings);
|
console.log(`Settings saved to ` + environment.files.settings);
|
||||||
|
checkAllServerDirectories()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,7 +63,7 @@ export function createPathIfNotExists(dirPath: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function checkAllServerDirectories(){
|
export function checkAllServerDirectories(){
|
||||||
environment.settings.servers.forEach(server => {
|
environment.settings.servers.forEach(server => {
|
||||||
createPathIfNotExists(environment.paths.servers + "/" + server.name)
|
createPathIfNotExists(environment.paths.servers + "/" + server.name)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Reference in a new issue