Fixed critical SQL type error in todo creation endpoint that prevented todos from being added to notes.
Bug:
- Line 417: Used FALSE (boolean) instead of 0 (integer) in COALESCE function
- SQL: COALESCE(MAX(position), FALSE) + 1
- This caused a type mismatch error when inserting new todos
Fix:
- Changed to: COALESCE(MAX(position), 0) + 1
- Now correctly defaults to 0 when no todos exist for a note
This fix resolves the issue where adding todos would fail silently or throw database errors.
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
🐘 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)
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
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
Images multiples :
- Ajout table note_images dans la base de données
- Routes POST/GET/DELETE /api/notes/:id/images
- Dossier uploads/images séparé pour les images
- Support de plusieurs images par note
Google Calendar :
- Ajout table calendar_events
- Routes /api/calendar pour sync et récupération des événements
- Box Rendez-vous dans l'interface avec affichage des événements à venir
- Configuration Google Calendar dans l'admin (API key et Calendar ID)
- Synchronisation manuelle avec Google Calendar API
- Installation package googleapis
Corrections :
- Fix RichTextEditor: ajout dir="ltr" pour empêcher l'écriture RTL
- Fix Image type conflict (lucide-react vs Note interface)
- Ajout original_name à l'interface Image pour compatibilité
Interface :
- Box Rendez-vous empilée avec Todos et RSS dans la colonne droite
- Nouvel onglet Google Calendar dans l'administration
- Bouton "Synchroniser" pour rafraîchir les événements
## Corrections UX:
- Fix coins arrondis en bas des notes étendues (overflow: hidden)
- Remplacement prompt() par input inline pour ajout de tâches
- Bouton "Ajouter" avec validation au clic ou touche Entrée
- Expérience utilisateur fluide sans popups intrusifs
## Système de fichiers complet:
### Backend:
- Création table note_files (id, note_id, filename, original_name, file_size, mime_type, created_at)
- POST /api/notes/:id/files - Upload fichier (multer, 10MB max)
- GET /api/notes/:id/files - Liste fichiers d'une note
- GET /api/notes/files/:fileId/download - Téléchargement sécurisé
- DELETE /api/notes/files/:fileId - Suppression fichier
- Stockage dans public/uploads/files/
- Auto-création dossier si nécessaire
### Frontend:
- Bouton "Ajouter un fichier" dans footer des notes
- Input hidden fileInput pour sélection
- Affichage liste fichiers avec icône, nom, taille
- Boutons télécharger/supprimer par fichier
- Fonction formatFileSize() pour affichage lisible
- Event delegation pour tous les boutons fichiers
- Confirmation avant suppression
### Intégration:
- GET /api/notes charge maintenant files[] et files_count
- renderInlineFiles() génère HTML des fichiers
- downloadFile() ouvre download dans nouvel onglet
- deleteFile() avec confirmation dialog
- Reload automatique après upload/delete
- Conservation état étendu après opérations
### CSS moderne:
- .note-file-item avec hover effect
- .note-file-icon, .note-file-info, .note-file-actions
- Buttons download/delete avec transitions
- Design cohérent avec reste de l'app
- Truncate nom fichier avec ellipsis
Tous types de fichiers acceptés (PDF, DOC, ZIP, etc.)
- Migration auto colonne archived (BOOLEAN DEFAULT 0)
- Route GET /api/notes?archived=true/false pour filtrer
- Route PUT /api/notes/:id/archive pour archiver/désarchiver
- Chargement des todos avec chaque note (todos[], todos_count, todos_completed)
- Support filtrage par statut archivé
- Logs archivage/désarchivage
- Remplacement de COUNT(DISTINCT) par une sous-requête dans SQLite
- Ajout de vérification Array.isArray côté client
- Gestion d'erreur améliorée avec fallback sur tableau vide
- Correction du bug 'filtered.forEach is not a function'
- L'API retourne toujours un tableau, même en cas d'erreur