Add Hytale config editor and Prometheus integration
All checks were successful
Deploy GSM / deploy (push) Successful in 25s
All checks were successful
Deploy GSM / deploy (push) Successful in 25s
- Add Node Exporter target for Hytale server (10.0.30.204:9100) - Add Hytale config read/write functions to ssh.js - Add GET/PUT /hytale/config API routes - Create HytaleConfigEditor.jsx with JSON syntax highlighting - Add Hytale config tab to ServerDetail.jsx - Add stopCmd and port to Hytale server config Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -102,7 +102,9 @@
|
||||
"sshUser": "beeck",
|
||||
"workDir": "/opt/hytale/Server",
|
||||
"startCmd": "./start-server.sh",
|
||||
"logCmd": "ls -t /opt/hytale/Server/logs/*.log | head -1 | xargs tail -n"
|
||||
"stopCmd": "/stop",
|
||||
"logCmd": "ls -t /opt/hytale/Server/logs/*.log | head -1 | xargs tail -n",
|
||||
"port": 5520
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { dirname, join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { EmbedBuilder } from 'discord.js';
|
||||
import { authenticateToken, optionalAuth, requireRole, rejectGuest } from '../middleware/auth.js';
|
||||
import { getServerStatus, startServer, stopServer, restartServer, getConsoleLog, getProcessUptime, listFactorioSaves, createFactorioWorld, deleteFactorioSave, getFactorioCurrentSave, isHostFailed, listZomboidConfigs, readZomboidConfig, writeZomboidConfig, listPalworldConfigs, readPalworldConfig, writePalworldConfig, readTerrariaConfig, writeTerrariaConfig, readOpenTTDConfig, writeOpenTTDConfig } from '../services/ssh.js';
|
||||
import { getServerStatus, startServer, stopServer, restartServer, getConsoleLog, getProcessUptime, listFactorioSaves, createFactorioWorld, deleteFactorioSave, getFactorioCurrentSave, isHostFailed, listZomboidConfigs, readZomboidConfig, writeZomboidConfig, listPalworldConfigs, readPalworldConfig, writePalworldConfig, readTerrariaConfig, writeTerrariaConfig, readOpenTTDConfig, writeOpenTTDConfig, readHytaleConfig, writeHytaleConfig } from '../services/ssh.js';
|
||||
import { sendRconCommand, getPlayers, getPlayerList } from '../services/rcon.js';
|
||||
import { getServerMetricsHistory, getCurrentMetrics } from '../services/prometheus.js';
|
||||
import { initWhitelistCache, getCachedWhitelist, setCachedWhitelist, initFactorioTemplates, getFactorioTemplates, createFactorioTemplate, deleteFactorioTemplate, initFactorioWorldSettings, getFactorioWorldSettings, saveFactorioWorldSettings, deleteFactorioWorldSettings, initAutoShutdownSettings, getAutoShutdownSettings, setAutoShutdownSettings, initActivityLog, logActivity, getActivityLog, initServerDisplaySettings, getServerDisplaySettings, getAllServerDisplaySettings, setServerDisplaySettings, initGuildSettings } from '../db/init.js';
|
||||
@@ -502,6 +502,47 @@ router.put("/openttd/config", authenticateToken, requireRole("moderator"), async
|
||||
}
|
||||
});
|
||||
|
||||
// ============ HYTALE ROUTES ============
|
||||
|
||||
// Get Hytale config
|
||||
router.get("/hytale/config", authenticateToken, requireRole("moderator"), async (req, res) => {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const server = config.servers.find(s => s.id === "hytale");
|
||||
if (!server) return res.status(404).json({ error: "Server not found" });
|
||||
|
||||
if (isHostFailed(server.host, server.sshUser)) {
|
||||
return res.status(503).json({ error: "Server host is unreachable", unreachable: true });
|
||||
}
|
||||
|
||||
const content = await readHytaleConfig(server);
|
||||
res.json({ content });
|
||||
} catch (error) {
|
||||
console.error("Error reading Hytale config:", error);
|
||||
if (error.message.includes('unreachable') || error.message.includes('ECONNREFUSED') || error.message.includes('ETIMEDOUT')) {
|
||||
return res.status(503).json({ error: 'Server host is unreachable', unreachable: true });
|
||||
}
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Save Hytale config
|
||||
router.put("/hytale/config", authenticateToken, requireRole("moderator"), async (req, res) => {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const server = config.servers.find(s => s.id === "hytale");
|
||||
if (!server) return res.status(404).json({ error: "Server not found" });
|
||||
const { content } = req.body;
|
||||
if (!content) return res.status(400).json({ error: "Content required" });
|
||||
await writeHytaleConfig(server, content);
|
||||
logActivity(req.user.id, req.user.username, "hytale_config", "hytale", "config.json", req.user.discordId, req.user.avatar);
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("Error writing Hytale config:", error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Get single server (guests not allowed)
|
||||
router.get('/:id', authenticateToken, rejectGuest, async (req, res) => {
|
||||
|
||||
@@ -642,3 +642,30 @@ export async function writeOpenTTDConfig(server, content) {
|
||||
await ssh.execCommand(`ls -t /opt/openttd/.openttd/openttd.cfg.backup.* 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null || true`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Hytale Config Management
|
||||
const HYTALE_CONFIG_PATH = "/opt/hytale/Server/config.json";
|
||||
|
||||
export async function readHytaleConfig(server) {
|
||||
const ssh = await getConnection(server.host, server.sshUser);
|
||||
const result = await ssh.execCommand(`cat ${HYTALE_CONFIG_PATH}`);
|
||||
if (result.code !== 0) {
|
||||
throw new Error(result.stderr || "Failed to read config file");
|
||||
}
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
export async function writeHytaleConfig(server, content) {
|
||||
const ssh = await getConnection(server.host, server.sshUser);
|
||||
const backupName = `config.json.backup.${Date.now()}`;
|
||||
await ssh.execCommand(`cp ${HYTALE_CONFIG_PATH} /opt/hytale/Server/${backupName} 2>/dev/null || true`);
|
||||
const sftp = await ssh.requestSFTP();
|
||||
await new Promise((resolve, reject) => {
|
||||
sftp.writeFile(HYTALE_CONFIG_PATH, content, (err) => {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
await ssh.execCommand(`ls -t /opt/hytale/Server/config.json.backup.* 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null || true`);
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user