feat(kernel): implementa XOS Discovery Provider
- Cria XOSDiscovery.ts para descobrir serviços XOS - Descobre filas de atendimento ativas - Descobre integrações (WhatsApp, Email) - Descobre automações ativas - Integra ao Kernel como provider de discovery O XOS Discovery consulta o banco de dados para encontrar serviços configurados no XOS (CRM/Atendimento/Marketing). Refs: xos, discovery, kernel-semana-5
This commit is contained in:
parent
aa83dbdbce
commit
b3166173df
|
|
@ -0,0 +1,323 @@
|
||||||
|
/**
|
||||||
|
* XOS Discovery Provider
|
||||||
|
* Descobre serviços e integrações do XOS (Experience Operating System)
|
||||||
|
*
|
||||||
|
* O XOS é o sistema operacional de experiência do cliente, unificando:
|
||||||
|
* - CRM (Pipeline, Contatos, Empresas)
|
||||||
|
* - Atendimento (Inbox, Tickets, Filas)
|
||||||
|
* - Marketing (Campanhas, Automações)
|
||||||
|
* - Integrações (WhatsApp, Email, SMS)
|
||||||
|
*
|
||||||
|
* Este provider consulta o banco de dados para descobrir:
|
||||||
|
* 1. Filas de atendimento ativas
|
||||||
|
* 2. Integrações de comunicação configuradas
|
||||||
|
* 3. Automações ativas
|
||||||
|
* 4. Canais de entrada (WhatsApp, Email, Chat)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { DiscoveryProvider, RegisteredService, ServiceCategory, ServiceCapability } from '../registry/types';
|
||||||
|
import { HealthStatus } from '../types';
|
||||||
|
import { db } from '../../../db';
|
||||||
|
import { sql } from 'drizzle-orm';
|
||||||
|
|
||||||
|
export interface XOSDiscoveryOptions {
|
||||||
|
// Opções futuras: filtros por tenant, etc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tipos de serviços XOS que podem ser descobertos
|
||||||
|
interface XOSQueue {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
is_active: boolean;
|
||||||
|
users_count?: number;
|
||||||
|
open_conversations?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface XOSIntegration {
|
||||||
|
id: number;
|
||||||
|
type: string; // 'whatsapp', 'email', 'sms', 'chat'
|
||||||
|
name: string;
|
||||||
|
status: string;
|
||||||
|
config?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface XOSAutomation {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
trigger_type: string;
|
||||||
|
is_active: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class XOSDiscovery implements DiscoveryProvider {
|
||||||
|
public readonly name = 'XOSDiscovery';
|
||||||
|
private options: XOSDiscoveryOptions;
|
||||||
|
|
||||||
|
constructor(options: XOSDiscoveryOptions = {}) {
|
||||||
|
this.options = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se o XOS está disponível (se as tabelas existem)
|
||||||
|
*/
|
||||||
|
async isAvailable(): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
// Verifica se a tabela xos_queues existe
|
||||||
|
const result = await db.execute(sql`
|
||||||
|
SELECT EXISTS (
|
||||||
|
SELECT FROM information.tables
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
AND table_name = 'xos_queues'
|
||||||
|
) as exists
|
||||||
|
`);
|
||||||
|
return result.rows[0]?.exists === true;
|
||||||
|
} catch {
|
||||||
|
// Se der erro, assume que não está disponível
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descobre todos os serviços XOS
|
||||||
|
*/
|
||||||
|
async discover(): Promise<RegisteredService[]> {
|
||||||
|
const services: RegisteredService[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Descobrir filas de atendimento ativas
|
||||||
|
const queues = await this.discoverQueues();
|
||||||
|
services.push(...queues);
|
||||||
|
|
||||||
|
// 2. Descobrir integrações de comunicação
|
||||||
|
const integrations = await this.discoverIntegrations();
|
||||||
|
services.push(...integrations);
|
||||||
|
|
||||||
|
// 3. Descobrir automações ativas
|
||||||
|
const automations = await this.discoverAutomations();
|
||||||
|
services.push(...automations);
|
||||||
|
|
||||||
|
console.log(`[XOSDiscovery] Descobertos ${services.length} serviços XOS`);
|
||||||
|
return services;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[XOSDiscovery] Erro ao descobrir serviços:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descobre filas de atendimento
|
||||||
|
*/
|
||||||
|
private async discoverQueues(): Promise<RegisteredService[]> {
|
||||||
|
try {
|
||||||
|
const result = await db.execute(sql`
|
||||||
|
SELECT
|
||||||
|
q.id,
|
||||||
|
q.name,
|
||||||
|
q.is_active,
|
||||||
|
(SELECT COUNT(*) FROM xos_queue_users qu WHERE qu.queue_id = q.id AND qu.is_active = true) as users_count,
|
||||||
|
(SELECT COUNT(*) FROM xos_conversations c WHERE c.queue_id = q.id AND c.status = 'open') as open_conversations
|
||||||
|
FROM xos_queues q
|
||||||
|
WHERE q.is_active = true
|
||||||
|
`);
|
||||||
|
|
||||||
|
const queues = result.rows as XOSQueue[];
|
||||||
|
|
||||||
|
return queues.map((queue): RegisteredService => ({
|
||||||
|
id: `xos-queue-${queue.id}`,
|
||||||
|
name: `Fila: ${queue.name}`,
|
||||||
|
displayName: queue.name,
|
||||||
|
type: 'node',
|
||||||
|
category: 'intelligence',
|
||||||
|
description: `Fila de atendimento XOS - ${queue.open_conversations || 0} conversas abertas, ${queue.users_count || 0} atendentes`,
|
||||||
|
host: 'localhost',
|
||||||
|
port: 5000, // Porta principal da aplicação
|
||||||
|
endpoint: 'http://localhost:5000',
|
||||||
|
healthCheckPath: '/api/xos/health',
|
||||||
|
healthStatus: queue.is_active ? 'healthy' : 'unhealthy',
|
||||||
|
lastHealthCheck: new Date(),
|
||||||
|
capabilities: [
|
||||||
|
{ name: 'queue_management', version: '1.0', endpoints: ['/api/xos/queues'] },
|
||||||
|
{ name: 'conversation_routing', version: '1.0', endpoints: ['/api/xos/conversations'] },
|
||||||
|
{ name: 'sla_monitoring', version: '1.0', endpoints: ['/api/xos/sla'] },
|
||||||
|
],
|
||||||
|
requiresIA: false,
|
||||||
|
metadata: {
|
||||||
|
source: 'manual', // XOS é configurado manualmente no banco
|
||||||
|
discoveredAt: new Date(),
|
||||||
|
lastUpdated: new Date(),
|
||||||
|
labels: {
|
||||||
|
'xos.type': 'queue',
|
||||||
|
'xos.queue_id': String(queue.id),
|
||||||
|
'xos.open_conversations': String(queue.open_conversations || 0),
|
||||||
|
'xos.active_users': String(queue.users_count || 0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enabled: queue.is_active,
|
||||||
|
version: '1.0.0',
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[XOSDiscovery] Erro ao descobrir filas:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descobre integrações de comunicação
|
||||||
|
*/
|
||||||
|
private async discoverIntegrations(): Promise<RegisteredService[]> {
|
||||||
|
const services: RegisteredService[] = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// WhatsApp Business (Miroflow)
|
||||||
|
const whatsappResult = await db.execute(sql`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM whatsapp_sessions
|
||||||
|
WHERE status = 'connected'
|
||||||
|
`);
|
||||||
|
const whatsappCount = parseInt(whatsappResult.rows[0]?.count || '0', 10);
|
||||||
|
|
||||||
|
if (whatsappCount > 0) {
|
||||||
|
services.push({
|
||||||
|
id: 'xos-whatsapp',
|
||||||
|
name: 'WhatsApp Business',
|
||||||
|
displayName: 'WhatsApp Business (Miroflow)',
|
||||||
|
type: 'node',
|
||||||
|
category: 'communication',
|
||||||
|
description: `Integração WhatsApp Business - ${whatsappCount} sessão(ões) conectada(s)`,
|
||||||
|
host: process.env.MIROFLOW_HOST || 'miroflow',
|
||||||
|
port: parseInt(process.env.MIROFLOW_PORT || '8006', 10),
|
||||||
|
endpoint: `http://${process.env.MIROFLOW_HOST || 'miroflow'}:${process.env.MIROFLOW_PORT || '8006'}`,
|
||||||
|
healthCheckPath: '/health',
|
||||||
|
healthStatus: 'unknown', // Será verificado pelo health check
|
||||||
|
lastHealthCheck: new Date(),
|
||||||
|
capabilities: [
|
||||||
|
{ name: 'whatsapp_messaging', version: '1.0', endpoints: ['/api/whatsapp/send'] },
|
||||||
|
{ name: 'whatsapp_webhook', version: '1.0', endpoints: ['/api/whatsapp/webhook'] },
|
||||||
|
{ name: 'template_messages', version: '1.0', endpoints: ['/api/whatsapp/templates'] },
|
||||||
|
],
|
||||||
|
requiresIA: true,
|
||||||
|
iaPolicies: ['communication', 'lgpd'],
|
||||||
|
metadata: {
|
||||||
|
source: 'manual',
|
||||||
|
discoveredAt: new Date(),
|
||||||
|
lastUpdated: new Date(),
|
||||||
|
labels: {
|
||||||
|
'xos.type': 'integration',
|
||||||
|
'xos.integration_type': 'whatsapp',
|
||||||
|
'xos.sessions_count': String(whatsappCount),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enabled: true,
|
||||||
|
version: '1.0.0',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email (se houver configuração)
|
||||||
|
const emailResult = await db.execute(sql`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM settings
|
||||||
|
WHERE key LIKE 'smtp_%' OR key LIKE 'email_%'
|
||||||
|
`);
|
||||||
|
const hasEmail = parseInt(emailResult.rows[0]?.count || '0', 10) > 0;
|
||||||
|
|
||||||
|
if (hasEmail) {
|
||||||
|
services.push({
|
||||||
|
id: 'xos-email',
|
||||||
|
name: 'Email Service',
|
||||||
|
displayName: 'Serviço de Email',
|
||||||
|
type: 'node',
|
||||||
|
category: 'communication',
|
||||||
|
description: 'Integração SMTP para envio de emails',
|
||||||
|
host: 'localhost',
|
||||||
|
port: 5000,
|
||||||
|
endpoint: 'http://localhost:5000',
|
||||||
|
healthCheckPath: '/api/health',
|
||||||
|
healthStatus: 'unknown',
|
||||||
|
lastHealthCheck: new Date(),
|
||||||
|
capabilities: [
|
||||||
|
{ name: 'email_sending', version: '1.0', endpoints: ['/api/email/send'] },
|
||||||
|
{ name: 'email_templates', version: '1.0', endpoints: ['/api/email/templates'] },
|
||||||
|
],
|
||||||
|
requiresIA: false,
|
||||||
|
metadata: {
|
||||||
|
source: 'manual',
|
||||||
|
discoveredAt: new Date(),
|
||||||
|
lastUpdated: new Date(),
|
||||||
|
labels: {
|
||||||
|
'xos.type': 'integration',
|
||||||
|
'xos.integration_type': 'email',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enabled: true,
|
||||||
|
version: '1.0.0',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[XOSDiscovery] Erro ao descobrir integrações:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Descobre automações ativas
|
||||||
|
*/
|
||||||
|
private async discoverAutomations(): Promise<RegisteredService[]> {
|
||||||
|
try {
|
||||||
|
const result = await db.execute(sql`
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
trigger_type,
|
||||||
|
is_active
|
||||||
|
FROM xos_automations
|
||||||
|
WHERE is_active = true
|
||||||
|
LIMIT 10
|
||||||
|
`);
|
||||||
|
|
||||||
|
const automations = result.rows as XOSAutomation[];
|
||||||
|
|
||||||
|
if (automations.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cria um serviço único para o motor de automações XOS
|
||||||
|
return [{
|
||||||
|
id: 'xos-automations',
|
||||||
|
name: 'XOS Automation Engine',
|
||||||
|
displayName: 'Motor de Automações XOS',
|
||||||
|
type: 'node',
|
||||||
|
category: 'automation',
|
||||||
|
description: `Motor de automações XOS - ${automations.length} automação(ões) ativa(s)`,
|
||||||
|
host: 'localhost',
|
||||||
|
port: 5000,
|
||||||
|
endpoint: 'http://localhost:5000',
|
||||||
|
healthCheckPath: '/api/xos/automations/health',
|
||||||
|
healthStatus: 'healthy',
|
||||||
|
lastHealthCheck: new Date(),
|
||||||
|
capabilities: [
|
||||||
|
{ name: 'workflow_automation', version: '1.0', endpoints: ['/api/xos/automations'] },
|
||||||
|
{ name: 'trigger_management', version: '1.0', endpoints: ['/api/xos/automations/triggers'] },
|
||||||
|
{ name: 'action_execution', version: '1.0', endpoints: ['/api/xos/automations/execute'] },
|
||||||
|
],
|
||||||
|
requiresIA: true,
|
||||||
|
iaPolicies: ['automation', 'lgpd'],
|
||||||
|
metadata: {
|
||||||
|
source: 'manual',
|
||||||
|
discoveredAt: new Date(),
|
||||||
|
lastUpdated: new Date(),
|
||||||
|
labels: {
|
||||||
|
'xos.type': 'automation_engine',
|
||||||
|
'xos.automations_count': String(automations.length),
|
||||||
|
'xos.triggers': automations.map(a => a.trigger_type).join(','),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
enabled: true,
|
||||||
|
version: '1.0.0',
|
||||||
|
}];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[XOSDiscovery] Erro ao descobrir automações:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ import { ServiceRegistry } from './registry/ServiceRegistry';
|
||||||
import { DockerDiscovery } from './discovery/DockerDiscovery';
|
import { DockerDiscovery } from './discovery/DockerDiscovery';
|
||||||
import { StaticDiscovery } from './discovery/StaticDiscovery';
|
import { StaticDiscovery } from './discovery/StaticDiscovery';
|
||||||
import { CoolifyDiscovery } from './discovery/CoolifyDiscovery';
|
import { CoolifyDiscovery } from './discovery/CoolifyDiscovery';
|
||||||
|
import { XOSDiscovery } from './discovery/XOSDiscovery';
|
||||||
import { RegistryConfig, RegisteredService } from './registry/types';
|
import { RegistryConfig, RegisteredService } from './registry/types';
|
||||||
|
|
||||||
// Suporte a ESM e CommonJS (build do Docker)
|
// Suporte a ESM e CommonJS (build do Docker)
|
||||||
|
|
@ -110,6 +111,7 @@ export class ArcadiaKernel {
|
||||||
baseUrl: 'https://coolify.onboardbi.com.br',
|
baseUrl: 'https://coolify.onboardbi.com.br',
|
||||||
token: process.env.COOLIFY_API_TOKEN || '',
|
token: process.env.COOLIFY_API_TOKEN || '',
|
||||||
}),
|
}),
|
||||||
|
new XOSDiscovery(), // Descoberta de serviços XOS (filas, integrações)
|
||||||
new DockerDiscovery({ networkName: 'arcadia' }), // Fallback
|
new DockerDiscovery({ networkName: 'arcadia' }), // Fallback
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue