mirror of
https://github.com/tcsenpai/rconuicraft.git
synced 2025-06-02 17:30:09 +00:00
177 lines
4.8 KiB
JavaScript
177 lines
4.8 KiB
JavaScript
require('dotenv').config();
|
|
const express = require('express');
|
|
const { Rcon } = require('rcon-client');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const basicAuth = require('express-basic-auth');
|
|
const app = express();
|
|
const port = process.env.PORT || 3000;
|
|
|
|
app.use(express.json());
|
|
|
|
// Basic Authentication
|
|
const users = {
|
|
[process.env.ADMIN_USERNAME]: process.env.ADMIN_PASSWORD
|
|
};
|
|
|
|
app.use(basicAuth({
|
|
users: users,
|
|
challenge: true,
|
|
realm: 'Minecraft Server Control Panel'
|
|
}));
|
|
|
|
app.use(express.static('public'));
|
|
|
|
// RCON configuration
|
|
const config = {
|
|
host: process.env.RCON_HOST,
|
|
port: parseInt(process.env.RCON_PORT),
|
|
password: process.env.RCON_PASSWORD,
|
|
modsPath: process.env.MODS_PATH,
|
|
serverPath: process.env.SERVER_PATH
|
|
};
|
|
|
|
let rcon = null;
|
|
let serverStats = {
|
|
players: 0,
|
|
maxPlayers: 0,
|
|
tps: 20,
|
|
uptime: 0,
|
|
mods: []
|
|
};
|
|
|
|
async function connectRcon() {
|
|
if (rcon === null || !rcon.connected) {
|
|
rcon = await Rcon.connect({
|
|
host: config.host,
|
|
port: config.port,
|
|
password: config.password
|
|
});
|
|
console.log('RCON authenticated');
|
|
}
|
|
return rcon;
|
|
}
|
|
|
|
async function getMaxPlayers() {
|
|
try {
|
|
const propertiesPath = path.join(config.serverPath || '.', 'server.properties');
|
|
if (fs.existsSync(propertiesPath)) {
|
|
const content = fs.readFileSync(propertiesPath, 'utf8');
|
|
const maxPlayersMatch = content.match(/max-players=(\d+)/);
|
|
if (maxPlayersMatch) {
|
|
return parseInt(maxPlayersMatch[1]);
|
|
}
|
|
}
|
|
|
|
const rcon = await connectRcon();
|
|
const response = await rcon.send('list');
|
|
const match = response.match(/\d+\/(\d+)/);
|
|
if (match) {
|
|
return parseInt(match[1]);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error getting max players:', error);
|
|
}
|
|
return 20;
|
|
}
|
|
|
|
function formatBytes(bytes) {
|
|
if (bytes === 0) return '0 Bytes';
|
|
const k = 1024;
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
}
|
|
|
|
async function updateServerStats() {
|
|
try {
|
|
const rcon = await connectRcon();
|
|
|
|
const listResponse = await rcon.send('list');
|
|
const playerMatch = listResponse.match(/There are (\d+)\/(\d+) players online/);
|
|
if (playerMatch) {
|
|
serverStats.players = parseInt(playerMatch[1]);
|
|
serverStats.maxPlayers = parseInt(playerMatch[2]);
|
|
} else {
|
|
serverStats.maxPlayers = await getMaxPlayers();
|
|
}
|
|
|
|
try {
|
|
const tpsResponse = await rcon.send('tps');
|
|
const tpsMatch = tpsResponse.match(/\d+\.\d+/);
|
|
if (tpsMatch) {
|
|
serverStats.tps = parseFloat(tpsMatch[0]);
|
|
}
|
|
} catch (e) {
|
|
serverStats.tps = 20;
|
|
}
|
|
|
|
try {
|
|
const modsDir = config.modsPath;
|
|
if (fs.existsSync(modsDir)) {
|
|
serverStats.mods = fs.readdirSync(modsDir)
|
|
.filter(file => file.endsWith('.jar'))
|
|
.map(file => ({
|
|
name: file.replace('.jar', ''),
|
|
size: formatBytes(fs.statSync(path.join(modsDir, file)).size)
|
|
}));
|
|
}
|
|
} catch (e) {
|
|
console.error('Error reading mods:', e);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Error updating server stats:', error);
|
|
rcon = null;
|
|
}
|
|
}
|
|
|
|
app.get('/api/stats', async (req, res) => {
|
|
try {
|
|
await updateServerStats();
|
|
res.json(serverStats);
|
|
} catch (error) {
|
|
res.status(500).json({ error: error.message });
|
|
}
|
|
});
|
|
|
|
app.post('/api/command', async (req, res) => {
|
|
const { command } = req.body;
|
|
|
|
if (!command) {
|
|
return res.status(400).json({ success: false, error: 'No command provided' });
|
|
}
|
|
|
|
try {
|
|
const rcon = await connectRcon();
|
|
const response = await rcon.send(command);
|
|
res.json({ success: true, response });
|
|
} catch (error) {
|
|
res.status(500).json({ success: false, error: error.message });
|
|
}
|
|
});
|
|
|
|
// Validate required environment variables
|
|
const requiredEnvVars = [
|
|
'ADMIN_USERNAME',
|
|
'ADMIN_PASSWORD',
|
|
'RCON_HOST',
|
|
'RCON_PORT',
|
|
'RCON_PASSWORD',
|
|
'MODS_PATH',
|
|
'SERVER_PATH'
|
|
];
|
|
|
|
const missingEnvVars = requiredEnvVars.filter(varName => !process.env[varName]);
|
|
if (missingEnvVars.length > 0) {
|
|
console.error('Missing required environment variables:', missingEnvVars.join(', '));
|
|
process.exit(1);
|
|
}
|
|
|
|
// Start server
|
|
app.listen(port, () => {
|
|
console.log(`Server running at http://localhost:${port}`);
|
|
updateServerStats(); // Initial stats update
|
|
setInterval(updateServerStats, 5000); // Update every 5 seconds
|
|
});
|