🤖 Motor Principal de Rastreio (main.js)
O arquivo main.js é o núcleo do sistema de monitoramento logístico. Ele é responsável por consultar a fila de pedidos pendentes no banco de dados, instanciar robôs de navegação headless via Crawlee/Playwright, rotear a execução para o script específico de cada transportadora com base na URL e persistir os resultados padronizados.
⚙️ Configuração Inicial e Conexão
- Dependências: O script utiliza os pacotes
crawlee(classePlaywrightCrawler) para orquestração web epg(classeClient) para comunicação com o PostgreSQL. - Banco de Dados: Conecta-se ao host
foconopreco_postgresna porta5432, utilizando o bancorastreio_dbcom o usuárioapp_user. O timeout de conexão está definido para5000milissegundos. Em caso de falha de conexão, o processo é encerrado comprocess.exit(1).
🚦 Configuração do Crawler
A instância do PlaywrightCrawler foi configurada para evitar sobrecarga de hardware e travamentos:
* maxConcurrency: 2: Limita a execução a no máximo duas abas de navegador simultâneas.
* requestHandlerTimeoutSecs: 45: Reduz o timeout geral de requisições para 45 segundos, impedindo que o script fique preso caso o site da transportadora demore a responder.
📥 Construção da Fila de Processamento
A carga de trabalho do Crawler é definida por uma query na tabela wake_orders:
* Campos Extraídos: chave_acesso_nfe (como danfe), nota_fiscal, cpf_cnpj, nome_transportadora, url_rastreamento.
* Filtros Aplicados: A query exige que chave_acesso_nfe e url_rastreamento não sejam nulos.
* Status Ignorados: Pedidos com situacao_pedido_id em (3, 4, 5, 6, 7, 8, 18, 23) são excluídos da fila de rastreio.
* Injeção: Os dados retornados são mapeados para a fila de requisições (crawler.addRequests) utilizando uma URL local fictícia (http://roteador.local/) e injetando as variáveis no objeto userData.
🚚 Roteamento e Lógica por Transportadora
Dentro do requestHandler, o sistema lê a variável url_rastreamento, converte para minúsculas e aplica uma lógica condicional (if/else) para definir qual script será executado.
Rota 1: SSW (ssw.inf.br)
- Gatilho: A URL contém a string
ssw.inf.br. - Estratégia:
- Navega para
https://ssw.inf.br/2/rastreamento_danfee aguarda a rede ociosa (networkidle). - Tenta clicar no botão de aceite de cookies (
#lnk_aceitar) com timeout de 2000ms. - Escuta eventos de diálogo (
page.on('dialog')). Se a SSW emitir um alerta (ex: nota não encontrada), define a flagalertaSSWcomo verdadeira, aceita o diálogo e interrompe o rastreio pacificamente. - Preenche o input
#danfee clica em#btn_rastrear. - Aguarda um evento de popup (
newPagePromise) com limite de 10 segundos. - Avalia as tabelas no novo contexto procurando pelas colunas
Data/Hora,UnidadeeSituação. - Extrai o conteúdo das linhas sob a classe
tr.mb-4, higienizando quebras de linha.
Rota 2: Rodonaves (rte.com.br ou rodonaves)
- Gatilho: A URL contém
rte.com.brourodonaves. - Estratégia:
- Valida a presença de
cpf_cnpj; se não houver, aborta o rastreio. - Navega para a página pública
https://rodonaves.com.br/rastreio-de-mercadoria. - Seleciona a opção
invoiceNumbernoselect.form-select. - Preenche os inputs mapeados por acessibilidade:
[aria-describedby="CPF"]e[aria-describedby="NF"]. - Configura uma escuta de API (
page.waitForResponse) para interceptar requisições que contenhamtrackingourastreare retornem status HTTP 200. - Clica no botão de submissão (
button[type="submit"]:has-text("RASTREAR")). - Extração Híbrida:
- Se a API responder, mapeia o JSON bruto (
rawTrackingData.Events) e normaliza a formatação de data para o padrão local (pt-BR). - Se a API falhar (fallback), aguarda a renderização do HTML (
.product-status-card) e extrai os textos diretamente dos seletores.status-card-item__titlee.status-card-item__date.
- Se a API responder, mapeia o JSON bruto (
Rota 3: Solistica / Tragetta (solistica.com.br ou tragetta.com.br)
- Gatilho: A URL contém
solistica.com.broutragetta.com.br. - Estratégia:
- Exige a validação prévia de CPF/CNPJ no banco de dados.
- Navega até a rota pública de consulta do Portal Único.
- Aguarda o texto
Consulta sem cadastroestar visível e insere um delay fixo de 2000ms para permitir o carregamento interno do Angular. - Abre o componente de dropdown
.ng-arrow-wrappere seleciona a opçãoNFeusando regras de acessibilidade (ARIA role). - Preenche os campos de Nota Fiscal e CPF/CNPJ forçando a entrada (
{ force: true }) para evitar bloqueios do framework front-end. - Prepara a interceptação da API, ignorando requisições do tipo
OPTIONS. - Clica no botão "Consultar".
- Extração Híbrida e Expandida:
- Via JSON: Constrói um histórico enriquecido extraindo não apenas o
statusAtual, mas gerando eventos logísticos a partir defilialOrigem/filialDestinoe registrando adataPrevisao. - Via HTML (Fallback): Lê os dados da tabela
.ag-row.
- Via JSON: Constrói um histórico enriquecido extraindo não apenas o
- Tratamento de erros inclui captura de tela (
page.screenshot) para facilitar o debug visual de bloqueios.
Rota 4: Senior TMS (senior.com.br)
- Gatilho: A URL contém
senior.com.br. - Estratégia:
- Exige a presença do CPF ou CNPJ.
- Acessa a plataforma utilizando um parâmetro
tenantem Base64 nativo na URL. - Quebra a primeira barreira de segurança inserindo o documento no input de texto inicial e clicando em "Entrar".
- Na segunda tela, preenche o
Número do documentocom a Nota Fiscal. - Escuta requisições de rede contendo
trackinge clica em filtrar. - Navega pela árvore do JSON de resposta (
listaTracking[0].listaTrackingFase) e extrai estritamente as fases onde a propriedadeexecutadaé igual atrue. - Ordena os dados capturados da data mais antiga para a mais recente utilizando o objeto
Datenativo do JavaScript.
Rota 5: Grupo Troca (grupotroca.com.br)
- Gatilho: A URL contém
grupotroca.com.br. - Estratégia:
- Valida a presença de documento e navega diretamente para a URL gravada no banco.
- Preenche os formulários mapeando os campos de texto por Expressões Regulares (
/Informe o CNPJ\/CPF/i). - Clica no botão
#tbutton_pesquisare aguarda a renderização da tabela.panel-body table tbody tr. - Extrai as colunas de data e ocorrência (TD 1 e 2).
- Aplica um filtro em memória ignorando linhas cujos textos sejam inválidos ou correspondam a cabeçalhos nativos do site da transportadora, como
PreparandoeNotas fiscais.
💾 Persistência de Dados
Independentemente da rota executada, se o array trackingData (ou rastreioData) retornar com conteúdo válido (maior que zero), o sistema executa uma instrução padronizada de gravação no banco de dados.
```sql INSERT INTO ssw_tracking_urls (danfe, tracking_url, tracking_data) VALUES ($1, $2, $3) ON CONFLICT (danfe) DO UPDATE SET tracking_url = EXCLUDED.tracking_url, tracking_data = EXCLUDED.tracking_data