added file upload and checksum checks

This commit is contained in:
WeeXnes 2025-03-05 13:08:04 +01:00
parent d0b73e91fa
commit 6dc0f8b743
5 changed files with 188 additions and 59 deletions

View file

@ -1,6 +1,17 @@
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
app: {
head: {
title: 'Minecraft Server Manager',
meta: [
{
name: 'description',
content: 'Minecraft Server Manager by WeeXnes'
},
],
},
},
compatibilityDate: '2024-11-01', compatibilityDate: '2024-11-01',
devtools: { enabled: true }, devtools: { enabled: true },
modules: ['@nuxtjs/tailwindcss', '@nuxt/icon'] modules: ['@nuxtjs/tailwindcss', '@nuxt/icon'],
}) })

View file

@ -9,7 +9,7 @@
<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>Memory:</strong> {{ server.maxMemory }}GB</p> <p><strong>Memory:</strong> {{ server.maxMemory }} GB</p>
</div> </div>
<div class="form-control mt-4 flex flex-row gap-2"> <div class="form-control mt-4 flex flex-row gap-2">
@ -48,11 +48,17 @@
</div> </div>
<input v-model="addDialog.newServer.version" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" /> <input v-model="addDialog.newServer.version" 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"> <label class="form-control w-full max-w-xs">
<div class="label"> <div class="label">
<span class="label-text">Jar Download URL</span> <span class="label-text">Jar File Upload</span>
</div> </div>
<input v-model="addDialog.newServer.jar_url" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" /> <input
type="file"
accept=".jar"
class="file-input file-input-bordered w-full max-w-xs"
@change="onFileChange"
/>
</label> </label>
<label class="form-control w-full max-w-xs"> <label class="form-control w-full max-w-xs">
@ -144,7 +150,42 @@
</div> </div>
<input v-model="editServerDialog.properties['pvp']" type="text" placeholder="Type here" class="input input-bordered w-full max-w-xs" /> <input v-model="editServerDialog.properties['pvp']" 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">Render Distance</span>
</div> </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>
@ -174,14 +215,15 @@
import type {MinecraftServer} from "~/types/MinecraftServer"; import type {MinecraftServer} from "~/types/MinecraftServer";
import axios from 'axios'; import axios from 'axios';
import { stringify , parse } from 'ini' import {parse, stringify} from 'ini'
const settings = reactive({ const settings = reactive({
servers: [] as MinecraftServer[], servers: [] as MinecraftServer[],
}) })
const addDialog = reactive({ const addDialog = reactive({
newServer: {} as MinecraftServer newServer: {} as MinecraftServer,
uploadData: '' as string
}) })
const serverLogDialog = reactive({ const serverLogDialog = reactive({
@ -241,6 +283,9 @@ const getLogs = async () => {
} }
} }
const showEditServerDialog = async (server: MinecraftServer) => { const showEditServerDialog = async (server: MinecraftServer) => {
editServerDialog.selectedServer = server editServerDialog.selectedServer = server
await getProperties() await getProperties()
@ -302,8 +347,13 @@ const deleteServer = async (server: MinecraftServer) => {
const addServer = async (server: MinecraftServer) => { const addServer = async (server: MinecraftServer) => {
try { try {
const checksum = await calculateChecksum(addDialog.uploadData)
const response = await axios.post('/api/addServer', { const response = await axios.post('/api/addServer', {
newServer: addDialog.newServer newServer: addDialog.newServer,
fileUpload:{
data: addDialog.uploadData,
checksum: checksum
}
}); });
console.log(response.data); console.log(response.data);
await getServers() await getServers()
@ -313,6 +363,66 @@ const addServer = async (server: MinecraftServer) => {
} }
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) => { const startServer = async (server: MinecraftServer) => {

View file

@ -1,11 +1,13 @@
import {getServerByName} from "~/types/MinecraftServer"; import {getJarPath, getServerByName} from "~/types/MinecraftServer";
import {environment} from "~/core/globals"; import {environment} from "~/core/globals";
import {saveJsonFile} from "~/util/jsonLoader"; import {saveJsonFile} from "~/util/jsonLoader";
import * as crypto from 'crypto';
import * as fs from "node:fs";
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const body = await readBody(event); const body = await readBody(event);
const { newServer } = body; const { newServer, fileUpload } = body;
const server = getServerByName(newServer.name); const server = getServerByName(newServer.name);
@ -13,12 +15,50 @@ export default defineEventHandler(async (event) => {
return { message: "Server with name " + newServer.name + " does already exist" }; return { message: "Server with name " + newServer.name + " does already exist" };
} }
let uploadData = fileUpload.data;
let checksumFromClient = fileUpload.checksum;
let checksumFromServer = calculateChecksum(uploadData);
try { try {
if(checksumFromServer != checksumFromClient) {
return { status: 'error', error: "Checksum failed" };
}
environment.settings.servers.push({...newServer}) environment.settings.servers.push({...newServer})
saveJsonFile() saveJsonFile()
const decodedData = decodeBase64Node(uploadData)
fs.writeFileSync(getJarPath(newServer), decodedData);
return { status: 'success' }; return { status: 'success' };
} catch (error) { } catch (error) {
return { status: 'error', error: error }; return { status: 'error', error: error };
} }
}); });
const calculateChecksum = (data: any) => {
try {
const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data, 'utf-8');
const hash = crypto.createHash('sha256');
hash.update(buffer);
const checksum = hash.digest('hex');
console.log('Checksum (SHA-256):', checksum);
return checksum;
} catch (error) {
console.error('Error calculating checksum:', error);
}
};
function decodeBase64Node(base64String: string): Buffer {
return Buffer.from(base64String, 'base64');
}

View file

@ -3,7 +3,7 @@ import {environment} from "~/core/globals";
export interface MinecraftServer { export interface MinecraftServer {
name: string; name: string;
version: string; version: string;
jar_url: string; jarName: string;
maxMemory: number; maxMemory: number;
process: any; process: any;
} }
@ -14,7 +14,7 @@ export function getServerDirectory (server: MinecraftServer): string {
} }
export function getJarPath (server: MinecraftServer): string { export function getJarPath (server: MinecraftServer): string {
return environment.paths.servers + "/" + server.name + "/server.jar" return environment.paths.servers + "/" + server.name + "/" + server.jarName
} }
export function getPropertiesPath (server: MinecraftServer): string { export function getPropertiesPath (server: MinecraftServer): string {
@ -25,6 +25,6 @@ export function getLogsPath (server: MinecraftServer): string {
return environment.paths.servers + "/" + server.name + "/logs/latest.log" return environment.paths.servers + "/" + server.name + "/logs/latest.log"
} }
export function getServerByName(server_name: string): MinecraftServer | undefined{ export function getServerByName(server_name: string): MinecraftServer | undefined {
return environment.settings.servers.find(server => server.name == server_name); return environment.settings.servers.find(server => server.name == server_name);
} }

View file

@ -1,8 +1,8 @@
import {environment} from "~/core/globals"; import {environment} from "~/core/globals";
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path';
import type {SettingsJsonFile} from "~/types/SettingsJsonFile"; import type {SettingsJsonFile} from "~/types/SettingsJsonFile";
import * as https from "node:https"; import type {MinecraftServer} from "~/types/MinecraftServer";
import {getServerDirectory} from "~/types/MinecraftServer";
export function loadJsonFile(){ export function loadJsonFile(){
try { try {
@ -65,54 +65,22 @@ export function createPathIfNotExists(dirPath: string) {
export function checkAllServerDirectories(){ export function checkAllServerDirectories(){
environment.settings.servers.forEach(server => { environment.settings.servers.forEach(server => {
createPathIfNotExists(environment.paths.servers + "/" + server.name) createPathIfNotExists(getServerDirectory(server))
checkEulaFile(server)
}) })
}
environment.settings.servers.forEach(server => {
const destinationPathJar = environment.paths.servers + "/" + server.name + "/server.jar" export function checkEulaFile(server: MinecraftServer){
const destinationPathEula = environment.paths.servers + "/" + server.name + "/eula.txt" const destinationPathEula = environment.paths.servers + "/" + server.name + "/eula.txt"
try {
fs.accessSync(destinationPathJar);
console.log('Settings already exists!');
} catch (error) {
downloadFile(server.jar_url, destinationPathJar);
}
try { try {
fs.accessSync(destinationPathEula); fs.accessSync(destinationPathEula);
console.log('eula exists'); console.log('eula exists');
} catch (error) { } catch (error) {
console.log("setting eula to true") console.log(`Setting eula for ${server.name} to ${environment.settings.eula}`)
fs.writeFileSync(destinationPathEula, 'eula=' + environment.settings.eula); fs.writeFileSync(destinationPathEula, 'eula=' + environment.settings.eula);
} }
})
} }
function downloadFile(url: string, dest: string) {
if(url == "")
return;
console.log(dest + ' doesnt exist, downloading now from ' + url);
const file = fs.createWriteStream(dest);
https.get(url, (response) => {
if (response.statusCode === 200) {
response.pipe(file);
} else {
console.error(`Failed to download file. Status Code: ${response.statusCode}`);
}
file.on('finish', () => {
file.close();
console.log('Download complete!');
});
}).on('error', (err) => {
// Handle any errors during the download
console.error('Error during download:', err.message);
fs.unlink(dest, () => {});
});
}