Fix native health polling and isolate file operation traffic
This commit is contained in:
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
|
||||
import { db } from '../db/index.js';
|
||||
import { getHost, queueHostOperation, waitHostOperation } from '../hosts/index.js';
|
||||
import { trackActivity } from '../pipeline/activity.js';
|
||||
import { latestGameSubmission } from '../fleet/lifecycle-evidence.js';
|
||||
|
||||
export interface NativeInstance {
|
||||
id: string; server: string; host_id: string; generation: number; library_id: string;
|
||||
@@ -17,10 +18,27 @@ export function nativeDeployment(id: string): boolean {
|
||||
return !!db().prepare('SELECT 1 FROM native_provisioning WHERE deployment_id = ?').get(id);
|
||||
}
|
||||
|
||||
/** Only the agent receives host paths, Docker access, or game credentials. */
|
||||
const observations = new Map<string, Promise<unknown>>();
|
||||
const readKinds = new Set(['instance.inspect', 'files.read', 'files.stat', 'files.list', 'logs.read']);
|
||||
|
||||
/** Share overlapping observations only, never completed results or mutations. */
|
||||
export async function nativeRequest<T = Record<string, unknown>>(instance: NativeInstance, kind: string, payload: Record<string, unknown> = {}, key?: string): Promise<T> {
|
||||
const host = getHost(instance.host_id);
|
||||
if (!host || host.connectionState !== 'online') throw new Error('Host agent is offline; instance state is unknown.');
|
||||
const readOnly = readKinds.has(kind) || (kind === 'rcon.execute' && ['status', 'getstatus', 'version'].includes(String(payload.command)));
|
||||
if (key || !readOnly) return executeRequest<T>(instance, kind, payload, key);
|
||||
// A probe after a lifecycle submission must not join an old-process read.
|
||||
const identity = JSON.stringify([instance.host_id, instance.id, instance.generation, instance.server,
|
||||
latestGameSubmission(instance.server), kind, payload]);
|
||||
const pending = observations.get(identity);
|
||||
if (pending) return pending as Promise<T>;
|
||||
const request = executeRequest<T>(instance, kind, payload).finally(() => { observations.delete(identity); });
|
||||
observations.set(identity, request);
|
||||
return request;
|
||||
}
|
||||
|
||||
/** Only the agent receives host paths, Docker access, or game credentials. */
|
||||
async function executeRequest<T>(instance: NativeInstance, kind: string, payload: Record<string, unknown>, key?: string): Promise<T> {
|
||||
const operation = queueHostOperation(instance.host_id, kind, payload, {
|
||||
instanceId: instance.id, generation: instance.generation, idempotencyKey: key ?? randomUUID()
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user