Complete workflow navigation and document page groups
This commit is contained in:
@@ -17,7 +17,7 @@ docker compose up -d --build
|
||||
Open EC and create the first administrator using `EC_SETUP_TOKEN` (at least 32
|
||||
characters; generate it with `openssl rand -hex 32`). After setup, remove that
|
||||
token from the environment. Administrators can add administrators, operators and
|
||||
viewers from **Settings → Users**. See [Users and access](docs/authentication.md)
|
||||
viewers from **Administration → Users**. See [Users and access](docs/authentication.md)
|
||||
for permissions, sessions, password recovery and upgrade instructions.
|
||||
|
||||
Operator pages and management APIs require login. Enrolled agents authenticate
|
||||
@@ -32,11 +32,11 @@ including SQLite metadata and published game-file storage.
|
||||
including its DLC files. There are no library names or version labels to enter.
|
||||
Each upload becomes the latest files for that game after verification.
|
||||
Uploads work before any host is enrolled.
|
||||
2. **Hosts → Add Host**: configure a Debian 12/13 amd64 machine and run its
|
||||
2. **Fleet → Hosts → Add Host**: configure a Debian 12/13 amd64 machine and run its
|
||||
generated enrollment command. The host automatically prepares its Plutainer
|
||||
runtime and the latest T5 and T6 files, when available. The host page
|
||||
shows setup progress. Automatic agent updates are enabled by default.
|
||||
3. Add the exact Plutonium server keys in **Keys**, or supply missing keys when
|
||||
3. Add the exact Plutonium server keys in **Administration → Keys**, or supply missing keys when
|
||||
launching.
|
||||
4. Launch a server or collection. Collection previews show cached capacity
|
||||
estimates; Apply saves the queue and opens its progress page. Provisioning
|
||||
@@ -61,6 +61,22 @@ continue. The host page shows update status and the last check.
|
||||
|
||||
## Fleet and operations
|
||||
|
||||
The sidebar groups pages into **Fleet**, **Library**, **Operations**, **Analytics**,
|
||||
and **Administration** (administrators only). On small screens, open the navigation
|
||||
menu in the header. The account menu contains **My account**, including your
|
||||
browser's display timezone; keyboard shortcuts are available through **?**.
|
||||
|
||||
**Operations → Overview** combines work that needs attention, running workflows,
|
||||
and recent results. Related provisioning attempts and host commands stay grouped
|
||||
under their workflow. Normal waits for empty lobbies remain under **Running**;
|
||||
successful routine checks remain in **Host operation history**. Full history is
|
||||
available on each workflow page.
|
||||
|
||||
Server details use **Live**, **Matches**, **Configuration**, **Diagnostics**, and
|
||||
**History** sections. Hosts use **Overview**, **Servers**, **Setup**, and **Settings**.
|
||||
Contextual Back links preserve the originating filters and views, while breadcrumbs
|
||||
show each page's place in the navigation. Existing URLs and server tab links work.
|
||||
|
||||
The fleet registry lives in SQLite, independently of host filesystems. Status
|
||||
combines agent container health, fresh game telemetry and agent-local RCON.
|
||||
Unreachable hosts report unknown instance state; they do not trigger duplicate
|
||||
|
||||
@@ -20,7 +20,7 @@ their access checks. HTTPS requirements still apply. See the
|
||||
password. Only the holder of the token can create the first administrator.
|
||||
4. Remove `EC_SETUP_TOKEN` after setup and restart/recreate the controller.
|
||||
Setup remains closed once any user exists, even if the token is still set.
|
||||
5. Use **Settings → Users** to create additional accounts. Share temporary
|
||||
5. Use **Administration → Users** to create additional accounts. Share temporary
|
||||
passwords privately; each user must change theirs on first login.
|
||||
|
||||
Existing installations become protected on upgrade. Existing game servers and
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# Metrics
|
||||
|
||||
Metrics is a primary navigation tab at `/metrics` (keyboard shortcut: `g m`).
|
||||
Metrics is under **Analytics** at `/metrics` (keyboard shortcut: `g m`).
|
||||
Choose the past 24 hours, 7 days, or 30 days. The page refreshes every minute.
|
||||
|
||||
The overview shows **Time with players** (occupied server-hours divided by known
|
||||
|
||||
@@ -291,7 +291,7 @@
|
||||
<BackLink fallback={basePath} />
|
||||
|
||||
{#if scopeComplete}<div class="scope-complete" role="status"><strong>Scope saved.</strong> {scopeComplete.composed ? `Intended configuration may change on ${scopeComplete.count} eligible servers.` : scopeComplete.detached ? `Inventory tracking detached on ${scopeComplete.detached} servers; live files remain.` : `${scopeComplete.count} servers are in the old or new scope.`} Maintenance detects resulting drift and rebuilds eligible servers when automation is enabled. <a href={scopeComplete.href}>View drift recovery →</a></div>{/if}
|
||||
{#if latestVersion}<Card padding="lg"><div class="release-summary"><div><h2>Latest {artifactVersionLabel(latestVersion)}</h2><StatusBadge state={latestVersion.id === a.current_version_id ? 'promoted' : latestVersion.state} /><p>{latestVersion.notes || 'No version notes provided.'}</p></div>{#if runningBetaId !== null}<Button variant="primary" href={contextHref(`/betas/${runningBetaId}`, $page.url)}>View active beta</Button>{:else if latestVersion.id !== a.current_version_id}<Button variant="primary" href={betaSelectionHref([{ scriptId: id, versionId: latestVersion.id }])}>Test latest version</Button>{:else}<span class="dim">Latest version is promoted</span>{/if}</div></Card>{/if}
|
||||
{#if latestVersion}<Card padding="lg"><div class="release-summary"><div><h2>Latest {artifactVersionLabel(latestVersion)}</h2><StatusBadge state={latestVersion.id === a.current_version_id ? 'promoted' : latestVersion.state} /><p>{latestVersion.notes || 'No version notes provided.'}</p></div>{#if runningBetaId !== null}<Button variant="primary" href={contextHref(`/betas/${runningBetaId}`, $page.url)}>View active beta</Button>{:else if latestVersion.id !== a.current_version_id}<Button variant="primary" href={contextHref(betaSelectionHref([{ scriptId: id, versionId: latestVersion.id }]), $page.url)}>Test latest version</Button>{:else}<span class="dim">Latest version is promoted</span>{/if}</div></Card>{/if}
|
||||
|
||||
{#if runningBetaId}
|
||||
<div class="warn-banner">
|
||||
@@ -382,7 +382,7 @@
|
||||
<Badge variant="online" size="sm">Promoted</Badge>
|
||||
{:else}
|
||||
<StatusBadge state={v.state} size="sm" />
|
||||
{#if runningBetaId === null}<Button size="sm" href={betaSelectionHref([{scriptId:id,versionId:v.id}])}>Test version</Button>{/if}
|
||||
{#if runningBetaId === null}<Button size="sm" href={contextHref(betaSelectionHref([{scriptId:id,versionId:v.id}]), $page.url)}>Test version</Button>{/if}
|
||||
{/if}
|
||||
<details class="version-technical"><summary>File details</summary><span>SHA-256: {v.sha256}</span><span>Size: {v.size.toLocaleString()} bytes</span></details>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { contextHref, safeReturnHref } from '$lib/navigation-context.js';
|
||||
import BackLink from '$ui/components/BackLink.svelte';
|
||||
import Breadcrumbs from '$ui/components/primitives/Breadcrumbs.svelte';
|
||||
import { timeFormat } from '$ui/stores/time-zone.js';
|
||||
import { onMount, tick } from 'svelte';
|
||||
import { beforeNavigate, goto } from '$app/navigation';
|
||||
@@ -167,14 +171,16 @@
|
||||
const response=await fetch('/api/betas',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({scripts:picks.map(p=>({scriptId:p.scriptId,scriptVersionId:p.versionId})),servers:[...pickedServers],testPlan:parseBetaPlan(testPlan),notes:betaLabel.trim()||undefined,waitForHealth:true,waitForMatch:false})}), body=await response.json();
|
||||
if(!response.ok||!body.ok)throw new Error(body.error??'Could not start beta. Review the selected versions and servers.');
|
||||
draftReady=false;try{localStorage.removeItem(betaDraftKey);}catch{}allowLeave=true;
|
||||
await goto(`/betas/${body.result.betaId}`);
|
||||
await goto(contextHref(`/betas/${body.result.betaId}`, safeReturnHref($page.url.searchParams.get('returnTo'), '/betas')));
|
||||
}catch(error){flash=(error as Error).message;}finally{openingBeta=false;}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head><title>New beta | Electric Cherry</title></svelte:head>
|
||||
<div class="page">
|
||||
<SectionHeader title="New beta" subtitle="Choose exact file versions and test servers, then review before starting.">{#snippet actions()}<Button href="/betas" variant="ghost">Back to betas</Button>{/snippet}</SectionHeader>
|
||||
<Breadcrumbs items={[{ label: 'Betas', href: '/betas' }, { label: 'New beta' }]} />
|
||||
<BackLink fallback="/betas" />
|
||||
<SectionHeader title="New beta" subtitle="Choose exact file versions and test servers, then review before starting." />
|
||||
{#if flash}<div class="ui-error" role="alert">{flash}{#if !scripts.length}<Button size="sm" onclick={()=>initialize()}>Retry</Button>{/if}</div>{/if}
|
||||
{#if $fleetError}<div class="ui-error" role="alert">{$fleetError}<Button size="sm" onclick={() => refreshFleet(true)}>Retry servers</Button></div>{/if}
|
||||
{#if notice}<p role="status" class="notice">{notice}</p>{/if}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
interface Detail {
|
||||
deployment: {
|
||||
id: string; action: string; started_at: string;
|
||||
finished_at: string | null; triggered_by: string | null;
|
||||
finished_at: string | null; triggered_by: string | null; beta_id?: number | null;
|
||||
};
|
||||
servers: ServerRow[];
|
||||
files: FileRow[];
|
||||
@@ -149,6 +149,7 @@
|
||||
</section>
|
||||
|
||||
{#if detail.watchdogIncidentId}<p class="watchdog-source">Started by Maintenance · <a href={contextHref(`/maintenance/${encodeURIComponent(detail.watchdogIncidentId)}`, $page.url)}>View incident and recovery history</a></p>{/if}
|
||||
{#if detail.deployment.beta_id}<p class="watchdog-source">Started from <a href={contextHref(`/betas/${detail.deployment.beta_id}`, $page.url)}>Beta #{detail.deployment.beta_id}</a> · Review its files, checks, and test servers.</p>{/if}
|
||||
|
||||
{#if detail.servers.some(server => !server.server_deleted_at && ['held', 'manual'].includes(server.restart_state))}
|
||||
<div><Button onclick={resume} loading={resuming} disabled={resuming}>Resume when empty</Button><p>Apply held changes and restart after the safety checks pass.</p></div>
|
||||
|
||||
Reference in New Issue
Block a user