Admin: Auslastung der Docker-Hosts mit den laufenden Matches

Die Admin-Übersicht zeigt pro Docker-Host belegte und freie Plätze und die
Matches, die dort laufen. Ohne konfigurierte Hosts bleibt die Karte aus.
This commit is contained in:
mregosar committed 2026-10-29 10:12:17 +01:00
1 parent c1c9250126
commit 91f979945e
6 files changed
+81 -1

No files matched your search

+36
View File
@@ -26,6 +26,7 @@ const storeToken = (t) => {
export default function AdminPage() {
const [token, setToken] = useState(getToken);
const [matches, setMatches] = useState(null);
const [hosts, setHosts] = useState([]);
const [error, setError] = useState(null);
const [notice, setNotice] = useState(null);
const [busy, setBusy] = useState(null);
@@ -42,6 +43,7 @@ export default function AdminPage() {
try {
const res = await api('/admin/matches', { token });
setMatches(res.matches);
setHosts(res.hosts ?? []);
setError(null);
} catch (err) {
if (err.status === 401) {
@@ -101,6 +103,7 @@ export default function AdminPage() {
{tab === 'settings' && token && <NotificationSettings token={token} setNotice={setNotice} />}
{notice && <div className={`toast toast-${notice.tone}`} role="status">{notice.text}</div>}
{error && <div className="alert alert-error">{error}</div>}
{tab === 'matches' && <HostUsage hosts={hosts} />}
{tab === 'matches' && matches === null && !error && <p className="muted">Lade Matches …</p>}
{tab === 'matches' && matches && matches.length === 0 && <p className="muted">Noch keine Matches.</p>}
@@ -386,3 +389,36 @@ function MatchAdminCard({ m, run, busy, token, reload, setNotice }) {
</section>
);
}
/** Auslastung der Docker-Hosts: wie viele Gameserver läuft auf welchem Host, mit Liste der Matches. */
function HostUsage({ hosts }) {
if (hosts.length === 0) return null;
return (
<section className="host-usage">
<h2>Docker-Hosts</h2>
<div className="host-grid">
{hosts.map((h) => {
const percent = h.total ? Math.round((h.used / h.total) * 100) : 0;
return (
<div key={h.name} className={`host-card ${percent >= 90 ? 'is-full' : ''}`}>
<div className="host-head">
<strong>{h.name}</strong>
<span className="muted small">{h.publicHost}</span>
<span className="host-count">{h.used} / {h.total} belegt</span>
</div>
<div className="host-bar" role="progressbar" aria-valuenow={percent} aria-valuemin={0} aria-valuemax={100}>
<span style={{ width: `${percent}%` }} />
</div>
{h.matches.length === 0 ? (
<p className="muted small">Keine Gameserver</p>
) : (
<ul className="host-matches">
{h.matches.map((m) => <li key={m.id}>{m.title} <span className="muted small">{m.status}</span></li>)}
</ul>
)}
</div>
);
})}
</div>
</section>
);
}