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:
1 parent
c1c9250126
commit
91f979945e
6 files changed
+81
-1
No files matched your search
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -554,3 +554,15 @@ input:focus, select:focus { border-color: var(--link); outline: none; }
|
||||
.run-vs { font-size: 1rem; color: var(--muted); font-style: normal; }
|
||||
.run-foot { display: flex; justify-content: space-between; gap: 8px; font-size: .875rem; color: var(--muted); }
|
||||
.run-series { font-variant-numeric: tabular-nums; }
|
||||
.host-usage { margin-bottom: 20px; }
|
||||
.host-usage h2 { font-size: 1.1rem; margin: 0 0 10px; }
|
||||
.host-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
|
||||
.host-card { border: 1px solid var(--line); background: var(--surface); border-radius: var(--radius); padding: 12px 14px; }
|
||||
.host-card.is-full { border-color: var(--pending); }
|
||||
.host-head { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; margin-bottom: 8px; }
|
||||
.host-count { margin-left: auto; font-variant-numeric: tabular-nums; font-size: .875rem; }
|
||||
.host-bar { height: 6px; border-radius: 3px; background: var(--line); overflow: hidden; margin-bottom: 8px; }
|
||||
.host-bar span { display: block; height: 100%; background: var(--primary); transition: width .3s ease; }
|
||||
.host-card.is-full .host-bar span { background: var(--pending); }
|
||||
.host-matches { list-style: none; margin: 0; padding: 0; font-size: .9rem; }
|
||||
.host-matches li { padding: 3px 0; border-top: 1px solid var(--line); }
|
||||
Reference in new issue
Block a user