Phase3
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user