Show provisioning status during server creation

This commit is contained in:
2026-09-19 22:53:24 -04:00
parent 5985959882
commit 44b6415821
12 changed files with 72 additions and 12 deletions
+3 -2
View File
@@ -86,6 +86,7 @@
let empty = $derived(healthy.filter(server => server.last_players === 0).length);
let problems = $derived(servers.filter(server => server.health === 'offline' || server.health === 'stale'));
let starting = $derived(servers.filter(server => server.health === 'starting').length);
let provisioning = $derived(servers.filter(server => server.health === 'provisioning').length);
let filtered = $derived(servers.filter(server => matchesFleet(server, query, filter, game, pinned, activeOperations, $uiNow))
.sort((a, b) => {
const pinOrder = Number(pinned.includes(b.name)) - Number(pinned.includes(a.name));
@@ -161,13 +162,13 @@
<div class="setup-note"><span><strong>Finish system setup</strong> · {!$fleetIntegrations.komodo ? 'Komodo' : ''}{!$fleetIntegrations.komodo && !$fleetIntegrations.rcon ? ' and ' : ''}{!$fleetIntegrations.rcon ? 'RCON' : ''} not configured.</span><a href="/settings">Review setup →</a></div>
{/if}
{#if problems.length || attentionOperations.length || starting}
{#if problems.length || attentionOperations.length || starting || provisioning}
<div class="attention" class:has-issues={problems.length > 0 || attentionOperations.length > 0}>
<span class="attention-mark" aria-hidden="true">{problems.length || attentionOperations.length ? '!' : '✓'}</span>
<div class="attention-copy">
{#if problems.length}
<button onclick={() => { resetFilters(); filter = 'attention'; }}>{problems.length} {problems.length === 1 ? 'server needs' : 'servers need'} attention →</button>
{:else}<strong>{servers.length ? starting ? `${starting} ${starting === 1 ? 'server is' : 'servers are'} starting` : 'All servers healthy' : 'Preparing your fleet'}</strong>{/if}
{:else}<strong>{provisioning ? `${provisioning} ${provisioning === 1 ? 'server is' : 'servers are'} provisioning` : servers.length ? starting ? `${starting} ${starting === 1 ? 'server is' : 'servers are'} starting` : 'All servers healthy' : 'Preparing your fleet'}</strong>{/if}
</div>
{#if attentionOperations.length}<a href="#fleet-operations" onclick={() => operationsOpen = true}>{attentionOperations.length} operation {attentionOperations.length === 1 ? 'needs' : 'need'} attention →</a>{/if}
+4 -2
View File
@@ -3,6 +3,7 @@ import { kick } from '$lib/server/pipeline/worker.js';
import { json } from '@sveltejs/kit';
import { listServers, heldByBeta } from '$lib/server/fleet/servers.js';
import { classifyHealth } from '$lib/server/fleet/health.js';
import { isProvisioning } from '$lib/server/fleet/provisioning.js';
import { queueCreateServer } from '$lib/server/fleet/create.js';
import { config, komodoConfigured, rconConfigured } from '$lib/server/config.js';
import type { RconPlayer } from '$lib/server/integrations/rcon.js';
@@ -20,7 +21,8 @@ function safeJson<T>(raw: string | null): T | null {
export async function GET() {
const rconHost = config.rcon.host;
const servers = listServers().map((s) => {
const ready = !s.container_state || s.container_state === 'running';
const provisioning = isProvisioning(s.name);
const ready = !provisioning && (!s.container_state || s.container_state === 'running');
return {
...s,
last_players: ready ? s.last_players : null,
@@ -30,7 +32,7 @@ export async function GET() {
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),
health: provisioning ? 'provisioning' : 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.
+6 -3
View File
@@ -160,7 +160,7 @@
let gameInProgress = $derived(!!effectiveGameStartedAt && !effectiveGameEndedAt && !!effectiveActiveSince);
let gameElapsed = $derived(fresh && gameInProgress ? elapsedSince(effectiveGameStartedAt, $uiNow) : null);
let hasDiagnosticIssue = $derived(effectiveHealth !== 'online' && effectiveHealth !== 'starting');
let hasDiagnosticIssue = $derived(effectiveHealth !== 'online' && effectiveHealth !== 'starting' && effectiveHealth !== 'provisioning');
function ago(iso: string | null, now: number): string {
if (!iso) return 'never';
@@ -243,6 +243,7 @@
<span class="health-label">
{effectiveHealth === 'online' ? 'Online' :
effectiveHealth === 'starting' ? 'Starting' :
effectiveHealth === 'provisioning' ? 'Provisioning' :
effectiveHealth === 'stale' ? 'Stale' : 'Offline'}
</span>
</div>
@@ -286,7 +287,7 @@
{/if}
</div>
{#if effectiveHealth !== 'starting'}<RoundTelemetry {name} />{/if}
{#if effectiveHealth !== 'starting' && effectiveHealth !== 'provisioning'}<RoundTelemetry {name} />{/if}
</Card>
@@ -348,7 +349,9 @@
<Card padding="lg">
<div class="panel-head">
<h2>RCON diagnostic</h2>
{#if effectiveHealth === 'starting'}
{#if effectiveHealth === 'provisioning'}
<Badge variant="info" uppercase>Provisioning</Badge>
{:else if effectiveHealth === 'starting'}
<Badge variant="info" uppercase>Waiting for container</Badge>
{:else if hasDiagnosticIssue}
<Badge variant="warning" uppercase>Issue</Badge>