diff --git a/.env b/.env index 31ed7d9..15779de 100644 --- a/.env +++ b/.env @@ -10,4 +10,13 @@ HUBSOFT_DATABASE_HOST=sothis.hubsoft.com.br HUBSOFT_DATABASE_PORT=9432 HUBSOFT_DATABASE_NAME=hubsoft HUBSOFT_DATABASE_USER=sothis_leitura -HUBSOFT_DATABASE_PASSWORD=2d0f4e138ba533693a1fc244d08a7f8596d8b472 \ No newline at end of file +HUBSOFT_DATABASE_PASSWORD=2d0f4e138ba533693a1fc244d08a7f8596d8b472 + +# Banco HUBGLPI (PostgreSQL) +HUBGLPI_DB_HOST=10.0.120.75 +HUBGLPI_DB_PORT=5432 +HUBGLPI_DB_NAME=hubglpi +HUBGLPI_DB_USER=desenvolvimento +HUBGLPI_DB_PASSWORD=Ut@2S@$M9Xs@@W + +NODE_ENV=development \ No newline at end of file diff --git a/data/hubglpiDataBase.js b/data/hubglpiDataBase.js new file mode 100644 index 0000000..9ddf9a3 --- /dev/null +++ b/data/hubglpiDataBase.js @@ -0,0 +1,31 @@ +// src/data/hubglpiDataBase.js +// Configuração da conexão com o banco de dados PostgreSQL +const { logInfo, logError } = require('../utils/logger'); + +const { Pool } = require('pg'); + +const pool = new Pool({ + host: process.env.HUBGLPI_DB_HOST, + port: process.env.HUBGLPI_DB_PORT, + database: process.env.HUBGLPI_DB_NAME, + user: process.env.HUBGLPI_DB_USER, + password: process.env.HUBGLPI_DB_PASSWORD, +}); + +// Teste de conexão +pool.on('connect', () => { + logInfo('Conexão com o banco de dados PostgreSQL estabelecida com sucesso.'); +}); + +pool.on('error', (err) => { + logError('Erro na conexão com o banco de dados PostgreSQL', err); +}); + + + +module.exports = pool; + +/** + * @module hubglpiDataBase + * @description Este módulo configura e exporta a conexão com o banco de dados PostgreSQL usado para armazenar dados sincronizados entre HubSoft e GLPI. +*/ \ No newline at end of file diff --git a/scripts/data/database.sql b/scripts/data/database.sql new file mode 100644 index 0000000..f4b225b --- /dev/null +++ b/scripts/data/database.sql @@ -0,0 +1,56 @@ +-- ============================================= +-- BANCO DE DADOS: hubglpi +-- DESCRIÇÃO: Sistema de integração entre HubSoft e GLPI +-- OBS: Chamados sempre são abertos pelo HubSoft +-- ============================================= + + +-- ============================================= +-- TABELA: hubsoft_tickets +-- DESCRIÇÃO: Armazena os chamados originados do HubSoft +-- ============================================= +CREATE TABLE hubsoft_tickets ( + id SERIAL PRIMARY KEY, + protocolo VARCHAR(50) UNIQUE NOT NULL, + descricao_abertura TEXT NOT NULL, + descricao_fechamento TEXT, + tipo_atendimento VARCHAR(100), + cliente_codigo INTEGER NOT NULL, + cliente_nome VARCHAR(255) NOT NULL, + servico_id VARCHAR(50), + servico_nome VARCHAR(255), + data_abertura TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + data_fechamento TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- ============================================= +-- TABELA: sync_data +-- DESCRIÇÃO: Armazena o estado da sincronização entre HubSoft e GLPI +-- ============================================= + +CREATE TABLE sync_logs ( + id SERIAL PRIMARY KEY, + hubsoft_ticket_id INTEGER NOT NULL REFERENCES hubsoft_tickets(id), + glpi_ticket_id INTEGER, + source_last source_last_enum DEFAULT 'hubsoft', + status_sync status_sync_enum DEFAULT 'pending_create', + sync_metadata JSONB, + last_sync_attempt TIMESTAMP, + sync_error_message TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE (hubsoft_ticket_id, glpi_ticket_id) +); + +CREATE TYPE source_last_enum AS ENUM ('hubsoft', 'glpi'); +CREATE TYPE status_sync_enum AS ENUM ( + 'pending_create', + 'created_glpi', + 'pending_close', + 'closed_glpi', + 'sync_error', + 'need_update' +); + diff --git a/services/hubsoftServices.js b/services/hubsoftServices.js new file mode 100644 index 0000000..c1c2136 --- /dev/null +++ b/services/hubsoftServices.js @@ -0,0 +1,39 @@ +const apiConfig = require('../config/apiConfig.js'); +const axios = require('axios'); + +async function getAuthToken() { + try { + const response = await axios.post(apiConfig.hubsoft.authUrl, apiConfig.hubsoft.authPayload); + return response.data.access_token; + } + catch (error) { + console.error('Error fetching auth token:', error); + throw error; + } +} + +// Função para consultar atendimentos + +const consultarAtendimento = async () => { + try { + const token = await getAuthToken(); + const today = new Date(); + const date = new Date(today.getFullYear(), today.getMonth(), today.getDate()).toISOString().split('T')[0]; + console.log('Consulting atendimentos for date:', date); + const url = `${apiConfig.hubsoft.consultarAtendimentoUrl}data_inicio=2025-10-03&data_fim=2025-10-06`; + + const response = await axios.get(url, { + headers: { Authorization: `Bearer ${token}` } + }); + + return response.data.atendimentos; + + }catch (error) { + console.error('Error consulting atendimentos:', error); + throw error; + } +}; + +module.exports = { + consultarAtendimento, getAuthToken +}; \ No newline at end of file diff --git a/utils/logger.js b/utils/logger.js new file mode 100644 index 0000000..1aec85a --- /dev/null +++ b/utils/logger.js @@ -0,0 +1,82 @@ +const winston = require('winston'); +const path = require('path'); +const fs = require('fs'); + +// verifica se a pasta de logs existe, se não, cria +const logsDir = path.join(__dirname, '../../logs'); +if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); +} + +// Configuração do logger com winston +const logger = winston.createLogger({ + level: 'info', + format: winston.format.combine( + winston.format.timestamp({ + format: 'YYYY-MM-DD HH:mm:ss' + }), + winston.format.errors({ stack: true }), // ← Mostra stack trace de erros + winston.format.json() + ), + transports: [ + // Log geral da aplicação + new winston.transports.File({ + filename: path.join(logsDir, 'app.log'), + maxsize: 5242880, // 5MB + maxFiles: 5 + }), + // Log de erros + new winston.transports.File({ + filename: path.join(logsDir, 'error.log'), + level: 'error', + maxsize: 5242880, + maxFiles: 3 + }), + ], +}); + +// Log no console em desenvolvimento +if (process.env.NODE_ENV !== 'production') { + logger.add(new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + winston.format.printf(({ timestamp, level, message, stack }) => { + return `${timestamp} [${level}]: ${stack || message}`; + }) + ) + })); +} + +// Funções utilitárias +const logError = (error, context = '') => { + if (error instanceof Error) { + logger.error(`${context} - ${error.message}`, { stack: error.stack }); + } else { + logger.error(`${context} - ${error}`); + } +}; + +const logInfo = (message, meta = {}) => { + logger.info(message, meta); +}; + +const logWarning = (message, meta = {}) => { + logger.warn(message, meta); +}; + +// Log de sincronização específico +const logSync = (service, count, type) => { + logger.info(`SYNC: ${service} - ${count} ${type} sincronizados`, { + service, + count, + type + }); +}; + +module.exports = { + logger, + logError, + logInfo, + logWarning, + logSync +}; \ No newline at end of file