diff --git a/scripts/README-FIXES.md b/scripts/README-FIXES.md new file mode 100644 index 0000000..31ca5d6 --- /dev/null +++ b/scripts/README-FIXES.md @@ -0,0 +1,60 @@ +# Scripts de correction PostgreSQL + +## Problème: Notes ne s'affichent pas (Erreur 500) + +Si vous voyez une erreur 500 sur `/api/notes` après la migration vers PostgreSQL, c'est probablement dû à un problème de conversion des données booléennes. + +### Cause + +Lors de la migration de SQLite vers PostgreSQL: +- SQLite stocke les booléens comme INTEGER (0/1) +- PostgreSQL utilise le type BOOLEAN (TRUE/FALSE) +- Si les données ont été migrées sans conversion, les valeurs restent en INTEGER +- Les requêtes SQL qui utilisent `completed = TRUE` échouent + +### Solution + +Exécutez le script de correction sur votre base de données de production: + +```bash +# Sur le serveur de production +cd /path/to/noteflow +node scripts/fix-boolean-data.js +``` + +Ce script va: +1. Se connecter à votre base PostgreSQL +2. Vérifier les types des colonnes booléennes +3. Convertir les colonnes INTEGER en BOOLEAN si nécessaire +4. Convertir automatiquement les valeurs 0/1 en FALSE/TRUE + +### Colonnes concernées + +- `users.is_admin` +- `notes.archived` +- `note_todos.completed`, `note_todos.priority` +- `global_todos.completed`, `global_todos.priority` +- `rss_feeds.enabled` +- `calendar_events.all_day` + +### Variables d'environnement + +Le script utilise `DATABASE_URL` de votre fichier `.env`: + +```env +DATABASE_URL=postgresql://noteflow:password@localhost:5499/noteflow +``` + +### Migration future + +Le script `migrate-sqlite-to-postgres.js` a été amélioré pour convertir automatiquement les booléens lors des futures migrations. Vous n'aurez plus besoin d'exécuter `fix-boolean-data.js` après de nouvelles migrations. + +### Vérification + +Après l'exécution du script, redémarrez votre serveur et vérifiez que les notes s'affichent correctement. + +```bash +pm2 restart noteflow +# ou +systemctl restart noteflow +``` diff --git a/scripts/fix-boolean-data.js b/scripts/fix-boolean-data.js new file mode 100755 index 0000000..c08c30b --- /dev/null +++ b/scripts/fix-boolean-data.js @@ -0,0 +1,92 @@ +#!/usr/bin/env node +// Script de correction des données booléennes dans PostgreSQL +// Convertit les valeurs 0/1 (INTEGER) en FALSE/TRUE (BOOLEAN) + +require('dotenv').config(); +const { Pool } = require('pg'); + +const DATABASE_URL = process.env.DATABASE_URL || + `postgresql://noteflow:noteflow_secure_password_change_me@localhost:5499/noteflow`; + +console.log('\n========== CORRECTION DES DONNÉES BOOLÉENNES ==========\n'); + +const pool = new Pool({ + connectionString: DATABASE_URL, +}); + +async function fixBooleanColumns() { + const client = await pool.connect(); + + try { + console.log('✓ Connexion PostgreSQL établie\n'); + + // Liste des tables et colonnes avec type BOOLEAN + const booleanColumns = [ + { table: 'users', columns: ['is_admin'] }, + { table: 'notes', columns: ['archived'] }, + { table: 'note_todos', columns: ['completed', 'priority'] }, + { table: 'global_todos', columns: ['completed', 'priority'] }, + { table: 'rss_feeds', columns: ['enabled'] }, + { table: 'calendar_events', columns: ['all_day'] } + ]; + + for (const { table, columns } of booleanColumns) { + console.log(`📋 Table: ${table}`); + + for (const column of columns) { + try { + // Vérifier le type actuel de la colonne + const typeCheck = await client.query(` + SELECT data_type + FROM information_schema.columns + WHERE table_name = $1 AND column_name = $2 + `, [table, column]); + + if (typeCheck.rows.length === 0) { + console.log(` ⏭️ Colonne ${column} n'existe pas, skip`); + continue; + } + + const dataType = typeCheck.rows[0].data_type; + console.log(` 📊 ${column}: type actuel = ${dataType}`); + + if (dataType === 'boolean') { + // Si la colonne est déjà BOOLEAN, vérifier s'il y a des valeurs INTEGER incorrectes + // En réalité, PostgreSQL convertit automatiquement lors de l'insertion + // Mais on peut forcer la conversion pour être sûr + console.log(` ✓ ${column} est déjà BOOLEAN`); + } else if (dataType === 'integer') { + // Si la colonne est INTEGER, la convertir en BOOLEAN + console.log(` 🔧 Conversion ${column}: INTEGER → BOOLEAN`); + + await client.query(` + ALTER TABLE ${table} + ALTER COLUMN ${column} TYPE BOOLEAN + USING CASE + WHEN ${column} = 0 THEN FALSE + WHEN ${column} = 1 THEN TRUE + ELSE ${column}::BOOLEAN + END + `); + + console.log(` ✅ ${column} converti en BOOLEAN`); + } + } catch (err) { + console.error(` ❌ Erreur sur ${column}:`, err.message); + } + } + console.log(''); + } + + console.log('✅ Correction terminée!\n'); + + } catch (error) { + console.error('❌ Erreur:', error); + process.exit(1); + } finally { + client.release(); + await pool.end(); + } +} + +fixBooleanColumns(); diff --git a/scripts/migrate-sqlite-to-postgres.js b/scripts/migrate-sqlite-to-postgres.js index d520244..edea403 100644 --- a/scripts/migrate-sqlite-to-postgres.js +++ b/scripts/migrate-sqlite-to-postgres.js @@ -69,6 +69,16 @@ function getSqliteData(tableName) { }); } +// Colonnes booléennes connues (pour conversion 0/1 → FALSE/TRUE) +const BOOLEAN_COLUMNS = { + users: ['is_admin'], + notes: ['archived'], + note_todos: ['completed', 'priority'], + global_todos: ['completed', 'priority'], + rss_feeds: ['enabled'], + calendar_events: ['all_day'] +}; + /** * Insérer les données dans PostgreSQL */ @@ -80,6 +90,7 @@ async function insertPostgresData(tableName, rows) { const client = await pgPool.connect(); let inserted = 0; + const booleanCols = BOOLEAN_COLUMNS[tableName] || []; try { await client.query('BEGIN'); @@ -87,10 +98,18 @@ async function insertPostgresData(tableName, rows) { for (const row of rows) { // Convertir les noms de colonnes et valeurs const columns = Object.keys(row); - const values = Object.values(row).map(val => { + const values = columns.map((col, idx) => { + const val = Object.values(row)[idx]; + // Convertir les valeurs SQLite vers PostgreSQL if (val === null) return null; if (typeof val === 'boolean') return val; + + // Convertir les colonnes booléennes: 0/1 → FALSE/TRUE + if (booleanCols.includes(col) && (val === 0 || val === 1)) { + return val === 1; + } + if (typeof val === 'number') return val; // Dates if (typeof val === 'string' && /^\d{4}-\d{2}-\d{2}/.test(val)) {