Add: Système de logging complet pour débuguer le décalage horaire

Ajout d'un système de logging exhaustif pour tracer le flux complet
des dates depuis Google Calendar jusqu'à l'affichage dans NoteFlow.

FICHIERS MODIFIÉS:

1. config/database.js:
   - Logs détaillés dans le parser TIMESTAMPTZ
   - Affiche l'input, la transformation et l'output
   - Différencie les dates avec/sans timezone

2. routes/calendar.routes.js:
   - Logs lors de la synchronisation Google Calendar
   - Affiche ce que Google renvoie (format brut)
   - Affiche la conversion en UTC et l'affichage Paris
   - Logs lors de la récupération des événements (GET /events)
   - Nouvel endpoint GET /api/calendar/debug pour diagnostic complet

3. DEBUG-TIMEZONE-LOGS.md:
   - Documentation complète du système de logging
   - Procédure étape par étape pour débuguer
   - Commandes utiles pour analyser les logs
   - Guide d'interprétation des résultats

ENDPOINT DE DIAGNOSTIC AJOUTÉ:

GET /api/calendar/debug renvoie:
- Timezone du serveur Node.js
- Timezone de PostgreSQL
- Un événement exemple avec toutes ses représentations
- Test de parsing complet (input → output → display)

UTILISATION:

1. Rebuild avec: docker-compose build --no-cache notes-app
2. Supprimer les événements: DELETE FROM calendar_events
3. Lancer les logs: docker-compose logs -f notes-app
4. Synchroniser Google Calendar
5. Analyser les logs pour identifier où se produit le décalage

Les logs permettront de voir EXACTEMENT:
- Ce que Google Calendar renvoie
- Comment c'est stocké dans PostgreSQL
- Comment le parser le transforme
- Comment le frontend l'affiche
This commit is contained in:
Claude committed 2025-11-16 15:54:38 +00:00
1 parent 24e4dcfbd5
commit 972dcacc50
3 files changed
+329 -6

No files matched your search

+103
View File
@@ -352,6 +352,17 @@ router.get('/events', authenticateToken, async (req, res) => {
LIMIT $3
`, [req.user.id, now, limit]);
// 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})`);
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' })}`);
}
});
res.json(events || []);
} catch (error) {
logger.error('Erreur lors de la récupération des événements:', error);
@@ -359,6 +370,87 @@ router.get('/events', authenticateToken, async (req, res) => {
}
});
/**
* GET /api/calendar/debug
* Endpoint de diagnostic détaillé pour le timezone
*/
router.get('/debug', authenticateToken, async (req, res) => {
try {
const diagnostics = {
timestamp: new Date().toISOString(),
serverTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
postgresTimezone: null,
sampleEvent: null,
parsing: {
input: null,
output: null,
display: null
}
};
// Récupérer le timezone de PostgreSQL
const tzResult = await getOne('SHOW timezone');
diagnostics.postgresTimezone = tzResult?.timezone || tzResult?.TimeZone;
// Récupérer un événement pour test
const event = await getOne(`
SELECT
title,
start_time,
pg_typeof(start_time) as type_col,
start_time::text as start_text,
to_char(start_time AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS"Z"') as start_utc_formatted,
to_char(start_time AT TIME ZONE 'Europe/Paris', 'YYYY-MM-DD HH24:MI:SS') as start_paris_formatted
FROM calendar_events
WHERE user_id = $1
ORDER BY start_time DESC
LIMIT 1
`, [req.user.id]);
if (event) {
diagnostics.sampleEvent = {
title: event.title,
type_col: event.type_col,
start_time_raw: event.start_time,
start_time_type: typeof event.start_time,
start_text: event.start_text,
start_utc_formatted: event.start_utc_formatted,
start_paris_formatted: event.start_paris_formatted
};
// Test de parsing
if (event.start_time) {
const date = new Date(event.start_time);
diagnostics.parsing = {
input: event.start_time,
inputType: typeof event.start_time,
dateObject: date.toISOString(),
displayUTC: date.toUTCString(),
displayParis: date.toLocaleString('fr-FR', {
timeZone: 'Europe/Paris',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
}),
displayParisTime: date.toLocaleTimeString('fr-FR', {
timeZone: 'Europe/Paris',
hour: '2-digit',
minute: '2-digit'
})
};
}
}
res.json(diagnostics);
} catch (error) {
logger.error('Erreur lors du diagnostic:', error);
res.status(500).json({ error: error.message });
}
});
/**
* POST /api/calendar/sync
* Synchroniser avec Google Calendar (OAuth2, Service Account ou API Key)
@@ -410,6 +502,17 @@ router.post('/sync', authenticateToken, async (req, res) => {
const endTime = event.end.dateTime || event.end.date;
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}`);
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' })}`);
}
// Vérifier si l'événement existe déjà
const existing = await getOne(
'SELECT id FROM calendar_events WHERE google_event_id = $1 AND user_id = $2',