Compare commits

...

5 Commits
main ... node

14 changed files with 2045 additions and 24 deletions

25
.gitignore vendored
View File

@ -1,23 +1,2 @@
# ---> Go # NPM dependencies
# If you prefer the allow list template instead of the deny list, see community template: node_modules/
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work

View File

@ -1,4 +1,4 @@
Copyright (c) <year> <owner> Copyright (c) 2022 Bit Goblin
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

64
app/MinecraftServer.js Normal file
View File

@ -0,0 +1,64 @@
const { exec } = require("child_process");
const fs = require('fs');
class Server {
constructor(dir) {
this.rootDir = dir;
this.name = dir.split('/').at(-1);
this.pidFilePath = this.rootDir + '/pid.txt';
// read version file
var versionFilePath = this.rootDir + '/current_version.txt';
if (fs.existsSync(versionFilePath)) {
this.version = fs.readFileSync(versionFilePath, {encoding:'utf8', flag:'r'});
}
}
start() {
console.log(`Starting server ${this.name}...`);
// create shell command and execute it
let cmd = `${this.rootDir}/start.sh`;
exec(cmd, (err, stdout, stderr) => {
if (err) {
console.log(`Error while starting server: ${error.message}`);
}
console.log(`Server ${this.name} has been started.`);
});
}
stop() {
console.log(`Stopping server ${this.name}...`);
}
getStatus() {
if (fs.existsSync(this.pidFilePath)) {
let pid = fs.readFileSync(this.pidFilePath, {encoding: 'utf8', flag: 'r'});
try {
return process.kill(pid, 0);
} catch (err) {
// ESRCH error means the process doesn't exist, so should return false
if (err.code !== 'ESRCH') {
console.error(err);
return err.code === 'EPERM';
} else {
// delete pid.txt so we don't have to worry about checking it again
fs.unlinkSync(this.pidFilePath);
}
}
}
return false
}
getPid() {
if (fs.existsSync(this.pidFilePath)) {
return fs.readFileSync(this.pidFilePath, {encoding:'utf8', flag:'r'})
}
return -1;
}
}
exports.Server = Server;

3
config/default.json Normal file
View File

@ -0,0 +1,3 @@
{
"server_directory": "/opt/minecraft"
}

30
index.js Normal file
View File

@ -0,0 +1,30 @@
const express = require('express');
const app = express();
const port = 3000;
// set template engine to Pug
app.set('view engine', 'pug');
// Using express.urlencoded middleware
app.use(express.urlencoded({
extended: true
}));
// import route handlers
var homeRoutes = require('./routes/home');
var serverRoutes = require('./routes/server');
// define routes
app.get('/', homeRoutes.getIndex);
app.get('/server/create', serverRoutes.getCreate);
app.post('/server/create', serverRoutes.postCreate);
app.get('/server/:serverName/start', serverRoutes.getStart);
app.get('/server/:serverName/stop', serverRoutes.getStop);
// set Express to serve static files (ideally this is only used in development)
app.use(express.static('./static/'));
// start Express.js app
app.listen(port, () => {
console.log(`MCST has started and is listening on port ${port}.`);
});

1706
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

26
package.json Normal file
View File

@ -0,0 +1,26 @@
{
"name": "mcst",
"version": "0.1.0",
"description": "Minecraft Java edition server management tool",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "gitea@git.metaunix.net:BitGoblin/mcst.git"
},
"keywords": [
"minecraft",
"minecraft java edition",
"java"
],
"author": "Gregory Ballantine <gballantine@bitgoblin.tech>",
"license": "BSD-2-Clause",
"dependencies": {
"config": "^3.3.8",
"express": "^4.18.1",
"pug": "^3.0.2"
}
}

19
routes/home.js Normal file
View File

@ -0,0 +1,19 @@
const config = require('config');
const fs = require('fs');
const path = require('path');
const minecraft = require('../app/MinecraftServer');
exports.getIndex = function(req, res) {
// search for minecraft server directories
let rootDir = config.get('server_directory');
let serverDirs = fs.readdirSync(rootDir);
let servers = [];
for (let i = 0; i < serverDirs.length; i++) {
servers.push(new minecraft.Server(path.join(rootDir, serverDirs[i])));
}
// render view
res.render('index', {
servers: servers,
});
};

70
routes/server.js Normal file
View File

@ -0,0 +1,70 @@
const config = require('config');
const fs = require('fs');
const https = require('https');
const path = require('path');
const minecraft = require('../app/MinecraftServer');
exports.getCreate = function(req, res) {
// render view
res.render('create');
};
exports.postCreate = function(req, res) {
mcDir = config.get('server_directory');
serverDir = path.join(mcDir, req.body.serverName);
// make server directory
fs.mkdirSync(serverDir);
// grab the server JAR file
serverJarUrl = 'https://piston-data.mojang.com/v1/objects/f69c284232d7c7580bd89a5a4931c3581eae1378/server.jar';
serverJarName = "server_" + req.body.serverVersion + ".jar";
serverJarPath = path.join(serverDir, serverJarName);
https.get(serverJarUrl, (res) => {
const filePath = fs.createWriteStream(serverJarPath);
res.pipe(filePath);
filePath.on('finish', () => {
filePath.close();
console.log('Download Completed');
});
});
// create the start.sh shell script
scriptFilePath = path.join(serverDir, 'start.sh');
scriptContent = "#!/bin/sh\n\ncd " + serverDir + "\njava -Xmx2048M -Xms2048M -jar server_" + req.body.serverVersion + ".jar nogui &\n\necho \"$!\" > ./pid.txt";
fs.writeFileSync(scriptFilePath, scriptContent);
fs.chmodSync(scriptFilePath, 0o755);
// save the current version to a text file - this will hopefully be temporary solution
versionFilePath = path.join(serverDir, 'current_version.txt');
versionContent = req.body.serverVersion;
fs.writeFileSync(versionFilePath, versionContent);
fs.chmodSync(versionFilePath, 0o644);
// redirect the user back to the home page
res.redirect('/');
};
exports.getStart = function(req, res) {
let serverName = req.params.serverName;
let rootDir = config.get('server_directory');
let server = new minecraft.Server(path.join(rootDir, serverName));
// start the server
server.start();
// redirect the user back to the home page
res.redirect('/');
};
exports.getStop = function(req, res) {
let serverName = req.params.serverName;
let rootDir = config.get('server_directory');
let server = new minecraft.Server(path.join(rootDir, serverName));
// stop the server
server.stop();
// redirect the user back to the home page
res.redirect('/');
};

46
static/css/wyrm.css Normal file
View File

@ -0,0 +1,46 @@
body{
background-color: #e6e6e6;
}
/* set the max-width for centered content */
.container{
max-width: 1100px;
}
#main-content{
margin-top: 25px;
padding: 12px 25px;
background: white;
}
/* global navigation bar styles */
.navbar{
padding: 10px 18px;
background-color: #212121;
color: white;
font-size: 2.5rem;
font-weight: bold;
}
.navbar ul{
margin: 0;
padding: 0;
list-style: none;
}
.navbar li{
display: inline-block;
margin-bottom: 0;
}
.navbar li:not(:first-child){
margin-left: 10px;
}
.navbar li>a{
color: #eee;
text-decoration: none;
transition: color 220ms ease-in-out;
}
.navbar li>a:hover{
color: white;
}

3
static/js/drake.js Normal file
View File

@ -0,0 +1,3 @@
$(document).ready(function () {
// This will be used when needed
});

24
views/create.pug Normal file
View File

@ -0,0 +1,24 @@
extends layout.pug
block content
header.row
div.columns.twelve
h1 Create new server
section.row
div.columns.twelve
form(action='/server/create', method='POST')
div.row
div.columns.six
label(for='serverName') Server name:
input#serverName.u-full-width(name='serverName', type='text', placeholder='MyServer')
div.columns.six
label(for='serverVersion') Minecraft version:
input#serverVersion.u-full-width(name='serverVersion', type='text', placeholder='1.19.2')
input(type='submit', value='Submit')
div.row
div.columns.twelve
p
a(href='/') Back

30
views/index.pug Normal file
View File

@ -0,0 +1,30 @@
extends layout.pug
block content
header.row
div.columns.twelve
h1 Welcome to MCST!
p Using MCST you can easily manage your Minecraft: Java Edition servers.
section.row
div.columns.twelve
h3 List of servers:
if servers.length < 1
p There are currently no servers registered.
else
table.u-full-width
thead
tr
th Server name
th Minecraft version
th State
th Actions
tbody
each m in servers
tr.serverItem
td.serverName= m.name
td.serverVersion= m.version
td.serverState= m.getStatus()
td
a(href='/server/' + m.name + '/start') Start
a(href='/server/' + m.name + '/stop') Stop

21
views/layout.pug Normal file
View File

@ -0,0 +1,21 @@
doctype html
html(lang="en")
head
title= pageTitle
link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css')
link(rel='stylesheet', href='/css/wyrm.css')
script(src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js')
script(src='/js/drake.js')
body
// global navigation
nav.navbar
div.navbar-left
ul
li.menu-text MCST
li: a(href='/') Home
li: a(href='/server/create') Create
li: a(href='/status') Status
// main content
div#main-content.container
block content