try this shi
This commit is contained in:
14
backend/Dockerfile
Normal file
14
backend/Dockerfile
Normal 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
19
backend/package.json
Normal 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
169
backend/src/index.js
Normal 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}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user