Implémente un système de migrations automatiques qui s'exécute à chaque
démarrage de l'application Docker/Node.
Problème résolu :
- Les utilisateurs n'ont plus besoin d'exécuter manuellement les migrations
- Les nouveaux champs SQL sont ajoutés automatiquement
- Évite les erreurs "column does not exist"
- Garantit la cohérence entre le code et le schéma DB
Fonctionnement :
1. Au démarrage : initDatabase() → autoMigrate() → startSchedulers()
2. autoMigrate() vérifie chaque colonne via information_schema
3. Si la colonne n'existe pas, elle est ajoutée automatiquement
4. Les triggers et index sont également créés/mis à jour
Migrations incluses :
- Migration 1 : Champs de tracking (archived_at, completed_at)
* notes.archived_at
* global_todos.completed_at
* note_todos.completed_at + created_at
* Triggers automatiques
- Migration 2 : Champ priority pour tâches
* global_todos.priority
* note_todos.priority
* Index de performance
Caractéristiques :
- ✅ Idempotent : Peut s'exécuter plusieurs fois sans problème
- ✅ Transactionnel : Utilise BEGIN/COMMIT/ROLLBACK
- ✅ Non-bloquant : L'application démarre même en cas d'erreur
- ✅ Intelligent : Vérifie avant de créer
- ✅ Sécurisé : Double protection avec rétrocompatibilité API
- ✅ Documenté : Guide complet dans docs/MIGRATIONS.md
Fichiers créés :
- scripts/auto-migrate.js - Script de migrations automatiques
- docs/MIGRATIONS.md - Documentation complète
Fichiers modifiés :
- server.js - Appel de autoMigrate() au démarrage
Logs au démarrage :
✓ Base de données initialisée avec succès
🔄 Vérification des migrations...
→ Ajout du champ priority à global_todos (si nécessaire)
✅ Migrations automatiques terminées avec succès
✓ Migrations automatiques appliquées
Impact :
Les utilisateurs peuvent maintenant simplement démarrer l'application avec
docker-compose up et tous les champs SQL seront automatiquement créés.
Plus besoin de lancer des scripts de migration manuellement !
Rend le code compatible avec les bases de données qui n'ont pas encore
exécuté la migration priority.
Changements :
- Route GET /api/todos utilise un fallback si priority n'existe pas
- Route PATCH /api/todos/:id/priority retourne un message explicite
- Les tâches s'affichent même sans migration
- Message clair pour guider l'utilisateur vers la migration
L'application fonctionne maintenant avec ou sans le champ priority.
Implémente un système de marquage de priorité pour les tâches avec une
icône étoile cliquable.
Fonctionnalités ajoutées :
- Champ priority (BOOLEAN) ajouté aux tables global_todos et note_todos
- Migration automatique avec script add-priority-field.js
- Route API PATCH /api/todos/:id/priority pour basculer la priorité
- Icône étoile (Star) dans l'interface frontend
- Tri automatique : tâches prioritaires en premier
- Index PostgreSQL pour optimiser les performances
Comportement de l'icône :
- Toujours visible et jaune quand la tâche est prioritaire
- Visible au survol (hover) quand la tâche n'est pas prioritaire
- Un clic bascule l'état prioritaire/non prioritaire
- Affichage discret (opacity 50%) pour les tâches complétées
Scripts npm ajoutés :
- npm run db:migrate:priority - Exécuter la migration
Routes API mises à jour :
- GET /api/todos - Inclut le champ priority, tri par priorité
- POST /api/todos - Accepte le champ priority
- PUT /api/todos/:id - Accepte le champ priority
- PATCH /api/todos/:id/priority - Bascule la priorité (nouveau)
UI améliorée :
- Icône étoile jaune pour les tâches prioritaires
- Animation au survol pour les interactions
- Tooltip explicatif au survol
- Onglets "Actives" et "Complétées" mis à jour
Implémente un système complet de purge automatique pour maintenir
la base de données propre et performante.
Fonctionnalités ajoutées :
- Migration pour ajouter les champs de tracking (archived_at, completed_at)
- Triggers PostgreSQL pour mise à jour automatique des dates
- Script de purge manuel avec mode simulation (dry-run)
- Scheduler automatique configurable (défaut: 24h)
- Routes API admin pour contrôle et monitoring
- Documentation complète
Éléments purgés :
- Flux RSS désactivés (tous)
- Tâches complétées > 3 mois (configurable)
- Notes archivées > 6 mois (configurable)
- Rendez-vous passés > 6 mois (configurable)
Scripts npm ajoutés :
- npm run db:migrate - Exécuter la migration
- npm run db:cleanup - Purge manuelle
- npm run db:cleanup:dry-run - Simulation de purge
Routes API ajoutées :
- POST /api/admin/cleanup - Déclencher la purge
- GET /api/admin/cleanup/preview - Prévisualiser la purge
- GET /api/admin/cleanup/status - Statut du scheduler
- GET /api/admin/stats - Statistiques DB
Configuration via variables d'environnement :
- CLEANUP_ENABLED (défaut: true)
- CLEANUP_INTERVAL_HOURS (défaut: 24)
- CLEANUP_COMPLETED_TASKS_DAYS (défaut: 90)
- CLEANUP_ARCHIVED_NOTES_DAYS (défaut: 180)
- CLEANUP_PAST_EVENTS_DAYS (défaut: 180)
Problème :
- Erreur 502 (Bad Gateway) sur /api/openrouter/chat
- Message d'erreur générique "Provider returned error" peu informatif
- Difficile de diagnostiquer la cause réelle du problème
Solution backend (routes/openrouter.routes.js) :
1. Ajout de logs détaillés à chaque étape :
- Début de requête avec modèle et nombre de messages
- Validation de la clé API
- Requête envoyée à OpenRouter
- Réponse reçue avec status HTTP
- Contenu de la réponse ou erreur détaillée
2. Amélioration de la gestion des erreurs :
- Extraction du message d'erreur d'OpenRouter
- Logs structurés avec contexte complet
- Messages d'erreur plus descriptifs
Solution frontend (src/services/OpenRouterService.ts) :
1. Ajout de logs console à chaque étape
2. Messages d'erreur contextuels selon le code HTTP :
- 400 : Requête invalide (message d'erreur API)
- 401 : Clé API invalide
- 402 : Crédits épuisés
- 429 : Trop de requêtes
- 502/503 : Service temporairement indisponible
3. Validation de la réponse :
- Vérification que le contenu existe
- Message d'erreur si réponse vide
Résultat :
- Logs détaillés backend + frontend pour diagnostic
- Messages d'erreur clairs et actionnables pour l'utilisateur
- Meilleure traçabilité des problèmes OpenRouter
- Création de 3 composants UI mobile réutilisables (MobileHeader, MobileFAB, MobileCard)
- Création du layout MobileDashboard avec sidebar navigation
- Implémentation de 6 pages mobiles :
* CalendarPage : gestion des événements avec pagination
* NotesPage : liste des notes avec recherche et filtres
* NoteDetailPage : édition complète de notes en fullscreen
* TodosPage : gestion des tâches actives/complétées
* RssPage : affichage des flux RSS avec actualisation
* SettingsPage : paramètres de l'application
- Configuration du routing automatique mobile/desktop dans App.tsx
- Détection automatique du device avec redirection intelligente
- Tous les services backend réutilisés sans modification
- Build testé et fonctionnel
Problème :
- Après modification d'une note, il fallait rafraîchir la page pour voir updated_at
- Après ajout d'un tag, il fallait rafraîchir la page pour le voir dans la note
Solution backend :
1. routes/notes.routes.js - PUT /api/notes/:id :
- Récupération de la note mise à jour après UPDATE
- Retour de { message, note } au lieu de juste { message }
- La note retournée contient la vraie updated_at générée par PostgreSQL
Solution frontend :
1. NotesService.ts - updateNote() :
- Changement du type de retour de Promise<boolean> vers Promise<Note | null>
- Récupération et retour de la note mise à jour depuis la réponse du serveur
- Merge de la note locale (todos, images) avec les données du serveur (updated_at)
2. Index.tsx - handleNoteChange et handleContentChange :
- Utilisation de la note retournée par updateNote() au lieu de l'état local
- Mise à jour de openNote et notes avec les données du serveur
3. Index.tsx - Synchronisation des tags :
- loadNoteTags : Mise à jour de openNote.tags après chargement
- confirmAddTag : Mise à jour de openNote.tags après ajout
- handleDeleteTag : Mise à jour de openNote.tags après suppression
- Conversion du format { id, tag } vers { id, name } pour cohérence
Résultat :
- Les dates de modification s'affichent immédiatement après sauvegarde
- Les tags ajoutés apparaissent immédiatement dans l'interface
- Plus besoin de rafraîchir la page manuellement
Problème identifié :
- Quand une nouvelle note était créée, elle écrasait la première note existante
- La fonction runQuery() dans database-postgres.js retournait rowCount (1) au lieu de l'ID réel
- Les requêtes INSERT n'utilisaient pas RETURNING pour récupérer l'ID généré
Solution implémentée :
1. Ajout de RETURNING * à toutes les requêtes INSERT dans :
- routes/notes.routes.js (notes, todos, images, fichiers, tags)
- routes/todos.routes.js (todos globaux)
- routes/users.routes.js (utilisateurs)
- routes/rss.routes.js (flux RSS, résumés)
- routes/settings.routes.js (paramètres)
- routes/calendar.routes.js (événements, tokens OAuth)
2. Modification de runQuery() pour :
- Retourner la ligne complète si RETURNING est présent
- Inclure tous les champs de la ligne insérée (spread operator)
- Ajouter des logs détaillés pour le debugging
3. Ajout de logs détaillés pour tracer :
- Chaque création d'entité avec ses paramètres
- L'ID retourné après insertion
- Les erreurs potentielles
Impact :
- Les nouvelles notes ont maintenant leur vrai ID auto-incrémenté
- Plus d'écrasement des notes existantes
- Meilleure traçabilité avec les logs détaillés
Problème identifié:
- Google Calendar envoie '2025-11-17T10:20:00+01:00' (10h20 Paris = 09h20 UTC)
- PostgreSQL TIMESTAMP enlève le fuseau horaire et stocke '2025-11-17 10:20:00' (heure locale)
- Le parser 1114 traite cette valeur comme UTC → '2025-11-17T10:20:00.000Z'
- Frontend affiche 11h20 en heure de Paris (10:20 UTC + 1h) au lieu de 10h20
Solution:
- Convertir tous les timestamps en UTC (ISO format) avant insertion dans PostgreSQL
- Appliquer la conversion dans toutes les routes: POST /sync, POST /events, PUT /events/:id
- Le parser 1114 traite maintenant correctement les valeurs UTC stockées
Changements:
- routes/calendar.routes.js: Conversion UTC avant insertion (3 routes corrigées)
- routes/calendar.routes.js: Correction syntaxe PostgreSQL (INSERT ... ON CONFLICT)
- scripts/cleanup-calendar-events.js: Script pour nettoyer les événements existants
Pour appliquer la correction sur les données existantes:
1. Exécuter: docker exec notes-todo-app node scripts/cleanup-calendar-events.js
2. Redémarrer: docker-compose restart notes-app
3. Resynchroniser Google Calendar depuis l'interface web
NOUVEAU SYSTÈME DE LOGGING:
Au lieu d'utiliser les logs Docker (inaccessibles depuis Portainer),
création d'un système de logs accessible directement dans le navigateur.
FICHIERS AJOUTÉS:
1. services/timezone-logger.js:
- Service singleton qui stocke les logs en mémoire
- Limite à 500 logs max (pour éviter les fuites mémoire)
- Méthodes: log(), getLogs(), clearLogs(), getLogsByCategory()
2. routes/timezone-logs.routes.js:
- GET /api/timezone-logs - Logs en JSON
- GET /api/timezone-logs/html - Interface web avec design moderne
- GET /api/timezone-logs/category/:cat - Filtrer par catégorie
- POST /api/timezone-logs/clear - Vider les logs
3. VOIR-LOGS-TIMEZONE.md:
- Documentation complète du système
- Procédure pas à pas
- Explications des logs attendus
FICHIERS MODIFIÉS:
1. config/database.js:
- Utilise timezoneLogger au lieu de logger.debug
- Catégorie: PARSER
2. routes/calendar.routes.js:
- Logs avec timezoneLogger lors de la synchronisation
- Catégorie: SYNC (synchronisation Google Calendar)
- Catégorie: GET (envoi au frontend)
3. server.js:
- Ajout de la route /api/timezone-logs
INTERFACE WEB DE LOGS:
Accessible sur: http://localhost:2222/api/timezone-logs/html
Fonctionnalités:
- ✅ Design moderne avec dégradés et animations
- ✅ Statistiques en temps réel (total, par catégorie)
- ✅ Filtrage par catégorie (SYNC, PARSER, GET)
- ✅ Auto-refresh toutes les 3 secondes
- ✅ Code couleur par catégorie
- ✅ Affichage des données JSON formatées
- ✅ Bouton pour vider les logs
- ✅ Responsive, fonctionne sur mobile
UTILISATION:
1. Rebuild: docker-compose build --no-cache notes-app
2. Ouvrir: http://localhost:2222/api/timezone-logs/html
3. Synchroniser Google Calendar
4. Observer les logs en temps réel dans le navigateur
Plus besoin d'accès aux logs Docker/Portainer !
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
PROBLÈME IDENTIFIÉ:
Les événements synchronisés depuis Google Calendar s'affichaient avec +1h
de décalage (ex: 10:20 dans Google → 11:20 dans NoteFlow).
CAUSE RACINE:
PostgreSQL renvoyait les TIMESTAMPTZ dans le timezone de sa session
(probablement Europe/Paris) au format "2024-11-17 10:20:00" SANS l'info
de timezone. JavaScript interprétait cette string comme UTC, créant un
décalage lors de l'affichage avec toLocaleTimeString Europe/Paris.
FLUX DU BUG:
1. Google Calendar: "2024-11-17T10:20:00+01:00" (10:20 Paris)
2. PostgreSQL stocke: 09:20 UTC (conversion automatique)
3. PostgreSQL renvoie avec timezone=Europe/Paris: "2024-11-17 10:20:00"
4. JavaScript interprète: 10:20 UTC (car pas de timezone)
5. Affichage Paris: 11:20 (10:20 UTC + 1h) ❌
CORRECTIONS APPORTÉES:
1. config/database.js:
- Ajout de l'option `timezone=UTC` au pool PostgreSQL
- Garantit que toutes les connexions utilisent UTC
- Parser TIMESTAMPTZ amélioré pour normaliser en ISO UTC:
* Détecte les formats avec/sans timezone
* Ajoute 'Z' aux dates sans timezone (car timezone=UTC)
* Renvoie toujours une ISO string UTC propre
2. Scripts de test ajoutés:
- scripts/test-parser.js: Validation du parser (5 tests, tous ✅)
- scripts/debug-sync.js: Debug du flux de synchronisation
FLUX CORRIGÉ:
1. Google Calendar: "2024-11-17T10:20:00+01:00" (10:20 Paris)
2. PostgreSQL stocke: 09:20 UTC
3. PostgreSQL renvoie (timezone=UTC): "2024-11-17 09:20:00"
4. Parser normalise: "2024-11-17T09:20:00.000Z"
5. JavaScript interprète: 09:20 UTC
6. Affichage Paris: 10:20 ✅
RÉSULTAT:
Les heures affichées dans NoteFlow correspondent maintenant exactement
à celles de Google Calendar.
IMPORTANT:
Pour que le fix s'applique aux événements existants, il faut:
1. Redémarrer l'application (pour appliquer timezone=UTC)
2. Resynchroniser avec Google Calendar
PROBLÈME IDENTIFIÉ:
Le backend envoyait des dates ISO UTC à Google Calendar API avec
timeZone: 'Europe/Paris', ce qui causait une double interprétation.
Google interprétait l'heure UTC comme étant l'heure locale de Paris,
créant ainsi un décalage d'1 heure.
CORRECTIONS APPORTÉES:
1. Frontend (src/pages/Index.tsx):
- Réécriture de toParisISO() pour convertir correctement les dates
datetime-local en ISO UTC avec gestion automatique de l'heure d'été/hiver
- Calcul dynamique de l'offset Europe/Paris (+01:00 hiver, +02:00 été)
- Tests de validation confirmant le bon fonctionnement (round-trip OK)
2. Backend (routes/calendar.routes.js):
- Retrait de timeZone: 'Europe/Paris' lors de la création d'événements
- Retrait de timeZone: 'Europe/Paris' lors de la mise à jour d'événements
- Google Calendar API interprète maintenant correctement les ISO UTC
3. Scripts de test ajoutés:
- scripts/test-timezone-functions.js: Validation des conversions
- scripts/diagnose-timezone.js: Diagnostic du flux complet
RÉSULTAT:
Les heures affichées dans NoteFlow sont maintenant identiques à celles
de Google Calendar, sans aucun décalage horaire.
Tests effectués:
✅ Conversion datetime-local → ISO UTC (hiver): 14:30 → 13:30 UTC
✅ Conversion datetime-local → ISO UTC (été): 14:30 → 12:30 UTC
✅ Round-trip: conservation de la valeur d'origine
✅ Affichage correct en Europe/Paris depuis ISO UTC
✅ Compilation réussie
Problèmes corrigés:
- Ajout de fonctions utilitaires toLocalDateTimeString() et toParisISO()
pour gérer correctement les conversions de timezone Europe/Paris
- Correction des valeurs par défaut des champs datetime-local lors de la
création d'événements (utilisaient toISOString().slice() incorrectement)
- Correction des valeurs par défaut lors de l'édition d'événements
- Correction de la soumission des formulaires (création et édition)
pour envoyer les dates ISO correctes avec timezone Europe/Paris
Les dates sont désormais:
1. Affichées correctement en heure Europe/Paris dans les formulaires
2. Converties correctement lors de l'envoi au backend
3. Stockées et récupérées sans décalage horaire
Fichiers modifiés:
- src/pages/Index.tsx: Ajout des fonctions utilitaires et correction
de toutes les conversions de dates pour les événements du calendrier
Problème: Décalage d'1 heure persistant malgré types.setTypeParser
Cause probable: Container pas rebuild avec le fix database.js
Ajouts:
1. scripts/check-calendar-timezone.js
- Diagnostic complet timezone PostgreSQL/Node.js
- Vérifie si types.setTypeParser(1184) fonctionne
- Affiche type de données retourné (string vs Date object)
- Compare valeurs SQL brutes avec valeurs JS
2. CALENDAR_TIMEZONE_FIX.md
- Documentation complète du problème
- Procédure de fix étape par étape
- Explication technique de la cause
- Historique des tentatives
Utilisation:
```bash
# 1. Diagnostic
node scripts/check-calendar-timezone.js
# 2. Si Date object au lieu de string:
docker-compose build --no-cache notes-app
docker-compose up -d
# 3. Resynchroniser calendrier depuis UI
```
Le fix types.setTypeParser(1184) est déjà en place dans database.js
mais nécessite un rebuild du container pour être actif.