Queue server creation and verify health and selected map before completion

This commit is contained in:
2026-09-17 17:14:36 -04:00
parent f37014fad9
commit c2ad5cc95a
12 changed files with 232 additions and 50 deletions
+5 -5
View File
@@ -8,7 +8,7 @@
interface Deployment {
id: string; server: string; action: 'create' | 'destroy';
state: 'waiting' | 'running' | 'done' | 'failed' | 'warning' | 'interrupted';
state: 'queued' | 'awaiting-health' | 'waiting' | 'running' | 'done' | 'failed' | 'warning' | 'interrupted';
stage: string; started_at: string; finished_at: string | null;
triggered_by: string; error: string | null; warnings: string; server_exists: number;
}
@@ -19,8 +19,8 @@
let action = $state('all');
let search = $state('');
let refreshing = false;
let active = $derived(deployments.filter(d => (d.state === 'running' || d.state === 'waiting')));
let history = $derived(deployments.filter(d => d.state !== 'running' && d.state !== 'waiting' &&
let active = $derived(deployments.filter(d => ['queued', 'awaiting-health', 'running', 'waiting'].includes(d.state)));
let history = $derived(deployments.filter(d => !['queued', 'awaiting-health', 'running', 'waiting'].includes(d.state) &&
(action === 'all' || d.action === action) && d.server.toLowerCase().includes(search.toLowerCase())));
async function load() {
if (refreshing) return;
@@ -41,7 +41,7 @@
function fmt(value: string) { return new Date(value).toLocaleString(); }
function warnings(d: Deployment): string[] { try { return JSON.parse(d.warnings); } catch { return []; } }
function status(d: Deployment) {
return { waiting: 'Waiting for empty', running: 'In flight', done: 'Completed', failed: 'Failed', warning: 'Completed with warnings', interrupted: 'Interrupted' }[d.state];
return { queued: 'Queued', 'awaiting-health': 'Verifying health', waiting: 'Waiting for empty', running: 'In flight', done: 'Completed', failed: 'Failed', warning: 'Completed with warnings', interrupted: 'Interrupted' }[d.state];
}
</script>
@@ -78,7 +78,7 @@
</div>
<Badge variant={d.state === 'done' ? 'online' : d.state === 'failed' ? 'danger' : 'warning'}>{status(d)}</Badge>
</div>
<p>{d.state === 'done' ? (d.action === 'create' ? 'Server created; container start requested.' : 'Server destroyed.') : d.stage}</p>
<p>{d.state === 'done' ? (d.action === 'create' ? 'Server creation completed.' : 'Server destroyed.') : d.stage}</p>
<div class="meta"><span>Started {fmt(d.started_at)}</span>{#if d.finished_at}<span>Finished {fmt(d.finished_at)}</span>{/if}<span>By {d.triggered_by}</span></div>
{#if d.error}<p class="error">{d.error}</p>{/if}
{#each warnings(d) as warning}<p class="error">{warning}</p>{/each}
+8 -6
View File
@@ -6,7 +6,7 @@
import Badge from '$ui/components/primitives/Badge.svelte';
interface Deployment {
id: string; server: string; action: 'create' | 'destroy'; state: string;
id: string; expected_map?: string; server: string; action: 'create' | 'destroy'; state: string;
stage: string; started_at: string; finished_at: string | null;
triggered_by: string; error: string | null; warnings: string; server_exists: number;
}
@@ -15,24 +15,25 @@
let error = $state('');
let refreshing = false;
let deployment = $derived(detail?.deployment);
let active = $derived(deployment?.state === 'waiting' || deployment?.state === 'running');
let active = $derived(['queued', 'awaiting-health', 'waiting', 'running'].includes(deployment?.state ?? ''));
let warnings = $derived.by(() => {
try { return JSON.parse(deployment?.warnings ?? '[]') as string[]; } catch { return []; }
});
let steps = $derived(deployment?.action === 'destroy'
? ['Request destruction', 'Wait for empty server', 'Stop and remove container', 'Delete Komodo deployment', 'Remove server files and registry']
: ['Request creation', 'Prepare server files', 'Create Komodo deployment', 'Start container']);
: ['Request creation', 'Prepare server files', 'Create Komodo deployment', 'Start container', 'Verify Komodo health', 'Verify RCON health', 'Verify selected map']);
let currentStep = $derived.by(() => {
if (!deployment) return 0;
const stages = deployment.action === 'destroy'
? ['Starting', 'Waiting', 'Stopping and removing container', 'Deleting Komodo deployment', 'Removing server files and registry']
: ['Starting', 'Preparing server files', 'Creating Komodo deployment', 'Starting container'];
: ['Starting', 'Preparing server files', 'Creating Komodo deployment', 'Starting container', 'Waiting for Komodo health', 'Waiting for RCON health', 'Waiting for selected map'];
if (deployment.state === 'waiting') return 1;
if (deployment.state === 'awaiting-health' && deployment.stage.startsWith('Waiting for health verification')) return 4;
if (deployment.state === 'done') return stages.length;
return Math.max(0, stages.findIndex(stage => deployment.stage.startsWith(stage)));
});
function stateLabel(state: string) {
return ({ waiting: 'Waiting for empty', running: 'In flight', done: 'Completed', failed: 'Failed', warning: 'Completed with warnings', interrupted: 'Interrupted' } as Record<string, string>)[state] ?? state;
return ({ queued: 'Queued', 'awaiting-health': 'Verifying health', waiting: 'Waiting for empty', running: 'In flight', done: 'Completed', failed: 'Failed', warning: 'Completed with warnings', interrupted: 'Interrupted' } as Record<string, string>)[state] ?? state;
}
async function load() {
if (refreshing) return;
@@ -65,10 +66,11 @@
<div class="top"><Badge variant={deployment.state === 'done' ? 'online' : deployment.state === 'failed' ? 'danger' : 'warning'}>{stateLabel(deployment.state)}</Badge>
{#if deployment.server_exists}<a href="/servers/{encodeURIComponent(deployment.server)}">View server</a>{/if}
</div>
<h2>{deployment.state === 'done' ? (deployment.action === 'destroy' ? 'Server destroyed' : 'Server created; container start requested') : deployment.stage}</h2>
<h2>{deployment.state === 'done' ? (deployment.action === 'destroy' ? 'Server destroyed' : 'Server creation completed') : deployment.stage}</h2>
{#if deployment.state === 'waiting'}<p>The server stays online while players finish. Player count is checked automatically; destruction starts only after a live check reports zero players.</p>{/if}
{#if deployment.error}<p role="alert" class="error">{deployment.error}</p>{/if}
{#each warnings as warning}<p class="warning">{warning}</p>{/each}
{#if deployment.expected_map}<p>Expected map: <strong>{deployment.expected_map}</strong></p>{/if}
<dl><div><dt>Started</dt><dd>{fmt(deployment.started_at)}</dd></div><div><dt>Finished</dt><dd>{deployment.finished_at ? fmt(deployment.finished_at) : 'In progress'}</dd></div><div><dt>Requested by</dt><dd>{deployment.triggered_by}</dd></div></dl>
</section>
<section>