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 = ` + + + + Logs Timezone - NoteFlow + + + + + +
+

🔍 Logs de débogage Timezone

+

Suivi en temps réel des conversions de dates

+
+
+
${logs.length}
+
Total logs
+
+
+
${logs.filter(l => l.category === 'SYNC').length}
+
Synchronisation
+
+
+
${logs.filter(l => l.category === 'PARSER').length}
+
Parser
+
+
+
${logs.filter(l => l.category === 'GET').length}
+
Frontend
+
+
+
+ +
+ + + +
+ + + + +
+
+ +
+ ${logs.length === 0 ? '
Aucun log pour le moment. Synchronisez avec Google Calendar pour voir les logs.
' : ''} + ${logs.map(log => ` +
+
+ ${new Date(log.timestamp).toLocaleString('fr-FR')} + ${log.category} +
+
${log.message}
+ ${log.data ? `
${log.data}
` : ''} +
+ `).join('')} +
+ + + + + `; + + res.send(html); +}); + +module.exports = router; diff --git a/server.js b/server.js index 0b3128f..31e7a36 100644 --- a/server.js +++ b/server.js @@ -93,6 +93,7 @@ app.use('/api/settings', require('./routes/settings.routes')); app.use('/api/rss', require('./routes/rss.routes')); app.use('/api/calendar', require('./routes/calendar.routes')); app.use('/api/openrouter', require('./routes/openrouter.routes')); +app.use('/api/timezone-logs', require('./routes/timezone-logs.routes')); // Route de recherche app.get('/api/search', require('./routes/notes.routes').searchNotes); diff --git a/services/timezone-logger.js b/services/timezone-logger.js new file mode 100644 index 0000000..57e49c9 --- /dev/null +++ b/services/timezone-logger.js @@ -0,0 +1,48 @@ +// Service de logging en mémoire pour débuguer le timezone +// Les logs sont stockés en mémoire et accessibles via /api/calendar/timezone-logs + +class TimezoneLogger { + constructor() { + this.logs = []; + this.maxLogs = 500; // Garder max 500 logs + } + + log(category, message, data = null) { + const entry = { + timestamp: new Date().toISOString(), + category, + message, + data: data ? JSON.stringify(data, null, 2) : null + }; + + this.logs.push(entry); + + // Limiter le nombre de logs + if (this.logs.length > this.maxLogs) { + this.logs.shift(); + } + + // Afficher aussi dans les logs console + console.log(`[${category}] ${message}`, data || ''); + } + + getLogs(limit = 100) { + return this.logs.slice(-limit).reverse(); + } + + clearLogs() { + this.logs = []; + } + + getLogsByCategory(category, limit = 50) { + return this.logs + .filter(log => log.category === category) + .slice(-limit) + .reverse(); + } +} + +// Singleton +const timezoneLogger = new TimezoneLogger(); + +module.exports = timezoneLogger;