diff --git a/migrations/20250903141000_create_webhook_bap_config.sql b/migrations/20250903141000_create_webhook_bap_config.sql new file mode 100644 index 0000000..5378dea --- /dev/null +++ b/migrations/20250903141000_create_webhook_bap_config.sql @@ -0,0 +1,25 @@ +-- Migration: Création table configuration webhook BAP +-- Created: 2025-09-03T14:10:00.000Z + +-- Table pour configuration webhook BAP +CREATE TABLE IF NOT EXISTS webhook_bap_config ( + id SERIAL PRIMARY KEY, + name VARCHAR(100) NOT NULL DEFAULT 'Configuration BAP', + webhook_url TEXT NOT NULL, + description TEXT, + is_active BOOLEAN NOT NULL DEFAULT true, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Configuration par défaut webhook BAP +INSERT INTO webhook_bap_config (name, webhook_url, description, is_active) +VALUES ( + 'Configuration BAP', + 'https://workflow.ffnancy.fr/webhook/a3d03176-b72f-412d-8fb9-f920b9fbab4d', + 'Configuration par défaut pour l''envoi des fichiers BAP vers n8n', + true +) ON CONFLICT DO NOTHING; + +-- Commentaire sur la table +COMMENT ON TABLE webhook_bap_config IS 'Configuration pour webhook BAP n8n'; \ No newline at end of file diff --git a/scripts/create-migration.js b/scripts/create-migration.js new file mode 100755 index 0000000..4c7382b --- /dev/null +++ b/scripts/create-migration.js @@ -0,0 +1,23 @@ +#!/usr/bin/env node + +// Script utilitaire pour créer une nouvelle migration +// Usage: node scripts/create-migration.js "nom de la migration" + +import { createMigration } from '../server/migrations.js'; + +const migrationName = process.argv[2]; + +if (!migrationName) { + console.error('❌ Usage: node scripts/create-migration.js "nom de la migration"'); + console.error(' Exemple: node scripts/create-migration.js "add user roles table"'); + process.exit(1); +} + +try { + const filePath = createMigration(migrationName); + console.log(`✅ Migration créée avec succès: ${filePath}`); + console.log(' Éditez ce fichier pour ajouter votre SQL'); +} catch (error) { + console.error('❌ Erreur lors de la création de la migration:', error); + process.exit(1); +} \ No newline at end of file diff --git a/server/index.ts b/server/index.ts index 732238b..bee0279 100644 --- a/server/index.ts +++ b/server/index.ts @@ -2,6 +2,19 @@ import express, { type Request, Response, NextFunction } from "express"; import { registerRoutes } from "./routes.js"; import { setupVite, serveStatic } from "./vite.js"; +// Run database migrations first +if (process.env.NODE_ENV === 'production') { + console.log('🔄 [STARTUP] Running database migrations...'); + try { + const { runMigrations } = await import('./migrations.js'); + await runMigrations(); + console.log('✅ [STARTUP] Database migrations completed'); + } catch (error) { + console.error('❌ [STARTUP] Database migrations failed:', error); + process.exit(1); + } +} + // Initialize weather system console.log('🌤️ [STARTUP] Initializing weather system...'); const { initializeWeatherConfig } = await import('./weatherAutoConfig.js'); diff --git a/server/migrations.ts b/server/migrations.ts new file mode 100644 index 0000000..c5917cc --- /dev/null +++ b/server/migrations.ts @@ -0,0 +1,122 @@ +import { sql } from 'drizzle-orm'; +import { db } from './db.js'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Table pour suivre les migrations appliquées +const createMigrationsTable = async () => { + await db.execute(sql` + CREATE TABLE IF NOT EXISTS migrations ( + id SERIAL PRIMARY KEY, + filename VARCHAR(255) UNIQUE NOT NULL, + executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + `); +}; + +interface MigrationFile { + filename: string; + sql: string; +} + +export async function runMigrations() { + console.log('🔄 Vérification des migrations...'); + + try { + // Créer la table des migrations si elle n'existe pas + await createMigrationsTable(); + + // Récupérer les migrations déjà appliquées + const appliedMigrations = await db.execute(sql`SELECT filename FROM migrations`); + const appliedFilenames = new Set(appliedMigrations.rows.map((row: any) => row.filename)); + + // Lire tous les fichiers de migration + const migrationsDir = path.join(__dirname, '..', 'migrations'); + + if (!fs.existsSync(migrationsDir)) { + console.log('📁 Création du dossier migrations...'); + fs.mkdirSync(migrationsDir, { recursive: true }); + } + + const migrationFiles = fs.readdirSync(migrationsDir) + .filter(file => file.endsWith('.sql')) + .sort(); // Important : tri pour exécuter dans l'ordre + + if (migrationFiles.length === 0) { + console.log('✅ Aucune migration à exécuter'); + return; + } + + let executedCount = 0; + + for (const filename of migrationFiles) { + if (appliedFilenames.has(filename)) { + console.log(`⏭️ Migration déjà appliquée: ${filename}`); + continue; + } + + console.log(`🔄 Exécution migration: ${filename}`); + + const filePath = path.join(migrationsDir, filename); + const migrationSql = fs.readFileSync(filePath, 'utf8'); + + try { + // Exécuter la migration dans une transaction + await db.transaction(async (tx: any) => { + // Exécuter le SQL de migration + await tx.execute(sql.raw(migrationSql)); + + // Marquer comme appliquée + await tx.execute(sql` + INSERT INTO migrations (filename) + VALUES (${filename}) + `); + }); + + console.log(`✅ Migration appliquée avec succès: ${filename}`); + executedCount++; + + } catch (error) { + console.error(`❌ Erreur lors de la migration ${filename}:`, error); + throw error; + } + } + + if (executedCount > 0) { + console.log(`✅ ${executedCount} migration(s) appliquée(s) avec succès`); + } else { + console.log('✅ Toutes les migrations sont déjà appliquées'); + } + + } catch (error) { + console.error('❌ Erreur lors de l\'exécution des migrations:', error); + throw error; + } +} + +// Fonction pour créer une nouvelle migration +export function createMigration(name: string): string { + const timestamp = new Date().toISOString().replace(/[^\d]/g, '').slice(0, 14); + const filename = `${timestamp}_${name.toLowerCase().replace(/\s+/g, '_')}.sql`; + const migrationsDir = path.join(__dirname, '..', 'migrations'); + + if (!fs.existsSync(migrationsDir)) { + fs.mkdirSync(migrationsDir, { recursive: true }); + } + + const filePath = path.join(migrationsDir, filename); + const template = `-- Migration: ${name} +-- Created: ${new Date().toISOString()} + +-- Add your SQL here +`; + + fs.writeFileSync(filePath, template); + console.log(`📝 Migration créée: ${filePath}`); + + return filePath; +} \ No newline at end of file