856 lines
33 KiB
JavaScript
856 lines
33 KiB
JavaScript
const express = require('express');
|
||
const cors = require('cors');
|
||
const sql = require('mssql');
|
||
require('dotenv').config();
|
||
|
||
const app = express();
|
||
const PORT = 3001;
|
||
|
||
// Configuration base de données
|
||
const dbConfig = {
|
||
server: process.env.DB_SERVER,
|
||
database: process.env.DB_DATABASE,
|
||
user: process.env.DB_USER,
|
||
password: process.env.DB_PASSWORD,
|
||
options: {
|
||
encrypt: true,
|
||
trustServerCertificate: true,
|
||
enableArithAbort: true
|
||
}
|
||
};
|
||
|
||
// Middleware
|
||
app.use(cors({
|
||
origin: ['http://localhost:5173', 'http://localhost:3000', 'http://127.0.0.1:5173'],
|
||
credentials: true
|
||
}));
|
||
app.use(express.json());
|
||
|
||
// Log de toutes les requêtes
|
||
app.use((req, res, next) => {
|
||
console.log(`${new Date().toISOString()} - ${req.method} ${req.path} depuis ${req.get('origin') || 'inconnu'}`);
|
||
if (req.query.user_email || req.query.email) {
|
||
console.log(` - Filtrage pour utilisateur: ${req.query.user_email || req.query.email}`);
|
||
}
|
||
next();
|
||
});
|
||
|
||
// Variable pour stocker la connexion et l'état de la migration
|
||
let pool = null;
|
||
let systemStatus = {
|
||
hasFormateurEmailColumn: false,
|
||
hasFormateurView: false,
|
||
canAccessFormateurView: false,
|
||
hasFormateurLocal: false,
|
||
operatingMode: 'unknown'
|
||
};
|
||
|
||
// Fonction pour se connecter à la base
|
||
async function connectDatabase() {
|
||
try {
|
||
pool = await sql.connect(dbConfig);
|
||
console.log('Base de données connectée');
|
||
|
||
// Diagnostic automatique de la structure et permissions
|
||
await checkSystemStatus();
|
||
|
||
return true;
|
||
} catch (error) {
|
||
console.error('Erreur de connexion :', error.message);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// Fonction pour vérifier l'état complet du système
|
||
async function checkSystemStatus() {
|
||
try {
|
||
// 1. Vérifier si la colonne formateur_email_fk existe
|
||
const columnCheck = await pool.request().query(`
|
||
SELECT COUNT(*) as count
|
||
FROM INFORMATION_SCHEMA.COLUMNS
|
||
WHERE TABLE_NAME = 'declarations'
|
||
AND COLUMN_NAME = 'formateur_email_fk'
|
||
`);
|
||
systemStatus.hasFormateurEmailColumn = columnCheck.recordset[0].count > 0;
|
||
|
||
// 2. Vérifier si la vue Formateurs existe
|
||
const viewCheck = await pool.request().query(`
|
||
SELECT COUNT(*) as count
|
||
FROM INFORMATION_SCHEMA.VIEWS
|
||
WHERE TABLE_NAME = 'Formateurs'
|
||
`);
|
||
systemStatus.hasFormateurView = viewCheck.recordset[0].count > 0;
|
||
|
||
// 3. Tester l'accès à la vue Formateurs si elle existe
|
||
if (systemStatus.hasFormateurView) {
|
||
try {
|
||
await pool.request().query(`SELECT TOP 1 userPrincipalName FROM [dbo].[Formateurs]`);
|
||
systemStatus.canAccessFormateurView = true;
|
||
console.log('✅ Accès à la vue Formateurs: OK');
|
||
} catch (error) {
|
||
systemStatus.canAccessFormateurView = false;
|
||
console.log('❌ Accès à la vue Formateurs: ERREUR -', error.message);
|
||
if (error.message.includes('HP-TO-O365')) {
|
||
console.log('💡 Problème de permissions sur la base HP-TO-O365');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 4. Vérifier si la table formateurs_local existe et est accessible
|
||
try {
|
||
await pool.request().query(`SELECT TOP 1 * FROM formateurs_local`);
|
||
systemStatus.hasFormateurLocal = true;
|
||
console.log('✅ Table formateurs_local: OK');
|
||
} catch (error) {
|
||
systemStatus.hasFormateurLocal = false;
|
||
console.log('❌ Table formateurs_local: non accessible');
|
||
}
|
||
|
||
// 5. Déterminer le mode de fonctionnement optimal
|
||
if (systemStatus.hasFormateurEmailColumn && systemStatus.canAccessFormateurView) {
|
||
systemStatus.operatingMode = 'new_with_view';
|
||
} else if (systemStatus.hasFormateurEmailColumn && systemStatus.hasFormateurLocal) {
|
||
systemStatus.operatingMode = 'new_with_local';
|
||
} else if (systemStatus.hasFormateurEmailColumn) {
|
||
systemStatus.operatingMode = 'new_email_only';
|
||
} else {
|
||
systemStatus.operatingMode = 'legacy_hash';
|
||
}
|
||
|
||
console.log('📊 État du système:');
|
||
console.log(` - Colonne formateur_email_fk: ${systemStatus.hasFormateurEmailColumn ? '✅' : '❌'}`);
|
||
console.log(` - Vue Formateurs: ${systemStatus.hasFormateurView ? '✅' : '❌'}`);
|
||
console.log(` - Accès vue Formateurs: ${systemStatus.canAccessFormateurView ? '✅' : '❌'}`);
|
||
console.log(` - Table formateurs_local: ${systemStatus.hasFormateurLocal ? '✅' : '❌'}`);
|
||
console.log(` - Mode de fonctionnement: ${systemStatus.operatingMode}`);
|
||
|
||
} catch (error) {
|
||
console.error('Erreur lors du diagnostic:', error.message);
|
||
systemStatus.operatingMode = 'legacy_hash';
|
||
}
|
||
}
|
||
|
||
// Route de diagnostic complet
|
||
app.get('/api/diagnostic', async (req, res) => {
|
||
try {
|
||
await checkSystemStatus();
|
||
|
||
let recommendations = [];
|
||
|
||
switch (systemStatus.operatingMode) {
|
||
case 'new_with_view':
|
||
recommendations.push('✅ Système optimal - toutes les fonctionnalités disponibles');
|
||
break;
|
||
case 'new_with_local':
|
||
recommendations.push('⚠️ Fonctionne avec la table locale - pas d\'accès à la vue distante');
|
||
recommendations.push('💡 Vérifier les permissions sur HP-TO-O365 pour utiliser la vue');
|
||
break;
|
||
case 'new_email_only':
|
||
recommendations.push('⚠️ Mode dégradé - sauvegarde par email mais pas de détails formateurs');
|
||
recommendations.push('💡 Restaurer l\'accès à la vue Formateurs ou table formateurs_local');
|
||
break;
|
||
case 'legacy_hash':
|
||
recommendations.push('🔄 Mode compatibilité - utilise l\'ancien système de hash');
|
||
recommendations.push('💡 Appliquer la migration avec POST /api/migrate');
|
||
break;
|
||
}
|
||
|
||
res.json({
|
||
systemStatus,
|
||
recommendations,
|
||
currentMode: systemStatus.operatingMode
|
||
});
|
||
|
||
} catch (error) {
|
||
res.status(500).json({ error: error.message });
|
||
}
|
||
});
|
||
|
||
// Route pour appliquer la migration
|
||
app.post('/api/migrate', async (req, res) => {
|
||
try {
|
||
const steps = [];
|
||
|
||
// Étape 1: Ajouter la colonne si nécessaire
|
||
if (!systemStatus.hasFormateurEmailColumn) {
|
||
try {
|
||
await pool.request().query(`
|
||
ALTER TABLE [dbo].[declarations]
|
||
ADD [formateur_email_fk] [nvarchar](255) NULL
|
||
`);
|
||
steps.push('✅ Colonne formateur_email_fk ajoutée');
|
||
} catch (error) {
|
||
if (!error.message.includes('already exists')) {
|
||
throw error;
|
||
}
|
||
steps.push('ℹ️ Colonne formateur_email_fk déjà existante');
|
||
}
|
||
}
|
||
|
||
// Étape 2: Créer un index
|
||
try {
|
||
await pool.request().query(`
|
||
CREATE NONCLUSTERED INDEX [IX_declarations_formateur_email_fk]
|
||
ON [dbo].[declarations] ([formateur_email_fk])
|
||
`);
|
||
steps.push('✅ Index créé');
|
||
} catch (error) {
|
||
if (error.message.includes('already exists')) {
|
||
steps.push('ℹ️ Index déjà existant');
|
||
} else {
|
||
steps.push(`⚠️ Erreur index: ${error.message}`);
|
||
}
|
||
}
|
||
|
||
// Vérifier à nouveau l'état
|
||
await checkSystemStatus();
|
||
|
||
res.json({
|
||
success: true,
|
||
steps,
|
||
newStatus: systemStatus,
|
||
message: `Migration appliquée - Mode: ${systemStatus.operatingMode}`
|
||
});
|
||
|
||
} catch (error) {
|
||
res.status(500).json({
|
||
success: false,
|
||
error: error.message,
|
||
message: 'Erreur lors de la migration'
|
||
});
|
||
}
|
||
});
|
||
|
||
// Route de test
|
||
app.get('/api/test', (req, res) => {
|
||
res.json({
|
||
message: 'Le serveur utilisateur fonctionne !',
|
||
timestamp: new Date().toISOString(),
|
||
systemStatus
|
||
});
|
||
});
|
||
|
||
// Fonction pour générer un hash reproductible depuis un email (mode legacy)
|
||
function generateHashFromEmail(email) {
|
||
let hash = 0;
|
||
for (let i = 0; i < email.length; i++) {
|
||
const char = email.charCodeAt(i);
|
||
hash = ((hash << 5) - hash) + char;
|
||
hash = hash & hash; // Convert to 32-bit integer
|
||
}
|
||
return Math.abs(hash) % 10000 + 1000;
|
||
}
|
||
|
||
// Route pour les formateurs (adaptée au mode de fonctionnement)
|
||
app.get('/api/formateurs', async (req, res) => {
|
||
try {
|
||
const { email, search } = req.query;
|
||
|
||
switch (systemStatus.operatingMode) {
|
||
case 'new_with_view':
|
||
// Utiliser la vue Formateurs
|
||
let query = `
|
||
SELECT
|
||
userPrincipalName as email,
|
||
displayName as nom_complet,
|
||
givenname as prenom,
|
||
surname as nom,
|
||
Campus,
|
||
departement,
|
||
Jobtitle
|
||
FROM [dbo].[Formateurs]
|
||
`;
|
||
|
||
const request = pool.request();
|
||
|
||
if (email) {
|
||
query += ` WHERE userPrincipalName = @email`;
|
||
request.input('email', sql.NVarChar, email);
|
||
} else if (search) {
|
||
query += ` WHERE displayName LIKE @search OR userPrincipalName LIKE @search`;
|
||
request.input('search', sql.NVarChar, `%${search}%`);
|
||
}
|
||
|
||
query += ` ORDER BY displayName`;
|
||
|
||
const result = await request.query(query);
|
||
|
||
res.json({
|
||
success: true,
|
||
data: result.recordset,
|
||
source: 'vue_formateurs'
|
||
});
|
||
break;
|
||
|
||
case 'new_with_local':
|
||
// Utiliser la table formateurs_local
|
||
let localQuery = `
|
||
SELECT
|
||
userPrincipalName as email,
|
||
displayName as nom_complet,
|
||
givenname as prenom,
|
||
surname as nom,
|
||
Campus,
|
||
departement,
|
||
Jobtitle
|
||
FROM formateurs_local
|
||
`;
|
||
|
||
const localRequest = pool.request();
|
||
|
||
if (email) {
|
||
localQuery += ` WHERE userPrincipalName = @email`;
|
||
localRequest.input('email', sql.NVarChar, email);
|
||
} else if (search) {
|
||
localQuery += ` WHERE displayName LIKE @search OR userPrincipalName LIKE @search`;
|
||
localRequest.input('search', sql.NVarChar, `%${search}%`);
|
||
}
|
||
|
||
localQuery += ` ORDER BY displayName`;
|
||
|
||
const localResult = await localRequest.query(localQuery);
|
||
|
||
res.json({
|
||
success: true,
|
||
data: localResult.recordset,
|
||
source: 'table_locale'
|
||
});
|
||
break;
|
||
|
||
case 'new_email_only':
|
||
case 'legacy_hash':
|
||
default:
|
||
// Mode dégradé - réponse basique
|
||
if (email) {
|
||
res.json({
|
||
success: true,
|
||
data: [{
|
||
email: email,
|
||
nom_complet: email.split('@')[0] || 'Utilisateur',
|
||
prenom: 'Utilisateur',
|
||
nom: email.split('@')[0] || 'Inconnu',
|
||
source: 'fallback'
|
||
}]
|
||
});
|
||
} else {
|
||
res.json({
|
||
success: true,
|
||
data: [],
|
||
source: 'fallback'
|
||
});
|
||
}
|
||
break;
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Erreur recherche formateurs:', error);
|
||
// Fallback en cas d'erreur
|
||
if (email) {
|
||
res.json({
|
||
success: true,
|
||
data: [{
|
||
email: email,
|
||
nom_complet: email.split('@')[0] || 'Utilisateur',
|
||
prenom: 'Utilisateur',
|
||
nom: email.split('@')[0] || 'Inconnu',
|
||
source: 'error_fallback'
|
||
}]
|
||
});
|
||
} else {
|
||
res.status(500).json({
|
||
success: false,
|
||
error: error.message
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
// Conversion simple en string "HH:mm:ss"
|
||
const toSQLTime = (timeStr) => {
|
||
if (!timeStr) return null;
|
||
const [hours, minutes] = timeStr.split(':').map(Number);
|
||
const d = new Date(Date.UTC(2000, 0, 1, hours, minutes, 0));
|
||
return d;
|
||
};
|
||
|
||
// Route pour sauvegarder une déclaration (ADAPTÉE AU MODE)
|
||
app.post('/api/save_declaration', async (req, res) => {
|
||
try {
|
||
const { date, activityType, hours, description, user, startTime, endTime } = req.body;
|
||
console.log('Données reçues:', { date, activityType, hours, description, user, startTime, endTime });
|
||
|
||
const heureDebutSQL = toSQLTime(startTime);
|
||
const heureFinSQL = toSQLTime(endTime);
|
||
|
||
// Validation
|
||
if (!date || !activityType || !hours || !startTime || !endTime) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
error: 'Données manquantes'
|
||
});
|
||
}
|
||
|
||
if (!user || !user.email) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
error: 'Email utilisateur requis'
|
||
});
|
||
}
|
||
|
||
const userEmail = user.email;
|
||
|
||
// Récupération du type de demande
|
||
const typeResult = await pool.request()
|
||
.input('activityType', sql.VarChar, activityType)
|
||
.query('SELECT id FROM types_demandes WHERE libelle = @activityType');
|
||
|
||
if (typeResult.recordset.length === 0) {
|
||
return res.status(400).json({
|
||
success: false,
|
||
error: `Type d'activité invalide: ${activityType}`
|
||
});
|
||
}
|
||
|
||
const typeDemandeId = typeResult.recordset[0].id;
|
||
|
||
// Logique selon le mode de fonctionnement
|
||
if (systemStatus.operatingMode.startsWith('new_')) {
|
||
// NOUVEAU SYSTÈME - par email
|
||
console.log(`💾 Sauvegarde avec nouveau système (email) - Mode: ${systemStatus.operatingMode}`);
|
||
|
||
// Vérifier si une déclaration existe déjà
|
||
const existingResult = await pool.request()
|
||
.input('formateurEmail', sql.NVarChar, userEmail)
|
||
.input('date', sql.Date, date)
|
||
.query('SELECT id FROM declarations WHERE formateur_email_fk = @formateurEmail AND date = @date');
|
||
|
||
const utilisateurId = 1; // À adapter selon votre logique
|
||
|
||
if (existingResult.recordset.length > 0) {
|
||
// UPDATE
|
||
await pool.request()
|
||
.input('formateurEmail', sql.NVarChar, userEmail)
|
||
.input('typeDemandeId', sql.Int, typeDemandeId)
|
||
.input('hours', sql.Float, hours)
|
||
.input('description', sql.NVarChar, description || null)
|
||
.input('date', sql.Date, date)
|
||
.input('heure_debut', sql.Time, heureDebutSQL)
|
||
.input('heure_fin', sql.Time, heureFinSQL)
|
||
.query(`
|
||
UPDATE declarations
|
||
SET type_demande_id = @typeDemandeId,
|
||
duree = @hours,
|
||
description = @description,
|
||
heure_debut = @heure_debut,
|
||
heure_fin = @heure_fin
|
||
WHERE formateur_email_fk = @formateurEmail AND date = @date
|
||
`);
|
||
console.log('✅ Déclaration mise à jour');
|
||
} else {
|
||
// INSERT
|
||
await pool.request()
|
||
.input('utilisateurId', sql.Int, utilisateurId)
|
||
.input('formateurEmail', sql.NVarChar, userEmail)
|
||
.input('typeDemandeId', sql.Int, typeDemandeId)
|
||
.input('date', sql.Date, date)
|
||
.input('hours', sql.Float, hours)
|
||
.input('description', sql.NVarChar, description || null)
|
||
.input('heure_debut', sql.Time, heureDebutSQL)
|
||
.input('heure_fin', sql.Time, heureFinSQL)
|
||
.query(`
|
||
INSERT INTO declarations (
|
||
utilisateur_id, formateur_email_fk, type_demande_id,
|
||
date, duree, description, heure_debut, heure_fin
|
||
)
|
||
VALUES (
|
||
@utilisateurId, @formateurEmail, @typeDemandeId,
|
||
@date, @hours, @description, @heure_debut, @heure_fin
|
||
)
|
||
`);
|
||
console.log('✅ Nouvelle déclaration créée');
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
message: `Déclaration sauvegardée avec succès (${systemStatus.operatingMode})`,
|
||
formateurEmail: userEmail
|
||
});
|
||
|
||
} else {
|
||
// ANCIEN SYSTÈME - par hash
|
||
console.log('💾 Sauvegarde avec ancien système (hash)');
|
||
|
||
const formateurNumero = generateHashFromEmail(userEmail);
|
||
|
||
// Vérifier si une déclaration existe déjà
|
||
const existingResult = await pool.request()
|
||
.input('formateurNumero', sql.Int, formateurNumero)
|
||
.input('date', sql.Date, date)
|
||
.query('SELECT id FROM declarations WHERE formateur_numero = @formateurNumero AND date = @date');
|
||
|
||
if (existingResult.recordset.length > 0) {
|
||
// UPDATE
|
||
await pool.request()
|
||
.input('utilisateurId', sql.Int, formateurNumero)
|
||
.input('typeDemandeId', sql.Int, typeDemandeId)
|
||
.input('hours', sql.Float, hours)
|
||
.input('description', sql.NVarChar, description || null)
|
||
.input('formateurNumero', sql.Int, formateurNumero)
|
||
.input('date', sql.Date, date)
|
||
.input('heure_debut', sql.Time, heureDebutSQL)
|
||
.input('heure_fin', sql.Time, heureFinSQL)
|
||
.query(`
|
||
UPDATE declarations
|
||
SET utilisateur_id = @utilisateurId,
|
||
type_demande_id = @typeDemandeId,
|
||
duree = @hours,
|
||
description = @description,
|
||
heure_debut = @heure_debut,
|
||
heure_fin = @heure_fin
|
||
WHERE formateur_numero = @formateurNumero AND date = @date
|
||
`);
|
||
console.log('✅ Déclaration mise à jour (legacy)');
|
||
} else {
|
||
// INSERT
|
||
await pool.request()
|
||
.input('utilisateurId', sql.Int, formateurNumero)
|
||
.input('formateurNumero', sql.Int, formateurNumero)
|
||
.input('typeDemandeId', sql.Int, typeDemandeId)
|
||
.input('date', sql.Date, date)
|
||
.input('hours', sql.Float, hours)
|
||
.input('description', sql.NVarChar, description || null)
|
||
.input('heure_debut', sql.Time, heureDebutSQL)
|
||
.input('heure_fin', sql.Time, heureFinSQL)
|
||
.query(`
|
||
INSERT INTO declarations (
|
||
utilisateur_id, formateur_numero, type_demande_id,
|
||
date, duree, description, heure_debut, heure_fin
|
||
)
|
||
VALUES (
|
||
@utilisateurId, @formateurNumero, @typeDemandeId,
|
||
@date, @hours, @description, @heure_debut, @heure_fin
|
||
)
|
||
`);
|
||
console.log('✅ Nouvelle déclaration créée (legacy)');
|
||
}
|
||
|
||
res.json({
|
||
success: true,
|
||
message: 'Déclaration sauvegardée avec succès (legacy)',
|
||
formateurNumero: formateurNumero
|
||
});
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Erreur lors de la sauvegarde:', error);
|
||
res.status(500).json({
|
||
success: false,
|
||
error: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
// Route pour récupérer par email (ADAPTÉE AU MODE)
|
||
app.get('/api/get_declarations_by_email', async (req, res) => {
|
||
try {
|
||
const { email } = req.query;
|
||
console.log('DEBUG - Email reçu:', email);
|
||
|
||
if (!email) {
|
||
return res.status(400).json({ error: 'Email requis' });
|
||
}
|
||
|
||
switch (systemStatus.operatingMode) {
|
||
case 'new_with_view':
|
||
// Avec vue Formateurs
|
||
const resultWithView = await pool.request()
|
||
.input('email', sql.NVarChar, email)
|
||
.query(`
|
||
SELECT
|
||
d.id,
|
||
d.utilisateur_id,
|
||
d.formateur_email_fk as formateur_email,
|
||
f.displayName as formateur_nom,
|
||
f.Campus,
|
||
f.departement,
|
||
td.id as type_demande_id,
|
||
td.libelle as activityType,
|
||
d.date,
|
||
d.duree,
|
||
d.heure_debut,
|
||
d.heure_fin,
|
||
d.description,
|
||
d.status
|
||
FROM declarations d
|
||
INNER JOIN types_demandes td ON d.type_demande_id = td.id
|
||
LEFT JOIN [dbo].[Formateurs] f ON d.formateur_email_fk = f.userPrincipalName
|
||
WHERE d.formateur_email_fk = @email
|
||
ORDER BY d.date DESC
|
||
`);
|
||
res.json(resultWithView.recordset);
|
||
break;
|
||
|
||
case 'new_with_local':
|
||
// Avec table formateurs_local
|
||
const resultWithLocal = await pool.request()
|
||
.input('email', sql.NVarChar, email)
|
||
.query(`
|
||
SELECT
|
||
d.id,
|
||
d.utilisateur_id,
|
||
d.formateur_email_fk as formateur_email,
|
||
f.displayName as formateur_nom,
|
||
f.Campus,
|
||
f.departement,
|
||
td.id as type_demande_id,
|
||
td.libelle as activityType,
|
||
d.date,
|
||
d.duree,
|
||
d.heure_debut,
|
||
d.heure_fin,
|
||
d.description,
|
||
d.status
|
||
FROM declarations d
|
||
INNER JOIN types_demandes td ON d.type_demande_id = td.id
|
||
LEFT JOIN formateurs_local f ON d.formateur_email_fk = f.userPrincipalName
|
||
WHERE d.formateur_email_fk = @email
|
||
ORDER BY d.date DESC
|
||
`);
|
||
res.json(resultWithLocal.recordset);
|
||
break;
|
||
|
||
case 'new_email_only':
|
||
// Sans jointure formateur
|
||
const resultEmailOnly = await pool.request()
|
||
.input('email', sql.NVarChar, email)
|
||
.query(`
|
||
SELECT
|
||
d.id,
|
||
d.utilisateur_id,
|
||
d.formateur_email_fk as formateur_email,
|
||
td.id as type_demande_id,
|
||
td.libelle as activityType,
|
||
d.date,
|
||
d.duree,
|
||
d.heure_debut,
|
||
d.heure_fin,
|
||
d.description,
|
||
d.status
|
||
FROM declarations d
|
||
INNER JOIN types_demandes td ON d.type_demande_id = td.id
|
||
WHERE d.formateur_email_fk = @email
|
||
ORDER BY d.date DESC
|
||
`);
|
||
res.json(resultEmailOnly.recordset);
|
||
break;
|
||
|
||
case 'legacy_hash':
|
||
default:
|
||
// Ancien système avec hash
|
||
const formateurNumero = generateHashFromEmail(email);
|
||
console.log(`Email ${email} -> Numéro ${formateurNumero}`);
|
||
|
||
const resultLegacy = await pool.request()
|
||
.input('formateur_numero', sql.Int, formateurNumero)
|
||
.query(`
|
||
SELECT
|
||
d.id,
|
||
d.formateur_numero as utilisateur_id,
|
||
td.id as type_demande_id,
|
||
d.date,
|
||
d.duree,
|
||
d.heure_debut,
|
||
d.heure_fin,
|
||
d.description,
|
||
td.libelle as activityType,
|
||
d.status
|
||
FROM declarations d
|
||
INNER JOIN types_demandes td ON d.type_demande_id = td.id
|
||
WHERE d.formateur_numero = @formateur_numero
|
||
ORDER BY d.date DESC
|
||
`);
|
||
res.json(resultLegacy.recordset);
|
||
break;
|
||
}
|
||
|
||
console.log(`✅ Déclarations récupérées pour ${email} (mode: ${systemStatus.operatingMode})`);
|
||
|
||
} catch (error) {
|
||
console.error('Erreur lors de la récupération par email:', error.message);
|
||
res.status(500).json({ error: error.message });
|
||
}
|
||
});
|
||
|
||
// Route pour récupérer toutes les déclarations
|
||
app.get('/api/get_declarations', async (req, res) => {
|
||
try {
|
||
const { user_email, admin } = req.query;
|
||
|
||
// Utiliser la même logique que pour get_declarations_by_email
|
||
if (user_email && !admin) {
|
||
// Rediriger vers get_declarations_by_email
|
||
req.query = { email: user_email };
|
||
return app._router.handle(req, res);
|
||
}
|
||
|
||
// Mode admin - récupérer toutes les déclarations
|
||
let result;
|
||
switch (systemStatus.operatingMode) {
|
||
case 'new_with_view':
|
||
result = await pool.request().query(`
|
||
SELECT
|
||
d.id,
|
||
d.utilisateur_id,
|
||
d.formateur_email_fk as formateur_email,
|
||
f.displayName as formateur_nom,
|
||
f.Campus,
|
||
f.departement,
|
||
td.id as type_demande_id,
|
||
td.libelle as activityType,
|
||
d.date,
|
||
d.duree,
|
||
d.heure_debut,
|
||
d.heure_fin,
|
||
d.description,
|
||
d.status
|
||
FROM declarations d
|
||
INNER JOIN types_demandes td ON d.type_demande_id = td.id
|
||
LEFT JOIN [dbo].[Formateurs] f ON d.formateur_email_fk = f.userPrincipalName
|
||
ORDER BY d.date DESC
|
||
`);
|
||
break;
|
||
|
||
case 'new_with_local':
|
||
result = await pool.request().query(`
|
||
SELECT
|
||
d.id,
|
||
d.utilisateur_id,
|
||
d.formateur_email_fk as formateur_email,
|
||
f.displayName as formateur_nom,
|
||
f.Campus,
|
||
f.departement,
|
||
td.id as type_demande_id,
|
||
td.libelle as activityType,
|
||
d.date,
|
||
d.duree,
|
||
d.heure_debut,
|
||
d.heure_fin,
|
||
d.description,
|
||
d.status
|
||
FROM declarations d
|
||
INNER JOIN types_demandes td ON d.type_demande_id = td.id
|
||
LEFT JOIN formateurs_local f ON d.formateur_email_fk = f.userPrincipalName
|
||
ORDER BY d.date DESC
|
||
`);
|
||
break;
|
||
|
||
case 'new_email_only':
|
||
result = await pool.request().query(`
|
||
SELECT
|
||
d.id,
|
||
d.utilisateur_id,
|
||
d.formateur_email_fk as formateur_email,
|
||
td.id as type_demande_id,
|
||
td.libelle as activityType,
|
||
d.date,
|
||
d.duree,
|
||
d.heure_debut,
|
||
d.heure_fin,
|
||
d.description,
|
||
d.status
|
||
FROM declarations d
|
||
INNER JOIN types_demandes td ON d.type_demande_id = td.id
|
||
ORDER BY d.date DESC
|
||
`);
|
||
break;
|
||
|
||
default:
|
||
result = await pool.request().query(`
|
||
SELECT
|
||
d.id,
|
||
d.formateur_numero as utilisateur_id,
|
||
td.id as type_demande_id,
|
||
d.date,
|
||
d.duree,
|
||
d.heure_debut,
|
||
d.heure_fin,
|
||
d.description,
|
||
d.formateur_numero,
|
||
td.libelle as activityType,
|
||
d.status
|
||
FROM declarations d
|
||
INNER JOIN types_demandes td ON d.type_demande_id = td.id
|
||
ORDER BY d.date DESC
|
||
`);
|
||
break;
|
||
}
|
||
|
||
res.json(result.recordset);
|
||
|
||
} catch (error) {
|
||
console.error('Erreur lors de la récupération:', error);
|
||
res.status(500).json({ error: error.message });
|
||
}
|
||
});
|
||
|
||
// Route de création de formateur (pour compatibilité)
|
||
app.post('/api/create_formateur', (req, res) => {
|
||
res.json({
|
||
success: true,
|
||
message: 'Route de compatibilité - utilisez /api/formateurs'
|
||
});
|
||
});
|
||
|
||
// Démarrage du serveur
|
||
async function startServer() {
|
||
const dbConnected = await connectDatabase();
|
||
|
||
if (!dbConnected) {
|
||
console.log('Impossible de démarrer sans base de données');
|
||
return;
|
||
}
|
||
|
||
app.listen(PORT, () => {
|
||
console.log(`🚀 Serveur UTILISATEUR démarré sur http://localhost:${PORT}`);
|
||
console.log(`📊 Mode de fonctionnement: ${systemStatus.operatingMode}`);
|
||
console.log('');
|
||
console.log('Routes disponibles :');
|
||
console.log('- GET /api/diagnostic (vérifier l\'état)');
|
||
console.log('- POST /api/migrate (appliquer la migration)');
|
||
console.log('- GET /api/test');
|
||
console.log('- GET /api/formateurs?email=test@example.com');
|
||
console.log('- POST /api/save_declaration');
|
||
console.log('- GET /api/get_declarations');
|
||
console.log('- GET /api/get_declarations_by_email?email=test@example.com');
|
||
console.log('');
|
||
|
||
switch (systemStatus.operatingMode) {
|
||
case 'new_with_view':
|
||
console.log('✅ Système optimal - utilise la vue Formateurs');
|
||
break;
|
||
case 'new_with_local':
|
||
console.log('⚠️ Mode dégradé - utilise la table formateurs_local');
|
||
console.log('💡 Conseil: Vérifier les permissions sur HP-TO-O365');
|
||
break;
|
||
case 'new_email_only':
|
||
console.log('⚠️ Mode minimal - sauvegarde par email sans détails formateurs');
|
||
break;
|
||
case 'legacy_hash':
|
||
console.log('🔄 Mode compatibilité - utilise l\'ancien système de hash');
|
||
console.log('💡 Conseil: Appliquer la migration avec POST /api/migrate');
|
||
break;
|
||
}
|
||
});
|
||
}
|
||
|
||
// Arrêt propre
|
||
process.on('SIGINT', async () => {
|
||
console.log('Arrêt du serveur utilisateur...');
|
||
if (pool) {
|
||
await pool.close();
|
||
}
|
||
process.exit(0);
|
||
});
|
||
|
||
// Démarrer
|
||
startServer() |