try this shi

This commit is contained in:
2025-10-13 13:53:48 -05:00
commit fc6d1237e9
24 changed files with 1345 additions and 0 deletions

14
backend/Dockerfile Normal file
View File

@@ -0,0 +1,14 @@
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3001
CMD ["npm", "start"]

19
backend/package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "mc-dashboard-backend",
"version": "1.0.0",
"description": "Minecraft Server Dashboard Backend",
"main": "src/index.js",
"type": "module",
"scripts": {
"start": "node src/index.js",
"dev": "node --watch src/index.js"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"minecraft-server-util": "^5.4.3",
"modern-rcon": "^1.2.1",
"dockerode": "^4.0.2"
}
}

169
backend/src/index.js Normal file
View File

@@ -0,0 +1,169 @@
import express from 'express';
import cors from 'cors';
import { status } from 'minecraft-server-util';
import Rcon from 'modern-rcon';
import Docker from 'dockerode';
const app = express();
const PORT = process.env.PORT || 3001;
app.use(cors());
app.use(express.json());
const MINECRAFT_HOST = process.env.MINECRAFT_HOST || 'mc-java';
const MINECRAFT_PORT = parseInt(process.env.MINECRAFT_PORT || '25565');
const RCON_PORT = parseInt(process.env.RCON_PORT || '25575');
const RCON_PASSWORD = process.env.RCON_PASSWORD || 'bethureddy1';
const CONTAINER_NAME = process.env.CONTAINER_NAME || 'mc-java';
const docker = new Docker({ socketPath: '/var/run/docker.sock' });
// Get server status
app.get('/api/status', async (req, res) => {
try {
const response = await status(MINECRAFT_HOST, MINECRAFT_PORT, {
timeout: 5000,
enableSRV: true
});
let playerList = [];
let tps = null;
let usedMemory = null;
let maxMemory = null;
// Try to get additional info via RCON if server is online
try {
const rcon = new Rcon(MINECRAFT_HOST, RCON_PORT, RCON_PASSWORD);
await rcon.connect();
// Get player list
const listResponse = await rcon.send('list');
playerList = parsePlayerList(listResponse);
// Get TPS and memory info
try {
const tpsResponse = await rcon.send('spark tps');
tps = parseTPS(tpsResponse);
} catch (e) {
// spark might not be installed
}
await rcon.disconnect();
} catch (rconError) {
console.error('RCON error:', rconError.message);
}
res.json({
online: true,
version: response.version.name,
protocol: response.version.protocol,
players: {
online: response.players.online,
max: response.players.max,
list: playerList
},
motd: response.motd.clean,
latency: response.roundTripLatency,
tps: tps,
memory: {
used: usedMemory,
max: maxMemory
}
});
} catch (error) {
console.error('Status error:', error.message);
res.json({
online: false,
error: error.message
});
}
});
// Get container status
app.get('/api/container/status', async (req, res) => {
try {
const container = docker.getContainer(CONTAINER_NAME);
const info = await container.inspect();
res.json({
running: info.State.Running,
status: info.State.Status,
startedAt: info.State.StartedAt,
health: info.State.Health?.Status || 'unknown'
});
} catch (error) {
console.error('Container status error:', error.message);
res.status(500).json({ error: error.message });
}
});
// Start the Minecraft server
app.post('/api/server/start', async (req, res) => {
try {
const container = docker.getContainer(CONTAINER_NAME);
const info = await container.inspect();
if (info.State.Running) {
return res.json({ success: false, message: 'Server is already running' });
}
await container.start();
res.json({ success: true, message: 'Server started successfully' });
} catch (error) {
console.error('Start error:', error.message);
res.status(500).json({ success: false, error: error.message });
}
});
// Stop the Minecraft server
app.post('/api/server/stop', async (req, res) => {
try {
const container = docker.getContainer(CONTAINER_NAME);
const info = await container.inspect();
if (!info.State.Running) {
return res.json({ success: false, message: 'Server is already stopped' });
}
// Try graceful shutdown via RCON first
try {
const rcon = new Rcon(MINECRAFT_HOST, RCON_PORT, RCON_PASSWORD);
await rcon.connect();
await rcon.send('save-all');
await rcon.send('stop');
await rcon.disconnect();
} catch (rconError) {
console.error('RCON shutdown error, using docker stop:', rconError.message);
await container.stop({ t: 30 }); // 30 second timeout
}
res.json({ success: true, message: 'Server stopped successfully' });
} catch (error) {
console.error('Stop error:', error.message);
res.status(500).json({ success: false, error: error.message });
}
});
// Helper function to parse player list from RCON
function parsePlayerList(response) {
const match = response.match(/There are \d+ of a max of \d+ players online: (.*)/);
if (match && match[1]) {
return match[1].split(', ').filter(name => name.length > 0);
}
return [];
}
// Helper function to parse TPS (if spark is installed)
function parseTPS(response) {
const match = response.match(/TPS.*?(\d+\.?\d*)/);
if (match) {
return parseFloat(match[1]);
}
return null;
}
app.listen(PORT, '0.0.0.0', () => {
console.log(`🚀 Minecraft Dashboard Backend running on port ${PORT}`);
console.log(`📡 Monitoring Minecraft server at ${MINECRAFT_HOST}:${MINECRAFT_PORT}`);
});