fix(engine-room): suporte a modo Docker para consultar serviços externos
- Modifica kernel-adapter.ts para detectar DOCKER_MODE - Em modo Docker, consulta serviços via HTTP health check direto - Em modo normal, continua usando Kernel local (porta 5001) - Resolve problema de apenas 2 motores aparecendo em producao
This commit is contained in:
parent
5ba2bd963d
commit
1fdeb72492
|
|
@ -1,10 +1,26 @@
|
||||||
/**
|
/**
|
||||||
* Kernel Adapter - Ponte entre Engine Room e Arcadia Kernel
|
* Kernel Adapter - Ponte entre Engine Room e Arcadia Kernel
|
||||||
* Traduz chamadas do formato antigo para o novo Kernel (porta 5001)
|
*
|
||||||
|
* MODO NORMAL: Conecta ao Kernel local (porta 5001) para gerenciar serviços
|
||||||
|
* MODO DOCKER: Consulta diretamente os serviços externos via HTTP health check
|
||||||
|
*
|
||||||
|
* A detecção de modo Docker é automática via variável DOCKER_MODE
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const KERNEL_BASE_URL = 'http://localhost:5001/api/kernel';
|
const KERNEL_BASE_URL = 'http://localhost:5001/api/kernel';
|
||||||
|
|
||||||
|
// Detectar modo Docker - em produção os serviços rodam em containers separados
|
||||||
|
const IS_DOCKER_MODE = process.env.DOCKER_MODE === 'true';
|
||||||
|
|
||||||
|
// URLs dos serviços externos em modo Docker (vindas das env vars)
|
||||||
|
const SERVICE_URLS: Record<string, string> = {
|
||||||
|
"contabil": process.env.CONTABIL_PYTHON_URL || 'http://contabil:8003',
|
||||||
|
"bi-engine": process.env.BI_PYTHON_URL || 'http://bi:8004',
|
||||||
|
"automation-engine": process.env.AUTOMATION_PYTHON_URL || 'http://automation:8005',
|
||||||
|
"fisco": process.env.FISCO_PYTHON_URL || 'http://fisco:8002',
|
||||||
|
"communication": process.env.MIROFLOW_HOST ? `http://${process.env.MIROFLOW_HOST}:${process.env.MIROFLOW_PORT || 8006}` : 'http://miroflow:8006',
|
||||||
|
};
|
||||||
|
|
||||||
// Mapeamento: Nome do Engine Room -> ID do Kernel
|
// Mapeamento: Nome do Engine Room -> ID do Kernel
|
||||||
// NOTA: metaset NÃO está aqui - é gerenciado externamente (Java)
|
// NOTA: metaset NÃO está aqui - é gerenciado externamente (Java)
|
||||||
const ENGINE_TO_KERNEL_ID: Record<string, string> = {
|
const ENGINE_TO_KERNEL_ID: Record<string, string> = {
|
||||||
|
|
@ -42,6 +58,15 @@ const DISPLAY_NAME_MAP: Record<string, string> = {
|
||||||
"node-communication": "Motor Comunicacao",
|
"node-communication": "Motor Comunicacao",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Configurações dos engines para health check em modo Docker
|
||||||
|
const ENGINE_CONFIG: Record<string, { port: number; type: string; healthPath: string }> = {
|
||||||
|
"contabil": { port: 8003, type: "python", healthPath: "/health" },
|
||||||
|
"bi-engine": { port: 8004, type: "python", healthPath: "/health" },
|
||||||
|
"automation-engine": { port: 8005, type: "python", healthPath: "/health" },
|
||||||
|
"fisco": { port: 8002, type: "python", healthPath: "/health" },
|
||||||
|
"communication": { port: 8006, type: "node", healthPath: "/health" },
|
||||||
|
};
|
||||||
|
|
||||||
interface KernelServiceState {
|
interface KernelServiceState {
|
||||||
config: {
|
config: {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -71,6 +96,87 @@ interface EngineStatus {
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Health check direto de um serviço (modo Docker)
|
||||||
|
async function checkServiceHealth(engineName: string): Promise<EngineStatus> {
|
||||||
|
const config = ENGINE_CONFIG[engineName];
|
||||||
|
const serviceUrl = SERVICE_URLS[engineName];
|
||||||
|
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
||||||
|
|
||||||
|
if (!config || !serviceUrl) {
|
||||||
|
return {
|
||||||
|
name: engineName,
|
||||||
|
displayName: DISPLAY_NAME_MAP[kernelId || engineName] || engineName,
|
||||||
|
type: "unknown",
|
||||||
|
port: 0,
|
||||||
|
category: CATEGORY_MAP[kernelId || engineName] || "data",
|
||||||
|
description: `Serviço ${engineName}`,
|
||||||
|
status: "offline",
|
||||||
|
error: "Configuração não encontrada",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const start = Date.now();
|
||||||
|
const response = await fetch(`${serviceUrl}${config.healthPath}`, {
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
clearTimeout(timeout);
|
||||||
|
const elapsed = Date.now() - start;
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json().catch(() => ({}));
|
||||||
|
return {
|
||||||
|
name: engineName,
|
||||||
|
displayName: DISPLAY_NAME_MAP[kernelId] || engineName,
|
||||||
|
type: config.type,
|
||||||
|
port: config.port,
|
||||||
|
category: CATEGORY_MAP[kernelId] || "data",
|
||||||
|
description: `Serviço ${engineName}`,
|
||||||
|
status: "online",
|
||||||
|
responseTime: elapsed,
|
||||||
|
details: {
|
||||||
|
url: serviceUrl,
|
||||||
|
health: "healthy",
|
||||||
|
...data,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
name: engineName,
|
||||||
|
displayName: DISPLAY_NAME_MAP[kernelId] || engineName,
|
||||||
|
type: config.type,
|
||||||
|
port: config.port,
|
||||||
|
category: CATEGORY_MAP[kernelId] || "data",
|
||||||
|
description: `Serviço ${engineName}`,
|
||||||
|
status: "error",
|
||||||
|
responseTime: elapsed,
|
||||||
|
details: {
|
||||||
|
url: serviceUrl,
|
||||||
|
httpStatus: response.status,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
return {
|
||||||
|
name: engineName,
|
||||||
|
displayName: DISPLAY_NAME_MAP[kernelId] || engineName,
|
||||||
|
type: config.type,
|
||||||
|
port: config.port,
|
||||||
|
category: CATEGORY_MAP[kernelId] || "data",
|
||||||
|
description: `Serviço ${engineName}`,
|
||||||
|
status: "offline",
|
||||||
|
error: err.name === "AbortError" ? "timeout" : err.message,
|
||||||
|
details: {
|
||||||
|
url: serviceUrl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper para fazer requests ao Kernel
|
// Helper para fazer requests ao Kernel
|
||||||
async function kernelRequest(endpoint: string, method = 'GET', body?: any): Promise<any> {
|
async function kernelRequest(endpoint: string, method = 'GET', body?: any): Promise<any> {
|
||||||
try {
|
try {
|
||||||
|
|
@ -114,8 +220,38 @@ function mapStatus(kernelStatus: string, health?: string): "online" | "offline"
|
||||||
return 'offline';
|
return 'offline';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obter todos os serviços do Kernel formatados para Engine Room
|
// Obter todos os serviços formatados para Engine Room
|
||||||
export async function getKernelEngines(): Promise<EngineStatus[]> {
|
export async function getKernelEngines(): Promise<EngineStatus[]> {
|
||||||
|
// MODO DOCKER: Consulta diretamente os serviços externos
|
||||||
|
if (IS_DOCKER_MODE) {
|
||||||
|
console.log('[KernelAdapter] Modo Docker detectado - consultando serviços externos diretamente');
|
||||||
|
|
||||||
|
const engineNames = Object.keys(ENGINE_CONFIG);
|
||||||
|
const results = await Promise.allSettled(
|
||||||
|
engineNames.map(name => checkServiceHealth(name))
|
||||||
|
);
|
||||||
|
|
||||||
|
return results.map((result, index) => {
|
||||||
|
if (result.status === 'fulfilled') {
|
||||||
|
return result.value;
|
||||||
|
} else {
|
||||||
|
const name = engineNames[index];
|
||||||
|
const kernelId = ENGINE_TO_KERNEL_ID[name];
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
displayName: DISPLAY_NAME_MAP[kernelId] || name,
|
||||||
|
type: ENGINE_CONFIG[name]?.type || "unknown",
|
||||||
|
port: ENGINE_CONFIG[name]?.port || 0,
|
||||||
|
category: CATEGORY_MAP[kernelId] || "data",
|
||||||
|
description: `Serviço ${name}`,
|
||||||
|
status: "offline" as const,
|
||||||
|
error: result.reason?.message || "Erro desconhecido",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODO NORMAL: Conecta ao Kernel local
|
||||||
try {
|
try {
|
||||||
const response = await kernelRequest('/services');
|
const response = await kernelRequest('/services');
|
||||||
const services: KernelServiceState[] = response.data || [];
|
const services: KernelServiceState[] = response.data || [];
|
||||||
|
|
@ -148,6 +284,23 @@ export async function getKernelEngines(): Promise<EngineStatus[]> {
|
||||||
|
|
||||||
// Obter info de um serviço específico (formato compatível com antigo)
|
// Obter info de um serviço específico (formato compatível com antigo)
|
||||||
export async function getKernelServiceInfo(engineName: string): Promise<any> {
|
export async function getKernelServiceInfo(engineName: string): Promise<any> {
|
||||||
|
// MODO DOCKER: Retorna info baseada no health check
|
||||||
|
if (IS_DOCKER_MODE) {
|
||||||
|
const health = await checkServiceHealth(engineName);
|
||||||
|
return {
|
||||||
|
name: engineName,
|
||||||
|
port: health.port,
|
||||||
|
status: health.status === 'online' ? 'running' : 'stopped',
|
||||||
|
startedAt: null,
|
||||||
|
restartCount: 0,
|
||||||
|
uptime: 0,
|
||||||
|
logLines: 0,
|
||||||
|
health: health.status === 'online' ? 'healthy' : 'unhealthy',
|
||||||
|
mode: 'docker-external',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODO NORMAL: Conecta ao Kernel
|
||||||
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
||||||
if (!kernelId) return null;
|
if (!kernelId) return null;
|
||||||
|
|
||||||
|
|
@ -166,7 +319,7 @@ export async function getKernelServiceInfo(engineName: string): Promise<any> {
|
||||||
uptime: service.startTime
|
uptime: service.startTime
|
||||||
? Math.round((Date.now() - new Date(service.startTime).getTime()) / 1000)
|
? Math.round((Date.now() - new Date(service.startTime).getTime()) / 1000)
|
||||||
: 0,
|
: 0,
|
||||||
logLines: 0, // Kernel não expõe isso diretamente
|
logLines: 0,
|
||||||
health: service.health,
|
health: service.health,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -177,6 +330,12 @@ export async function getKernelServiceInfo(engineName: string): Promise<any> {
|
||||||
|
|
||||||
// Obter logs de um serviço (formato compatível)
|
// Obter logs de um serviço (formato compatível)
|
||||||
export async function getKernelServiceLogs(engineName: string, lines = 50): Promise<string[]> {
|
export async function getKernelServiceLogs(engineName: string, lines = 50): Promise<string[]> {
|
||||||
|
// MODO DOCKER: Logs não disponíveis via HTTP
|
||||||
|
if (IS_DOCKER_MODE) {
|
||||||
|
return [`[Docker Mode] Logs não disponíveis para ${engineName}. Use 'docker logs' no container.`];
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODO NORMAL: Conecta ao Kernel
|
||||||
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
||||||
if (!kernelId) return [];
|
if (!kernelId) return [];
|
||||||
|
|
||||||
|
|
@ -197,6 +356,13 @@ export async function getKernelServiceLogs(engineName: string, lines = 50): Prom
|
||||||
|
|
||||||
// Iniciar serviço
|
// Iniciar serviço
|
||||||
export async function startKernelService(engineName: string): Promise<boolean> {
|
export async function startKernelService(engineName: string): Promise<boolean> {
|
||||||
|
// MODO DOCKER: Não pode iniciar container externo
|
||||||
|
if (IS_DOCKER_MODE) {
|
||||||
|
console.log(`[KernelAdapter] Modo Docker: não é possível iniciar ${engineName} (container externo)`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODO NORMAL: Conecta ao Kernel
|
||||||
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
||||||
if (!kernelId) return false;
|
if (!kernelId) return false;
|
||||||
|
|
||||||
|
|
@ -211,6 +377,13 @@ export async function startKernelService(engineName: string): Promise<boolean> {
|
||||||
|
|
||||||
// Parar serviço
|
// Parar serviço
|
||||||
export async function stopKernelService(engineName: string): Promise<boolean> {
|
export async function stopKernelService(engineName: string): Promise<boolean> {
|
||||||
|
// MODO DOCKER: Não pode parar container externo
|
||||||
|
if (IS_DOCKER_MODE) {
|
||||||
|
console.log(`[KernelAdapter] Modo Docker: não é possível parar ${engineName} (container externo)`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODO NORMAL: Conecta ao Kernel
|
||||||
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
||||||
if (!kernelId) return false;
|
if (!kernelId) return false;
|
||||||
|
|
||||||
|
|
@ -225,6 +398,13 @@ export async function stopKernelService(engineName: string): Promise<boolean> {
|
||||||
|
|
||||||
// Reiniciar serviço
|
// Reiniciar serviço
|
||||||
export async function restartKernelService(engineName: string): Promise<boolean> {
|
export async function restartKernelService(engineName: string): Promise<boolean> {
|
||||||
|
// MODO DOCKER: Não pode reiniciar container externo
|
||||||
|
if (IS_DOCKER_MODE) {
|
||||||
|
console.log(`[KernelAdapter] Modo Docker: não é possível reiniciar ${engineName} (container externo)`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODO NORMAL: Conecta ao Kernel
|
||||||
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
const kernelId = ENGINE_TO_KERNEL_ID[engineName];
|
||||||
if (!kernelId) return false;
|
if (!kernelId) return false;
|
||||||
|
|
||||||
|
|
@ -239,6 +419,14 @@ export async function restartKernelService(engineName: string): Promise<boolean>
|
||||||
|
|
||||||
// Verificar health geral do Kernel
|
// Verificar health geral do Kernel
|
||||||
export async function getKernelHealth(): Promise<any> {
|
export async function getKernelHealth(): Promise<any> {
|
||||||
|
if (IS_DOCKER_MODE) {
|
||||||
|
return {
|
||||||
|
mode: 'docker',
|
||||||
|
message: 'Modo Docker - serviços gerenciados externamente',
|
||||||
|
services: Object.keys(ENGINE_CONFIG),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await kernelRequest('/health');
|
const response = await kernelRequest('/health');
|
||||||
return response.data;
|
return response.data;
|
||||||
|
|
@ -248,8 +436,14 @@ export async function getKernelHealth(): Promise<any> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verificar se Kernel está disponível
|
// Verificar se Kernel está disponível (ou modo Docker está ativo)
|
||||||
export async function isKernelAvailable(): Promise<boolean> {
|
export async function isKernelAvailable(): Promise<boolean> {
|
||||||
|
// MODO DOCKER: Sempre "disponível" (serviços externos)
|
||||||
|
if (IS_DOCKER_MODE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODO NORMAL: Verifica se Kernel responde
|
||||||
try {
|
try {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => controller.abort(), 3000);
|
const timeout = setTimeout(() => controller.abort(), 3000);
|
||||||
|
|
@ -265,4 +459,4 @@ export async function isKernelAvailable(): Promise<boolean> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[KernelAdapter] Adapter loaded - proxying to localhost:5001');
|
console.log(`[KernelAdapter] Adapter loaded - Modo: ${IS_DOCKER_MODE ? 'DOCKER (serviços externos)' : 'Kernel local (porta 5001)'}`);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue