arcadiasuite/server/kernel/index.ts

310 lines
8.4 KiB
TypeScript

/**
* Arcadia Kernel - Entry Point
* Sistema nativo de gerenciamento de serviços
*
* V3.0 - Adicionado Service Registry + Docker Discovery
*/
import { readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { ProcessManager } from './core/ProcessManager';
import { HealthMonitor } from './core/HealthMonitor';
import { LogAggregator } from './core/LogAggregator';
import { DashboardServer } from './dashboard/DashboardServer';
import { ServiceConfig, ServiceState } from './types';
// NOVO: Service Registry e Discovery
import { ServiceRegistry } from './registry/ServiceRegistry';
import { DockerDiscovery } from './discovery/DockerDiscovery';
import { StaticDiscovery } from './discovery/StaticDiscovery';
import { CoolifyDiscovery } from './discovery/CoolifyDiscovery';
import { RegistryConfig, RegisteredService } from './registry/types';
// Suporte a ESM e CommonJS (build do Docker)
const __dirname = (() => {
try {
return dirname(fileURLToPath(import.meta.url));
} catch {
return join(process.cwd(), 'server', 'kernel');
}
})();
export interface KernelOptions {
configPath?: string;
dashboard?: {
enabled?: boolean;
port?: number;
host?: string;
};
autoStart?: boolean;
// NOVO: Opções do Service Registry
registry?: {
enabled?: boolean;
config?: Partial<RegistryConfig>;
};
}
export class ArcadiaKernel {
public processManager: ProcessManager;
public healthMonitor: HealthMonitor;
public logAggregator: LogAggregator;
public dashboard?: DashboardServer;
// NOVO: Service Registry
public serviceRegistry?: ServiceRegistry;
private options: Required<KernelOptions>;
private services: ServiceConfig[] = [];
private started = false;
constructor(options: KernelOptions = {}) {
this.options = {
configPath: join(__dirname, 'config', 'services.json'),
dashboard: {
enabled: true,
port: 5001,
host: '0.0.0.0',
},
autoStart: false,
// NOVO: Registry ativado por padrão
registry: {
enabled: true,
config: {
enableAutoDiscovery: true,
discoveryInterval: 30000,
healthCheckInterval: 15000,
},
},
...options,
};
// Inicializa componentes existentes
this.logAggregator = new LogAggregator({ maxLines: 10000 });
this.processManager = new ProcessManager({
onLog: (entry) => this.logAggregator.add(entry),
onStateChange: (serviceId, state) => {
console.log(`[Kernel] ${serviceId}: ${state.status}`);
},
onHealthChange: (serviceId, health) => {
console.log(`[Kernel] ${serviceId} health: ${health}`);
},
});
this.healthMonitor = new HealthMonitor({
onHealthChange: (serviceId, health, error) => {
this.processManager.setHealth(serviceId, health);
if (error) {
console.log(`[Kernel] ${serviceId} health error: ${error}`);
}
},
});
// NOVO: Inicializa Service Registry se habilitado
if (this.options.registry.enabled) {
this.serviceRegistry = new ServiceRegistry({
config: this.options.registry.config,
discoveryProviders: [
new StaticDiscovery(), // Descoberta via DNS interno
new CoolifyDiscovery({ // Descoberta via API Coolify
baseUrl: 'https://coolify.onboardbi.com.br',
token: process.env.COOLIFY_API_TOKEN || '',
}),
new DockerDiscovery({ networkName: 'arcadia' }), // Fallback
],
});
// Escuta eventos do registry
this.serviceRegistry.on('event', (event) => {
console.log(`[Registry] ${event.type}: ${event.serviceId}`);
});
}
// Carrega configurações
this.loadConfig();
}
/**
* Inicia o Kernel
*/
async start(): Promise<void> {
if (this.started) {
return;
}
console.log('[Kernel] Iniciando Arcadia Kernel...');
console.log(`[Kernel] ${this.services.length} serviços configurados (services.json)`);
// Registra serviços no ProcessManager
for (const config of this.services) {
this.processManager.registerService(config);
}
// NOVO: Inicia Service Registry
if (this.serviceRegistry) {
await this.serviceRegistry.start();
}
// Inicia Dashboard
if (this.options.dashboard.enabled) {
this.dashboard = new DashboardServer(
this.processManager,
this.healthMonitor,
this.logAggregator,
this.serviceRegistry, // Passa o Registry pro Dashboard
{
port: this.options.dashboard.port,
host: this.options.dashboard.host,
}
);
await this.dashboard.start();
}
// Auto-start serviços
if (this.options.autoStart) {
console.log('[Kernel] Auto-iniciando serviços...');
await this.processManager.startAll(true);
// Inicia health monitoring para serviços em execução
for (const state of this.processManager.getAllStates()) {
if (state.status === 'running') {
this.healthMonitor.startMonitoring(state.config.id, state);
}
}
}
this.started = true;
console.log('[Kernel] Kernel iniciado com sucesso!');
// Eventos de shutdown
this.setupShutdownHandlers();
}
/**
* Para o Kernel
*/
async stop(): Promise<void> {
if (!this.started) {
return;
}
console.log('[Kernel] Parando Kernel...');
// NOVO: Para Service Registry
this.serviceRegistry?.stop();
// Para health monitor
this.healthMonitor.stopAll();
// Para todos os serviços
await this.processManager.stopAll(true);
// Para dashboard
await this.dashboard?.stop();
this.started = false;
console.log('[Kernel] Kernel parado.');
}
/**
* Retorna se o kernel está rodando
*/
isRunning(): boolean {
return this.started;
}
/**
* NOVO: Retorna estatísticas combinadas (ProcessManager + Registry)
*/
getStats(): { processManager: any; registry: any } {
return {
processManager: {
total: this.services.length,
states: this.processManager.getAllStates(),
},
registry: this.serviceRegistry?.getStats(),
};
}
/**
* NOVO: Lista serviços descobertos (via Registry)
*/
getDiscoveredServices(): RegisteredService[] {
return this.serviceRegistry?.getServices() || [];
}
/**
* Carrega configurações dos serviços
*/
private loadConfig(): void {
try {
const configData = readFileSync(this.options.configPath, 'utf-8');
const config = JSON.parse(configData);
this.services = config.services || [];
} catch (error) {
console.error('[Kernel] Erro ao carregar config:', error);
this.services = [];
}
}
/**
* Configura handlers de shutdown
*/
private setupShutdownHandlers(): void {
const shutdown = async (signal: string) => {
console.log(`\n[Kernel] Sinal ${signal} recebido. Desligando...`);
await this.stop();
process.exit(0);
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
process.on('uncaughtException', (error) => {
console.error('[Kernel] Uncaught Exception:', error);
});
process.on('unhandledRejection', (reason) => {
console.error('[Kernel] Unhandled Rejection:', reason);
});
}
}
// Exporta componentes individuais
export { ProcessManager } from './core/ProcessManager';
export { HealthMonitor } from './core/HealthMonitor';
export { LogAggregator } from './core/LogAggregator';
export { DashboardServer } from './dashboard/DashboardServer';
export { KernelWebSocket } from './websocket/KernelWebSocket';
export { createKernelRoutes } from './api/routes';
// NOVO: Exports do Registry e Discovery
export { ServiceRegistry } from './registry/ServiceRegistry';
export { DockerDiscovery } from './discovery/DockerDiscovery';
export type {
RegisteredService,
ServiceFilter,
RegistryEvent,
DiscoverySource,
ServiceCategory,
} from './registry/types';
export * from './types';
// Entry point para execução direta
try {
if (import.meta.url && import.meta.url === `file://${process.argv[1]}`) {
const kernel = new ArcadiaKernel({
dashboard: { enabled: true, port: 5001 },
autoStart: true,
});
kernel.start().catch((error) => {
console.error('[Kernel] Falha ao iniciar:', error);
process.exit(1);
});
}
} catch {
// No build CommonJS, o Kernel é iniciado pelo server/index.ts
}