Show instance and player counts on hosts
This commit is contained in:
@@ -1,16 +1,29 @@
|
||||
import { db } from '../db/index.js';
|
||||
import { listHosts } from './index.js';
|
||||
import { presentHostPreparation } from './preparation.js';
|
||||
import { observationReader } from '../fleet/status.js';
|
||||
import type { ServerRow } from '../fleet/servers.js';
|
||||
import type { Host } from '../../hosts.js';
|
||||
|
||||
export interface HostOverview extends Host { serverCount: number }
|
||||
export interface HostOverview extends Host { serverCount: number; playerCount: number; unreportedInstances: number }
|
||||
|
||||
/** Count assigned servers without loading their telemetry or historical placements. */
|
||||
/** Read current placements once and use the fleet's shared telemetry/RCON selection. */
|
||||
export function hostOverview(): HostOverview[] {
|
||||
const hosts = listHosts();
|
||||
const counts = new Map((db().prepare(`SELECT i.host_id, COUNT(*) AS count FROM native_instances i
|
||||
const instances = db().prepare(`SELECT s.*, i.host_id FROM native_instances i
|
||||
JOIN servers s ON s.name = i.server
|
||||
WHERE i.retired_at IS NULL AND s.destroyed_at IS NULL
|
||||
GROUP BY i.host_id`).all() as { host_id: string; count: number }[]).map(row => [row.host_id, row.count]));
|
||||
return hosts.map(host => ({ ...presentHostPreparation(host), serverCount: counts.get(host.id) ?? 0 }));
|
||||
WHERE i.retired_at IS NULL AND s.destroyed_at IS NULL`).all() as (ServerRow & { host_id: string })[];
|
||||
const counts = new Map<string, { serverCount: number; playerCount: number; unreportedInstances: number }>();
|
||||
if (instances.length) {
|
||||
const observe = observationReader();
|
||||
for (const instance of instances) {
|
||||
const count = counts.get(instance.host_id) ?? { serverCount: 0, playerCount: 0, unreportedInstances: 0 };
|
||||
count.serverCount++;
|
||||
const observation = observe(instance);
|
||||
if (observation.health === 'online' && observation.last_players !== null) count.playerCount += observation.last_players;
|
||||
else count.unreportedInstances++;
|
||||
counts.set(instance.host_id, count);
|
||||
}
|
||||
}
|
||||
return hosts.map(host => ({ ...presentHostPreparation(host), ...(counts.get(host.id) ?? { serverCount: 0, playerCount: 0, unreportedInstances: 0 }) }));
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
import { contextHref } from '$lib/navigation-context.js';
|
||||
import { hostListStatus, hostReadingsStale, hostCpuUsage, hostUsedSpace } from '$ui/host-overview.js';
|
||||
import Badge from './primitives/Badge.svelte';
|
||||
let { hosts, now, returnTo = '/hosts' }: { hosts: (Host & { serverCount: number })[]; now: number; returnTo?: string } = $props();
|
||||
let { hosts, now, returnTo = '/hosts' }: { hosts: (Host & { serverCount: number; playerCount: number; unreportedInstances: number })[]; now: number; returnTo?: string } = $props();
|
||||
</script>
|
||||
|
||||
<div class="table-wrap"><table>
|
||||
<thead><tr><th>Host</th><th>Status</th><th title="Assigned servers, including stopped instances">Servers</th><th>CPU</th><th>Memory <span>used / total</span></th><th>Disk <span>used / total</span></th></tr></thead>
|
||||
<thead><tr><th>Host</th><th>Status</th><th title="Assigned instances, including stopped instances">Instances</th><th title="Players reported by online instances">Players</th><th>CPU</th><th>Memory <span>used / total</span></th><th>Disk <span>used / total</span></th></tr></thead>
|
||||
<tbody>{#each hosts as host (host.id)}
|
||||
{@const status = hostListStatus(host, now)}
|
||||
{@const stale = hostReadingsStale(host, now)}
|
||||
@@ -15,7 +15,8 @@
|
||||
<td><a class="name" href={contextHref(`/hosts/${host.id}`, returnTo)}>{host.name}</a>{#if host.hostname && host.hostname !== host.name}<span class="secondary">{host.hostname}</span>{/if}
|
||||
{#if stale && host.lastHeartbeat}<span class="secondary">Resource readings stale</span>{/if}</td>
|
||||
<td class="status"><Badge variant={status.tone}>{status.label}</Badge>{#if status.detail}<a class="reason" href={contextHref(`/hosts/${host.id}?tab=${status.tab}`, returnTo)}>{status.detail}</a>{/if}</td>
|
||||
<td><a href={contextHref(`/hosts/${host.id}?tab=servers`, returnTo)} aria-label={`${host.serverCount} assigned servers on ${host.name}`}>{host.serverCount}</a></td>
|
||||
<td><a href={contextHref(`/hosts/${host.id}?tab=servers`, returnTo)} aria-label={`${host.serverCount} assigned instances on ${host.name}`}>{host.serverCount}</a></td>
|
||||
<td title={host.unreportedInstances ? `Player counts unavailable for ${host.unreportedInstances} of ${host.serverCount} instances` : 'Players reported by online instances'}>{host.unreportedInstances === host.serverCount && host.serverCount > 0 ? '—' : `${host.playerCount}${host.unreportedInstances ? '+' : ''}`}</td>
|
||||
<td class:stale>{hostCpuUsage(host.inventory.cpuUsagePercent)}</td>
|
||||
<td class:stale>{hostUsedSpace(host.inventory.memoryTotal, host.inventory.memoryAvailable)}</td>
|
||||
<td class:stale>{hostUsedSpace(host.inventory.storageTotal, host.inventory.storageAvailable)}</td>
|
||||
|
||||
Reference in New Issue
Block a user