Use database fleet registry and gate RCON polling on container readiness
This commit is contained in:
@@ -19,16 +19,25 @@ function safeJson<T>(raw: string | null): T | null {
|
||||
|
||||
export async function GET() {
|
||||
const rconHost = config.rcon.host;
|
||||
const servers = listServers().map((s) => ({
|
||||
...s,
|
||||
roster: safeJson<RconPlayer[]>(s.last_roster),
|
||||
health: classifyHealth(s.last_seen_at, s.last_error),
|
||||
replySnippet: s.last_reply_snippet,
|
||||
// Derived endpoint — same logic as endpointFor() but without asking
|
||||
// Komodo. If we don't have a port yet, endpoint is null.
|
||||
endpoint: s.game_port ? { host: s.rcon_host ?? rconHost, port: s.game_port } : null,
|
||||
held: [...heldByBeta(s.name).entries()].map(([target, betaId]) => ({ target, betaId }))
|
||||
}));
|
||||
const servers = listServers().map((s) => {
|
||||
const ready = !s.container_state || s.container_state === 'running';
|
||||
return {
|
||||
...s,
|
||||
last_players: ready ? s.last_players : null,
|
||||
last_error: ready ? s.last_error : null,
|
||||
active_since: ready ? s.active_since : null,
|
||||
inactive_since: ready ? s.inactive_since : null,
|
||||
game_started_at: ready ? s.game_started_at : null,
|
||||
game_ended_at: ready ? s.game_ended_at : null,
|
||||
roster: ready ? safeJson<RconPlayer[]>(s.last_roster) : null,
|
||||
health: classifyHealth(s.last_seen_at, s.last_error, s.container_state),
|
||||
replySnippet: ready ? s.last_reply_snippet : null,
|
||||
// Derived endpoint — same logic as endpointFor() but without asking
|
||||
// Komodo. If we don't have a port yet, endpoint is null.
|
||||
endpoint: s.game_port ? { host: s.rcon_host ?? rconHost, port: s.game_port } : null,
|
||||
held: [...heldByBeta(s.name).entries()].map(([target, betaId]) => ({ target, betaId }))
|
||||
};
|
||||
});
|
||||
return json({
|
||||
servers,
|
||||
reservedNames: (db().prepare("SELECT j.server FROM server_creation_jobs j JOIN server_deployments d ON d.id = j.deployment_id WHERE d.state IN ('queued', 'running', 'awaiting-health')").all() as { server: string }[]).map(row => row.server),
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
let effectiveRoster = $derived(live?.roster ?? server?.roster ?? null);
|
||||
let effectiveHealth = $derived<Health>(live?.health ?? server?.health ?? 'offline');
|
||||
let effectiveSeen = $derived(live?.lastSeenAt ?? server?.last_seen_at ?? null);
|
||||
let effectiveErr = $derived(live?.lastError ?? server?.last_error ?? null);
|
||||
let effectiveErr = $derived(live ? live.lastError : server?.last_error ?? null);
|
||||
let effectiveEndpoint = $derived<RconEndpoint | null>(live?.endpoint ?? server?.endpoint ?? null);
|
||||
let liveReason = $derived<RconFailure | null>(live?.reason ?? null);
|
||||
let decoded = $derived(decodeRconError(effectiveErr));
|
||||
@@ -139,12 +139,12 @@
|
||||
let effectiveInactiveSince = $derived<string | null>(live?.inactiveSince ?? server?.inactive_since ?? null);
|
||||
let effectiveGameStartedAt = $derived<string | null>(live?.gameStartedAt ?? server?.game_started_at ?? null);
|
||||
let effectiveGameEndedAt = $derived<string | null>(live?.gameEndedAt ?? server?.game_ended_at ?? null);
|
||||
let activeElapsed = $derived(effectiveActiveSince ? elapsedSince(effectiveActiveSince, $uiNow) : null);
|
||||
let inactiveElapsed = $derived(effectiveInactiveSince ? elapsedSince(effectiveInactiveSince, $uiNow) : null);
|
||||
let activeElapsed = $derived(effectiveHealth !== 'starting' && effectiveActiveSince ? elapsedSince(effectiveActiveSince, $uiNow) : null);
|
||||
let inactiveElapsed = $derived(effectiveHealth !== 'starting' && effectiveInactiveSince ? elapsedSince(effectiveInactiveSince, $uiNow) : null);
|
||||
let gameInProgress = $derived(!!effectiveGameStartedAt && !effectiveGameEndedAt && !!effectiveActiveSince);
|
||||
let gameElapsed = $derived(gameInProgress ? elapsedSince(effectiveGameStartedAt, $uiNow) : null);
|
||||
let gameElapsed = $derived(effectiveHealth !== 'starting' && gameInProgress ? elapsedSince(effectiveGameStartedAt, $uiNow) : null);
|
||||
|
||||
let hasDiagnosticIssue = $derived(effectiveHealth !== 'online');
|
||||
let hasDiagnosticIssue = $derived(effectiveHealth !== 'online' && effectiveHealth !== 'starting');
|
||||
|
||||
// Auto-open the Diagnostics tab if there's a problem the operator should see.
|
||||
$effect(() => {
|
||||
@@ -237,13 +237,14 @@
|
||||
<StatusPip health={effectiveHealth} size="md" />
|
||||
<span class="health-label">
|
||||
{effectiveHealth === 'online' ? 'Online' :
|
||||
effectiveHealth === 'starting' ? 'Starting' :
|
||||
effectiveHealth === 'stale' ? 'Stale' : 'Offline'}
|
||||
</span>
|
||||
</div>
|
||||
<div class="metric-cell">
|
||||
<Stat
|
||||
label="Players"
|
||||
value={effectivePlayers ?? 0}
|
||||
value={effectiveHealth === 'starting' ? '—' : effectivePlayers ?? 0}
|
||||
size="xl"
|
||||
tone={(effectivePlayers ?? 0) > 0 ? 'online' : 'primary'}
|
||||
tween
|
||||
@@ -276,7 +277,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<RoundTelemetry {name} />
|
||||
{#if effectiveHealth !== 'starting'}<RoundTelemetry {name} />{/if}
|
||||
|
||||
</Card>
|
||||
|
||||
@@ -327,7 +328,9 @@
|
||||
<Card padding="lg">
|
||||
<div class="panel-head">
|
||||
<h2>RCON diagnostic</h2>
|
||||
{#if hasDiagnosticIssue}
|
||||
{#if effectiveHealth === 'starting'}
|
||||
<Badge variant="info" uppercase>Waiting for container</Badge>
|
||||
{:else if hasDiagnosticIssue}
|
||||
<Badge variant="warning" uppercase>Issue</Badge>
|
||||
{:else}
|
||||
<Badge variant="online" uppercase>Healthy</Badge>
|
||||
|
||||
Reference in New Issue
Block a user