Unify UI workflows and streamline fleet management
This commit is contained in:
@@ -2,7 +2,11 @@
|
||||
import { artifactVersionLabel } from '$lib/artifact-version.js';
|
||||
import BetaTestProgress from "$ui/components/BetaTestProgress.svelte";
|
||||
import type { BetaTestView } from "$lib/beta-plan.js";
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { pollVisible } from '$ui/poll.js';
|
||||
import { confirmAction } from '$ui/confirm.js';
|
||||
import ServerIdentity from '$ui/components/ServerIdentity.svelte';
|
||||
import StatusBadge from '$ui/components/primitives/StatusBadge.svelte';
|
||||
import { page } from '$app/stores';
|
||||
import Card from '$ui/components/primitives/Card.svelte';
|
||||
import Badge from '$ui/components/primitives/Badge.svelte';
|
||||
@@ -63,11 +67,7 @@
|
||||
|
||||
$effect(() => { id; beta = null; loading = true; loadError = ''; void load(); });
|
||||
|
||||
let poll: ReturnType<typeof setInterval> | null = null;
|
||||
onMount(() => {
|
||||
poll = setInterval(load, 5000);
|
||||
});
|
||||
onDestroy(() => { if (poll) clearInterval(poll); });
|
||||
onMount(() => pollVisible(load, 5000, false));
|
||||
|
||||
async function promote(): Promise<void> {
|
||||
acting = 'promote'; flash = null;
|
||||
@@ -76,7 +76,7 @@
|
||||
const body = (await res.json()) as { ok: boolean; error?: string };
|
||||
if (!body.ok) { flash = body.error ?? 'Promote failed'; return; }
|
||||
await load();
|
||||
} finally { acting = null; }
|
||||
} catch (error) { flash = (error as Error).message; } finally { acting = null; }
|
||||
}
|
||||
async function rollback(): Promise<void> {
|
||||
const isReject = beta?.state === 'ready-promote';
|
||||
@@ -84,14 +84,14 @@
|
||||
? `Reject beta #${id}? This restores original files on every beta server and marks the version as test-failed.`
|
||||
: `Rollback beta #${id}? This restores original files on every beta server.`;
|
||||
const restart = test ? test.plan.rollbackRestart === 'when-empty' ? ' Restarts will roll out when servers are empty.' : ' Restart servers manually to load the restored files.' : '';
|
||||
if (!confirm(prompt + restart)) return;
|
||||
if (!await confirmAction({title:isReject ? 'Reject beta?' : 'Roll back beta?',message:prompt + restart,confirmLabel:isReject ? 'Reject beta' : 'Roll back',danger:true})) return;
|
||||
acting = 'rollback'; flash = null;
|
||||
try {
|
||||
const res = await fetch(`/api/betas/${id}/rollback`, { method: 'POST' });
|
||||
const body = (await res.json()) as { ok: boolean; error?: string };
|
||||
if (!body.ok) { flash = body.error ?? 'Rollback failed'; return; }
|
||||
await load();
|
||||
} finally { acting = null; }
|
||||
} catch (error) { flash = (error as Error).message; } finally { acting = null; }
|
||||
}
|
||||
async function retry(server: string): Promise<void> {
|
||||
acting = `retry:${server}`; flash = null;
|
||||
@@ -104,21 +104,7 @@
|
||||
const body = (await res.json()) as { ok: boolean; error?: string };
|
||||
if (!body.ok) { flash = body.error ?? 'Retry failed'; return; }
|
||||
await load();
|
||||
} finally { acting = null; }
|
||||
}
|
||||
|
||||
function betaStateVariant(s: string): 'online' | 'warning' | 'info' | 'neutral' {
|
||||
return s === 'ready-promote' ? 'online' :
|
||||
s === 'running' ? 'warning' :
|
||||
s === 'promoted' ? 'info' :
|
||||
'neutral';
|
||||
}
|
||||
function stageVariant(s: string): 'online' | 'warning' | 'danger' | 'neutral' {
|
||||
return s === 'ready' ? 'online' :
|
||||
s === 'failed' ? 'danger' :
|
||||
s === 'awaiting-empty' || s === 'restarting' || s === 'awaiting-health' || s === 'awaiting-match' || s === 'deploying'
|
||||
? 'warning' :
|
||||
'neutral';
|
||||
} catch (error) { flash = (error as Error).message; } finally { acting = null; }
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -137,7 +123,7 @@
|
||||
<Breadcrumbs items={[{ label: 'Betas', href: '/betas' }, { label: `#${b.id}` }]} />
|
||||
{/snippet}
|
||||
{#snippet actions()}
|
||||
<Badge variant={betaStateVariant(b.state)} size="md" uppercase>{b.state}</Badge>
|
||||
<StatusBadge state={b.state} />
|
||||
{/snippet}
|
||||
</SectionHeader>
|
||||
|
||||
@@ -163,7 +149,7 @@
|
||||
{#each scripts as s (s.id)}
|
||||
<div class="script-row">
|
||||
<Badge variant="neutral" size="sm" uppercase>{s.kind}</Badge>
|
||||
<a class="s-name" href="/scripts/{s.id}">{s.name}</a>
|
||||
<a class="s-name" href={"/" + (s.kind === "baseline" ? "configs" : s.kind + "s") + "/" + s.id}>{s.name}</a>
|
||||
<span class="s-target">{s.target_rel_path}</span>
|
||||
<span class="s-ver">{artifactVersionLabel(s.version)}</span>
|
||||
<span class="s-sha">{s.version?.sha256.slice(0, 8) ?? ''}</span>
|
||||
@@ -202,8 +188,8 @@
|
||||
<div class="srv-list">
|
||||
{#each servers as bs (bs.server)}
|
||||
<div class="srv-row">
|
||||
<a class="srv-name" href="/servers/{bs.server}">{bs.server}</a>
|
||||
<Badge variant={stageVariant(bs.stage)} size="sm" uppercase>{bs.stage}</Badge>
|
||||
<a class="srv-name" href="/servers/{bs.server}"><ServerIdentity name={bs.server} /></a>
|
||||
<StatusBadge state={bs.stage} />
|
||||
<div class="ticks" aria-label="pipeline progress">
|
||||
<span class:on={bs.deployed_at}>Deploy</span>
|
||||
<span class:on={bs.restarted_at || bs.stage === 'restarting'}>Empty</span>
|
||||
|
||||
Reference in New Issue
Block a user