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
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É:
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ème: Console spam 400 errors quand clé API OpenRouter non configurée
- Frontend: Chat appelle getModels() sans vérifier si API key existe
- Backend: Retourne 400 au lieu de 200 avec tableau vide
Solutions:
- Frontend (Index.tsx:556): Ajout check settings.openrouter_api_key avant loadOpenRouterModels()
- Backend (openrouter.routes.js:28,93): Return 200 [] au lieu de 400 error quand pas de clé API
Résultat: Plus d'erreurs console, UI affiche simplement pas de modèles
Correction du décalage de timezone de 1h dans Google Calendar
PROBLÈME:
- Les événements s'affichaient avec 1h de décalage
- Événement à 10h → affiché à 11h (ou 09h selon cas)
CAUSE:
- TIMESTAMP (sans timezone) dans PostgreSQL
- Google envoie dates avec timezone info
- PostgreSQL stockait sans timezone
- JavaScript réinterprétait avec timezone local
- = Double application du décalage
SOLUTION:
1. calendar_events: TIMESTAMP → TIMESTAMPTZ
- start_time: TIMESTAMPTZ (conserve timezone)
- end_time: TIMESTAMPTZ (conserve timezone)
2. Migration automatique:
- scripts/migrate-calendar-timezone.js
- Exécuté automatiquement au démarrage Docker
- Convertit les données existantes
- USING start_time AT TIME ZONE 'UTC'
3. Correction booléens all_day:
- Ligne 433: isAllDay ? 1 : 0 → isAllDay
- Ligne 451: isAllDay ? 1 : 0 → isAllDay
FICHIERS MODIFIÉS:
- config/database.js: TIMESTAMPTZ pour calendar_events
- routes/calendar.routes.js: Booléens corrigés
- docker-entrypoint.sh: Migration auto timezone
- migrations/002_calendar_timestamptz.sql: SQL manuel
- scripts/migrate-calendar-timezone.js: Script migration
DÉPLOIEMENT:
Au prochain rebuild Docker:
1. La table sera recréée avec TIMESTAMPTZ
2. Les événements existants seront migrés
3. Les nouvelles synchros utiliseront le bon format
4. Plus de décalage horaire!
Resynchronisez Google Calendar après déploiement
pour appliquer le nouveau format.
🐘 NoteFlow utilise maintenant EXCLUSIVEMENT PostgreSQL
CHANGEMENTS MAJEURS:
==================
1. Base de données (config/database.js)
✅ Remplacé par PostgreSQL (backup SQLite créé)
✅ Toutes les routes et services mis à jour
✅ database-loader.js supprimé (plus nécessaire)
2. Migration automatique (docker-entrypoint.sh)
✅ Attend que PostgreSQL soit prêt
✅ Détecte si PostgreSQL est vide
✅ Migre automatiquement SQLite si présent
✅ Lance le serveur après migration
3. Package.json
✅ Description mise à jour (PostgreSQL)
✅ Keywords: postgresql au lieu de sqlite
✅ sqlite3 gardé uniquement pour migration
4. Documentation
✅ POSTGRESQL-ONLY.md: Guide complet PostgreSQL
✅ Commandes, dépannage, sécurité
✅ Instructions de migration
MIGRATION UTILISATEUR:
=====================
Au prochain démarrage Docker:
1. Le container détecte PostgreSQL vide
2. Cherche ./data/notes.db (si existe)
3. Migre AUTOMATIQUEMENT toutes les données
4. Démarre avec PostgreSQL
COMMANDES:
=========
docker-compose build
docker-compose up -d
→ Tout se fait automatiquement!
Les données SQLite sont conservées comme backup dans ./data/
mais l'application n'utilise plus que PostgreSQL.
FICHIERS MODIFIÉS:
=================
- config/database.js (PostgreSQL uniquement)
- docker-entrypoint.sh (migration automatique)
- server.js + 11 routes + 2 services
- package.json (description + keywords)
- Documentation complète: POSTGRESQL-ONLY.md
Cette migration garantit:
- Performance optimale
- Meilleure gestion de la concurrence
- Support production à grande échelle
- Pas de perte de données (migration auto)
CORRECTIF URGENT: L'application chargeait toujours SQLite même avec
PostgreSQL configuré, causant une perte apparente de toutes les données.
Ajout de database-loader.js qui détecte automatiquement:
- DB_TYPE=postgres → PostgreSQL
- DATABASE_URL=postgresql:// → PostgreSQL
- Sinon → SQLite
Tous les routes et services mis à jour pour utiliser database-loader
au lieu de charger directement database.js.
Les données ne sont PAS perdues, elles sont dans PostgreSQL.
L'application se reconnectera correctement après redémarrage.
Fichiers modifiés:
- config/database-loader.js (nouveau)
- server.js
- routes/*.js (11 fichiers)
- services/*.js (2 fichiers)
Problèmes résolus:
- Articles non mis à jour (toujours 12 nov au lieu de 14 nov)
- Détection de doublons défaillante
- Cache trop agressif bloquant les nouveaux articles
- Logique complexe et difficile à déboguer
Architecture V2 (simple et robuste):
**services/rss-scheduler-v2.js**
- Détection doublons SIMPLE: uniquement par lien
- Pas de double vérification titre+date (trop complexe)
- Nettoyage automatique (garde les 100 derniers)
- Logs clairs pour chaque étape
- Fetch séquentiel pour éviter les races
- Timeout robuste (15s par flux)
**routes/rss.routes-v2.js**
- Suppression du cache de 30s
- Requêtes SQL directes simples
- Pas de logique de filtre temporel compliquée
- Endpoint /refresh déclenche un fetch immédiat
- Logs cohérents
**Scripts utilitaires**
- reset-rss.js: Nettoie complètement la DB RSS
- migrate-to-rss-v2.js: Migre de V1 à V2
- Plus tous les scripts de debug existants
Backups:
- rss-scheduler.js.backup (ancien système)
- rss.routes.js.backup (ancien système)
Migration:
1. node scripts/reset-rss.js (optionnel, nettoie la DB)
2. Redémarrer le serveur
3. Ajouter les flux RSS via l'interface
4. Les articles seront récupérés toutes les 2 minutes
Résultat attendu:
- Les nouveaux articles apparaissent dans les 2 minutes
- Pas de doublons même avec liens changeants
- Affichage du plus récent au plus ancien
- Système simple, maintenable et debuggable
- Backend : Remet ORDER BY DESC (récent en premier de la DB)
- Frontend : Ajoute .reverse() pour inverser et afficher ancien en premier
- Résultat : Articles affichés du plus ancien au plus récent
Cette approche garantit que le tri fonctionne côté frontend
indépendamment du cache backend.
- Augmente limite par défaut de 5 à 50 articles dans /api/rss/articles
- Ajoute paramètre optionnel 'hours' pour filtrer par période (ex: ?hours=24)
- Augmente limite de récupération par flux de 20 à 100 articles
- Améliore le logging pour afficher les paramètres appliqués
- Désactive le cache lors de l'utilisation du filtre temporel
Résout le problème où toujours les mêmes articles étaient affichés
malgré plus de 100 nouveaux articles disponibles dans les flux.
Problème:
L'utilisateur signale que les articles RSS ne se mettent pas à jour -
"toujours les mêmes articles depuis ce matin, pas de changement d'affichage".
Causes identifiées:
1. Le scheduler RSS s'exécutait toutes les 5 minutes (peut-être trop long)
2. Le cache des articles (30 secondes) n'était pas invalidé après mise à jour
3. Pas assez de logs pour diagnostiquer les problèmes
Solutions appliquées:
1. **Fréquence augmentée du scheduler** (services/rss-scheduler.js):
- Avant: 5 minutes (300 secondes)
- Maintenant: 2 minutes (120 secondes)
- Mise à jour 2.5x plus fréquente des flux RSS
- Log au démarrage: "mise à jour toutes les 2 minutes"
2. **Invalidation automatique du cache** (services/rss-scheduler.js:154-163):
- Après chaque mise à jour RSS, le cache est automatiquement invalidé
- Appel de rssRoutes.invalidateCache() après fetchAllFeeds()
- Log: "Cache des articles RSS invalidé"
- Garantit que les nouveaux articles sont immédiatement disponibles
3. **Export de la fonction invalidateCache** (routes/rss.routes.js:161-165):
- Nouvelle fonction exportée pour invalidation externe
- Utilisée par le scheduler après chaque mise à jour
- Garantit synchronisation entre scheduler et API
Changements:
- services/rss-scheduler.js:
* Ligne 176: Intervalle réduit de 5 min à 2 min
* Lignes 154-163: Invalidation du cache après mise à jour
* Logs améliorés avec durée et stats
- routes/rss.routes.js:
* Lignes 158-165: Fonction invalidateCache() exportée
* Ligne 399: Export de invalidateCache en tant que propriété
* Cache invalidé automatiquement après mises à jour scheduler
Comportement attendu:
- Nouvea
ux articles récupérés toutes les 2 minutes
- Cache invalidé automatiquement après chaque mise à jour
- Articles frais disponibles immédiatement après synchronisation
- Logs détaillés pour diagnostic (nombre d'articles, durée, erreurs)
L'utilisateur devrait voir de nouveaux articles maximum 2 minutes après
leur publication dans les flux RSS.
L'utilisateur a confirmé que "ça marchait avant" et "tout est bien configuré".
Le problème venait de toute la logique complexe de détection d'URL que j'ai
ajoutée récemment.
Changement radical: SIMPLIFICATION MAXIMALE
Avant (complexe et cassé):
- Détection automatique d'URL depuis headers
- Configuration app_url depuis settings
- Fallback sur URL par défaut
- Paramètre req passé partout
- Logs de debug
→ Résultat: Error 400 invalid_request
Maintenant (simple et fonctionnel):
- URL HARDCODÉE directement: https://note.ffnancy.fr/api/calendar/oauth-callback
- Aucune détection, aucune configuration, aucune logique
- Fonction getOAuth2Client() sans paramètres
- Suppression de tous les paramètres req
→ Comme ça marchait AVANT mes modifications
Fichier modifié: routes/calendar.routes.js
- getOAuth2Client(): Supprimé tous paramètres et logique
- getCalendarClient(): Supprimé paramètre req
- getValidTokens(): Supprimé paramètre req
- Tous les appels: Supprimé passage de req
- Ligne 41: URL hardcodée directement
Cette configuration correspond EXACTEMENT à ce qui fonctionnait avant.
Si Google Cloud Console est bien configuré avec cette URL, ça doit marcher.
Problème:
L'authentification OAuth Google Calendar échouait avec "Error 400: invalid_request"
et "Accès bloqué" depuis que l'URL est devenue configurable. Avant, l'URL
était hardcodée à https://note.ffnancy.fr et fonctionnait correctement.
Cause:
Quand app_url n'est pas configuré dans les paramètres ET que la détection
automatique échoue, l'application ne pouvait pas générer de redirect_uri
valide, causant le rejet par Google OAuth.
Solution:
Ajout d'une URL de fallback hardcodée en dernière priorité pour garantir
que l'authentification OAuth fonctionne toujours :
Ordre de priorité (cascade):
1. customRedirectUri (si fournie explicitement)
2. app_url configurée dans paramètres
3. URL détectée depuis headers HTTP
4. **URL hardcodée par défaut: https://note.ffnancy.fr** (NOUVEAU)
Changements:
- routes/calendar.routes.js:
* Ajout constante defaultProductionUrl = 'https://note.ffnancy.fr'
* Modification cascade redirectUri pour inclure fallback hardcodé
* Ajout log: "OAuth redirect URI utilisée" pour debug
* Suppression erreur bloquante si aucune URL trouvée
- src/pages/Index.tsx:
* Revert des messages d'erreur complexes (pas nécessaires)
* Retour au message simple et clair
- GUIDE_ERREUR_OAUTH_GOOGLE.md: (NOUVEAU)
* Guide complet pour résoudre "Error 400: invalid_request"
* Instructions pour ajouter utilisateurs testeurs dans Google Console
* Instructions pour publier l'application OAuth
* FAQ et troubleshooting détaillé
Comportement:
- OAuth fonctionne immédiatement sans configuration manuelle app_url
- Compatible avec proxy inverse et détection automatique
- Garde la flexibilité de configuration pour autres environnements
- Log l'URL utilisée pour faciliter le debug
Cette correction restaure le fonctionnement d'avant tout en gardant
la flexibilité de configuration pour les cas d'usage avancés.
Problème:
L'application échouait avec l'erreur "URL du site non configurée"
lors de l'utilisation de Google Calendar car l'URL du site (app_url)
n'était pas définie dans les paramètres, bloquant toutes les opérations
OAuth (récupération événements, synchronisation, ajout d'événements).
Erreur:
```
Error: URL du site non configurée. Veuillez configurer l'URL dans les paramètres.
at getOAuth2Client (/app/routes/calendar.routes.js:47:11)
at async getValidTokens (/app/routes/calendar.routes.js:312:24)
at async getCalendarClient (/app/routes/calendar.routes.js:113:26)
```
Solution:
Ajout d'une détection automatique de l'URL depuis les headers HTTP de
la requête Express, avec cascade de fallback :
1. URL personnalisée (customRedirectUri) - si fournie explicitement
2. URL configurée (app_url settings) - si définie dans paramètres
3. **URL détectée automatiquement** - depuis headers HTTP (NOUVEAU)
- x-forwarded-proto ou req.protocol
- x-forwarded-host ou req.headers.host
Changements:
- routes/calendar.routes.js:
* getOAuth2Client: Ajout paramètre `req` et logique détection URL
* getCalendarClient: Ajout paramètre `req` et transmission
* getValidTokens: Ajout paramètre `req` et transmission
* Mise à jour tous les appels (5 emplacements):
- GET /auth-url
- GET /oauth-callback
- POST /sync
- POST /events
- PUT /events/:id
* Amélioration message d'erreur avec chemin exact paramètres
Comportement:
- Compatible avec proxy inverse (Nginx) via x-forwarded-* headers
- Fonctionne en direct ou derrière proxy
- Ne nécessite plus de configuration manuelle app_url (optionnel)
- Fallback intelligent sur configuration existante si présente
Cette correction permet à l'application de fonctionner immédiatement
sans configuration supplémentaire tout en respectant les configurations
manuelles existantes.
- Événements toute la journée affichés correctement (au lieu de 1:00)
- Édition des événements avec synchronisation Google Calendar
- Configuration Google Calendar simplifiée (OAuth 2.0 uniquement)
- Ajout du champ app_url pour éviter URLs codées en dur
- Assistant IA simplifié (sélecteur unique au lieu de recherche + liste)
- Remplacement URLs hardcodées par configuration app_url
Correction d'une erreur critique qui empêchait le chargement des notes:
- La requête tentait de joindre une table "tags" inexistante
- La structure réelle utilise directement la colonne "tag" dans note_tags
- Correction: SELECT id, tag as name FROM note_tags au lieu de la jointure
Cette erreur causait:
- SQLITE_ERROR: no such table: tags
- Disparition des notes après création
- Impossibilité de charger la liste des notes
Fix immédiat pour restaurer le fonctionnement normal de l'application.
Ajout de deux nouvelles fonctionnalités pour les notes:
1. Affichage des tags dans les cartes repliées:
- Les tags sont maintenant visibles dans les cartes de notes sans avoir à les ouvrir
- Badge avec icône TagIcon et nom du tag
- Chargement automatique des tags depuis la base de données
2. Icône de priorité cliquable:
- Bouton "!" en haut à droite de chaque carte de note
- Rouge (!) quand la note est prioritaire (toujours visible)
- Gris clair (!) quand non prioritaire (visible au hover)
- Toggle au clic pour marquer/démarquer comme prioritaire
- Les notes prioritaires sont triées en premier
Backend:
- Route PATCH /api/notes/:id/priority pour toggle la priorité
- Modification GET /api/notes pour charger tags et priority
- Tri par priority DESC, updated_at DESC
- Migration automatique pour ajouter colonne priority
- Index pour performance des requêtes de tri
Frontend:
- Interface Note étendue avec tags et priority
- Méthode togglePriority dans NotesService
- Affichage conditionnel de l'icône (toujours visible si prioritaire)
- Empêche le clic sur l'icône d'ouvrir la note (stopPropagation)
Migration:
- Ajout automatique de la colonne priority (INTEGER DEFAULT 0)
- Création d'index idx_notes_priority pour performance
- Fichier de migration 002_add_priority.sql créé pour référence
Corrections et améliorations du système RSS et de l'assistant IA:
RSS:
- Correction de la route backend /api/rss/articles pour respecter le paramètre limit
- Changement de LIMIT 5 fixe à LIMIT ? avec paramètre dynamique
- Support de 14 articles avec pagination de 7 par page
- Amélioration du cache pour supporter différentes limites
Assistant IA:
- Suppression de la vérification settings.openrouter_api_key côté frontend
- Les modèles se chargent maintenant automatiquement via le backend
- Retrait de la section "Modèle IA" dans l'administration OpenRouter
- Amélioration de la gestion des erreurs lors du chargement des modèles
Les utilisateurs peuvent maintenant voir tous les modèles OpenRouter disponibles dans le sélecteur du chatbox, sans configuration manuelle dans l'admin.
Corrections pour permettre le chargement automatique des modèles IA:
- Retrait de la restriction admin sur la route GET /api/openrouter/models
- Retrait de la condition is_admin dans loadOpenRouterModels()
- Ajout d'un useEffect pour charger les modèles à l'ouverture du chatbox
- Ajout d'un indicateur "Chargement des modèles..." dans le sélecteur
- Désactivation du sélecteur pendant le chargement
Les modèles se chargent maintenant automatiquement quand l'utilisateur ouvre le chatbox, et tous les utilisateurs authentifiés peuvent y accéder (pas seulement les admins).
Ajout d'un chatbot interactif alimenté par OpenRouter API avec les fonctionnalités suivantes:
- Bouton flottant en bas à droite pour ouvrir le chat
- Sélecteur de modèles IA avec tous les modèles disponibles via l'API OpenRouter
- Interface de chat avec historique des messages (utilisateur/assistant)
- Indicateur de chargement avec animation
- Boutons pour effacer l'historique et fermer le chat
- Auto-scroll vers le dernier message
- Backend: route POST /api/openrouter/chat pour la communication avec l'API
- Service frontend: méthode sendMessage() dans OpenRouterService
Retrait du composant MadeWithDyad comme demandé.
Ajout d'un modal complet pour créer des événements directement dans Google Calendar avec toutes les fonctionnalités :
Backend (routes/calendar.routes.js) :
- Changement du scope de 'calendar.readonly' à 'calendar' pour permettre l'écriture
- Nouvelle route POST /api/calendar/events pour créer des événements
- Support complet des fonctionnalités Google Calendar :
* Titre, description, dates de début/fin
* Lieu
* Participants (attendees) avec notifications automatiques
* Rappels personnalisables (popup)
* Récurrence (quotidienne, hebdomadaire, mensuelle, annuelle)
* Visibilité (public, privé, par défaut)
* Couleurs d'événement (11 couleurs disponibles)
* Fuseau horaire Europe/Paris
- Synchronisation automatique après création pour mise à jour locale
Frontend :
- Service CalendarService.ts : nouvelle méthode createEvent()
- Bouton "Ajouter" dans la box Rendez-vous
- Modal complet avec formulaire de création d'événement
- Tous les champs Google Calendar disponibles :
* Titre et description
* Date/heure de début et fin (datetime-local)
* Lieu
* Participants (emails séparés par virgules)
* Rappels (10min, 30min, 1h, 1 jour)
* Récurrence (quotidienne, hebdomadaire, mensuelle, annuelle)
* Visibilité (public/privé)
* Couleur (11 options)
- Validation des champs obligatoires
- Messages de succès/erreur
- Rechargement automatique de la liste après création
La route /auth-url nécessitait requireAdmin mais devrait être accessible
à tous les utilisateurs authentifiés pour qu'ils puissent connecter
leur propre compte Google Calendar.
Les routes oauth-callback et force-oauth2 doivent être accessibles
sans authentification car elles sont appelées par Google ou publiquement.
- Suppression du router.use(authenticateToken) global
- Ajout de authenticateToken individuellement aux routes qui en ont besoin
- oauth-callback et force-oauth2 restent publiques
Ajout de la route GET /api/calendar/force-oauth2 pour forcer
le changement du type d'authentification vers OAuth2 dans la BDD.
Cette route permet de contourner un problème où le bouton
"Enregistrer" ne sauvegarde pas correctement google_auth_type.
Ajout d'une troisième méthode d'authentification pour Google Calendar :
- OAuth 2.0 (existant)
- Service Account (existant)
- API Key (nouveau)
Modifications :
- Interface d'administration avec sélection API Key
- Champs pour configurer la clé API et l'ID du calendrier
- Backend mis à jour pour gérer l'authentification par API Key
- Routes calendar adaptées pour supporter le nouveau type
- Affichage du statut de connexion mis à jour
L'utilisateur peut maintenant :
1. Sélectionner "API externe" dans les paramètres Google Calendar
2. Entrer sa clé API Google Calendar
3. Spécifier l'ID du calendrier (ou "primary")
4. Synchroniser directement avec Google Calendar
Ajout de deux méthodes d'authentification au choix pour Google Calendar :
1. **OAuth 2.0** (méthode existante améliorée) :
- ✅ Prompt de sélection de compte Google ('select_account consent')
- ✅ Gestion dynamique du redirect_uri avec APP_URL
- ✅ Idéal pour accès au calendrier personnel avec consentement
2. **Service Account** (nouvelle méthode) :
- ✅ Authentification via clé JSON sans interaction utilisateur
- ✅ Parfait pour calendriers partagés en entreprise
- ✅ Synchronisation automatique en arrière-plan
- ✅ Configuration de l'email du calendrier à synchroniser
## Backend (routes/calendar.routes.js) :
- Ajout des constantes AUTH_TYPES (oauth2, service_account)
- Fonction getAuthType() pour récupérer la méthode configurée
- Fonction getServiceAccountClient() pour Service Account
- Fonction getCalendarClient() unifiée qui choisit la bonne méthode
- Route /auth-status mise à jour pour retourner le type d'auth
- Route /sync adaptée pour supporter les deux méthodes
- Support du calendrier email personnalisé pour Service Account
## Frontend (src/pages/Index.tsx) :
- Menu déroulant pour choisir entre OAuth2 et Service Account
- Interface conditionnelle selon la méthode choisie
- Champ textarea pour la clé JSON du Service Account
- Champ email pour spécifier le calendrier à synchroniser
- Affichage du type d'authentification dans le statut
- Amélioration de l'UI avec descriptions claires
## Documentation :
- OAUTH_SETUP.md : Guide complet mis à jour avec les deux méthodes
- Section dédiée au choix de la méthode
- Guide pas à pas pour Service Account
- Dépannage pour les deux méthodes
- README_GOOGLE_CALENDAR.md : Guide de démarrage rapide (nouveau)
- Comparaison des deux méthodes
- Configuration rapide pour chaque méthode
- Liens vers la documentation complète
Cette implémentation offre une flexibilité maximale pour s'adapter
à différents cas d'usage : personnel (OAuth2) ou professionnel (Service Account).
Corrections apportées :
1. **redirect_uri_mismatch** :
- Ajout de la variable APP_URL dans .env.example avec documentation
- Amélioration de la fonction getOAuth2Client pour accepter une URI personnalisée
- Gestion plus flexible du redirect_uri (APP_URL ou localhost par défaut)
2. **Choix du compte Google** :
- Modification du prompt OAuth : 'consent' → 'select_account consent'
- Force maintenant l'affichage du sélecteur de compte Google
3. **Interface utilisateur** :
- Ajout d'un encadré d'avertissement visible avec l'URL de redirection exacte
- Instructions claires pour la configuration dans Google Cloud Console
- Indication pour configurer APP_URL si nécessaire
4. **Documentation** :
- Création du fichier OAUTH_SETUP.md avec guide complet
- Checklist de vérification pour le débogage
- Exemples de configuration pour local et production
Ces modifications permettent de résoudre l'erreur 400 redirect_uri_mismatch
et d'offrir le choix du compte Google lors de la connexion.
Corrections critiques:
1. Ajout de la route PUT /api/settings manquante
- Permet la mise à jour de plusieurs paramètres en une fois
- Utilisé par le frontend pour sauvegarder la configuration
- Erreur 404 résolue
2. Configuration trust proxy dans Express
- app.set('trust proxy', true)
- Nécessaire car l'application est derrière nginx/proxy
- Résout l'erreur express-rate-limit avec X-Forwarded-For
- Permet d'obtenir la vraie IP du client
Routes settings maintenant complètes:
- GET /api/settings - Récupérer tous les paramètres
- PUT /api/settings - Mettre à jour plusieurs paramètres (NOUVEAU)
- GET /api/settings/:key - Récupérer un paramètre
- PUT /api/settings/:key - Mettre à jour un paramètre
- DELETE /api/settings/:key - Supprimer un paramètre
Problème majeur résolu:
- L'éditeur de notes réinitialisait le contenu à chaque modification
- Causé par dangerouslySetInnerHTML qui forçait React à synchroniser
- Résultat: impossible d'éditer les notes correctement
Solution:
- Suppression de dangerouslySetInnerHTML du RichTextEditor
- Conservation du useEffect avec deps vides pour initialisation unique
- Utilisation du key prop pour forcer remount lors du changement de note
- Éditeur maintenant complètement "uncontrolled"
Migration complète Google Calendar vers OAuth 2.0:
- Remplacement de l'API Key simple par OAuth 2.0 (Client ID + Secret)
- Nouvelle table google_oauth_tokens pour stocker les tokens
- Routes OAuth:
* GET /api/calendar/auth-url - Génère l'URL d'authentification
* GET /api/calendar/oauth-callback - Callback OAuth avec échange de code
* GET /api/calendar/auth-status - Vérifier le statut de connexion
* POST /api/calendar/disconnect - Déconnecter Google
- Gestion automatique du rafraîchissement des tokens expirés
- Interface utilisateur mise à jour:
* Champs Client ID et Client Secret
* Indicateur de statut de connexion (connecté/non connecté)
* Bouton "Se connecter avec Google" ouvrant popup OAuth
* Bouton "Déconnecter"
* URI de redirection affichée pour configuration Google Cloud Console
- Synchronisation maintenant avec calendrier principal de l'utilisateur
- Accès complet aux données privées du calendrier
Architecture:
- CalendarService.ts: Nouvelles méthodes getAuthUrl(), getAuthStatus(), disconnect()
- Settings: Ajout de google_client_id et google_client_secret
- Popup OAuth avec communication window.postMessage
- Gestion d'erreur avec re-authentification automatique si token expiré
Corrections:
- Correction de l'import des middlewares dans openrouter.routes.js
- Utilisation de authenticateToken au lieu de requireAuth
- Fix du bug qui empêchait le serveur de démarrer
Le serveur crashait avec l'erreur:
Error: Route.get() requires a callback function but got a [object Undefined]
Résolu en utilisant le bon nom de middleware.
Ajouts majeurs:
- Système de tags complet pour les notes
* Table note_tags en base de données avec indexes
* API REST pour CRUD des tags (GET/POST/DELETE)
* UI avec onglet Tags dans l'éditeur de notes
* Badges cliquables avec icônes et suppression au survol
* Modal pour ajouter des tags
- Liste automatique des modèles OpenRouter
* Nouvelle route API /api/openrouter/models
* Cache des modèles avec rafraîchissement automatique (1h)
* Dropdown dans l'admin avec tous les modèles disponibles
* Bouton "Actualiser" pour forcer le refresh
* Affichage du nom et description de chaque modèle
* Fallback sur input manuel si pas de clé API
Améliorations UI:
- Ajout de dates de création et modification dans les notes
- Affichage des dates dans les cartes et l'éditeur
- Format français pour toutes les dates
- Onglets passés de 4 à 5 colonnes pour inclure Tags
- Animation de chargement sur le bouton refresh des modèles
Architecture:
- Nouveau service TagsService.ts pour la gestion des tags
- Nouveau service OpenRouterService.ts pour l'API OpenRouter
- Nouvelles routes openrouter.routes.js
- Support Select component pour les dropdowns
- Modals InputModal utilisés pour les tags
Modaux :
- Création ConfirmModal pour confirmations avec variants (default/destructive)
- Création InputModal pour saisie de texte/URL/mot de passe
- Création AddUserModal pour création d'utilisateur avec admin checkbox
- Remplacement de tous les prompt() et confirm() par des modaux modernes
- Modaux pour : suppression note, ajout todo, ajout RSS, ajout utilisateur, suppression utilisateur, changement mot de passe, ajout todo dans note
Fix RSS :
- Ajout route POST /api/rss/refresh (alias de /api/rss/fetch)
- Fix uploadImage retour avec result.image || result
UX améliorée :
- Plus de popups navigateur natifs
- Interface cohérente et moderne
- Validation dans les modaux
- Support touche Entrée dans les InputModals
- Auto-focus sur le champ de saisie