feat(monitoramento): status de registro dos ramais (online/offline) em tempo real
Pedido do usuário: "antes de ir para IVR no monitoramento tem que mostrar quantos ramais estão online e o status de cada ramal criado". apps/api roda no host e não alcança o ESL do FreeSWITCH diretamente (freeswitch:8021 só existe na rede interna do Docker) — mesmo padrão já documentado em platform-freeswitch.controller.ts. Por isso, seguido o mesmo caminho já usado por Trunk.status: apps/freeswitch-events (conexão ESL permanente) já consumia sofia::register/unregister/expire pra outros fins, então virou a fonte de verdade — grava `Extension.registeredAt` a cada evento, mais uma reconciliação completa (`show registrations`) ao conectar/reconectar no ESL pra não ficar com dado desatualizado se o serviço caiu no meio de uma sessão de registro. GET /extensions agora devolve `registeredAt`; a página /app/monitoramento ganhou um painel "Ramais" com status ao vivo (online/offline, desde quando, grupo de captura) e um instrumento "Ramais online: X de Y" — a mesma conexão SSE que já existia só precisou aprender a atualizar esse novo estado a partir dos eventos EXTENSION_REGISTERED/UNREGISTERED, que já estavam sendo publicados e só apareciam no log de eventos. Testado: reconciliação confirmada rodando no container real (2 ramais já registrados antes do restart do fs-events foram marcados online corretamente); GET /extensions confirmado devolvendo registeredAt (null num ramal recém-criado, sem registro). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
@@ -3,6 +3,7 @@ import type { FreeSwitchEventData } from "esl";
|
||||
import { FreeSwitchTelephonyProvider, normalizeEslEvent } from "@b2bcall/telephony";
|
||||
import { createLogger } from "@b2bcall/shared";
|
||||
import { updateTrunkStatusFromGatewayEvent } from "./trunk-status";
|
||||
import { updateExtensionRegistrationStatus, reconcileExtensionRegistrations } from "./extension-status";
|
||||
import { resolveTenantIdForAgent, resolveTenantIdForQueue } from "./tenant-resolve";
|
||||
import { persistCallEvent } from "./cdr";
|
||||
import { uploadRecordingIfPresent } from "./recording";
|
||||
@@ -90,6 +91,10 @@ async function main() {
|
||||
call.on(eventName, (raw) => handleEvent(eventName, raw));
|
||||
}
|
||||
call.on("CUSTOM", (raw) => handleEvent("CUSTOM", raw));
|
||||
|
||||
reconcileExtensionRegistrations(provider).catch((err) => {
|
||||
logger.error("falha na reconciliacao de registrations de ramais", { error: String(err) });
|
||||
});
|
||||
});
|
||||
|
||||
client.on("reconnecting", (retryMs) => {
|
||||
@@ -170,6 +175,14 @@ async function main() {
|
||||
logger.error("falha ao atualizar status do trunk", { error: String(err), gateway });
|
||||
});
|
||||
}
|
||||
|
||||
if (normalized.type === "EXTENSION_REGISTERED" || normalized.type === "EXTENSION_UNREGISTERED") {
|
||||
const user = normalized.data.user as string | undefined;
|
||||
const host = normalized.data.host as string | undefined;
|
||||
updateExtensionRegistrationStatus(user, host, normalized.type === "EXTENSION_REGISTERED").catch((err) => {
|
||||
logger.error("falha ao atualizar status de registro do ramal", { error: String(err), user, host });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
provider.connect();
|
||||
|
||||
Reference in New Issue
Block a user