added file upload and checksum checks
This commit is contained in:
parent
d0b73e91fa
commit
6dc0f8b743
5 changed files with 188 additions and 59 deletions
|
@ -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'],
|
||||||
})
|
})
|
122
pages/index.vue
122
pages/index.vue
|
@ -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,10 +150,45 @@
|
||||||
</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>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="modal-action flex justify-between space-x-4">
|
<div class="modal-action flex justify-between space-x-4">
|
||||||
<!-- Save Button -->
|
<!-- Save Button -->
|
||||||
<form method="dialog">
|
<form method="dialog">
|
||||||
|
@ -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) => {
|
||||||
|
|
|
@ -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');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
|
@ -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"
|
|
||||||
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 {
|
|
||||||
fs.accessSync(destinationPathEula);
|
|
||||||
console.log('eula exists');
|
|
||||||
} catch (error) {
|
|
||||||
console.log("setting eula to true")
|
|
||||||
fs.writeFileSync(destinationPathEula, 'eula=' + environment.settings.eula);
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function downloadFile(url: string, dest: string) {
|
export function checkEulaFile(server: MinecraftServer){
|
||||||
if(url == "")
|
const destinationPathEula = environment.paths.servers + "/" + server.name + "/eula.txt"
|
||||||
return;
|
try {
|
||||||
|
fs.accessSync(destinationPathEula);
|
||||||
|
console.log('eula exists');
|
||||||
console.log(dest + ' doesnt exist, downloading now from ' + url);
|
} catch (error) {
|
||||||
const file = fs.createWriteStream(dest);
|
console.log(`Setting eula for ${server.name} to ${environment.settings.eula}`)
|
||||||
|
fs.writeFileSync(destinationPathEula, 'eula=' + environment.settings.eula);
|
||||||
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, () => {});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue