Initial commit - SNGLPI

This commit is contained in:
Equipe de Desenvolvimento Sothis 2025-08-30 22:04:35 +00:00
commit 67dce4c1e6
19 changed files with 616 additions and 0 deletions

29
.env.example Normal file
View File

@ -0,0 +1,29 @@
# Contents of the file: /servicenow-glpi-sync/servicenow-glpi-sync/.env.example
# This is an example of the environment variables needed for the application.
# ServiceNow API Configuration
SERVICENOW_URL=https://your-instance.service-now.com/api/now/table/
SERVICENOW_USERNAME=your_servicenow_username
SERVICENOW_PASSWORD=your_servicenow_password
# GLPI API Configuration
GLPI_URL=https://your-glpi-instance/apirest.php/
GLPI_APP_TOKEN=your_glpi_app_token
GLPI_USER_TOKEN=your_glpi_user_token
# Database Configuration
DB_HOST=localhost
DB_PORT=3306 # or 5432 for PostgreSQL
DB_USER=your_database_user
DB_PASSWORD=your_database_password
DB_NAME=your_database_name
# Logging Configuration
LOG_LEVEL=info
LOG_FILE_PATH=logs/app.log
ERROR_LOG_FILE_PATH=logs/error.log
# Other Configuration
PORT=3000
NODE_ENV=development

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
node_modules/
logs/*.log
.env
config/*.json
.DS_Store
*.tmp

80
README.md Normal file
View File

@ -0,0 +1,80 @@
# servicenow-glpi-sync/README.md
# ServiceNow and GLPI Sync
This project is designed to facilitate the integration between ServiceNow and GLPI, allowing for the synchronization of tickets between the two platforms. It utilizes Node.js and follows the MVC architecture pattern.
## Table of Contents
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Configuration](#configuration)
- [Running the Application](#running-the-application)
- [Endpoints](#endpoints)
- [Logging](#logging)
- [Contributing](#contributing)
- [License](#license)
## Prerequisites
- Node.js (version 14 or higher)
- npm (Node Package Manager)
- PostgreSQL or MySQL database
- Access to ServiceNow and GLPI APIs
## Installation
1. Clone the repository:
```
git clone https://github.com/yourusername/servicenow-glpi-sync.git
cd servicenow-glpi-sync
```
2. Install the dependencies:
```
npm install
```
3. Copy the `.env.example` file to `.env` and fill in the required environment variables:
```
cp .env.example .env
```
## Configuration
- Update the `.env` file with your ServiceNow and GLPI API credentials, database connection details, and any other necessary configurations.
## Running the Application
To start the application, run:
```
node server.js
```
The application will listen on the port specified in the `.env` file.
## Endpoints
- `POST /tickets/sync` - Synchronize tickets from ServiceNow to GLPI.
- `GET /tickets/:id` - Retrieve details of a specific ticket.
- `POST /tickets` - Create a new ticket in GLPI based on ServiceNow data.
- `PUT /tickets/:id` - Update an existing ticket in GLPI.
## Logging
The application uses Winston for logging. Logs are stored in the `logs` directory:
- `app.log` - Logs application events.
- `error.log` - Logs errors that occur during execution.
## Contributing
Contributions are welcome! Please open an issue or submit a pull request for any improvements or bug fixes.
## License
This project is licensed under the MIT License. See the LICENSE file for details.

28
config/databaseConfig.js Normal file
View File

@ -0,0 +1,28 @@
const { Sequelize } = require('sequelize');
require('dotenv').config();
// Database connection configuration
const dbConfig = {
host: process.env.DB_HOST,
dialect: process.env.DB_DIALECT || 'mysql', // Default to MySQL if not specified
logging: false, // Disable logging; set to console.log to enable
};
// Create a new Sequelize instance
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, dbConfig);
// Test the database connection
const testConnection = async () => {
try {
await sequelize.authenticate();
console.log('Connection to the database has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}
};
// Export the sequelize instance and the test connection function
module.exports = {
sequelize,
testConnection,
};

202
package-lock.json generated Normal file
View File

@ -0,0 +1,202 @@
{
"name": "servicenow-glpi-sync",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "servicenow-glpi-sync",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"axios": "^0.21.1",
"dotenv": "^10.0.0",
"pg": "^8.7.1"
}
},
"node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.14.0"
}
},
"node_modules/dotenv": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=10"
}
},
"node_modules/follow-redirects": {
"version": "1.15.11",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/pg": {
"version": "8.16.3",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
"license": "MIT",
"dependencies": {
"pg-connection-string": "^2.9.1",
"pg-pool": "^3.10.1",
"pg-protocol": "^1.10.3",
"pg-types": "2.2.0",
"pgpass": "1.0.5"
},
"engines": {
"node": ">= 16.0.0"
},
"optionalDependencies": {
"pg-cloudflare": "^1.2.7"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-cloudflare": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
"integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
"license": "MIT",
"optional": true
},
"node_modules/pg-connection-string": {
"version": "2.9.1",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
"integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
"license": "MIT"
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"license": "ISC",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
"integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
"license": "MIT",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
"integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==",
"license": "MIT"
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"license": "MIT",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"license": "MIT",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"license": "MIT",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"license": "ISC",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"license": "MIT",
"engines": {
"node": ">=0.4"
}
}
}
}

18
package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "servicenow-glpi-sync",
"version": "1.0.0",
"description": "A Node.js application for integrating ServiceNow and GLPI.",
"main": "app.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "echo \"No tests specified\" && exit 0"
},
"dependencies": {
"axios": "^0.21.1",
"dotenv": "^10.0.0",
"pg": "^8.7.1"
},
"author": "Your Name",
"license": "MIT"
}

29
src/app.js Normal file
View File

@ -0,0 +1,29 @@
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const routes = require('./src/routes/index');
const { logger } = require('./utils/logger');
const app = express();
// Middleware setup
app.use(cors()); // Enable CORS for all routes
app.use(bodyParser.json()); // Parse JSON request bodies
app.use(bodyParser.urlencoded({ extended: true })); // Parse URL-encoded request bodies
// Logger middleware
app.use((req, res, next) => {
logger.info(`${req.method} ${req.url}`);
next();
});
// Routes setup
app.use('/api', routes);
// Error handling middleware
app.use((err, req, res, next) => {
logger.error(err.message);
res.status(500).json({ error: 'Internal Server Error' });
});
module.exports = app;

View File

View File

View File

0
src/data/database.js Normal file
View File

View File

@ -0,0 +1,28 @@
const { Sequelize } = require('sequelize');
require('dotenv').config();
// Database connection configuration
const dbConfig = {
host: process.env.DB_HOST,
dialect: process.env.DB_DIALECT || 'mysql', // Default to MySQL if not specified
logging: false, // Disable logging; enable for debugging
};
// Create a new Sequelize instance
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, dbConfig);
// Test the database connection
const testConnection = async () => {
try {
await sequelize.authenticate();
console.log('Connection to the database has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}
};
// Export the sequelize instance and the test function
module.exports = {
sequelize,
testConnection,
};

View File

View File

@ -0,0 +1,44 @@
// src/models/ticketModel.js
const { DataTypes } = require('sequelize');
const db = require('./db');
// Define the Ticket model
const Ticket = db.define('Ticket', {
id: {
type: DataTypes.STRING,
primaryKey: true,
allowNull: false,
},
short_description: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: true,
},
state: {
type: DataTypes.STRING,
allowNull: false,
},
priority: {
type: DataTypes.STRING,
allowNull: true,
},
created_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
updated_at: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
}, {
timestamps: false, // Disable automatic timestamps
});
// Export the Ticket model
module.exports = Ticket;

View File

@ -0,0 +1,57 @@
// src/services/glpiService.js
const axios = require('axios');
const { GLPI_URL, GLPI_APP_TOKEN, GLPI_USER_TOKEN } = process.env;
// Function to create a ticket in GLPI
const createTicketInGLPI = async (ticketData) => {
try {
const response = await axios.post(`${GLPI_URL}/ticket`, ticketData, {
headers: {
'Content-Type': 'application/json',
'App-Token': GLPI_APP_TOKEN,
'Authorization': `Bearer ${GLPI_USER_TOKEN}`
}
});
return response.data;
} catch (error) {
throw new Error(`Error creating ticket in GLPI: ${error.message}`);
}
};
// Function to update a ticket in GLPI
const updateTicketInGLPI = async (ticketId, ticketData) => {
try {
const response = await axios.put(`${GLPI_URL}/ticket/${ticketId}`, ticketData, {
headers: {
'Content-Type': 'application/json',
'App-Token': GLPI_APP_TOKEN,
'Authorization': `Bearer ${GLPI_USER_TOKEN}`
}
});
return response.data;
} catch (error) {
throw new Error(`Error updating ticket in GLPI: ${error.message}`);
}
};
// Function to fetch ticket details from GLPI
const getTicketFromGLPI = async (ticketId) => {
try {
const response = await axios.get(`${GLPI_URL}/ticket/${ticketId}`, {
headers: {
'App-Token': GLPI_APP_TOKEN,
'Authorization': `Bearer ${GLPI_USER_TOKEN}`
}
});
return response.data;
} catch (error) {
throw new Error(`Error fetching ticket from GLPI: ${error.message}`);
}
};
module.exports = {
createTicketInGLPI,
updateTicketInGLPI,
getTicketFromGLPI
};

View File

View File

@ -0,0 +1,56 @@
// src/services/servicenowService.js
const axios = require('axios');
const { logError, logInfo } = require('../utils/logger');
// Configurações da API do ServiceNow
const serviceNowConfig = {
baseURL: process.env.SERVICENOW_BASE_URL,
auth: {
username: process.env.SERVICENOW_USERNAME,
password: process.env.SERVICENOW_PASSWORD,
},
};
// Função para buscar um ticket pelo ID
const getTicketById = async (ticketId) => {
try {
const response = await axios.get(`/api/now/table/ticket/${ticketId}`, serviceNowConfig);
logInfo(`Ticket ${ticketId} fetched successfully.`);
return response.data.result;
} catch (error) {
logError(`Error fetching ticket ${ticketId}: ${error.message}`);
throw error;
}
};
// Função para criar um novo ticket no ServiceNow
const createTicket = async (ticketData) => {
try {
const response = await axios.post('/api/now/table/ticket', ticketData, serviceNowConfig);
logInfo(`Ticket created successfully with ID: ${response.data.result.sys_id}`);
return response.data.result;
} catch (error) {
logError(`Error creating ticket: ${error.message}`);
throw error;
}
};
// Função para atualizar um ticket existente
const updateTicket = async (ticketId, ticketData) => {
try {
const response = await axios.put(`/api/now/table/ticket/${ticketId}`, ticketData, serviceNowConfig);
logInfo(`Ticket ${ticketId} updated successfully.`);
return response.data.result;
} catch (error) {
logError(`Error updating ticket ${ticketId}: ${error.message}`);
throw error;
}
};
// Exporta as funções para uso em outros módulos
module.exports = {
getTicketById,
createTicket,
updateTicket,
};

View File

39
src/utils/logger.js Normal file
View File

@ -0,0 +1,39 @@
const winston = require('winston');
const path = require('path');
// Create a logger instance with specified settings
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
// Log to a file for application events
new winston.transports.File({ filename: path.join(__dirname, '../../logs/app.log') }),
// Log to a file for error events
new winston.transports.File({ filename: path.join(__dirname, '../../logs/error.log'), level: 'error' }),
],
});
// If not in production, log to the console as well
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple(),
}));
}
// Utility function to log errors
const logError = (error) => {
logger.error(error);
};
// Utility function to log info messages
const logInfo = (message) => {
logger.info(message);
};
module.exports = {
logError,
logInfo,
};