Phase3
Some checks failed
CI / Node — lint, typecheck, test, build (push) Failing after 9s
CI / Go — node-agent tests (push) Failing after 9s
CI / Go — edge-gateway build (push) Successful in 13s

This commit is contained in:
smueller
2026-06-26 12:17:26 +02:00
parent c4077d4673
commit 9b061c3ee7
92 changed files with 4128 additions and 146 deletions

View File

@@ -13,6 +13,7 @@ import { verifyTokenHash } from '@hexahost/auth';
import type { GameServer, GameNode, Prisma } from '@hexahost/database';
import { PrismaService } from '../prisma/prisma.service';
import { ConsoleSessionService } from '../servers/console/console-session.service';
import { REDIS_CLIENT, ServersService } from '../servers/servers.service';
type GameServerStatus = GameServer['status'];
@@ -47,6 +48,7 @@ export class NodesService {
constructor(
private readonly prisma: PrismaService,
private readonly serversService: ServersService,
private readonly consoleSessionService: ConsoleSessionService,
@Inject(REDIS_CLIENT) private readonly redis: Redis,
) {}
@@ -145,12 +147,21 @@ export class NodesService {
case 'server.state':
await this.handleServerState(envelope);
break;
case 'server.log':
this.handleServerLog(envelope);
break;
case 'server.operation.completed':
await this.handleOperationCompleted(envelope);
break;
case 'server.operation.failed':
await this.handleOperationFailed(envelope);
break;
case 'server.files.list.result':
case 'server.files.read.result':
case 'server.files.write.result':
case 'server.command.result':
await this.handleDirectResult(envelope);
break;
default:
this.logger.debug(`Ignored agent message type ${envelope.type ?? 'unknown'}`);
}
@@ -210,28 +221,53 @@ export class NodesService {
});
}
private handleServerLog(envelope: AgentEnvelope): void {
const serverId = envelope.payload?.['serverId'];
const line =
envelope.payload?.['line'] ?? envelope.payload?.['message'];
if (typeof serverId === 'string' && typeof line === 'string') {
this.consoleSessionService.broadcast(serverId, line);
}
}
private async handleDirectResult(envelope: AgentEnvelope): Promise<void> {
const messageId = envelope.correlationId ?? envelope.messageId;
if (typeof messageId !== 'string') {
return;
}
await this.publishNodeResponse(messageId, {
success: true,
result: envelope.payload?.['result'] ?? envelope.payload,
});
}
private async handleOperationCompleted(
envelope: AgentEnvelope,
): Promise<void> {
const serverId = envelope.payload?.['serverId'];
const operation = envelope.payload?.['operation'];
const messageId = envelope.messageId;
const operation =
envelope.payload?.['operation'] ?? envelope.payload?.['resultCode'];
const messageId = envelope.correlationId ?? envelope.messageId;
const result = envelope.payload?.['result'];
const isBridgeOperation = this.isBridgeOperation(operation);
if (typeof serverId !== 'string') {
return;
}
const target = this.resolveCompletedStatus(operation);
if (target) {
await this.serversService.applyAgentStatus(serverId, target, {
correlationId: envelope.correlationId,
reason: `agent operation completed: ${String(operation ?? 'unknown')}`,
});
if (typeof serverId === 'string' && !isBridgeOperation) {
const target = this.resolveCompletedStatus(operation);
if (target) {
await this.serversService.applyAgentStatus(serverId, target, {
correlationId: envelope.correlationId,
reason: `agent operation completed: ${String(operation ?? 'unknown')}`,
});
}
}
if (typeof messageId === 'string') {
await this.publishNodeResponse(messageId, {
success: true,
result,
resultCode: typeof operation === 'string' ? operation : 'completed',
});
}
@@ -239,9 +275,12 @@ export class NodesService {
private async handleOperationFailed(envelope: AgentEnvelope): Promise<void> {
const serverId = envelope.payload?.['serverId'];
const messageId = envelope.messageId;
const operation =
envelope.payload?.['operation'] ?? envelope.payload?.['resultCode'];
const messageId = envelope.correlationId ?? envelope.messageId;
const isBridgeOperation = this.isBridgeOperation(operation);
if (typeof serverId === 'string') {
if (typeof serverId === 'string' && !isBridgeOperation) {
await this.serversService.applyAgentStatus(serverId, 'ERROR', {
correlationId: envelope.correlationId,
reason: 'agent operation failed',
@@ -271,10 +310,25 @@ export class NodesService {
}
}
private async publishNodeResponse(
private isBridgeOperation(operation: unknown): boolean {
if (typeof operation !== 'string') {
return false;
}
const normalized = operation.replace(/^server\./, '');
return (
normalized === 'files.list' ||
normalized === 'files.read' ||
normalized === 'files.write' ||
normalized === 'command'
);
}
async publishNodeResponse(
messageId: string,
response: {
success: boolean;
result?: unknown;
resultCode?: string;
errorCode?: string;
errorMessage?: string;