GTARH_Fonctionnel_V1
This commit is contained in:
31
Backend/.env
Normal file
31
Backend/.env
Normal file
@@ -0,0 +1,31 @@
|
||||
# Configuration Base de données MySQL
|
||||
DB_SERVER=192.168.0.4
|
||||
DB_DATABASE=DemandeConge
|
||||
DB_PASSWORD=-2b/)ru5/Bi8P[7_
|
||||
DB_ENCRYPT=true
|
||||
DB_TRUST_SERVER_CERTIFICATE=true
|
||||
PORT=3000
|
||||
|
||||
COLLABORATEURS_URL=http://localhost:3000
|
||||
RH_URL=http://localhost:3001
|
||||
WEBHOOK_SECRET=secret-rh-2025
|
||||
|
||||
|
||||
|
||||
# Configuration Serveur
|
||||
PORT=3001
|
||||
NODE_ENV=development
|
||||
|
||||
# Configuration Azure AD (Microsoft Graph)
|
||||
AZURE_TENANT_ID=9840a2a0-6ae1-4688-b03d-d2ec291be0f9
|
||||
AZURE_CLIENT_ID=4bb4cc24-bac3-427c-b02c-5d14fc67b561
|
||||
AZURE_CLIENT_SECRET=o2q8Q~nGLCkry6XOriVFBMvKUk.5cXtFutAQVdx9
|
||||
AZURE_GROUP_ID=c1ea877c-6bca-4f47-bfad-f223640813a0
|
||||
JWT_SECRET=o2q8Q~nGLCkry6XOriVFBMvKUk.5cXtFutAQVdx9
|
||||
|
||||
# Email configuration
|
||||
EMAIL_FROM=gtanoreply@ensup.eu
|
||||
|
||||
# Upload configuration
|
||||
UPLOAD_DIR=./uploads
|
||||
MAX_FILE_SIZE=5242880
|
||||
8
Backend/DockerFileGTARH.backend
Normal file
8
Backend/DockerFileGTARH.backend
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM node:18-alpine
|
||||
RUN apk add --no-cache curl mysql-client python3 make g++
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install --production
|
||||
COPY . .
|
||||
EXPOSE 3000
|
||||
CMD ["node", "server.js"]
|
||||
1840
Backend/package-lock.json
generated
Normal file
1840
Backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
Backend/package.json
Normal file
24
Backend/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "gtarh-backend",
|
||||
"version": "1.0.0",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "nodemon server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@azure/msal-node": "^3.8.0",
|
||||
"axios": "^1.12.2",
|
||||
"cors": "^2.8.5",
|
||||
"crypto": "^1.0.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^5.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"mysql2": "^3.15.1",
|
||||
"node-fetch": "^2.7.0",
|
||||
"pdfkit": "^0.17.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.10"
|
||||
}
|
||||
}
|
||||
4263
Backend/server.js
Normal file
4263
Backend/server.js
Normal file
File diff suppressed because it is too large
Load Diff
18
Backend/webhook-config.js
Normal file
18
Backend/webhook-config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// Configuration des webhooks
|
||||
const WEBHOOKS = {
|
||||
COLLABORATEURS_URL: process.env.COLLABORATEURS_URL || 'http://localhost:3000',
|
||||
RH_URL: process.env.RH_URL || 'http://localhost:3001',
|
||||
SECRET_KEY: process.env.WEBHOOK_SECRET || 'secret-key-securise'
|
||||
};
|
||||
|
||||
// Types d'événements
|
||||
const EVENTS = {
|
||||
DEMANDE_VALIDATED: 'demande.validated',
|
||||
DEMANDE_CREATED: 'demande.created',
|
||||
DEMANDE_UPDATED: 'demande.updated',
|
||||
DEMANDE_CANCELLED: 'demande.cancelled',
|
||||
DEMANDE_DELETED: 'demande.deleted',
|
||||
COMPTEUR_UPDATED: 'compteur.updated'
|
||||
};
|
||||
|
||||
module.exports = { WEBHOOKS, EVENTS };
|
||||
111
Backend/webhook-utils.js
Normal file
111
Backend/webhook-utils.js
Normal file
@@ -0,0 +1,111 @@
|
||||
// webhook-utils.js (VERSION COMMONJS)
|
||||
const axios = require('axios');
|
||||
const crypto = require('crypto');
|
||||
|
||||
class WebhookManager {
|
||||
constructor(secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère une signature HMAC SHA-256 pour sécuriser le webhook
|
||||
* @param {Object} payload - Les données à signer
|
||||
* @returns {string} La signature hexadécimale
|
||||
*/
|
||||
generateSignature(payload) {
|
||||
return crypto
|
||||
.createHmac('sha256', this.secretKey)
|
||||
.update(JSON.stringify(payload))
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie la signature d'un webhook reçu
|
||||
* @param {Object} payload - Les données reçues
|
||||
* @param {string} receivedSignature - La signature reçue dans le header
|
||||
* @returns {boolean} True si la signature est valide
|
||||
*/
|
||||
verifySignature(payload, receivedSignature) {
|
||||
if (!receivedSignature) {
|
||||
console.error('❌ Aucune signature fournie');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const expectedSignature = this.generateSignature(payload);
|
||||
return crypto.timingSafeEqual(
|
||||
Buffer.from(expectedSignature),
|
||||
Buffer.from(receivedSignature)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur vérification signature:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un webhook à une URL cible avec retry automatique
|
||||
* @param {string} targetUrl - URL du serveur cible
|
||||
* @param {string} eventType - Type d'événement (ex: 'demande.validated')
|
||||
* @param {Object} data - Données de l'événement
|
||||
* @param {number} retries - Nombre de tentatives (défaut: 3)
|
||||
* @returns {Promise<Object>} La réponse du serveur
|
||||
*/
|
||||
async sendWebhook(targetUrl, eventType, data, retries = 3) {
|
||||
const payload = {
|
||||
event: eventType,
|
||||
data: data,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
const signature = this.generateSignature(payload);
|
||||
|
||||
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||
try {
|
||||
console.log(`📤 Envoi webhook: ${eventType} vers ${targetUrl} (tentative ${attempt}/${retries})`);
|
||||
|
||||
const response = await axios.post(
|
||||
`${targetUrl}/api/webhook/receive`,
|
||||
payload,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Webhook-Signature': signature
|
||||
},
|
||||
timeout: 5000
|
||||
}
|
||||
);
|
||||
|
||||
console.log(`✅ Webhook envoyé avec succès: ${eventType}`);
|
||||
return response.data;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Erreur envoi webhook (tentative ${attempt}/${retries}):`, error.message);
|
||||
|
||||
if (attempt === retries) {
|
||||
console.error(`❌ Échec définitif du webhook après ${retries} tentatives`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
const waitTime = 1000 * attempt;
|
||||
console.log(`⏳ Nouvelle tentative dans ${waitTime}ms...`);
|
||||
await new Promise(resolve => setTimeout(resolve, waitTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un webhook sans attendre la réponse (fire and forget)
|
||||
* @param {string} targetUrl - URL du serveur cible
|
||||
* @param {string} eventType - Type d'événement
|
||||
* @param {Object} data - Données de l'événement
|
||||
*/
|
||||
sendWebhookAsync(targetUrl, eventType, data) {
|
||||
this.sendWebhook(targetUrl, eventType, data)
|
||||
.catch(error => {
|
||||
console.error('❌ Webhook async échoué (non bloquant):', error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebhookManager;
|
||||
Reference in New Issue
Block a user