Desenvolvimento

Backend (WebSocket Server)

Detalhes do servidor WebSocket - arquitetura, serviços e integração ARI.

Backend (stcall-ws)

O servidor WebSocket é uma aplicação Bun nativa que conecta os navegadores dos agentes ao Asterisk ARI.

Stack

TecnologiaUso
Bun 1.xRuntime TypeScript nativo
WebSocket (Bun)Comunicação em tempo real
Prisma 7ORM para PostgreSQL e MySQL
pgDriver PostgreSQL
mariadbDriver MySQL/MariaDB
bcryptjsHash de senhas

Entry Point: server.ts

O servidor principal:

  1. Inicia servidor HTTP/WebSocket Bun
  2. Conecta ao Asterisk ARI via WebSocket
  3. Aceita conexões de agentes (com validação JWT)
  4. Roteia eventos ARI para agentes
  5. Executa comandos dos agentes via ARI HTTP

Módulos Principais

agents/registry.ts

Mantém um Map<string, AgentConnection> em memória com todos os agentes conectados:

interface AgentConnection {
  agentId: string
  websocket: WebSocket
  connectedAt: Date
  lastPing: Date
  channels: Set<string>
}

agents/auth.ts

Validação JWT manual (sem dependência de biblioteca JWT):

  1. Divide token em 3 partes (header, payload, signature)
  2. Decodifica payload (base64)
  3. Verifica campos obrigatórios
  4. Verifica expiração
  5. Verifica assinatura HMAC-SHA256

asterisk/connection.ts

Mantém conexão WebSocket persistente com Asterisk ARI:

  • URL: ws://host:8088/ari/events?app=stCall
  • Reconexão automática com backoff exponencial
  • Heartbeat configurável

asterisk/api-client.ts

Cliente HTTP para ARI REST API:

  • Executa comandos (originate, answer, hangup, hold, etc.)
  • Autenticação Basic
  • Retorna resultados ao agente via WebSocket

commands/handler.ts

Processa comandos recebidos dos agentes:

switch (command.action) {
  case 'originate':    // Iniciar chamada
  case 'answer':       // Atender canal
  case 'hangup':       // Desligar canal
  case 'hold':         // Colocar em espera
  case 'unhold':       // Remover da espera
  case 'mute':         // Silenciar
  case 'unmute':       // Ativar áudio
  case 'fetchCallHistory': // Buscar histórico
  // ...
}

routing/filter.ts

Filtra eventos ARI e determina para qual agente enviar:

  • Eventos de canal do agente → agente específico
  • Eventos de sistema → todos os admins
  • Eventos de chamada → agente responsável

services/call-tracker.ts

Registra chamadas no PostgreSQL:

class CallTracker {
  async handleStasisStart(event) {
    await this.prisma.call.upsert({
      where: { channelId: channel.id },
      create: { /* dados da chamada */ }
    })
  }
}

services/pjsip-sync.ts

Sincroniza endpoints PJSIP com FreePBX MySQL:

class PJSIPSyncService {
  async createEndpoint(config) {
    await this.prisma.$transaction(async (tx) => {
      await tx.psEndpoint.create({ data: { /* ... */ } })
      await tx.psAuth.create({ data: { /* ... */ } })
      await tx.psAor.create({ data: { /* ... */ } })
    })
  }
}

utils/logger.ts

Logger estruturado que emite JSON para stdout:

logger.info('Agent connected', { agentId: 'agent-123' })
// {"level":"INFO","message":"Agent connected","agentId":"agent-123","timestamp":"..."}

Níveis: DEBUG, INFO, WARN, ERROR

utils/encryption.ts

Criptografia de senhas SIP usando SIP_ENCRYPTION_KEY:

  • Senhas armazenadas criptografadas em users.sipPassword
  • Descriptografadas apenas quando necessário para sincronização PJSIP

Protocolo WebSocket

Mensagens do Servidor → Cliente

// Evento ARI
{ type: "event", data: { type: "StasisStart", channel: {...} }, timestamp: "..." }

// Mensagem do sistema
{ type: "system", data: { message: "Connected", severity: "info" }, timestamp: "..." }

// Resposta a comando
{ type: "command_response", requestId: "uuid", data: { success: true, result: {...} } }

// Pong (heartbeat)
{ type: "pong", timestamp: "..." }

Mensagens do Cliente → Servidor

// Comando
{ type: "command", requestId: "uuid", data: { action: "hangup", params: { channelId: "..." } } }

// Ping (heartbeat)
{ type: "ping" }

Rotas HTTP

RotaMétodoDescrição
/healthGETHealth check do servidor
/api/auth/loginPOSTAutenticação de agente
/api/usersGET/POSTGerenciamento de usuários
/api/pjsip/syncPOSTSincronização PJSIP
Copyright © 2026