diff --git a/VOIR-LOGS-TIMEZONE.md b/VOIR-LOGS-TIMEZONE.md new file mode 100644 index 0000000..a494489 --- /dev/null +++ b/VOIR-LOGS-TIMEZONE.md @@ -0,0 +1,190 @@ +# 🔍 VOIR LES LOGS DE TIMEZONE DIRECTEMENT DANS VOTRE NAVIGATEUR + +Ce système vous permet de voir en temps réel ce qui se passe avec les dates, **sans avoir besoin d'accès aux logs Docker**. + +--- + +## 🚀 COMMENT UTILISER + +### **ÉTAPE 1 : Rebuild et redémarrage** + +```bash +docker-compose down +docker-compose build --no-cache notes-app +docker-compose up -d +``` + +### **ÉTAPE 2 : Supprimer les anciens événements** + +```bash +docker exec noteflow-postgres psql -U noteflow -d noteflow -p 5499 \ + -c "DELETE FROM calendar_events" +``` + +### **ÉTAPE 3 : Ouvrir la page de logs** + +Ouvrez votre navigateur et allez sur : + +``` +http://localhost:2222/api/timezone-logs/html +``` + +**Laissez cette page ouverte** dans un onglet. + +### **ÉTAPE 4 : Synchroniser avec Google Calendar** + +1. Dans un autre onglet, ouvrez http://localhost:2222 +2. Allez dans **Admin** → **Google Calendar** +3. Cliquez sur **🔄 Synchroniser** + +### **ÉTAPE 5 : Regarder les logs apparaître** + +Retournez sur l'onglet des logs (`/api/timezone-logs/html`) et vous verrez : + +- 🟣 **SYNC** : Ce que Google Calendar renvoie +- 🟠 **PARSER** : Comment PostgreSQL parse les dates +- 🟢 **GET** : Ce qui est envoyé au frontend + +--- + +## 📊 FONCTIONNALITÉS DE LA PAGE DE LOGS + +### **Boutons en haut** : + +- **🔄 Rafraîchir** : Recharger la page pour voir les nouveaux logs +- **🗑️ Vider les logs** : Supprimer tous les logs +- **⏱️ Auto-refresh** : Activer le rafraîchissement automatique toutes les 3 secondes +- **Filtres** : Afficher seulement les logs d'une catégorie (Tous, Sync, Parser, Get) + +### **Statistiques affichées** : + +- Total de logs +- Nombre de logs par catégorie + +### **Chaque log affiche** : + +- ⏰ Timestamp (date et heure exacte) +- 🏷️ Catégorie (SYNC, PARSER, GET) +- 📝 Message descriptif +- 📦 Données JSON (si disponibles) + +--- + +## 🔍 EXEMPLE DE LOGS ATTENDUS + +### **Lors de la synchronisation** : + +``` +[SYNC] 📅 Événement: "Dr Julian Wlodarczak" +{ + "googleStartBrut": { + "dateTime": "2024-11-17T10:20:00+01:00" + }, + "startTimeExtrait": "2024-11-17T10:20:00+01:00", + "type": "string", + "isAllDay": false +} + +[SYNC] → Conversion: new Date("2024-11-17T10:20:00+01:00") = 2024-11-17T09:20:00.000Z +{ + "affichageParis": "17/11/2024 10:20:00", + "heureParisSeule": "10:20" +} +``` + +### **Lors du parsing PostgreSQL** : + +``` +[PARSER] Input sans TZ: "2024-11-17 09:20:00" → ISO+Z: "2024-11-17T09:20:00Z" → Output: "2024-11-17T09:20:00.000Z" +``` + +### **Lors de l'envoi au frontend** : + +``` +[GET] 📤 Envoi au frontend: "Dr Julian Wlodarczak" +{ + "start_time_DB": "2024-11-17T09:20:00.000Z", + "type": "string" +} + +[GET] → Frontend recevra: 2024-11-17T09:20:00.000Z +{ + "apresNewDate": "2024-11-17T09:20:00.000Z", + "affichageParisAttendu": "10:20" +} +``` + +--- + +## ✅ CE QU'IL FAUT VÉRIFIER + +Dans les logs, vérifiez que : + +1. ✅ **Google renvoie** : `"2024-11-17T10:20:00+01:00"` (10:20 heure de Paris) +2. ✅ **Conversion UTC** : `2024-11-17T09:20:00.000Z` (09:20 UTC = 10:20 Paris) +3. ✅ **PostgreSQL stocke** : `"2024-11-17 09:20:00"` (09:20 UTC) +4. ✅ **Parser normalise** : `"2024-11-17T09:20:00.000Z"` +5. ✅ **Frontend reçoit** : `"2024-11-17T09:20:00.000Z"` +6. ✅ **Affichage attendu** : `"10:20"` + +--- + +## 🎨 COULEURS DANS L'INTERFACE + +- 🟣 **Violet** : Logs de synchronisation (SYNC) +- 🟠 **Orange** : Logs du parser PostgreSQL (PARSER) +- 🟢 **Vert** : Logs d'envoi au frontend (GET) + +--- + +## 📱 ACCESSIBLE DEPUIS PORTAINER + +Si vous utilisez Portainer : + +1. Ouvrez Portainer +2. Cliquez sur votre container `notes-app` +3. Dans le navigateur, allez sur : `http://localhost:2222/api/timezone-logs/html` + +Vous pouvez aussi accéder à la page depuis **n'importe quel appareil** sur le même réseau : + +``` +http://VOTRE_IP:2222/api/timezone-logs/html +``` + +--- + +## 🆘 SI VOUS VOYEZ UN DÉCALAGE + +Prenez une **capture d'écran de la page de logs** et cherchez : + +- ❌ Où l'heure change de 10:20 à 11:20 +- ❌ Quelle étape cause le décalage (SYNC, PARSER, ou GET) + +Envoyez-moi la capture et je pourrai corriger le problème exact. + +--- + +## 🔄 AUTRES ENDPOINTS DISPONIBLES + +### **Logs en JSON** : +``` +GET http://localhost:2222/api/timezone-logs +``` + +### **Vider les logs** : +``` +POST http://localhost:2222/api/timezone-logs/clear +``` + +### **Logs d'une catégorie** : +``` +GET http://localhost:2222/api/timezone-logs/category/SYNC +GET http://localhost:2222/api/timezone-logs/category/PARSER +GET http://localhost:2222/api/timezone-logs/category/GET +``` + +--- + +## 💡 ASTUCE + +Activez l'**Auto-refresh** pour voir les logs apparaître en temps réel pendant que vous synchronisez ! diff --git a/config/database.js b/config/database.js index b823e3a..db6924f 100644 --- a/config/database.js +++ b/config/database.js @@ -2,6 +2,7 @@ const { Pool, types } = require('pg'); const bcrypt = require('bcrypt'); const logger = require('./logger'); +const timezoneLogger = require('../services/timezone-logger'); // IMPORTANT: Parser personnalisé pour TIMESTAMPTZ // On force PostgreSQL à renvoyer en UTC (options: '-c timezone=UTC') @@ -22,14 +23,14 @@ types.setTypeParser(1184, function(stringValue) { // Si déjà au format ISO avec Z ou timezone (+HH:MM ou +HH) if (stringValue.includes('Z') || stringValue.match(/[+-]\d{2}(:\d{2})?$/)) { result = new Date(stringValue).toISOString(); - logger.debug(`[PARSER TIMESTAMPTZ] Input avec TZ: "${originalValue}" → Output: "${result}"`); + timezoneLogger.log('PARSER', `Input avec TZ: "${originalValue}" → Output: "${result}"`); } else { // Si format "YYYY-MM-DD HH:MM:SS" sans timezone // Comme timezone=UTC, on sait que c'est en UTC // On ajoute 'Z' pour forcer JavaScript à l'interpréter comme UTC const isoString = stringValue.replace(' ', 'T') + 'Z'; result = new Date(isoString).toISOString(); - logger.debug(`[PARSER TIMESTAMPTZ] Input sans TZ: "${originalValue}" → ISO+Z: "${isoString}" → Output: "${result}"`); + timezoneLogger.log('PARSER', `Input sans TZ: "${originalValue}" → ISO+Z: "${isoString}" → Output: "${result}"`); } return result; diff --git a/routes/calendar.routes.js b/routes/calendar.routes.js index fc45ac0..0a02863 100644 --- a/routes/calendar.routes.js +++ b/routes/calendar.routes.js @@ -5,6 +5,7 @@ const { google } = require('googleapis'); const { getAll, getOne, runQuery } = require('../config/database'); const { authenticateToken, requireAdmin } = require('../middleware/auth'); const logger = require('../config/logger'); +const timezoneLogger = require('../services/timezone-logger'); // Scopes Google Calendar requis (lecture ET écriture) const SCOPES = ['https://www.googleapis.com/auth/calendar']; @@ -354,12 +355,17 @@ router.get('/events', authenticateToken, async (req, res) => { // LOG pour chaque événement récupéré events.forEach(event => { - logger.debug(`[GET /events] "${event.title}"`); - logger.debug(`[GET /events] - start_time de la DB: "${event.start_time}" (type: ${typeof event.start_time})`); + timezoneLogger.log('GET', `📤 Envoi au frontend: "${event.title}"`, { + start_time_DB: event.start_time, + type: typeof event.start_time + }); + if (event.start_time) { const date = new Date(event.start_time); - logger.debug(`[GET /events] - new Date(): ${date.toISOString()}`); - logger.debug(`[GET /events] - Affichage Paris: ${date.toLocaleString('fr-FR', { timeZone: 'Europe/Paris', hour: '2-digit', minute: '2-digit' })}`); + timezoneLogger.log('GET', ` → Frontend recevra: ${event.start_time}`, { + apresNewDate: date.toISOString(), + affichageParisAttendu: date.toLocaleTimeString('fr-FR', { timeZone: 'Europe/Paris', hour: '2-digit', minute: '2-digit' }) + }); } }); @@ -503,14 +509,19 @@ router.post('/sync', authenticateToken, async (req, res) => { const isAllDay = !event.start.dateTime; // Si pas de dateTime, c'est un événement toute la journée // LOG DÉTAILLÉ pour debug timezone - logger.info(`[SYNC CALENDAR] Événement: "${event.summary}"`); - logger.info(`[SYNC CALENDAR] - Google startTime brut: ${JSON.stringify(event.start)}`); - logger.info(`[SYNC CALENDAR] - startTime extrait: "${startTime}"`); - logger.info(`[SYNC CALENDAR] - Type: ${typeof startTime}`); + timezoneLogger.log('SYNC', `📅 Événement: "${event.summary}"`, { + googleStartBrut: event.start, + startTimeExtrait: startTime, + type: typeof startTime, + isAllDay + }); + if (startTime) { const testDate = new Date(startTime); - logger.info(`[SYNC CALENDAR] - new Date(startTime): ${testDate.toISOString()}`); - logger.info(`[SYNC CALENDAR] - Affichage Paris: ${testDate.toLocaleString('fr-FR', { timeZone: 'Europe/Paris' })}`); + timezoneLogger.log('SYNC', ` → Conversion: new Date("${startTime}") = ${testDate.toISOString()}`, { + affichageParis: testDate.toLocaleString('fr-FR', { timeZone: 'Europe/Paris' }), + heureParisSeule: testDate.toLocaleTimeString('fr-FR', { timeZone: 'Europe/Paris', hour: '2-digit', minute: '2-digit' }) + }); } // Vérifier si l'événement existe déjà diff --git a/routes/timezone-logs.routes.js b/routes/timezone-logs.routes.js new file mode 100644 index 0000000..e94f448 --- /dev/null +++ b/routes/timezone-logs.routes.js @@ -0,0 +1,331 @@ +// Route pour afficher les logs de timezone +const express = require('express'); +const router = express.Router(); +const { authenticateToken } = require('../middleware/auth'); +const timezoneLogger = require('../services/timezone-logger'); + +/** + * GET /api/timezone-logs + * Afficher tous les logs de timezone + */ +router.get('/', authenticateToken, (req, res) => { + const limit = parseInt(req.query.limit) || 100; + const logs = timezoneLogger.getLogs(limit); + + res.json({ + total: logs.length, + limit, + logs + }); +}); + +/** + * GET /api/timezone-logs/category/:category + * Afficher les logs d'une catégorie spécifique + */ +router.get('/category/:category', authenticateToken, (req, res) => { + const limit = parseInt(req.query.limit) || 50; + const logs = timezoneLogger.getLogsByCategory(req.category.toUpperCase(), limit); + + res.json({ + category: req.params.category, + total: logs.length, + limit, + logs + }); +}); + +/** + * POST /api/timezone-logs/clear + * Vider tous les logs + */ +router.post('/clear', authenticateToken, (req, res) => { + timezoneLogger.clearLogs(); + res.json({ message: 'Logs vidés avec succès' }); +}); + +/** + * GET /api/timezone-logs/html + * Afficher les logs en HTML (pour voir directement dans le navigateur) + */ +router.get('/html', authenticateToken, (req, res) => { + const limit = parseInt(req.query.limit) || 200; + const logs = timezoneLogger.getLogs(limit); + + const html = ` + + +
+Suivi en temps réel des conversions de dates
+${log.data}