diff --git a/RECUPERATION-DONNEES.md b/RECUPERATION-DONNEES.md new file mode 100644 index 0000000..2820cd6 --- /dev/null +++ b/RECUPERATION-DONNEES.md @@ -0,0 +1,157 @@ +# 🚹 RÉCUPÉRATION DES DONNÉES - Guide d'urgence + +## 📋 Qu'est-ce qui s'est passĂ© ? + +Vos donnĂ©es ne sont **PAS perdues** ! Le problĂšme Ă©tait que: + +1. Docker Ă©tait configurĂ© pour utiliser PostgreSQL (`DB_TYPE=postgres`) +2. Mais le code chargeait toujours SQLite +3. RĂ©sultat: une nouvelle base SQLite vide Ă©tait créée +4. Vos vraies donnĂ©es sont restĂ©es dans la base SQLite d'origine + +## ✅ Solution appliquĂ©e + +Un systĂšme de **dĂ©tection automatique** a Ă©tĂ© ajoutĂ© (`config/database-loader.js`) qui: +- DĂ©tecte `DB_TYPE=postgres` → charge PostgreSQL +- DĂ©tecte `DATABASE_URL=postgresql://` → charge PostgreSQL +- Sinon → charge SQLite + +## 🔄 Deux options pour rĂ©cupĂ©rer vos donnĂ©es + +### Option 1: Utiliser SQLite (RAPIDE - recommandĂ© pour l'instant) + +**Revenir temporairement Ă  SQLite le temps de tout vĂ©rifier:** + +```bash +# 1. ArrĂȘter les containers +docker-compose down + +# 2. Modifier docker-compose.yml temporairement +# Commentez ces lignes dans la section notes-app > environment: +# - DATABASE_URL=postgresql://... +# - DB_TYPE=postgres + +# 3. Relancer +docker-compose build notes-app +docker-compose up -d +``` + +Vos donnĂ©es rĂ©apparaĂźtront immĂ©diatement car SQLite sera rechargĂ©. + +### Option 2: Migrer vers PostgreSQL (PROPRE - recommandĂ© pour production) + +**Migration complĂšte de SQLite → PostgreSQL:** + +```bash +# 1. Reconstruire avec le nouveau code +docker-compose build notes-app + +# 2. DĂ©marrer les containers +docker-compose up -d + +# 3. Attendre 30 secondes que PostgreSQL dĂ©marre +sleep 30 + +# 4. Lancer la migration +bash scripts/run-migration.sh +``` + +Cette option copie toutes vos donnĂ©es de SQLite vers PostgreSQL. + +## 🔍 VĂ©rifier que tout fonctionne + +### AprĂšs Option 1 (SQLite): + +```bash +# VĂ©rifier les donnĂ©es +docker exec notes-todo-app node -e " +const { getAll } = require('./config/database-loader'); +getAll('SELECT COUNT(*) as count FROM notes').then(r => { + console.log('Notes:', r[0].count); + process.exit(0); +}); +" +``` + +### AprĂšs Option 2 (PostgreSQL): + +```bash +# VĂ©rifier les donnĂ©es dans PostgreSQL +docker exec noteflow-postgres psql -U noteflow -d noteflow -p 5499 \ + -c "SELECT COUNT(*) FROM notes;" + +docker exec noteflow-postgres psql -U noteflow -d noteflow -p 5499 \ + -c "SELECT COUNT(*) FROM rss_articles;" +``` + +## 🎯 Recommandation + +**Pour l'instant:** +1. Utilisez **Option 1** (SQLite) pour rĂ©cupĂ©rer immĂ©diatement vos donnĂ©es +2. VĂ©rifiez que tout fonctionne bien +3. Plus tard, quand vous ĂȘtes prĂȘt, utilisez **Option 2** pour migrer vers PostgreSQL + +## 📂 OĂč sont vos donnĂ©es ? + +- **SQLite actuel:** `./data/notes.db` (vos vraies donnĂ©es sont ici) +- **PostgreSQL:** Container Docker `noteflow-postgres` (vide ou partiellement migrĂ©) + +## ⚠ Important + +**NE SUPPRIMEZ PAS** le fichier `./data/notes.db` tant que vous n'avez pas: +1. VĂ©rifiĂ© que tout fonctionne avec PostgreSQL +2. ConfirmĂ© que toutes vos donnĂ©es sont migrĂ©es +3. TestĂ© l'application pendant plusieurs jours + +## 🆘 En cas de problĂšme + +### Voir les logs +```bash +docker-compose logs notes-app | tail -50 +``` + +### Identifier quelle base est utilisĂ©e +```bash +docker exec notes-todo-app node -e " +console.log('DB_TYPE:', process.env.DB_TYPE); +console.log('DATABASE_URL:', process.env.DATABASE_URL?.replace(/:[^:@]+@/, ':***@')); +" +``` + +### Forcer SQLite mĂȘme avec Docker +```bash +docker exec -e DB_TYPE=sqlite notes-todo-app node server.js +``` + +## 📞 Debug rapide + +```bash +# Compter les notes dans SQLite +docker exec notes-todo-app node -e " +const sqlite3 = require('sqlite3'); +const db = new sqlite3.Database('./data/notes.db'); +db.get('SELECT COUNT(*) as count FROM notes', (err, row) => { + console.log('Notes SQLite:', row.count); + db.close(); +}); +" 2>/dev/null + +# Compter les notes dans PostgreSQL +docker exec noteflow-postgres psql -U noteflow -d noteflow -p 5499 -t \ + -c "SELECT COUNT(*) FROM notes;" 2>/dev/null || echo "PostgreSQL non accessible" +``` + +## ✅ Checklist de rĂ©cupĂ©ration + +- [ ] Containers redĂ©marrĂ©s avec nouvelle configuration +- [ ] Base de donnĂ©es accessible (SQLite OU PostgreSQL) +- [ ] Notes visibles dans l'interface +- [ ] TĂąches visibles +- [ ] Flux RSS configurĂ©s +- [ ] ÉvĂ©nements calendrier prĂ©sents +- [ ] ParamĂštres restaurĂ©s + +--- + +**Date de crĂ©ation:** 2025-11-14 +**Commit de correction:** 55a1809 - Fix: Chargement automatique SQLite/PostgreSQL diff --git a/config/database-loader.js b/config/database-loader.js new file mode 100644 index 0000000..2e1435d --- /dev/null +++ b/config/database-loader.js @@ -0,0 +1,22 @@ +// Chargeur automatique de base de donnĂ©es (SQLite ou PostgreSQL) +const logger = require('./logger'); + +// DĂ©tecter le type de base de donnĂ©es +const DB_TYPE = process.env.DB_TYPE || 'sqlite'; +const DATABASE_URL = process.env.DATABASE_URL; + +logger.info(`Configuration de base de donnĂ©es: ${DB_TYPE}`); + +// Charger le module appropriĂ© +let databaseModule; + +if (DB_TYPE === 'postgres' || DATABASE_URL?.startsWith('postgresql://')) { + logger.info('Chargement de la configuration PostgreSQL'); + databaseModule = require('./database-postgres'); +} else { + logger.info('Chargement de la configuration SQLite'); + databaseModule = require('./database'); +} + +// Exporter toutes les fonctions du module chargĂ© +module.exports = databaseModule; diff --git a/routes/auth.routes.js b/routes/auth.routes.js index f0bb807..1ff6e10 100644 --- a/routes/auth.routes.js +++ b/routes/auth.routes.js @@ -4,7 +4,7 @@ const router = express.Router(); const bcrypt = require('bcrypt'); const { body, validationResult } = require('express-validator'); -const { getOne } = require('../config/database'); +const { getOne } = require('../config/database-loader'); const { generateToken, authenticateToken } = require('../middleware/auth'); const logger = require('../config/logger'); diff --git a/routes/calendar.routes.js b/routes/calendar.routes.js index 82eea6b..21313d7 100644 --- a/routes/calendar.routes.js +++ b/routes/calendar.routes.js @@ -2,7 +2,7 @@ const express = require('express'); const router = express.Router(); const { google } = require('googleapis'); -const { getAll, getOne, runQuery } = require('../config/database'); +const { getAll, getOne, runQuery } = require('../config/database-loader'); const { authenticateToken, requireAdmin } = require('../middleware/auth'); const logger = require('../config/logger'); diff --git a/routes/notes.routes.js b/routes/notes.routes.js index 4960f7b..3b88977 100644 --- a/routes/notes.routes.js +++ b/routes/notes.routes.js @@ -6,7 +6,7 @@ const path = require('path'); const fs = require('fs'); const { body, validationResult } = require('express-validator'); -const { getAll, getOne, runQuery } = require('../config/database'); +const { getAll, getOne, runQuery } = require('../config/database-loader'); const { authenticateToken } = require('../middleware/auth'); const logger = require('../config/logger'); diff --git a/routes/openrouter.routes.js b/routes/openrouter.routes.js index 42a43e8..59b242f 100644 --- a/routes/openrouter.routes.js +++ b/routes/openrouter.routes.js @@ -2,7 +2,7 @@ const express = require('express'); const router = express.Router(); const { authenticateToken, requireAdmin } = require('../middleware/auth'); -const { getOne } = require('../config/database'); +const { getOne } = require('../config/database-loader'); const logger = require('../config/logger'); // Cache pour les modĂšles (rafraĂźchi toutes les heures) diff --git a/routes/rss.routes-v2.js b/routes/rss.routes-v2.js index aee90eb..4b3a289 100644 --- a/routes/rss.routes-v2.js +++ b/routes/rss.routes-v2.js @@ -3,7 +3,7 @@ const express = require('express'); const router = express.Router(); const Parser = require('rss-parser'); const axios = require('axios'); -const { getAll, getOne, runQuery } = require('../config/database'); +const { getAll, getOne, runQuery } = require('../config/database-loader'); const { authenticateToken, requireAdmin } = require('../middleware/auth'); const logger = require('../config/logger'); diff --git a/routes/rss.routes.js b/routes/rss.routes.js index aee90eb..4b3a289 100644 --- a/routes/rss.routes.js +++ b/routes/rss.routes.js @@ -3,7 +3,7 @@ const express = require('express'); const router = express.Router(); const Parser = require('rss-parser'); const axios = require('axios'); -const { getAll, getOne, runQuery } = require('../config/database'); +const { getAll, getOne, runQuery } = require('../config/database-loader'); const { authenticateToken, requireAdmin } = require('../middleware/auth'); const logger = require('../config/logger'); diff --git a/routes/settings.routes.js b/routes/settings.routes.js index 8943729..ede4272 100644 --- a/routes/settings.routes.js +++ b/routes/settings.routes.js @@ -1,7 +1,7 @@ // Routes de gestion des paramĂštres const express = require('express'); const router = express.Router(); -const { getAll, getOne, runQuery } = require('../config/database'); +const { getAll, getOne, runQuery } = require('../config/database-loader'); const { authenticateToken, requireAdmin } = require('../middleware/auth'); const logger = require('../config/logger'); diff --git a/routes/todos.routes.js b/routes/todos.routes.js index 20a93a5..31d2441 100644 --- a/routes/todos.routes.js +++ b/routes/todos.routes.js @@ -3,7 +3,7 @@ const express = require('express'); const router = express.Router(); const { body, validationResult } = require('express-validator'); -const { getAll, getOne, runQuery } = require('../config/database'); +const { getAll, getOne, runQuery } = require('../config/database-loader'); const { authenticateToken } = require('../middleware/auth'); const logger = require('../config/logger'); diff --git a/routes/users.routes.js b/routes/users.routes.js index 8973e4d..de19b63 100644 --- a/routes/users.routes.js +++ b/routes/users.routes.js @@ -4,7 +4,7 @@ const router = express.Router(); const bcrypt = require('bcrypt'); const { body, validationResult } = require('express-validator'); -const { getAll, getOne, runQuery } = require('../config/database'); +const { getAll, getOne, runQuery } = require('../config/database-loader'); const { authenticateToken, requireAdmin } = require('../middleware/auth'); const logger = require('../config/logger'); diff --git a/scripts/check-data.js b/scripts/check-data.js new file mode 100755 index 0000000..9358865 --- /dev/null +++ b/scripts/check-data.js @@ -0,0 +1,123 @@ +// Script de diagnostic - VĂ©rifier oĂč sont les donnĂ©es +const sqlite3 = require('sqlite3').verbose(); +const path = require('path'); +const { Pool } = require('pg'); + +console.log('\n🔍 DIAGNOSTIC DES DONNÉES\n'); +console.log('═══════════════════════════════════════════\n'); + +// Variables d'environnement +console.log('📋 Configuration actuelle:'); +console.log(' DB_TYPE:', process.env.DB_TYPE || '(non dĂ©fini → SQLite)'); +console.log(' DATABASE_URL:', process.env.DATABASE_URL ? + process.env.DATABASE_URL.replace(/:[^:@]+@/, ':***@') : '(non dĂ©fini)'); +console.log(''); + +// 1. VĂ©rifier SQLite +const SQLITE_PATH = path.join(__dirname, '../data/notes.db'); +console.log('📊 DonnĂ©es dans SQLite (' + SQLITE_PATH + '):'); + +const sqliteDb = new sqlite3.Database(SQLITE_PATH, async (err) => { + if (err) { + console.log(' ❌ Erreur connexion SQLite:', err.message); + checkPostgres(); + return; + } + + const tables = ['users', 'notes', 'global_todos', 'rss_feeds', 'rss_articles', 'calendar_events', 'settings']; + let completed = 0; + + for (const table of tables) { + sqliteDb.get(`SELECT COUNT(*) as count FROM ${table}`, (err, row) => { + if (err) { + console.log(` ‱ ${table}: ❌ ${err.message}`); + } else { + const icon = row.count > 0 ? '✅' : 'âšȘ'; + console.log(` ${icon} ${table.padEnd(20)} : ${row.count}`); + } + + completed++; + if (completed === tables.length) { + sqliteDb.close(() => { + console.log(''); + checkPostgres(); + }); + } + }); + } +}); + +// 2. VĂ©rifier PostgreSQL +async function checkPostgres() { + const DATABASE_URL = process.env.DATABASE_URL; + + if (!DATABASE_URL || !DATABASE_URL.startsWith('postgresql://')) { + console.log('📊 PostgreSQL: Non configurĂ© (DATABASE_URL manquant)\n'); + showConclusion(); + return; + } + + console.log('📊 DonnĂ©es dans PostgreSQL:'); + + const pool = new Pool({ + connectionString: DATABASE_URL, + max: 1, + connectionTimeoutMillis: 3000, + }); + + try { + const client = await pool.connect(); + + const tables = ['users', 'notes', 'global_todos', 'rss_feeds', 'rss_articles', 'calendar_events', 'settings']; + + for (const table of tables) { + try { + const result = await client.query(`SELECT COUNT(*) as count FROM ${table}`); + const count = parseInt(result.rows[0].count); + const icon = count > 0 ? '✅' : 'âšȘ'; + console.log(` ${icon} ${table.padEnd(20)} : ${count}`); + } catch (err) { + console.log(` ‱ ${table.padEnd(20)} : ❌ ${err.message.split('\n')[0]}`); + } + } + + client.release(); + await pool.end(); + console.log(''); + showConclusion(); + + } catch (error) { + console.log(' ❌ Connexion impossible:', error.message); + console.log(''); + showConclusion(); + } +} + +function showConclusion() { + console.log('═══════════════════════════════════════════'); + console.log('\n💡 CONCLUSION:\n'); + + const dbType = process.env.DB_TYPE || 'sqlite'; + const hasDbUrl = process.env.DATABASE_URL?.startsWith('postgresql://'); + + if (dbType === 'postgres' || hasDbUrl) { + console.log(' L\'application est configurĂ©e pour PostgreSQL'); + console.log(' '); + console.log(' ⚠ Si PostgreSQL est vide et SQLite contient des donnĂ©es:'); + console.log(' → Vos donnĂ©es sont dans SQLite'); + console.log(' → Lancez la migration: bash scripts/run-migration.sh'); + console.log(' '); + console.log(' ✅ Si PostgreSQL contient des donnĂ©es:'); + console.log(' → Migration dĂ©jĂ  effectuĂ©e'); + console.log(' → RedĂ©marrez: docker-compose restart notes-app'); + } else { + console.log(' L\'application est configurĂ©e pour SQLite'); + console.log(' '); + console.log(' ✅ Si SQLite contient des donnĂ©es:'); + console.log(' → Tout est normal'); + console.log(' → VĂ©rifiez l\'interface web'); + } + + console.log('\n═══════════════════════════════════════════\n'); + process.exit(0); +} diff --git a/server.js b/server.js index 0b3128f..d1db284 100644 --- a/server.js +++ b/server.js @@ -7,7 +7,7 @@ const path = require('path'); const fs = require('fs'); const logger = require('./config/logger'); -const { initDatabase } = require('./config/database'); +const { initDatabase } = require('./config/database-loader'); // CrĂ©er l'application Express const app = express(); diff --git a/services/rss-scheduler-v2.js b/services/rss-scheduler-v2.js index 44617bf..32d3f3a 100644 --- a/services/rss-scheduler-v2.js +++ b/services/rss-scheduler-v2.js @@ -1,6 +1,6 @@ // Service RSS NOUVEAU - Architecture simple et robuste const Parser = require('rss-parser'); -const { getAll, getOne, runQuery } = require('../config/database'); +const { getAll, getOne, runQuery } = require('../config/database-loader'); const logger = require('../config/logger'); const parser = new Parser({ diff --git a/services/rss-scheduler.js b/services/rss-scheduler.js index 44617bf..32d3f3a 100644 --- a/services/rss-scheduler.js +++ b/services/rss-scheduler.js @@ -1,6 +1,6 @@ // Service RSS NOUVEAU - Architecture simple et robuste const Parser = require('rss-parser'); -const { getAll, getOne, runQuery } = require('../config/database'); +const { getAll, getOne, runQuery } = require('../config/database-loader'); const logger = require('../config/logger'); const parser = new Parser({