mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-11 17:29:37 +02:00
219 lines
8.0 KiB
JavaScript
219 lines
8.0 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Script de migrations automatiques
|
|
* S'exécute au démarrage de l'application pour mettre à jour le schéma
|
|
*/
|
|
|
|
const { Pool } = require('pg');
|
|
const logger = require('../config/logger');
|
|
|
|
const DATABASE_URL = process.env.DATABASE_URL ||
|
|
`postgresql://${process.env.PGUSER || 'noteflow'}:${process.env.PGPASSWORD || 'noteflow_secure_password_change_me'}@${process.env.PGHOST || 'postgres'}:${process.env.PGPORT || '5499'}/${process.env.PGDATABASE || 'noteflow'}`;
|
|
|
|
async function autoMigrate() {
|
|
const pool = new Pool({ connectionString: DATABASE_URL });
|
|
|
|
try {
|
|
logger.info('🔄 Vérification des migrations...');
|
|
|
|
const client = await pool.connect();
|
|
|
|
try {
|
|
await client.query('BEGIN');
|
|
|
|
// Migration 1: Champs de tracking pour la purge (archived_at, completed_at)
|
|
logger.info(' Vérification: champs de tracking pour purge...');
|
|
|
|
// Vérifier si archived_at existe
|
|
const archivedAtExists = await client.query(`
|
|
SELECT column_name
|
|
FROM information_schema.columns
|
|
WHERE table_name='notes' AND column_name='archived_at'
|
|
`);
|
|
|
|
if (archivedAtExists.rows.length === 0) {
|
|
logger.info(' → Ajout du champ archived_at à notes');
|
|
await client.query(`ALTER TABLE notes ADD COLUMN archived_at TIMESTAMP`);
|
|
await client.query(`UPDATE notes SET archived_at = updated_at WHERE archived = TRUE AND archived_at IS NULL`);
|
|
}
|
|
|
|
// Vérifier si completed_at existe pour global_todos
|
|
const globalTodosCompletedAtExists = await client.query(`
|
|
SELECT column_name
|
|
FROM information_schema.columns
|
|
WHERE table_name='global_todos' AND column_name='completed_at'
|
|
`);
|
|
|
|
if (globalTodosCompletedAtExists.rows.length === 0) {
|
|
logger.info(' → Ajout du champ completed_at à global_todos');
|
|
await client.query(`ALTER TABLE global_todos ADD COLUMN completed_at TIMESTAMP`);
|
|
await client.query(`UPDATE global_todos SET completed_at = created_at WHERE completed = TRUE AND completed_at IS NULL`);
|
|
}
|
|
|
|
// Vérifier si note_todos a created_at et completed_at
|
|
const noteTodosCreatedAtExists = await client.query(`
|
|
SELECT column_name
|
|
FROM information_schema.columns
|
|
WHERE table_name='note_todos' AND column_name='created_at'
|
|
`);
|
|
|
|
if (noteTodosCreatedAtExists.rows.length === 0) {
|
|
logger.info(' → Ajout du champ created_at à note_todos');
|
|
await client.query(`ALTER TABLE note_todos ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP`);
|
|
}
|
|
|
|
const noteTodosCompletedAtExists = await client.query(`
|
|
SELECT column_name
|
|
FROM information_schema.columns
|
|
WHERE table_name='note_todos' AND column_name='completed_at'
|
|
`);
|
|
|
|
if (noteTodosCompletedAtExists.rows.length === 0) {
|
|
logger.info(' → Ajout du champ completed_at à note_todos');
|
|
await client.query(`ALTER TABLE note_todos ADD COLUMN completed_at TIMESTAMP`);
|
|
await client.query(`UPDATE note_todos SET completed_at = CURRENT_TIMESTAMP WHERE completed = TRUE AND completed_at IS NULL`);
|
|
}
|
|
|
|
// Créer les triggers pour mise à jour automatique
|
|
await client.query(`
|
|
CREATE OR REPLACE FUNCTION update_notes_archived_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF NEW.archived = TRUE AND OLD.archived = FALSE THEN
|
|
NEW.archived_at = CURRENT_TIMESTAMP;
|
|
ELSIF NEW.archived = FALSE THEN
|
|
NEW.archived_at = NULL;
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
`);
|
|
|
|
await client.query(`
|
|
DROP TRIGGER IF EXISTS trigger_notes_archived_at ON notes;
|
|
CREATE TRIGGER trigger_notes_archived_at
|
|
BEFORE UPDATE ON notes
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_notes_archived_at();
|
|
`);
|
|
|
|
await client.query(`
|
|
CREATE OR REPLACE FUNCTION update_global_todos_completed_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF NEW.completed = TRUE AND OLD.completed = FALSE THEN
|
|
NEW.completed_at = CURRENT_TIMESTAMP;
|
|
ELSIF NEW.completed = FALSE THEN
|
|
NEW.completed_at = NULL;
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
`);
|
|
|
|
await client.query(`
|
|
DROP TRIGGER IF EXISTS trigger_global_todos_completed_at ON global_todos;
|
|
CREATE TRIGGER trigger_global_todos_completed_at
|
|
BEFORE UPDATE ON global_todos
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_global_todos_completed_at();
|
|
`);
|
|
|
|
await client.query(`
|
|
CREATE OR REPLACE FUNCTION update_note_todos_completed_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF NEW.completed = TRUE AND (OLD.completed IS NULL OR OLD.completed = FALSE) THEN
|
|
NEW.completed_at = CURRENT_TIMESTAMP;
|
|
ELSIF NEW.completed = FALSE THEN
|
|
NEW.completed_at = NULL;
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
`);
|
|
|
|
await client.query(`
|
|
DROP TRIGGER IF EXISTS trigger_note_todos_completed_at ON note_todos;
|
|
CREATE TRIGGER trigger_note_todos_completed_at
|
|
BEFORE UPDATE ON note_todos
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION update_note_todos_completed_at();
|
|
`);
|
|
|
|
// Migration 2: Champ priority pour les tâches
|
|
logger.info(' Vérification: champ priority pour tâches...');
|
|
|
|
const globalTodosPriorityExists = await client.query(`
|
|
SELECT column_name
|
|
FROM information_schema.columns
|
|
WHERE table_name='global_todos' AND column_name='priority'
|
|
`);
|
|
|
|
if (globalTodosPriorityExists.rows.length === 0) {
|
|
logger.info(' → Ajout du champ priority à global_todos');
|
|
await client.query(`ALTER TABLE global_todos ADD COLUMN priority BOOLEAN DEFAULT FALSE`);
|
|
}
|
|
|
|
const noteTodosPriorityExists = await client.query(`
|
|
SELECT column_name
|
|
FROM information_schema.columns
|
|
WHERE table_name='note_todos' AND column_name='priority'
|
|
`);
|
|
|
|
if (noteTodosPriorityExists.rows.length === 0) {
|
|
logger.info(' → Ajout du champ priority à note_todos');
|
|
await client.query(`ALTER TABLE note_todos ADD COLUMN priority BOOLEAN DEFAULT FALSE`);
|
|
}
|
|
|
|
// Migration 3: Champ in_progress pour les tâches globales
|
|
logger.info(' Vérification: champ in_progress pour global_todos...');
|
|
|
|
const globalTodosInProgressExists = await client.query(`
|
|
SELECT column_name
|
|
FROM information_schema.columns
|
|
WHERE table_name='global_todos' AND column_name='in_progress'
|
|
`);
|
|
|
|
if (globalTodosInProgressExists.rows.length === 0) {
|
|
logger.info(' → Ajout du champ in_progress à global_todos');
|
|
await client.query('ALTER TABLE global_todos ADD COLUMN in_progress INTEGER DEFAULT 0');
|
|
}
|
|
|
|
// Créer les index pour performance
|
|
await client.query(`CREATE INDEX IF NOT EXISTS idx_global_todos_priority ON global_todos(priority DESC, created_at DESC)`);
|
|
await client.query(`CREATE INDEX IF NOT EXISTS idx_note_todos_priority ON note_todos(priority DESC, position)`);
|
|
|
|
await client.query('COMMIT');
|
|
|
|
logger.info('✅ Migrations automatiques terminées avec succès');
|
|
|
|
} catch (error) {
|
|
await client.query('ROLLBACK');
|
|
logger.error('❌ Erreur lors des migrations automatiques:', error);
|
|
throw error;
|
|
} finally {
|
|
client.release();
|
|
}
|
|
|
|
await pool.end();
|
|
|
|
} catch (error) {
|
|
logger.error('❌ Erreur fatale lors des migrations:', error);
|
|
// Ne pas crasher l'application, juste logger l'erreur
|
|
}
|
|
}
|
|
|
|
// Si appelé directement
|
|
if (require.main === module) {
|
|
autoMigrate().then(() => {
|
|
process.exit(0);
|
|
}).catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
module.exports = { autoMigrate };
|