Complete provisioning job detail pages and lifecycle evidence
This commit is contained in:
@@ -36,30 +36,48 @@ export function telemetryEvidence(server: string, after: string | null = latestG
|
||||
|
||||
/** Positive occupancy from either source always vetoes an empty-server claim. */
|
||||
export async function probePlayerCount(server: string): Promise<number | null> {
|
||||
return (await probePlayerEvidence(server)).players;
|
||||
}
|
||||
|
||||
export interface PlayerEvidence {
|
||||
players: number | null;
|
||||
source: 'rcon' | 'telemetry' | null;
|
||||
observedAt: string;
|
||||
receivedAt?: string;
|
||||
}
|
||||
|
||||
/** The same eligibility decision, with provenance from the probes already performed. */
|
||||
export async function probePlayerEvidence(server: string): Promise<PlayerEvidence> {
|
||||
const fromTelemetry = (value: TelemetryEvidence): PlayerEvidence => ({
|
||||
players: value.players, source: 'telemetry', observedAt: new Date().toISOString(), receivedAt: value.receivedAt
|
||||
});
|
||||
const occupied = telemetryEvidence(server);
|
||||
if (occupied && occupied.players > 0) return occupied.players;
|
||||
if (occupied && occupied.players > 0) return fromTelemetry(occupied);
|
||||
const players = await rconGetPlayerCount(server).catch(() => null);
|
||||
// Read after the network request: a player may join while RCON is pending.
|
||||
const telemetry = telemetryEvidence(server);
|
||||
if (players !== null && players > 0) return players;
|
||||
if (telemetry && telemetry.players > 0) return telemetry.players;
|
||||
return players === 0 || telemetry?.players === 0 ? 0 : null;
|
||||
if (players !== null && players > 0) return { players, source: 'rcon', observedAt: new Date().toISOString() };
|
||||
if (telemetry && telemetry.players > 0) return fromTelemetry(telemetry);
|
||||
if (players === 0) return { players, source: 'rcon', observedAt: new Date().toISOString() };
|
||||
if (telemetry?.players === 0) return fromTelemetry(telemetry);
|
||||
return { players: null, source: null, observedAt: new Date().toISOString() };
|
||||
}
|
||||
|
||||
/** Komodo readiness remains a caller prerequisite. Old-process telemetry is excluded. */
|
||||
export async function probeServerStatus(server: string, after?: string | null): Promise<
|
||||
{ ok: true; players: number; map: string | null; source: 'rcon' | 'telemetry' } | { ok: false; reason: string }
|
||||
{ ok: true; players: number; map: string | null; source: 'rcon' | 'telemetry'; observedAt: string; receivedAt?: string } | { ok: false; reason: string; observedAt: string }
|
||||
> {
|
||||
const current = telemetryEvidence(server, after);
|
||||
if (current) return { ok: true, ...current, source: 'telemetry' };
|
||||
if (current) return { ok: true, ...current, source: 'telemetry', observedAt: new Date().toISOString() };
|
||||
let reason = 'RCON unavailable';
|
||||
try {
|
||||
const rcon = await rconStatusResult(server);
|
||||
const latest = telemetryEvidence(server, after);
|
||||
if (latest) return { ok: true, ...latest, source: 'telemetry' };
|
||||
if (rcon.ok) return { ok: true, players: rcon.status.players, map: rcon.status.map, source: 'rcon' };
|
||||
if (latest) return { ok: true, ...latest, source: 'telemetry', observedAt: new Date().toISOString() };
|
||||
if (rcon.ok) return { ok: true, players: rcon.status.players, map: rcon.status.map, source: 'rcon', observedAt: new Date().toISOString() };
|
||||
reason = rcon.reason;
|
||||
} catch (err) { reason = (err as Error).message; }
|
||||
const telemetry = telemetryEvidence(server, after);
|
||||
return telemetry ? { ok: true, ...telemetry, source: 'telemetry' } : { ok: false, reason };
|
||||
return telemetry ? { ok: true, ...telemetry, source: 'telemetry', observedAt: new Date().toISOString() }
|
||||
: { ok: false, reason, observedAt: new Date().toISOString() };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user