INTELI JÚNIOR / ECOROTA

Eventos e WebSocket

Operação simulada de coleta. Construa a experiência dos moradores e coletores com HTTP ou eventos em tempo real.

WebSocket é opcional. Todo comando permanece em HTTP. Conecte a partir do backend da sua equipe, mantendo a credencial fora do navegador.

Conexão e contrato

Use ws://HOST/v1/stream localmente ou wss://HOST/v1/stream na hospedagem HTTPS. Envie Authorization: Bearer no handshake. Há até cinco conexões por ambiente. Mensagens de aplicação enviadas ao servidor encerram a conexão; ping/pong do protocolo são aceitos.

A primeira mensagem tem formato {"type":"snapshot","data":{...}}; data tem o mesmo schema de data em GET /v1/snapshot. Aplique o snapshot integralmente. As seguintes são envelopes de evento:

{
 "id":"125", "revision":42, "generation":1,
 "simulationTime":84000, "occurredAt":"2026-09-13T12:00:00.000Z",
 "type":"request.assigned",
 "data":{"id":"ID_SOLICITACAO","pointId":"ID_PONTO",
   "externalReference":"pedido-local-001","collectorId":"ID_COLETOR",
   "status":"assigned","createdAt":"2026-09-13T12:00:00.000Z",
   "createdSimulationTime":80000,"updatedAt":"2026-09-13T12:00:00.000Z"}
}
Evento Schema de data
collector.created / collector.updated Objeto completo de coletor, igual ao recurso HTTP.
collector.deleted {id}
collector.position_updated {id, position: {type: "Point", coordinates: [longitude, latitude]}, observedAt}
route.updated Objeto completo de rota, igual ao recurso HTTP.
request.created / assigned / started / completed / cancelled / requeued Objeto completo de solicitação, igual ao recurso HTTP.
simulation.incident {collectorId, kind: "delay" | "unavailable" | "telemetry", active, until?}. until em ms simulados.
simulation.updated {paused}
simulation.reset {generation}. Conexões ativas recebem um snapshot substituto ao perceber a nova geração; o evento também fica no histórico HTTP.

Ordenação e recuperação

Um snapshot substituto também pode aparecer durante a conexão quando houver uma atualização administrativa ou uma lacuna de eventos. Trate qualquer mensagem type=snapshot como substituição integral do cache, não apenas a primeira mensagem.

Vários eventos podem compartilhar uma revision; não descarte eventos diferentes só porque a revisão é igual. Use id para duplicatas e descarte revisões menores que a última aplicada. IDs de negócio são sequências numéricas em string; posições têm IDs efêmeros position-REVISION-COLLECTOR. O snapshot fornece eventCursor para a sequência de negócio.

Após desconexão, reconecte com espera exponencial e aleatoriedade. A nova conexão recebe snapshot atual, não replay de posições. Posições intermediárias podem ser perdidas e não há garantia de entrega exatamente uma vez. Compare observedAt para perceber telemetria desatualizada, mesmo recebendo eventos de posição repetidos.

Um incidente pode gerar atraso, indisponibilidade ou atualização de posição congelada. Coletas custom continuam exigindo confirmação. A revogação de credencial fecha conexões no próximo ciclo. O servidor usa ping/pong a cada 30 segundos e desconecta consumidores com fila excessiva.

Exemplo de consumidor Node.js

// Instale ws na API da sua equipe. Não execute este trecho no frontend.
import WebSocket from 'ws';
const url = process.env.ECOROTA_URL.replace(/^http/, 'ws') + '/v1/stream';
let retry = 1000;
function connect() {
  const ws = new WebSocket(url, {
    headers: { Authorization: 'Bearer ' + process.env.ECOROTA_KEY }
  });
  let revision = -1;
  const seen = new Set();
  ws.on('message', raw => {
    const message = JSON.parse(raw.toString());
    if (message.type === 'snapshot') {
      retry = 1000; revision = message.data.revision; seen.clear();
      // Substitua seu cache pelo estado completo.
      return;
    }
    if (message.revision < revision || seen.has(message.id)) return;
    if (message.revision > revision) seen.clear();
    revision = message.revision; seen.add(message.id);
    // Aplique a atualização ao cache e notifique o frontend autorizado.
  });
  ws.on('error', error => console.error(error.message));
  ws.on('close', () => {
    setTimeout(connect, retry + Math.random() * 500);
    retry = Math.min(30000, retry * 2);
  });
}
connect();

Em produção, interrompa novas tentativas e avise a equipe em caso de credencial inválida ou revogada. O snapshot e a consulta HTTP fornecem recuperação caso seu consumidor detecte perda de estado.