From 4194f68968a55ddfb2abaf2b3bd5de6f62bbe1b7 Mon Sep 17 00:00:00 2001 From: Alexander Zielonka Date: Tue, 6 Jan 2026 21:22:10 +0100 Subject: [PATCH 1/2] xd --- .claude/settings.local.json | 6 +- ZeasyWG-Alex.conf | 10 + gsm-backend/services/rcon.js | 159 ++++++++++++ gsm-backend/services/ssh.js | 286 +++++++++++++++++++++ gsm-frontend/public/zomboid.png | Bin 0 -> 71130 bytes gsm-frontend/src/components/ServerCard.jsx | 16 ++ gsm-frontend/src/pages/ServerDetail.jsx | 36 ++- 7 files changed, 497 insertions(+), 16 deletions(-) create mode 100644 ZeasyWG-Alex.conf create mode 100644 gsm-backend/services/rcon.js create mode 100644 gsm-backend/services/ssh.js create mode 100644 gsm-frontend/public/zomboid.png diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 2c41e8e..b140acf 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -3,12 +3,8 @@ "allow": [ "Bash(ssh:*)", "Bash(scp:*)", - "Bash(veth.*)", - "Bash(docker.*)", + "Bash(docker:*)", "Bash(curl:*)", - "Bash(findstr:*)", - "Bash(cat:*)", - "Bash(powershell -Command @'\n$content = @\"\"\nimport { useState, useEffect } from 'react'\nimport { useNavigate } from 'react-router-dom'\nimport { getServers } from '../api'\nimport { useUser } from '../context/UserContext'\nimport ServerCard from '../components/ServerCard'\nimport SettingsModal from '../components/SettingsModal'\nimport UserManagement from '../components/UserManagement'\n\nexport default function Dashboard\\({ onLogout }\\) {\n const navigate = useNavigate\\(\\)\n const { user, token, loading: userLoading, isSuperadmin, role } = useUser\\(\\)\n const [servers, setServers] = useState\\([]\\)\n const [loading, setLoading] = useState\\(true\\)\n const [error, setError] = useState\\(''\\)\n const [showSettings, setShowSettings] = useState\\(false\\)\n const [showUserMgmt, setShowUserMgmt] = useState\\(false\\)\n\n const fetchServers = async \\(\\) => {\n try {\n const data = await getServers\\(token\\)\n setServers\\(data\\)\n setError\\(''\\)\n } catch \\(err\\) {\n setError\\('Failed to connect to server'\\)\n if \\(err.message.includes\\('401'\\) || err.message.includes\\('403'\\)\\) {\n onLogout\\(\\)\n }\n } finally {\n setLoading\\(false\\)\n }\n }\n\n useEffect\\(\\(\\) => {\n if \\(!userLoading\\) {\n fetchServers\\(\\)\n const interval = setInterval\\(fetchServers, 10000\\)\n return \\(\\) => clearInterval\\(interval\\)\n }\n }, [token, userLoading]\\)\n\n const roleLabels = {\n user: 'Viewer',\n moderator: 'Operator',\n superadmin: 'Admin'\n }\n\n if \\(userLoading\\) {\n return \\(\n
\n
Loading...
\n
\n \\)\n }\n\n const onlineCount = servers.filter\\(s => s.running\\).length\n const totalPlayers = servers.reduce\\(\\(sum, s\\) => sum + \\(s.players?.online || 0\\), 0\\)\n\n return \\(\n
\n {/* Header */}\n
\n
\n
\n
\n

\n Gameserver Monitor\n

\n
\n \n {onlineCount}/{servers.length} online\n \n |\n \n {totalPlayers} players\n \n
\n
\n\n
\n
\n
{user?.username}
\n
{roleLabels[role]}
\n
\n\n {isSuperadmin && \\(\n setShowUserMgmt\\(true\\)}\n className=\"\"btn btn-ghost\"\"\n >\n Users\n \n \\)}\n\n setShowSettings\\(true\\)}\n className=\"\"btn btn-ghost\"\"\n >\n Settings\n \n\n \n Sign out\n \n
\n
\n
\n
\n\n {/* Main Content */}\n
\n {error && \\(\n
\n {error}\n
\n \\)}\n\n {loading ? \\(\n
\n
Loading servers...
\n
\n \\) : \\(\n
\n {servers.map\\(\\(server, index\\) => \\(\n \n navigate\\('/server/' + server.id\\)}\n />\n
\n \\)\\)}\n
\n \\)}\n \n\n {/* Modals */}\n {showSettings && \\(\n setShowSettings\\(false\\)} />\n \\)}\n\n {showUserMgmt && \\(\n setShowUserMgmt\\(false\\)} />\n \\)}\n \n \\)\n}\n\"\"@\n$content | Out-File -FilePath \"\"Dashboard.jsx\"\" -Encoding UTF8\n'@)", "Bash(git add:*)" ] } diff --git a/ZeasyWG-Alex.conf b/ZeasyWG-Alex.conf new file mode 100644 index 0000000..028fdaa --- /dev/null +++ b/ZeasyWG-Alex.conf @@ -0,0 +1,10 @@ +[Interface] +PrivateKey = WC2HEXQ10sm5sE9Apj5wBBv2/CSRmpQNUovT1xSO8kM= +Address = 10.0.200.201/32 +DNS = 10.0.0.1 + +[Peer] +PublicKey = wgP8/WMTzhr45bH2GFabcYUhycCLF/pPczghWgLJN0Q= +Endpoint = beeck.zeasy.dev:47199 +AllowedIPs = 10.0.0.0/16 +PersistentKeepalive = 60 \ No newline at end of file diff --git a/gsm-backend/services/rcon.js b/gsm-backend/services/rcon.js new file mode 100644 index 0000000..c86b886 --- /dev/null +++ b/gsm-backend/services/rcon.js @@ -0,0 +1,159 @@ +import { Rcon } from 'rcon-client'; + +const rconConnections = new Map(); +const playerCache = new Map(); +const CACHE_TTL = 30000; // 30 seconds + +async function getConnection(server) { + const key = `${server.host}:${server.rconPort}`; + + if (rconConnections.has(key)) { + const conn = rconConnections.get(key); + if (conn.authenticated) { + return conn; + } + rconConnections.delete(key); + } + + const rcon = await Rcon.connect({ + host: server.host, + port: server.rconPort, + password: server.rconPassword, + timeout: 5000 + }); + + rcon.on('error', (err) => { + console.error(`RCON error for ${key}:`, err.message); + rconConnections.delete(key); + }); + + rcon.on('end', () => { + rconConnections.delete(key); + }); + + // Handle socket errors to prevent crash + if (rcon.socket) { + rcon.socket.on('error', (err) => { + console.error(`RCON socket error for ${key}:`, err.message); + rconConnections.delete(key); + }); + } + + rconConnections.set(key, rcon); + return rcon; +} + +export async function sendRconCommand(server, command) { + if (!server.rconPassword) { + throw new Error('RCON password not configured'); + } + + const rcon = await getConnection(server); + return await rcon.send(command); +} + +export async function getPlayers(server) { + const cacheKey = `${server.id}-count`; + const cached = playerCache.get(cacheKey); + + if (cached && Date.now() - cached.time < CACHE_TTL) { + return cached.data; + } + + try { + let result = { online: 0, max: null }; + + if (server.type === 'minecraft') { + const response = await sendRconCommand(server, 'list'); + const match = response.match(/There are (\d+) of a max of (\d+) players online/); + if (match) { + result = { online: parseInt(match[1]), max: parseInt(match[2]) }; + } + } else if (server.type === 'factorio') { + const response = await sendRconCommand(server, '/players online count'); + const match = response.match(/(\d+)/); + if (match) { + result = { online: parseInt(match[1]), max: null }; + } + } else if (server.type === 'zomboid') { + const response = await sendRconCommand(server, 'players'); + // Format: "Players connected (X):" or "Players connected (X): Player1, Player2" + const match = response.match(/Players connected \((\d+)\)/); + if (match) { + result = { online: parseInt(match[1]), max: null }; + } + } else if (server.type === 'vrising') { + const response = await sendRconCommand(server, 'listusers'); + // Count lines that contain player info + const lines = response.split('\n').filter(l => l.trim() && !l.includes('listusers')); + result = { online: lines.length, max: null }; + } + + playerCache.set(cacheKey, { data: result, time: Date.now() }); + return result; + } catch (err) { + console.error(`Failed to get players for ${server.id}:`, err.message); + // Clear cache on error - server might be offline + playerCache.delete(cacheKey); + return { online: 0, max: null }; + } +} + +export async function getPlayerList(server) { + const cacheKey = `${server.id}-list`; + const cached = playerCache.get(cacheKey); + + if (cached && Date.now() - cached.time < CACHE_TTL) { + return cached.data; + } + + try { + let players = []; + + if (server.type === 'minecraft') { + const response = await sendRconCommand(server, 'list'); + // Format: "There are X of a max of Y players online: Player1, Player2, Player3" + const colonIndex = response.indexOf(':'); + if (colonIndex !== -1) { + const playerPart = response.substring(colonIndex + 1).trim(); + if (playerPart) { + players = playerPart.split(',').map(p => p.trim()).filter(p => p); + } + } + } else if (server.type === 'factorio') { + const response = await sendRconCommand(server, '/players online'); + // Format: "Online players (X):\n player1 (online)\n player2 (online)" + const lines = response.split('\n'); + for (const line of lines) { + const match = line.match(/^\s*(\S+)\s*\(online\)/); + if (match) { + players.push(match[1]); + } + } + } else if (server.type === 'zomboid') { + const response = await sendRconCommand(server, 'players'); + // Format: "Players connected (X):" or "Players connected (X): Player1, Player2" + const colonIndex = response.indexOf(':'); + if (colonIndex !== -1) { + const playerPart = response.substring(colonIndex + 1).trim(); + if (playerPart) { + players = playerPart.split(',').map(p => p.trim()).filter(p => p); + } + } + } else if (server.type === 'vrising') { + const response = await sendRconCommand(server, 'listusers'); + // Parse player names from listusers output + const lines = response.split('\n').filter(l => l.trim() && !l.includes('listusers')); + players = lines.map(l => l.trim()).filter(p => p); + } + + const result = { players }; + playerCache.set(cacheKey, { data: result, time: Date.now() }); + return result; + } catch (err) { + console.error(`Failed to get player list for ${server.id}:`, err.message); + // Clear cache on error - server might be offline + playerCache.delete(cacheKey); + return { players: [] }; + } +} diff --git a/gsm-backend/services/ssh.js b/gsm-backend/services/ssh.js new file mode 100644 index 0000000..8edbe96 --- /dev/null +++ b/gsm-backend/services/ssh.js @@ -0,0 +1,286 @@ +import { NodeSSH } from 'node-ssh'; +import { readFileSync } from 'fs'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const sshConnections = new Map(); + +function loadConfig() { + return JSON.parse(readFileSync(join(__dirname, '..', 'config.json'), 'utf-8')); +} + +async function getConnection(host, username = "root") { + if (sshConnections.has(username + "@" + host)) { + const conn = sshConnections.get(username + "@" + host); + if (conn.isConnected()) return conn; + } + + const ssh = new NodeSSH(); + await ssh.connect({ + host, + username: username, + privateKeyPath: '/root/.ssh/id_ed25519' + }); + + sshConnections.set(username + "@" + host, ssh); + return ssh; +} + +// Returns: "online", "starting", "stopping", "offline" +export async function getServerStatus(server) { + try { + const ssh = await getConnection(server.host, server.sshUser); + + if (server.runtime === 'docker') { + const result = await ssh.execCommand(`docker inspect --format='{{.State.Status}}' ${server.containerName} 2>/dev/null`); + const status = result.stdout.trim(); + if (status === 'running') return 'online'; + if (status === 'restarting' || status === 'created') return 'starting'; + if (status === 'removing' || status === 'paused') return 'stopping'; + return 'offline'; + } else if (server.runtime === 'systemd') { + const result = await ssh.execCommand(`systemctl is-active ${server.serviceName}`); + const status = result.stdout.trim(); + if (status === 'active') return 'online'; + if (status === 'activating' || status === 'reloading') return 'starting'; + if (status === 'deactivating') return 'stopping'; + return 'offline'; + } else { + const result = await ssh.execCommand(`screen -ls | grep -E "\\.${server.screenName}[[:space:]]"`); + if (result.code === 0) { + const uptimeResult = await ssh.execCommand(`ps -o etimes= -p $(screen -ls | grep "\\.${server.screenName}" | awk '{print $1}' | cut -d. -f1) 2>/dev/null | head -1`); + const uptime = parseInt(uptimeResult.stdout.trim()) || 999; + if (uptime < 60) return 'starting'; + return 'online'; + } + return 'offline'; + } + } catch (err) { + console.error(`Failed to get status for ${server.id}:`, err.message); + return 'offline'; + } +} + +export async function startServer(server, options = {}) { + const ssh = await getConnection(server.host, server.sshUser); + + if (server.runtime === 'docker') { + // Factorio with specific save + if (server.type === 'factorio' && options.save) { + const saveName = options.save.endsWith('.zip') ? options.save.replace('.zip', '') : options.save; + // Stop and remove existing container + await ssh.execCommand(`docker stop ${server.containerName} 2>/dev/null || true`); + await ssh.execCommand(`docker rm ${server.containerName} 2>/dev/null || true`); + // Start with specific save + await ssh.execCommand(` + docker run -d \ + --name ${server.containerName} \ + -p 34197:34197/udp \ + -p 27015:27015/tcp \ + -v /srv/docker/factorio/data:/factorio \ + -e SAVE_NAME=${saveName} -e LOAD_LATEST_SAVE=false \ + -e TZ=Europe/Berlin \ + -e PUID=845 \ + -e PGID=845 \ + --restart=unless-stopped \ + factoriotools/factorio + `); + } else { + await ssh.execCommand(`docker start ${server.containerName}`); + } + } else if (server.runtime === 'systemd') { + await ssh.execCommand(`systemctl start ${server.serviceName}`); + } else { + await ssh.execCommand(`screen -S ${server.screenName} -X quit 2>/dev/null`); + await new Promise(resolve => setTimeout(resolve, 1000)); + await ssh.execCommand(`cd ${server.workDir} && screen -dmS ${server.screenName} ${server.startCmd}`); + } +} + +export async function stopServer(server) { + const ssh = await getConnection(server.host, server.sshUser); + + if (server.runtime === 'docker') { + await ssh.execCommand(`docker stop ${server.containerName}`); + } else if (server.runtime === 'systemd') { + await ssh.execCommand(`systemctl stop ${server.serviceName}`); + } else { + // Different stop commands per server type + const stopCmd = server.type === 'zomboid' ? 'quit' : 'stop'; + await ssh.execCommand(`screen -S ${server.screenName} -p 0 -X stuff '${stopCmd}\n'`); + + for (let i = 0; i < 30; i++) { + await new Promise(resolve => setTimeout(resolve, 2000)); + const check = await ssh.execCommand(`screen -ls | grep -E "\\.${server.screenName}[[:space:]]"`); + if (check.code !== 0) { + console.log(`Server ${server.id} stopped after ${(i + 1) * 2} seconds`); + return; + } + } + + console.log(`Force killing ${server.id} after timeout`); + await ssh.execCommand(`screen -S ${server.screenName} -X quit`); + await new Promise(resolve => setTimeout(resolve, 2000)); + } +} + +export async function restartServer(server) { + await stopServer(server); + await new Promise(resolve => setTimeout(resolve, 2000)); + await startServer(server); +} + +export async function getConsoleLog(server, lines = 50) { + const ssh = await getConnection(server.host, server.sshUser); + + if (server.runtime === 'docker') { + const result = await ssh.execCommand(`/usr/local/bin/docker-logs-tz ${server.containerName} ${lines}`); + return result.stdout || result.stderr; + } else if (server.runtime === 'systemd') { + const result = await ssh.execCommand(`tail -n ${lines} ${server.workDir}/logs/VRisingServer.log 2>/dev/null || journalctl -u ${server.serviceName} -n ${lines} --no-pager`); + return result.stdout || result.stderr; + } else if (server.logFile) { + const result = await ssh.execCommand(`tail -n ${lines} ${server.logFile} 2>/dev/null || echo No log file found`); + return result.stdout; + } else { + const result = await ssh.execCommand(`tail -n ${lines} ${server.workDir}/logs/latest.log 2>/dev/null || echo No log file found`); + return result.stdout; + } +} + +export async function getProcessUptime(server) { + try { + const ssh = await getConnection(server.host, server.sshUser); + + if (server.runtime === "docker") { + const result = await ssh.execCommand(`docker inspect --format="{{.State.StartedAt}}" ${server.containerName} 2>/dev/null`); + if (result.stdout.trim()) { + const startTime = new Date(result.stdout.trim()); + if (!isNaN(startTime.getTime())) { + return Math.floor((Date.now() - startTime.getTime()) / 1000); + } + } + } else if (server.runtime === "systemd") { + const result = await ssh.execCommand(`systemctl show ${server.serviceName} --property=ActiveEnterTimestamp | cut -d= -f2 | xargs -I{} date -d "{}" +%s 2>/dev/null`); + const startEpoch = parseInt(result.stdout.trim()); + if (!isNaN(startEpoch)) { + return Math.floor(Date.now() / 1000) - startEpoch; + } + } else { + const result = await ssh.execCommand(`ps -o etimes= -p $(pgrep -f "SCREEN.*${server.screenName}") 2>/dev/null | head -1`); + const uptime = parseInt(result.stdout.trim()); + if (!isNaN(uptime)) return uptime; + } + return 0; + } catch (err) { + console.error(`Failed to get uptime for ${server.id}:`, err.message); + return 0; + } +} + +// ============ FACTORIO-SPECIFIC FUNCTIONS ============ + +export async function listFactorioSaves(server) { + const ssh = await getConnection(server.host, server.sshUser); + const result = await ssh.execCommand('ls -la /srv/docker/factorio/data/saves/*.zip 2>/dev/null'); + + if (result.code !== 0 || !result.stdout.trim()) { + return []; + } + + const saves = []; + const lines = result.stdout.trim().split('\n'); + + for (const line of lines) { + const match = line.match(/(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+\s+\d+\s+[\d:]+)\s+(.+)$/); + if (match) { + const filename = match[7].split('/').pop(); + // Skip autosaves + if (filename.startsWith('_autosave')) continue; + + const size = match[5]; + const modified = match[6]; + + saves.push({ + name: filename.replace('.zip', ''), + filename, + size, + modified + }); + } + } + + return saves; +} + +export async function deleteFactorioSave(server, saveName) { + const ssh = await getConnection(server.host, server.sshUser); + const filename = saveName.endsWith('.zip') ? saveName : `${saveName}.zip`; + + // Security check - prevent path traversal + if (filename.includes('/') || filename.includes('..')) { + throw new Error('Invalid save name'); + } + + const result = await ssh.execCommand(`rm /srv/docker/factorio/data/saves/${filename}`); + if (result.code !== 0) { + throw new Error(result.stderr || 'Failed to delete save'); + } +} + +export async function createFactorioWorld(server, saveName, settings) { + const ssh = await getConnection(server.host, server.sshUser); + + // Security check + if (saveName.includes("/") || saveName.includes("..") || saveName.includes(" ")) { + throw new Error("Invalid save name - no spaces or special characters allowed"); + } + + // Write map-gen-settings.json + const settingsJson = JSON.stringify(settings, null, 2); + await ssh.execCommand(`cat > /srv/docker/factorio/data/config/map-gen-settings.json << 'SETTINGSEOF' +${settingsJson} +SETTINGSEOF`); + + // Create new world using --create flag (correct method) + console.log(`Creating new Factorio world: ${saveName}`); + const createResult = await ssh.execCommand(` + docker run --rm \ + -v /srv/docker/factorio/data:/factorio \ + factoriotools/factorio \ + /opt/factorio/bin/x64/factorio \ + --create /factorio/saves/${saveName}.zip \ + --map-gen-settings /factorio/config/map-gen-settings.json + `, { execOptions: { timeout: 120000 } }); + + console.log("World creation output:", createResult.stdout, createResult.stderr); + + // Verify save was created + const checkResult = await ssh.execCommand(`ls /srv/docker/factorio/data/saves/${saveName}.zip`); + if (checkResult.code !== 0) { + throw new Error("World creation failed - save file not found. Output: " + createResult.stderr); + } + + return true; +} + +export async function getFactorioCurrentSave(server) { + const ssh = await getConnection(server.host, server.sshUser); + + // Check if container has SAVE_NAME env set + const envResult = await ssh.execCommand(`docker inspect --format="{{range .Config.Env}}{{println .}}{{end}}" ${server.containerName} 2>/dev/null | grep "^SAVE_NAME="`); + if (envResult.stdout.trim()) { + const saveName = envResult.stdout.trim().replace("SAVE_NAME=", ""); + return { save: saveName, source: "configured" }; + } + + // Otherwise find newest save file + const result = await ssh.execCommand("ls -t /srv/docker/factorio/data/saves/*.zip 2>/dev/null | head -1"); + if (result.stdout.trim()) { + const filename = result.stdout.trim().split("/").pop().replace(".zip", ""); + return { save: filename, source: "newest" }; + } + + return { save: null, source: null }; +} diff --git a/gsm-frontend/public/zomboid.png b/gsm-frontend/public/zomboid.png new file mode 100644 index 0000000000000000000000000000000000000000..67d2e8a38c36dec9194de8a2d6b7e0c5be1726fb GIT binary patch literal 71130 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ΠsTn6a3Hr_1oP4;6vnD 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

l*q9-&AIE?A>Q-A39K<(y|KzCKmCVXy?(uf z_lC+KgdxjPjMlW<9gZE`OBjR*EUh@@4_hoE+tk6vW`wWyPQ!FRYs9s?6udiXdxUTr%rqRIfAH) ziahB`S-Z0(`m)Rp3jro2Z(bu9nkCVSpZUbQc=v-xeNV!U>v-sg1 zngi$`eWAds*4n>@(-bIz$Zh!m&GDm$_@y6vKmYBY{2z1@&EW%++<*6ekN9&h{_0lF z!g`lI<5i|d6$pbA7GpFb2+4v9xvWyF=FE=HlBL6}ZMIn3XtUMHS#QIuLJ$RlMwBvM zlT-r%#<0@0G*j5RvCgmltIzOV?|qc1@e!)k2qh&!5KyZ`AOyKK+*n>`b+d&DxwdSzx+H1N=y07YHmm>SvdpH2w}D^SnViGvW`wR z2*Q!_m_qC#1&%Wg;plPJ7XJfto68K1&rus1r%{KE)^%zVcMd2;S!)P_8e!NVO*be* z@gh#I&9QamY3h3)Le;J|dmP}RwQFRV0KUTe~7 zcWJg`a-%VZlDb<+QBd_c41SuSlw$wfH2Y?!n3@=+QLB<`O{>!-O*1ZCU*OWU#l9Cz zpp7BR^HNK`*H&wZLU+q$|Gpp6i)k3EUDsPU0+k7@tfNH*71mHH1l_@`J_}isKq*pf zIWQvl_z_7cJz>f=O0R-C%w5a&9{+9gZ(Vobt84IutBy&hI`t5h$%h=sso?sT%6lV} z$xbO0LRINp{~U9B>l}ITqe!V(xptaYpM8<~>=UJEN_lBQkY#P!&1>Gj_X$^1Je!VRf^`s~4}MjU@!XtB%_Z<+V`Aus59tVKsmXd9LEwh=zXI`_cb~2*s z1daV4MU-BES5dMSFg?ydA&{zqj@QvEpX1bfKS6DD2CU(mU;YEy*(|k*6RxDXjd{4; zyhfIHeDjWQosc%ibQY;cIU`5kL%Ovooncj9lk*;}mUmxoc}Lv=LR*02zg`mb?HW6$!V#E{Fd2 zPow;z76=T1EM%)KBAOr=J>~QVOKP9IXD~$IT-?rlxtOi*0eF7Na9~(*bQEIE4h@AO z#xA%6Z{`IQzQa%ma}8MO1ej!vynUT$e5?dI6$`$fn8jMxX&Ka}N#qO*=bz!|!yiKi zA#;cB;Pi9fq%v|4A*&2F=qSQyh%9gSO`vp`j>;Hu>orD`{3ERn>NqUGQbL1=AMwAV#CPoegiwCm*M{ZwRh%0 zc3t(I|D1D|w{^eX)auqQS&J>%l5OPO-~|jeu}zF|W(F7t2`0&87$B*FgqoQu2!Vo3 zsxnllnh-LOsUbrW5<|fZfq=teFa#Srw!xMqYu}|_U%%}x=bZWDocrGEMZ#u_CEah8 zO1->y`@Vbc?{|LZw|u{I2QZEO2o-_w?99LGr%#RGn}-$4${rQp@+K_KyVEg>vpE6W zxvr}Uc265rYKUfc5mYzX2!t#dneD%Nn&^bUn3Tfs4o)2S0wZfLWpHc*qieQs;=o=S zGy5rxT(Y3Az~K88JQ-SrBS*W2khU32mS`OLK9zMZ!|xlTuX-otb+-_me3JH&hiN?V zaVEqtSKo0z?|sLAqOTmB$@qJde(%nv{LZy~e=p7{2LPR^L0<^*lriS5z~|ks<|PHe zIRT@&@y@rRmEoU0_m@-#s{~=s!|PVHlQRhiPq$GT`bs`4hC_rj2<@f>MZm;Vjotf? zf-zM4`>9kaHgTl2WhZi6bpo4tJ71+5#vZbmC zgaMcIGi0sF!ZGx?ffQkdzl>BwBIXCclP$nVRO=+7yvE9AWz9Ci0~KTDo`vR-ET(Yd zuSHBah-ppW>xhY#;K75MyDqY1D9S9oN9=V1EKwcEnxn?bFj9o$bsyQDK(&tJ_g&<+ zBb`_G+&b4Yp;NrlC=}LksRrDx*Gcs&A>^W48fo`dt^HPD$nt0BMg`S-i@c9^%sC*fmU@I?m*gU0`5f`Dz9R zO2qZ!9hTmnJ53l4^mJWjg9`%TmrY2m%ok=eC)3oT(+Vk zPP+iT8UNS02-G{*jUUcY2LK%>;0YoA4ETMzoq|g^0jmOY)7@|3+Sj}bm8J+|*>men z&E8{8BFF!~dbG&sV9+7QRLWqC$Fuv7GBGtve_w^sWuuge1*DL+%gz0b%n8JCM59r+ z4kV57HoY9V?m7s{m^8+uF>#zQ(^Rw)NgAg#V(s*~dIyeO*xh4@Hfbo40un74D8LnC z9uk9)J|;SaX&kh4S4kZ+^FxSEyZQSf-tgrVMz5tXu!%xx9N#Y?$}16-wYC|@6YQGQ zd}+TvvlB3jzswSs^Byh%Yx*I~ghN^<(Mi42vp*{}i62x5i$gu9=fqJ&fGCa8IQ1OuV~;wv7e&1lbKQ=0y!%&Q zN?*bDlE|g^{1P|~{2L*}o^v_rw-D#718~TC1U>?Mh%>Yc<9OYT(OmWNf5-K&eGgg$ zw3-ozPQ^6ZmS=u^xX7x}&`!UBW2W^vbbNw+hfh$clo=lzqgb@<#8IMp9(udy)JoBu znHU4ysw8i<=%X=@*n2I|<8C?uB*B=mdAW$Nvt>v;mtv!TD$@RLYsf9Ct$ zJ+s6Rr-qF~f=wg7Ex}0-Gq)d8+l{V03(1Tn`V5CCthkxt@THwW8qW*xf)Y}AP+AGW z07ff{KJ1+~e0jg_Bm@_Y@+|m4#}RbJ1xPiFmIY&FP{xC#M%p?-x3^$kzhmb}+G#l& z6;oJtCAG;Zjy?O&v|CNWLWxQ#WqRM|Napszyhy+h1pOV=rQZ7@!U0kf&^-EmgpO#o zVs5|T65jcynQM+*W^>)9$~qqaT_^)Dc&WowjsF?b}9El^sy&VwG&iJA)5xeiZmTX9)UCdOf;lh_%m*}r@P_` zJTy11@fj&u`7nvX%lR}lRhkba24s{Zbt zHhk-_CQ;peeRd*Q{eEFGt{2&e@RlLVF{pJ)GI!94X=aaJZ}XAO=o{mRdA%X((O+uQWpK@MBEv`ZBM- z>ss!=?|L_aZO*pxH?VvDFQ0G559hc8fO$?p3L$=n`A%T*c0fvuR&3sJBX4>4M_I9E zBW16}rqv~@_ux7PqtvGz3!dJ4m?%+l!!RLAkR z-r1d~acV_0C((x=L5YC1RTwThL6V*hH=Q-xEKsr0u2k8}kdjT|4FsB(tPDD`q|I1Vd$rI3CFX%f(pa}ENHpyhpF z7S~npaTzM6Y)+fF7Ec7|-f1nOAhf8cyHemD;t0LNdFI3@eFht{ccf(j7~ zQe1WglSg)O^1xFZ+xsN@pL&>LQKH+&s2_O{)t>A)0pBm;`(bu6_(4d$-ehd$dj9w? zzQ}9-?fq`zQ!aJ>S+;)~c%u;FZ_iibhx5b%KzDG+MFRf?cyG_=TRcWAEp^wj(J}7* ztq*bWjjyNOYSC(?Ad2iibc{nsPcS?*OkZC=o+n9CO|zYJT4Rkdcupp2X6iJJxj8FF za_KK4`o}xs61LkW#LOOnlTRW1kPTJMnm$)*b0NQV1mMY@9Kczac;@k+Akb;breR4X zlqjtr97gu7M+y&#*GW`n3Ph=D6P zx@mq48EbY=y8kX3gcW7bA|PoTM72(K47R&-$b7c_aNQ^%CLG#E6#G2>;NS6`Fa9O% zMvWVO>GdpMxt6qfoMdjV>nkuw8Q^&(JkLi;!R%~}D_(L1fAY~kV&}EjovF+RUE@Cv z+$)6m!uf3ca1QnXoDHr~D4QGjurX#9_!wPj=6P+y(#d&+g(5G%@12aVyOf6>_%oh( z;&JvJpJsSyh=GABz9-Sf&}hYW0LAm{;ET2`r_+-sX|~!3o#I{mD&(?@AZc~NTpT^Q zIrKe*N+=deTsmwBq%Ac{*);+|XLP>HV3X3008QLcwMmLWky#S0x_!WZcCIp&zb`O{s*|)(OR&5XMG~uyRKKo{2O}}A%RWMu< zlswx_*s19Etp3^dzUT{9RSc)w9y*DLrk|u-903tv>;JN+GcaopRv2R)m-PGas~gc1 zlVAkvwqC_W+pZ>RHaK-;FKK;0acPX8Zygv-7?x?b>%?))8(x1eZ+qLnr&uglg=Xoo z1hVI24{)Cl;>q*f_~ATr0MLm9l)HKV!x(cEt3Eu+5(7ltW){P?t8ZiFhD$mAf!}5C z(JxV;$i6ML; zNvEEmxbo%5mA4^l`$(HdG0kJdNrNM`fMd;oa7Iw^4b>1zzHwAzhS^9`Pa*IObCKv_ z;7N(5pTR4ywhBg_uD)#QFe7=*hK5qS;&Qyo8ch93;+EB92*M)Uu6YHgk35HorZ8I3 zXtmk7b1T2~oAV){Zcepq6X?tH@g-~ay3?7;ip_rA{o zR{$HaPGAwsmFSGC!lrFkuzlxCIk0y(2lnnJ2m-48B?3?2Nso5B$>iiDI*kzJVS>x= z>n8m=Sv&)N5q0?cn4^#37m6$^39cR!6hle56u3RP!Sf`3Af=t(l5|H!^#tW3U1on429U5#bnw5W~<@OVpDTLxAUU@faPZ`#;h!8>IwzP*OPr zhIQ`$VHe3rG2+TDO(w>scIBLYrJ`$9*AQ_^)^RFhx6INsCIdgG6@YC^YZq4=5BZ8@ zyXffGDd^Yop%S@_qn=9UeYep})Y?35qN!+lV`p7KW9ipswpMlX`vbnfJs@CKqd~4~ zS8smQN+1?WvC{$3N5tnR(V{FXN8y{@#UQOhPsy4GIedR2ry} z52U1|_`Csj33UhhLX;mHOL7_`!Lmx_nV_}itM2i|% zlDfsS7fZ)`cWFAJjbj|`Oas+TOtYbRU8(1{SrSj{AB_0yp(Z$d+v({wNeMZ-yt|Qr2mQ4koZPFP_h?)3Y^HB;U%w7j@|C=E zTUO1!a(&*=Vc1^j+GZ$(O&0yJqNVa}dtPE~!Z!xoE=?f#jXTvOxm`P8ubgnC=h`aj zrF)byQ)BM~i=obo|JonF;1&=}2V%}CNmadP z|0|mUn&KEv7VgPN0bI!&FtEU{HI~&EWv{!onX)EEyeTK)JShSrqsa$e7#*w%Gjpb4 zp&HBd2r5`}ew+{0D4c9J?n-F(XqOwB#`M)+O)l144NFw$&ss$3oo*=lDX`n4?$^y& z0Ko`IxCQUCcW@hZL>-Wu11;2qj$gWEOd{{Vk0^dPZbi^B(Dxz@Xz zN-}oVQR_r)8mU23j3hJ@KM-}adti7Qw-m{n70`R8nnI5S%gXt4E@`z6WUp^2u_}42 zCKwOD&aT6#w}GqTl59O<6#`&ktM2S-AVPB|R--EHUGYR0FTC!y}}WR%`5g`U9z$Z?~h0{(W(yW3YpqQT2aPiS&zo}3T9o# zU8*(LL>uCLn}W2NDOivmKb6T-jQkID8EjgO$26fF@&&>xOg!R$axMK0YkvN^tr@L9Bdc-TxkYs{TcI%6X`rZ+ z>DJGqa{n?U?o*nW{I@6N=DLUdi*OcsCP=+{j0t%H{W4M&Ev+Bi+anagtwlH1<-aLh zoH$2|GC@sj?YgX&oKx^63TPQ;7M=W>CmpA~M>`BwQ`*l}$V^tV0S?qw|9Rv?ZzZ1_ z-d~a(mGX9=7-fDRumHT^N%JR!cL$wETy&t5Sdp>is@ze~uk4QUe*v?8?~0fkJ&s|% z>!g3Wc>P}#-0}2-UVHo6krlJ38AVMg?oADrQfeH!a|l+e~89MRND`-CO`Sv=9&DoSlBfuVho{8TjTVi^hFtZ*bz?o@E{PRd3s(%2(Cutaq}OCOr_os$4iJPHyP zZA~>psZ6}CblczxSh{@ky7fkYn0SBLM0@t(J}MsPV1Bbq4}%`w-V*({7OYsG{|4`6 zR3^M?G~yww5AB9*UEKsOwTGleUTSr4TzcT!v^W!f@|uCVIfQaS^2T*s5lNKjc% zQ?D@U1i~jpy*%LoRJM&fyT?GpJGwT+@6HL?YIjm@F!BXC1>d(0M!IAJdF=&oep}~< z`WD251-!+2`Yl@pD+@5>U$*1x-FQAxR>zo7@#K4yhGJ4x}4ZYrc=Bmbw-b<)^J&jAt@;m2v>3^s})VX z-CoYpGAh*B?=KB!Ep=Y&phJP<=6!gsa68G_R}`@#FvO75yX^)UGO0xVzswMs-vuGZ z9xG-VidP)_wRr9|`5MMu&2<{X)_8jm&*-6fajod{z5p#Mr5Jc1G5ZjA)5w@VFj0l~ zVfwr0CNah{w06jo7-fJWyfQ11TLM-j9c@gkr1=e9LrO?Z*p)V^tVX8h@L%}0FfB1b zEd@D7_w9rryg)AOfuxX${FUBrDZCI`72EDt$J3lXG|jK)3ytSIPwPQ3ea$!BXKUk8 zoZ{!U2}Vr`YxCbrXraOuDE}wZix>K@n|HKpUx#aLUHmTkTIs7O-MD{v=ZP226@!%< zp-umja<|{d8-dD)I6hBiCSE$cP5J#vWQSDlUI|oKY0hDT-WOE3QWz^*R-L4BOqcE; z_joJ%Wns_touA;^OsxF!92~#cWpFZK?s2tbwKZ+Z)A?PhT9ZP}0@EIu=b9#}yQkEj zQCGY8L*u2qJ<`oA(a*0&AVSohj$T80efbn~Zv^2XR~58&30l@_DO!KADS@$$+OY-t zuilg#6D|q~NjW%yUBA23@QNpCcfah#ysN+5+^gTr0ev&=pM7$a%$uk34!j;ZD7tIX z7EY*mP@-aM#SgjOieA8nQj2&2SR(@PJ&CHY|H@PIJMV>vA_)44zbHg62e-nBOo@M& z^dKo(o#fCfmAP*_a7m)7r-09GJom?Jf7Ix`{2U)};1BzeMWb%}a{@fcm-v*hSoydJ z*lCX8kLeC;%75R{M*Mx^pLQp&@Idh<9C%C<4B2Zp$Ocqk~O6Tq!nYqY|2`7KUjjBI{^ae8Oa5y~74vvR%!^nGXC%m>@%5J0&5gRtPj~Ln{Q&g1>-Q)B{W*$nw9C7H3*$mHRAQh`f6a!`^?Yz%Rq=BLk(}QwaeZoN5&QSUrPx53r_*3GP8>Q@<+fd4qsvmLCluT zpZ!?HHpf6eo!z_L`a>t*<|)gMc5ai!L>HjWzkJ+Y6+tX7U|EG178b7UjFv-2YjJ`8@%_j98@*x~K+_~Oba z*ImX?WRf!^jgrzE>#Ah}A%W08)6`5YVO#}r^--gb157xq-m7uP@=@>+Zphu-GE7E> z#XaqreS7I#ZrPev8O~f~VH<+!83Zlf0G`?tw!}Q%2}Q;qNkFdiB;Jr5OLcT0BXP5n zZLE>g1h0baZV7Vn2j9_1Zp=$H8}nx)V)c1_O*Pbg%FHO)Ck+o|tu9!+e~IxNNIW?yXI5*Om#(xV7F0G(osn z%*?nCyHDqxEO!8&l2dN`JqX1d{~tjGF>&xxInGdy3&-K9ee7*$TJk!Q!%_nwwiWL$)e2*T$RmgLXpdyeR1c{ZqI!0V-e*pK7*d1k$i+%<9~&~3Jcc?HvSlXkX?1DSPcIKh{`V1}8;F$s(zB_YM(Y4`ON zqhoRc^dyv*`mb`#AhSCyN-OCbZ7rzXNz4ej4Y9KDXMjO`lE}rG)TYI@_Yuv_I4sdt z?gi-<1yl@nUj=nCM)vbfstIO0M!Zmqxb>PgVh18=W2P>-6vuyl4}=KS0~P=-gnH%P zXb1B%SCNci1~b7UaW8-jAM8^Js> zG*PO26+&72(mDS1>HCEhw8DKzwqAnX5nM$^c`!GB4g9ao5o+`cc7XB*dcTQ8Y0hPm rq { if (name.includes('minecraft') || name.includes('all the mods')) return serverInfo.minecraft if (name.includes('factorio')) return serverInfo.factorio if (name.includes('vrising') || name.includes('v rising')) return serverInfo.vrising + if (name.includes('zomboid')) return serverInfo.zomboid return null } @@ -127,6 +136,13 @@ export default function ServerCard({ server, onClick, isAuthenticated }) { )} + {/* Project Zomboid notice - only for authenticated users */} + {isAuthenticated && server.type === 'zomboid' && ( +

+ Version 42.13 +
+ )} + {/* Metrics */}
{/* CPU */} diff --git a/gsm-frontend/src/pages/ServerDetail.jsx b/gsm-frontend/src/pages/ServerDetail.jsx index 785644c..ce5709f 100644 --- a/gsm-frontend/src/pages/ServerDetail.jsx +++ b/gsm-frontend/src/pages/ServerDetail.jsx @@ -10,6 +10,7 @@ const getServerLogo = (serverName) => { if (name.includes("minecraft") || name.includes("all the mods")) return "/minecraft.png" if (name.includes("factorio")) return "/factorio.png" if (name.includes("vrising") || name.includes("v rising")) return "/vrising.png" + if (name.includes("zomboid")) return "/zomboid.png" return null } export default function ServerDetail() { @@ -28,6 +29,7 @@ export default function ServerDetail() { const [whitelistInput, setWhitelistInput] = useState('') const [whitelistLoading, setWhitelistLoading] = useState(false) const [currentSave, setCurrentSave] = useState(null) + const [logsUpdated, setLogsUpdated] = useState(null) const logsRef = useRef(null) const rconRef = useRef(null) @@ -62,7 +64,10 @@ export default function ServerDetail() { useEffect(() => { fetchServer() fetchCurrentSave() - const interval = setInterval(fetchServer, 10000) + const interval = setInterval(() => { + fetchServer() + fetchCurrentSave() + }, 10000) return () => clearInterval(interval) }, [token, serverId]) @@ -97,8 +102,9 @@ export default function ServerDetail() { const fetchLogs = async () => { try { - const data = await getServerLogs(token, server.id, 20) + const data = await getServerLogs(token, server.id, 50) setLogs(data.logs || '') + setLogsUpdated(new Date()) if (logsRef.current) { logsRef.current.scrollTop = logsRef.current.scrollHeight } @@ -299,6 +305,15 @@ const formatUptime = (seconds) => {
CPU Cores
{server.metrics.cpuCores}
+ {server.type === 'factorio' && currentSave?.save && ( +
+
{server.running ? 'Current World' : 'Next World'}
+
{currentSave.save}
+ {!server.running && currentSave.source === 'newest' && ( +
newest save
+ )} +
+ )} {/* Players List */} @@ -318,14 +333,6 @@ const formatUptime = (seconds) => {

Server Controls

- {/* Factorio: Show which save will be loaded */} - {server.type === 'factorio' && !server.running && currentSave?.save && ( -
- Will load: {currentSave.save} - {currentSave.source === 'newest' && (newest save)} -
- )} -
{(server.status === 'online' || (server.running && server.status !== 'starting' && server.status !== 'stopping')) ? ( <> @@ -369,7 +376,14 @@ const formatUptime = (seconds) => {
{/* Logs */}
- Server Logs (last 20 lines) +
+ Server Logs (last 50 lines) + {logsUpdated && ( + + Aktualisiert: {logsUpdated.toLocaleTimeString('de-DE')} + + )} +
From 2bb4dd723c78bdb7e6b45b3acfad7b7a9ab8144e Mon Sep 17 00:00:00 2001 From: Alexander Zielonka Date: Wed, 7 Jan 2026 02:41:37 +0100 Subject: [PATCH 2/2] ja --- ActivityLog.jsx | 172 ++++++ App.jsx | 156 +++++ Dashboard.jsx | 164 ++++++ UserContext.jsx | 61 ++ UserManagement.jsx | 102 ++++ ZomboidConfigEditor.jsx | 326 +++++++++++ auth.js | 211 +++++++ discord.js | 82 +++ discordBot.js | 353 ++++++++++++ gsm-backend/services/rcon.js | 12 +- gsm-frontend/src/components/ServerCard.jsx | 24 +- temp_Dashboard.jsx | 168 ++++++ temp_ServerCard.jsx | 208 +++++++ temp_ServerDetail.jsx | 640 +++++++++++++++++++++ zomboid_funcs.js | 86 +++ 15 files changed, 2753 insertions(+), 12 deletions(-) create mode 100644 ActivityLog.jsx create mode 100644 App.jsx create mode 100644 Dashboard.jsx create mode 100644 UserContext.jsx create mode 100644 UserManagement.jsx create mode 100644 ZomboidConfigEditor.jsx create mode 100644 auth.js create mode 100644 discord.js create mode 100644 discordBot.js create mode 100644 temp_Dashboard.jsx create mode 100644 temp_ServerCard.jsx create mode 100644 temp_ServerDetail.jsx create mode 100644 zomboid_funcs.js diff --git a/ActivityLog.jsx b/ActivityLog.jsx new file mode 100644 index 0000000..8aaddd2 --- /dev/null +++ b/ActivityLog.jsx @@ -0,0 +1,172 @@ +import { useState, useEffect } from 'react' +import { useUser } from '../context/UserContext' +import { getActivityLog } from '../api' + +const actionLabels = { + server_start: 'Server gestartet', + server_stop: 'Server gestoppt', + server_restart: 'Server neugestartet', + rcon_command: 'RCON Befehl', + autoshutdown_config: 'Auto-Shutdown geändert', + zomboid_config: 'Config geändert', + factorio_world_create: 'Welt erstellt', + factorio_world_delete: 'Welt gelöscht' +} + +const actionIcons = { + server_start: '▶️', + server_stop: '⏹️', + server_restart: '🔄', + rcon_command: '💻', + autoshutdown_config: '⏱️', + zomboid_config: '📝', + factorio_world_create: '🌍', + factorio_world_delete: '🗑️' +} + +const serverLabels = { + minecraft: 'Minecraft', + factorio: 'Factorio', + zomboid: 'Project Zomboid', + vrising: 'V Rising' +} + +function getAvatarUrl(discordId, avatar) { + if (!discordId || !avatar) return null + return `https://cdn.discordapp.com/avatars/${discordId}/${avatar}.png?size=32` +} + +function getDiscordProfileUrl(discordId) { + return `https://discord.com/users/${discordId}` +} + +export default function ActivityLog({ onClose }) { + const { token } = useUser() + const [logs, setLogs] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState('') + + useEffect(() => { + const fetchLogs = async () => { + try { + const data = await getActivityLog(token, 100) + setLogs(data) + setError('') + } catch (err) { + setError('Fehler beim Laden des Activity Logs') + } finally { + setLoading(false) + } + } + fetchLogs() + }, [token]) + + const formatDate = (dateStr) => { + const date = new Date(dateStr + 'Z') + const now = new Date() + const diff = now - date + + if (diff < 60000) return 'Gerade eben' + if (diff < 3600000) return Math.floor(diff / 60000) + ' Min' + if (diff < 86400000) return Math.floor(diff / 3600000) + ' Std' + + return date.toLocaleDateString('de-DE', { + day: '2-digit', + month: '2-digit', + hour: '2-digit', + minute: '2-digit' + }) + } + + return ( +
+
e.stopPropagation()}> +
+

Activity Log

+ +
+ +
+
+
+ ) +} diff --git a/App.jsx b/App.jsx new file mode 100644 index 0000000..6991efa --- /dev/null +++ b/App.jsx @@ -0,0 +1,156 @@ +import { useState, useEffect } from 'react' +import { BrowserRouter, Routes, Route, Navigate, useSearchParams, useNavigate } from 'react-router-dom' +import { UserProvider } from './context/UserContext' +import Dashboard from './pages/Dashboard' +import ServerDetail from './pages/ServerDetail' + +// OAuth Callback Handler +function AuthCallback({ onLogin }) { + const [searchParams] = useSearchParams() + const navigate = useNavigate() + const [error, setError] = useState(null) + + useEffect(() => { + const token = searchParams.get('token') + const errorParam = searchParams.get('error') + + if (errorParam) { + const errorMessages = { + 'discord_denied': 'Discord-Anmeldung abgebrochen', + 'no_code': 'Kein Autorisierungscode erhalten', + 'not_in_guild': 'Du bist nicht Mitglied des Discord-Servers', + 'oauth_failed': 'Anmeldung fehlgeschlagen' + } + setError(errorMessages[errorParam] || 'Unbekannter Fehler') + setTimeout(() => navigate('/'), 3000) + return + } + + if (token) { + onLogin(token) + navigate('/') + } else { + navigate('/') + } + }, [searchParams, onLogin, navigate]) + + if (error) { + return ( +
+
+
{error}
+
Weiterleitung...
+
+
+ ) + } + + return ( +
+
Anmeldung wird verarbeitet...
+
+ ) +} + +// Login page for unauthenticated users +function LoginPage() { + const [searchParams] = useSearchParams() + const error = searchParams.get('error') + + const errorMessages = { + 'discord_denied': 'Discord-Anmeldung abgebrochen', + 'no_code': 'Kein Autorisierungscode erhalten', + 'not_in_guild': 'Du bist nicht Mitglied des Discord-Servers', + 'oauth_failed': 'Anmeldung fehlgeschlagen' + } + + const handleDiscordLogin = () => { + window.location.href = '/api/auth/discord' + } + + return ( +
+
+ Logo +

Gameserver Management

+

Melde dich mit Discord an, um fortzufahren

+ + {error && ( +
+ {errorMessages[error] || 'Anmeldung fehlgeschlagen'} +
+ )} + + + +

+ Nur für Mitglieder des Discord-Servers +

+
+
+ ) +} + +export default function App() { + const [token, setToken] = useState(localStorage.getItem('gsm_token')) + + const handleLogin = (newToken) => { + localStorage.setItem('gsm_token', newToken) + setToken(newToken) + } + + const handleLogout = () => { + localStorage.removeItem('gsm_token') + setToken(null) + } + + return ( + + + + + ) : ( + + ) + } + /> + + ) : ( + + ) + } + /> + } + /> + } + /> + } + /> + } /> + + + + ) +} diff --git a/Dashboard.jsx b/Dashboard.jsx new file mode 100644 index 0000000..ff2ff6c --- /dev/null +++ b/Dashboard.jsx @@ -0,0 +1,164 @@ +import { useState, useEffect } from 'react' +import { useNavigate } from 'react-router-dom' +import { getServers } from '../api' +import { useUser } from '../context/UserContext' +import ServerCard from '../components/ServerCard' +import UserManagement from '../components/UserManagement' +import ActivityLog from '../components/ActivityLog' + +export default function Dashboard({ onLogout }) { + const navigate = useNavigate() + const { user, token, loading: userLoading, isSuperadmin, role, avatarUrl } = useUser() + const [servers, setServers] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState('') + const [showUserMgmt, setShowUserMgmt] = useState(false) + const [showActivityLog, setShowActivityLog] = useState(false) + + const fetchServers = async () => { + try { + const data = await getServers(token) + setServers(data) + setError('') + } catch (err) { + if (err.message.includes('401') || err.message.includes('403')) { + onLogout() + } else { + setError('Verbindung zum Server fehlgeschlagen') + } + } finally { + setLoading(false) + } + } + + useEffect(() => { + if (!userLoading && token) { + fetchServers() + const interval = setInterval(fetchServers, 10000) + return () => clearInterval(interval) + } + }, [token, userLoading]) + + const roleLabels = { + user: 'Viewer', + moderator: 'Operator', + superadmin: 'Admin' + } + + if (userLoading) { + return ( +
+
Laden...
+
+ ) + } + + const onlineCount = servers.filter(s => s.running).length + document.title = 'Dashboard | Zeasy GSM' + const totalPlayers = servers.reduce((sum, s) => sum + (s.players?.online || 0), 0) + + return ( +
+ {/* Header */} +
+
+
+
+ + Logo + Logo + + Gameserver Management +
+
+ + {onlineCount}/{servers.length} online + + | + + {totalPlayers} Spieler + +
+
+ {/* User info with Discord avatar */} +
+ {avatarUrl && ( + Avatar + )} +
+
{user?.username}
+
{roleLabels[role]}
+
+
+ + {isSuperadmin && ( + <> + + + + )} + +
+
+
+
+ + {/* Main Content */} +
+ {error && ( +
+ {error} +
+ )} + {loading ? ( +
+
Server werden geladen...
+
+ ) : ( +
+ {servers.map((server, index) => ( +
+ navigate('/server/' + server.id)} + isAuthenticated={true} + /> +
+ ))} +
+ )} +
+ + {/* Modals */} + {showUserMgmt && ( + setShowUserMgmt(false)} /> + )} + {showActivityLog && ( + setShowActivityLog(false)} /> + )} +
+ ) +} diff --git a/UserContext.jsx b/UserContext.jsx new file mode 100644 index 0000000..86287cd --- /dev/null +++ b/UserContext.jsx @@ -0,0 +1,61 @@ +import { createContext, useContext, useState, useEffect } from 'react' +import { getMe } from '../api' + +const UserContext = createContext(null) + +export function UserProvider({ children, token }) { + const [user, setUser] = useState(null) + const [loading, setLoading] = useState(true) + + useEffect(() => { + if (!token) { + setUser(null) + setLoading(false) + return + } + + getMe(token) + .then(data => { + setUser(data) + setLoading(false) + }) + .catch(() => { + // Token invalid, clear it + localStorage.removeItem('gsm_token') + setUser(null) + setLoading(false) + }) + }, [token]) + + // Get Discord avatar URL + const getAvatarUrl = () => { + if (!user?.discordId || !user?.avatar) { + return null + } + return `https://cdn.discordapp.com/avatars/${user.discordId}/${user.avatar}.png?size=64` + } + + const value = { + user, + token, + loading, + role: user?.role || 'user', + isModerator: ['moderator', 'superadmin'].includes(user?.role), + isSuperadmin: user?.role === 'superadmin', + avatarUrl: getAvatarUrl() + } + + return ( + + {children} + + ) +} + +export function useUser() { + const context = useContext(UserContext) + if (!context) { + throw new Error('useUser must be used within UserProvider') + } + return context +} diff --git a/UserManagement.jsx b/UserManagement.jsx new file mode 100644 index 0000000..cf2f285 --- /dev/null +++ b/UserManagement.jsx @@ -0,0 +1,102 @@ +import { useState, useEffect } from 'react' +import { useUser } from '../context/UserContext' +import { getUsers } from '../api' + +export default function UserManagement({ onClose }) { + const { token } = useUser() + const [users, setUsers] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState('') + + useEffect(() => { + const fetchUsers = async () => { + try { + const data = await getUsers(token) + setUsers(data) + setError('') + } catch (err) { + setError('Fehler beim Laden der Benutzer') + } finally { + setLoading(false) + } + } + fetchUsers() + }, [token]) + + const roleLabels = { + user: 'Viewer', + moderator: 'Operator', + superadmin: 'Admin' + } + + const roleColors = { + user: 'text-neutral-400', + moderator: 'text-blue-400', + superadmin: 'text-yellow-400' + } + + const getAvatarUrl = (user) => { + if (!user.discord_id || !user.avatar) return null + return `https://cdn.discordapp.com/avatars/${user.discord_id}/${user.avatar}.png?size=64` + } + + return ( +
+
e.stopPropagation()}> +
+

Benutzerliste

+ +
+ +
+ {error && ( +
{error}
+ )} + +

+ Benutzer, die sich über Discord angemeldet haben. Rollen werden durch Discord-Rollen bestimmt. +

+ + {loading ? ( +
Laden...
+ ) : users.length === 0 ? ( +
Noch keine Benutzer angemeldet
+ ) : ( +
+ {users.map((user) => ( +
+ {getAvatarUrl(user) ? ( + + ) : ( +
+ + {user.username?.charAt(0)?.toUpperCase()} + +
+ )} + +
+
{user.username}
+
+ {user.discord_id} +
+
+ + + {roleLabels[user.role]} + +
+ ))} +
+ )} +
+
+
+ ) +} diff --git a/ZomboidConfigEditor.jsx b/ZomboidConfigEditor.jsx new file mode 100644 index 0000000..493bac0 --- /dev/null +++ b/ZomboidConfigEditor.jsx @@ -0,0 +1,326 @@ +import { useState, useEffect, useRef } from 'react' +import { FileText, Save, RefreshCw, AlertTriangle, Check, X, ChevronDown } from 'lucide-react' +import { getZomboidConfigs, getZomboidConfig, saveZomboidConfig } from '../api' + +export default function ZomboidConfigEditor({ token }) { + const [files, setFiles] = useState([]) + const [selectedFile, setSelectedFile] = useState(null) + const [content, setContent] = useState('') + const [originalContent, setOriginalContent] = useState('') + const [loading, setLoading] = useState(true) + const [saving, setSaving] = useState(false) + const [error, setError] = useState(null) + const [success, setSuccess] = useState(null) + const [hasChanges, setHasChanges] = useState(false) + const textareaRef = useRef(null) + const highlightRef = useRef(null) + + // Load file list + useEffect(() => { + loadFiles() + }, [token]) + + // Track changes + useEffect(() => { + setHasChanges(content !== originalContent) + }, [content, originalContent]) + + // Sync scroll between textarea and highlight div + const handleScroll = () => { + if (highlightRef.current && textareaRef.current) { + highlightRef.current.scrollTop = textareaRef.current.scrollTop + highlightRef.current.scrollLeft = textareaRef.current.scrollLeft + } + } + + async function loadFiles() { + setLoading(true) + setError(null) + try { + const data = await getZomboidConfigs(token) + setFiles(data.files || []) + if (data.files?.length > 0 && !selectedFile) { + loadFile(data.files[0].filename) + } + } catch (err) { + setError('Fehler beim Laden der Config-Dateien: ' + err.message) + } finally { + setLoading(false) + } + } + + async function loadFile(filename) { + setLoading(true) + setError(null) + setSuccess(null) + try { + const data = await getZomboidConfig(token, filename) + setSelectedFile(filename) + setContent(data.content) + setOriginalContent(data.content) + } catch (err) { + setError('Fehler beim Laden: ' + err.message) + } finally { + setLoading(false) + } + } + + async function handleSave() { + if (!selectedFile || !hasChanges) return + + setSaving(true) + setError(null) + setSuccess(null) + try { + await saveZomboidConfig(token, selectedFile, content) + setOriginalContent(content) + setSuccess('Config gespeichert! Server-Neustart erforderlich für Änderungen.') + setTimeout(() => setSuccess(null), 5000) + } catch (err) { + setError('Fehler beim Speichern: ' + err.message) + } finally { + setSaving(false) + } + } + + function handleDiscard() { + setContent(originalContent) + setError(null) + setSuccess(null) + } + + function getFileDescription(filename) { + const descriptions = { + 'Project.ini': 'Server-Einstellungen (PVP, Spieler, Netzwerk)', + 'Project_SandboxVars.lua': 'Gameplay-Einstellungen (Zombies, Loot, Schwierigkeit)', + 'Project_spawnpoints.lua': 'Spawn-Punkte für neue Spieler', + 'Project_spawnregions.lua': 'Spawn-Regionen Konfiguration' + } + return descriptions[filename] || filename + } + + // Highlight syntax based on file type + function highlightSyntax(text, filename) { + if (!text) return '' + + const isLua = filename?.endsWith('.lua') + const lines = text.split('\n') + + return lines.map((line, i) => { + let highlighted = line + .replace(/&/g, '&') + .replace(//g, '>') + + if (isLua) { + // Lua: -- comments + if (line.trim().startsWith('--')) { + highlighted = `${highlighted}` + } else if (line.includes('--')) { + const idx = line.indexOf('--') + const code = highlighted.substring(0, idx) + const comment = highlighted.substring(idx) + highlighted = `${code}${comment}` + } + // Highlight true/false/nil + highlighted = highlighted + .replace(/\b(true|false|nil)\b/g, '$1') + // Highlight numbers + highlighted = highlighted + .replace(/\b(\d+\.?\d*)\b/g, '$1') + } else { + // INI: # comments + if (line.trim().startsWith('#')) { + highlighted = `${highlighted}` + } + // Highlight key=value + else if (line.includes('=')) { + const idx = line.indexOf('=') + const key = highlighted.substring(0, idx) + const value = highlighted.substring(idx + 1) + highlighted = `${key}=${value}` + } + } + + return highlighted + }).join('\n') + } + + if (loading && files.length === 0) { + return ( +
+ + Lade Config-Dateien... +
+ ) + } + + return ( +
+ {/* File selector */} +
+
+ + +
+ + +
+ + {/* File description */} + {selectedFile && ( +
+ + {getFileDescription(selectedFile)} +
+ )} + + {/* Error/Success messages */} + {error && ( +
+ + {error} +
+ )} + + {success && ( +
+ + {success} +
+ )} + + {/* Editor with syntax highlighting */} +
+ {/* Highlighted background layer */} +