mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-23 15:18:16 +02:00
feat: add scripts to fix PostgreSQL boolean data type issues
Added tools to resolve the 500 error when notes don't display due to boolean type mismatches between SQLite (0/1) and PostgreSQL (TRUE/FALSE). New files: - scripts/fix-boolean-data.js: Converts INTEGER columns to BOOLEAN type and migrates existing 0/1 data to FALSE/TRUE - scripts/README-FIXES.md: Instructions for running the fix script Improved: - scripts/migrate-sqlite-to-postgres.js: Now automatically converts 0/1 to FALSE/TRUE for known boolean columns during migration This resolves issues where: - Migrated data contains INTEGER values (0/1) instead of BOOLEAN - SQL queries using "completed = TRUE" fail to match rows - API endpoints return 500 errors To fix an existing database, run: node scripts/fix-boolean-data.js
This commit is contained in:
3 files changed
+172
-1
No files matched your search
@@ -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
|
||||||
|
```
|
||||||
Executable
+92
@@ -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();
|
||||||
@@ -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
|
* Insérer les données dans PostgreSQL
|
||||||
*/
|
*/
|
||||||
@@ -80,6 +90,7 @@ async function insertPostgresData(tableName, rows) {
|
|||||||
|
|
||||||
const client = await pgPool.connect();
|
const client = await pgPool.connect();
|
||||||
let inserted = 0;
|
let inserted = 0;
|
||||||
|
const booleanCols = BOOLEAN_COLUMNS[tableName] || [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.query('BEGIN');
|
await client.query('BEGIN');
|
||||||
@@ -87,10 +98,18 @@ async function insertPostgresData(tableName, rows) {
|
|||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
// Convertir les noms de colonnes et valeurs
|
// Convertir les noms de colonnes et valeurs
|
||||||
const columns = Object.keys(row);
|
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
|
// Convertir les valeurs SQLite vers PostgreSQL
|
||||||
if (val === null) return null;
|
if (val === null) return null;
|
||||||
if (typeof val === 'boolean') return val;
|
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;
|
if (typeof val === 'number') return val;
|
||||||
// Dates
|
// Dates
|
||||||
if (typeof val === 'string' && /^\d{4}-\d{2}-\d{2}/.test(val)) {
|
if (typeof val === 'string' && /^\d{4}-\d{2}-\d{2}/.test(val)) {
|
||||||
|
|||||||
Reference in new issue
Block a user