import { useState, useEffect, useRef } from 'react' import { useParams, useNavigate } from 'react-router-dom' import { getServers, serverAction, sendRcon, getServerLogs, getWhitelist, getFactorioCurrentSave, getAutoShutdownSettings, setAutoShutdownSettings } from '../api' import { useUser } from '../context/UserContext' import MetricsChart from '../components/MetricsChart' import FactorioWorldManager from '../components/FactorioWorldManager' const getServerLogo = (serverName) => { const name = serverName.toLowerCase() 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() { const { serverId } = useParams() const navigate = useNavigate() const { token, isModerator } = useUser() const [server, setServer] = useState(null) const [loading, setLoading] = useState(true) const [activeTab, setActiveTab] = useState('overview') const [rconCommand, setRconCommand] = useState('') const [rconHistory, setRconHistory] = useState([]) const [logs, setLogs] = useState('') const [whitelistPlayers, setWhitelistPlayers] = useState([]) 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) // Auto-shutdown state const [autoShutdown, setAutoShutdown] = useState({ enabled: false, timeoutMinutes: 15, emptySinceMinutes: null }) const [autoShutdownLoading, setAutoShutdownLoading] = useState(false) const fetchCurrentSave = async () => { if (token && serverId === 'factorio') { try { const result = await getFactorioCurrentSave(token) setCurrentSave(result) } catch (err) { console.error('Failed to fetch current save:', err) } } } const fetchServer = async () => { try { const servers = await getServers(token) const found = servers.find(s => s.id === serverId) if (found) { setServer(found); document.title = found.name + " | Zeasy GSM" } else { navigate('/') } } catch (err) { console.error(err) navigate('/') } finally { setLoading(false) } } const fetchAutoShutdownSettings = async () => { if (token && serverId) { try { const data = await getAutoShutdownSettings(token, serverId) setAutoShutdown(data) } catch (err) { console.error('Failed to load auto-shutdown settings:', err) } } } const handleAutoShutdownToggle = async () => { setAutoShutdownLoading(true) try { await setAutoShutdownSettings(token, serverId, !autoShutdown.enabled, autoShutdown.timeoutMinutes) setAutoShutdown(prev => ({ ...prev, enabled: !prev.enabled })) } catch (err) { console.error(err) } setAutoShutdownLoading(false) } const handleAutoShutdownTimeoutChange = async (newTimeout) => { const timeout = Math.max(1, Math.min(1440, parseInt(newTimeout) || 15)) setAutoShutdown(prev => ({ ...prev, timeoutMinutes: timeout })) clearTimeout(window.autoShutdownSaveTimeout) window.autoShutdownSaveTimeout = setTimeout(async () => { try { await setAutoShutdownSettings(token, serverId, autoShutdown.enabled, timeout) } catch (err) { console.error(err) } }, 500) } useEffect(() => { fetchServer() fetchCurrentSave() const interval = setInterval(() => { fetchServer() fetchCurrentSave() }, 10000) return () => clearInterval(interval) }, [token, serverId]) useEffect(() => { if (activeTab === 'settings' && isModerator) { fetchAutoShutdownSettings() const interval = setInterval(fetchAutoShutdownSettings, 10000) return () => clearInterval(interval) } }, [activeTab, isModerator, token, serverId]) const handleAction = async (action) => { // Immediately set status locally const newStatus = action === 'start' ? 'starting' : (action === 'stop' ? 'stopping' : 'starting') setServer(prev => ({ ...prev, status: newStatus })) try { await serverAction(token, server.id, action) setTimeout(() => { fetchServer() }, 2000) } catch (err) { console.error(err) } } const handleRcon = async (e) => { e.preventDefault() if (!rconCommand.trim()) return const cmd = rconCommand setRconCommand('') try { const { response } = await sendRcon(token, server.id, cmd) setRconHistory([...rconHistory, { cmd, res: response, time: new Date() }]) } catch (err) { setRconHistory([...rconHistory, { cmd, res: 'Error: ' + err.message, time: new Date(), error: true }]) } } const fetchLogs = async () => { try { const data = await getServerLogs(token, server.id, 50) setLogs(data.logs || '') setLogsUpdated(new Date()) if (logsRef.current) { logsRef.current.scrollTop = logsRef.current.scrollHeight } } catch (err) { console.error(err) } } useEffect(() => { if (activeTab === 'console' && isModerator && server) { fetchLogs() const interval = setInterval(fetchLogs, 5000) return () => clearInterval(interval) } }, [activeTab, isModerator, server]) useEffect(() => { if (activeTab === 'whitelist' && isModerator && server?.type === 'minecraft') { fetchWhitelist() } }, [activeTab, server]) useEffect(() => { if (rconRef.current) { rconRef.current.scrollTop = rconRef.current.scrollHeight } }, [rconHistory]) const fetchWhitelist = async () => { if (!server?.hasRcon) return try { const { players } = await getWhitelist(token, server.id) setWhitelistPlayers(players) } catch (err) { console.error("Failed to fetch whitelist:", err) } } const addToWhitelist = async (e) => { e.preventDefault() if (!whitelistInput.trim()) return setWhitelistLoading(true) try { await sendRcon(token, server.id, 'whitelist add ' + whitelistInput.trim()) setWhitelistInput('') await fetchWhitelist() } catch (err) { console.error('Failed to add to whitelist:', err) } finally { setWhitelistLoading(false) } } const removeFromWhitelist = async (player) => { setWhitelistLoading(true) try { await sendRcon(token, server.id, 'whitelist remove ' + player) await fetchWhitelist() } catch (err) { console.error('Failed to remove from whitelist:', err) } finally { setWhitelistLoading(false) } } const formatUptime = (seconds) => { const days = Math.floor(seconds / 86400) const hours = Math.floor((seconds % 86400) / 3600) const minutes = Math.floor((seconds % 3600) / 60) if (days > 0) return days + 'd ' + hours + 'h ' + minutes + 'm' return hours + 'h ' + minutes + 'm' } const tabs = [ { id: 'overview', label: 'Übersicht' }, { id: 'metrics', label: 'Metriken' }, ...(isModerator ? [ { id: 'console', label: 'Konsole' }, ] : []), ...(isModerator && server?.type === 'minecraft' ? [ { id: 'whitelist', label: 'Whitelist' }, ] : []), ...(isModerator && server?.type === 'factorio' ? [ { id: 'worlds', label: 'Welten' }, ] : []), ...(isModerator ? [ { id: 'settings', label: 'Einstellungen' }, ] : []), ] if (loading) { return (
Laden...
) } if (!server) { return (
Server nicht gefunden
) } const cpuPercent = Math.min(server.metrics.cpu, 100) const memPercent = Math.min(server.metrics.memory, 100) const getStatusBadge = () => { const status = server.status || (server.running ? 'online' : 'offline') switch (status) { case 'online': return { class: 'badge badge-success', text: 'Online' } case 'starting': return { class: 'badge badge-warning', text: 'Startet...' } case 'stopping': return { class: 'badge badge-warning', text: 'Stoppt...' } default: return { class: 'badge badge-destructive', text: 'Offline' } } } const statusBadge = getStatusBadge() return (
{/* Header */}
{getServerLogo(server.name) && }

{server.name}

{statusBadge.text}
{server.running && (

Laufzeit: {formatUptime(server.metrics.uptime)}

)}
{/* Tabs */}
{tabs.map((tab) => ( ))}
{/* Content */}
{/* Overview Tab */} {activeTab === 'overview' && (
{/* Stats Grid */}
CPU Auslastung
{server.metrics.cpu.toFixed(1)}%
Arbeitsspeicher
{server.metrics.memoryUsed?.toFixed(1)} {server.metrics.memoryUnit}
von {server.metrics.memoryTotal?.toFixed(1)} {server.metrics.memoryUnit}
Spieler
{server.players.online}
{server.players.max ? 'von ' + server.players.max + ' max' : 'Kein Limit'}
CPU Kerne
{server.metrics.cpuCores}
{server.type === 'factorio' && currentSave?.save && (
{server.running ? 'Aktuelle Welt' : 'Nächste Welt'}
{currentSave.save}
{!server.running && currentSave.source === 'newest' && (
neuester Speicherstand
)}
)}
{/* Players List */} {server.players?.list?.length > 0 && (

Spieler Online

{server.players.list.map((player, i) => ( {player} ))}
)} {/* Power Controls */} {isModerator && (

Server Steuerung

{(server.status === 'online' || (server.running && server.status !== 'starting' && server.status !== 'stopping')) ? ( <> ) : ( )}
)}
)} {/* Metrics Tab */} {activeTab === 'metrics' && (
)} {/* Console Tab - Logs + RCON */} {activeTab === 'console' && isModerator && (
{/* Logs */}
Server Logs (letzte 50 Zeilen) {logsUpdated && ( Aktualisiert: {logsUpdated.toLocaleTimeString('de-DE')} )}
{logs || 'Laden...'}
{/* RCON History */} {rconHistory.length > 0 && (
RCON Verlauf
{rconHistory.map((entry, i) => (
[{entry.time.toLocaleTimeString('de-DE')}] > {entry.cmd}
{entry.res}
))}
)} {/* RCON Input */} {server.hasRcon && (
setRconCommand(e.target.value)} placeholder="RCON Befehl..." className="input flex-1" />
)}
)} {/* Whitelist Tab - Minecraft only */} {activeTab === 'whitelist' && isModerator && server.type === 'minecraft' && (

Zur Whitelist hinzufügen

setWhitelistInput(e.target.value)} placeholder="Minecraft Benutzername..." className="input flex-1" disabled={whitelistLoading || !server.running} />

Whitelist Spieler ({whitelistPlayers.length})

{whitelistPlayers.length > 0 ? (
{whitelistPlayers.map((player, i) => (
{player}
))}
) : (
Keine Spieler auf der Whitelist
)}
)} {/* Worlds Tab - Factorio only */} {activeTab === 'worlds' && isModerator && server.type === 'factorio' && (
{server.running || server.status === 'starting' || server.status === 'stopping' ? (
Weltverwaltung ist gesperrt während der Server läuft
Server stoppen um Speicherstände zu verwalten
) : ( )}
)} {/* Settings Tab */} {activeTab === 'settings' && isModerator && (

Auto-Shutdown

Server automatisch stoppen wenn keine Spieler online sind

{/* Toggle Switch */}
{autoShutdown.enabled ? 'Aktiviert' : 'Deaktiviert'}
{/* Timeout mit +/- Buttons */} {autoShutdown.enabled && (
Timeout:
{autoShutdown.timeoutMinutes}
Minuten
)} {/* Status */} {autoShutdown.enabled && server.running && (
Status: {autoShutdown.emptySinceMinutes !== null ? ( Leer seit {autoShutdown.emptySinceMinutes} Min. Shutdown in {autoShutdown.timeoutMinutes - autoShutdown.emptySinceMinutes} Min. ) : ( Spieler online )}
)}
)}
) }