GTARH_Fonctionnel_V1

This commit is contained in:
2025-12-02 17:57:33 +01:00
parent 721c02759f
commit bbebf6f44a
96 changed files with 27158 additions and 0 deletions

31
Backend/.env Normal file
View 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

View 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

File diff suppressed because it is too large Load Diff

24
Backend/package.json Normal file
View 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

File diff suppressed because it is too large Load Diff

18
Backend/webhook-config.js Normal file
View 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
View 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;