# Sistema de Sincronização ServiceNow <> GLPI ## Visão Geral Esta aplicação Node.js é um middleware projetado para realizar a sincronização bidirecional de tickets entre uma instância do **ServiceNow (SNOW)** e uma do **GLPI**. O objetivo é manter os tickets, seus status e comentários consistentes entre as duas plataformas, automatizando o fluxo de trabalho para as equipes de suporte. A aplicação funciona como um job agendado (por exemplo, via cron) que executa um ciclo completo de sincronização. --- ## Como Executar a Aplicação Esta seção contém as instruções essenciais para configurar e rodar a aplicação em diferentes ambientes. ### 1. Pré-requisitos - **Node.js**: Certifique-se de ter o Node.js (versão 18 ou superior) instalado. - **Dependências**: Na raiz do projeto, execute o comando abaixo para instalar todas as dependências necessárias: ```bash npm install ``` - **Arquivos de Ambiente**: Crie os arquivos `.env.development` e `.env.production` na raiz do projeto, baseando-se no exemplo `.env.example` (se houver) e preenchendo com as credenciais e URLs corretas para cada ambiente. ### 2. Executando com Scripts NPM ### 2. Configuração do Ambiente Python A aplicação utiliza um script Python para sincronizar o mapeamento de localidades. #### a) Dependências Python Navegue até a pasta do script e instale as dependências listadas no `requirements.txt`: ```bash cd src/scripts/python pip install -r requirements.txt ``` #### b) Montagem do Compartilhamento de Arquivos (CIFS/Samba) O script Python lê um arquivo `.csv` de um compartilhamento de rede. No servidor de produção, é necessário montar este compartilhamento para que o caminho definido em `LOCATION_MAPPING_CSV_PATH` seja acessível. Adicione a seguinte linha ao seu arquivo `/etc/fstab` para montar o compartilhamento automaticamente durante o boot do sistema. **Ajuste o IP, credenciais e caminhos conforme necessário.** ```fstab //10.0.121.40/Tecnica/Controle\040CAOA/ENTIDADE\040SERVICE\040NOW /mnt/csv_share cifs username=,password=,uid=1000,gid=1000,iocharset=utf8 0 0 ``` Após editar o `/etc/fstab`, execute `sudo mount -a` para montar o compartilhamento imediatamente. Os comandos abaixo utilizam a variável `NODE_ENV` para carregar o arquivo de ambiente (`.env`) correto. #### Ambiente de Desenvolvimento Para rodar em modo de desenvolvimento com reinício automático a cada alteração de arquivo (usando `nodemon`): ```bash npm run dev ``` #### Ambiente de Produção Para rodar a aplicação em modo de produção (usando `node`): ```bash npm start ``` ou o comando explícito: ```bash npm run start:prod ``` ### 3. Executando com PM2 O PM2 é o gerenciador de processos recomendado para ambientes de produção. Ele garante que a aplicação reinicie automaticamente em caso de falhas e facilita o gerenciamento. - **Para iniciar em modo de produção:** ```bash pm2 start ecosystem.config.js --env production ``` - **Para iniciar em modo de desenvolvimento:** ```bash pm2 start ecosystem.config.js --env development ``` - **Comandos úteis do PM2:** ```bash pm2 list # Lista todos os processos pm2 logs sn-glpi-sync-cron # Exibe os logs em tempo real pm2 restart sn-glpi-sync-cron # Reinicia a aplicação pm2 stop sn-glpi-sync-cron # Para a aplicação ``` ## Fluxo de Execução A aplicação segue uma ordem de execução estrita para garantir a consistência dos dados. O ponto de entrada é o arquivo `src/app.js`, que orquestra a chamada dos seguintes controladores: 1. **`processErrorController` - Limpeza de Erros** - **Objetivo**: Aumentar a resiliência do sistema. - **Ação**: Busca por tickets que falharam em execuções anteriores (marcados com status de `error`). Ele reseta o status desses tickets para um estado anterior válido (ex: `pending_check` ou `synced`), permitindo que eles sejam reprocessados no ciclo atual. 2. **`processTicketsController` - Criação de Tickets** - **Objetivo**: Trazer novos tickets do ServiceNow para o GLPI. - **Ação**: - **Busca no SNOW**: Invoca o `processSyncController` para buscar incidentes e requisições novas ou atualizadas no ServiceNow, utilizando uma "marca d'água" (timestamp) para otimização. - **Criação no GLPI**: Para cada novo ticket coletado, o `glpiTicketService` é chamado para: - Verificar se o ticket já existe no GLPI para evitar duplicatas. - Formatar os dados (título, descrição, categoria, etc.). - Criar o ticket correspondente no banco de dados do GLPI. 3. **`processCommentsController` - Sincronização de Comentários** - **Objetivo**: Manter as conversas dos tickets sincronizadas. - **Ação**: Para cada ticket ativo, realiza uma sincronização bidirecional de comentários (notas/follow-ups): - **SNOW -> GLPI**: Busca novos comentários no ServiceNow, os salva no banco intermediário e os insere como follow-ups no GLPI. - **GLPI -> SNOW**: Busca novos follow-ups no GLPI, os sanitiza (remove HTML, trata imagens) e os envia como `work_notes` para o ServiceNow. 4. **`processStatusAndClosureController` - Sincronização de Status e Fechamento** - **Objetivo**: Manter os status dos tickets alinhados e gerenciar o ciclo de vida (resolução e fechamento). - **Ação**: Realiza uma sincronização bidirecional de status, tratando reaberturas, pausas, resoluções e fechamentos em ambas as direções. --- ## Regras de Negócio Principais A aplicação implementa várias regras de negócio para gerenciar a complexidade da sincronização: ### Controle de Fluxo com "Bastão" (`source_last`) - Para evitar conflitos de atualização (race conditions), o sistema utiliza um campo `source_last` na tabela `ticket_sync`. - Este campo funciona como um "bastão da palavra": o sistema que realiza a última atualização (SNOW ou GLPI) "segura o bastão". - O sistema oposto só pode atualizar o ticket se ele estiver com o bastão. Isso garante que as atualizações de status e comentários ocorram de forma ordenada. ### Criação e Enriquecimento de Tickets - **Mapeamento**: Tickets do ServiceNow são mapeados para o GLPI com base em regras de categoria, prioridade e SLA definidas no `ticketGlpiModel.js`. - **Título**: O título do ticket no GLPI é padronizado como `[TIPO] - [ENTIDADE] - [TÍTULO ORIGINAL]`. - **Enriquecimento de Descrição**: Se uma requisição do ServiceNow chega com a descrição vazia, o sistema busca automaticamente o conteúdo de variáveis de catálogo (como `justificativa` e `telefone`) e as utiliza para preencher a descrição e os dados do ticket no GLPI. ### Sincronização de Comentários - **Lógica de 4 Casos (GLPI -> SNOW)**: Para evitar duplicatas, a sincronização de comentários do GLPI para o SNOW verifica 4 cenários: 1. **Já Sincronizado**: O comentário existe em ambos os locais. Nenhuma ação. 2. **Existe no SN, Falta no BD Local**: O registro local é criado para corrigir o estado. 3. **Existe no BD Local, Falta no SN**: O comentário é reenviado para o ServiceNow. 4. **Novo Comentário**: O comentário é enviado para o SNOW e registrado localmente. - **Sanitização**: Comentários do GLPI passam por um processo de limpeza (`commentSanitizer.js`) que remove tags HTML e substitui imagens por um texto placeholder, garantindo que o conteúdo seja legível no ServiceNow. ### Sincronização de Status - **Fechamento de Ticket em Espera**: Se um ticket é resolvido no GLPI, mas seu correspondente no ServiceNow está com o status "Em Espera" ou "Aguardando Atendimento", o sistema primeiro altera o status no ServiceNow para "Em Atendimento" e só então prossegue com a resolução. Isso automatiza o fluxo e evita falhas. - **Regra "Fora do Escopo"**: Se um ticket no GLPI é solucionado com o tipo de solução "Fora do Escopo", o sistema não o resolve no ServiceNow. Em vez disso, ele adiciona uma nota de trabalho (`work_note`) explicando o motivo e fecha o registro de sincronização, tratando-o como um caso especial. - **Reabertura**: O sistema permite a reabertura de tickets em ambas as direções, desde que o ticket não esteja permanentemente fechado. ### Ambientes (`.env`) - A aplicação suporta múltiplos ambientes através de arquivos `.env`. - Se a variável de ambiente `NODE_ENV` for definida como `production`, o arquivo `.env.production` será carregado. - Caso contrário (ou se `NODE_ENV` for `development`), o arquivo `.env.development` será utilizado. ### Atualização do Mapeamento de Localidades (`update_location.py`) - A aplicação conta com um script Python (`src/scripts/python/update_location.py`) para manter o mapeamento entre as localidades do ServiceNow e as entidades do GLPI. - **Fonte da Verdade**: Um arquivo `.csv` localizado em um servidor de arquivos (`LOCATION_MAPPING_CSV_PATH` no `.env`) é a fonte da verdade para este mapeamento. - **Processo**: 1. Um usuário (`sothis`) atualiza o arquivo `.csv` com as correspondências corretas entre os nomes das localidades (SNOW) e os nomes das entidades (GLPI). 2. Um `cron job` no servidor executa o script `update_location.py` periodicamente. 3. O script lê o `.csv`, busca os IDs correspondentes nos bancos de dados de ambos os sistemas e recria a tabela `location_mapping` no banco de dados intermediário (PostgreSQL). - **Resultado**: Garante que os tickets criados no GLPI sejam sempre associados à entidade correta, com base em um mapeamento centralizado e de fácil manutenção. --- ## Estrutura do Banco de Dados Intermediário (PostgreSQL) - **`tickets_sn`**: Armazena uma cópia local dos dados dos tickets do ServiceNow para otimizar consultas. - **`ticket_sync`**: Tabela central que controla o estado da sincronização de cada ticket, incluindo os IDs de ambos os sistemas e o campo `source_last`. - **`ticket_updates`**: Log de todas as atualizações (comentários, etc.), usado para evitar duplicidade e rastrear o histórico. - **`location_mapping`**: Tabela de mapeamento entre as localizações do ServiceNow e as entidades do GLPI. - **`sync_control`**: Armazena metadados do processo, como a marca d'água (timestamp) da última sincronização bem-sucedida. --- ## Diagrama de Fluxo (Exemplo) ### Sincronização de Resolução: GLPI -> ServiceNow O diagrama abaixo ilustra como o sistema lida com um ticket que foi marcado como "Solucionado" no GLPI. ```mermaid sequenceDiagram participant App as app.js participant StatusCtrl as processStatusAndClosureController participant GlpiModel as TicketGlpiModel participant SyncModel as TicketSyncModel participant SnowService as servicenowService participant ServiceNow as ServiceNow API App->>StatusCtrl: Iniciar ciclo de status StatusCtrl->>SyncModel: getTicketsToMonitor() SyncModel-->>StatusCtrl: Lista de tickets ativos loop Para cada ticket monitorado StatusCtrl->>GlpiModel: getTicketStatus(glpi_id) GlpiModel-->>StatusCtrl: Status 'Solucionado' (5) alt Status mudou para 'Solucionado' StatusCtrl->>GlpiModel: getTicketSolution(glpi_id) GlpiModel-->>StatusCtrl: Detalhes da solução StatusCtrl->>SnowService: closeTicketInServiceNow(sn_id, solucao) SnowService->>ServiceNow: PATCH /api/now/table/incident/{sys_id} (state: 'Resolved') ServiceNow-->>SnowService: 200 OK SnowService-->>StatusCtrl: Sucesso StatusCtrl->>SyncModel: updateStatus(sn_id, 'solved', 'solved') SyncModel-->>StatusCtrl: OK end end ```